-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2243aa8
commit 1cc2aa5
Showing
8 changed files
with
411 additions
and
260 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
language: node_js | ||
after_success: npm run coverage | ||
node_js: | ||
- 10 | ||
- 12 | ||
- 14 | ||
- node | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
const flatten = require('lodash/flatten') | ||
const flattenDeep = require('lodash/flattenDeep') | ||
const isPlainObject = require('lodash/isPlainObject') | ||
const uniq = require('lodash/uniq') | ||
const uniqWith = require('lodash/uniqWith') | ||
const without = require('lodash/without') | ||
|
||
function deleteUndefinedProps(returnObject) { | ||
// cleanup empty | ||
for (const prop in returnObject) { | ||
if (has(returnObject, prop) && isEmptySchema(returnObject[prop])) { | ||
delete returnObject[prop] | ||
} | ||
} | ||
return returnObject | ||
} | ||
|
||
const allUniqueKeys = (arr) => uniq(flattenDeep(arr.map(keys))) | ||
const getValues = (schemas, key) => schemas.map(schema => schema && schema[key]) | ||
const has = (obj, propName) => Object.prototype.hasOwnProperty.call(obj, propName) | ||
const keys = obj => { | ||
if (isPlainObject(obj) || Array.isArray(obj)) { | ||
return Object.keys(obj) | ||
} else { | ||
return [] | ||
} | ||
} | ||
|
||
const notUndefined = (val) => val !== undefined | ||
const isSchema = (val) => isPlainObject(val) || val === true || val === false | ||
const isEmptySchema = (obj) => (!keys(obj).length) && obj !== false && obj !== true | ||
const withoutArr = (arr, ...rest) => without.apply(null, [arr].concat(flatten(rest))) | ||
|
||
module.exports = { | ||
allUniqueKeys, | ||
deleteUndefinedProps, | ||
getValues, | ||
has, | ||
isEmptySchema, | ||
isSchema, | ||
keys, | ||
notUndefined, | ||
uniqWith, | ||
withoutArr | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
const { has } = require('../common') | ||
|
||
const conditonalRelated = ['if', 'then', 'else'] | ||
|
||
module.exports = { | ||
// test with same if-then-else resolver | ||
keywords: ['if', 'then', 'else'], | ||
resolver(schemas, paths, mergers, options) { | ||
const allWithConditional = schemas.filter(schema => | ||
conditonalRelated.some(keyword => has(schema, keyword))) | ||
|
||
// merge sub schemas completely | ||
// if,then,else must not be merged to the base schema, but if they contain allOf themselves, that should be merged | ||
function merge(schema) { | ||
const obj = {} | ||
if (has(schema, 'if')) obj.if = mergers.if([schema.if]) | ||
if (has(schema, 'then')) obj.then = mergers.then([schema.then]) | ||
if (has(schema, 'else')) obj.else = mergers.else([schema.else]) | ||
return obj | ||
} | ||
|
||
// first schema with any of the 3 keywords is used as base | ||
const first = merge(allWithConditional.shift()) | ||
return allWithConditional.reduce((all, schema) => { | ||
all.allOf = (all.allOf || []).concat(merge(schema)) | ||
return all | ||
}, first) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
|
||
const compare = require('json-schema-compare') | ||
const forEach = require('lodash/forEach') | ||
const { | ||
allUniqueKeys, | ||
deleteUndefinedProps, | ||
has, | ||
isSchema, | ||
notUndefined, | ||
uniqWith | ||
} = require('../common') | ||
|
||
function removeFalseSchemasFromArray(target) { | ||
forEach(target, function(schema, index) { | ||
if (schema === false) { | ||
target.splice(index, 1) | ||
} | ||
}) | ||
} | ||
|
||
function getItemSchemas(subSchemas, key) { | ||
return subSchemas.map(function(sub) { | ||
if (!sub) { | ||
return undefined | ||
} | ||
|
||
if (Array.isArray(sub.items)) { | ||
const schemaAtPos = sub.items[key] | ||
if (isSchema(schemaAtPos)) { | ||
return schemaAtPos | ||
} else if (has(sub, 'additionalItems')) { | ||
return sub.additionalItems | ||
} | ||
} else { | ||
return sub.items | ||
} | ||
|
||
return undefined | ||
}) | ||
} | ||
|
||
function getAdditionalSchemas(subSchemas) { | ||
return subSchemas.map(function(sub) { | ||
if (!sub) { | ||
return undefined | ||
} | ||
if (Array.isArray(sub.items)) { | ||
return sub.additionalItems | ||
} | ||
return sub.items | ||
}) | ||
} | ||
|
||
// Provide source when array | ||
function mergeItems(group, mergeSchemas, items) { | ||
const allKeys = allUniqueKeys(items) | ||
return allKeys.reduce(function(all, key) { | ||
const schemas = getItemSchemas(group, key) | ||
const compacted = uniqWith(schemas.filter(notUndefined), compare) | ||
all[key] = mergeSchemas(compacted, key) | ||
return all | ||
}, []) | ||
} | ||
|
||
module.exports = { | ||
keywords: ['items', 'additionalItems'], | ||
resolver(values, parents, mergers) { | ||
// const createSubMerger = groupKey => (schemas, key) => mergeSchemas(schemas, parents.concat(groupKey, key)) | ||
const items = values.map(s => s.items) | ||
const itemsCompacted = items.filter(notUndefined) | ||
const returnObject = {} | ||
|
||
// if all items keyword values are schemas, we can merge them as simple schemas | ||
// if not we need to merge them as mixed | ||
if (itemsCompacted.every(isSchema)) { | ||
returnObject.items = mergers.items(items) | ||
} else { | ||
returnObject.items = mergeItems(values, mergers.items, items) | ||
} | ||
|
||
let schemasAtLastPos | ||
if (itemsCompacted.every(Array.isArray)) { | ||
schemasAtLastPos = values.map(s => s.additionalItems) | ||
} else if (itemsCompacted.some(Array.isArray)) { | ||
schemasAtLastPos = getAdditionalSchemas(values) | ||
} | ||
|
||
if (schemasAtLastPos) { | ||
returnObject.additionalItems = mergers.additionalItems(schemasAtLastPos) | ||
} | ||
|
||
if (returnObject.additionalItems === false && Array.isArray(returnObject.items)) { | ||
removeFalseSchemasFromArray(returnObject.items) | ||
} | ||
|
||
return deleteUndefinedProps(returnObject) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
|
||
const compare = require('json-schema-compare') | ||
const forEach = require('lodash/forEach') | ||
const { | ||
allUniqueKeys, | ||
deleteUndefinedProps, | ||
getValues, | ||
keys, | ||
notUndefined, | ||
uniqWith, | ||
withoutArr | ||
} = require('../common') | ||
|
||
function removeFalseSchemas(target) { | ||
forEach(target, function(schema, prop) { | ||
if (schema === false) { | ||
delete target[prop] | ||
} | ||
}) | ||
} | ||
|
||
function mergeSchemaGroup(group, mergeSchemas) { | ||
const allKeys = allUniqueKeys(group) | ||
return allKeys.reduce(function(all, key) { | ||
const schemas = getValues(group, key) | ||
const compacted = uniqWith(schemas.filter(notUndefined), compare) | ||
all[key] = mergeSchemas(compacted, key) | ||
return all | ||
}, {}) | ||
} | ||
|
||
module.exports = { | ||
keywords: ['properties', 'patternProperties', 'additionalProperties'], | ||
resolver(values, parents, mergers, options) { | ||
// first get rid of all non permitted properties | ||
if (!options.ignoreAdditionalProperties) { | ||
values.forEach(function(subSchema) { | ||
const otherSubSchemas = values.filter(s => s !== subSchema) | ||
const ownKeys = keys(subSchema.properties) | ||
const ownPatternKeys = keys(subSchema.patternProperties) | ||
const ownPatterns = ownPatternKeys.map(k => new RegExp(k)) | ||
otherSubSchemas.forEach(function(other) { | ||
const allOtherKeys = keys(other.properties) | ||
const keysMatchingPattern = allOtherKeys.filter(k => ownPatterns.some(pk => pk.test(k))) | ||
const additionalKeys = withoutArr(allOtherKeys, ownKeys, keysMatchingPattern) | ||
additionalKeys.forEach(function(key) { | ||
other.properties[key] = mergers.properties([ | ||
other.properties[key], subSchema.additionalProperties | ||
], key) | ||
}) | ||
}) | ||
}) | ||
|
||
// remove disallowed patternProperties | ||
values.forEach(function(subSchema) { | ||
const otherSubSchemas = values.filter(s => s !== subSchema) | ||
const ownPatternKeys = keys(subSchema.patternProperties) | ||
if (subSchema.additionalProperties === false) { | ||
otherSubSchemas.forEach(function(other) { | ||
const allOtherPatterns = keys(other.patternProperties) | ||
const additionalPatternKeys = withoutArr(allOtherPatterns, ownPatternKeys) | ||
additionalPatternKeys.forEach(key => delete other.patternProperties[key]) | ||
}) | ||
} | ||
}) | ||
} | ||
|
||
const returnObject = { | ||
additionalProperties: mergers.additionalProperties(values.map(s => s.additionalProperties)), | ||
patternProperties: mergeSchemaGroup(values.map(s => s.patternProperties), mergers.patternProperties), | ||
properties: mergeSchemaGroup(values.map(s => s.properties), mergers.properties) | ||
} | ||
|
||
if (returnObject.additionalProperties === false) { | ||
removeFalseSchemas(returnObject.properties) | ||
} | ||
|
||
return deleteUndefinedProps(returnObject) | ||
} | ||
} |
Oops, something went wrong.