Skip to content

Instantly share code, notes, and snippets.

@ChuckJonas
Last active October 31, 2019 02:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ChuckJonas/fed9ecc8979ef5716c19 to your computer and use it in GitHub Desktop.
Save ChuckJonas/fed9ecc8979ef5716c19 to your computer and use it in GitHub Desktop.
RestClient1Sync sync = new RestClient1Sync();
sync.syncUserPosts();
//Author: Charlie Jonas (charlie@callawaycloudconsulting.com)
// Description: Class is use for creating mock http callouts in test methods.
// See http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_restful_http_testing_httpcalloutmock.htm
// for info on how to use class.
@isTest
global class MockHttpResponseGenerator implements HttpCalloutMock {
//require properites
private Map<String,String> responseMap = new Map<String,String>();
//optional
public String method { get; set;}
//optional
public String contentType { get; set;}
//defaults to xml
{contentType = 'text/xml';}
//optional
public Integer statusCode {get; set;}
//defaults to success
{statusCode = 200;}
//optional
public Boolean ignoreQuery {get; set;}
//defaults to success
{ignoreQuery = true;}
public MockHttpResponseGenerator(){}
public MockHttpResponseGenerator(String endpoint, String response){
addResp(endpoint,response);
}
//add an additional endpoint response pairs
public void addResponse(String endpoint, String response){
addResp(endpoint,response);
}
// Implement this interface method
global HTTPResponse respond(HTTPRequest req) {
// Optionally, only send a mock response for a specific endpoint
// and method.
String endpoint = req.getEndpoint();
if(ignoreQuery){
endpoint = getBaseUrl(endpoint);
}
String response = responseMap.get(endpoint);
System.assertNotEquals(response, null);
if(method != null){
System.assertEquals(method, req.getMethod());
}
// Create a fake response
HttpResponse res = new HttpResponse();
res.setHeader('Content-Type', contentType);
res.setBody(response);
res.setStatusCode(statusCode);
return res;
}
private void addResp(String endpoint, String response){
if(ignoreQuery){
endpoint = getBaseUrl(endpoint);
}
responseMap.put(endpoint, response);
}
private String getBaseUrl(String endpoint){
Url baseUrl = new URL(endpoint);
return baseUrl.getHost() + baseUrl.getPath();
}
}
//Client for retrieving Authors and Posts from fake API
public class RestClient1 {
//---CONSTANTS---
private static final String HOST = 'http://jsonplaceholder.typicode.com';
//map that defines eligible resources and their endpoints based on class type
private static final Map<Type,String> RESOURCE_MAP = new Map<Type,String>{
List<RestClient1DTO.User>.class => HOST + '/users',
List<RestClient1DTO.Post>.class => HOST + '/posts'
};
//--- PROPERTIES ---
public List<RestClient1DTO.User> users {get; private set;}
public List<RestClient1DTO.Post> posts {get; private set;}
//--- PUBLICS ---
public void retrieveUsers(){
users = (List<RestClient1DTO.User>) get(List<RestClient1DTO.User>.class);
}
public void retrievePosts(){
posts = (List<RestClient1DTO.Post>) get(List<RestClient1DTO.Post>.class);
}
//--- PRIVATES ---
private Object get(Type type){
if(!RESOURCE_MAP.containsKey(type)){
throw new APIException('Type not mapped to resource endpoint');
}
String endpoint = RESOURCE_MAP.get(type);
HttpRequest req = new HttpRequest();
req.setHeader('Content-Type', 'application/json');
req.setEndpoint(endpoint);
req.setMethod('GET');
req.setTimeout(12000);
Http http = new Http();
HttpResponse response = http.send(req);
return JSON.deserialize(response.getBody(), type);
}
public class APIException extends Exception{}
}
//Data Transfer class for example API. Properties Generated from JSON using http://json2csharp.com/
// Used for deseralization/serialization and mapping to SOBJECT
public class RestClient1DTO {
//ROOT Objects
public class Post
{
public Integer userId { get; set; }
public Integer id { get; set; }
public String title { get; set; }
public String body { get; set; }
public Post__c toSObject(){
Contact author = new Contact(
Author_Id__c = String.valueOf(userId)
);
return new Post__c(
Name = String.valueOf(id),
Author__r = author,
Title__c = title,
Body__c = body
);
}
}
public class User
{
public Integer id { get; set; }
public String name { get; set; }
public String username { get; set; }
public String email { get; set; }
public Address address { get; set; }
public String phone { get; set; }
public String website { get; set; }
public Company company { get; set; }
public Contact toSObject(){
return new Contact(
Author_Id__c = String.valueOf(id),
LastName = name,
Email = email,
Phone = phone,
MailingStreet = address.street,
MailingCity = address.city,
MailingPostalCode = address.zipcode
);
}
}
//Child Objects for User
public class Geo
{
public String lat { get; set; }
public String lng { get; set; }
}
public class Address
{
public String street { get; set; }
public String suite { get; set; }
public String city { get; set; }
public String zipcode { get; set; }
public Geo geo { get; set; }
}
public class Company
{
public String name { get; set; }
public String catchPhrase { get; set; }
public String bs { get; set; }
}
}
//Calls Client To Sync Authors to accounts and Posts to Authors
//I was torn between using private instances VS passing by param here...
//Ended up going with private instances
public with sharing class RestClient1Sync {
private RestClient1 client;
private Map<String,Id> accountWebsiteMap;
private Map<Integer,Contact> eligibleAuthors;
public RestClient1Sync(){
client = new RestClient1();
// retrieve from API
client.retrieveUsers();
client.retrievePosts();
initAccountWebsiteMap();
}
public void syncUserPosts(){
syncAuthors();
syncPosts();
}
private void initAccountWebsiteMap(){
List<String> userWebsites = new List<String>();
for(RestClient1DTO.User u : client.users){
userWebsites.add(u.website);
}
accountWebsiteMap = new Map<String,Id>();
for(Account acc : [SELECT Website FROM Account WHERE Website IN :userWebsites]){
accountWebsiteMap.put(acc.Website, acc.Id);
}
}
//upserts eligible authors (must have matching account)
private void syncAuthors(){
eligibleAuthors = new Map<Integer,Contact>();
for(RestClient1DTO.User u : client.users){
if(accountWebsiteMap.containsKey(u.website)){
Contact c = u.toSObject();
c.AccountId = accountWebsiteMap.get(u.website);
eligibleAuthors.put(u.Id, c);
}
}
upsert eligibleAuthors.values() Author_Id__c;
}
//upserts eligible posts (must have matching author)
private void syncPosts(){
List<Post__c> eligiblePosts = new List<Post__c>();
for(RestClient1DTO.Post p : client.posts){
if(eligibleAuthors.containsKey(p.userId)){
eligiblePosts.add(p.toSObject());
}
}
upsert eligiblePosts Name;
}
}
@isTest
private class RestClient1Test {
private static final String ELIGIBLE_WEBSITE = 'test.com';
private static final Integer ELIGIBLE_USERID = 1;
private static final Integer INELIGIBLE_USERID = 2;
@isTest static void test_userMapping() {
Account a = new Account(Name = 'test account',
Website = ELIGIBLE_WEBSITE);
insert a;
List<RestClient1DTO.User> users = new List<RestClient1DTO.User>();
RestClient1DTO.Address address = new RestClient1DTO.Address();
address.street = '123 aspen dr';
address.city = 'Jackson';
address.zipcode = '12345';
//user that will map
RestClient1DTO.User u1 = new RestClient1DTO.User();
u1.Id = ELIGIBLE_USERID;
u1.name = 'testing1';
u1.email = 'testing@gmail.com';
u1.phone = '123-456-7890';
u1.website = ELIGIBLE_WEBSITE;
u1.address = address;
users.add(u1);
//user that wont map
RestClient1DTO.User u2 = new RestClient1DTO.User();
u2.Id = INELIGIBLE_USERID;
u2.name = 'testing2';
u2.email = 'testing@gmail.com';
u2.phone = '123-456-7890';
u2.website = 'not.' + ELIGIBLE_WEBSITE;
u1.address = address;
users.add(u2);
String userMockResponse = Json.serialize(users);
String userEndpoint = RestClient1.RESOURCE_MAP.get(List<RestClient1DTO.User>.class);
String postsEndpoint = RestClient1.RESOURCE_MAP.get(List<RestClient1DTO.Post>.class);
MockHttpResponseGenerator generator = new MockHttpResponseGenerator(
userEndpoint, userMockResponse
);
//make posts list empty
generator.addResponse(postsEndpoint, '[]');
Test.startTest();
Test.setMock(HttpCalloutMock.class, generator);
RestClient1Sync sync = new RestClient1Sync();
sync.syncUserPosts();
Test.stopTest();
List<Contact> contacts = [SELECT Name, Email, Phone, Author_Id__c FROM Contact];
System.assertEquals(1, contacts.size(), 'Only 1 contact should have been added');
System.assertEquals(u1.email, contacts[0].Email, 'Email Should be Equal');
System.assertEquals(u1.phone, contacts[0].Phone, 'Phone Should be Equal');
System.assertEquals(String.valueOf(u1.Id), contacts[0].Author_Id__c, 'Author Id Should be Equal');
}
@isTest static void test_postMapping() {
Account a = new Account(Name = 'test account',
Website = ELIGIBLE_WEBSITE);
insert a;
List<RestClient1DTO.User> users = new List<RestClient1DTO.User>();
RestClient1DTO.Address address = new RestClient1DTO.Address();
address.street = '123 aspen dr';
address.city = 'Jackson';
address.zipcode = '12345';
//user that will map
RestClient1DTO.User u1 = new RestClient1DTO.User();
u1.Id = ELIGIBLE_USERID;
u1.name = 'testing1';
u1.email = 'testing@gmail.com';
u1.phone = '123-456-7890';
u1.website = ELIGIBLE_WEBSITE;
u1.address = address;
users.add(u1);
String userMockResponse = Json.serialize(users);
List<RestClient1DTO.Post> postsObjects = new List<RestClient1DTO.Post>();
RestClient1DTO.Post p1 = new RestClient1DTO.Post();
p1.id = 1;
p1.title = 'testign';
p1.userId = ELIGIBLE_USERID;
p1.body = 'asbvaijsdais';
postsObjects.add(p1);
//this one should not sync
RestClient1DTO.Post p2 = new RestClient1DTO.Post();
p2.id = 2;
p2.title = 'testign';
p2.userId = ELIGIBLE_USERID + 10;
p2.body = 'asbvaijsdais';
postsObjects.add(p2);
String postMockResponse = Json.serialize(postsObjects);
String userEndpoint = RestClient1.RESOURCE_MAP.get(List<RestClient1DTO.User>.class);
String postsEndpoint = RestClient1.RESOURCE_MAP.get(List<RestClient1DTO.Post>.class);
MockHttpResponseGenerator generator = new MockHttpResponseGenerator(
userEndpoint, userMockResponse
);
//make posts list empty
generator.addResponse(postsEndpoint, postMockResponse);
Test.startTest();
Test.setMock(HttpCalloutMock.class, generator);
RestClient1Sync sync = new RestClient1Sync();
sync.syncUserPosts();
Test.stopTest();
List<Post__c> posts = [SELECT Name, Author__c, Title__c, Body__c FROM Post__c];
System.assertEquals(1, posts.size(), 'Only 1 Post should have been added');
System.assertEquals(String.valueOf(p1.id), posts[0].Name, 'Name Should equal Id');
System.assertEquals(p1.title, posts[0].Title__c, 'Title Should be Equal');
System.assertEquals(p1.body, posts[0].Body__c, 'Body Should be Equal');
Contact author = [SELECT Id FROM Contact WHERE Author_Id__c = : String.valueOf(ELIGIBLE_USERID)];
System.assertEquals(author.Id, posts[0].Author__c, 'Body Should be Equal');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment