Skip to content

Commit

Permalink
feat(samples): add support for type keyword defined as list of types (#…
Browse files Browse the repository at this point in the history
…8883)

This change is specific to JSON Schema 2020-12
and OpenAPI 3.1.0.

Refs #8577
  • Loading branch information
char0n committed Jun 6, 2023
1 parent 28b1233 commit 3587696
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 77 deletions.
144 changes: 73 additions & 71 deletions src/core/plugins/json-schema-2020-12/samples-extensions/fn.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,34 @@ const primitives = {
integer_int64: () => 2 ** 53 - 1,
boolean: (schema) =>
typeof schema.default === "boolean" ? schema.default : true,
null: () => null,
}
/* eslint-enable camelcase */

const primitive = (schema) => {
schema = objectify(schema)
const { type, format } = schema
const { type: typeList, format } = schema
const type = Array.isArray(typeList) ? typeList.at(0) : typeList

const fn = primitives[`${type}_${format}`] || primitives[type]

return typeof fn === "function" ? fn(schema) : `Unknown Type: ${schema.type}`
}

const isURI = (uri) => {
try {
return new URL(uri) && true
} catch {
return false
}
}

/**
* Do a couple of quick sanity tests to ensure the value
* looks like a $$ref that swagger-client generates.
*/
const sanitizeRef = (value) =>
deeplyStripKey(
value,
"$$ref",
(val) => typeof val === "string" && val.indexOf("#") > -1
)
deeplyStripKey(value, "$$ref", (val) => typeof val === "string" && isURI(val))

const objectContracts = ["maxProperties", "minProperties"]
const arrayContracts = ["minItems", "maxItems"]
Expand Down Expand Up @@ -117,7 +124,7 @@ const liftSampleHelper = (oldSchema, target, config = {}) => {
}
let props = objectify(oldSchema.properties)
for (let propName in props) {
if (!Object.prototype.hasOwnProperty.call(props, propName)) {
if (!Object.hasOwn(props, propName)) {
continue
}
if (props[propName] && props[propName].deprecated) {
Expand Down Expand Up @@ -193,7 +200,7 @@ export const sampleFromSchemaGeneric = (
}
let props = objectify(schemaToAdd.properties)
for (let propName in props) {
if (!Object.prototype.hasOwnProperty.call(props, propName)) {
if (!Object.hasOwn(props, propName)) {
continue
}
if (props[propName] && props[propName].deprecated) {
Expand Down Expand Up @@ -256,10 +263,9 @@ export const sampleFromSchemaGeneric = (
res[displayName] = []
}

const schemaHasAny = (keys) =>
keys.some((key) => Object.prototype.hasOwnProperty.call(schema, key))
const schemaHasAny = (keys) => keys.some((key) => Object.hasOwn(schema, key))
// try recover missing type
if (schema && !type) {
if (schema && typeof type !== "string" && !Array.isArray(type)) {
if (properties || additionalProperties || schemaHasAny(objectContracts)) {
type = "object"
} else if (items || schemaHasAny(arrayContracts)) {
Expand Down Expand Up @@ -419,11 +425,11 @@ export const sampleFromSchemaGeneric = (
return
}
if (
Object.prototype.hasOwnProperty.call(schema, "discriminator") &&
Object.hasOwn(schema, "discriminator") &&
schema.discriminator &&
Object.prototype.hasOwnProperty.call(schema.discriminator, "mapping") &&
Object.hasOwn(schema.discriminator, "mapping") &&
schema.discriminator.mapping &&
Object.prototype.hasOwnProperty.call(schema, "$$ref") &&
Object.hasOwn(schema, "$$ref") &&
schema.$$ref &&
schema.discriminator.propertyName === propName
) {
Expand Down Expand Up @@ -459,11 +465,11 @@ export const sampleFromSchemaGeneric = (
// if json just return
if (!respectXML) {
// spacial case yaml parser can not know about
if (typeof sample === "number" && type === "string") {
if (typeof sample === "number" && type?.includes("string")) {
return `${sample}`
}
// return if sample does not need any parsing
if (typeof sample !== "string" || type === "string") {
if (typeof sample !== "string" || type?.includes("string")) {
return sample
}
// check if sample is parsable or just a plain string
Expand All @@ -481,7 +487,7 @@ export const sampleFromSchemaGeneric = (
}

// generate xml sample recursively for array case
if (type === "array") {
if (type?.includes("array")) {
if (!Array.isArray(sample)) {
if (typeof sample === "string") {
return sample
Expand Down Expand Up @@ -509,13 +515,13 @@ export const sampleFromSchemaGeneric = (
}

// generate xml sample recursively for object case
if (type === "object") {
if (type?.includes("object")) {
// case literal example
if (typeof sample === "string") {
return sample
}
for (let propName in sample) {
if (!Object.prototype.hasOwnProperty.call(sample, propName)) {
if (!Object.hasOwn(sample, propName)) {
continue
}
if (
Expand Down Expand Up @@ -557,10 +563,56 @@ export const sampleFromSchemaGeneric = (
}

// use schema to generate sample
if (type?.includes("array")) {
if (!items) {
return []
}

if (type === "object") {
let sampleArray
if (respectXML) {
items.xml = items.xml || schema?.xml || {}
items.xml.name = items.xml.name || xml.name
}

if (Array.isArray(items.anyOf)) {
sampleArray = items.anyOf.map((i) =>
sampleFromSchemaGeneric(
liftSampleHelper(items, i, config),
config,
undefined,
respectXML
)
)
} else if (Array.isArray(items.oneOf)) {
sampleArray = items.oneOf.map((i) =>
sampleFromSchemaGeneric(
liftSampleHelper(items, i, config),
config,
undefined,
respectXML
)
)
} else if (!respectXML || (respectXML && xml.wrapped)) {
sampleArray = [
sampleFromSchemaGeneric(items, config, undefined, respectXML),
]
} else {
return sampleFromSchemaGeneric(items, config, undefined, respectXML)
}
sampleArray = handleMinMaxItems(sampleArray)
if (respectXML && xml.wrapped) {
res[displayName] = sampleArray
if (!isEmpty(_attr)) {
res[displayName].push({ _attr: _attr })
}
return res
}
return sampleArray
}

if (type?.includes("object")) {
for (let propName in props) {
if (!Object.prototype.hasOwnProperty.call(props, propName)) {
if (!Object.hasOwn(props, propName)) {
continue
}
if (props[propName] && props[propName].deprecated) {
Expand Down Expand Up @@ -630,53 +682,6 @@ export const sampleFromSchemaGeneric = (
return res
}

if (type === "array") {
if (!items) {
return
}

let sampleArray
if (respectXML) {
items.xml = items.xml || schema?.xml || {}
items.xml.name = items.xml.name || xml.name
}

if (Array.isArray(items.anyOf)) {
sampleArray = items.anyOf.map((i) =>
sampleFromSchemaGeneric(
liftSampleHelper(items, i, config),
config,
undefined,
respectXML
)
)
} else if (Array.isArray(items.oneOf)) {
sampleArray = items.oneOf.map((i) =>
sampleFromSchemaGeneric(
liftSampleHelper(items, i, config),
config,
undefined,
respectXML
)
)
} else if (!respectXML || (respectXML && xml.wrapped)) {
sampleArray = [
sampleFromSchemaGeneric(items, config, undefined, respectXML),
]
} else {
return sampleFromSchemaGeneric(items, config, undefined, respectXML)
}
sampleArray = handleMinMaxItems(sampleArray)
if (respectXML && xml.wrapped) {
res[displayName] = sampleArray
if (!isEmpty(_attr)) {
res[displayName].push({ _attr: _attr })
}
return res
}
return sampleArray
}

let value
if (schema && Array.isArray(schema.enum)) {
//display enum first value
Expand Down Expand Up @@ -714,9 +719,6 @@ export const sampleFromSchemaGeneric = (
} else {
return
}
if (type === "file") {
return
}

if (respectXML) {
res[displayName] = !isEmpty(_attr) ? [{ _attr: _attr }, value] : value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,34 @@ describe("sampleFromSchema", () => {
2 ** 53 - 1
)
expect(sample({ type: "boolean" })).toStrictEqual(true)
expect(sample({ type: "null" })).toStrictEqual(null)
})

it("should handle type keyword defined as list of types", function () {
const definition = fromJS({
type: ["object", "string"],
})
const expected = {}

expect(sampleFromSchema(definition)).toEqual(expected)
})

it("should prioritize array when array and object defined as list of types", function () {
const definition = fromJS({
type: ["object", "array"],
})
const expected = []

expect(sampleFromSchema(definition)).toEqual(expected)
})

it("should handle primitive types defined as list of types", function () {
const definition = fromJS({
type: ["string", "number"],
})
const expected = "string"

expect(sampleFromSchema(definition)).toEqual(expected)
})

it("handles Immutable.js objects for nested schemas", function () {
Expand Down Expand Up @@ -353,9 +381,9 @@ describe("sampleFromSchema", () => {
value: {
message: "Hello, World!",
},
$$ref: "#/components/examples/WelcomeExample",
$$ref: "https://example.com/#/components/examples/WelcomeExample",
},
$$ref: "#/components/schemas/Welcome",
$$ref: "https://example.com/#/components/schemas/Welcome",
}

const expected = {
Expand All @@ -382,10 +410,10 @@ describe("sampleFromSchema", () => {
value: {
message: "Hello, World!",
},
$$ref: "#/components/examples/WelcomeExample",
$$ref: "https://example.com/#/components/examples/WelcomeExample",
},
},
$$ref: "#/components/schemas/Welcome",
$$ref: "https://example.com/#/components/schemas/Welcome",
}

const expected = {
Expand Down Expand Up @@ -414,10 +442,10 @@ describe("sampleFromSchema", () => {
value: {
message: "Hello, World!",
},
$$ref: "#/components/examples/WelcomeExample",
$$ref: "https://example.com/#/components/examples/WelcomeExample",
},
},
$$ref: "#/components/schemas/Welcome",
$$ref: "https://example.com/#/components/schemas/Welcome",
}

const expected = {
Expand Down

0 comments on commit 3587696

Please sign in to comment.