another-json-schema
Advanced tools
Comparing version 1.2.0 to 2.0.0
'use strict'; | ||
exports.type = function (actual, expected, key, parentNode) { | ||
if (expected === 'any') return true; | ||
var toString = Object.prototype.toString; | ||
//return value or throw error | ||
exports.type = function (actual, expected) { | ||
if (expected === 'any') return actual; | ||
if ('function' === typeof expected) { | ||
return expected.apply(this, arguments); | ||
return expected.call(this, actual); | ||
} | ||
return expected === Object.prototype.toString.call(actual).match(/^\[object\s(.*)\]$/)[1].toLowerCase(); | ||
if (expected === toString.call(actual).match(/^\[object\s(.*)\]$/)[1].toLowerCase()) { | ||
return actual; | ||
} else { | ||
throw null; | ||
} | ||
}; | ||
//return true|false | ||
/* | ||
* Number | ||
*/ | ||
exports.gt = function (actual, expected, key, parentNode) { | ||
exports.gt = function (actual, expected) { | ||
return actual > expected; | ||
}; | ||
exports.gte = function (actual, expected, key, parentNode) { | ||
exports.gte = function (actual, expected) { | ||
return actual >= expected; | ||
}; | ||
exports.lt = function (actual, expected, key, parentNode) { | ||
exports.lt = function (actual, expected) { | ||
return actual < expected; | ||
}; | ||
exports.lte = function (actual, expected, key, parentNode) { | ||
exports.lte = function (actual, expected) { | ||
return actual <= expected; | ||
}; | ||
exports.range = function (actual, expected, key, parentNode) { | ||
exports.range = function (actual, expected) { | ||
return (actual >= expected[0]) && (actual <= expected[1]); | ||
@@ -37,3 +45,3 @@ }; | ||
*/ | ||
exports.enum = function (actual, expected, key, parentNode) { | ||
exports.enum = function (actual, expected) { | ||
return expected.indexOf(actual) !== -1; | ||
@@ -45,18 +53,4 @@ }; | ||
*/ | ||
exports.pattern = function (actual, expected, key, parentNode) { | ||
exports.pattern = function (actual, expected) { | ||
return expected.test(actual); | ||
}; | ||
/* | ||
*Function | ||
*/ | ||
exports.validate = function (actual, expected, key, parentNode) { | ||
return expected(actual, key, parentNode); | ||
}; | ||
/* | ||
* other | ||
*/ | ||
exports.required = function (actual, expected, key, parentNode) { | ||
return expected ? !!actual : true; | ||
}; |
85
index.js
@@ -107,18 +107,18 @@ 'use strict'; | ||
iterator(obj, opts, ctx); | ||
function iterator(children, opts, ctx, currentKey, parentNode) { | ||
if (error) return; | ||
try { | ||
obj = iterator(obj, opts, ctx); | ||
} catch (e) { | ||
error = e; | ||
} | ||
function iterator(children, opts, ctx) { | ||
var isObject = 'object' === typeof children; | ||
var isArray = Array.isArray(children); | ||
parentNode = parentNode || {}; | ||
error = validateType(children, ctx); | ||
if (error) return; | ||
validateType(children, ctx); | ||
if (ctx._leaf || isBuffer(children) || !isObject) { | ||
error = validateLeaf(children, opts, ctx, currentKey, parentNode); | ||
return validateLeaf(children, opts, ctx); | ||
} else { | ||
if (isArray) { | ||
children.forEach(function (item, index) { | ||
iterator(item, opts, ctx, index, children); | ||
return children.map(function (item) { | ||
return iterator(item, opts, ctx); | ||
}); | ||
@@ -132,5 +132,6 @@ } else { | ||
} else { | ||
iterator(children[key], opts, ctx._children[key], key, children); | ||
children[key] = iterator(children[key], opts, ctx._children[key]); | ||
} | ||
} | ||
return children; | ||
} | ||
@@ -150,3 +151,2 @@ } | ||
var isArray = Array.isArray(value); | ||
var error = null; | ||
@@ -156,5 +156,5 @@ if (ctx._leaf) { | ||
if (isArray && !ctx._array) { | ||
error = genError(value, ctx); | ||
throwError(value, ctx); | ||
} else if (ctx._array && !isArray) { | ||
error = genError(value, ctx, false, 'array'); | ||
throwError(value, ctx, null, 'array'); | ||
} | ||
@@ -164,46 +164,47 @@ } | ||
if (ctx._object && !isObject) { | ||
error = genError(value, ctx, false, 'object'); | ||
throwError(value, ctx, null, 'object'); | ||
} | ||
} | ||
return error; | ||
} | ||
function validateLeaf(value, opts, ctx, currentKey, parentNode) { | ||
var valid = true; | ||
var error = null; | ||
function validateLeaf(value, opts, ctx) { | ||
// leaf also is array | ||
if (Array.isArray(value)) { | ||
for (var index in value) { | ||
if (!valid) break; | ||
valid = validate(value[index], index, value); | ||
} | ||
return value.map(function (item) { | ||
return validate(item); | ||
}); | ||
} else { | ||
validate(value, currentKey, parentNode); | ||
return validate(value); | ||
} | ||
function validate(value, currentKey, parentNode) { | ||
var valid = helpersFuncs.type.call(ctx, value, ctx._children.type, currentKey, parentNode); | ||
if (!valid) { | ||
error = genError(value, ctx, 'type'); | ||
return valid; | ||
function validate(value) { | ||
var valid = true;//default passed | ||
//check type first, can modify `value` | ||
try { | ||
value = helpersFuncs.type.call(ctx, value, ctx._children.type); | ||
} catch (e) { | ||
throwError(value, ctx, 'type', null, e); | ||
} | ||
//check others, can not modify `value` | ||
for (var helper in ctx._children) { | ||
if (!valid || 'type' === helper || !helpersFuncs[helper] || (opts[helper] !== undefined && !opts[helper])) { | ||
if ('type' === helper || (opts[helper] != null && !opts[helper])) { | ||
continue; | ||
} | ||
valid = helpersFuncs[helper].call(ctx, value, ctx._children[helper], currentKey, parentNode); | ||
if ('function' === typeof ctx._children[helper]) { | ||
//custom function validator | ||
valid = ctx._children[helper].call(ctx, value); | ||
} else if (helpersFuncs[helper]) { | ||
//registered helpers | ||
valid = helpersFuncs[helper].call(ctx, value, ctx._children[helper]); | ||
} | ||
if (!valid) { | ||
error = genError(value, ctx, helper); | ||
return valid; | ||
throwError(value, ctx, helper); | ||
} | ||
} | ||
return valid; | ||
return value; | ||
} | ||
return error; | ||
} | ||
function genError(value, ctx, helper, type) { | ||
function throwError(value, ctx, helper, type, originError) { | ||
var error = null; | ||
@@ -232,3 +233,7 @@ if (!type) { | ||
error.schema = ctx._name; | ||
return error; | ||
if (originError) { | ||
error.originError = originError; | ||
} | ||
throw error; | ||
} | ||
@@ -235,0 +240,0 @@ |
{ | ||
"name": "another-json-schema", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"description": "Another JSON Schema, simple & flexible & intuitive.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
169
README.md
@@ -19,2 +19,4 @@ ### another-json-schema | ||
simple: | ||
``` | ||
@@ -24,4 +26,33 @@ var AJS = require('another-json-schema'); | ||
var userSchema = AJS('userSchema', { | ||
name: { type: 'string' }, | ||
age: { type: 'number', gte: 18 } | ||
}); | ||
var user = { | ||
name: 'nswbmw', | ||
age: 17 | ||
}; | ||
console.log(userSchema.validate(user)); | ||
/* | ||
{ valid: false, | ||
error: | ||
{ [Error: ($.age: 17) ✖ (gte: 18)] | ||
validator: 'gte', | ||
actual: 17, | ||
expected: { type: 'number', gte: 18 }, | ||
path: '$.age', | ||
schema: 'userSchema' }, | ||
result: { name: 'nswbmw', age: 17 } } | ||
*/ | ||
``` | ||
complex: | ||
``` | ||
var AJS = require('another-json-schema'); | ||
var userSchema = AJS('userSchema', { | ||
_id: { type: 'string', pattern: /^[0-9a-z]{24}$/ }, | ||
name: { type: 'string', required: true }, | ||
name: { type: 'string' }, | ||
age: { type: 'number', gte: 18 }, | ||
@@ -34,3 +65,3 @@ gender: { type: 'string', enum: ['male', 'female'] } | ||
user: userSchema, | ||
content: { type: 'string', required: true } | ||
content: { type: 'string' } | ||
}); | ||
@@ -41,3 +72,3 @@ | ||
author: userSchema, | ||
content: { type: 'string', required: true }, | ||
content: { type: 'string' }, | ||
comments: [commentSchema] | ||
@@ -65,56 +96,76 @@ }); | ||
content: 'sofa' | ||
}, { | ||
_id: 'comment22222222222222222', | ||
user: { | ||
_id: 'user22222222222222222222', | ||
name: 'user2', | ||
age: 100, | ||
gender: 'female' | ||
}, | ||
content: 'bench' | ||
}] | ||
}; | ||
var output = postSchema.validate(post); | ||
console.log(postSchema.validate(post)); | ||
/* | ||
{ valid: false, | ||
error: | ||
{ [Error: ($.comments[].user._id: "wrong_id") ✖ (pattern: /^[0-9a-z]{24}$/)] | ||
validator: 'pattern', | ||
actual: 'wrong_id', | ||
expected: { type: 'string', pattern: /^[0-9a-z]{24}$/ }, | ||
path: '$.comments[].user._id', | ||
schema: 'userSchema' }, | ||
result: | ||
{ _id: 'post11111111111111111111', | ||
author: | ||
{ _id: 'user11111111111111111111', | ||
name: 'nswbmw', | ||
age: 100, | ||
gender: 'male' }, | ||
content: 'lalala', | ||
comments: [ [Object] ] } } | ||
*/ | ||
``` | ||
assert.deepEqual(output.error.message, '($.comments[].user._id: "wrong_id") ✖ (pattern: /^[0-9a-z]{24}$/)'); | ||
assert.deepEqual(output, { | ||
valid: false, | ||
error: { | ||
validator: 'pattern', | ||
actual: 'wrong_id', | ||
expected: { type: 'string', pattern: /^[0-9a-z]{24}$/ }, | ||
path: '$.comments[].user._id', | ||
schema: 'userSchema' }, | ||
result: { | ||
_id: 'post11111111111111111111', | ||
author: { _id: 'user11111111111111111111', | ||
name: 'nswbmw', | ||
age: 100, | ||
gender: 'male' | ||
}, | ||
content: 'lalala', | ||
comments: [{ | ||
_id: 'comment11111111111111111', | ||
user: { | ||
_id: 'wrong_id', | ||
name: 'user1', | ||
age: 100, | ||
gender: 'male' | ||
}, | ||
content: 'sofa' | ||
}, { | ||
_id: 'comment22222222222222222', | ||
user: { | ||
_id: 'user22222222222222222222', | ||
name: 'user2', | ||
age: 100, | ||
gender: 'female' | ||
}, | ||
content: 'bench' | ||
}] | ||
} | ||
custom validate function(like: ObjectId): | ||
``` | ||
var validator = require('validator'); | ||
var toObjectId = require('mongodb').ObjectId; | ||
var AJS = require('another-json-schema'); | ||
var postSchema = AJS('postSchema', { | ||
author: { | ||
type: function ObjectId(value) { | ||
if (!value || !validator.isMongoId(value.toString())) { | ||
throw new Error('author is not a valid ObjectId'); | ||
} | ||
return toObjectId(value); | ||
} | ||
}, | ||
content: { type: 'string' } | ||
}); | ||
var post = { | ||
author: '111111111111111111111111', | ||
content: 'haha' | ||
}; | ||
console.log(postSchema.validate(post)); | ||
/* | ||
{ valid: true, | ||
error: null, | ||
result: { author: 111111111111111111111111, content: 'haha' } } | ||
*/ | ||
//validate specific field | ||
console.log(postSchema._children.author.validate('lalala')); | ||
/* | ||
{ valid: false, | ||
error: | ||
{ [Error: ($.author: "lalala") ✖ (type: ObjectId)] | ||
validator: 'type', | ||
actual: 'lalala', | ||
expected: { type: [Function: ObjectId] }, | ||
path: '$.author', | ||
schema: 'postSchema', | ||
originError: [Error: author is not a valid ObjectId] }, | ||
result: 'lalala' } | ||
*/ | ||
``` | ||
**Note:** `type` validator is special, it can overwrite original value by value returned from function. others validator can only validate its value. | ||
### API | ||
@@ -143,3 +194,3 @@ | ||
_id: { type: 'string', pattern: /^[0-9a-z]{24}$/ }, | ||
name: { type: 'string', required: true }, | ||
name: { type: 'string' }, | ||
age: { type: 'number', gte: 18 }, | ||
@@ -154,3 +205,3 @@ gender: { type: 'string', enum: ['male', 'female'] } | ||
_id: { type: 'string', pattern: /^[0-9a-z]{24}$/ }, | ||
name: { type: 'string', required: true }, | ||
name: { type: 'string' }, | ||
age: { type: 'number', gte: 18 }, | ||
@@ -173,2 +224,3 @@ gender: { type: 'string', enum: ['male', 'female'] } | ||
- schema: schema name, eg: `userSchema` | ||
- originError: original error thrown from validator | ||
- result: {Any} | ||
@@ -179,4 +231,15 @@ | ||
- additionalProperties: {Boolean} if true, retain the original field. default `false` | ||
- gt, gte, lt, lte ...: {Boolean} if false, will not execute this build-in validator. | ||
- gt, gte, lt, lte ...: {Boolean} if false, will not execute this validator. | ||
### Buit-in validator | ||
- type | ||
- gt | ||
- gte | ||
- lt | ||
- lte | ||
- range | ||
- enum | ||
- pattern | ||
### More examples | ||
@@ -183,0 +246,0 @@ |
@@ -39,2 +39,5 @@ 'use strict'; | ||
}); | ||
//gt18: false | ||
assert.deepEqual(schema.validate(0, { gt18: false }), { valid: true, error: null, result: 0 }); | ||
}); | ||
@@ -61,5 +64,9 @@ | ||
var checkIsObject = function (actual) { | ||
return typeof actual === 'object'; | ||
}; | ||
function checkIsObject (actual) { | ||
if (typeof actual === 'object') { | ||
return actual; | ||
} else { | ||
throw 'Not object!'; | ||
} | ||
} | ||
var schema = AJS('typeSchema', { type: checkIsObject }); | ||
@@ -76,5 +83,30 @@ | ||
path: '$', | ||
schema: 'typeSchema' }, | ||
schema: 'typeSchema', | ||
originError: 'Not object!' }, | ||
result: 0 | ||
}); | ||
function toUpperCase(value) { | ||
if ('string' !== typeof value) { | ||
throw 'name is not String'; | ||
} | ||
return value.toUpperCase(); | ||
} | ||
var schema = AJS('typeSchema', { | ||
name: { type: toUpperCase } | ||
}); | ||
assert.deepEqual(schema._children.name.validate('a'), { valid: true, error: null, result: 'A' }); | ||
assert.deepEqual(schema._children.name.validate(0), { | ||
valid: false, | ||
error: | ||
{ | ||
validator: 'type', | ||
actual: 0, | ||
expected: { type: toUpperCase }, | ||
path: '$.name', | ||
schema: 'typeSchema', | ||
originError: 'name is not String' }, | ||
result: 0 | ||
}); | ||
}); | ||
@@ -173,4 +205,7 @@ | ||
it('.required', function () { | ||
var schema = AJS('requiredSchema', { type: 'string', required: true }); | ||
it('.required custom', function () { | ||
function required(value) { | ||
return !!value; | ||
} | ||
var schema = AJS('requiredSchema', { type: 'string', required: required }); | ||
assert.deepEqual(schema.validate('aaa'), { valid: true, error: null, result: 'aaa' }); | ||
@@ -182,3 +217,3 @@ assert.deepEqual(schema.validate(''), { valid: false, | ||
actual: '', | ||
expected: { type: 'string', required: true }, | ||
expected: { type: 'string', required: required }, | ||
path: '$', | ||
@@ -189,3 +224,3 @@ schema: 'requiredSchema' }, | ||
var schema = AJS('requiredSchema', { type: 'string', required: 0 }); | ||
var schema = AJS('requiredSchema', { type: 'string', required: 'will be ignore' }); | ||
assert.deepEqual(schema.validate('aaa'), { valid: true, error: null, result: 'aaa' }); | ||
@@ -210,3 +245,3 @@ assert.deepEqual(schema.validate(''), { valid: true, error: null, result: '' }); | ||
it('.validate', function () { | ||
it('.validate custom', function () { | ||
var validate = function (actual) { | ||
@@ -228,11 +263,2 @@ return actual === 'aaa'; | ||
}); | ||
it('.validate false', function () { | ||
var validate = function (actual) { | ||
return actual === 'aaa'; | ||
}; | ||
var schema = AJS('validateSchema', { type: 'string', validate: validate }); | ||
assert.deepEqual(schema.validate('aaa'), { valid: true, error: null, result: 'aaa' }); | ||
assert.deepEqual(schema.validate('bbb', { validate: false }), { valid: true, error: null, result: 'bbb' }); | ||
}); | ||
}); |
@@ -96,3 +96,11 @@ 'use strict'; | ||
}); | ||
assert.deepEqual(userSchema._children._id.validate('1'), { | ||
valid: false, | ||
error: { | ||
validator: 'pattern', | ||
actual: '1', | ||
expected: { type: 'string', pattern: /^[0-9a-z]{24}$/ }, | ||
path: '$._id', | ||
schema: 'userSchema' }, | ||
result: '1' }); | ||
assert.deepEqual(userSchema.validate({ | ||
@@ -99,0 +107,0 @@ _id: '111111111111111111111111', |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33983
918
260