Configure validation service
Learn how to configure the Validation Framework.
Validation Framework
The Validation Framework allows you to execute custom validations on entities. This code is executed interactively when the user saves the entity.
Validation Service URL {ValidationServiceURL}
is usually an instance
name plus /validation
service identifier. Example:
http://idev01.ih.reltio.com/validation
.
The Validation Service is an LCA that is executed on a specified
environment. To select which LCA server should be used, Validation
Service checks the tenant configuration. If there is no specified
LCA server, then the value of a system variable LCA_URI
is
used.
- If there are lookups on at least one tenant on the environment.
- If cardinality feature is enabled on at least one tenant on the environment.
- If a validation hook is defined on at least one tenant on the environment.
In Edit mode, if the profile is not valid according to the customized validation code, then Validation Service displays a message for the overall profile and error messages for every specific attribute type. Note that at least one address should have the primary flag or attribute value (an attribute value that is not meeting length requirements) that has a problem.
The overall profile error message summaries all the errors in the profile. It may look like this:
The overall message appears at the top of the page, while messages for individual fields are shown adjacent to the field and the field is highlighted in red.
In this example, the middle name is missing and the customized help message is displayed.
How to Work With Validation
Create a custom validator:
- Create your own LCA handler with the implemented validate hook. See LCA Handler. The LCA hook has
two inputs. One for the implementation of an
IReltioAPI
interface and the other for implementation of aILifeCycleValidationData
interface:ILifeCycleAction.validate(...) signature ILifeCycleValidationData validate(IReltioAPI reltioAPI, ILifeCycleValidationData data);
The
data
object has the following specific methods:List<IValidationError> getValidationErrors(); //get resulting list of validation errors void addValidationError(IValidationError validationError); //add one validation error to the resulting list void addValidationErrors(List<IValidationError> validationErrors); //add list of validation errors to the resulting list IObject getObject(); // get object representing entity
- Build your LCA handler and place the resulting jar file on S3 or deploy it as an AWS Lambda function.
- Register your LCA handler for your tenant. For more information, see Register Actions).
- Specify the validate action in the tenant physical configuration.
Creating Validator
This class contains an implementation of a validate hook that validates the address reference attribute. If the entity contains more than one address, then the validator checks that and only one of them is marked as primary.
public class AddressValidation extends LifeCycleActionHandler {
public static final String NO_PRIMARY_ADDRESS_KEY = "validation.address.no_primary_address";
public static final String MANY_PRIMARY_ADDRESSES_KEY = "validation.address.many_primary_addresses";
public ILifeCycleValidationData validate(IReltioAPI reltioAPI,
ILifeCycleValidationData data) {
IObject dataObject = data.getObject();
List < IAttributeValue > addressValues = dataObject.getAttributes().getAttributeValues("Address");
if (addressValues.size() <= 1) {
return data;
}
int activeAddressesCount = 0;
for (IAttributeValue addressValue: addressValues) {
IReferenceAttributeValue address = (IReferenceAttributeValue) addressValue;
List < IAttributeValue > primaryValues = address.getValue().getAttributeValues("Primary");
if (primaryValues.isEmpty()) {
continue;
}
for (IAttributeValue primaryValue: primaryValues) {
ISimpleAttributeValue primary = (ISimpleAttributeValue) primaryValue;
if (primary.isOv() && primary.getBooleanValue()) {
activeAddressesCount++;
}
}
if (activeAddressesCount > 1) {
break;
}
}
if (activeAddressesCount != 1) {
List < Locale > acceptedLocales = data.getAcceptedLocales();
Locale locale = !acceptedLocales.isEmpty() ? acceptedLocales.get(0) : Locale.ENGLISH;
ResourceBundle bundle = ResourceBundle.getBundle("messages",
locale);
String errorMessageKey = activeAddressesCount < 1 ? NO_PRIMARY_ADDRESS_KEY : MANY_PRIMARY_ADDRESSES_KEY;
String errorMessage = bundle.getString(errorMessageKey);
IValidationError error = new ValidationError(
ValidationErrorType.INCORRECT,
"",
dataObject.getType() + "/attributes/Address",
errorMessage
);
data.addValidationError(error);
}
return data;
}
}
Create Custom Validator
In LCA, there are many hooks that you can implement, for example,
beforeSave
, afterSave
,
beforeMerge
, and others. This example shows a custom validator,
which is an implementation of a validate LCA hook. This example shows a custom
validator, which is an implementation of a validate LCA hook. To create a custom
validator, add the latest released version of the LCA Framework to your project and
define a validate hook inside.
validate
method must always return a non null value.import com.reltio.lifecycle.framework.*;
import com.reltio.lifecycle.framework.validation.IValidationError;
import com.reltio.lifecycle.framework.validation.ValidationError;
import java.util.List;
public class FactoryMethods extends LifeCycleActionBase {
@Override
public ILifeCycleValidationData validate(IReltioAPI reltioAPI, ILifeCycleValidationData data) {
IObject object = data.getObject();
if (object == null) {
return data;
}
IAttributes attributes = object.getAttributes();
if (attributes == null) {
return data;
}
List<IAttributeValue> valuesOfCallingName = attributes.getAttributeValues("CallingName");
if (valuesOfCallingName.isEmpty()) {
IValidationError error = ValidationError.missedAttribute("configuration/entityTypes/HCP/attributes/CallingName",
object,
"Calling Name attribute is missing. Please, add this attribute");
data.addValidationError(error);
}
List<IAttributeValue> valuesOfMiddleName = attributes.getAttributeValues("MiddleName");
if (!valuesOfMiddleName.isEmpty()) {
for (IAttributeValue value : valuesOfMiddleName) {
if (value.getValue().toString().contains("Middle")) {
IValidationError error = ValidationError.incorrectAttribute(value,
"Middle Name cannot contain 'Middle'. Please, correct value of this attribute");
data.addValidationError(error);
}
}
}
List<IAttributeValue> valuesOfAttributeAddress = attributes.getAttributeValues("Address");
if (valuesOfAttributeAddress.size() > 2) {
IValidationError error = ValidationError.incorrectAttributeType("configuration/entityTypes/HCP/attributes/Address",
object,
"More than one Address is not possible");
data.addValidationError(error);
}
return data;
}
}
beforeSave
action code must avoid object changes when LCA
is executed from the Validation Services. This can be achieved by adding the
following check in LCA: public ILifeCycleObjectData beforeSave(IReltioAPI reltioAPI, ILifeCycleObjectData data) {
if(data.getActionType().equals(ActionType.VALIDATE)){
// Skip the execution of the before save hook as this call is from the validation service.
}
//Customer code implementation
}
Localize Validation Message
To localize validation messages inside customer validators, you can use the
LifeCycleValidation.getAcceptedLocales()
method. This method
returns a list of locales which it receives from the value of an
Accept-Language
header of a validate request.
Build Validation Service
To build the validation service, perform the following steps:
- For the web sub-project, execute the following command:
mvn clean package
- Find the validation.war file inside the /reltio-validation/webtarget directory.
Instantiate Validation Error
Reltio supports three ways to instantiate a validation error:
MISSED
error typeINCORRECT
error typeINCORRECT
entire attribute error type
MISSED Error Type
This error type is used when the attribute does not
have a value. However, according to the validation rule, it should have at least one
value. In this case, the author of this custom validator should use the
ValidationError.missedAttribute(...)
method. To create a new
validation error with MISSED error type, use:
ValidationError.missedAttribute(String missedAttrTypeUri, IObjectWithURI
parentObject, String validationMessage)
Example
If the FirstName
attribute is required, but
the entity that was sent to validation does not have an attribute, then the user
must be notified that the value for the FirstName
attribute type
was missed by using the ValidationError.missedAttribute(...)
method.
This error type is used when some value inside the attribute is incorrect. In this
case, the ValidationError.incorrectAttribute(...)
method should be
used.
To create a new validation with INCORRECT error type, use:
ValidationError.incorrectAttribute(IAttributeValue attrValue
,
String validationMessage
)
If the attribute
FirstName
can contain only letters, but some value in this
attribute inside the entity sent to validation has numbers, then this value should
be marked as incorrect by using the
ValidationError.incorrectAttribute(...)
method.
This error type is used for situations when the entire attribute type should be
marked as incorrect. In this case, the
ValidationError.incorrectAttributeType(...)
method should be
used.
To create a new validation to mark the entire attribute type as INCORRECT, use:
public static ValidationError incorrectAttributeType(String incorrectAttrTypeUri, IObjectWithURI parentObject, String validationMessage)
Assume that the attribute Phone is nested in the configuration and Phone has the sub-attribute Type. According to the validation rule, Phone can have only one value of a particular type. But, in the inside entity sent to validation more than one value of this type is found. You can use one of the following two options to correct this problem:
- Mark extra values as incorrect (like for the INCORRECT error type example).
- Mark the Phone attribute type as incorrect by using the
ValidationError.incorrectAttributeType(...)
method.
Built-in Validators
Validation Service has built-in validators that are executed every time you send a validation request. These validators are applied to the entity itself and to all new referenced entities.
Cardinality ValidatorThis validator checks that the OV values are aligned with the specified cardinality. This validator is applied to all simple and nested attributes of the entity, all simple and nested attributes of all relations, and to all new referenced entities.
Validation ErrorsThere are two possible validation errors. They are min cardinality and max cardinality.
Error for Min CardinalityThis validation error is returned if the min cardinality for attribute type in metadata, is greater than the number of OV values in the attribute.
Example
{
"errorType": "INCORRECT",
"message": "Attribute Phone must have at least 1 ov value(s), but it has 0",
"objectParentUri": "relations/aJejHNF",
"objectTypeUri": "configuration/relationTypes/HasPhone/attributes/Phone",
"objectUri": null
}
Error for Max Cardinality
This validation error is returned in such a case that the max cardinality for the attribute type in metadata, is less that the number of values in the attribute.
Example
{
"errorType": "INCORRECT",
"message": "Attribute Phone must have at most 1 ov value(s), but it has 2",
"objectParentUri": "relations/aJejHNF",
"objectTypeUri": "configuration/relationTypes/HasPhone/attributes/Phone",
"objectUri": null
}
Configuring Validation Service for the UI
To enable
cardinality and the lookups validation service defined in the environment, define
ValidationServicePath
for the environment.
In the reltio-ui-configuration/<env>/ui.properties file, specify the URL of the Validation Service:
ValidationServicePath =https://tst‐01‐validation.reltio.com/
Create Project for Validation Hook
- Use the following pom.dita into the Java project:
Example: Validation service.pom.dita
<dependencies> <dependency> <groupId>com.reltio</groupId> <artifactId>life-cycle-framework</artifactId> <version>[2019.1.0.0.0, 2019.2.0.0.1)</version> </dependency> <dependency> <groupId>com.reltio.services.lca</groupId> <artifactId>reltio-lca-service-test-framework</artifactId> <version>[2019.1.0.0.0, 2019.2.0.0.1)</version> <scope>test</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.4</version> <scope>provided</scope> </dependency> </dependencies> <repositories> <repository> <id>reltio-libs-release</id> <name>Reltio release repository</name> <url>http://repo.dev.reltio.com/content/repositories/releases</url> </repository> </repositories>
- Create the validation hook based on the LCA functionality. For more information,
see LCA Framework.
Validation service that checks all not-ignored values and shows an error message.
Validation Service Example:
public class RDMValidationExample extends LifeCycleActionBase { @Override public ILifeCycleValidationData validate(IReltioAPI reltioAPI, ILifeCycleValidationData data) { IObject object = data.getObject(); if (object != null) { validateInternal(data, object.getAttributes()); } return data; } private void validateInternal(ILifeCycleValidationData data, IAttributes attributes) { if (attributes == null) { return; } for (IAttributeValue value : attributes.getAllAttributeValues()) { if (value instanceof INestedAttributeValue) { INestedAttributeValue nestedValue = (INestedAttributeValue) value; validateInternal(data, nestedValue.getValue()); } else if (value instanceof IReferenceAttributeValue) { IReferenceAttributeValue referenceValue = (IReferenceAttributeValue) value; validateInternal(data, referenceValue.getValue()); } else { Map<String, Object> attributeValueFields = value.toMap(); if (!value.isIgnored() && attributeValueFields.containsKey("lookupError")){ String lookupError = attributeValueFields.get("lookupError").toString(); data.addValidationError(new ValidationError(ValidationErrorType.INCORRECT, value.getUri(), value.getType(), String.format("RDM transcode lookup error: %s", lookupError))); } } } }
- Build the created project (for example, RDMValidation.jar).
Deploy and Register Validation Service Project
- Deploy the created *.jar file.
- Configure the validation hook for the necessary entity types.
Example: MDM L3 Tenant Configuration: Configure Validation Hook
...
{
"URI": "configuration/entityTypes/ForRDM",
"label": "ForRDM",
"lifecycleActions": {
"validate": [
"RDMValidationExample"
]
},
...
- Open the MDM UI, and find the entity that has the MDM-RDM transcode problem.
- Open Edit mode, and click Save (this starts the
validation process).
The MDM UI executes validation service and displays the error message that was configured when you created the project for the validation hook.
Validate API
Validation Service provides endpoints to execute validation over the entity or the changes. All these endpoints are available to admins and users with access to the tenant.
Validate Entity
The operation applies available validators to the incoming entity. All non-existing reference attributes are sent to the validate hook as well. Validation Service assumes that the reference exists if there are no sub-attributes for it in JSON. Here is the syntax of the request.
Request
POST {ValidationServiceURL}/validate/{tenantId}/entity
Name | Required | Description |
---|---|---|
Authorization | Yes | Information about authentication access token in format Bearer
<accessToken> (For more information, see
Authentication API). |
Content-Type
| Yes |
Should be |
For more information about the access_token
, see the Authentication API.
Name | Required | Description |
---|---|---|
-- | Yes | The JSON object representing the entity. |
The operation applies available validators to the result of applying incoming changes to the entity. All reference attributes touched in update request are sent to the validate hook as well.
RequestPOST [ValidationServiceURL}/validate/{tenantID}/entity/{entityID}
Name | Required | Description |
---|---|---|
Authorization | Yes | Information about authentication access token in format Bearer
<accessToken> (For more information, see
Authentication API). |
Content-Type
| Yes |
Should be |
Name | Required | Description |
---|---|---|
-- | Yes | Array of JSON objects, where each object represents changing of an attribute. For more information, see Entities API. |
The response is always in the same format. It is a JSON object that represents the result of the validate operation. Result is a list of validation errors. An empty list means that there are no validation errors. A validation error object consists of the following fields:
errorType
[MISSED
,INCORRECT
]objectUri
objectParentUri
objectTypeUri
message
Here is an example of a failure response.
[
{
"errorType": "INCORRECT",
"message": "Local Number in this Phone starts with 123. It is incorrect",
"objectParentUri": "entities/X8MNwmi/attributes/Phone/1Tempo4rary1492538921993",
"objectTypeUri": "configuration/entityTypes/HCP/attributes/Phone",
"objectUri": "entities/X8MNwmi/attributes/Phone/4kMvEi/LocalNumber/sJu6Ahbh"
},
{
"errorType": "MISSED",
"message": "Rank is missed",
"objectParentUri": "entities/X8MNwmi/attributes/Phone/1Tempo4rary1492538921993",
"objectTypeUri": "configuration/entityTypes/HCP/attributes/Phone/attributes/Rank",
"objectUri": null
}
]
Validation Service provides endpoints to obtain useful information about the service itself.
VersionReturns the version of a service.
Request
GET {validation_serviceURL}/version
Name | Required | Description |
---|---|---|
Authorization | Yes | Information about authentication access token in format Bearer
<accessToken> (For more informaion, see
Authentication API). |
Content-Type
| Yes |
Should be |
If the request is successful, the response is a JSON object containing information about the version of the service.
{
"server": {
"hash": "2813888b5ca4e6296948a78ddcca8a75f50e83b2",
"branch": "master",
"commitTime": "11.01.2017 @ 20:55:56 MSK",
"release": "2017.1.0.0.0"
},
"sdk": {
"hash": "5db36287b8578cee9e7d1b9a1eccf89ce14983d4",
"branch": "5db36287b8578cee9e7d1b9a1eccf89ce14983d4",
"commitTime": "12.01.2017 @ 12:05:54 MSK",
"tag": "reltio-platform-sdk-2017.1.0.0.32",
"release": "2017.1.0.0.32"
}
}
Get the Version for a Particular Component
Server Request
GET../version/server
Response
{
"hash": "2813888b5ca4e6296948a78ddcca8a75f50e83b2",
"branch": "master",
"commitTime": "11.01.2017 @ 20:55:56 MSK",
"release": "2017.1.0.0.0"
}
SDK
GET../version/sdk
Response
{
"hash": "5db36287b8578cee9e7d1b9a1eccf89ce14983d4",
"branch": "5db36287b8578cee9e7d1b9a1eccf89ce14983d4",
"commitTime": "12.01.2017 @ 12:05:54 MSK",
"tag": "reltio-platform-sdk-2017.1.0.0.32",
"release": "2017.1.0.0.32"
}
Status
Returns the status of a service and is available to everyone.
Request
GET {ValidationServiceURL}/status
Response
The response is a JSON object containing information about the status of a service.
{
"Status": "Running",
"JVM_heap_size": "1589116928",
"Memory_used": "902518984"
}
Get Thread Dump
Returns the thread dump.
Request
GET {ValidationServiceURL}/status/threads
Response
A JSON object containing information about the running threads.
Example
{
"ContainerBackgroundProcessor[StandardEngine[Catalina]]": {
"cpu": 0,
"id": 21,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"Finalizer": {
"cpu": 0,
"id": 3,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"GC Daemon": {
"cpu": 0,
"id": 16,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"Hector.me.prettyprint.cassandra.connection.CassandraHostRetryService-1": {
"cpu": 0,
"id": 32,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"Hector.me.prettyprint.cassandra.connection.HostTimeoutTracker-1": {
"cpu": 0,
"id": 33,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"Hector.me.prettyprint.cassandra.connection.NodeAutoDiscoverService-1": {
"cpu": 0,
"id": 34,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"Hector.me.prettyprint.cassandra.connection.NodeFailDetectionService-1": {
"cpu": 0,
"id": 35,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"Keep-Alive-Timer": {
"cpu": 0,
"id": 56,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"RMI Scheduler(0)": {
"cpu": 0,
"id": 17,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"RMI TCP Accept-0": {
"cpu": 0,
"id": 12,
"state": "RUNNABLE",
"blockTime": -1,
"waitTime": -1
},
"RMI TCP Accept-1099": {
"cpu": 0,
"id": 13,
"state": "RUNNABLE",
"blockTime": -1,
"waitTime": -1
},
"Reference Handler": {
"cpu": 0,
"id": 2,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"Reltio Platform Heartbeat Thread": {
"cpu": 0,
"id": 36,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"Signal Dispatcher": {
"cpu": 0,
"id": 4,
"state": "RUNNABLE",
"blockTime": -1,
"waitTime": -1
},
"ajp-bio-8009-Acceptor-0": {
"cpu": 0,
"id": 24,
"state": "RUNNABLE",
"blockTime": -1,
"waitTime": -1
},
"ajp-bio-8009-AsyncTimeout": {
"cpu": 0,
"id": 25,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-Acceptor-0": {
"cpu": 0,
"id": 22,
"state": "RUNNABLE",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-AsyncTimeout": {
"cpu": 0,
"id": 23,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-1": {
"cpu": 0,
"id": 38,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-10": {
"cpu": 0,
"id": 53,
"state": "RUNNABLE",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-2": {
"cpu": 0,
"id": 39,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-3": {
"cpu": 0,
"id": 40,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-4": {
"cpu": 0,
"id": 41,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-5": {
"cpu": 0,
"id": 42,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-6": {
"cpu": 0,
"id": 43,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-7": {
"cpu": 0,
"id": 49,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-8": {
"cpu": 0,
"id": 51,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"http-bio-8080-exec-9": {
"cpu": 0,
"id": 52,
"state": "WAITING",
"blockTime": -1,
"waitTime": -1
},
"main": {
"cpu": 0,
"id": 1,
"state": "RUNNABLE",
"blockTime": -1,
"waitTime": -1
},
"s3 cleanse functions updater": {
"cpu": 0,
"id": 30,
"state": "TIMED_WAITING",
"blockTime": -1,
"waitTime": -1
}
}
Monitoring Endpoints
Validation Service provides the ability to obtain statistics on some methods' execution. The endpoints are available to admins.
Get Available Metrics
Returns metrics available for statistics collection.
Request
GET {ValidationServiceURL}/monitoring/availableMetrics
Name | Required | Description |
---|---|---|
Authorization | Yes | Information about authentication access token in format Bearer
<accessToken> (For more information, see
Authentication API). |
Content-Type
| Yes |
Should be |
Response
A JSON object containing information about the available metrics.
Example
[
"CPU",
"Memory",
"ValidationController.validate.override",
"ValidationController.validate.cumulative"
]
Get Statistics for a Metric
Returns statistics for a particular metric.
Request
GET {ValidationServiceURL}/monitoring/statistics_json?select={metric_name}
Name | Required | Description |
---|---|---|
Authorization | Yes | Information about authentication access token in format Bearer
<accessToken> (For more information, see
Authentication API). |
Content-Type
| Yes |
Should be |
Name | Required | Description |
---|---|---|
select | Yes | Any available metric. The list of available metrics can be
obtained using the GetAvailableMetrics request.
|
Response
JSON object containing information about statistics for a particular metric.
Example Request
GET {ValidationServiceURL}/monitoring/statistics_json?select=ValidationController.validate.override
Response
{
"1487234340000": {
"ValidationController.validate.override.frequency": "0.030721022",
"ValidationController.validate.override.totalexec": "5",
"ValidationController.validate.override.average": "49.87688",
"ValidationController.validate.override.variance": "872.3840221868805",
"ValidationController.validate.override.exectime": "249.3844"
},
"1487234220000": {
"ValidationController.validate.override.frequency": "0.041012462",
"ValidationController.validate.override.totalexec": "6",
"ValidationController.validate.override.average": "5494.1704",
"ValidationController.validate.override.variance": "1.7605326290808934E8",
"ValidationController.validate.override.exectime": "32965.0224"
}
}