-
Notifications
You must be signed in to change notification settings - Fork 27
/
render.go
88 lines (73 loc) · 2.66 KB
/
render.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package handler
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/go-chi/chi"
"github.com/pressly/lg"
"github.com/src-d/gitbase-web/server/serializer"
)
// RequestProcessFunc is a function that takes an http.Request, and returns a serializer.Response and an error
type RequestProcessFunc func(*http.Request) (*serializer.Response, error)
// APIHandlerFunc returns an http.HandlerFunc that will serve the user request taking the serializer.Response and errors
// from the passed RequestProcessFunc
func APIHandlerFunc(rp RequestProcessFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
response, err := rp(r)
if err == context.Canceled {
return
}
if response == nil {
response = serializer.NewEmptyResponse()
}
write(w, r, response, err)
}
}
// write is the responsible of writing the response with the data from the passed *serializer.Response and error
// If the passed error has StatusCode, the http.Response will be returned with the StatusCode of the passed error
// If the passed error has not StatusCode, the http.Response will be returned as a http.StatusInternalServerError
func write(w http.ResponseWriter, r *http.Request, response *serializer.Response, err error) {
var statusCode int
// TODO: There should be no ppl calling write from the outside
if response == nil {
response = serializer.NewEmptyResponse()
}
if err == nil {
statusCode = http.StatusOK
} else if httpError, ok := err.(serializer.HTTPError); ok {
statusCode = httpError.StatusCode()
response.Status = statusCode
response.Errors = []serializer.HTTPError{httpError}
} else {
statusCode = http.StatusInternalServerError
response.Status = statusCode
response.Errors = []serializer.HTTPError{serializer.NewHTTPError(statusCode, http.StatusText(statusCode))}
}
if statusCode >= http.StatusBadRequest {
lg.RequestLog(r).Error(err.Error())
}
content, err := json.Marshal(response)
if err != nil {
err = fmt.Errorf("response could not be marshalled; %s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
lg.RequestLog(r).Error(err.Error())
return
}
w.Header().Add("content-type", "application/json")
w.WriteHeader(statusCode)
w.Write(content)
}
// urlParamInt returns the url parameter from an http.Request object. If the
// param cannot be converted to int, it returns a serializer.NewHTTPError
func urlParamInt(r *http.Request, key string) (int, error) {
str := chi.URLParam(r, key)
val, err := strconv.Atoi(str)
if err != nil {
err = serializer.NewHTTPError(
http.StatusBadRequest,
fmt.Sprintf("Wrong format for URL parameter %q; received %q", key, str))
}
return val, err
}