feat(plugins): expose JSON Schema merging mechanism from samples plugins (#9766)

Refs #9765
This commit is contained in:
Oliwia Rogala
2024-03-29 12:56:20 +01:00
committed by GitHub
parent c0e3eb63d0
commit 6a493fb4f3
7 changed files with 148 additions and 28 deletions

View File

@@ -11,3 +11,4 @@ export {
export { default as encoderAPI } from "./api/encoderAPI" export { default as encoderAPI } from "./api/encoderAPI"
export { default as formatAPI } from "./api/formatAPI" export { default as formatAPI } from "./api/formatAPI"
export { default as mediaTypeAPI } from "./api/mediaTypeAPI" export { default as mediaTypeAPI } from "./api/mediaTypeAPI"
export { default as mergeJsonSchema } from "./core/merge"

View File

@@ -10,6 +10,7 @@ import {
encoderAPI, encoderAPI,
mediaTypeAPI, mediaTypeAPI,
formatAPI, formatAPI,
mergeJsonSchema,
} from "./fn/index" } from "./fn/index"
import makeGetJsonSampleSchema from "./fn/get-json-sample-schema" import makeGetJsonSampleSchema from "./fn/get-json-sample-schema"
import makeGetYamlSampleSchema from "./fn/get-yaml-sample-schema" import makeGetYamlSampleSchema from "./fn/get-yaml-sample-schema"
@@ -37,6 +38,7 @@ const JSONSchema202012SamplesPlugin = ({ getSystem }) => {
getYamlSampleSchema, getYamlSampleSchema,
getXmlSampleSchema, getXmlSampleSchema,
getSampleSchema, getSampleSchema,
mergeJsonSchema,
}, },
}, },
} }

View File

@@ -56,10 +56,12 @@ const numberContracts = [
] ]
const stringContracts = ["minLength", "maxLength"] const stringContracts = ["minLength", "maxLength"]
const liftSampleHelper = (oldSchema, target, config = {}) => { export const mergeJsonSchema = (target, source, config = {}) => {
const merged = { ...target }
const setIfNotDefinedInTarget = (key) => { const setIfNotDefinedInTarget = (key) => {
if(target[key] === undefined && oldSchema[key] !== undefined) { if(merged[key] === undefined && source[key] !== undefined) {
target[key] = oldSchema[key] merged[key] = source[key]
} }
} }
@@ -75,22 +77,22 @@ const liftSampleHelper = (oldSchema, target, config = {}) => {
...stringContracts, ...stringContracts,
].forEach(key => setIfNotDefinedInTarget(key)) ].forEach(key => setIfNotDefinedInTarget(key))
if(oldSchema.required !== undefined && Array.isArray(oldSchema.required)) { if(source.required !== undefined && Array.isArray(source.required)) {
if(target.required === undefined || !target.required.length) { if(merged.required === undefined || !merged.required.length) {
target.required = [] merged.required = []
} }
oldSchema.required.forEach(key => { source.required.forEach(key => {
if(target.required.includes(key)) { if(merged.required.includes(key)) {
return return
} }
target.required.push(key) merged.required.push(key)
}) })
} }
if(oldSchema.properties) { if(source.properties) {
if(!target.properties) { if(!merged.properties) {
target.properties = {} merged.properties = {}
} }
let props = objectify(oldSchema.properties) let props = objectify(source.properties)
for (let propName in props) { for (let propName in props) {
if (!Object.prototype.hasOwnProperty.call(props, propName)) { if (!Object.prototype.hasOwnProperty.call(props, propName)) {
continue continue
@@ -104,26 +106,26 @@ const liftSampleHelper = (oldSchema, target, config = {}) => {
if ( props[propName] && props[propName].writeOnly && !config.includeWriteOnly ) { if ( props[propName] && props[propName].writeOnly && !config.includeWriteOnly ) {
continue continue
} }
if(!target.properties[propName]) { if(!merged.properties[propName]) {
target.properties[propName] = props[propName] merged.properties[propName] = props[propName]
if(!oldSchema.required && Array.isArray(oldSchema.required) && oldSchema.required.indexOf(propName) !== -1) { if(!source.required && Array.isArray(source.required) && source.required.indexOf(propName) !== -1) {
if(!target.required) { if(!merged.required) {
target.required = [propName] merged.required = [propName]
} else { } else {
target.required.push(propName) merged.required.push(propName)
} }
} }
} }
} }
} }
if(oldSchema.items) { if(source.items) {
if(!target.items) { if(!merged.items) {
target.items = {} merged.items = {}
} }
target.items = liftSampleHelper(oldSchema.items, target.items, config) merged.items = mergeJsonSchema(merged.items, source.items, config)
} }
return target return merged
} }
export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = undefined, respectXML = false) => { export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = undefined, respectXML = false) => {
@@ -138,7 +140,7 @@ export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = und
? schema.oneOf[0] ? schema.oneOf[0]
: schema.anyOf[0] : schema.anyOf[0]
) )
liftSampleHelper(schemaToAdd, schema, config) schema = mergeJsonSchema(schema, schemaToAdd, config)
if(!schema.xml && schemaToAdd.xml) { if(!schema.xml && schemaToAdd.xml) {
schema.xml = schemaToAdd.xml schema.xml = schemaToAdd.xml
} }
@@ -537,9 +539,9 @@ export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = und
} }
if(Array.isArray(items.anyOf)) { if(Array.isArray(items.anyOf)) {
sampleArray = items.anyOf.map(i => sampleFromSchemaGeneric(liftSampleHelper(items, i, config), config, undefined, respectXML)) sampleArray = items.anyOf.map(i => sampleFromSchemaGeneric(mergeJsonSchema(i, items, config), config, undefined, respectXML))
} else if(Array.isArray(items.oneOf)) { } else if(Array.isArray(items.oneOf)) {
sampleArray = items.oneOf.map(i => sampleFromSchemaGeneric(liftSampleHelper(items, i, config), config, undefined, respectXML)) sampleArray = items.oneOf.map(i => sampleFromSchemaGeneric(mergeJsonSchema(i, items, config), config, undefined, respectXML))
} else if(!respectXML || respectXML && xml.wrapped) { } else if(!respectXML || respectXML && xml.wrapped) {
sampleArray = [sampleFromSchemaGeneric(items, config, undefined, respectXML)] sampleArray = [sampleFromSchemaGeneric(items, config, undefined, respectXML)]
} else { } else {

View File

@@ -8,6 +8,7 @@ import {
createXMLExample, createXMLExample,
memoizedCreateXMLExample, memoizedCreateXMLExample,
memoizedSampleFromSchema, memoizedSampleFromSchema,
mergeJsonSchema,
} from "./fn/index" } from "./fn/index"
import makeGetJsonSampleSchema from "./fn/get-json-sample-schema" import makeGetJsonSampleSchema from "./fn/get-json-sample-schema"
import makeGetYamlSampleSchema from "./fn/get-yaml-sample-schema" import makeGetYamlSampleSchema from "./fn/get-yaml-sample-schema"
@@ -33,6 +34,7 @@ const JSONSchema5SamplesPlugin = ({ getSystem }) => {
getYamlSampleSchema, getYamlSampleSchema,
getXmlSampleSchema, getXmlSampleSchema,
getSampleSchema, getSampleSchema,
mergeJsonSchema,
}, },
inferSchema, inferSchema,
sampleFromSchema, sampleFromSchema,
@@ -44,6 +46,7 @@ const JSONSchema5SamplesPlugin = ({ getSystem }) => {
getYamlSampleSchema, getYamlSampleSchema,
getXmlSampleSchema, getXmlSampleSchema,
getSampleSchema, getSampleSchema,
mergeJsonSchema,
}, },
} }
} }

View File

@@ -31,6 +31,7 @@ function afterLoad({ fn, getSystem }) {
getYamlSampleSchema: fn.jsonSchema202012.getYamlSampleSchema, getYamlSampleSchema: fn.jsonSchema202012.getYamlSampleSchema,
getXmlSampleSchema: fn.jsonSchema202012.getXmlSampleSchema, getXmlSampleSchema: fn.jsonSchema202012.getXmlSampleSchema,
getSampleSchema: fn.jsonSchema202012.getSampleSchema, getSampleSchema: fn.jsonSchema202012.getSampleSchema,
mergeJsonSchema: fn.jsonSchema202012.mergeJsonSchema,
}, },
getSystem() getSystem()
) )

View File

@@ -8,6 +8,7 @@ import {
sampleFromSchema, sampleFromSchema,
memoizedCreateXMLExample, memoizedCreateXMLExample,
memoizedSampleFromSchema, memoizedSampleFromSchema,
mergeJsonSchema,
} from "core/plugins/json-schema-2020-12-samples/fn" } from "core/plugins/json-schema-2020-12-samples/fn"
describe("sampleFromSchema", () => { describe("sampleFromSchema", () => {
@@ -2983,3 +2984,55 @@ describe("memoizedCreateXMLExample", () => {
).toEqual(updatedExpected) ).toEqual(updatedExpected)
}) })
}) })
describe("merge", function () {
it("should merge two schemas", function () {
const schema = {
properties: {
name: {
type: "string",
},
id: {
type: "integer",
},
},
example: {
name: "test",
id: 1,
},
required: ["name"],
}
const target = {
type: "object",
properties: {
username: {
type: "string",
},
},
required: ["username"],
}
const result = mergeJsonSchema(target, schema)
expect(result).toStrictEqual({
type: "object",
properties: {
username: {
type: "string",
},
name: {
type: "string",
},
id: {
type: "integer",
},
},
example: {
name: "test",
id: 1,
},
required: ["username", "name"],
})
})
})

View File

@@ -1,5 +1,11 @@
import { fromJS } from "immutable" import { fromJS } from "immutable"
import { createXMLExample, sampleFromSchema, memoizedCreateXMLExample, memoizedSampleFromSchema } from "core/plugins/json-schema-5-samples/fn/index" import {
createXMLExample,
sampleFromSchema,
memoizedCreateXMLExample,
memoizedSampleFromSchema,
mergeJsonSchema,
} from "core/plugins/json-schema-5-samples/fn/index"
describe("sampleFromSchema", () => { describe("sampleFromSchema", () => {
it("handles Immutable.js objects for nested schemas", function () { it("handles Immutable.js objects for nested schemas", function () {
@@ -2438,3 +2444,55 @@ describe("memoizedCreateXMLExample", () => {
expect(memoizedCreateXMLExample(definition, {}, updatedOverrideExample)).toEqual(updatedExpected) expect(memoizedCreateXMLExample(definition, {}, updatedOverrideExample)).toEqual(updatedExpected)
}) })
}) })
describe("mergeJsonSchema", function () {
it("should merge two schemas", function () {
const schema = {
properties: {
name: {
type: "string",
},
id: {
type: "integer",
},
},
example: {
name: "test",
id: 1,
},
required: ["name"],
}
const target = {
type: "object",
properties: {
username: {
type: "string",
},
},
required: ["username"],
}
const result = mergeJsonSchema(target, schema)
expect(result).toStrictEqual({
type: "object",
properties: {
username: {
type: "string",
},
name: {
type: "string",
},
id: {
type: "integer",
},
},
example: {
name: "test",
id: 1,
},
required: ["username", "name"],
})
})
})