-
Notifications
You must be signed in to change notification settings - Fork 407
Expand file tree
/
Copy pathBaseInterface.java
More file actions
364 lines (320 loc) · 9.07 KB
/
BaseInterface.java
File metadata and controls
364 lines (320 loc) · 9.07 KB
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
package com.sendgrid;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* This class is the base interface to the Twilio SendGrid Web API.
*/
public abstract class BaseInterface implements SendGridAPI {
private static final String VERSION = "4.10.3";
private static final String USER_AGENT = "sendgrid/" + VERSION + ";java";
private static final int RATE_LIMIT_RESPONSE_CODE = 429;
private static final int THREAD_POOL_SIZE = 8;
private static final Map<String, String> allowedRegionsHostMap;
static {
//todo: change this to Map.of() when we've moved on from Java 8
allowedRegionsHostMap = new HashMap<>();
allowedRegionsHostMap.put("eu", "api.eu.sendgrid.com");
allowedRegionsHostMap.put("global", "api.sendgrid.com");
}
private ExecutorService pool;
/**
* The host to which to connect.
*/
private String host;
/**
* The API version.
*/
private String version;
/**
* The HTTP client.
*/
private Client client;
/**
* The request headers container.
*/
private Map<String, String> requestHeaders;
/**
* The number of times to try after a rate limit.
*/
private int rateLimitRetry;
/**
* The number of milliseconds to sleep between retries.
*/
private int rateLimitSleep;
/**
* The subuser to be impersonated.
*/
private String subuser;
/**
* Construct a new API wrapper.
*/
public BaseInterface() {
this.client = new Client();
}
/**
* Construct a new API wrapper.
*
* @param test is true if you are unit testing
*/
public BaseInterface(final Boolean test) {
this.client = new Client(test);
}
/**
* Construct a new API wrapper.
*
* @param client the Client to use (allows to customize its configuration)
*/
public BaseInterface(final Client client) {
this.client = client;
}
/**
* Initialize the client.
*
* @param auth authorization header value
* @param host the base URL for the API
*/
public void initialize(final String auth, final String host) {
this.host = host;
this.version = "v3";
this.requestHeaders = new HashMap<>();
this.requestHeaders.put("Authorization", auth);
this.requestHeaders.put("User-Agent", USER_AGENT);
this.requestHeaders.put("Accept", "application/json");
this.rateLimitRetry = 5;
this.rateLimitSleep = 1100;
this.pool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
/**
* Get the current library version.
*
* @return the current version
*/
public String getLibraryVersion() {
return VERSION;
}
/**
* Get the API version.
*
* @return the current API version
*/
public String getVersion() {
return this.version;
}
/**
* Set the API version.
*
* @param version the new version
*/
public void setVersion(final String version) {
this.version = version;
}
/**
* Get the request headers.
*
* @return the request headers
*/
public Map<String, String> getRequestHeaders() {
return this.requestHeaders;
}
/**
* Add/update a request header.
*
* @param key the header key
* @param value the header value
* @return the new set of request headers
*/
public Map<String, String> addRequestHeader(final String key, final String value) {
this.requestHeaders.put(key, value);
return getRequestHeaders();
}
/**
* Remove a request header.
*
* @param key the header key to remove
* @return the new set of request headers
*/
public Map<String, String> removeRequestHeader(final String key) {
this.requestHeaders.remove(key);
return getRequestHeaders();
}
/**
* Get the host.
*
* @return the host
*/
public String getHost() {
return this.host;
}
/**
* Set the host.
*
* @param host the new host
*/
public void setHost(final String host) {
this.host = host;
}
/**
* Get the maximum number of retries on a rate limit response.
* The default is 5.
*
* @return the number of retries on a rate limit
*/
public int getRateLimitRetry() {
return this.rateLimitRetry;
}
/**
* Set the maximum number of retries on a rate limit response.
*
* @param rateLimitRetry the maximum retry count
*/
public void setRateLimitRetry(final int rateLimitRetry) {
this.rateLimitRetry = rateLimitRetry;
}
/**
* Get the duration of time (in milliseconds) to sleep between
* consecutive rate limit retries. The Twilio SendGrid API enforces
* the rate limit to the second. The default value is 1.1 seconds.
*
* @return the sleep duration
*/
public int getRateLimitSleep() {
return this.rateLimitSleep;
}
/**
* Set the duration of time (in milliseconds) to sleep between
* consecutive rate limit retries.
*
* @param rateLimitSleep the sleep duration
*/
public void setRateLimitSleep(final int rateLimitSleep) {
this.rateLimitSleep = rateLimitSleep;
}
/**
* Impersonate subuser for subsequent requests
*
* @param subuser the subuser to be impersonated
*/
public void addImpersonateSubuser(final String subuser) {
this.subuser = subuser;
this.addRequestHeader("on-behalf-of", subuser);
}
/**
* Stop Impersonating the subuser
*/
public void removeImpersonateSubuser() {
this.subuser = null;
this.removeRequestHeader("on-behalf-of");
}
/**
* Get the impersonated subuser or null if empty
*
* @return the impersonated subuser
*/
public String getImpersonateSubuser() {
return this.subuser;
}
/**
* Makes the call to the Twilio SendGrid API, override this method for testing.
*
* @param request the request to make
* @return the response object
* @throws IOException in case of a network error
*/
public Response makeCall(final Request request) throws IOException {
return client.api(request);
}
/**
* Class api sets up the request to the Twilio SendGrid API, this is main interface.
*
* @param request the request object
* @return the response object
* @throws IOException in case of a network error
*/
public Response api(final Request request) throws IOException {
final Request req = new Request();
req.setMethod(request.getMethod());
req.setBaseUri(this.host);
req.setEndpoint("/" + version + "/" + request.getEndpoint());
req.setBody(request.getBody());
req.getHeaders().putAll(this.requestHeaders);
req.getHeaders().putAll(request.getHeaders());
req.getQueryParams().putAll(request.getQueryParams());
return makeCall(req);
}
/**
* Attempt an API call. This method executes the API call asynchronously
* on an internal thread pool. If the call is rate limited, the thread
* will retry up to the maximum configured time.
*
* @param request the API request
*/
public void attempt(final Request request) {
this.attempt(request, new APICallback() {
@Override
public void error(final Exception ex) {
}
public void response(final Response r) {
}
});
}
/**
* Attempt an API call. This method executes the API call asynchronously
* on an internal thread pool. If the call is rate limited, the thread
* will retry up to the maximum configured time. The supplied callback
* will be called in the event of an error, or a successful response.
*
* @param request the API request
* @param callback the callback
*/
public void attempt(final Request request, final APICallback callback) {
this.pool.execute(new Runnable() {
@Override
public void run() {
Response response;
// Retry until the retry limit has been reached.
for (int i = 0; i < rateLimitRetry; ++i) {
try {
response = api(request);
} catch (IOException ex) {
// Stop retrying if there is a network error.
callback.error(ex);
return;
}
// We have been rate limited.
if (response.getStatusCode() == RATE_LIMIT_RESPONSE_CODE) {
try {
Thread.sleep(rateLimitSleep);
} catch (InterruptedException ex) {
// Can safely ignore this exception and retry.
}
} else {
callback.response(response);
return;
}
}
// Retries exhausted. Return error.
callback.error(new RateLimitException(request, rateLimitRetry));
}
});
}
/*
* Client libraries contain setters for specifying region/edge.
* This allows support global and eu regions only. This set will likely expand in the future.
* Global should be the default
* Global region means the message should be sent through:
* HTTP: api.sendgrid.com
* EU region means the message should be sent through:
* HTTP: api.eu.sendgrid.com
*/
public void setDataResidency(String region){
if (allowedRegionsHostMap.containsKey(region)){
this.host = allowedRegionsHostMap.get(region);
}
else{
throw new IllegalArgumentException("region can only be \"eu\" or \"global\"");
}
}
}