Monday, November 20, 2017

Spring Boot RestTemplate to call Salesforce REST API

Purpose of this blog post

Using Spring Boot RestTemplate to communicate with a REST API is nothing new. There are many examples on the net. Using RestTemplate to communicate with a Salesforce REST API is also not  new and there are many examples out there. This post is an attempt to share my learnings with many others who are not new to Spring Boot RestTemplate but maybe relatively new to the Salesforce REST API. Its my attempt to share the things I discovered and provide a quick way to accomplish this REST based communication.

Example Scenario

You have written a REST API in your Salesforce instance to get details of Accounts. You now want to use an external host like a Heroku hosted java application to query this API and get the results.

Prerequisites

I am assuming you are quite comfortable with Java and Spring Boot. I further assume that you are familiar with Salesforce and can find your way by reading the online documentation and help topics published by Salesforce.

Java

You know what you need, but I will list what I used
  • Spring STS IDE
  • Java 8
  • Spring Boot 1.5.6.RELEASE
I am assuming you are familiar with creating a Spring Boot starter project either in your STS IDE or from the Spring Initializr site.

Salesforce 

I am assuming you have access to a Salesforce instance. If not, signup for a free developer edition. I further assume that you know how to create a connected app and write a simple Apex class with SOQL queries to query for standard or custom objects. This blog post cannot cover all the basics of Salesforce and the Apex programming language.

Steps to take on Salesforce side

You need to do two things on the salesforce side -

  • Create a new connected application or use an existing connected application.
  • Create a Apex class and expose it via the Salesforce REST API mechanism


Create a new connected application

  • Create a connected app and ensure that you enable OAuth. Details here.

Use an existing connected application 

If you already have a connected app and have not enabled OAuth, then follow these steps
  • Click on the connected app name.


Connected Apps













  • On the page showing your app details, look at the OAuth Policies section. For getting this example to work, ensure that the IP Relaxation has a value of "Relax IP restrictions".
    What this means is that any host can connect with your app. Later on, you can fine tune access based on a set of IP addresses. (Initially, I was unable to connect to the REST API and received error messages when I called the API using CURL from the command line. Finally, I figured out that I needed to set this feature to relax the IP addresses which can call the Salesforce API.)
  • Now go the Setup page by clicking the link on the top right in the header of the page (next to your name. For the new Lightning Experience UI, this is in a drop down menu.). 
  • Using the left search menu look for "Apps"
  • Click on the Apps option under the Build -> Create menu on the left side.
  • You should see your existing connected app listed in the Connected Apps section at the bottom of the page on the right side.
  • Once again, click on the name of your app to see the details page of your app
  • Click on the Edit button to edit your application settings
  • On the edit page, enable OAuth and enter data to make your app's OAuth settings look similar to this screen shot.
OAuth settings












  • When you enable OAuth and click Save you should see the API (Enable OAuth Settings) section populated with important data which is required for connecting to your Salesforce REST API from an external REST client.
  • Copy the Consumer Key and store it safely. This will be the client_id will be used in the API client code for authentication
  • Click on the Click to reveal link next to the Consumer Secret to view the value and store it safely. This will be the client_secret which will be used in the API client code for authentication

Writing a Apex class and exposing it via REST

Its quite easy to write an Apex class and expose it via REST. The following example code returns just 2 Account objects when invoked via the REST url. You should refer the official Salesforce documentation for details.

Create a new Apex class, named anything you like, as follows using the Dev console.

 @RestResource(urlMapping = '/Account/*')  
 global with sharing class MyRestResource {  
   @HttpGet  
   global static List<Account> getAccounts() {  
     List<Account> result = [SELECT Id, Name, Phone, Website FROM Account LIMIT 2];  
     return result;  
   }  
 }  


Now work is done from Salesforce side. If you are familiar with Curl then you can try calling this URL and test the response. Note that you will have to authenticate first to obtain a OAuth token to access the REST url.

Setting up the Spring Boot RestTemplate code

The following code is brief and meant to just shows how to authenticate and then access the REST url.  I assume that you are familiar with creating a Spring Boot starter app.

Configure the Spring RestTemplate

Create a package called config , e.g. com.example.config in your project and copy paste the following class. This will set some defaults like timeout for attributes of the RestTemplate. For more details refer the Spring docs at this link.


import org.springframework.context.annotation.Bean;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;

@Component
public class RestTemplateConfig {
 
 @Bean
 public ClientHttpRequestFactory clientHttpRequestFactory() {
  int timeout = 5000;
  HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
  clientHttpRequestFactory.setConnectTimeout(timeout);
  clientHttpRequestFactory.setConnectionRequestTimeout(timeout);
  clientHttpRequestFactory.setReadTimeout(timeout);
  return clientHttpRequestFactory;
 }
}

Code that authenticates and calls the REST api

The following code will first authenticate with the Salesforce OAuth service and then call the REST url.


import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.salesforce.bigobjects.config.RestTemplateConfig;

@RestController
public class RestTemplateRequestToSalesforce {

 private static final Logger LOG = LoggerFactory.getLogger(RestTemplateRequestToSalesforce.class);

 // Inject the Rest template configuration
 @Autowired
 RestTemplateConfig restConfig;

 /**
  * Simple Rest end point in this app to demonstrate authenticating and then calling the Salesforce REST url
  * 
  * @return
  */
 @GetMapping("/accounts")
 public String callRestUrl() {

  // First define the OAuth2 token URL as follows by replacing YOUR_INSTANCE
  // with the actual instance that you have your org on
  String oauthUrl = "https://YOUR_INSTANCE.salesforce.com/services/oauth2/token";

  // Create a multi value map to put necessary attributes
  // for the authentication request
  MultiValueMap<String, String> mvMap = new LinkedMultiValueMap<>();
  mvMap.add("grant_type", "password");
  mvMap.add("client_id", "Your Client Id");
  mvMap.add("client_secret", "Your Client Secret");
  mvMap.add("username", "Your username");
  mvMap.add("password", "Your password");

  // Create an instance of the RestTemplate
  RestTemplate restTemplate = new RestTemplate(restConfig.clientHttpRequestFactory());

  // Send the REST request to get authenticated and receive an OAuth token
  Map<String, String> token = (Map<String, String>) restTemplate.postForObject(oauthUrl, mvMap, Map.class);
  LOG.info("--------------------------------------------");
  LOG.info("Access Token  :: " + token.get("access_token"));
  LOG.info("Instance Url  :: " + token.get("instance_url"));
  LOG.info("Id  :: " + token.get("id"));
  LOG.info("Token_Type  :: " + token.get("token_type"));
  LOG.info("Signature  :: " + token.get("signature"));
  LOG.info("--------------------------------------------");

  // The oauth token is now required for all further calls to the REST resources
  String oauthToken = token.get("access_token");

  // The REST url of your Salesforce Apex class will be of the form
  String restUrl = "https://YOUR_ISTANCE.salesforce.com/services/apexrest/Account";

  // Create Http headers and add the oauth token to them
  HttpHeaders restHeaders = new HttpHeaders();
  restHeaders.setContentType(MediaType.APPLICATION_JSON);
  restHeaders.add("X-PrettyPrint", "1");
  restHeaders.add("Authorization", "OAuth " + oauthToken);

  // Create a Multi value map in case there are query params. In this example
  // there are none so just an empty map is okay
  MultiValueMap<String, String> mv2Map = new LinkedMultiValueMap<>();

  // Following example code is using the RestTemplate exchange(..) method for making a GET request
  // Other methods like getForEntity() or getForObject() can also be used.
  HttpEntity<?> restRequest = new HttpEntity<>(mv2Map, restHeaders);
  RestTemplate getRestTemplate = new RestTemplate(restConfig.clientHttpRequestFactory());
  // Make a request and read the response string
  ResponseEntity<String> responseStr = getRestTemplate.exchange(restUrl, HttpMethod.GET, restRequest,
    String.class);

  // Return just the body of the response. You can examine the headers, etc if you wish
  HttpStatus responseStatus = responseStr.getStatusCode();
  HttpHeaders responseHeaders = responseStr.getHeaders();
  String responseBody = responseStr.getBody();
  LOG.info("REST API response:" + responseBody);
  return responseBody;
 }
}



Summary

The code is quite straightforward. Authenticate and obtain an OAuth token. Use this token is subsequent requests.

No comments: