0

When a Lead is created, I have a trigger that references a class that calls an external webservice, that digests its response, and should update a field called region_code__c from a value in the response.

It’s doing everything except update the region_code__c field.

The error I received is: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, execution of AfterUpdate

When I perused the rat nest of Google search results for this error I have come to know that either this is a recursion(sp?) issue or that I’m a big dummy and should be using (before insert, before update instead).

Fair enough, but when changing to (before insert, before update)—the process breaks. The trigger no longer passes the ipAddress__c value to the class so that the webservice can respond with relevant information—and the line that updates the Lead throws an error about not having an ID which makes sense since there isn’t an ID for that record yet.

Any help transmuting this from (after insert, after update) to (before insert, before update) is greatly appreciated.

Trigger

trigger triggerLeadIPStack on Lead (after insert, after update) {
    for (Lead a : trigger.new)
        if (a.ipAddress__c != null){
            ClassGetGeo.getLocation(a.id);
        }
}

Class

public with sharing class ClassGetGeo {
      
      @future (callout=true)  
      static public void getLocation(id leadID){
      Lead a = [Select id, ipAddress__c from Lead where id =: leadId];
                       
        string countrycode ='';
        string countryname ='';
        string regioncode ='';
        string regionname ='';
        
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        
        //access_key is functional for demo testing
        req.setEndpoint('https://api.ipstack.com/'+a.ipAddress__c+'?access_key=e767bdd2ee983a9407fccd6ccb7bff0e'); 
        req.setMethod('GET');
        HttpResponse res;
        res = h.send(req);
        System.debug('Result'+res.getbody());
        Map<string,object>m = (Map<string,object>)JSON.deserializeUntyped(res.getbody());
        regioncode=(String)m.get('region_code');
        regionname=(String)m.get('region_name');
        countrycode=(String)m.get('country_code');
        countryname=(String)m.get('country_name');

        System.debug('########Region Code:'+regioncode);
               
        a.Region_Code__c = regioncode;
        update a;
                   
    }
}

Example ipstack response:

{
ip: "174.201.250.155",
type: "ipv4",
continent_code: "NA",
continent_name: "North America",
country_code: "US",
country_name: "United States",
region_code: "CA",
region_name: "California",
city: "Rosemont",
zip: "95819",
latitude: 38.57093048095703,
longitude: -121.4357681274414,
location: {
geoname_id: 5388873,
capital: "Washington D.C.",
languages: [
{
code: "en",
name: "English",
native: "English"
}
],
country_flag: "http://assets.ipstack.com/flags/us.svg",
country_flag_emoji: "🇺🇸",
country_flag_emoji_unicode: "U+1F1FA U+1F1F8",
calling_code: "1",
is_eu: false
}
}
1

First of all, it should be in bulk. You should consider what would happen if a bulk load of records comes in. You could hit the 50 future calls per transaction limit if there's 51 leads loaded at once.

Second, yes, you need to not make a future call inside a future method. This recursion is specifically blocked.

Here's the fix for the class:

public with sharing class ClassGetGeo {
    @future (callout=true)
    static public void getLocation(Set<Id> leadIDs){
        Lead[] records = [Select id, ipAddress__c from Lead where id = :leadIds];
        for(Lead record: records) {
            HttpRequest req = new HttpRequest();
            req.setEndpoint('https://api.ipstack.com/'+record.ipAddress__c+'?access_key=e767bdd2ee983a9407fccd6ccb7bff0e'); 
            req.setMethod('GET');
            HttpResponse res = new http().send(req);
            Map<string,object>m = (Map<string,object>)JSON.deserializeUntyped(res.getbody());
            String regioncode=(String)m.get('region_code');
            String regionname=(String)m.get('region_name');
            String countrycode=(String)m.get('country_code');
            String countryname=(String)m.get('country_name');
            record.Region_Code__c = regioncode;
        }
        update records;
    }
}

Now, you just need to fix the trigger to support 100 callouts per future:

trigger triggerLeadIPStack on Lead (after insert, after update) {
    if(System.isFuture()) {
        return; // This is a recursive update, let's skip
    }
    Set<Id> updates = new Set<Id>();
    for(Integer i = 0, s = Trigger.new.size(); i < s; i++) {
        Lead oldRecord = Trigger.isInsert? null: Trigger.old[i], newRecord = Trigger.new[i];
        // Is new or needs region code or IP Address has changed
        if((Trigger.isInsert && newRecord.ipAddress__c != null) ||
        (Trigger.isUpdate && newRecord.ipAddress__c != null && 
            (newRecord.Region_Code__c == null || newRecord.ipAddress__c != oldRecord.ipAddress__c))) {
            updates.add(newRecord.Id);
            if(updates.size() == 100) { // Max callout limit 100
                ClassGetGeo.getLocation(updates);
                updates.clear();
            }
        }
    }
    if(updates.size() > 0) {
        ClassGetGeo.getLocation(updates);
    }
}
4
  • Thank you this seems to mostly solve the problem but in the class I am still getting an error on line 8 "Variable does not exist: a.ipAddress__c." Even when removing the "a." the error still remains. This is the variable that passes the ipAddress from the Lead record to the endpoint. – notBobRoss Nov 18 '20 at 7:12
  • I think I resolved it by changing +a.ipAddress__c+ to +records[0].ipAddress__c+ – notBobRoss Nov 18 '20 at 7:29
  • @notBobRoss Yes, that was a copy-paste error. I'll edit that in for future visitors. – sfdcfox Nov 18 '20 at 14:12
  • @notBobRoss It should be record.ipaddress__c, not record[0].ipaddress__c; this will cause problems when used in bulk. – sfdcfox Nov 18 '20 at 14:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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