Skip to Content

This blog post is related to an integrated scenario from ERP to Salesforce using HCI. This content was based on HCI -Integrating SalesForce (SFDC) using HCI -Part 1 and HCI -Integrating SalesForce (SFDC) using HCI -Part 2, that was create using SOAP API.

The following content I will show how to create an integration using the Salesforce REST API.

Accessing objects in Salesforce organization using REST does not require WSDL. Well-suited for browser-based applications, mobile apps, and highly-interactive social applications.

REST API use data format in JSON or XML and works as synchronous communication.

End-To End Flow

The Integration Project was divided in:

  • OAuth Token request flow
  • Main Flow
  • Exception Flow

The better option to create a full scenario is separate the OAuth and Main Flow in 2 iflows to re-use the develop and Salesforce session in multiple calls (explained in ´s posts). I included in on iflow just to present the end-to-end solution in one post only.

Process Flow

OAuth Token request flow

Process Flow

Content Modifier Get the inputPayload and provide property variables
Script Dynamic query for HTTP request through script
Content Modifier Use the property generated on script to pass thought the HTTP
Request-Reply HTTP to request a valid session
Write variables Write the data on external variables
Content Modifier Use the external variables and provide the inputPayload

The following information should provide by Salesforce to generate a Token/Session ID

  • grant_type with a content value “password”
  • client_id is the Consumer Key
  • client_secret is the Consumer Secret
  • username
  • password

The OAuth will provide to the Token request:

  • redirect_uri is the Callback URL.
  • session_id
  • token_type

Content Modifier

Properties

Action Name Type Data Type Value
Create grant_type Constant java.lang.String password
Create inputPayload Expression java.lang.String ${in.body}

Provide de grant_type content to be used on groovy script and inputPayload to be used after the token session.

Script

The Secret and User parameters was storage on 2 Security Credentials for security and maintenance reasons.

The script will return a query that should be used at the next step.

// query_http.groovy

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import com.sap.it.api.ITApiFactory;
import com.sap.it.api.securestore.SecureStoreService;
import com.sap.it.api.securestore.UserCredential;

def Message processData(Message message) {

	//Message Log
	def messageLog = messageLogFactory.getMessageLog(message);
	
	//Credentials
	def secret = "SF_Secret";
	def user = "SF_User";
	
	//service to get the credentials
	def service = ITApiFactory.getApi(SecureStoreService.class, null);
	
	//get secret credential
	def secretCred = service.getUserCredential(secret);
	if (secretCred == null){
		throw new IllegalStateException("No credential found for alias " + secret);
	}
	
	//get user credential
	def userCred = service.getUserCredential(user);
	if (userCred == null){
		throw new IllegalStateException("No credential found for alias " + user);
	}
	
	//HTTP Parameters value
	String client_id = secretCred.getUsername();
	String client_secret = new String(secretCred.getPassword());
	String username = userCred.getUsername();
	String password = new String(userCred.getPassword());
	
	//Properties  
	def map = message.getProperties();
	def grant_type = map.get("grant_type");
	
	//Query
	def query = "";
	query = query + "grant_type=" + grant_type + "&";
	query = query + "client_id=" + client_id + "&";
	query = query + "client_secret=" + client_secret + "&";
	query = query + "username=" + username + "&";
	query = query + "password=" + password;
	
	message.setProperty("query", query);
	
	return message;
}

Content Modifier

Header

Action Name Type Data Type Value
Create CamelHttpQuery Expression java.lang.String ${property.query}
Create Accept Constant java.lang.String application/xml

At this point we should provide to the HTTP Communication Channel a query to request a token. We will use a dynamic query created at the previous script an pass trough the CamelHttpQuery on header. This variable changes the HTTP Query on the Communication Channel.

Additionally, I include the header “Accept” to request a “application/xml” at the response, is it possible to request a JSON data also.

Request-Reply

This request provides a POST message as the following:

//separeted parameters

curl https://login.salesforce.com/services/oauth2/token 
-d "grant_type=password" 
-d "client_id=myclientid" 
-d "client_secret=myclientsecret" 
-d "username=mylogin@salesforce.com" 
-d "password=mypassword123456"

//grouped parameters on query

curl https://login.salesforce.com/services/oauth2/token 
"grant_type=password&" +
"client_id=myclientid&" +
"client_secret=myclientsecret&" +
"username=mylogin@salesforce.com&" +
"password=mypassword123456"

 

Write variables

The access_token is our Session ID Authorization that we need. Remember at the previous step I use the Accept parameter with “application/xml” value, so, we can set variables reading directly the Channel response.

Name Data Type Type Value Global Scope
access_token java.lang.String XPath /OAuth/access_token
instance_url java.lang.String XPath /OAuth/instance_url
token_type java.lang.String XPath /OAuth/token_type

 

Content Modifier

Header

Action Name Type Data Type Value
Delete CamelHttpQuery
Delete Accept
Delete Content-Type

Properties

Action Name Type Data Type Value
Create access_token Local Variable access_token
Create instance_url Local Variable instance_url
Create token_type Local Variable token_type
Delete query
Delete grant_type

Using the same integration flow for request the token and provide the update query, I included the Delete commander on the header and properties to delete the previous parameters that do not be used.

Body

${property.inputPayload}

The inputPayload set on body, to request update the Object ID on Salesforce

Main Flow

Process Flow

Process Call Request a valid session ID on Salesforce
Content Modifier Ready the data of inputPayload to use on next script
Script Groovy mapping to define the fields to update (JSON)
Content modifier Provides URL and Auth details to the HTTP
Request-Reply The HTTP request to Salesforce – Update query on body
Content Modifier  retun in XML format to the sender

Process Call

Call the previous iflow

Salesforce Request Token OAuth

Content Modifier

Properties

Action Name Type Data Type Value
Create type Constant Account
Create ExternalID XPath /update/BusinessPartner/ExternalID
Create Status XPath /update/BusinessPartner/Status
Delete ServiceURL Constant /services/data/v39.0/sobjects/

Use the ServiceURL parameter to add a variable string to the URL, the value “/services/data/v39.0/sobjects/” request the REST service data and inform the version of Salesforce API. This information will be used on the next script.

Script

The syntax to provide the access token in your REST requests:

Authorization: Bearer access_token

// For example:
curl https://instance_name.salesforce.com/services/data/v39.0/ 
-H 'Authorization: Bearer access_token'

To update a field on a record, the Salesforce accept the HTTP method PATCH, but the HCI does not provide support on this Method until this moment.

The following cURL command retrieves the specified Account object using its ID field, and updates its Status field.

curl https://na1.salesforce.com/services/data/v39.0/sobjects/Account/001D000000IroHJ 
-H "Authorization: Bearer access_token" 
-H "X-PrettyPrint:1" 
-H "Content-Type: application/json" 
--data-binary '{ "Status__c" : "Inactive" }' 
-X PATCH

In order to resolve the difference between Salesforce and HCI update method, we just provide a POST method on HCI side but including at the URL the command “?_HttpMethod=PATCH“.

https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm

This information will update the method on calling the Communication Channel request.

The information included on the following script:

//query_update.groovy
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;

def Message processData(Message message) {
	
	def fieldName = "Status__c"; // field [s] to be change the value
	def map = message.getProperties();
	
	//XML Properties  
	def type = map.get("type"); //sObject type to change (E.g. "Account")
	def id = map.get("ExternalID");
	def status = map.get("Status"); // map the value that should be change
	
	//Auth Properties
	def access_token = map.get("access_token");
	def instance_url = map.get("instance_url");
	def token_type = map.get("token_type");
	
	//URL
	def serviceURL = map.get("ServiceURL");
	def url = "";
	url = instance_url + serviceURL + type + "/Id/" + id + "?_HttpMethod=PATCH";
	
	//Header
	def auth = token_type + " " + access_token;
	
	//Query
	def query = "";
	query = query + "{ \"" + fieldName + "\" : \"" + status + "\" }";
	
	//Set Properties
	message.setProperty("url", url);
	message.setProperty("auth", auth);
	message.setProperty("query", query);
	
	return message;
}

 

Content modifier

Header

Action Name Type Data Type Value
Create CamelHttpUri Expression ${property.url}
Create Content-Type Constant application/json
Create Authorization Expression ${property.auth}
Delete Accept

Dynamic change the HTTP URL using the Camel parameter CamelHttpUri

Body

${property.query}

Request-Reply

Note that the Address provided at the Request-Reply is just to have some information, because the correct URL will be change dynamically with CamelHttpUri.

Content Modifier

Body

<return></return>

I just include a return tag on the final content modifier to return back a XML response. the reason is that the response provided by Salesforce is in JSON format. There is better way to convert the JSON to XML format to get the correctly response.

When successful, return an empty message, this is the tag I found to does not return a error for all responses on HCI.

Exception Flow

Process Flow

Content Modifier  Provide the exception message on error

In any error message from Salesforce, the error return only the message monitoring on HCI. In order to revert back to the Sender system, I included the exception flow just with the exception.message log on the body.

Content Modifier

<return>
${exception.message}
</return>

Test the Interface

Finally, to test the interface we are using the SOAP UI to provider the Sender message with the External ID and the field to update on Salesforce.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
	<soapenv:Body>
     	<update>
      		<BusinessPartner>
      			<ExternalID>0017000000abCDe</ExternalID>
      			<Status>Inactive</Status>
      		</BusinessPartner>
      	</update>
   	</soapenv:Body>
</soapenv:Envelope>

This interface is just an example how to use the REST API from Salesforce based that HCI does not support PATCH method and how to resolve it.

The interface should have mode fields and is possible to work with a message mapping and groovy mapping to create the complete JSON Query to update the Salesforce fields.

I hope this integration process bring how to use the REST API and help in any new integration with Salesforce system.

Other patterns and models can be added into the comment sections!

Reference:

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply