From a31feae6d4aabb8755d0416291ab3f6d0a501c98 Mon Sep 17 00:00:00 2001 From: Panagiotis Siatras Date: Fri, 18 Mar 2022 17:18:23 +0200 Subject: [PATCH] api/render: initial implementation of the package --- api/render/render.go | 58 +++++++++++++++++++++++++++++++++++++++ api/render/render_test.go | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 api/render/render.go create mode 100644 api/render/render_test.go diff --git a/api/render/render.go b/api/render/render.go new file mode 100644 index 00000000..e4fbaffa --- /dev/null +++ b/api/render/render.go @@ -0,0 +1,58 @@ +// Package render implements functionality related to response rendering. +package render + +import ( + "encoding/json" + "net/http" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + + "github.com/smallstep/certificates/api/log" +) + +// JSON writes the passed value into the http.ResponseWriter. +func JSON(w http.ResponseWriter, v interface{}) { + JSONStatus(w, v, http.StatusOK) +} + +// JSONStatus writes the given value into the http.ResponseWriter and the +// given status is written as the status code of the response. +func JSONStatus(w http.ResponseWriter, v interface{}, status int) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + if err := json.NewEncoder(w).Encode(v); err != nil { + log.Error(w, err) + + return + } + + log.EnabledResponse(w, v) +} + +// ProtoJSON writes the passed value into the http.ResponseWriter. +func ProtoJSON(w http.ResponseWriter, m proto.Message) { + ProtoJSONStatus(w, m, http.StatusOK) +} + +// ProtoJSONStatus writes the given value into the http.ResponseWriter and the +// given status is written as the status code of the response. +func ProtoJSONStatus(w http.ResponseWriter, m proto.Message, status int) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + + b, err := protojson.Marshal(m) + if err != nil { + log.Error(w, err) + + return + } + + if _, err := w.Write(b); err != nil { + log.Error(w, err) + + return + } + + // log.EnabledResponse(w, v) +} diff --git a/api/render/render_test.go b/api/render/render_test.go new file mode 100644 index 00000000..de591972 --- /dev/null +++ b/api/render/render_test.go @@ -0,0 +1,53 @@ +package render + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/smallstep/certificates/logging" +) + +func TestJSON(t *testing.T) { + type args struct { + rw http.ResponseWriter + v interface{} + } + tests := []struct { + name string + args args + ok bool + }{ + {"ok", args{httptest.NewRecorder(), map[string]interface{}{"foo": "bar"}}, true}, + {"fail", args{httptest.NewRecorder(), make(chan int)}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rw := logging.NewResponseLogger(tt.args.rw) + JSON(rw, tt.args.v) + + rr, ok := tt.args.rw.(*httptest.ResponseRecorder) + if !ok { + t.Error("ResponseWriter does not implement *httptest.ResponseRecorder") + return + } + + fields := rw.Fields() + if tt.ok { + if body := rr.Body.String(); body != "{\"foo\":\"bar\"}\n" { + t.Errorf(`Unexpected body = %v, want {"foo":"bar"}`, body) + } + if len(fields) != 0 { + t.Errorf("ResponseLogger fields = %v, wants 0 elements", fields) + } + } else { + if body := rr.Body.String(); body != "" { + t.Errorf("Unexpected body = %s, want empty string", body) + } + if len(fields) != 1 { + t.Errorf("ResponseLogger fields = %v, wants 1 element", fields) + } + } + }) + } +}