From 2d9241a6580de2827b790e1ecdd2609192897373 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 15 Aug 2016 21:16:59 +0100 Subject: [PATCH] feat: allow refs to fragments in "getSchema" and "validate" methods, closes #47 --- lib/ajv.js | 24 ++++++++++++++++++- lib/compile/resolve.js | 17 +++++++++---- package.json | 2 +- spec/ajv.spec.js | 54 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 7 deletions(-) diff --git a/lib/ajv.js b/lib/ajv.js index f06251d..ca8ba0e 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -39,6 +39,7 @@ function Ajv(opts) { opts = this._opts = util.copy(opts) || {}; this._schemas = {}; this._refs = {}; + this._fragments = {}; this._formats = formats(opts.format); this._cache = opts.cache || new Cache; this._loadingSchemas = {}; @@ -187,13 +188,34 @@ function Ajv(opts) { switch (typeof schemaObj) { case 'object': return schemaObj.validate || _compile(schemaObj); case 'string': return getSchema(schemaObj); + case 'undefined': return _getSchemaFragment(keyRef); + } + } + + + function _getSchemaFragment(ref) { + var res = resolve.schema.call(self, { schema: {} }, ref); + if (res) { + var schema = res.schema + , root = res.root + , baseId = res.baseId; + var v = compileSchema.call(self, schema, root, undefined, baseId); + self._fragments[ref] = new SchemaObject({ + ref: ref, + fragment: true, + schema: schema, + root: root, + baseId: baseId, + validate: v + }); + return v; } } function _getSchemaObj(keyRef) { keyRef = resolve.normalizeId(keyRef); - return self._schemas[keyRef] || self._refs[keyRef]; + return self._schemas[keyRef] || self._refs[keyRef] || self._fragments[keyRef]; } diff --git a/lib/compile/resolve.js b/lib/compile/resolve.js index e834ef4..f9bd713 100644 --- a/lib/compile/resolve.js +++ b/lib/compile/resolve.js @@ -12,6 +12,7 @@ resolve.fullPath = getFullPath; resolve.url = resolveUrl; resolve.ids = resolveIds; resolve.inlineRef = inlineRef; +resolve.schema = resolveSchema; /** * [resolve and compile the references ($ref)] @@ -36,7 +37,7 @@ function resolve(compile, root, ref) { : refVal.validate || this._compile(refVal); } - var res = _resolve.call(this, root, ref); + var res = resolveSchema.call(this, root, ref); var schema, v, baseId; if (res) { schema = res.schema; @@ -56,8 +57,14 @@ function resolve(compile, root, ref) { } -/* @this Ajv */ -function _resolve(root, ref) { +/** + * Resolve schema, its root and baseId + * @this Ajv + * @param {Object} root root object with properties schema, refVal, refs + * @param {String} ref reference to resolve + * @return {Object} object with properties schema, root, baseId + */ +function resolveSchema(root, ref) { /* jshint validthis: true */ var p = url.parse(ref, false, true) , refPath = _getFullPath(p) @@ -91,7 +98,7 @@ function _resolve(root, ref) { /* @this Ajv */ function resolveRecursive(root, ref, parsedRef) { /* jshint validthis: true */ - var res = _resolve.call(this, root, ref); + var res = resolveSchema.call(this, root, ref); if (res) { var schema = res.schema; var baseId = res.baseId; @@ -119,7 +126,7 @@ function getJsonPointer(parsedRef, baseId, schema, root) { if (schema.id && !PREVENT_SCOPE_CHANGE[part]) baseId = resolveUrl(baseId, schema.id); if (schema.$ref) { var $ref = resolveUrl(baseId, schema.$ref); - var res = _resolve.call(this, root, $ref); + var res = resolveSchema.call(this, root, $ref); if (res) { schema = res.schema; root = res.root; diff --git a/package.json b/package.json index a66a9dc..a1253f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "4.4.1", + "version": "4.5.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", diff --git a/spec/ajv.spec.js b/spec/ajv.spec.js index 0c99afe..645dad8 100644 --- a/spec/ajv.spec.js +++ b/spec/ajv.spec.js @@ -88,6 +88,32 @@ describe('Ajv', function () { ajv.validate('integer', 1) .should.equal(true); should.throw(function() { ajv.validate('string', 'foo'); }); }); + + it('should validate schema fragment by ref', function() { + ajv.addSchema({ + "id": "http://e.com/types.json", + "definitions": { + "int": { "type": "integer" }, + "str": { "type": "string" } + } + }); + + ajv.validate('http://e.com/types.json#/definitions/int', 1) .should.equal(true); + ajv.validate('http://e.com/types.json#/definitions/int', '1') .should.equal(false); + }); + + it('should return schema fragment by id', function() { + ajv.addSchema({ + "id": "http://e.com/types.json", + "definitions": { + "int": { "id": "#int", "type": "integer" }, + "str": { "id": "#str", "type": "string" } + } + }); + + ajv.validate('http://e.com/types.json#int', 1) .should.equal(true); + ajv.validate('http://e.com/types.json#int', '1') .should.equal(false); + }); }); @@ -210,6 +236,34 @@ describe('Ajv', function () { v(1) .should.equal(true); v('1') .should.equal(false); }); + + it('should return schema fragment by ref', function() { + ajv.addSchema({ + "id": "http://e.com/types.json", + "definitions": { + "int": { "type": "integer" }, + "str": { "type": "string" } + } + }); + + var vInt = ajv.getSchema('http://e.com/types.json#/definitions/int'); + vInt(1) .should.equal(true); + vInt('1') .should.equal(false); + }); + + it('should return schema fragment by id', function() { + ajv.addSchema({ + "id": "http://e.com/types.json", + "definitions": { + "int": { "id": "#int", "type": "integer" }, + "str": { "id": "#str", "type": "string" } + } + }); + + var vInt = ajv.getSchema('http://e.com/types.json#int'); + vInt(1) .should.equal(true); + vInt('1') .should.equal(false); + }); });