diff --git a/lib/compile/index.js b/lib/compile/index.js index 00a6bbd..98faedb 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -209,7 +209,7 @@ function compile(schema, root, localRefs, baseId) { refCode = addLocalRef(ref); var v = resolve.call(self, localCompile, root, ref); - if (!v) { + if (v === undefined) { var localSchema = localRefs && localRefs[ref]; if (localSchema) { v = resolve.inlineRef(localSchema, opts.inlineRefs) @@ -218,7 +218,7 @@ function compile(schema, root, localRefs, baseId) { } } - if (v) { + if (v !== undefined) { replaceLocalRef(ref, v); return resolvedRef(v, refCode); } @@ -237,7 +237,7 @@ function compile(schema, root, localRefs, baseId) { } function resolvedRef(refVal, code) { - return typeof refVal == 'object' + return typeof refVal == 'object' || typeof refVal == 'boolean' ? { code: code, schema: refVal, inline: true } : { code: code, $async: refVal && refVal.$async }; } @@ -377,7 +377,7 @@ function defaultCode(i) { function refValCode(i, refVal) { - return refVal[i] ? 'var refVal' + i + ' = refVal[' + i + '];' : ''; + return refVal[i] === undefined ? '' : 'var refVal' + i + ' = refVal[' + i + '];'; } diff --git a/lib/compile/resolve.js b/lib/compile/resolve.js index db2b91f..211493c 100644 --- a/lib/compile/resolve.js +++ b/lib/compile/resolve.js @@ -47,7 +47,7 @@ function resolve(compile, root, ref) { if (schema instanceof SchemaObject) { v = schema.validate || compile.call(this, schema.schema, root, undefined, baseId); - } else if (schema) { + } else if (schema !== undefined) { v = inlineRef(schema, this._opts.inlineRefs) ? schema : compile.call(this, schema, root, undefined, baseId); @@ -122,7 +122,7 @@ function getJsonPointer(parsedRef, baseId, schema, root) { if (part) { part = util.unescapeFragment(part); schema = schema[part]; - if (!schema) break; + if (schema === undefined) break; if (schema.id && !PREVENT_SCOPE_CHANGE[part]) baseId = resolveUrl(baseId, schema.id); if (schema.$ref) { var $ref = resolveUrl(baseId, schema.$ref); @@ -135,7 +135,7 @@ function getJsonPointer(parsedRef, baseId, schema, root) { } } } - if (schema && schema != root.schema) + if (schema !== undefined && schema !== root.schema) return { schema: schema, root: root, baseId: baseId }; } diff --git a/lib/compile/util.js b/lib/compile/util.js index 744705d..5dbef47 100644 --- a/lib/compile/util.js +++ b/lib/compile/util.js @@ -13,7 +13,7 @@ module.exports = { varOccurences: varOccurences, varReplace: varReplace, cleanUpCode: cleanUpCode, - cleanUpVarErrors: cleanUpVarErrors, + finalCleanUpCode: finalCleanUpCode, schemaHasRules: schemaHasRules, schemaHasRulesExcept: schemaHasRulesExcept, stableStringify: require('json-stable-stringify'), @@ -150,16 +150,22 @@ var ERRORS_REGEXP = /[^v\.]errors/g , RETURN_VALID = 'return errors === 0;' , RETURN_TRUE = 'validate.errors = null; return true;' , RETURN_ASYNC = /if \(errors === 0\) return true;\s*else throw new ValidationError\(vErrors\);/ - , RETURN_TRUE_ASYNC = 'return true;'; + , RETURN_TRUE_ASYNC = 'return true;' + , ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g + , REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/; -function cleanUpVarErrors(out, async) { +function finalCleanUpCode(out, async) { var matches = out.match(ERRORS_REGEXP); if (!matches || matches.length !== 2) return out; - return async - ? out.replace(REMOVE_ERRORS_ASYNC, '') - .replace(RETURN_ASYNC, RETURN_TRUE_ASYNC) - : out.replace(REMOVE_ERRORS, '') - .replace(RETURN_VALID, RETURN_TRUE); + out = async + ? out.replace(REMOVE_ERRORS_ASYNC, '') + .replace(RETURN_ASYNC, RETURN_TRUE_ASYNC) + : out.replace(REMOVE_ERRORS, '') + .replace(RETURN_VALID, RETURN_TRUE); + + matches = out.match(ROOTDATA_REGEXP); + if (!matches || matches.length !== 3) return out; + return out.replace(REMOVE_ROOTDATA, ''); } diff --git a/lib/dot/definitions.def b/lib/dot/definitions.def index a30cfd5..7632fe7 100644 --- a/lib/dot/definitions.def +++ b/lib/dot/definitions.def @@ -113,7 +113,7 @@ {{## def.cleanUp: {{ out = it.util.cleanUpCode(out); }} #}} -{{## def.cleanUpVarErrors: {{ out = it.util.cleanUpVarErrors(out, $async); }} #}} +{{## def.finalCleanUp: {{ out = it.util.finalCleanUpCode(out, $async); }} #}} {{## def.$data: diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 026b70a..82023a7 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -43,9 +43,9 @@ {{?}} {{? typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref) }} + {{ var $keyword = 'false schema'; }} + {{# def.setupKeyword }} {{? it.schema === false}} - {{ var $keyword = 'false schema'; }} - {{# def.setupKeyword }} {{? it.isTop}} {{ $breakOnError = true; }} {{??}} @@ -85,7 +85,7 @@ var vErrors = null; {{ /* don't edit, used in replace */ }} var errors = 0; {{ /* don't edit, used in replace */ }} - if (rootData === undefined) rootData = data; + if (rootData === undefined) rootData = data; {{ /* don't edit, used in replace */ }} {{??}} {{ var $lvl = it.level @@ -227,7 +227,7 @@ {{# def.cleanUp }} {{? $top && $breakOnError }} - {{# def.cleanUpVarErrors }} + {{# def.finalCleanUp }} {{?}} {{ diff --git a/spec/async/boolean.json b/spec/async/boolean.json new file mode 100644 index 0000000..59284c5 --- /dev/null +++ b/spec/async/boolean.json @@ -0,0 +1,126 @@ +[ + { + "description": "boolean schema = true in properties", + "schema": { + "$async": true, + "properties": { + "foo": true + } + }, + "tests": [ + { + "description": "any data is valid", + "data": { "foo": 1 }, + "valid": true + } + ] + }, + { + "description": "boolean schema = false in properties", + "schema": { + "$async": true, + "properties": { + "foo": false + } + }, + "tests": [ + { + "description": "any property is invalid", + "data": { "foo": 1 }, + "valid": false + }, + { + "description": "without property is valid", + "data": { "bar": 1 }, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "boolean schema = true in $ref", + "schema": { + "$async": true, + "$ref": "#/definitions/true", + "definitions": { + "true": true + } + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "boolean schema = false in $ref", + "schema": { + "$async": true, + "$ref": "#/definitions/false", + "definitions": { + "false": false + } + }, + "tests": [ + { + "description": "any data is invalid", + "data": 1, + "valid": false + } + ] + }, + { + "description": "boolean schema = true in properties with $ref", + "schema": { + "$async": true, + "properties": { + "foo": { "$ref": "#/definitions/foo" } + }, + "definitions": { + "foo": true + } + }, + "tests": [ + { + "description": "any data is valid", + "data": { "foo": 1 }, + "valid": true + } + ] + }, + { + "description": "boolean schema = false in properties with $ref", + "schema": { + "$async": true, + "properties": { + "foo": { "$ref": "#/definitions/foo" } + }, + "definitions": { + "foo": false + } + }, + "tests": [ + { + "description": "any property is invalid", + "data": { "foo": 1 }, + "valid": false + }, + { + "description": "without property is valid", + "data": { "bar": 1 }, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/spec/boolean.spec.js b/spec/boolean.spec.js index 2a3474c..eeb8a47 100644 --- a/spec/boolean.spec.js +++ b/spec/boolean.spec.js @@ -8,7 +8,11 @@ describe('boolean schemas', function() { var ajvs; before(function() { - ajvs = [ new Ajv, new Ajv({allErrors: true}) ]; + ajvs = [ + new Ajv, + new Ajv({allErrors: true}), + new Ajv({inlineRefs: false}) + ]; }); describe('top level schema', function() { @@ -431,6 +435,35 @@ describe('boolean schemas', function() { }); + describe('in $ref', function() { + describe('schema = true', function() { + it('should be valid with any data', function() { + ajvs.forEach(test(true, true)); + }); + }); + + describe('schema = false', function() { + it('should be invalid with any data', function() { + ajvs.forEach(test(false, false)); + }); + }); + + function test(boolSchema, valid) { + return function (ajv) { + var schema = { + $ref: '#/definitions/bool', + definitions: { + bool: boolSchema + } + }; + + var validate = ajv.compile(schema); + testSchema(validate, valid); + }; + } + }); + + function testSchema(validate, valid) { validate(1) .should.equal(valid); validate('foo') .should.equal(valid);