I recently received a very interesting resume for a position at Salesforce.com. What caught my attention was that instead of the traditional resume structure (summary/objective, employment history, languages/platforms etc.), the resume started with two pages of blurbs from the applicant's LinkedIn recommendations. That's as good an example as any of the importance of social networks in general and the power of the world's largest professional networking site – LinkedIn – in particular. Apparently the market agrees.

Wouldn't it be cool if you could harness the power of someones professional network from within Chatter? What if certain status updates in Chatter could automatically be broadcasted to the user's LinkedIn network (check out the attached screenshot for a simple example)? That's one example of integrating Force.com with another Cloud based application, and I recently hosted a webinar to discuss the unique characteristics and patterns of such Cloud to Cloud integrations. You can watch a recording of the webinar here. One of the integration patterns that I discussed during the webinar was Outbound integrations – i.e. when Force.com initiates the integration with an outbound callout to the external cloud application/platform. The Chatter–>LinkedIn synchronization use case was one of the demos that I did to help illustrate that pattern and I've since posted the code to GitHub. You can jump to this point in the recording if you're only interested in the LinkedIn demo (go ahead, I won't take it personally!).

The Chatter–>LinkedIn integration uses Apex HTTP callouts and I wanted to highlight and discuss some of the code here. (Note: I would recommend watching the recording first to get a better sense of the high-level integration architecture). The starting point of the integration is the 'SyncLinkedInChatterStatus' trigger on the User object. This trigger looks for the special '#linkedin' hashtag in each Chatter status update and if found, invokes the 'updateStatus' method of the 'UpdateLinkedInUserStatus' class. Why delegate the actual LinkedIn sync to another class (other than having a nice modular code structure)? Because any Apex callout from a trigger context has to be made asynchronously using the special @future annotation. Lets take a closer look at that code

public with sharing class UpdateLinkedInUserStatus {
@future(callout=true)
public static void updateStatus(List userIds, List statusUpdates)
{
for (Integer i = 0; i< userIds.size(); i++)
{
LinkedIn l = new LinkedIn(userIds.get(i));
LinkedIn.Share s = new LinkedIn.Share();
s.comment = statusUpdates.get(i);
s.visibility = 'anyone';
try
{
if (Limits.getLimitCallouts() > Limits.getCallouts())
{
l.updateUserShare(s);
}
}
catch(Exception e){}
}
}
}

As you can see, the actual logic for invoking the LinkedIn APIs in encapsulated in the appropriately named 'LinkedIn' class. Lets now take a look at where the real action is – the 'LinkedIn' class.

public with sharing virtual class LinkedIn
{
public static final String LINKED_IN_API_URL = 'http://api.linkedin.com/v1';
public static final String LINKED_OAUTH_SERVICE_NAME = 'LinkedIn';
private String sfdcUserId;
private OAuth oa;
public LinkedIn(String userId)
{
sfdcUserId = userId;
}
public String updateUserShare(Share s)
{
HttpRequest req = createRequest('/people/~/shares','POST', s.toXML());
HttpResponse resp = executeLinkedInRequest(req);
return getResponseBody(req, resp);
}
protected virtual HttpRequest createRequest(String path, String method, DOM.Document request)
{
HttpRequest req = new HttpRequest();
req.setEndpoint(LINKED_IN_API_URL  + path);
req.setMethod(method == null ? 'GET' : method);
req.setHeader('Content-Type', 'text/xml');
req.setTimeout(60000);
if (request != null)
req.setBodyDocument(request);
if (oa == null)
{
oa = new OAuth();
if (!oa.setService(LINKED_OAUTH_SERVICE_NAME, sfdcUserId))
{
System.debug('Didnt work:'+oa.message);
throw new AuthenticationException(oa.message);
}
}
oa.sign(req);
return req;
}
protected virtual HttpResponse executeLinkedInRequest(HttpRequest req)
{
HttpResponse res = new Http().send(req);
if (req.getMethod() == 'POST' && res.getStatusCode() != 201)
{
System.debug('OAuth header:'+res.getHeader('oauth_problem'));
throw new TwitterApiException (res.getBody());
}
else if (req.getMethod() == 'GET' && res.getStatusCode() != 200)
throw new TwitterApiException (res.getBody());
return res;
}
protected virtual String getResponseBody(HttpRequest req, HttpResponse resp )
{
return resp.getBody();
}
.....
}

Lets start with the 'updateUserShare' method that is invoked to update the user's LinkedIn status ('Share' is the LinkedIn terminology for when users share updates with their network, analogous to a Chatter user's status update). The method simply invokes the LinkedIn REST API by calling 3 separate methods in sequence – the 'createRequest' method to create an HTTP request, the 'executeLinkedInRequest' method to execute the HTTP request and finally the 'getResponseBody' method to parse the response from LinkedIn. The reason for breaking out an Apex HTTP callout into these 3 steps is two fold. One, it makes the code more modular and reusable (we can easily add support for additional LinkedIn API calls by reusing the same 3 methods and simply changing the combination of URL, HTTP method and request body). Also, it's a good strategy for testing Apex callouts. As explained during the webinar, testing Apex callouts requires special considerations and breaking your callout into 3 separate steps is one of those strategies. The 'LinkedIn' class uses a more advanced version of that basic testing design pattern that also requires the class/methods to be defined as Virtual/Protected. For a blast from the past, check out this post from the esteemed DevAngel that explains this particular pattern in more detail. As you'll see from that post, this pattern was first implemented by the equally esteemed Simon Fell in the TwitterForce toolkit and I simply reused (ok, ok – stole) the pattern for my LinkedIn integration. 

You'll also notice the use of an 'OAuth' class to sign the request before sending it off to LinkedIn. As is true for most modern cloud applications, LinkedIn supports the OAuth authentication protocol and I used the excellent OAuth 1.0 Apex client library to implement the authentication. Note that there are a couple of preliminary, one-time setup steps before you can use that OAuth Apex client library. For the sake of brevity I didn't cover those steps in the webinar (or in this post) and so let me know if you need additional details on setting up the OAuth Apex client in Force.com.

The 'LinkedIn' class included in this sample currently only supports the LinkedIn Share API that allows a user to share updates with his/her professional network. It can however be easily enhanced to support additional LinkedIn API features (like the Profile API, the Connections API etc.). So if your LinkedIn use case requires a different integration scenario, feel free to use this sample as a starting point. Let me know if you have any questions or comments.

And yes, we did hire the applicant.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS