fix: response schema is now inferred from each endpoint's sample JSON
This commit is contained in:
1 parent
d2af39da61
commit
9ab98fd50c
1 file changed
+51
-1
@@ -212,6 +212,53 @@ function parseExample(response: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Schema = Record<string, unknown>
|
||||||
|
|
||||||
|
function mergeSchemas(schemas: Schema[]): Schema {
|
||||||
|
const unique = [...new Map(schemas.map((schema) => [JSON.stringify(schema), schema])).values()]
|
||||||
|
if (unique.length === 1) return unique[0]
|
||||||
|
const objects = unique.filter((schema) => schema.type === 'object')
|
||||||
|
if (objects.length > 1 && objects.length === unique.length) {
|
||||||
|
const keys = [...new Set(objects.flatMap((schema) => Object.keys(schema.properties as Schema)))]
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
properties: Object.fromEntries(
|
||||||
|
keys.map((key) => [
|
||||||
|
key,
|
||||||
|
mergeSchemas(
|
||||||
|
objects
|
||||||
|
.map((schema) => (schema.properties as Record<string, Schema>)[key])
|
||||||
|
.filter((schema) => schema !== undefined),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
required: keys.filter((key) =>
|
||||||
|
objects.every((schema) => key in (schema.properties as Schema)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { anyOf: unique }
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferSchema(value: unknown): Schema {
|
||||||
|
if (value === null) return { type: 'null' }
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.length === 0
|
||||||
|
? { type: 'array', items: {} }
|
||||||
|
: { type: 'array', items: mergeSchemas(value.map(inferSchema)) }
|
||||||
|
}
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
const entries = Object.entries(value as Record<string, unknown>)
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
properties: Object.fromEntries(entries.map(([key, child]) => [key, inferSchema(child)])),
|
||||||
|
required: entries.map(([key]) => key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof value === 'number') return { type: Number.isInteger(value) ? 'integer' : 'number' }
|
||||||
|
return { type: typeof value }
|
||||||
|
}
|
||||||
|
|
||||||
function operation(item: ApiEndpoint) {
|
function operation(item: ApiEndpoint) {
|
||||||
const pathParameters = [...item.path.matchAll(/\{(\w+)\}/g)].map((match) => ({
|
const pathParameters = [...item.path.matchAll(/\{(\w+)\}/g)].map((match) => ({
|
||||||
name: match[1],
|
name: match[1],
|
||||||
@@ -234,7 +281,10 @@ function operation(item: ApiEndpoint) {
|
|||||||
: {
|
: {
|
||||||
description: 'Success',
|
description: 'Success',
|
||||||
content: {
|
content: {
|
||||||
'application/json': { schema: {}, ...(example === undefined ? {} : { example }) },
|
'application/json': {
|
||||||
|
schema: example === undefined ? {} : inferSchema(example),
|
||||||
|
...(example === undefined ? {} : { example }),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user