diff --git a/README.md b/README.md index fa782e6..9adf95e 100644 --- a/README.md +++ b/README.md @@ -419,7 +419,7 @@ If your schema uses asynchronous formats/keywords or refers to some schema that __Please note__: all asynchronous subschemas that are referenced from the current or other schemas should have `"$async": true` keyword as well, otherwise the schema compilation will fail. -Validation function for an asynchronous custom format/keyword should return a promise that resolves to `true` or `false` (or rejects with `new Ajv.ValidationError(errors)` if you want to return custom errors from the keyword function). Ajv compiles asynchronous schemas to either [es7 async functions](http://tc39.github.io/ecmascript-asyncawait/) (default) that can optionally be transpiled with [nodent](https://github.com/MatAtBread/nodent) or with [regenerator](https://github.com/facebook/regenerator) or to [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) that can be optionally transpiled with regenerator as well. You can also supply any other transpiler as a function. See [Options](#options). +Validation function for an asynchronous custom format/keyword should return a promise that resolves with `true` or `false` (or rejects with `new Ajv.ValidationError(errors)` if you want to return custom errors from the keyword function). Ajv compiles asynchronous schemas to either [es7 async functions](http://tc39.github.io/ecmascript-asyncawait/) (default) that can optionally be transpiled with [nodent](https://github.com/MatAtBread/nodent) or with [regenerator](https://github.com/facebook/regenerator) or to [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) that can be optionally transpiled with regenerator as well. You can also supply any other transpiler as a function. See [Options](#options). The compiled validation function has `$async: true` property (if the schema is asynchronous), so you can differentiate these functions if you are using both syncronous and asynchronous schemas. @@ -431,7 +431,7 @@ Generator functions are currently supported in Chrome, Firefox and node.js. If you are using Ajv in other browsers or in older versions of node.js you should use one of available transpiling options. All provided async modes use global Promise class. If your platform does not have Promise you should use a polyfill that defines it. -Validation result will be a promise that resolves to `true` or rejects with an exception `Ajv.ValidationError` that has the array of validation errors in `errors` property. +Validation result will be a promise that resolves with validated data or rejects with an exception `Ajv.ValidationError` that has the array of validation errors in `errors` property. Example: @@ -481,9 +481,8 @@ var schema = { var validate = ajv.compile(schema); validate({ userId: 1, postId: 19 })) -.then(function (valid) { - // "valid" is always true here - console.log('Data is valid'); +.then(function (data) { + console.log('Data is valid', data); // { userId: 1, postId: 19 } }) .catch(function (err) { if (!(err instanceof Ajv.ValidationError)) throw err; diff --git a/lib/compile/util.js b/lib/compile/util.js index 5dbef47..8bd58f7 100644 --- a/lib/compile/util.js +++ b/lib/compile/util.js @@ -149,8 +149,8 @@ var ERRORS_REGEXP = /[^v\.]errors/g , REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/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_ASYNC = /if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/ + , RETURN_DATA_ASYNC = 'return data;' , ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g , REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/; @@ -159,7 +159,7 @@ function finalCleanUpCode(out, async) { if (!matches || matches.length !== 2) return out; out = async ? out.replace(REMOVE_ERRORS_ASYNC, '') - .replace(RETURN_ASYNC, RETURN_TRUE_ASYNC) + .replace(RETURN_ASYNC, RETURN_DATA_ASYNC) : out.replace(REMOVE_ERRORS, '') .replace(RETURN_VALID, RETURN_TRUE); diff --git a/lib/dot/ref.jst b/lib/dot/ref.jst index deca860..4a08896 100644 --- a/lib/dot/ref.jst +++ b/lib/dot/ref.jst @@ -63,12 +63,16 @@ {{? $async }} {{ if (!it.async) throw new Error('async schema referenced by sync schema'); }} - try { {{? $breakOnError }}var {{=$valid}} ={{?}} {{=it.yieldAwait}} {{=__callValidate}}; } - catch (e) { + {{? $breakOnError }} var {{=$valid}}; {{?}} + try { + {{=it.yieldAwait}} {{=__callValidate}}; + {{? $breakOnError }} {{=$valid}} = true; {{?}} + } catch (e) { if (!(e instanceof ValidationError)) throw e; if (vErrors === null) vErrors = e.errors; else vErrors = vErrors.concat(e.errors); errors = vErrors.length; + {{? $breakOnError }} {{=$valid}} = false; {{?}} } {{? $breakOnError }} if ({{=$valid}}) { {{?}} {{??}} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 82023a7..9a88529 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -54,8 +54,12 @@ {{# def.error:'false schema' }} {{??}} {{? it.isTop}} - {{? !$async }} validate.errors = null; {{?}} - return true; + {{? $async }} + return data; + {{??}} + validate.errors = null; + return true; + {{?}} {{??}} var {{=$valid}} = true; {{?}} @@ -211,7 +215,7 @@ {{? $top }} {{? $async }} - if (errors === 0) return true; {{ /* don't edit, used in replace */ }} + if (errors === 0) return data; {{ /* don't edit, used in replace */ }} else throw new ValidationError(vErrors); {{ /* don't edit, used in replace */ }} {{??}} validate.errors = vErrors; {{ /* don't edit, used in replace */ }} diff --git a/package.json b/package.json index 37ef414..6c439b1 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "glob": "^7.0.0", "if-node-version": "^1.0.0", "js-beautify": "^1.5.6", - "json-schema-test": "^1.1.1", + "json-schema-test": "^1.3.0", "karma": "^1.0.0", "karma-chrome-launcher": "^2.0.0", "karma-mocha": "^1.1.1", diff --git a/spec/async_schemas.spec.js b/spec/async_schemas.spec.js index ef87e69..cca8202 100644 --- a/spec/async_schemas.spec.js +++ b/spec/async_schemas.spec.js @@ -23,6 +23,7 @@ jsonSchemaTest(instances, { : './async/{**/,}*.json' }, async: true, + asyncValid: 'data', assert: require('./chai').assert, Promise: Promise, afterError: after.error, diff --git a/spec/async_validate.spec.js b/spec/async_validate.spec.js index 915f713..10992f7 100644 --- a/spec/async_validate.spec.js +++ b/spec/async_validate.spec.js @@ -38,7 +38,7 @@ describe('async schemas, formats and keywords', function() { var _co = useCo(_ajv); return Promise.all([ - shouldBeValid( _co(validate('abc')) ), + shouldBeValid( _co(validate('abc')), 'abc' ), shouldBeInvalid( _co(validate('abcd')) ), shouldBeInvalid( _co(validate(1)) ), ]); @@ -214,9 +214,10 @@ describe('async schemas, formats and keywords', function() { return repeat(function() { return Promise.all(instances.map(function (_ajv) { var validate = _ajv.compile(schema); var _co = useCo(_ajv); + var validData = { word: 'tomorrow' }; return Promise.all([ - shouldBeValid( _co(validate({ word: 'tomorrow' })) ), + shouldBeValid( _co(validate(validData)), validData ), shouldBeInvalid( _co(validate({ word: 'manana' })) ), shouldBeInvalid( _co(validate({ word: 1 })) ), shouldThrow( _co(validate({ word: 'today' })), 'unknown word' ) @@ -339,17 +340,18 @@ describe('async schemas, formats and keywords', function() { if (refSchema) try { _ajv.addSchema(refSchema); } catch(e) {} var validate = _ajv.compile(schema); var _co = useCo(_ajv); + var data; return Promise.all([ - shouldBeValid( _co(validate({ foo: 'tomorrow' })) ), + shouldBeValid( _co(validate(data = { foo: 'tomorrow' })), data ), shouldBeInvalid( _co(validate({ foo: 'manana' })) ), shouldBeInvalid( _co(validate({ foo: 1 })) ), shouldThrow( _co(validate({ foo: 'today' })), 'unknown word' ), - shouldBeValid( _co(validate({ foo: { foo: 'tomorrow' }})) ), + shouldBeValid( _co(validate(data = { foo: { foo: 'tomorrow' }})), data ), shouldBeInvalid( _co(validate({ foo: { foo: 'manana' }})) ), shouldBeInvalid( _co(validate({ foo: { foo: 1 }})) ), shouldThrow( _co(validate({ foo: { foo: 'today' }})), 'unknown word' ), - shouldBeValid( _co(validate({ foo: { foo: { foo: 'tomorrow' }}})) ), + shouldBeValid( _co(validate(data = { foo: { foo: { foo: 'tomorrow' }}})), data ), shouldBeInvalid( _co(validate({ foo: { foo: { foo: 'manana' }}})) ), shouldBeInvalid( _co(validate({ foo: { foo: { foo: 1 }}})) ), shouldThrow( _co(validate({ foo: { foo: { foo: 'today' }}})), 'unknown word' ) @@ -417,9 +419,9 @@ function shouldThrowFunc(message, func) { } -function shouldBeValid(p) { +function shouldBeValid(p, data) { return p.then(function (valid) { - valid .should.equal(true); + valid .should.equal(data); }); }