UNPKG

@stoplight/json-schema-merge-allof

Version:

Simplify your schema by combining allOf into the root schema, safely.

727 lines (686 loc) 15.8 kB
var chai = require('chai') var sinon = require('sinon') var _ = require('lodash') var expect = chai.expect var Ajv = require('ajv') var mergerModule = require('../../src') function merger(schema, options) { var schemaClone = JSON.parse(JSON.stringify(schema)) var result = mergerModule(schema, options) expect(schema).to.deep.eql(schemaClone) return result } var ajv = new Ajv() describe('properties', function() { describe('when property name has same as a reserved word', function() { it('does not treat it as a reserved word', function() { var stub = sinon.stub().returns({ properties: { properties: { type: 'string', minLength: 5 } } }) merger({ allOf: [{ properties: { properties: { type: 'string', minLength: 5 } } }, { properties: { properties: { type: 'string', minLength: 7 } } }] }, { resolvers: { properties: stub } }) sinon.assert.calledOnce(stub) }) }) describe('additionalProperties', function() { it('allows no extra properties if additionalProperties is false', function() { var result = merger({ allOf: [{ additionalProperties: true }, { additionalProperties: false }] }) expect(result).to.eql({ additionalProperties: false }) }) it('allows only intersecting properties', function() { var result = merger({ allOf: [{ properties: { foo: true }, additionalProperties: true }, { properties: { bar: true }, additionalProperties: false }] }) expect(result).to.eql({ properties: { bar: true }, additionalProperties: false }) }) it('allows intersecting patternproperties', function() { var result = merger({ allOf: [{ properties: { foo: true, foo123: true }, additionalProperties: true }, { properties: { bar: true }, patternProperties: { '.+\\d+$': true }, additionalProperties: false }] }) expect(result).to.eql({ properties: { bar: true, foo123: true }, patternProperties: { '.+\\d+$': true }, additionalProperties: false }) }) it('disallows all except matching patternProperties if both false', function() { var result = merger({ allOf: [{ properties: { foo: true, foo123: true }, additionalProperties: false }, { properties: { bar: true }, patternProperties: { '.+\\d+$': true }, additionalProperties: false }] }) expect(result).to.eql({ properties: { foo123: true }, additionalProperties: false }) }) it('disallows all except matching patternProperties if both false', function() { var result = merger({ allOf: [{ properties: { foo: true, foo123: true }, patternProperties: { '.+\\d+$': { type: 'string' } }, additionalProperties: false }, { properties: { bar: true, bar123: true }, patternProperties: { '.+\\d+$': true }, additionalProperties: false }] }) expect(result).to.eql({ properties: { foo123: true, bar123: true }, patternProperties: { '.+\\d+$': { type: 'string' } }, additionalProperties: false }) }) it('disallows all except matching patternProperties if both false', function() { var schema = { allOf: [{ properties: { foo: true, foo123: true }, patternProperties: { '^bar': true }, additionalProperties: false }, { properties: { bar: true, bar123: true }, patternProperties: { '.+\\d+$': true }, additionalProperties: false }] } var origSchema = _.cloneDeep(schema) var result = merger(schema) expect(result).not.to.eql(origSchema) expect(result).to.eql({ properties: { bar: true, foo123: true, bar123: true }, additionalProperties: false }) ; [{ foo123: 'testfdsdfsfd' }, { bar123: 'testfdsdfsfd' }, { foo123: 'testfdsdfsfd' }, { bar: 'fdsaf' }, { abc123: 'fdsaf' }, { bar123: 'fdsaf' }, { barabc: 'fdsaf' }, { // additionalProp foo234: 'testffdsafdsads' }].forEach(function(val) { validateInputOutput(origSchema, result, val) }) }) it('disallows all except matching patternProperties if both true', function() { var schema = { allOf: [{ properties: { foo: true, foo123: true }, patternProperties: { '^bar': true } }, { properties: { bar: true, bar123: true }, patternProperties: { '.+\\d+$': true } }] } var origSchema = _.cloneDeep(schema) var result = merger(schema) expect(result).not.to.eql(origSchema) expect(result).to.eql({ properties: { foo: true, bar: true, foo123: true, bar123: true }, patternProperties: { '^bar': true, '.+\\d+$': true } }) ; [{ foo123: 'testfdsdfsfd' }, { bar123: 'testfdsdfsfd' }, { foo123: 'testfdsdfsfd' }, { foo: 'fdsaf' }, { bar: 'fdsaf' }, { abc123: 'fdsaf' }, { bar123: 'fdsaf' }, { barabc: 'fdsaf' }, { foo234: 'testffdsafdsads' }].forEach(function(val) { validateInputOutput(origSchema, result, val) }) }) it('disallows all except matching patternProperties if one false', function() { var schema = { allOf: [{ properties: { foo: true, foo123: true } }, { properties: { bar: true, bar123: true }, patternProperties: { '.+\\d+$': true }, additionalProperties: false }] } var origSchema = _.cloneDeep(schema) var result = merger(schema) expect(result).not.to.eql(origSchema) expect(result).to.eql({ properties: { bar: true, foo123: true, bar123: true }, patternProperties: { '.+\\d+$': true }, additionalProperties: false }) ; [{ foo123: 'testfdsdfsfd' }, { bar123: 'testfdsdfsfd' }, { foo123: 'testfdsdfsfd' }, { foo: 'fdsaf' }, { bar: 'fdsaf' }, { abc123: 'fdsaf' }, { bar123: 'fdsaf' }, { barabc: 'fdsaf' }, { foo234: 'testffdsafdsads' }].forEach(function(val) { validateInputOutput(origSchema, result, val) }) }) it('disallows all if no patternProperties and if both false', function() { var result = merger({ allOf: [{ properties: { foo: true, foo123: true }, additionalProperties: false }, { properties: { bar: true }, additionalProperties: false }] }) expect(result).to.eql({ additionalProperties: false }) }) it('applies additionalProperties to other schemas properties if they have any', function() { var result = merger({ properties: { common: true, root: true }, additionalProperties: false, allOf: [{ properties: { common: { type: 'string' }, allof1: true }, additionalProperties: { type: [ 'string', 'null' ], maxLength: 10 } }, { properties: { common: { minLength: 1 }, allof2: true }, additionalProperties: { type: [ 'string', 'integer', 'null' ], maxLength: 8 } }, { properties: { common: { minLength: 6 }, allof3: true } }] }) expect(result).to.eql({ properties: { common: { type: 'string', minLength: 6 }, root: { type: [ 'string', 'null' ], maxLength: 8 } }, additionalProperties: false }) }) it('considers patternProperties before merging additionalProperties to other schemas properties if they have any', function() { var result = merger({ properties: { common: true, root: true }, patternProperties: { '.+\\d{2,}$': { minLength: 7 } }, additionalProperties: false, allOf: [{ properties: { common: { type: 'string' }, allof1: true }, additionalProperties: { type: [ 'string', 'null', 'integer' ], maxLength: 10 } }, { properties: { common: { minLength: 1 }, allof2: true, allowed123: { type: 'string' } }, patternProperties: { '.+\\d{2,}$': { minLength: 9 } }, additionalProperties: { type: [ 'string', 'integer', 'null' ], maxLength: 8 } }, { properties: { common: { minLength: 6 }, allof3: true, allowed456: { type: 'integer' } } }] }) expect(result).to.eql({ properties: { common: { type: 'string', minLength: 6 }, root: { type: [ 'string', 'null', 'integer' ], maxLength: 8 }, allowed123: { type: 'string', maxLength: 10 }, allowed456: { type: 'integer', maxLength: 10 } }, patternProperties: { '.+\\d{2,}$': { minLength: 9 } }, additionalProperties: false }) }) it('combines additionalProperties when schemas', function() { var result = merger({ additionalProperties: true, allOf: [{ additionalProperties: { type: [ 'string', 'null' ], maxLength: 10 } }, { additionalProperties: { type: [ 'string', 'integer', 'null' ], maxLength: 8 } }] }) expect(result).to.eql({ additionalProperties: { type: [ 'string', 'null' ], maxLength: 8 } }) }) }) describe('patternProperties', function() { it('merges simliar schemas', function() { var result = merger({ patternProperties: { '^\\$.+': { type: [ 'string', 'null', 'integer' ], allOf: [{ minimum: 5 }] } }, allOf: [{ patternProperties: { '^\\$.+': { type: [ 'string', 'null' ], allOf: [{ minimum: 7 }] }, '.*': { type: 'null' } } }] }) expect(result).to.eql({ patternProperties: { '^\\$.+': { type: [ 'string', 'null' ], minimum: 7 }, '.*': { type: 'null' } } }) }) }) describe('when patternProperties present', function() { it('merges patternproperties', function() { var result = merger({ allOf: [{ patternProperties: { '.*': { type: 'string', minLength: 5 } } }, { patternProperties: { '.*': { type: 'string', minLength: 7 } } }] }) expect(result).to.eql({ patternProperties: { '.*': { type: 'string', minLength: 7 } } }) }) it('merges with properties if matching property name', function() { var schema = { allOf: [{ properties: { 'name': { type: 'string', minLength: 1 } }, patternProperties: { '_long$': { type: 'string', minLength: 7 } } }, { properties: { 'foo_long': { type: 'string', minLength: 9 } }, patternProperties: { '^name.*': { type: 'string', minLength: 8 } } }] } var origSchema = _.cloneDeep(schema) var result = merger(schema) expect(result).not.to.eql(origSchema) expect(result).to.eql({ properties: { 'foo_long': { type: 'string', minLength: 9 }, 'name': { type: 'string', minLength: 1 } }, patternProperties: { '_long$': { type: 'string', minLength: 7 }, '^name.*': { type: 'string', minLength: 8 } } }); [{ name: 'test' }, { name: 'fdsaffsda', name_long: 'testfdsdfsfd' }, { name: 'fdsafdsafas', foo_long: 'testfdsdfsfd' }, { name: 'dfsafdsa', name_long: 'testfdsdfsfd' }, { name: 'test', name2: 'testffdsafdsads' }].forEach(function(val) { validateInputOutput(schema, result, val) }) }) }) }) function validateInputOutput(schema, transformedSchema, obj) { var validOriginal = ajv.validate(schema, obj) var validNew = ajv.validate(transformedSchema, obj) expect(validOriginal).to.eql(validNew) }