2

I have a process builder executing the following Apex class when the criteria is met. Everything runs nicely until the last line, where the API method is called and a map is passed.

public class Cls_Location {

    @InvocableMethod(label='Get Device Id')
    public static void getCoordinates(List<Id> dList) {

        List<Id> ids = new List<Id>();
        for(Id i : dList) {
            ids.add(i);
            system.debug(i);
        }

        //sensor coordinates map and respective list
        Map<Id, List<Double>> sCoord = new Map<Id, List<Double>>();
        List<Double> sCoordList = new List<Double>();
        //device coordinates map and respective list
        Map<Id, List<Double>> dCoord = new Map<Id, List<Double>>();
        List<Double> dCoordList = new List<Double>();

        for(Device__c qDevice : [SELECT Id, Name, 
                                   Geo_Ip_Location__c, Geo_Ip_Location__Latitude__s, Geo_Ip_Location__Longitude__s
                                    FROM Device__c
                                    WHERE Id IN :ids]) {



                                        system.debug('Query: ' + qDevice);        
                                    Double dLat  = qDevice.Geo_Ip_Location__Latitude__s;
                                    Double dLgtd = qDevice.Geo_Ip_Location__Longitude__s;
                                        system.debug('dLat: ' + dLat);
                                        system.debug('dLgtd: ' + dLgtd);

                                    dCoordList.add(dLat);
                                    dCoordList.add(dLgtd);
                                    dCoord.put(qDevice.Id, dCoordList);
                                        system.debug('dCoord: ' + dCoord);
                                         } 


        if (dCoord.size() > 0) {

        for (Id c : dCoord.keySet()) {
            system.debug('Device Lat: ' + dCoord.get(c)[0]);
            system.debug('Device Longtd: '+ dCoord.get(c)[1]);
        }

        Cls_GoogleMapsCalloutService.runCallout(dCoord);

        } 
    }
}

Here's a snippet of the API callout class and method:

public class Cls_GoogleMapsCalloutService {

public static HttpResponse runCallout(Map<Id, List<Double>> m1) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(MAPS_URL);
        request.setMethod('GET');
        HttpResponse response = http.send(request);



            if (response.getStatusCode() == 200) {

//JSON PARSING is done here

Is there any way around this?

Grateful for your time.

1
  • 2
    Do you see any exceptions thrown in the debug log when you execute this callout? – Mark Pond Jul 30 '18 at 17:56
3

So the error you would get in your debug logs is Uncommited Work Pending:.

There is a limitation in SF, if you do a dml, you cannot do a callout in same transaction. This is just the way SF works.

So if your process builder is running, that means you already did a DML. Now from Process Builder you calling an Invocable method, which does callout.

Thus it will fail. Is there a workaround for this?

ofc. You have to use future method.

public class Cls_GoogleMapsCalloutService  {

@future(callout=true)

public static void runCallout(String input) {
        Map<Id, List<Double>> m1 =(Map<Id, List<Double>>) JSON.deserialize(input, Map<Id, List<Double>>.class);
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        //request.setEndpoint(MAPS_URL);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
}
}

Calling from your invocable method like

 Cls_GoogleMapsCalloutService.runCallout(JSON.serialize(dCoord));

As future method can only accept primitive datatype, I have to convert your Map of (Map<Id, List<Double>>) to JSON string and then deserialize and use it.

Edit : If you want to use queuable Apex then Main class will be

public class Cls_GoogleMapsCalloutService  implements  Queueable,Database.AllowsCallout{

    private Map<Id, List<Double>> m1;


    public Cls_GoogleMapsCalloutService(Map<Id, List<Double>> m1){
        this.m1 = m1;
    }

    public void execute(QueueableContext qc) {

            Http http = new Http();
            HttpRequest request = new HttpRequest();
            request.setEndpoint(MAPS_URL);
            request.setMethod('GET');
            HttpResponse response = http.send(request);
    }
¬}

And then you would call your queuable like :

 System.enqueueJob(new Cls_GoogleMapsCalloutService  (dCoord));

Source: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_queueing_jobs.htm

Source: http://cloudyworlds.blogspot.com/2015/03/invoking-apex-callout-from-process.html

5
  • 1
    could also use queueable and avoid the serialization/deserialization of the map – cropredy Jul 30 '18 at 18:40
  • 1
    Yups, I prefer Queuable.... cause of chaining feature. – Pranay Jaiswal Jul 30 '18 at 18:42
  • Wow. Thank you so much, that helps a lot! I just got home but I'll try this tomorrow at work. Is this the queuable you're referring to? I'm just starting as a developer, grateful for the patience :) – Rui Torres Jul 30 '18 at 19:03
  • Good Morning, @PranayJaiswal . I'm curious how can I use the queuable interface to substitute the JSON serialize/deserialize. How can I do this? Thank you. – Rui Torres Jul 31 '18 at 8:10
  • 1
    @RuiTorres edited answer to represent it – Pranay Jaiswal Jul 31 '18 at 9:32

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.