feat: support boolean schemas in $ref

master
Evgeny Poberezkin 2016-12-25 17:43:26 +00:00
parent 225ef67ce5
commit f1028c8411
7 changed files with 186 additions and 21 deletions

View File

@ -209,7 +209,7 @@ function compile(schema, root, localRefs, baseId) {
refCode = addLocalRef(ref); refCode = addLocalRef(ref);
var v = resolve.call(self, localCompile, root, ref); var v = resolve.call(self, localCompile, root, ref);
if (!v) { if (v === undefined) {
var localSchema = localRefs && localRefs[ref]; var localSchema = localRefs && localRefs[ref];
if (localSchema) { if (localSchema) {
v = resolve.inlineRef(localSchema, opts.inlineRefs) v = resolve.inlineRef(localSchema, opts.inlineRefs)
@ -218,7 +218,7 @@ function compile(schema, root, localRefs, baseId) {
} }
} }
if (v) { if (v !== undefined) {
replaceLocalRef(ref, v); replaceLocalRef(ref, v);
return resolvedRef(v, refCode); return resolvedRef(v, refCode);
} }
@ -237,7 +237,7 @@ function compile(schema, root, localRefs, baseId) {
} }
function resolvedRef(refVal, code) { function resolvedRef(refVal, code) {
return typeof refVal == 'object' return typeof refVal == 'object' || typeof refVal == 'boolean'
? { code: code, schema: refVal, inline: true } ? { code: code, schema: refVal, inline: true }
: { code: code, $async: refVal && refVal.$async }; : { code: code, $async: refVal && refVal.$async };
} }
@ -377,7 +377,7 @@ function defaultCode(i) {
function refValCode(i, refVal) { function refValCode(i, refVal) {
return refVal[i] ? 'var refVal' + i + ' = refVal[' + i + '];' : ''; return refVal[i] === undefined ? '' : 'var refVal' + i + ' = refVal[' + i + '];';
} }

View File

@ -47,7 +47,7 @@ function resolve(compile, root, ref) {
if (schema instanceof SchemaObject) { if (schema instanceof SchemaObject) {
v = schema.validate || compile.call(this, schema.schema, root, undefined, baseId); v = schema.validate || compile.call(this, schema.schema, root, undefined, baseId);
} else if (schema) { } else if (schema !== undefined) {
v = inlineRef(schema, this._opts.inlineRefs) v = inlineRef(schema, this._opts.inlineRefs)
? schema ? schema
: compile.call(this, schema, root, undefined, baseId); : compile.call(this, schema, root, undefined, baseId);
@ -122,7 +122,7 @@ function getJsonPointer(parsedRef, baseId, schema, root) {
if (part) { if (part) {
part = util.unescapeFragment(part); part = util.unescapeFragment(part);
schema = schema[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.id && !PREVENT_SCOPE_CHANGE[part]) baseId = resolveUrl(baseId, schema.id);
if (schema.$ref) { if (schema.$ref) {
var $ref = resolveUrl(baseId, 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 }; return { schema: schema, root: root, baseId: baseId };
} }

View File

@ -13,7 +13,7 @@ module.exports = {
varOccurences: varOccurences, varOccurences: varOccurences,
varReplace: varReplace, varReplace: varReplace,
cleanUpCode: cleanUpCode, cleanUpCode: cleanUpCode,
cleanUpVarErrors: cleanUpVarErrors, finalCleanUpCode: finalCleanUpCode,
schemaHasRules: schemaHasRules, schemaHasRules: schemaHasRules,
schemaHasRulesExcept: schemaHasRulesExcept, schemaHasRulesExcept: schemaHasRulesExcept,
stableStringify: require('json-stable-stringify'), stableStringify: require('json-stable-stringify'),
@ -150,16 +150,22 @@ var ERRORS_REGEXP = /[^v\.]errors/g
, RETURN_VALID = 'return errors === 0;' , RETURN_VALID = 'return errors === 0;'
, RETURN_TRUE = 'validate.errors = null; return true;' , RETURN_TRUE = 'validate.errors = null; return true;'
, RETURN_ASYNC = /if \(errors === 0\) return true;\s*else throw new ValidationError\(vErrors\);/ , 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); var matches = out.match(ERRORS_REGEXP);
if (!matches || matches.length !== 2) return out; if (!matches || matches.length !== 2) return out;
return async out = async
? out.replace(REMOVE_ERRORS_ASYNC, '') ? out.replace(REMOVE_ERRORS_ASYNC, '')
.replace(RETURN_ASYNC, RETURN_TRUE_ASYNC) .replace(RETURN_ASYNC, RETURN_TRUE_ASYNC)
: out.replace(REMOVE_ERRORS, '') : out.replace(REMOVE_ERRORS, '')
.replace(RETURN_VALID, RETURN_TRUE); .replace(RETURN_VALID, RETURN_TRUE);
matches = out.match(ROOTDATA_REGEXP);
if (!matches || matches.length !== 3) return out;
return out.replace(REMOVE_ROOTDATA, '');
} }

View File

@ -113,7 +113,7 @@
{{## def.cleanUp: {{ out = it.util.cleanUpCode(out); }} #}} {{## 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: {{## def.$data:

View File

@ -43,9 +43,9 @@
{{?}} {{?}}
{{? typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref) }} {{? typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref) }}
{{ var $keyword = 'false schema'; }}
{{# def.setupKeyword }}
{{? it.schema === false}} {{? it.schema === false}}
{{ var $keyword = 'false schema'; }}
{{# def.setupKeyword }}
{{? it.isTop}} {{? it.isTop}}
{{ $breakOnError = true; }} {{ $breakOnError = true; }}
{{??}} {{??}}
@ -85,7 +85,7 @@
var vErrors = null; {{ /* don't edit, used in replace */ }} var vErrors = null; {{ /* don't edit, used in replace */ }}
var errors = 0; {{ /* 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 var $lvl = it.level
@ -227,7 +227,7 @@
{{# def.cleanUp }} {{# def.cleanUp }}
{{? $top && $breakOnError }} {{? $top && $breakOnError }}
{{# def.cleanUpVarErrors }} {{# def.finalCleanUp }}
{{?}} {{?}}
{{ {{

126
spec/async/boolean.json Normal file
View File

@ -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
}
]
}
]

View File

@ -8,7 +8,11 @@ describe('boolean schemas', function() {
var ajvs; var ajvs;
before(function() { 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() { 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) { function testSchema(validate, valid) {
validate(1) .should.equal(valid); validate(1) .should.equal(valid);
validate('foo') .should.equal(valid); validate('foo') .should.equal(valid);