fix(response): attach meta to all json responses
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
76aa4c9974
commit
d7ef3610f7
2 changed files with 67 additions and 1 deletions
|
|
@ -84,6 +84,18 @@ func (g autoErrorResponseMetaTestGroup) RegisterRoutes(rg *gin.RouterGroup) {
|
|||
})
|
||||
}
|
||||
|
||||
type plusJSONResponseMetaTestGroup struct{}
|
||||
|
||||
func (g plusJSONResponseMetaTestGroup) Name() string { return "plus-json-response-meta" }
|
||||
func (g plusJSONResponseMetaTestGroup) BasePath() string { return "/v1" }
|
||||
func (g plusJSONResponseMetaTestGroup) RegisterRoutes(rg *gin.RouterGroup) {
|
||||
rg.GET("/plus-json", func(c *gin.Context) {
|
||||
c.Header("Content-Type", "application/problem+json")
|
||||
c.Status(http.StatusOK)
|
||||
_, _ = c.Writer.Write([]byte(`{"success":true,"data":"ok"}`))
|
||||
})
|
||||
}
|
||||
|
||||
// ── Bearer auth ─────────────────────────────────────────────────────────
|
||||
|
||||
func TestBearerAuth_Bad_MissingToken(t *testing.T) {
|
||||
|
|
@ -357,6 +369,43 @@ func TestResponseMeta_Good_AttachesMetaToErrorResponses(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestResponseMeta_Good_AttachesMetaToPlusJSONContentType(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
e, _ := api.New(
|
||||
api.WithRequestID(),
|
||||
api.WithResponseMeta(),
|
||||
)
|
||||
e.Register(plusJSONResponseMetaTestGroup{})
|
||||
|
||||
h := e.Handler()
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodGet, "/v1/plus-json", nil)
|
||||
req.Header.Set("X-Request-ID", "client-id-plus-json-meta")
|
||||
h.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
if got := w.Header().Get("Content-Type"); got != "application/problem+json" {
|
||||
t.Fatalf("expected Content-Type to be preserved, got %q", got)
|
||||
}
|
||||
|
||||
var resp api.Response[string]
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("unmarshal error: %v", err)
|
||||
}
|
||||
if resp.Meta == nil {
|
||||
t.Fatal("expected Meta to be present")
|
||||
}
|
||||
if resp.Meta.RequestID != "client-id-plus-json-meta" {
|
||||
t.Fatalf("expected request_id=%q, got %q", "client-id-plus-json-meta", resp.Meta.RequestID)
|
||||
}
|
||||
if resp.Meta.Duration == "" {
|
||||
t.Fatal("expected duration to be populated")
|
||||
}
|
||||
}
|
||||
|
||||
// ── CORS ────────────────────────────────────────────────────────────────
|
||||
|
||||
func TestCORS_Good_PreflightAllOrigins(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
|
@ -184,10 +185,26 @@ func refreshResponseMetaBody(body []byte, meta *Meta) []byte {
|
|||
}
|
||||
|
||||
func shouldAttachResponseMeta(contentType string, body []byte) bool {
|
||||
if !strings.Contains(contentType, "application/json") {
|
||||
if !isJSONContentType(contentType) {
|
||||
return false
|
||||
}
|
||||
|
||||
trimmed := bytes.TrimSpace(body)
|
||||
return len(trimmed) > 0 && trimmed[0] == '{'
|
||||
}
|
||||
|
||||
func isJSONContentType(contentType string) bool {
|
||||
if strings.TrimSpace(contentType) == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
mediaType, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
mediaType = strings.TrimSpace(contentType)
|
||||
}
|
||||
mediaType = strings.ToLower(mediaType)
|
||||
|
||||
return mediaType == "application/json" ||
|
||||
strings.HasSuffix(mediaType, "+json") ||
|
||||
strings.HasSuffix(mediaType, "/json")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue