Validation Plugin

The Validation Plugin allows you to execute custom validation logic on an entity. Validation requests trigger the Precommit Plugin, followed by the Validation Plugin.

When a validation request is processed, the system executes two validation steps in the following order:

  1. Check if the entity adheres to the entity schema defined in the configuration

  2. Check if the entity adheres to the custom validation logic defined in the Validation Plugin, if an implementation exists for the relevant entity type

If both of these steps determine that the entity is valid, the entity will move to the validated state. Once an entity moves to the validated state, it becomes immutable. If either of these steps determines that the entity is invalid, the entity will remain in its original state. The system will only execute the second step if the first step determines that the entity is valid.

Supported Entity Types

The Validation Plugin supports the following entity types:

  • Accounts

  • Quotes

  • Policy Transactions

  • Payments

  • Disbursements

Implementation

Create a new Java class in the src/main/java/com/socotra/deployment/customer folder. All plugin code must be contained within this folder. We named our class ValidationPluginImpl.java in the example below, but you can name your class whatever you’d like.

Implement the ValidationPlugin interface, and override the method corresponding to the entity type you wish to validate.

For example, the following class contains a method that validates commercial accounts:

public class ValidationPluginImpl implements ValidationPlugin {
    private static final Logger log = LoggerFactory.getLogger(ValidationPluginImpl.class);

    // Validate commercial accounts

    @Override
    public ValidationItem validate(CommercialAccountRequest commercialAccountRequest) {
        return ValidationItem.builder().build();
    }
}

The method argument contains the entity to be validated.

The method returns a ValidationItem object, which contains the following fields:

  • locator - The entity locator

  • elementType - The element type that caused the validation failure

  • addError - An error message

  • addErrors - Multiple error messages

  • errors - Multiple error messages

A return object that does not contain any error messages, as shown in the example above, indicates that the entity is valid. A return object containing one or more error messages indicates that the entity is invalid.

Examples

All examples are based on the Prism configuration. Contact your Socotra representative for more information.

Account

// Check if the company name is blank

@Override
public ValidationItem validate(CommercialAccountRequest commercialAccountRequest) {
    log.info("Account locator: {}", commercialAccountRequest.account().locator());

    CommercialAccount commercialAccount = commercialAccountRequest.account();
    String companyName = commercialAccount.data().companyName();

    if (companyName == null || companyName.isEmpty()) {
        return ValidationItem.builder()
                .locator(commercialAccount.locator())
                .addError("Company name cannot be blank")
                .build();
    } else {
        return ValidationItem.builder().build();
    }
}

Quote

// Check for duplicate VINs

@Override
public ValidationItem validate(CommercialAutoQuoteRequest commercialAutoQuoteRequest) {

    List<VehicleQuote> vehicleSchedule = commercialAutoQuoteRequest.quote().vehicleSchedule().vehicles().stream().toList();

    Map<String, List<String>> vinToVehicleNumbers = new HashMap<>();
    List<String> duplicateVins = new ArrayList<>();

    // Check for duplicate VINs
    for (VehicleQuote vehicle : vehicleSchedule) {
        String vin = vehicle.data().vin();
        String vehicleNumber = vehicle.data().vehicleNumber();

        log.info("Vehicle {} VIN: {}", vehicleNumber, vin);

        if (vin != null && !vin.isBlank()) {
            vinToVehicleNumbers.computeIfAbsent(vin, v -> new ArrayList<>()).add(vehicleNumber);

            // Track duplicate VINs
            if (vinToVehicleNumbers.get(vin).size() == 2) {
                duplicateVins.add(vin);
            }
        }
    }

    if (!duplicateVins.isEmpty()) {
        List<String> errorMessages = new ArrayList<>();

        for (String vin : duplicateVins) {
            List<String> vehicleNumbers = vinToVehicleNumbers.get(vin);

            log.warn("Duplicate VIN {} found: {}", vin, vehicleNumbers);

            errorMessages.add(String.format(
                    "VIN %s is duplicated for vehicles %s",
                    vin,
                    String.join(" & ", vehicleNumbers)
            ));
        }

        return ValidationItem.builder()
                .locator(commercialAutoQuoteRequest.quote().vehicleSchedule().locator())
                .elementType(commercialAutoQuoteRequest.quote().vehicleSchedule().type())
                .addErrors(errorMessages)
                .build();
    } else {
        return ValidationPlugin.super.validate(commercialAutoQuoteRequest);
    }
}

Policy Transaction

// Check if a policy transaction contains a blanket coverage selection

@Override
public ValidationItem validate(CommercialAutoRequest commercialAutoRequest) {
    List<String> errorMessages = new ArrayList<>();

    if (commercialAutoRequest.segment().isPresent()) {
        CommercialAuto.CommercialAutoData commercialAutoData = commercialAutoRequest.segment().get().data();

        if (!commercialAutoData.blanketCoverage() || commercialAutoData.blanketCoverageSelections() == null) {
            errorMessages.add("Commercial auto policy transaction must contain a blanket coverage selection");
        }
    }

    if (!errorMessages.isEmpty()) {
        return ValidationItem.builder()
                .locator(commercialAutoRequest.policy().locator())
                .elementType(commercialAutoRequest.segment().get().data().blanketCoverageSelections().type())
                .errors(errorMessages)
                .build();
    } else {
        return ValidationItem.builder().build();
    }
}

Payment

// Check if a payment note is blank

@Override
public ValidationItem validate(StandardPaymentRequest standardPaymentRequest) {
    log.info("Payment locator: {}", standardPaymentRequest.payment().locator());

    String note = standardPaymentRequest.payment().data().note();

    if (note == null || note.isEmpty()) {
        return ValidationItem.builder()
                .locator(standardPaymentRequest.payment().locator())
                .addError("Payment note cannot be blank")
                .build();
    } else {
        return ValidationItem.builder().build();
    }
}

Disbursement

// Check if a disbursement note is blank

@Override
public ValidationItem validate(StandardDisbursementRequest standardDisbursementRequest) {
    log.info("Disbursement locator: {}", standardDisbursementRequest.disbursement().locator());

    String note = standardDisbursementRequest.disbursement().data().note();

    if (note == null || note.isEmpty()) {
        return ValidationItem.builder()
                .locator(standardDisbursementRequest.disbursement().locator())
                .addError("Disbursement note cannot be blank")
                .build();
    } else {
        return ValidationItem.builder().build();
    }
}

Next Steps

See Also