An Introduction to Copado functions

COPADO

  • Introduction to Copado Functions
  • 5 MAY 2023
  • blog

Copado Functions

This is the second article in a series on Copado Functions. If you dont know what Copado Functions are head over to the first article and catch up on how to create one, and some sample code that will get you started.

For this month I'm going to focus on a reasonably simple scenario that would have been really helpful if Copado included this out of the box - I want to know what version each of my Sandboxes are running ... yes I know I can login to my Production instance and check on the Sandbox setup page, did I happen to mention highly secure environment access?

Okay so, given I don't want my Release Manager logging into Production, I do want the information in a report and potentially want to execute functions based on the result I therefore want a value stored against my copado__Environment__c records.


Show us your code!

The point of this post is not how to get the code up and running so I wont bother with this detail. Following on from the first article and using the same pattern the code itself is pretty simple, if you need a refresher - again head to the first article in this series for how this works.
On the subject of code .... here's my own little rant - why Copado cant provide a container image that includes a decent URLEncode function ... seriously this would have saved me HOURS! For what its worth this is getting close:


                            QRYBODY=$(echo $QRYBODY | sed "s+ +%20+g" | sed "s+;+%3B+g" | sed "s+&+%26+g" | sed "s+@+%40+g" | sed "s+:+%3A+g" | sed "s+\[+%5B+g" | sed "s+\]+%5D+g" )
                            


Running the Function

The documentation for how to do this is here https://docs.copado.com/articles/#!copado-developer-center-publication/execute-a-functionand of course after reading this you're fully prepared to smash out for function execution process, flow, button or whatever. You head off into your happy Salesforce sandbox and smash this out in 5 mins flat right.

Not so much ....

The documentation is a little inconsistent and it has conflicting processes and approaches, there's three approaches to executing a function:

  • Run as part of a pipeline
    For today I'm going to ignore this - Ill write a later article about how to do this but for now I'm assuming your latest and greatest function is not applied to your pipeline.
  • Run from a Flow
    There's definitely the most amount of documentation around this option and I'd recommend this as your go to approach except for anything that you want that's scheduled, ill explain why in a moment.
  • Run from Apex
    This is for me the easiest approach but it has the least flexibility, that being said there's a lot going for an apex approach ... I'll explain why.


Executing Copado functions by Flow

The best part about using Flow is that it is quickly customisable and can support executing multiple Functions from a single Flow, this is where consistency comes back to save us! First thing we need to achieve is to collect the parameters together that we need to pass to the Function and store them into a JSON formatted string.

The most difficult parameter to generate is the SessionId that we will use to identify our session for the callback functionality. To do this you will need a single InvokableFunction that returns the detail from UserInfo.getSessionId(). Here's the code for this - its a one liner but you get the picture.


    public class copadoSessionId {
        @InvocableMethod(label='getSessionID' description='Returns the session id from UserInfo.' category='Copado')
        public static List getSessionID() {
            return new List{ UserInfo.getSessionId() };
        }
    }
                            

With this function in place we can now get the session id for the running user, no more pasting in the debug output into your hard coded parameters! We use this function to return a value to us and store this in the sessionId variable:

Invokable Function result in Flow

When following the examples on Copado it states that, instead of creating a collection of parameters you can use a single JSON formatted string for all parameters and there values if you follow the format:


    [
        {"name": "value"},
        {"name": "value"}
    ]
                            


Standards, why we have them.

So back in the first article I mentioned I pretty much have a standard set of Parameters for each function, if I need an additional parameter for a script it gets a consistent name and more importantly all of the functions I use get the same Parameter - even if they don't use it. Here's the reason why.

Once I have the Session Id and Environment Id, I build a single variable that contains a JSON string for all of the parameters, then all I need to do is assign this to the ApexAction for the Copado Function. If I chain multiple functions together - I use the same JSONString for all of them!

JSON String assignment
As you can see from the below, the formatting of the JSON string is pretty easy and consistent.


    [ 
        {"RemoteSessionID": "{$Context.Credential.SessionId}", 
        {"RemoteEndpoint": "{$Context.Credential.Endpoint}"}, 
        {"LocalSessionID": "$Flow.SessionID"}, 
        {"EnvName": "{$Context.Name}"}, 
        {"LocalEndpoint": "$Flow.LocalEndpoint"}, 
        {"Version": "$Flow.Version"} 
     ]
                            

This approach means that I'm not playing around with multiple assignment operations and more importantly, it means adding another action is 2 mins work without mucking up the JSON data yet again, for like the 100th time.

Passing it to the Copado Function is easy, we just specify the JSON variable and execute the function.

Perfect! Bingo ... Easy as 1,2,3.

Hooking the Flow up to a button is trivial and easy, I wont bother showing the setup for this. Note I havn't discussed scheduling the Flow - that's because it wont work.


Scheduled Flows.

Scheduled Flows are relitivley new and while they are really cool and work pretty much as you would expect they do operate in the context of the running Flow user - there the problem starts as the running user is actually the automation user and not a user context - so the approach above while great for interactive execution of functions, wont work at all for scheduled Flows - for that you will need to go to Apex.

When you schedule an Apex Schedulable job in Salesforce, it executes in the user context of the user who scheduled the job. The job runs with the permissions and access rights of the user who initiated the scheduling - this solves our ownership and getSessionId() problem. To do this - you will need to revert to Apex.


Executing Copado function using Apex.

Executing a function via Apex is just as easy as using a Flow. We need to create an instance of the copado.RunCopadoFunction.InvocableVariables then we need to build the collection of parameters, using the same parameters as above we get the following, note that the documentation here is also wrong - you cant construct them like this


    List parameterList = new List();
    copado.Parameter parameter1 = copado.Parameter(name='key1', value='value1');
    parameterList.add(parameter1);
    copado.Parameter parameter2 = copado.Parameter(name='key2', value='value2');
    parameterList.add(parameter2);
                            
You need to have way more lines than that!


    List parameterList = new List();

    copado.Parameter remoteSessionId = new copado.Parameter();
    remoteSessionId.name = 'RemoteSessionID';
    remoteSessionId.value = '{$Context.Credential.SessionId}';
    parameterList.add(remoteSessionId);

    copado.Parameter remoteSessionId = new copado.Parameter();
    remoteSessionId.name = 'RemoteSessionID';
    remoteSessionId.value = '{$Context.Credential.SessionId}';
    parameterList.add(remoteSessionId);

    copado.Parameter remoteEndpoint = new copado.Parameter();
    remoteEndpoint.name = 'RemoteEndpoint';
    remoteEndpoint.value = '{$Context.Credential.Endpoint}';
    parameterList.add(remoteEndpoint);

    .
    .
    .

    copado.RunCopadoFunction.InvocableVariables request = new copado.RunCopadoFunction.InvocableVariables();
    request.functionApiName = 'MyFunction';
    request.contextId = copado__Environment__c.Id;
    request.parameters = parametersList;

                            
As you can see you need to add all of the parameters, including the ones that are Context only, this is a huge pain but at least it makes you check your work.

Thats it!

We could have created another JSON String and assigned this also, the documentation shows you how to do that, make sure you change the JSON to the format above - otherwise it just wont work.



    copado.RunCopadoFunction.InvocableVariables request = new copado.RunCopadoFunction.InvocableVariables();
    request.functionApiName = 'MyFunction';
    request.contextId = copado__Environment__c.Id;
    request.parametersJSON = '[ 
        {"RemoteSessionID": "{$Context.Credential.SessionId}", 
        {"RemoteEndpoint": "{$Context.Credential.Endpoint}"}, 
        {"LocalSessionID": "$Flow.SessionID"}, 
        {"EnvName": "{$Context.Name}"}, 
        {"LocalEndpoint": "$Flow.LocalEndpoint"}, 
        {"Version": "$Flow.Version"} 
     ]';

                            


Limitations & Next steps

The actual Flow execution user approach is pretty ugly, I'd like something a lot simpler, maybe a list of functions and then the ability to pick these and execute them sequentially for that I'd need a visual flow or maybe a lightning component - next month maybe Ill look at doing this.


Wrapping it up.

I thought my next article will put this together and focus on implementing a function across the pipeline - that will wrap up how to write a Copado Function, How to execute and then Ill finish up with some nice to haves.

As always please let me know how your journey with Copado Functions is progressing. Feel free to reach out and chat anytime at AppGenie.com.au..


This is article 2 on Copado Functions, in a series of 2. Please find links to each in the series.