This repository has been archived by the owner on Jul 24, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
request.go
178 lines (155 loc) · 4.03 KB
/
request.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
package aftership
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
"time"
"github.com/google/go-querystring/query"
"github.com/google/uuid"
)
// makeRequest makes a AfterShip API calls
func (client *Client) makeRequest(ctx context.Context, method string, path string,
queryParams interface{}, inputData interface{}, resultData interface{}) error {
// Read input data
var body io.Reader
var bodyStr string
if inputData != nil {
jsonData, err := json.Marshal(inputData)
if err != nil {
return &APIError{
Code: codeJSONError,
Message: errMarshallingJSON,
}
}
bodyStr = string(jsonData)
body = bytes.NewBuffer(jsonData)
}
req, err := http.NewRequestWithContext(ctx, method, client.Config.BaseURL+path, body)
if err != nil {
return &APIError{
Code: codeBadRequest,
Message: "Bad request.",
}
}
apiKey := client.Config.APIKey
// Add headers
contentType := "application/json"
req.Header.Add("Content-Type", contentType)
req.Header.Add("request-id", uuid.New().String())
req.Header.Add("User-Agent", fmt.Sprintf("%s/%s", client.Config.UserAgentPrefix, VERSION))
req.Header.Add("aftership-agent", fmt.Sprintf("go-sdk-%s", VERSION))
req.Header.Add("as-api-key", apiKey)
if queryParams != nil {
queryStringObj, err := query.Values(queryParams)
if err != nil {
return &APIError{
Code: codeBadParam,
Message: "Error when parsing query parameters.",
}
}
req.URL.RawQuery = queryStringObj.Encode()
}
authenticationType := client.Config.AuthenticationType
// set signature
if authenticationType == AES {
asHeaders := make(map[string]string)
for key, value := range req.Header {
asHeaders[key] = value[0]
}
date := time.Now().UTC().Format(http.TimeFormat)
signatureHeader, signature, err := GetSignature(
authenticationType, []byte(client.Config.APISecret), asHeaders,
contentType, req.URL.RequestURI(), req.Method, date, bodyStr)
if err != nil {
return &APIError{
Code: codeSignatureError,
Message: "Error when generating the request signature.",
}
}
req.Header.Add("date", date)
req.Header.Add(signatureHeader, signature)
}
// Send request
resp, err := client.httpClient.Do(req)
if err != nil {
if os.IsTimeout(err) {
return &APIError{
Code: codeRequestTimeout,
Message: "HTTP request timeout.",
}
}
return &APIError{
Code: codeRequestFailed,
Message: "HTTP request failed.",
}
}
defer resp.Body.Close()
contents, err := ioutil.ReadAll(resp.Body)
if err != nil {
return &APIError{
Code: codeEmptyBody,
Message: "Unable to parse the API response.",
}
}
// Rate Limit
setRateLimit(client.rateLimit, resp)
result := &Response{
Meta: Meta{},
Data: resultData,
}
// Unmarshal response object
err = json.Unmarshal(contents, result)
if err != nil {
return &APIError{
Code: codeJSONError,
Message: "Invalid JSON data.",
}
}
if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
// The 2xx range indicate success
return nil
}
apiError := APIError{
Type: result.Meta.Type,
Code: result.Meta.Code,
Message: result.Meta.Message,
Path: path,
}
// Too many requests error
if resp.StatusCode == http.StatusTooManyRequests {
return &TooManyRequestsError{
APIError: apiError,
RateLimit: client.rateLimit,
}
}
// API error
return &apiError
}
func setRateLimit(rateLimit *RateLimit, resp *http.Response) {
if rateLimit != nil && resp != nil && resp.Header != nil {
// reset timestamp
if reset := resp.Header.Get("x-ratelimit-reset"); reset != "" {
if n, err := strconv.ParseInt(reset, 10, 64); err == nil {
rateLimit.Reset = n
}
}
// limit
if limit := resp.Header.Get("x-ratelimit-limit"); limit != "" {
if i, err := strconv.Atoi(limit); err == nil {
rateLimit.Limit = i
}
}
// remaining
if remaining := resp.Header.Get("x-ratelimit-remaining"); remaining != "" {
if i, err := strconv.Atoi(remaining); err == nil {
rateLimit.Remaining = i
}
}
}
}