refactor(samples): do the general code cleanup (#8911)
This change is specific to JSON Schema 2020-12 and OpenAPI 3.1.0. Refs #8577
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
import { ALL_TYPES } from "./constants"
|
import { ALL_TYPES } from "./constants"
|
||||||
import { isJSONSchemaObject } from "./predicates"
|
import { isJSONSchemaObject } from "./predicates"
|
||||||
import { pick as randomPick } from "./random"
|
import { pick as randomPick } from "./random"
|
||||||
|
import { hasExample, extractExample } from "./example"
|
||||||
|
|
||||||
const inferringKeywords = {
|
const inferringKeywords = {
|
||||||
array: [
|
array: [
|
||||||
@@ -50,6 +51,15 @@ inferringKeywords.number = inferringKeywords.integer
|
|||||||
|
|
||||||
const fallbackType = "string"
|
const fallbackType = "string"
|
||||||
|
|
||||||
|
const inferTypeFromValue = (value) => {
|
||||||
|
if (typeof value === "undefined") return null
|
||||||
|
if (value === null) return "null"
|
||||||
|
if (Array.isArray(value)) return "array"
|
||||||
|
if (Number.isInteger(value)) return "integer"
|
||||||
|
|
||||||
|
return typeof value
|
||||||
|
}
|
||||||
|
|
||||||
export const foldType = (type) => {
|
export const foldType = (type) => {
|
||||||
if (Array.isArray(type) && type.length >= 1) {
|
if (Array.isArray(type) && type.length >= 1) {
|
||||||
if (type.includes("array")) {
|
if (type.includes("array")) {
|
||||||
@@ -100,17 +110,8 @@ export const inferType = (schema, processedSchemas = new WeakSet()) => {
|
|||||||
|
|
||||||
// inferring type from const keyword
|
// inferring type from const keyword
|
||||||
if (typeof type !== "string" && typeof constant !== "undefined") {
|
if (typeof type !== "string" && typeof constant !== "undefined") {
|
||||||
if (constant === null) {
|
const constType = inferTypeFromValue(constant)
|
||||||
type = "null"
|
type = typeof constType === "string" ? constType : type
|
||||||
} else if (typeof constant === "boolean") {
|
|
||||||
type = "boolean"
|
|
||||||
} else if (typeof constant === "number") {
|
|
||||||
type = Number.isInteger(constant) ? "integer" : "number"
|
|
||||||
} else if (typeof constant === "string") {
|
|
||||||
type = "string"
|
|
||||||
} else if (typeof constant === "object") {
|
|
||||||
type = "object"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// inferring type from combining schemas
|
// inferring type from combining schemas
|
||||||
@@ -135,6 +136,13 @@ export const inferType = (schema, processedSchemas = new WeakSet()) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inferring type from example
|
||||||
|
if (typeof type !== "string" && hasExample(schema)) {
|
||||||
|
const example = extractExample(schema)
|
||||||
|
const exampleType = inferTypeFromValue(example)
|
||||||
|
type = typeof exampleType === "string" ? exampleType : type
|
||||||
|
}
|
||||||
|
|
||||||
processedSchemas.delete(schema)
|
processedSchemas.delete(schema)
|
||||||
|
|
||||||
return type || fallbackType
|
return type || fallbackType
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import XML from "xml"
|
import XML from "xml"
|
||||||
import isEmpty from "lodash/isEmpty"
|
import isEmpty from "lodash/isEmpty"
|
||||||
|
import isPlainObject from "lodash/isPlainObject"
|
||||||
|
|
||||||
import { objectify, normalizeArray } from "core/utils"
|
import { objectify, normalizeArray } from "core/utils"
|
||||||
import memoizeN from "../../../../../helpers/memoizeN"
|
import memoizeN from "../../../../../helpers/memoizeN"
|
||||||
@@ -26,9 +27,9 @@ export const sampleFromSchemaGeneric = (
|
|||||||
let usePlainValue = exampleOverride !== undefined || hasExample(schema)
|
let usePlainValue = exampleOverride !== undefined || hasExample(schema)
|
||||||
// first check if there is the need of combining this schema with others required by allOf
|
// first check if there is the need of combining this schema with others required by allOf
|
||||||
const hasOneOf =
|
const hasOneOf =
|
||||||
!usePlainValue && schema && schema.oneOf && schema.oneOf.length > 0
|
!usePlainValue && Array.isArray(schema.oneOf) && schema.oneOf.length > 0
|
||||||
const hasAnyOf =
|
const hasAnyOf =
|
||||||
!usePlainValue && schema && schema.anyOf && schema.anyOf.length > 0
|
!usePlainValue && Array.isArray(schema.anyOf) && schema.anyOf.length > 0
|
||||||
if (!usePlainValue && (hasOneOf || hasAnyOf)) {
|
if (!usePlainValue && (hasOneOf || hasAnyOf)) {
|
||||||
const schemaToAdd = typeCast(
|
const schemaToAdd = typeCast(
|
||||||
hasOneOf ? randomPick(schema.oneOf) : randomPick(schema.anyOf)
|
hasOneOf ? randomPick(schema.oneOf) : randomPick(schema.anyOf)
|
||||||
@@ -39,47 +40,6 @@ export const sampleFromSchemaGeneric = (
|
|||||||
}
|
}
|
||||||
if (hasExample(schema) && hasExample(schemaToAdd)) {
|
if (hasExample(schema) && hasExample(schemaToAdd)) {
|
||||||
usePlainValue = true
|
usePlainValue = true
|
||||||
} else if (schemaToAdd.properties) {
|
|
||||||
if (!schema.properties) {
|
|
||||||
schema.properties = {}
|
|
||||||
}
|
|
||||||
let props = objectify(schemaToAdd.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 (!schema.properties[propName]) {
|
|
||||||
schema.properties[propName] = props[propName]
|
|
||||||
if (
|
|
||||||
!schemaToAdd.required &&
|
|
||||||
Array.isArray(schemaToAdd.required) &&
|
|
||||||
schemaToAdd.required.indexOf(propName) !== -1
|
|
||||||
) {
|
|
||||||
if (!schema.required) {
|
|
||||||
schema.required = [propName]
|
|
||||||
} else {
|
|
||||||
schema.required.push(propName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const _attr = {}
|
const _attr = {}
|
||||||
@@ -99,10 +59,10 @@ export const sampleFromSchemaGeneric = (
|
|||||||
if (respectXML) {
|
if (respectXML) {
|
||||||
name = name || "notagname"
|
name = name || "notagname"
|
||||||
// add prefix to name if exists
|
// add prefix to name if exists
|
||||||
displayName = (prefix ? prefix + ":" : "") + name
|
displayName = (prefix ? `${prefix}:` : "") + name
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
//add prefix to namespace if exists
|
//add prefix to namespace if exists
|
||||||
let namespacePrefix = prefix ? "xmlns:" + prefix : "xmlns"
|
let namespacePrefix = prefix ? `xmlns:${prefix}` : "xmlns"
|
||||||
_attr[namespacePrefix] = namespace
|
_attr[namespacePrefix] = namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,13 +78,12 @@ export const sampleFromSchemaGeneric = (
|
|||||||
let propertyAddedCounter = 0
|
let propertyAddedCounter = 0
|
||||||
|
|
||||||
const hasExceededMaxProperties = () =>
|
const hasExceededMaxProperties = () =>
|
||||||
schema &&
|
|
||||||
Number.isInteger(schema.maxProperties) &&
|
Number.isInteger(schema.maxProperties) &&
|
||||||
schema.maxProperties > 0 &&
|
schema.maxProperties > 0 &&
|
||||||
propertyAddedCounter >= schema.maxProperties
|
propertyAddedCounter >= schema.maxProperties
|
||||||
|
|
||||||
const requiredPropertiesToAdd = () => {
|
const requiredPropertiesToAdd = () => {
|
||||||
if (!schema || !schema.required) {
|
if (!Array.isArray(schema.required) || schema.required.length === 0) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
let addedCount = 0
|
let addedCount = 0
|
||||||
@@ -133,26 +92,25 @@ export const sampleFromSchemaGeneric = (
|
|||||||
(key) => (addedCount += res[key] === undefined ? 0 : 1)
|
(key) => (addedCount += res[key] === undefined ? 0 : 1)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
schema.required.forEach(
|
schema.required.forEach((key) => {
|
||||||
(key) =>
|
addedCount +=
|
||||||
(addedCount +=
|
res[displayName]?.find((x) => x[key] !== undefined) === undefined
|
||||||
res[displayName]?.find((x) => x[key] !== undefined) === undefined
|
? 0
|
||||||
? 0
|
: 1
|
||||||
: 1)
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return schema.required.length - addedCount
|
return schema.required.length - addedCount
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOptionalProperty = (propName) => {
|
const isOptionalProperty = (propName) => {
|
||||||
if (!schema || !schema.required || !schema.required.length) {
|
if (!Array.isArray(schema.required)) return true
|
||||||
return true
|
if (schema.required.length === 0) return true
|
||||||
}
|
|
||||||
return !schema.required.includes(propName)
|
return !schema.required.includes(propName)
|
||||||
}
|
}
|
||||||
|
|
||||||
const canAddProperty = (propName) => {
|
const canAddProperty = (propName) => {
|
||||||
if (!schema || schema.maxProperties == null) {
|
if (!(Number.isInteger(schema.maxProperties) && schema.maxProperties > 0)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (hasExceededMaxProperties()) {
|
if (hasExceededMaxProperties()) {
|
||||||
@@ -170,7 +128,7 @@ export const sampleFromSchemaGeneric = (
|
|||||||
if (respectXML) {
|
if (respectXML) {
|
||||||
addPropertyToResult = (propName, overrideE = undefined) => {
|
addPropertyToResult = (propName, overrideE = undefined) => {
|
||||||
if (schema && props[propName]) {
|
if (schema && props[propName]) {
|
||||||
// case it is an xml attribute
|
// case it is a xml attribute
|
||||||
props[propName].xml = props[propName].xml || {}
|
props[propName].xml = props[propName].xml || {}
|
||||||
|
|
||||||
if (props[propName].xml.attribute) {
|
if (props[propName].xml.attribute) {
|
||||||
@@ -203,7 +161,7 @@ export const sampleFromSchemaGeneric = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
let t = sampleFromSchemaGeneric(
|
let t = sampleFromSchemaGeneric(
|
||||||
(schema && props[propName]) || undefined,
|
props[propName],
|
||||||
config,
|
config,
|
||||||
overrideE,
|
overrideE,
|
||||||
respectXML
|
respectXML
|
||||||
@@ -225,15 +183,11 @@ export const sampleFromSchemaGeneric = (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
Object.hasOwn(schema, "discriminator") &&
|
isPlainObject(schema.discriminator?.mapping) &&
|
||||||
schema.discriminator &&
|
schema.discriminator.propertyName === propName &&
|
||||||
Object.hasOwn(schema.discriminator, "mapping") &&
|
typeof schema.$$ref === "string"
|
||||||
schema.discriminator.mapping &&
|
|
||||||
Object.hasOwn(schema, "$$ref") &&
|
|
||||||
schema.$$ref &&
|
|
||||||
schema.discriminator.propertyName === propName
|
|
||||||
) {
|
) {
|
||||||
for (let pair in schema.discriminator.mapping) {
|
for (const pair in schema.discriminator.mapping) {
|
||||||
if (schema.$$ref.search(schema.discriminator.mapping[pair]) !== -1) {
|
if (schema.$$ref.search(schema.discriminator.mapping[pair]) !== -1) {
|
||||||
res[propName] = pair
|
res[propName] = pair
|
||||||
break
|
break
|
||||||
@@ -273,17 +227,12 @@ export const sampleFromSchemaGeneric = (
|
|||||||
// check if sample is parsable or just a plain string
|
// check if sample is parsable or just a plain string
|
||||||
try {
|
try {
|
||||||
return JSON.parse(sample)
|
return JSON.parse(sample)
|
||||||
} catch (e) {
|
} catch {
|
||||||
// sample is just plain string return it
|
// sample is just plain string return it
|
||||||
return sample
|
return sample
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// recover missing type
|
|
||||||
if (!schema) {
|
|
||||||
type = Array.isArray(sample) ? "array" : typeof sample
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate xml sample recursively for array case
|
// generate xml sample recursively for array case
|
||||||
if (type === "array") {
|
if (type === "array") {
|
||||||
if (!Array.isArray(sample)) {
|
if (!Array.isArray(sample)) {
|
||||||
@@ -295,7 +244,7 @@ export const sampleFromSchemaGeneric = (
|
|||||||
|
|
||||||
let itemSamples = []
|
let itemSamples = []
|
||||||
|
|
||||||
if (items != null && typeof items === "object") {
|
if (isJSONSchemaObject(items)) {
|
||||||
items.xml = items.xml || xml || {}
|
items.xml = items.xml || xml || {}
|
||||||
items.xml.name = items.xml.name || xml.name
|
items.xml.name = items.xml.name || xml.name
|
||||||
itemSamples = sample.map((s) =>
|
itemSamples = sample.map((s) =>
|
||||||
@@ -303,7 +252,7 @@ export const sampleFromSchemaGeneric = (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contains != null && typeof contains === "object") {
|
if (isJSONSchemaObject(contains)) {
|
||||||
contains.xml = contains.xml || xml || {}
|
contains.xml = contains.xml || xml || {}
|
||||||
contains.xml.name = contains.xml.name || xml.name
|
contains.xml.name = contains.xml.name || xml.name
|
||||||
itemSamples = [
|
itemSamples = [
|
||||||
@@ -330,32 +279,17 @@ export const sampleFromSchemaGeneric = (
|
|||||||
if (typeof sample === "string") {
|
if (typeof sample === "string") {
|
||||||
return sample
|
return sample
|
||||||
}
|
}
|
||||||
for (let propName in sample) {
|
for (const propName in sample) {
|
||||||
if (!Object.hasOwn(sample, propName)) {
|
if (!Object.hasOwn(sample, propName)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (
|
if (props[propName]?.readOnly && !includeReadOnly) {
|
||||||
schema &&
|
|
||||||
props[propName] &&
|
|
||||||
props[propName].readOnly &&
|
|
||||||
!includeReadOnly
|
|
||||||
) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (
|
if (props[propName]?.writeOnly && !includeWriteOnly) {
|
||||||
schema &&
|
|
||||||
props[propName] &&
|
|
||||||
props[propName].writeOnly &&
|
|
||||||
!includeWriteOnly
|
|
||||||
) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (
|
if (props[propName]?.xml?.attribute) {
|
||||||
schema &&
|
|
||||||
props[propName] &&
|
|
||||||
props[propName].xml &&
|
|
||||||
props[propName].xml.attribute
|
|
||||||
) {
|
|
||||||
_attr[props[propName].xml.name || propName] = sample[propName]
|
_attr[props[propName].xml.name || propName] = sample[propName]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -376,9 +310,9 @@ export const sampleFromSchemaGeneric = (
|
|||||||
if (type === "array") {
|
if (type === "array") {
|
||||||
let sampleArray = []
|
let sampleArray = []
|
||||||
|
|
||||||
if (contains != null && typeof contains === "object") {
|
if (isJSONSchemaObject(contains)) {
|
||||||
if (respectXML) {
|
if (respectXML) {
|
||||||
contains.xml = contains.xml || schema?.xml || {}
|
contains.xml = contains.xml || schema.xml || {}
|
||||||
contains.xml.name = contains.xml.name || xml.name
|
contains.xml.name = contains.xml.name || xml.name
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,9 +347,9 @@ export const sampleFromSchemaGeneric = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items != null && typeof items === "object") {
|
if (isJSONSchemaObject(items)) {
|
||||||
if (respectXML) {
|
if (respectXML) {
|
||||||
items.xml = items.xml || schema?.xml || {}
|
items.xml = items.xml || schema.xml || {}
|
||||||
items.xml.name = items.xml.name || xml.name
|
items.xml.name = items.xml.name || xml.name
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,13 +401,13 @@ export const sampleFromSchemaGeneric = (
|
|||||||
if (!Object.hasOwn(props, propName)) {
|
if (!Object.hasOwn(props, propName)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (props[propName] && props[propName].deprecated) {
|
if (props[propName]?.deprecated) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (props[propName] && props[propName].readOnly && !includeReadOnly) {
|
if (props[propName]?.readOnly && !includeReadOnly) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (props[propName] && props[propName].writeOnly && !includeWriteOnly) {
|
if (props[propName]?.writeOnly && !includeWriteOnly) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
addPropertyToResult(propName)
|
addPropertyToResult(propName)
|
||||||
@@ -494,7 +428,7 @@ export const sampleFromSchemaGeneric = (
|
|||||||
}
|
}
|
||||||
propertyAddedCounter++
|
propertyAddedCounter++
|
||||||
} else if (isJSONSchemaObject(additionalProperties)) {
|
} else if (isJSONSchemaObject(additionalProperties)) {
|
||||||
const additionalProps = typeCast(additionalProperties)
|
const additionalProps = additionalProperties
|
||||||
const additionalPropSample = sampleFromSchemaGeneric(
|
const additionalPropSample = sampleFromSchemaGeneric(
|
||||||
additionalProps,
|
additionalProps,
|
||||||
config,
|
config,
|
||||||
@@ -504,9 +438,8 @@ export const sampleFromSchemaGeneric = (
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
respectXML &&
|
respectXML &&
|
||||||
additionalProps.xml &&
|
typeof additionalProps?.xml?.name === "string" &&
|
||||||
additionalProps.xml.name &&
|
additionalProps?.xml?.name !== "notagname"
|
||||||
additionalProps.xml.name !== "notagname"
|
|
||||||
) {
|
) {
|
||||||
res[displayName].push(additionalPropSample)
|
res[displayName].push(additionalPropSample)
|
||||||
} else {
|
} else {
|
||||||
@@ -535,25 +468,23 @@ export const sampleFromSchemaGeneric = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
let value
|
let value
|
||||||
if (typeof schema?.const !== "undefined") {
|
if (typeof schema.const !== "undefined") {
|
||||||
// display const value
|
// display const value
|
||||||
value = schema.const
|
value = schema.const
|
||||||
} else if (schema && Array.isArray(schema.enum)) {
|
} else if (schema && Array.isArray(schema.enum)) {
|
||||||
//display enum first value
|
//display enum first value
|
||||||
value = randomPick(normalizeArray(schema.enum))
|
value = randomPick(normalizeArray(schema.enum))
|
||||||
} else if (schema) {
|
} else {
|
||||||
// display schema default
|
// display schema default
|
||||||
const contentSample = Object.hasOwn(schema, "contentSchema")
|
const contentSample = isJSONSchemaObject(schema.contentSchema)
|
||||||
? sampleFromSchemaGeneric(
|
? sampleFromSchemaGeneric(
|
||||||
typeCast(schema.contentSchema),
|
schema.contentSchema,
|
||||||
config,
|
config,
|
||||||
undefined,
|
undefined,
|
||||||
respectXML
|
respectXML
|
||||||
)
|
)
|
||||||
: undefined
|
: undefined
|
||||||
value = typeMap[type](schema, { sample: contentSample })
|
value = typeMap[type](schema, { sample: contentSample })
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (respectXML) {
|
if (respectXML) {
|
||||||
|
|||||||
Reference in New Issue
Block a user