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:
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.
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);
}}