Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

json-schema-merge-allof

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-schema-merge-allof - npm Package Compare versions

Comparing version 0.5.1 to 0.5.2

3

package.json
{
"name": "json-schema-merge-allof",
"version": "0.5.1",
"version": "0.5.2",
"description": "Simplify your schema by combining allOf into the root schema, safely.",

@@ -32,2 +32,3 @@ "main": "src/index.js",

"devDependencies": {
"ajv": "^5.2.3",
"chai": "^4.1.2",

@@ -34,0 +35,0 @@ "coveralls": "^3.0.0",

@@ -12,2 +12,3 @@ # json-schema-merge-allof [![Build Status](https://travis-ci.org/mokkabonna/json-schema-merge-allof.svg?branch=master)](https://travis-ci.org/mokkabonna/json-schema-merge-allof) [![Coverage Status](https://coveralls.io/repos/github/mokkabonna/json-schema-merge-allof/badge.svg?branch=master)](https://coveralls.io/github/mokkabonna/json-schema-merge-allof?branch=master)

- **Real** and **safe** merging of schemas combined with **allOf**
- Takes away all allOf found in the whole schema
- Lossless in terms of validation rules, merged schema does not validate more or less than original schema

@@ -18,5 +19,8 @@ - Results in a more readable root schema

- Validates in a way not possible by regular simple meta validators
- Correctly considers additionalProperties, patternProperties and properties as a part of an whole when merging schemas containing those
- Correctly considers items and additionalItems as a whole when merging schemas containing those
- Supports merging schemas with items as array and direct schema
- Supports all JSON schema core/validation keywords
- Option to override common impossibility like adding properties when using **additionalProperties: false**
- Pluggable keyword resolvers
- Option to override common impossibility like adding properties when using **additionalProperties: false**
- Supports all JSON schema core/validation keywords

@@ -110,3 +114,3 @@ ## How

You can set a default resolver that catches any unknown keyword. Let's say you want to use the same strategy as the ones for the meta keywords, to use the first value found. You van accomplish that like this:
You can set a default resolver that catches any unknown keyword. Let's say you want to use the same strategy as the ones for the meta keywords, to use the first value found. You can accomplish that like this:

@@ -113,0 +117,0 @@ ```js

@@ -18,2 +18,23 @@ var cloneDeep = require('lodash/cloneDeep')

var withoutArr = (arr, ...rest) => without.apply(null, [arr].concat(flatten(rest)))
var isPropertyRelated = (key) => contains(propertyRelated, key)
var isItemsRelated = (key) => contains(itemsRelated, key)
var contains = (arr, val) => arr.indexOf(val) !== -1
var isEmptySchema = (obj) => (!keys(obj).length) && obj !== false && obj !== true
var isSchema = (val) => isPlainObject(val) || val === true || val === false
var isFalse = (val) => val === false
var isTrue = (val) => val === true
var schemaResolver = (compacted, key, mergeSchemas, totalSchemas, parent) => mergeSchemas(compacted, compacted[0])
var stringArray = (values) => sortBy(uniq(flattenDeep(values)))
var notUndefined = (val) => val !== undefined
var allUniqueKeys = (arr) => uniq(flattenDeep(arr.map(keys)))
// resolvers
var first = compacted => compacted[0]
var required = compacted => stringArray(compacted)
var maximumValue = compacted => Math.max.apply(Math, compacted)
var minimumValue = compacted => Math.min.apply(Math, compacted)
var uniqueItems = compacted => compacted.some(isTrue)
var examples = compacted => uniqWith(flatten(compacted), isEqual)
function compareProp(key) {

@@ -64,2 +85,12 @@ return function(a, b) {

function tryMergeSchemaGroups(schemaGroups, mergeSchemas) {
return schemaGroups.map(function(schemas) {
try {
return mergeSchemas(schemas)
} catch (e) {
return undefined
}
}).filter(notUndefined)
}
function getAdditionalSchemas(subSchemas) {

@@ -83,15 +114,2 @@ return subSchemas.map(function(sub) {

function withoutArr(arr) {
var rest = flatten([].slice.call(arguments, 1))
return without.apply(null, [arr].concat(rest))
}
function isPropertyRelated(key) {
return contains(propertyRelated, key)
}
function isItemsRelated(key) {
return contains(itemsRelated, key)
}
function getAnyOfCombinations(arrOfArrays, combinations) {

@@ -111,6 +129,2 @@ combinations = combinations || []

function contains(arr, val) {
return arr.indexOf(val) !== -1
}
function mergeWithArray(base, newItems) {

@@ -125,18 +139,2 @@ if (Array.isArray(base)) {

function isEmptySchema(obj) {
return (!keys(obj).length) && obj !== false && obj !== true
}
function isSchema(val) {
return isPlainObject(val) || val === true || val === false
}
function isFalse(val) {
return val === false
}
function isTrue(val) {
return val === true
}
function throwIncompatible(values, key) {

@@ -152,14 +150,72 @@ var asJSON

function schemaResolver(compacted, key, mergeSchemas, totalSchemas, parent) {
return mergeSchemas(compacted, compacted[0])
function cleanupReturnValue(returnObject) {
// cleanup empty
for (var prop in returnObject) {
if (returnObject.hasOwnProperty(prop) && isEmptySchema(returnObject[prop])) {
delete returnObject[prop]
}
}
return returnObject
}
function stringArray(values) {
return sortBy(uniq(flattenDeep(values)))
function callGroupResolver(keys, resolverName, schemas, mergeSchemas, options) {
if (keys.length) {
var resolver = options.resolvers[resolverName]
if (!resolver) {
throw new Error('No resolver found for ' + resolverName)
}
var compacted = uniqWith(schemas.map(function(schema) {
return keys.reduce(function(all, key) {
if (schema[key] !== undefined) {
all[key] = schema[key]
}
return all
}, {})
}).filter(notUndefined), compare)
var result = resolver(compacted, resolverName, mergeSchemas, options)
if (!isPlainObject(result)) {
throwIncompatible(compacted, resolverName)
}
return cleanupReturnValue(result)
}
}
function notUndefined(val) {
return val !== undefined
// Provide source when array
function mergeSchemaGroup(group, mergeSchemas, source) {
var allKeys = allUniqueKeys(source || group)
var extractor = source ? getItemSchemas : getValues
return allKeys.reduce(function(all, key) {
var schemas = extractor(group, key)
var compacted = uniqWith(schemas.filter(notUndefined), compare)
all[key] = mergeSchemas(compacted)
return all
}, source ? [] : {})
}
function removeFalseSchemas(target) {
forEach(target, function(schema, prop) {
if (schema === false) {
delete target[prop]
}
})
}
function removeFalseSchemasFromArray(target) {
forEach(target, function(schema, index) {
if (schema === false) {
target.splice(index, 1)
}
})
}
function createRequiredMetaArray(arr) {
return {
required: arr
}
}
var propertyRelated = ['properties', 'patternProperties', 'additionalProperties']

@@ -179,7 +235,6 @@ var itemsRelated = ['items', 'additionalItems']

var defaultResolvers = {
type: function(compacted, key) {
type(compacted, key) {
if (compacted.some(Array.isArray)) {
var normalized = compacted.map(function(val) {
return Array.isArray(val)
? val : [val]
return Array.isArray(val) ? val : [val]
})

@@ -195,3 +250,3 @@ var common = intersection.apply(null, normalized)

},
properties: function(values, key, mergeSchemas, totalSchemas, options) {
properties(values, key, mergeSchemas, options) {
// first get rid of all non permitted properties

@@ -228,57 +283,16 @@ if (!options.ignoreAdditionalProperties) {

var properties = values.map(s => s.properties)
var allProperties = uniq(flattenDeep(properties.map(keys)))
var returnObject = {}
var returnObject = {
additionalProperties: mergeSchemas(values.map(s => s.additionalProperties)),
patternProperties: mergeSchemaGroup(values.map(s => s.patternProperties), mergeSchemas),
properties: mergeSchemaGroup(values.map(s => s.properties), mergeSchemas)
}
returnObject.additionalProperties = mergeSchemas(values.map(s => s.additionalProperties))
var allPatternProps = values.map(function(schema) {
return schema.patternProperties
})
var allPatternKeys = uniq(flattenDeep(allPatternProps.map(keys)))
returnObject.patternProperties = allPatternKeys.reduce(function(all, patternKey) {
var subSchemas = getValues(allPatternProps, patternKey)
var compacted = uniqWith(subSchemas.filter(notUndefined), compare)
all[patternKey] = mergeSchemas(compacted)
return all
}, {})
returnObject.properties = allProperties.reduce(function(all, propKey) {
var propSchemas = getValues(properties, propKey)
var innerCompacted = uniqWith(propSchemas.filter(notUndefined), compare)
var foundBase = totalSchemas.find(function(schema) {
return schema === all[propKey] && schema !== undefined && isPlainObject(schema)
})
if (foundBase) {
all[propKey] = foundBase
return all
}
totalSchemas.splice.apply(totalSchemas, [0, 0].concat(innerCompacted))
all[propKey] = mergeSchemas(innerCompacted, all[propKey] || {}, true)
return all
}, properties[0] || {})
if (returnObject.additionalProperties === false) {
forEach(returnObject.properties, function(schema, prop) {
if (schema === false) {
delete returnObject.properties[prop]
}
})
removeFalseSchemas(returnObject.properties)
}
// cleanup empty
for (var prop in returnObject) {
if (returnObject.hasOwnProperty(prop) && isEmptySchema(returnObject[prop])) {
delete returnObject[prop]
}
}
return returnObject
},
dependencies: function(compacted, key, mergeSchemas) {
var allChildren = uniq(flattenDeep(compacted.map(keys)))
dependencies(compacted, key, mergeSchemas) {
var allChildren = allUniqueKeys(compacted)

@@ -290,5 +304,12 @@ return allChildren.reduce(function(all, childKey) {

// to support dependencies
var allArray = innerCompacted.every(Array.isArray)
if (allArray) {
all[childKey] = stringArray(innerCompacted)
var innerArrays = innerCompacted.filter(Array.isArray)
if (innerArrays.length) {
if (innerArrays.length === innerCompacted.length) {
all[childKey] = stringArray(innerCompacted)
} else {
var innerSchemas = innerCompacted.filter(isSchema)
var arrayMetaScheams = innerArrays.map(createRequiredMetaArray)
all[childKey] = mergeSchemas(innerSchemas.concat(arrayMetaScheams))
}
return all

@@ -303,6 +324,5 @@ }

},
items: function(values, key, mergeSchemas) {
items(values, key, mergeSchemas) {
var items = values.map(s => s.items)
var itemsCompacted = items.filter(notUndefined)
var returnObject = {}

@@ -313,10 +333,3 @@

} else {
var allItems = uniq(flattenDeep(items.map(keys)))
returnObject.items = allItems.reduce(function(all, index) {
var itemSchemas = getItemSchemas(values, index)
var innerCompacted = uniqWith(itemSchemas.filter(notUndefined), compare)
all[index] = mergeSchemas(innerCompacted)
return all
}, [])
returnObject.items = mergeSchemaGroup(values, mergeSchemas, items)
}

@@ -335,33 +348,13 @@

if (returnObject.additionalItems === false) {
forEach(returnObject.items, function(schema, prop) {
if (schema === false) {
returnObject.items.splice(prop, 1)
}
})
if (returnObject.additionalItems === false && Array.isArray(returnObject.items)) {
removeFalseSchemasFromArray(returnObject.items)
}
// cleanup empty
for (var prop in returnObject) {
if (returnObject.hasOwnProperty(prop) && isEmptySchema(returnObject[prop])) {
delete returnObject[prop]
}
}
return returnObject
},
oneOf: function(compacted, key, mergeSchemas) {
oneOf(compacted, key, mergeSchemas) {
var combinations = getAnyOfCombinations(cloneDeep(compacted))
var result = combinations.map(function(combination) {
try {
return mergeSchemas(combination)
} catch (e) {
return undefined
}
}).filter(notUndefined)
var result = tryMergeSchemaGroups(combinations, mergeSchemas)
var unique = uniqWith(result, compare)
// TODO implement merging to main schema if only one left
if (unique.length) {

@@ -371,3 +364,3 @@ return unique

},
not: function(compacted) {
not(compacted) {
return {

@@ -377,15 +370,3 @@ anyOf: compacted

},
first: function(compacted) {
return compacted[0]
},
required: function(compacted) {
return stringArray(compacted)
},
minLength: function(compacted) {
return Math.max.apply(Math, compacted)
},
maxLength: function(compacted) {
return Math.min.apply(Math, compacted)
},
pattern: function(compacted, key, mergeSchemas, totalSchemas, reportUnresolved) {
pattern(compacted, key, mergeSchemas, totalSchemas, reportUnresolved) {
reportUnresolved(compacted.map(function(regexp) {

@@ -397,3 +378,3 @@ return {

},
multipleOf: function(compacted) {
multipleOf(compacted) {
var integers = compacted.slice(0)

@@ -407,11 +388,3 @@ var factor = 1

},
uniqueItems: function(compacted) {
return compacted.some(function(val) {
return val === true
})
},
examples: function(compacted) {
return uniqWith(flatten(compacted), isEqual)
},
enum: function(compacted, key) {
enum(compacted, key) {
var enums = intersectionWith.apply(null, compacted.concat(isEqual))

@@ -424,22 +397,28 @@ if (enums.length) {

defaultResolvers.$id = defaultResolvers.first
defaultResolvers.$schema = defaultResolvers.first
defaultResolvers.$ref = defaultResolvers.first // TODO correct? probably throw
defaultResolvers.title = defaultResolvers.first
defaultResolvers.description = defaultResolvers.first
defaultResolvers.default = defaultResolvers.first
defaultResolvers.minimum = defaultResolvers.minLength
defaultResolvers.exclusiveMinimum = defaultResolvers.minLength
defaultResolvers.minItems = defaultResolvers.minLength
defaultResolvers.minProperties = defaultResolvers.minLength
defaultResolvers.maximum = defaultResolvers.maxLength
defaultResolvers.exclusiveMaximum = defaultResolvers.maxLength
defaultResolvers.maxItems = defaultResolvers.maxLength
defaultResolvers.maxProperties = defaultResolvers.maxLength
defaultResolvers.contains = schemaResolver
defaultResolvers.$id = first
defaultResolvers.$ref = first
defaultResolvers.$schema = first
defaultResolvers.additionalItems = schemaResolver
defaultResolvers.additionalProperties = schemaResolver
defaultResolvers.anyOf = defaultResolvers.oneOf
defaultResolvers.additionalProperties = schemaResolver
defaultResolvers.contains = schemaResolver
defaultResolvers.default = first
defaultResolvers.definitions = defaultResolvers.dependencies
defaultResolvers.description = first
defaultResolvers.examples = examples
defaultResolvers.exclusiveMaximum = minimumValue
defaultResolvers.exclusiveMinimum = maximumValue
defaultResolvers.first = first
defaultResolvers.maximum = minimumValue
defaultResolvers.maxItems = minimumValue
defaultResolvers.maxLength = minimumValue
defaultResolvers.maxProperties = minimumValue
defaultResolvers.minimum = maximumValue
defaultResolvers.minItems = maximumValue
defaultResolvers.minLength = maximumValue
defaultResolvers.minProperties = maximumValue
defaultResolvers.propertyNames = schemaResolver
defaultResolvers.definitions = defaultResolvers.dependencies
defaultResolvers.required = required
defaultResolvers.title = first
defaultResolvers.uniqueItems = uniqueItems

@@ -474,3 +453,3 @@ function merger(rootSchema, options, totalSchemas) {

var allKeys = uniq(flattenDeep(schemas.map(keys)))
var allKeys = allUniqueKeys(schemas)

@@ -484,57 +463,9 @@ if (contains(allKeys, 'allOf')) {

var propertyKeys = allKeys.filter(isPropertyRelated)
Object.assign(merged, callGroupResolver(propertyKeys, 'properties', schemas, mergeSchemas, options))
pullAll(allKeys, propertyKeys)
if (propertyKeys.length) {
var propResolver = options.resolvers.properties
if (!propResolver) {
throw new Error('No resolver found for properties.')
}
var compacted = uniqWith(schemas.map(function(schema) {
return propertyKeys.reduce(function(all, key) {
if (schema[key] !== undefined) {
all[key] = schema[key]
}
return all
}, {})
}).filter(notUndefined), compare)
var result = propResolver(compacted, 'properties', mergeSchemas, totalSchemas, options)
if (!isPlainObject(result)) {
throwIncompatible(compacted, 'properties')
}
Object.assign(merged, result)
pullAll(allKeys, propertyKeys)
}
var itemKeys = allKeys.filter(isItemsRelated)
Object.assign(merged, callGroupResolver(itemKeys, 'items', schemas, mergeSchemas, options))
pullAll(allKeys, itemKeys)
if (itemKeys.length) {
var itemsResolver = options.resolvers.items
if (!itemsResolver) {
throw new Error('No resolver found for items.')
}
var itemsCompacted = uniqWith(schemas.map(function(schema) {
return itemKeys.reduce(function(all, key) {
if (schema[key] !== undefined) {
all[key] = schema[key]
}
return all
}, {})
}).filter(notUndefined), compare)
var itemsResult = itemsResolver(itemsCompacted, 'items', mergeSchemas, totalSchemas, options)
if (!isPlainObject(itemsResult)) {
throwIncompatible(itemsCompacted, 'items')
}
Object.assign(merged, itemsResult)
pullAll(allKeys, itemKeys)
}
allKeys.forEach(function(key) {

@@ -541,0 +472,0 @@ var values = getValues(schemas, key)

var chai = require('chai')
var merger = require('../../src')
var mergerModule = require('../../src')
var Ajv = require('ajv')
var $RefParser = require('json-schema-ref-parser')
var expect = chai.expect
var ajv = new Ajv()
function merger(schema, options) {
var result = mergerModule(schema, options)
try {
if (!ajv.validateSchema(result)) {
throw new Error('Schema returned by resolver isn\'t valid.')
}
return result
} catch (e) {
if (!/stack/i.test(e.message)) {
throw e
}
}
}
describe('module', function() {

@@ -1381,2 +1397,31 @@ it('combines simple usecase', function() {

})
it('merges mixed mode dependency', function() {
var result = merger({
dependencies: {
'bar': {
type: [
'string', 'null', 'integer'
],
required: ['abc']
}
},
allOf: [{
dependencies: {
'bar': ['prop4']
}
}]
})
expect(result).to.eql({
dependencies: {
'bar': {
type: [
'string', 'null', 'integer'
],
required: ['abc', 'prop4']
}
}
})
})
})

@@ -1383,0 +1428,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc