-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbase.go
160 lines (133 loc) · 3.39 KB
/
base.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
package requests
import (
"fmt"
"net/http"
"net/url"
"time"
)
// Request hold the request builder data
type Request struct {
method stringer
baseUrl stringer
path stringer
body stringer
header stringerMap
query stringerMap
secrets stringerMap
timeout time.Duration
err error
doer Doer // this doer should do all error handling, if it returns err=nil we are ready to use the payload
}
type Doer interface {
// Do attempt to do one http request (and retries/redirects)
Do(r *http.Request) (*http.Response, error)
}
type stringerMap map[string]stringer
func (m stringerMap) CopyTo(newMap stringerMap) {
for k, v := range m {
newMap[k] = v
}
}
type defaultDoer struct {
doer Doer
}
func (d defaultDoer) Do(r *http.Request) (*http.Response, error) {
resp, err := d.doer.Do(r)
if err != nil {
return nil, err
} else if resp.StatusCode != 200 {
_ = drain(resp.Body)
return nil, fmt.Errorf("invalid status %s", resp.Status)
}
return resp, nil
}
// New creates a new *Request
func New(url interface{}) *Request {
c := &Request{header: map[string]stringer{}, query: map[string]stringer{}, doer: &defaultDoer{doer: http.DefaultClient}, secrets: map[string]stringer{}}
return c.Url(url).Path("")
}
func FromRawURL(rawUrl string) (*Request, error) {
u, err := url.Parse(rawUrl)
if err != nil {
return nil, err
}
q, fragment := u.Query(), u.Fragment
_ = fragment // todo: use fragment
u.RawQuery, u.Fragment = "", ""
req := New(u.String())
for k, v := range q {
req.Query(k, v[0])
}
return req, nil
}
const (
applicationJSON = "application/json"
)
func (req *Request) toStringer(v interface{}) stringer {
return req._toStringer(v, false)
}
func (req *Request) _toStringer(v interface{}, secret bool) stringer {
s := toStringer(v)
if s == nil {
req.setErr(fmt.Errorf("can not convert %v to stringer", v))
}
if secret {
key := fmt.Sprintf("MASKED_%d", len(req.secrets)+1)
req.Secret(key, s)
return toStringer(SecretKey(key))
} else {
return s
}
}
func (req *Request) setErr(err error) {
if req.err == nil {
req.err = err
}
}
// Header sets a http header
func (req *Request) Header(key string, value interface{}) *Request {
req.header[key] = req.toStringer(value)
return req
}
// SecretHeader sets a http header
func (req *Request) SecretHeader(key string, value interface{}) *Request {
req.header[key] = req._toStringer(value, true)
return req
}
func SecretKey(key string) string {
return "${" + key + "}"
}
func (req *Request) Secret(key string, value interface{}) *Request {
req.secrets[SecretKey(key)] = req.toStringer(value)
return req
}
// Path sets a path
func (req *Request) Path(value interface{}) *Request {
req.path = req.toStringer(value)
return req
}
// Timeout sets the timeout
func (req *Request) Timeout(d time.Duration) *Request {
req.timeout = d
return req
}
// Body set the http body
func (req *Request) Body(contentType string, value interface{}) *Request {
req.body = req.toStringer(value)
return req.ContentType(contentType)
}
// Query sets a http query
func (req *Request) Query(key string, value interface{}) *Request {
req.query[key] = req.toStringer(value)
return req
}
// Method sets the http method
func (req *Request) Method(method interface{}) *Request {
req.method = req.toStringer(method)
return req
}
// Url sets the http url
func (req *Request) Url(url interface{}) *Request {
req.baseUrl = req.toStringer(url)
return req
}