Awesome
ts.validator.fluent
TypeScript based generic validation framework library
Demo Angular 6 CLI app using the framework library
Rules | Description |
---|---|
If | Used for program flow. The then part is only evaluated if the if part is true. |
ForEach | Used to iterate and apply validations to an array. |
ForStringProperty | Used to stack multiple validations against a single String property. |
ForDateProperty | Used to stack multiple validations against a single Date property. |
ForNumberProperty | Used to stack multiple validations against a single Number property. |
For (deprecated) | Used to stack multiple validations against a single property. |
ForType | Used to chain validation rules for a type against a single property. |
Required | Used to test if a property is true to a conditon. |
NotNull | Used to test if a property is not null. |
IsNull | Used to test if a property is null. |
ToResult | Returns the validation result. |
String Rules | Description |
---|---|
NotEmpty | Used to test if a string is not empty. |
IsEmpty | Used to test if a string is empty. |
Length | Used to test if a string length is between specified lengths. |
Matches | Used to test if a string matches a regular expression. |
NotMatches | Used to test if a string does not match a regular expression. |
Used to test if a string is a valid email address. | |
IsCreditCard | Used to test if a string is a valid credit card number. |
IsLowercase | Used to test if a string is lower case. |
IsUppercase | Used to test if a string is upper case. |
IsMixedcase | Used to test if a string is mixed case. |
IsNumeric | Used to test if a string is numeric. |
IsAlpha | Used to test if a string is alpha. |
IsAlphaNumeric | Used to test if a string is alpha numeric. |
IsGuid | Used to test if a string is guid/uuid. |
IsBase64 | Used to test if a string is base64. |
IsUrl | Used to test if a string is an url. |
IsCountryCode | Used to test if a string is a 2 letter country code. |
Contains | Used to test if a sub string is contained in the string. |
Date Rules | Description |
---|---|
IsDateOn | Used to test if a date is on the specified date. |
IsDateAfter | Used to test if a date is after the specified date. |
IsDateOnOrAfter | Used to test if a date is on or after the specified date. |
IsDateBefore | Used to test if a date is before the specified date. |
IsDateOnOrBefore | Used to test if a date is on or before the specified date. |
IsDateBetween | Used to test if a date is between two specified dates. |
IsDateLeapYear | Used to test if a date is in a leap year. |
Number Rules | Description |
---|---|
IsNumberEqual | Used to test if a number is equal to a specified number. |
IsNumberNotEqual | Used to test if a number is not equal to a specified number. |
IsNumberLessThan | Used to test if a number is less than a specified number. |
IsNumberLessThanOrEqual | Used to test if a number is less than or equal to a specified number. |
IsNumberGreaterThan | Used to test if a number is greater than a specified number. |
IsNumberGreaterThanOrEqual | Used to test if a number is greater than or equal to a specified number. |
CreditCard | Used to test if a number is a valid credit card number. |
- These rules are used to lay the validation rules for any model.
- These rules can be chained in a fluent manner.
- These rules are available via IValidator<T> interface the framework provides.
Sample usage:
Models
class Employee {
Name: string;
Password: string;
PreviousPasswords: string[];
CreditCards: CreditCard[];
Super: Super;
Email: string;
}
class CreditCard {
Number: string;
Name: string;
ExpiryDate: Date;
}
class Super {
Name: string;
Code: string;
}
Validation rules
/* Install npm package ts.validator.fluent and then import like below */
import { IValidator, Validator, ValidationResult } from 'ts.validator.fluent/dist';
let validateSuperRules = (validator: IValidator<Super>) : ValidationResult => {
return validator
.NotNull(m => m.Name, "Should not be null", "Super.Name.Null")
.NotNull(m => m.Code, "Should not be null", "Super.Code.Null")
.If(m => m.Name != null && m.Code != null, validator => validator
.NotEmpty(m => m.Name, "Should not be empty", "Super.Name.Empty")
.Matches(m => m.Code, "^[a-zA-Z]{2}\\d{4}$", "Should not be invalid", "Super.Code.Invalid")
.ToResult())
.ToResult();
};
let validateCreditCardRules = (validator: IValidator<CreditCard>) : ValidationResult => {
return validator
.NotNull(m => m.Name, "Should not be null", "CreditCard.Name.Null")
.NotNull(m => m.Number, "Should not be null", "CreditCard.Number.Null")
.NotNull(m => m.ExpiryDate, "Should not be null", "CreditCard.ExpiryDate.Null")
.If(m => m.Name != null && m.ExpiryDate != null, validator => validator
.NotEmpty(m => m.Name, "Should not be empty", "CreditCard.Name.Empty")
.IsCreditCard(m => m.Number, "Should not be invalid", "CreditCard.Number.Invalid")
.IsDateOnOrAfter(m => m.ExpiryDate, new Date(), "Should be on or after today's date", "CreditCard.ExpiryDate.Invalid")
.ToResult())
.ToResult();
};
let validateEmployeeRules = (validator: IValidator<Employee>) : ValidationResult => {
return validator
.NotEmpty(m => m.Name, "Should not be empty", "Employee.Name.Empty")
.NotNull(m => m.CreditCards, "Should not be null", "CreditCard.Null")
.NotNull(m => m.Super, "Should not be null", "Super.Null")
.NotEmpty(m => m.Email, "Should not be empty", "Employee.Email.Empty")
.If(m => m.Super != null, validator => validator.ForType(m => m.Super, validateSuperRules).ToResult())
.If(m => m.Email != '', validator =>
validator.Email(m => m.Email, "Should not be invalid", "Employee.Email.Invalid")
.ToResult())
.Required(m => m.CreditCards, (m, creditCards) => creditCards.length > 0, "Must have atleast 1 credit card", "Employee.CreditCards.Required")
.If(m => m.CreditCards != null && m.CreditCards.length > 0,
validator => validator
.ForEach(m => m.CreditCards, validateCreditCardRules)
.ToResult())
.If(m => m.Password != '', validator => validator
.ForStringProperty(m => m.Password, passwordValidator => passwordValidator
.Matches("(?=.*?[0-9])(?=.*?[a-z])(?=.*?[A-Z])", "Password strength is not valid", "Employee.Password.Strength")
.Required((m, pwd) => pwd.length > 3, "Password length should be greater than 3", "Employee.Password.Length")
.Required((m, pwd) => !m.PreviousPasswords.some(prevPwd => prevPwd == pwd), "Password is already used", "Employee.Password.AlreadyUsed")
.ToResult())
.ToResult())
.ToResult();
};
Populate models
let model = new Employee();
model.Name = "John Doe";
model.Password = "sD4A3";
model.PreviousPasswords = new Array<string>()
model.PreviousPasswords.push("sD4A");
model.PreviousPasswords.push("sD4A1");
model.PreviousPasswords.push("sD4A2");
var expiryDate = new Date();
model.CreditCards = new Array<CreditCard>();
var masterCard = new CreditCard();
masterCard.Number = "5105105105105100";
masterCard.Name = "John Doe"
masterCard.ExpiryDate = expiryDate;
var amexCard = new CreditCard();
amexCard.Number = "371449635398431";
amexCard.Name = "John Doe"
amexCard.ExpiryDate = expiryDate;
model.CreditCards.push(masterCard);
model.CreditCards.push(amexCard);
model.Super = new Super();
model.Super.Name = "XYZ Super Fund";
model.Super.Code = "XY1234";
model.Email = "john.doe@xyx.com";
Synchronous validation
let validationResult = new Validator(model).Validate(validateEmployeeRules);
Asynchronous validation
let validationResult = await new Validator(model).ValidateAsync(validateEmployeeRules);
Validation result
//Check if the model is valid.
let isValid = validationResult.IsValid;
//Get all errors.
let allErrors = validationResult.Errors;
//Get error for a particular identifier
let employeeNameError = validationResult.Identifier("Employee.Name.Empty");
//Get all errors which start with some identifier string.
//Below code will return Employee.Password.Strength and Employee.Password.Length and
//Employee.Password.AlreadyUsed errors
let superCodeErrors = validationResult.IdentifierStartsWith("Employee.Password");
Summary of above code snippets
- The models are Employee, Credit Card, Super.
- The Employee model has CreditCard and Super as the child models.
- The rules for Super, CreditCard and Employee validation are laid in the validateSuperRules, validateCreditCardRules and validateEmployeeRules functions, using the IValidator<T> interface the framework provides.
- The Super and CreditCard rules are chained and used in the Employee validation.
- The rules are the same for both Sync and Async.
- For Sync and Async validation, the Validate and ValidateAsync methods on the framework class Validator are used.
- The Employee object is passed to this class and goes through the validation rules laid.
- Each validation rule comprises of a property on which the validation will apply, a message for any error and an identifier string for the error.
- The identifier string is used to group messages together for a field.
- The framework provides an API called IdentifierStartsWith which fetches all the validation errors for a particular identifier starts with the text.
- Eg. "Employee.Password" will fetch all errors whose identifier starts with Employee.Password.
Inheritance support
Let us say there is a class Accountant that inherits from Employee.
Models
class Accountant extends Employee {
Code: string;
}
Validation rules
let validateAccountantRules = (validator: IValidator<Accountant>) : ValidationResult => {
return validator
.NotEmpty(m => m.Code, "Should not be empty")
.ToResult();
};
Populate models
let accountant = new Accountant();
accountant.Code = "ACC001";
//Employee data
accountant.Name = "John Doe";
accountant.Password = "sD4A3";
accountant.PreviousPasswords = new Array<string>()
accountant.PreviousPasswords.push("sD4A");
accountant.PreviousPasswords.push("sD4A1");
accountant.PreviousPasswords.push("sD4A2");
var expiryDate = new Date();
accountant.CreditCards = new Array<CreditCard>();
var masterCard = new CreditCard();
masterCard.Number = "5105105105105100";
masterCard.Name = "John Doe"
masterCard.ExpiryDate = expiryDate;
var amexCard = new CreditCard();
amexCard.Number = "371449635398431";
amexCard.Name = "John Doe";
amexCard.ExpiryDate = expiryDate;
accountant.CreditCards.push(masterCard);
accountant.CreditCards.push(amexCard);
accountant.Super = new Super();
accountant.Super.Name = "XYZ Super Fund";
accountant.Super.Code = "XY1234";
accountant.Email = "john.doe@xyx.com";
Synchronous validation
let validationResult = new Validator(accountant).ValidateBase(validateEmployeeRules)
.Validate(validateAccountantRules);
Asynchronous validation
let validationResult = await new Validator(accountant).ValidateBaseAsync(validateEmployeeRules)
.ValidateAsync(validateAccountantRules);
Summary of above code snippets
- The Accountant model inherits from Employee.
- The validation rules for Accountant model (validateAccountantRules) only validate the properties of the Accountant class.
- The base class Employee is validated using ValidateBase and ValidateBaseAsync methods and the Employee validation rules.