Merge branch 'master' into beta
commit
04d62ae67e
129
CUSTOM.md
129
CUSTOM.md
|
@ -42,18 +42,27 @@ __Please note__: In cases when validation flow is different depending on the sch
|
|||
Example. `constant` keyword (a synonym for draft6 keyword `const`, it is equivalent to `enum` keyword with one item):
|
||||
|
||||
```javascript
|
||||
ajv.addKeyword('constant', { validate: function (schema, data) {
|
||||
return typeof schema == 'object' && schema !== null
|
||||
? deepEqual(schema, data)
|
||||
: schema === data;
|
||||
}, errors: false });
|
||||
ajv.addKeyword('constant', {
|
||||
validate: function (schema, data) {
|
||||
return typeof schema == 'object' && schema !== null
|
||||
? deepEqual(schema, data)
|
||||
: schema === data;
|
||||
},
|
||||
errors: false
|
||||
});
|
||||
|
||||
var schema = { "constant": 2 };
|
||||
var schema = {
|
||||
"constant": 2
|
||||
};
|
||||
var validate = ajv.compile(schema);
|
||||
console.log(validate(2)); // true
|
||||
console.log(validate(3)); // false
|
||||
|
||||
var schema = { "constant": { "foo": "bar" } };
|
||||
var schema = {
|
||||
"constant": {
|
||||
"foo": "bar"
|
||||
}
|
||||
};
|
||||
var validate = ajv.compile(schema);
|
||||
console.log(validate({foo: 'bar'})); // true
|
||||
console.log(validate({foo: 'baz'})); // false
|
||||
|
@ -86,20 +95,31 @@ All custom keywords types can have an optional `metaSchema` property in their de
|
|||
Example. `range` and `exclusiveRange` keywords using compiled schema:
|
||||
|
||||
```javascript
|
||||
ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema) {
|
||||
var min = sch[0];
|
||||
var max = sch[1];
|
||||
ajv.addKeyword('range', {
|
||||
type: 'number',
|
||||
compile: function (sch, parentSchema) {
|
||||
var min = sch[0];
|
||||
var max = sch[1];
|
||||
|
||||
return parentSchema.exclusiveRange === true
|
||||
? function (data) { return data > min && data < max; }
|
||||
: function (data) { return data >= min && data <= max; }
|
||||
}, errors: false, metaSchema: {
|
||||
type: 'array',
|
||||
items: [ { type: 'number' }, { type: 'number' } ],
|
||||
additionalItems: false
|
||||
} });
|
||||
return parentSchema.exclusiveRange === true
|
||||
? function (data) { return data > min && data < max; }
|
||||
: function (data) { return data >= min && data <= max; }
|
||||
},
|
||||
errors: false,
|
||||
metaSchema: {
|
||||
type: 'array',
|
||||
items: [
|
||||
{ type: 'number' },
|
||||
{ type: 'number' }
|
||||
],
|
||||
additionalItems: false
|
||||
}
|
||||
});
|
||||
|
||||
var schema = { "range": [2, 4], "exclusiveRange": true };
|
||||
var schema = {
|
||||
"range": [2, 4],
|
||||
"exclusiveRange": true
|
||||
};
|
||||
var validate = ajv.compile(schema);
|
||||
console.log(validate(2.01)); // true
|
||||
console.log(validate(3.99)); // true
|
||||
|
@ -122,27 +142,30 @@ In addition to the errors from the expanded schema macro keyword will add its ow
|
|||
Example. `range` and `exclusiveRange` keywords from the previous example defined with macro:
|
||||
|
||||
```javascript
|
||||
ajv.addKeyword('range', { type: 'number', macro: function (schema, parentSchema) {
|
||||
return {
|
||||
minimum: schema[0],
|
||||
maximum: schema[1],
|
||||
exclusiveMinimum: !!parentSchema.exclusiveRange,
|
||||
exclusiveMaximum: !!parentSchema.exclusiveRange
|
||||
};
|
||||
}, metaSchema: {
|
||||
type: 'array',
|
||||
items: [ { type: 'number' }, { type: 'number' } ],
|
||||
additionalItems: false
|
||||
} });
|
||||
ajv.addKeyword('range', {
|
||||
type: 'number',
|
||||
macro: function (schema, parentSchema) {
|
||||
return {
|
||||
minimum: schema[0],
|
||||
maximum: schema[1],
|
||||
exclusiveMinimum: !!parentSchema.exclusiveRange,
|
||||
exclusiveMaximum: !!parentSchema.exclusiveRange
|
||||
};
|
||||
},
|
||||
metaSchema: {
|
||||
type: 'array',
|
||||
items: [
|
||||
{ type: 'number' },
|
||||
{ type: 'number' }
|
||||
],
|
||||
additionalItems: false
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Example. `contains` keyword from version 5 proposals that requires that the array has at least one item matching schema (see https://github.com/json-schema/json-schema/wiki/contains-(v5-proposal)):
|
||||
|
||||
```javascript
|
||||
ajv.addKeyword('contains', { type: 'array', macro: function (schema) {
|
||||
return { "not": { "items": { "not": schema } } };
|
||||
} });
|
||||
|
||||
var schema = {
|
||||
"contains": {
|
||||
"type": "number",
|
||||
|
@ -151,7 +174,20 @@ var schema = {
|
|||
}
|
||||
};
|
||||
|
||||
var validate = ajv.compile(schema);
|
||||
var validate = ajv.addKeyword('contains', {
|
||||
type: 'array',
|
||||
macro: function (schema) {
|
||||
return {
|
||||
"not": {
|
||||
"items": {
|
||||
"not": schema
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
.compile(schema);
|
||||
|
||||
console.log(validate([1,2,3])); // false
|
||||
console.log(validate([2,3,4])); // false
|
||||
console.log(validate([3,4,5])); // true, number 5 matches schema inside "contains"
|
||||
|
@ -177,14 +213,18 @@ While it can be more challenging to define keywords with "inline" functions, it
|
|||
Example `even` keyword:
|
||||
|
||||
```javascript
|
||||
ajv.addKeyword('even', { type: 'number', inline: function (it, keyword, schema) {
|
||||
var op = schema ? '===' : '!==';
|
||||
return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0';
|
||||
}, metaSchema: { type: 'boolean' } });
|
||||
|
||||
var schema = { "even": true };
|
||||
|
||||
var validate = ajv.compile(schema);
|
||||
var validate = ajv.addKeyword('even', {
|
||||
type: 'number',
|
||||
inline: function (it, keyword, schema) {
|
||||
var op = schema ? '===' : '!==';
|
||||
return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0';
|
||||
},
|
||||
metaSchema: { type: 'boolean' }
|
||||
})
|
||||
.compile(schema);
|
||||
|
||||
console.log(validate(2)); // true
|
||||
console.log(validate(3)); // false
|
||||
```
|
||||
|
@ -214,7 +254,10 @@ ajv.addKeyword('range', {
|
|||
statements: true,
|
||||
metaSchema: {
|
||||
type: 'array',
|
||||
items: [ { type: 'number' }, { type: 'number' } ],
|
||||
items: [
|
||||
{ type: 'number' },
|
||||
{ type: 'number' }
|
||||
],
|
||||
additionalItems: false
|
||||
}
|
||||
});
|
||||
|
|
|
@ -49,7 +49,7 @@ The keywords and their values define what rules the data should satisfy to be va
|
|||
|
||||
`type` keyword requires that the data is of certain type (or some of types). Its value can be a string (the allowed type) or an array of strings (multiple allowed types).
|
||||
|
||||
Type can be: number, integer, string, boolean, array, object or null.
|
||||
Type can be: `number`, `integer`, `string`, `boolean`, `array`, `object` or `null`.
|
||||
|
||||
|
||||
__Examples__
|
||||
|
|
55
README.md
55
README.md
|
@ -7,8 +7,8 @@ The fastest JSON Schema validator for Node.js and browser with draft-07 support.
|
|||
|
||||
[![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv)
|
||||
[![npm version](https://badge.fury.io/js/ajv.svg)](https://www.npmjs.com/package/ajv)
|
||||
[![npm@beta](https://img.shields.io/npm/v/ajv/beta.svg)](https://github.com/epoberezkin/ajv/tree/beta)
|
||||
[![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv)
|
||||
[![Code Climate](https://codeclimate.com/github/epoberezkin/ajv/badges/gpa.svg)](https://codeclimate.com/github/epoberezkin/ajv)
|
||||
[![Coverage Status](https://coveralls.io/repos/epoberezkin/ajv/badge.svg?branch=master&service=github)](https://coveralls.io/github/epoberezkin/ajv?branch=master)
|
||||
[![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/)
|
||||
[![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv)
|
||||
|
@ -110,6 +110,12 @@ Currently Ajv is the only validator that passes all the tests from [JSON Schema
|
|||
npm install ajv
|
||||
```
|
||||
|
||||
or to install [version 6](https://github.com/epoberezkin/ajv/tree/beta):
|
||||
|
||||
```
|
||||
npm install ajv@beta
|
||||
```
|
||||
|
||||
|
||||
## <a name="usage"></a>Getting started
|
||||
|
||||
|
@ -139,8 +145,8 @@ or
|
|||
|
||||
```javascript
|
||||
// ...
|
||||
ajv.addSchema(schema, 'mySchema');
|
||||
var valid = ajv.validate('mySchema', data);
|
||||
var valid = ajv.addSchema(schema, 'mySchema')
|
||||
.validate('mySchema', data);
|
||||
if (!valid) console.log(ajv.errorsText());
|
||||
// ...
|
||||
```
|
||||
|
@ -293,8 +299,8 @@ or use `addSchema` method:
|
|||
|
||||
```javascript
|
||||
var ajv = new Ajv;
|
||||
ajv.addSchema(defsSchema);
|
||||
var validate = ajv.compile(schema);
|
||||
var validate = ajv.addSchema(defsSchema)
|
||||
.compile(schema);
|
||||
```
|
||||
|
||||
See [Options](#options) and [addSchema](#api) method.
|
||||
|
@ -450,14 +456,17 @@ Ajv allows defining keywords with:
|
|||
Example. `range` and `exclusiveRange` keywords using compiled schema:
|
||||
|
||||
```javascript
|
||||
ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema) {
|
||||
var min = sch[0];
|
||||
var max = sch[1];
|
||||
ajv.addKeyword('range', {
|
||||
type: 'number',
|
||||
compile: function (sch, parentSchema) {
|
||||
var min = sch[0];
|
||||
var max = sch[1];
|
||||
|
||||
return parentSchema.exclusiveRange === true
|
||||
? function (data) { return data > min && data < max; }
|
||||
: function (data) { return data >= min && data <= max; }
|
||||
} });
|
||||
return parentSchema.exclusiveRange === true
|
||||
? function (data) { return data > min && data < max; }
|
||||
: function (data) { return data >= min && data <= max; }
|
||||
}
|
||||
});
|
||||
|
||||
var schema = { "range": [2, 4], "exclusiveRange": true };
|
||||
var validate = ajv.compile(schema);
|
||||
|
@ -869,7 +878,7 @@ __Please note__: every time this method is called the errors are overwritten so
|
|||
If the schema is asynchronous (has `$async` keyword on the top level) this method returns a Promise. See [Asynchronous validation](#asynchronous-validation).
|
||||
|
||||
|
||||
##### .addSchema(Array<Object>|Object schema [, String key])
|
||||
##### .addSchema(Array<Object>|Object schema [, String key]) -> Ajv
|
||||
|
||||
Add schema(s) to validator instance. This method does not compile schemas (but it still validates them). Because of that dependencies can be added in any order and circular dependencies are supported. It also prevents unnecessary compilation of schemas that are containers for other schemas but not used as a whole.
|
||||
|
||||
|
@ -884,8 +893,14 @@ Although `addSchema` does not compile schemas, explicit compilation is not requi
|
|||
|
||||
By default the schema is validated against meta-schema before it is added, and if the schema does not pass validation the exception is thrown. This behaviour is controlled by `validateSchema` option.
|
||||
|
||||
__Please note__: Ajv uses the [method chaining syntax](https://en.wikipedia.org/wiki/Method_chaining) for all methods with the prefix `add*` and `remove*`.
|
||||
This allows you to do nice things like the following.
|
||||
|
||||
##### .addMetaSchema(Array<Object>|Object schema [, String key])
|
||||
```javascript
|
||||
var validate = new Ajv().addSchema(schema).addFormat(name, regex).getSchema(uri);
|
||||
```
|
||||
|
||||
##### .addMetaSchema(Array<Object>|Object schema [, String key]) -> Ajv
|
||||
|
||||
Adds meta schema(s) that can be used to validate other schemas. That function should be used instead of `addSchema` because there may be instance options that would compile a meta schema incorrectly (at the moment it is `removeAdditional` option).
|
||||
|
||||
|
@ -910,7 +925,7 @@ Errors will be available at `ajv.errors`.
|
|||
Retrieve compiled schema previously added with `addSchema` by the key passed to `addSchema` or by its full reference (id). The returned validating function has `schema` property with the reference to the original schema.
|
||||
|
||||
|
||||
##### .removeSchema([Object schema|String key|String ref|RegExp pattern])
|
||||
##### .removeSchema([Object schema|String key|String ref|RegExp pattern]) -> Ajv
|
||||
|
||||
Remove added/cached schema. Even if schema is referenced by other schemas it can be safely removed as dependent schemas have local references.
|
||||
|
||||
|
@ -923,7 +938,7 @@ Schema can be removed using:
|
|||
If no parameter is passed all schemas but meta-schemas will be removed and the cache will be cleared.
|
||||
|
||||
|
||||
##### <a name="api-addformat"></a>.addFormat(String name, String|RegExp|Function|Object format)
|
||||
##### <a name="api-addformat"></a>.addFormat(String name, String|RegExp|Function|Object format) -> Ajv
|
||||
|
||||
Add custom format to validate strings or numbers. It can also be used to replace pre-defined formats for Ajv instance.
|
||||
|
||||
|
@ -941,7 +956,7 @@ If object is passed it should have properties `validate`, `compare` and `async`:
|
|||
Custom formats can be also added via `formats` option.
|
||||
|
||||
|
||||
##### <a name="api-addkeyword"></a>.addKeyword(String keyword, Object definition)
|
||||
##### <a name="api-addkeyword"></a>.addKeyword(String keyword, Object definition) -> Ajv
|
||||
|
||||
Add custom validation keyword to Ajv instance.
|
||||
|
||||
|
@ -982,7 +997,7 @@ See [Defining custom keywords](#defining-custom-keywords) for more details.
|
|||
Returns custom keyword definition, `true` for pre-defined keywords and `false` if the keyword is unknown.
|
||||
|
||||
|
||||
##### .removeKeyword(String keyword)
|
||||
##### .removeKeyword(String keyword) -> Ajv
|
||||
|
||||
Removes custom or pre-defined keyword so you can redefine them.
|
||||
|
||||
|
@ -1016,6 +1031,7 @@ Defaults:
|
|||
formats: {},
|
||||
unknownFormats: true,
|
||||
schemas: {},
|
||||
logger: undefined,
|
||||
// referenced schema options:
|
||||
schemaId: undefined // recommended '$id'
|
||||
missingRefs: true,
|
||||
|
@ -1064,6 +1080,9 @@ Defaults:
|
|||
- `[String]` - an array of unknown format names that will be ignored. This option can be used to allow usage of third party schemas with format(s) for which you don't have definitions, but still fail if another unknown format is used. If `format` keyword value is [$data reference](#data-reference) and it is not in this array the validation will fail.
|
||||
- `"ignore"` - to log warning during schema compilation and always pass validation (the default behaviour in versions before 5.0.0). This option is not recommended, as it allows to mistype format name and it won't be validated without any error message. This behaviour is required by JSON-schema specification.
|
||||
- _schemas_: an array or object of schemas that will be added to the instance. In case you pass the array the schemas must have IDs in them. When the object is passed the method `addSchema(value, key)` will be called for each schema in this object.
|
||||
- _logger_: sets the logging method. Default is the global `console` object that should have methods `log`, `warn` and `error`. Option values:
|
||||
- custom logger - it should have methods `log`, `warn` and `error`. If any of these methods is missing an exception will be thrown.
|
||||
- `false` - logging is disabled.
|
||||
|
||||
|
||||
##### Referenced schema options
|
||||
|
|
|
@ -36,15 +36,17 @@ declare namespace ajv {
|
|||
* Adds schema to the instance.
|
||||
* @param {Object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored.
|
||||
* @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
addSchema(schema: Array<Object> | Object, key?: string): void;
|
||||
addSchema(schema: Array<Object> | Object, key?: string): Ajv;
|
||||
/**
|
||||
* Add schema that will be used to validate other schemas
|
||||
* options in META_IGNORE_OPTIONS are alway set to false
|
||||
* @param {Object} schema schema object
|
||||
* @param {String} key optional schema key
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
addMetaSchema(schema: Object, key?: string): void;
|
||||
addMetaSchema(schema: Object, key?: string): Ajv;
|
||||
/**
|
||||
* Validate schema
|
||||
* @param {Object|Boolean} schema schema to validate
|
||||
|
@ -63,21 +65,24 @@ declare namespace ajv {
|
|||
* If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed.
|
||||
* Even if schema is referenced by other schemas it still can be removed as other schemas have local references.
|
||||
* @param {String|Object|RegExp|Boolean} schemaKeyRef key, ref, pattern to match key/ref or schema object
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
removeSchema(schemaKeyRef?: Object | string | RegExp | boolean): void;
|
||||
removeSchema(schemaKeyRef?: Object | string | RegExp | boolean): Ajv;
|
||||
/**
|
||||
* Add custom format
|
||||
* @param {String} name format name
|
||||
* @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid)
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
addFormat(name: string, format: FormatValidator | FormatDefinition): void;
|
||||
addFormat(name: string, format: FormatValidator | FormatDefinition): Ajv;
|
||||
/**
|
||||
* Define custom keyword
|
||||
* @this Ajv
|
||||
* @param {String} keyword custom keyword, should be a valid identifier, should be different from all standard, custom and macro keywords.
|
||||
* @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`.
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
addKeyword(keyword: string, definition: KeywordDefinition): void;
|
||||
addKeyword(keyword: string, definition: KeywordDefinition): Ajv;
|
||||
/**
|
||||
* Get keyword definition
|
||||
* @this Ajv
|
||||
|
@ -89,8 +94,9 @@ declare namespace ajv {
|
|||
* Remove keyword
|
||||
* @this Ajv
|
||||
* @param {String} keyword pre-defined or custom keyword.
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
removeKeyword(keyword: string): void;
|
||||
removeKeyword(keyword: string): Ajv;
|
||||
/**
|
||||
* Convert array of error message objects to string
|
||||
* @param {Array<Object>} errors optional array of validation errors, if not passed errors from the instance are used.
|
||||
|
|
43
lib/ajv.js
43
lib/ajv.js
|
@ -51,6 +51,7 @@ var META_SUPPORT_DATA = ['/properties'];
|
|||
function Ajv(opts) {
|
||||
if (!(this instanceof Ajv)) return new Ajv(opts);
|
||||
opts = this._opts = util.copy(opts) || {};
|
||||
setLogger(this);
|
||||
this._schemas = {};
|
||||
this._refs = {};
|
||||
this._fragments = {};
|
||||
|
@ -122,6 +123,7 @@ function compile(schema, _meta) {
|
|||
* @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`.
|
||||
* @param {Boolean} _skipValidation true to skip schema validation. Used internally, option validateSchema should be used instead.
|
||||
* @param {Boolean} _meta true if schema is a meta-schema. Used internally, addMetaSchema should be used instead.
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
function addSchema(schema, key, _skipValidation, _meta) {
|
||||
if (Array.isArray(schema)){
|
||||
|
@ -134,6 +136,7 @@ function addSchema(schema, key, _skipValidation, _meta) {
|
|||
key = resolve.normalizeId(key || id);
|
||||
checkUnique(this, key);
|
||||
this._schemas[key] = this._addSchema(schema, _skipValidation, _meta, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -144,9 +147,11 @@ function addSchema(schema, key, _skipValidation, _meta) {
|
|||
* @param {Object} schema schema object
|
||||
* @param {String} key optional schema key
|
||||
* @param {Boolean} skipValidation true to skip schema validation, can be used to override validateSchema option for meta-schema
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
function addMetaSchema(schema, key, skipValidation) {
|
||||
this.addSchema(schema, key, skipValidation, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -163,7 +168,7 @@ function validateSchema(schema, throwOrLogError) {
|
|||
throw new Error('$schema must be a string');
|
||||
$schema = $schema || this._opts.defaultMeta || defaultMeta(this);
|
||||
if (!$schema) {
|
||||
console.warn('meta-schema not available');
|
||||
this.logger.warn('meta-schema not available');
|
||||
this.errors = null;
|
||||
return true;
|
||||
}
|
||||
|
@ -176,7 +181,7 @@ function validateSchema(schema, throwOrLogError) {
|
|||
finally { this._formats.uri = currentUriFormat; }
|
||||
if (!valid && throwOrLogError) {
|
||||
var message = 'schema is invalid: ' + this.errorsText();
|
||||
if (this._opts.validateSchema == 'log') console.error(message);
|
||||
if (this._opts.validateSchema == 'log') this.logger.error(message);
|
||||
else throw new Error(message);
|
||||
}
|
||||
return valid;
|
||||
|
@ -243,25 +248,26 @@ function _getSchemaObj(self, keyRef) {
|
|||
* Even if schema is referenced by other schemas it still can be removed as other schemas have local references.
|
||||
* @this Ajv
|
||||
* @param {String|Object|RegExp} schemaKeyRef key, ref, pattern to match key/ref or schema object
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
function removeSchema(schemaKeyRef) {
|
||||
if (schemaKeyRef instanceof RegExp) {
|
||||
_removeAllSchemas(this, this._schemas, schemaKeyRef);
|
||||
_removeAllSchemas(this, this._refs, schemaKeyRef);
|
||||
return;
|
||||
return this;
|
||||
}
|
||||
switch (typeof schemaKeyRef) {
|
||||
case 'undefined':
|
||||
_removeAllSchemas(this, this._schemas);
|
||||
_removeAllSchemas(this, this._refs);
|
||||
this._cache.clear();
|
||||
return;
|
||||
return this;
|
||||
case 'string':
|
||||
var schemaObj = _getSchemaObj(this, schemaKeyRef);
|
||||
if (schemaObj) this._cache.del(schemaObj.cacheKey);
|
||||
delete this._schemas[schemaKeyRef];
|
||||
delete this._refs[schemaKeyRef];
|
||||
return;
|
||||
return this;
|
||||
case 'object':
|
||||
var serialize = this._opts.serialize;
|
||||
var cacheKey = serialize ? serialize(schemaKeyRef) : schemaKeyRef;
|
||||
|
@ -273,6 +279,7 @@ function removeSchema(schemaKeyRef) {
|
|||
delete this._refs[id];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -375,15 +382,15 @@ function chooseGetId(opts) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* @this Ajv */
|
||||
function _getId(schema) {
|
||||
if (schema.$id) console.warn('schema $id ignored', schema.$id);
|
||||
if (schema.$id) this.logger.warn('schema $id ignored', schema.$id);
|
||||
return schema.id;
|
||||
}
|
||||
|
||||
|
||||
/* @this Ajv */
|
||||
function _get$Id(schema) {
|
||||
if (schema.id) console.warn('schema id ignored', schema.id);
|
||||
if (schema.id) this.logger.warn('schema id ignored', schema.id);
|
||||
return schema.$id;
|
||||
}
|
||||
|
||||
|
@ -423,10 +430,12 @@ function errorsText(errors, options) {
|
|||
* @this Ajv
|
||||
* @param {String} name format name
|
||||
* @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid)
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
function addFormat(name, format) {
|
||||
if (typeof format == 'string') format = new RegExp(format);
|
||||
this._formats[name] = format;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -472,3 +481,19 @@ function getMetaSchemaOptions(self) {
|
|||
delete metaOpts[META_IGNORE_OPTIONS[i]];
|
||||
return metaOpts;
|
||||
}
|
||||
|
||||
|
||||
function setLogger(self) {
|
||||
var logger = self._opts.logger;
|
||||
if (logger === false) {
|
||||
self.logger = {log: noop, warn: noop, error: noop};
|
||||
} else {
|
||||
if (logger === undefined) logger = console;
|
||||
if (!(typeof logger == 'object' && logger.log && logger.warn && logger.error))
|
||||
throw new Error('logger must implement log, warn and error methods');
|
||||
self.logger = logger;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function noop() {}
|
||||
|
|
|
@ -103,6 +103,7 @@ function compile(schema, root, localRefs, baseId) {
|
|||
useCustomRule: useCustomRule,
|
||||
opts: opts,
|
||||
formats: formats,
|
||||
logger: self.logger,
|
||||
self: self
|
||||
});
|
||||
|
||||
|
@ -143,7 +144,7 @@ function compile(schema, root, localRefs, baseId) {
|
|||
|
||||
refVal[0] = validate;
|
||||
} catch(e) {
|
||||
console.error('Error compiling schema, function code:', sourceCode);
|
||||
self.logger.error('Error compiling schema, function code:', sourceCode);
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
@ -257,7 +258,7 @@ function compile(schema, root, localRefs, baseId) {
|
|||
var valid = validateSchema(schema);
|
||||
if (!valid) {
|
||||
var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);
|
||||
if (self._opts.validateSchema == 'log') console.error(message);
|
||||
if (self._opts.validateSchema == 'log') self.logger.error(message);
|
||||
else throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
{{ var $format = it.formats[$schema]; }}
|
||||
{{? !$format }}
|
||||
{{? $unknownFormats == 'ignore' }}
|
||||
{{ console.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); }}
|
||||
{{ it.logger.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); }}
|
||||
{{# def.skipFormat }}
|
||||
{{?? $allowUnknown && $unknownFormats.indexOf($schema) >= 0 }}
|
||||
{{# def.skipFormat }}
|
||||
|
|
|
@ -27,11 +27,11 @@
|
|||
{{? $refVal === undefined }}
|
||||
{{ var $message = it.MissingRefError.message(it.baseId, $schema); }}
|
||||
{{? it.opts.missingRefs == 'fail' }}
|
||||
{{ console.error($message); }}
|
||||
{{ it.logger.error($message); }}
|
||||
{{# def.error:'$ref' }}
|
||||
{{? $breakOnError }} if (false) { {{?}}
|
||||
{{?? it.opts.missingRefs == 'ignore' }}
|
||||
{{ console.warn($message); }}
|
||||
{{ it.logger.warn($message); }}
|
||||
{{? $breakOnError }} if (true) { {{?}}
|
||||
{{??}}
|
||||
{{ throw new it.MissingRefError(it.baseId, $schema, $message); }}
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
{{?? it.opts.extendRefs !== true }}
|
||||
{{
|
||||
$refKeywords = false;
|
||||
console.warn('$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"');
|
||||
it.logger.warn('$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"');
|
||||
}}
|
||||
{{?}}
|
||||
{{?}}
|
||||
|
@ -163,7 +163,7 @@
|
|||
{{?}}
|
||||
{{??}}
|
||||
{{? it.opts.v5 && it.schema.patternGroups }}
|
||||
{{ console.warn('keyword "patternGroups" is deprecated and disabled. Use option patternGroups: true to enable.'); }}
|
||||
{{ it.logger.warn('keyword "patternGroups" is deprecated and disabled. Use option patternGroups: true to enable.'); }}
|
||||
{{?}}
|
||||
{{~ it.RULES:$rulesGroup }}
|
||||
{{? $shouldUseGroup($rulesGroup) }}
|
||||
|
|
|
@ -14,6 +14,7 @@ module.exports = {
|
|||
* @this Ajv
|
||||
* @param {String} keyword custom keyword, should be unique (including different from all standard, custom and macro keywords).
|
||||
* @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`.
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
function addKeyword(keyword, definition) {
|
||||
/* jshint validthis: true */
|
||||
|
@ -91,6 +92,8 @@ function addKeyword(keyword, definition) {
|
|||
function checkDataType(dataType) {
|
||||
if (!RULES.types[dataType]) throw new Error('Unknown type ' + dataType);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -111,6 +114,7 @@ function getKeyword(keyword) {
|
|||
* Remove keyword
|
||||
* @this Ajv
|
||||
* @param {String} keyword pre-defined or custom keyword.
|
||||
* @return {Ajv} this for method chaining
|
||||
*/
|
||||
function removeKeyword(keyword) {
|
||||
/* jshint validthis: true */
|
||||
|
@ -127,4 +131,5 @@ function removeKeyword(keyword) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,10 @@
|
|||
"type": "string"
|
||||
},
|
||||
"default": {},
|
||||
"examples": {
|
||||
"type": "array",
|
||||
"items": {}
|
||||
},
|
||||
"multipleOf": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0
|
||||
|
|
|
@ -119,8 +119,7 @@ describe('Ajv', function () {
|
|||
|
||||
describe('addSchema method', function() {
|
||||
it('should add and compile schema with key', function() {
|
||||
var res = ajv.addSchema({ type: 'integer' }, 'int');
|
||||
should.not.exist(res);
|
||||
ajv.addSchema({ type: 'integer' }, 'int');
|
||||
var validate = ajv.getSchema('int');
|
||||
validate .should.be.a('function');
|
||||
|
||||
|
@ -217,6 +216,11 @@ describe('Ajv', function () {
|
|||
e.message .should.equal('schema id must be string');
|
||||
}
|
||||
});
|
||||
|
||||
it('should return instance of itself', function() {
|
||||
var res = ajv.addSchema({ type: 'integer' }, 'int');
|
||||
res.should.equal(ajv);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
@ -382,6 +386,13 @@ describe('Ajv', function () {
|
|||
should.not.exist(ajv._cache.get(str2));
|
||||
ajv._cache.get(str3) .should.be.an('object');
|
||||
});
|
||||
|
||||
it('should return instance of itself', function() {
|
||||
var res = ajv
|
||||
.addSchema({ type: 'integer' }, 'int')
|
||||
.removeSchema('int');
|
||||
res.should.equal(ajv);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
@ -408,6 +419,11 @@ describe('Ajv', function () {
|
|||
testFormat();
|
||||
});
|
||||
|
||||
it('should return instance of itself', function() {
|
||||
var res = ajv.addFormat('identifier', /^[a-z_$][a-z0-9_$]*$/i);
|
||||
res.should.equal(ajv);
|
||||
});
|
||||
|
||||
function testFormat() {
|
||||
var validate = ajv.compile({ format: 'identifier' });
|
||||
validate('Abc1') .should.equal(true);
|
||||
|
|
|
@ -239,6 +239,32 @@ describe('async schemas, formats and keywords', function() {
|
|||
return recursiveTest(schema);
|
||||
});
|
||||
|
||||
it.skip('should validate recursive ref to async sub-schema, issue #612', function() {
|
||||
var schema = {
|
||||
$async: true,
|
||||
type: 'object',
|
||||
properties: {
|
||||
foo: {
|
||||
$async: true,
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
format: 'english_word'
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
foo: { $ref: '#/properties/foo' }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return recursiveTest(schema);
|
||||
});
|
||||
|
||||
it('should validate ref from referenced async schema to root schema', function() {
|
||||
var schema = {
|
||||
$async: true,
|
||||
|
|
|
@ -939,6 +939,13 @@ describe('Custom keywords', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('should return instance of itself', function() {
|
||||
var res = ajv.addKeyword('any', {
|
||||
validate: function() { return true; }
|
||||
});
|
||||
res.should.equal(ajv);
|
||||
});
|
||||
|
||||
it('should throw if unknown type is passed', function() {
|
||||
should.throw(function() {
|
||||
addKeyword('custom1', 'wrongtype');
|
||||
|
@ -1041,6 +1048,15 @@ describe('Custom keywords', function () {
|
|||
validate(1) .should.equal(false);
|
||||
validate(2) .should.equal(true);
|
||||
});
|
||||
|
||||
it('should return instance of itself', function() {
|
||||
var res = ajv
|
||||
.addKeyword('any', {
|
||||
validate: function() { return true; }
|
||||
})
|
||||
.removeKeyword('any');
|
||||
res.should.equal(ajv);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -1307,4 +1307,89 @@ describe('Ajv Options', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('logger', function() {
|
||||
|
||||
/**
|
||||
* The logger option tests are based on the meta scenario which writes into the logger.warn
|
||||
*/
|
||||
|
||||
var origConsoleWarn = console.warn;
|
||||
var consoleCalled;
|
||||
|
||||
beforeEach(function() {
|
||||
consoleCalled = false;
|
||||
console.warn = function() {
|
||||
consoleCalled = true;
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
console.warn = origConsoleWarn;
|
||||
});
|
||||
|
||||
it('no custom logger is given - global console should be used', function() {
|
||||
var ajv = new Ajv({
|
||||
meta: false
|
||||
});
|
||||
|
||||
ajv.compile({
|
||||
type: 'number',
|
||||
minimum: 1
|
||||
});
|
||||
|
||||
should.equal(consoleCalled, true);
|
||||
});
|
||||
|
||||
it('custom logger is an object - logs should only report to it', function() {
|
||||
var loggerCalled = false;
|
||||
|
||||
var logger = {
|
||||
warn: log,
|
||||
log: log,
|
||||
error: log
|
||||
};
|
||||
|
||||
var ajv = new Ajv({
|
||||
meta: false,
|
||||
logger: logger
|
||||
});
|
||||
|
||||
ajv.compile({
|
||||
type: 'number',
|
||||
minimum: 1
|
||||
});
|
||||
|
||||
should.equal(loggerCalled, true);
|
||||
should.equal(consoleCalled, false);
|
||||
|
||||
function log() {
|
||||
loggerCalled = true;
|
||||
}
|
||||
});
|
||||
|
||||
it('logger option is false - no logs should be reported', function() {
|
||||
var ajv = new Ajv({
|
||||
meta: false,
|
||||
logger: false
|
||||
});
|
||||
|
||||
ajv.compile({
|
||||
type: 'number',
|
||||
minimum: 1
|
||||
});
|
||||
|
||||
should.equal(consoleCalled, false);
|
||||
});
|
||||
|
||||
it('logger option is an object without required methods - an error should be thrown', function() {
|
||||
(function(){
|
||||
new Ajv({
|
||||
meta: false,
|
||||
logger: {}
|
||||
});
|
||||
}).should.throw(Error, /logger must implement log, warn and error methods/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue