From f2d014e2adf159e0fcdbb2f8f93ee53b4aabcc87 Mon Sep 17 00:00:00 2001 From: Louis Duchemin Date: Sun, 28 Apr 2024 14:22:28 +0200 Subject: [PATCH 1/2] Allow providing name hints using tags for anonymous body structs --- huma.go | 13 ++++++++++--- huma_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/huma.go b/huma.go index 20aaa9c0..f6b14561 100644 --- a/huma.go +++ b/huma.go @@ -582,8 +582,11 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I) if c := f.Tag.Get("contentType"); c != "" { contentType = c } - - s := SchemaFromField(registry, f, getHint(inputType, f.Name, op.OperationID+"Request")) + hint := getHint(inputType, f.Name, op.OperationID+"Request") + if nameHint := f.Tag.Get("nameHint"); nameHint != "" { + hint = nameHint + } + s := SchemaFromField(registry, f, hint) op.RequestBody = &RequestBody{ Required: required, @@ -708,7 +711,11 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I) op.Responses[statusStr].Headers = map[string]*Param{} } if !outBodyFunc { - outSchema := SchemaFromField(registry, f, getHint(outputType, f.Name, op.OperationID+"Response")) + hint := getHint(outputType, f.Name, op.OperationID+"Response") + if nameHint := f.Tag.Get("nameHint"); nameHint != "" { + hint = nameHint + } + outSchema := SchemaFromField(registry, f, hint) if op.Responses[statusStr].Content == nil { op.Responses[statusStr].Content = map[string]*MediaType{} } diff --git a/huma_test.go b/huma_test.go index 5aa6364b..9521e143 100644 --- a/huma_test.go +++ b/huma_test.go @@ -605,6 +605,32 @@ func TestFeatures(t *testing.T) { assert.Equal(t, http.StatusBadRequest, resp.Code) }, }, + { + Name: "request-body-nameHint", + Register: func(t *testing.T, api huma.API) { + huma.Register(api, huma.Operation{ + Method: http.MethodPut, + Path: "/body", + }, func(ctx context.Context, input *struct { + Body struct { + Name string `json:"name"` + } `nameHint:"ANameHint"` + }) (*struct{}, error) { + return nil, nil + }) + + registry := api.OpenAPI().Components.Schemas + origType := registry.TypeFromRef( + api.OpenAPI().Paths["/body"].Put.RequestBody.Content["application/json"].Schema.Ref, + ) + assert.Equal(t, "ANameHint", + huma.DefaultSchemaNamer(origType, "ANameHint"), + ) + }, + Method: http.MethodPut, + URL: "/body", + Body: `{"name": "Name"}`, + }, { Name: "request-ptr-body-required", Register: func(t *testing.T, api huma.API) { @@ -1009,6 +1035,34 @@ Content of example2.txt. assert.Equal(t, "application/custom-type", resp.Header().Get("Content-Type")) }, }, + { + Name: "response-body-nameHint", + Register: func(t *testing.T, api huma.API) { + type Resp struct { + Body struct { + Greeting string `json:"greeting" ` + } `nameHint:"GreetingResp"` + } + huma.Register(api, huma.Operation{ + Method: http.MethodGet, + Path: "/response", + }, func(ctx context.Context, input *struct{}) (*Resp, error) { + resp := &Resp{} + resp.Body.Greeting = "Hello, world!" + return resp, nil + }) + + registry := api.OpenAPI().Components.Schemas + origType := registry.TypeFromRef( + api.OpenAPI().Paths["/response"].Get.Responses["200"].Content["application/json"].Schema.Ref, + ) + assert.Equal(t, "GreetingResp", + huma.DefaultSchemaNamer(origType, "GreetingResp"), + ) + }, + Method: http.MethodGet, + URL: "/response", + }, { Name: "response", Register: func(t *testing.T, api huma.API) { From 831b1db72c71fafba2309a81fbb4e4cc8318ba0f Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Sun, 28 Apr 2024 08:25:20 -0700 Subject: [PATCH 2/2] fix: simplify tests --- huma_test.go | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/huma_test.go b/huma_test.go index 9521e143..bd2a6ee4 100644 --- a/huma_test.go +++ b/huma_test.go @@ -618,14 +618,7 @@ func TestFeatures(t *testing.T) { }) (*struct{}, error) { return nil, nil }) - - registry := api.OpenAPI().Components.Schemas - origType := registry.TypeFromRef( - api.OpenAPI().Paths["/body"].Put.RequestBody.Content["application/json"].Schema.Ref, - ) - assert.Equal(t, "ANameHint", - huma.DefaultSchemaNamer(origType, "ANameHint"), - ) + assert.Equal(t, "#/components/schemas/ANameHint", api.OpenAPI().Paths["/body"].Put.RequestBody.Content["application/json"].Schema.Ref) }, Method: http.MethodPut, URL: "/body", @@ -1051,14 +1044,7 @@ Content of example2.txt. resp.Body.Greeting = "Hello, world!" return resp, nil }) - - registry := api.OpenAPI().Components.Schemas - origType := registry.TypeFromRef( - api.OpenAPI().Paths["/response"].Get.Responses["200"].Content["application/json"].Schema.Ref, - ) - assert.Equal(t, "GreetingResp", - huma.DefaultSchemaNamer(origType, "GreetingResp"), - ) + assert.Equal(t, "#/components/schemas/GreetingResp", api.OpenAPI().Paths["/response"].Get.Responses["200"].Content["application/json"].Schema.Ref) }, Method: http.MethodGet, URL: "/response",