Apex Integration
  • 6 Minutes to read

    Apex Integration


      Article summary

      In this example, we will create an Apex Class to update a custom field on our audience results dynamically.


      Apex Setup

      • We need to implement an interface so that our apex class is a valid final action. 
      • It has two methods, one for synchronous execution and one for asynchronous execution, which will be run if the batch size defined during the action setup is exceeded.
      
      global <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">interface</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">FinalAction_Interface</span> </span>{
      
          <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">runSync</span><span class="hljs-params" style="box-sizing: border-box;">(String actionApiName, Map</span></span><string, object="" style="box-sizing: border-box;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="box-sizing: border-box;"> args)</span></span>;
      
          <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">runAsync</span><span class="hljs-params" style="box-sizing: border-box;">(String actionApiName, Map</span></span><string, object="" style="box-sizing: border-box;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="box-sizing: border-box;"> args)</span></span>;
      }
      </string,></string,>
      • Here's the class we'd like to embed. It implements both the FinalAction_Interface and the Database.Batchable<SObject> interfaces. 
      • The Database.Batchable interface runs the class as a batch job for the asynchronous executions. You might also use other asynchronous approaches, such as Queueable Apex chaining, so that is not a requirement. 
      • We recommend Batchable Apex because it's the most reliable mechanism.
      • Important: The class needs to be global. Otherwise, Campaign Audience Builder will not be able to access it.
      
      global <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">DynamicFieldUpdate_FinalActionImpl</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">implements</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">lb</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">FinalAction_Interface</span>, <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">Database</span>.<span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">Batchable</span></span><sobject style="box-sizing: border-box;"><span class="hljs-class" style="box-sizing: border-box;"> </span>{
      
          <span class="hljs-function" style="box-sizing: border-box;">global <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">static</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">runSync</span><span class="hljs-params" style="box-sizing: border-box;">(String actionApiName, Map</span></span><string,object style="box-sizing: border-box;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="box-sizing: border-box;"> args)</span> </span>{
              List<sobject style="box-sizing: border-box;"> results = (List<sobject style="box-sizing: border-box;">) args.get(FinalAction.Variable.AUDIENCE_RESULTS.name());
              updateField(results, args);
          }
      
          <span class="hljs-function" style="box-sizing: border-box;">global <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">static</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">runAsync</span><span class="hljs-params" style="box-sizing: border-box;">(String actionApiName, Map</span></span><string,object style="box-sizing: border-box;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="box-sizing: border-box;"> args)</span> </span>{
              Integer batchSize = (Integer) args.get(FinalAction.Variable.BATCH_SIZE.name());
              Database.executeBatch(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">new</span> DynamicFieldUpdate_FinalActionImpl(actionApiName, args), batchSize);
          }
      
          String actionApiName;
          Map<string, object="" style="box-sizing: border-box;"> args;
      
          <span class="hljs-function" style="box-sizing: border-box;">global <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">DynamicFieldUpdate_FinalActionImpl</span><span class="hljs-params" style="box-sizing: border-box;">()</span> </span>{
              
          }
          
          <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">public</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">DynamicFieldUpdate_FinalActionImpl</span><span class="hljs-params" style="box-sizing: border-box;">(String actionApiName, Map</span></span><string, object="" style="box-sizing: border-box;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="box-sizing: border-box;"> args)</span> </span>{
              <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">this</span>.actionApiName = actionApiName;
              <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">this</span>.args = args;
          }
          
          <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">public</span> Database.<span class="hljs-function" style="box-sizing: border-box;">QueryLocator <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">start</span><span class="hljs-params" style="box-sizing: border-box;">(Database.BatchableContext bc)</span> </span>{
              <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">return</span> FinalAction.getFinalActionQueryLocator(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">this</span>.actionApiName, (Id) args.get(FinalAction.Variable.AUDIENCE_VERSION_ID.name()));
          }
          
          <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">execute</span><span class="hljs-params" style="box-sizing: border-box;">(Database.BatchableContext bc, List</span></span><sobject style="box-sizing: border-box;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="box-sizing: border-box;"> scope)</span> </span>{
              updateField(scope, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">this</span>.args);
          }
          
          <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">finish</span><span class="hljs-params" style="box-sizing: border-box;">(Database.BatchableContext bc)</span> </span>{
              <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">// user notification logic - skipped for simplicity</span>
          }
      
          <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">static</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">updateField</span><span class="hljs-params" style="box-sizing: border-box;">(List</span></span><sobject style="box-sizing: border-box;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="box-sizing: border-box;"> results, Map</span></span><string,object style="box-sizing: border-box;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="box-sizing: border-box;"> args)</span> </span>{
              String fieldName = (String) args.get(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">'fieldName'</span>);
              Schema.SoapType dataType = results[<span class="hljs-number" style="box-sizing: border-box; color: rgb(136, 0, 0);">0</span>].getSObjectType().getDescribe().fields.getMap().get(fieldName).getDescribe().getSOAPType();
              Object value = getCastedValue(dataType, args.get(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">'value'</span>));
              
              <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">for</span>(SObject result : results) {
                  result.put(fieldName, value);
              }
              update results;
          }
          
          <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">static</span> Object <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: 700;">getCastedValue</span><span class="hljs-params" style="box-sizing: border-box;">(Schema.SoapType dataType, Object value)</span> </span>{
              <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">switch</span> on dataType {
                  when Boolean {
                          <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">return</span> Boolean.valueOf(value);
                  }
                  when Integer {
                      <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">return</span> Integer.valueOf(value);
                  }
                  when Double {
                      <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">return</span> Double.valueOf(value);
                  }
                  when <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">else</span> {
                      <span class="hljs-keyword" style="box-sizing: border-box; font-weight: 700;">return</span> value;
                  }  
              }
          } 
      }
      </string,object></sobject></sobject></string,></string,></string,object></sobject></sobject></string,object></sobject>

      As you can see, the arguments are passed in as a string-to-object map. This map includes some predefined arguments and custom variables defined during the setup of the action.

      The predefined arguments always have the same keys, all part of the FinalAction.Variable Enum. They include:

      • AUDIENCE_VERSION_ID: Used to identify the audience on which the action is executed
      • AUDIENCE_RESULTS: The audience result records (e.g., Contacts) with all the fields selected during the setup of the Final Action
      • BATCH_SIZE: Batch size for asynchronous execution, defined during the setup of the Final Action

      The custom variables in this example are "fieldName", which stores the API name of the field which should be updated, and "value", which holds the new value accessed in the updateField method.


      Final Action Setup

      Open the CAB Settings tab in the Campaign Audience Builder app, navigate to Final Actions in the sidebar, and click New to create a new action.

      In the first modal screen, we enter the following information.

      • Final Action Name: Used as the button label on the audience results page.
      • Final Action API Name: Set automatically to identify the final action.
      • Type: Choose if you want to use Flow or Apex logic.
      • Flow: Search the available autolaunched flows to find the flow you'd like to use.
      • Description: Optional. Rich text will be displayed to the user before they run the action.
      • Success Message: Optional. Displayed when the action has been executed.
      • Custom Permission Name: Optional. The action will only be available to users with the corresponding custom permission if set.
      • Batch Size: Defines the batch size for asynchronous runs of the action. Defaults to 500. Depending on the logic you've implemented in your Flow / Apex, this can be increased or needs to be decreased.

      • In the next step, choose the object for which your logic has been configured. The action will only be available for audiences with the same result object type.
      • In our example, we've created the logic for the Contact object, so we select Contact:

      • Our Apex class doesn't reference fields on the audience results, so we don't need to add any.
      • In the third step, the custom variables used in the custom logic and entered by the user must be defined.

      • After clicking New Input Variable, a modal opens:

      • The label entered here is displayed to the user. The name is the key with which the variable is passed in the argument map. The type defines which input is displayed to the user.
      • We need to set up two variables, "fieldName" and "value":

      • And we're done!

      Using the Final Action

      • The action is now available on the results page in the dropdown next to Add to Campaign.
      • To celebrate the power of Salesforce, we want to set all Contact descriptions to "May the force be with you":

      • ‍When we click run, our custom apex logic is executed and updates the field values to our celebratory text: