[SalesForce] REST API calls to 3rd Party (Pardot), multiple @future callouts

Setup:
Pardot (3rd Party services I'm using) requires you to request an API_Key by submitting your email, password, and user_key so that you can then use the api_key with the user_key to do any API calls. At the moment I have a Pardot class like this.

public class Pardot {

    public static void createProspectsByLead (Lead[] leads){
        String api_key = getAPIKey();
        // More HttpGetRequest using pardot api endpoint (https://pi.pardot.com/api/) with user_key + api_key body parameters
    }

    public static String getAPIKey (){
        /* Found out my credentials were correct but I needed to urlEncode them */
        String Connector_Email = EncodingUtil.urlEncode(Globals.get('Pardot Connector Email', 'Value__c'), 'UTF-8');
        String Connector_Password = EncodingUtil.urlEncode(Globals.get('Pardot Connector Password', 'Value__c'), 'UTF-8');
        String Connector_User_Key = EncodingUtil.urlEncode(Globals.get('Pardot User Key', 'Value__c'), 'UTF-8');

        String body = 'email='+Connector_Email
            +'&password='+Connector_Password
            +'&user_key='+Connector_User_Key;
        String url = 'https://pi.pardot.com/api/login/version/3?'+body;
        System.debug(body);

        /* Request API Key */
        String api_response = HttpGetRequest(url, body);

        /* Generate the HTTP response as an XML stream */
        XmlStreamReader reader = api_response.getXmlStreamReader(); // Need to figure this out next

        // return String API_Key;

    }

    /* HERE COMES THE PROBLEM */
    @future (callout=true) // future method needed to run callouts from Triggers
    static void HttpGetRequest(String url, String body){
        /* Instantiate a new http object */
        Http h = new Http();

        /* Instantiate a new HTTP request, specify the method (GET) as well as the endpoint */
        HttpRequest req = new HttpRequest();
        req.setEndpoint(url);
        req.setMethod('POST');
        req.setBody(body);
        req.setCompressed(true); 

        /* Try to send the request */
        try {
            HttpResponse res = h.send(req);
            System.debug(res.getBody());
            // I want to now return 'res' but I can't
        } catch(System.CalloutException e) {
            System.debug('ERROR: '+ e);
        }

    }

}

The Problems

  1. @future can only return void: As you can see from the code above, I expected with each API call to request the API_Key and then return that key to my method so that I could use it as necessary. But since httpRequest need to be made with @future and it can only return void there is no way to pass api_key directly. I imagine from here I would attempt to store this in my Global__c object in a text field but then I also need to add a timestamp since Pardot only allows that API key for 1 hour.
  2. Handling the Response: I was able to get a response in the debug output but I'm not sure how to handle the response to parse out the API key specifically. Here is the response:

    <?xml version="1.0" encoding="UTF-8"?>
    <rsp stat="ok" version="1.0">
    <api_key>6d800bfaab63274b1b908dacd8544936</api_key>
    </rsp>

This is all part of my first APEX project and has become quite the task to get to this point including other questions I've already posted:

Really appreciate even some resource that outline the exact process of what I'm attempting because I would think its a common process using 3rd party web services, but up till now I'm having trouble finding any simple working examples. Also I'm completely open to suggestion for better approaches to something like this. Thank you so much in advance for any help you can provide.

Follow Up Question posted here: Verify getChildElement() is not null

Best Answer

The reason why @future returns void is because it's asynchronous. It doesn't start until the calling code's transaction has finished. There's no way to communicate back to the calling code at all. Odds are, you'll want to make createProspectsByLead a @future method instead, so you can authenticate and then perform your action. You can have 100 calls per transaction (as of Winter '15 release), so it's unlikely you'll need more than just a couple of calls. And you should definitely cache the API key so you don't waste calls (if applicable), and perform more efficiently anyways.

For handling the response, see Dom.Document for reading an XML file. A successful load would probably reduce the complexity to:

Dom.Document d = new Dom.Document();
d.load(res.getBody());
String api_key = d.getRootElement().getChildElement('rsp',null).getChildElement('api_key', null).getText();
Related Topic