/
api.go
146 lines (115 loc) · 4 KB
/
api.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package api
import (
"encoding/json"
"errors"
"net/http"
"github.com/AgencyPMG/go-from-scratch/app/internal/data"
"github.com/AgencyPMG/go-from-scratch/app/internal/data/client"
"github.com/AgencyPMG/go-from-scratch/app/internal/data/user"
"github.com/AgencyPMG/go-from-scratch/app/internal/gfsweb/handler/api/dto"
"github.com/gogolfing/cbus"
"github.com/gorilla/mux"
)
//ErrBadRequestRouteParameter is a sentinel error indicating a route parameter
//could not be parsed or found.
var ErrBadRequestRouteParameter = errors.New("api: bad request route parameter")
//API allows for handling all incoming requests dealing with the application's API.
//Its Handler method should be used for serving API requests.
//
//All fields must be non-nil and safe for use by multiple goroutintes due to their
//use inside a http.Server and http.Handler.
type API struct {
//Bus is a command bus that is used to execute commands to update entities
//within the API.
Bus *cbus.Bus
//Users is a query repository used to retrieve Users.
Users user.QueryRepo
//Clients is a query repository used to retrieve Clients.
Clients client.QueryRepo
}
//Handler returns an http.Handler that serves all requests for a.
func (a *API) Handler() http.Handler {
router := mux.NewRouter()
//User routes.
router.HandleFunc("/users", a.listUsers).
Methods(http.MethodGet)
router.HandleFunc("/users", a.createUser).
Methods(http.MethodPost)
router.HandleFunc("/users/{"+routeParamUserId+"}", a.getUser).
Methods(http.MethodGet)
router.HandleFunc("/users/{"+routeParamUserId+"}", a.updateUser).
Methods(http.MethodPatch)
//Client routes.
router.HandleFunc("/clients", a.listClients).
Methods(http.MethodGet)
router.HandleFunc("/clients", a.createClient).
Methods(http.MethodPost)
router.HandleFunc("/clients/{"+routeParamClientId+"}", a.getClient).
Methods(http.MethodGet)
router.HandleFunc("/clients/{"+routeParamClientId+"}", a.updateClient).
Methods(http.MethodPatch)
router.HandleFunc("/clients/{"+routeParamClientId+"}", a.deleteClient).
Methods(http.MethodDelete)
return router
}
//idFrom is a helper method to retrieve data.Id(s) from route parameters.
func (a *API) idFrom(w http.ResponseWriter, r *http.Request, varName string) (data.Id, bool) {
vars := mux.Vars(r)
varId, ok := vars[varName]
if !ok {
a.sendError(w, ErrBadRequestRouteParameter, http.StatusBadRequest)
return data.EmptyId(), false
}
id, err := data.ParseId(varId)
if err != nil {
a.sendError(w, err, http.StatusBadRequest)
return data.EmptyId(), false
}
return id, true
}
//parseForm is a helper method to parse form types that this package knows how
//to use.
func (a *API) parseForm(w http.ResponseWriter, r *http.Request, f interface{}) bool {
dec := json.NewDecoder(r.Body)
err := dec.Decode(f)
if err != nil {
a.sendError(w, err, http.StatusBadRequest)
return false
}
//Do some sort of validation here on f.
return true
}
//sendData is a helper method to send successful responses by marshaling data
//and sending a response with status.
func (a *API) sendData(w http.ResponseWriter, data interface{}, status int) {
transformed, err := dto.Transform(data)
if err != nil {
a.sendError(w, err, http.StatusInternalServerError)
return
}
a.sendResponse(w, transformed, status)
}
//sendError is a helper method to send failure responses by marshaling err into
//a simple value and sending status.
func (a *API) sendError(w http.ResponseWriter, err error, status int) {
a.sendResponse(
w,
&apiError{
Error: err.Error(),
},
status,
)
}
//sendResponse is a helper method to send all responses by marshaling resp and
//sending status.
func (a *API) sendResponse(w http.ResponseWriter, resp interface{}, status int) {
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(status)
enc := json.NewEncoder(w)
enc.SetIndent("", "\t") //Do this for prettier output for our example application.
enc.Encode(resp)
}
//apiError is a simple type that knows how to marshal an error.
type apiError struct {
Error string `json:"error"`
}