UNPKG

validator-list

Version:
206 lines (172 loc) 7.98 kB
import * as _ from 'lodash'; import * as _String from 'underscore.string'; import { ValidationError } from 'error-list'; import { AbstractValidator } from './validators/abstract-validator'; import { FilterValidator } from './validators/filter-validator'; import { DefaultValueValidator } from './validators/default-value-validator'; import { RequiredValidator } from './validators/required-validator'; import { ArrayValidator } from './validators/array-validator'; import { BooleanValidator } from './validators/boolean-validator'; import { CompareValidator } from './validators/compare-validator'; import { NumberValidator } from './validators/number-validator'; import { EmailValidator } from './validators/email-validator'; import { RangeValidator } from './validators/range-validator'; import { RegularExpressionValidator } from './validators/regular-expression-validator'; import { StringValidator } from './validators/string-validator'; import { BaseValidator } from './validators/base-validator'; class Validator { errors: any = {}; requireErrors: string[] = []; constructor(public rules, public data, public attributeLabels = {}, public mixin = {}) { for (const rule of this.rules) { if (rule.length < 2) { throw new Error(`Rule should contain at least 2 arguments: ${rule}`); } const validator = _.isString(rule[1]) ? rule[1].trim() : rule[1]; if (!_.isFunction(validator) && !_.has(this.defaultValidators, validator)) { throw new Error(`Validator not found ${validator}`); } } this.rules = _.sortBy(this.rules, function (rule) { switch (rule[1]) { case 'required': return 1; case 'filter' : return 2; case 'default': return 4; default : return 3; } }); } defaultValidators = { filter: FilterValidator, default: DefaultValueValidator, required: RequiredValidator, array: ArrayValidator, boolean: BooleanValidator, compare: CompareValidator, double: NumberValidator, email: EmailValidator, id: { class: NumberValidator, options: { integerOnly: true, min: 1, tooSmall: '{attribute} must be correct id.' } }, in: RangeValidator, integer: { class: NumberValidator, options: { integerOnly: true } }, match: RegularExpressionValidator, number: NumberValidator, string: StringValidator }; validate() { for (const rule of this.rules) { const attributes = _.isArray(rule[0]) ? rule[0] : [rule[0]]; const validator = rule[1]; let options: any = rule[2] || {}; options.mixin = _.extend({}, this.mixin); if (_.isFunction(validator)) { for (const attribute of attributes) { if (this.isAvailableForValidation(this.data[attribute], attribute, options)) { const error = validator(this.data[attribute], this.getAttributeLabel(attribute, options.lowercaseLabel), options); if (error) { this.addError(attribute, error); } } } } else if (_.isString(validator)) { if (validator === 'filter') { for (const attribute of attributes) { const attributeLabel = this.getAttributeLabel(attribute, options.lowercaseLabel); if (_.isFunction(options.skip) && options.skip(this.data[attribute], attributeLabel, options)) { continue; } // hasOwnProperty returns false for getters/setters if (attribute in this.data) { this.data[attribute] = FilterValidator.validate(options.filter, this.data[attribute]); } } } else if (validator === 'required') { for (const attribute of attributes) { const attributeLabel = this.getAttributeLabel(attribute, options.lowercaseLabel); if (_.isFunction(options.skip) && options.skip(this.data[attribute], attributeLabel, options)) { continue; } const error = new RequiredValidator(attributeLabel, this.data[attribute], options).validate(); if (error) { this.addError(attribute, error); this.requireErrors.push(attribute); } } } else if (validator === 'default') { for (const attribute of attributes) { if (_.isFunction(options.skip) && options.skip(this.data[attribute], this.getAttributeLabel(attribute, options.lowercaseLabel), options)) { continue; } if (!this.isHasError(attribute)) { this.data[attribute] = DefaultValueValidator.validate(this.data[attribute], options.value); } } } else { let validatorClass = this.defaultValidators[validator]; if (!AbstractValidator.isPrototypeOf(validatorClass)) { options = _.extend(options, validatorClass.options); validatorClass = validatorClass.class; } for (const attribute of attributes) { if (this.isAvailableForValidation(this.data[attribute], attribute, options)) { const error = new validatorClass(this.getAttributeLabel(attribute, options.lowercaseLabel), this.data[attribute], options).validate(); if (error) { this.addError(attribute, error); } } } } } } return _.size(this.errors) ? this.errors : undefined; } isHasError(attribute) { return _.has(this.errors, attribute); } addError(attribute, error) { if (!this.errors[attribute]) { this.errors[attribute] = []; } this.errors[attribute].push(error); } isAvailableForValidation(value, attribute, options) { if (_.isFunction(options.skip) && options.skip(value, this.getAttributeLabel(attribute, options.lowercaseLabel), options)) { return false; } if (this.requireErrors.indexOf(attribute) !== -1) { return false; } if (options.skipOnEmpty && BaseValidator.isEmptyValue(value)) { return false; } if (options.skipOnError && this.isHasError(attribute)) { return false; } return true; } getAttributeLabel(attribute, lowercase) { let label = !_.isUndefined(this.attributeLabels) && !_.isUndefined(this.attributeLabels[attribute]) ? this.attributeLabels[attribute] : attribute; label = _String.humanize(label); return lowercase ? _String.decapitalize(label) : label; // label = label.replace(/_/g, ' ').trim(); // return lowercase ? _String.decapitalize(label) : _String.capitalize(label); } } export const validate = function (rules, data, attributeLabels?, mixin?) { return new Validator(rules, data, attributeLabels, mixin).validate(); };