Rest API Callouts from One Salesforce Org to Another
There are various ways for integrating two salesforce orgs (Salesforce to Salesforce, SOAP with enterprise or partner wsdl, REST, middleware, appexchange etc) each with its own pros and cons. In this blog I aim to show a very specific simple use case where we want to send a file from one salesforce org to another with some additional metadata using a simple REST API callout from one salesforce org to another. Of course this can be done through multiple ways but in my opinion this is the fastest way to accomplish this and for very specific cases in which a large scale integration is not required.
MockFileUploadService Connected App
FileUploadService.cls
Server
On the server side we need two things a Connected App that defines the authentication method and access policies and a simple apex rest service to accept the file upload request.
Connected App
Couple of important things to notice in the connected app is the "refresh_token" OAuth scope and the callback url from the client org. These two settings are important for setting up seamless authentication between the two orgs. The "refresh token" allows the client to keep refreshing the token and keep a session alive without user interaction and the callback url generated by the Auth. Provider on the client org provides a seamless authentication redirect.
MockFileUploadService Connected App
FileUploadService.cls
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | @RestResource(urlMapping='/fileUpload/*') global with sharing class FileUploadService { public class File { public String body; public String contentType; public String name; } public class FileContainer { public File_Metadata__c metadata; public File file; } public class FileUploadRequest { public List<FileContainer> files; } @HttpPost global static void doPost() { //json will be taken directly from RestContext FileUploadRequest payload = (FileUploadRequest)System.JSON.deserialize( RestContext.request.requestBody.tostring(), FileUploadRequest.class); List<FileContainer> fileContainers = payload.files; //Parse Metadata and files Map<String, HOG_File_Metadata__c> metadataMap = new Map<String, HOG_File_Metadata__c>(); Map<String, File> fileMap = new Map<String, File>(); for(FileContainer container : fileContainers) { System.debug('Metadata Name: ' + container.metadata.Name); metadataMap.put(container.metadata.Name, container.metadata); fileMap.put(container.metadata.Name, container.file); //clear ids container.metadata.Id = null; } //Setting state for transaction control Boolean revertTransaction = false; Savepoint sp = Database.setSavepoint(); //Insert Metadata System.debug('metadataMap.values(): ' + metadataMap.values()); List<Database.SaveResult> saveResults = Database.insert(metadataMap.values(), false); //Insert Attachments List<Attachment> attachmentsToInsert = new List<Attachment>(); for (String metadataName : metadataMap.keySet()) { HOG_File_Metadata__c metadata = metadataMap.get(metadataName); File file = fileMap.get(metadataName); attachmentsToInsert.add( new Attachment(parentId = metadata.Id, name = file.name, ContentType = file.ContentType, Body = EncodingUtil.base64Decode(file.body))); } saveResults = Database.insert(attachmentsToInsert, false); RestContext.response.statusCode = 200; for(Database.SaveResult result : saveResults) { if(!result.isSuccess()) { RestContext.response.statusCode = 500; revertTransaction = true; } } RestContext.response.responseBody = Blob.valueOf(JSON.serialize(saveResults)); if(revertTransaction) Database.rollback(sp); } } |
Client
On the client side all you need is a Named Credential configured with the consumer key and secret of the connected app on the server and an authentication provide authenticated with a valid user's credentials on the server org with access to the resources served by the REST service.
Auth. Provider
The important things to note in the Auth. Provider is the default scopes "refresh_token" this allows the auth provided to keep the session alive by refreshing the token generated and the callback URL generated (configured in the server connected app) allows for seamless authentication redirect.
MockFileUploadServiceAuth Auth. Provider
Named Credential
Important things to note in the named credential is the "Generate Authorization Header" checkbox. This will automatically generate the authentication header in a callout to the server done using the named credential.
MockFileUploadServiceCred Named Credential
Client Usage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | HttpRequest httpReq = new HttpRequest(); httpReq.setMethod('POST'); httpReq.setEndpoint('callout:MockFileUploadServiceCred/services/apexrest/fileUpload'); httpReq.setHeader('Content-Type', 'application/json'); httpReq.setBody('{"files": [' + '{' + '"metadata": {' + '"attributes": {' + '"type": "File_Metadata__c"' + '},' + '"Folder__c": "Notification",' + '"Subfolder__c": "Maintenance Reports",' + '"Content_Group__c": "Functional Business Management",' + '"Content_Type__c": "Report",' + '"Business_Function__c": "Facility Operations and Maintenance",' + '"Document_Type__c": "Report",' + '"File_Label__c": "NO - Final Report - 2018-11-12 00:00:00"' + '},' + '"file": {' + '"name": "NO - Final Report - 2018-11-12 00:00:00",' + '"contentType": "text\/plain",' + '"body": "TG9yZW0gSXBzdW0gaXMgc2ltcGx5IGR1bW15IHRleHQgb2YgdGhlIHByaW50aW5nIGFuZC' + 'B0eXBlc2V0dGluZyBpbmR1c3RyeS4gTG9yZW0gSXBzdW0gaGFzIGJlZW4gdGhlIGluZHVzdHJ5' + 'J3Mgc3RhbmRhcmQgZHVtbXkgdGV4dCBldmVyIHNpbmNlIHRoZSAxNTAwcywgd2hlbiBhbiB1bm' + 'tub3duIHByaW50ZXIgdG9vayBhIGdhbGxleSBvZiB0eXBlIGFuZCBzY3JhbWJsZWQgaXQgdG8g' + 'bWFrZSBhIHR5cGUgc3BlY2ltZW4gYm9vay4gSXQgaGFzIHN1cnZpdmVkIG5vdCBvbmx5IGZpdm' + 'UgY2VudHVyaWVzLCBidXQgYWxzbyB0aGUgbGVhcCBpbnRvIGVsZWN0cm9uaWMgdHlwZXNldHRp' + 'bmcsIHJlbWFpbmluZyBlc3NlbnRpYWxseSB1bmNoYW5nZWQuIEl0IHdhcyBwb3B1bGFyaXNlZC' + 'BpbiB0aGUgMTk2MHMgd2l0aCB0aGUgcmVsZWFzZSBvZiBMZXRyYXNldCBzaGVldHMgY29udGFp' + 'bmluZyBMb3JlbSBJcHN1bSBwYXNzYWdlcywgYW5kIG1vcmUgcmVjZW50bHkgd2l0aCBkZXNrdG' + '9wIHB1Ymxpc2hpbmcgc29mdHdhcmUgbGlrZSBBbGR1cyBQYWdlTWFrZXIgaW5jbHVkaW5nIHZl' + 'cnNpb25zIG9mIExvcmVtIElwc3VtLg=="' + '}' + '}' + ']' + '}'); Http http = new Http(); HttpResponse resp = http.send(httpReq); System.debug('resp: ' + resp); System.debug('resp.body: ' + resp.getBody()); |
Conclusion
For very specific use cases where a full fledged integration is not required a simple rest api from salesforce to salesforce can be the answer. It has helped me in multiple cases in a multitenant org environment. I hope this blog can provide you some guidance on how to setup a simple rest api connection between two salesforce orgs.
All the contents you mentioned in post is too good and can be very useful. I will keep it in mind, thanks for sharing the information keep updating, looking forward for more posts.Thanks salesforce
ReplyDeleteThe Salesforce App Builder certification is one such course that offers trainees with the knowledge needed to build applications using Salesforce training in Chennai
ReplyDeleteGreat blog and Keep doing...!
ReplyDeleteselenium with java training
Selenium with c# training
Selenium with java course
Selenium with python Training