Friday, May 12, 2017

Salesforce Integration with Ballerina

Introduction



Ballerina is a programming language which is designed to do integrations with applications and services that can handle all integration scenarios. It is a language which is flexible which allows you to build your integrations from sequence diagrams as well. Its native support for REST, Swagger, JSON, XML and also the support for popular connectors like Facebook, twitter, salesforce makes Ballerina a powerful language. The beauty of Ballerina is exposed by its own composer which allows you to easily draw diagrams that generates the integration code for you and vise versa. Ballerina language syntax is modeled after the best practices of popular languages like Java.


Ballerina contains Salesforce SOAP and REST connectors that allows you to easily work  with records in Salesforce, a web-based service that allows organizations to manage Customer Relationship Management (CRM) data. You can use the Salesforce connector to create, query, retrieve, update, and delete records in your organization's Salesforce data. The connector uses the Salesforce APIs to interact with Salesforce.


Nowadays most companies use Salesforce to monitor, maintain and manage their customer  relationships in order to optimize revenue growth. Salesforce is known as the world’s first CRM platform which can be accessible from anywhere over the internet which also perfectly utilizes the Cloud computing concept. Salesforce App Cloud provides an integrated architecture that lets you focus on building solutions using Salesforce that can improve Enterprise Business generation. Developers can now assemble applications with a few clicks or code and then instantly launch them across any device to connect with customers, employees or partners.


Below are some of the advantages that you can gain by using Ballerina when it comes to Salesforce.
  • Easy to integrate with Salesforce APIs
  • Query/Insert/Update data within Ballerina
  • Enrich queried results into standard coding formats like JSON, XML
  • Connect to other third party backends where you can easily send the enriched data


User Story



In enterprise Salesforce integration scenarios, most often than not there is a backend which expects Salesforce user account details in a json object format. This post will demonstrate how easy it is to query Salesforce objects and construct a json object as expected by the backend, using Ballerina.


Used tools and techniques



  • Use Salesforce Soap connector provided by Ballerina to create the connection and query the record.
  • Retrieve the data by xpath.
  • Transform the record into a JSON object using particular transformation functions and build the json object.
  • Send it to a backend which expects the enriched JSON object


Resources

Ballerina 0.8.6 distribution


Prerequisites

User should have a salesforce Dev account with username, password and security token
Note : 39.0 is the API version
Soap version 1.1 will be used


Let's Code ..

Step 1 : Starting the Composer

You can simply use ballerina composer to code since it provides you both the designer and source view. In order to start the composer, go to <BALLERINA_HOME>/bin and type ‘sh composer’ for unix based systems or ‘composer’ for windows.


You will see a log as,


INFO  - Ballerina Composer URL: http://localhost:9091


You can open the Composer tool in your browser, using the following URL.


http://localhost:9091


Step 2 :Arguments when running the scenario



In this example, I will be using the main function to perform the required functionality. In Ballerina, the main() function takes an array of strings as its only argument and optionally returns an int.  This is also the potential entry point for command line execution. Any package may have such a function and the execution instruction decides which main() is executed. This documentation provides more information about functions in Ballerina.


I will be sending all the required information as arguments when running the sample.


I will be providing query [0], username [1], password+securityToken [2] ,  the query string [3], login url [4] and soap version [5] as arguments.  


query <userName> <password+securityToken> <queryString> <loginUrl> <soapVersion>


E.g.,
query ushanib@gmail.com password1@\!bE6zsu58ZxvxbNgVSmGXvVgG6 "Select u.Username, u.ProfileId, u.Name, u.LastName, u.Email From User u where Username = 'ushani1@wso2.com'" https://login.salesforce.com/services/Soap/u/39.0 1.1


If you want, you can also assign them in your code without passing them as arguments.


E.g.,
salesforcesoap:ClientConnector sales = create salesforcesoap:ClientConnector("ushanib@gmail.com", "password1@!bE6zsu58ZxvxbNgVSmGXvVgG6", "https://login.salesforce.com/services/Soap/u/39.0", "1.1");


Step 3 : Querying the salesforce object



Inside the main() function, we need to create the salesforce connector connection using the arguments passed in the command line.


Refer this documentation for more information on salesforce soap connector. You need to pass the below mentioned parameters to create the connection.


connector ClientConnector (string username, string password, string loginUrl, string soapVersion) {


function main(string[] args) {
     ..
xml soapResponse;
xml[] headers = [];
string username = args[1];
string password = args[2];
   string loginurl = args[4];
string soapver = args[5];


salesforcesoap:ClientConnector sales = create salesforcesoap:ClientConnector(username, password, loginurl, soapver);


I will be passing our connector details, headers and the query as arguments to retrieve the response in soap. You can see the information in our connector implementation here.


query (ClientConnector s, xml[] headers, string query) (xml) {


Create the header value to be passed as an argument.


xml queryOptions = `<urn:QueryOptions xmlns:urn="urn:partner.soap.sforce.com"><urn:batchSize>1</urn:batchSize></urn:QueryOptions>`;
headers = [queryOptions];
soapResponse = salesforcesoap:ClientConnector.query(sales, headers, args[3]);


I will be printing the queried response as below for our reference.


system:println(xmls:toString(soapResponse));


If you need to perform a different operation, you can extend this by adding another if statement.
E.g., update record, insert record. All the available functions can be found in here. I have used a query statement here in order to execute other operations just by adding an if statement.
Then you should pass any arguments to recognise the operation (E.g., updateRecord) as your 0th argument in command line and other arguments if you wish to pass them as command lines arguments.  


E.g.,
  } else if (args[0] == "updateRecord") {


Step 4 : Transform salesforce search result into a json payload



Use xpath to retrieve the value as a string variable as below :


string Sfusername = xmls:getString(soapResponse, "/*:queryResponse/*:result/*:records/*:Username/text()");


string SfProfileId = xmls:getString(soapResponse, "/*:queryResponse/*:result/*:records/*:ProfileId/text()");


string SfName = xmls:getString(soapResponse, "/*:queryResponse/*:result/*:records/*:Name/text()");


string SfLastName = xmls:getString(soapResponse, "/*:queryResponse/*:result/*:records/*:LastName/text()");


string SfEmail = xmls:getString(soapResponse, "/*:queryResponse/*:result/*:records/*:Email/text()");


Create a json payload with dummy values as below :


json jsPayload = `{"SFUser":{"Sfusername":"Sfusername", "SfProfileId":"SfProfileId", "SfName":"SfName", "SfEmail":"SfLastName", "SfEmail":"SfEmail"}}`;


Construct the json payload using json path as below. You can find the API specification for the json API in here.


Set the string value of the element that matches the given JSONPath.


function set(json j, string jsonPath, string value)


Parameter Name
Data Type
Description
j
A JSON object
jsonPath
The path of the JSON element
value
A string value


jsons:set(jsPayload, "$.SFUser.Sfusername", Sfusername);
jsons:set(jsPayload, "$.SFUser.SfProfileId", SfProfileId);
jsons:set(jsPayload, "$.SFUser.SfName", SfName);
jsons:set(jsPayload, "$.SFUser.SfLastName", SfLastName);
jsons:set(jsPayload, "$.SFUser.SfEmail", SfEmail);
system:println(strings:valueOf(jsPayload));


Step 5 : Send the payload to the particular backend



First I will be initialising a message.
message payloadBack = {};


Then I have set the message payload using the JSON payload object I have created.  Particular API documentation can be found in here.


function setJsonPayload(message m, json payload)


Parameter Name
Data Type
Description
m
message
The current message object
payload
json
The JSON payload object


messages:setJsonPayload(payloadBack, jsPayload);


Then I have used the POST action of HTTP connector as explained in this documentation.


I have created a connection of my backend at the beginning as below.


http:ClientConnector payloadEP = create http:ClientConnector("http://backendjson.com");


Payload will be sent to the configured backend which expects a JSON payload and it will print the response message once it is sent.


message response = http:ClientConnector.post(payloadEP, "/", payloadBack);
system:println("Payload sent to the backend");
system:println(response);


The entire sample is shown below (image 1) as displayed in composer design view :
Screenshot from 2017-04-24 12-26-10.png
(Image 1- Design View of the code)


Below is the representation in source view :
Screenshot from 2017-04-24 12-25-56.png
(Image 2 - Ballerina composer source view)

Running the Sample

You can run the sample from <BALLERINA_HOME>/bin by providing the below command in UNIX.

./ballerina run main <path to .bal file> query <userName> <password+securityToken> <queryString> <loginUrl> <soapVersion>


E.g.,


./ballerina run main ../../ballerina-tools-0.86/samples/sfquery.bal query ushanib@gmail.com password1@!bE6zsu58ZxvxbNgVSmGXvVgG6 "Select u.Username, u.ProfileId, u.Name, u.LastName, u.Email From User u where Username = 'ushani1@wso2.com'" https://login.salesforce.com/services/Soap/u/39.0 1.1


Results



<queryResponse xmlns="urn:partner.soap.sforce.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sf="urn:sobject.partner.soap.sforce.com">
  <result xsi:type="QueryResult">
     <done>true</done>
     <queryLocator xsi:nil="true"/>
     <records xsi:type="sf:sObject">
        <sf:type>User</sf:type>
        <sf:Id xsi:nil="true"/>
        <sf:Username>ushani1@wso2.com</sf:Username>
        <sf:ProfileId>00e90000001aVwiAAE</sf:ProfileId>
        <sf:Name>Balasooriya</sf:Name>
        <sf:LastName>Balasooriya</sf:LastName>
        <sf:Email>ushani@wso2.com</sf:Email>
     </records>
     <size>1</size>
  </result>
</queryResponse>
{"SFUser":{"Sfusername":"ushani1@wso2.com","SfProfileId":"00e90000001aVwiAAE","SfName":"Balasooriya","SfEmail":"ushani@wso2.com"}}
Payload sent to the backend
{
   "success": true,
}

References