alamid-schema
Advanced tools
Comparing version 0.4.0 to 1.0.0
@@ -6,22 +6,21 @@ "use strict"; | ||
var supportedTypes = [ | ||
"String", | ||
"Number", | ||
"Boolean", | ||
"Date", | ||
"Array", | ||
"Object" | ||
], | ||
fnName = /function ([a-z]+?)\(/i; | ||
"String", | ||
"Number", | ||
"Boolean", | ||
"Date", | ||
"Array", | ||
"Object" | ||
]; | ||
var fnName = /function ([a-z]+?)\(/i; | ||
/** | ||
* determine the type of an object | ||
* inspired by mongoose | ||
* Determine the type of an object. Inspired by mongoose. | ||
* | ||
* @param obj | ||
* @return {*} | ||
* @param {*} obj | ||
* @returns {string} | ||
*/ | ||
function determineType(obj) { | ||
var type, | ||
typeValue, | ||
name; | ||
var type; | ||
var typeValue; | ||
var name; | ||
@@ -38,3 +37,2 @@ if (value(obj).typeOf(Object)) { | ||
name = type.charAt(0).toUpperCase() + type.substring(1); | ||
return supportedTypes.indexOf(name) === -1 ? "String" : name; | ||
@@ -54,3 +52,3 @@ } else if (typeValue.typeOf(Function)) { | ||
if (supportedTypes.indexOf(name) === -1) { | ||
throw new TypeError("(alamid-schema) Type '" + name + "' is not supported"); | ||
throw new TypeError("Type '" + name + "' is not supported"); | ||
} | ||
@@ -61,2 +59,2 @@ | ||
module.exports = determineType; | ||
module.exports = determineType; |
@@ -6,5 +6,5 @@ "use strict"; | ||
function merge(source, target) { | ||
var key, | ||
sourceValue, | ||
targetValue; | ||
var key; | ||
var sourceValue; | ||
var targetValue; | ||
@@ -33,2 +33,2 @@ for (key in source) { | ||
module.exports = merge; | ||
module.exports = merge; |
"use strict"; | ||
var determineType = require("./determineType.js"), | ||
value = require("value"); | ||
var determineType = require("./determineType.js"); | ||
var value = require("value"); | ||
/** | ||
* Normalizes the schema definition and extracts keys and types | ||
* Normalizes the schema definition and extracts fields and types. | ||
* | ||
* @param {Object} definition | ||
* @return {Object} | ||
* @returns {Object} | ||
*/ | ||
function processDefinition(definition) { | ||
var key, | ||
fieldDefinition, | ||
type, | ||
keys = [], | ||
types = {}; | ||
var fields = []; | ||
var types = {}; | ||
var key; | ||
var fieldDefinition; | ||
var type; | ||
for (key in definition) { | ||
if (definition.hasOwnProperty(key)) { | ||
keys.push(key); | ||
fields.push(key); | ||
@@ -36,3 +36,3 @@ fieldDefinition = definition[key]; | ||
if (value(fieldDefinition).getConstructor() === Object) { | ||
definition[key].writable = fieldDefinition.hasOwnProperty("writable")? fieldDefinition.writable : true; | ||
definition[key].writable = fieldDefinition.hasOwnProperty("writable") ? fieldDefinition.writable : true; | ||
} else { | ||
@@ -43,3 +43,3 @@ definition[key].writable = true; | ||
if (value(fieldDefinition).getConstructor() === Object) { | ||
definition[key].readable = fieldDefinition.hasOwnProperty("readable")? fieldDefinition.readable : true; | ||
definition[key].readable = fieldDefinition.hasOwnProperty("readable") ? fieldDefinition.readable : true; | ||
} else { | ||
@@ -54,3 +54,3 @@ definition[key].readable = true; | ||
return { | ||
keys: keys, | ||
fields: fields, | ||
types: types | ||
@@ -60,2 +60,2 @@ }; | ||
module.exports = processDefinition; | ||
module.exports = processDefinition; |
"use strict"; | ||
var processDefinition = require("./processDefinition.js"), | ||
merge = require("./merge.js"); | ||
var processDefinition = require("./processDefinition.js"); | ||
var merge = require("./merge.js"); | ||
@@ -13,7 +13,6 @@ var slice = Array.prototype.slice; | ||
/** | ||
* Creates a new Schema | ||
* Creates a new Schema. | ||
* | ||
* @param {String=Anonymous} name | ||
* @param {String|Object} definition | ||
* @constructor | ||
* @param {string} [name=Anonymous] | ||
* @param {string|Object} definition | ||
*/ | ||
@@ -33,3 +32,3 @@ Schema.prototype.constructor = function (name, definition) { | ||
this.keys = schema.keys; | ||
this.fields = schema.fields; | ||
this.types = schema.types; | ||
@@ -39,25 +38,25 @@ }; | ||
/** | ||
* Returns a subset of the current schema with the given keys. You may pass an array with keys | ||
* Returns a subset of the current schema with the given fields. You may pass an array with fields | ||
* or just the keys as arguments. | ||
* | ||
* @param {String|Array} key1 | ||
* @param {String} key2 | ||
* @param {String} key3 | ||
* @param {string|Array} key1 | ||
* @param {string} key2 | ||
* @param {string} key3 | ||
* @returns {Schema} | ||
*/ | ||
Schema.prototype.only = function (key1, key2, key3) { | ||
var subset = Object.create(this), | ||
keys; | ||
var subset = Object.create(this); | ||
var fields; | ||
if (arguments.length === 1 && Array.isArray(key1)) { | ||
keys = key1; | ||
fields = key1; | ||
} else { | ||
keys = slice.call(arguments); | ||
fields = slice.call(arguments); | ||
} | ||
if (keys.length === 0) { | ||
throw new Error("(alamid-schema) Cannot create a subset of the schema with no keys"); | ||
if (fields.length === 0) { | ||
throw new Error("Cannot create a subset of the schema with no keys"); | ||
} | ||
subset.keys = keys; | ||
subset.fields = fields; | ||
@@ -68,26 +67,26 @@ return subset; | ||
/** | ||
* Returns a subset of the current schema without the given keys. You may pass an array with keys | ||
* Returns a subset of the current schema without the given keys. You may pass an array with fields | ||
* or just the keys as arguments. | ||
* | ||
* @param {String|Array} key1 | ||
* @param {String} key2 | ||
* @param {String} key3 | ||
* @param {string|Array} key1 | ||
* @param {string} key2 | ||
* @param {string} key3 | ||
* @returns {Schema} | ||
*/ | ||
Schema.prototype.except = function (key1, key2, key3) { | ||
var subset = Object.create(this), | ||
keys; | ||
var subset = Object.create(this); | ||
var fields; | ||
if (arguments.length === 1 && Array.isArray(key1)) { | ||
keys = key1; | ||
fields = key1; | ||
} else { | ||
keys = slice.call(arguments); | ||
fields = slice.call(arguments); | ||
} | ||
if (keys.length === this.keys.length) { | ||
throw new Error("(alamid-schema) Cannot create a subset of the schema with no keys"); | ||
if (fields.length === this.fields.length) { | ||
throw new Error("Cannot create a subset of the schema with no keys"); | ||
} | ||
subset.keys = this.keys.filter(function (value) { | ||
return keys.indexOf(value) === -1; | ||
subset.fields = this.fields.filter(function (value) { | ||
return fields.indexOf(value) === -1; | ||
}); | ||
@@ -98,10 +97,11 @@ | ||
/*** | ||
* Returns all writable Fields as Array | ||
/** | ||
* Returns all writable fields as array. | ||
* | ||
* @returns {Array} | ||
*/ | ||
Schema.prototype.getWritableFields = function () { | ||
Schema.prototype.writableFields = function () { | ||
var self = this; | ||
return this.keys.filter(function (key) { | ||
return this.fields.filter(function (key) { | ||
return self.definition[key].writable === true; | ||
@@ -111,18 +111,20 @@ }); | ||
/*** | ||
* Returns a Schema with only writable fields | ||
/** | ||
* Returns a schema with only writable fields. | ||
* | ||
* @returns {Schema} | ||
*/ | ||
Schema.prototype.getWritableSchema = function () { | ||
return this.only(this.getWritableFields()); | ||
Schema.prototype.writable = function () { | ||
return this.only(this.writableFields()); | ||
}; | ||
/*** | ||
* Returns all writable Fields as Array | ||
/** | ||
* Returns all writable Fields as array. | ||
* | ||
* @returns {Array} | ||
*/ | ||
Schema.prototype.getReadableFields = function () { | ||
Schema.prototype.readableFields = function () { | ||
var self = this; | ||
return this.keys.filter(function (key) { | ||
return this.fields.filter(function (key) { | ||
return self.definition[key].readable === true; | ||
@@ -132,8 +134,9 @@ }); | ||
/*** | ||
* Returns a Schema with only readable fields | ||
/** | ||
* Returns a Schema with only readable fields. | ||
* | ||
* @returns {Schema} | ||
*/ | ||
Schema.prototype.getReadableSchema = function () { | ||
return this.only(this.getReadableFields()); | ||
Schema.prototype.readable = function () { | ||
return this.only(this.readableFields()); | ||
}; | ||
@@ -145,3 +148,3 @@ | ||
* | ||
* @param {String=Anonymous} name | ||
* @param {string} [name=Anonymous] | ||
* @param {Object} definition | ||
@@ -160,7 +163,22 @@ * @returns {Schema} | ||
/** | ||
* Removes all properties that are not defined in this.fields. | ||
* Will not remove properties that are inherited from the prototype. | ||
* | ||
* @param {Object} model | ||
*/ | ||
Schema.prototype.strip = function (model) { | ||
var fields = this.fields; | ||
Object.keys(model) | ||
.forEach(function (key) { | ||
if (fields.indexOf(key) === -1) { | ||
delete model[key]; | ||
} | ||
}); | ||
}; | ||
/** | ||
* Calls the given function with the Schema as first argument and the given config (optionally). Plugins can be used | ||
* to hook into class methods by overriding them. | ||
* | ||
* You may call this function multiple times with the same plugin, the plugin will only be applied once. | ||
* | ||
* @param {Function} plugin | ||
@@ -171,12 +189,7 @@ * @param {Object=} config | ||
Schema.use = function (plugin, config) { | ||
this._plugins = this._plugins || []; | ||
plugin(this, config); | ||
if (this._plugins.indexOf(plugin) === -1) { | ||
plugin(this, config); | ||
this._plugins.push(plugin); | ||
} | ||
return this; | ||
}; | ||
module.exports = Schema; | ||
module.exports = Schema; |
{ | ||
"name": "alamid-schema", | ||
"version": "0.4.0", | ||
"version": "1.0.0", | ||
"description": "Extendable mongoose-like schemas for node.js and the browser", | ||
"main": "lib/Schema.js", | ||
"scripts": { | ||
"test": "node node_modules/mocha/bin/mocha -R spec" | ||
"test": "mocha -R spec", | ||
"posttest": "npm run lint", | ||
"lint": "eslint --fix examples lib plugins test" | ||
}, | ||
@@ -13,2 +15,5 @@ "repository": { | ||
}, | ||
"engines": { | ||
"node": ">=0.11" | ||
}, | ||
"keywords": [ | ||
@@ -31,4 +36,3 @@ "schema", | ||
"dependencies": { | ||
"value": "0.3.x", | ||
"when": "^3.7.3" | ||
"value": "^0.3.0" | ||
}, | ||
@@ -39,4 +43,6 @@ "devDependencies": { | ||
"chai-spies": "^0.7.0", | ||
"eslint": "^2.9.0", | ||
"eslint-config-peerigon": "^4.0.0", | ||
"mocha": "^2.2.5" | ||
} | ||
} |
"use strict"; | ||
if (typeof Promise === "undefined") { | ||
require("when/es6-shim/Promise"); | ||
} | ||
var value = require("value"); | ||
var defaultValidators = require("./validators.js"); | ||
var value = require("value"), | ||
defaultValidators = require("./validators.js"); | ||
/** | ||
* Runs the given validators on a single field | ||
* Runs the given validators on a single field. | ||
* | ||
* @private | ||
* Caution: callback will be called in synchronously in some situations. This behavior should usually be avoided in | ||
* public APIs, but since runValidation() is internal, we know how to deal with it. It allows us to speed up | ||
* validation and to return all synchronous validation results as soon as possible. | ||
* | ||
* The final callback, however, is guaranteed to be asynchronous. | ||
* | ||
* @param {Array} validators | ||
@@ -18,43 +19,43 @@ * @param {*} field | ||
* @param {Function} callback | ||
* @returns {Array} | ||
*/ | ||
function runValidation(validators, field, context, callback) { | ||
var result = [], | ||
pending = validators.length; | ||
var fieldErrors = []; | ||
var pending = 0; | ||
// Return immediately if the field has no validator defined | ||
if (pending === 0) { | ||
setTimeout(function () { | ||
return callback(result); | ||
}, 0); | ||
function saveResult(result) { | ||
if (result !== true) { | ||
fieldErrors.push(result); | ||
} | ||
} | ||
function validationDone(res) { | ||
function doCallback() { | ||
callback(fieldErrors); | ||
} | ||
function asyncValidationDone(result) { | ||
saveResult(result); | ||
pending--; | ||
if (res !== true) { | ||
result.push(res); | ||
} | ||
if (pending === 0) { | ||
callback(result); | ||
} | ||
pending === 0 && doCallback(); | ||
} | ||
validators.forEach(function (validator) { | ||
//async | ||
if (validator.length === 2) { | ||
validator.call(context, field, validationDone); | ||
pending++; | ||
validator.call(context, field, asyncValidationDone); | ||
} else { | ||
saveResult(validator.call(context, field)); | ||
} | ||
//sync | ||
else { | ||
//no setImmediate on client! | ||
setTimeout(function () { | ||
validationDone(validator.call(context, field)); | ||
}, 0); | ||
} | ||
}); | ||
if (pending === 0) { | ||
// synchronous callback | ||
doCallback(); | ||
} | ||
return fieldErrors; | ||
} | ||
/** | ||
* Adds validation methods to the schema | ||
* Adds validation methods to the schema. | ||
* | ||
@@ -64,8 +65,7 @@ * @param {Function} Schema | ||
function validationPlugin(Schema) { | ||
var constructor = Schema.prototype.constructor; | ||
Schema.prototype.constructor = function (name, schema) { | ||
var key, | ||
fieldDefinition; | ||
var key; | ||
var fieldDefinition; | ||
@@ -76,3 +76,3 @@ if (arguments.length === 1) { | ||
//call super constructor | ||
// call super constructor | ||
constructor.apply(this, arguments); | ||
@@ -87,3 +87,3 @@ | ||
//predefined validators | ||
// predefined validators | ||
if (fieldDefinition.required) { | ||
@@ -104,3 +104,20 @@ this.validators[key].push(defaultValidators.required()); | ||
//custom validators | ||
if (value(fieldDefinition.minLength).typeOf(Number)) { | ||
this.validators[key].push(defaultValidators.minLength(fieldDefinition.minLength)); | ||
} | ||
if (value(fieldDefinition.maxLength).typeOf(Number)) { | ||
this.validators[key].push(defaultValidators.maxLength(fieldDefinition.maxLength)); | ||
} | ||
if (value(fieldDefinition.hasLength).typeOf(Number)) { | ||
this.validators[key].push(defaultValidators.hasLength(fieldDefinition.hasLength)); | ||
} | ||
// The matches validators works on all types, so we just check if the key is present | ||
if ("matches" in fieldDefinition) { | ||
this.validators[key].push(defaultValidators.matches(fieldDefinition.matches)); | ||
} | ||
// custom validators | ||
if (value(fieldDefinition.validate).typeOf(Function)) { | ||
@@ -116,3 +133,5 @@ this.validators[key].push(fieldDefinition.validate); | ||
/** | ||
* Validate if given model matches schema-definition | ||
* Validate if given model matches schema definition. Returns a promise with the validation result object | ||
* which contains the intermediate result of all synchronous validators. | ||
* | ||
* @param {Object} model | ||
@@ -123,10 +142,22 @@ * @param {Function=} callback | ||
Schema.prototype.validate = function (model, callback) { | ||
var self = this, | ||
pending = 0, | ||
result = { | ||
model: model, | ||
result: true, | ||
failedFields: {} | ||
}; | ||
var self = this; | ||
var pending = 0; | ||
var result = { | ||
model: model, | ||
result: true, | ||
errors: {} | ||
}; | ||
var promise; | ||
function handleFieldErrors(key, fieldErrors) { | ||
if (fieldErrors.length > 0) { | ||
result.result = false; | ||
result.errors[key] = fieldErrors; | ||
} | ||
} | ||
function doCallback() { | ||
callback(result); | ||
} | ||
if (value(model).notTypeOf(Object)) { | ||
@@ -140,40 +171,37 @@ throw new TypeError("Model must be an object"); | ||
var promise = new Promise(function (resolve, reject) { | ||
if (self.keys.length === 0) { | ||
setTimeout(function () { | ||
resolve(result); | ||
}, 0); | ||
promise = new Promise(function (resolve) { | ||
function done() { | ||
if (typeof callback === "function") { | ||
setTimeout(doCallback, 0); | ||
} | ||
resolve(result); | ||
} | ||
self.keys.forEach(function (key) { | ||
pending++; | ||
runValidation(self.validators[key], model[key], model, function (failedFields) { | ||
if (self.fields.length === 0) { | ||
done(); | ||
return; | ||
} | ||
pending = self.fields.length; | ||
self.fields.forEach(function (key) { | ||
var fieldErrors = runValidation(self.validators[key], model[key], model, function onFieldValidation(fieldErrors) { | ||
pending--; | ||
handleFieldErrors(key, fieldErrors); | ||
if (failedFields.length > 0) { | ||
result.result = false; | ||
result.failedFields[key] = failedFields; | ||
} | ||
//was final call | ||
if (pending === 0) { | ||
if (result.result === false) { | ||
reject(result); | ||
return; | ||
} | ||
resolve(result); | ||
done(); | ||
} | ||
}); | ||
handleFieldErrors(key, fieldErrors); | ||
}); | ||
}); | ||
// Attach intermediate result to promise | ||
promise.validation = result; | ||
if (!callback || typeof callback !== "function") { | ||
return promise; | ||
} | ||
promise.then(callback, callback); | ||
return promise; | ||
}; | ||
} | ||
module.exports = validationPlugin; | ||
module.exports = validationPlugin; |
"use strict"; | ||
/** | ||
* returns a required validator | ||
* Returns a required validator. | ||
* | ||
* @returns {Function} | ||
@@ -9,3 +10,3 @@ */ | ||
return function validateRequired(val) { | ||
return (val !== undefined && val !== null && val !== "") || "required"; | ||
return (typeof val !== "undefined" && val !== null && val !== "") || "required"; | ||
}; | ||
@@ -15,5 +16,6 @@ } | ||
/** | ||
* returns an enum validator | ||
* Returns an enum validator. | ||
* | ||
* @param {Array} enumValues | ||
* @return {Function} | ||
* @returns {Function} | ||
*/ | ||
@@ -27,5 +29,6 @@ function enumValidator(enumValues) { | ||
/** | ||
* returns a min validator | ||
* @param {Number} minValue | ||
* @return {Function} | ||
* Returns a min validator. | ||
* | ||
* @param {number} minValue | ||
* @returns {Function} | ||
*/ | ||
@@ -39,5 +42,6 @@ function minValidator(minValue) { | ||
/** | ||
* returns a max validator | ||
* @param {Number} minValue | ||
* @return {Function} | ||
* Returns a max validator. | ||
* | ||
* @param {number} minValue | ||
* @returns {Function} | ||
*/ | ||
@@ -50,5 +54,68 @@ function maxValidator(minValue) { | ||
/** | ||
* Returns a minLength validator. | ||
* | ||
* @param {number} minLength | ||
* @returns {Function} | ||
*/ | ||
function minLengthValidator(minLength) { | ||
return function validateMinLength(lengthable) { | ||
return lengthable && (lengthable.length >= minLength) || "min-length"; | ||
}; | ||
} | ||
/** | ||
* Returns a maxLength validator. | ||
* | ||
* @param {number} maxLength | ||
* @returns {Function} | ||
*/ | ||
function maxLengthValidators(maxLength) { | ||
return function validateMaxLength(lengthable) { | ||
return lengthable && (lengthable.length <= maxLength) || "max-length"; | ||
}; | ||
} | ||
/** | ||
* Returns a hasLength validator. | ||
* | ||
* @param {number} hasLength | ||
* @returns {Function} | ||
*/ | ||
function hasLengthValidator(hasLength) { | ||
return function validateHasLength(lengthable) { | ||
return lengthable && (lengthable.length === hasLength) || "has-length"; | ||
}; | ||
} | ||
/** | ||
* Returns true if the given value matches the pattern. The pattern | ||
* may be a value or a regular expression. If it's a value, a strict comparison | ||
* will be performed. In case it's a regex, the test method will be invoked. | ||
* | ||
* @param {*|RegExp} match | ||
* @returns {Function} | ||
*/ | ||
function matchesValidator(match) { | ||
return function matchValue(value) { | ||
var result; | ||
if (typeof match.test === "function") { | ||
match.lastIndex = 0; // reset the lastIndex just in case the regexp is accidentally global | ||
result = match.test(value); | ||
} else { | ||
result = match === value; | ||
} | ||
return result || "matches"; | ||
}; | ||
} | ||
exports.required = requiredValidator; | ||
exports.min = minValidator; | ||
exports.max = maxValidator; | ||
exports.enum = enumValidator; | ||
exports.enum = enumValidator; | ||
exports.minLength = minLengthValidator; | ||
exports.maxLength = maxLengthValidators; | ||
exports.hasLength = hasLengthValidator; | ||
exports.matches = matchesValidator; |
170
README.md
alamid-schema | ||
======================================================================== | ||
============= | ||
**Extendable mongoose-like schemas for node.js and the browser** | ||
@@ -12,3 +12,3 @@ | ||
__alamid-schema__ helps you with | ||
__alamid-schema__ helps you with | ||
@@ -22,7 +22,7 @@ - validation of data | ||
Use it on the server to... | ||
- normalize and validate incoming requests | ||
- normalize and validate incoming requests | ||
- strip private fields from the response | ||
Use it on the client to... | ||
- validate forms | ||
Use it on the client to... | ||
- validate forms | ||
- define view models | ||
@@ -65,3 +65,3 @@ | ||
...or with abstract types... | ||
...or with abstract types... | ||
@@ -78,3 +78,3 @@ ```javascript | ||
### Extend | ||
### Extend | ||
@@ -99,3 +99,3 @@ Sometimes you want to extend your Schema and add new properties. | ||
}); | ||
``` | ||
``` | ||
@@ -113,3 +113,3 @@ We have a superpanda now... which can fly and has xray eyes! | ||
xRay: true, //added | ||
canFly: { //added | ||
canFly: { //added | ||
type: Boolean | ||
@@ -121,5 +121,5 @@ } | ||
__Overwriting properties__ | ||
__Overwriting properties__ | ||
If you define a property in the schema you are extending with, the extending schema takes precedence. | ||
If you define a property in the schema you are extending with, the extending schema takes precedence. | ||
@@ -145,3 +145,3 @@ | ||
age: Number, //overwritten | ||
color: String //added | ||
color: String //added | ||
}); | ||
@@ -152,6 +152,7 @@ ``` | ||
The validation plugins adds - *suprise!* - validation support. | ||
The validation plugins adds - *suprise!* - validation support. | ||
```javascript | ||
var Schema = require("alamid-schema"); | ||
Schema.use(require("alamid-schema/plugins/validation")); | ||
@@ -173,2 +174,6 @@ | ||
}, | ||
treasures: { | ||
type: Array, | ||
minLength: 3 | ||
}, | ||
birthday: Date | ||
@@ -184,5 +189,4 @@ }); | ||
PandaSchema.validate(panda, function(validation) { | ||
if(!validation.result) { | ||
console.log(validation); | ||
if (!validation.result) { | ||
console.log(validation.errors); | ||
return; | ||
@@ -195,3 +199,3 @@ } | ||
outputs... | ||
outputs... | ||
@@ -201,4 +205,4 @@ ```javascript | ||
result: false, | ||
failedFields: { | ||
age: [ 'min' ] | ||
errors: { | ||
age: [ 'min' ] | ||
} | ||
@@ -209,16 +213,20 @@ } | ||
_Included validators:_ | ||
_Included validators:_ | ||
- required | ||
- min (Number) | ||
- max (Number) | ||
- enum | ||
- min (works on Number) | ||
- max (works on Number) | ||
- enum | ||
- minLength (works on String, Array) | ||
- maxLength (works on String, Array) | ||
- hasLength (works on String, Array) | ||
- matches (performs a strict comparison, also accepts RegExp) | ||
_Writing custom validators:_ | ||
You can write sync and async validators.. | ||
You can write sync and async validators.. | ||
```javascript | ||
//sync | ||
// sync | ||
function oldEnough(age) { | ||
@@ -228,5 +236,4 @@ return age > 18 || "too-young"; | ||
//async | ||
// async | ||
function nameIsUnique(name, callback) { | ||
fs.readFile(__dirname + "/names.json", function(err, names) { | ||
@@ -238,3 +245,2 @@ if(err) { | ||
names = JSON.parse(names); | ||
callback(names.indexOf(name) === -1 || "name-already-taken"); | ||
@@ -264,5 +270,4 @@ }); | ||
PandaSchema.validate(panda, function(validation) { | ||
if(!validation.result) { | ||
console.log(validation.failedFields); | ||
console.log(validation.errors); | ||
return; | ||
@@ -272,10 +277,10 @@ } | ||
}); | ||
``` | ||
``` | ||
outputs... | ||
outputs... | ||
```javascript | ||
{ | ||
name: [ "name-already-taken" ], | ||
{ | ||
name: [ "name-already-taken" ], | ||
age: [ "too-young" ] | ||
@@ -285,39 +290,61 @@ } | ||
_Note:_ validators will be called with `this` bound to `model`. | ||
_Note:_ validators will be called with `this` bound to `model`. | ||
### Promises | ||
Instead of using a callback it is possible to return a Promise instead. | ||
The `validate()` method also returns a promise: | ||
```javascript | ||
PandaSchema.validate(panda) | ||
.then(function (res) { | ||
// ... | ||
}); | ||
.then(function (validation) { | ||
... | ||
}) | ||
``` | ||
We included an [ES2015 shim](https://github.com/cujojs/when/tree/master/es6-shim) in case you are using Node.js < 0.12 or a browser without native Promise support. | ||
The promise will still be resolved even when the validation fails, because a failed validation is | ||
not an error, it's an expected state. | ||
## API | ||
The promise provides a reference to the final validation result object. It contains the intermediate | ||
result of all synchronous validators: | ||
### Core | ||
```javascript | ||
var promise = PandaSchema.validate(panda); | ||
#### Schema(name?: String, definition: Object) | ||
if (promise.validation.result) { | ||
console.log("Synchronous validation of " + Object.keys(validation.errors) + " failed"); | ||
} | ||
``` | ||
Creates a new schema. | ||
__Important notice:__ You must bring your own ES6 Promise compatible polyfill! | ||
#### .only(key1: Array|String, key2?: String, key3?: String, ...) | ||
## API | ||
### Core | ||
#### Schema([name: String, ]definition: Object) | ||
Creates a new schema. | ||
#### .fields: Array | ||
The array of property names that are defined on the schema. Do not modify this array. | ||
#### .only(key1: Array|String[, key2: String, key3: String, ...]): Schema | ||
Returns a subset with the given keys of the current schema. You may pass an array with keys or just the keys as arguments. | ||
#### .extend(name?: String, definition: Object) | ||
#### .extend([name: String, ]definition: Object): Schema | ||
Creates a new schema that inherits from the current schema. Field definitions are merged where appropriate. | ||
Creates a new schema that inherits from the current schema. Field definitions are merged where appropriate. | ||
If a definition conflicts with the parent definition, the child's definition supersedes. | ||
#### .strip(model: Object) | ||
Removes all properties from `model` that are not defined in `fields`. Will not remove properties that are inherited from the prototype. | ||
### Readable & Writable fields | ||
You can define readable and writable fields in the schema. As default every field is read- and writable. | ||
You can define readable and writable fields in the schema. By default, every field is read- and writable. | ||
``` | ||
```javascript | ||
var PandaSchema = new Schema({ | ||
@@ -337,13 +364,38 @@ id: { | ||
#### .getWritableFields() | ||
#### .getWritableSchema() | ||
#### .getReadableFields() | ||
#### .getReadableSchema() | ||
#### .writableFields() | ||
Returns an array containing the keys of all writable fields: | ||
```javascript | ||
PandaSchema.writableFields(); // ["name", "age", "mood", "treasures", "birthday"] | ||
``` | ||
#### .writable() | ||
Creates a new schema that contains only the writable fields: | ||
```javascript | ||
var PandaWritableSchema = PandaSchema.writable(); | ||
``` | ||
#### .readableFields() | ||
Returns an array containing the keys of all readable fields: | ||
```javascript | ||
PandaSchema.readableFields(); // ["name", "age", "mood", "treasures", "birthday"] | ||
``` | ||
#### .readable() | ||
Creates a new schema that contains only the readable fields: | ||
```javascript | ||
var PandaReadableSchema = PandaSchema.readable(); | ||
``` | ||
### Plugin: Validation | ||
#### .validate(model: Object, callback: Function) | ||
#### .validate(model: Object[, callback: Function]): Promise | ||
Validate given model using the schema-definitions. | ||
Callback will be called with a validation object with `result` (Boolean) and `failedFields` (Object). | ||
Validate given model using the schema definitions. Callback will be called/Promise will be fulfilled with a validation object with `result` (Boolean) and `errors` (Object) containing the error codes. |
"use strict"; | ||
var chai = require("chai"), | ||
expect = chai.expect, | ||
merge = require("../lib/merge.js"); | ||
var chai = require("chai"); | ||
var expect = chai.expect; | ||
var merge = require("../lib/merge.js"); | ||
chai.config.includeStack = true; | ||
describe("merge(source, target)", function () { | ||
var a, | ||
b; | ||
var a; | ||
var b; | ||
@@ -38,4 +40,4 @@ it("should copy all properties from source to target", function () { | ||
it("should not alter the source", function () { | ||
var a = {}, | ||
b = { b: "b" }; | ||
var a = {}; | ||
var b = { b: "b" }; | ||
@@ -50,3 +52,3 @@ merge(a, b); | ||
1: { | ||
1: "1" | ||
1: "1" | ||
} | ||
@@ -56,3 +58,3 @@ }; | ||
1: { | ||
2: "2" | ||
2: "2" | ||
} | ||
@@ -65,4 +67,4 @@ }; | ||
1: { | ||
1: "1", | ||
2: "2" | ||
1: "1", | ||
2: "2" | ||
} | ||
@@ -169,2 +171,2 @@ }); | ||
}); | ||
}); |
"use strict"; | ||
var chai = require("chai"), | ||
expect = chai.expect, | ||
Schema = require("../" + require("../package.json").main); | ||
var chai = require("chai"); | ||
var expect = chai.expect; | ||
var Schema = require("../lib/Schema.js"); | ||
chai.config.includeStack = true; | ||
describe("Schema", function () { | ||
@@ -11,5 +13,6 @@ | ||
var schema; | ||
var definition; | ||
beforeEach(function () { | ||
schema = new Schema({ | ||
definition = { | ||
name: { | ||
@@ -22,5 +25,7 @@ type: "String", | ||
friends: { | ||
writable: false, | ||
type: Array | ||
} | ||
}); | ||
}; | ||
schema = new Schema(definition); | ||
}); | ||
@@ -30,4 +35,4 @@ | ||
it("should extract the keys from the definition", function () { | ||
expect(schema.keys).to.eql(["name", "age", "friends"]); | ||
it("should extract the fields from the definition", function () { | ||
expect(schema.fields).to.eql(["name", "age", "friends"]); | ||
}); | ||
@@ -52,3 +57,2 @@ | ||
it("should extract the types from the definition if using values", function () { | ||
@@ -73,20 +77,20 @@ | ||
expect(schema.definition).to.eql({ | ||
"age": { | ||
"readable": true, | ||
"type": "Number", | ||
"writable": true | ||
age: { | ||
readable: true, | ||
type: "Number", | ||
writable: true | ||
}, | ||
"friends": { | ||
"readable": true, | ||
"type": "Array", | ||
"writable": true | ||
friends: { | ||
readable: true, | ||
type: "Array", | ||
writable: false | ||
}, | ||
"name": { | ||
"readable": false, | ||
"type": "String", | ||
"writable": false | ||
name: { | ||
readable: false, | ||
type: "String", | ||
writable: false | ||
} | ||
}); | ||
}); | ||
it("should apply 'Anonymous' as schema name", function () { | ||
@@ -102,13 +106,3 @@ expect(schema.name).to.equal("Anonymous"); | ||
beforeEach(function () { | ||
namedSchema = new Schema("User", { | ||
name: { | ||
type: "String", | ||
readable: false, | ||
writable: false | ||
}, | ||
age: 3, | ||
friends: { | ||
type: Array | ||
} | ||
}); | ||
namedSchema = new Schema("User", definition); | ||
}); | ||
@@ -135,5 +129,5 @@ | ||
it("should inherit all properties prototypically except 'keys'", function () { | ||
var ownProperties = [], | ||
key; | ||
it("should inherit all properties prototypically except 'fields'", function () { | ||
var ownProperties = []; | ||
var key; | ||
@@ -146,3 +140,3 @@ subset = schema.only("name", "age"); | ||
} | ||
expect(ownProperties).to.eql(["keys"]); | ||
expect(ownProperties).to.eql(["fields"]); | ||
expect(schema.types).to.be.an("object"); | ||
@@ -152,5 +146,5 @@ expect(schema.name).to.be.an("string"); | ||
it("should change the .keys-property to the given keys", function () { | ||
it("should change the .fields-property to the given keys", function () { | ||
subset = schema.only("name", "age"); | ||
expect(subset.keys).to.eql(["name", "age"]); | ||
expect(subset.fields).to.eql(["name", "age"]); | ||
}); | ||
@@ -171,3 +165,3 @@ | ||
subset = schema.only(["name", "age"]); | ||
expect(subset.keys).to.eql(["name", "age"]); | ||
expect(subset.fields).to.eql(["name", "age"]); | ||
}); | ||
@@ -191,5 +185,5 @@ | ||
it("should inherit all properties prototypically except 'keys'", function () { | ||
var ownProperties = [], | ||
key; | ||
it("should inherit all properties prototypically except 'fields'", function () { | ||
var ownProperties = []; | ||
var key; | ||
@@ -202,3 +196,3 @@ subset = schema.except("name", "age"); | ||
} | ||
expect(ownProperties).to.eql(["keys"]); | ||
expect(ownProperties).to.eql(["fields"]); | ||
expect(schema.types).to.be.an("object"); | ||
@@ -208,5 +202,5 @@ expect(schema.name).to.be.an("string"); | ||
it("should exclude the given keys from the keys-property", function () { | ||
it("should exclude the given keys from the fields-property", function () { | ||
subset = schema.except("friends", "age"); | ||
expect(subset.keys).to.eql(["name"]); | ||
expect(subset.fields).to.eql(["name"]); | ||
}); | ||
@@ -216,3 +210,3 @@ | ||
expect(function () { | ||
schema.except.apply(schema, schema.keys); | ||
schema.except.apply(schema, schema.fields); | ||
}).to.throw("Cannot create a subset of the schema with no keys"); | ||
@@ -228,3 +222,3 @@ }); | ||
subset = schema.only(["name", "age"]); | ||
expect(subset.keys).to.eql(["name", "age"]); | ||
expect(subset.fields).to.eql(["name", "age"]); | ||
}); | ||
@@ -234,3 +228,3 @@ | ||
expect(function () { | ||
schema.except(schema.keys); | ||
schema.except(schema.fields); | ||
}).to.throw("Cannot create a subset of the schema with no keys"); | ||
@@ -248,3 +242,3 @@ }); | ||
expect(extended).to.not.equal(schema); | ||
expect(extended.keys).to.not.equal(schema.keys); | ||
expect(extended.fields).to.not.equal(schema.fields); | ||
expect(extended.types).to.not.equal(schema.types); | ||
@@ -262,3 +256,3 @@ expect(extended).to.eql(schema); | ||
expect(extended.keys).to.contain("password", "token"); | ||
expect(extended.fields).to.contain("password", "token"); | ||
expect(extended.types.password).to.equal("String"); | ||
@@ -300,3 +294,3 @@ expect(extended.types.token).to.equal("String"); | ||
expect(extended.keys).to.contain("password", "token"); | ||
expect(extended.fields).to.contain("password", "token"); | ||
expect(extended.types.password).to.equal("String"); | ||
@@ -308,7 +302,8 @@ expect(extended.types.token).to.equal("String"); | ||
describe(".getWritableFields()", function () { | ||
describe(".writableFields()", function () { | ||
it("should return writable fields", function () { | ||
var writableFields = schema.getWritableFields(); | ||
expect(writableFields).to.eql(["age", "friends"]); | ||
var writableFields = schema.writableFields(); | ||
expect(writableFields).to.eql(["age"]); | ||
}); | ||
@@ -318,7 +313,9 @@ | ||
describe(".getWritableSchema()", function () { | ||
describe(".writable()", function () { | ||
it("should return a schema with only writeable Fields", function () { | ||
var writableSchema = schema.getWritableSchema(); | ||
expect(writableSchema.keys.length).to.eql(2); | ||
it("should return a schema with only writable fields", function () { | ||
var writableSchema = schema.writable(); | ||
expect(writableSchema).to.be.an.instanceOf(Schema); | ||
expect(writableSchema.fields).to.eql(["age"]); | ||
}); | ||
@@ -328,6 +325,7 @@ | ||
describe(".getReadableFields()", function () { | ||
describe(".readableFields()", function () { | ||
it("should return readable fields", function () { | ||
var readableFields = schema.getReadableFields(); | ||
var readableFields = schema.readableFields(); | ||
expect(readableFields).to.eql(["age", "friends"]); | ||
@@ -338,8 +336,9 @@ }); | ||
describe(".readable()", function () { | ||
describe(".getReadableSchema()", function () { | ||
it("should return a schema with only readable fields", function () { | ||
var readableSchema = schema.readable(); | ||
it("should return a schema with only readable Fields", function () { | ||
var readableSchema = schema.getReadableSchema(); | ||
expect(readableSchema.keys.length).to.eql(2); | ||
expect(readableSchema).to.be.an.instanceOf(Schema); | ||
expect(readableSchema.fields).to.eql(["age", "friends"]); | ||
}); | ||
@@ -349,4 +348,39 @@ | ||
describe(".strip(model)", function () { | ||
it("should remove all additional keys", function () { | ||
var model = { | ||
name: "Octocat", | ||
someOtherProperty: true, | ||
andAnArray: [1, 2, 3] | ||
}; | ||
schema.strip(model); | ||
expect(model).to.eql({ name: "Octocat" }); | ||
}); | ||
it("will not work with prototype inheritance", function () { | ||
var model = Object.create({ | ||
someOtherProperty: true, | ||
andAnArray: [1, 2, 3] | ||
}); | ||
model.name = "Octocat"; | ||
schema.strip(model); | ||
expect(model).to.eql(model); | ||
}); | ||
it("should work on empty objects", function () { | ||
var model = {}; | ||
expect(function () { | ||
schema.strip(model); | ||
}).to.not.throw(); | ||
expect(model).to.eql({}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
"use strict"; | ||
var chai = require("chai"), | ||
expect = chai.expect, | ||
spies = require('chai-spies'), | ||
chaiAsPromised = require("chai-as-promised"); | ||
var chai = require("chai"); | ||
var spies = require("chai-spies"); | ||
var chaiAsPromised = require("chai-as-promised"); | ||
var Schema = require("../lib/Schema.js"); | ||
var validators = require("../plugins/validation/validators.js"); | ||
var validationPlugin = require("../plugins/validation/index.js"); | ||
var expect = chai.expect; | ||
var Schema = require("../lib/Schema.js"), | ||
validators = require("../plugins/validation/validators.js"), | ||
validationPlugin = require("../plugins/validation/index.js"); | ||
chai.use(spies); | ||
chai.use(chaiAsPromised); | ||
chai.Assertion.includeStack = true; | ||
function oldEnough(age) { | ||
@@ -24,2 +19,5 @@ return age > 18 || "too-young"; | ||
chai.use(spies); | ||
chai.use(chaiAsPromised); | ||
chai.config.includeStack = true; | ||
Schema.use(validationPlugin); | ||
@@ -49,7 +47,6 @@ | ||
it("should add a enum validator", function () { | ||
var schema = new Schema({ | ||
tags: { | ||
type: String, | ||
"enum": ["a", "b"] | ||
enum: ["a", "b"] | ||
} | ||
@@ -64,3 +61,2 @@ }); | ||
it("should add a min validator", function () { | ||
var schema = new Schema({ | ||
@@ -79,3 +75,2 @@ age: { | ||
it("should add a max validator", function () { | ||
var schema = new Schema({ | ||
@@ -93,2 +88,55 @@ age: { | ||
it("should add a minLength validator", function () { | ||
var schema = new Schema({ | ||
name: { | ||
type: String, | ||
minLength: 5 | ||
} | ||
}); | ||
expect(schema.validators.name).to.be.an("array"); | ||
expect(schema.validators.name).to.have.length(1); | ||
expect(schema.validators.name[0].name).to.eql(validators.minLength().name); | ||
}); | ||
it("should add a maxLength validator", function () { | ||
var schema = new Schema({ | ||
name: { | ||
type: String, | ||
maxLength: 5 | ||
} | ||
}); | ||
expect(schema.validators.name).to.be.an("array"); | ||
expect(schema.validators.name).to.have.length(1); | ||
expect(schema.validators.name[0].name).to.eql(validators.maxLength().name); | ||
}); | ||
it("should add a hasLength validator", function () { | ||
var schema = new Schema({ | ||
name: { | ||
type: String, | ||
hasLength: 5 | ||
} | ||
}); | ||
expect(schema.validators.name).to.be.an("array"); | ||
expect(schema.validators.name).to.have.length(1); | ||
expect(schema.validators.name[0].name).to.eql(validators.hasLength().name); | ||
}); | ||
it("should add a matches validator", function () { | ||
var schema = new Schema({ | ||
age: { | ||
type: Number, | ||
matches: 2 | ||
} | ||
}); | ||
expect(schema.validators.age).to.be.an("array"); | ||
expect(schema.validators.age).to.have.length(1); | ||
expect(schema.validators.age[0].name).to.eql(validators.matches().name); | ||
}); | ||
}); | ||
@@ -99,3 +147,2 @@ | ||
it("should accept a single validation function", function () { | ||
var schema = new Schema({ | ||
@@ -114,3 +161,2 @@ age: { | ||
it("should accept an array of validation functions", function () { | ||
var schema = new Schema({ | ||
@@ -154,15 +200,15 @@ age: { | ||
var schema = new Schema({ | ||
age: { | ||
required: true, | ||
max: 99, | ||
validate: oldEnough | ||
} | ||
}), | ||
extended = schema.extend({ | ||
age: { | ||
required: false, | ||
validate: notTooOld | ||
} | ||
}), | ||
ageValidators = extended.validators.age.toString(); | ||
age: { | ||
required: true, | ||
max: 99, | ||
validate: oldEnough | ||
} | ||
}); | ||
var extended = schema.extend({ | ||
age: { | ||
required: false, | ||
validate: notTooOld | ||
} | ||
}); | ||
var ageValidators = extended.validators.age.toString(); | ||
@@ -226,5 +272,5 @@ expect(ageValidators).to.contain(validators.max(), oldEnough, notTooOld); | ||
schema.validate({age: 18}, function (validation) { | ||
schema.validate({ age: 18 }, function (validation) { | ||
expect(validation.result).to.eql(true); | ||
expect(validation.failedFields).to.eql({}); | ||
expect(validation.errors).to.eql({}); | ||
done(); | ||
@@ -246,5 +292,5 @@ }); | ||
schema.validate({age: 18}, function (validation) { | ||
schema.validate({ age: 18 }, function (validation) { | ||
expect(validation.result).to.eql(true); | ||
expect(validation.failedFields).to.eql({}); | ||
expect(validation.errors).to.eql({}); | ||
done(); | ||
@@ -266,8 +312,6 @@ }); | ||
it("should return a promise if no callback is given", function () { | ||
expect(schema.validate({age: 2})).to.be.a("promise"); | ||
expect(schema.validate({ age: 2 })).to.be.a("promise"); | ||
}); | ||
it("should resolve if validation succeeds", function () { | ||
schema = new Schema({ | ||
@@ -280,10 +324,9 @@ age: { | ||
return schema.validate({age: 4}) | ||
.then(function(validation) { | ||
expect(validation).to.eql({result: true, model: { age: 4 }, failedFields: {}}); | ||
return schema.validate({ age: 4 }) | ||
.then(function (validation) { | ||
expect(validation).to.eql({ result: true, model: { age: 4 }, errors: {} }); | ||
}); | ||
}); | ||
it("should reject if validation fails", function () { | ||
it("should not reject if validation fails", function () { | ||
schema = new Schema({ | ||
@@ -296,10 +339,45 @@ age: { | ||
return schema.validate({age: 1}) | ||
.then(function() { | ||
throw new Error("Should not resolve"); | ||
return schema.validate({ age: 1 }) | ||
.then(function (validation) { | ||
expect(validation).to.eql({ result: false, model: { age: 1 }, errors: { age: ["min"] } }); | ||
}) | ||
.catch(function(validation) { | ||
expect(validation).to.eql({result: false, model: { age: 1 }, failedFields: {age: ["min"]}}); | ||
.catch(function () { | ||
throw new Error("Should not reject"); | ||
}); | ||
}); | ||
it("should provide the intermediate result of all synchronous validators", function () { | ||
var intermediateResult; | ||
schema = new Schema({ | ||
age: { | ||
type: Number, | ||
min: 5, | ||
validate: [ | ||
function syncTrue() { | ||
return true; | ||
}, | ||
function asyncTrue(value, callback) { | ||
setTimeout(function () { | ||
callback(true); | ||
}, 0); | ||
}, | ||
function syncFail() { | ||
return "sync-fail"; | ||
}, | ||
function asyncFail(value, callback) { | ||
setTimeout(function () { | ||
callback("async-fail"); | ||
}, 0); | ||
} | ||
] | ||
} | ||
}); | ||
intermediateResult = schema.validate({ age: 1 }).validation; | ||
expect(intermediateResult.result).to.equal(false); | ||
expect(intermediateResult.errors.age).to.eql(["min", "sync-fail"]); | ||
}); | ||
}); | ||
@@ -320,4 +398,4 @@ | ||
it("should pass if async & sync validators pass", function (done) { | ||
var asyncSpy = chai.spy(validateAgeAsync), | ||
syncSpy = chai.spy(validateAgeSync); | ||
var asyncSpy = chai.spy(validateAgeAsync); | ||
var syncSpy = chai.spy(validateAgeSync); | ||
@@ -331,7 +409,7 @@ schema = new Schema({ | ||
schema.validate({age: 18}, function (validation) { | ||
schema.validate({ age: 18 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(true); | ||
expect(validation.failedFields).to.eql({}); | ||
expect(validation.errors).to.eql({}); | ||
done(); | ||
@@ -342,4 +420,4 @@ }); | ||
it("should fail if an async and sync validator fail", function (done) { | ||
var asyncSpy = chai.spy(validateAgeAsync), | ||
syncSpy = chai.spy(validateAgeSync); | ||
var asyncSpy = chai.spy(validateAgeAsync); | ||
var syncSpy = chai.spy(validateAgeSync); | ||
@@ -353,7 +431,7 @@ schema = new Schema({ | ||
schema.validate({age: 6}, function (validation) { | ||
schema.validate({ age: 6 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-async", "fail-sync"); | ||
expect(validation.errors.age).to.contain("fail-async", "fail-sync"); | ||
done(); | ||
@@ -365,7 +443,7 @@ }); | ||
var asyncSpy = chai.spy(function (age, callback) { | ||
setTimeout(function () { | ||
callback("fail-async"); | ||
}, 0); | ||
}), | ||
syncSpy = chai.spy(validateAgeSync); | ||
setTimeout(function () { | ||
callback("fail-async"); | ||
}, 0); | ||
}); | ||
var syncSpy = chai.spy(validateAgeSync); | ||
@@ -379,7 +457,7 @@ schema = new Schema({ | ||
schema.validate({age: 6}, function (validation) { | ||
schema.validate({ age: 6 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-async"); | ||
expect(validation.errors.age).to.contain("fail-async"); | ||
done(); | ||
@@ -389,9 +467,31 @@ }); | ||
it("should degrade gracefully with an false async validator", function (done) { | ||
var falseAsyncSpy = chai.spy(function (age, callback) { | ||
callback("fail-false-async"); // callback is called synchronously. This is a common error. | ||
}); | ||
schema = new Schema({ | ||
age: { | ||
type: Number, | ||
validate: falseAsyncSpy | ||
} | ||
}); | ||
schema.validate({ age: 8 }, function (validation) { | ||
expect(falseAsyncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.equal(false); | ||
expect(validation.errors.age).to.contain("fail-false-async"); | ||
done(); | ||
}); | ||
}); | ||
it("should fail if the sync validator fails & async passes", function (done) { | ||
var asyncSpy = chai.spy(function (age, callback) { | ||
callback(true); | ||
}), | ||
syncSpy = chai.spy(function (age) { | ||
return "fail-sync"; | ||
}); | ||
setTimeout(function () { | ||
callback("fail-async"); | ||
}, 0); | ||
}); | ||
var syncSpy = chai.spy(function (age) { | ||
return "fail-sync"; | ||
}); | ||
@@ -405,7 +505,7 @@ schema = new Schema({ | ||
schema.validate({age: 8}, function (validation) { | ||
schema.validate({ age: 8 }, function (validation) { | ||
expect(asyncSpy).to.have.been.called.once(); | ||
expect(syncSpy).to.have.been.called.once(); | ||
expect(validation.result).to.eql(false); | ||
expect(validation.failedFields.age).to.contain("fail-sync"); | ||
expect(validation.result).to.equal(false); | ||
expect(validation.errors.age).to.contain("fail-sync"); | ||
done(); | ||
@@ -419,2 +519,2 @@ }); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
62906
1
18
1390
0
382
0
6
- Removedwhen@^3.7.3
- Removedwhen@3.7.8(transitive)
Updatedvalue@^0.3.0