-
Notifications
You must be signed in to change notification settings - Fork 62
/
get.go
123 lines (106 loc) · 3.2 KB
/
get.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package rest
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"github.com/PacktPublishing/Hands-On-Dependency-Injection-in-Go/ch10/acme/internal/logging"
"github.com/PacktPublishing/Hands-On-Dependency-Injection-in-Go/ch10/acme/internal/modules/data"
"github.com/gorilla/mux"
)
const (
// default person id (returned on error)
defaultPersonID = 0
// key in the mux where the ID is stored
muxVarID = "id"
)
// GetModel will load a registration
//go:generate mockery -name=GetModel -case underscore -testonly -inpkg -note @generated
type GetModel interface {
Do(ID int) (*data.Person, error)
}
// GetConfig is the config for the Get Handler
type GetConfig interface {
Logger() logging.Logger
}
// NewGetHandler is the constructor for GetHandler
func NewGetHandler(cfg GetConfig, model GetModel) *GetHandler {
return &GetHandler{
cfg: cfg,
getter: model,
}
}
// GetHandler is the HTTP handler for the "Get Person" endpoint
// In this simplified example we are assuming all possible errors are user errors and returning "bad request" HTTP 400
// or "not found" HTTP 404
// There are some programmer errors possible but hopefully these will be caught in testing.
type GetHandler struct {
cfg GetConfig
getter GetModel
}
// ServeHTTP implements http.Handler
func (h *GetHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
// extract person id from request
id, err := h.extractID(request)
if err != nil {
// output error
response.WriteHeader(http.StatusBadRequest)
return
}
// attempt get
person, err := h.getter.Do(id)
if err != nil {
// not need to log here as we can expect other layers to do so
response.WriteHeader(http.StatusNotFound)
return
}
// happy path
err = h.writeJSON(response, person)
if err != nil {
// this error should not happen but if it does there is nothing we can do to recover
response.WriteHeader(http.StatusInternalServerError)
}
}
// extract the person ID from the request
func (h *GetHandler) extractID(request *http.Request) (int, error) {
// ID is part of the URL, so we extract it from there
vars := mux.Vars(request)
idAsString, exists := vars[muxVarID]
if !exists {
// log and return error
err := errors.New("[get] person id missing from request")
h.cfg.Logger().Warn(err.Error())
return defaultPersonID, err
}
// convert ID to int
id, err := strconv.Atoi(idAsString)
if err != nil {
// log and return error
err = fmt.Errorf("[get] failed to convert person id into a number. err: %s", err)
h.cfg.Logger().Error(err.Error())
return defaultPersonID, err
}
return id, nil
}
// output the supplied person as JSON
func (h *GetHandler) writeJSON(writer io.Writer, person *data.Person) error {
output := &getResponseFormat{
ID: person.ID,
FullName: person.FullName,
Phone: person.Phone,
Currency: person.Currency,
Price: person.Price,
}
// call to http.ResponseWriter.Write() will cause HTTP OK (200) to be output as well
return json.NewEncoder(writer).Encode(output)
}
// the JSON response format
type getResponseFormat struct {
ID int `json:"id"`
FullName string `json:"name"`
Phone string `json:"phone"`
Currency string `json:"currency"`
Price float64 `json:"price"`
}