Skip to content

Commit 8772a87

Browse files
committed
Add support for oneOf/anyOf/allOf
1 parent c316bb8 commit 8772a87

File tree

4 files changed

+666
-38
lines changed

4 files changed

+666
-38
lines changed

src/data.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,28 @@ import {FILLER} from './constants';
55
export function getBlankObject(schema, getRef) {
66
let keys = {};
77

8-
let schema_keys = schema.keys || schema.properties;
8+
let schema_keys = getKeyword(schema, 'keys', 'properties', {});
99

1010
for (let key in schema_keys) {
1111
let value = schema_keys[key];
12-
12+
1313
let isRef = value.hasOwnProperty('$ref');
1414

1515
if (isRef)
1616
value = getRef(value['$ref']);
1717

1818
let type = normalizeKeyword(value.type);
1919

20+
if (!type) {
21+
// check for oneOf/anyOf
22+
if (value.hasOwnProperty('oneOf'))
23+
value = value.oneOf[0];
24+
else if (value.hasOwnProperty('anyOf'))
25+
value = value.anyOf[0];
26+
27+
type = normalizeKeyword(value.type);
28+
}
29+
2030
if (type === 'array')
2131
keys[key] = isRef ? [] : getBlankArray(value, getRef);
2232
else if (type === 'object')
@@ -25,10 +35,22 @@ export function getBlankObject(schema, getRef) {
2535
keys[key] = value.default === false ? false : (value.default || null);
2636
else if (type === 'integer' || type === 'number')
2737
keys[key] = value.default === 0 ? 0 : (value.default || null);
28-
else // string etc.
38+
else
2939
keys[key] = value.default || '';
3040
}
3141

42+
if (schema.hasOwnProperty('oneOf'))
43+
keys = {...keys, ...getBlankObject(schema.oneOf[0])};
44+
45+
if (schema.hasOwnProperty('anyOf'))
46+
keys = {...keys, ...getBlankObject(schema.anyOf[0])};
47+
48+
if (schema.hasOwnProperty('allOf')) {
49+
for (let i = 0; i < schema.allOf.length; i++) {
50+
keys = {...keys, ...getBlankObject(schema.allOf[i])};
51+
}
52+
}
53+
3254
return keys;
3355
}
3456

@@ -55,6 +77,15 @@ export function getBlankArray(schema, getRef) {
5577

5678
let type = normalizeKeyword(schema.items.type);
5779

80+
if (!type) {
81+
if (schema.items.hasOwnProperty['oneOf'])
82+
type = schema.items.oneOf[0];
83+
else if (schema.items.hasOwnProperty['anyOf'])
84+
type = schema.items.anyOf[0];
85+
else if (schema.items.hasOwnProperty['allOf'])
86+
type = schema.items.allOf[0];
87+
}
88+
5889
if (type === 'array') {
5990
while (items.length < minItems)
6091
items.push(getBlankArray(schema.items, getRef));
@@ -103,7 +134,6 @@ export function getBlankData(schema, getRef) {
103134
}
104135

105136

106-
107137
function getSyncedArray(data, schema, getRef) {
108138
let newData = JSON.parse(JSON.stringify(data));
109139

@@ -149,7 +179,14 @@ function getSyncedArray(data, schema, getRef) {
149179
function getSyncedObject(data, schema, getRef) {
150180
let newData = JSON.parse(JSON.stringify(data));
151181

152-
let schema_keys = schema.keys || schema.properties;
182+
let schema_keys = getKeyword(schema, 'keys', 'properties', {});
183+
184+
185+
if (schema.hasOwnProperty('allOf')) {
186+
for (let i = 0; i < schema.allOf.length; i++) {
187+
schema_keys = {...schema_keys, ...getBlankObject(schema.allOf[i])};
188+
}
189+
}
153190

154191
let keys = [...Object.keys(schema_keys)];
155192

src/schemaValidation.js

Lines changed: 169 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,76 @@ export function validateSchema(schema) {
3434

3535

3636
export function validateObject(schema) {
37-
if (!schema.hasOwnProperty('keys') && !schema.hasOwnProperty('properties'))
37+
if (
38+
!schema.hasOwnProperty('keys') &&
39+
!schema.hasOwnProperty('properties') &&
40+
!schema.hasOwnProperty('oneOf') &&
41+
!schema.hasOwnProperty('anyOf') &&
42+
!schema.hasOwnProperty('allOf')
43+
)
3844
return {
3945
isValid: false,
40-
msg: "Schema of type '" + schema.type + "' must have a key called 'properties' or 'keys'"
46+
msg: "Schema of type '" + schema.type + "' must have at least one of these keys: " +
47+
"['properties' or 'keys' or 'oneOf' or 'anyOf' or 'allOf']"
4148
};
4249

50+
let validation;
51+
4352
let keys = schema.properties || schema.keys;
53+
if (keys) {
54+
validation = validateKeys(keys);
55+
if (!validation.isValid)
56+
return validation;
57+
}
58+
59+
if (schema.hasOwnProperty('additionalProperties')) {
60+
if (!(schema.additionalProperties instanceof Object) && typeof schema.additionalProperties !== 'boolean')
61+
return {
62+
isValid: false,
63+
msg: "'additionalProperties' must be either a JavaScript boolean or a JavaScript object"
64+
};
65+
66+
if (schema.additionalProperties instanceof Object) {
67+
if (schema.additionalProperties.hasOwnProperty('$ref')) {
68+
validation = validateRef(schema.additionalProperties);
69+
if (!validation.isValid)
70+
return validation;
71+
} else {
72+
let type = normalizeKeyword(schema.additionalProperties.type);
4473

74+
if (type === 'object')
75+
return validateObject(schema.additionalProperties);
76+
else if (type === 'array')
77+
return validateSchema(schema.additionalProperties);
78+
/* :TODO: else validate allowed types */
79+
}
80+
}
81+
82+
}
83+
84+
if (schema.hasOwnProperty('oneOf')) {
85+
validation = validateOneOf(schema);
86+
if (!validation.isValid)
87+
return validation;
88+
}
89+
90+
if (schema.hasOwnProperty('anyOf')) {
91+
validation = validateAnyOf(schema);
92+
if (!validation.isValid)
93+
return validation;
94+
}
95+
96+
if (schema.hasOwnProperty('allOf')) {
97+
validation = validateAllOf(schema);
98+
if (!validation.isValid)
99+
return validation;
100+
}
101+
102+
return {isValid: true, msg: ""};
103+
}
104+
105+
106+
function validateKeys(keys) {
45107
if (!(keys instanceof Object))
46108
return {
47109
isValid: false,
@@ -71,6 +133,12 @@ export function validateObject(schema) {
71133
validation = validateArray(value);
72134
} else if (value.hasOwnProperty('$ref')) {
73135
validation = validateRef(value);
136+
} else if (value.hasOwnProperty('oneOf')) {
137+
validation = validateOneOf(value);
138+
} else if (value.hasOwnProperty('anyOf')) {
139+
validation = validateAnyOf(value);
140+
} else if (value.hasOwnProperty('allOf')) {
141+
validation = validateAllOf(value);
74142
} else {
75143
validation = {isValid: false, msg: "Key '" + key + "' must have a 'type' or a '$ref"};
76144
}
@@ -79,31 +147,6 @@ export function validateObject(schema) {
79147
return validation;
80148
}
81149

82-
if (schema.hasOwnProperty('additionalProperties')) {
83-
if (!(schema.additionalProperties instanceof Object) && typeof schema.additionalProperties !== 'boolean')
84-
return {
85-
isValid: false,
86-
msg: "'additionalProperties' must be either a JavaScript boolean or a JavaScript object"
87-
};
88-
89-
if (schema.additionalProperties instanceof Object) {
90-
if (schema.additionalProperties.hasOwnProperty('$ref')) {
91-
validation = validateRef(schema.additionalProperties);
92-
if (!validation.isValid)
93-
return validation;
94-
} else {
95-
let type = normalizeKeyword(schema.additionalProperties.type);
96-
97-
if (type === 'object')
98-
return validateObject(schema.additionalProperties);
99-
else if (type === 'array')
100-
return validateSchema(schema.additionalProperties);
101-
/* :TODO: else validate allowed types */
102-
}
103-
}
104-
105-
}
106-
107150
return {isValid: true, msg: ""};
108151
}
109152

@@ -132,7 +175,29 @@ export function validateArray(schema) {
132175
} else if (schema.items.hasOwnProperty('$ref')) {
133176
return validateRef(schema.items);
134177
} else {
135-
return {isValid: false, msg: "'items' key must have a 'type' or a '$ref'"};
178+
if (!schema.items.hasOwnProperty('oneOf') &&
179+
!schema.items.hasOwnProperty('anyOf') &&
180+
!schema.items.hasOwnProperty('allOf')
181+
)
182+
return {isValid: false, msg: "Array 'items' must have a 'type' or '$ref' or 'oneOf' or 'anyOf' or 'allOf'"};
183+
}
184+
185+
if (schema.hasOwnProperty('oneOf')) {
186+
validation = validateOneOf(schema.items);
187+
if (!validation.isValid)
188+
return validation;
189+
}
190+
191+
if (schema.hasOwnProperty('anyOf')) {
192+
validation = validateAnyOf(schema.items);
193+
if (!validation.isValid)
194+
return validation;
195+
}
196+
197+
if (schema.hasOwnProperty('allOf')) {
198+
validation = validateAllOf(schema.items);
199+
if (!validation.isValid)
200+
return validation;
136201
}
137202

138203
return {isValid: true, msg: ""};
@@ -150,13 +215,88 @@ export function validateRef(schema) {
150215
return {
151216
isValid: false,
152217
msg: "'$ref' value must begin with a hash (#) character"
153-
}
218+
};
154219

155220
if (schema['$ref'].lenght > 1 && !schema['$ref'].startsWith('#/'))
156221
return {
157222
isValid: false,
158223
msg: "Invalid '$ref' path"
224+
};
225+
226+
return {isValid: true, msg: ""};
227+
}
228+
229+
230+
export function validateOneOf(schema) {
231+
return validateSubschemas(schema, 'oneOf');
232+
}
233+
234+
235+
export function validateAnyOf(schema) {
236+
return validateSubschemas(schema, 'anyOf');
237+
}
238+
239+
240+
export function validateAllOf(schema) {
241+
return validateSubschemas(schema, 'allOf');
242+
}
243+
244+
245+
function validateSubschemas(schema, keyword) {
246+
/*
247+
Common validator for oneOf/anyOf/allOf
248+
249+
Params:
250+
schema: the schema containing the oneOf/anyOf/allOf subschema
251+
keyword: one of 'oneOf' or 'anyOf' or 'allOf'
252+
253+
Validation:
254+
1. Must be an array
255+
2. If directly inside an object, each subschema in array must have 'properties' or 'keys keyword
256+
*/
257+
let subschemas = schema[keyword];
258+
259+
if (!Array.isArray(subschemas))
260+
return {
261+
isValid: false,
262+
msg: "'" + keyword + "' property must be an array"
263+
};
264+
265+
for (let i = 0; i < subschemas.length; i++) {
266+
let subschema = subschemas[i];
267+
let subType = getSchemaType(subschema);
268+
269+
if (subType === 'object') {
270+
let validation = validateObject(subschema);
271+
if (!validation.isValid)
272+
return validation;
273+
} else if (subType === 'array') {
274+
let validation = validateArray(subschema);
275+
if (!validation.isValid)
276+
return validation;
159277
}
278+
}
160279

161280
return {isValid: true, msg: ""};
162281
}
282+
283+
284+
/* Utility functions */
285+
286+
function getSchemaType(schema) {
287+
/* Returns type of the given schema */
288+
let type = normalizeKeyword(schema.type);
289+
290+
291+
if (!type) {
292+
if (schema.hasOwnProperty('properties') ||
293+
schema.hasOwnProperty('keys')
294+
) {
295+
type = 'object';
296+
} else {
297+
type = 'string';
298+
}
299+
}
300+
301+
return type;
302+
}

0 commit comments

Comments
 (0)