Apex One Endpoint to Rule them all
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); } }