Reading and Writing to Salesforce
Beginning with Logi Info v12.2 SP5, it's possible to use Salesforce's REST API to "write back" (Insert, Update, Delete) data to your Salesforce instance. This is a complex development task, however, and requires in-depth knowledge of both Logi Info and Salesforce.
The following discussion of operations using the Salesforce REST API assumes a security model in which a single Salesforce account (the "service" account) will be used to access Salesforce on behalf of all users of your Logi application. This account will require a standard Salesforce User License and should be restricted to limit its interactions to just the Salesforce objects to be modified. Do not use an Administrator account for this. You'll need to know the account's Salesforce password and security token value. Salesforce uses OAuth 2 security, which includes a Username-Password authentication flow mode, which will be used in this example.
The general steps for developing a Logi application that uses the API are:
- Register your Logi application as a "Connected App" in Salesforce, and get its "Consumer Key" and "Consumer Secret" values. See this Salesforce document for more information.
- You may need to create a Salesforce Custom Profile and Permission Set for the service account, in order to ensure access to the Salesforce objects you want your Logi application to interact with.
- As shown above, a Connection.REST element is used in the _Settings definition to connect to Salesforce in order to login. No login credentials are configured in this element.
- In your application's Default report definition, add a Local Data element and DataLayer.REST to perform the login. Set the Local Data element's Condition attribute to "@Session.SF_AccessToken~" = ""to ensure that the access token will only be requested once per session (it has a default 4-hour lifetime).
An Http Body element is used to pass parameters during the login. The Http Body Params name-value pairs that need to be passed are shown above. The client_id and client_secret parameter values are, respectively, the Consumer Key and Consumer Secret values you noted when you created the Salesforce Connected App. The grant_type value must be password. The password parameter value is the Salesforce service account's password, concatenated with its security token. The username is the account's Salesforce login ID. - Finally a Flattener element, with no attributes set, is used to make the returned values usable as @Local tokens.
- Next, add a Set Session Variables element and configure its parameters as shown above. This makes the values returned from a successful login available for later use as session variables.
- Back in the _Settings definition, add another Connection.REST element, to be used when making requests using the Salesforce REST API. Configure is as shown above, with one of the session variables created in the previous step.
- Add a Request Header element beneath the Connection element, as shown above, and configure it as shown, using the second session variable.
Making Salesforce API Queries
Now we're ready to make a call to the Salesforce REST API, using DataLayer.REST, as shown below:
The Accept Type value is application/json once again, and the Http Method this time is GET. The Url Path attribute contains the Salesforce Object Query Language (SOQL) query and here's an example:
/services/data/v38.0/query?q=SELECT Id, FirstName, LastName, Email, SuperUser FROM SelfServiceUser WHERE ContactId = '@Request.someID~'
You'll recall that, at execution, the datalayer's Url Path attribute value will be appended to the Connection element's Url Host value to make a complete URL.
Once again, we'll need a Flattener element to shape the returned JSON data. In the case of SOQL queries, most of the time we need to tell the Flattener where to begin, by using its Top Level XPath attribute:
And in most cases, the records node, as shown above, is the correct one.
Writing to Salesforce via the API
Writing to Salesforce is somewhat similar to making queries, in that the same Connection.REST element is used and important information is placed in the URL Path attribute. However, there are some significant differences. The suggested approach is to use Process tasks, and you might think to use the Procedure.REST element to call the API.
However, when you make an API call you may want to use data it returns, but the Procedure.REST element is not a parent of the Flattener element, so you can't get the returned data into the proper format. The solution to this is to use a Procedure.Run DataLayer Rows element with a child DataLayer.REST element.
As shown above, the configured datalayer's attribute values are similar to those provided in the earlier Query example, except the Http Method is now POST. The Url Path is not a SOQL query but a direct reference to a Salesforce object endpoint, in this example, the Case object. This is the way the API expects to receive a command to insert that object.
In addition, an Http Body child element, configured as shown above, is used to pass the values to be inserted. Here's an example of the JSON data array in the Http Body Content attribute:
{
"AccountId" : "@Request.Acct_ID~",
"Description" : "@Request!Json.hdnDesc~",
"Product__c" : "Logi Info",
"Product_Version__c" : "@Request.inpVersion~",
"Service_Pack_Version__c" : "@Request.inpSPNo~",
"Subject" : "@Request!Json.inpSubject~"
}
Note the use of literal and @Request token values. Also, notice the use of the highlighted !Json token modifier. It's used when the data is free-form text and it encodes any special characters, such as double-quotes and line-feeds, so that Salesforce won't reject the Json data stream as invalid.
Once again, a Flattener is used to shape the returned data in the example but, in this case, no Top Level XPath attribute needs to be specified.
Please remember that the Salesforce API may not enforce referential integrity and therefore it may be up to you to ensure that all related tables are updated and other appropriate actions aretaken for each operation, in order to ensure a complete transaction.