diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/merge.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/merge.js new file mode 100644 index 00000000000..b0367a982fa --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/merge.js @@ -0,0 +1,74 @@ +/** + * @prettier + */ +import { isBooleanJSONSchema, isJSONSchema } from "./predicates" + +const merge = (target, source, config = {}) => { + if (isBooleanJSONSchema(target) && target === true) return true + if (isBooleanJSONSchema(target) && target === false) return false + if (isBooleanJSONSchema(source) && source === true) return true + if (isBooleanJSONSchema(source) && source === false) return false + + if (!isJSONSchema(target)) return source + if (!isJSONSchema(source)) return target + + /** + * Merging properties from the source object into the target object + * only if they do not already exist in the target object. + */ + const merged = { ...source, ...target } + + // merging required keyword + if (Array.isArray(source.required) && Array.isArray(target.required)) { + merged.required = [...new Set([...target.required, ...source.required])] + } + + // merging properties keyword + if (source.properties && target.properties) { + const allPropertyNames = new Set([ + ...Object.keys(source.properties), + ...Object.keys(target.properties), + ]) + + merged.properties = {} + for (const name of allPropertyNames) { + const sourceProperty = source.properties[name] || {} + const targetProperty = target.properties[name] || {} + + if ( + (sourceProperty.readOnly && !config.includeReadOnly) || + (sourceProperty.writeOnly && !config.includeWriteOnly) + ) { + merged.required = (merged.required || []).filter((p) => p !== name) + } else { + merged.properties[name] = merge(targetProperty, sourceProperty, config) + } + } + } + + // merging items keyword + if (isJSONSchema(source.items) && isJSONSchema(target.items)) { + merged.items = merge(target.items, source.items, config) + } + + // merging contains keyword + if (isJSONSchema(source.contains) && isJSONSchema(target.contains)) { + merged.contains = merge(target.contains, source.contains, config) + } + + // merging contentSchema keyword + if ( + isJSONSchema(source.contentSchema) && + isJSONSchema(target.contentSchema) + ) { + merged.contentSchema = merge( + target.contentSchema, + source.contentSchema, + config + ) + } + + return merged +} + +export default merge diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js index daeed6ea7c8..b528069d99b 100644 --- a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js +++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js @@ -11,113 +11,8 @@ import { getType } from "./core/type" import { typeCast } from "./core/utils" import { hasExample, extractExample } from "./core/example" import { pick as randomPick } from "./core/random" - -const objectConstraints = ["maxProperties", "minProperties", "required"] -const arrayConstraints = [ - "minItems", - "maxItems", - "uniqueItems", - "minContains", - "maxContains", -] -const numberConstraints = [ - "minimum", - "maximum", - "exclusiveMinimum", - "exclusiveMaximum", - "multipleOf", -] -const stringConstraints = [ - "minLength", - "maxLength", - "pattern", - "contentEncoding", - "contentMediaType", -] - -const liftSampleHelper = (oldSchema, target, config = {}) => { - const setIfNotDefinedInTarget = (key) => { - if (target[key] === undefined && oldSchema[key] !== undefined) { - target[key] = oldSchema[key] - } - } - - ;[ - "examples", - "example", - "default", - "enum", - "xml", - "type", - "const", - ...objectConstraints, - ...arrayConstraints, - ...numberConstraints, - ...stringConstraints, - ].forEach((key) => setIfNotDefinedInTarget(key)) - - if (oldSchema.required !== undefined && Array.isArray(oldSchema.required)) { - if (target.required === undefined || !target.required.length) { - target.required = [] - } - oldSchema.required.forEach((key) => { - if (target.required.includes(key)) { - return - } - target.required.push(key) - }) - } - if (oldSchema.properties) { - if (!target.properties) { - target.properties = {} - } - let props = objectify(oldSchema.properties) - for (let propName in props) { - if (!Object.hasOwn(props, propName)) { - continue - } - if (props[propName] && props[propName].deprecated) { - continue - } - if ( - props[propName] && - props[propName].readOnly && - !config.includeReadOnly - ) { - continue - } - if ( - props[propName] && - props[propName].writeOnly && - !config.includeWriteOnly - ) { - continue - } - if (!target.properties[propName]) { - target.properties[propName] = props[propName] - if ( - !oldSchema.required && - Array.isArray(oldSchema.required) && - oldSchema.required.indexOf(propName) !== -1 - ) { - if (!target.required) { - target.required = [propName] - } else { - target.required.push(propName) - } - } - } - } - } - if (oldSchema.items) { - if (!target.items) { - target.items = {} - } - target.items = liftSampleHelper(oldSchema.items, target.items, config) - } - - return target -} +import merge from "./core/merge" +import { isBooleanJSONSchema, isJSONSchemaObject } from "./core/predicates" export const sampleFromSchemaGeneric = ( schema, @@ -138,7 +33,7 @@ export const sampleFromSchemaGeneric = ( const schemaToAdd = typeCast( hasOneOf ? randomPick(schema.oneOf) : randomPick(schema.anyOf) ) - liftSampleHelper(schemaToAdd, schema, config) + schema = merge(schema, schemaToAdd, config) if (!schema.xml && schemaToAdd.xml) { schema.xml = schemaToAdd.xml } @@ -489,9 +384,9 @@ export const sampleFromSchemaGeneric = ( if (Array.isArray(contains.anyOf)) { sampleArray.push( - ...contains.anyOf.map((i) => + ...contains.anyOf.map((anyOfSchema) => sampleFromSchemaGeneric( - liftSampleHelper(contains, i, config), + merge(anyOfSchema, contains, config), config, undefined, respectXML @@ -500,9 +395,9 @@ export const sampleFromSchemaGeneric = ( ) } else if (Array.isArray(contains.oneOf)) { sampleArray.push( - ...contains.oneOf.map((i) => + ...contains.oneOf.map((oneOfSchema) => sampleFromSchemaGeneric( - liftSampleHelper(contains, i, config), + merge(oneOfSchema, contains, config), config, undefined, respectXML @@ -528,7 +423,7 @@ export const sampleFromSchemaGeneric = ( sampleArray.push( ...items.anyOf.map((i) => sampleFromSchemaGeneric( - liftSampleHelper(items, i, config), + merge(i, items, config), config, undefined, respectXML @@ -539,7 +434,7 @@ export const sampleFromSchemaGeneric = ( sampleArray.push( ...items.oneOf.map((i) => sampleFromSchemaGeneric( - liftSampleHelper(items, i, config), + merge(i, items, config), config, undefined, respectXML @@ -591,14 +486,14 @@ export const sampleFromSchemaGeneric = ( return res } - if (additionalProperties === true) { + if (isBooleanJSONSchema(additionalProperties)) { if (respectXML) { res[displayName].push({ additionalProp: "Anything can be here" }) } else { res.additionalProp1 = {} } propertyAddedCounter++ - } else if (additionalProperties) { + } else if (isJSONSchemaObject(additionalProperties)) { const additionalProps = typeCast(additionalProperties) const additionalPropSample = sampleFromSchemaGeneric( additionalProps, diff --git a/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js b/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js index df51e6df3df..181173efe0f 100644 --- a/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js +++ b/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js @@ -1154,7 +1154,7 @@ describe("sampleFromSchema", () => { expect(sampleFromSchema(definition)).toEqual(expected) }) - it("should lift items with anyOf", () => { + it("should merge items with anyOf", () => { const definition = { type: "array", anyOf: [ @@ -1172,7 +1172,7 @@ describe("sampleFromSchema", () => { expect(sampleFromSchema(definition)).toEqual(expected) }) - it("should lift items with oneOf", () => { + it("should merge items with oneOf", () => { const definition = { type: "array", oneOf: [