feat(openapi): document route headers on all responses

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 23:21:56 +00:00
parent 14eedd7f91
commit 47e8c8a795
2 changed files with 21 additions and 10 deletions

View file

@ -325,15 +325,14 @@ func normaliseOpenAPIPath(path string) string {
// operation. The framework always exposes the common envelope responses, plus
// middleware-driven 429 and 504 errors.
func operationResponses(method string, statusCode int, dataSchema map[string]any, example any, responseHeaders map[string]string, security []map[string][]string, deprecationHeaders map[string]any) map[string]any {
successHeaders := mergeHeaders(standardResponseHeaders(), rateLimitSuccessHeaders(), deprecationHeaders)
documentedHeaders := documentedResponseHeaders(responseHeaders)
successHeaders := mergeHeaders(standardResponseHeaders(), rateLimitSuccessHeaders(), deprecationHeaders, documentedHeaders)
if method == "get" {
successHeaders = mergeHeaders(successHeaders, cacheSuccessHeaders())
}
if headers := documentedResponseHeaders(responseHeaders); len(headers) > 0 {
successHeaders = mergeHeaders(successHeaders, headers)
}
isPublic := security != nil && len(security) == 0
errorHeaders := mergeHeaders(standardResponseHeaders(), rateLimitSuccessHeaders(), deprecationHeaders, documentedHeaders)
code := successStatusCode(statusCode)
successResponse := map[string]any{
@ -364,7 +363,7 @@ func operationResponses(method string, statusCode int, dataSchema map[string]any
"schema": envelopeSchema(nil),
},
},
"headers": mergeHeaders(standardResponseHeaders(), rateLimitSuccessHeaders(), deprecationHeaders),
"headers": errorHeaders,
},
"429": map[string]any{
"description": "Too many requests",
@ -373,7 +372,7 @@ func operationResponses(method string, statusCode int, dataSchema map[string]any
"schema": envelopeSchema(nil),
},
},
"headers": mergeHeaders(standardResponseHeaders(), rateLimitHeaders(), deprecationHeaders),
"headers": mergeHeaders(standardResponseHeaders(), rateLimitHeaders(), deprecationHeaders, documentedHeaders),
},
"504": map[string]any{
"description": "Gateway timeout",
@ -382,7 +381,7 @@ func operationResponses(method string, statusCode int, dataSchema map[string]any
"schema": envelopeSchema(nil),
},
},
"headers": mergeHeaders(standardResponseHeaders(), rateLimitSuccessHeaders(), deprecationHeaders),
"headers": errorHeaders,
},
"500": map[string]any{
"description": "Internal server error",
@ -391,7 +390,7 @@ func operationResponses(method string, statusCode int, dataSchema map[string]any
"schema": envelopeSchema(nil),
},
},
"headers": mergeHeaders(standardResponseHeaders(), rateLimitSuccessHeaders(), deprecationHeaders),
"headers": errorHeaders,
},
}
@ -403,7 +402,7 @@ func operationResponses(method string, statusCode int, dataSchema map[string]any
"schema": envelopeSchema(nil),
},
},
"headers": mergeHeaders(standardResponseHeaders(), rateLimitSuccessHeaders(), deprecationHeaders),
"headers": errorHeaders,
}
responses["403"] = map[string]any{
"description": "Forbidden",
@ -412,7 +411,7 @@ func operationResponses(method string, statusCode int, dataSchema map[string]any
"schema": envelopeSchema(nil),
},
},
"headers": mergeHeaders(standardResponseHeaders(), rateLimitSuccessHeaders(), deprecationHeaders),
"headers": errorHeaders,
}
}

View file

@ -1112,6 +1112,18 @@ func TestSpecBuilder_Good_ResponseHeaders(t *testing.T) {
if schema["type"] != "string" {
t.Fatalf("expected response header schema type string, got %v", schema["type"])
}
errorResp := responses["500"].(map[string]any)
errorHeaders, ok := errorResp["headers"].(map[string]any)
if !ok {
t.Fatalf("expected 500 headers map, got %T", errorResp["headers"])
}
if _, ok := errorHeaders["Content-Disposition"]; !ok {
t.Fatal("expected route-specific headers on error responses too")
}
if _, ok := errorHeaders["X-Export-ID"]; !ok {
t.Fatal("expected route-specific headers on error responses too")
}
}
func TestSpecBuilder_Good_PathParameters(t *testing.T) {