Monday, July 27, 2015

Salesforce integration across two different organizations

For this post, I will offer a simple explanation of the complex, yet interesting areas essential for the complete understanding of Salesforce Integration’s capabilities. The business scenario quoted, along with the working code samples, will be a good starting point for entering into the world of non-declarative ways for integrating with Salesforce. Here’s what I’ll cover:

Understanding authentication and its prerequisites
OAuth authentication flows for REST API calls
Apex triggers and callouts
REST API, REST web services, and Apex web services

1) Authentication and Its Prerequisites
Authenticating a client application is the first and foremost step while building an interface.

The authentication method depends on your chosen call type (REST or SOAP). Let’s see how to do it using REST.

Before moving any further, let’s frame a business scenario. We’ll use two Salesforce instances that exchange Account details. When a new Account is created in one org, it will flow down to the other Salesforce org, and in return the source org will get the Salesforce Record ID of the created record. These two orgs are:

Source Salesforce org (used for callout) – Source
Target Salesforce org (used for receiving requests) – Target

Following our business scenario, we can say that authentication is a collection of the following sub-steps. Though actual authentication calls a trigger from the Apex code, consider these steps as the prerequisites because without them being completed first, the authentication calls won’t work.

Choosing OAuth Endpoint (to be invoked from Source org)
Choosing OAuth Authentication flow (to be used by Source org)

Remote Site Setting in Source Org.

 Connected App enables Salesforce to recognize and authenticate an external application as a new entry point. OAuth is used for this authentication. We need to create a Connected App record in Target org. Below is an illustration.

Connected App in Target Org.
callback url should be specified in the remote site setting
Once after saving the record the client id and clientSecret are generated.

Create a Trigger to make an asynchronous call from source org to target org

Trigger on account Object:
trigger SendAccount on Account(after insert)
{
for(Account a : Trigger.new){
   SendAccountFromSource.createAccount(a.Name, a.Id);
}
}
Apex Class to make a callout :
Replace the clientid,secret,username and password's of your org.

public class SendAccountFromSource {
 private final String clientId = 'Clent Id from App';
 private final String clientSecret = 'clientSecretfrom app';
 private final String username = 'username';
 private final String password = 'passwordwithsecuritytoken';
 public class deserializeResponse
 {
  public String id;
  public String access_token;
 }
 public String ReturnAccessToken (SendAccountFromSource acount)
 {
  String reqbody = 'grant_type=password&client_id='+clientId+'&client_secret='+clientSecret+'&username='+username+'&password='+password;
  Http h = new Http();
  HttpRequest req = new HttpRequest();
  req.setBody(reqbody);
  req.setMethod('POST');
  req.setEndpoint('https://login.salesforce.com/services/oauth2/token');
  HttpResponse res = h.send(req);
  deserializeResponse resp1 = (deserializeResponse)JSON.deserialize(res.getbody(),deserializeResponse.class);
  return resp1.access_token;
 }
 @future(callout=true)
 public static void createAccount(String accName, String accId) 
 {
  SendAccountFromSource acount = new SendAccountFromSource();
  String accessToken = acount.ReturnAccessToken (acount);

  if(accessToken != null)
  {
   String endPoint = 'https://ap2.salesforce.com/services/data/v32.0/sobjects/Account/';
   String jsonstr = '{"Name" : "' + accName + '"}';
   Http h2 = new Http();
   HttpRequest req1 = new HttpRequest();
   req1.setHeader('Authorization','Bearer ' + accessToken);
   req1.setHeader('Content-Type','application/json');
   req1.setHeader('accept','application/json');
   req1.setBody(jsonstr);
   req1.setMethod('POST');
   req1.setEndpoint(endPoint);
   HttpResponse res1 = h2.send(req1);

   deserializeResponse resp2 = (deserializeResponse)JSON.deserialize(res1.getbody(),deserializeResponse.class);
   Account a = [SELECT Id FROM Account WHERE Id = :accId];
   a.externalId__c = resp2.id;
   update a;
  }
 }
}

Explanation –
1 Setting the REST API resource to create an Account (sObject)
2 Creating the JSON string to be sent as the input
3 Setting the Header to include the access token
4 Querying the Account record in Source Org so it can be updated
5 Setting the custom foreign key field on Account in Source Org with the extracted Account ID from the response

Till here we have worked with rest api.

When working with rest webservices we need to change the code in the createAccount method in SendAccountFromSource class . Which looks like
if(accessToken != null)
{
 String endPoint = 'https://ap2.salesforce.com/services/apexrest/v1/createAccount/';
 String jsonstr = '{"AccName" : "' + accName + '"}';

 Http h2 = new Http();
 HttpRequest req1 = new HttpRequest();
 req1.setHeader('Authorization','Bearer ' + accessToken);
 req1.setHeader('Content-Type','application/json');
 req1.setHeader('accept','application/json');
 req1.setBody(jsonstr);
 req1.setMethod('POST');
 req1.setEndpoint(endPoint);
 HttpResponse res1 = h2.send(req1);

 String trimmedResponse = res1.getBody().unescapeCsv().remove('\\');
 deserializeResponse resp2 = (deserializeResponse)JSON.deserialize(trimmedResponse, deserializeResponse.class);
 Account a = [SELECT Id FROM Account WHERE Id = :accId];
 a.externalId__c= resp2.id;
 update a;
}

Here in this example we need to change the endpoint url that is pointing to createAccount class in the target org. Create a new rest web service class createAccount in target org.
@RestResource(urlMapping='/v1/createAccount/*')
global with sharing class createAccount 
{
 @HttpPost
 global static String createAccount(String AccName)
 {
  Account a = new Account();
  a.Name = AccName;
  insert a;
  String returnResponse = JSON.serialize(a);
  return returnResponse;
 }
}

Explanation –
1 Exposing the web service using the @RestResource annotation
2 Exposing method as REST resource to be called when HTTP Post request is made to this web service
3 Creating a new Account in Target org and setting the name as passed from Source org
4 Serializing the response (Account details) before sending
5 Sending the response.

Apex Web Service

Finally, the third option is the Apex Web Service that uses SOAP to handle integration. The class written at the target needs to be exposed as a global Web Service.
global class CreateAccountApexWS 
{
 global class sAccount
 {
  webservice String name;
 }
 webservice static String createAccount(sAccount sAcct)
 {
  Account acct = new Account();
  acct.Name = sAcct.name;
  insert acct;
  String returnResponse = JSON.serialize(acct);
  return returnResponse;
 }
}

Explanation –
1 Creating a Global class that can be accessed from anywhere
2 Using the webservice keyword to expose class variable as an input to the web service
3 Using the webservice keyword to expose the class method as a custom SOAP Web Service

No comments :

Post a Comment

Labels

visualforce page ( 13 ) apex integration ( 5 ) apex trigger ( 4 ) csv file from vf page ( 4 ) javascript ( 4 ) csv visualforce page ( 3 ) Too many ( 2 ) call out ( 2 ) integration ( 2 ) rest api ( 2 ) salesforce rest api ( 2 ) salesforce to salesforce integration ( 2 ) sfdc rest api ( 2 ) trigger ( 2 ) 15 digit to 18 digit ( 1 ) DML rows in Apex ( 1 ) Date Conversion ( 1 ) Date/Time conversion ( 1 ) Deploy ( 1 ) Objects to Future Annotated Methods ( 1 ) SFDC limits ( 1 ) Sobject to Future Annotated Methods ( 1 ) Test Class ( 1 ) TimeZone Conversion ( 1 ) Too many dml rows ( 1 ) Too many future calls ( 1 ) annotations ( 1 ) apex code ( 1 ) closed opportunities ( 1 ) commit ( 1 ) convert ( 1 ) create records ( 1 ) csv create records ( 1 ) custom setting ( 1 ) deployment ( 1 ) deployment changeset ( 1 ) disable apex class ( 1 ) disable apex trigger ( 1 ) disable in production ( 1 ) document ( 1 ) download ( 1 ) field name ( 1 ) formula fields ( 1 ) iframe ( 1 ) inactive ( 1 ) intellisense ( 1 ) jsforce ( 1 ) limits ( 1 ) matrix report in vf page ( 1 ) multi select ( 1 ) multi select salesforce ( 1 ) multiselect ( 1 ) paypal ( 1 ) picklist ( 1 ) record type ( 1 ) rollback ( 1 ) salesforce limits ( 1 ) salesforce list ( 1 ) salesforce map ( 1 ) salesforce rest ( 1 ) salesforce set ( 1 ) salesforce1 ( 1 ) sandbox deployment ( 1 ) sfdc collection ( 1 ) sfdc list ( 1 ) sfdc map ( 1 ) sfdc rest ( 1 ) sfdc set ( 1 ) uncommitted ( 1 ) updated field ( 1 ) user ( 1 ) validation rule opportunity ( 1 ) validation rules opportunities ( 1 ) vf page ( 1 )

Ad