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);
	}
}

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s