Skip to content

Add subschemas#22

Open
pickfire wants to merge 1 commit intohigress-group:mainfrom
pickfire:subschemas
Open

Add subschemas#22
pickfire wants to merge 1 commit intohigress-group:mainfrom
pickfire:subschemas

Conversation

@pickfire
Copy link

Description

Add support for anyOf, allOf, oneOf, if all the subschemas within is the same type, it should follow that type.

Type

  • 🐛 Bug fix
  • ✨ New feature
  • 📚 Documentation update
  • 🎨 Code refactoring
  • 🧪 Test updates
  • 🔧 Build/tooling updates

Checklist

  • My code follows the project's coding standards
  • I have added necessary tests
  • All tests are passing
  • I have updated relevant documentation
  • My changes do not break existing functionality

Testing

Please describe how you tested these changes:

  • Unit tests
  • Integration tests
  • Manual testing

Related Issues

If applicable, link to related issues:

  • Closes #
  • Related #

Additional Information

If there is any other information that should be included, please add it here.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@lingma-agents
Copy link

lingma-agents bot commented Oct 28, 2025

支持 OpenAPI 中的 allOf、anyOf 和 oneOf 子模式处理

变更概述
  • 新功能

    • 在请求体转换逻辑中新增对 allOfanyOfoneOf 子模式的支持。
    • 当所有子模式类型相同时,将其统一为该类型;当使用 allOf 并且只有一个引用时,会进一步解析其属性结构。
    • 添加了辅助函数 hasCommonType 来判断一组 Schema 引用是否具有相同的类型。
  • 测试更新

    • 新增针对子模式(subschemas)处理的端到端测试案例。
    • 包含一个完整的 OpenAPI JSON 示例文件 (test/subschemas.json),用于验证各种组合方式(allOf, anyOf, oneOf)的行为。
    • 同时提供了预期输出 YAML 文件 (test/expected-subschemas-mcp.yaml) 作为断言依据。
  • 其他

    • 调整部分调试打印语句(如:fmt.Printf),可能需要后续清理或替换为标准日志记录机制。
变更文件
文件路径 变更说明
pkg/converter/converter.go 在处理请求体参数时增加了对 allOf、anyOf 和 oneOf 的支持,并实现了一个辅助方法来检测多个 schema 是否拥有共同的类型。
pkg/​converter/​converter_​test.​go 扩展了测试套件以包含新的子模式测试场景。
test/​expected-subschemas-mcp.​yaml 定义了期望生成的 MCP 工具定义内容,确保 allOf、anyOf 和 oneOf 被正确映射成对象类型的参数。
test/subschemas.json 创建了一个专门用于测试不同组合模式(allOf, anyOf, oneOf)的 OpenAPI 规范文档。

💡 小贴士

与 lingma-agents 交流的方式

📜 直接回复评论
直接回复本条评论,lingma-agents 将自动处理您的请求。例如:

  • 在当前代码中添加详细的注释说明。

  • 请详细介绍一下你说的 LRU 改造方案,并使用伪代码加以说明。

📜 在代码行处标记
在文件的特定位置创建评论并 @lingma-agents。例如:

  • @lingma-agents 分析这个方法的性能瓶颈并提供优化建议。

  • @lingma-agents 对这个方法生成优化代码。

📜 在讨论中提问
在任何讨论中 @lingma-agents 来获取帮助。例如:

  • @lingma-agents 请总结上述讨论并提出解决方案。

  • @lingma-agents 请根据讨论内容生成优化代码。

Copy link

@lingma-agents lingma-agents bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔎 代码评审报告

🎯 评审意见概览
严重度 数量 说明
🔴 Blocker 0 阻断性问题,需立即修复。例如:系统崩溃、关键功能不可用或严重安全漏洞。
🟠 Critical 1 严重问题,高优先级修复。例如:核心功能异常或性能瓶颈影响用户体验。
🟡 Major 1 主要问题,建议修复。例如:非核心功能缺陷或代码维护性较差。
🟢 Minor 1 次要问题,酬情优化。例如:代码格式不规范或注释缺失。

总计: 3 个问题

📋 评审意见详情
💡 代码实现建议
以下是文件级别的代码建议,聚焦于代码的可读性、可维护性和潜在问题。
🔹 pkg/converter/converter.go (3 💬)
🚀 架构设计建议
以下是对代码架构和设计的综合分析,聚焦于跨文件交互、系统一致性和潜在优化空间。
🔍1. 处理 allOf、anyOf 和 oneOf 的逻辑一致性问题

当前对 allOf、anyOf 和 oneOf 的处理方式存在不一致性和潜在错误。例如,在 allOf 处理中如果只有一个元素,则直接将其视为对象并调用 allOfHandle;而在 anyOf 和 oneOf 中则使用 hasCommonType 来决定类型。这种差异可能导致行为不符合预期,并且可能引入业务逻辑上的不一致。建议统一这些组合模式的处理策略,确保它们的行为符合 OpenAPI 规范。

📌 关键代码

if propRef.Value.Type == "" {
						// Handle allOf, anyOf, oneOf
						if len(propRef.Value.AllOf) == 1 {
							arg.Type = "object"
							arg.Properties = c.allOfHandle(propRef.Value.AllOf[0])
						} else if c.hasCommonType(propRef.Value.AllOf) {
							arg.Type = propRef.Value.AllOf[0].Value.Type
						}
						if c.hasCommonType(propRef.Value.AnyOf) {
							arg.Type = propRef.Value.AnyOf[0].Value.Type
						}
						if c.hasCommonType(propRef.Value.OneOf) {
							arg.Type = propRef.Value.OneOf[0].Value.Type
							fmt.Printf("%d %#v\n", len(propRef.Value.OneOf), propRef.Value.OneOf[0])
						}
					}

⚠️ 潜在风险

可能导致生成的请求模板与实际的 OpenAPI 定义不符,影响后续工具链或服务端的正确性验证。

🔍2. hasCommonType 函数实现过于简化

hasCommonType 函数目前只检查所有 schema 是否具有相同的 type 字段,但没有考虑 format、enum 等其他关键属性的一致性。这可能会导致即使类型相同,但由于格式不同而产生错误的结果。应该增强该函数以全面评估 schema 兼容性。

📌 关键代码

func (c *Converter) hasCommonType(schemaRefs []*openapi3.SchemaRef) bool {
	if len(schemaRefs) == 0 {
		return false
	}
	first := schemaRefs[0].Value.Type
	for _, schemaRef := range schemaRefs {
		if first != schemaRef.Value.Type {
			return false
		}
	}
	return true
}

⚠️ 潜在风险

在面对复杂的 schema 组合时(如包含多种 format 或 enum 值),此方法会误判 schema 相似度,从而引发数据结构解析错误。

🔍3. 调试信息未清理

代码中保留了用于调试目的的 fmt.Printf 输出语句,这对于生产环境是不合适的安全隐患和性能负担。需要彻底删除这类临时打印内容。

📌 关键代码

fmt.Printf("%d %#v\n", len(propRef.Value.OneOf), propRef.Value.OneOf[0])

⚠️ 潜在风险

泄露内部状态信息给用户或日志系统,增加安全风险及不必要的 I/O 开销。

🔍4. 测试覆盖范围有限

虽然新增了一个针对 subschema 的测试案例,但仅通过一个简单的 JSON 文件来验证 allOf/anyOf/oneOf 的转换逻辑显然不够充分。应当扩展更多边界条件和复杂嵌套场景的单元测试,以保证各种情况下的稳定性和准确性。

📌 关键代码

{
			name:           "Handle Subschemas",
			inputFile:      "../../test/subschemas.json",
			expectedOutput: "../../test/expected-subschemas-mcp.yaml",
			serverName:     "openapi-server",
		},

⚠️ 潜在风险

缺乏足够的测试会导致难以发现深层次的兼容性问题,降低系统的可靠性。

审查详情
📒 文件清单 (4 个文件)
新增: 2 个文件
📝 变更: 2 个文件

✅ 新增文件:

  • test/expected-subschemas-mcp.yaml
  • test/subschemas.json

📝 变更文件:

  • pkg/converter/converter.go
  • pkg/converter/converter_test.go

💡 小贴士

与 lingma-agents 交流的方式

📜 直接回复评论
直接回复本条评论,lingma-agents 将自动处理您的请求。例如:

  • 在当前代码中添加详细的注释说明。

  • 请详细介绍一下你说的 LRU 改造方案,并使用伪代码加以说明。

📜 在代码行处标记
在文件的特定位置创建评论并 @lingma-agents。例如:

  • @lingma-agents 分析这个方法的性能瓶颈并提供优化建议。

  • @lingma-agents 对这个方法生成优化代码。

📜 在讨论中提问
在任何讨论中 @lingma-agents 来获取帮助。例如:

  • @lingma-agents 请总结上述讨论并提出解决方案。

  • @lingma-agents 请根据讨论内容生成优化代码。

Comment on lines +425 to +439
if propRef.Value.Type == "" {
// Handle allOf, anyOf, oneOf
if len(propRef.Value.AllOf) == 1 {
arg.Type = "object"
arg.Properties = c.allOfHandle(propRef.Value.AllOf[0])
} else if c.hasCommonType(propRef.Value.AllOf) {
arg.Type = propRef.Value.AllOf[0].Value.Type
}
if c.hasCommonType(propRef.Value.AnyOf) {
arg.Type = propRef.Value.AnyOf[0].Value.Type
}
if c.hasCommonType(propRef.Value.OneOf) {
arg.Type = propRef.Value.OneOf[0].Value.Type
fmt.Printf("%d %#v\n", len(propRef.Value.OneOf), propRef.Value.OneOf[0])
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

处理 allOf、anyOf 和 oneOf 时,应确保正确解析和合并其子模式,而不仅仅是依赖第一个模式的类型。

🟠 Critical | 🐞 Bugs

📋 问题详情

当前代码在处理 allOfanyOfoneOf 时,仅检查了第一个模式的类型,并未真正合并或解析这些组合模式。这可能导致生成的参数结构不完整或不正确,尤其是在 allOf 需要合并多个模式的情况下。

💡 解决方案

当前处理 allOf, anyOf, oneOf 的逻辑过于简化,仅依赖第一个元素的类型,未真正合并或解析这些组合。建议重构为更完整的处理方式,例如对 allOf 合并多个 schema,对 anyOf/oneOf 生成合适的联合类型结构。

- if propRef.Value.Type == "" {
-     // Handle allOf, anyOf, oneOf
-     if len(propRef.Value.AllOf) == 1 {
-         arg.Type = "object"
-         arg.Properties = c.allOfHandle(propRef.Value.AllOf[0])
-     } else if c.hasCommonType(propRef.Value.AllOf) {
-         arg.Type = propRef.Value.AllOf[0].Value.Type
-     }
-     if c.hasCommonType(propRef.Value.AnyOf) {
-         arg.Type = propRef.Value.AnyOf[0].Value.Type
-     }
-     if c.hasCommonType(propRef.Value.OneOf) {
-         arg.Type = propRef.Value.OneOf[0].Value.Type
-         fmt.Printf("%d %#v\n", len(propRef.Value.OneOf), propRef.Value.OneOf[0])
-     }
- }
+ if propRef.Value.Type == "" {
+     // Handle allOf, anyOf, oneOf
+     if len(propRef.Value.AllOf) > 0 {
+         arg.Type = "object"
+         arg.Properties = c.mergeAllOfSchemas(propRef.Value.AllOf)
+     } else if len(propRef.Value.AnyOf) > 0 {
+         arg.Type = "object" // 或其他合适类型
+         arg.AnyOf = c.convertSchemaRefs(propRef.Value.AnyOf)
+     } else if len(propRef.Value.OneOf) > 0 {
+         arg.Type = "object" // 或其他合适类型
+         arg.OneOf = c.convertSchemaRefs(propRef.Value.OneOf)
+     }
+ }

您的反馈对我们很重要!(建议右键在新标签页中打开以下链接)

有用意见👍无用意见👎错误意见❌

}
if c.hasCommonType(propRef.Value.OneOf) {
arg.Type = propRef.Value.OneOf[0].Value.Type
fmt.Printf("%d %#v\n", len(propRef.Value.OneOf), propRef.Value.OneOf[0])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

调试用的 fmt.Printf 应在生产代码中移除。

🟢 Minor | 🧹 Code Smells

📋 问题详情

在处理 oneOf 的代码中,有一行调试用的 fmt.Printf 输出,这在生产环境中会引入不必要的日志输出,可能影响性能或干扰日志系统。

💡 解决方案

移除调试输出语句,避免在生产环境中产生无用日志。

- fmt.Printf("%d %#v\\n", len(propRef.Value.OneOf), propRef.Value.OneOf[0])

您的反馈对我们很重要!(建议右键在新标签页中打开以下链接)

有用意见👍无用意见👎错误意见❌

Comment on lines +473 to +484
func (c *Converter) hasCommonType(schemaRefs []*openapi3.SchemaRef) bool {
if len(schemaRefs) == 0 {
return false
}
first := schemaRefs[0].Value.Type
for _, schemaRef := range schemaRefs {
if first != schemaRef.Value.Type {
return false
}
}
return true
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

新增的 hasCommonType 函数逻辑不完整,仅比较类型是否一致,未考虑其他属性如格式、枚举等。

🟡 Major | 🐞 Bugs

📋 问题详情

该函数用于判断一组 schema 是否具有相同的类型,但实际在 OpenAPI 规范中,即使类型相同,格式(如 string + email)或枚举值不同也会导致语义不同。当前实现可能误判,导致错误的类型合并。

💡 解决方案

建议增强 hasCommonType 函数,不仅比较 Type,还应考虑 FormatEnum 等关键属性,确保语义一致性。

- func (c *Converter) hasCommonType(schemaRefs []*openapi3.SchemaRef) bool {
-     if len(schemaRefs) == 0 {
-         return false
-     }
-     first := schemaRefs[0].Value.Type
-     for _, schemaRef := range schemaRefs {
-         if first != schemaRef.Value.Type {
-             return false
-         }
-     }
-     return true
- }
+ func (c *Converter) hasCommonType(schemaRefs []*openapi3.SchemaRef) bool {
+     if len(schemaRefs) == 0 {
+         return false
+     }
+     first := schemaRefs[0].Value
+     for _, schemaRef := range schemaRefs {
+         if first.Type != schemaRef.Value.Type ||
+            first.Format != schemaRef.Value.Format ||
+            !reflect.DeepEqual(first.Enum, schemaRef.Value.Enum) {
+             return false
+         }
+     }
+     return true
+ }

您的反馈对我们很重要!(建议右键在新标签页中打开以下链接)

有用意见👍无用意见👎错误意见❌

@johnlanni
Copy link
Contributor

Could you describe what issue this is addressing? Can you also create an issue to describe the behavior before this change?

@pickfire
Copy link
Author

I added it in #24

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants