From bbb7abcdef614dcd698133b9d0deb957a6d3d409 Mon Sep 17 00:00:00 2001 From: Dizzle Date: Thu, 21 Apr 2016 22:41:47 +0000 Subject: [PATCH] Adds regex and hasOneOf; adds object.assign tests --- lib/index.js | 196 +++++++++++++++++++++++++++------------------ lib/rules/index.js | 14 +++- src/index.js | 58 ++++++++------ src/rules/index.js | 4 +- tests/example.js | 12 ++- tests/index.js | 60 +++++++++++++- 6 files changed, 234 insertions(+), 110 deletions(-) diff --git a/lib/index.js b/lib/index.js index 45eaa7d..cb29555 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,8 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; - function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; } var validate = function () { @@ -46,160 +44,202 @@ var validate = function () { }(); var validateCount = function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee2(path) { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee2(parentPath) { var data = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - var schema = arguments[2]; + var schemata = arguments[2]; var maximum = arguments[3]; - var errors, key, value, rules, subPath, subErrors, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, rule, message; + var errors, keys, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, key, value, schema, path, rules, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, rule, message, schemaKeys, subErrors; return regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: errors = []; - _context2.t0 = regeneratorRuntime.keys(schema); - case 2: - if ((_context2.t1 = _context2.t0()).done) { - _context2.next = 51; + // filter out array indices + + keys = Object.keys(schemata).filter(isNaN); + _iteratorNormalCompletion = true; + _didIteratorError = false; + _iteratorError = undefined; + _context2.prev = 5; + _iterator = keys[Symbol.iterator](); + + case 7: + if (_iteratorNormalCompletion = (_step = _iterator.next()).done) { + _context2.next = 58; break; } - key = _context2.t1.value; + key = _step.value; value = data[key]; - rules = schema[key]; - subPath = (path ? path + "." : "") + key; + schema = schemata[key]; + path = (parentPath ? parentPath + "." : "") + key; - // sub objects + // direct rules - if (!((typeof rules === "undefined" ? "undefined" : _typeof(rules)) === 'object' && !Array.isArray(rules))) { - _context2.next = 16; - break; - } + rules = []; - _context2.next = 10; - return validateCount(subPath, value, rules, maximum); + if (typeof schema === 'function') rules = [schema]; + if (Array.isArray(schema)) rules = schema; - case 10: - subErrors = _context2.sent; + // console.log(path, ' has ', rules.length, ' rules'); - if (!subErrors.length) { - _context2.next = 15; + _iteratorNormalCompletion2 = true; + _didIteratorError2 = false; + _iteratorError2 = undefined; + _context2.prev = 18; + _iterator2 = rules[Symbol.iterator](); + + case 20: + if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) { + _context2.next = 31; break; } - errors = errors.concat(subErrors); + rule = _step2.value; + _context2.next = 24; + return Promise.resolve(rule(value)); + + case 24: + message = _context2.sent; + + + if (message) errors.push({ + property: path, + message: message + }); if (!(errors.length >= maximum)) { - _context2.next = 15; + _context2.next = 28; break; } return _context2.abrupt("return", errors); - case 15: - return _context2.abrupt("continue", 2); + case 28: + _iteratorNormalCompletion2 = true; + _context2.next = 20; + break; + + case 31: + _context2.next = 37; + break; - case 16: + case 33: + _context2.prev = 33; + _context2.t0 = _context2["catch"](18); + _didIteratorError2 = true; + _iteratorError2 = _context2.t0; - // single validation rule - just convert it to an array and process it below - if (typeof rules === 'function') rules = [rules]; + case 37: + _context2.prev = 37; + _context2.prev = 38; + + if (!_iteratorNormalCompletion2 && _iterator2.return) { + _iterator2.return(); + } - if (Array.isArray(rules)) { - _context2.next = 19; + case 40: + _context2.prev = 40; + + if (!_didIteratorError2) { + _context2.next = 43; break; } - return _context2.abrupt("return", errors); + throw _iteratorError2; - case 19: + case 43: + return _context2.finish(40); - // process actual validation rules - _iteratorNormalCompletion = true; - _didIteratorError = false; - _iteratorError = undefined; - _context2.prev = 22; - _iterator = rules[Symbol.iterator](); + case 44: + return _context2.finish(37); - case 24: - if (_iteratorNormalCompletion = (_step = _iterator.next()).done) { - _context2.next = 35; + case 45: + + // sub schema + schemaKeys = Object.keys(schema).filter(isNaN); + + // console.log(path, ' has ', schemaKeys.length, ' keys'); + + if (!schemaKeys.length) { + _context2.next = 55; break; } - rule = _step.value; - _context2.next = 28; - return Promise.resolve(rule(value)); + _context2.next = 49; + return validateCount(path, value, schema, maximum); - case 28: - message = _context2.sent; + case 49: + subErrors = _context2.sent; + if (!subErrors.length) { + _context2.next = 54; + break; + } - if (message) errors.push({ - property: subPath, - message: message - }); + errors = errors.concat(subErrors); if (!(errors.length >= maximum)) { - _context2.next = 32; + _context2.next = 54; break; } return _context2.abrupt("return", errors); - case 32: + case 54: + return _context2.abrupt("continue", 55); + + case 55: _iteratorNormalCompletion = true; - _context2.next = 24; + _context2.next = 7; break; - case 35: - _context2.next = 41; + case 58: + _context2.next = 64; break; - case 37: - _context2.prev = 37; - _context2.t2 = _context2["catch"](22); + case 60: + _context2.prev = 60; + _context2.t1 = _context2["catch"](5); _didIteratorError = true; - _iteratorError = _context2.t2; + _iteratorError = _context2.t1; - case 41: - _context2.prev = 41; - _context2.prev = 42; + case 64: + _context2.prev = 64; + _context2.prev = 65; if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } - case 44: - _context2.prev = 44; + case 67: + _context2.prev = 67; if (!_didIteratorError) { - _context2.next = 47; + _context2.next = 70; break; } throw _iteratorError; - case 47: - return _context2.finish(44); + case 70: + return _context2.finish(67); - case 48: - return _context2.finish(41); - - case 49: - _context2.next = 2; - break; + case 71: + return _context2.finish(64); - case 51: + case 72: return _context2.abrupt("return", errors); - case 52: + case 73: case "end": return _context2.stop(); } } - }, _callee2, undefined, [[22, 37, 41, 49], [42,, 44, 48]]); + }, _callee2, undefined, [[5, 60, 64, 72], [18, 33, 37, 45], [38,, 40, 44], [65,, 67, 71]]); })); return function validateCount(_x4, _x5, _x6, _x7) { diff --git a/lib/rules/index.js b/lib/rules/index.js index 3689cd9..8e4fb67 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.oneOf = exports.range = exports.number = exports.required = undefined; +exports.hasOneOf = exports.regex = exports.oneOf = exports.range = exports.number = exports.required = undefined; var _required = require('./required'); @@ -21,9 +21,19 @@ var _oneOf = require('./oneOf'); var _oneOf2 = _interopRequireDefault(_oneOf); +var _regex = require('./regex'); + +var _regex2 = _interopRequireDefault(_regex); + +var _hasOneOf = require('./hasOneOf'); + +var _hasOneOf2 = _interopRequireDefault(_hasOneOf); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.required = _required2.default; exports.number = _number2.default; exports.range = _range2.default; -exports.oneOf = _oneOf2.default; \ No newline at end of file +exports.oneOf = _oneOf2.default; +exports.regex = _regex2.default; +exports.hasOneOf = _hasOneOf2.default; \ No newline at end of file diff --git a/src/index.js b/src/index.js index cd58b5b..629477b 100644 --- a/src/index.js +++ b/src/index.js @@ -5,44 +5,52 @@ const validate = async (data, schema, maximum) => { return validateCount("", data, schema, maximum); }; -const validateCount = async (path, data = {}, schema, maximum) => { +const validateCount = async (parentPath, data = {}, schemata, maximum) => { let errors = []; - for (const key in schema) { + // filter out array indices + const keys = Object.keys(schemata).filter(isNaN); + + for (const key of keys) { const value = data[key]; - let rules = schema[key]; + const schema = schemata[key]; - const subPath = (path ? path + "." : "") + key; - - // sub objects - if (typeof rules === 'object' && !Array.isArray(rules)) { - const subErrors = await validateCount(subPath, value, rules, maximum); - - if (subErrors.length) { - errors = errors.concat(subErrors); - - if (errors.length >= maximum) return errors; - } - - continue; - } + const path = (parentPath ? parentPath + "." : "") + key; + + // direct rules + let rules = []; + if (typeof schema === 'function') rules = [ schema ]; + if (Array.isArray(schema)) rules = schema; + + // console.log(path, ' has ', rules.length, ' rules'); - // single validation rule - just convert it to an array and process it below - if (typeof rules === 'function') rules = [rules]; - - if (!Array.isArray(rules)) return errors; - - // process actual validation rules for (const rule of rules) { const message = await Promise.resolve(rule(value)); - + if (message) errors.push({ - property: subPath, + property: path, message }); if (errors.length >= maximum) return errors; } + + // sub schema + const schemaKeys = Object.keys(schema).filter(isNaN); + + // console.log(path, ' has ', schemaKeys.length, ' keys'); + + if (schemaKeys.length) { + const subErrors = await validateCount(path, value, schema, maximum); + + if (subErrors.length) { + errors = errors.concat(subErrors); + + if (errors.length >= maximum) return errors; + } + + continue; + } } return errors; diff --git a/src/rules/index.js b/src/rules/index.js index 80e1e9c..60bcd27 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -2,5 +2,7 @@ import required from './required'; import number from './number'; import range from './range'; import oneOf from './oneOf'; +import regex from './regex'; +import hasOneOf from './hasOneOf'; -export { required, number, range, oneOf }; +export { required, number, range, oneOf, regex, hasOneOf }; diff --git a/tests/example.js b/tests/example.js index fc47da1..a148a5b 100644 --- a/tests/example.js +++ b/tests/example.js @@ -1,5 +1,5 @@ import createValidator from '../src'; -import { required, number, range, oneOf } from '../src/rules'; +import { required, number, range, oneOf, regex } from '../src/rules'; const schema = { id: [required(), number()], @@ -8,7 +8,8 @@ const schema = { last: required() }, age: range(1, 100), - favoriteColor: oneOf(['blue', 'red', 'yellow'], 'invalid color') + favoriteColor: oneOf(['blue', 'red', 'yellow'], 'invalid color'), + email: regex(/^[^@]+@[^@]+\.[^@]+$/, 'invalid email address') }; // tests @@ -22,7 +23,8 @@ describe('example validator', () => { first: 'foo' }, age: 99, - favoriteColor: 'potato' + favoriteColor: 'potato', + email: 'derp' }; const errors = await v(data); @@ -35,6 +37,10 @@ describe('example validator', () => { { property: 'favoriteColor', message: 'invalid color' + }, + { + property: 'email', + message: 'invalid email address' } ]); }); diff --git a/tests/index.js b/tests/index.js index 6f75343..63beded 100644 --- a/tests/index.js +++ b/tests/index.js @@ -143,5 +143,63 @@ describe('object validator', () => { property: 'foo.bar', message: 'truth' }); - }); + }); + + pit('allows rules to dually function as schema', async() => { + const truthAndBaz = Object.assign(eq(true, 'truth'), { + baz: truth + }); + + const v = createValidator({ + foo: { + bar: truthAndBaz + } + }); + + const data = { }; + + const errors = await v(data); + + expect(errors).toEqual([ + { + property: 'foo.bar', + message: 'truth' + }, + { + property: 'foo.bar.baz', + message: 'truth' + }]); + }); + + pit('allows arrays of rules to dually function as schema', async() => { + const truthRules = [ truth, truth ]; + + const truthsAndBaz = Object.assign(truthRules, { + baz: truth + }); + + const v = createValidator({ + foo: { + bar: truthsAndBaz + } + }); + + const data = { }; + + const errors = await v(data); + + expect(errors).toEqual([ + { + property: 'foo.bar', + message: 'truth' + }, + { + property: 'foo.bar', + message: 'truth' + }, + { + property: 'foo.bar.baz', + message: 'truth' + }]); + }); });