3

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.

  • The lead object is read only but you can always query for the records from the DB and update those instead.... – Sebastian Kessel Jul 7 '16 at 20:18
  • @SebastianKessel I tried updating the lead by querying them as you suggested but now I'm running into a recursion issue since the update sets off my before insert/after trigger which in turn sets of the after update/insert trigger until salesforce shuts the recursive loop down. Any suggestions on how to get around that? – Bruno Jul 8 '16 at 16:12
  • You can not make webservice calls in trigger context but you can initiate future call from which you can make a webservice call and update the record. Results will not reflect immediately since this will not be in the same transaction. – Kiran Machhewar Jul 8 '16 at 16:39
  • 1
    I am using @future methods on my callouts. I'll update with sample code shortly. – Bruno Jul 8 '16 at 16:54
  • 1
    Okay, that's definitely new information. Code samples will always make your questions less likely to be closed! Once we see that code we can better judge what's going on. – Adrian Larson Jul 8 '16 at 16:55
3

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();
    }
}
  • When/where do I call afterUpdate()? Or is that a special method that gets called by salesforce automatically? – Bruno Jul 8 '16 at 18:27
  • @Bruno updated my example to fill out more of the pattern I use. – Adrian Larson Jul 8 '16 at 18:35

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.