Kaynağa Gözat

Allow sending metadata in http response (#3345)

Signed-off-by: Niko Kovacevic <nikovacevic@gmail.com>
Signed-off-by: Malthe Poulsen <malthe@grundtvigsvej.dk>
Signed-off-by: malpou <malthe@grundtvigsvej.dk>
Co-authored-by: Malthe Poulsen <30603252+malpou@users.noreply.github.com>
Niko Kovacevic 8 ay önce
ebeveyn
işleme
43ecf50be5
2 değiştirilmiş dosya ile 195 ekleme ve 5 silme
  1. 86 4
      core/pkg/protocol/http.go
  2. 109 1
      core/pkg/protocol/http_test.go

+ 86 - 4
core/pkg/protocol/http.go

@@ -64,6 +64,7 @@ func (hp HTTPProtocol) NotImplemented(message string) HTTPError {
 		Body:       message,
 	}
 }
+
 func (hp HTTPProtocol) Forbidden(message string) HTTPError {
 	if message == "" {
 		message = "Forbidden"
@@ -84,10 +85,11 @@ func (hp HTTPProtocol) NotFound() HTTPError {
 
 // HTTPResponse represents a data envelope for our HTTP messaging
 type HTTPResponse struct {
-	Code    int         `json:"code"`
-	Data    interface{} `json:"data"`
-	Message string      `json:"message,omitempty"`
-	Warning string      `json:"warning,omitempty"`
+	Code    int                    `json:"code"`
+	Data    interface{}            `json:"data"`
+	Meta    map[string]interface{} `json:"meta,omitempty"`
+	Message string                 `json:"message,omitempty"`
+	Warning string                 `json:"warning,omitempty"`
 }
 
 // ToResponse accepts a data payload and/or error to encode into a new HTTPResponse instance. Responses
@@ -106,6 +108,7 @@ func (hp HTTPProtocol) ToResponse(data interface{}, err error) *HTTPResponse {
 		Data: data,
 	}
 }
+
 func (hp HTTPProtocol) WriteRawOK(w http.ResponseWriter) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Length", "0")
@@ -267,3 +270,82 @@ func (hp HTTPProtocol) WriteResponse(w http.ResponseWriter, r *HTTPResponse) {
 		w.Write([]byte(internalServerErrorJSON))
 	}
 }
+
+func (hp HTTPProtocol) NewError(err error, statusCode ...int) *HTTPError {
+	code := http.StatusInternalServerError
+	if len(statusCode) > 0 {
+		code = statusCode[0]
+	}
+
+	var body string
+	if err != nil {
+		body = err.Error()
+	} else {
+		body = "Internal Server Error"
+	}
+
+	return &HTTPError{
+		StatusCode: code,
+		Body:       body,
+	}
+}
+
+func (hp HTTPProtocol) NewResponse(code ...int) *HTTPResponse {
+	r := &HTTPResponse{Code: http.StatusOK}
+
+	if len(code) == 1 {
+		r.Code = code[0]
+	}
+
+	return r
+}
+
+func (r *HTTPResponse) WithCode(code int) *HTTPResponse {
+	if r == nil {
+		r = &HTTPResponse{}
+	}
+
+	r.Code = code
+
+	return r
+}
+
+func (r *HTTPResponse) WithData(data interface{}) *HTTPResponse {
+	if r == nil {
+		r = &HTTPResponse{}
+	}
+
+	r.Data = data
+
+	return r
+}
+
+func (r *HTTPResponse) WithMeta(meta map[string]interface{}) *HTTPResponse {
+	if r == nil {
+		r = &HTTPResponse{}
+	}
+
+	r.Meta = meta
+
+	return r
+}
+
+func (r *HTTPResponse) WithMessage(message string) *HTTPResponse {
+	if r == nil {
+		r = &HTTPResponse{}
+	}
+
+	r.Message = message
+
+	return r
+}
+
+func (r *HTTPResponse) WithWarning(warning string) *HTTPResponse {
+	if r == nil {
+		r = &HTTPResponse{}
+	}
+
+	r.Warning = warning
+
+	return r
+}

+ 109 - 1
core/pkg/protocol/http_test.go

@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"net/http/httptest"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -184,4 +185,111 @@ func TestHTTPProtocol_WriteData_Structure(t *testing.T) {
 	assert.NotContains(t, body, "message")
 	assert.NotContains(t, body, "warning")
 }
- 
+
+func TestHTTPProtocol_HTTPResponse(t *testing.T) {
+	proto := HTTP()
+
+	// Test data, meta, warning, message
+	data := struct {
+		Apples  int
+		Bananas int
+	}{
+		Apples:  12,
+		Bananas: 4,
+	}
+
+	meta := map[string]interface{}{
+		"lastUpdated": time.Date(2025, time.September, 5, 13, 27, 3, 0, time.UTC),
+	}
+
+	warning := "warning"
+
+	message := "message"
+
+	// Test building an HTTPResponse
+
+	var r *HTTPResponse
+
+	r = proto.NewResponse()
+	if r == nil || r.Code != 200 {
+		t.Errorf("expected code %d, received %d", 200, r.Code)
+	}
+
+	r = proto.NewResponse(302).WithMessage("Moved Temporarily")
+	if r == nil || r.Code != 302 || r.Message != "Moved Temporarily" {
+		t.Errorf("expected %d %s, received %d %s", 302, "Moved Temporarily", r.Code, r.Message)
+	}
+
+	r = r.WithCode(200).WithData(data).WithMeta(meta).WithMessage(message).WithWarning(warning)
+	if r == nil {
+		t.Errorf("unexpected nil response")
+	}
+	if r.Code != 200 {
+		t.Errorf("expected code %d, received %d", 200, r.Code)
+	}
+	if r.Data == nil {
+		t.Error("unexpected nil data")
+	}
+	if r.Meta == nil {
+		t.Error("unexpected nil meta")
+	}
+	if r.Message == "" {
+		t.Error("unexpected empty message")
+	}
+	if r.Warning == "" {
+		t.Error("unexpected empty warning")
+	}
+
+	// Test assigning attribtues to nil response
+
+	r = nil
+	r = r.WithCode(413)
+	if r == nil || r.Code != 413 {
+		t.Errorf("expected code %d, received %d", 413, r.Code)
+	}
+
+	r = nil
+	r = r.WithData(data)
+	if r == nil || r.Data == nil {
+		t.Error("expected data, received nil")
+	}
+
+	r = nil
+	r = r.WithMeta(meta)
+	if r == nil || r.Meta == nil {
+		t.Error("expected meta, received nil")
+	}
+
+	r = nil
+	r = r.WithWarning(warning)
+	if r == nil || r.Warning == "" {
+		t.Error("expected warning, received empty string")
+	}
+
+	r = nil
+	r = r.WithMessage(message)
+	if r == nil || r.Message == "" {
+		t.Error("expected message, received empty string")
+	}
+}
+
+func TestHTTPProtocol_NewError(t *testing.T) {
+	proto := HTTP()
+
+	err := errors.New("error")
+
+	httpErr := proto.NewError(err)
+	if httpErr == nil || httpErr.StatusCode != 500 || httpErr.Body != "error" {
+		t.Errorf("expected 500 error, received %d %s", httpErr.StatusCode, httpErr.Body)
+	}
+
+	httpErr = proto.NewError(err, 400)
+	if httpErr == nil || httpErr.StatusCode != 400 || httpErr.Body != "error" {
+		t.Errorf("expected 400 error, received %d %s", httpErr.StatusCode, httpErr.Body)
+	}
+
+	httpErr = proto.NewError(err, 400, 404)
+	if httpErr == nil || httpErr.StatusCode != 400 || httpErr.Body != "error" {
+		t.Errorf("expected 400 error, received %d %s", httpErr.StatusCode, httpErr.Body)
+	}
+}