2 things that surprised us while developing Attachment Exchange Interface for Salesforce instances with Mulesoft.
Build interface
for synchronization of Cases with attachments between two Salesforce instances.
Main assumption of the implementation:
- Attchaments
linked directly to case and also attachments from emails are process
separately. Exchange of Attachments should be triggered by successful transfer
of Case.
- So
generally first Case exange flow is process
and when it is successfully done, Attachments exchange flow is triggered.
Today we are
focused on Attachment part. Implementation for the first sight seems to be easy.
All we need to do is to query needed Attachment data basing on Case Id from one
Salesforce instance and insert it to second Salesforce instead.
Unexpected issue:
The main problem was proper approach for querying Attachments data. As we know the best practice for that is to use for that Content Version object. Because we need to retrieve attachments that are linked directly with Case as well as attachments from linked to Case Email Messages, we decided to use Salesforce Composite connector (Execute Composite Request) with this code in Composite Request body:
%dw 2.0 output application/json --- { "allOrNone": false, "compositeRequest":[ { "method": "GET", "referenceId": "getCase", "url": "/services/data/v49.0/query/?q=SELECT+Id+FROM+Case+WHERE+ExternalCaseNumber__c='"++ vars.caseId ++ "'" }, { "method": "GET", "referenceId": "getCaseAttachments", "url": "/services/data/v49.0/query/?q=SELECT+ContentDocumentId,VersionData,Title,FileType,FileExtension,FirstPublishLocationId,ContentSize,Id+FROM+ContentVersion+WHERE+FirstPublishLocationId= '" ++ "@{getCase.records[0].Id}" ++ "'" }, { "method": "GET", "referenceId": "getEmailsAttachments", "url": "/services/data/v46.0/query/?q=SELECT+ContentDocumentId,VersionData,Title,FileType,FileExtension,FirstPublishLocationId,ContentSize,Id+FROM+ContentVersion+WHERE+FirstPublishLocationId+IN+(SELECT+Id+FROM+EmailMessage+WHERE+ParentId= '" ++ "@{getCase.records[0].Id}" ++ "')" } ] }
We were
more than happy that with one request we are able to get all needed data for transferring
them. But after tests we have realized that with this approach we are getting
only Attachments that were created by our User. It was our first surprise.
Solution:
To get all Attachments from Case we need to first get all ContentDocumentId using Content Document Link object. Content Document Link object represents the link between a Salesforce CRM Content document or Salesforce file and where it's shared.
%dw 2.0 output application/json --- { "allOrNone": false, "compositeRequest":[ { "method": "GET", "referenceId": "getCase", "url": "/services/data/v49.0/query/?q=SELECT+Id+FROM+Case+WHERE+ExternalCaseNumber__c='"++ vars.caseid ++ "'" }, { "method": "GET", "referenceId": "getCaseAttachments", "url": "/services/data/v49.0/query/?q=SELECT+ContentDocumentId+FROM+ContentDocumentLink+WHERE+LinkedEntityId='" ++ "@{getCase.records[0].Id}" ++ "'" }, { "method": "GET", "referenceId": "getEmailsAttachments", "url": "/services/data/v49.0/query/?q=SELECT+ContentDocumentId+FROM+ContentDocumentLink+WHERE+LinkedEntityId+IN+(SELECT+Id+FROM+EmailMessage+WHERE+ParentId='" ++ "@{getCase.records[0].Id}" ++ "')" } ] }
With this approach we were able to retrieve all Attachments linked to Case.
In our design we use VM module to publish every Attachment's ContentDocumentId value and process them one by one in asynchronous way. After that we have second Composite request to get Content Version data for specific ContentDocumentId:
{ "allOrNone": false, "compositeRequest":[ { "method": "GET", "referenceId": "getAttachmentData", "url": "/services/data/v49.0/query/?q=SELECT+ContentDocumentId,VersionData,Title,FileType,FileExtension,FirstPublishLocationId,ContentSize,Id+FROM+ContentVersion+WHERE+ContentDocumentId ='"++ vars.contentDocumentId ++ "'" } ] }
After investigation we
have realised that VersionData is returned as sObject Blob Retrieve resource. We
have made research and it is bevaiors of REST Salesforce API which composite
connector is using.
Second surprise!
With Salesforce Composite
Request connector we are using SOAP API. So in the end we have used standard Salesforce
Query Connector which use SOAP API:
WHERE: :contentDocumentId is bind variable to vars.contentDocumentId.
SELECT ContentDocumentId,VersionData,Title,FileType,FileExtension,FirstPublishLocationId,ContentSize,Id FROM ContentVersion WHERE ContentDocumentId = ':contentDocumentId'
Comments
Post a Comment