Skip to content

Commit 6db56ac

Browse files
committed
feat: enhance method parsing and request compilation with improved response schema extraction, recursive required marking, and optimized mock template generation
1 parent 622488f commit 6db56ac

3 files changed

Lines changed: 160 additions & 172 deletions

File tree

packages/parser/src/parses/method.ts

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -97,52 +97,76 @@ export function parseMethodParameters({ method, parameters, path }: PathMethod,
9797
export function parseMethodMetadata({ method, path, responses, options: meta }: PathMethod) {
9898
const { configRead, interfaces } = inject()
9999
const metaAny = meta as { consumes?: string[] }
100+
101+
// 1. 结构化注释生成
100102
const comments = [
101103
meta.summary && `@summary ${meta.summary}`,
102104
meta.description && `@description ${meta.description}`,
103105
`@method ${method}`,
104-
meta.tags?.length ? `@tags ${meta.tags.join(' | ') || '-'}` : undefined,
105-
metaAny.consumes?.length ? `@consumes ${metaAny.consumes.join('; ') || '-'}` : undefined,
106-
].filter((c): c is string => typeof c === 'string')
106+
meta.tags?.length && `@tags ${meta.tags.join(' | ')}`,
107+
metaAny.consumes?.length && `@consumes ${metaAny.consumes.join('; ')}`,
108+
].filter((c): c is string => !!c)
107109

110+
// 2. 路径处理
108111
let name = camelCase(`${method}/${path}`)
109-
110-
const url = `${path.replace(/(\{)/g, '${paths.')}`
111-
function hasContent(r: unknown): r is { content?: Record<string, { schema?: unknown }> } {
112-
return r != null && typeof r === 'object' && 'content' in r
112+
const url = path.replace(/\{/g, '${paths.')
113+
114+
// 3. 响应 Schema 提取逻辑 (解耦与简化)
115+
const getResponseSchema = () => {
116+
const res200 = responses['200']
117+
const resDefault = responses.default
118+
119+
// 优先尝试从 content/application/json 获取 (OpenAPI 3.0)
120+
const getContentSchema = (res: any) => res?.content?.['application/json']?.schema
121+
const schemaFromContent = getContentSchema(res200) ?? getContentSchema(resDefault)
122+
if (schemaFromContent)
123+
return schemaFromContent
124+
125+
// 兜底从根节点获取 (Swagger 2.0)
126+
if (res200 && 'schema' in res200)
127+
return (res200 as any).schema
128+
return null
113129
}
114-
const resDefault = responses.default && hasContent(responses.default) ? responses.default : null
115-
const res200 = responses['200'] && typeof responses['200'] === 'object' ? responses['200'] : null
116-
const contentDefault = resDefault?.content?.['application/json']
117-
const content200 = res200 && hasContent(res200) ? res200.content?.['application/json'] : null
118-
const schemaFromContent = (contentDefault && typeof contentDefault === 'object' && 'schema' in contentDefault ? (contentDefault as { schema: unknown }).schema : null)
119-
?? (content200 && typeof content200 === 'object' && 'schema' in content200 ? (content200 as { schema: unknown }).schema : null)
120-
const schemaFromRes200 = res200 && typeof res200 === 'object' && 'schema' in res200 && !('content' in res200) ? (res200 as { schema: unknown }).schema : null
121-
const responseSchema = schemaFromContent ?? schemaFromRes200
122-
let responseType = responseSchema && typeof responseSchema === 'object' ? parseSchemaType(responseSchema as Parameters<typeof parseSchemaType>[0]) : 'void'
123-
124-
if (configRead.config.responseRequired)
125-
deepSignRequired(interfaces.find(v => v.name === responseType)?.properties || [])
126-
127-
function deepSignRequired(properties: StatementField[]) {
128-
for (const property of properties) {
129-
property.required = true
130-
131-
for (const { properties } of interfaces.filter(v => v.name === property.type))
132-
deepSignRequired(properties || [])
130+
131+
const responseSchema = getResponseSchema()
132+
let responseType = responseSchema ? parseSchemaType(responseSchema as any) : 'void'
133+
134+
// 4. 强制必填标记逻辑 (递归优化)
135+
if (configRead.config.responseRequired && responseType !== 'void') {
136+
const processedTypes = new Set<string>() // 防止循环引用导致死循环
137+
const markRequiredRecursive = (typeName: string) => {
138+
if (processedTypes.has(typeName))
139+
return
140+
processedTypes.add(typeName)
141+
const targetInterface = interfaces.find(v => v.name === typeName)
142+
targetInterface?.properties?.forEach((prop) => {
143+
prop.required = true
144+
if (prop.type)
145+
markRequiredRecursive(prop.type)
146+
})
133147
}
148+
markRequiredRecursive(responseType)
134149
}
135150

151+
// 5. 转换与注入
136152
const config = inject(`${method}/${path}`)
137-
;({ name, responseType } = transformOperation({
153+
const transformed = transformOperation({
138154
configRead,
139155
name,
140156
parameters: config?.parameters,
141157
responseType,
142-
}))
143-
// Inject function return type into named context (function name)
144-
// Referenced at:
145-
// - packages/pipeline/src/compiler/request.ts:172 (inject(item.name) gets returnType)
146-
provide(name, { returnType: responseType })
147-
return { description: comments, name, url, responseType, body: [] as string[] }
158+
})
159+
160+
name = transformed.name
161+
responseType = transformed.responseType
162+
163+
provide(name, { responseType })
164+
165+
return {
166+
description: comments,
167+
name,
168+
url,
169+
responseType,
170+
body: [] as string[],
171+
}
148172
}

0 commit comments

Comments
 (0)