3 // The profilesvc is just over HTTP, so we just have a single transport.go.
14 "github.com/gorilla/mux"
16 "github.com/go-kit/kit/log"
17 httptransport "github.com/go-kit/kit/transport/http"
21 // ErrBadRouting is returned when an expected path variable is missing.
22 // It always indicates programmer error.
23 ErrBadRouting = errors.New("inconsistent mapping between route and handler (programmer error)")
26 // MakeHTTPHandler mounts all of the service endpoints into an http.Handler.
27 // Useful in a profilesvc server.
28 func MakeHTTPHandler(s Service, logger log.Logger) http.Handler {
30 e := MakeServerEndpoints(s)
31 options := []httptransport.ServerOption{
32 httptransport.ServerErrorLogger(logger),
33 httptransport.ServerErrorEncoder(encodeError),
36 // POST /profiles/ adds another profile
37 // GET /profiles/:id retrieves the given profile by id
38 // PUT /profiles/:id post updated profile information about the profile
39 // PATCH /profiles/:id partial updated profile information
40 // DELETE /profiles/:id remove the given profile
41 // GET /profiles/:id/addresses/ retrieve addresses associated with the profile
42 // GET /profiles/:id/addresses/:addressID retrieve a particular profile address
43 // POST /profiles/:id/addresses/ add a new address
44 // DELETE /profiles/:id/addresses/:addressID remove an address
46 r.Methods("POST").Path("/profiles/").Handler(httptransport.NewServer(
47 e.PostProfileEndpoint,
48 decodePostProfileRequest,
52 r.Methods("GET").Path("/profiles/{id}").Handler(httptransport.NewServer(
54 decodeGetProfileRequest,
58 r.Methods("PUT").Path("/profiles/{id}").Handler(httptransport.NewServer(
60 decodePutProfileRequest,
64 r.Methods("PATCH").Path("/profiles/{id}").Handler(httptransport.NewServer(
65 e.PatchProfileEndpoint,
66 decodePatchProfileRequest,
70 r.Methods("DELETE").Path("/profiles/{id}").Handler(httptransport.NewServer(
71 e.DeleteProfileEndpoint,
72 decodeDeleteProfileRequest,
76 r.Methods("GET").Path("/profiles/{id}/addresses/").Handler(httptransport.NewServer(
77 e.GetAddressesEndpoint,
78 decodeGetAddressesRequest,
82 r.Methods("GET").Path("/profiles/{id}/addresses/{addressID}").Handler(httptransport.NewServer(
84 decodeGetAddressRequest,
88 r.Methods("POST").Path("/profiles/{id}/addresses/").Handler(httptransport.NewServer(
89 e.PostAddressEndpoint,
90 decodePostAddressRequest,
94 r.Methods("DELETE").Path("/profiles/{id}/addresses/{addressID}").Handler(httptransport.NewServer(
95 e.DeleteAddressEndpoint,
96 decodeDeleteAddressRequest,
103 func decodePostProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
104 var req postProfileRequest
105 if e := json.NewDecoder(r.Body).Decode(&req.Profile); e != nil {
111 func decodeGetProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
115 return nil, ErrBadRouting
117 return getProfileRequest{ID: id}, nil
120 func decodePutProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
124 return nil, ErrBadRouting
127 if err := json.NewDecoder(r.Body).Decode(&profile); err != nil {
130 return putProfileRequest{
136 func decodePatchProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
140 return nil, ErrBadRouting
143 if err := json.NewDecoder(r.Body).Decode(&profile); err != nil {
146 return patchProfileRequest{
152 func decodeDeleteProfileRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
156 return nil, ErrBadRouting
158 return deleteProfileRequest{ID: id}, nil
161 func decodeGetAddressesRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
165 return nil, ErrBadRouting
167 return getAddressesRequest{ProfileID: id}, nil
170 func decodeGetAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
174 return nil, ErrBadRouting
176 addressID, ok := vars["addressID"]
178 return nil, ErrBadRouting
180 return getAddressRequest{
182 AddressID: addressID,
186 func decodePostAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
190 return nil, ErrBadRouting
193 if err := json.NewDecoder(r.Body).Decode(&address); err != nil {
196 return postAddressRequest{
202 func decodeDeleteAddressRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
206 return nil, ErrBadRouting
208 addressID, ok := vars["addressID"]
210 return nil, ErrBadRouting
212 return deleteAddressRequest{
214 AddressID: addressID,
218 func encodePostProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
219 // r.Methods("POST").Path("/profiles/")
220 req.Method, req.URL.Path = "POST", "/profiles/"
221 return encodeRequest(ctx, req, request)
224 func encodeGetProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
225 // r.Methods("GET").Path("/profiles/{id}")
226 r := request.(getProfileRequest)
227 profileID := url.QueryEscape(r.ID)
228 req.Method, req.URL.Path = "GET", "/profiles/"+profileID
229 return encodeRequest(ctx, req, request)
232 func encodePutProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
233 // r.Methods("PUT").Path("/profiles/{id}")
234 r := request.(putProfileRequest)
235 profileID := url.QueryEscape(r.ID)
236 req.Method, req.URL.Path = "PUT", "/profiles/"+profileID
237 return encodeRequest(ctx, req, request)
240 func encodePatchProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
241 // r.Methods("PATCH").Path("/profiles/{id}")
242 r := request.(patchProfileRequest)
243 profileID := url.QueryEscape(r.ID)
244 req.Method, req.URL.Path = "PATCH", "/profiles/"+profileID
245 return encodeRequest(ctx, req, request)
248 func encodeDeleteProfileRequest(ctx context.Context, req *http.Request, request interface{}) error {
249 // r.Methods("DELETE").Path("/profiles/{id}")
250 r := request.(deleteProfileRequest)
251 profileID := url.QueryEscape(r.ID)
252 req.Method, req.URL.Path = "DELETE", "/profiles/"+profileID
253 return encodeRequest(ctx, req, request)
256 func encodeGetAddressesRequest(ctx context.Context, req *http.Request, request interface{}) error {
257 // r.Methods("GET").Path("/profiles/{id}/addresses/")
258 r := request.(getAddressesRequest)
259 profileID := url.QueryEscape(r.ProfileID)
260 req.Method, req.URL.Path = "GET", "/profiles/"+profileID+"/addresses/"
261 return encodeRequest(ctx, req, request)
264 func encodeGetAddressRequest(ctx context.Context, req *http.Request, request interface{}) error {
265 // r.Methods("GET").Path("/profiles/{id}/addresses/{addressID}")
266 r := request.(getAddressRequest)
267 profileID := url.QueryEscape(r.ProfileID)
268 addressID := url.QueryEscape(r.AddressID)
269 req.Method, req.URL.Path = "GET", "/profiles/"+profileID+"/addresses/"+addressID
270 return encodeRequest(ctx, req, request)
273 func encodePostAddressRequest(ctx context.Context, req *http.Request, request interface{}) error {
274 // r.Methods("POST").Path("/profiles/{id}/addresses/")
275 r := request.(postAddressRequest)
276 profileID := url.QueryEscape(r.ProfileID)
277 req.Method, req.URL.Path = "POST", "/profiles/"+profileID+"/addresses/"
278 return encodeRequest(ctx, req, request)
281 func encodeDeleteAddressRequest(ctx context.Context, req *http.Request, request interface{}) error {
282 // r.Methods("DELETE").Path("/profiles/{id}/addresses/{addressID}")
283 r := request.(deleteAddressRequest)
284 profileID := url.QueryEscape(r.ProfileID)
285 addressID := url.QueryEscape(r.AddressID)
286 req.Method, req.URL.Path = "DELETE", "/profiles/"+profileID+"/addresses/"+addressID
287 return encodeRequest(ctx, req, request)
290 func decodePostProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
291 var response postProfileResponse
292 err := json.NewDecoder(resp.Body).Decode(&response)
296 func decodeGetProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
297 var response getProfileResponse
298 err := json.NewDecoder(resp.Body).Decode(&response)
302 func decodePutProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
303 var response putProfileResponse
304 err := json.NewDecoder(resp.Body).Decode(&response)
308 func decodePatchProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
309 var response patchProfileResponse
310 err := json.NewDecoder(resp.Body).Decode(&response)
314 func decodeDeleteProfileResponse(_ context.Context, resp *http.Response) (interface{}, error) {
315 var response deleteProfileResponse
316 err := json.NewDecoder(resp.Body).Decode(&response)
320 func decodeGetAddressesResponse(_ context.Context, resp *http.Response) (interface{}, error) {
321 var response getAddressesResponse
322 err := json.NewDecoder(resp.Body).Decode(&response)
326 func decodeGetAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) {
327 var response getAddressResponse
328 err := json.NewDecoder(resp.Body).Decode(&response)
332 func decodePostAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) {
333 var response postAddressResponse
334 err := json.NewDecoder(resp.Body).Decode(&response)
338 func decodeDeleteAddressResponse(_ context.Context, resp *http.Response) (interface{}, error) {
339 var response deleteAddressResponse
340 err := json.NewDecoder(resp.Body).Decode(&response)
344 // errorer is implemented by all concrete response types that may contain
345 // errors. It allows us to change the HTTP response code without needing to
346 // trigger an endpoint (transport-level) error. For more information, read the
347 // big comment in endpoints.go.
348 type errorer interface {
352 // encodeResponse is the common method to encode all response types to the
353 // client. I chose to do it this way because, since we're using JSON, there's no
354 // reason to provide anything more specific. It's certainly possible to
355 // specialize on a per-response (per-method) basis.
356 func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
357 if e, ok := response.(errorer); ok && e.error() != nil {
358 // Not a Go kit transport error, but a business-logic error.
359 // Provide those as HTTP errors.
360 encodeError(ctx, e.error(), w)
363 w.Header().Set("Content-Type", "application/json; charset=utf-8")
364 return json.NewEncoder(w).Encode(response)
367 // encodeRequest likewise JSON-encodes the request to the HTTP request body.
368 // Don't use it directly as a transport/http.Client EncodeRequestFunc:
369 // profilesvc endpoints require mutating the HTTP method and request path.
370 func encodeRequest(_ context.Context, req *http.Request, request interface{}) error {
372 err := json.NewEncoder(&buf).Encode(request)
376 req.Body = ioutil.NopCloser(&buf)
380 func encodeError(_ context.Context, err error, w http.ResponseWriter) {
382 panic("encodeError with nil error")
384 w.Header().Set("Content-Type", "application/json; charset=utf-8")
385 w.WriteHeader(codeFrom(err))
386 json.NewEncoder(w).Encode(map[string]interface{}{
387 "error": err.Error(),
391 func codeFrom(err error) int {
394 return http.StatusNotFound
395 case ErrAlreadyExists, ErrInconsistentIDs:
396 return http.StatusBadRequest
398 return http.StatusInternalServerError