Skip to content

Document Transfer Message Processing

John Tanner edited this page Apr 17, 2020 · 8 revisions

In the Document Transfer scenario, Vault Spark allows developers to use the Vault Java SDK to send and receive messages from one vault (the source) to another (the target), whilst transferring a document, and it's source file, renditions, attachments and versions.

Note: Whilst this example copies documents between Vaults, it is also possible to copy documents within the same Vault, using a Connection that points back to the same Vault.

This page covers how to receive the messages in the target vault and process then using SDK Document Transfer support.

The Vault Java SDK QueueService, RecordService, HttpService, 'DocumentServiceandQueryService` interfaces are used to implement this functionality.

For full details on the interfaces and methods used, please review the Javadocs.

Details of how to create the Spark Messages in the source vault are covered in the page Document Transfer Source Messages.

Code Logic

Once the vSDKDocCopyDocumentAction action adds a Message to the outbound queue, Spark sends the Message to the target vault's inbound queue where it is processed by a MessageProcessor Vault Java SDK class.

The MessageProcessor then processes the messages by retrieving the Document details, making an HttpService callouts to the source vault to query for more information and then creating a document in the target vault.

In this example, vSDKDocCopyMessageProcessor contains the processCopyDocuments method to process the inbound queue messages.

Key Concepts - Target Vault

  • There are 3 classes, the processor class and two User Defined Classes (UDC):
    • The vSDKDocCopyMessageProcessor - processes Message records received in the inbound queue - vsdk_doc_copy_in_queue__c.
    • The vSDKSparkHelper provides helper methods to process objects from the Integration Transaction object on the Source Vault.
    • The vSDKDocCopy process the document transfer.
  • As a Message comes in, if an existing Document exists in the target vault it will be updated, otherwise a new record will be created.
    • Run a QueryService query to check for unprocessed records in the Integration Transaction object on the Source Vault.
    • Use the DocumentService to get and copy the document(s) from the
      • Create a map of incoming messages
      • Check to make sure none of the document(s) already exist in the Target Vault
      • The source document IDs are added to the query
      • Use the HttpService to get the document details (e.g. DocFields)
      • Use the DocumentService to copy the document from the source vault to the target vault
        • DocumentSourceFileReference used to establish a connection to the source vaults file and version files to be copied
        • DocumentRenditionFileReference used to establish a connection to the source vaults viewable renditions to be copied
        • DocumentService.createDocuments to copy the document including the source and renditions
        • DocumentService.migrateDocumentVersions to copy the document versions
        • DocumentService.createAttachments used to copy any document attachments

Message Processor and Inbound Queue

The messages received from the queue have to be identified and processed according to custom logic. In this sample project, we only want to process messages that are not empty and have an document type of vSDK Spark Copy Document.

If the document has the vSDK Spark Copy Document document type on the source vault the Approve Document user action will be available. If the Approve Document user action is used the message is pushed to the target vault inbound queue and the processCopyDocuments method that processes the document and performs custom logic that is specific to a new vsdk_doc_copy_in_queue__c message.

@MessageProcessorInfo()
public class vSDKDocCopyMessageProcessor implements MessageProcessor {

    /**
     * Main Spark v2v MessaageProcessor. It determines the Integration Point
     * related to the message and call the appropriate Processing code
     * to copy documents between two Vaults
     */

	public void execute(MessageContext context) {

        //Processes a message from the queue which aren't blank
    	Message incomingMessage = context.getMessage();

        // Here we want to process messages specific to the different integration
        // points. These message attributes are set in the document action
        // "vSdkDocCopyDocumentAction" in the source Vault
        String integrationPointApiName =
                incomingMessage.getAttribute("integration_point",
                                             MessageAttributeValueType.STRING);

        switch(integrationPointApiName) {
            case "receive_copy_document__c":
                // Here we want to process the messages associated with the inbound
                // Integration Point "Receive Copy Document". This is done in the method
                // "processCopyDocuments"
                processCopyDocuments(incomingMessage, context);
                break;
                // Add further case statements here if you wish to handle further
                // integration points
        }
    }

Processing the Message Items

Once the code has entered into the processCopyDocuments method, the message items are parsed through and then the source document IDs are used to find existing Documents in the target vault.

The code uses the vSDKSparkHelper.getUnprocessedObjects() method to get all unprocessed items from the Source Vault Integration Transaction object using a query against the records in that object. The query uses the Status field in the Integration Transaction records to get all pending_c records.

  • If records are found, add them to a List<String> copyDocsList that is used to update the target.
public static void processCopyDocuments(Message message,
                                        MessageContext context) {

        LogService logService = ServiceLocator.locate(LogService.class);
        List<String> copyDocsList = VaultCollections.newList();
        String sourceVaultId = context.getRemoteVaultId();
        String connectionId = context.getConnectionId();
        String integrationPointApiName =
                message.getAttribute("integration_point", MessageAttributeValueType.STRING);

        StringBuilder logMessage = new StringBuilder();
        logMessage.append("Processing integrationPointApiName: ");
        logMessage.append(integrationPointApiName);
        logService.info(logMessage.toString());

        String connectionName = "vsdk_doc_copy_connection";

        // Check for documents which haven't been copied
        // This uses a callback to the source vault for the document
        // then add them to the incoming message list for processing
        List<String> unprocessedDocIdsList = vSDKSparkHelper.getUnprocessedObjects(
                connectionName,
                "documents",
                integrationPointApiName,
                true);
        copyDocsList.addAll(unprocessedDocIdsList);

        // Copy the documents from the Source Vault
        vSDKDocCopy.process(connectionName,
                integrationPointApiName,
                unprocessedDocIdsList,
                true,
                true,
                true);
	}
    ...
}

Get Unprocessed Documents

The vSDKSparkHelper.getUnprocessedObjects() method returns a list of objects which haven't been migrated to the target vault yet. This uses a callback to the source vault to check for records in the integration_transaction__c object for the object/integration point, which have a processed status of pending and a last modified date greater than 10 seconds before. This wait is to allow for the job already being processed on a separate thread.

  StringBuilder logMessage = new StringBuilder();
  logMessage.append("Unprocessed Callback query: ").append(query.toString());
  logService.info(logMessage.toString());

  //This is a vault to vault Http Request to the source connection
  HttpService httpService = ServiceLocator.locate(HttpService.class);
  HttpRequest request = httpService.newHttpRequest(connectionName);

  //The configured connection provides the full DNS name.
  //For the path, you only need to append the API endpoint after the DNS.
  //The query endpoint takes a POST where the BODY is the query itself.
  request.setMethod(HttpMethod.POST);
  request.appendPath("/api/v19.3/query");
  request.setHeader("Content-Type", "application/x-www-form-urlencoded");
  request.setBodyParam("q", query.toString());

  //Send the request to the source vault via a callback. The response received back should be a JSON response.
  //First, the response is parsed into a `JsonData` object
  //From the response, the `getJsonObject()` will get the response as a parseable `JsonObject`
  //    * Here the `getValue` method can be used to retrieve `responseStatus`, `responseDetails`, and `data`
  //The `data` element is an array of JSON data. This is parsed into a `JsonArray` object.
  //    * Each queried record is returned as an element of the array and must be parsed into a `JsonObject`.
  //    * Individual fields can then be retrieved from each `JsonObject` that is in the `JsonArray`.
  httpService.send(request, HttpResponseBodyValueType.JSONDATA)
    .onSuccess(httpResponse -> {

      JsonData response = httpResponse.getResponseBody();

      if (response.isValidJson()) {
        String responseStatus = response.getJsonObject().getValue("responseStatus", JsonValueType.STRING);

        if (responseStatus.equals("SUCCESS")) {

          JsonArray data = response.getJsonObject().getValue("data", JsonValueType.ARRAY);
          logService.info("HTTP Query Request: SUCCESS");

          //Retrieve each record returned from the VQL query.
          //Each element of the returned `data` JsonArray is a record with it's queried fields.
          for (int i = 0; i < data.getSize();i++) {
            JsonObject queryRecord = data.getValue(i, JsonValueType.OBJECT);
            String sourceId = queryRecord.getValue("source_key__c", JsonValueType.STRING);
            unprocessedObjectsList.add(sourceId);
          }
          data = null;
        }
        response = null;
      } else {
        logService.info("getUnprocessedObjects error: Received a non-JSON response.");
      }
    })
    .onError(httpOperationError -> {
      logService.info("getUnprocessedObjects error: httpOperationError.");
    }).execute();

    request = null;
    return unprocessedObjectsList;

  }
Callback to Source Vault

The callback to the source vault queries the Integration Transaction object to see what items are 'Pending' processing on the target vault. The query is built to get all pending document approval transactions for the sample integration point on the target system. That means, that only documents that are approved using the Approve Document user action on the vSDK Document document type.

public static List<String> getUnprocessedObjects(String connectionName, // e.g. vsdk_connection_to_warranties
                                                     String objectName, // e.g. vsdk_warranty__c
                                                     String targetIntegrationPoint, // e.g. send_to_claims_pending__c
                                                     Boolean runImmediately) {

        String reprocessBeforeDate = java.time.Clock.systemUTC().instant().minusSeconds(10).toString();

        LogService logService = ServiceLocator.locate(LogService.class);
        List<String> unprocessedObjectsList = VaultCollections.newList();
        StringBuilder query = new StringBuilder();
        query.append("SELECT source_key__c ");
        query.append("FROM integration_transaction__c ");
        query.append("WHERE transaction_status__c = 'pending__c' ");
        query.append("AND source_object__c = '").append(objectName).append("' ");
        query.append("AND target_integration_point__c = '").append(targetIntegrationPoint).append("' ");
        if (!runImmediately) {
            query.append("AND modified_date__v < '").append(reprocessBeforeDate).append("' ");
        }
   ...
Setting up the HTTP callout

Once the query is established the HTTP callout is defined to send the query to the \query API endpoint. The query result is put into the List<String> unprocessedObjectsList list and returned to the vSDKDocCopyMessageProcessor.processCopyDocuments() method which then calls the vSDKDocCopy.process() method to finalize and copy the document(s) from the source vault.

  ...
  //This is a vault to vault Http Request to the source connection
  HttpService httpService = ServiceLocator.locate(HttpService.class);
  HttpRequest request = httpService.newHttpRequest(connectionName);

  //The configured connection provides the full DNS name.
  //For the path, you only need to append the API endpoint after the DNS.
  //The query endpoint takes a POST where the BODY is the query itself.
  request.setMethod(HttpMethod.POST);
  request.appendPath("/api/v19.3/query");
  request.setHeader("Content-Type", "application/x-www-form-urlencoded");
  request.setBodyParam("q", query.toString());

  //Send the request to the source vault via a callback. The response received back should be a JSON response.
  //First, the response is parsed into a `JsonData` object
  //From the response, the `getJsonObject()` will get the response as a parseable `JsonObject`
  //    * Here the `getValue` method can be used to retrieve `responseStatus`, `responseDetails`, and `data`
  //The `data` element is an array of JSON data. This is parsed into a `JsonArray` object.
  //    * Each queried record is returned as an element of the array and must be parsed into a `JsonObject`.
  //    * Individual fields can then be retrieved from each `JsonObject` that is in the `JsonArray`.
  httpService.send(request, HttpResponseBodyValueType.JSONDATA)
    .onSuccess(httpResponse -> {

      JsonData response = httpResponse.getResponseBody();

      if (response.isValidJson()) {
        String responseStatus = response.getJsonObject().getValue("responseStatus", JsonValueType.STRING);

        if (responseStatus.equals("SUCCESS")) {

          JsonArray data = response.getJsonObject().getValue("data", JsonValueType.ARRAY);
          logService.info("HTTP Query Request: SUCCESS");

          //Retrieve each record returned from the VQL query.
          //Each element of the returned `data` JsonArray is a record with it's queried fields.
          for (int i = 0; i < data.getSize();i++) {
            JsonObject queryRecord = data.getValue(i, JsonValueType.OBJECT);
            String sourceId = queryRecord.getValue("source_key__c", JsonValueType.STRING);
            unprocessedObjectsList.add(sourceId);
          }
          data = null;
        }
        response = null;
      } else {
        logService.info("getUnprocessedObjects error: Received a non-JSON response.");
      }
    }).onError(httpOperationError -> {
      logService.info("getUnprocessedObjects error: httpOperationError.");
    }).execute();

    request = null;
    return unprocessedObjectsList;
  ...

Copy documents from Source Vault

The vSDKDocCopy.process() method is used to copy documents and document metadata from the source vault and handle errors. It uses a query and HTTP callout to get the relevant information from the source vault.

Establish the successful and failed document lists

We establish a successful and failed list to house the successfully copied or failed to copy documents so the status on the source vault can be established later for later retry if necessary.

    public static void process(String connectionName,
                               String integrationPointApiName,
                               List<String> docGlobalIds,
                               Boolean includeVersions,
                               Boolean includeViewableRendition,
                               Boolean includeAttachments) {

        ConnectionService connectionService = ServiceLocator.locate(ConnectionService.class);
        ConnectionContext connectionContext = connectionService.newConnectionContext(connectionName,
                ConnectionUser.CONNECTION_AUTHORIZED_USER); // Use Connection API name and user
        DocumentService documentService = ServiceLocator.locate(DocumentService.class);
        LogService logService = ServiceLocator.locate(LogService.class);

        List<String> successfulIdList = VaultCollections.newList();
        List<String> failedIdList = VaultCollections.newList();
   ...
Build the document query

The query is built against the document object on the source vault. It grabs the relevant document fields that need to be copied to the target system. It also uses the ALLVERSIONS operator in the FROM clause when versions of the document are to be included. It appends in an IN statement each of the document IDs established in from the Integration Transaction source vault query.

  ...
  String queryObject = "documents";

  StringBuilder query = new StringBuilder();
  query.append("SELECT id, global_id__sys, global_version_id__sys, latest_version__v,");
  query.append(" version_id, lifecycle__v, status__v, type__v, name__v ");
  if (includeVersions) {
    query.append("FROM ALLVERSIONS ").append(queryObject).append(" ");
  } else {
    query.append("FROM ").append(queryObject).append(" ");
  }
    query.append("WHERE global_id__sys CONTAINS ('" + String.join("','", docGlobalIds) + "')");
  ...
Setting up the HTTP callout

Once the query is established the HTTP callout is defined to send the query to the \query API endpoint. The query results are written to local variables and the version information is split into 2 local variables housing the major and minor versions of the document. Those split values are later used when creating the document on the target vault.

  ...
  //This is a vault to vault Http Request to the source connection
  HttpService httpService = ServiceLocator.locate(HttpService.class);
  HttpRequest request = httpService.newHttpRequest(connectionName);

  //The configured connection provides the full DNS name.
  //For the path, you only need to append the API endpoint after the DNS.
  //The query endpoint takes a POST where the BODY is the query itself.
  request.setMethod(HttpMethod.POST);
  request.appendPath("/api/v19.3/query");
  request.setHeader("Content-Type", "application/x-www-form-urlencoded");
  request.setBodyParam("q", query.toString());

  //Send the request the source vault via a callback. The response received back should be a JSON response.
  //First, the response is parsed into a `JsonData` object
  //From the response, the `getJsonObject()` will get the response as a parseable `JsonObject`
  //    * Here the `getValue` method can be used to retrieve `responseStatus`, `responseDetails`, and `data`
  //The `data` element is an array of JSON data. This is parsed into a `JsonArray` object.
  //    * Each queried record is returned as an element of the array and must be parsed into a `JsonObject`.
  //    * Individual fields can then be retrieved from each `JsonObject` that is in the `JsonArray`.
  httpService.send(request, HttpResponseBodyValueType.JSONDATA)
    .onSuccess(httpResponse -> {
      JsonData response = httpResponse.getResponseBody();

      String responseStatus = response.getJsonObject().getValue("responseStatus", JsonValueType.STRING);

      if (responseStatus.equals("SUCCESS")) {

        JsonArray data = response.getJsonObject().getValue("data", JsonValueType.ARRAY);
        logService.info("HTTP Query Request: SUCCESS");

        String latestGlobalId = "";
        List<DocumentVersion> newVersions = VaultCollections.newList();
        String latestNewDocId = "";

        //Retrieve each record returned from the VQL query, which corresponds to a document.
        //Each element of the returned `data` JsonArray is a record with it's queried fields.
        for (int i = 0; i < data.getSize(); i++) {
          JsonObject queryRecord = data.getValue(i, JsonValueType.OBJECT);

          String globalId = queryRecord.getValue("global_id__sys", JsonValueType.STRING);
          String globalVersionId = queryRecord.getValue("global_version_id__sys", JsonValueType.STRING);
          Boolean latestVersion = queryRecord.getValue("latest_version__v", JsonValueType.BOOLEAN);
          BigDecimal id = queryRecord.getValue("id", JsonValueType.NUMBER);

          // Retrieve the desired document metadata fields from the VQL query data
          String versionId = queryRecord.getValue("version_id", JsonValueType.STRING);
          String docName = queryRecord.getValue("name__v", JsonValueType.STRING);
          String type = queryRecord.getValue("type__v", JsonValueType.STRING);
          String lifecycle = queryRecord.getValue("lifecycle__v", JsonValueType.STRING);
          String status = queryRecord.getValue("status__v", JsonValueType.STRING);
          // Workout the major and minor versions to match the source
          String[] parts = StringUtils.split(versionId, "_");
          BigDecimal majorVersion = BigDecimal.valueOf(Integer.valueOf(parts[1]));
          BigDecimal minorVersion = BigDecimal.valueOf(Integer.valueOf(parts[2]));
  ...
Processing new vs. additional document versions
Processing new document

Creating a new document and creating a new document version need to be done independently, so the code has if/else statement for each.

In the case of a new document the DocumentService.newDocument() method is first called create the document if if doesn't already exist in the target. The information returned from the query is then used to assign all of the desired metadata fields with a .setValue().

Note: It's important to make sure that all the metadata fields returned in the query match those configured in the target vault, otherwise the subsequent save may fail. This includes populating mandatory values and ensuring values are valid.

Then a reference to the source file which needs to be copied is created using the DocumentService.newDocumentSourceFileReference(). This method that accepts connection information and the id of the document to copy from the source vault.

Additionally, renditions are suppressed using documentVersion.suppressRendition() and then the viewable rendition is manually copied from the source document using the documentService.newDocumentRenditionFileReference() method. Should other rendition types be needed further rendition file references should be added, replacing viewable_rendition__v with the desired rendition type.

Note: If these rendition steps were omitted, renditions will instead be regenerated in the target vault, rather than being copied from the source vault.

The documents are then saved to the target vault using the documentService.createDocuments method.

The latestNewDocId of the document created is then obtained, as this will be needed later on in order to create any subsequent document versions.

Lastly, any attachments to the document are created using the DocumentService.createAttachments() method. This example only creates an attachment against the latest document version.

Note: If multiple versions of attachments are needed, the new attachment version should also be passed to DocumentService.createAttachments() for each required version.

 ...
 // Create a new document if different from the previous version
 if (!globalId.equals(latestGlobalId)) {

   String newDocId = getDocumentIdForVersion(globalVersionId);

   // Create the doc if it doesn't already exist in the target vault
   if (!docVersionExists(globalVersionId)) {

     // Create a new document in the target, setting the metadata to match the
     // values from the source
     DocumentVersion documentVersion = documentService.newDocument();
     documentVersion.setValue("name__v", docName);
     documentVersion.setValue("type__v", VaultCollections.asList(type));
     documentVersion.setValue("lifecycle__v", VaultCollections.asList(lifecycle));
     documentVersion.setValue("status__v", VaultCollections.asList(status));
     documentVersion.setValue("link__sys", globalId);
     documentVersion.setValue("version_link__sys", globalVersionId);
     documentVersion.setValue("major_version_number__v", majorVersion);
     documentVersion.setValue("minor_version_number__v", minorVersion);

     // Copy the source file reference
     DocumentSourceFileReference documentSourceFileReference =
         documentService.newDocumentSourceFileReference(connectionContext, versionId);
     documentVersion.setSourceFile(documentSourceFileReference);

     // Suppress the rendition so it can be copied in from the source vault,
     // rather than being regenerated
     if (includeViewableRendition) {
       documentVersion.suppressRendition();
     }

     // Save the newly created document copy in the target vault
     SaveDocumentVersionsResponse documents =
       documentService.createDocuments(VaultCollections.asList(documentVersion));
     List<PositionalDocumentVersionId> docversion = documents.getSuccesses();
     newDocId = docversion.get(0).getDocumentVersionId();
     docversion.stream().forEach(newdoc ->
       logService.info("Document  Created is : " + newdoc.getDocumentVersionId()));

       // Create viewable rendition (if required)
       //
       // Note: similar logic can be used for other rendition types if so desired
       if (includeViewableRendition) {
         DocumentRenditionFileReference documentRenditionFileReference =
             documentService.newDocumentRenditionFileReference(connectionContext,
                                                               versionId,
                                                               "viewable_rendition__v");
         DocumentRendition viewableRendition =
             documentService.newDocumentRendition(documentRenditionFileReference,
                                                  newDocId,
                                                  "viewable_rendition__v");

         // Add the rendition
         SaveDocumentVersionsResponse docRendition =
             documentService.createRenditions(VaultCollections.asList(viewableRendition));
         //Logging
         List<PositionalDocumentVersionId> docRenditionsList = docRendition.getSuccesses();
         docRenditionsList.stream().forEach(renditionId ->
           logService.info("Document Rendition created is : "
                           + renditionId.getDocumentVersionId()));
       }
     }

     //Get new doc id, for subsequent versions
     String[] parts2 = StringUtils.split(newDocId, "_");
     latestNewDocId = parts2[0];

     // Create document attachments if required
     if (includeAttachments) {
       List<String> docAttachmentIds = getAttachmentIds(connectionName,id.toString());
       List<DocumentAttachment> documentAttachments = VaultCollections.newList();

       for (String docAttachmentId : docAttachmentIds) {
         DocumentAttachmentFileReference documentAttachmentFileReference =
           documentService.newDocumentAttachmentFileReference(connectionContext,
                                                              docAttachmentId);
         DocumentAttachment documentAttachment =
           documentService.newDocumentAttachment(documentAttachmentFileReference,
                                                latestNewDocId);
           documentAttachments.add(documentAttachment);
           documentService.createAttachments(VaultCollections.asList(documentAttachment));

       }
       // Document Attachments are only created where they don't already exist
       documentService.createAttachments(documentAttachments);
     }

     // Log the successfully updated records
     successfulIdList.add(globalId);

   ...
Processing additional document versions

When processing an existing document, if there is a new version that doesn't already appear in the target Vault we copy the new version and save it. For this the method documentService.newVersion(latestNewDocId) is used, using the existing documents id, before setting the values and adding the source file using .setSourceFile() in the same way as when creating new source documents. It's important to note that when creating new document versions, you must set the major and minor version numbers.

After each new version is created it should be added to an array, and they should all be created together using the documentService.migrateDocumentVersions(newVersions) method.

Note: If the vSDKDocCopyDocumentAction action is rerun in the source Vault after creating a new document version and subsequently approving it, this code will copy these new version to the target Vault. It doesn't however edit any of the previously copied version where the metadata might have changed. For example the previous steady state version of the document will have changed from approved to superceded. If this is required if will be necessary to delete and re-add the previous versions.

  ...

      // Otherwise create a new version
      } else {

        // Create the doc if it doesn't already exist in the target vault
        if (!docVersionExists(globalVersionId)) {

          logService.info("Target document id =" );
          logService.info("Document version to be created against docId " + latestNewDocId
                          + " with version_link__sys=" + globalVersionId);

          // Create a new document version in the target, setting the metadata to match the
          // values from the source
          DocumentSourceFileReference versionSourceFileReference =
              documentService.newDocumentSourceFileReference(connectionContext, versionId);
          // Pass Doc Id to create the version from
          DocumentVersion newVersion = documentService.newVersion(latestNewDocId);
          newVersion.setValue("name__v", docName);
          newVersion.setValue("type__v", VaultCollections.asList(type));
          newVersion.setValue("lifecycle__v", VaultCollections.asList(lifecycle));
          newVersion.setValue("status__v", VaultCollections.asList(status));
          newVersion.setValue("link__sys", globalId);
          newVersion.setValue("version_link__sys", globalVersionId);
          newVersion.setValue("major_version_number__v", majorVersion);
          newVersion.setValue("minor_version_number__v", minorVersion); 

          // Copy the version's source file reference
          newVersion.setSourceFile(versionSourceFileReference);

          // Add the version to a list to be created once all versions form the document
          // have been processed
          newVersions.add(newVersion);
        }

        // If this is the final version for the document, migrate all the versions
        if (latestVersion) {
          documentService.migrateDocumentVersions(newVersions);
          latestNewDocId = "";
          newVersions.clear();
        }
      }
      latestGlobalId = globalId;

    }

    // If the response is unsuccessful the transfer of the integration records
    // should be marked as having failed
    } else {
      for (String docGlobalId : docGlobalIds) {
        failedIdList.add(docGlobalId);
      }
    }
    response = null;
  })
  ...
Set status on source vault

The last step in processing the documents is to set the processing status on the source vault in the Integration Transaction object. There is a list of documents for:

  1. Those processed successfully
  2. Those processed unsuccessfully

The two lists mentioned earlier are use to house the documents processed successfully and unsuccessfully, respectively, using the vSDKSparkHelper.setIntTransProcessedStatuses() method.

The HttpService is used to update the values on the source side.

  ...
  // Update the source system to say whether the documents have been copied or not,
  // by making a callback to the source vault and updating the integration transaction
  // record associated with the source record.
  vSDKSparkHelper.setIntTransProcessedStatuses(
          successfulIdList,
          connectionName,
          queryObject,
          integrationPointApiName,
          true);

  vSDKSparkHelper.setIntTransProcessedStatuses(
          failedIdList,
          connectionName,
          queryObject,
          integrationPointApiName,
          false);
  ...
  //The recordsUpdate list parameter contains the affected source Ids.
  //The source Ids are used to build a batch of vsdk_bike_store__c updates for the source system.
  //The Update Record API takes JSON data as the body of the HttpRequest, so we can
  //build a Json request with the `JsonObjectBuilder` and `JsonArrayBuilder`
  public static void setIntTransProcessedStatuses(List<String> recordsUpdateSourceRecordIds,
                                                  String connectionName, // i.e. vsdk_connection_to_warranties
                                                  String objectName, // i.e. vsdk_warranty__c
                                                  String targetIntegrationPoint,
                                                  Boolean processSuccess) { // i.e. receive_warranties__c

    HttpService httpService = ServiceLocator.locate(HttpService.class);
    LogService logService = ServiceLocator.locate(LogService.class);

    String processValue = "process_success__c";
    if (!processSuccess) {
      processValue = "process_failure__c";
    }

    // Get a list of Integration Transaction Record Ids, associated with the given
    // composite key which consists of source_record_id, source_object, target_integration_point
    // and processed_status of pending
    List<String> recordUpdateIds = getIntTransIds(connectionName,
                recordsUpdateSourceRecordIds,
                objectName,
                targetIntegrationPoint);

    HttpRequest request = httpService.newHttpRequest(connectionName);

    JsonService jsonService = ServiceLocator.locate(JsonService.class);
    JsonObjectBuilder jsonObjectBuilder = jsonService.newJsonObjectBuilder();
    JsonArrayBuilder jsonArrayBuilder = jsonService.newJsonArrayBuilder();

    //The Update Object Record API takes JSON data as input.
    //The input format is an array of Json objects.
    //Use the `JsonObjectBuilder` to build the individual Json Objects with the necessary updates.
    //Then add the resulting `JsonObject` objects to the `JsonArrayBuilder`
    for (String value : recordUpdateIds) {

      JsonObject inputJsonObject = jsonObjectBuilder.setValue("id", value)
                .setValue("transaction_status__c", processValue)
                .build();
      jsonArrayBuilder.add(inputJsonObject);
    }

    //Once all the Json objects are added to the `JsonArray`, use the `build` method to generate the array.
    JsonArray inputJsonArray = jsonArrayBuilder.build();

    if (inputJsonArray.getSize() > 0) {
      request.setMethod(HttpMethod.PUT);
      request.appendPath("/api/v19.1/vobjects/integration_transaction__c");
      request.setHeader("Content-Type", "application/json");
      request.setBody(inputJsonArray);

      httpService.send(request, HttpResponseBodyValueType.JSONDATA)
        .onSuccess(httpResponse -> {

          JsonData response = httpResponse.getResponseBody();

          if (response.isValidJson()) {
            String responseStatus = response.getJsonObject().getValue("responseStatus", JsonValueType.STRING);

            if (responseStatus.equals("SUCCESS")) {
              JsonArray data = response.getJsonObject().getValue("data", JsonValueType.ARRAY);

              //Retrieve the results for each record that was updated.
              //Each element of the returned `data` JsonArray is the results of a single updated record.
              for (int i = 0; i <= data.getSize()-1;i++) {
                JsonObject recordResponse = data.getValue(i, JsonValueType.OBJECT);
                String recordStatus = recordResponse.getValue("responseStatus", JsonValueType.STRING);

                JsonObject recordData = recordResponse.getValue("data", JsonValueType.OBJECT);
                String recordId = recordData.getValue("id", JsonValueType.STRING);

                StringBuilder logMessage = new StringBuilder();
                logMessage.append("HTTP Update Request ").append(recordStatus)
                          .append(": ").append(recordId);
                logService.info(logMessage.toString());
              }
              data = null;
            }
            response = null;
          } else {
            logService.info("v2vHttpUpdate error: Received a non-JSON response.");
          }
        }).onError(httpOperationError -> {
          logService.info(httpOperationError.getMessage());
          logService.info(httpOperationError.getHttpResponse().getResponseBody());
        }).execute();

        request = null;
        inputJsonArray = null;
      }
    }