-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathresponse_handler.go
148 lines (129 loc) · 4.2 KB
/
response_handler.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
package hx
import (
"bytes"
"fmt"
"net/http"
"github.com/izumin5210/hx/hxutil"
)
type ResponseHandler func(*http.Response, error) (*http.Response, error)
func HandleResponse(f func(*http.Response, error) (*http.Response, error)) Option {
return OptionFunc(func(c *Config) error {
c.ResponseHandlers = append(c.ResponseHandlers, f)
return nil
})
}
type ResponseError struct {
Response *http.Response
Err error
}
func (e *ResponseError) Error() string {
msg := fmt.Sprintf("the server responeded with status %d", e.Response.StatusCode)
if e.Err != nil {
msg = fmt.Sprintf("%s: %s", msg, e.Err.Error())
}
return msg
}
func (e *ResponseError) Unwrap() error {
if e.Err != nil {
return e.Err
}
return e
}
func AsJSON(dst interface{}) ResponseHandler { return DefaultJSONConfig.AsJSON(dst) }
func AsBytesBuffer(dst *bytes.Buffer) ResponseHandler {
return func(r *http.Response, err error) (*http.Response, error) {
if r == nil || err != nil {
return r, err
}
defer r.Body.Close()
_, err = dst.ReadFrom(r.Body)
if err != nil {
return nil, &ResponseError{Response: r, Err: err}
}
return r, nil
}
}
func AsError() ResponseHandler {
return func(r *http.Response, err error) (*http.Response, error) {
if r == nil || err != nil {
return r, err
}
err = hxutil.DrainResponseBody(r)
if err != nil {
return nil, &ResponseError{Response: r, Err: err}
}
return r, &ResponseError{Response: r}
}
}
// AsJSONError is ResponseHandler that will populate an error with the JSON returned within the response body.
// And it will wrap the error with ResponseError and return it.
// err := hx.Post(ctx, "https://example.com/posts",
// hx.JSON(body)
// hx.WhenSuccess(hx.AsJSON(&post), http.StatusBadRequest),
// hx.WhenStatus(hx.AsErrorOf(&InvalidArgument{}), http.StatusBadRequest),
// hx.WhenFailure(hx.AsError()),
// )
// if err != nil {
// var (
// invalidArgErr *InvalidArgument
// respErr *hx.ResponseError
// )
// if errors.As(err, &invalidArgErr) {
// // handle known error
// } else if errors.As(err, &respErr) {
// // handle unknown response error
// } else {
// err := errors.Unwrap(err)
// // handle unknown error
// }
// }
func AsJSONError(dst error) ResponseHandler { return DefaultJSONConfig.AsJSONError(dst) }
func checkStatus(f func(int) bool) func(*http.Response, error) bool {
return func(r *http.Response, err error) bool {
return err == nil && r != nil && f(r.StatusCode)
}
}
type ResponseHandlerCond func(*http.Response, error) bool
func Any(conds ...ResponseHandlerCond) ResponseHandlerCond {
return func(r *http.Response, err error) bool {
for _, c := range conds {
if c(r, err) {
return true
}
}
return false
}
}
func Not(cond ResponseHandlerCond) ResponseHandlerCond {
return func(r *http.Response, err error) bool { return !cond(r, err) }
}
var (
IsSuccess ResponseHandlerCond = checkStatus(func(c int) bool { return c/100 == 2 })
IsFailure ResponseHandlerCond = Not(IsSuccess)
IsClientError ResponseHandlerCond = checkStatus(func(c int) bool { return c/100 == 4 })
IsServerError ResponseHandlerCond = checkStatus(func(c int) bool { return c/100 == 5 })
IsTemporaryError ResponseHandlerCond = func(r *http.Response, err error) bool {
terr, ok := err.(interface{ Temporary() bool })
return ok && terr.Temporary()
}
)
func IsStatus(codes ...int) ResponseHandlerCond {
m := make(map[int]struct{}, len(codes))
for _, c := range codes {
m[c] = struct{}{}
}
return checkStatus(func(code int) bool { _, ok := m[code]; return ok })
}
func When(cond ResponseHandlerCond, rh ResponseHandler) Option {
return HandleResponse(func(resp *http.Response, err error) (*http.Response, error) {
if cond(resp, err) {
return rh(resp, err)
}
return resp, err
})
}
func WhenSuccess(h ResponseHandler) Option { return When(IsSuccess, h) }
func WhenFailure(h ResponseHandler) Option { return When(IsFailure, h) }
func WhenClientError(h ResponseHandler) Option { return When(IsClientError, h) }
func WhenServerError(h ResponseHandler) Option { return When(IsServerError, h) }
func WhenStatus(h ResponseHandler, codes ...int) Option { return When(IsStatus(codes...), h) }