Skip to content

Instantly share code, notes, and snippets.

@rapsacnz
Last active March 30, 2020 05:39
Show Gist options
  • Save rapsacnz/31dc4b142882cdf41787cea0b3faa697 to your computer and use it in GitHub Desktop.
Save rapsacnz/31dc4b142882cdf41787cea0b3faa697 to your computer and use it in GitHub Desktop.

To escape the current context and run as the "Automated Process" user (high priviliges), do this:

First define a platform event. For example: Async_Message__e with a paylod and a type.

Then define an AsyncActionUtil class. This will contain a way of inserting these platform events and handling them after insert. The class should contain an insert method:

public static void insertAsyncMessage(String type, String payload) {
  Async_Message__e[] messages = new List<Async_Message__e>{
    new Async_Message__e(Type__c = type, Payload__c = payload)
  };
  // Call method to publish events
  Database.SaveResult[] results = EventBus.publish(messages);
}

The handle insert method (called from a trigger)

public static void handle(Async_Message__e[] messages) {
  for (Async_Message__e message : (Async_Message__e[]) Trigger.new) {
      handleAsyncMessage(message);
    }
  }

public static void handleAsyncMessage(Async_Message__e message) {
  try {
    switch on message.Type__c {
      when 'Callable' {
        Map<String, Object> callableParams = (Map<String, Object>) JSON.deserializeUntyped(
          message.Payload__c
        );
        String className = (String) callableParams.get('className');
        String methodName = (String) callableParams.get('methodName');
        String params = (String) callableParams.get('params');
        Map<String, Object> paramsMap = (Map<String, Object>) JSON.deserializeUntyped(params);
        //make the call!
        Callable someClass = (Callable) Type.forName(className).newInstance();
        someClass.call(methodName, paramsMap);
      }
    }
  } catch (Exception e) {
    System.debug('ASYNC EXCEPTION: ' + 'Line ' + e.getLineNumber() + ' ' + e.getMessage());
  }
}

"Callable" is just a string type added to the Async_Message__e on insert so that this object can be used for multiple porposes.

The Payload__c is just a JSON encoded string with any sort of data.

The trigger looks like this:

trigger AsyncMessageTrigger on Async_Message__e(after insert) {
  AsyncActionUtil.handle(Trigger.new);
}

The Callable class (implements the Callable interface) uses the AsyncActionUtil like this:

//completely bypasses any restrictions on the user running this.
AsyncActionUtil.enqueueCallable(
  'MyClass',
  'saveSObjects',
  new Map<String, Object>{'sObjects' => caseObjects}
);

The saveSObjects method is a not actually a method - it's a action that invokes something on the Callable class and looks like this:

// Dispatch actual methods
public Object call(String action, Map<String, Object> args) {
  switch on action {
    when 'saveSObjects' {
      try {
        Object[] objs = (Object[]) args.get('sObjects');
        SObject[] sobjs = (SObject[]) JSON.deserialize(JSON.serialize(objs), SObject[].class);
        Database.insert(sobjs, false);
      } catch (Exception e) {
        return null;
      }
    }
    when else {
      throw new YourCustomException('Callable Method not implemented');
    }
  }
  return null;
}

So in short:

  • Originating class uses AsyncActionUtil to insert a Platform Event.
  • Originating class is a Callable
  • The Platform Event is handled by AsyncActionUtil
  • AsyncActionUtil calls the action on the Callable class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment