Few months ago, I wrote about accessing an external web service, in this case Zillow to enrich lead data in Salesforce CRM. Very briefly, Zillow provides the valuation of a property given an address using a proprietary algorithm. To incorporate this transparently into the application, we built an extension to the standard controller for Lead sObject and defined our own custom save method to override the standard behavior. The save method we wrote, made the callout to the Zillow service.
Though this works fine for Leads entered through the UI, it doesn't work if a lead were to be entered through the API. A company may purchase leads from a marketing company and load it through the API – our custom save method will not be called since the Visualforce controller is not instantiated at all. Triggers provide the ideal place to place this callout logic, since triggers always get invoked when a database operation is involved. In our case we want to make the callout to the web service when a lead is inserted into the database, either through the API or the UI. Since Winter 09 release, it is possible to do this – well kind of. Though you can't have the callout code in the trigger itself, you can call a asynchronous method from your trigger code which can then make the web service callout. Remember that since triggers may be holding database locks etc. you don't want any lengthy operations in the trigger code - a web service callout can potentially be lengthy. Delegating the callout to an asynchronous method and having the control return immediately to the trigger solves this issue.
Let's look at the trigger code and the asynch method
trigger GetZillowPrice on Lead (after insert) { Set leadIDs = new Set() ;
for( Lead l : Trigger.new ){ leadIDs.add( l.Id ) ; } AsynchCallZillow.callZillow(leadIDs) ;}
public class AsynchCallZillow {
@future (callout=true) public static void callZillow(Set leadIDs){
Map<ID,Lead> leadMap = new Map<ID,Lead>( [select Lead.Home_Value__c, Lead.Street, Lead.City, Lead.State from Lead where id IN :leadIDs] ) ;
for( ID i : leadIDs ){ Lead curLead = leadMap.get(i) ; try{ ZillowService p = new ZillowService() ; ZillowTypes.PropertySearchResponse r = p.searchZillow( curLead.Street, curLead.City, curLead.State) ; curLead.Home_Value__c = r.getZEstimateAmount() ; } catch( ZillowTypes.ZillowException e){ System.debug( '**** Caught Exception' +e ) ; } } update leadMap.values() ; }
We have essentially moved the callout code to the asynchronous method and call this method from our trigger.Now our leads will have the Zillow calculated home value estimate irrespective of how the lead gets entered into the system, via the UI or through the API.There are a few things to keep in mind when using this approach.
The impact of these factors ofcourse, depends on the application you are building. This article has a good introduction to web services and callouts on the Force.com platform. This thread on our discussion board has some good pointers on testing asynchronous methods.
