Data Validation

How to create complex validation expressions.

Key takeaways

Schema Defines validation schemas.
NotRule Negates a rule.
AndRule Validates combinations of rules created with AND logical operations.
OrRule Validates combinations of rules created with OR logical operations.
IncludedRule Checks that a list contains only specified values.
ExcludedRule Verifies that none of the values specified in the rule is present in a list of constants.
AtLeastOneExistsRule Checks that given a set of properties, at least one of them exists.
ValueComparisonRule Compares a value to a constant.

Introduction

In this tutorial, we will learn how to use a set of validation rules available in the Pip.Services toolkit. First, we will see the necessary pre-requisites. Then, we will see the Schema class, which provides a way to create validation schemas. Lastly, we will see the different validation rules through the use of examples.

Data validation

Pre-requisites

In order to perform validations, we need to import the Schema class and the different validation rules that we want to use. The following example shows how to import the Schema class and two validation rules named ValueComparisonRule and AndRule.

import { AndRule, Schema, ValueComparisonRule } from "pip-services4-data-node"
Not available
import (
	validate "github.com/pip-services4/pip-services4-go/pip-services4-data-go/validate"
)
Not available
from pip_services4_data.validate import Schema, ValueComparisonRule, AndRule

Not available

The schema component

The Schema class provides a mechanism to create validation schemas, which can later be used to validate objects, project properties, arrays and maps.

There are two ways to create a validation schema. The first is to create an instance of the Schema class and use a list with our validation rules as an input parameter. An example of this is:

// Comparing 1 < x < 10 by using a list of rules
let myRules = [new ValueComparisonRule("LTE", 10), new ValueComparisonRule("GTE", 1)];
let mySchema1 = new Schema(false, myRules);



Not available
// Comparing 1 < x < 10 by using a list of rules
myRules := []validate.IValidationRule{validate.NewValueComparisonRule("LTE", 10), validate.NewValueComparisonRule("GTE", 1)}
mySchema := validate.NewSchemaWithRules(false, myRules)
Not available
# Comparing 1 < x < 10 by using a list of rules
my_rules = [ValueComparisonRule("LTE", 10), ValueComparisonRule("GTE", 1)]
my_schema1 = Schema(rules=my_rules)

Not available

Alternatively, we can first create an instance and define our rule using the with_rule() method. An example of this approach is:

// Comparing 1 < x < 10 by using the AND rule
let mySchema2 = new Schema().withRule(new AndRule(new ValueComparisonRule("GTE", 1), new ValueComparisonRule("LTE", 10)));

Not available
// Comparing 1 < x < 10 by using the AND rule
mySchema2 := validate.NewSchema().WithRule(
	validate.NewAndRule(
		validate.NewValueComparisonRule("GTE", 1),
		validate.NewValueComparisonRule("LTE", 10),
))
Not available
# Comparing 1 < x < 10 by using the AND rule
my_schema2 = Schema().with_rule(AndRule(ValueComparisonRule("GTE", 1), ValueComparisonRule("LTE", 10)))

Not available

Validation mechanism

To perform a validation based on a specified schema, we need to use the validate() method available from the Schema() class.

This method returns an empty list if the validation was successful, and a list with result information if it wasn’t. As a result, we need to differentiate between both cases, and for the unsuccessful option, we need to invoke the method get_message() and/or get_code() to obtain the reason for failure and/or the result code.

In the example below, we examine two cases based on the rule that the value must be between one and ten. In the first case, we evaluate the value 0, which results in a BAD_VALUE code and the message “must GTE 1 but found 0”. In the second case, we evaluate 5, which results in an empty list and the message “Value within range”.

// Comparing 1 <= x <= 10 by using a list of rules
let myRules = [new ValueComparisonRule("LTE", 10), new ValueComparisonRule("GTE", 1) ];
let schema = new Schema(false, myRules);

// Case 1: bad value
let validation = schema.validate(0);

if (validation.length > 0) {
    // Case: bad value
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
} else {
    // Case: good value
    console.log("Value within range");
}


// Case 2: good value   
validation = schema.validate(5);

if (validation.length > 0) {
    // Case: bad value
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}
else {
    // Case: good value
    console.log("Value within range");
}


Not available
// Comparing 1 <= x <= 10 by using a list of rules
myRules := []validate.IValidationRule{
	validate.NewValueComparisonRule("LTE", 10),
	validate.NewValueComparisonRule("GTE", 1),
}

mySchema := validate.NewSchemaWithRules(false, myRules)

// Case 1: bad value
validation := mySchema.Validate(0)

if len(validation) > 0 {
	// Case: bad value
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
} else {
	// Case: good value
	fmt.Println("Value within range")
}

// Case 2: good value
validation = mySchema.Validate(5)

if len(validation) > 0 {
	// Case: bad value
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
} else {
	// Case: good value
	fmt.Println("Value within range")
}
Not available
# Comparing 1 <= x <= 10 by using a list of rules
my_rules = [ValueComparisonRule("LTE", 10), ValueComparisonRule("GTE", 1)]
schema = Schema(rules=my_rules)

validation = schema.validate(0)

# Case 1: bad value
if len(validation) > 0:
    # Case: bad value
    print(validation[0].get_message())
    print(validation[0].get_code())
else:
    # Case: good value
    print("Value within range")

# Case 2: good value    
validate_schema1_2 = schema.validate(5)

if len(validate_schema1_2) > 0:
    # Case: bad value
    print(validate_schema1_2[0].get_message())
    print(validate_schema1_2[0].get_code())
else:
    # Case: good value
    print("Value within range")

Not available

After running the above code, we get the following results:

figure 1

Validation rules

Pip.Services provides a comprehensive set of validation rules. In this section, we will see an explanation and examples of each of them.

NotRule

The NotRule class allows us to negate a given rule. The examples below show how to use it.

// NotRule - Case: value different from 1
let schema = new Schema().withRule(new NotRule(new ValueComparisonRule("EQ", 1)));

// Case 1: good value
let validation = schema.validate(2);

if (validation.length == 0) {
    console.log("No errors");
} else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


// Case 2: bad value
validation = schema.validate(1);

if (validation.length == 0) {
    console.log("No errors");
}
else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


Not available
// NotRule - Case: value different from 1
schema := validate.NewSchema().WithRule(validate.NewNotRule(validate.NewValueComparisonRule("EQ", 1)))

// Case 1: good value
validation := schema.Validate(2)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}

// Case 2: bad value
validation = schema.Validate(1)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}
Not available
# NotRule - Case: value different from 1
from pip_services4_data.validate import Schema,NotRule
schema = Schema().with_rule(NotRule(ValueComparisonRule("EQ", 1)))

# Case 1: good value
validation = schema.validate(2)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

# Case 2: bad value
validation = schema.validate(1)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())


Not available

After running the above code, we get the following output:

figure 2

AndRule

The AndRule class allows us to validate combinations of rules created with AND logical operations. The examples below show how to use it.

// AndRule - Case:  1<= x <= 10
let schema = new Schema().withRule(new AndRule(new ValueComparisonRule("GTE", 1), new ValueComparisonRule("LTE", 10)));

// Case 1: good value
let validation = schema.validate(1);

if (validation.length == 0) {
    console.log("No errors");
} else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


// Case 2: bad value
validation = schema.validate(12);

if (validation.length == 0) {
    console.log("No errors");
}
else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


Not available

// AndRule - Case:  1<= x <= 10
schema := validate.NewSchema().WithRule(validate.NewAndRule(
	validate.NewValueComparisonRule("GTE", 1),
	validate.NewValueComparisonRule("LTE", 10),
))

// Case 1: good value
validation := schema.Validate(1)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}

// Case 2: bad value
validation = schema.Validate(12)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}
Not available
# AndRule - Case:  1<= x <= 10
from pip_services4_data.validate import Schema,AndRule
schema = Schema().with_rule(AndRule(ValueComparisonRule("GTE", 1), ValueComparisonRule("LTE", 10)))

# Case 1: good value
validation = schema.validate(1)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

# Case 2: bad value
validation = schema.validate(12)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())


Not available

After running the above code, we get the following output:

figure 3

OrRule

The OrRule class allows us to validate combinations of rules created with OR logical operations. The examples below show how to use it.

// Or rule - Case x < 1 OR x > 10
let schema = new Schema().withRule(new OrRule(new ValueComparisonRule("LT", 1), new ValueComparisonRule("GT", 10)));

// Case 1: good value
let validation = schema.validate(0);

if (validation.length == 0) {
    console.log("No errors");
} else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


// Case 2: bad value
validation = schema.validate(5);

if (validation.length == 0) {
    console.log("No errors");
}
else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


Not available
// Or rule - Case x < 1 OR x > 10
schema := validate.NewSchema().WithRule(validate.NewOrRule(
	validate.NewValueComparisonRule("LT", 1),
	validate.NewValueComparisonRule("GT", 10),
))

// Case 1: good value
validation := schema.Validate(0)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}

// Case 2: bad value
validation = schema.Validate(5)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}
Not available
# Or rule - Case x < 1 OR x > 10
from pip_services4_data.validate import Schema,OrRule
schema = Schema().with_rule(OrRule(ValueComparisonRule("LT", 1), ValueComparisonRule("GT", 10)))

# Case 1: good value
validation = schema.validate(0)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

# Case 2: bad value
validation = schema.validate(5)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

Not available

After running the above code, we get the following output:

figure 4

IncludedRule

The IncludedRule class allows us to check that a value is included in a given set of constants. The examples below show how to use it.

// Included rule - Case: include 1, 2, 3
let schema = new Schema().withRule(new IncludedRule(1, 2, 3));

// Case 1: good value
let validation = schema.validate(2);

if (validation.length == 0) {
    console.log("No errors");
} else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


// Case 2: bad value
validation = schema.validate(10);

if (validation.length == 0) {
    console.log("No errors");
}
else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


Not available
// Included rule - Case: include 1, 2, 3
schema := validate.NewSchema().WithRule(validate.NewIncludedRule(1, 2, 3))

// Case 1: good value
validation := schema.Validate(2)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}

// Case 2: bad value
validation = schema.Validate(10)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}
Not available
# Included rule - Case: include 1, 2, 3
from pip_services4_data.validate import Schema,IncludedRule
schema = Schema().with_rule(IncludedRule(1, 2, 3))

# Case 1: good value
validation = schema.validate(2)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

# Case 2: bad value
validation = schema.validate(10)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

Not available

After running the above code, we get the following output:

figure 5

ExcludedRule

The ExcludedRule allows us to verify that a value is not included in a given set of constants. The examples below show how to use it.

// Excluded rule - Case: excluded values are 1, 2 3
let schema = new Schema().withRule(new ExcludedRule(1, 2, 3));

// Case 1: good value
let validation = schema.validate(10);

if (validation.length == 0) {
    console.log("No errors");
} else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


// Case 2: bad value
validation = schema.validate(2);

if (validation.length == 0) {
    console.log("No errors");
}
else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


Not available
// Excluded rule - Case: excluded values are 1, 2 3
schema := validate.NewSchema().WithRule(validate.NewExcludedRule(1, 2, 3))

// Case 1: good value
validation := schema.Validate(10)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}

// Case 2: bad value
validation = schema.Validate(2)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}
Not available
# Excluded rule - Case: excluded values are 1, 2 3
from pip_services4_data.validate import Schema,ExcludedRule
# 
schema = Schema().with_rule(ExcludedRule(1, 2, 3))

# Case 1: good value
validation = schema.validate(10)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

# Case 2: bad value
validation = schema.validate(2)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

Not available

After running the above code, we get the following output:

figure 6

AtLeastOneExistsRule

The AtLeastOneExistsRule class allows us to check that given a set of properties, at least one of them exists. The examples below show how to use it.

// Rule At least one exists - Case: field1, field2
let schema = new Schema().withRule(new AtLeastOneExistsRule("field1", "field2"));

// Case 1: good value
let validation = schema.validate({ "field1": 1 , "field2": "A" });

if (validation.length == 0) {
    console.log("No errors");
} else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


// Case 2: bad value
validation = schema.validate(2);

if (validation.length == 0) {
    console.log("No errors");
}
else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


Not available
// Rule At least one exists - Case: field1, field2
schema := validate.NewSchema().WithRule(validate.NewAtLeastOneExistsRule("field1", "field2"))

// Case 1: good value
validation := schema.Validate(map[string]interface{}{"field1": 1, "field2": "A"})

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}

// Case 2: bad value
validation = schema.Validate(map[string]interface{}{})

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}

Not available
# Rule At least one exists - Case: field1, field2
from pip_services4_data.validate import Schema,AtLeastOneExistsRule
schema = Schema().with_rule(AtLeastOneExistsRule("field1", "field2"))

# Case 1: good value
validation = schema.validate({ 'field1': 1, 'field2': "A" })
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

# Case 2: bad value
validation = schema.validate({ })
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

Not available

After running the above code, we get the following output:

figure 7

ValueComparisonRule

The ValueComparisonRule class allows us to create a validation rule that compares a value to a constant. The following examples show how to use it.

var schema = new Schema().withRule(new ValueComparisonRule("LT", 1));

// Case 1: good value
var validation = schema.validate(0);

if (validation.length == 0) {
    console.log("No errors");
} else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


// Case 2: bad value
validation = schema.validate(5);

if (validation.length == 0) {
    console.log("No errors");
}
else {
    console.log(validation[0].getMessage());
    console.log(validation[0].getCode());
}


Not available
schema := validate.NewSchema().WithRule(validate.NewValueComparisonRule("LT", 1))

// Case 1: good value
validation := schema.Validate(0)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}

// Case 2: bad value
validation = schema.Validate(5)

if len(validation) == 0 {
	fmt.Println("No errors")
} else {
	fmt.Println(validation[0].Message())
	fmt.Println(validation[0].Code())
}
Not available
from pip_services4_data.validate import Schema, ValueComparisonRule

# Case x < 1
schema = Schema().with_rule(ValueComparisonRule("LT", 1))

# Case 1: good value
validation = schema.validate(0)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

# Case 2: bad value
validation = schema.validate(5)
if len(validation) == 0:
    print("No errors")
else:
    print(validation[0].get_message())
    print(validation[0].get_code())

Not available

After running the above code, we get the following output:

figure 8

Wrapping up

In this tutorial, we have seen how to create different types of validation rules and apply them to different situations. These rules included NotRule, AndRule, OrRule, IncludedRule, ExcludedRule and AtLestOneExistsRule.

These rules are valuable in cases where we have to compile highly complex logical expressions, as they help to simplify our code, and thus, avoid unintentional errors.