From 84b0531e396807c96fb957aaa2a8cd670398891f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 8 Nov 2016 00:49:29 +0000 Subject: [PATCH] feat: async loading of meta-schema, #334 --- lib/compile/async.js | 28 +++++++++++++++++++++++----- spec/async.spec.js | 20 ++++++++++++++------ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/lib/compile/async.js b/lib/compile/async.js index aebffbc..d80a12b 100644 --- a/lib/compile/async.js +++ b/lib/compile/async.js @@ -8,20 +8,25 @@ module.exports = compileAsync; * `loadSchema` option should be a function that accepts schema uri and returns promise that resolves with the schema. * @this Ajv * @param {Object} schema schema object + * @param {Boolean} meta optional true to compile meta-schema; this parameter can be skipped * @param {Function} callback an optional node-style callback, it is called with 2 parameters: error (or null) and validating function. * @return {Promise} promise that resolves with a validating function. */ -function compileAsync(schema, callback) { +function compileAsync(schema, meta, callback) { /* eslint no-shadow: 0 */ /* global Promise */ /* jshint validthis: true */ - var schemaObj; var self = this; if (typeof this._opts.loadSchema != 'function') throw new Error('options.loadSchema should be a function'); - var p = Promise.resolve().then(function () { - schemaObj = self._addSchema(schema); + if (typeof meta == 'function') { + callback = meta; + meta = undefined; + } + + var p = loadMetaSchemaOf(schema).then(function () { + var schemaObj = self._addSchema(schema, undefined, meta); return schemaObj.validate || _compileAsync(schemaObj); }); @@ -35,6 +40,14 @@ function compileAsync(schema, callback) { return p; + function loadMetaSchemaOf(sch) { + var $schema = sch.$schema; + return $schema && !self.getSchema($schema) + ? compileAsync.call(self, { $ref: $schema }, true) + : Promise.resolve(); + } + + function _compileAsync(schemaObj) { try { return self._compile(schemaObj); } catch(e) { @@ -54,7 +67,12 @@ function compileAsync(schema, callback) { } return schemaPromise.then(function (sch) { - if (!added(ref)) self.addSchema(sch, ref); + if (!added(ref)) { + return loadMetaSchemaOf(sch).then(function () { + if (!added(ref)) self.addSchema(sch, ref, undefined, meta); + }); + } + }).then(function() { return _compileAsync(schemaObj); }); diff --git a/spec/async.spec.js b/spec/async.spec.js index 1702dad..5363e32 100644 --- a/spec/async.spec.js +++ b/spec/async.spec.js @@ -177,9 +177,16 @@ describe('compileAsync method', function() { }); - describe.skip('loading metaschemas (#334)', function() { + describe('loading metaschemas (#334)', function() { it('should load metaschema if not available', function() { - var schema = { "$ref": "http://example.com/foobar.json" }; + return test(SCHEMAS['http://example.com/foobar.json'], 1); + }); + + it('should load metaschema of referenced schema if not available', function() { + return test({ "$ref": "http://example.com/foobar.json" }, 2); + }); + + function test(schema, expectedLoadCallCount) { ajv.addKeyword('myFooBar', { type: 'string', validate: function (sch, data) { @@ -188,12 +195,12 @@ describe('compileAsync method', function() { }); return ajv.compileAsync(schema).then(function (validate) { - should.equal(loadCallCount, 2); + should.equal(loadCallCount, expectedLoadCallCount); validate .should.be.a('function'); validate('foo') .should.equal(true); validate('bar') .should.equal(false); }); - }); + } }); @@ -237,13 +244,14 @@ describe('compileAsync method', function() { } }; - return Promise.all[ + return Promise.all([ ajv.compileAsync(schema).then(spec), ajv.compileAsync(schema).then(spec), ajv.compileAsync(schema).then(spec) - ]; + ]); function spec(validate) { + should.equal(loadCallCount, 2); validate .should.be.a('function'); validate({ a: { b: 2 } }) .should.equal(true); validate({ a: { b: 1 } }) .should.equal(false);