/
transport.go
155 lines (127 loc) · 3.84 KB
/
transport.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
147
148
149
150
151
152
153
154
155
package banking
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-kit/kit/log"
kithttp "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/shopspring/decimal"
"github.com/twonegatives/coinsph_challenge/pkg/entities"
)
var (
errBadRequest = errors.New("bad request")
)
type payment struct {
From string `json:"from"`
To string `json:"to"`
Amount decimal.Decimal `json:"amount"`
}
type sendPaymentBody struct {
Payment payment `json:"payment"`
}
type account struct {
Name string `json:"name"`
}
type createAccountBody struct {
Account account `json:"account"`
}
func decodeCreateAccountRequest(_ context.Context, r *http.Request) (interface{}, error) {
var body createAccountBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
return nil, errBadRequest
}
newAccountRequest := createAccountRequest{
Name: body.Account.Name,
}
return newAccountRequest, nil
}
func decodeSendPaymentRequest(_ context.Context, r *http.Request) (interface{}, error) {
var body sendPaymentBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
return nil, errBadRequest
}
paymentRequest := sendPaymentRequest{
From: entities.Account{Name: body.Payment.From},
To: entities.Account{Name: body.Payment.To},
Amount: body.Payment.Amount,
}
return paymentRequest, nil
}
func encodePaymentsAsJSON(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
resp := response.(getPaymentsResponse)
encoder := paymentsJSONEncoder{}
encoded, err := encoder.encode(resp.Payments)
if err != nil {
return errors.Wrap(err, "Can't encode payments into bytes")
}
_, writeErr := w.Write(encoded)
return errors.Wrap(writeErr, "Can't write response body")
}
func MakeHandler(svc BankingService, l log.Logger) http.Handler {
opts := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(errorEncoder),
kithttp.ServerErrorLogger(l),
}
createAccount := kithttp.NewServer(
MakeCreateAccountEndpoint(svc),
decodeCreateAccountRequest,
kithttp.EncodeJSONResponse,
opts...,
)
getAccounts := kithttp.NewServer(
MakeGetAccountsEndpoint(svc),
kithttp.NopRequestDecoder,
kithttp.EncodeJSONResponse,
opts...,
)
getPayments := kithttp.NewServer(
MakeGetPaymentsEndpoint(svc),
kithttp.NopRequestDecoder,
encodePaymentsAsJSON,
opts...,
)
sendPayment := kithttp.NewServer(
MakeSendPaymentEndpoint(svc),
decodeSendPaymentRequest,
kithttp.EncodeJSONResponse,
opts...,
)
m := mux.NewRouter()
m.Handle("/accounts", createAccount).Methods(http.MethodPost)
m.Handle("/accounts", getAccounts).Methods(http.MethodGet)
m.Handle("/payments", getPayments).Methods(http.MethodGet)
m.Handle("/payments", sendPayment).Methods(http.MethodPost)
m.NotFoundHandler = http.HandlerFunc(notFoundEncoder)
return m
}
func errorEncoder(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
var exposedErrDescription string
switch errors.Cause(err) {
case errAmountShouldBePositive,
errNamesNotPresent,
errSenderIsReceiver,
errInsufficientFunds,
errAccountNameBlank:
w.WriteHeader(http.StatusBadRequest)
exposedErrDescription = err.Error()
default:
w.WriteHeader(http.StatusInternalServerError)
exposedErrDescription = "internal server error"
}
encodeErr := json.NewEncoder(w).Encode(map[string]string{"error": exposedErrDescription})
if encodeErr != nil {
panic(fmt.Sprintf("Can't encode error, %s. Original error: %s", encodeErr, err))
}
}
func notFoundEncoder(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": "page not found",
})
}