UNPKG

2valid

Version:

JavaScript simple data validator

215 lines (182 loc) 6.93 kB
/*! * 2valid * Copyright(c) 2015-2017 ivanoff .$ curl -A cv ivanoff.org.ua * MIT Licensed */ 'use strict'; var types = require('./types'); // Check new model before registration function deepLook(obj, types) { if (typeof (obj) !== 'object') return obj; for (var key in obj) { if (!obj[key].type) { obj[key] = deepLook(obj[key], types); } else { if (!types[obj[key].type]) { throw new Error('No type ' + obj[key].type + ' in Types: key ' + key); } // check for range in new object if (typeof obj[key].min !== 'undefined' && typeof types[obj[key].type].min !== 'undefined' && types[obj[key].type].min > obj[key].min) { throw new Error('Key ' + key + ' minimal value (' + obj[key].min + ') is less than acceptable minimal in Types (' + types[obj[key].type].min + ')'); } if (typeof obj[key].max !== 'undefined' && typeof types[obj[key].type].max !== 'undefined' && types[obj[key].type].max < obj[key].max) { throw new Error('Key ' + key + ' maximal value (' + obj[key].max + ') is in excess of maximal acceptable value in Types (' + types[obj[key].type].max + ')'); } } // get properties and methods from Types for (var keyParent in types[obj[key].type]) { if (!obj[key][keyParent]) { obj[key][keyParent] = types[obj[key].type][keyParent]; } } } return obj; }; // check for required fields recursively function validateObjectRequired(options, modelObject, entity, parents, errors) { for (var key in modelObject) { if (!modelObject[key].type && entity) { validateObjectRequired( options, modelObject[key], entity[key], parents + '.' + key, errors); } else if (!options.notRequired && modelObject[key].required && (!entity || !entity[key])) { if (!errors.notFound) errors.notFound = []; var fieldName = parents + '.' + key; errors.notFound.push(fieldName); errors.text.push('Field ' + fieldName + ' not found'); } } return errors; }; // check for extra fields and match recursively function validateObjectEntity(modelObject, entity, parents, errors) { if (!errors) errors = {}; if (!errors.text) errors.text = []; if (!parents) parents = []; for (var key in entity) { var fieldName = parents + '.' + key; if (!modelObject || !modelObject[key]) { if (!errors.notRequired) errors.notRequired = []; errors.notRequired.push(fieldName); errors.text.push('Field ' + fieldName + ' not required'); } else if (!modelObject[key].type) { validateObjectEntity(modelObject[key], entity[key], [parents, key], errors); } else if (!modelObject[key].check(entity[key])) { if (!errors.notMatched) errors.notMatched = {}; errors.notMatched[fieldName] = modelObject[key].type; errors.text.push('Field ' + fieldName + ' not matched with type ' + modelObject[key].type); } } return errors; }; exports = module.exports = { // All avaible type defenitions are in types.js types: types, errors: [], registeredModels: [], // Register new model. Return 2valid object // Parameters: modelName - name of the model, modelObject - model object registerModel: function (modelName, modelObject) { // check for name, object and if model already exists if (!modelName) return ('Name is not defined'); if (!modelObject) return ('Model in ' + modelName + ' is not defined'); if (this.registeredModels[modelName]) return ('Model ' + modelName + ' is already registered'); var o = Object.create(modelObject); this.registeredModels[modelName] = deepLook(o, this.types); return false; }, // Show information of registered models. All registered models are stored in .registeredModels // If params.displayEverything is true, module will show additional info showModels: function (params) { var res = []; if (typeof (params) === 'undefined') params = { displayEverything: false }; if (!this.registeredModels || Object.keys(this.registeredModels).length === 0) { res.push('There is no registered models'); } else { res.push('List of registered models'); for (var modelName in this.registeredModels) { res.push(' - ' + modelName); if (params.displayEverything) { for (var key in this.registeredModels[modelName]) { res.push(' ' + key + ' : ' + this.registeredModels[modelName][key].type); } } } } return res.join('\n'); }, // Show expanded information of registared models showModelsExpanded: function () { return this.showModels({ displayEverything: true }); }, /** * @deprecated Since version 3.0. Will be deleted in version 4.0. Use check instead. */ validate: function (modelName, entity, options, next) { console.warn('Calling deprecated function! Use check instead of validate'); return this.check(modelName, entity, options, next); }, // Check if entity pass modelName's validation check: function (modelName, entity, options, next) { if (typeof options === 'function') next = options; if (!options) options = {}; var modelObject = this.registeredModels[modelName]; if (typeof modelName === 'object') { modelObject = deepLook(modelName, this.types); } else if (this.types[modelName]) { if (options.one) this.types[modelName].one = options.one; var result = this.types[modelName].check(entity) ? null : { notMatched: modelName }; return typeof next === 'function' ? next(result) : result; } var errors = validateObjectRequired( options, modelObject, entity, [], validateObjectEntity(modelObject, entity) ); if (!errors.text[0]) errors = {}; if (errors && errors.text) errors.text = errors.text.join('. '); if (typeof next === 'function') { next(Object.keys(errors).length ? errors : null); } else { return Object.keys(errors).length ? errors : {}; } }, valid: function (modelName, entity, options) { var result = this.check(modelName, entity, options); return Object.keys(result) == 0 || !result; }, getAllTypes: function () { return Object.keys(this.types); }, // Get one example for type getExample: function (type) { var examples = this.getExamples(type); return examples[0]; }, // Get one random example for type getRandomExample: function (type) { var examples = this.getExamples(type); return examples[Math.floor(Math.random() * examples.length)]; }, // Get all examples for type // Result: Array getExamples: function (type) { return this.types[type] ? this.types[type].examples : []; }, // 'Forget' about all registered models dispose: function () { this.registeredModels = []; return 1; }, };