diff --git a/api/render/render.go b/api/render/render.go index 56a68f30..12ad70aa 100644 --- a/api/render/render.go +++ b/api/render/render.go @@ -113,3 +113,30 @@ func Error(w http.ResponseWriter, err error) { log.Error(w, err) } } + +// BadRequest renders the JSON representation of err into w and sets its +// status code to http.StatusBadRequest. +func BadRequest(w http.ResponseWriter, err error) { + codedError(w, http.StatusBadRequest, err) +} + +func codedError(w http.ResponseWriter, code int, err error) { + var wrapper = struct { + Status int `json:"status"` + Message string `json:"message"` + }{ + Status: code, + Message: err.Error(), + } + + data, err := json.Marshal(wrapper) + if err != nil { + log.Error(w, err) + + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + w.Write(data) +} diff --git a/api/render/render_test.go b/api/render/render_test.go index de591972..f1d7c145 100644 --- a/api/render/render_test.go +++ b/api/render/render_test.go @@ -3,8 +3,11 @@ package render import ( "net/http" "net/http/httptest" + "strconv" "testing" + "github.com/stretchr/testify/assert" + "github.com/smallstep/certificates/logging" ) @@ -51,3 +54,35 @@ func TestJSON(t *testing.T) { }) } } + +func TestErrors(t *testing.T) { + cases := []struct { + fn func(http.ResponseWriter, error) // helper + err error // error being render + code int // expected status code + body string // expected body + }{ + // --- BadRequest + 0: { + fn: BadRequest, + err: assert.AnError, + code: http.StatusBadRequest, + body: `{"status":400,"message":"assert.AnError general error for testing"}`, + }, + } + + for caseIndex := range cases { + kase := cases[caseIndex] + + t.Run(strconv.Itoa(caseIndex), func(t *testing.T) { + rec := httptest.NewRecorder() + kase.fn(rec, kase.err) + + ret := rec.Result() + + assert.Equal(t, "application/json", ret.Header.Get("Content-Type")) + assert.Equal(t, kase.code, ret.StatusCode) + assert.Equal(t, kase.body, rec.Body.String()) + }) + } +}