committed by
Vladimír Gorej
parent
3059a63a0a
commit
ac3b69fb3c
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @prettier
|
* @prettier
|
||||||
*/
|
*/
|
||||||
import { useFn } from "./hooks"
|
import { useFn } from "../hooks"
|
||||||
|
|
||||||
export const upperFirst = (value) => {
|
export const upperFirst = (value) => {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
@@ -58,9 +58,13 @@ export const getType = (schema, processedSchemas = new WeakSet()) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const inferType = () => {
|
const inferType = () => {
|
||||||
if (prefixItems || items) {
|
if (prefixItems || items || schema.contains) {
|
||||||
return getArrayType()
|
return getArrayType()
|
||||||
} else if (schema.properties || schema.additionalProperties) {
|
} else if (
|
||||||
|
schema.properties ||
|
||||||
|
schema.additionalProperties ||
|
||||||
|
schema.patternProperties
|
||||||
|
) {
|
||||||
return "object"
|
return "object"
|
||||||
} else if (
|
} else if (
|
||||||
schema.pattern ||
|
schema.pattern ||
|
||||||
@@ -77,7 +81,7 @@ export const getType = (schema, processedSchemas = new WeakSet()) => {
|
|||||||
schema.multipleOf
|
schema.multipleOf
|
||||||
) {
|
) {
|
||||||
return "number | integer"
|
return "number | integer"
|
||||||
} else if (schema.const !== undefined) {
|
} else if (typeof schema.const !== "undefined") {
|
||||||
if (schema.const === null) {
|
if (schema.const === null) {
|
||||||
return "null"
|
return "null"
|
||||||
} else if (typeof schema.const === "boolean") {
|
} else if (typeof schema.const === "boolean") {
|
||||||
744
src/core/plugins/json-schema-2020-12/fn/samples.js
Normal file
744
src/core/plugins/json-schema-2020-12/fn/samples.js
Normal file
@@ -0,0 +1,744 @@
|
|||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
import XML from "xml"
|
||||||
|
import RandExp from "randexp"
|
||||||
|
import isEmpty from "lodash/isEmpty"
|
||||||
|
import { objectify, isFunc, normalizeArray, deeplyStripKey } from "core/utils"
|
||||||
|
import memoizeN from "../../../../helpers/memoizeN"
|
||||||
|
|
||||||
|
const generateStringFromRegex = (pattern) => {
|
||||||
|
try {
|
||||||
|
const randexp = new RandExp(pattern)
|
||||||
|
return randexp.gen()
|
||||||
|
} catch (e) {
|
||||||
|
// Invalid regex should not cause a crash (regex syntax varies across languages)
|
||||||
|
return "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const primitives = {
|
||||||
|
string: (schema) =>
|
||||||
|
schema.pattern ? generateStringFromRegex(schema.pattern) : "string",
|
||||||
|
string_email: () => "user@example.com",
|
||||||
|
"string_date-time": () => new Date().toISOString(),
|
||||||
|
string_date: () => new Date().toISOString().substring(0, 10),
|
||||||
|
string_uuid: () => "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||||||
|
string_hostname: () => "example.com",
|
||||||
|
string_ipv4: () => "198.51.100.42",
|
||||||
|
string_ipv6: () => "2001:0db8:5b96:0000:0000:426f:8e17:642a",
|
||||||
|
number: () => 0,
|
||||||
|
number_float: () => 0.0,
|
||||||
|
integer: () => 0,
|
||||||
|
boolean: (schema) =>
|
||||||
|
typeof schema.default === "boolean" ? schema.default : true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const primitive = (schema) => {
|
||||||
|
schema = objectify(schema)
|
||||||
|
let { type, format } = schema
|
||||||
|
|
||||||
|
let fn = primitives[`${type}_${format}`] || primitives[type]
|
||||||
|
|
||||||
|
if (isFunc(fn)) return fn(schema)
|
||||||
|
|
||||||
|
return "Unknown Type: " + schema.type
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
|
||||||
|
const objectContracts = ["maxProperties", "minProperties"]
|
||||||
|
const arrayContracts = ["minItems", "maxItems"]
|
||||||
|
const numberContracts = [
|
||||||
|
"minimum",
|
||||||
|
"maximum",
|
||||||
|
"exclusiveMinimum",
|
||||||
|
"exclusiveMaximum",
|
||||||
|
]
|
||||||
|
const stringContracts = ["minLength", "maxLength"]
|
||||||
|
|
||||||
|
const liftSampleHelper = (oldSchema, target, config = {}) => {
|
||||||
|
const setIfNotDefinedInTarget = (key) => {
|
||||||
|
if (target[key] === undefined && oldSchema[key] !== undefined) {
|
||||||
|
target[key] = oldSchema[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
;[
|
||||||
|
"example",
|
||||||
|
"default",
|
||||||
|
"enum",
|
||||||
|
"xml",
|
||||||
|
"type",
|
||||||
|
...objectContracts,
|
||||||
|
...arrayContracts,
|
||||||
|
...numberContracts,
|
||||||
|
...stringContracts,
|
||||||
|
].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.prototype.hasOwnProperty.call(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
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sampleFromSchemaGeneric = (
|
||||||
|
schema,
|
||||||
|
config = {},
|
||||||
|
exampleOverride = undefined,
|
||||||
|
respectXML = false
|
||||||
|
) => {
|
||||||
|
if (schema && isFunc(schema.toJS)) schema = schema.toJS()
|
||||||
|
let usePlainValue =
|
||||||
|
exampleOverride !== undefined ||
|
||||||
|
(schema && schema.example !== undefined) ||
|
||||||
|
(schema && schema.default !== undefined)
|
||||||
|
// first check if there is the need of combining this schema with others required by allOf
|
||||||
|
const hasOneOf =
|
||||||
|
!usePlainValue && schema && schema.oneOf && schema.oneOf.length > 0
|
||||||
|
const hasAnyOf =
|
||||||
|
!usePlainValue && schema && schema.anyOf && schema.anyOf.length > 0
|
||||||
|
if (!usePlainValue && (hasOneOf || hasAnyOf)) {
|
||||||
|
const schemaToAdd = objectify(hasOneOf ? schema.oneOf[0] : schema.anyOf[0])
|
||||||
|
liftSampleHelper(schemaToAdd, schema, config)
|
||||||
|
if (!schema.xml && schemaToAdd.xml) {
|
||||||
|
schema.xml = schemaToAdd.xml
|
||||||
|
}
|
||||||
|
if (schema.example !== undefined && schemaToAdd.example !== undefined) {
|
||||||
|
usePlainValue = true
|
||||||
|
} else if (schemaToAdd.properties) {
|
||||||
|
if (!schema.properties) {
|
||||||
|
schema.properties = {}
|
||||||
|
}
|
||||||
|
let props = objectify(schemaToAdd.properties)
|
||||||
|
for (let propName in props) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(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 = {}
|
||||||
|
let { xml, type, example, properties, additionalProperties, items } =
|
||||||
|
schema || {}
|
||||||
|
let { includeReadOnly, includeWriteOnly } = config
|
||||||
|
xml = xml || {}
|
||||||
|
let { name, prefix, namespace } = xml
|
||||||
|
let displayName
|
||||||
|
let res = {}
|
||||||
|
|
||||||
|
// set xml naming and attributes
|
||||||
|
if (respectXML) {
|
||||||
|
name = name || "notagname"
|
||||||
|
// add prefix to name if exists
|
||||||
|
displayName = (prefix ? prefix + ":" : "") + name
|
||||||
|
if (namespace) {
|
||||||
|
//add prefix to namespace if exists
|
||||||
|
let namespacePrefix = prefix ? "xmlns:" + prefix : "xmlns"
|
||||||
|
_attr[namespacePrefix] = namespace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// init xml default response sample obj
|
||||||
|
if (respectXML) {
|
||||||
|
res[displayName] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaHasAny = (keys) =>
|
||||||
|
keys.some((key) => Object.prototype.hasOwnProperty.call(schema, key))
|
||||||
|
// try recover missing type
|
||||||
|
if (schema && !type) {
|
||||||
|
if (properties || additionalProperties || schemaHasAny(objectContracts)) {
|
||||||
|
type = "object"
|
||||||
|
} else if (items || schemaHasAny(arrayContracts)) {
|
||||||
|
type = "array"
|
||||||
|
} else if (schemaHasAny(numberContracts)) {
|
||||||
|
type = "number"
|
||||||
|
schema.type = "number"
|
||||||
|
} else if (!usePlainValue && !schema.enum) {
|
||||||
|
// implicit cover schemaHasAny(stringContracts) or A schema without a type matches any data type is:
|
||||||
|
// components:
|
||||||
|
// schemas:
|
||||||
|
// AnyValue:
|
||||||
|
// anyOf:
|
||||||
|
// - type: string
|
||||||
|
// - type: number
|
||||||
|
// - type: integer
|
||||||
|
// - type: boolean
|
||||||
|
// - type: array
|
||||||
|
// items: {}
|
||||||
|
// - type: object
|
||||||
|
//
|
||||||
|
// which would resolve to type: string
|
||||||
|
type = "string"
|
||||||
|
schema.type = "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMinMaxItems = (sampleArray) => {
|
||||||
|
if (schema?.maxItems !== null && schema?.maxItems !== undefined) {
|
||||||
|
sampleArray = sampleArray.slice(0, schema?.maxItems)
|
||||||
|
}
|
||||||
|
if (schema?.minItems !== null && schema?.minItems !== undefined) {
|
||||||
|
let i = 0
|
||||||
|
while (sampleArray.length < schema?.minItems) {
|
||||||
|
sampleArray.push(sampleArray[i++ % sampleArray.length])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sampleArray
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to result helper init for xml or json
|
||||||
|
const props = objectify(properties)
|
||||||
|
let addPropertyToResult
|
||||||
|
let propertyAddedCounter = 0
|
||||||
|
|
||||||
|
const hasExceededMaxProperties = () =>
|
||||||
|
schema &&
|
||||||
|
schema.maxProperties !== null &&
|
||||||
|
schema.maxProperties !== undefined &&
|
||||||
|
propertyAddedCounter >= schema.maxProperties
|
||||||
|
|
||||||
|
const requiredPropertiesToAdd = () => {
|
||||||
|
if (!schema || !schema.required) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
let addedCount = 0
|
||||||
|
if (respectXML) {
|
||||||
|
schema.required.forEach(
|
||||||
|
(key) => (addedCount += res[key] === undefined ? 0 : 1)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
schema.required.forEach(
|
||||||
|
(key) =>
|
||||||
|
(addedCount +=
|
||||||
|
res[displayName]?.find((x) => x[key] !== undefined) === undefined
|
||||||
|
? 0
|
||||||
|
: 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return schema.required.length - addedCount
|
||||||
|
}
|
||||||
|
|
||||||
|
const isOptionalProperty = (propName) => {
|
||||||
|
if (!schema || !schema.required || !schema.required.length) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !schema.required.includes(propName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const canAddProperty = (propName) => {
|
||||||
|
if (
|
||||||
|
!schema ||
|
||||||
|
schema.maxProperties === null ||
|
||||||
|
schema.maxProperties === undefined
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (hasExceededMaxProperties()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!isOptionalProperty(propName)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
schema.maxProperties - propertyAddedCounter - requiredPropertiesToAdd() >
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (respectXML) {
|
||||||
|
addPropertyToResult = (propName, overrideE = undefined) => {
|
||||||
|
if (schema && props[propName]) {
|
||||||
|
// case it is an xml attribute
|
||||||
|
props[propName].xml = props[propName].xml || {}
|
||||||
|
|
||||||
|
if (props[propName].xml.attribute) {
|
||||||
|
const enumAttrVal = Array.isArray(props[propName].enum)
|
||||||
|
? props[propName].enum[0]
|
||||||
|
: undefined
|
||||||
|
const attrExample = props[propName].example
|
||||||
|
const attrDefault = props[propName].default
|
||||||
|
|
||||||
|
if (attrExample !== undefined) {
|
||||||
|
_attr[props[propName].xml.name || propName] = attrExample
|
||||||
|
} else if (attrDefault !== undefined) {
|
||||||
|
_attr[props[propName].xml.name || propName] = attrDefault
|
||||||
|
} else if (enumAttrVal !== undefined) {
|
||||||
|
_attr[props[propName].xml.name || propName] = enumAttrVal
|
||||||
|
} else {
|
||||||
|
_attr[props[propName].xml.name || propName] = primitive(
|
||||||
|
props[propName]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
props[propName].xml.name = props[propName].xml.name || propName
|
||||||
|
} else if (!props[propName] && additionalProperties !== false) {
|
||||||
|
// case only additionalProperty that is not defined in schema
|
||||||
|
props[propName] = {
|
||||||
|
xml: {
|
||||||
|
name: propName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = sampleFromSchemaGeneric(
|
||||||
|
(schema && props[propName]) || undefined,
|
||||||
|
config,
|
||||||
|
overrideE,
|
||||||
|
respectXML
|
||||||
|
)
|
||||||
|
if (!canAddProperty(propName)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
propertyAddedCounter++
|
||||||
|
if (Array.isArray(t)) {
|
||||||
|
res[displayName] = res[displayName].concat(t)
|
||||||
|
} else {
|
||||||
|
res[displayName].push(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addPropertyToResult = (propName, overrideE) => {
|
||||||
|
if (!canAddProperty(propName)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Object.prototype.hasOwnProperty.call(schema, "discriminator") &&
|
||||||
|
schema.discriminator &&
|
||||||
|
Object.prototype.hasOwnProperty.call(schema.discriminator, "mapping") &&
|
||||||
|
schema.discriminator.mapping &&
|
||||||
|
Object.prototype.hasOwnProperty.call(schema, "$$ref") &&
|
||||||
|
schema.$$ref &&
|
||||||
|
schema.discriminator.propertyName === propName
|
||||||
|
) {
|
||||||
|
for (let pair in schema.discriminator.mapping) {
|
||||||
|
if (schema.$$ref.search(schema.discriminator.mapping[pair]) !== -1) {
|
||||||
|
res[propName] = pair
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res[propName] = sampleFromSchemaGeneric(
|
||||||
|
props[propName],
|
||||||
|
config,
|
||||||
|
overrideE,
|
||||||
|
respectXML
|
||||||
|
)
|
||||||
|
}
|
||||||
|
propertyAddedCounter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for plain value and if found use it to generate sample from it
|
||||||
|
if (usePlainValue) {
|
||||||
|
let sample
|
||||||
|
if (exampleOverride !== undefined) {
|
||||||
|
sample = sanitizeRef(exampleOverride)
|
||||||
|
} else if (example !== undefined) {
|
||||||
|
sample = sanitizeRef(example)
|
||||||
|
} else {
|
||||||
|
sample = sanitizeRef(schema.default)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if json just return
|
||||||
|
if (!respectXML) {
|
||||||
|
// spacial case yaml parser can not know about
|
||||||
|
if (typeof sample === "number" && type === "string") {
|
||||||
|
return `${sample}`
|
||||||
|
}
|
||||||
|
// return if sample does not need any parsing
|
||||||
|
if (typeof sample !== "string" || type === "string") {
|
||||||
|
return sample
|
||||||
|
}
|
||||||
|
// check if sample is parsable or just a plain string
|
||||||
|
try {
|
||||||
|
return JSON.parse(sample)
|
||||||
|
} catch (e) {
|
||||||
|
// sample is just plain string return it
|
||||||
|
return sample
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover missing type
|
||||||
|
if (!schema) {
|
||||||
|
type = Array.isArray(sample) ? "array" : typeof sample
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate xml sample recursively for array case
|
||||||
|
if (type === "array") {
|
||||||
|
if (!Array.isArray(sample)) {
|
||||||
|
if (typeof sample === "string") {
|
||||||
|
return sample
|
||||||
|
}
|
||||||
|
sample = [sample]
|
||||||
|
}
|
||||||
|
const itemSchema = schema ? schema.items : undefined
|
||||||
|
if (itemSchema) {
|
||||||
|
itemSchema.xml = itemSchema.xml || xml || {}
|
||||||
|
itemSchema.xml.name = itemSchema.xml.name || xml.name
|
||||||
|
}
|
||||||
|
let itemSamples = sample.map((s) =>
|
||||||
|
sampleFromSchemaGeneric(itemSchema, config, s, respectXML)
|
||||||
|
)
|
||||||
|
itemSamples = handleMinMaxItems(itemSamples)
|
||||||
|
if (xml.wrapped) {
|
||||||
|
res[displayName] = itemSamples
|
||||||
|
if (!isEmpty(_attr)) {
|
||||||
|
res[displayName].push({ _attr: _attr })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res = itemSamples
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate xml sample recursively for object case
|
||||||
|
if (type === "object") {
|
||||||
|
// case literal example
|
||||||
|
if (typeof sample === "string") {
|
||||||
|
return sample
|
||||||
|
}
|
||||||
|
for (let propName in sample) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(sample, propName)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
schema &&
|
||||||
|
props[propName] &&
|
||||||
|
props[propName].readOnly &&
|
||||||
|
!includeReadOnly
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
schema &&
|
||||||
|
props[propName] &&
|
||||||
|
props[propName].writeOnly &&
|
||||||
|
!includeWriteOnly
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
schema &&
|
||||||
|
props[propName] &&
|
||||||
|
props[propName].xml &&
|
||||||
|
props[propName].xml.attribute
|
||||||
|
) {
|
||||||
|
_attr[props[propName].xml.name || propName] = sample[propName]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addPropertyToResult(propName, sample[propName])
|
||||||
|
}
|
||||||
|
if (!isEmpty(_attr)) {
|
||||||
|
res[displayName].push({ _attr: _attr })
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
res[displayName] = !isEmpty(_attr) ? [{ _attr: _attr }, sample] : sample
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// use schema to generate sample
|
||||||
|
|
||||||
|
if (type === "object") {
|
||||||
|
for (let propName in props) {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(props, propName)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (props[propName] && props[propName].deprecated) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (props[propName] && props[propName].readOnly && !includeReadOnly) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (props[propName] && props[propName].writeOnly && !includeWriteOnly) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addPropertyToResult(propName)
|
||||||
|
}
|
||||||
|
if (respectXML && _attr) {
|
||||||
|
res[displayName].push({ _attr: _attr })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasExceededMaxProperties()) {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalProperties === true) {
|
||||||
|
if (respectXML) {
|
||||||
|
res[displayName].push({ additionalProp: "Anything can be here" })
|
||||||
|
} else {
|
||||||
|
res.additionalProp1 = {}
|
||||||
|
}
|
||||||
|
propertyAddedCounter++
|
||||||
|
} else if (additionalProperties) {
|
||||||
|
const additionalProps = objectify(additionalProperties)
|
||||||
|
const additionalPropSample = sampleFromSchemaGeneric(
|
||||||
|
additionalProps,
|
||||||
|
config,
|
||||||
|
undefined,
|
||||||
|
respectXML
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
respectXML &&
|
||||||
|
additionalProps.xml &&
|
||||||
|
additionalProps.xml.name &&
|
||||||
|
additionalProps.xml.name !== "notagname"
|
||||||
|
) {
|
||||||
|
res[displayName].push(additionalPropSample)
|
||||||
|
} else {
|
||||||
|
const toGenerateCount =
|
||||||
|
schema.minProperties !== null &&
|
||||||
|
schema.minProperties !== undefined &&
|
||||||
|
propertyAddedCounter < schema.minProperties
|
||||||
|
? schema.minProperties - propertyAddedCounter
|
||||||
|
: 3
|
||||||
|
for (let i = 1; i <= toGenerateCount; i++) {
|
||||||
|
if (hasExceededMaxProperties()) {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
if (respectXML) {
|
||||||
|
const temp = {}
|
||||||
|
temp["additionalProp" + i] = additionalPropSample["notagname"]
|
||||||
|
res[displayName].push(temp)
|
||||||
|
} else {
|
||||||
|
res["additionalProp" + i] = additionalPropSample
|
||||||
|
}
|
||||||
|
propertyAddedCounter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
value = normalizeArray(schema.enum)[0]
|
||||||
|
} else if (schema) {
|
||||||
|
// display schema default
|
||||||
|
value = primitive(schema)
|
||||||
|
if (typeof value === "number") {
|
||||||
|
let min = schema.minimum
|
||||||
|
if (min !== undefined && min !== null) {
|
||||||
|
if (schema.exclusiveMinimum) {
|
||||||
|
min++
|
||||||
|
}
|
||||||
|
value = min
|
||||||
|
}
|
||||||
|
let max = schema.maximum
|
||||||
|
if (max !== undefined && max !== null) {
|
||||||
|
if (schema.exclusiveMaximum) {
|
||||||
|
max--
|
||||||
|
}
|
||||||
|
value = max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof value === "string") {
|
||||||
|
if (schema.maxLength !== null && schema.maxLength !== undefined) {
|
||||||
|
value = value.slice(0, schema.maxLength)
|
||||||
|
}
|
||||||
|
if (schema.minLength !== null && schema.minLength !== undefined) {
|
||||||
|
let i = 0
|
||||||
|
while (value.length < schema.minLength) {
|
||||||
|
value += value[i++ % value.length]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (type === "file") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (respectXML) {
|
||||||
|
res[displayName] = !isEmpty(_attr) ? [{ _attr: _attr }, value] : value
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
export const inferSchema = (thing) => {
|
||||||
|
if (thing.schema) thing = thing.schema
|
||||||
|
|
||||||
|
if (thing.properties) {
|
||||||
|
thing.type = "object"
|
||||||
|
}
|
||||||
|
|
||||||
|
return thing // Hopefully this will have something schema like in it... `type` for example
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createXMLExample = (schema, config, o) => {
|
||||||
|
const json = sampleFromSchemaGeneric(schema, config, o, true)
|
||||||
|
if (!json) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof json === "string") {
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
return XML(json, { declaration: true, indent: "\t" })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sampleFromSchema = (schema, config, o) =>
|
||||||
|
sampleFromSchemaGeneric(schema, config, o, false)
|
||||||
|
|
||||||
|
const resolver = (arg1, arg2, arg3) => [
|
||||||
|
arg1,
|
||||||
|
JSON.stringify(arg2),
|
||||||
|
JSON.stringify(arg3),
|
||||||
|
]
|
||||||
|
|
||||||
|
export const memoizedCreateXMLExample = memoizeN(createXMLExample, resolver)
|
||||||
|
|
||||||
|
export const memoizedSampleFromSchema = memoizeN(sampleFromSchema, resolver)
|
||||||
@@ -56,7 +56,7 @@ import {
|
|||||||
stringify,
|
stringify,
|
||||||
stringifyConstraints,
|
stringifyConstraints,
|
||||||
getDependentRequired,
|
getDependentRequired,
|
||||||
} from "./fn"
|
} from "./fn/index"
|
||||||
|
|
||||||
export const withJSONSchemaContext = (Component, overrides = {}) => {
|
export const withJSONSchemaContext = (Component, overrides = {}) => {
|
||||||
const value = {
|
const value = {
|
||||||
|
|||||||
@@ -43,7 +43,15 @@ import KeywordWriteOnly from "./components/keywords/WriteOnly"
|
|||||||
import Accordion from "./components/Accordion/Accordion"
|
import Accordion from "./components/Accordion/Accordion"
|
||||||
import ExpandDeepButton from "./components/ExpandDeepButton/ExpandDeepButton"
|
import ExpandDeepButton from "./components/ExpandDeepButton/ExpandDeepButton"
|
||||||
import ChevronRightIcon from "./components/icons/ChevronRight"
|
import ChevronRightIcon from "./components/icons/ChevronRight"
|
||||||
import { upperFirst, hasKeyword, isExpandable } from "./fn"
|
import { upperFirst, hasKeyword, isExpandable } from "./fn/index"
|
||||||
|
import {
|
||||||
|
inferSchema,
|
||||||
|
sampleFromSchema,
|
||||||
|
sampleFromSchemaGeneric,
|
||||||
|
createXMLExample,
|
||||||
|
memoizedSampleFromSchema,
|
||||||
|
memoizedCreateXMLExample,
|
||||||
|
} from "./fn/samples"
|
||||||
import { JSONSchemaDeepExpansionContext } from "./context"
|
import { JSONSchemaDeepExpansionContext } from "./context"
|
||||||
import { useFn, useConfig, useComponent, useIsExpandedDeeply } from "./hooks"
|
import { useFn, useConfig, useComponent, useIsExpandedDeeply } from "./hooks"
|
||||||
import { withJSONSchemaContext } from "./hoc"
|
import { withJSONSchemaContext } from "./hoc"
|
||||||
@@ -97,6 +105,7 @@ const JSONSchema202012Plugin = () => ({
|
|||||||
},
|
},
|
||||||
fn: {
|
fn: {
|
||||||
upperFirst,
|
upperFirst,
|
||||||
|
memoizedSampleFromSchema: null,
|
||||||
jsonSchema202012: {
|
jsonSchema202012: {
|
||||||
isExpandable,
|
isExpandable,
|
||||||
hasKeyword,
|
hasKeyword,
|
||||||
@@ -104,6 +113,12 @@ const JSONSchema202012Plugin = () => ({
|
|||||||
useConfig,
|
useConfig,
|
||||||
useComponent,
|
useComponent,
|
||||||
useIsExpandedDeeply,
|
useIsExpandedDeeply,
|
||||||
|
inferSchema,
|
||||||
|
sampleFromSchema,
|
||||||
|
sampleFromSchemaGeneric,
|
||||||
|
createXMLExample,
|
||||||
|
memoizedSampleFromSchema,
|
||||||
|
memoizedCreateXMLExample,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -96,3 +96,30 @@ export const createOnlyOAS31ComponentWrapper =
|
|||||||
return <Original {...props} />
|
return <Original {...props} />
|
||||||
}
|
}
|
||||||
/* eslint-enable react/jsx-filename-extension */
|
/* eslint-enable react/jsx-filename-extension */
|
||||||
|
|
||||||
|
/** Utilize JSON Schema 2020-12 samples **/
|
||||||
|
export const wrapSampleFn =
|
||||||
|
(fnName) =>
|
||||||
|
(getSystem) =>
|
||||||
|
(...args) => {
|
||||||
|
const { fn, specSelectors } = getSystem()
|
||||||
|
|
||||||
|
if (specSelectors.isOpenAPI31()) {
|
||||||
|
return fn.jsonSchema202012[fnName](...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn[fnName](...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const wrapInferSchema = wrapSampleFn("inferSchema")
|
||||||
|
export const wrapSampleFromSchema = wrapSampleFn("sampleFromSchema")
|
||||||
|
export const wrapSampleFromSchemaGeneric = wrapSampleFn(
|
||||||
|
"sampleFromSchemaGeneric"
|
||||||
|
)
|
||||||
|
export const wrapCreateXMLExample = wrapSampleFn("createXMLExample")
|
||||||
|
export const wrapMemoizedSampleFromSchema = wrapSampleFn(
|
||||||
|
"memoizedSampleFromSchema"
|
||||||
|
)
|
||||||
|
export const wrapMemoizedCreateXMLExample = wrapSampleFn(
|
||||||
|
"memoizedCreateXMLExample"
|
||||||
|
)
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ import {
|
|||||||
isOAS31 as isOAS31Fn,
|
isOAS31 as isOAS31Fn,
|
||||||
createOnlyOAS31Selector as createOnlyOAS31SelectorFn,
|
createOnlyOAS31Selector as createOnlyOAS31SelectorFn,
|
||||||
createSystemSelector as createSystemSelectorFn,
|
createSystemSelector as createSystemSelectorFn,
|
||||||
|
wrapInferSchema,
|
||||||
|
wrapSampleFromSchema,
|
||||||
|
wrapSampleFromSchemaGeneric,
|
||||||
|
wrapCreateXMLExample,
|
||||||
|
wrapMemoizedSampleFromSchema,
|
||||||
|
wrapMemoizedCreateXMLExample,
|
||||||
} from "./fn"
|
} from "./fn"
|
||||||
import {
|
import {
|
||||||
license as selectLicense,
|
license as selectLicense,
|
||||||
@@ -82,6 +88,15 @@ const OAS31Plugin = ({ getSystem }) => {
|
|||||||
getProperties,
|
getProperties,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// wraps schema generators and make them specific to OpenAPI version
|
||||||
|
if (typeof system.fn.inferSchema === "function") {
|
||||||
|
pluginFn.inferSchema = wrapInferSchema(getSystem)
|
||||||
|
pluginFn.sampleFromSchema = wrapSampleFromSchema(getSystem)
|
||||||
|
pluginFn.sampleFromSchemaGeneric = wrapSampleFromSchemaGeneric(getSystem)
|
||||||
|
pluginFn.createXMLExample = wrapCreateXMLExample(getSystem)
|
||||||
|
pluginFn.memoizedSampleFromSchema = wrapMemoizedSampleFromSchema(getSystem)
|
||||||
|
pluginFn.memoizedCreateXMLExample = wrapMemoizedCreateXMLExample(getSystem)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fn: pluginFn,
|
fn: pluginFn,
|
||||||
|
|||||||
Reference in New Issue
Block a user