Creating an Umbraco-Salesforce partnership

mario.jpg
Story by Mario Lopez, Published 01 Nov, 2017
4 minutes to read

Creating and Umbraco-Salesforce partnership

One of the last projects I had to work on has involved the syncing of data from Salesforce to Umbraco.

Basically, the client wanted to keep a list of suppliers (or Accounts) on SF. On the Umbraco website, these suppliers must be displayed in a list/detail pattern. They even had to be indexed so the users could search for them on the site.

Talking to Salesforce

The first thing to do a Salesforce integration is, of course, call the Salesforce API. To do so, instead of creating my own models and methods I just used the toolkit already available from Force.com:

https://github.com/developerforce/Force.com-Toolkit-for-NET

This toolkit allows us to connect to the REST API. It doesn’t offer everything you can possibly do with the API but it does a good job saving you a lot of time. And you could always extend it with your own methods.

To be able to connect to SF API the first thing you will need is get authorization. To do so you will need a bunch of different keys and passwords. I’m not going to enter into details about SF related tasks to configure your API to accept external queries but you can find more information here: https://developer.salesforce.com/page/REST_API

SF REST API supports OAuth 2.0 so you will need an authorization token in order to query it.  You can choose from 3 different flows: Web server flow, User-agent flow or Username-Password flow.

Because the sync is going to be done by our server, we can just store our username and password in our web.config to use them at any time the server wants to query SF. This allows us to use the username-password flow.

This is my security manager, where I will retrieve that token:

readonly string _consumerKey;
readonly string _consumerSecret;
readonly string _username;
readonly string _password;
readonly string _tokenBaseUrl;

public ForceClient Client { get; set; }
public string InstanceUrl { get; private set; } public string AccessToken { get; private set; } public string ApiVersion { get; private set; }

AuthenticationClient _auth;

public string InstanceUrl { get; private set; } public string AccessToken { get; private set; } public string ApiVersion { get; private set; }

AuthenticationClient _auth;

public SFSecurityManager()
{

      _consumerKey = WebConfigurationManager.AppSettings["SFClientId"];
      _consumerSecret = WebConfigurationManager.AppSettings["SFClientSecret"];
      _username = WebConfigurationManager.AppSettings["SFUsername"];
       _password = WebConfigurationManager.AppSettings["SFPassword"];
      _tokenBaseUrl = WebConfigurationManager.AppSettings["SFTokenBase"];
      _auth = new AuthenticationClient();

      ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
}

public async System.Threading.Tasks.Task Login()
{
      var endpoint = "/services/oauth2/token";
      await _auth.UsernamePasswordAsync(_consumerKey, _consumerSecret, _username, _password, _tokenBaseUrl + endpoint);
      InstanceUrl = _auth.InstanceUrl;
      AccessToken = _auth.AccessToken;
      ApiVersion = _auth.ApiVersion;
      Client = new ForceClient(_auth.InstanceUrl, _auth.AccessToken, _auth.ApiVersion);

}

This manager will get the token from SF and will store the response in a ForceClient. This ForceClient will have all the information the API will need to handle your request: the instance URL, our access token and the API version we are using. All this information is returned by the same API when we ask for our token so we don’t really need to store any details like the instance URL. It is common to store one URL for production and another one for the sandbox, but it’s not actually needed.

 

Salesforce API security

One important thing to take into account is to query the API using TLS 2.0, more info here

To activate TLS 2.0 you have to use this before querying the API:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

In our case, we just included that line in our manager constructor before we ask for our token and from that point on the client is already configured for the next queries.

Once we have the client we are ready to query the API.

In our project, we will do the following query. We’ll get the Accounts of type ‘Partner’ that has been modified in the last hours (that startDate):

public async Task<QueryResult> GetUpdatedMembersAsync(DateTime startDate) {     
      _client = SingletonWrapper.Instance.Client;
      var sqlFormattedDate = startDate.Date.ToString("O");

      //It will sync accounts from last date we run a sync.
      //In the example we only get Id and Name but of course you can create your own query

      var query = $"SELECT Id, Name FROM Account WHERE SystemModstamp >= { sqlFormattedDate} AND RecordType.DeveloperName = 'Partner'";

      var accounts = await _client.QueryAsync(query);

      //we cast the returned values to our model. return accounts;

}

This method will cast the response from SF to your model. We will use that model to create or update our Umbraco content.

//member is IEnumerable<SFMemberModel> coming from the previous method
foreach (var details in members)
{
      var cs = ApplicationContext.Current.Services.ContentService;
      var currentMember = cs.GetById(rootNodeId).Children()
.FirstOrDefault(m => m.Properties.FirstOrDefault(p => p.Alias == "memberId").Value?.ToString() == details.Id);

      //If our member exists we update with new info. Here we could check if anything has changed but in this case we don’t really care, just update everything.
      if (currentMember != null)
      {
            importer.UpdateMember(currentMember, details);
      }
      else
      {
            importer.ImportNewMember(details);
      }
}

Mapping

So, once we know if we want to update or create a new member it’s just a matter of casting our Salesforce object to IContent and save it.

This is an extract of our mapping method:

public void CreateContent(this SFMemberModel member)
{
      IContent supplier = cs.CreateContent(member.Name, rootNodeId, "supplierDetail");

      supplier.Name = member.Name;
      supplier.SetValue("umbracoNaviHide", false);
      supplier.SetValue("memberId", member.Id);
      supplier.SetValue("supplierName", member.Name);
      supplier.SetValue("accountSubtype", member.AccountSubtype);
      //more properties here….
      var cs = ApplicationContext.Current.Services.ContentService;
      cs.SaveAndPublishWithStatus(supplier);
}

Salesforce Workbench

A very useful tool to help you in the process is the Salesforce Workbench

This is a nice tool that you can use to create queries to your API without having to create your own client.

You just log in with your SF authorisation credentials (the same ones you use to get your token)

salesforce workbench

Summary

We did a basic integration with Salesforce API. Our clients will use SF to store their accounts that have some information they want to be displayed in their website. To do so we just had to follow these steps:

  1. We have asked SF to give us a token using our credentials.
  2. We have used that token to query the SF API to give us a list of the members (accounts in SF) that have been updated in the last 24 hours (in our example).
  3. We decoded that response into our own model.
  4. We checked if the member is new or not so we can create a new one of update it.
  5. We casted our SF model into an IContent
  6. We saved the new node.

 

Tags: Salesforce, Integrations,