Skip to content

Commit 6e49042

Browse files
committed
feat: deepRequired keyword
1 parent 4c4b2ab commit 6e49042

File tree

4 files changed

+149
-2
lines changed

4 files changed

+149
-2
lines changed

keywords/deepRequired.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
3+
module.exports = function defFunc(ajv) {
4+
defFunc.definition = {
5+
type: 'object',
6+
inline: function (it, keyword, schema) {
7+
var expr = '';
8+
for (var i=0; i<schema.length; i++) {
9+
if (i) expr += ' && ';
10+
expr += '(' + getData(schema[i], it.dataLvl) + ' !== undefined)';
11+
}
12+
return expr;
13+
}
14+
};
15+
16+
ajv.addKeyword('deepRequired', defFunc.definition);
17+
return ajv;
18+
};
19+
20+
21+
function getData(jsonPointer, lvl) {
22+
var data = 'data' + (lvl || '');
23+
if (!jsonPointer) return data;
24+
25+
var expr = data;
26+
var segments = jsonPointer.split('/');
27+
for (var i=0; i<segments.length; i++) {
28+
var segment = segments[i];
29+
if (segment) {
30+
data += getProperty(unescapeJsonPointer(segment));
31+
expr += ' && ' + data;
32+
}
33+
}
34+
return expr;
35+
}
36+
37+
38+
var IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i;
39+
var INTEGER = /^[0-9]+$/;
40+
var SINGLE_QUOTE = /'|\\/g;
41+
function getProperty(key) {
42+
return INTEGER.test(key)
43+
? '[' + key + ']'
44+
: IDENTIFIER.test(key)
45+
? '.' + key
46+
: "['" + key.replace(SINGLE_QUOTE, '\\$&') + "']";
47+
}
48+
49+
50+
function unescapeJsonPointer(str) {
51+
return str.replace(/~1/g, '/').replace(/~0/g, '~');
52+
}

keywords/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ module.exports = {
77
regexp: require('./regexp'),
88
'typeof': require('./typeof'),
99
dynamicDefaults: require('./dynamicDefaults'),
10-
'if': require('./if')
10+
'if': require('./if'),
11+
deepRequired: require('./deepRequired')
1112
// formatMinimum: require('./formatMinimum'),
1213
// formatMaximum: require('./formatMaximum'),
1314
// patternRequired: require('./patternRequired'),

spec/schema-tests.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ var defineKeywords = require('..');
77
var ajvs = [
88
// defineKeywords(getAjv(),
99
// ['switch', 'patternRequired', 'formatMinimum', 'formatMaximum']),
10-
defineKeywords(getAjv(), ['if']),
10+
defineKeywords(getAjv(), ['if', 'deepRequired']),
1111
defineKeywords(getAjv()),
1212
defineKeywords(getAjv(true))
1313
];

spec/tests/deepRequired.json

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
[
2+
{
3+
"description": "deepRequired keyword validation",
4+
"schema": {
5+
"type": "object",
6+
"deepRequired": [ "/foo/bar", "/foo/baz", "/quux" ]
7+
},
8+
"tests": [
9+
{
10+
"description": "object with all required properties is valid",
11+
"data": {
12+
"foo": {
13+
"bar": 1,
14+
"baz": 2
15+
},
16+
"quux": 3
17+
},
18+
"valid": true
19+
},
20+
{
21+
"description": "object without any required properties is invalid",
22+
"data": {
23+
"foo": {}
24+
},
25+
"valid": false
26+
},
27+
{
28+
"description": "object with one required property is invalid",
29+
"data": {
30+
"quux": 3
31+
},
32+
"valid": false
33+
},
34+
{
35+
"description": "object with one required sub-property is invalid",
36+
"data": {
37+
"foo": {
38+
"bar": 1
39+
},
40+
"quux": 3
41+
},
42+
"valid": false
43+
}
44+
]
45+
},
46+
{
47+
"description": "deepRequired keyword with items",
48+
"schema": {
49+
"type": "object",
50+
"deepRequired": [ "/foo/1/bar", "/foo/2/baz", "/quux/3" ]
51+
},
52+
"tests": [
53+
{
54+
"description": "object with all required properties/items is valid",
55+
"data": {
56+
"foo": [
57+
{},
58+
{ "bar": 1 },
59+
{ "baz": 2 }
60+
],
61+
"quux": [ 0, 1, 2, 3 ]
62+
},
63+
"valid": true
64+
},
65+
{
66+
"description": "object without any required properties is invalid",
67+
"data": {
68+
"foo": [{}, {}, {}],
69+
"quux": [0, 1, 2]
70+
},
71+
"valid": false
72+
},
73+
{
74+
"description": "object with one required property is invalid",
75+
"data": {
76+
"quux": [ 0, 1, 2, 3 ]
77+
},
78+
"valid": false
79+
},
80+
{
81+
"description": "object with one required sub-property is invalid",
82+
"data": {
83+
"foo": [
84+
{},
85+
{ "bar": 1 },
86+
{}
87+
],
88+
"quux": [ 0, 1, 2, 3 ]
89+
},
90+
"valid": false
91+
}
92+
]
93+
}
94+
]

0 commit comments

Comments
 (0)