From 394ffcc19a7e0a03ef3bf26f703bd579f6b9fa71 Mon Sep 17 00:00:00 2001 From: lxffong Date: Mon, 20 Jul 2026 00:20:41 +0800 Subject: [PATCH] fix(swagger): expand inline pointer members (#5664) Co-authored-by: kevin --- tools/goctl/api/swagger/example/example.api | 27 ++++++++++ tools/goctl/api/swagger/swagger.go | 2 + tools/goctl/api/swagger/swagger_test.go | 58 +++++++++++++++++++-- 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/tools/goctl/api/swagger/example/example.api b/tools/goctl/api/swagger/example/example.api index 2b8f00573..038ab1aad 100644 --- a/tools/goctl/api/swagger/example/example.api +++ b/tools/goctl/api/swagger/example/example.api @@ -103,6 +103,27 @@ type ( Language string `json:"language"` Gender string `json:"gender"` } + EmbeddedUser { + UserId int `json:"userId,example=10"` + Username string `json:"username,example=keson.an"` + } + EmbeddedAudit { + TraceId string `json:"traceId,example=trace-001"` + CreatedBy string `json:"createdBy,optional,example=system"` + } + EmbeddedProfile { + EmbeddedUser + *EmbeddedAudit + Nickname string `json:"nickname,optional,example=keson"` + } + EmbeddedJsonReq { + EmbeddedProfile + RequestId string `json:"requestId,example=req-001"` + } + EmbeddedJsonResp { + EmbeddedProfile + Success bool `json:"success,example=true"` + } ComplexJsonLevel2 { // basic Integer int `json:"integer,example=1"` @@ -238,4 +259,10 @@ service Swagger { ) @handler jsonComplex post /json/complex (ComplexJsonReq) returns (ComplexJsonResp) + + @doc ( + description: "embedded json request body API" + ) + @handler jsonEmbedded + post /json/embedded (EmbeddedJsonReq) returns (EmbeddedJsonResp) } diff --git a/tools/goctl/api/swagger/swagger.go b/tools/goctl/api/swagger/swagger.go index 106b041ef..23f367fc0 100644 --- a/tools/goctl/api/swagger/swagger.go +++ b/tools/goctl/api/swagger/swagger.go @@ -202,6 +202,8 @@ func expandMembers(ctx Context, tp apiSpec.Type) []apiSpec.Member { } members = append(members, v) } + case apiSpec.PointerType: + members = expandMembers(ctx, val.Type) } return members diff --git a/tools/goctl/api/swagger/swagger_test.go b/tools/goctl/api/swagger/swagger_test.go index 2493f175d..b755d8e6a 100644 --- a/tools/goctl/api/swagger/swagger_test.go +++ b/tools/goctl/api/swagger/swagger_test.go @@ -3,8 +3,8 @@ package swagger import ( "testing" - "github.com/zeromicro/go-zero/tools/goctl/api/spec" "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/tools/goctl/api/spec" ) func Test_pathVariable2SwaggerVariable(t *testing.T) { @@ -66,7 +66,7 @@ func TestArrayDefinitionsBug(t *testing.T) { // Verify the array field has correct structure assert.Equal(t, "array", arrayField.Type[0]) - + // Check that we have items assert.NotNil(t, arrayField.Items, "Array should have items defined") assert.NotNil(t, arrayField.Items.Schema, "Array items should have schema") @@ -74,7 +74,7 @@ func TestArrayDefinitionsBug(t *testing.T) { // The FIX: $ref should be inside items, not at schema level hasRef := arrayField.Ref.String() != "" assert.False(t, hasRef, "Schema level should NOT have $ref") - + // The $ref should be in the items hasItemsRef := arrayField.Items.Schema.Ref.String() != "" assert.True(t, hasItemsRef, "Items should have $ref") @@ -138,3 +138,55 @@ func TestArrayWithoutDefinitions(t *testing.T) { assert.Contains(t, arrayField.Items.Schema.Properties, "itemName") assert.Equal(t, []string{"itemName"}, arrayField.Items.Schema.Required) } + +func TestPropertiesFromTypeInlinePointerMembers(t *testing.T) { + ctx := testingContext(t) + + baseStruct := spec.DefineStruct{ + RawName: "EmbeddedUser", + Members: []spec.Member{ + { + Name: "UserId", + Type: spec.PrimitiveType{RawName: "int"}, + Tag: `json:"userId"`, + }, + }, + } + auditStruct := spec.DefineStruct{ + RawName: "EmbeddedAudit", + Members: []spec.Member{ + { + Name: "TraceId", + Type: spec.PrimitiveType{RawName: "string"}, + Tag: `json:"traceId"`, + }, + }, + } + testStruct := spec.DefineStruct{ + RawName: "EmbeddedProfile", + Members: []spec.Member{ + { + Type: baseStruct, + IsInline: true, + }, + { + Type: spec.PointerType{ + Type: auditStruct, + }, + IsInline: true, + }, + { + Name: "Nickname", + Type: spec.PrimitiveType{RawName: "string"}, + Tag: `json:"nickname,optional"`, + }, + }, + } + + properties, required := propertiesFromType(ctx, testStruct) + + assert.Contains(t, properties, "userId") + assert.Contains(t, properties, "traceId") + assert.Contains(t, properties, "nickname") + assert.ElementsMatch(t, []string{"userId", "traceId"}, required) +}