Skip to content

Commit faf05ff

Browse files
authored
Merge pull request #61 from bunq/feature/#55-add_response_id_to_request_error
(#55) add response id to request error
2 parents 9e18b4d + 727622b commit faf05ff

13 files changed

+109
-45
lines changed

src/main/java/com/bunq/sdk/exception/ApiException.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33
public class ApiException extends RuntimeException {
44

55
private int responseCode;
6+
private String responseId;
67

7-
public ApiException(String message, int responseCode) {
8+
protected ApiException(String message, int responseCode, String responseId) {
89
super(message);
910

1011
this.responseCode = responseCode;
12+
this.responseId = responseId;
1113
}
1214

1315
public int getResponseCode() {
1416
return responseCode;
1517
}
1618

19+
public String getResponseId() {
20+
return responseId;
21+
}
22+
1723
}

src/main/java/com/bunq/sdk/exception/BadRequestException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
public class BadRequestException extends ApiException {
44

5-
public BadRequestException(String message, int responseCode) {
6-
super(message, responseCode);
5+
public BadRequestException(String message, int responseCode, String responseId) {
6+
super(message, responseCode, responseId);
77
}
88

99
}

src/main/java/com/bunq/sdk/exception/ExceptionFactory.java

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ public class ExceptionFactory {
1818
/**
1919
* Some glue to glue the error message together.
2020
*/
21-
private static final String GLUE_ERROR_MESSAGES = "\n";
21+
private static final String SEPARATOR_ERROR_MESSAGES = "\n";
22+
23+
/**
24+
* String format constants.
25+
*/
26+
private static final String FORMAT_ERROR_MESSAGE = "Response id to help bunq debug: %s. \n Error message: %s";
2227

2328
/**
2429
*
@@ -27,31 +32,31 @@ public class ExceptionFactory {
2732
*
2833
* @return The exception that belongs to this status code.
2934
*/
30-
public static ApiException createExceptionForResponse(int responseCode, List<String> messages){
31-
String error_message = concatenateMessages(messages);
35+
public static ApiException createExceptionForResponse(int responseCode, List<String> messages, String responseId){
36+
String error_message = concatenateAllMessage(messages, responseId);
3237

3338
switch (responseCode) {
3439
case HTTP_RESPONSE_CODE_BAD_REQUEST:
35-
return new BadRequestException(error_message, responseCode);
40+
return new BadRequestException(error_message, responseCode, responseId);
3641
case HTTP_RESPONSE_CODE_UNAUTHORIZED:
37-
return new UnauthorizedException(error_message, responseCode);
42+
return new UnauthorizedException(error_message, responseCode, responseId);
3843
case HTTP_RESPONSE_CODE_FORBIDDEN:
39-
return new UnauthorizedException(error_message, responseCode);
44+
return new ForbiddenException(error_message, responseCode, responseId);
4045
case HTTP_RESPONSE_CODE_NOT_FOUND:
41-
return new NotFoundException(error_message, responseCode);
46+
return new NotFoundException(error_message, responseCode, responseId);
4247
case HTTP_RESPONSE_CODE_METHOD_NOT_ALLOWED:
43-
return new MethodNotAllowedException(error_message, responseCode);
48+
return new MethodNotAllowedException(error_message, responseCode, responseId);
4449
case HTTP_RESPONSE_CODE_TOO_MANY_REQUESTS:
45-
return new TooManyRequestsException(error_message, responseCode);
50+
return new TooManyRequestsException(error_message, responseCode, responseId);
4651
case HTTP_RESPONSE_CODE_INTERNAL_SERVER_ERROR:
47-
return new PleaseContactBunqException(error_message, responseCode);
52+
return new PleaseContactBunqException(error_message, responseCode, responseId);
4853
default:
49-
return new UnknownApiErrorException(error_message, responseCode);
54+
return new UnknownApiErrorException(error_message, responseCode, responseId);
5055
}
5156
}
5257

53-
private static String concatenateMessages(List<String> messages) {
54-
return String.join(GLUE_ERROR_MESSAGES, messages);
58+
private static String concatenateAllMessage(List<String> messages, String responseId) {
59+
return String.format(FORMAT_ERROR_MESSAGE, responseId, String.join(SEPARATOR_ERROR_MESSAGES, messages));
5560
}
5661

5762
}

src/main/java/com/bunq/sdk/exception/ForbiddenException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
public class ForbiddenException extends ApiException {
44

5-
public ForbiddenException(String message, int responseCode) {
6-
super(message, responseCode);
5+
public ForbiddenException(String message, int responseCode, String responseId) {
6+
super(message, responseCode, responseId);
77
}
88

99
}

src/main/java/com/bunq/sdk/exception/MethodNotAllowedException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
public class MethodNotAllowedException extends ApiException {
44

5-
public MethodNotAllowedException(String message, int responseCode) {
6-
super(message, responseCode);
5+
public MethodNotAllowedException(String message, int responseCode, String responseId) {
6+
super(message, responseCode, responseId);
77
}
88

99
}

src/main/java/com/bunq/sdk/exception/NotFoundException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
public class NotFoundException extends ApiException {
44

5-
public NotFoundException(String message, int responseCode) {
6-
super(message, responseCode);
5+
public NotFoundException(String message, int responseCode, String responseId) {
6+
super(message, responseCode, responseId);
77
}
88

99
}

src/main/java/com/bunq/sdk/exception/PleaseContactBunqException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
public class PleaseContactBunqException extends ApiException {
44

5-
public PleaseContactBunqException(String message, int responseCode) {
6-
super(message, responseCode);
5+
public PleaseContactBunqException(String message, int responseCode, String responseId) {
6+
super(message, responseCode, responseId);
77
}
88

99
}

src/main/java/com/bunq/sdk/exception/TooManyRequestsException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
public class TooManyRequestsException extends ApiException {
44

5-
public TooManyRequestsException(String message, int responseCode) {
6-
super(message, responseCode);
5+
public TooManyRequestsException(String message, int responseCode, String responseId) {
6+
super(message, responseCode, responseId);
77
}
88

99
}

src/main/java/com/bunq/sdk/exception/UnauthorizedException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
public class UnauthorizedException extends ApiException {
44

5-
public UnauthorizedException(String message, int responseCode) {
6-
super(message, responseCode);
5+
public UnauthorizedException(String message, int responseCode, String responseId) {
6+
super(message, responseCode, responseId);
77
}
88

99
}
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package com.bunq.sdk.exception;
22

3-
43
public class UnknownApiErrorException extends ApiException {
54

6-
public UnknownApiErrorException(String message, int responseCode) {
7-
super(message, responseCode);
5+
public UnknownApiErrorException(String message, int responseCode, String responseId) {
6+
super(message, responseCode, responseId);
87
}
98

109
}

src/main/java/com/bunq/sdk/http/ApiClient.java

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.bunq.sdk.context.ApiContext;
44
import com.bunq.sdk.context.InstallationContext;
55
import com.bunq.sdk.exception.ApiException;
6+
import com.bunq.sdk.exception.BunqException;
67
import com.bunq.sdk.exception.ExceptionFactory;
78
import com.bunq.sdk.exception.UncaughtExceptionError;
89
import com.bunq.sdk.json.BunqGsonBuilder;
@@ -44,7 +45,6 @@
4445
import org.apache.http.impl.client.CloseableHttpClient;
4546
import org.apache.http.impl.client.HttpClientBuilder;
4647
import org.apache.http.impl.client.HttpClients;
47-
import org.apache.http.message.BasicHeader;
4848
import org.apache.http.ssl.SSLContextBuilder;
4949
import org.apache.http.util.EntityUtils;
5050

@@ -54,6 +54,12 @@
5454
*/
5555
public class ApiClient {
5656

57+
/**
58+
* Error constants.
59+
*/
60+
private static final String ERROR_COULD_NOT_DETERMINE_RESPONSE_ID_HEADER =
61+
"The response header \"X-Bunq-Client-Response-Id\" or \"x-bunq-client-response-id\" could not be found.";
62+
5763
/**
5864
* Endpoints not requiring active session for the request to succeed.
5965
*/
@@ -79,6 +85,8 @@ public class ApiClient {
7985
private static final String HEADER_GEOLOCATION = "X-Bunq-Geolocation";
8086
private static final String HEADER_SIGNATURE = "X-Bunq-Client-Signature";
8187
private static final String HEADER_AUTHENTICATION = "X-Bunq-Client-Authentication";
88+
private static final String HEADER_RESPONSE_ID_LOWER_CASE = "x-bunq-client-response-id";
89+
private static final String HEADER_RESPONSE_ID_UPPER_CASE = "X-Bunq-Client-Response-Id";
8290

8391
/**
8492
* Field constants.
@@ -229,47 +237,59 @@ private BunqResponseRaw createBunqResponseRaw(CloseableHttpResponse response)
229237
Integer responseCode = response.getStatusLine().getStatusCode();
230238
byte[] responseBodyBytes = EntityUtils.toByteArray(response.getEntity());
231239

232-
assertResponseSuccess(responseCode, responseBodyBytes);
240+
assertResponseSuccess(responseCode, responseBodyBytes, getResponseId(response));
233241
validateResponseSignature(responseCode, responseBodyBytes, response);
234242

235243
return new BunqResponseRaw(responseBodyBytes, getHeadersMap(response));
236244
}
237245

238-
private static void assertResponseSuccess(int responseCode, byte[] responseBodyBytes) {
246+
private static String getResponseId(CloseableHttpResponse response) {
247+
Map<String, String> headerMap = getHeadersMap(response);
248+
249+
if (headerMap.containsKey(HEADER_RESPONSE_ID_LOWER_CASE)) {
250+
return headerMap.get(HEADER_RESPONSE_ID_LOWER_CASE);
251+
} else if (headerMap.containsKey(HEADER_RESPONSE_ID_UPPER_CASE)) {
252+
return headerMap.get(HEADER_RESPONSE_ID_UPPER_CASE);
253+
} else {
254+
throw new BunqException(ERROR_COULD_NOT_DETERMINE_RESPONSE_ID_HEADER);
255+
}
256+
}
257+
258+
private static void assertResponseSuccess(int responseCode, byte[] responseBodyBytes, String responseId) {
239259
if (responseCode != HttpStatus.SC_OK) {
240-
throw createApiExceptionRequestUnsuccessful(responseCode, new String(responseBodyBytes));
260+
throw createApiExceptionRequestUnsuccessful(responseCode, new String(responseBodyBytes), responseId);
241261
}
242262
}
243263

244264
private static ApiException createApiExceptionRequestUnsuccessful(Integer responseCode,
245-
String responseBody) {
246-
List<String> errorDescriptions = new ArrayList<>();
265+
String responseBody, String responseId) {
266+
List<String> allErrorDescription = new ArrayList<>();
247267

248268
try {
249-
errorDescriptions.addAll(fetchErrorDescriptions(responseBody));
269+
allErrorDescription.addAll(fetchAllErrorDescription(responseBody));
250270
} catch (JsonSyntaxException exception) {
251-
errorDescriptions.add(responseBody);
271+
allErrorDescription.add(responseBody);
252272
}
253273

254-
return ExceptionFactory.createExceptionForResponse(responseCode, errorDescriptions);
274+
return ExceptionFactory.createExceptionForResponse(responseCode, allErrorDescription, responseId);
255275
}
256276

257-
private static List<String> fetchErrorDescriptions(String responseBody)
277+
private static List<String> fetchAllErrorDescription(String responseBody)
258278
throws JsonSyntaxException {
259279
List<String> errorDescriptions = new ArrayList<>();
260280
GsonBuilder gsonBuilder = BunqGsonBuilder.buildDefault();
261281
JsonObject responseBodyJson = gsonBuilder.create().fromJson(responseBody, JsonObject.class);
262282

263283
if (responseBodyJson.getAsJsonObject().has(FIELD_ERROR)) {
264-
errorDescriptions.addAll(fetchErrorDescriptions(responseBodyJson));
284+
errorDescriptions.addAll(fetchAllErrorDescription(responseBodyJson));
265285
} else {
266286
errorDescriptions.add(responseBody);
267287
}
268288

269289
return errorDescriptions;
270290
}
271291

272-
private static List<String> fetchErrorDescriptions(JsonObject responseBodyJson) {
292+
private static List<String> fetchAllErrorDescription(JsonObject responseBodyJson) {
273293
List<String> errorDescriptions = new ArrayList<>();
274294
JsonArray exceptionBodies = responseBodyJson.getAsJsonObject().getAsJsonArray(FIELD_ERROR);
275295

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.bunq.sdk.http;
2+
3+
import com.bunq.sdk.BunqSdkTestBase;
4+
import com.bunq.sdk.exception.ApiException;
5+
import com.bunq.sdk.model.generated.endpoint.UserPerson;
6+
import org.junit.Test;
7+
8+
import static org.junit.Assert.assertNotNull;
9+
10+
public class ErrorResponseIdTest extends BunqSdkTestBase {
11+
12+
/**
13+
* Invalid user id to trigger BadRequestException
14+
*/
15+
private static final int INVALID_USER_PERSON_ID = 0;
16+
17+
/**
18+
*/
19+
@Test
20+
public void badRequestWitResponseIdTest()
21+
{
22+
ApiException caughtException = null;
23+
24+
try {
25+
UserPerson.get(getApiContext(), INVALID_USER_PERSON_ID);
26+
} catch (ApiException $exception) {
27+
caughtException = $exception;
28+
}
29+
30+
assertNotNull(caughtException);
31+
assertNotNull(caughtException.getResponseId());
32+
}
33+
34+
}

src/test/java/com/bunq/sdk/model/generated/endpoint/SessionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.bunq.sdk.BunqSdkTestBase;
44
import com.bunq.sdk.Config;
55
import com.bunq.sdk.context.ApiContext;
6-
import com.bunq.sdk.exception.UnauthorizedException;
6+
import com.bunq.sdk.exception.ForbiddenException;
77
import java.util.concurrent.TimeUnit;
88
import org.junit.After;
99
import org.junit.Test;
@@ -27,7 +27,7 @@ public class SessionTest extends BunqSdkTestBase {
2727
/**
2828
* Tests deletion of the current session
2929
*/
30-
@Test(expected = UnauthorizedException.class)
30+
@Test(expected = ForbiddenException.class)
3131
public void deleteSessionTest() throws Exception {
3232
Session.delete(apiContext, SESSION_ID_DUMMY);
3333
User.list(apiContext);

0 commit comments

Comments
 (0)