-
Notifications
You must be signed in to change notification settings - Fork 1
Feedback #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feedback
Are you sure you want to change the base?
Feedback #1
Changes from all commits
585f063
cdc6255
049c3ab
9317fca
439e888
39b9b61
4f92aa0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /** | ||
| * Key Behaviors: | ||
| * 1. When a new Contact is inserted and doesn't have a value for the DummyJSON_Id__c field, the trigger generates a random number between 0 and 100 for it. | ||
| * 2. Upon insertion, if the generated or provided DummyJSON_Id__c value is less than or equal to 100, the trigger initiates the getDummyJSONUserFromId API call. | ||
| * 3. If a Contact record is updated and the DummyJSON_Id__c value is greater than 100, the trigger initiates the postCreateDummyJSONUser API call. | ||
| * | ||
| * Best Practices for Callouts in Triggers: | ||
| * | ||
| * 1. Avoid Direct Callouts: Triggers do not support direct HTTP callouts. Instead, use asynchronous methods like @future or Queueable to make the callout. | ||
| * 2. Bulkify Logic: Ensure that the trigger logic is bulkified so that it can handle multiple records efficiently without hitting governor limits. | ||
| * 3. Avoid Recursive Triggers: Ensure that the callout logic doesn't result in changes that re-invoke the same trigger, causing a recursive loop. | ||
| */ | ||
|
|
||
| public with sharing class ContactTriggerHandler extends TriggerHandler { | ||
|
|
||
| private List<Contact> newConts; | ||
|
|
||
| /** | ||
| * Constructor. Set instance variables. | ||
| */ | ||
| public ContactTriggerHandler() { | ||
| this.newConts = (List<Contact>) Trigger.new; | ||
| } | ||
|
|
||
| /** | ||
| * Before Insert method. | ||
| * | ||
| * When a contact is inserted | ||
| * if DummyJSON_Id__c is null, generate a random number between 0 and 100 and set this as the contact's DummyJSON_Id__c value | ||
| * if DummyJSON_Id__c is less than or equal to 100, call the getDummyJSONUserFromId API | ||
| */ | ||
| public override void beforeInsert() { | ||
| List<String> dummyJSONIdList = new List<String>(); | ||
| for (Contact cont : newConts) { | ||
| if (cont.DummyJSON_Id__c == null) { | ||
| cont.DummyJSON_Id__c = String.valueOf(Math.round(Math.random() * 100)); | ||
| } | ||
| if (Integer.valueOf(cont.DummyJSON_Id__c) <= 100 && !System.isFuture()) { | ||
| DummyJSONCallout.getDummyJSONUserFromId(cont.DummyJSON_Id__c); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * After Update method. | ||
| * | ||
| * When a contact is updated | ||
| * if DummyJSON_Id__c is greater than 100, call the postCreateDummyJSONUser API | ||
| */ | ||
| public override void afterUpdate() { | ||
| for (Contact cont : newConts) { | ||
| if (Integer.valueOf(cont.DummyJSON_Id__c) > 100 && !System.isFuture()) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also consider adding an additional validation on the DummyJSON_Last_Updated__c field. |
||
| DummyJSONCallout.postCreateDummyJSONUser(cont.Id); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
| <apiVersion>59.0</apiVersion> | ||
| <status>Active</status> | ||
| </ApexClass> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ | |
| * | ||
| * For more detailed information on HTTP callouts in Apex, refer to the official Salesforce documentation: | ||
| * https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_classes_restful_http_httprequest.htm | ||
| * | ||
| * Implemented by Oxana Suvorova | ||
| */ | ||
|
|
||
| public with sharing class DummyJSONCallout { | ||
|
|
@@ -33,19 +35,26 @@ public with sharing class DummyJSONCallout { | |
| * | ||
| * @param dummyUserId The ID of the user in the external system to fetch data for. | ||
| */ | ||
|
|
||
| @future(callout = true) | ||
| public static void getDummyJSONUserFromId(String dummyUserId) { | ||
| // Create HTTP request to send. | ||
|
|
||
| HttpRequest request = new HttpRequest(); | ||
| // Set the endpoint URL. Use direct URL or for best practices use Named Credential. | ||
|
|
||
| request.setEndpoint('callout:DummyJsonUser' + '/' + dummyUserId); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| // Set the HTTP method to GET. | ||
|
|
||
| request.setMethod('GET'); | ||
| // Send the HTTP request and get the response. | ||
|
|
||
| // If the HTTP response code is successful, parse the JSON response and update the contact. | ||
| Contact cont = null; | ||
| upsert cont DummyJSON_Id__c; //insert/update from the JSON response using the external id (dummyUserId) | ||
| try { | ||
| Http http = new Http(); | ||
| HttpResponse response = http.send(request); | ||
| // If the HTTP response code is successful, parse the JSON response and update the contact. | ||
| if (response.getStatusCode() == 200) { | ||
| Contact cont = createContactFromJson(response.getBody()); | ||
| upsert cont DummyJSON_Id__c; // insert/update from the JSON response using the external id (dummyUserId) | ||
| } | ||
| } catch (Exception ex) { | ||
| System.debug('Error: ' + ex.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| /* | ||
|
|
@@ -66,16 +75,27 @@ public with sharing class DummyJSONCallout { | |
| @TestVisible // Allows test class to see this method. Since it is private, it would not be visible otherwise. | ||
| private static Contact createContactFromJson(String jsonResponse) { | ||
| // Deserialize the JSON string into map of primitive data types. | ||
|
|
||
| Map<String, Object> userDataMap = (Map<String, Object>) JSON.deserializeUntyped(jsonResponse); | ||
| // Create a new contact from the JSON response. | ||
|
|
||
| Contact cont = new Contact(); | ||
| // Set the contact fields from the JSON response. | ||
|
|
||
| cont.FirstName = (String) userDataMap.get('firstName'); | ||
| cont.LastName = (String) userDataMap.get('lastName'); | ||
| cont.Email = (String) userDataMap.get('email'); | ||
| cont.Phone = (String) userDataMap.get('phone'); | ||
| cont.Birthdate = Date.valueOf((String) userDataMap.get('birthDate')); | ||
| cont.DummyJSON_Id__c = String.valueOf(userDataMap.get('id')); | ||
|
|
||
| // Deserialize the address from the JSON response. | ||
|
|
||
| Map<String, Object> addressMap = (Map<String, Object>) userDataMap.get('address'); | ||
| // Set the address fields on the contact. | ||
| cont.MailingStreet = (String) addressMap.get('address'); | ||
| cont.MailingCity = (String) addressMap.get('city'); | ||
| cont.MailingPostalCode = (String) addressMap.get('postalCode'); | ||
| cont.MailingState = (String) addressMap.get('state'); | ||
| cont.MailingCountry = (String) addressMap.get('country'); | ||
|
|
||
| return null; | ||
| return cont; | ||
| } | ||
|
|
||
| /* | ||
|
|
@@ -91,19 +111,31 @@ public with sharing class DummyJSONCallout { | |
| * | ||
| * @param contactId The Salesforce Contact ID used to generate the JSON payload for the external system. | ||
| */ | ||
|
|
||
| @future(callout = true) | ||
| public static void postCreateDummyJSONUser(String contactId) { | ||
| // Create HTTP request to send. | ||
|
|
||
| HttpRequest request = new HttpRequest(); | ||
| // Set the endpoint URL. Use direct URL or for best practices use Named Credential. | ||
|
|
||
| request.setEndpoint('callout:DummyJsonUser/add'); | ||
| // Set the HTTP method to POST. | ||
|
|
||
| request.setMethod('POST'); | ||
| // Set the body using generateDummyJsonUserPayload method. | ||
|
|
||
| // String jsonBody = generateDummyJsonUserPayload(); | ||
| // if (jsonBody.length() > 0) | ||
| request.setBody(generateDummyJsonUserPayload(contactId)); | ||
|
|
||
| // Send the HTTP request and get the response. | ||
|
|
||
| Http http = new Http(); | ||
| HttpResponse response = http.send(request); | ||
| // If the HTTP response code is successful, update the contact. | ||
| if (response.getStatusCode() >= 200 || response.getStatusCode() <= 299) { | ||
| Contact cont = [ | ||
| SELECT Id, DummyJSON_Last_Updated__c | ||
| FROM Contact | ||
| WHERE Id = :contactId]; | ||
| cont.DummyJSON_Last_Updated__c = Datetime.now(); | ||
| update cont; | ||
| } | ||
| } | ||
|
|
||
| /* | ||
|
|
@@ -124,13 +156,23 @@ public with sharing class DummyJSONCallout { | |
| @TestVisible // Allows test class to see this method. Since it is private, it would not be visible otherwise. | ||
| private static String generateDummyJsonUserPayload(String contactId) { | ||
| // Query the contact to get the field values to generate the JSON payload. | ||
|
|
||
| Contact cont = [ | ||
| SELECT Id, FirstName, LastName, Email, Phone, DummyJSON_Id__c | ||
| FROM Contact | ||
| WHERE Id = :contactId | ||
| ]; | ||
| // Create a map of the field values. | ||
|
|
||
| Map<String, Object> valuesMap = new Map<String, Object>(); | ||
| valuesMap.put('salesforceId', contactId); | ||
| valuesMap.put('firstName', String.isNotBlank(cont.FirstName) ? cont.FirstName : 'unknown'); | ||
| valuesMap.put('lastName', String.isNotBlank(cont.LastName) ? cont.LastName : 'unknown'); | ||
| valuesMap.put('email', String.isNotBlank(cont.Email) ? cont.Email : 'unknown'); | ||
| valuesMap.put('phone', String.isNotBlank(cont.Phone) ? cont.Phone : 'unknown'); | ||
|
Comment on lines
+167
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice work catching these values if they are empty. |
||
| // Serialize the map into a JSON string. | ||
| String json = JSON.serialize(valuesMap); | ||
| // Make sure to check that required contacts fields have a value. Default the value to unknown if it does not exists. | ||
| // Integration data can change over time. It is a best practice to add safeguards/validation to ensure the integration does not break. | ||
|
|
||
| return null; | ||
| return json; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
| <apiVersion>57.0</apiVersion> | ||
| <apiVersion>60.0</apiVersion> | ||
| <status>Active</status> | ||
| </ApexClass> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,7 @@ | ||
| /* | ||
| * Update the ContactTrigger.trigger to do a callout to the DummyJSONCallout class to retrieve/send user data from the Dummy JSON API. | ||
| * Implement the DummyJSONCallout class to handle the callouts to the Dummy JSON API. | ||
| */ | ||
| @IsTest | ||
| public with sharing class DummyJSONCalloutTest { | ||
| // This test method will test the getDummyJSONUserFromId future method with a mock HTTP response. | ||
|
|
@@ -129,28 +133,35 @@ public with sharing class DummyJSONCalloutTest { | |
|
|
||
| @IsTest | ||
| static void testContactInsert() { | ||
| Contact cont = new Contact(FirstName = 'Test', LastName = 'User'); | ||
| //Create 100 contact | ||
| List<Contact> contacts = new List<Contact>(); | ||
| for (Integer i = 0; i < 1; i++) { | ||
| contacts.add(new Contact(FirstName = 'Test', LastName = 'User' + i)); | ||
| } | ||
|
|
||
| // Register the mock callout class | ||
| Test.setMock(HttpCalloutMock.class, new DummyJSONCalloutMockGenerator()); | ||
|
|
||
| // As this is a future method, we need to enclose it in Test.startTest() and Test.stopTest() to ensure it's executed in the test context. | ||
| Test.startTest(); | ||
| insert cont; | ||
| insert contacts; | ||
| Test.stopTest(); | ||
|
|
||
| // After the stopTest, the future method will have run. Now we can check if the contact was created correctly. | ||
| cont = [ | ||
| SELECT Email, Phone, Birthdate, MailingStreet, MailingCity, MailingPostalCode, MailingState, MailingCountry | ||
| contacts = [ | ||
| SELECT DummyJSON_Id__c, Email, Phone, Birthdate, MailingStreet, MailingCity, MailingPostalCode, MailingState, MailingCountry | ||
| FROM Contact | ||
| WHERE Id = :cont.Id | ||
| WHERE Id IN :contacts AND DummyJSON_Id__c != null | ||
| ]; | ||
| System.assertEquals('[email protected]', cont.Email, 'Email does not match your value: ' + cont.Email); | ||
| System.assertEquals('+123456789', cont.Phone, 'Phone does not match your value: ' + cont.Phone); | ||
| System.assertEquals( | ||
| cont.Birthdate, | ||
| Date.valueOf('1990-01-01'), | ||
| 'Birthdate does not match your value: ' + cont.Birthdate | ||
| ); | ||
| Assert.isTrue(!contacts.isEmpty(), 'No contacts were created'); | ||
|
|
||
| for (Contact cont : contacts) { | ||
| Assert.isTrue(cont.DummyJSON_Id__c.isNumeric(), 'DummyJSON_Id is not numeric string'); | ||
| Integer dummyJSONId = Integer.valueOf(cont.DummyJSON_Id__c); | ||
| Assert.isTrue( | ||
| dummyJSONId >=0 && dummyJSONId <= 100, | ||
| 'Expected range of DummyJSON_Id is [0-100]' | ||
| ); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍