| | import { renderContent } from './render-content' |
| |
|
| | interface Schema { |
| | oneOf?: any[] |
| | type?: string |
| | items?: any |
| | properties?: Record<string, any> |
| | required?: string[] |
| | additionalProperties?: any |
| | description?: string |
| | enum?: string[] |
| | nullable?: boolean |
| | allOf?: any[] |
| | anyOf?: any[] |
| | [key: string]: any |
| | } |
| |
|
| | export interface TransformedParam { |
| | type: string |
| | name: string |
| | description: string |
| | isRequired?: boolean |
| | in?: string |
| | childParamsGroups?: TransformedParam[] |
| | enum?: string[] |
| | oneOfObject?: boolean |
| | default?: any |
| | } |
| |
|
| | interface BodyParamProps { |
| | paramKey?: string |
| | required?: string[] |
| | childParamsGroups?: TransformedParam[] |
| | topLevel?: boolean |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | async function getTopLevelOneOfProperty( |
| | schema: Schema, |
| | ): Promise<{ properties: Record<string, any>; required: string[] }> { |
| | if (!schema.oneOf) { |
| | throw new Error('Schema does not have a requestBody oneOf property defined') |
| | } |
| | if (!(Array.isArray(schema.oneOf) && schema.oneOf.length > 0)) { |
| | throw new Error('Schema requestBody oneOf property is not an array') |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | const firstOneOfObject = schema.oneOf[0] |
| | const allOneOfAreObjects = schema.oneOf.every((elem) => elem.type === 'object') |
| | let required = firstOneOfObject.required || [] |
| | let properties = firstOneOfObject.properties || {} |
| |
|
| | |
| | |
| | |
| | if (allOneOfAreObjects) { |
| | for (const each of schema.oneOf.slice(1)) { |
| | Object.assign(firstOneOfObject.properties, each.properties) |
| | required = firstOneOfObject.required.concat(each.required) |
| | } |
| | properties = firstOneOfObject.properties |
| | } |
| | return { properties, required } |
| | } |
| |
|
| | |
| | |
| | async function handleObjectOnlyOneOf( |
| | param: Schema, |
| | paramType: string[], |
| | ): Promise<TransformedParam[]> { |
| | if (param.oneOf && param.oneOf.every((object: TransformedParam) => object.type === 'object')) { |
| | paramType.push('object') |
| | param.oneOfObject = true |
| | return await getOneOfChildParams(param) |
| | } |
| | return [] |
| | } |
| |
|
| | export async function getBodyParams(schema: Schema, topLevel = false): Promise<TransformedParam[]> { |
| | const bodyParametersParsed: TransformedParam[] = [] |
| | const schemaObject = schema.oneOf && topLevel ? await getTopLevelOneOfProperty(schema) : schema |
| | const properties = schemaObject.properties || {} |
| | const required = schemaObject.required || [] |
| |
|
| | |
| | |
| | if (topLevel && schema.type === 'array') { |
| | const childParamsGroups: TransformedParam[] = [] |
| | const arrayType = schema.items.type |
| | const paramType = [schema.type] |
| | if (arrayType === 'object') { |
| | childParamsGroups.push(...(await getBodyParams(schema.items, false))) |
| | } else { |
| | paramType.splice(paramType.indexOf('array'), 1, `array of ${arrayType}s`) |
| | } |
| | const paramDecorated = await getTransformedParam(schema, paramType, { |
| | required, |
| | topLevel, |
| | childParamsGroups, |
| | }) |
| | return [paramDecorated] |
| | } |
| |
|
| | for (const [paramKey, param] of Object.entries(properties)) { |
| | |
| | |
| | |
| | |
| | |
| | const paramType = Array.isArray(param.type) ? param.type : [param.type] |
| | const additionalPropertiesType = param.additionalProperties |
| | ? Array.isArray(param.additionalProperties.type) |
| | ? param.additionalProperties.type |
| | : [param.additionalProperties.type] |
| | : [] |
| | const childParamsGroups: TransformedParam[] = [] |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | if (param.additionalProperties && additionalPropertiesType.includes('object')) { |
| | const keyParam: TransformedParam = { |
| | type: 'object', |
| | name: 'key', |
| | description: await renderContent( |
| | `A user-defined key to represent an item in \`${paramKey}\`.`, |
| | ), |
| | isRequired: param.required, |
| | enum: param.enum, |
| | default: param.default, |
| | childParamsGroups: [], |
| | } |
| | if (keyParam.childParamsGroups) { |
| | keyParam.childParamsGroups.push(...(await getBodyParams(param.additionalProperties, false))) |
| | } |
| | childParamsGroups.push(keyParam) |
| | } else if (paramType.includes('array') && param.items) { |
| | if (param.items.oneOf) { |
| | if (param.items.oneOf.every((object: TransformedParam) => object.type === 'object')) { |
| | paramType.splice(paramType.indexOf('array'), 1, `array of objects`) |
| | param.oneOfObject = true |
| | childParamsGroups.push(...(await getOneOfChildParams(param.items))) |
| | } |
| | } else { |
| | const arrayType = param.items.type |
| | if (arrayType) { |
| | paramType.splice(paramType.indexOf('array'), 1, `array of ${arrayType}s`) |
| | } |
| | if (arrayType === 'object') { |
| | childParamsGroups.push(...(await getBodyParams(param.items, false))) |
| | } |
| | if (arrayType === 'string' && param.items.enum) { |
| | param.description += `${ |
| | param.description ? '\n' : '' |
| | }Supported values are: ${param.items.enum.map((lang: string) => `<code>${lang}</code>`).join(', ')}` |
| | } |
| | } |
| | } else if (paramType.includes('object')) { |
| | if (param.oneOf) { |
| | const oneOfChildren = await handleObjectOnlyOneOf(param, paramType) |
| | if (oneOfChildren.length > 0) { |
| | childParamsGroups.push(...oneOfChildren) |
| | } |
| | } else { |
| | childParamsGroups.push(...(await getBodyParams(param, false))) |
| | } |
| | } else if (param.oneOf) { |
| | |
| | const oneOfChildren = await handleObjectOnlyOneOf(param, paramType) |
| | if (oneOfChildren.length > 0) { |
| | childParamsGroups.push(...oneOfChildren) |
| | } else { |
| | |
| | const descriptions: { type: string; description: string }[] = [] |
| | for (const childParam of param.oneOf) { |
| | paramType.push(childParam.type) |
| | if (!param.description) { |
| | if (childParam.type === 'array') { |
| | if (childParam.items.description) { |
| | descriptions.push({ |
| | type: childParam.type, |
| | description: childParam.items.description, |
| | }) |
| | } |
| | } else { |
| | if (childParam.description) { |
| | descriptions.push({ type: childParam.type, description: childParam.description }) |
| | } |
| | } |
| | } else { |
| | descriptions.push({ type: param.type, description: param.description }) |
| | } |
| | } |
| | |
| | |
| | const oneOfDescriptions = descriptions.length ? descriptions[0].description : '' |
| | if (!param.description) param.description = oneOfDescriptions |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | } else if (param.anyOf && Object.keys(param).length === 1) { |
| | const firstObject = Object.values(param.anyOf).find( |
| | (item) => (item as Schema).type === 'object', |
| | ) as Schema |
| | if (firstObject) { |
| | paramType.push('object') |
| | param.description = firstObject.description |
| | param.isRequired = firstObject.required |
| | childParamsGroups.push(...(await getBodyParams(firstObject, false))) |
| | } else { |
| | paramType.push(param.anyOf[0].type) |
| | param.description = param.anyOf[0].description |
| | param.isRequired = param.anyOf[0].required |
| | } |
| | |
| | } else if (param.allOf) { |
| | for (const prop of param.allOf) { |
| | paramType.push('object') |
| | childParamsGroups.push(...(await getBodyParams(prop, false))) |
| | } |
| | } |
| |
|
| | const paramDecorated = await getTransformedParam(param, paramType, { |
| | paramKey, |
| | required, |
| | childParamsGroups, |
| | topLevel, |
| | }) |
| | bodyParametersParsed.push(paramDecorated) |
| | } |
| | return bodyParametersParsed |
| | } |
| |
|
| | async function getTransformedParam( |
| | param: Schema, |
| | paramType: string[], |
| | props: BodyParamProps, |
| | ): Promise<TransformedParam> { |
| | const { paramKey, required, childParamsGroups, topLevel } = props |
| | const paramDecorated: TransformedParam = {} as TransformedParam |
| | |
| | |
| | |
| | if (param.nullable) paramType.push('null') |
| | paramDecorated.type = Array.from(new Set(paramType.filter(Boolean))).join(' or ') |
| | paramDecorated.name = paramKey || '' |
| | if (topLevel) { |
| | paramDecorated.in = 'body' |
| | } |
| | paramDecorated.description = await renderContent(param.description || '') |
| | if (required && required.includes(paramKey || '')) { |
| | paramDecorated.isRequired = true |
| | } |
| | if (childParamsGroups && childParamsGroups.length > 0 && !param.oneOfObject) { |
| | |
| | |
| | const mergedChildParamsGroups = Array.from( |
| | childParamsGroups |
| | .reduce((childParam, obj) => { |
| | const curr = childParam.get(obj.name) |
| | return childParam.set( |
| | obj.name, |
| | curr ? (!Object.hasOwn(curr, 'isRequired') ? obj : curr) : obj, |
| | ) |
| | }, new Map<string, TransformedParam>()) |
| | .values(), |
| | ) |
| |
|
| | paramDecorated.childParamsGroups = mergedChildParamsGroups |
| | } else if (childParamsGroups && childParamsGroups.length > 0) { |
| | paramDecorated.childParamsGroups = childParamsGroups |
| | } |
| | if (param.enum) { |
| | paramDecorated.enum = param.enum |
| | } |
| |
|
| | if (param.oneOfObject) { |
| | paramDecorated.oneOfObject = true |
| | } |
| |
|
| | if (param.default !== undefined) { |
| | paramDecorated.default = param.default |
| | } |
| | return paramDecorated |
| | } |
| |
|
| | async function getOneOfChildParams(param: Schema): Promise<TransformedParam[]> { |
| | const childParamsGroups: TransformedParam[] = [] |
| | if (!param.oneOf) { |
| | return childParamsGroups |
| | } |
| | for (const oneOfParam of param.oneOf) { |
| | const objParam: TransformedParam = { |
| | type: 'object', |
| | name: oneOfParam.title, |
| | description: await renderContent(oneOfParam.description), |
| | isRequired: oneOfParam.required, |
| | childParamsGroups: [], |
| | } |
| | if (objParam.childParamsGroups) { |
| | objParam.childParamsGroups.push(...(await getBodyParams(oneOfParam, false))) |
| | } |
| | childParamsGroups.push(objParam) |
| | } |
| | return childParamsGroups |
| | } |
| |
|