Do you remember the Lightning Connect Custom Adapters and MongoDB post?
Things have changed after that post and finally the platform supports all DML statements: insert, update and delete!
To make the magic work I had to make:
- some changes on the heroku-crest MondoDB NodeJS proxy to better support for PUT/DELETE/POST methods
- some changes on the Apex side
Follow the post instructions to setup up your Heroku app.
The core changes are the support for the methods.
Open the MongoDBDataSrouceProvider.cls class:
override global List getCapabilities() { List capabilities = new List(); capabilities.add(DataSource.Capability.ROW_QUERY); capabilities.add(DataSource.Capability.SEARCH); //new capabilities added capabilities.add(DataSource.Capability.ROW_CREATE); capabilities.add(DataSource.Capability.ROW_UPDATE); capabilities.add(DataSource.Capability.ROW_DELETE); return capabilities; }
The provider has been added with more capabilities, CREATE, UPDATE and DELETE.
Let’s open the MongoDBDataSourceConnectio.cls class and look at the 2 new methods:
global override List<DataSource.UpsertResult> upsertRows(DataSource.UpsertContext context) { List<DataSource.UpsertResult> results = new List<DataSource.UpsertResult>(); List<Map<String, Object>> rows = context.rows; Http h = new Http(); for(Integer i = 0; i < rows.size(); i++){ Map<String,Object> row = rows[i]; HttpRequest request = new HttpRequest(); request.setHeader('Content-Type','application/json'); request.setTimeout(60000); Map<String,Object> invoice = new Map<String,Object>(); if(String.isBlank((String)row.get('ExternalId'))){ request.setMethod('POST'); request.setEndpoint(DB_ENDPOINT_NC); }else{ request.setMethod('PUT'); request.setEndpoint(DB_ENDPOINT_NC+'/'+row.get('ExternalId')); } invoice.put('accountid', row.get('Account')); invoice.put('contractid', row.get('Contract')); invoice.put('created', row.get('CreatedDate')); invoice.put('amount', row.get('Amount')); invoice.put('description', row.get('Description')); request.setBody(JSON.serialize(invoice)); HttpResponse response = h.send(request); List<Object> mList = (List<Object>)JSON.deserializeUntyped(response.getBody()); Map<String, Object> m = (Map<String, Object>)mList[0]; if (response.getStatusCode() == 200){ String objId = String.valueOf(m.get('_id')); if(String.isBlank(objId)){ objId = String.valueOf(row.get('ExternalId')); } results.add(DataSource.UpsertResult.success(objId)); } else { results.add(DataSource.UpsertResult.failure( String.valueOf(row.get('ExternalId')), 'The callout resulted in an error: ' + response.getStatusCode()+' - '+response.getBody())); } } return results; } global override List<DataSource.DeleteResult> deleteRows(DataSource.DeleteContext context) { List<DataSource.DeleteResult> results = new List<DataSource.DeleteResult>(); Http h = new Http(); for (String externalId : context.externalIds){ HttpRequest request = new HttpRequest(); request.setHeader('Content-Type','application/json'); request.setTimeout(60000); request.setMethod('DELETE'); request.setEndpoint(DB_ENDPOINT_NC+'/'+externalId); HttpResponse response = h.send(request); if (response.getStatusCode() == 200 || response.getStatusCode() == 201){ results.add(DataSource.DeleteResult.success(String.valueOf(externalId))); } else { results.add(DataSource.DeleteResult.failure( String.valueOf(externalId), 'The callout resulted in an error: ' + response.getStatusCode()+' - '+response.getBody())); } } return results; }
WARNING: this code is not optimized for bulk upsert/delete because it makes a callout for every record.
It’s a proove of concept, so I challenge you to bulkify the class!
How can you insert an external object provided by a Lightning Connect adapter?
The Database class has been provided with new methods:
- deleteAsync
- insertAsync
- updateAsync
These methods are used to make the calls to the remote system and actually do the work!
Database.insertAsync(new List<MongoDB_Invoice__x>{ new MongoDB_Invoice__x(Amount__c=1, Description__c ='Async Test 1'), new MongoDB_Invoice__x(Amount__c=2, Description__c ='Async Test 2') }); Database.deleteAsync([Select Id From MongoDB_Invoice__x Where Description__c = 'Async Test 1']);
Every method has an alternative method that provides a callback class, which allows to make further actions after the records are upserted/deleted.
For instance, the asyncUpdate has an optional second parameter of type Database.AsyncSaveCallback that can be created to execute some logic after a specific record is done (the class is called every time a record is updated).
Every asyncDML method returns a List of Database.DeleteResult or Database.SaveResult that contains a link to the asynchrounous operation that can be retrieved by calling the Database.getAsyncLocator(result) method and passing the value to the Database.getAsyncSaveResult(asyncLocator) or Database.getAsyncDeleteResult(asyncLocator): this way you can get the status of the asynchronous operation.