未验证 提交 032bd71e 编写于 作者: M Mahtis Michel 提交者: GitHub

feat(sample-gen): infer implicit type and validation constraint types (#7117)

* this closes some dead ends of sample generation where no sample can be generated
上级 75865f31
...@@ -130,7 +130,7 @@ export default class Response extends React.Component { ...@@ -130,7 +130,7 @@ export default class Response extends React.Component {
// Goal: find an example value for `sampleResponse` // Goal: find an example value for `sampleResponse`
if(isOAS3) { if(isOAS3) {
sampleSchema = activeMediaType.get("schema", Map({})).toJS() sampleSchema = activeMediaType.get("schema")?.toJS()
if(examplesForMediaType) { if(examplesForMediaType) {
const targetExamplesKey = this.getTargetExamplesKey() const targetExamplesKey = this.getTargetExamplesKey()
const targetExample = examplesForMediaType const targetExample = examplesForMediaType
......
...@@ -35,28 +35,33 @@ const primitive = (schema) => { ...@@ -35,28 +35,33 @@ const primitive = (schema) => {
const sanitizeRef = (value) => deeplyStripKey(value, "$$ref", (val) => const sanitizeRef = (value) => deeplyStripKey(value, "$$ref", (val) =>
typeof val === "string" && val.indexOf("#") > -1) 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 liftSampleHelper = (oldSchema, target, config = {}) => {
const setIfNotDefinedInTarget = (key) => { const setIfNotDefinedInTarget = (key) => {
if(target[key] === undefined && oldSchema[key] !== undefined) { if(target[key] === undefined && oldSchema[key] !== undefined) {
target[key] = oldSchema[key] target[key] = oldSchema[key]
} }
} }
[ [
"example", "example",
"default", "default",
"enum", "enum",
"xml", "xml",
"type", "type",
"maxProperties", ...objectContracts,
"minProperties", ...arrayContracts,
"minItems", ...numberContracts,
"maxItems", ...stringContracts,
"minimum",
"maximum",
"exclusiveMinimum",
"exclusiveMaximum",
"minLength",
"maxLength"
].forEach(key => setIfNotDefinedInTarget(key)) ].forEach(key => setIfNotDefinedInTarget(key))
if(oldSchema.required !== undefined && Array.isArray(oldSchema.required)) { if(oldSchema.required !== undefined && Array.isArray(oldSchema.required)) {
...@@ -111,8 +116,9 @@ const liftSampleHelper = (oldSchema, target, config = {}) => { ...@@ -111,8 +116,9 @@ const liftSampleHelper = (oldSchema, target, config = {}) => {
} }
export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = undefined, respectXML = false) => { export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = undefined, respectXML = false) => {
schema = objectify(schema) if(schema && isFunc(schema.toJS))
let usePlainValue = exampleOverride !== undefined || schema.example !== undefined || schema && schema.default !== undefined 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 // 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 hasOneOf = !usePlainValue && schema && schema.oneOf && schema.oneOf.length > 0
const hasAnyOf = !usePlainValue && schema && schema.anyOf && schema.anyOf.length > 0 const hasAnyOf = !usePlainValue && schema && schema.anyOf && schema.anyOf.length > 0
...@@ -159,7 +165,7 @@ export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = und ...@@ -159,7 +165,7 @@ export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = und
} }
} }
const _attr = {} const _attr = {}
let { xml, type, example, properties, additionalProperties, items } = schema let { xml, type, example, properties, additionalProperties, items } = schema || {}
let { includeReadOnly, includeWriteOnly } = config let { includeReadOnly, includeWriteOnly } = config
xml = xml || {} xml = xml || {}
let { name, prefix, namespace } = xml let { name, prefix, namespace } = xml
...@@ -183,24 +189,43 @@ export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = und ...@@ -183,24 +189,43 @@ export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = und
res[displayName] = [] res[displayName] = []
} }
const schemaHasAny = (keys) => keys.some(key => Object.prototype.hasOwnProperty.call(schema, key))
// try recover missing type // try recover missing type
if(schema && !type) { if(schema && !type) {
if(properties || additionalProperties) { if(properties || additionalProperties || schemaHasAny(objectContracts)) {
type = "object" type = "object"
} else if(items) { } else if(items || schemaHasAny(arrayContracts)) {
type = "array" type = "array"
} else if(schemaHasAny(numberContracts)) {
type = "number"
schema.type = "number"
} else if(!usePlainValue && !schema.enum){ } else if(!usePlainValue && !schema.enum){
return // 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) => { const handleMinMaxItems = (sampleArray) => {
if (schema.maxItems !== null && schema.maxItems !== undefined) { if (schema?.maxItems !== null && schema?.maxItems !== undefined) {
sampleArray = sampleArray.slice(0, schema.maxItems) sampleArray = sampleArray.slice(0, schema?.maxItems)
} }
if (schema.minItems !== null && schema.minItems !== undefined) { if (schema?.minItems !== null && schema?.minItems !== undefined) {
let i = 0 let i = 0
while (sampleArray.length < schema.minItems) { while (sampleArray.length < schema?.minItems) {
sampleArray.push(sampleArray[i++ % sampleArray.length]) sampleArray.push(sampleArray[i++ % sampleArray.length])
} }
} }
...@@ -477,7 +502,7 @@ export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = und ...@@ -477,7 +502,7 @@ export const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = und
if(type === "array") { if(type === "array") {
let sampleArray let sampleArray
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
} }
......
...@@ -314,7 +314,7 @@ describe("OpenAPI 3.0 Multiple Examples - core features", () => { ...@@ -314,7 +314,7 @@ describe("OpenAPI 3.0 Multiple Examples - core features", () => {
.get(".json-schema-form-item-add") .get(".json-schema-form-item-add")
.click() .click()
.get(".json-schema-form-item:last-of-type > input") .get(".json-schema-form-item:last-of-type > input")
.type("5") .type("{selectall}5")
// Assert against the input fields // Assert against the input fields
.get(".json-schema-form-item > input") .get(".json-schema-form-item > input")
.then((inputs) => { .then((inputs) => {
...@@ -345,7 +345,7 @@ describe("OpenAPI 3.0 Multiple Examples - core features", () => { ...@@ -345,7 +345,7 @@ describe("OpenAPI 3.0 Multiple Examples - core features", () => {
.get(".json-schema-form-item-add") .get(".json-schema-form-item-add")
.click() .click()
.get(".json-schema-form-item:last-of-type > input") .get(".json-schema-form-item:last-of-type > input")
.type("5") .type("{selectall}5")
// Reset to an example // Reset to an example
.get(".parameters-col_description .examples-select > select") .get(".parameters-col_description .examples-select > select")
.select("ArrayExampleB") .select("ArrayExampleB")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册