0

I am trying to call the external API endpoint from Salesforce when an account is created. I created a trigger on the Account which looks like below

trigger createSegSubSegmentD on Account (after insert) {        
    Id AccParentRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('Parent Account').getRecordTypeId();
    String recordType = '';        
    if(trigger.isinsert)
    {
        set<Id> AccountIds = new Set<Id>();
        list<Account> acclist = new list<Account>();
        for(Account acc : trigger.new){
            AccountIds.add(acc.Id); 
        }
        
        if(AccountIds.size()>0)
        {
            acclist = [Select id,Data_Source_ID__c,Parent.Data_Source_ID__c,RecordTypeId from Account WHERE Id IN: AccountIds ];
            
            for(Account acc : acclist)
            {
               // If the record is Parent account
                    if(acc.RecordTypeId == AccParentRecordTypeId)
                    {
                        recordType = 'Segment';
                        if(String.isNotBlank(acc.Data_Source_ID__c) ||String.isNotEmpty(acc.Data_Source_ID__c)) 
                        {                                handleDRequest.createSegSubSegD(acc.Data_Source_ID__c,acc.Parent.Data_Source_ID__c,recordType);
                        }}
                   }}

Where I have a apex Class which makes the future callouts to the third party endpoints

    // Called from the trigger when a Parent/Site account is created
    public static void createSegSubSegD (String segID,String parSegID,String recordType)
    {
        String jsonBody = '';
        String endPoint = '';
        if (segID != null){             
            if(recordType == 'Segment')
            {
                // Compose Json Body for the Segment creation
                SegJSON js = new SegJSON();
                js.dataAreaId = 'abc';
                js.SegmentCode = segID;
                jsonBody = json.serialize(js); 
                endPoint = '/data/ABCParentAccounts'; 
                createSegSubSegment(jsonBody,endPoint);
            }
        }

  @future (callout=true)
    public static void createSegSubSegment(String jsonBody,String endPoint) {
        String clientId = '';
        String clientSecret = '';
        String resource = '';
        String tenant_id = '';
        String bearerToken = '';        
        try{                       
            DSetting__c[] DObj_Data = [SELECT Client_ID__c,Client_Secret__c,Resource__c,Tenant_ID__c from DSetting__c where Name = 'ccc' ];
            if( DObj_Data.size() > 0 ) 
            {
                clientId = DObj_Data[0].Client_ID__c;
                clientSecret = DObj_Data[0].Client_Secret__c;
                resource = DObj_Data[0].Resource__c;
                tenant_id =  DObj_Data[0].Tenant_ID__c;

                String reqbody = 'grant_type=client_credentials&client_id='+clientId+'&client_secret='+clientSecret+'&tenant_id='+tenant_id+'&resource='+resource;
                
                Http h = new Http();
                HttpRequest req = new HttpRequest();
                req.setBody(reqbody);
                req.setMethod('POST');
                req.setEndpoint('https://login.microsoftonline.com/abc.org/oauth2/token');
                HttpResponse res = h.send(req);
                
                if(res.getstatusCode() == 200 && res.getbody() != null){
                    deserializeResponse resp1 = (deserializeResponse)JSON.deserialize(res.getbody(),deserializeResponse.class);
                    bearerToken = resp1.access_token;
                }
                
                Http http1 = new Http();
                HttpRequest req1 = new HttpRequest();
                String dEndPoint = resource + endPoint;           
                req1.setMethod('POST');
                req1.setTimeout(20000);
                req1.setHeader('Authorization','Bearer '+bearerToken);
                req1.setEndpoint(dEndPoint);
                req1.setBody(jsonBody);
                req1.setHeader('Content-Type', 'application/json;charset=UTF-8');
                HttpResponse res1 = http1.send(req1);
           }
        }
        catch(CalloutException  e)
        {
            system.debug('Exception Occurred in Call Out to ' + e.getMessage());
        }
        catch(Exception e)
        {
            system.debug('Exception Occurred' + e.getMessage());
        }
    }
    
    //Deserialize the response received from the Authorization provider
    public class deserializeResponse
    {
        public String token_type;
        public String expires_in;
        public String ext_expires_in;
        public String expires_on;
        public String not_before;
        public String resource;
        public String access_token;
    }       
}

When I tried to create the account using Data loader with 10 records it worked. But when I tried to create the over 100 records I am getting error in the data loader like createSegSubSegmentD: System.LimitException: Too many future calls: 51

Looking in to different questions on the stackexchange there is different solution batcahable, Queueable. Can anyone suggest whether queuable/batcahble works for future callouts here. Also here I the external API uses the OAUTH authentication, so I am calling the Auth Provider get the access token and then calling the Endpoint. Also if I am changing the apex calss to Batcahable/Queable does that work well with just 1 record? Any help is greatly appreciated

  • @identigral I did take a look at it. I wasnt sure how the fewer records work without any issue and I guess I bulkified my code here was hoping it would make the callouts sequentials. Pretty new to salesforce not able to follow the anwer there – user81642 Jun 25 at 4:18
  • Have a look at Batch Apex trailhead, then follow the referenced answer. – identigral Jun 25 at 4:20
  • @identigral Can I just place the entire createSegSubSegment code in to the execute method of the Batcahable? – user81642 Jun 25 at 4:41
  • @identigral I also fund this https://salesforce.stackexchange.com/questions/57154/calling-future-method-from-batch-class It would be great if anyone can suggest if batcahable or queabale works here. – user81642 Jun 25 at 4:46
1

You could still use future methods, you just need to bulkify your future code to accept a list of inputs instead of just one. Have every 50 records or so be placed into a single future call. If you decide to use Queueable or Batchable, you would still need to bulkify your code, since you only get a handful of each type you can use per transaction. I would recommend using future or Queueable for code to be called from triggers, since there may be many calls, and Batchable calls are severely limited.

| improve this answer | |
  • Thank you so much, when you say I need to bulkify the future code Do I need to the send the array of the JSON in to the Call. I am sorry if asking dumb question. – user81642 Jun 25 at 15:14
  • @user81642 Yes, it could be a list of JSON strings, or any other way you'd like to represent the callouts. – sfdcfox Jun 25 at 16:58
  • As you suggested I did bulkify the code salesforce.stackexchange.com/questions/310822/… and it worked when I tried to create more than 100 records through Data loader. And also I am checking at the future Call limit and can you help me how I can move to queueable if the future Callout limit is crossed. Because currently my Class is just using the futire Callout method and can we call the queueable from the same class? – user81642 Jun 28 at 3:40

Not the answer you're looking for? Browse other questions tagged or ask your own question.