feat: async loading of meta-schema, #334

master
Evgeny Poberezkin 2016-11-08 00:49:29 +00:00
parent f95b83d890
commit 84b0531e39
2 changed files with 37 additions and 11 deletions

View File

@ -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. * `loadSchema` option should be a function that accepts schema uri and returns promise that resolves with the schema.
* @this Ajv * @this Ajv
* @param {Object} schema schema object * @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. * @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. * @return {Promise} promise that resolves with a validating function.
*/ */
function compileAsync(schema, callback) { function compileAsync(schema, meta, callback) {
/* eslint no-shadow: 0 */ /* eslint no-shadow: 0 */
/* global Promise */ /* global Promise */
/* jshint validthis: true */ /* jshint validthis: true */
var schemaObj;
var self = this; var self = this;
if (typeof this._opts.loadSchema != 'function') if (typeof this._opts.loadSchema != 'function')
throw new Error('options.loadSchema should be a function'); throw new Error('options.loadSchema should be a function');
var p = Promise.resolve().then(function () { if (typeof meta == 'function') {
schemaObj = self._addSchema(schema); callback = meta;
meta = undefined;
}
var p = loadMetaSchemaOf(schema).then(function () {
var schemaObj = self._addSchema(schema, undefined, meta);
return schemaObj.validate || _compileAsync(schemaObj); return schemaObj.validate || _compileAsync(schemaObj);
}); });
@ -35,6 +40,14 @@ function compileAsync(schema, callback) {
return p; 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) { function _compileAsync(schemaObj) {
try { return self._compile(schemaObj); } try { return self._compile(schemaObj); }
catch(e) { catch(e) {
@ -54,7 +67,12 @@ function compileAsync(schema, callback) {
} }
return schemaPromise.then(function (sch) { 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); return _compileAsync(schemaObj);
}); });

View File

@ -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() { 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', { ajv.addKeyword('myFooBar', {
type: 'string', type: 'string',
validate: function (sch, data) { validate: function (sch, data) {
@ -188,12 +195,12 @@ describe('compileAsync method', function() {
}); });
return ajv.compileAsync(schema).then(function (validate) { return ajv.compileAsync(schema).then(function (validate) {
should.equal(loadCallCount, 2); should.equal(loadCallCount, expectedLoadCallCount);
validate .should.be.a('function'); validate .should.be.a('function');
validate('foo') .should.equal(true); validate('foo') .should.equal(true);
validate('bar') .should.equal(false); 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), ajv.compileAsync(schema).then(spec),
ajv.compileAsync(schema).then(spec) ajv.compileAsync(schema).then(spec)
]; ]);
function spec(validate) { function spec(validate) {
should.equal(loadCallCount, 2);
validate .should.be.a('function'); validate .should.be.a('function');
validate({ a: { b: 2 } }) .should.equal(true); validate({ a: { b: 2 } }) .should.equal(true);
validate({ a: { b: 1 } }) .should.equal(false); validate({ a: { b: 1 } }) .should.equal(false);