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.

Monday, October 23, 2017

Sending form data as a custom object to a Spring MVC Controller


Form Data and Spring MVC Controllers

In a Spring MVC application, sending form data to the controller is quite well known and trivial. Typically all data gets sent as a bunch of key=value pairs. Each key is a request param which can be captured in the Controller using the @RequestParam("key") String key construct in the method signature.

If the number of fields are less, then you can use the request param construct to capture each form field, but if there are several form fields, then using this approach can create a unwieldy method signature and create room for error.

There are several ways to collect form data but in this post, I am going to address one specific use case.

Motivation for this post

Recently, someone at work asked me how to send data so that it was received by the Spring controller as a custom object/Pojo. I suggested using javascript to massage and format the data as desired and then send it.

Now using jquery forms on the browser side you can send data in any format after converting it to a JSON string.

For some business reasons, javascript was not an option. The HTML form had to be submitted as is using traditional http post method.

The purpose of this short blog post is to demonstrate how to send data from the browser without the help of javascript or any jquery plugins. This is typically useful when you have checkboxes and radio buttons in the UI and there is a relation between them.

Example

I have created an example UI which shows the kind of form that was to be submitted and then the controller code which processes the http post of this form.



As you can see, this form shows a list of car makes and their colors. The idea is that the user will check a checkbox for a car make and then select a color using the radio buttons.

Note that this form is rendered on the server side by reading the car makes and colors and then sent as a HTML page to the browser.

The HTML generated for this form is like this

 <form name="customObjectForm" id="customObjectForm" method="post" action="/submit">  
   <table class="table table-striped table-condensed table-bordered">  
     <thead>  
       <tr>  
         <th>Vehicle</th>  
         <th>Select</th>  
         <th>Color</th>  
       </tr>  
     </thead>  
     <tbody>  
       <tr>  
         <td>Toyota Camry</td>  
         <td><input type="checkbox" name="carModel" value="Camry" /></td>  
         <td>  
           <input type="radio" name="colors[Camry]" value="RED" /> RED &nbsp;  
           <input type="radio" name="colors[Camry]" value="YELLOW" /> YELLOW&nbsp;  
           <input type="radio" name="colors[Camry]" value="GREEN" /> GREEN&nbsp;  
         </td>  
       </tr>  
       <tr>  
         <td>Honda Civic</td>  
         <td><input type="checkbox" name="carModel" value="Honda" /></td>  
         <td>  
           <input type="radio" name="colors[Honda]" value="BLUE" /> BLUE&nbsp;  
           <input type="radio" name="colors[Honda]" value="BLACK" /> BLACK&nbsp;  
           <input type="radio" name="colors[Honda]" value="GREEN" /> GREEN&nbsp;  
         </td>  
       </tr>  
     </tbody>  
     <tfoot>  
       <tr><td colspan="3"><input type="Submit" name="submit" class="btn btn-primary" /></td></tr>  
     </tfoot>  
   </table>  
 </form>  

Domain model

If you examine the HTML, you will notice that we can infer a Key Value pair type relationship between the car make and the color options. To relate the car make to the colors, the name of the radio buttons is named like an associative array with the car make as the value of the colors variable. 

The car make checkboxes can be represented as a List in the domain model. If a user checks a checkbox then that checkbox value is submitted by the form, otherwise it is not.

Next, the row showing color options must be related to the car make in the domain model. To make that happen, the radio button selection can be submitted as a Map in the domain model.

So the car model can be a List<String> carMakes and the car colors can be a Map<String, String> colors where the key is the car make and the value is the actual selected color.

There can be a better domain model created and the HTML can be structured accordingly, but this is a quick example to show how Spring can convert browser submitted values in ready to use custom objects that you define.

Here is the CarMakeColorModel domain object / POJO.

package com.example.formobjects.model;

import java.util.List;
import java.util.Map;

public class CarMakeColorModel {

 // for the checkboxes for selecting the car makes
 private List<String> carMakes;

 // for the radio buttons to select the car color
 private Map<String, String> colors;

 public CarMakeColorModel() {
  // default constructor
  super();
 }

 public CarMakeColorModel(List<String> carMakes, Map<String, String> colors) {
  super();
  this.carMakes = carMakes;
  this.colors = colors;
 }

 public List<String> getCarMakes() {
  return carMakes;
 }

 public void setCarMakes(List<String> carMakes) {
  this.carMakes = carMakes;
 }

 public Map<String, String> getColors() {
  return colors;
 }

 public void setColors(Map<String, String> colors) {
  this.colors = colors;
 }

}


On the Controller side, use the @ModelAttribute annotation to make Spring collect all the form submitted values and populate the CarMakeColorModel pojo.

And here is the Spring Controller snippet which processes this HTML form.


package com.example.formobjects.controllers;

import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.example.formobjects.model.CarMakeColorModel;

/**
 * Controller to handle the form submission
 * 
 * @author <a href="mailto:sunitkatkar@gmail.com">Sunit Katkar</a>
 * @version 1.0
 * @since Oct 2017
 */
@Controller
public class FormCustomObjectController {

 @RequestMapping(value = "/", method = RequestMethod.GET)
 public String index(ModelMap modelMap) {
  return "index";
 }

 @RequestMapping(value = "/submit", method = RequestMethod.POST)
 public String submit(HttpServletRequest request, ModelMap modelMap,
   @ModelAttribute CarMakeColorModel carMakeColorModel) {

  List<String> carMakes = carMakeColorModel.getCarMakes();
  Map<String, String> carColors = carMakeColorModel.getColors();
  for (String carModel : carMakes) {
   String color = carColors.get(carModel);
   System.out.println("Car make:" + carModel + " Car color:" + color);
  }
  return "index";
 }

}


What I have shown is a known way of processing forms completely on the server side without aid of javascript. Though many of you know this technique, I am sure there are some out there, like my colleague, who are not aware of this; hence this post.

That's all there is to it. Happy coding :)