@@ -16,13 +16,22 @@ import (
16
16
17
17
const apiVersion = "3.2"
18
18
19
+ // RetryStrategyFunc is called by each API method if set to retry when handling an error.
20
+ // If not set, there will be no retry at all.
21
+ //
22
+ // It accepts two arguments: attempts - number of sent requests (starting from 0)
23
+ // and err - error as ErrAPI struct (with StatusCode and Details)
24
+ // It returns info whether to retry the request.
25
+ type RetryStrategyFunc func (attempts uint , err error ) bool
26
+
19
27
type api struct {
20
28
httpClient * http.Client
21
29
clientID string
22
30
tokenGetter authorization.TokenGetter
23
31
httpRequestGenerator HTTPRequestGenerator
24
32
host string
25
33
customHeaders http.Header
34
+ retryStrategy RetryStrategyFunc
26
35
}
27
36
28
37
// HTTPRequestGenerator is called by each API method to generate api http url.
@@ -59,12 +68,9 @@ func (a *api) Call(action string, reqPayload interface{}, respPayload interface{
59
68
if err != nil {
60
69
return err
61
70
}
62
- token := a .tokenGetter ()
63
- if token == nil {
64
- return fmt .Errorf ("couldn't get token" )
65
- }
66
- if token .Type != authorization .BearerToken && token .Type != authorization .BasicToken {
67
- return fmt .Errorf ("unsupported token type" )
71
+ token , err := a .getToken ()
72
+ if err != nil {
73
+ return err
68
74
}
69
75
70
76
req , err := a .httpRequestGenerator (token , a .host , action )
@@ -93,6 +99,11 @@ func (a *api) SetCustomHeader(key, val string) {
93
99
a .customHeaders .Set (key , val )
94
100
}
95
101
102
+ // SetRetryStrategy allows to set a retry strategy that will be performed in every failed request
103
+ func (a * api ) SetRetryStrategy (f RetryStrategyFunc ) {
104
+ a .retryStrategy = f
105
+ }
106
+
96
107
type fileUploadAPI struct { * api }
97
108
98
109
// NewAPIWithFileUpload returns ready to use raw API client with file upload functionality.
@@ -133,7 +144,7 @@ func (a *fileUploadAPI) UploadFile(filename string, file []byte) (string, error)
133
144
req .Body = ioutil .NopCloser (body )
134
145
135
146
req .Header .Set ("Content-Type" , writer .FormDataContentType ())
136
- req .Header .Set ("Authorization" , fmt .Sprintf ("Bearer %s" , token .AccessToken ))
147
+ req .Header .Set ("Authorization" , fmt .Sprintf ("%s %s" , token . Type , token .AccessToken ))
137
148
req .Header .Set ("User-agent" , fmt .Sprintf ("GO SDK Application %s" , a .clientID ))
138
149
req .Header .Set ("X-Region" , token .Region )
139
150
@@ -145,28 +156,59 @@ func (a *fileUploadAPI) UploadFile(filename string, file []byte) (string, error)
145
156
}
146
157
147
158
func (a * api ) send (req * http.Request , respPayload interface {}) error {
148
- resp , err := a .httpClient .Do (req )
149
- if err != nil {
150
- return err
151
- }
152
- defer resp .Body .Close ()
153
- bodyBytes , err := ioutil .ReadAll (resp .Body )
154
- if resp .StatusCode != http .StatusOK {
155
- apiErr := & api_errors.ErrAPI {}
156
- if err := json .Unmarshal (bodyBytes , apiErr ); err != nil {
157
- return fmt .Errorf ("couldn't unmarshal error response: %s (code: %d, raw body: %s)" , err .Error (), resp .StatusCode , string (bodyBytes ))
159
+ var attempts uint
160
+ var do func () error
161
+
162
+ do = func () error {
163
+ resp , err := a .httpClient .Do (req )
164
+ if err != nil {
165
+ return err
158
166
}
159
- if apiErr .Error () == "" {
160
- return fmt .Errorf ("couldn't unmarshal error response (code: %d, raw body: %s)" , resp .StatusCode , string (bodyBytes ))
167
+ defer resp .Body .Close ()
168
+ bodyBytes , err := ioutil .ReadAll (resp .Body )
169
+ if resp .StatusCode != http .StatusOK {
170
+ apiErr := & api_errors.ErrAPI {}
171
+ if err := json .Unmarshal (bodyBytes , apiErr ); err != nil {
172
+ return fmt .Errorf ("couldn't unmarshal error response: %s (code: %d, raw body: %s)" , err .Error (), resp .StatusCode , string (bodyBytes ))
173
+ }
174
+ if apiErr .Error () == "" {
175
+ return fmt .Errorf ("couldn't unmarshal error response (code: %d, raw body: %s)" , resp .StatusCode , string (bodyBytes ))
176
+ }
177
+
178
+ if a .retryStrategy == nil || ! a .retryStrategy (attempts , apiErr ) {
179
+ return apiErr
180
+ }
181
+
182
+ token , err := a .getToken ()
183
+ if err != nil {
184
+ return err
185
+ }
186
+
187
+ req .Header .Set ("Authorization" , fmt .Sprintf ("%s %s" , token .Type , token .AccessToken ))
188
+ attempts ++
189
+ return do ()
190
+ }
191
+
192
+ if err != nil {
193
+ return err
161
194
}
162
- return apiErr
195
+
196
+ return json .Unmarshal (bodyBytes , respPayload )
163
197
}
164
198
165
- if err != nil {
166
- return err
199
+ return do ()
200
+ }
201
+
202
+ func (a * api ) getToken () (* authorization.Token , error ) {
203
+ token := a .tokenGetter ()
204
+ if token == nil {
205
+ return nil , fmt .Errorf ("couldn't get token" )
206
+ }
207
+ if token .Type != authorization .BearerToken && token .Type != authorization .BasicToken {
208
+ return nil , fmt .Errorf ("unsupported token type" )
167
209
}
168
210
169
- return json . Unmarshal ( bodyBytes , respPayload )
211
+ return token , nil
170
212
}
171
213
172
214
// SetCustomHost allows to change API host address. This method is mostly for LiveChat internal testing and should not be used in production environments.
0 commit comments