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);
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 + '];';
}

View File

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

View File

@ -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, '');
}

View File

@ -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:

View File

@ -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 }}
{{?}}
{{

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;
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);