UNPKG

qantra-pineapple

Version:

advanced json object validator specially designed to validate user json input and return readable error messages

389 lines (335 loc) 8.38 kB
# pineapple advanced json object validator specially designed to validate user json input and return readable error messages ![alt text](./pineapple.jpg "juice") # Install ```npm install qantra-pineapple``` # Change Log * Starting from version 1.0.9, validators can return true, false or value. if value is returned it will override the real object value in the same exact path when the trim/format function is called. this feature has been tested on none array values and is yet to be implemented for array values # Simple Usuage ``` let Pineapple = require('../index.js'); let pine = new Pineapple(); const run = async ()=>{ let schema = [ /** will fail in length */ { path: 'user.firstname', label: 'First Name', length: { min:3 , max: 8 }, }, /** will fail in regex*/ { path: 'user.email', label: 'Email', regex: '^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$', }, /** will fail in type */ { path: 'user.age', label: 'Age', type: 'Number', }, /** will fail in option */ { path: 'user.gender', oneOf: ['male','female'], required: true, }, /** will fail in reqiured */ { path: 'user.licenseId', label: 'license Id', required: true, } ] let payload = { user: { firstname: 'bahi hussein abdel baset', email: 'jumbo@mumbo@shit', age: 'twelve', gender: 'monkey', } } let error = await pine.validate(payload, schema); if(error){ console.log(error); /*** OUTPUT [ { label: 'First Name', path: 'user.firstname', message: 'First Name has invalid length', log: '_length', errors: [] }, { label: 'Age', path: 'user.age', message: 'Age invalid type', log: '_type', errors: [] }, { label: 'user.gender', path: 'user.gender', message: 'user.gender invalid option', log: '_oneOf', errors: [] }, { label: 'license Id', path: 'user.licenseId', message: 'license Id is required', log: '_required', errors: [] } ] */ } } run(); ``` ### Using validation models to avoid repetition validation models can be used to define validation object that can be used in multiple schemes ``` const models = { id: { type: 'string' regex: '^[a-f\d]{24}$' } } pine = Pine({models}); let schema1 = [ { path: 'user.id', model: 'id' }, ] let schema2 = [ { path: 'product.id', model: 'id' } ] pine.validate(obj1, schema1); pine.validate(obj2, schema2); this will simply compine {...model, ...schemaObject} ``` # Validation Props * **label**: label is useful for returning a user readable message * **type**: can be 'string', 'number', 'array', 'object', 'boolean', 'date' * **required**: true or false * **length**: can be a number or object length: 5 , length: {min: 10, max: 100} * **regex**: takes a regex expression to match * **oneOf**: array of string from which the user value must be included ['male','female'] * **canParse**: takes a string 'int' , 'date' , 'float' check if a value can be parsed. * **items**: validates array items and takes an a validation model reference object if validating array of strings of validation models reference array if validating array of objects ### Advanced Example ``` //validation schema let validationModels = [ { model: 'name', label: 'Username', regex: /^([a-zA-Z0-9\s]{3,50})$/ }, { model: 'mobile', required: false, }, { model: 'age', label: 'Age', type: 'Number', }, { model: 'skills', label: 'User Skills', type: 'Array', length: {max:5}, items: { type: 'String', length: { min: 3, max: 50} } }, { model: 'gender', label: 'Gender', type: 'String', oneOf: ['male', 'female'], }, { model: 'books', label: 'User Books', type: 'Array', items: [ { label: 'Book Title', path: 'title', type: 'String', required: true, }, { label: 'Prints', path: 'prints', type: 'Array', items: [ { label: 'Country', path: 'country', type: 'String', required: true, }, { label: 'Release Date', path: 'release.date', canParse: 'date' } ] } ] } ] // user object let userInput = { user: { name: 'bahi hussein', age: '19', skills: ['coding','drawing', 'ts'], games: [ {title: 'outlast', score: 80}, {title: 'red alert', score: 100}, ], gender: 'ninja', books: [ { title: 'bezzels', prints: [ { country: 'egypt', release:{ date: 1586193356622 }, }, { country: 'uk', release:{ date: 'king toot' }, } ] } ], adult: true, } } //model reference let modelReference = [ { model: 'name', path: 'user.name', label: 'Writer Name'}, { model: 'mobile', path: 'user.mobile', required: true,}, { model: 'age', path: 'user.age' }, { model: 'skills', path: 'user.skills' }, { model: 'gender', path: 'user.gender' }, { model: 'books', path: 'user.books' }, ] let pineapple = new Pineapple({models: validationModels}); let result = pineapple.validate(userInput, modelReference); ``` console.log(JSON.stringify(result)) ``` [ { "label":"user.mobile", "path":"user.mobile", "message":"user.mobile is required", "log":"_required", "errors":[ ] }, { "label":"Age", "path":"user.age", "message":"Age invalid type", "log":"_type", "errors":[ ] }, { "label":"User Skills", "path":"user.skills", "message":"one of the User Skills items is invalid", "log":"_length @index(2)", "errors":[ ] }, { "label":"Gender", "path":"user.gender", "message":"Gender invalid option", "log":"_oneOf", "errors":[ ] }, { "label":"User Books", "path":"user.books", "message":"one of the User Books items is invalid", "log":"_items @index(0)", "errors":[ { "label":"Prints", "path":"prints", "message":"one of the Prints items is invalid", "log":"_items @index(1)", "errors":[ { "label":"Release Date", "path":"release.date", "message":"Release Date invalid parsing", "log":"_canParse", "errors":[ ] } ] } ] } ] ``` ## Methods * validate(payload, schema) validates object against schema * trim(payload, schema) trim's the object to match schema requirments # custom validator ``` const blockPine = new Pineapple({ models, customValidators: { 'switch': (data)=>{ let switchKeys = Object.keys(data); let valid = true; for(let i=0; i<switchKeys.length; i++){ if(typeof data[switchKeys[i]] !== "boolean"){ valid = false; break; } } return valid; }, counter: (data)=>{ let counterKeys = Object.keys(data); let valid = true; for(let i=0; i<counterKeys.length; i++){ if(!_.isNumber(data[counterKeys[i]])){ valid = false; break; } } return valid; } }}); ``` it can be used in the schema with prop custom:<custom validator link>