-
Notifications
You must be signed in to change notification settings - Fork 0
/
rest_client.go
136 lines (109 loc) · 3.06 KB
/
rest_client.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
package tatsu_api
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"golang.org/x/xerrors"
)
type restClient struct {
apikey string
bucket *bucket
httpClient *http.Client
}
func newRestClient(apikey string) *restClient {
return &restClient{
apikey: apikey,
bucket: newBucket(),
httpClient: http.DefaultClient,
}
}
func (rc *restClient) get(ctx context.Context, endpoint string, v interface{}) error {
// Get rate limit clearance.
rc.bucket.acquire()
// Create request.
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return xerrors.Errorf("create request: %w", err)
}
// Set headers.
rc.setHeaders(req)
// Execute request.
resp, err := rc.httpClient.Do(req)
if err != nil {
return xerrors.Errorf("execute request: %w", err)
}
// Defer closing body.
defer resp.Body.Close()
// Pass headers to bucket.
rc.bucket.parseHeaders(resp.Header)
// Return error if status is not OK.
if resp.StatusCode != http.StatusOK {
// Try parsing error body.
var errorResp ApiError
err = json.NewDecoder(resp.Body).Decode(&errorResp)
if err != nil {
// Return status as error.
return xerrors.New(resp.Status)
}
return xerrors.New(fmt.Sprintf("%d: %s", errorResp.Code, errorResp.Message))
}
// Parse response.
err = json.NewDecoder(resp.Body).Decode(v)
if err != nil {
return xerrors.Errorf("parse response: %w", err)
}
return nil
}
func (rc *restClient) patch(ctx context.Context, endpoint string, r interface{}, v interface{}) error {
// Acquire bucket token.
rc.bucket.acquire()
// Marshal request body.
buf := &bytes.Buffer{}
err := json.NewEncoder(buf).Encode(r)
if err != nil {
return xerrors.Errorf("marshal request body: %w", err)
}
// Create request.
req, err := http.NewRequestWithContext(ctx, http.MethodPatch, endpoint, buf)
if err != nil {
return xerrors.Errorf("create request: %w", err)
}
// Set headers.
rc.setHeaders(req)
// Execute request.
resp, err := rc.httpClient.Do(req)
if err != nil {
return xerrors.Errorf("execute request: %w", err)
}
// Defer closing body.
defer resp.Body.Close()
// Pass headers to bucket.
rc.bucket.parseHeaders(resp.Header)
// Return error if status is not 200 OK or 204 No Content.
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
// Try parsing error body.
var errorResp ApiError
err = json.NewDecoder(resp.Body).Decode(&errorResp)
if err != nil {
// Return status as error.
return xerrors.New(resp.Status)
}
return xerrors.New(fmt.Sprintf("%d: %s", errorResp.Code, errorResp.Message))
}
// If status 204 No Content, return.
if resp.StatusCode == http.StatusNoContent {
return nil
}
// Status is 200 OK, parse response.
err = json.NewDecoder(resp.Body).Decode(v)
if err != nil {
return xerrors.Errorf("parse response: %w", err)
}
return nil
}
func (rc *restClient) setHeaders(r *http.Request) {
r.Header.Set("Authorization", rc.apikey)
r.Header.Set("User-Agent", "Tatsu API Go/"+version+" (Hassie, https://github.com/tatsuworks/tatsu-api-go)")
}