If your company is already using Salesforce for Case Management, you probably want to set up a synchronization from Salesforce to CustomerGauge. That way, your Salesforce users can continue working in their system, while your CustomerGauge users will not be left out.
You can achieve this by setting up an APEX class and trigger, that will call CustomerGauge's PUT Close the Loop API, to post notes or status changes.
Disclaimer: This is a highly technical solution. Use only if you're well versed in Salesforce APEX classes and triggers.
Requirements
- Access to both your Sandbox and Enterprise Salesforce versions
- A Salesforce Administrator with basic coding skills
- A field in the Case object to hold the CG-ID of survey records
- This will need to be populated
- CustomerGauge REST API (https://support.customergauge.com/support/solutions/articles/5000883702-put-close-the-loop)
Before you start: Whitelist the CustomerGauge API Suite in Salesforce
In order to call our APIs from Salesforce, you will first need to let Salesforce know you trust us.
To do this, you'll need to whitelist our API Suite;
- Go to Setup → Remote Site Settings
- Check if "https://api.eu.customergauge.com" (change "eu" to your region) is already whitelisted
- Check if "https://auth.eu.customergauge.com" (change "eu" to your region) is already whitelisted
- If it doesn't exist yet, hit the "New Remote Site" button
- Remote Site Name: give a descriptive name for future reference
- Remote Site URL: add in the API domain applicable to your region
- Active: Make sure the checkbox is in an enabled state
Overview of APEX classes
- SyncCaseWorkflowCG: the Apex Trigger that will initiate the API request
- CGWorkflowAPI: the engine that calls the POST Workflow API
- testSyncWorkflowCG: the Test class used to test the code after changes are made
- MockHttpResponseGenerator: A helper class for unit testing that mocks the API requests
- stoprecursion: A helper class that prevents recursion
Building the Main Class
- In Salesforce → Setup, go to the "Apex Classes" page
- Hit the "New" button
- Name the class "CGWorkflowAPI"
- Add the following code
global class CGWorkflowAPI { private static String clientID = 'XXXXXXXX'; private static String clientSecret = 'XXXXXXXXXX'; public static String generatedToken {set; get;} public static String errorMessage {set; get;} public static void authenticate() { try{ String url = 'https://auth.eu.customergauge.com/oauth2/token'; String idSecret = clientID + ':'+ clientSecret; String authorization = EncodingUtil.base64Encode(Blob.valueof(idSecret)); String body = 'client_id=' + clientID + '&grant_type=client_credentials' + '&client_secret=' + clientSecret; HttpRequest objReq = new HttpRequest(); objReq.setEndpoint(url); objReq.setMethod('POST'); String authHeadParam = 'Basic ' + authorization; objReq.setHeader('Authorization', authHeadParam); objReq.setHeader('Content-Type', 'application/x-www-form-urlencoded'); objReq.setBody(body); Http objHttp = new Http(); HTTPResponse objRes = objHttp.send(objReq); JSONParser objParse = JSON.createParser(objRes.getBody()); while (objParse.nextToken() != null) { if (objParse.getCurrentToken() == JSONToken.FIELD_NAME && objParse.getText() == 'access_token') { objParse.nextToken(); generatedToken = objParse.getText(); System.debug(generatedToken); } } } catch(Exception ex) { errorMessage = ex.getMessage(); System.debug('Exception :' + ex.getMessage()); } } @future (callout=true) WebService static void syncCase(String CGID, String workflowStatus, String workflowNote, String workflowDate){ // do authentication first to get the token authenticate(); // Map Workflow Statuses to CG equivalents String wfStatus = workflowStatus; if( wfStatus == 'Closed' ) { wfStatus = 'closed'; } else if( wfStatus == 'Open' || wfStatus == 'New' ) { wfStatus = 'open'; } else { wfStatus = 'progress'; } String body = 'number_customergauge=' + CGID + '&status=' + wfStatus + '¬e=' + workflowNote + '&happened_at=' + workflowDate; //Construct HTTP request and response HttpRequest req = new HttpRequest(); HttpResponse res = new HttpResponse(); Http http = new Http(); req.setHeader('Content-Type','application/x-www-form-urlencoded'); req.setHeader('Authorization', 'Bearer ' + generatedToken); //Construct Endpoint String endpoint = 'https://api.eu.customergauge.com/v7/rest/close-loop/status'; //Set Method and Endpoint and Body req.setMethod('PUT'); req.setEndpoint(endpoint); req.setBody(body); try { //Send endpoint to CG res = http.send(req); System.debug('Success' + res.getBody()); } catch(System.CalloutException e) { System.debug('failed' + res.toString() + ' - details ' + e); } } }
- Make a few changes to the above code to make it applicable to your CG system
- Change the API Key value to your API Key
- Change the region to your region ("eu", "us", or "au)
- Add additional If/Else statements for each Case Status available in your Salesforce system, mapping them to "OPEN", "PROGRESS", or "CLOSED"
- Make sure to Save (File > Save)
Setting up the Trigger
- In Salesforce → Setup, go to the "APEX Triggers" page
- Hit the Developer Console button
- Go to File > New > Apex Trigger
- Give it the name "SyncCaseWorkflowCG"
- Select the sObject "Case"
Add the following code;
trigger SyncCaseWorkflowCG on Case (after insert, after update) { List<Case> case2Update = new List<Case>(); // SOQL Query to get applicable fields for(Case[] cases : [SELECT Id, CG_ID__c, Status, Owner.Email, Description, (SELECT Id, ParentId, CommentBody, LastModifiedDate FROM CaseComments ORDER BY LastModifiedDate DESC LIMIT 1) FROM Case WHERE id IN :Trigger.New LIMIT 1 ]){ for(Integer i=0 ; i<cases.size() ; i++){ String CaseId = String.valueof(cases[i].id); String CaseIdCG = String.valueof( cases[i].CG_ID__c ); String wfComment = String.valueof( cases[i].Description ); String CaseStatus = cases[i].Status; String CaseOwnerEmail = cases[i].Owner.Email; DateTime dt = DateTime.now(); String happened_at = dt.format('yyyy-MM-dd hh:mm:ss'); //for(CaseComment comm : cases[i].CaseComments) { // wfComment = comm.CommentBody; //} CGWorkflowAPI.syncCase( CaseIdCG, CaseStatus, wfComment, happened_at ); } } }
Make adjustments to the SOQL Query where applicable.
Make sure to Save (File > Save)
Writing the Test Code
Mock Class
- In the Developer Console, go to File > New > Apex Class
- Name it "MockHttpResponseGenerator"
Add the following code;
@isTest global class MockHttpResponseGenerator implements HttpCalloutMock { // Implement this interface method global HTTPResponse respond(HTTPRequest req) { // Optionally, only send a mock response for a specific endpoint // and method. //System.assertEquals('http://api.salesforce.com/foo/bar', req.getEndpoint()); //System.assertEquals('GET', req.getMethod()); // Create a fake response HttpResponse res = new HttpResponse(); res.setHeader('Content-Type', 'application/json'); res.setStatusCode(200); return res; } }
Make sure to Save (File > Save)
Recursion Class
- In the Developer Console, go to File > New > Apex Class
- Name it "stoprecursion"
Add the following code;
public class stoprecursion{ public static boolean flag=true; public static boolean runonce(){ if(flag){ flag=false; } else { return flag; } return true; } }
Make sure to Save (File > Save)
Test Class
- In the Developer Console, go to File > New > Apex Class
- Name it "testSyncWorkflowCG"
Add the following code;
@isTest(seeAllData=true) public class testSyncWorkflowCG { static testMethod void testCaseAPI() { if(stoprecursion.runonce()){ Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator()); Case c = new Case(CG_ID__c='1',Status='Completed',Description='Test',OwnerId = '005f4K3us3RId' ); test.startTest(); insert c; test.stopTest(); } } }
Replace the value of OwnerId to any existing Salesforce user in your Salesforce system.
Make sure to Save (File > Save)
Running Test Code
- When in testSyncWorkflowCG, hit the "Run Test" button at the top-right of the screen
- If all is OK, you should have more than 70% Code Coverage
Finishing
That's it! You should now have an automatic Workflow synchronisation from Salesforce to your CustomerGauge system.
You may need to publish your APEX classes when this is built in a Sandbox environment.