2020-01-13 21:05:38 +13:00
|
|
|
package endpoint
|
|
|
|
|
|
|
|
import (
|
2021-04-05 08:37:33 +12:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"runtime/debug"
|
2020-01-13 21:05:38 +13:00
|
|
|
|
2021-04-05 08:37:33 +12:00
|
|
|
"github.com/go-chi/chi/middleware"
|
|
|
|
"github.com/rs/zerolog/log"
|
2020-01-13 21:05:38 +13:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2021-04-05 08:37:33 +12:00
|
|
|
Endpoint func(http.ResponseWriter, *http.Request) error
|
|
|
|
|
|
|
|
ErrResponse struct {
|
|
|
|
Status int `json:"status,omitempty"`
|
|
|
|
Err string `json:"error,omitempty"`
|
|
|
|
Message string `json:"message,omitempty"`
|
|
|
|
Details string `json:"details,omitempty"`
|
|
|
|
Code string `json:"code,omitempty"`
|
|
|
|
RequestID string `json:"request,omitempty"`
|
|
|
|
}
|
2020-01-13 21:05:38 +13:00
|
|
|
)
|
|
|
|
|
|
|
|
func Handle(handler Endpoint) http.HandlerFunc {
|
2021-04-05 08:37:33 +12:00
|
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if err := handler(w, r); err != nil {
|
|
|
|
WriteError(w, r, err)
|
|
|
|
}
|
|
|
|
}
|
2020-01-13 21:05:38 +13:00
|
|
|
|
2021-04-05 08:37:33 +12:00
|
|
|
return http.HandlerFunc(fn)
|
2020-01-13 21:05:38 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
var nonErrorsCodes = map[int]bool{
|
2021-04-05 08:37:33 +12:00
|
|
|
404: true,
|
2020-01-13 21:05:38 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
func errResponse(input interface{}) *ErrResponse {
|
2021-04-05 08:37:33 +12:00
|
|
|
var res *ErrResponse
|
|
|
|
var err interface{}
|
|
|
|
|
|
|
|
switch input.(type) {
|
|
|
|
case *HandlerError:
|
|
|
|
e := input.(*HandlerError)
|
|
|
|
res = &ErrResponse{
|
|
|
|
Status: e.Status,
|
|
|
|
Err: http.StatusText(e.Status),
|
|
|
|
Message: e.Message,
|
|
|
|
}
|
|
|
|
err = e.Err
|
|
|
|
default:
|
|
|
|
res = &ErrResponse{
|
|
|
|
Status: http.StatusInternalServerError,
|
|
|
|
Err: http.StatusText(http.StatusInternalServerError),
|
|
|
|
}
|
|
|
|
err = input
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
switch err.(type) {
|
|
|
|
case *error:
|
|
|
|
e := err.(error)
|
|
|
|
res.Details = e.Error()
|
|
|
|
default:
|
|
|
|
res.Details = fmt.Sprintf("%+v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
2020-01-13 21:05:38 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
func WriteError(w http.ResponseWriter, r *http.Request, err interface{}) {
|
2021-04-05 08:37:33 +12:00
|
|
|
hlog := log.With().
|
|
|
|
Str("module", "http").
|
|
|
|
Logger()
|
|
|
|
|
|
|
|
res := errResponse(err)
|
|
|
|
|
|
|
|
if reqID := middleware.GetReqID(r.Context()); reqID != "" {
|
|
|
|
res.RequestID = reqID
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
w.WriteHeader(res.Status)
|
|
|
|
|
|
|
|
if err := json.NewEncoder(w).Encode(res); err != nil {
|
|
|
|
hlog.Warn().Err(err).Msg("Failed writing json error response")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !nonErrorsCodes[res.Status] {
|
|
|
|
logEntry := middleware.GetLogEntry(r)
|
|
|
|
if logEntry != nil {
|
|
|
|
logEntry.Panic(err, debug.Stack())
|
|
|
|
} else {
|
|
|
|
hlog.Error().Str("stack", string(debug.Stack())).Msgf("%+v", err)
|
|
|
|
}
|
|
|
|
}
|
2020-01-13 21:05:38 +13:00
|
|
|
}
|