├── .eslintignore ├── .gitignore ├── lib ├── messages │ ├── notAllowedMessage.js │ ├── notArrayMessage.js │ ├── notObjectMessage.js │ ├── notBooleanMessage.js │ ├── notDateMessage.js │ ├── notIntegerMessage.js │ ├── notNumberMessage.js │ ├── notStringMessage.js │ ├── notUUIDMessage.js │ ├── requiredMessage.js │ ├── notRegxFormatMessage.js │ ├── belowMinNumberMessage.js │ ├── notEmailMessage.js │ ├── aboveMaxNumberMessage.js │ ├── belowMinStringMessage.js │ └── aboveMaxStringMessage.js ├── helpers │ ├── index.js │ └── getRules.js ├── validators │ ├── required.js │ ├── date.js │ ├── array.js │ ├── integer.js │ ├── string.js │ ├── boolean.js │ ├── object.js │ ├── regx.js │ ├── number.js │ ├── uuid.js │ ├── index.js │ ├── email.js │ ├── max.js │ └── min.js ├── index.js └── hasErrors.js ├── src ├── messages │ ├── notAllowedMessage.js │ ├── notArrayMessage.js │ ├── notObjectMessage.js │ ├── notBooleanMessage.js │ ├── notDateMessage.js │ ├── notNumberMessage.js │ ├── notStringMessage.js │ ├── notUUIDMessage.js │ ├── requiredMessage.js │ ├── notIntegerMessage.js │ ├── notRegxFormatMessage.js │ ├── notEmailMessage.js │ ├── belowMinNumberMessage.js │ ├── aboveMaxNumberMessage.js │ ├── belowMinStringMessage.js │ └── aboveMaxStringMessage.js ├── helpers │ ├── index.js │ └── getRules.js ├── validators │ ├── date.js │ ├── integer.js │ ├── boolean.js │ ├── required.js │ ├── array.js │ ├── string.js │ ├── object.js │ ├── regx.js │ ├── uuid.js │ ├── number.js │ ├── email.js │ ├── index.js │ ├── max.js │ └── min.js ├── index.js └── hasErrors.js ├── .hound.yml ├── .prettierrc ├── __tests__ ├── messages │ ├── notArrayMessage.test.js │ ├── notAllowedMessage.test.js │ ├── notNumberMessage.test.js │ ├── notObjectMessage.test.js │ ├── notUUIDMessage.test.js │ ├── notIntegerMessage.test.js │ ├── requiredMessage.test.js │ ├── notDateMessage.test.js │ ├── notStringMessage.test.js │ ├── notBooleanMessage.test.js │ ├── notEmailMessage.test.js │ ├── notMinMessage.test.js │ └── notMaxMessage.test.js ├── helpers │ └── getRules.test.js ├── validators │ ├── max.test.js │ ├── array.test.js │ ├── object.test.js │ ├── string.test.js │ ├── date.test.js │ ├── boolean.test.js │ ├── uuid.test.js │ ├── required.test.js │ ├── email.test.js │ ├── regx.test.js │ ├── integer.test.js │ ├── number.test.js │ └── min.test.js └── hasErrors.test.js ├── .travis.yml ├── jest.config.js ├── .eslintrc ├── LICENSE.md ├── package.json ├── CONTRIBUTING.md └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | lib -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage -------------------------------------------------------------------------------- /lib/messages/notAllowedMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`${a} is not allowed.`; -------------------------------------------------------------------------------- /lib/messages/notArrayMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`${a} must be an array.`; -------------------------------------------------------------------------------- /lib/messages/notObjectMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`${a} must be an object.`; -------------------------------------------------------------------------------- /lib/helpers/index.js: -------------------------------------------------------------------------------- 1 | const getRules=require("./getRules");module.exports={getRules}; -------------------------------------------------------------------------------- /lib/messages/notBooleanMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} must be a boolean.`; -------------------------------------------------------------------------------- /lib/messages/notDateMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} is not a valid date.`; -------------------------------------------------------------------------------- /lib/messages/notIntegerMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} must be an integer.`; -------------------------------------------------------------------------------- /lib/messages/notNumberMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} must be a number.`; -------------------------------------------------------------------------------- /lib/messages/notStringMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} must be a string.`; -------------------------------------------------------------------------------- /lib/messages/notUUIDMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} is not a valid UUID.`; -------------------------------------------------------------------------------- /lib/messages/requiredMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} field is required.`; -------------------------------------------------------------------------------- /lib/messages/notRegxFormatMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} format is invalid.`; -------------------------------------------------------------------------------- /lib/messages/belowMinNumberMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=(a,b)=>`The ${a} must be at least ${b}.`; -------------------------------------------------------------------------------- /lib/messages/notEmailMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=a=>`The ${a} must be a valid email address.`; -------------------------------------------------------------------------------- /src/messages/notAllowedMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `${label} is not allowed.`; 2 | -------------------------------------------------------------------------------- /src/messages/notArrayMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `${label} must be an array.`; 2 | -------------------------------------------------------------------------------- /src/messages/notObjectMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `${label} must be an object.`; 2 | -------------------------------------------------------------------------------- /src/messages/notBooleanMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} must be a boolean.`; 2 | -------------------------------------------------------------------------------- /src/messages/notDateMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} is not a valid date.`; 2 | -------------------------------------------------------------------------------- /src/messages/notNumberMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} must be a number.`; 2 | -------------------------------------------------------------------------------- /src/messages/notStringMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} must be a string.`; 2 | -------------------------------------------------------------------------------- /src/messages/notUUIDMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} is not a valid UUID.`; 2 | -------------------------------------------------------------------------------- /src/messages/requiredMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} field is required.`; 2 | -------------------------------------------------------------------------------- /lib/messages/aboveMaxNumberMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=(a,b)=>`The ${a} must not be greater than ${b}.`; -------------------------------------------------------------------------------- /src/messages/notIntegerMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} must be an integer.`; 2 | -------------------------------------------------------------------------------- /src/messages/notRegxFormatMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} format is invalid.`; 2 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | eslint: 2 | enabled: true 3 | config_file: .eslintrc 4 | ignore_file: .eslintignore 5 | -------------------------------------------------------------------------------- /lib/messages/belowMinStringMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=(a,b)=>`The ${a} must be at least ${b} characters.`; -------------------------------------------------------------------------------- /src/helpers/index.js: -------------------------------------------------------------------------------- 1 | const getRules = require('./getRules'); 2 | 3 | module.exports = { getRules }; 4 | -------------------------------------------------------------------------------- /src/messages/notEmailMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = label => `The ${label} must be a valid email address.`; 2 | -------------------------------------------------------------------------------- /src/messages/belowMinNumberMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = (label, min) => `The ${label} must be at least ${min}.`; 2 | -------------------------------------------------------------------------------- /lib/messages/aboveMaxStringMessage.js: -------------------------------------------------------------------------------- 1 | module.exports=(a,b)=>`The ${a} must have less than or equal to ${b} characters.`; -------------------------------------------------------------------------------- /src/messages/aboveMaxNumberMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = (label, max) => `The ${label} must not be greater than ${max}.`; 2 | -------------------------------------------------------------------------------- /src/messages/belowMinStringMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = (label, min) => `The ${label} must be at least ${min} characters.`; 2 | -------------------------------------------------------------------------------- /src/messages/aboveMaxStringMessage.js: -------------------------------------------------------------------------------- 1 | module.exports = (label, max) => `The ${label} must have less than or equal to ${max} characters.`; 2 | -------------------------------------------------------------------------------- /lib/helpers/getRules.js: -------------------------------------------------------------------------------- 1 | const getRules=a=>"object"==typeof a?a.type.split("|").map(a=>a.trim()):a.split("|").map(a=>a.trim());module.exports=getRules; -------------------------------------------------------------------------------- /lib/validators/required.js: -------------------------------------------------------------------------------- 1 | const requiredMessage=require("../messages/requiredMessage");module.exports=({value:a,label:b}={})=>!a&&requiredMessage(b); -------------------------------------------------------------------------------- /lib/validators/date.js: -------------------------------------------------------------------------------- 1 | const notDateMessage=require("../messages/notDateMessage");module.exports=({value:a,label:b}={})=>!Date.parse(a)&¬DateMessage(b); -------------------------------------------------------------------------------- /lib/validators/array.js: -------------------------------------------------------------------------------- 1 | const notArrayMessage=require("../messages/notArrayMessage");module.exports=({value:a,label:b}={})=>!(a instanceof Array)&¬ArrayMessage(b); -------------------------------------------------------------------------------- /lib/validators/integer.js: -------------------------------------------------------------------------------- 1 | const integerMessage=require("../messages/notIntegerMessage");module.exports=({value:a,label:b}={})=>!Number.isInteger(a)&&integerMessage(b); -------------------------------------------------------------------------------- /lib/validators/string.js: -------------------------------------------------------------------------------- 1 | const notStringMessage=require("../messages/notStringMessage");module.exports=({value:a,label:b}={})=>"string"!=typeof a&¬StringMessage(b); -------------------------------------------------------------------------------- /lib/validators/boolean.js: -------------------------------------------------------------------------------- 1 | const notStringMessage=require("../messages/notBooleanMessage");module.exports=({value:a,label:b}={})=>"boolean"!=typeof a&¬StringMessage(b); -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "singleQuote": true, 4 | "eslintIntegration": true, 5 | "tabWidth": 2, 6 | "trailingComma": "all", 7 | "bracketSpacing": true 8 | } 9 | -------------------------------------------------------------------------------- /lib/validators/object.js: -------------------------------------------------------------------------------- 1 | const notObjectMessage=require("../messages/notObjectMessage");module.exports=({value:a,label:b}={})=>!!(a!==Object(a)||a instanceof Array)&¬ObjectMessage(b); -------------------------------------------------------------------------------- /lib/validators/regx.js: -------------------------------------------------------------------------------- 1 | const regxMessage=require("../messages/notRegxFormatMessage");module.exports=({value:a,label:b,valid:c}={})=>{const d=new RegExp(c);return!d.test(a)&®xMessage(b)}; -------------------------------------------------------------------------------- /src/validators/date.js: -------------------------------------------------------------------------------- 1 | const notDateMessage = require('../messages/notDateMessage'); 2 | 3 | module.exports = ({ value, label } = {}) => (Date.parse(value) ? false : notDateMessage(label)); 4 | -------------------------------------------------------------------------------- /src/validators/integer.js: -------------------------------------------------------------------------------- 1 | const integerMessage = require('../messages/notIntegerMessage'); 2 | 3 | module.exports = ({ value, label } = {}) => 4 | Number.isInteger(value) ? false : integerMessage(label); 5 | -------------------------------------------------------------------------------- /src/validators/boolean.js: -------------------------------------------------------------------------------- 1 | const notStringMessage = require('../messages/notBooleanMessage'); 2 | 3 | module.exports = ({ value, label } = {}) => 4 | typeof value === 'boolean' ? false : notStringMessage(label); 5 | -------------------------------------------------------------------------------- /src/validators/required.js: -------------------------------------------------------------------------------- 1 | const requiredMessage = require('../messages/requiredMessage'); 2 | 3 | module.exports = ({ value, label } = {}) => { 4 | if (value) return false; 5 | return requiredMessage(label); 6 | }; 7 | -------------------------------------------------------------------------------- /lib/validators/number.js: -------------------------------------------------------------------------------- 1 | const numberMessage=require("../messages/notNumberMessage");module.exports=({value:a,label:b,path:c}={})=>"query"===c||"params"===c?!!Number.isNaN(+a)&&numberMessage(b):"number"!=typeof a&&numberMessage(b); -------------------------------------------------------------------------------- /lib/validators/uuid.js: -------------------------------------------------------------------------------- 1 | const notUUIDMessage=require("../messages/notUUIDMessage"),regx=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;module.exports=({value:a,label:b}={})=>!regx.test(a)&¬UUIDMessage(b); -------------------------------------------------------------------------------- /src/validators/array.js: -------------------------------------------------------------------------------- 1 | const notArrayMessage = require('../messages/notArrayMessage'); 2 | 3 | module.exports = ({ value, label } = {}) => { 4 | if (value instanceof Array) return false; 5 | return notArrayMessage(label); 6 | }; 7 | -------------------------------------------------------------------------------- /src/validators/string.js: -------------------------------------------------------------------------------- 1 | const notStringMessage = require('../messages/notStringMessage'); 2 | 3 | module.exports = ({ value, label } = {}) => { 4 | if (typeof value === 'string') return false; 5 | return notStringMessage(label); 6 | }; 7 | -------------------------------------------------------------------------------- /src/helpers/getRules.js: -------------------------------------------------------------------------------- 1 | const getRules = rules => { 2 | if (typeof rules === 'object') { 3 | return rules.type.split('|').map(rule => rule.trim()); 4 | } 5 | return rules.split('|').map(rule => rule.trim()); 6 | }; 7 | 8 | module.exports = getRules; 9 | -------------------------------------------------------------------------------- /src/validators/object.js: -------------------------------------------------------------------------------- 1 | const notObjectMessage = require('../messages/notObjectMessage'); 2 | 3 | module.exports = ({ value, label } = {}) => { 4 | if (value === Object(value) && !(value instanceof Array)) return false; 5 | return notObjectMessage(label); 6 | }; 7 | -------------------------------------------------------------------------------- /src/validators/regx.js: -------------------------------------------------------------------------------- 1 | const regxMessage = require('../messages/notRegxFormatMessage'); 2 | 3 | module.exports = ({ value, label, valid } = {}) => { 4 | const regx = new RegExp(valid); 5 | if (regx.test(value)) return false; 6 | return regxMessage(label); 7 | }; 8 | -------------------------------------------------------------------------------- /__tests__/messages/notArrayMessage.test.js: -------------------------------------------------------------------------------- 1 | const notArray = require('../../src/messages/notArrayMessage'); 2 | 3 | describe('not-an-array-message', () => { 4 | test('should return `users must be an array.`', () => { 5 | expect(notArray('users')).toBe('users must be an array.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /lib/validators/index.js: -------------------------------------------------------------------------------- 1 | const fs=require("fs"),path=require("path"),basename=path.basename(__filename),validators={};fs.readdirSync(__dirname).filter(a=>0!==a.indexOf(".")&&a!==basename&&".js"===a.slice(-3)).forEach(a=>{validators[a.slice(0,-3)]=require(path.join(__dirname,a))}),module.exports=validators; -------------------------------------------------------------------------------- /__tests__/messages/notAllowedMessage.test.js: -------------------------------------------------------------------------------- 1 | const notAllowed = require('../../src/messages/notAllowedMessage'); 2 | 3 | describe('not-allowed-message', () => { 4 | test('should return `users is not allowed.`', () => { 5 | expect(notAllowed('users')).toBe('users is not allowed.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /__tests__/messages/notNumberMessage.test.js: -------------------------------------------------------------------------------- 1 | const notNumber = require('../../src/messages/notNumberMessage'); 2 | 3 | describe('not-a-number-message', () => { 4 | test('should return The age must be a number.', () => { 5 | expect(notNumber('age')).toBe('The age must be a number.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /__tests__/messages/notObjectMessage.test.js: -------------------------------------------------------------------------------- 1 | const notObject = require('../../src/messages/notObjectMessage'); 2 | 3 | describe('not-an-object-message', () => { 4 | test('should return `user must be an object.`', () => { 5 | expect(notObject('user')).toBe('user must be an object.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /__tests__/messages/notUUIDMessage.test.js: -------------------------------------------------------------------------------- 1 | const notUUID = require('../../src/messages/notUUIDMessage'); 2 | 3 | describe('not-a-uuid-message', () => { 4 | test('should return The userId is not a valid UUID.', () => { 5 | expect(notUUID('userId')).toBe('The userId is not a valid UUID.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /__tests__/messages/notIntegerMessage.test.js: -------------------------------------------------------------------------------- 1 | const notInteger = require('../../src/messages/notIntegerMessage'); 2 | 3 | describe('not-an-integer-message', () => { 4 | test('should return The age must be an integer.', () => { 5 | expect(notInteger('age')).toBe('The age must be an integer.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /__tests__/messages/requiredMessage.test.js: -------------------------------------------------------------------------------- 1 | const required = require('../../src/messages/requiredMessage'); 2 | 3 | describe('required-message', () => { 4 | test('should return The username field is required.', () => { 5 | expect(required('username')).toBe('The username field is required.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /__tests__/messages/notDateMessage.test.js: -------------------------------------------------------------------------------- 1 | const notDate = require('../../src/messages/notDateMessage'); 2 | 3 | describe('not-a-date-message', () => { 4 | test('should return The birtDate is not a valid date.', () => { 5 | expect(notDate('birthDate')).toBe('The birthDate is not a valid date.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /__tests__/messages/notStringMessage.test.js: -------------------------------------------------------------------------------- 1 | const notString = require('../../src/messages/notStringMessage'); 2 | 3 | describe('not-a-string-message', () => { 4 | test('should return The username must be a string.', () => { 5 | expect(notString('username')).toBe('The username must be a string.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /__tests__/messages/notBooleanMessage.test.js: -------------------------------------------------------------------------------- 1 | const notString = require('../../src/messages/notStringMessage'); 2 | 3 | describe('not-a-boolean-message', () => { 4 | test('should return The confirmed must be a boolean.', () => { 5 | expect(notString('confirmed')).toBe('The confirmed must be a string.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /lib/validators/email.js: -------------------------------------------------------------------------------- 1 | const notEmailMessage=require("../messages/notEmailMessage"),EMAIL_REGEX=/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;module.exports=({value:a,label:b}={})=>!EMAIL_REGEX.test(a)&¬EmailMessage(b); -------------------------------------------------------------------------------- /src/validators/uuid.js: -------------------------------------------------------------------------------- 1 | const notUUIDMessage = require('../messages/notUUIDMessage'); 2 | 3 | const regx = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; // eslint-disable-line no-useless-escape 4 | module.exports = ({ value, label } = {}) => (regx.test(value) ? false : notUUIDMessage(label)); 5 | -------------------------------------------------------------------------------- /__tests__/messages/notEmailMessage.test.js: -------------------------------------------------------------------------------- 1 | const notEmail = require('../../src/messages/notEmailMessage'); 2 | 3 | describe('not-an-email-message', () => { 4 | test('should return The email must be a valid email address.', () => { 5 | expect(notEmail('email')).toBe('The email must be a valid email address.'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /lib/validators/max.js: -------------------------------------------------------------------------------- 1 | const maxNumberMessage=require("../messages/aboveMaxNumberMessage"),maxStringMessage=require("../messages/aboveMaxStringMessage");module.exports=({value:d="",label:a,valid:b,isNumber:c}={})=>!("number"==typeof d&&d<=b||"string"==typeof d&&d.length<=b)&&("number"==typeof d||c?maxNumberMessage(a,b):maxStringMessage(a,b)); -------------------------------------------------------------------------------- /lib/validators/min.js: -------------------------------------------------------------------------------- 1 | const minNumberMessage=require("../messages/belowMinNumberMessage"),minStringMessage=require("../messages/belowMinStringMessage");module.exports=({value:d="",label:a,valid:b,isNumber:c}={})=>!("string"==typeof d&&d.length>=b||"number"==typeof d&&d>=b)&&("number"==typeof d||c?minNumberMessage(a,b):minStringMessage(a,b)); -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | env: 3 | global: 4 | - NODE_ENV=test 5 | language: node_js 6 | node_js: 7 | - "10.15.1" 8 | cache: 9 | directories: 10 | - node_modules 11 | branches: 12 | only: 13 | - master 14 | install: 15 | - npm install 16 | script: 17 | - npm run test 18 | after_success: 19 | - npm run coveralls 20 | -------------------------------------------------------------------------------- /src/validators/number.js: -------------------------------------------------------------------------------- 1 | const numberMessage = require('../messages/notNumberMessage'); 2 | 3 | module.exports = ({ value, label, path } = {}) => { 4 | if (path === 'query' || path === 'params') { 5 | return !Number.isNaN(Number(value)) ? false : numberMessage(label); 6 | } 7 | return typeof value === 'number' ? false : numberMessage(label); 8 | }; 9 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const hasErrors=require("./hasErrors"),requests=["params","body","query"],cheke=({errors:b="errors",...a})=>async(c,d,e)=>{if("string"!=typeof b)throw Error("errors should be a string");let f;return await new Promise(b=>{requests.forEach(async d=>{f||a[d]&&(f=await hasErrors({data:c[d],reqRules:a[d],path:d}),b())})}),f?d.status(400).json({[b]:f}):e()};module.exports=cheke; -------------------------------------------------------------------------------- /__tests__/helpers/getRules.test.js: -------------------------------------------------------------------------------- 1 | const { getRules } = require('../../src/helpers'); 2 | 3 | describe('getRules()', () => { 4 | test('should return an array`', () => { 5 | expect(getRules('required')).toBeDefined(); 6 | }); 7 | }); 8 | 9 | describe('getRules()', () => { 10 | test('should return an array`', () => { 11 | expect(getRules({ type: 'required' })).toBeDefined(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/validators/email.js: -------------------------------------------------------------------------------- 1 | const notEmailMessage = require('../messages/notEmailMessage'); 2 | 3 | const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; // eslint-disable-line no-useless-escape 4 | 5 | module.exports = ({ value, label } = {}) => { 6 | if (EMAIL_REGEX.test(value)) return false; 7 | return notEmailMessage(label); 8 | }; 9 | -------------------------------------------------------------------------------- /src/validators/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const basename = path.basename(__filename); 5 | 6 | const validators = {}; 7 | 8 | fs.readdirSync(__dirname) 9 | .filter(file => file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js') 10 | .forEach(file => { 11 | validators[file.slice(0, -3)] = require(path.join(__dirname, file)); 12 | }); 13 | 14 | module.exports = validators; 15 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | clearMocks: true, 4 | coverageDirectory: 'coverage', 5 | coveragePathIgnorePatterns: [ 6 | 'node_modules', 7 | 'lib', 8 | 'jest.config.js', 9 | 'package.json', 10 | 'src/index.js', 11 | ], 12 | verbose: true, 13 | coverageThreshold: { 14 | global: { 15 | functions: 80, 16 | lines: 80, 17 | statements: -10, 18 | }, 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /src/validators/max.js: -------------------------------------------------------------------------------- 1 | const maxNumberMessage = require('../messages/aboveMaxNumberMessage'); 2 | const maxStringMessage = require('../messages/aboveMaxStringMessage'); 3 | 4 | module.exports = ({ value = '', label, valid, isNumber } = {}) => { 5 | if ( 6 | (typeof value === 'number' && value <= valid) || 7 | (typeof value === 'string' && value.length <= valid) 8 | ) { 9 | return false; 10 | } 11 | 12 | if (typeof value === 'number' || isNumber) return maxNumberMessage(label, valid); 13 | return maxStringMessage(label, valid); 14 | }; 15 | -------------------------------------------------------------------------------- /src/validators/min.js: -------------------------------------------------------------------------------- 1 | const minNumberMessage = require('../messages/belowMinNumberMessage'); 2 | const minStringMessage = require('../messages/belowMinStringMessage'); 3 | 4 | module.exports = ({ value = '', label, valid, isNumber } = {}) => { 5 | if ( 6 | (typeof value === 'string' && value.length >= valid) || 7 | (typeof value === 'number' && value >= valid) 8 | ) { 9 | return false; 10 | } 11 | 12 | if (typeof value === 'number' || isNumber) return minNumberMessage(label, valid); 13 | return minStringMessage(label, valid); 14 | }; 15 | -------------------------------------------------------------------------------- /__tests__/messages/notMinMessage.test.js: -------------------------------------------------------------------------------- 1 | const minString = require('../../src/messages/belowMinStringMessage'); 2 | const minNumber = require('../../src/messages/belowMinNumberMessage'); 3 | 4 | describe('above-max-message', () => { 5 | test('should return The page must be at least 100.', () => { 6 | expect(minNumber('page', 100)).toBe('The page must be at least 100.'); 7 | }); 8 | 9 | test('should return The username must be at least characters.', () => { 10 | expect(minString('username', 10)).toBe('The username must be at least 10 characters.'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /__tests__/messages/notMaxMessage.test.js: -------------------------------------------------------------------------------- 1 | const notMaxString = require('../../src/messages/aboveMaxStringMessage'); 2 | const notMaxNumber = require('../../src/messages/aboveMaxNumberMessage'); 3 | 4 | describe('above-max-message', () => { 5 | test('should return The page must not be greater than 100.', () => { 6 | expect(notMaxNumber('page', 100)).toBe('The page must not be greater than 100.'); 7 | }); 8 | 9 | test('should return The username must have less than or equal to 10 characters.', () => { 10 | expect(notMaxString('username', 10)).toBe('The username must have less than or equal to 10 characters.',); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const hasErrors = require('./hasErrors'); 2 | 3 | const requests = ['params', 'body', 'query']; 4 | const cheke = ({ errors = 'errors', ...args }) => async (req, res, next) => { 5 | if (typeof errors !== 'string') throw Error('errors should be a string'); 6 | let failed; 7 | await new Promise(resolve => { 8 | requests.forEach(async value => { 9 | if (failed) return; 10 | if (args[value]) { 11 | failed = await hasErrors({ 12 | data: req[value], 13 | reqRules: args[value], 14 | path: value, 15 | }); 16 | resolve(); 17 | } 18 | }); 19 | }); 20 | 21 | if (failed) { 22 | return res.status(400).json({ [errors]: failed }); 23 | } 24 | return next(); 25 | }; 26 | 27 | module.exports = cheke; 28 | -------------------------------------------------------------------------------- /__tests__/validators/max.test.js: -------------------------------------------------------------------------------- 1 | const max = require('../../src/validators/max'); 2 | 3 | describe('max-validator', () => { 4 | test('should return The undefined must have less than or equal to undefined characters.', () => { 5 | expect(max()).toBe('The undefined must have less than or equal to undefined characters.'); 6 | }); 7 | 8 | test('should return The year must not be greater than 2019.', () => { 9 | expect(max({ value: 2020, label: 'year', valid: 2019 })).toBe('The year must not be greater than 2019.',); 10 | }); 11 | 12 | test('should return false.', () => { 13 | expect(max({ value: 2000, label: 'year', valid: 2019 })).toBe(false); 14 | }); 15 | 16 | test('should return false.', () => { 17 | expect(max({ value: 'usernameusername', label: 'username', valid: 20 })).toBe(false); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /__tests__/validators/array.test.js: -------------------------------------------------------------------------------- 1 | const array = require('../../src/validators/array'); 2 | 3 | describe('array-validator', () => { 4 | test('should return `undefined must be an array.`', () => { 5 | expect(array()).toBe('undefined must be an array.'); 6 | }); 7 | 8 | test('should return `users must be an array.`', () => { 9 | expect(array({ 10 | value: 'not an array', 11 | label: 'users', 12 | }),).toBe('users must be an array.'); 13 | }); 14 | 15 | test('should return `users must be an array.`', () => { 16 | expect(array({ 17 | value: {}, 18 | label: 'users', 19 | }),).toBe('users must be an array.'); 20 | }); 21 | 22 | test('should return false', () => { 23 | expect(array({ 24 | value: [], 25 | label: 'users', 26 | }),).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base", "prettier"], 3 | "env": { 4 | "jest": true 5 | }, 6 | "plugins": ["prettier"], 7 | "rules": { 8 | "import/no-dynamic-require": 0, 9 | "global-require": 0, 10 | "consistent-return": 0, 11 | "function-paren-newline": ["error", "multiline"], 12 | "curly": ["error", "multi-line"], 13 | "valid-jsdoc": [ 14 | "error", 15 | { 16 | "requireReturn": true, 17 | "requireReturnType": true, 18 | "requireParamDescription": false, 19 | "requireReturnDescription": true 20 | } 21 | ], 22 | "require-jsdoc": [ 23 | "error", 24 | { 25 | "require": { 26 | "FunctionDeclaration": true, 27 | "MethodDefinition": true, 28 | "ClassDeclaration": true 29 | } 30 | } 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /__tests__/validators/object.test.js: -------------------------------------------------------------------------------- 1 | const object = require('../../src/validators/object'); 2 | 3 | describe('object-validator', () => { 4 | test('should return `undefined must be an object.`', () => { 5 | expect(object()).toBe('undefined must be an object.'); 6 | }); 7 | 8 | test('should return `user must be an object.`', () => { 9 | expect(object({ 10 | value: 'not an object', 11 | label: 'user', 12 | }),).toBe('user must be an object.'); 13 | }); 14 | 15 | test('should return `user must be an object.`', () => { 16 | expect(object({ 17 | value: [], 18 | label: 'user', 19 | }),).toBe('user must be an object.'); 20 | }); 21 | 22 | test('should return false', () => { 23 | expect(object({ 24 | value: {}, 25 | label: 'user', 26 | }),).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /__tests__/validators/string.test.js: -------------------------------------------------------------------------------- 1 | const string = require('../../src/validators/string'); 2 | 3 | describe('string-validator', () => { 4 | test('should return The undefined must be a string.', () => { 5 | expect(string()).toBe('The undefined must be a string.'); 6 | }); 7 | 8 | test('should return The username must be a string.', () => { 9 | expect(string({ 10 | value: 21, 11 | label: 'username', 12 | }),).toBe('The username must be a string.'); 13 | }); 14 | 15 | test('should return The username must be a string.', () => { 16 | expect(string({ 17 | value: undefined, 18 | label: 'username', 19 | }),).toBe('The username must be a string.'); 20 | }); 21 | 22 | test('should return false', () => { 23 | expect(string({ 24 | value: 'username', 25 | label: 'username', 26 | }),).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /__tests__/validators/date.test.js: -------------------------------------------------------------------------------- 1 | const date = require('../../src/validators/date'); 2 | 3 | describe('date-validator', () => { 4 | test('should return The undefined is not a valid date.', () => { 5 | expect(date()).toBe('The undefined is not a valid date.'); 6 | }); 7 | 8 | test('should return The birthDate is not a valid date.', () => { 9 | expect(date({ 10 | value: 'date', 11 | label: 'birthDate', 12 | }),).toBe('The birthDate is not a valid date.'); 13 | }); 14 | 15 | test('should return The birthDate is not a valid date.', () => { 16 | expect(date({ 17 | value: 21, 18 | label: 'birthDate', 19 | }),).toBe('The birthDate is not a valid date.'); 20 | }); 21 | 22 | test('should return false', () => { 23 | expect(date({ 24 | value: '2019-01-07', 25 | label: 'birthDate', 26 | }),).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /__tests__/validators/boolean.test.js: -------------------------------------------------------------------------------- 1 | const boolean = require('../../src/validators/boolean'); 2 | 3 | describe('string-validator', () => { 4 | test('should return The undefined must be a boolean.', () => { 5 | expect(boolean()).toBe('The undefined must be a boolean.'); 6 | }); 7 | 8 | test('should return The confirmed must be a boolean.', () => { 9 | expect(boolean({ 10 | value: 21, 11 | label: 'confirmed', 12 | }),).toBe('The confirmed must be a boolean.'); 13 | }); 14 | 15 | test('should return The confirmed must be a boolean.', () => { 16 | expect(boolean({ 17 | value: undefined, 18 | label: 'confirmed', 19 | }),).toBe('The confirmed must be a boolean.'); 20 | }); 21 | 22 | test('should return false', () => { 23 | expect(boolean({ 24 | value: true, 25 | label: 'confirmed', 26 | }),).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /__tests__/validators/uuid.test.js: -------------------------------------------------------------------------------- 1 | const uuid = require('../../src/validators/uuid'); 2 | 3 | describe('date-validator', () => { 4 | test('should return The undefined is not a valid UUID.', () => { 5 | expect(uuid()).toBe('The undefined is not a valid UUID.'); 6 | }); 7 | 8 | test('should return The userId is not a valid UUID.', () => { 9 | expect(uuid({ 10 | value: 'date', 11 | label: 'userId', 12 | }),).toBe('The userId is not a valid UUID.'); 13 | }); 14 | 15 | test('should return The userId is not a valid UUID.', () => { 16 | expect(uuid({ 17 | value: 21, 18 | label: 'userId', 19 | }),).toBe('The userId is not a valid UUID.'); 20 | }); 21 | 22 | test('should return false', () => { 23 | expect(uuid({ 24 | value: '8320ceae-987e-464e-abbd-df0315be4623', 25 | label: 'userId', 26 | }),).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /__tests__/validators/required.test.js: -------------------------------------------------------------------------------- 1 | const required = require('../../src/validators/required'); 2 | 3 | describe('required-validator', () => { 4 | test('should return The undefined field is required.', () => { 5 | expect(required()).toBe('The undefined field is required.'); 6 | }); 7 | 8 | test('should return The username field is required.', () => { 9 | expect(required({ 10 | value: undefined, 11 | label: 'username', 12 | }),).toBe('The username field is required.'); 13 | }); 14 | 15 | test('should return The username field is required.', () => { 16 | expect(required({ 17 | value: null, 18 | label: 'username', 19 | }),).toBe('The username field is required.'); 20 | }); 21 | 22 | test('should return false.', () => { 23 | expect(required({ 24 | value: 'username', 25 | label: 'username', 26 | }),).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /__tests__/validators/email.test.js: -------------------------------------------------------------------------------- 1 | const email = require('../../src/validators/email'); 2 | 3 | describe('email-validator', () => { 4 | test('should return The undefined must be a valid email address.', () => { 5 | expect(email()).toBe('The undefined must be a valid email address.'); 6 | }); 7 | 8 | test('should return The email must be a valid email address.', () => { 9 | expect(email({ 10 | value: 'not a number', 11 | label: 'email', 12 | }),).toBe('The email must be a valid email address.'); 13 | }); 14 | 15 | test('should return The email must be a valid email address.', () => { 16 | expect(email({ 17 | value: 21, 18 | label: 'email', 19 | }),).toBe('The email must be a valid email address.'); 20 | }); 21 | 22 | test('should return false', () => { 23 | expect(email({ 24 | value: 'email@email.com', 25 | label: 'email', 26 | }),).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /__tests__/validators/regx.test.js: -------------------------------------------------------------------------------- 1 | const regx = require('../../src/validators/regx'); 2 | 3 | const regxPassword = '^([A-Za-z]){8,}$'; // eslint-disable-line no-useless-escape 4 | 5 | describe('regx-validator', () => { 6 | test('should return The password format is invalid.', () => { 7 | expect(regx({ 8 | value: 'passwor', 9 | label: 'password', 10 | valid: regxPassword, 11 | }),).toBe('The password format is invalid.'); 12 | }); 13 | 14 | test('should return The password format is invalid.', () => { 15 | expect(regx({ 16 | value: 'passwor1', 17 | label: 'password', 18 | valid: regxPassword, 19 | }),).toBe('The password format is invalid.'); 20 | }); 21 | 22 | test('should return false for empty value and regx.', () => { 23 | expect(regx()).toBe(false); 24 | }); 25 | 26 | test('should return false when the value is correct', () => { 27 | expect(regx({ 28 | value: 'password', 29 | label: 'password', 30 | valid: regxPassword, 31 | }),).toBe(false); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /lib/hasErrors.js: -------------------------------------------------------------------------------- 1 | const validators=require("./validators"),notAllowedMessage=require("./messages/notAllowedMessage"),{getRules}=require("./helpers"),valids=Object.keys(validators),hasErrors=({data:c={},reqRules:a,path:b}={})=>new Promise((d,e)=>{a||d(!1);const f={...c,...a};Object.keys(f).forEach(f=>{(a[f]===void 0||"object"==typeof a[f]&&a[f].type===void 0)&&d({[f]:{path:b,message:notAllowedMessage(f)}}),a[f]&&("object"!=typeof a[f]||a[f].type)||e(Error(`${f}'s rule can not be empty`));const g=getRules(a[f]);Object.keys(g).forEach(e=>{const[h,i]=g[e].split(":");if(-1===valids.indexOf(h)&&d({[f]:{path:b,message:`${h} rule does not exist`}}),"undefined"==typeof c[f]&&-1!==g.indexOf("required")&&d({[f]:{path:b,message:validators.required({value:c[f],label:"object"==typeof a[f]?a[f].label||f:f})}}),"undefined"==typeof c[f]&&d(!1),"undefined"!=typeof c[f]){const e=validators[h]({value:c[f],label:"object"==typeof a[f]?a[f].label||f:f,valid:i,path:b,isNumber:g.indexOf("number")||g.indexOf("integer"),isRequired:-1!==g.indexOf("required")});e&&d({[f]:{path:b,message:e}})}})}),d(!1)});module.exports=hasErrors; -------------------------------------------------------------------------------- /__tests__/validators/integer.test.js: -------------------------------------------------------------------------------- 1 | const integer = require('../../src/validators/integer'); 2 | 3 | describe('integer-validator', () => { 4 | test('should return The undefined must be an integer.', () => { 5 | expect(integer()).toBe('The undefined must be an integer.'); 6 | }); 7 | 8 | test('should return The age must be an integer.', () => { 9 | expect(integer({ 10 | value: 'not an integer', 11 | label: 'age', 12 | }),).toBe('The age must be an integer.'); 13 | }); 14 | 15 | test('should return The age must be an integer.', () => { 16 | expect(integer({ 17 | value: '21', 18 | label: 'age', 19 | }),).toBe('The age must be an integer.'); 20 | }); 21 | 22 | test('should return The age must be an integer.', () => { 23 | expect(integer({ 24 | value: 25.5, 25 | label: 'age', 26 | }),).toBe('The age must be an integer.'); 27 | }); 28 | 29 | test('should return false', () => { 30 | expect(integer({ 31 | value: 21, 32 | label: 'age', 33 | }),).toBe(false); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Olivier Esuka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /__tests__/validators/number.test.js: -------------------------------------------------------------------------------- 1 | const number = require('../../src/validators/number'); 2 | 3 | describe('number-validator', () => { 4 | test('should return The undefined must be a number.', () => { 5 | expect(number()).toBe('The undefined must be a number.'); 6 | }); 7 | 8 | test('should return The age must be a number.', () => { 9 | expect(number({ 10 | value: 'not a number', 11 | label: 'age', 12 | }),).toBe('The age must be a number.'); 13 | }); 14 | 15 | test('should return The age must be a number.', () => { 16 | expect(number({ 17 | value: '21', 18 | label: 'age', 19 | }),).toBe('The age must be a number.'); 20 | }); 21 | 22 | test('should return false', () => { 23 | expect(number({ 24 | value: '21p', 25 | label: 'age', 26 | path: 'query', 27 | }),).toBe('The age must be a number.'); 28 | }); 29 | 30 | test('should return false', () => { 31 | expect(number({ 32 | value: '21', 33 | label: 'age', 34 | path: 'params', 35 | }),).toBe(false); 36 | }); 37 | 38 | test('should return false', () => { 39 | expect(number({ 40 | value: 21, 41 | label: 'age', 42 | }),).toBe(false); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cheke", 3 | "version": "1.0.5", 4 | "description": "Express request validator with object's style response body and inspired by Laravel's Validator", 5 | "author": "Olivier Esuka (https://oesukam.me)", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/oesukam/cheke.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/oesukam/cheke/issues" 13 | }, 14 | "homepage": "https://github.com/oesukam/cheke#readme", 15 | "main": "lib/index.js", 16 | "scripts": { 17 | "test": "jest --coverage", 18 | "test:watch": "jest --watch --coverage", 19 | "coveralls": "cat ./coverage/lcov.info | coveralls", 20 | "lint": "node_modules/.bin/eslint .", 21 | "lint:fix": "prettier-eslint --write '**/*.js'", 22 | "build": "jest && eslint . ./ && minify src -d lib" 23 | }, 24 | "keywords": [ 25 | "cheke", 26 | "check", 27 | "validator", 28 | "validation", 29 | "express", 30 | "express-validator", 31 | "request" 32 | ], 33 | "devDependencies": { 34 | "babel-minify": "^0.5.0", 35 | "coveralls": "^3.0.3", 36 | "eslint": "^4.18.2", 37 | "eslint-config-airbnb-base": "^13.1.0", 38 | "eslint-config-prettier": "^4.1.0", 39 | "eslint-plugin-import": "^2.16.0", 40 | "eslint-plugin-prettier": "^3.0.1", 41 | "jest": "^24.1.0", 42 | "prettier": "^1.16.4", 43 | "prettier-eslint-cli": "^4.7.1" 44 | }, 45 | "dependencies": {} 46 | } 47 | -------------------------------------------------------------------------------- /__tests__/validators/min.test.js: -------------------------------------------------------------------------------- 1 | const min = require('../../src/validators/min'); 2 | 3 | describe('min-validator', () => { 4 | test('should return The undefined must be at least undefined characters.', () => { 5 | expect(min()).toBe('The undefined must be at least undefined characters.'); 6 | }); 7 | 8 | test('should return The year must be at least 2019.', () => { 9 | expect(min({ 10 | value: 2018, 11 | label: 'year', 12 | valid: 2019, 13 | }),).toBe('The year must be at least 2019.'); 14 | }); 15 | 16 | test('should return false.', () => { 17 | expect(min({ 18 | value: 2020, 19 | label: 'year', 20 | valid: 2019, 21 | }),).toBe(false); 22 | }); 23 | 24 | test('should return false.', () => { 25 | expect(min({ 26 | value: 'usernameusername', 27 | label: 'username', 28 | valid: 6, 29 | }),).toBe(false); 30 | }); 31 | }); 32 | const max = require('../../src/validators/max'); 33 | 34 | describe('max-validator', () => { 35 | test('should return The undefined must have less than or equal to undefined characters.', () => { 36 | expect(max()).toBe('The undefined must have less than or equal to undefined characters.'); 37 | }); 38 | 39 | test('should return The year must not be greater than 2019.', () => { 40 | expect(max({ 41 | value: 2020, 42 | label: 'year', 43 | valid: 2019, 44 | }),).toBe('The year must not be greater than 2019.'); 45 | }); 46 | 47 | test('should return false.', () => { 48 | expect(max({ 49 | value: 2000, 50 | label: 'year', 51 | valid: 2019, 52 | }),).toBe(false); 53 | }); 54 | 55 | test('should return false.', () => { 56 | expect(max({ 57 | value: 'usernameusername', 58 | label: 'username', 59 | valid: 20, 60 | }),).toBe(false); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## We 💜 contributions 2 | 3 | While we love contributions, we also need to ensure that our library is of great quality. Thus we require you to follow some simple guidelines when you're submitting your contributions. 4 | 5 | ## Reporting Issues and Asking Questions 6 | 7 | Before opening an issue, please search the [issue tracker](https://github.com/oesukam/cheke/issues) to make sure your issue hasn’t already been reported. 8 | 9 | ## Development 10 | 11 | Visit the [issue tracker](https://github.com/oesukam/cheke/issues) to find a list of open issues that need attention. 12 | 13 | Fork, then clone the repo: 14 | 15 | ``` 16 | git clone https://github.com/oesukam/cheke.git 17 | ``` 18 | 19 | ### Testing 20 | 21 | To run tests: 22 | 23 | ``` 24 | npm run test // Will run tests once 25 | ``` 26 | 27 | To continuously watch and run tests, run the following: 28 | 29 | ``` 30 | npm run test:watch 31 | ``` 32 | 33 | ### Linting 34 | 35 | To Lint 36 | 37 | ``` 38 | npm run lint 39 | ``` 40 | 41 | ### Building 42 | 43 | To build run: 44 | 45 | ``` 46 | npm run build 47 | ``` 48 | 49 | ### Quality insurance 50 | 51 | The `build` command should handle it but always run the `lint` command to ensure we are all developing with the same code standards. 52 | 53 | ### Submitting a Pull Request 54 | 55 | For non-trivial changes, please open an issue with a proposal for a new feature or refactoring before starting on the work. We don’t want you to waste your efforts on a pull request that we won’t want to accept. 56 | 57 | On the other hand, sometimes the best way to start a conversation _is_ to send a pull request. Use your best judgement! 58 | 59 | 1. Open a new issue in the [Issue tracker](https://github.com/oesukam/cheke/issues) 60 | 1. Fork the repo 61 | 1. Create a new feature branch based off the `master` branch 62 | 1. Create breaking test(s) before implementing any fixes or functionality 63 | 1. Make your changes 64 | 1. Submit a pull request, referencing any issue that it resolves 65 | 66 | Thank you, we 💜 your contributions! 67 | -------------------------------------------------------------------------------- /src/hasErrors.js: -------------------------------------------------------------------------------- 1 | const validators = require('./validators'); 2 | const notAllowedMessage = require('./messages/notAllowedMessage'); 3 | const { getRules } = require('./helpers'); 4 | 5 | const valids = Object.keys(validators); 6 | /** 7 | * 8 | * @param {Object} data 9 | * @param {Object} reqRules - User validation rules 10 | * @param {String} path - Tha path of the request to be validated 11 | * @returns {Promise} - Success or failed object 12 | */ 13 | const hasErrors = ({ data = {}, reqRules, path } = {}) => 14 | new Promise((resolve, reject) => { 15 | if (!reqRules) resolve(false); 16 | const dataRules = { ...data, ...reqRules }; 17 | Object.keys(dataRules).forEach(key => { 18 | if ( 19 | reqRules[key] === undefined || 20 | (typeof reqRules[key] === 'object' && reqRules[key].type === undefined) 21 | ) { 22 | resolve({ 23 | [key]: { 24 | path, 25 | message: notAllowedMessage(key), 26 | }, 27 | }); 28 | } 29 | if (!reqRules[key] || (typeof reqRules[key] === 'object' && !reqRules[key].type)) { 30 | reject(Error(`${key}'s rule can not be empty`)); 31 | } 32 | const rules = getRules(reqRules[key]); 33 | Object.keys(rules).forEach(k => { 34 | const [rule, valid] = rules[k].split(':'); 35 | 36 | if (valids.indexOf(rule) === -1) { 37 | resolve({ [key]: { path, message: `${rule} rule does not exist` } }); 38 | } 39 | 40 | if (typeof data[key] === 'undefined' && rules.indexOf('required') !== -1) { 41 | resolve({ 42 | [key]: { 43 | path, 44 | message: validators.required({ 45 | value: data[key], 46 | label: typeof reqRules[key] === 'object' ? reqRules[key].label || key : key, 47 | }), 48 | }, 49 | }); 50 | } 51 | 52 | if (typeof data[key] === 'undefined') resolve(false); 53 | 54 | if (typeof data[key] !== 'undefined') { 55 | const failed = validators[rule]({ 56 | value: data[key], 57 | label: typeof reqRules[key] === 'object' ? reqRules[key].label || key : key, 58 | valid, 59 | path, 60 | isNumber: rules.indexOf('number') || rules.indexOf('integer'), 61 | isRequired: rules.indexOf('required') !== -1, 62 | }); 63 | if (failed) resolve({ [key]: { path, message: failed } }); 64 | } 65 | }); 66 | }); 67 | resolve(false); 68 | }); 69 | 70 | module.exports = hasErrors; 71 | -------------------------------------------------------------------------------- /__tests__/hasErrors.test.js: -------------------------------------------------------------------------------- 1 | const hasErrors = require('../src/hasErrors'); 2 | 3 | describe('hasErrors', () => { 4 | test("should return false if you don't provide reqRules", () => 5 | hasErrors().then(res => { 6 | expect(res).toBeFalsy(); 7 | })); 8 | 9 | test("should return name's rule can not be empty", () => 10 | hasErrors({ reqRules: { name: {} } }).catch(err => { 11 | expect(err.message).toBe("name's rule can not be empty"); 12 | })); 13 | 14 | test("should return name's rule can not be empty", () => 15 | hasErrors({ reqRules: { name: { type: '' } } }).catch(err => { 16 | expect(err.message).toBe("name's rule can not be empty"); 17 | })); 18 | 19 | test("should return name's rule can not be empty", () => 20 | hasErrors({ reqRules: { name: '' } }).catch(err => { 21 | expect(err.message).toBe("name's rule can not be empty"); 22 | })); 23 | 24 | test('should return The name field is required.', () => 25 | hasErrors({ reqRules: { name: 'required' } }).then(res => { 26 | expect(res.name.message).toBe('The name field is required.'); 27 | })); 28 | 29 | test('should return The age must be a number', () => 30 | hasErrors({ 31 | data: { age: '21k' }, 32 | reqRules: { age: 'number' }, 33 | }).then(res => { 34 | expect(res.age.message).toBe('The age must be a number.'); 35 | })); 36 | 37 | test('should return The age must be a number', () => 38 | hasErrors({ 39 | data: { age: '21' }, 40 | reqRules: { page: 'number' }, 41 | }).then(res => { 42 | expect(res.age.message).toBe('age is not allowed.'); 43 | })); 44 | 45 | test('should return The age must be a number', () => 46 | hasErrors({ 47 | data: { age: 21 }, 48 | reqRules: { age: 'number' }, 49 | }).then(res => { 50 | expect(res).toBe(false); 51 | })); 52 | 53 | describe('object rules', () => { 54 | test('should return The name field is required.', () => 55 | hasErrors({ reqRules: { name: { label: 'Name', type: 'required' } } }).then(res => { 56 | expect(res.name.message).toBe('The Name field is required.'); 57 | })); 58 | 59 | test('should return `The age must be a number.`', () => 60 | hasErrors({ 61 | data: { age: 'age' }, 62 | reqRules: { age: { label: 'age', type: 'number' } }, 63 | }).then(res => { 64 | expect(res.age.message).toBe('The age must be a number.'); 65 | })); 66 | 67 | test('should return `The age must be a number.` without label', () => 68 | hasErrors({ 69 | data: { age: 'age' }, 70 | reqRules: { age: { type: 'number' } }, 71 | }).then(res => { 72 | expect(res.age.message).toBe('The age must be a number.'); 73 | })); 74 | 75 | test('should return `The age field is required.` without data', () => 76 | hasErrors({ 77 | reqRules: { age: { type: 'number|required' } }, 78 | }).then(res => { 79 | expect(res.age.message).toBe('The age field is required.'); 80 | })); 81 | }); 82 | 83 | describe('no required rules', () => { 84 | test('should return false.', () => 85 | hasErrors({ reqRules: { name: { label: 'Name', type: 'string' } } }).then(res => { 86 | expect(res).toBeFalsy(); 87 | })); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cheke 2 | 3 | --- 4 | 5 | [![npm version](https://badge.fury.io/js/cheke.svg)](https://badge.fury.io/js/cheke) 6 | [![Build Status](https://travis-ci.org/oesukam/cheke.svg?branch=master)](https://travis-ci.org/oesukam/cheke) 7 | [![Coverage Status](https://coveralls.io/repos/github/oesukam/cheke/badge.svg?branch=master)](https://coveralls.io/github/oesukam/cheke?branch=master) 8 | [![Maintainability](https://api.codeclimate.com/v1/badges/f0f25d4e5bc5182f32a5/maintainability)](https://codeclimate.com/github/oesukam/cheke/maintainability) 9 | [![Known Vulnerabilities](https://snyk.io/test/github/oesukam/cheke/badge.svg?targetFile=package.json)](https://snyk.io/test/github/oesukam/cheke?targetFile=package.json) 10 | 11 | Express request validator with object's style response body and inspired by Laravel's Validator 12 | 13 | ## How to install 14 | 15 | ``` 16 | yarn add cheke 17 | or 18 | npm install --save cheke 19 | ``` 20 | 21 | ## How to use 22 | 23 | #### Rules 24 | 25 | 1. Array: `array` 26 | 1. Boolean: `boolean` 27 | 1. Date: `date` 28 | 1. Email: `email` 29 | 1. Integer: `integer` 30 | 1. Maximum: `max:maximumNumber` 31 | 1. Minimum: `min:minimumNumber` 32 | 1. Number: `number`, `number|min:200`, `number|max:200` 33 | 1. Regular Expression: `regx:^[a-z]{6,}` 34 | 1. Required: `required` 35 | 1. String: `string`, `string|min:200`, `string|max:200` 36 | 1. UUID: `uuid` 37 | 1. Object: `object` 38 | 39 | #### Types of validation 40 | 41 | - query -> `GET /validate-query?page=pageNumber` 42 | ``` 43 | app.get('/validate-query', cheke({ 44 | query: { 45 | pageNumber: 'required|number|min:1', 46 | } 47 | }),(req, res) => { 48 | return res.send({ message: 'Validated' }); 49 | }); 50 | ``` 51 | ``` 52 | app.get('/validate-query', cheke({ 53 | query: { 54 | pageNumber: { label: 'Page', type: 'required|number|min:1' }, 55 | } 56 | }),(req, res) => { 57 | return res.send({ message: 'Validated' }); 58 | }); 59 | ``` 60 | - params -> `GET /validate-params/:id` 61 | ``` 62 | app.get('/validate-params/:id', cheke({ 63 | params: { 64 | id: 'required|uuid' 65 | } 66 | }),(req, res) => { 67 | return res.send({ message: 'Validated' }); 68 | }); 69 | ``` 70 | ``` 71 | app.get('/validate-params/:id', cheke({ 72 | params: { 73 | id: { label: 'ID', type: 'required|uuid' } 74 | } 75 | }),(req, res) => { 76 | return res.send({ message: 'Validated' }); 77 | }); 78 | ``` 79 | - body -> `POST /validate-body` 80 | ``` 81 | app.post('/validate-body', cheke({ 82 | body: { 83 | username: 'required|string|min:6', 84 | password: 'required|string|min:6' 85 | } 86 | }),(req, res) => { 87 | return res.send({ message: 'Validated' }); 88 | }); 89 | ``` 90 | ``` 91 | app.post('/validate-body', cheke({ 92 | body: { 93 | username: { label: 'Username', type: 'required|string|min:6' }, 94 | password: { label: 'Password', type: 'required|string|min:6' } 95 | } 96 | }),(req, res) => { 97 | return res.send({ message: 'Validated' }); 98 | }); 99 | ``` 100 | 101 | #### Example `GET /validate?page=4` 102 | 103 | ``` 104 | const express = require('express'); 105 | const cheke = require('cheke); 106 | 107 | const app = express(); 108 | const { PORT = 3000 } = process.env 109 | 110 | app.use(express.json()) 111 | app.use(express.urlencoded({ extended: false })) 112 | 113 | app.get('/validate', cheke({ 114 | query: { 115 | page: 'required|number' 116 | } 117 | }),(req, res) => { 118 | return res.send({ message: 'Validated' }); 119 | }); 120 | 121 | app.use((req, res, next) => { 122 | const err = new Error('Not Found'); 123 | err.status = 404; 124 | next(err); 125 | }); 126 | 127 | app.use((err, req, res) => { 128 | res.status(err.status || 500).json({ 129 | errors: { 130 | message: err.message, 131 | error: err 132 | } 133 | }); 134 | }); 135 | 136 | app.listen(PORT, () => console.log(`Server listen on port ${PORT}...`)); 137 | 138 | ``` 139 | 140 | ## Issues 141 | 142 | Should it happen that the tool broke down on you please head to our [Issue tracker](https://github.com/oesukam/cheke/issues) 143 | 144 | 1. Search if the issue is already discussed or explained. 145 | 2. If no luck feel free to open a new issue and we will get back to you as soon as possible. 146 | 147 | ## Acknowledgments 148 | 149 | - Alpha Ogilo [Ogilo](https://github.com/Ogilo) 150 | - Grace Njoki Kimotho [kimotho-njoki](https://github.com/kimotho-njoki) 151 | - Rene Christian Nshogoza [filschristian](https://github.com/filschristian) 152 | --------------------------------------------------------------------------------