I have a after insert trigger on an object that calls a @future method to do a callout to an external system.
If the callout fails I want it to update the original object.
I am batching up 100 at a time and there are timeouts on the callout, but the record is not being updated.
The debug log is showing an update but the object is not updated. Any thoughts?
Trigger
/**
* Copyright (c) 2012, FinancialForce.com, inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the FinancialForce.com, inc nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
trigger SyncLog_TRG on SyncLog__c (after delete, after insert, after update, before delete, before insert, before update)
{
// Creates Domain class instance and calls appropriate overideable methods according to Trigger state
if (!System.isFuture()) {
SObjectDomain.triggerHandler(DOM_SyncLogs.class);
}
}
DOM_Synclogs.class
/* **************************************************************************************** */
public with sharing class DOM_SyncLogs extends SObjectDomainLocal {
/* *************************************************************************************** */
public DOM_SyncLogs(List<SyncLog__c> sObjectList) {
// Domain classes are initialised with lists to enforce bulkification throughout
super(sObjectList);
}
/* *************************************************************************************** */
public class Constructor implements SObjectDomain.IConstructable
{
public SObjectDomain construct(List<SObject> sObjectList)
{
return new DOM_SyncLogs(sObjectList);
}
}
/* *************************************************************************************** */
/* Beginning trigger handlers
/* *************************************************************************************** */
/* *************************************************************************************** */
public override void afterInsert()
{
super.afterInsert();
SVC_SyncLogService.sendSyncLogFuture(new List<ID>(Trigger.newMap.keySet()));
}
}
SVC_SyncLogService
/******************************************************************************************************************/
/** sendSyncLogFuture
* @author MCAMP
* @date 08/15/2019
* @description in Future Send the SyncLog to iClassPro - **************** Demonstrates using @future ****************
* @param List<ID> List of Synclog IDs. - **************** @future required primative types or collections of primitive types ****************
* @return
*/
@future (callout=true)
public static void sendSyncLogFuture(List<ID> SyncLogList) {
sendSyncLog(SyncLogList);
}
/******************************************************************************************************************/
/** sendSyncLog
* @author MCAMP
* @date 08/15/2019
* @description Send the SyncLog to iClassPro - **************** Demonstrates using @future ****************
* @param List<ID> List of Synclog IDs.
* @return
*/
public static void sendSyncLog(List<ID> SyncLogList) {
//place any code needed before sending to manager here
// use list of IDs to get list of SyncLogs
// MGR_iCampPro.sendSyncLog(SyncLogRecordList);
DM_SyncLogSelector querySelector = new DM_SyncLogSelector();
Set<Id> SyncLogIds = new Set<Id>();
for (Id SyncLogId: SyncLogList) {
SyncLogIds.add(SyncLogId);
}
List<SyncLog__c> SyncLogRecordList = querySelector.selectSObjectsById(SyncLogIds);
//if running as part of a test class do not call manager
if(!Test.isRunningTest()){
MGR_iCampPro MgrICampPro = new MGR_iCampPro();
MgrICampPro.sendSyncLog(SyncLogRecordList);
}
}
MGR_iCampPro
public HttpResponse sendSyncLog(List<SyncLog__c> SyncLogList) {
Http http = new Http();
HttpRequest req = new HttpRequest();
iCampPro_Settings__c iCampSettings = iCampPro_Settings__c.getOrgDefaults();
string endPoint = iCampSettings.iCampPro_Base_URL__c + iCampSettings.iCampPro_Api_Url__c + iCampSettings.SyncLog_Endpoint__c;
req.setEndpoint(endPoint);
req.setHeader('Content-Type','application/x-www-form-urlencoded') ;
req.setHeader('Accept','application/json') ;
req.setMethod('POST');
HttpResponse res;
for (SyncLog__c SyncLogObject: SyncLogList) {
String email = '';
String firstName = '';
String lastName = '';
Boolean adult = false;
if(SyncLogObject.Email__c != null){email = EncodingUtil.UrlEncode(SyncLogObject.Email__c,'UTF-8');}
if(SyncLogObject.FirstName__c != null){firstName = EncodingUtil.UrlEncode(SyncLogObject.FirstName__c,'UTF-8');}
if(SyncLogObject.LastName__c != null){lastName = EncodingUtil.UrlEncode(SyncLogObject.LastName__c,'UTF-8');}
if(SyncLogObject.Confirmed_Adult__c != null){adult = SyncLogObject.Confirmed_Adult__c;}
String bodyParams = 'objectId=' + SyncLogObject.Id +
'&accountDefaultContact=' + SyncLogObject.Account_Default_Contact__c +
'&accountId=' + SyncLogObject.AccountId__c +
'&contact=' + SyncLogObject.Contact__c +
'&contactId=' + SyncLogObject.ContactID__c +
'&createdById=' + SyncLogObject.CreatedById +
'&email=' + email +
'&firstName=' + firstName +
'&gender=' + SyncLogObject.Gender__c +
'&homePhone=' + SyncLogObject.HomePhone__c +
'&lastModifiedById=' + SyncLogObject.LastModifiedById +
'&lastName=' + lastName +
'&mailingCity=' + SyncLogObject.MailingCity__c +
'&mailingCountry=' + SyncLogObject.MailingCountry__c +
'&mailingPostalCode=' + SyncLogObject.MailingPostalCode__c +
'&mailingState=' + SyncLogObject.MailingState__c +
'&mailingStreet=' + SyncLogObject.MailingStreet__c +
'&middleName=' + SyncLogObject.Middle_Name__c +
'&mobilePhone=' + SyncLogObject.MobilePhone__c +
'&ownerId=' + SyncLogObject.OwnerId +
'&recordVersion=' + SyncLogObject.Record_Version__c +
'&status=' + SyncLogObject.Status__c +
'&statusDetail=' + SyncLogObject.Status_Detail__c +
'&statusLastUpdated=' + SyncLogObject.Status_Last_Updated__c +
'&name=' + SyncLogObject.Name +
'&grade=' + SyncLogObject.Grade__c +
'&birthDate=' + SyncLogObject.Birthdate__c +
'&adult=' + adult;
req.setBody(bodyParams);
try {
res = http.send(req);
} catch (exception e) {
//Need to mark as error here
SyncLog__c syncLogUpdate = new SyncLog__c();
syncLogUpdate.Id = SyncLogObject.Id;
syncLogUpdate.Status__c='Failed';
syncLogUpdate.Status_Detail__c=e.getMessage();
syncLogUpdate.Status_Last_Updated__c=System.now();
update syncLogUpdate;
throw e;
}
}
return res;
}