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.

6
  • The lead object is read only but you can always query for the records from the DB and update those instead.... Commented Jul 7, 2016 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? Commented Jul 8, 2016 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. Commented Jul 8, 2016 at 16:39
  • 1
    I am using @future methods on my callouts. I'll update with sample code shortly. Commented Jul 8, 2016 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. Commented Jul 8, 2016 at 16:55

1 Answer 1

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

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.