Most systems need to do CRUD on some objects and also query some information. Why not have one endpoint to do it all?I have created a endpoint that can do all that using mapping between systems field and Salesforce fields. System field does not contain any underscores or Salesforce specific syntax like (__c). With that a system can send:
{“name”:”John Doe”, “taxRatePerClient”:20}
And it will be translated to:
{“Name”:”John Doe”, “Tax_Rate_Per_Client__c”:20}
Do CRUD and execute any query
global with sharing class App_Rest_Data { global with sharing class Post implements App_Rest_Dispatcher.Dispatchable { private App_Rest_Global_Request_Json requestBody; global String getURIMapping(){ return App_Rest_Constants.URI_DATA +'/{object}/{operation}'; } global void setRequestBody(App_Rest_Global_Request_Json requestBody){ this.requestBody = requestBody; } global App_Rest_Global_Json execute(Map<String, String> parameters){ App_Rest_Global_Json globalJson = new App_Rest_Global_Json(); String objectName = parameters.get('object'); String operation = parameters.get('operation'); if ('q'.equals(operation)){ globalJson.setResult(Database.query(String.valueOf(((Map<String, Object>)JSON.deserializeUntyped(requestBody.getRequest())).get('query')))); } else { Map<String,String> uiFieldNamesForObject = App_Rest_Settings.getModelByObject(objectName); App_JSON.DynamicModel dynamicModel = new App_JSON.DynamicModel(uiFieldNamesForObject); dynamicModel.setMapKeysFlag(true); Type classType = Type.forName(objectName); SObject sobjectModel = (SObject) classType.newInstance(); sobjectModel = (SObject)dynamicModel.deserialize(requestBody.getRequest(), classType); fflib_ISObjectUnitOfWork uow = App_Rest_App.UnitOfWork.newInstance(); if ('c'.equals(operation)){ uow.registerNew(sobjectModel); } else if ('u'.equals(operation)){ uow.registerDirty(sobjectModel); } else if('d'.equals(operation)){ uow.registerDeleted(sobjectModel); } uow.commitWork(); Schema.SObjectType sobjectModelType = Schema.getGlobalDescribe().get(objectName); fflib_QueryFactory queryFactory = new fflib_QueryFactory(sobjectModelType); queryFactory.selectFields(uiFieldNamesForObject.keySet()); globalJson.setResult((List<Object>) Database.query(queryFactory.toSOQL())); } return globalJson; } global override String toString(){ App_Rest_Endpoint.Path path = new App_Rest_Endpoint.Path(); path.setPath(getURIMapping()); App_Rest_Endpoint endpoint = new App_Rest_Endpoint(path); return JSON.serialize(endpoint); } } }
Testing out CRUD and query operations
@isTest private class App_Rest_Data_Test { @isTest(SeeAllData=true) static void testPost() { Test.setMock(HttpCalloutMock.class, new App_LogEntries_Api_Mock()); RestRequest req = new RestRequest(); RestResponse res = new RestResponse(); User communityUser = App_Global_Test.setupCommunityUserAndLogin(); Test.startTest(); req.requestBody = Blob.valueOf('{"firstName":"Test FirstName","lastName":"Test LastName", "email":"test@mail.com"}'); req.requestURI = '/v1/data/contact/c'; req.httpMethod = 'POST'; RestContext.request = req; RestContext.response = res; App_Rest_Dispatcher.doPost(); Test.stopTest(); System.assert(res.responseBody!=null); System.assertEquals(res.statusCode, 200); } @isTest(SeeAllData=true) static void testPatch() { Test.setMock(HttpCalloutMock.class, new App_LogEntries_Api_Mock()); RestRequest req = new RestRequest(); RestResponse res = new RestResponse(); User communityUser = App_Global_Test.setupCommunityUserAndLogin(); Test.startTest(); Account setupAccount = App_Global_Test.setupAccount(); req.requestBody = Blob.valueOf('{"id":"'+setupAccount.Id+'","name":"Test Update"}'); req.requestURI = '/v1/data/account/u'; req.httpMethod = 'POST'; RestContext.request = req; RestContext.response = res; App_Rest_Dispatcher.doPost(); Test.stopTest(); System.assert(res.responseBody!=null); System.assertEquals(res.statusCode, 200); } @isTest(SeeAllData=true) static void testDelete() { Test.setMock(HttpCalloutMock.class, new App_LogEntries_Api_Mock()); RestRequest req = new RestRequest(); RestResponse res = new RestResponse(); User communityUser = App_Global_Test.setupCommunityUserAndLogin(); Test.startTest(); Account setupAccount = App_Global_Test.setupAccount(); req.requestBody = Blob.valueOf('{"id":"'+setupAccount.Id+'","name":"Test Update"}'); req.requestURI = '/v1/data/account/d'; req.httpMethod = 'POST'; RestContext.request = req; RestContext.response = res; App_Rest_Dispatcher.doPost(); Test.stopTest(); System.assert(res.responseBody!=null); System.assertEquals(res.statusCode, 200); } @isTest(SeeAllData=true) static void testQuery() { Test.setMock(HttpCalloutMock.class, new App_LogEntries_Api_Mock()); RestRequest req = new RestRequest(); RestResponse res = new RestResponse(); User communityUser = App_Global_Test.setupCommunityUserAndLogin(); Test.startTest(); Account setupAccount = App_Global_Test.setupAccount(); req.requestBody = Blob.valueOf('{"query":"Select id from User"}'); req.requestURI = '/v1/data/account/q'; req.httpMethod = 'POST'; RestContext.request = req; RestContext.response = res; App_Rest_Dispatcher.doPost(); Test.stopTest(); System.assert(res.responseBody!=null); System.assertEquals(res.statusCode, 200); } }