diff --git a/README.md b/README.md index edfce12..f8e9433 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,6 @@ Request field validator for expressjs $ npm install expressjs-field-validator ``` -## Dependencies - - [lodash](https://www.npmjs.com/package/lodash) - - [moment](https://www.npmjs.com/package/moment) - ## How To Use ```js @@ -85,13 +81,23 @@ validator([{param : 'id', location : 'params', isRequired : true}], { mode : 're |isEmail |`Boolean` |The value is `Email` or not (default `false`)| |isBoolean |`Boolean` |The value is `Boolean` or not (default `false`)| |isDate |`Boolean` |The value is `Date` or not (default `false`)| -|format |`String` |Date format. Please reffer https://momentjs.com/docs/ for date formats| +|format |`String` |Date format. (default `YYYY-MM-DD`)| |mobileNumber |`Object` |Object `{countryCode : '91', isCountryCodeMandatory : true, length: {min : 1, max : 10}}` ,describes characteristics of mobile number, length is the length range of mobile number excluding country code | |length |`Object` |Object `{min : 1, max : 10}` describes minimum and maximum length| |includes |`Object[]` |Value must be one of the element in the array| |excludes |`Object[]` |Value must not be one of the element in the array| |message |`String` |Error message thrown in case of test fails default : Invalid Field Error| +#### Supported date formats +If `isDate` is true you can pass `format`, Only supported the following +``` +YYYY-MM-DD +DD-MM-YYYY' +MM-DD-YYYY' +YYYY/MM/DD' +DD/MM/YYYY' +MM/DD/YYYY' +``` #### Nested Objets In case of `Object` or `Array`, `isArray` or `isObject` must be true if json structure is diff --git a/lib/validator/validator.js b/lib/validator/validator.js index be4baba..dbac51a 100644 --- a/lib/validator/validator.js +++ b/lib/validator/validator.js @@ -1,8 +1,5 @@ "use strict"; -const _ = require('lodash'); -const moment = require('moment'); - module.exports = (validation = [], response = {}) => { const validator = (() => { const validate = (validation, req) => { @@ -27,13 +24,13 @@ module.exports = (validation = [], response = {}) => { } const checkEmpty = (value) => { //Checking if value is empty - if (_.isObject(value)) { + if (value instanceof Object) { //Array or Object if (!value || JSON.stringify(value).length === 2) { return true; } - } else if (isNaN(value) && !_.isBoolean(value) && (!value || value === 'undefined' || value.trim() === '')) { + } else if (isNaN(value) && value !== !!value && (!value || value === 'undefined' || value.trim() === '')) { //Not Number Not Boolean => String return true; } @@ -68,6 +65,53 @@ module.exports = (validation = [], response = {}) => { } } } + + const dateCheck = (dateString, format) => { + if (!dateString || !format) { + return false; + } + let regEx = /^\d{4}\/\d{2}\/\d{2}$/; + let YYYY,MM,DD; + + switch (format) { + case 'YYYY-MM-DD': + regEx = /^\d{4}-\d{2}-\d{2}$/; + [YYYY, MM, DD] = dateString.split('-'); + break; + case 'DD-MM-YYYY': + regEx = /^\d{2}-\d{2}-\d{4}$/; + [DD, MM, YYYY] = dateString.split('-'); + break; + case 'MM-DD-YYYY': + regEx = /^\d{2}-\d{2}-\d{4}$/; + [MM, DD, YYYY] = dateString.split('-'); + break; + case 'YYYY/MM/DD': + regEx = /^\d{4}\/\d{2}\/\d{2}$/; + [YYYY, MM, DD] = dateString.split('/'); + break; + case 'DD/MM/YYYY': + regEx = /^\d{2}\/\d{2}\/\d{4}$/; + [DD, MM, YYYY] = dateString.split('/'); + break; + case 'MM/DD/YYYY': + regEx = /^\d{2}\/\d{2}\/\d{4}$/; + [MM, DD, YYYY] = dateString.split('/'); + break; + default: + break; + } + + if (!dateString.match(regEx)) { // Invalid format + return false; + } + var d = new Date(`${YYYY}-${MM}-${DD}`); + var dNum = d.getTime(); + if(!dNum && dNum !== 0) { // NaN value, Invalid date + return false; + } + return true; + } try { const isEmpty = checkEmpty(value); @@ -100,16 +144,30 @@ module.exports = (validation = [], response = {}) => { if (isArray && !Array.isArray(value)) { throw new Error('Must Be An Array'); } - if (isObject && !_.isObject(value) && Array.isArray(value)) { + if (isObject && !(value instanceof Object) && Array.isArray(value)) { throw new Error('Must Be An Object'); } if (isDate) { - if (!moment(value, format || 'YYYY-MM-DD', true).isValid()) { - throw new Error(`Must Be A Date With Format ${format || 'YYYY-MM-DD'}`); + const supportedFormats = [ + 'YYYY-MM-DD', + 'DD-MM-YYYY', + 'MM-DD-YYYY', + 'YYYY/MM/DD', + 'DD/MM/YYYY', + 'MM/DD/YYYY', + ] + const checkFormat = format || 'YYYY-MM-DD'; + + if (!supportedFormats.includes(checkFormat)) { + throw new Error(`Configured format not supported ${checkFormat}`); + } + + if (!dateCheck(value, checkFormat)) { + throw new Error(`Must Be A Date With Format ${checkFormat}`); } } if (isBoolean) { - if (!_.isBoolean(value) && value.toLowerCase() !== "true" && value.toLowerCase() !== "false") { + if (value !== !!value && value.toLowerCase() !== "true" && value.toLowerCase() !== "false") { throw new Error('Must Be A Boolean'); } } @@ -187,7 +245,8 @@ module.exports = (validation = [], response = {}) => { return errorList; } const validateFields = (req, res, next) => { - const error = _.reject(_.flattenDeep(validation.map(validateObj => validate(validateObj, req))), _.isNil); + const isNull = val => val === null + const error = validation.map(validateObj => validate(validateObj, req)).flat(Infinity).filter(obj => obj); if (error && error.length > 0) { if (response.mode === 'forward') { diff --git a/lib/validator/validator.test.js b/lib/validator/validator.test.js index c1a68bd..fbf7432 100644 --- a/lib/validator/validator.test.js +++ b/lib/validator/validator.test.js @@ -550,11 +550,11 @@ describe('Test for body params', () => { }) }); - test('Test nested objects Success case - Date test', () => { + test('Test nested objects Success case - Date test Format defauly YYYY-MM-DD', () => { const req = { body: { page: { - sorted: '2012-09-09' + sorted: '2012-09-19' } } } @@ -573,11 +573,11 @@ describe('Test for body params', () => { expect(resp.send).toHaveBeenCalledTimes(0) }); - test('Test nested objects Success case - Date test - Format', () => { + test('Test nested objects Success case - Date test - Format DD-MM-YYYY', () => { const req = { body: { page: { - sorted: '09-08-2019' + sorted: '19-08-2019' } } } @@ -596,6 +596,98 @@ describe('Test for body params', () => { expect(resp.send).toHaveBeenCalledTimes(0) }); + test('Test nested objects Success case - Date test - Format MM-DD-YYYY', () => { + const req = { + body: { + page: { + sorted: '08-18-2019' + } + } + } + const validation = [ + {param : 'page', location : 'body', isObject : true, children : [ + {param : 'sorted', location : 'body.page', isRequired : true, isDate: true, format: 'MM-DD-YYYY' }, + ]} + ] + const response = { + mode: 'reject' + } + const validatorfn = validaor(validation, response); + validatorfn(req, resp, next); + expect(next).toHaveBeenCalledTimes(1); + expect(resp.status).toHaveBeenCalledTimes(0); + expect(resp.send).toHaveBeenCalledTimes(0) + }); + + test('Test nested objects Success case - Date test - Format MM/DD/YYYY', () => { + const req = { + body: { + page: { + sorted: '12/18/2019' + } + } + } + const validation = [ + {param : 'page', location : 'body', isObject : true, children : [ + {param : 'sorted', location : 'body.page', isRequired : true, isDate: true, format: 'MM/DD/YYYY' }, + ]} + ] + const response = { + mode: 'reject' + } + const validatorfn = validaor(validation, response); + validatorfn(req, resp, next); + expect(next).toHaveBeenCalledTimes(1); + expect(resp.status).toHaveBeenCalledTimes(0); + expect(resp.send).toHaveBeenCalledTimes(0) + }); + + test('Test nested objects Success case - Date test - Format DD/MM/YYYY', () => { + const req = { + body: { + page: { + sorted: '12/07/2019' + } + } + } + const validation = [ + {param : 'page', location : 'body', isObject : true, children : [ + {param : 'sorted', location : 'body.page', isRequired : true, isDate: true, format: 'DD/MM/YYYY' }, + ]} + ] + const response = { + mode: 'reject' + } + const validatorfn = validaor(validation, response); + validatorfn(req, resp, next); + expect(next).toHaveBeenCalledTimes(1); + expect(resp.status).toHaveBeenCalledTimes(0); + expect(resp.send).toHaveBeenCalledTimes(0) + }); + + test('Test nested objects Success case - Date test - Format YYYY/MM/DD', () => { + const req = { + body: { + page: { + sorted: '2901/07/19' + } + } + } + const validation = [ + {param : 'page', location : 'body', isObject : true, children : [ + {param : 'sorted', location : 'body.page', isRequired : true, isDate: true, format: 'YYYY/MM/DD' }, + ]} + ] + const response = { + mode: 'reject' + } + const validatorfn = validaor(validation, response); + validatorfn(req, resp, next); + expect(next).toHaveBeenCalledTimes(1); + expect(resp.status).toHaveBeenCalledTimes(0); + expect(resp.send).toHaveBeenCalledTimes(0) + }); + test('Test nested objects Error case - Date test', () => { const req = { body: { @@ -658,6 +750,37 @@ describe('Test for body params', () => { }) }); + test('Test nested objects Error case - Date test - Invalid Format', () => { + const req = { + body: { + page: { + sorted: '2019-23-02' + } + } + } + const validation = [ + {param : 'page', location : 'body', isObject : true, children : [ + {param : 'sorted', location : 'body.page', isRequired : true, isDate : true, format: 'YYYY/DD/MM' }, + ]} + ] + const response = { + mode: 'reject' + } + const validatorfn = validaor(validation, response); + validatorfn(req, resp, next); + expect(next).toHaveBeenCalledTimes(0); + expect(resp.status).toHaveBeenCalledTimes(1); + expect(resp.send).toHaveBeenCalledWith({ + error: [ + { + location: 'body.page', + message: 'Invalid Field Error', + param: 'sorted', + } + ] + }) + }); + test('Test nested objects Success case - Mobile number test', () => { const req = { body: { diff --git a/package-lock.json b/package-lock.json index 857c1b8..1ea7349 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,21 @@ { "name": "expressjs-field-validator", - "version": "2.1.0", + "version": "2.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "2.1.0", + "name": "expressjs-field-validator", + "version": "2.2.1", "license": "MIT", - "dependencies": { - "lodash": "^4.17.21", - "moment": "^2.29.1" - }, "devDependencies": { "@node-minify/cli": "^6.2.0", "@node-minify/uglify-es": "^6.2.0", "jest": "^26.6.3" + }, + "engines": { + "node": "16.13.1", + "npm": "8.1.2" } }, "node_modules/@babel/code-frame": { @@ -4306,7 +4307,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", @@ -4500,14 +4502,6 @@ "node": ">=10" } }, - "node_modules/moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", - "engines": { - "node": "*" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -10426,7 +10420,8 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "log-symbols": { "version": "4.1.0", @@ -10566,11 +10561,6 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index 7323f2b..6c0f46f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "expressjs-field-validator", - "version": "2.2.1", + "version": "2.3.0", "description": "Plugin for validating field values of json request in expressjs", "homepage": "https://gsmithun4.github.io/expressjs-field-validator", "repository": { @@ -29,13 +29,13 @@ ], "author": "Midhun G S", "license": "MIT", - "dependencies": { - "lodash": "^4.17.21", - "moment": "^2.29.1" - }, "devDependencies": { "@node-minify/cli": "^6.2.0", "@node-minify/uglify-es": "^6.2.0", "jest": "^26.6.3" + }, + "engines": { + "node": "16.13.1", + "npm": "8.1.2" } }