fixed #52 (compileAsync); loadSchema should be called only once for any schema; error is always passed via callback
parent
d6ed06766c
commit
a67b661e10
59
lib/ajv.js
59
lib/ajv.js
|
@ -30,6 +30,7 @@ function Ajv(opts) {
|
|||
this._refs = {};
|
||||
this._formats = formats(this.opts.format);
|
||||
this._cache = this.opts.cache || new Cache;
|
||||
this._loadingSchemas = {};
|
||||
|
||||
// this is done on purpose, so that methods are bound to the instance
|
||||
// (without using bind) so that they can be used without the instance
|
||||
|
@ -91,12 +92,16 @@ function Ajv(opts) {
|
|||
* @param {Function} callback node-style callback, it is always called with 2 parameters: error (or null) and validating function.
|
||||
*/
|
||||
function compileAsync(schema, callback) {
|
||||
var schemaObj = _addSchema(schema);
|
||||
if (schemaObj.validate) {
|
||||
setTimeout(function() {
|
||||
callback(null, schemaObj.validate);
|
||||
});
|
||||
} else {
|
||||
var schemaObj;
|
||||
try {
|
||||
schemaObj = _addSchema(schema);
|
||||
} catch(e) {
|
||||
setTimeout(function() { callback(e); });
|
||||
return;
|
||||
}
|
||||
if (schemaObj.validate)
|
||||
setTimeout(function() { callback(null, schemaObj.validate); });
|
||||
else {
|
||||
if (typeof self.opts.loadSchema != 'function')
|
||||
throw new Error('options.loadSchema should be a function');
|
||||
_compileAsync(schema, callback, true);
|
||||
|
@ -109,27 +114,51 @@ function Ajv(opts) {
|
|||
try { validate = compile(schema); }
|
||||
catch(e) {
|
||||
if (e.missingSchema) loadMissingSchema(e);
|
||||
else if (firstCall) setTimeout(function() { callback(e); });
|
||||
else callback(e);
|
||||
|
||||
return;
|
||||
}
|
||||
if (firstCall)
|
||||
setTimeout(function() {
|
||||
callback(null, validate);
|
||||
});
|
||||
else
|
||||
callback(null, validate);
|
||||
if (firstCall) setTimeout(function() { callback(null, validate); });
|
||||
else callback(null, validate);
|
||||
|
||||
function loadMissingSchema(e) {
|
||||
var ref = e.missingSchema;
|
||||
if (self._refs[ref] || self._schemas[ref])
|
||||
return callback(new Error('Schema ' + ref + ' is loaded but' + e.missingRef + 'cannot be resolved'));
|
||||
self.opts.loadSchema(ref, function(err, sch) {
|
||||
var _callbacks = self._loadingSchemas[ref];
|
||||
if (_callbacks) {
|
||||
if (typeof _callbacks == 'function')
|
||||
self._loadingSchemas[ref] = [_callbacks, schemaLoaded];
|
||||
else
|
||||
_callbacks[_callbacks.length] = schemaLoaded;
|
||||
} else {
|
||||
self._loadingSchemas[ref] = schemaLoaded;
|
||||
self.opts.loadSchema(ref, function (err, sch) {
|
||||
var _callbacks = self._loadingSchemas[ref];
|
||||
delete self._loadingSchemas[ref];
|
||||
if (typeof _callbacks == 'function')
|
||||
_callbacks(err, sch);
|
||||
else
|
||||
for (var i=0; i<_callbacks.length; i++)
|
||||
_callbacks[i](err, sch);
|
||||
});
|
||||
}
|
||||
|
||||
function schemaLoaded(err, sch) {
|
||||
if (err) callback(err);
|
||||
else {
|
||||
addSchema(sch, ref);
|
||||
if (!(self._refs[ref] || self._schemas[ref])) {
|
||||
try {
|
||||
addSchema(sch, ref);
|
||||
} catch(e) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_compileAsync(schema, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ajv",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.2",
|
||||
"description": "Another JSON Schema Validator",
|
||||
"main": "lib/ajv.js",
|
||||
"files": [
|
||||
|
|
|
@ -7,7 +7,7 @@ var Ajv = require(typeof window == 'object' ? 'ajv' : '../lib/ajv')
|
|||
|
||||
|
||||
describe('compileAsync method', function() {
|
||||
var ajv;
|
||||
var ajv, loadCallCount;
|
||||
|
||||
var SCHEMAS = {
|
||||
"http://example.com/object.json": {
|
||||
|
@ -39,10 +39,18 @@ describe('compileAsync method', function() {
|
|||
"b": { "$ref": "parent.json" }
|
||||
},
|
||||
"required": ["b"]
|
||||
},
|
||||
"http://example.com/invalid.json": {
|
||||
"id": "http://example.com/recursive.json",
|
||||
"properties": {
|
||||
"invalid": { "type": "number" }
|
||||
},
|
||||
"required": "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
loadCallCount = 0;
|
||||
ajv = Ajv({ loadSchema: loadSchema });
|
||||
});
|
||||
|
||||
|
@ -55,6 +63,7 @@ describe('compileAsync method', function() {
|
|||
}
|
||||
};
|
||||
ajv.compileAsync(schema, function (err, validate) {
|
||||
loadCallCount .should.equal(2);
|
||||
should.not.exist(err);
|
||||
validate .should.be.a('function');
|
||||
validate({ a: { b: 2 } }) .should.equal(true);
|
||||
|
@ -72,6 +81,7 @@ describe('compileAsync method', function() {
|
|||
}
|
||||
};
|
||||
ajv.compileAsync(schema, function (err, validate) {
|
||||
loadCallCount .should.equal(2);
|
||||
should.not.exist(err);
|
||||
validate .should.be.a('function');
|
||||
validate({ a: 2 }) .should.equal(true);
|
||||
|
@ -113,6 +123,7 @@ describe('compileAsync method', function() {
|
|||
}
|
||||
};
|
||||
ajv.compileAsync(schema, function (err, validate) {
|
||||
loadCallCount .should.equal(1);
|
||||
should.not.exist(err);
|
||||
validate .should.be.a('function');
|
||||
var validData = { a: { b: { a: { b: {} } } } };
|
||||
|
@ -124,11 +135,7 @@ describe('compileAsync method', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should return compiled schema on the next tick if there are no references', function (done) {
|
||||
var loadCalled = false;
|
||||
var ajv = Ajv({ loadSchema: function() {
|
||||
loadCalled = true;
|
||||
} });
|
||||
it('should return compiled schema on the next tick if there are no references (#51)', function (done) {
|
||||
var schema = {
|
||||
"id": "http://example.com/int2plus.json",
|
||||
"type": "integer",
|
||||
|
@ -148,7 +155,7 @@ describe('compileAsync method', function() {
|
|||
|
||||
function spec(err, validate) {
|
||||
should.not.exist(err);
|
||||
loadCalled .should.equal(false);
|
||||
loadCallCount .should.equal(0);
|
||||
validate .should.be.a('function');
|
||||
var validData = 2;
|
||||
var invalidData = 1;
|
||||
|
@ -158,7 +165,104 @@ describe('compileAsync method', function() {
|
|||
});
|
||||
|
||||
|
||||
it('should queue calls so only one compileAsync executes at a time (#52)', function (done) {
|
||||
var schema = {
|
||||
"id": "http://example.com/parent.json",
|
||||
"properties": {
|
||||
"a": { "$ref": "object.json" }
|
||||
}
|
||||
};
|
||||
|
||||
var completedCount = 0;
|
||||
ajv.compileAsync(schema, spec);
|
||||
ajv.compileAsync(schema, spec);
|
||||
ajv.compileAsync(schema, spec);
|
||||
|
||||
function spec(err, validate) {
|
||||
should.not.exist(err);
|
||||
validate .should.be.a('function');
|
||||
validate({ a: { b: 2 } }) .should.equal(true);
|
||||
validate({ a: { b: 1 } }) .should.equal(false);
|
||||
completed();
|
||||
}
|
||||
|
||||
function completed() {
|
||||
completedCount++;
|
||||
if (completedCount == 3) {
|
||||
loadCallCount .should.equal(2);
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it('should throw exception if loadSchema is not passed', function (done) {
|
||||
var schema = {
|
||||
"id": "http://example.com/int2plus.json",
|
||||
"type": "integer",
|
||||
"minimum": 2
|
||||
};
|
||||
var ajv = Ajv();
|
||||
should.throw(function() {
|
||||
ajv.compileAsync(schema, function() {
|
||||
done(new Error('it should have thrown exception'));
|
||||
});
|
||||
});
|
||||
setTimeout(done);
|
||||
});
|
||||
|
||||
|
||||
describe('should return error via callback', function() {
|
||||
it('if passed schema is invalid', function (done) {
|
||||
var invalidSchema = {
|
||||
"id": "http://example.com/int2plus.json",
|
||||
"type": "integer",
|
||||
"minimum": "invalid"
|
||||
};
|
||||
ajv.compileAsync(invalidSchema, function (err, validate) {
|
||||
should.exist(err);
|
||||
should.not.exist(validate);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('if loaded schema is invalid', function (done) {
|
||||
var schema = {
|
||||
"id": "http://example.com/parent.json",
|
||||
"properties": {
|
||||
"a": { "$ref": "invalid.json" }
|
||||
}
|
||||
};
|
||||
ajv.compileAsync(schema, function (err, validate) {
|
||||
should.exist(err);
|
||||
should.not.exist(validate);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('if loadSchema returned error', function (done) {
|
||||
var schema = {
|
||||
"id": "http://example.com/parent.json",
|
||||
"properties": {
|
||||
"a": { "$ref": "object.json" }
|
||||
}
|
||||
};
|
||||
var ajv = Ajv({ loadSchema: badLoadSchema });
|
||||
ajv.compileAsync(schema, function (err, validate) {
|
||||
should.exist(err);
|
||||
should.not.exist(validate);
|
||||
done();
|
||||
});
|
||||
|
||||
function badLoadSchema(ref, callback) {
|
||||
setTimeout(function() { callback(new Error('cant load')); });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function loadSchema(uri, callback) {
|
||||
loadCallCount++;
|
||||
setTimeout(function() {
|
||||
if (SCHEMAS[uri]) callback(null, SCHEMAS[uri]);
|
||||
else callback(new Error('404'));
|
||||
|
|
|
@ -142,7 +142,7 @@ describe('Validation errors', function () {
|
|||
});
|
||||
|
||||
|
||||
it('errors for items should include item index without quotes in dataPath', function() {
|
||||
it('errors for items should include item index without quotes in dataPath (#48)', function() {
|
||||
var schema1 = {
|
||||
id: 'schema1',
|
||||
type: 'array',
|
||||
|
|
Loading…
Reference in New Issue