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 ListgetCapabilities() { 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.

No comments:
Post a Comment