mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
feat(api): wrap JSON responses in envelope
Add response envelope middleware to standardize JSON responses as
`{code,data,message}` with consistent business codes across endpoints.
Update Swagger annotations and tests to reflect the new response shape.
BREAKING CHANGE: API responses are now wrapped in a response envelope; clients must read payloads from `data` and handle `code`/`message` fields.
This commit is contained in:
@@ -3,12 +3,12 @@ package api
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/ez-api/ez-api/internal/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
@@ -21,6 +21,7 @@ func TestFeatureHandler_UpdateFeatures_LogOverrides(t *testing.T) {
|
||||
|
||||
h := NewFeatureHandler(rdb)
|
||||
r := gin.New()
|
||||
r.Use(middleware.ResponseEnvelope())
|
||||
r.PUT("/admin/features", h.UpdateFeatures)
|
||||
|
||||
body := []byte(`{"log_retention_days":7,"log_max_records":123}`)
|
||||
@@ -36,8 +37,12 @@ func TestFeatureHandler_UpdateFeatures_LogOverrides(t *testing.T) {
|
||||
Updated map[string]string `json:"updated"`
|
||||
}
|
||||
var got resp
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
env := decodeEnvelope(t, rr, &got)
|
||||
if env.Code != "ok" {
|
||||
t.Fatalf("expected code=ok, got %q", env.Code)
|
||||
}
|
||||
if env.Message != "" {
|
||||
t.Fatalf("expected empty message, got %q", env.Message)
|
||||
}
|
||||
if got.Updated["log_retention_days"] != "7" {
|
||||
t.Fatalf("expected log_retention_days=7, got %q", got.Updated["log_retention_days"])
|
||||
@@ -75,6 +80,7 @@ func TestFeatureHandler_UpdateFeatures_LogOverridesClear(t *testing.T) {
|
||||
|
||||
h := NewFeatureHandler(rdb)
|
||||
r := gin.New()
|
||||
r.Use(middleware.ResponseEnvelope())
|
||||
r.PUT("/admin/features", h.UpdateFeatures)
|
||||
|
||||
body := []byte(`{"log_retention_days":0,"log_max_records":0}`)
|
||||
@@ -101,6 +107,7 @@ func TestFeatureHandler_UpdateFeatures_RegularKeys(t *testing.T) {
|
||||
|
||||
h := NewFeatureHandler(rdb)
|
||||
r := gin.New()
|
||||
r.Use(middleware.ResponseEnvelope())
|
||||
r.PUT("/admin/features", h.UpdateFeatures)
|
||||
|
||||
body := []byte(`{"dp_context_preflight_enabled":false,"dp_state_store_backend":"redis","dp_claude_cross_upstream":"false","custom_number":12}`)
|
||||
@@ -115,8 +122,9 @@ func TestFeatureHandler_UpdateFeatures_RegularKeys(t *testing.T) {
|
||||
var got struct {
|
||||
Updated map[string]string `json:"updated"`
|
||||
}
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
env := decodeEnvelope(t, rr, &got)
|
||||
if env.Code != "ok" {
|
||||
t.Fatalf("expected code=ok, got %q", env.Code)
|
||||
}
|
||||
if got.Updated["dp_context_preflight_enabled"] != "false" {
|
||||
t.Fatalf("expected dp_context_preflight_enabled=false, got %q", got.Updated["dp_context_preflight_enabled"])
|
||||
@@ -153,6 +161,7 @@ func TestFeatureHandler_UpdateFeatures_MixedKeys(t *testing.T) {
|
||||
|
||||
h := NewFeatureHandler(rdb)
|
||||
r := gin.New()
|
||||
r.Use(middleware.ResponseEnvelope())
|
||||
r.PUT("/admin/features", h.UpdateFeatures)
|
||||
|
||||
body := []byte(`{"dp_context_preflight_enabled":true,"log_retention_days":5}`)
|
||||
@@ -167,8 +176,9 @@ func TestFeatureHandler_UpdateFeatures_MixedKeys(t *testing.T) {
|
||||
var got struct {
|
||||
Updated map[string]string `json:"updated"`
|
||||
}
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
env := decodeEnvelope(t, rr, &got)
|
||||
if env.Code != "ok" {
|
||||
t.Fatalf("expected code=ok, got %q", env.Code)
|
||||
}
|
||||
if got.Updated["dp_context_preflight_enabled"] != "true" {
|
||||
t.Fatalf("expected dp_context_preflight_enabled=true, got %q", got.Updated["dp_context_preflight_enabled"])
|
||||
@@ -205,6 +215,7 @@ func TestFeatureHandler_ListFeatures_IncludesLogOverrides(t *testing.T) {
|
||||
|
||||
h := NewFeatureHandler(rdb)
|
||||
r := gin.New()
|
||||
r.Use(middleware.ResponseEnvelope())
|
||||
r.GET("/admin/features", h.ListFeatures)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/admin/features", nil)
|
||||
@@ -218,8 +229,9 @@ func TestFeatureHandler_ListFeatures_IncludesLogOverrides(t *testing.T) {
|
||||
var got struct {
|
||||
Features map[string]string `json:"features"`
|
||||
}
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
env := decodeEnvelope(t, rr, &got)
|
||||
if env.Code != "ok" {
|
||||
t.Fatalf("expected code=ok, got %q", env.Code)
|
||||
}
|
||||
if got.Features["registration_code_enabled"] != "true" {
|
||||
t.Fatalf("expected registration_code_enabled=true, got %q", got.Features["registration_code_enabled"])
|
||||
|
||||
Reference in New Issue
Block a user