1- import { normalizeKeyword , getKeyword } from './util' ;
1+ import { normalizeKeyword , getKeyword , getSchemaType , actualType ,
2+ isEqualset , isSubset } from './util' ;
23import { FILLER } from './constants' ;
34
45
@@ -115,16 +116,48 @@ export function getBlankArray(schema, getRef) {
115116}
116117
117118
119+ export function getBlankAllOf ( schema , getRef ) {
120+ // currently, we support allOf only inside an object
121+ return getBlankObject ( schema , getRef ) ;
122+ }
123+
124+
125+ export function getBlankOneOf ( schema , getRef ) {
126+ // for blank data, we always return the first option
127+ let nextSchema = schema . oneOf [ 0 ] ;
128+
129+ let type = getSchemaType ( nextSchema ) ;
130+
131+ return getBlankData ( nextSchema , getRef ) ;
132+ }
133+
134+
135+ export function getBlankAnyOf ( schema , getRef ) {
136+ // for blank data, we always return the first option
137+ let nextSchema = schema . anyOf [ 0 ] ;
138+
139+ let type = getSchemaType ( nextSchema ) ;
140+
141+ return getBlankData ( nextSchema , getRef ) ;
142+ }
143+
144+
118145export function getBlankData ( schema , getRef ) {
119146 if ( schema . hasOwnProperty ( '$ref' ) )
120147 schema = getRef ( schema [ '$ref' ] ) ;
121148
122- let type = normalizeKeyword ( schema . type ) ;
149+ let type = getSchemaType ( schema ) ;
123150
124151 if ( type === 'array' )
125152 return getBlankArray ( schema , getRef ) ;
126153 else if ( type === 'object' )
127154 return getBlankObject ( schema , getRef ) ;
155+ else if ( type === 'allOf' )
156+ return getBlankAllOf ( schema , getRef ) ;
157+ else if ( type === 'oneOf' )
158+ return getBlankOneOf ( schema , getRef ) ;
159+ else if ( type === 'anyOf' )
160+ return getBlankAnyOf ( schema , getRef ) ;
128161 else if ( type === 'boolean' )
129162 return schema . default === false ? false : ( schema . default || null ) ;
130163 else if ( type === 'integer' || type === 'number' )
@@ -135,6 +168,9 @@ export function getBlankData(schema, getRef) {
135168
136169
137170function getSyncedArray ( data , schema , getRef ) {
171+ if ( actualType ( data ) !== 'array' )
172+ throw new Error ( "Schema expected an 'array' but the data type was '" + actualType ( data ) + "'" ) ;
173+
138174 let newData = JSON . parse ( JSON . stringify ( data ) ) ;
139175
140176 if ( schema . items . hasOwnProperty ( '$ref' ) ) {
@@ -177,14 +213,20 @@ function getSyncedArray(data, schema, getRef) {
177213
178214
179215function getSyncedObject ( data , schema , getRef ) {
216+ if ( actualType ( data ) !== 'object' )
217+ throw new Error ( "Schema expected an 'object' but the data type was '" + actualType ( data ) + "'" ) ;
218+
180219 let newData = JSON . parse ( JSON . stringify ( data ) ) ;
181220
182221 let schema_keys = getKeyword ( schema , 'keys' , 'properties' , { } ) ;
183222
184-
185223 if ( schema . hasOwnProperty ( 'allOf' ) ) {
186224 for ( let i = 0 ; i < schema . allOf . length ; i ++ ) {
187- schema_keys = { ...schema_keys , ...getBlankObject ( schema . allOf [ i ] ) } ;
225+ // ignore items in allOf which are not object
226+ if ( getSchemaType ( schema . allOf [ i ] ) !== 'object' )
227+ continue ;
228+
229+ schema_keys = { ...schema_keys , ...getKeyword ( schema . allOf [ i ] , 'properties' , 'keys' , { } ) } ;
188230 }
189231 }
190232
@@ -199,7 +241,7 @@ function getSyncedObject(data, schema, getRef) {
199241 if ( isRef )
200242 schemaValue = getRef ( schemaValue [ '$ref' ] ) ;
201243
202- let type = normalizeKeyword ( schemaValue . type ) ;
244+ let type = getSchemaType ( schemaValue , data . key ) ;
203245
204246 if ( ! data . hasOwnProperty ( key ) ) {
205247 if ( type === 'array' )
@@ -235,19 +277,200 @@ function getSyncedObject(data, schema, getRef) {
235277}
236278
237279
280+ export function getSyncedAllOf ( data , schema , getRef ) {
281+ // currently we only support allOf inside an object
282+ // so, we'll treat the curent schema and data to be an object
283+
284+ return getSyncedObject ( data , schema , getRef ) ;
285+ }
286+
287+
288+ export function getSyncedOneOf ( data , schema , getRef ) {
289+ let index = findMatchingSubschemaIndex ( data , schema , getRef , 'oneOf' ) ;
290+ let subschema = schema [ 'oneOf' ] [ index ] ;
291+
292+ let syncFunc = getSyncFunc ( getSchemaType ( subschema ) ) ;
293+
294+ if ( syncFunc )
295+ return syncFunc ( data , subschema , getRef ) ;
296+
297+ return data ;
298+ }
299+
300+
301+ export function getSyncedAnyOf ( data , schema , getRef ) {
302+ let index = findMatchingSubschemaIndex ( data , schema , getRef , 'anyOf' ) ;
303+ let subschema = schema [ 'anyOf' ] [ index ] ;
304+
305+ let syncFunc = getSyncFunc ( getSchemaType ( subschema ) ) ;
306+
307+ if ( syncFunc )
308+ return syncFunc ( data , subschema , getRef ) ;
309+
310+ return data ;
311+ }
312+
313+
238314export function getSyncedData ( data , schema , getRef ) {
239315 // adds those keys to data which are in schema but not in data
240-
241316 if ( schema . hasOwnProperty ( '$ref' ) )
242317 schema = getRef ( schema [ '$ref' ] ) ;
243318
244- let type = normalizeKeyword ( schema . type ) ;
245319
246- if ( type === 'array' ) {
247- return getSyncedArray ( data , schema , getRef ) ;
248- } else if ( type === 'object' ) {
249- return getSyncedObject ( data , schema , getRef ) ;
250- }
320+ let type = getSchemaType ( schema ) ;
321+
322+ let syncFunc = getSyncFunc ( type ) ;
323+
324+ if ( syncFunc )
325+ return syncFunc ( data , schema , getRef ) ;
251326
252327 return data ;
253328}
329+
330+
331+ function getSyncFunc ( type ) {
332+ if ( type === 'array' )
333+ return getSyncedArray ;
334+ else if ( type === 'object' )
335+ return getSyncedObject ;
336+ else if ( type === 'allOf' )
337+ return getSyncedAllOf ;
338+ else if ( type === 'oneOf' )
339+ return getSyncedOneOf ;
340+ else if ( type === 'anyOf' )
341+ return getSyncedAnyOf ;
342+
343+ return null ;
344+ }
345+
346+
347+ export function findMatchingSubschemaIndex ( data , schema , getRef , schemaName ) {
348+ let dataType = actualType ( data ) ;
349+ let subschemas = schema [ schemaName ] ;
350+
351+ let index = null ;
352+
353+ for ( let i = 0 ; i < subschemas . length ; i ++ ) {
354+ let subschema = subschemas [ i ] ;
355+
356+ if ( subschema . hasOwnProperty ( '$ref' ) )
357+ subschema = getRef ( subschema [ '$ref' ] ) ;
358+
359+ let subType = getSchemaType ( subschema ) ;
360+
361+ if ( dataType === 'object' ) {
362+ // check if all keys match
363+ if ( dataObjectMatchesSchema ( data , subschema ) ) {
364+ index = i ;
365+ break ;
366+ }
367+ } else if ( dataType === 'array' ) {
368+ // check if item types match
369+ if ( dataArrayMatchesSchema ( data , subschema ) ) {
370+ index = i ;
371+ break ;
372+ }
373+ } else if ( dataType === subType ) {
374+ index = i ;
375+ break ;
376+ }
377+ }
378+
379+ if ( index === null ) {
380+ // no exact match found
381+ // so we'll just return the first schema that matches the data type
382+ for ( let i = 0 ; i < subschemas . length ; i ++ ) {
383+ let subschema = subschemas [ i ] ;
384+
385+ if ( subschema . hasOwnProperty ( '$ref' ) )
386+ subschema = getRef ( subschema [ '$ref' ] ) ;
387+
388+ let subType = getSchemaType ( subschema ) ;
389+
390+ if ( dataType === subType ) {
391+ index = i ;
392+ break ;
393+ }
394+ }
395+ }
396+
397+ return index ;
398+ }
399+
400+ export function dataObjectMatchesSchema ( data , subschema ) {
401+ let dataType = actualType ( data ) ;
402+ let subType = getSchemaType ( subschema ) ;
403+
404+ if ( subType !== dataType )
405+ return false ;
406+
407+ let subSchemaKeys = getKeyword ( subschema , 'properties' , 'keys' , { } ) ;
408+
409+ // check if all keys in the schema are present in the data
410+ keyset1 = new Set ( Object . keys ( data ) ) ;
411+ keyset2 = new Set ( Object . keys ( subSchemaKeys ) ) ;
412+
413+ if ( subschema . hasOwnProperty ( 'additionalProperties' ) ) {
414+ // subSchemaKeys must be a subset of data
415+ if ( ! isSubset ( keyset2 , keyset1 ) )
416+ return false ;
417+ } else {
418+ // subSchemaKeys must be equal to data
419+ if ( ! isEqualset ( keyset2 , keyset1 ) )
420+ return false ;
421+ }
422+
423+ for ( let key in subSchemaKeys ) {
424+ if ( ! subSchemaKeys . hasOwnProperty ( key ) )
425+ continue ;
426+
427+ if ( ! data . hasOwnProperty ( key ) )
428+ return false ;
429+
430+
431+ let keyType = normalizeKeyword ( subSchemaKeys [ key ] . type ) ;
432+ let dataValueType = actualType ( data [ key ] ) ;
433+
434+ if ( keyType === 'number' && [ 'number' , 'integer' , 'null' ] . indexOf ( dataValueType ) === - 1 ) {
435+ return false ;
436+ } else if ( keyType === 'integer' && [ 'number' , 'integer' , 'null' ] . indexOf ( dataValueType ) === - 1 ) {
437+ return false ;
438+ } else if ( keyType === 'boolean' && [ 'boolean' , 'null' ] . indexOf ( dataValueType ) === - 1 ) {
439+ return false ;
440+ } else if ( keyType === 'string' && dataValueType !== 'string' ) {
441+ return false ;
442+ }
443+ }
444+
445+ // if here, all checks have passed
446+ return true ;
447+ }
448+
449+
450+ export function dataArrayMatchesSchema ( data , subschema ) {
451+ let dataType = actualType ( data ) ;
452+ let subType = getSchemaType ( subschema ) ;
453+
454+ if ( subType !== dataType )
455+ return false ;
456+
457+ let itemsType = subschema . items . type ; // Temporary. Nested subschemas inside array.items won't work.
458+
459+ // check each item in data conforms to array items.type
460+ for ( let i = 0 ; i < data . length ; i ++ ) {
461+ dataValueType = actualType ( data [ i ] ) ;
462+
463+ if ( itemsType === 'number' && [ 'number' , 'integer' , 'null' ] . indexOf ( dataValueType ) === - 1 ) {
464+ return false ;
465+ } else if ( itemsType === 'integer' && [ 'number' , 'integer' , 'null' ] . indexOf ( dataValueType ) === - 1 ) {
466+ return false ;
467+ } else if ( itemsType === 'boolean' && [ 'boolean' , 'null' ] . indexOf ( dataValueType ) === - 1 ) {
468+ return false ;
469+ } else if ( itemsType === 'string' && dataValueType !== 'string' ) {
470+ return false ;
471+ }
472+ }
473+
474+ // if here, all checks have passed
475+ return true ;
476+ }
0 commit comments