1

We are co-developing some code with a external vendor.

We wrote some custom code that when certain field on the contact are updated, it creates a record in a sync object(SyncLog). Then there is a trigger that calls a @future method to send that object to an external system. This is required for callouts in triggers.

This is working fine, however we have NPSP installed.

When the address changes it kicks off a address validation process in the @future context. If the address gets validated, it updated the contact and the process creates the SyncLog (still in @future) At this time we get an error - Future method cannot be called from a future or batch method.

So I figure this out and check for @future and call a method without @future. Then we getan error - Http post failed with exception Callout from triggers are currently not supported.

Due to our agreement we are trying to avoid any third party integration utilities.

The callout need to happen in near realtime.

We are caught in a Catch 22.  Does anyone have any suggestions?

Here is a diagram of the problem: enter image description here Here is the pertinent code. trigger Code

        if (!System.isFuture()) {
        SVC_SyncLogService.sendSyncLogFuture(new List<ID>(Trigger.newMap.keySet()));
    } else {
        SVC_SyncLogService.sendSyncLog(new List<ID>(Trigger.newMap.keySet()));
    }

SVC_SyncLogService Code

@future (callout=true)
public static void sendSyncLogFuture(List<ID> SyncLogList) {
    try {
        sendSyncLog(SyncLogList);
    } catch (Exception e) {
        {exception code here}
    }
}

public static void sendSyncLog(List<ID> SyncLogList) {
    List<SyncLog__c> SyncLogRecordList = [{select goes here}];
    //if running as part of a test class do not call manager
    if(!Test.isRunningTest()){
        {vendorClass} Mgr = new {vendorClass}();
        Mgr.sendSyncLog(SyncLogRecordList);
    }
}

Our Solution

@gNerb, Thanks for pointing us down this path. Here is the proof of concept for using Platform Events as a solution. We kept all our other code just changed the trigger and added a platform event, new method to publish event, and a trigger to listen to event.

Diagram enter image description here

SyncLog Trigger Code

SVC_SyncLogService.publishSyncLogEvent(new List<ID>(Trigger.newMap.keySet()));

New SVC_SyncLogService Code (copied from https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_events_publish_apex.htm)

    public static void publishSyncLogEvent(List<ID> SyncLogList) {
    list<{vendor}_Update__e> iCPEvents = new list<{vendor}_Update__e>();
    {vendor}_Update__e iCPEvent = new {vendor}_Update__e();
    for (Id SyncLogId: SyncLogList) {
        iCPEvent = new iCampPro_Update__e();
        iCPEvent.SynclogID__c = SyncLogId;
        iCPEvents.add(iCPEvent);
    }
    if(iCPEvents.size() > 0){
        // Call method to publish events
        List<Database.SaveResult> results = EventBus.publish(iCPEvents);
        // Inspect publishing result for each event
        for (Database.SaveResult sr : results) {
            if (sr.isSuccess()) {
                System.debug('Successfully published event.');
            } else {
                for(Database.Error err : sr.getErrors()) {
                    System.debug('Error returned: ' +
                                err.getStatusCode() +
                                ' - ' +
                                err.getMessage());
                }
            }
        }
    }
}

New {vendor} Update Event Trigger

trigger {vendor}Update_TRG on {vendor}_Update__e (after insert) {
list<ID> SyncLogList = new list<ID>();
for ({vendor}_Update__e event : Trigger.New) {
    if(event.SynclogID__c <> ''){
        SynclogList.add((ID)event.SynclogID__c);
    }
}
if(SyncLogList.size() > 0){
    SVC_SyncLogService.sendSyncLogFuture(SyncLogList);
}}
8
  • 2
    Have you looked into Platform events? developer.salesforce.com/docs/atlas.en-us.platform_events.meta/… It removes the need for a future, or any callout, to an external system, instead the external system subscribes via CometD to recieve the updates.
    – gNerb
    Apr 16, 2020 at 20:07
  • Your future method (Validate) on completion is calling the future method ( callout) and hence the issue. You need to control the execution contexts. Why validation is in future (Aync) ? Do you need acknowledgement ? Do you have middle ware ? Apr 16, 2020 at 20:23
  • @VishalSharma As stated, the validation occurs in a managed package which the user doesn't have access to the code. Additionally, the validation happens in a trigger. SFDC does not allow callouts within triggers unless they are in future methods. This means even if it weren't a managed package, he would still require it to be in async.
    – gNerb
    Apr 16, 2020 at 20:28
  • @gNerb I have not looked at platform events in depth. That may be a way to do this. Having external vendor subscribe, may cause issue, but I might be able to publish event in trigger and have flow act on it to send. That should break the future context of the trigger. I'll investigate that.
    – M. Camp
    Apr 16, 2020 at 20:54
  • You can catch the PE in an apex trigger, and then do the callout yourself. This will work because the PE will have broken the Apex context. No need for the external system su subscribe. Apr 16, 2020 at 22:31

1 Answer 1

0

Have you looked into Platform events? It removes the need for a future, or any callout, to an external system, instead the external system subscribes via CometD to recieve the updates.

1
  • Thanks for pointing us down this path.
    – M. Camp
    Apr 17, 2020 at 16:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .