Package com.veeva.vault.sdk.api.data


package com.veeva.vault.sdk.api.data
This package provides interfaces to manage Vault objects and create record triggers.

Record Trigger

When you insert, update, or delete an object record, the record passes into a record trigger class. Inside this class, you can use custom logic to manipulate the current record or other related records.

A record trigger is a Java class that implements the RecordTrigger interface and has the RecordTriggerInfo annotation as shown below:

 
  @RecordTriggerInfo(object = "product__v", events = RecordEvent.BEFORE_INSERT)
   public class ProductFieldDefaults implements RecordTrigger {

       public void execute(RecordTriggerContext recordTriggerContext) {
           for (RecordChange inputRecord : recordTriggerContext.getRecordChanges()) {
               // Default Expected Date a week ahead
               inputRecord.getNew().setValue("expected_date__c", LocalDate.now().plusWeeks(1));
           }
       }
   }
 
 
The sample code above demonstrates how to manipulate current records by setting the expected_date__c field to a week ahead. The annotation declares that the record trigger executes before a new product__v record is inserted.

In addition to working with the current record, custom logic in a trigger can also use RecordService to manage other records, such as creating related child records. In the example below, we have an AFTER trigger on the Product object that creates a Product Brand record, which is a child of Product. We then update the parent field to the Product's ID.

     
     RecordService recordService = ServiceLocator.locate(RecordService.class);
     Record record = recordService.newRecord("product_brand__c");
     record.setValue("name__v", "Cholecap");
     record.setValue("product__c", productId);
     List<Record> records = VaultCollections.asList(record);

     RecordBatchSaveRequest saveRequest = recordService.newRecordBatchSaveRequestBuilder().withRecords(records).build();

     recordService.batchSaveRecords(saveRequest)
         .onErrors(batchOperationErrors ->{
         batchOperationErrors.stream().findFirst().ifPresent(error -> {
                  String errMsg = error.getError().getMessage();
                  int errPosition = error.getInputPosition();
                  String name = records.get(errPosition).getValue("name__v", ValueType.STRING);
                  throw new RollbackException("OPERATION_NOT_ALLOWED", "Unable to create: " + name +
                                             "because of " + errMsg);
                  });
         })
        .execute();
       
 

In order to have an upsert operation using the RecordService, we can specify the unique field to identify if the record is an insert or update. By doing this, we will no longer have to query through existing records to determine if the record exists, and specify the ID of the record we want to update. The unique field will be set in the RecordBatchSaveRequest. In the following example, we specify our unique field to be the name field.

     
     RecordService recordService = ServiceLocator.locate(RecordService.class);
     Record record = recordService.newRecord("product_brand__c");
     record.setValue("name__v", "Cholecap");
     record.setValue("product__c", productId);
     List<Record> records = VaultCollections.asList(record);

     RecordBatchSaveRequest saveRequest = recordService.newRecordBatchSaveRequestBuilder();
     saveRequest.withUniqueField("name__v").withRecords(records).build();

     recordService.batchSaveRecords(saveRequest)
       .onErrors(batchOperationErrors ->{
       batchOperationErrors.stream().findFirst().ifPresent(error -> {
         String errMsg = error.getError().getMessage();
         int errPosition = error.getInputPosition();
         String name = records.get(errPosition).getValue("name__v", ValueType.STRING);
         throw new RollbackException("OPERATION_NOT_ALLOWED", "Unable to create: " + name +
                                               "because of " + errMsg);
          });
       }).execute();
     
 

Record Migration Mode in Record Triggers

Triggers can also execute in Record Migration Mode. When Record Migration Mode is enabled, Vault can create records in any lifecycle state.

The following example checks whether Record Migration Mode was enabled when Vault executed a trigger.

     
     public void execute(RecordTriggerContext recordTriggerContext) {
          if (recordTriggerContext.isMigrationModeEnabled()) {
               // Actions to take if migration mode is enabled
           }
       }
     
 

You can also enable Record Migration Mode directly with the SDK. When we enable migration mode with RecordBatchSaveRequest.Builder.withMigrationMode(), Vault bypasses entry criteria, entry actions, validation rules, and reference constraints when saving object records and allows you to save records in a specific state or state type. If a valid state or state type is not specified, records will be saved in their initial state. The SDK user must have the Record Migration permission to use this method.

This method cannot be used in conjunction with RecordBatchSaveRequest.Builder.withAllowObjectTypeChange(). In the following example, we enable the migration mode and set the lifecycle state field.

     
     RecordService recordService = ServiceLocator.locate(RecordService.class);
     Record record = recordService.newRecord("product_brand__c");
     record.setValue("name__v", "Cholecap");
     record.setValue("product__c", productId);
     record.setValue("state__v", "draft_state__c");
     List<Record> records = VaultCollections.asList(record);

     RecordBatchSaveRequest saveRequest = recordService.newRecordBatchSaveRequestBuilder();

     //Enable migration mode
     saveRequest.withRecords(records).withMigrationMode().build();

     recordService.batchSaveRecords(saveRequest)
       .onErrors(batchOperationErrors ->{
       batchOperationErrors.stream().findFirst().ifPresent(error -> {
         String errMsg = error.getError().getMessage();
         int errPosition = error.getInputPosition();
         String name = records.get(errPosition).getValue("name__v", ValueType.STRING);
         throw new RollbackException("OPERATION_NOT_ALLOWED", "Unable to create: " + name +
                                               "because of " + errMsg);
          });
       }).execute();
     
 

Changing Object Types

To change an object's type with the SDK, you must allow object type changes with RecordBatchSaveRequest.Builder.withAllowObjectTypeChange(). When enabled, any field values that exist on both the original and new object type will carry over to the new type. All other field values will be removed, as only fields on the new type are valid.

When using this method, all records must have either object_type__v or object_type__vr.api_name__v set. If both are set, the object type is changed to object_type__vr.api_name__v and object_type__v is ignored. For example, object_type__v = 'OOT000000000A08' or object_type__vr.api_name__v = 'base__v'.

This method cannot be used in conjunction with RecordBatchSaveRequest.Builder.withMigrationMode(). In the following example, we enable the mode to allow object type change and set the object type name and object type ID.

     
     RecordService recordService = ServiceLocator.locate(RecordService.class);
     Record record = recordService.newRecord("product_brand__c");
     record.setValue("name__v", "Cholecap");
     record.setValue("product__c", productId);
     record.setValue("object_type__v", "OOT000000018002");
     record.setValue("object_type__v.api_name__v", "complex_product__c");
     List<Record> records = VaultCollections.asList(record);

     RecordBatchSaveRequest saveRequest = recordService.newRecordBatchSaveRequestBuilder();

     //Enable 'allow object type change'
     saveRequest.withRecords(records).withAllowObjectTypeChange().build();

     recordService.batchSaveRecords(saveRequest)
       .onErrors(batchOperationErrors ->{
       batchOperationErrors.stream().findFirst().ifPresent(error -> {
         String errMsg = error.getError().getMessage();
         int errPosition = error.getInputPosition();
         String name = records.get(errPosition).getValue("name__v", ValueType.STRING);
         throw new RollbackException("OPERATION_NOT_ALLOWED", "Unable to create: " + name +
                                               "because of " + errMsg);
          });
       }).execute();
     
 

Object Lookup Fields

When creating or updating object records using RecordService, you can use object lookup fields to set values for related object fields. Learn more about object lookup fields in Vault Help.

When using object lookup fields with RecordService:

  • For standard objects and fields, use the format object_relationship_name__vr.lookup_field_name__v.
  • For custom objects and fields, use the format object_relationship_name__cr.lookup_field_name__c.
  • Object lookup fields must require unique values on the referenced object, but they are not required to be unique on the referring object.
  • When setting object lookup field values, supplying null or empty values results in an error.
  • To clear existing object lookup field values, use the field name on the referring object only and set the value to null. For example, (“product__v”, null).
In the following example, the study sample object has reference relationships to the product and study objects. When creating a new study sample record, the example code sets values for lookup fields that reference these objects.
 
 String productName = "CholeCap";
 String studyExternalId = "STUDY-XYZ";

 RecordService recordService = ServiceLocator.locate(RecordService.class);
 Record record = recordService.newRecord("study_sample__c");
 record.setValue("name__v", "SAMPLE-0001");
 record.setValue("product__vr.name__v", productName);
 record.setValue("study__vr.external_id__v", studyExternalId);

 RecordBatchSaveRequest batchSaveRequest = recordService
 		.newRecordBatchSaveRequestBuilder()
 		.withRecords(asList(record))
 		.build();

 	
        

Retrieving Vault Object Metadata

To retrieve object and object field metadata, first create a request object using one of the builders in ObjectMetadataService. Then pass in the request objects to retrieve either the ObjectMetadata or the ObjectFieldMetadata. Once you have the metadata object, you can retrieve the desired metadata attributes, such as name or label.

The following example retrieves metadata for the country__v object, including the label and active status:

     
     // Initialize service
     ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

     // Build the request object for retrieving the country__v object metadata
     ObjectMetadataRequest objectMetadataRequest = objectMetadataService.newObjectRequestBuilder()
         .withObjectName("country__v")
         .build();

     // Retrieve country__v object metadata
     ObjectMetadata countryMetadata = objectMetadataService.getObject(objectMetadataRequest);

     // Retrieve the label and status of the country__v object
     String countryLabel = countryMetadata.getLabel();
     boolean isCountryActive = countryMetadata.isActive();
     
 

You can also retrieve object metadata by prefix. Your request must include either an object name or an object prefix, but not both. If you provide both, only the object name will be used.

The following example retrieves metadata for on object with the prefix "00P":

     
     // Initialize service
     ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

     // Build the request object for retrieving an object's metadata by its prefix, in this case "00P"
     ObjectMetadataRequest objectMetadataRequest = objectMetadataService.newObjectRequestBuilder()
         .withObjectPrefix("00P")
         .build();

     // Retrieve object metadata with prefix "00P"
     ObjectMetadata objectMetadata = objectMetadataService.getObject(objectMetadataRequest);

     // Retrieve an object's prefix from a given ObjectMetadata object
     String objectPrefix = objectMetadata.getPrefix();
     
 

The following example uses an optional parameter on ObjectFieldMetadataCollectionRequest.Builder to retrieve all required fields for the country__v object and then stores the names in a List:

     
      // Initialize service
      ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

      // Get the builder for building the request object of the ObjectFieldMetadataCollectionResponse
      ObjectFieldMetadataCollectionRequest.Builder fieldCollectionMetadataRequestBuilder = objectMetadataService.newObjectFieldMetadataCollectionRequestBuilder();

      ObjectFieldMetadataCollectionRequest objectFieldCollectionRequest = fieldCollectionMetadataRequestBuilder
          .withObjectName("country__v")
          .withRequiredOnly()
          .build();

      ObjectFieldMetadataCollectionResponse requiredCountryFields = objectMetadataService.getObjectFieldMetadataCollection(objectFieldCollectionRequest);

      // Store all the names of the required fields in a List
      List<String> requiredFieldNames = VaultCollections.newList();
      requiredCountryFields.getFields().forEach(requiredField -> requiredFieldNames.add(requiredField.getName()));
      
 
If a specific subset of fields is required, use one or more of the optional parameters in ObjectFieldMetadataCollectionRequest.

The following example uses the withFieldNames(List<String> fieldNames) method to retrieve multiple fields by their name.

     
     ObjectFieldMetadataCollectionRequest objectFieldCollectionRequest = fieldCollectionMetadataRequestBuilder
         .withObjectName("country__v")
         .withFieldNames(VaultCollections.asList("name__v", "status__v", "link__sys"))
         .build();
     
 
If you have an ObjectFieldMetadata, you can also fetch specific field type attributes.

The following example uses the getTypedObjectFieldMetadata(Class<T> metadataClass) method to fetch the max length of the name field and whether the name field is system-managed.

      
      // Initialize service
      ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

      // Get the builder for building the request object of the ObjectFieldCollectionResponse
      ObjectFieldMetadataCollectionRequest.Builder fieldCollectionMetadataRequestBuilder = objectMetadataService.newObjectFieldMetadataCollectionRequestBuilder();

      // Only fetch the name field of the object
      ObjectFieldMetadataCollectionRequest objectFieldCollectionRequest = fieldCollectionMetadataRequestBuilder
          .withObjectName("country__v")
          .withFieldNames(VaultCollections.asList("name__v"))
          .build();

      ObjectFieldMetadataCollectionResponse onlyCountryNameField = objectMetadataService.getObjectFieldMetadataCollection(objectFieldCollectionRequest);

      // Grab the name field
      ObjectFieldMetadata nameField = onlyCountryNameField.getField("name__v");

      // Transform the field into an ObjectTextFieldMetadata so we can grab text-field specific attributes
      ObjectTextFieldMetadata textNameField = nameField.getTypedObjectFieldMetadata(ObjectTextFieldMetadata.class);

      // Fetch the max length and whether the field is system-managed
      textNameField.getMaxLength();
      textNameField.isSystemManaged();
      
  

Retrieving Vault Object Picklist Dependency Metadata

To retrieve object picklist dependency metadata, first create a request object using the ObjectMetadataService.newObjectPicklistDependencyRequestBuilder() method. Then pass in the request object name and the picklist field name to retrieve the List of ObjectPicklistDependency objects. Once you have the List of picklist dependencies, you can retrieve the desired picklist dependency metadata, such as the controlling value or dependent picklist values.

The following example retrieves picklist dependency metadata for the country__v object's language__c field:

     
     // Initialize service
     ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

     // Build the request object for retrieving the List of picklist dependency metadata for the country__v object
     ObjectPicklistDependencyRequest objectPicklistDependencyRequest = objectMetadataService.newObjectPicklistDependencyRequestBuilder()
          .withObjectName("country__v")
          .withFieldName("language__c")
          .build();

     // Retrieve the List of picklist dependency metadata for the country__v object
     List<ObjectPicklistDependency> objectPicklistDependencyList = objectMetadataService.getObjectPicklistDependencies(objectPicklistDependencyRequest).getPicklistDependencies();

     // Loop through the picklist dependencies of the country__v object's language__c field
      for (ObjectPicklistDependency dependency : objectPicklistDependencyList) {
          // Retrieve the controlling value and dependent picklist values of the picklist dependency
          String controllingPicklistValue = dependency.getControllingValue();
          List<String> dependentPicklistValues = dependency.getPicklistValues();

      }
     
 

Retrieving Vault Object Validation Rule Metadata

Vault Admins can configure validation rules for specific object fields at the object or object type level. These rules contain an expression that Vault uses to evaluate the value a user enters into the field. Learn more about validation rules in Vault Help.

To retrieve object validation rule metadata, first create a request object using the ObjectMetadataService.newObjectValidationRuleMetadataRequestBuilder() method. Then pass in the request object name and validation rule names to retrieve the List of ObjectValidationRuleMetadata. Once you have the List of validation metadata, you can retrieve the desired metadata attributes, such as error message or validation expression.

The following example retrieves validation rule metadata for the country__v object's object_validation_rule__c validation rule:

     
     // Initialize service
     ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

      // Only fetch the object_validation_rule__c object validation rule
      List<String> onlyValidationRuleName = VaultCollections.asList("object_validation_rule__c");

     // Build the request object for retrieving the List of validation rule metadata for the country__v object
     ObjectValidationRuleMetadataRequest validationRuleMetadataRequest = objectMetadataService.newObjectValidationRuleMetadataRequestBuilder()
         .withObjectName("country__v")
         .withValidationRuleNames(onlyValidationRuleName)
         .build();

     // Retrieve object_validation_rule__c validation rule metadata
     ObjectValidationRuleMetadataResponse objectValidationRuleMetadataResponse = objectMetadataService.getObjectValidationRules(validationRuleMetadataRequest);

     // Retrieve the error message and validation expression of the object_validation_rule__c validation rule
     if(objectValidationRuleMetadataResponse.containsValidationRule("object_validation_rule__c")){
          String validationRuleErrorMessage = objectValidationRuleMetadata.getErrorMessage();
          String validationRuleExpression = objectValidationRuleMetadata.getValidationExpression().getExpressionString();
     }
     
 

How to Include Inactive Object Validation Rules

Specify ObjectValidationRuleMetadataRequest.Builder.includeInactive() to retrieve both active and inactive validation rules. When ObjectValidationRuleMetadataRequest.Builder.includeInactive() is omitted, you can only retrieve active validation rules.

The following example uses the optional parameter ObjectValidationRuleMetadataRequest.Builder.includeInactive() on ObjectValidationRuleMetadataRequest.Builder to include all inactive validation rules in addition to active validation rules on country__v object:

     
      // Initialize service
      ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

      // Only fetch the inactive_object_validation_rule__c object validation rule
      List<String> onlyValidationRuleName = VaultCollections.asList("inactive_object_validation_rule__c");

     // Build the request object for retrieving the List of validation rule metadata for the country__v object
     ObjectValidationRuleMetadataRequest validationRuleMetadataRequest = objectMetadataService.newObjectValidationRuleMetadataRequestBuilder()
         .withObjectName("country__v")
         .withValidationRuleNames(onlyValidationRuleName)
         .includeInactive()
         .build();

     // Retrieve inactive_object_validation_rule__c validation rule metadata
     ObjectValidationRuleMetadataResponse objectValidationRuleMetadataResponse = objectMetadataService.getObjectValidationRules(validationRuleMetadataRequest);

     // Retrieve the error message and validation expression of the inactive_object_validation_rule__c validation rule
     if(objectValidationRuleMetadataResponse.containsValidationRule("inactive_object_validation_rule__c")){
          String validationRuleErrorMessage = objectValidationRuleMetadata.getErrorMessage();
          String validationRuleExpression = objectValidationRuleMetadata.getValidationExpression().getExpressionString();
     }
 
 

Retrieving Vault Object Type Metadata

When enabled, Vault Objects can be partitioned into Object Types. For example, the Country object may have two different object types: European (european__c) and Asian (asian__c). These object types may share some fields but also have fields that only appear on one of the object types.

All objects, including those where object types are not enabled, have a Base (base__v) object type containing all object fields. For objects with object types enabled, the base object type contains all fields configured on all object types. For example, an “EMA Status” field that appears only on the European object type and a “CFDA Status” field that appears only on the Asian object type would both appear on the base object type.

Learn more about object type configuration in Vault Help.

To retrieve object type and object type field metadata, you must first create a request object using one of the builders in ObjectMetadataService. Then pass in the request objects to retrieve either the ObjectTypeMetadata or the ObjectTypeFieldMetadata. Once you have the metadata object, you can retrieve the desired metadata attributes, such as name or label.

The following example retrieves metadata for the country__v object types, including the label and active status:

      
      // Initialize service
      ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

      // Build the request object for retrieving the country__v object metadata
      ObjectTypeMetadataCollectionRequest objectTypeMetadataRequest = objectMetadataService.newObjectTypeMetadataCollectionRequestBuilder()
          .withObjectName("country__v")
          .build();

      // Retrieve country__v object metadata
      ObjectTypeMetadataCollectionResponse countryObjectTypeMetadata = objectMetadataService.getObjectTypes(objectTypeMetadataRequest);

      // Retrieve the label and status of the european__c object type
      if(countryObjectTypeMetadata.containsObjectType("european__c")){
          ObjectTypeMetadata european = countryObjectTypeMetadata.getObjectType("european__c");
          String europeLabel = european.getLabel();
          boolean isEuropeActive = european.isActive();
      }

      
  

The following example uses an optional parameter on ObjectTypeFieldMetadataCollectionRequest to retrieve all object type fields for the european__c object type on the country__v object and then stores the names in a list.

      
          // Initialize service
          ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

          // Get the builder for building the request object of the ObjectTypeFieldMetadataCollectionResponse
          ObjectTypeFieldMetadataCollectionRequest.Builder objectTypeFieldMetadataCollectionRequestBuilder =
          objectMetadataService.newObjectTypeMetadataCollectionRequestBuilder();

          ObjectTypeFieldMetadataCollectionRequest objectTypeFieldMetadataCollectionRequest =
          objectTypeFieldMetadataCollectionRequestBuilder
              .withObjectName("country__v")
              .withObjectTypeName("european__c")
              .build();

           ObjectTypeFieldMetadataCollectionResponse europeObjectTypeFields =
           objectMetadataService.getObjectTypeFields(objectTypeFieldMetadataCollectionRequest);


          List<String> europeObjectTypeFieldNames = VaultCollections.newList();
          europeObjectTypeFields.getObjectTypeFields().forEach(field -> europeObjectTypeFieldNames.add(field.getName()));
      
  
If you have an ObjectTypeFieldMetadata, you can also fetch specific field type attributes.

The following example uses the getTypedObjectTypeFieldMetadata(Class<T> metadataClass) method to fetch the default value of the code__sys field for the European object type.

      
          //Initialize Service
          ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

          // Get the builder for building the request object of the ObjectTypeFieldMetadataCollectionResponse
          ObjectTypeFieldMetadataCollectionRequest.Builder objectTypeFieldMetadataCollectionRequestBuilder =
          objectMetadataService.newObjectTypeMetadataCollectionRequestBuilder();


          // Only fetch the code field
          List<String> onlyCodeField = VaultCollections.asList("code__sys");

          ObjectTypeFieldMetadataCollectionRequest objectTypeFieldMetadataCollectionRequest =
          objectTypeFieldMetadataCollectionRequestBuilder
              .withObjectName("country__v")
              .withObjectTypeName("european__c")
              .withFieldNames(onlyCodeField)
              .build();

          ObjectTypeFieldMetadataCollectionResponse onlyEuropeTypeCodeField =
          objectMetadataService.getObjectTypeFields(objectTypeFieldMetadataCollectionRequest);

          ObjectTypeFieldMetadata codeField = onlyEuropeTypeCodeField.getField("code__sys");

          // Transform the field into an ObjectTypeTextFieldMetadata so we can grab text-field specific attributes
          ObjectTypeTextFieldMetadata textCodeField =
          codeField.getTypedObjectTypeFieldMetadata(ObjectTypeTextFieldMetadata.class);

          // Fetch the default value
          textCodeField.getDefaultValue();
      
  

Retrieving Vault Object Type Validation Rule Metadata

To retrieve object type validation rule metadata, first create a request object using the ObjectMetadataService.newObjectTypeValidationRuleMetadataRequestBuilder() method. Then pass in the request object name, object type name, and validation rule names to retrieve the List of ObjectTypeValidationRuleMetadata. Once you have the List of validation metadata, you can retrieve the desired metadata attributes, such as error message or validation expression.

The following example retrieves validation rule metadata for the european__c object type on the country__v object:

     
     // Initialize service
     ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

      // Only fetch the object_type_validation_rule__c object validation rule
      List<String> onlyValidationRuleName = VaultCollections.asList("object_type_validation_rule__c");

     // Build the request object for retrieving the List of validation rule metadata for the european__c object type
     ObjectTypeValidationRuleMetadataRequest validationRuleMetadataRequest = objectMetadataService.newObjectTypeValidationRuleMetadataRequestBuilder()
         .withObjectName("country__v")
         .withObjectTypeName("european__c")
         .withValidationRuleNames(onlyValidationRuleName)
         .build();

     // Retrieve object_type_validation_rule__c validation rule metadata
     ObjectTypeValidationRuleMetadataResponse objectTypeValidationRuleMetadataResponse = objectMetadataService.getObjectTypeValidationRules(validationRuleMetadataRequest);

     // Retrieve the error message and validation expression of the object_type_validation_rule__c validation rule
     if(objectTypeValidationRuleMetadataResponse.containsValidationRule("object_type_validation_rule__c")){
          String validationRuleErrorMessage = objectTypeValidationRuleMetadata.getErrorMessage();
          String validationRuleExpression = objectTypeValidationRuleMetadata.getValidationExpression().getExpressionString();
     }
     
 

How to Include Inactive Object Type Validation Rules

Specify ObjectTypeValidationRuleMetadataRequest.Builder.includeInactive() to retrieve both active and inactive validation rules. When ObjectTypeValidationRuleMetadataRequest.Builder.includeInactive() is omitted, you can only retrieve active validation rules.

The following example uses the optional parameter ObjectTypeValidationRuleMetadataRequest.Builder.includeInactive() on ObjectTypeValidationRuleMetadataRequest.Builder to include all inactive validation rules in addition to active validation rules for the european__c object type on the country__v object:

     
      // Initialize service
      ObjectMetadataService objectMetadataService = ServiceLocator.locate(ObjectMetadataService.class);

      // Only fetch the inactive_object_type_validation_rule__c object type validation rule
      List<String> onlyValidationRuleName = VaultCollections.asList("inactive_object_type_validation_rule__c");

     // Build the request object for retrieving the List of validation rule metadata for the european__c object type
     ObjectTypeValidationRuleMetadataRequest validationRuleMetadataRequest = objectMetadataService.newObjectTypeValidationRuleMetadataRequestBuilder()
         .withObjectName("country__v")
         .withObjectTypeName("european__c")
         .includeInactive()
         .build();

     // Retrieve inactive_object_type_validation_rule__c validation rule metadata
     ObjectTypeValidationRuleMetadataResponse objectTypeValidationRuleMetadataResponse = objectMetadataService.getObjectTypeValidationRules(validationRuleMetadataRequest);

     // Retrieve the error message and validation expression of the inactive_object_type_validation_rule__c validation rule
     if(objectTypeValidationRuleMetadataResponse.containsValidationRule("inactive_object_type_validation_rule__c")){
          String validationRuleErrorMessage = objectTypeValidationRuleMetadata.getErrorMessage();
          String validationRuleExpression = objectTypeValidationRuleMetadata.getValidationExpression().getExpressionString();
     }
 
 

Creating Object Attachments

The following examples uses the new method batchSaveRecordAttachments in the service RecordService to create a new object attachment from an existing one under the same object record.

      
          ConnectionService connectionService = ServiceLocator.locate(ConnectionService.class);
          ConnectionContext connectionContext = connectionService.newConnectionContext(
              "connectionApiName",
              ConnectionUser.CONNECTION_AUTHORIZED_USER);

          RecordService recordService = ServiceLocator.locate(RecordService.class);

          RecordAttachmentFileReferenceRequest fileReferenceRequest = recordService.newRecordAttachmentFileReferenceRequestBuilder()
              .withObjectName(objectName)
              .withObjectRecordId(objectRecordId)
              .withAttachmentId(oldAttachmentId)
              .withAttachmentId(oldAttachmentId)
              .withAttachmentVersionId("2") //default is latest
              .withConnectionContext(connectionContext)
              .build();

          RecordAttachmentFileReference oldAttachmentReference = recordService
              .newRecordAttachmentFileReference(fileReferenceRequest);

          SaveRecordAttachment saveRecordAttachment = recordService
              .newSaveRecordAttachmentBuilder()
              .withObjectName(objectName)
              .withObjectRecordId(objectRecordId)
              .withAttachmentExternalId("XYZ-ATTACH")
              .withFileReference(oldAttachmentReference)
              .withDescription("Example attachment")
          .build();

          List<SaveRecordAttachment> attachments = VaultCollections.asList(saveRecordAttachment);

          BatchSaveRecordAttachmentRequest saveRequest = recordService
              .newBatchSaveRecordAttachmentRequestBuilder()
              .withRecordAttachments(attachments)
          .build();

          recordService.batchSaveRecordAttachments(saveRequest)
              .onErrors(batchOperationErrors -> {

              })
              .onSuccesses(recordAttachmentPositionResults -> {
                  for (RecordAttachmentPositionResult result : recordAttachmentPositionResults){
                      int position = result.getInputPosition();

                      SaveRecordAttachment source = attachments.get(position);

                      LogService logService = ServiceLocator.locate(LogService.class);
                      logService.debug("object name {}", source.getObjectName());
                  }
              })
              .execute();
      
  

Merging Object Records

To remove duplicate records while preserving the records’ information, SDK developers can merge records from the same Vault object together.

The following example executes two record merges within the account__v object.

    
      LogService logService = ServiceLocator.locate(LogService.class);
      RecordService recordService = ServiceLocator.locate(RecordService.class);
      RecordMergeSetInput recordMergeSetInput1 = recordService.newRecordMergeSetInputBuilder()
          .withDuplicateRecordId("V6G000000001005")
          .withMainRecordId("V6G000000001002")
          .build();

          RecordMergeSetInput recordMergeSetInput2 = recordService.newRecordMergeSetInputBuilder()
          .withDuplicateRecordId("V6G000000006002")
          .withMainRecordId("V6G000000001002")
          .build();

          RecordMergeRequest request = recordService.newRecordMergeRequestBuilder()
          .withObjectName("account__v")
          .withRecordMerges(VaultCollections.asList(recordMergeSetInput1, recordMergeSetInput2))
          .build();

          recordService.mergeRecords(request)
              .onSuccess(success -> {
                  String jobId = success.getJobId();
                  logService.info(String.format("Successfully started merge with job [%s]", jobId));
                  })
                  .onError(recordMergeOperationError -> {
                      recordMergeOperationError.getErrors().forEach(error -> {
                          String errorMessage = error.getError().getMessage();
                          RecordMergeOperationErrorType errorType = error.getErrorType();
                          if (errorType.equals(RecordMergeOperationErrorType.INVALID_DATA)) {
                              logService.info(String.format("Failed to start merge due to INVALID_DATA. Error: [%s]", errorMessage));
                          } else if (errorType.equals(RecordMergeOperationErrorType.OPERATION_NOT_ALLOWED)) {
                               logService.info(String.format("Failed to start merge due to OPERATION_NOT_ALLOWED. Error: [%s]", errorMessage));
                          }
                        });
                    })
                    .execute();
  
  

Record Merge Event Handlers

To add custom logic when starting or completing a record merge, you can create a RecordMergeEventHandler.

You can only implement one RecordMergeEventHandler per object. For example, you cannot have more than one RecordMergeEventHandler implemented for the the account__v object.

      
      @RecordMergeEventHandlerInfo(object = "account__v")
      public class RecordMergeEventHandlerSample implements RecordMergeEventHandler {

     @Override
     public void onMergeStart(RecordMergeEventStartContext context) {
         LogService logService = ServiceLocator.locate(LogService.class);

         String objectName = context.getObjectName();
         String jobId = context.getJobId();
         List mergeSets = context.getRecordMergeSets();
         for (RecordMergeSet mergeSet : mergeSets) {
             String dupRecordId = mergeSet.getDuplicateRecordId();
             String mainRecordId = mergeSet.getMainRecordId();
             logService.info(String.format("Started merge of duplicate record [%s] into main record [%s] for object [%s] job [%s]", dupRecordId, mainRecordId, objectName, jobId));
             //do something
         }
     }

     @Override
     public void onMergeComplete(RecordMergeEventCompletionContext context) {
         LogService logService = ServiceLocator.locate(LogService.class);

         String objectName = context.getObjectName();
         String jobId = context.getJobId();
         List mergeSetResults = context.getRecordMergeSetResults();
         for (RecordMergeSetResult mergeSetResult : mergeSetResults) {
             String dupRecordId = mergeSetResult.getDuplicateRecordId();
             String mainRecordId = mergeSetResult.getMainRecordId();
             RecordMergeCompletionStatus status = mergeSetResult.getStatus();
             if (status == RecordMergeCompletionStatus.SUCCESS) {
                 logService.info(String.format("Succeeded in merge of duplicate record [%s] into main record [%s] for object [%s] job [%s]",  dupRecordId, mainRecordId, objectName, jobId));
                 //do something for success
             } else if (status == RecordMergeCompletionStatus.FAILURE) {
                 logService.info(String.format("Failed in merge of duplicate record [%s] into main record [%s] for object %s job %s",  dupRecordId, mainRecordId, objectName, jobId));
                 //do something for failure
             }
         }
     }
 }