• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hyperwallet / java-sdk / #654

01 May 2025 10:10PM CUT coverage: 97.037%. Remained the same
#654

push

grmeyer-hw-dev
Update changelog

5567 of 5737 relevant lines covered (97.04%)

67.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.73
/src/main/java/com/hyperwallet/clientsdk/util/HyperwalletApiClient.java
1
package com.hyperwallet.clientsdk.util;
2

3
import cc.protea.util.http.Response;
4
import com.fasterxml.jackson.core.type.TypeReference;
5
import com.hyperwallet.clientsdk.HyperwalletException;
6
import com.hyperwallet.clientsdk.model.HyperwalletErrorList;
7
import com.nimbusds.jose.JOSEException;
8

9
import javax.xml.bind.DatatypeConverter;
10
import java.io.IOException;
11
import java.net.InetSocketAddress;
12
import java.net.Proxy;
13
import java.text.ParseException;
14
import java.util.HashMap;
15
import java.util.UUID;
16

17
/**
18
 * The Hyperwallet API Client
19
 */
20
public class HyperwalletApiClient {
21

22
    private static final String ACCEPT = "Accept";
23
    private static final String APPLICATION_JOSE_JSON = "application/jose+json";
24
    private static final String APPLICATION_JSON = "application/json";
25
    private static final String CONTENT_TYPE = "Content-Type";
26
    private static final String MULTIPART_FORM_DATA_BOUNDARY = "multipart/form-data; boundary=";
27
    private static final String SDK_TYPE = "java";
28
    private final HyperwalletEncryption hyperwalletEncryption;
29
    private Proxy proxy;
30
    private String proxyPassword;
31
    private String proxyUsername;
32
    private final String contextId;
33
    private final String password;
34
    private final String username;
35
    private final String version;
36
    private final boolean isEncrypted;
37
    private final int connectionTimeout;
38
    private final int readTimeout;
39

40
    /**
41
     * Create a instance of the API client
42
     *
43
     * @param username              the API username
44
     * @param password              the API password
45
     * @param version               the SDK version
46
     * @param hyperwalletEncryption the {@link HyperwalletEncryption}
47
     * @param connectionTimeout     the timeout value that will be used for making new connections to the Hyperwallet API (in milliseconds).
48
     * @param readTimeout           the timeout value that will be used when reading data from an established connection to
49
     *                              the Hyperwallet API (in milliseconds).
50
     */
51
    public HyperwalletApiClient(final String username, final String password, final String version,
52
            final HyperwalletEncryption hyperwalletEncryption, final int connectionTimeout, final int readTimeout) {
592✔
53
        this.username = username;
592✔
54
        this.password = password;
592✔
55
        this.version = version;
592✔
56
        this.hyperwalletEncryption = hyperwalletEncryption;
592✔
57
        this.isEncrypted = hyperwalletEncryption != null;
592✔
58
        this.contextId = String.valueOf(UUID.randomUUID());
592✔
59

60
        this.connectionTimeout = connectionTimeout;
592✔
61
        this.readTimeout = readTimeout;
592✔
62

63
        // TLS fix
64
        if (System.getProperty("java.version").startsWith("1.7.")) {
592✔
65
            System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
×
66
        }
67
    }
592✔
68

69
    /**
70
     * Perform a GET call to the Hyperwallet API server
71
     *
72
     * @param <T>  Response class type
73
     * @param url  The api endpoint to call
74
     * @param type The response class type
75
     * @return an instance of response class type
76
     */
77
    public <T> T get(final String url, final Class<T> type) {
78
        Response response = null;
18✔
79
        try {
80
            response = buildGetRequest(url)
18✔
81
                    .getResource();
18✔
82
            return processResponse(response, type);
13✔
83
        } catch (IOException | JOSEException | ParseException e) {
5✔
84
            throw new HyperwalletException(e);
5✔
85
        }
86
    }
87

88
    /**
89
     * Perform a GET call to the Hyperwallet API server
90
     *
91
     * @param <T>  Response class type
92
     * @param url  The api endpoint to call
93
     * @param type The response {@link TypeReference} type
94
     * @return an instance of {@link TypeReference}
95
     */
96
    public <T> T get(final String url, final TypeReference<T> type) {
97
        Response response = null;
7✔
98
        try {
99
            response = buildGetRequest(url)
7✔
100
                    .getResource();
7✔
101
            return processResponse(response, type);
6✔
102
        } catch (IOException | JOSEException | ParseException e) {
1✔
103
            throw new HyperwalletException(e);
1✔
104
        }
105
    }
106

107
    /**
108
     * Perform a PUT call to the Hyperwallet API server to upload {@link Multipart}
109
     *
110
     * @param <T>        Response class type
111
     * @param url        The api endpoint to call
112
     * @param uploadData The {@link Multipart}
113
     * @param type       The response class type
114
     * @return an instance of response class type
115
     */
116
    public <T> T put(final String url, final Multipart uploadData, final Class<T> type) {
117
        try {
118
            Response response = buildMultipartRequest(url)
10✔
119
                    .putMultipartResource(uploadData);
10✔
120
            return processResponse(response, type);
5✔
121
        } catch (IOException | JOSEException | ParseException e) {
5✔
122
            throw new HyperwalletException(e);
5✔
123
        }
124
    }
125

126
    /**
127
     * Perform a PUT call to the Hyperwallet API server to upload {@link Object}
128
     *
129
     * @param <T>        Response class type
130
     * @param url        The api endpoint to call
131
     * @param bodyObject The {@link Object} body
132
     * @param type       The response class type
133
     * @return an instance of response class type
134
     */
135
    public <T> T put(final String url, final Object bodyObject, final Class<T> type) {
136
        Response response = null;
15✔
137
        try {
138
            String body = convert(bodyObject);
15✔
139
            response = buildRequest(url)
15✔
140
                    .setBody(encrypt(body))
15✔
141
                    .putResource();
15✔
142
            return processResponse(response, type);
11✔
143
        } catch (IOException | JOSEException | ParseException e) {
4✔
144
            throw new HyperwalletException(e);
4✔
145
        }
146
    }
147

148
    /**
149
     * Perform a POST call to the Hyperwallet API server to upload {@link Object}
150
     *
151
     * @param <T>        Response class type
152
     * @param url        The api endpoint to call
153
     * @param bodyObject The {@link Object} body
154
     * @param type       The response class type
155
     * @return an instance of response class type
156
     */
157
    public <T> T post(final String url, final Object bodyObject, final Class<T> type) {
158
        Response response = null;
29✔
159
        try {
160
            Request request = buildRequest(url);
29✔
161
            String body = bodyObject != null ? encrypt(convert(bodyObject)) : "";
29✔
162
            request.setBody(body);
29✔
163
            response = request.postResource();
29✔
164
            return processResponse(response, type);
25✔
165
        } catch (IOException | JOSEException | ParseException e) {
4✔
166
            throw new HyperwalletException(e);
4✔
167
        }
168
    }
169

170
    /**
171
     * Perform a POST call to the Hyperwallet API server to upload {@link Object}
172
     *
173
     * @param <T>        Response class type
174
     * @param url        The api endpoint to call
175
     * @param bodyObject The {@link Object} body
176
     * @param type       The response class type
177
     * @param header     HTTP header properties
178
     * @return an instance of response class type
179
     */
180
    public <T> T post(final String url, final Object bodyObject, final Class<T> type, HashMap<String, String> header) {
181
        Response response = null;
4✔
182
        try {
183
            String body = convert(bodyObject);
4✔
184
            Request request = buildRequest(url)
4✔
185
                    .setBody(encrypt(body));
4✔
186
            if (header != null) {
4✔
187
                for (String key : header.keySet()) {
3✔
188
                    request = request.addHeader(key, header.get(key));
3✔
189
                }
3✔
190
            }
191

192
            response = request.postResource();
4✔
193
            return processResponse(response, type);
3✔
194
        } catch (IOException | JOSEException | ParseException e) {
1✔
195
            throw new HyperwalletException(e);
1✔
196
        }
197
    }
198

199
    /**
200
     * Checks if current API Client instance uses a proxy
201
     *
202
     * @return Boolean if client has a proxy config
203
     */
204
    public Boolean usesProxy() {
205
        return proxy != null;
×
206
    }
207

208
    /**
209
     * Create Proxy setting for API Client instance
210
     *
211
     * @param url  url of Proxy
212
     * @param port port of Proxy
213
     */
214
    public void setProxy(String url, Integer port) {
215
        this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(url, port));
12✔
216
    }
12✔
217

218
    /**
219
     * Returns Proxy setting for Authentication
220
     *
221
     * @return Proxy current Proxy config of client
222
     */
223
    public Proxy getProxy() {
224
        return proxy;
×
225
    }
226

227
    /**
228
     * Create Proxy setting for API Client instance
229
     *
230
     * @param proxy value of Proxy
231
     */
232
    public void setProxy(Proxy proxy) {
233
        this.proxy = proxy;
×
234
    }
×
235

236
    /**
237
     * Returns Proxy Username for Authentication
238
     *
239
     * @return current ProxyUsername
240
     */
241
    public String getProxyUsername() {
242
        return proxyUsername;
×
243
    }
244

245
    /**
246
     * Create Proxy Username for Authentication
247
     *
248
     * @param proxyUsername username of Proxy
249
     */
250
    public void setProxyUsername(String proxyUsername) {
251
        this.proxyUsername = proxyUsername;
×
252
    }
×
253

254
    /**
255
     * Returns Proxy Password for Authentication
256
     *
257
     * @return current ProxyUsername
258
     */
259
    public String getProxyPassword() {
260
        return proxyPassword;
×
261
    }
262

263
    /**
264
     * Create Proxy Password setting for Authentication
265
     *
266
     * @param proxyPassword username of Proxy
267
     */
268
    public void setProxyPassword(String proxyPassword) {
269
        this.proxyPassword = proxyPassword;
×
270
    }
×
271

272
    /**
273
     * Process the {@link  Response} to return success <T> class
274
     *
275
     * @param response the {@link  Response}
276
     * @param type     The response class type
277
     * @return an instance of <T>
278
     */
279
    private <T> T processResponse(final Response response, final Class<T> type)
280
            throws ParseException, JOSEException, IOException {
281
        checkErrorResponse(response);
57✔
282
        checkResponseHeader(response);
27✔
283
        if (response.getResponseCode() == 204) {
25✔
284
            return convert("{}", type);
8✔
285
        } else {
286
            return convert(decryptResponse(response.getBody()), type);
17✔
287
        }
288
    }
289

290
    /**
291
     * Process the {@link  Response} to return success {@link TypeReference<T>} class
292
     *
293
     * @param response the {@link  Response}
294
     * @param type     The response {@link TypeReference<T>} type
295
     * @return an instance of {@link TypeReference<T>}
296
     */
297
    private <T> T processResponse(final Response response, final TypeReference<T> type)
298
            throws ParseException, JOSEException, IOException {
299
        checkErrorResponse(response);
6✔
300
        checkResponseHeader(response);
3✔
301
        if (response.getResponseCode() == 204) {
3✔
302
            return convert("{}", type);
1✔
303
        } else {
304
            return convert(decryptResponse(response.getBody()), type);
2✔
305
        }
306
    }
307

308
    /**
309
     * Evaluates the {@link Response} contains Api Server error when HTTP status from 4.x.x and raise {@link HyperwalletException}
310
     *
311
     * @param response the {@link  Response}
312
     * @throws HyperwalletException a {@link HyperwalletException}
313
     */
314
    protected void checkErrorResponse(final Response response) {
315
        HyperwalletErrorList errorList = null;
64✔
316
        if (response.getResponseCode() >= 400) {
64✔
317
            try {
318
                errorList = convert(decryptErrorResponse(response), HyperwalletErrorList.class);
34✔
319
            } catch (Exception e) {
6✔
320
                //Failed to convert the response
321
                throw new HyperwalletException(response, response.getResponseCode(), response.getResponseMessage(), e);
6✔
322
            }
28✔
323

324
            if (errorList != null && !errorList.getErrors().isEmpty()) {
28✔
325
                throw new HyperwalletException(response, errorList);
23✔
326
            } else {//unmapped errors
327
                throw new HyperwalletException(response, response.getResponseCode(), response.getResponseMessage());
5✔
328
            }
329
        }
330
    }
30✔
331

332
    private void checkResponseHeader(Response response) {
333
        String contentTypeHeader = response.getHeader(CONTENT_TYPE);
30✔
334
        String expectedContentType = getContentType();
30✔
335
        boolean invalidContentType = response.getResponseCode() != 204 && contentTypeHeader != null
30✔
336
                && !contentTypeHeader.contains(expectedContentType);
20✔
337
        if (invalidContentType) {
30✔
338
            throw new HyperwalletException("Invalid Content-Type specified in Response Header");
2✔
339
        }
340
    }
28✔
341

342
    private String getAuthorizationHeader() {
343
        final String pair = this.username + ":" + this.password;
83✔
344
        final String base64 = DatatypeConverter.printBase64Binary(pair.getBytes());
83✔
345
        return "Basic " + base64;
83✔
346
    }
347

348
    private Request populateCommonHTTPHeader(final Request request) {
349
        return request.addHeader("Authorization", getAuthorizationHeader())
166✔
350
                .addHeader("User-Agent", "Hyperwallet Java SDK v" + version)
83✔
351
                .addHeader("x-sdk-version", version)
83✔
352
                .addHeader("x-sdk-type", SDK_TYPE)
83✔
353
                .addHeader("x-sdk-contextId", contextId);
83✔
354
    }
355

356
    private Request buildGetRequest(final String url) {
357
        Request request = new Request(url, connectionTimeout, readTimeout, proxy, proxyUsername, proxyPassword);
73✔
358
        return populateCommonHTTPHeader(request)
146✔
359
                .addHeader(ACCEPT, getContentType());
73✔
360
    }
361

362
    private Request buildRequest(final String url) {
363
        String contentType = getContentType();
48✔
364
        return buildGetRequest(url)
96✔
365
                .addHeader(ACCEPT, contentType)
48✔
366
                .addHeader(CONTENT_TYPE, contentType);
48✔
367
    }
368

369
    private MultipartRequest buildMultipartRequest(final String url) {
370
        String contentType = buildMultipartContentType();
10✔
371
        MultipartRequest request = new MultipartRequest(url, connectionTimeout, readTimeout, proxy, proxyUsername, proxyPassword);
10✔
372
        return (MultipartRequest) populateCommonHTTPHeader(request)
20✔
373
                .addHeader(ACCEPT, APPLICATION_JSON)
10✔
374
                .addHeader(CONTENT_TYPE, contentType);
10✔
375
    }
376

377
    private String getContentType() {
378
        return isEncrypted ? APPLICATION_JOSE_JSON : APPLICATION_JSON;
151✔
379
    }
380

381
    private String buildMultipartContentType() {
382
        return MULTIPART_FORM_DATA_BOUNDARY + MultipartRequest.BOUNDARY;
10✔
383
    }
384

385
    private <T> T convert(final String responseBody, final Class<T> type) {
386
        if (responseBody == null) {
59✔
387
            return null;
7✔
388
        }
389
        return HyperwalletJsonUtil.fromJson(responseBody, type);
52✔
390
    }
391

392
    private <T> T convert(final String responseBody, final TypeReference<T> type) {
393
        return HyperwalletJsonUtil.fromJson(responseBody, type);
3✔
394
    }
395

396
    private String convert(final Object object) {
397
        return HyperwalletJsonUtil.toJson(object);
48✔
398
    }
399

400
    private String encrypt(String body) throws JOSEException, IOException, ParseException {
401
        return isEncrypted ? hyperwalletEncryption.encrypt(body) : body;
48✔
402
    }
403

404
    private String decryptResponse(String responseBody) throws ParseException, IOException, JOSEException {
405
        if (responseBody == null || responseBody.length() == 0) {
19✔
406
            return null;
3✔
407
        }
408
        return isEncrypted ? hyperwalletEncryption.decrypt(responseBody) : responseBody;
16✔
409
    }
410

411
    /**
412
     * Method to handle error responses based on the content type header.  Although the HyperWallet encryption object is set, it is still possible to
413
     * receive an error response with content type=application/json.  Please see the following
414
     * <a href="https://docs.hyperwallet.com/content/api/v4/overview/payload-encryption">documentation</a>.
415
     *
416
     * @param response The response received from the server
417
     * @return The decrypted error response
418
     */
419
    private String decryptErrorResponse(Response response) throws ParseException, IOException, JOSEException {
420
        String responseBody = response.getBody();
34✔
421
        if (responseBody == null || responseBody.length() == 0) {
34✔
422
            return null;
5✔
423
        }
424
        return isEncrypted && isJoseContentType(response) ? hyperwalletEncryption.decrypt(responseBody) : responseBody;
29✔
425
    }
426

427
    private Boolean isJoseContentType(Response response) {
428
        String contentTypeHeader = response.getHeader(CONTENT_TYPE);
10✔
429
        return contentTypeHeader != null && contentTypeHeader.contains(APPLICATION_JOSE_JSON);
10✔
430
    }
431
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc