From 72854744fb8b4482b388fdc545ce4b12bade9e26 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Fri, 27 Apr 2018 17:27:23 -0300 Subject: [PATCH 1/4] expose additional error response properties in the exception --- README.md | 2 +- .../com/auth0/exception/APIException.java | 20 +++++++++++++-- .../java/com/auth0/client/MockServer.java | 1 + .../java/com/auth0/net/CustomRequestTest.java | 25 ++++++++++++++++++- ...with_description_and_extra_properties.json | 5 ++++ 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/auth/error_with_description_and_extra_properties.json diff --git a/README.md b/README.md index a5b87ffe..76f5327b 100644 --- a/README.md +++ b/README.md @@ -526,7 +526,7 @@ If you need to handle different error scenarios you need to catch first `APIExce The APIExplorer includes a list of response messages for each endpoint. You can get a clue of what went wrong by asking the Http status code: `exception.getStatusCode()`. i.e. a `status_code=403` would mean that the token has an insufficient scope. -An error code will be included to categorize the type of error, you can get it by calling `exception.getError()`. If you want to see a user friendly description of what happened and why the request is failing check the `exception.getDescription()`. +An error code will be included to categorize the type of error, you can get it by calling `exception.getError()`. If you want to see a user friendly description of what happened and why the request is failing check the `exception.getDescription()`. Finally, if the error response includes additional properties they can be obtained by calling `exception.getValue("{THE_KEY}")`. ``` diff --git a/src/main/java/com/auth0/exception/APIException.java b/src/main/java/com/auth0/exception/APIException.java index 6956b9bc..ed635c32 100644 --- a/src/main/java/com/auth0/exception/APIException.java +++ b/src/main/java/com/auth0/exception/APIException.java @@ -1,5 +1,6 @@ package com.auth0.exception; +import java.util.Collections; import java.util.Map; /** @@ -21,6 +22,7 @@ public class APIException extends Auth0Exception { private String error; private String description; private int statusCode; + private Map values; public APIException(String payload, int statusCode, Throwable cause) { super(createMessage(payload, statusCode), cause); @@ -30,6 +32,7 @@ public APIException(String payload, int statusCode, Throwable cause) { public APIException(Map values, int statusCode) { super(createMessage(obtainExceptionMessage(values), statusCode)); + this.values = Collections.unmodifiableMap(values); this.error = obtainExceptionError(values); this.description = obtainExceptionMessage(values); this.statusCode = statusCode; @@ -55,6 +58,19 @@ public String getError() { return error; } + /** + * Returns a value from the error map, if any. + * + * @param key key of the value to return + * @return the value if found or null + */ + public Object getValue(String key) { + if (values == null) { + return null; + } + return values.get(key); + } + /** * Getter for the exception user friendly description of why the request failed. * i.e. the description may say which query parameters are valid for that endpoint. @@ -75,9 +91,9 @@ private static String obtainExceptionMessage(Map values) { } if (values.containsKey("description")) { Object description = values.get("description"); - if(description instanceof String) { + if (description instanceof String) { return (String) description; - } else{ + } else { PasswordStrengthErrorParser policy = new PasswordStrengthErrorParser((Map) description); return policy.getDescription(); } diff --git a/src/test/java/com/auth0/client/MockServer.java b/src/test/java/com/auth0/client/MockServer.java index a413a966..06bff61c 100644 --- a/src/test/java/com/auth0/client/MockServer.java +++ b/src/test/java/com/auth0/client/MockServer.java @@ -23,6 +23,7 @@ public class MockServer { public static final String AUTH_ERROR_WITH_ERROR_DESCRIPTION = "src/test/resources/auth/error_with_error_description.json"; public static final String AUTH_ERROR_WITH_ERROR = "src/test/resources/auth/error_with_error.json"; public static final String AUTH_ERROR_WITH_DESCRIPTION = "src/test/resources/auth/error_with_description.json"; + public static final String AUTH_ERROR_WITH_DESCRIPTION_AND_EXTRA_PROPERTIES = "src/test/resources/auth/error_with_description_and_extra_properties.json"; public static final String AUTH_ERROR_PLAINTEXT = "src/test/resources/auth/error_plaintext.json"; public static final String MGMT_ERROR_WITH_MESSAGE = "src/test/resources/mgmt/error_with_message.json"; public static final String MGMT_CLIENT_GRANTS_LIST = "src/test/resources/mgmt/client_grants_list.json"; diff --git a/src/test/java/com/auth0/net/CustomRequestTest.java b/src/test/java/com/auth0/net/CustomRequestTest.java index 0549ead3..7065a16b 100644 --- a/src/test/java/com/auth0/net/CustomRequestTest.java +++ b/src/test/java/com/auth0/net/CustomRequestTest.java @@ -1,8 +1,8 @@ package com.auth0.net; import com.auth0.client.MockServer; -import com.auth0.exception.Auth0Exception; import com.auth0.exception.APIException; +import com.auth0.exception.Auth0Exception; import com.auth0.json.auth.TokenHolder; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; @@ -214,6 +214,29 @@ public void shouldParseJSONErrorResponseWithError() throws Exception { assertThat(authException.getStatusCode(), is(400)); } + @Test + public void shouldParseJSONErrorResponseWithDescriptionAndExtraProperties() throws Exception { + CustomRequest request = new CustomRequest<>(client, server.getBaseUrl(), "GET", listType); + server.jsonResponse(AUTH_ERROR_WITH_DESCRIPTION_AND_EXTRA_PROPERTIES, 400); + Exception exception = null; + try { + request.execute(); + server.takeRequest(); + } catch (Exception e) { + exception = e; + } + assertThat(exception, is(notNullValue())); + assertThat(exception, is(instanceOf(APIException.class))); + assertThat(exception.getCause(), is(nullValue())); + assertThat(exception.getMessage(), is("Request failed with status code 400: Multifactor authentication required")); + APIException authException = (APIException) exception; + assertThat(authException.getDescription(), is("Multifactor authentication required")); + assertThat(authException.getError(), is("mfa_required")); + assertThat(authException.getValue("mfa_token"), is("Fe26...Ha")); + assertThat(authException.getValue("non_existing_key"), is(nullValue())); + assertThat(authException.getStatusCode(), is(400)); + } + @Test public void shouldParseJSONErrorResponseWithDescription() throws Exception { CustomRequest request = new CustomRequest<>(client, server.getBaseUrl(), "GET", listType); diff --git a/src/test/resources/auth/error_with_description_and_extra_properties.json b/src/test/resources/auth/error_with_description_and_extra_properties.json new file mode 100644 index 00000000..a1799ae1 --- /dev/null +++ b/src/test/resources/auth/error_with_description_and_extra_properties.json @@ -0,0 +1,5 @@ +{ + "error": "mfa_required", + "error_description": "Multifactor authentication required", + "mfa_token": "Fe26...Ha" +} \ No newline at end of file From 55075b15cd871bcf5f325b92996988f57773c2d4 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Fri, 27 Apr 2018 17:29:51 -0300 Subject: [PATCH 2/4] use unmmodifiable map when possible --- src/main/java/com/auth0/exception/APIException.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/auth0/exception/APIException.java b/src/main/java/com/auth0/exception/APIException.java index ed635c32..aa068352 100644 --- a/src/main/java/com/auth0/exception/APIException.java +++ b/src/main/java/com/auth0/exception/APIException.java @@ -33,8 +33,8 @@ public APIException(String payload, int statusCode, Throwable cause) { public APIException(Map values, int statusCode) { super(createMessage(obtainExceptionMessage(values), statusCode)); this.values = Collections.unmodifiableMap(values); - this.error = obtainExceptionError(values); - this.description = obtainExceptionMessage(values); + this.error = obtainExceptionError(this.values); + this.description = obtainExceptionMessage(this.values); this.statusCode = statusCode; } From c20a76c38484206736f7dfa6a0ca87d060305a34 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Fri, 27 Apr 2018 17:31:52 -0300 Subject: [PATCH 3/4] add missing test case --- src/test/java/com/auth0/net/CustomRequestTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/auth0/net/CustomRequestTest.java b/src/test/java/com/auth0/net/CustomRequestTest.java index 7065a16b..a2069d30 100644 --- a/src/test/java/com/auth0/net/CustomRequestTest.java +++ b/src/test/java/com/auth0/net/CustomRequestTest.java @@ -297,6 +297,7 @@ public void shouldParsePlainTextErrorResponse() throws Exception { APIException authException = (APIException) exception; assertThat(authException.getDescription(), is("A plain-text error response")); assertThat(authException.getError(), is(nullValue())); + assertThat(authException.getValue("non_existing_key"), is(nullValue())); assertThat(authException.getStatusCode(), is(400)); } From 8ae7239fd2c3e8e7731e13520bc8357349697de4 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Thu, 3 May 2018 19:50:58 -0300 Subject: [PATCH 4/4] add casting. fixes jdk7 test --- src/test/java/com/auth0/net/CustomRequestTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/auth0/net/CustomRequestTest.java b/src/test/java/com/auth0/net/CustomRequestTest.java index a2069d30..f52ef446 100644 --- a/src/test/java/com/auth0/net/CustomRequestTest.java +++ b/src/test/java/com/auth0/net/CustomRequestTest.java @@ -214,6 +214,7 @@ public void shouldParseJSONErrorResponseWithError() throws Exception { assertThat(authException.getStatusCode(), is(400)); } + @SuppressWarnings("RedundantCast") @Test public void shouldParseJSONErrorResponseWithDescriptionAndExtraProperties() throws Exception { CustomRequest request = new CustomRequest<>(client, server.getBaseUrl(), "GET", listType); @@ -232,7 +233,7 @@ public void shouldParseJSONErrorResponseWithDescriptionAndExtraProperties() thro APIException authException = (APIException) exception; assertThat(authException.getDescription(), is("Multifactor authentication required")); assertThat(authException.getError(), is("mfa_required")); - assertThat(authException.getValue("mfa_token"), is("Fe26...Ha")); + assertThat(authException.getValue("mfa_token"), is((Object) "Fe26...Ha")); assertThat(authException.getValue("non_existing_key"), is(nullValue())); assertThat(authException.getStatusCode(), is(400)); }