1.1.7 • Published 10 years ago

pojo-fluent-validator v1.1.7

Weekly downloads
2
License
MIT
Repository
github
Last release
10 years ago

pojo-fluent-validator

pojo-fluent-validator is javascript library used to check and convert plain old javascript objects with fluent configuration syntax.

NOTE: Validator works with plain old javascript objects only. That means no classes, no functions, no prototypes - it works with object literals, primitive types and arrays. Validator doesn't change source object, instead it makes a copy of the object but doesn't call constructor and don't copy prototype.

Features

  • Validates and converts primitive types - numbers, strings, and any type - with custom validation rule.
  • Validates objects by validating each object property.
  • Validates hashes (maps) - objects where each property value has the same structure.
  • Validates arrays of primitive types or objects.
  • Composability - validators for complex objects uses regular validators, so nested value can be object, array, hash or primitive type.
  • Fluent syntax - validation rules are configured using chained method calls.

Usage

Installation

npm install pojo-fluent-validator

NOTE: validate function which returns a promise is not included in package, it looks like:

Javascript

import { validate as validateWithCallback } from "pojo-fluent-validator";

export function validate(value: any, ...validators) {
    if (!validators || !validators.length) {
        throw new Error("At least one validator is required");
    }

    return new Promise((resolve, reject) => {
        validateWithCallback(
            value,
            (errors, result) => {
                if (errors) {
                    reject(errors);
                }
                else {
                    resolve(result);
                };
            },
            ...validators);
    });
}

Typescript

import { validate as validateWithCallback, ValidationRule } from "pojo-fluent-validator";

export function validate<T>(value: any, ...validators: ValidationRule<T>[]): Promise<T> {
    if (!validators || !validators.length) {
        throw new Error("At least one validator is required");
    }

    return new Promise((resolve, reject) => {
        validateWithCallback(
            value,
            (errors, result) => {
                if (errors) {
                    reject(errors);
                }
                else {
                    resolve(result);
                };
            },
            ...validators);
    });
}

Single value validation example.

import { rules } from "pojo-fluent-validator";


const rule = rules.num().must(v => v > 0, { errorMessage: "Must be greater than zero!" });

validate("10", rule).then(v => {
    // Value successfuly converted to number and validated
    console.log(v, v + 2); // Outputs 10 12
}).catch(err => { 
    // Don't reach this point until value is invalid.
});

validate("sdf", rule).then(v => {
    // Value is invalid number, then block is not executed
).catch(err => {
    // err[""] contains validation messages for whole object
    console.log(err[""]); // Outputs ["Value is not a valid number"]
});

Object validation example. Shows validator composability.

import { rules } from "pojo-fluent-validator";


const positiveNumberRule = rules.num().must(v => v > 0);

const productRule = rules.obj({
    // Use previously defined rule
    id: positiveNumberRule,
    // Or inlined rule
    title: rules.str().notEmpty(),
    // Rule can be extended with additional conditions using fluent syntax.
    // .required() call need to fail on null values. Number validator by default passes nulls.    
    vendorPrice: positiveNumberRule.required(),
    // Second parameter of `must` rule is validating object
    // Inside .must(..) check we don't assume retailPrice can be null.
    // That's because required() rule stop rule chain on fail.
    // To control this use { stopOnFailure: false } as rule options.
    retailPrice: positiveNumberRule.required()
        .must((v, product) => product.retailPrice > product.vendorPrice, 
            { errorMessage: "Product should be profitable" })
});


// Valid object 
validate({
    id: 1,
    title: "Melon",
    // rules.num() parses number by default. 
    vendorPrice: "12.3",
    retailPrice: 14.44
}, productRule).then(p => {
    // Product successfuly validated
    // p.vendorPrice is now number.
    console.dir(p); 
}).catch(err => { 
    // Don't reach this point until value is invalid.
});

// Valid object 
validate({
    id: 1,
    title: "Melon",
    // rules.num() parses number by default. 
    vendorPrice: "12.3",
    retailPrice: 4.44
}, productRule).then(p => { })
.catch(err => { 
    // err is hash of "path.to.error.property.or[index]": ["validation", "messages"]
    console.log(err["deliveryPrice"]); // Outputs ["Product should be profitable"] 
});

Array validation

import { rules } from "pojo-fluent-validator";

const numArrayRule = rules.arr(
        rules.num().required().must(v => v > 0));

validate([1, 2, "3"], numArrayRule)
    .then(arr => {
        // Array validated. Third element converted from string.
        console.log(arr); // Outputs [1, 2, 3]
    });

validate([1, 2, "three"],  numArrayRule)
    .catch(err => {
        // For arrays error path is "[2]"
        console.log(err["[2]"]); // Outpus ["Value is not a valid number"] 
    }); 

Array of objects validation

import { rules } from "pojo-fluent-validator";

const objArrayRule = rules.arr(
        rules.obj({
            id: rules.num().required(),
            title: rules.str().required()
        }).required() // rule.obj passes null values by default
          .expandable() // Expandable object allows to have extra non-validatable properties
    );

const invalidArray = [{
    id: 1,
    title: "First"
}, {
    id: null,
    title: "Second"
}];

validate(invalidArray, objArrayRule)
    .catch(err => {
        // Rules pathes are nested in the way of plain javascript access operations
        console.log(err["[1].id"]); // Outputs ["Value is required"]
    });
1.1.7

10 years ago

1.1.6

10 years ago

1.1.5

10 years ago

1.1.4

10 years ago

1.1.3

10 years ago

1.1.2

10 years ago

1.1.1

10 years ago

1.1.0

10 years ago

1.0.5

10 years ago

1.0.4

10 years ago

1.0.3

10 years ago

1.0.2

10 years ago

1.0.1

10 years ago

1.0.0

10 years ago