feat(openapi): reuse deprecation header components

This commit is contained in:
Virgil 2026-04-01 23:12:40 +00:00
parent 06f2263b73
commit eb7e1e51cb
2 changed files with 51 additions and 16 deletions

View file

@ -155,6 +155,7 @@ func (sb *SpecBuilder) Build(groups []RouteGroup) ([]byte, error) {
"bearerFormat": "JWT",
},
},
"headers": deprecationHeaderComponents(),
}
return json.MarshalIndent(spec, "", " ")
@ -507,40 +508,61 @@ func deprecationResponseHeaders(deprecated bool, sunsetDate, replacement string)
headers := map[string]any{
"Deprecation": map[string]any{
"description": "Indicates the endpoint is deprecated",
"schema": map[string]any{
"type": "string",
},
"$ref": "#/components/headers/deprecation",
},
"X-API-Warn": map[string]any{
"description": "Human-readable deprecation warning",
"schema": map[string]any{
"type": "string",
},
"$ref": "#/components/headers/xapiwarn",
},
}
if sunsetDate != "" {
headers["Sunset"] = map[string]any{
"description": "RFC 7231 date when the endpoint will be removed",
"schema": map[string]any{
"type": "string",
},
"$ref": "#/components/headers/sunset",
}
}
if replacement != "" {
headers["Link"] = map[string]any{
"description": "Successor endpoint advertised with rel=\"successor-version\"",
"schema": map[string]any{
"type": "string",
},
"$ref": "#/components/headers/link",
}
}
return headers
}
// deprecationHeaderComponents returns reusable OpenAPI header components for
// the standard deprecation and sunset middleware headers.
func deprecationHeaderComponents() map[string]any {
return map[string]any{
"deprecation": map[string]any{
"description": "Indicates that the endpoint is deprecated.",
"schema": map[string]any{
"type": "string",
"enum": []string{"true"},
},
},
"sunset": map[string]any{
"description": "The date and time after which the endpoint will no longer be supported.",
"schema": map[string]any{
"type": "string",
"format": "date-time",
},
},
"link": map[string]any{
"description": "Reference to the successor endpoint, when one is provided.",
"schema": map[string]any{
"type": "string",
},
},
"xapiwarn": map[string]any{
"description": "Human-readable deprecation warning for clients.",
"schema": map[string]any{
"type": "string",
},
},
}
}
// buildTags generates the tags array from all RouteGroups.
func (sb *SpecBuilder) buildTags(groups []preparedRouteGroup) []map[string]any {
tags := []map[string]any{

View file

@ -1491,6 +1491,19 @@ func TestSpecBuilder_Good_DeprecatedOperation(t *testing.T) {
t.Fatalf("expected deprecation header %q in operation response headers", name)
}
}
components := spec["components"].(map[string]any)
headerComponents := components["headers"].(map[string]any)
for _, name := range []string{"deprecation", "sunset", "link", "xapiwarn"} {
if _, ok := headerComponents[name]; !ok {
t.Fatalf("expected reusable header component %q", name)
}
}
deprecationHeader := headers["Deprecation"].(map[string]any)
if got := deprecationHeader["$ref"]; got != "#/components/headers/deprecation" {
t.Fatalf("expected Deprecation header to reference shared component, got %v", got)
}
}
func TestSpecBuilder_Good_BlankTagsAreIgnored(t *testing.T) {