From 9449c195c3a8e003387c0a8c74a514d4de6da978 Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 23:29:12 +0000 Subject: [PATCH] fix(api): preserve existing link headers in sunset middleware Co-Authored-By: Virgil --- sunset.go | 5 +++-- sunset_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/sunset.go b/sunset.go index 6349905..a8b17e5 100644 --- a/sunset.go +++ b/sunset.go @@ -28,15 +28,16 @@ func ApiSunset(sunsetDate, replacement string) gin.HandlerFunc { } return func(c *gin.Context) { + c.Next() + c.Header("Deprecation", "true") if formatted != "" { c.Header("Sunset", formatted) } if replacement != "" { - c.Header("Link", "<"+replacement+">; rel=\"successor-version\"") + c.Writer.Header().Add("Link", "<"+replacement+">; rel=\"successor-version\"") } c.Header("X-API-Warn", warning) - c.Next() } } diff --git a/sunset_test.go b/sunset_test.go index b8b46fc..78637ae 100644 --- a/sunset_test.go +++ b/sunset_test.go @@ -22,6 +22,17 @@ func (sunsetStubGroup) RegisterRoutes(rg *gin.RouterGroup) { }) } +type sunsetLinkStubGroup struct{} + +func (sunsetLinkStubGroup) Name() string { return "legacy-link" } +func (sunsetLinkStubGroup) BasePath() string { return "/legacy-link" } +func (sunsetLinkStubGroup) RegisterRoutes(rg *gin.RouterGroup) { + rg.GET("/status", func(c *gin.Context) { + c.Header("Link", "; rel=\"help\"") + c.JSON(http.StatusOK, api.OK("ok")) + }) +} + func TestWithSunset_Good_AddsDeprecationHeaders(t *testing.T) { gin.SetMode(gin.TestMode) @@ -51,3 +62,32 @@ func TestWithSunset_Good_AddsDeprecationHeaders(t *testing.T) { t.Fatalf("expected deprecation warning, got %q", got) } } + +func TestWithSunset_Good_PreservesExistingLinkHeaders(t *testing.T) { + gin.SetMode(gin.TestMode) + + e, err := api.New(api.WithSunset("2025-06-01", "/api/v2/status")) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + e.Register(sunsetLinkStubGroup{}) + + w := httptest.NewRecorder() + req, _ := http.NewRequest(http.MethodGet, "/legacy-link/status", nil) + e.Handler().ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", w.Code) + } + + links := w.Header().Values("Link") + if len(links) != 2 { + t.Fatalf("expected 2 Link header values, got %v", links) + } + if links[0] != "; rel=\"help\"" { + t.Fatalf("expected existing Link header to be preserved first, got %q", links[0]) + } + if links[1] != "; rel=\"successor-version\"" { + t.Fatalf("expected successor Link header to be appended, got %q", links[1]) + } +}