Commit 7676e2eb authored by john-vester's avatar john-vester
Browse files

Additional updates, plus updates

parent 55f7499d
......@@ -56,6 +56,7 @@ For this example, only the `Contact` object in Salesforce will be utilized, whic
```java
public class Contact {
private String id;
private String name;
private String title;
private String department;
......@@ -71,14 +72,106 @@ public class SalesforceAttributes {
The following URIs exist with this service:
`GET - /contacts` - returns a list of `Contacts` from Salesforce
* `GET - /contacts` - returns a list of `Contact` objects from the connected Salesforce instance
* `GET - /contacts/{id}` - returns a single `Contact` object for the provided `id` from the connected Salesforce instance
* `PATCH - /contacts/{id}` - performs the necessary updates and returns the updated object for the following `PatchUpdates` object:
`PATCH - /contacts/{id}` - performs the necessary updates and returns the updated object
```java
public class PatchUpdates extends HashMap<String, String> { }
```
### GET Contacts
Below, is an example of a `GET` request to retrieve all `Contact` objects in Salesforce:
```shell
curl --location --request GET 'http://localhost:9999/contacts'
```
An HTTP `200` (OK) response will be returned, along with a full list of `Contact` entries:
```json
[
{
"attributes": {
"type": "Contact",
"url": "/services/data/v52.0/sobjects/Contact/0035e000008eXq0AAE"
},
"id": "0035e000008eXq0AAE",
"Name": "Rose Gonzalez",
"Title": "SVP, Procurement",
"Department": "Procurement"
},
// additional items here
{
"attributes": {
"type": "Contact",
"url": "/services/data/v52.0/sobjects/Contact/0035e000008eXqJAAU"
},
"id": "0035e000008eXqJAAU",
"Name": "Jake Llorrac",
"Title": null,
"Department": null
}
]
```
### GET Single Contact
Below, is an example of `GET` request for a single `Contact` (using values from the list above):
```shell
curl --location --request GET 'http://localhost:9999/contacts/0035e000008eXq0AAE'
```
An HTTP `200` (OK) response will be returned, along with the requested `Contact` entry:
```json
{
"attributes": {
"type": "Contact",
"url": "/services/data/v52.0/sobjects/Contact/0035e000008eXq0AAE"
},
"id": "0035e000008eXq0AAE",
"Name": "Rose Gonzalez",
"Title": "SVP, Procurement",
"Department": "Procurement"
}
```
### PATCH Contact
Below, is an example of a `PATCH` request using cURL:
```shell
curl --location --request PATCH 'http://localhost:9999/contacts/0035e000008eXq0AAE' \
--header 'Content-Type: application/json' \
--data-raw '{
"Title": "SVP, Procurement 2"
}'
```
An HTTP `202` (Accepted) response will be returned, along with the updated `Contact` entry:
```json
{
"attributes": {
"type": "Contact",
"url": "/services/data/v52.0/sobjects/Contact/0035e000008eXq0AAE"
},
"id": "0035e000008eXq0AAE",
"Name": "Rose Gonzalez",
"Title": "SVP, Procurement 2",
"Department": "Procurement"
}
```
## Caching
This repository employs the very basic `spring-boot-starter-cache` in order to reduce the need to make API calls to Salesforce.
To keep things simple, when the `PATCH` URI (noted above) is called, the entire cache will be evicted.
## Additional Information
......
......@@ -34,6 +34,16 @@
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
......
package com.gitlab.johnjvester.salesforceintegrationservice.controllers;
import com.gitlab.johnjvester.salesforceintegrationservice.models.Contact;
import com.gitlab.johnjvester.salesforceintegrationservice.models.PatchUpdates;
import com.gitlab.johnjvester.salesforceintegrationservice.services.ContactService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
......@@ -9,6 +10,9 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
......@@ -31,4 +35,24 @@ public class ContactController {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
@GetMapping(value = "/contacts/{id}")
public ResponseEntity<Contact> getContacts(@PathVariable String id) {
try {
return new ResponseEntity<>(contactService.getContact(id), HttpStatus.OK);
} catch (Exception e) {
log.error(e.getMessage(), e);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
@PatchMapping(value = "/contacts/{id}")
public ResponseEntity<Contact> updateContact(@PathVariable String id, @RequestBody PatchUpdates patchUpdates) {
try {
return new ResponseEntity<>(contactService.updateContact(id, patchUpdates), HttpStatus.ACCEPTED);
} catch (Exception e) {
log.error(e.getMessage(), e);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
}
......@@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
@AllArgsConstructor
@NoArgsConstructor
......@@ -23,4 +24,12 @@ public class Contact {
private String department;
private SalesforceAttributes attributes;
public String getId() {
if (attributes != null && attributes.getUrl() != null) {
return StringUtils.substringAfterLast(attributes.getUrl(), "/");
}
return null;
}
}
......@@ -7,14 +7,20 @@ import com.gitlab.johnjvester.salesforceintegrationservice.models.PatchUpdates;
import com.gitlab.johnjvester.salesforceintegrationservice.models.SalesforceLoginResult;
import com.gitlab.johnjvester.salesforceintegrationservice.models.SalesforceResponse;
import com.gitlab.johnjvester.salesforceintegrationservice.utils.BearerTokenUtilities;
import com.gitlab.johnjvester.salesforceintegrationservice.utils.HttpUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import java.util.List;
......@@ -23,23 +29,48 @@ import java.util.List;
@RequiredArgsConstructor
@Service
public class ContactService {
public static final String QUERY_PATH = "/services/data/v52.0/query/";
public static final String QUERY_PATH = "/services/data/v52.0/";
private final CloseableHttpClient closeableHttpClient;
private final ObjectMapper objectMapper;
private final SalesforceConfigurationProperties salesforceConfigurationProperties;
@Cacheable("contacts")
public Contact getContact(String id) throws Exception {
if (StringUtils.isEmpty(id)) {
throw new NullPointerException("id cannot be null");
}
SalesforceLoginResult salesforceLoginResult = BearerTokenUtilities.loginToSalesforce(closeableHttpClient, salesforceConfigurationProperties, objectMapper);
URIBuilder builder = new URIBuilder(salesforceLoginResult.getInstanceUrl());
builder.setPath(QUERY_PATH + "sobjects/Contact/" + id);
HttpGet get = new HttpGet(builder.build());
get.setHeader("Authorization", "Bearer " + salesforceLoginResult.getAccessToken());
HttpResponse httpResponse = closeableHttpClient.execute(get);
HttpUtils.checkResponse(httpResponse);
Contact contact = objectMapper.readValue(httpResponse.getEntity().getContent(), Contact.class);
log.debug("contact={}", contact);
return contact;
}
@Cacheable("contacts")
public List<Contact> getContacts() throws Exception {
SalesforceLoginResult salesforceLoginResult = BearerTokenUtilities.loginToSalesforce(closeableHttpClient, salesforceConfigurationProperties, objectMapper);
URIBuilder builder = new URIBuilder(salesforceLoginResult.getInstanceUrl());
builder.setPath(QUERY_PATH).setParameter("q", Contact.CONTACT_QUERY);
builder.setPath(QUERY_PATH + "query").setParameter("q", Contact.CONTACT_QUERY);
HttpGet get = new HttpGet(builder.build());
get.setHeader("Authorization", "Bearer " + salesforceLoginResult.getAccessToken());
HttpResponse httpResponse = closeableHttpClient.execute(get);
HttpUtils.checkResponse(httpResponse);
SalesforceResponse salesforceResponse = objectMapper.readValue(httpResponse.getEntity().getContent(), SalesforceResponse.class);
List<Contact> contacts = salesforceResponse.getRecords();
......@@ -48,9 +79,32 @@ public class ContactService {
return contacts;
}
@CacheEvict
public Contact updateContact(PatchUpdates patchUpdates) throws Exception {
@CacheEvict(value = "contacts", allEntries = true)
public Contact updateContact(String id, PatchUpdates patchUpdates) throws Exception {
log.debug("updateContact(id={}, patchUpdates={})", id, patchUpdates);
if (StringUtils.isEmpty(id)) {
throw new NullPointerException("id cannot be null");
}
if (MapUtils.isEmpty(patchUpdates)) {
throw new NullPointerException("patchUpdates cannot be null");
}
SalesforceLoginResult salesforceLoginResult = BearerTokenUtilities.loginToSalesforce(closeableHttpClient, salesforceConfigurationProperties, objectMapper);
URIBuilder builder = new URIBuilder(salesforceLoginResult.getInstanceUrl());
builder.setPath(QUERY_PATH + "sobjects/Contact/" + id);
HttpPatch patch = new HttpPatch(builder.build());
patch.setHeader("Authorization", "Bearer " + salesforceLoginResult.getAccessToken());
patch.setHeader("Content-type", MediaType.APPLICATION_JSON_VALUE);
patch.setEntity(new StringEntity(objectMapper.writeValueAsString(patchUpdates)));
HttpResponse httpResponse = closeableHttpClient.execute(patch);
HttpUtils.checkResponse(httpResponse);
return null;
return getContact(id);
}
}
package com.gitlab.johnjvester.salesforceintegrationservice.utils;
import org.apache.http.HttpResponse;
public final class HttpUtils {
private HttpUtils() { }
public static void checkResponse(HttpResponse httpResponse) throws Exception {
// Simple validation to make sure the response is 2xx.
if (httpResponse.getStatusLine().getStatusCode() < 200
|| httpResponse.getStatusLine().getStatusCode() > 299) {
throw new Exception(httpResponse.getStatusLine().getReasonPhrase());
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment