-
Notifications
You must be signed in to change notification settings - Fork 0
/
restreq.go
223 lines (192 loc) · 5.07 KB
/
restreq.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package restreq
import (
"bytes"
"context"
"encoding/json"
"log"
"net/http"
"time"
)
type httpClient interface {
Do(req *http.Request) (*http.Response, error)
}
// Response inherits from http.Response, so you can use almost every
// field and method of http.Response.
//
// http.Response.Body is an exception. You cannot use it, because
// content of http.Response.Body is copied to Response.Body.
// You don't have to call http.Response.Body.Close()
type Response struct {
*http.Response
Body []byte
}
// Header returns header
func (r *Response) Header(s string) string {
return r.Response.Header.Get(s)
}
// DecodeJSON decodes JSON
func (r *Response) DecodeJSON(s any) error {
return json.Unmarshal(r.Body, &s)
}
type requester interface {
Context(context.Context) requester
SetHTTPClient(httpClient) requester
AddHeader(string, string) requester
AddCookie(*http.Cookie) requester
AddJSONKeyValue(string, any) requester
SetTimeoutSec(int) requester
SetUserAgent(string) requester
SetContentType(string) requester
SetContentTypeJSON() requester
SetJSONPayload(any) requester
SetBasicAuth(username, password string) requester
Debug(*log.Logger, DebugFlag) requester
WithBodyReader() requester
Post() (*Response, error)
Put() (*Response, error)
Patch() (*Response, error)
Get() (*Response, error)
Delete() (*Response, error)
}
// Request contains all methods to operate on REST API
type Request struct {
ctx context.Context
timeout time.Duration
url string
json map[string]any
headers map[string]string
cookies map[string]*http.Cookie
username string
password string
jsonPayload []byte
client httpClient
debugFlags int32
logger *log.Logger
bodyReader bool
}
func New(u string) *Request {
return &Request{
url: u,
json: make(map[string]any),
headers: make(map[string]string),
cookies: make(map[string]*http.Cookie),
}
}
type DebugFlag int32
// DebugFlags to control logger behavior.
const (
// Debug request body
ReqBody DebugFlag = 1 << iota
// Debug request headers
ReqHeaders
// Debug request cookies
ReqCookies
// Debug response body
RespBody
// Debug response header
RespHeaders
// Debug response cookies
RespCookies
)
// WithBodyReader allows direct reading from http.Response.Body without
// copying to restreq.Response.Body
func (r *Request) WithBodyReader() requester {
r.bodyReader = true
return r
}
// SetHTTPClient sets external http client.
func (r *Request) SetHTTPClient(c httpClient) requester {
r.client = c
return r
}
// Debug sets logger and debug flags.
// You can combine flags, ReqBody+ReqHeader etc.
func (r *Request) Debug(logger *log.Logger, flags DebugFlag) requester {
r.debugFlags = int32(flags)
r.logger = logger
return r
}
// SetJSONPayload encodes map or struct to json byte array.
func (r *Request) SetJSONPayload(p any) requester {
w := bytes.NewBuffer([]byte{})
json.NewEncoder(w).Encode(p)
r.jsonPayload = w.Bytes()
return r
}
// SetBasicAuth sets basic auth with username and password.
func (r *Request) SetBasicAuth(username, password string) requester {
r.username = username
r.password = password
return r
}
// AddCookie adds cookie to request.
func (r *Request) AddCookie(c *http.Cookie) requester {
r.cookies[c.Name] = c
return r
}
// SetContentType sets Content-Type.
func (r *Request) SetContentType(s string) requester {
r.headers["Content-Type"] = s
return r
}
// SetContentTypeJSON sets Content-Type to application/json.
func (r *Request) SetContentTypeJSON() requester {
r.headers["Content-Type"] = "application/json"
return r
}
// SetUserAgent sets User-Agent header.
func (r *Request) SetUserAgent(s string) requester {
r.headers["User-Agent"] = s
return r
}
// Context sets context to ctx
func (r *Request) Context(ctx context.Context) requester {
r.ctx = ctx
return r
}
// SetTimeoutSec sets connection timeout.
func (r *Request) SetTimeoutSec(t int) requester {
r.timeout = time.Second * time.Duration(t)
return r
}
// AddHeader adds header with value.
func (r *Request) AddHeader(k string, v string) requester {
r.headers[k] = v
return r
}
// AddJSONKeyValue converts KV to json byte array.
// You can add many KV, they will be added to the map
// and converted to an byte array when the request is sent.
func (r *Request) AddJSONKeyValue(key string, value any) requester {
if key == "" || value == "" {
return r
}
r.json[key] = value
return r
}
// Post executes the post method
func (r *Request) Post() (*Response, error) {
return r.do(http.MethodPost)
}
// Get executes the get method
func (r *Request) Get() (*Response, error) {
return r.do(http.MethodGet)
}
// Delete executes the delete method
func (r *Request) Delete() (*Response, error) {
return r.do(http.MethodDelete)
}
// Patch executes the patch method
func (r *Request) Patch() (*Response, error) {
return r.do(http.MethodPatch)
}
// Put executes the put method
func (r *Request) Put() (*Response, error) {
return r.do(http.MethodPut)
}
func (r *Request) debug(f DebugFlag, s string) {
if r.logger == nil || r.debugFlags&(1<<(f-1)) == 0 {
return
}
r.logger.Printf("%s\n", s)
}