1.3.0 • Published 6 years ago

ts.validator.fluent v1.3.0

Weekly downloads
432
License
MIT
Repository
-
Last release
6 years ago

ts.validator.fluent

TypeScript based generic validation framework library

NPM package

Demo Angular 6 CLI app using the framework library

RulesDescription
IfUsed for program flow. The then part is only evaluated if the if part is true.
ForEachUsed to iterate and apply validations to an array.
ForStringPropertyUsed to stack multiple validations against a single String property.
ForDatePropertyUsed to stack multiple validations against a single Date property.
ForNumberPropertyUsed to stack multiple validations against a single Number property.
For (deprecated)Used to stack multiple validations against a single property.
ForTypeUsed to chain validation rules for a type against a single property.
RequiredUsed to test if a property is true to a conditon.
NotNullUsed to test if a property is not null.
IsNullUsed to test if a property is null.
ToResultReturns the validation result.
String RulesDescription
NotEmptyUsed to test if a string is not empty.
IsEmptyUsed to test if a string is empty.
LengthUsed to test if a string length is between specified lengths.
MatchesUsed to test if a string matches a regular expression.
NotMatchesUsed to test if a string does not match a regular expression.
EmailUsed to test if a string is a valid email address.
IsCreditCardUsed to test if a string is a valid credit card number.
IsLowercaseUsed to test if a string is lower case.
IsUppercaseUsed to test if a string is upper case.
IsMixedcaseUsed to test if a string is mixed case.
IsNumericUsed to test if a string is numeric.
IsAlphaUsed to test if a string is alpha.
IsAlphaNumericUsed to test if a string is alpha numeric.
IsGuidUsed to test if a string is guid/uuid.
IsBase64Used to test if a string is base64.
IsUrlUsed to test if a string is an url.
IsCountryCodeUsed to test if a string is a 2 letter country code.
ContainsUsed to test if a sub string is contained in the string.
Date RulesDescription
IsDateOnUsed to test if a date is on the specified date.
IsDateAfterUsed to test if a date is after the specified date.
IsDateOnOrAfterUsed to test if a date is on or after the specified date.
IsDateBeforeUsed to test if a date is before the specified date.
IsDateOnOrBeforeUsed to test if a date is on or before the specified date.
IsDateBetweenUsed to test if a date is between two specified dates.
IsDateLeapYearUsed to test if a date is in a leap year.
Number RulesDescription
IsNumberEqualUsed to test if a number is equal to a specified number.
IsNumberNotEqualUsed to test if a number is not equal to a specified number.
IsNumberLessThanUsed to test if a number is less than a specified number.
IsNumberLessThanOrEqualUsed to test if a number is less than or equal to a specified number.
IsNumberGreaterThanUsed to test if a number is greater than a specified number.
IsNumberGreaterThanOrEqualUsed to test if a number is greater than or equal to a specified number.
CreditCardUsed 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.