patternRequired keyword (v5 proposal), closes #123

master
Evgeny Poberezkin 2016-02-27 22:36:11 +00:00
parent dfe773735f
commit ec0f1c1b22
7 changed files with 176 additions and 17 deletions

View File

@ -31,6 +31,7 @@ The keywords and their values define what rules the data should satisfy to be va
- [additionalProperties](#additionalproperties)
- [dependencies](#dependencies)
- [patternGroups](#patterngroups-v5-proposal) (v5)
- [patternRequired](#patternrequired-v5-proposal) (v5)
- [Keywords for all types](#keywords-for-all-types)
- [enum](#enum)
- [constant](#constant-v5-proposal) (v5)
@ -270,7 +271,7 @@ __Examples__
2. _schema_:
```
```json
{
"items": [
{ "type": "integer" },
@ -308,7 +309,7 @@ __Examples__
2. _schema_:
```
```json
{
"items": { "type": "integer" },
"additionalItems": { "type": "string" }
@ -321,7 +322,7 @@ __Examples__
3. _schema_:
```
```json
{
"items": [
{ "type": "integer" },
@ -337,7 +338,7 @@ __Examples__
4. _schema_:
```
```json
{
"items": [
{ "type": "integer" },
@ -476,7 +477,7 @@ If the value is a schema for the data object to be valid the values in all "addi
__Examples__
1. _schema_:
```
```json
{
"properties": {
"foo": { "type": "number" }
@ -493,7 +494,7 @@ __Examples__
_invalid_: `{"a": 3}`, `{"foo": 1, "baz": 3}`
2. _schema_:
```
```json
{
"properties": {
"foo": { "type": "number" }
@ -523,7 +524,7 @@ For schema dependency, if the data object contains a property that is a key in t
__Examples__
1. _schema (property dependency)_:
```
```json
{
"dependencies": {
"foo": ["bar", "baz"]
@ -537,7 +538,7 @@ __Examples__
2. _schema (schema dependency)_:
```
```json
{
"dependencies": {
"foo": {
@ -588,6 +589,28 @@ _invalid_: `{}`, `{ "foo": "bar" }`, `{ "1": "2" }`
### patternRequired (v5 proposal)
The value of this keyword should be an array of strings, each string being a regular expression. For data object to be valid each regular expression in this array should match at least one property name in the data object.
If the array contains multiple regular expressions, more than one expression can match the same property name.
__Examples__
1. _schema_: `{ "patternRequired": [ "f.*o" ] }`
_valid_: `{ "foo": 1 }`, `{ "-fo-": 1 }`, { "foo": 1, "bar": 2 }`, any non-object
_invalid_: `{}`, `{ "bar": 2 }`, `{ "Foo": 1 }`,
2. _schema_: `{ "patternRequired": [ "f.*o", "b.*r" ] }`
_valid_: { "foo": 1, "bar": 2 }`, `{ "foobar": 3 }`, any non-object
_invalid_: `{}`, `{ "foo": 1 }`, `{ "bar": 2 }`
## Keywords for all types
### `enum`
@ -655,7 +678,7 @@ __Examples__
2. _schema_:
```
```json
{
"not": {
"items": {

View File

@ -53,7 +53,7 @@ It uses [doT templates](https://github.com/olado/doT) to generate super-fast val
- [assigning defaults](#assigning-defaults) to missing properties and items
- NEW: [coercing data](#coercing-data-types) to the types specified in `type` keywords
- [custom keywords](#defining-custom-keywords)
- keywords `switch`, `constant`, `contains`, `patternGroups`, `formatMaximum` / `formatMinimum` and `exclusiveFormatMaximum` / `exclusiveFormatMinimum` from [JSON-schema v5 proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) with [option v5](#options)
- keywords `switch`, `constant`, `contains`, `patternGroups`, `patternRequired`, `formatMaximum` / `formatMinimum` and `exclusiveFormatMaximum` / `exclusiveFormatMinimum` from [JSON-schema v5 proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) with [option v5](#options)
- [v5 meta-schema](https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#) for schemas using v5 keywords
- NEW: [v5 $data reference](#data-reference) to use values from the validated data as values for the schema keywords
- NEW: [asynchronous validation](#asynchronous-validation) of custom formats and keywords
@ -160,6 +160,7 @@ With option `v5: true` Ajv also supports all validation keywords and [$data refe
- [contains](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#contains-v5-proposal) - check that array contains a valid item
- [constant](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#constant-v5-proposal) - check that data is equal to some value
- [patternGroups](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#patterngroups-v5-proposal) - a more powerful alternative to patternProperties
- [patternRequired](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#patternrequired-v5-proposal) - like `required` but with patterns that some property should match.
- [formatMaximum, formatMinimum, exclusiveFormatMaximum, exclusiveFormatMinimum](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#formatmaximum--formatminimum-and-exclusiveformatmaximum--exclusiveformatminimum-v5-proposal) - setting limits for date, time, etc.
See [JSON-Schema validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md) for more details.
@ -766,7 +767,7 @@ Defaults:
##### Validation and reporting options
- _v5_: add keywords `switch`, `constant`, `contains`, `patternGroups`, `formatMaximum` / `formatMinimum` and `exclusiveFormatMaximum` / `exclusiveFormatMinimum` from [JSON-schema v5 proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals). With this option added schemas without `$schema` property are validated against [v5 meta-schema](https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#). `false` by default.
- _v5_: add keywords `switch`, `constant`, `contains`, `patternGroups`, `patternRequired`, `formatMaximum` / `formatMinimum` and `exclusiveFormatMaximum` / `exclusiveFormatMinimum` from [JSON-schema v5 proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals). With this option added schemas without `$schema` property are validated against [v5 meta-schema](https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#). `false` by default.
- _allErrors_: check all rules collecting all errors. Default is to return after the first error.
- _verbose_: include the reference to the part of the schema (`schema` and `parentSchema`) and validated data in errors (false by default).
- _jsonPointers_: set `dataPath` propery of errors using [JSON Pointers](https://tools.ietf.org/html/rfc6901) instead of JavaScript property access notation.
@ -874,6 +875,7 @@ Properties of `params` object in errors depend on the keyword that failed valida
- `multipleOf` - property `multipleOf` (the schema of the keyword)
- `pattern` - property `pattern` (the schema of the keyword)
- `required` - property `missingProperty` (required property that is missing).
- `patternRequired` (with v5 option) - property `missingPattern` (required pattern that did not match any property).
- `type` - property `type` (required type(s), a string, can be a comma-separated list)
- `uniqueItems` - properties `i` and `j` (indices of duplicate items).
- `$ref` - property `ref` with the referenced schema URI.

View File

@ -112,6 +112,7 @@
uniqueItems: "'should NOT have duplicate items (items ## ' + j + ' and ' + i + ' are identical)'",
custom: "'should pass \"{{=$rule.keyword}}\" keyword validation'",
patternGroups: "'should NOT have {{=$moreOrLess}} than {{=$limit}} properties matching pattern \"{{=it.util.escapeQuotes($pgProperty)}}\"'",
patternRequired: "'should have property matching pattern \\'{{=$missingPattern}}\\''",
switch: "'should pass \"switch\" keyword validation'",
constant: "'should be equal to constant'",
_formatLimit: "'should be {{=$opStr}} \"{{#def.concatSchemaEQ}}\"'",
@ -144,6 +145,7 @@
uniqueItems: "{{#def.schemaRefOrVal}}",
custom: "validate.schema{{=$schemaPath}}",
patternGroups: "validate.schema{{=$schemaPath}}",
patternRequired: "validate.schema{{=$schemaPath}}",
switch: "validate.schema{{=$schemaPath}}",
constant: "validate.schema{{=$schemaPath}}",
_formatLimit: "{{#def.schemaRefOrQS}}",
@ -175,6 +177,7 @@
uniqueItems: "{ i: i, j: j }",
custom: "{ keyword: '{{=$rule.keyword}}' }",
patternGroups: "{ reason: '{{=$reason}}', limit: {{=$limit}}, pattern: '{{=it.util.escapeQuotes($pgProperty)}}' }",
patternRequired: "{ missingPattern: '{{=$missingPattern}}' }",
switch: "{ caseIndex: {{=$caseIndex}} }",
constant: "{}",
_formatLimit: "{ limit: {{#def.schemaValueQS}} }",

View File

@ -0,0 +1,26 @@
{{# def.definitions }}
{{# def.errors }}
{{# def.setupKeyword }}
{{
var $key = 'key' + $lvl
, $matched = 'patternMatched' + $lvl
, $closingBraces = '';
}}
var {{=$valid}} = true;
{{~ $schema:$pProperty }}
var {{=$matched}} = false;
for (var {{=$key}} in {{=$data}}) {
{{=$matched}} = {{= it.usePattern($pProperty) }}.test({{=$key}});
if ({{=$matched}}) break;
}
{{ var $missingPattern = it.util.escapeQuotes($pProperty); }}
if (!{{=$matched}}) {
{{=$valid}} = false;
{{# def.addError:'patternRequired' }}
} {{# def.elseIfValid }}
{{~}}
{{= $closingBraces }}

View File

@ -9,23 +9,42 @@ module.exports = {
function enableV5(ajv) {
var inlineFunctions = {
'switch': require('./dotjs/switch'),
'constant': require('./dotjs/constant'),
'_formatLimit': require('./dotjs/_formatLimit'),
'patternRequired': require('./dotjs/patternRequired')
};
if (ajv._opts.meta !== false) {
var metaSchema = require('./refs/json-schema-v5.json');
ajv.addMetaSchema(metaSchema, META_SCHEMA_ID);
}
ajv.addKeyword('constant', { inline: require('./dotjs/constant'), statements: true, errors: 'full' });
_addKeyword('constant');
ajv.addKeyword('contains', { type: 'array', macro: containsMacro });
var formatLimit = require('./dotjs/_formatLimit');
ajv.addKeyword('formatMaximum', { type: 'string', inline: formatLimit, statements: true, errors: 'full' });
ajv.addKeyword('formatMinimum', { type: 'string', inline: formatLimit, statements: true, errors: 'full' });
_addKeyword('formatMaximum', 'string', inlineFunctions._formatLimit);
_addKeyword('formatMinimum', 'string', inlineFunctions._formatLimit);
ajv.addKeyword('exclusiveFormatMaximum');
ajv.addKeyword('exclusiveFormatMinimum');
ajv.addKeyword('patternGroups'); // implemented in properties.jst
ajv.addKeyword('switch', { inline: require('./dotjs/switch'), statements: true, errors: 'full' });
_addKeyword('patternRequired', 'object');
_addKeyword('switch');
function _addKeyword(keyword, types, inlineFunc) {
var definition = {
inline: inlineFunc || inlineFunctions[keyword],
statements: true,
errors: 'full'
};
if (types) definition.type = types;
ajv.addKeyword(keyword, definition);
}
}
function containsMacro(schema) {
return {
not: { items: { not: schema } }

View File

@ -1,6 +1,6 @@
{
"name": "ajv",
"version": "3.7.2",
"version": "3.8.0",
"description": "Another JSON Schema Validator",
"main": "lib/ajv.js",
"files": [

View File

@ -0,0 +1,86 @@
[
{
"description": "patternRequired requires that some property matching pattern is present",
"schema": {
"patternRequired": [ "f.*o" ]
},
"tests": [
{
"description": "property matching required pattern is valid",
"data": { "foo": 1 },
"valid": true
},
{
"description": "one of properties matching required pattern is valid",
"data": { "foo": 1, "bar": 2 },
"valid": true
},
{
"description": "non-present property matching required pattern is invalid",
"data": { "bar": 1 },
"valid": false
}
]
},
{
"description": "multiple patterns in patternRequired",
"schema": {
"patternRequired": [ "a+", "b+" ]
},
"tests": [
{
"description": "both patterns matched with one property is valid",
"data": { "ab": 2 },
"valid": true
},
{
"description": "both patterns matched with separate properties is valid",
"data": { "aa": 1, "bb": 2 },
"valid": true
},
{
"description": "both patterns matched with multiple properties is valid",
"data": { "a": 1, "aa": 2, "ab": 3, "b": 4, "bb": 5 },
"valid": true
},
{
"description": "one pattern not matched is invalid",
"data": { "aa": 1 },
"valid": false
},
{
"description": "another pattern not matched is invalid",
"data": { "bb": 2 },
"valid": false
},
{
"description": "both patterns not matched is invalid",
"data": { "c": 3 },
"valid": false
}
]
},
{
"description": "regexes in patternRequired are not anchored by default and are case sensitive",
"schema": {
"patternRequired": [ "X_[0-9]{2,}" ]
},
"tests": [
{
"description": "regexes are not anchored",
"data": { "aX_25b": 1 },
"valid": true
},
{
"description": "regexes are case sensitive",
"data": { "X_25": 2 },
"valid": true
},
{
"description": "regexes are case sensitive, 2",
"data": { "x_25": 3 },
"valid": false
}
]
}
]