From 90e237ae318827ab34d361aa33db1ae6406027be Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 14:29:30 +0000 Subject: [PATCH] feat(api): include HEAD request bodies in OpenAPI Co-Authored-By: Virgil --- openapi.go | 2 +- openapi_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/openapi.go b/openapi.go index 4d7624f..8ef928f 100644 --- a/openapi.go +++ b/openapi.go @@ -137,7 +137,7 @@ func (sb *SpecBuilder) buildPaths(groups []RouteGroup) map[string]any { // Add request body for methods that accept one. // The contract only excludes GET; other verbs may legitimately carry bodies. - if rd.RequestBody != nil && method != "get" && method != "head" { + if rd.RequestBody != nil && method != "get" { operation["requestBody"] = map[string]any{ "required": true, "content": map[string]any{ diff --git a/openapi_test.go b/openapi_test.go index fe47995..e513224 100644 --- a/openapi_test.go +++ b/openapi_test.go @@ -373,6 +373,51 @@ func TestSpecBuilder_Good_RequestBodyOnDelete(t *testing.T) { } } +func TestSpecBuilder_Good_RequestBodyOnHead(t *testing.T) { + sb := &api.SpecBuilder{ + Title: "Test", + Version: "1.0.0", + } + + group := &specStubGroup{ + name: "resources", + basePath: "/api", + descs: []api.RouteDescription{ + { + Method: "HEAD", + Path: "/resources/{id}", + Summary: "Check resource", + Tags: []string{"resources"}, + RequestBody: map[string]any{ + "type": "object", + "properties": map[string]any{ + "include": map[string]any{"type": "string"}, + }, + }, + Response: map[string]any{ + "type": "object", + }, + }, + }, + } + + data, err := sb.Build([]api.RouteGroup{group}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + var spec map[string]any + if err := json.Unmarshal(data, &spec); err != nil { + t.Fatalf("invalid JSON: %v", err) + } + + paths := spec["paths"].(map[string]any) + headOp := paths["/api/resources/{id}"].(map[string]any)["head"].(map[string]any) + if headOp["requestBody"] == nil { + t.Fatal("expected requestBody on HEAD /api/resources/{id}") + } +} + func TestSpecBuilder_Good_NonDescribableGroup(t *testing.T) { sb := &api.SpecBuilder{ Title: "Test",