Last active
May 16, 2024 16:21
-
-
Save ChuckJonas/fed9ecc8979ef5716c19 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
RestClient1Sync sync = new RestClient1Sync(); | |
sync.syncUserPosts(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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{} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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