-
Notifications
You must be signed in to change notification settings - Fork 126
Expand file tree
/
Copy pathApiClient.java
More file actions
383 lines (330 loc) · 11.9 KB
/
ApiClient.java
File metadata and controls
383 lines (330 loc) · 11.9 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
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
package com.plaid.client;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.JsonElement;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
import com.plaid.client.auth.HttpBasicAuth;
import com.plaid.client.auth.HttpBearerAuth;
import com.plaid.client.request.PlaidApi;
import com.plaid.client.auth.ApiKeyAuth;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class ApiClient {
private Map<String, Interceptor> apiAuthorizations;
private OkHttpClient.Builder okBuilder;
private Retrofit.Builder adapterBuilder;
private JSON json;
private OkHttpClient okHttpClient;
public static final String Production = "https://production.plaid.com";
public static final String Development = "https://development.plaid.com";
public static final String Sandbox = "https://sandbox.plaid.com";
public ApiClient() {
apiAuthorizations = new LinkedHashMap<String, Interceptor>();
createDefaultAdapter();
okBuilder = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS).connectTimeout(60, TimeUnit.SECONDS)
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.header("User-Agent", "Plaid Java v11.4.0")
.header("Plaid-Version", "2020-09-14")
.build();
return chain.proceed(requestWithUserAgent);
}
});
}
public ApiClient(OkHttpClient client){
apiAuthorizations = new LinkedHashMap<String, Interceptor>();
createDefaultAdapter();
okHttpClient = client;
}
public ApiClient(String[] authNames) {
this();
for(String authName : authNames) {
Interceptor auth;
if ("clientId".equals(authName)) {
auth = new ApiKeyAuth("header", "PLAID-CLIENT-ID");
} else if ("plaidVersion".equals(authName)) {
auth = new ApiKeyAuth("header", "Plaid-Version");
} else if ("secret".equals(authName)) {
auth = new ApiKeyAuth("header", "PLAID-SECRET");
} else {
throw new RuntimeException("auth name \"" + authName + "\" not found in available auth names");
}
addAuthorization(authName, auth);
}
}
/**
* Helper constructor for multiple api keys. Preferred constructor
*/
public ApiClient(Map<String, String> apiKeys) {
this();
for(String authName : apiKeys.keySet()) {
Interceptor auth;
if ("clientId".equals(authName)) {
ApiKeyAuth apiKeyAuth = new ApiKeyAuth("header", "PLAID-CLIENT-ID");
apiKeyAuth.setApiKey(apiKeys.get(authName));
auth = (Interceptor) apiKeyAuth;
} else if ("plaidVersion".equals(authName)) {
ApiKeyAuth apiKeyAuth = new ApiKeyAuth("header", "Plaid-Version");
apiKeyAuth.setApiKey(apiKeys.get(authName));
auth = (Interceptor) apiKeyAuth;
} else if ("secret".equals(authName)) {
ApiKeyAuth apiKeyAuth = new ApiKeyAuth("header", "PLAID-SECRET");
apiKeyAuth.setApiKey(apiKeys.get(authName));
auth = (Interceptor) apiKeyAuth;
} else {
throw new RuntimeException("auth name \"" + authName + "\" not found in available auth names");
}
addAuthorization(authName, auth);
}
}
/**
* Helper constructor for creating an ApiClient and setting the environment
* @param apiKeys
* @param environment
*/
public ApiClient(Map<String, String> apiKeys, Environment environment) {
this(apiKeys);
this.setPlaidAdapter(environment);
}
/**
* Basic constructor for single auth name
* @param authName Authentication name
*/
public ApiClient(String authName) {
this(new String[]{authName});
}
/**
* Helper constructor for single api key
* @param authName Authentication name
* @param apiKey API key
*/
public ApiClient(String authName, String apiKey) {
this(authName);
this.setApiKey(apiKey);
}
/**
* Helper constructor for single basic auth or password oauth2
* @param authName Authentication name
* @param username Username
* @param password Password
*/
public ApiClient(String authName, String username, String password) {
this(authName);
this.setCredentials(username, password);
}
public void createDefaultAdapter() {
json = new JSON();
String baseUrl = "https://production.plaid.com";
if (!baseUrl.endsWith("/"))
baseUrl = baseUrl + "/";
adapterBuilder = new Retrofit
.Builder()
.baseUrl(baseUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonCustomConverterFactory.create(json.getGson()));
}
public void setPlaidAdapter(Environment environment) {
json = new JSON();
String baseUrl = environment.toString();
// final List<String> PLAID_ENVS = Arrays.asList("https://sandbox.plaid.com", "https://production.plaid.com", "https://development.plaid.com");
//
// if(!PLAID_ENVS.contains(baseUrl)) {
// System.out.println("baseUrl not found in PLAID_ENVS, must be one of the following: https://sandbox.plaid.com, https://production.plaid.com, https://development.plaid.com");
// }
//
// if (!baseUrl.endsWith("/"))
// baseUrl = baseUrl + "/";
adapterBuilder = new Retrofit
.Builder()
.baseUrl(baseUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonCustomConverterFactory.create(json.getGson()));
}
public <S> S createService(Class<S> serviceClass) {
if (okHttpClient != null) {
return adapterBuilder.client(okHttpClient).build().create(serviceClass);
} else {
return adapterBuilder.client(okBuilder.build()).build().create(serviceClass);
}
}
public PlaidApi newPlaidApi() {
return createService(PlaidApi.class);
}
public ApiClient setDateFormat(DateFormat dateFormat) {
this.json.setDateFormat(dateFormat);
return this;
}
public ApiClient setSqlDateFormat(DateFormat dateFormat) {
this.json.setSqlDateFormat(dateFormat);
return this;
}
public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) {
this.json.setOffsetDateTimeFormat(dateFormat);
return this;
}
public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) {
this.json.setLocalDateFormat(dateFormat);
return this;
}
/**
* Helper method to configure the first api key found
* @param apiKey API key
* @return ApiClient
*/
public ApiClient setApiKey(String apiKey) {
for(Interceptor apiAuthorization : apiAuthorizations.values()) {
if (apiAuthorization instanceof ApiKeyAuth) {
ApiKeyAuth keyAuth = (ApiKeyAuth) apiAuthorization;
keyAuth.setApiKey(apiKey);
return this;
}
}
return this;
}
/**
* Helper method to set token for the first Http Bearer authentication found.
* @param bearerToken Bearer token
* @return ApiClient
*/
public ApiClient setBearerToken(String bearerToken) {
for (Interceptor apiAuthorization : apiAuthorizations.values()) {
if (apiAuthorization instanceof HttpBearerAuth) {
((HttpBearerAuth) apiAuthorization).setBearerToken(bearerToken);
return this;
}
}
return this;
}
/**
* Helper method to configure the username/password for basic auth or password oauth
* @param username Username
* @param password Password
* @return ApiClient
*/
public ApiClient setCredentials(String username, String password) {
for(Interceptor apiAuthorization : apiAuthorizations.values()) {
if (apiAuthorization instanceof HttpBasicAuth) {
HttpBasicAuth basicAuth = (HttpBasicAuth) apiAuthorization;
basicAuth.setCredentials(username, password);
return this;
}
}
return this;
}
/**
* Adds an authorization to be used by the client
* @param authName Authentication name
* @param authorization Authorization interceptor
* @return ApiClient
*/
public ApiClient addAuthorization(String authName, Interceptor authorization) {
if (apiAuthorizations.containsKey(authName)) {
throw new RuntimeException("auth name \"" + authName + "\" already in api authorizations");
}
apiAuthorizations.put(authName, authorization);
if(okBuilder == null){
throw new RuntimeException("The ApiClient was created with a built OkHttpClient so it's not possible to add an authorization interceptor to it");
}
okBuilder.addInterceptor(authorization);
return this;
}
public Map<String, Interceptor> getApiAuthorizations() {
return apiAuthorizations;
}
public ApiClient setApiAuthorizations(Map<String, Interceptor> apiAuthorizations) {
this.apiAuthorizations = apiAuthorizations;
return this;
}
public Retrofit.Builder getAdapterBuilder() {
return adapterBuilder;
}
public ApiClient setAdapterBuilder(Retrofit.Builder adapterBuilder) {
this.adapterBuilder = adapterBuilder;
return this;
}
public OkHttpClient.Builder getOkBuilder() {
return okBuilder;
}
public void addAuthsToOkBuilder(OkHttpClient.Builder okBuilder) {
for(Interceptor apiAuthorization : apiAuthorizations.values()) {
okBuilder.addInterceptor(apiAuthorization);
}
}
/**
* Clones the okBuilder given in parameter, adds the auth interceptors and uses it to configure the Retrofit
* @param okClient An instance of OK HTTP client
*/
public void configureFromOkclient(OkHttpClient okClient) {
this.okBuilder = okClient.newBuilder();
addAuthsToOkBuilder(this.okBuilder);
}
}
/**
* This wrapper is to take care of this case:
* when the deserialization fails due to JsonParseException and the
* expected type is String, then just return the body string.
*/
class GsonResponseBodyConverterToString<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final Type type;
GsonResponseBodyConverterToString(Gson gson, Type type) {
this.gson = gson;
this.type = type;
}
@Override public T convert(ResponseBody value) throws IOException {
String returned = value.string();
try {
return gson.fromJson(returned, type);
}
catch (JsonParseException e) {
return (T) returned;
}
}
}
class GsonCustomConverterFactory extends Converter.Factory
{
private final Gson gson;
private final GsonConverterFactory gsonConverterFactory;
public static GsonCustomConverterFactory create(Gson gson) {
return new GsonCustomConverterFactory(gson);
}
private GsonCustomConverterFactory(Gson gson) {
if (gson == null)
throw new NullPointerException("gson == null");
this.gson = gson;
this.gsonConverterFactory = GsonConverterFactory.create(gson);
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type.equals(String.class))
return new GsonResponseBodyConverterToString<Object>(gson, type);
else
return gsonConverterFactory.responseBodyConverter(type, annotations, retrofit);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return gsonConverterFactory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
}
}