Skip to content

Commit d37a5a1

Browse files
DTSERWONE-821 - Handle 401 (#138)
The Hyperwallet SDK can receive unauthorized (HTTP 401) response from the Hyperwallet API due the client change the password from the consumer UI, the core is bubble up HTTP error as Unexpected error. This PR will bubble up HTTP 401 error as Authentication error. Enhanced Hyperwallet Error to handle 401 Introduce test by performing Rest and GraphQL call to handle 401
1 parent 04d7ee4 commit d37a5a1

File tree

12 files changed

+190
-52
lines changed

12 files changed

+190
-52
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
Changelog
22
=========
3+
[1.0.0-beta12](https://github.com/hyperwallet/hyperwallet-android-sdk/releases/tag/1.0.0-beta12)
4+
-------------------
5+
* Handle HTTP 401
6+
37
[1.0.0-beta11](https://github.com/hyperwallet/hyperwallet-android-sdk/releases/tag/1.0.0-beta11)
48
-------------------
59
* Connection timeout increased

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ We also provide an out-of-the-box [Hyperwallet Android UI SDK](https://github.c
2121
To install Hyperwallet Core SDK, you just need to add the dependency into your build.gradle file in Android Studio (or Gradle). For example:
2222

2323
```bash
24-
api 'com.hyperwallet.android:core-sdk:1.0.0-beta11'
24+
api 'com.hyperwallet.android:core-sdk:1.0.0-beta12'
2525
```
2626

2727
### Proguard

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ allprojects {
2020
jcenter()
2121
mavenLocal()
2222
}
23-
project.version = "1.0.0-beta11"
23+
project.version = "1.0.0-beta12"
2424
}
2525

2626
task clean(type: Delete) {

core/src/main/java/com/hyperwallet/android/ExceptionMapper.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,21 @@ public final class ExceptionMapper {
6565
*/
6666
public static HyperwalletException toHyperwalletException(@NonNull final Exception exception) {
6767
if (exception instanceof HyperwalletGqlException) {
68+
HyperwalletGqlException hyperwalletGqlException = (HyperwalletGqlException) exception;
69+
if (hyperwalletGqlException.getHttpCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
70+
return initHyperwalletException(R.string.authentication_token_provider_exception,
71+
EC_AUTHENTICATION_TOKEN_PROVIDER_EXCEPTION, exception);
72+
}
6873
return (HyperwalletGqlException) exception;
6974
} else if (exception instanceof HyperwalletRestException) {
7075
HyperwalletRestException hyperwalletRestException = (HyperwalletRestException) exception;
7176
if (hyperwalletRestException.getHttpCode() == HttpURLConnection.HTTP_BAD_REQUEST) {
7277
return hyperwalletRestException;
73-
} else {
74-
return initHyperwalletException(R.string.unexpected_exception, EC_UNEXPECTED_EXCEPTION, exception);
78+
} else if (hyperwalletRestException.getHttpCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
79+
return initHyperwalletException(R.string.authentication_token_provider_exception,
80+
EC_AUTHENTICATION_TOKEN_PROVIDER_EXCEPTION, exception);
7581
}
82+
return initHyperwalletException(R.string.unexpected_exception, EC_UNEXPECTED_EXCEPTION, exception);
7683
} else if (exception instanceof HyperwalletException) {
7784
return (HyperwalletException) exception;
7885
} else if (exception instanceof IOException) {
@@ -90,11 +97,11 @@ public static HyperwalletException toHyperwalletException(@NonNull final Excepti
9097
}
9198

9299
private static HyperwalletException initHyperwalletException(@StringRes int stringResourceId,
93-
@NonNull final String code, @NonNull final Throwable throwable) {
100+
@NonNull final String code, @NonNull final Throwable throwable) {
94101
Error error = new Error(stringResourceId, code);
95102
List<Error> errorList = new ArrayList<>();
96103
errorList.add(error);
97104
Errors errors = new Errors(errorList);
98105
return new HyperwalletException(throwable, errors);
99106
}
100-
}
107+
}

core/src/main/java/com/hyperwallet/android/GqlTransaction.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ class GqlTransaction extends HttpTransaction {
4949
* @param typeReference The class type reference to use in order to deserialize response into Hyperwallet SDK
5050
* object
5151
*/
52-
private GqlTransaction(@NonNull final String uri, @NonNull final String body,
53-
@NonNull final String authenticationToken, @NonNull final HyperwalletListener hyperwalletListener,
54-
@NonNull final TypeReference typeReference) {
52+
private GqlTransaction(@NonNull final String uri,
53+
@NonNull final String body,
54+
@NonNull final String authenticationToken,
55+
@NonNull final HyperwalletListener hyperwalletListener,
56+
@NonNull final TypeReference typeReference) {
5557
super(HttpMethod.POST, uri, typeReference, hyperwalletListener);
5658
addHeader(HTTP_HEADER_AUTHORIZATION, AUTHENTICATION_STRATEGY + authenticationToken);
5759
setPayload(body);
@@ -69,11 +71,12 @@ protected int performRequest(final @NonNull HttpClient client) throws IOExceptio
6971
* Refer to {@link HttpTransaction#handleErrors(int, String)}
7072
*/
7173
@Override
72-
protected void handleErrors(final int responseCode, @NonNull final String response) throws JSONException,
74+
protected void handleErrors(final int responseCode,
75+
@NonNull final String response) throws JSONException,
7376
InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
7477
GqlErrors gqlErrors = JsonUtils.fromJsonString(response, new TypeReference<GqlErrors>() {
7578
});
76-
onFailure(new HyperwalletGqlException(gqlErrors));
79+
onFailure(new HyperwalletGqlException(responseCode, gqlErrors));
7780
}
7881

7982
/**
@@ -92,17 +95,18 @@ protected static final class Builder<T> {
9295
* @param listener callback object; refer to {@link HyperwalletListener}
9396
*/
9497
protected Builder(@NonNull final GqlQuery gqlQuery,
95-
@NonNull final TypeReference<T> typeReference,
96-
@NonNull final HyperwalletListener listener) {
98+
@NonNull final TypeReference<T> typeReference,
99+
@NonNull final HyperwalletListener listener) {
97100
this.gqlQuery = gqlQuery;
98101
this.typeReference = typeReference;
99102
this.listener = listener;
100103
}
101104

102-
protected GqlTransaction build(@NonNull final String uri, @NonNull final String userToken,
103-
@NonNull final String authenticationToken) {
105+
protected GqlTransaction build(@NonNull final String uri,
106+
@NonNull final String userToken,
107+
@NonNull final String authenticationToken) {
104108
String query = gqlQuery.toQuery(userToken);
105109
return new GqlTransaction(uri, query, authenticationToken, listener, typeReference);
106110
}
107111
}
108-
}
112+
}

core/src/main/java/com/hyperwallet/android/exception/HyperwalletGqlException.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@
3535
*/
3636
public class HyperwalletGqlException extends HyperwalletException {
3737

38+
private final int mHttpCode;
39+
3840
/**
3941
* Constructs a {@code HyperwalletGqlException} with the specified {@link GqlErrors} cause.
4042
*
4143
* @param gqlErrors the {@code GqlErrors} cause of the exception, must not be null
4244
*/
43-
public HyperwalletGqlException(@NonNull final GqlErrors gqlErrors) {
45+
public HyperwalletGqlException(int httpCode, @NonNull final GqlErrors gqlErrors) {
4446
super();
47+
mHttpCode = httpCode;
48+
4549
List<Error> errorList = new ArrayList<>();
4650
List<GqlError> errors = gqlErrors.getGQLErrors();
4751
for (GqlError gqlError : errors) {
@@ -51,4 +55,13 @@ public HyperwalletGqlException(@NonNull final GqlErrors gqlErrors) {
5155
}
5256
mErrors = new Errors(errorList);
5357
}
54-
}
58+
59+
/**
60+
* Returns the HTTP response code.
61+
*
62+
* @return the HTTP response code
63+
*/
64+
public int getHttpCode() {
65+
return mHttpCode;
66+
}
67+
}

core/src/test/java/com/hyperwallet/android/ExceptionMapperTest.java

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,50 @@ public void testToHyperwalletException_convertHyperwalletException() throws Exce
6969
assertThat(error.getMessage(), is(equalTo("The caller does not have access to the requested resource")));
7070
}
7171

72+
@Test
73+
public void testToHyperwalletException_convertRestUnauthenticatedHyperwalletException() throws Exception {
74+
when(mResources.getString(R.string.authentication_token_provider_exception)).thenReturn(
75+
"Authentication token retrieval attempt resulted in an error");
76+
Errors errorsFromJson = JsonUtils.fromJsonString(mResourceManager.getResourceContentError(
77+
"jwt_token_expired.json"),
78+
new TypeReference<Errors>() {
79+
});
80+
HyperwalletRestException hyperwalletException = new HyperwalletRestException(HttpURLConnection.HTTP_UNAUTHORIZED, errorsFromJson);
81+
HyperwalletException hyperwalletExceptionResult = toHyperwalletException(hyperwalletException);
82+
assertNotNull(hyperwalletExceptionResult);
83+
84+
final Errors errors = hyperwalletExceptionResult.getErrors();
85+
assertNotNull(errors);
86+
final List<Error> list = errors.getErrors();
87+
assertThat(list, hasSize(1));
88+
89+
Error error = list.get(0);
90+
assertThat(error.getCode(), is(equalTo("EC_AUTHENTICATION_TOKEN_PROVIDER_EXCEPTION")));
91+
assertThat(error.getMessageFromResourceWhenAvailable(mResources), is(equalTo("Authentication token retrieval attempt resulted in an error")));
92+
}
93+
94+
@Test
95+
public void testToHyperwalletException_convertGqlUnauthenticatedHyperwalletException() throws Exception {
96+
when(mResources.getString(R.string.authentication_token_provider_exception)).thenReturn(
97+
"Authentication token retrieval attempt resulted in an error");
98+
GqlErrors gqlErrors = JsonUtils.fromJsonString(mResourceManager.getResourceContentError(
99+
"jwt_token_expired.json"), new TypeReference<GqlErrors>() {
100+
});
101+
102+
HyperwalletGqlException hyperwalletException = new HyperwalletGqlException(HttpURLConnection.HTTP_UNAUTHORIZED, gqlErrors);
103+
HyperwalletException hyperwalletExceptionResult = toHyperwalletException(hyperwalletException);
104+
assertNotNull(hyperwalletExceptionResult);
105+
106+
final Errors errors = hyperwalletExceptionResult.getErrors();
107+
assertNotNull(errors);
108+
final List<Error> list = errors.getErrors();
109+
assertThat(list, hasSize(1));
110+
111+
Error error = list.get(0);
112+
assertThat(error.getCode(), is(equalTo("EC_AUTHENTICATION_TOKEN_PROVIDER_EXCEPTION")));
113+
assertThat(error.getMessageFromResourceWhenAvailable(mResources), is(equalTo("Authentication token retrieval attempt resulted in an error")));
114+
}
115+
72116
@Test
73117
public void testToHyperwalletException_convertIOException() {
74118
when(mResources.getString(R.string.io_exception)).thenReturn(
@@ -167,7 +211,8 @@ public void testToHyperwalletException_convertHyperwalletGqlException() throws E
167211
"gql_error_response.json"), new TypeReference<GqlErrors>() {
168212
});
169213

170-
HyperwalletException hyperwalletException = toHyperwalletException(new HyperwalletGqlException(gqlErrors));
214+
HyperwalletException hyperwalletException = toHyperwalletException(
215+
new HyperwalletGqlException(HttpURLConnection.HTTP_BAD_REQUEST, gqlErrors));
171216
assertNotNull(hyperwalletException);
172217

173218
final Errors errors = hyperwalletException.getErrors();
@@ -203,26 +248,6 @@ public void testToHyperwalletRestException_convertHyperwalletRestException() thr
203248
assertThat(error.getMessage(), is(equalTo("A system error has occurred. Please try again. If you continue to receive this error, please contact customer support for assistance (Ref ID: 99b4ad5c-4aac-4cc2-aa9b-4b4f4844ac9b).")));
204249
}
205250

206-
@Test
207-
public void testToHyperwalletRestException_convertUnmappedExceptionUnauthorized() throws Exception {
208-
when(mResources.getString(R.string.unexpected_exception)).thenReturn(
209-
"An unexpected error has occurred, please try again");
210-
HyperwalletRestException hyperwalletRestException = new HyperwalletRestException(HttpURLConnection.HTTP_UNAUTHORIZED, null);
211-
HyperwalletException hyperwalletException = toHyperwalletException(hyperwalletRestException);
212-
assertNotNull(hyperwalletException);
213-
assertThat(hyperwalletException, instanceOf(HyperwalletException.class));
214-
215-
final Errors errors = hyperwalletException.getErrors();
216-
assertNotNull(errors);
217-
final List<Error> list = errors.getErrors();
218-
assertThat(list, hasSize(1));
219-
220-
Error error = list.get(0);
221-
assertThat(error.getCode(), is(equalTo("EC_UNEXPECTED_EXCEPTION")));
222-
assertThat(error.getMessageFromResourceWhenAvailable(mResources),
223-
is(equalTo("An unexpected error has occurred, please try again")));
224-
}
225-
226251
@Test
227252
public void testToHyperwalletRestException_convertUnmappedExceptionForbidden() throws Exception {
228253
when(mResources.getString(R.string.unexpected_exception)).thenReturn(

core/src/test/java/com/hyperwallet/android/GqlTransactionTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,24 @@
33
import static org.hamcrest.CoreMatchers.is;
44
import static org.hamcrest.CoreMatchers.notNullValue;
55
import static org.hamcrest.MatcherAssert.assertThat;
6+
import static org.hamcrest.Matchers.equalTo;
67
import static org.mockito.Mockito.verify;
8+
import static org.mockito.Mockito.when;
9+
10+
import android.content.res.Resources;
711

812
import com.hyperwallet.android.exception.HyperwalletException;
13+
import com.hyperwallet.android.exception.HyperwalletRestException;
914
import com.hyperwallet.android.listener.HyperwalletListener;
1015
import com.hyperwallet.android.model.Error;
16+
import com.hyperwallet.android.model.Errors;
1117
import com.hyperwallet.android.model.TypeReference;
1218
import com.hyperwallet.android.model.graphql.HyperwalletTransferMethodConfigurationKey;
1319
import com.hyperwallet.android.model.graphql.query.TransferMethodConfigurationKeysQuery;
1420
import com.hyperwallet.android.rule.ExternalResourceManager;
1521
import com.hyperwallet.android.util.HttpClient;
1622
import com.hyperwallet.android.util.HttpMethod;
23+
import com.hyperwallet.android.sdk.R;
1724

1825
import org.json.JSONException;
1926
import org.junit.Rule;
@@ -29,6 +36,7 @@
2936

3037
import java.io.IOException;
3138
import java.net.HttpURLConnection;
39+
import java.util.List;
3240

3341
@RunWith(RobolectricTestRunner.class)
3442
public class GqlTransactionTest {
@@ -44,6 +52,8 @@ public class GqlTransactionTest {
4452
private HyperwalletListener<HyperwalletTransferMethodConfigurationKey> mListener;
4553
@Mock
4654
private HttpClient mHttpClient;
55+
@Mock
56+
private Resources mResources;
4757

4858
@Captor
4959
private ArgumentCaptor<String> mPayloadCaptor;
@@ -108,6 +118,33 @@ public void testHandleErrors_responseParsedSuccessfully() throws Exception {
108118
assertThat(error.getMessage(), is("Could not find any currency."));
109119
}
110120

121+
@Test
122+
public void testHandleErrors_responseUnauthorized() throws Exception {
123+
when(mResources.getString(R.string.authentication_token_provider_exception)).thenReturn(
124+
"Authentication token retrieval attempt resulted in an error");
125+
String responseBody = mExternalResourceManager.getResourceContentError("jwt_token_expired.json");
126+
127+
TransferMethodConfigurationKeysQuery keysQuery = new TransferMethodConfigurationKeysQuery();
128+
129+
GqlTransaction.Builder<HyperwalletTransferMethodConfigurationKey> builder =
130+
new GqlTransaction.Builder<>(keysQuery,
131+
new TypeReference<HyperwalletTransferMethodConfigurationKey>() {
132+
}, mListener);
133+
final GqlTransaction gqlTransaction = builder.build("test", "test-user-token",
134+
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9");
135+
136+
gqlTransaction.handleErrors(HttpURLConnection.HTTP_UNAUTHORIZED, responseBody);
137+
verify(mListener).onFailure(mExceptionArgumentCaptor.capture());
138+
139+
HyperwalletException exception = mExceptionArgumentCaptor.getValue();
140+
assertThat(exception.getErrors().getErrors().size(), is(1));
141+
Error error = exception.getErrors().getErrors().get(0);
142+
143+
assertThat(error.getCode(), is(equalTo("EC_AUTHENTICATION_TOKEN_PROVIDER_EXCEPTION")));
144+
assertThat(error.getMessageFromResourceWhenAvailable(mResources),
145+
is(equalTo("Authentication token retrieval attempt resulted in an error")));
146+
}
147+
111148
@Test
112149
public void testHandleErrors_errorWhenParsingResponse() throws Exception {
113150
mThrown.expect(JSONException.class);

core/src/test/java/com/hyperwallet/android/RestTransactionBuilderTest.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void testBuild_withRequiredParametersOnly() throws JSONException {
6767
assertThat(headers.get("Content-Type"), is("application/json"));
6868
assertThat(headers.get("User-Agent"), is("HyperwalletSDK/Android/" + BuildConfig.VERSION_NAME +
6969
"; App: HyperwalletSDK; Android: " + Build.VERSION.RELEASE));
70-
assertThat(headers.get("X-Sdk-Version"), is("1.0.0-beta11"));
70+
assertThat(headers.get("X-Sdk-Version"), is("1.0.0-beta12"));
7171
assertThat(headers.get("X-Sdk-Type"), is("android"));
7272
assertThat(headers.get("X-Sdk-ContextId"), is(notNullValue()));
7373
assertThat(headers.get("X-Sdk-ContextId"), is(contextId));
@@ -104,7 +104,7 @@ public void testBuild_withJsonModelOptionalParameter() throws JSONException {
104104
assertThat(headers.get("Content-Type"), is("application/json"));
105105
assertThat(headers.get("User-Agent"), is("HyperwalletSDK/Android/" + BuildConfig.VERSION_NAME +
106106
"; App: HyperwalletSDK; Android: " + Build.VERSION.RELEASE));
107-
assertThat(headers.get("X-Sdk-Version"), is("1.0.0-beta11"));
107+
assertThat(headers.get("X-Sdk-Version"), is("1.0.0-beta12"));
108108
assertThat(headers.get("X-Sdk-Type"), is("android"));
109109
assertThat(headers.get("X-Sdk-ContextId"), is(notNullValue()));
110110
assertThat(headers.get("X-Sdk-ContextId"), is(contextId));
@@ -140,11 +140,9 @@ public void testBuild_withQueryModelOptionalParameter() throws JSONException {
140140
assertThat(headers.get("Content-Type"), is("application/json"));
141141
assertThat(headers.get("User-Agent"), is("HyperwalletSDK/Android/" + BuildConfig.VERSION_NAME +
142142
"; App: HyperwalletSDK; Android: " + Build.VERSION.RELEASE));
143-
assertThat(headers.get("X-Sdk-Version"), is("1.0.0-beta11"));
143+
assertThat(headers.get("X-Sdk-Version"), is("1.0.0-beta12"));
144144
assertThat(headers.get("X-Sdk-Type"), is("android"));
145145
assertThat(headers.get("X-Sdk-ContextId"), is(notNullValue()));
146146
assertThat(headers.get("X-Sdk-ContextId"), is(contextId));
147147
}
148-
}
149-
150-
148+
}

0 commit comments

Comments
 (0)