@@ -34,14 +34,76 @@ export function validateSchema(schema) {
3434
3535
3636export 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