[SalesForce] Avoiding Recursion While Updating Trigger Records

I'm running into roadblocks everywhere with this particular task. My goal is simply to have a trigger do a GET request on an external REST API service, get some data, and update the lead currently being worked on with said data during before insert/before update.

Here are some of the challenges I've run into so far:

  1. I can't call the external web service synchronously from a trigger because it's not allowed. Fine, I tried breaking up the logic into a new asynchronous trigger that works on after insert/after update.

    1. I can't run the update logic on after insert/after update because the lead object is read-only at that point.

My goal, at least to me, is very simple. I feel like there should be a simple solution to this. Is there something I'm missing?

Here's a simplified version of the trigger that already existed:

trigger ExistingTrigger on Lead (before insert, before update) 
{
  for (Lead lead : System.Trigger.new)
  {
    //here we do a bunch of updates to the lead based on data that already exists in salesforce. For example: 
    if (lead.Special_Field__c != NULL && lead.Special_Field__c != '' && System.Trigger.isInsert)
    {
        if (lead.Special_Field__c .contains('A Cat') && lead.Cat_Color__c == 'Orange')
        {
            lead.Cat_Toy__c = 'Mouse';
            lead.Ginger__c = 'Y';
        }
    }
  }
}

This is the new trigger I've created to handle the call to the third party API:

trigger AssignVet on Lead (after insert, after update) 
{    
    VetHelper vetHelper = new VetHelper();

    for (Lead lead : System.Trigger.new)
    {
        try
        {
            VetHelper.UpdateVet(lead.id);
        }
    }
}

Finally, this is the class that has the async method that is supposed to update lead with data from the third party API:

public with sharing class VetHelper
{
    @future (callout=true)
    public static void UpdateVet(string leadId)
    {
       string url = 'http://192.168.0.1/lookup/' + leadId;
       HttpRequest reqData = new httpRequest();
       Http http = new Http();
       reqData.setHeader('Content-Type','application/json');
       reqData.setTimeout(20000); 
       reqData.setEndpoint(url);
       reqData.setMethod('GET');

       HTTPResponse res = http.send(reqData);
       string response = res.getBody();

       dbLead = [select VetName from  Lead where Id =: leadId LIMIT 1];

       //let's pretend we can simply grab the values needed from the response object
      dbLead.VetName = response.vetName;
      Database.update(dbLead, false);
    }
}

The code as it stands does not work because Database.update causes both triggers to fire indefinitely.

Best Answer

If you are worried about recursive calls, a Boolean flag is a time tested strategy. Just set one in your @future method and don't rerun that logic if the flag is set. Using a handler pattern will greatly facilitate this strategy. Below is a simple outline of what mine would look like.

Handler

public class LeadTriggerHandler
{
    public static Boolean isCalledFromVetHelper = false;

    final List<Lead> newRecords;
    final Map<Id, Lead> oldMap;
    public LeadTriggerhandler(List<Lead> newRecords, Map<Id, Lead> oldMap)
    {
        this.newRecords = newRecords;
        this.oldMap = oldMap;
    }

    public void afterUpdate()
    {
        if (!isCalledFromVetHelper)
        {
            // VetHelper logic
        }
        // other update logic
    }
}

Callout Helper

public class VetHelper
{
    @future
    {
        LeadTriggerHandler.isCalledFromVetHelper = true;
        // existing logic
    }
}

Trigger

trigger Lead on Lead (after insert, after update)
{
    LeadTriggerHandler handle = new LeadTriggerHandler(trigger.new, trigger.oldMap);
    if (trigger.isBefore)
    {
        // events
    }
    if (trigger.isAfter)
    {
        if (trigger.isInsert) handle.afterInsert();
        if (trigger.isUpdate) handle.afterUpdate();
    }
}
Related Topic