Sunday, January 6, 2019

Salesforce to Salesforce Rest API Integration using Username-Password flow

In this lesson, we will learn how to integrate two Salesforce organizations using the Username-Password OAuth flow. Our objective is to fetch data from one Salesforce org (ORG B) and display it in another Salesforce org (ORG A). This type of integration is useful when you need to access or synchronize data between two different Salesforce instances.

 Prerequisites:

Before we begin, ensure you have the following:


  • Access to two Salesforce organizations (ORG A and ORG B).
  • Administrator privileges in both organizations to set up the necessary configurations.
  • Basic understanding of Salesforce REST API.

Steps Overview:

Here is a high-level overview of the steps we will follow:

 

  • Create a Connected App in ORG B: This app will allow ORG A to authenticate and access ORG B's data.

 

  • Configure the Connected App in ORG B: Set the necessary OAuth settings and permissions.

 

  • Create a Webservice in ORG B which will be invoked from ORG A to get the data.

 

  • Fetch Data from ORG B Using Username-Password Flow: Write a script in ORG A to get an access token from ORG B and fetch data from ORG B and display Data in ORG A.

 

Let's start,

 

ACTIVITIES WE NEED TO DO IN ORG B.

 

  • Create a Connected App in ORG B.
  • Create a Webservice in ORG B which will be invoked from ORG A to get the data.

 

Create A Connected App In ORG B:

Configure a connected app as shown below in ORG B and Click Save.

Salesforce to Salesforce Rest API Integration using Username-Password flow

Salesforce to Salesforce Rest API Integration using Username-Password flow

Salesforce to Salesforce Rest API Integration using Username-Password flow

Salesforce to Salesforce Rest API Integration using Username-Password flow


Note down the Consumer Key and Consumer Secret. These will be provided to ORG A for authentication purposes, along with the username, password, and security token of the user from ORG B that ORG A will be using for authentication.

A callback URL is the URL that is invoked after OAuth authorization for the consumer (connected app). In some contexts, the URL must be a real URL to which the client’s web browser is redirected. In others, the URL isn’t actually used, but the value between your client app and the server (the connected app definition) must be the same. Here, we are providing the URL of ORG A in the required format for the Callback URL.

Salesforce to Salesforce Rest API Integration using Username-Password flow

Create a Webservice as shown below in ORG B:

Web Service Description: Fetch Contact Record by Email and Phone

The getContactRecord class is marked as global with sharing, ensuring that the sharing rules are enforced and the data visibility is respected according to the user's permissions.

The fetchContact method is annotated with @HttpGet, indicating that it handles HTTP GET requests. This method performs the following actions:

Create a New Contact Object:

A new Contact object is instantiated.

Retrieve Request Parameters:

The method retrieves the current REST request and response objects.

It extracts the email and phone parameters from the request.

Query Salesforce for Contact Record:

Using SOQL, the method queries the Contact object in Salesforce to find a record that matches the provided email and phone number.

The query returns the Id, LastName, Email, and Phone fields of the matching Contact.

Modify HTTP Status Code:

The HTTP status code of the response is set to 201, indicating that the request has been fulfilled and a new resource has been created.

Return Contact Object:

The method returns the fetched Contact object, which will be serialized into JSON and sent back to the external system.

This web service allows external systems to easily retrieve specific Contact records from Salesforce by providing the necessary email and phone parameters in the request. It ensures that the data is fetched securely and returned in a standardized format, facilitating seamless integration with other systems.

@RestResource(urlMapping='/getContactFromEmailAndPhone/*')

   global with sharing class getContactRecord {

        @Httpget

      global static Contact fetchContact(){

        Contact obj=new Contact();

        RestRequest req = RestContext.request;

        RestResponse res = Restcontext.response;

        Map<String, String> requestParam = req.params;

        String conEmail=requestParam.get('email');

        String conPhone=requestParam.get('phone');

        obj=[Select id,lastname,email,phone from contact where email=:conEmail and phone=:conPhone];

       /***Modify the HTTP status code that will be returned to external system***/

        res.statuscode=201;

       /***Modify the HTTP status code that will be returned to external system***/

        return obj;

      }

   }

ACTIVITIES WE NEED TO DO IN ORG A.

To integrate with ORG B, we need to perform the following activities in ORG A:

  • Create an Apex Controller:

Develop an Apex controller that will call the web service in ORG B.

  • Create a Visualforce Page:

 

Design a Visualforce page to display the contact information received from ORG B.

  • Create a Remote Site Setting:

Configure a remote site setting for the URL of ORG B to allow outbound calls.

Let's Get Started

To store the Username, Password, Consumer Key, and Consumer Secret provided by ORG B, create a custom metadata type in ORG A as shown below.

Note: The password should be a combination of the password and security token of the user from ORG B, without any spaces.  

Salesforce to Salesforce Rest API Integration using Username-Password flow

Now, create a record under the above custom metadata as shown below.

Salesforce to Salesforce Rest API Integration using Username-Password flow


CREATE AN APEX CONTROLLER:

Now, create an Apex controller that will call the web service in ORG B. This controller's method will be invoked from a Visualforce page to retrieve the necessary data from ORG B. For demonstration purposes, we are hardcoding the email and phone number of the contact in the class below to fetch the contact associated with these details from ORG B. This can be made dynamic as needed.

Class Structure and Key Components:

Private Variables:

cKey, cSecret, uName, passwd: These store the client ID, client secret, username, and password required for authentication.

Public Variables:

instanceURL: Stores the instance URL of the authenticated Salesforce org.

listWrap: A static list to hold the results returned from the web service.

Constructor:

Initializes listWrap as a new list of resultWrapper objects.

Nested Classes:

responseWrapper: Holds the response data from the authentication request, including id, access_token, and instance_url.

resultWrapper: Holds the Contact data returned from the web service, including id, LastName, Email, and Phone.

Methods:

getRequestToken() à Retrieves connection parameters from custom metadata (Store_Cred__mdt). Constructs the request body for OAuth 2.0 authentication. Sends an HTTP POST request to the Salesforce token endpoint. Deserializes the JSON response into a responseWrapper object and returns it.

getConList() à Hardcodes email and phone parameters for testing. Calls getRequestToken() to obtain the access token and instance URL. Constructs the endpoint URL for the web service call. Sends an HTTP GET request to fetch the Contact record. Deserializes the JSON response into a resultWrapper object if the status code is 201 and adds it to listWrap.

public class restApiTofetchSingleRecord {

    private string cKey;

    private string cSecret;

    private string uName;

    private string passwd;

    public string instanceURL;

    public static list < resultWrapper > listWrap {

        get;

        set;

    }

    public restApiTofetchSingleRecord() {

        listWrap = new list < resultWrapper > ();

    }

    public class responseWrapper {

        public string id;

        public string access_token;

        public string instance_url;

    }

    public responseWrapper getRequestToken() {

 

        List < Store_Cred__mdt > connectionParam = [SELECT Id, MasterLabel, client_id__c, client_secret__c, username__c, password__c from Store_Cred__mdt];

        if(connectionParam.size() >0){

        cKey=connectionParam[0].client_id__c;

        cSecret=connectionParam[0].client_secret__c;

        uName=connectionParam[0].username__c;

        passwd=connectionParam[0].password__c ;

        }

        System.debug('Store_Cred__mdt' + connectionParam);

        string reqBody = 'grant_type=password&client_id=' + cKey + '&client_secret=' + cSecret + '&username=' + uName + '&password=' + passwd;

        Http h = new Http();

        HttpRequest req = new HttpRequest();

        req.setBody(reqBody);

        req.setMethod('POST');

        req.setEndpoint('https://login.salesforce.com/services/oauth2/token');

        HttpResponse hresp = h.send(req);

        responseWrapper wResp = (responseWrapper) JSON.deserialize(hresp.getBody(), responseWrapper.class);

        system.debug('reqBody '+reqBody );

        system.debug('wResp'+wResp );

        system.debug('Instance url' + wResp.instance_url);

        system.debug('session id' + wResp.access_token);

        return wResp;

    }

    public void getConList() {

 

        string integration1 = 'Testing integration';

        list < account > accList1 = new list < account > ();

        String accToken;

        String instanceUrl;

        string responseBody;

        string email = 'testemail123@gmail.com';

        string phone = '123';

        restApiTofetchSingleRecord obj = new restApiTofetchSingleRecord();

        responseWrapper obj1= obj.getRequestToken();

        accToken = obj1.access_token;

        instanceUrl = obj1.instance_url;

        string endPoint = instanceURL+'/services/apexrest/getContactFromEmailAndPhone/?' + 'email=' + email + '&phone=' + phone;

        system.debug('endPoint'+endPoint );

        system.debug('access token' + accToken);

        if (accToken != '') {

            Http h1 = new Http();

            HttpRequest req1 = new HttpRequest();

            req1.setHeader('Authorization', 'Bearer ' + accToken);

            req1.setHeader('Content-Type', 'application/json');    

            req1.setMethod('GET');

            req1.setEndpoint(endPoint);

            HttpResponse hresp1 = h1.send(req1);

            system.debug('hresp1'+hresp1 );

            system.debug('hresp1.getStatusCode()'+hresp1.getStatusCode());

             system.debug('hresp1.getBody()'+hresp1.getBody());

 

            if (hresp1.getStatusCode() == 201) {

                resultWrapper wResp1 = (resultWrapper) JSON.deserialize(hresp1.getBody(), resultWrapper.class);

                listWrap.add(wResp1);

            }

        }

    }

    public class resultWrapper {

        public string id {

            get;

            set;

        }

        public string LastName{

            get;

            set;

        }

        public string Email{

            get;

            set;

        }

        public string Phone{

            get;

            set;

        }

    }

}

CREATE A VISUALFORCE PAGE:

This Visualforce page is designed to interact with the restApiTofetchSingleRecord controller to fetch and display Contact information from an external Salesforce organization (ORG B).

<apex:pageblockButtons>) contains a command button labeled "TEST" which triggers the getConList method in the controller to fetch Contact data.

<apex:pageblocktable> displays the list of Contact records (listWrap) returned from the controller.

Iterates over each Contact record (a) and displays the LastName, Email, and Phone in separate columns.

<apex:page controller="restApiTofetchSingleRecord">

<apex:form >

<apex:pageBlock >

 <apex:pageblockButtons >

 <apex:commandButton value="TEST" action="{!getConList}"/>

 </apex:pageblockButtons>

<apex:pageblocktable value="{!listWrap}" var="a" >

 <apex:column value="{!a.LastName}"/>

 <apex:column value="{!a.Email}"/>

 <apex:column value="{!a.Phone}"/>

 </apex:pageBlockTable>

 </apex:pageBlock>

 </apex:form>

</apex:page>

 

Remote Site URL:

The remote site url here is nothing but the instance URL of ORG B.

Salesforce to Salesforce Rest API Integration using Username-Password flow

We have completed all the necessary customization and configuration in both ORG A and ORG B.

Before testing, ensure that a contact with the email "testemail123@gmail.com" and phone "123" exists in ORG B, as we will be retrieving this contact information from ORG B.

Now, let's preview the Visualforce page and click the "TEST" button to initiate the data retrieval.

Salesforce to Salesforce Rest API Integration using Username-Password flow

13 comments:

  1. How to we do the same if we need to display any Images?

    ReplyDelete
    Replies
    1. Sorry for the late reply. I have not checked yet, but I will update here if i try the same.

      Delete
  2. If its using Named Credentials, what changes we need to make on Apex controller?

    ReplyDelete
  3. Hi, after login is success how can we open SYSTEM B's home page? Thanks

    ReplyDelete
  4. How can we hide username, password, client secret etc from the apex class?

    ReplyDelete
    Replies
    1. You can use custom labels or custom settings or custom metadata to store username, password, client secret etc and than refer them in apex class.

      Delete
    2. Just use Named Credentials

      Delete
  5. Hello, the response of the oauth call is "access_token":"SESSION_ID_REMOVED"?
    How fix this?

    ReplyDelete
    Replies
    1. In debug log you will not be able to see it. however in class you can access it.

      Delete
  6. Hi, How to I include composite data in request body?

    ReplyDelete
  7. Hello, Iam not able to see connected app under create apps? can anyone help me please

    ReplyDelete
  8. Cleared 90% of my doubts... Thanks a lot for the resource

    ReplyDelete