Skip to content

Commit 1b40ae6

Browse files
authored
feat: add swagger validation dto reuse (#4510)
* feat(swagger): reuse validation dto metadata for schema inference * chore(openspec): mark swagger validation dto tasks complete * feat(swagger): infer validate rules for schema metadata * fix(swagger): resolve lint errors and sync i18n docs
1 parent 9a38b66 commit 1b40ae6

15 files changed

Lines changed: 1271 additions & 7 deletions

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
## Context
2+
Midway 已有两条并行能力链:
3+
- 文档链:`@midwayjs/swagger` 通过 `@ApiProperty` 元数据构造 OpenAPI Schema。
4+
- 校验链:`@midwayjs/validation`(多 validator)与 `@midwayjs/validate`(Joi)通过 DTO 元数据执行运行时校验。
5+
6+
当前 Swagger 的 DTO 解析只消费 swagger 装饰器元数据,导致校验 DTO 无法直接复用为文档 DTO。
7+
8+
## Goals / Non-Goals
9+
- Goals:
10+
- 在不破坏现有 Swagger 行为的前提下,复用 Validation DTO 元数据生成 schema。
11+
-`@midwayjs/validation` 的 joi、zod、class-validator adapter 下提供一致的“最低保证”推导语义。
12+
- 将 DTO 字段的 required/optional 从校验规则推导到 OpenAPI required。
13+
- 给出稳定优先级与回退策略,确保结果可预测。
14+
- Non-Goals:
15+
- 不在本变更中实现完整的任意校验规则到 OpenAPI 的 1:1 语义映射。
16+
- 不改变 Validation/Validate 组件本身的校验行为。
17+
- 不在首期纳入 `@midwayjs/validate`(Joi 独立组件)的复用支持。
18+
- 不处理与 DTO 无关的路由注解(如 security/response)自动推导。
19+
20+
## Decisions
21+
- Decision: 采用“swagger 显式优先,validation 推导兜底”的合并策略。
22+
- Rationale: 保护现有用户对文档精细控制能力,避免推导结果覆盖手工文档。
23+
- Decision: 通过统一抽象层读取 validator 的 schema/required 能力,而不是在 swagger 中硬编码某个 validator 实现。
24+
- Rationale: `@midwayjs/validation` 已提供 `schemaHelper` 能力,便于统一接入与扩展。
25+
- Decision: 将多 adapter 行为定义为“共同子集优先”,对 adapter 特有高级语义不做强制统一。
26+
- Rationale: joi/zod/class-validator 表达能力与元数据结构差异明显,先保证一致性最小集更稳妥。
27+
- Decision: 增加配置开关(建议为 `swagger.useValidationSchema`),默认安全值保持当前体验。
28+
- Rationale: 该变更可能影响输出 schema,需可渐进启用。
29+
30+
## Mapping Strategy (High-level)
31+
- 字段来源:
32+
- 如果字段有 `@ApiProperty`,按现有逻辑直接使用。
33+
- 如果无 `@ApiProperty`,尝试从 validation schema 推导字段类型与 required。
34+
- required 规则:
35+
- 优先读 swagger `required`
36+
- 否则使用 validator 的 `isRequired/isOptional` 推导。
37+
- 类型映射:
38+
- 仅承诺基础类型和常见对象/数组推导;复杂场景回退到 `object` 或保持空 schema。
39+
- 在 joi/zod/class-validator 三类 adapter 下,以上映射语义保持一致;无法一致映射的能力标记为降级项。
40+
41+
## Risks / Trade-offs
42+
- 风险: 各 validator(Joi/Zod/class-validator)可表达能力差异大,推导精度不一致。
43+
- Mitigation: 先定义“最低保证映射”,超出范围的规则显式降级。
44+
- 风险: 开启后可能导致 schema 与历史快照不同。
45+
- Mitigation: 提供配置开关、补充快照测试与迁移说明。
46+
- 风险: Swagger 解析阶段可能增加计算开销。
47+
- Mitigation: 以 DTO 级缓存复用推导结果,避免重复计算。
48+
49+
## Migration Plan
50+
1. 引入开关并默认安全行为。
51+
2. 增加测试覆盖:纯 `@ApiProperty`、纯 validation DTO、混合 DTO、边界回退。
52+
3. 文档新增“复用 DTO”章节,说明优先级、限制和推荐实践。
53+
54+
## Open Questions
55+
- 开关默认值是否应在下一个大版本再切为开启。
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Change: 复用 Validation DTO 元数据生成 Swagger Schema
2+
3+
## Why
4+
当前在 Midway 中,参数校验 DTO 与 Swagger 文档 DTO 通常需要重复标注两套装饰器:`@Rule`/class-validator 用于校验,`@ApiProperty` 用于文档。重复定义导致维护成本高、字段漂移风险高,且会降低 DTO 作为单一事实来源(SSOT)的价值。
5+
6+
## What Changes
7+
- 为 Swagger 增加“从 Validation DTO 元数据推导 Schema”的能力,在 DTO 字段缺少 `@ApiProperty` 时自动补齐文档描述。
8+
- 该能力 SHALL 面向 `@midwayjs/validation` 的多 adapter 模型(joi、zod、class-validator),并通过统一 schemaHelper 能力读取元数据。
9+
- 首期范围优先支持 `@midwayjs/validation``@midwayjs/validate` 不纳入本次交付范围。
10+
- 明确元数据优先级:`@ApiProperty` 显式定义优先,Validation 推导作为兜底。
11+
- 规范必填/可选推导规则(required 列表)与基础类型映射(string/number/boolean/object/array)。
12+
- 定义不支持或降级场景(复杂联合类型、自定义转换器、无法静态推导的规则)及回退策略。
13+
- 增加配置开关,支持逐步启用,避免对现有项目文档输出造成不可控漂移。
14+
15+
## Impact
16+
- Affected specs: `swagger-validation-dto-reuse`(新增)
17+
- Affected code:
18+
- `packages/swagger/src/swaggerExplorer.ts`(DTO 属性解析与 schema 组装)
19+
- `packages/swagger/src/config/*``packages/swagger/src/interfaces/*`(配置与类型声明)
20+
- `packages/validation*/``packages/validate/`(仅消费公开 schemaHelper 能力,不修改其校验语义)
21+
- `packages/swagger/test/*`(新增/更新解析与集成测试)
22+
- Compatibility:
23+
- 默认保持向后兼容(不开启或按安全默认策略运行)
24+
- 已声明的 `@ApiProperty` 行为不变
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
## ADDED Requirements
2+
### Requirement: Swagger SHALL Reuse Validation DTO Metadata
3+
系统 SHALL 在 DTO 字段未显式声明 `@ApiProperty` 元数据时,尝试复用 Validation DTO 元数据生成 OpenAPI schema 字段。
4+
5+
#### Scenario: DTO 仅定义 validation 规则
6+
- **GIVEN** 一个用于 `@Body``@Query` 的 DTO 仅定义 validation 元数据且未使用 `@ApiProperty`
7+
- **WHEN** Swagger 生成 OpenAPI 文档
8+
- **THEN** DTO 对应 schema SHALL 包含可推导的字段定义
9+
- **AND** 文档生成过程不应因无法推导的单个字段失败
10+
11+
### Requirement: Swagger SHALL Support Validation Adapters With Consistent Baseline
12+
系统 SHALL 支持 `@midwayjs/validation` 的 joi、zod、class-validator adapter,并在共同能力范围内输出一致语义的 OpenAPI schema。
13+
14+
#### Scenario: 不同 adapter 下的基础字段推导一致
15+
- **GIVEN** 三个 DTO 分别使用 joi、zod、class-validator 描述等价的基础字段约束
16+
- **WHEN** Swagger 生成 OpenAPI 文档
17+
- **THEN** 三者在 type 与 required 语义上的结果 SHALL 保持一致
18+
- **AND** adapter 特有且无法统一映射的约束 SHALL 按降级策略处理
19+
20+
### Requirement: Swagger SHALL Preserve ApiProperty Priority
21+
系统 SHALL 保持 `@ApiProperty` 的显式配置优先于 validation 推导结果。
22+
23+
#### Scenario: DTO 同时存在 ApiProperty 与 validation 元数据
24+
- **GIVEN** DTO 某字段同时定义 `@ApiProperty` 和 validation 规则
25+
- **WHEN** Swagger 生成该字段 schema
26+
- **THEN** 字段类型、格式、示例、描述与 required 语义 SHALL 优先采用 `@ApiProperty` 明确值
27+
- **AND** validation 推导仅用于补齐 `@ApiProperty` 未定义的信息
28+
29+
### Requirement: Swagger SHALL Derive Required Semantics From Validation Metadata
30+
系统 SHALL 在缺少显式 `@ApiProperty.required` 时,从 validation 元数据推导 required/optional 语义。
31+
32+
#### Scenario: validation 标记字段为必填
33+
- **GIVEN** DTO 字段在 validation 元数据中被标记为必填
34+
- **WHEN** Swagger 生成该 DTO schema
35+
- **THEN** 该字段名 SHALL 出现在 schema.required 列表中
36+
37+
#### Scenario: validation 标记字段为可选
38+
- **GIVEN** DTO 字段在 validation 元数据中被标记为可选
39+
- **WHEN** Swagger 生成该 DTO schema
40+
- **THEN** 该字段名 SHALL 不出现在 schema.required 列表中
41+
42+
### Requirement: Swagger SHALL Provide Safe Fallback For Unmappable Rules
43+
系统 SHALL 对无法稳定映射到 OpenAPI 的 validation 规则执行安全降级,并保持文档输出可用。
44+
45+
#### Scenario: 复杂规则无法静态映射
46+
- **GIVEN** DTO 字段使用复杂联合或自定义校验规则
47+
- **WHEN** Swagger 无法稳定映射该规则
48+
- **THEN** 系统 SHALL 回退为最小可用 schema(如 `object` 或无额外约束)
49+
- **AND** 其余字段和路由文档生成 SHALL 正常完成
50+
51+
### Requirement: Swagger SHALL Offer Configurable Enablement For Validation Reuse
52+
系统 SHALL 提供配置项控制是否启用 validation DTO 到 Swagger schema 的复用能力。
53+
54+
#### Scenario: 关闭复用开关
55+
- **GIVEN** 应用配置关闭 validation DTO 复用
56+
- **WHEN** Swagger 生成文档
57+
- **THEN** 系统 SHALL 保持现有行为,仅依据 Swagger 装饰器元数据生成 DTO schema
58+
59+
#### Scenario: 开启复用开关
60+
- **GIVEN** 应用配置开启 validation DTO 复用
61+
- **WHEN** Swagger 生成文档
62+
- **THEN** 系统 SHALL 对缺省字段应用 validation 元数据推导
63+
- **AND** 不应破坏已有 `@ApiProperty` 输出
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## 1. Specification
2+
- [x] 1.1 明确 Swagger 与 Validation DTO 元数据合并优先级与降级规则
3+
- [x] 1.2 明确首期支持范围(仅 `@midwayjs/validation` 的 joi/zod/class-validator 共同子集:基础类型、required 推导、复杂规则降级)
4+
- [x] 1.3 明确配置开关与默认行为(兼容策略)
5+
6+
## 2. Implementation
7+
- [x] 2.1 在 `@midwayjs/swagger` 增加 validation DTO schema 推导入口(仅字段缺省时补齐)
8+
- [x] 2.2 实现 required/optional 推导并与现有 `@ApiProperty` 逻辑合并
9+
- [x] 2.3 增加 DTO 推导缓存,避免重复解析
10+
- [x] 2.4 增加配置项与类型声明,并接入组件默认配置
11+
12+
## 3. Validation & Tests
13+
- [x] 3.1 为纯 validation DTO 场景增加 parser 单测(joi/zod/class-validator 各 1 组)
14+
- [x] 3.2 为 `@ApiProperty` + validation 混用场景增加优先级单测
15+
- [x] 3.3 为复杂规则降级场景增加回退单测
16+
- [x] 3.4 运行 `pnpm -C packages/swagger test` 并确保通过
17+
18+
## 4. Documentation
19+
- [x] 4.1 更新 `site/docs/extensions/swagger.md`,新增“复用 validation DTO”章节
20+
- [x] 4.2 记录已支持映射、限制项和迁移建议

packages/swagger/src/config/config.default.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ export const swagger: SwaggerOptions = {
1414
: renderSwaggerUIRemote,
1515
swaggerUIRenderOptions: {},
1616
isGenerateTagForController: true,
17+
useValidationSchema: true,
1718
};

packages/swagger/src/interfaces/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,12 @@ export interface SwaggerOptions {
449449
* Weather to generate the Tag for controller
450450
*/
451451
isGenerateTagForController?: boolean;
452+
/**
453+
* Enable inferring schema metadata from @midwayjs/validation DTO rules.
454+
* When enabled, inferred metadata only fills fields that are not explicitly
455+
* defined by @ApiProperty.
456+
*/
457+
useValidationSchema?: boolean;
452458
}
453459

454460
export interface MixDecoratorMetadata {

0 commit comments

Comments
 (0)