Skip to content
This repository was archived by the owner on Jul 7, 2021. It is now read-only.

Commit e02c2bd

Browse files
authored
#241: catch FinapiAuthenticationException (#360)
1 parent 823465b commit e02c2bd

File tree

5 files changed

+191
-119
lines changed

5 files changed

+191
-119
lines changed

src/main/java/org/proshin/finapi/endpoint/FpEndpoint.java

Lines changed: 48 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.cactoos.text.UncheckedText;
4444
import org.proshin.finapi.Jsonable;
4545
import org.proshin.finapi.accesstoken.AccessToken;
46+
import org.proshin.finapi.exception.FinapiAuthenticationException;
4647
import org.proshin.finapi.exception.FinapiException;
4748
import org.slf4j.Logger;
4849
import org.slf4j.LoggerFactory;
@@ -71,19 +72,8 @@ public String get(final String path, final AccessToken token, final Iterable<Nam
7172
builder.setParameters(new ListOf<>(parameters));
7273
final HttpUriRequest get = new HttpGet(builder.build());
7374
get.addHeader(new AuthorizationHeader(token.accessToken()));
74-
final HttpResponse response = this.client.execute(get);
75-
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
76-
throw new FinapiException(HttpStatus.SC_OK, response);
77-
}
78-
final String responseBody = new UncheckedText(
79-
new TextOf(
80-
new InputOf(response.getEntity().getContent()),
81-
StandardCharsets.UTF_8
82-
)
83-
).asString();
84-
LOGGER.info("Response body was: {}", responseBody);
85-
return responseBody;
86-
} catch (final IOException | URISyntaxException e) {
75+
return this.execute(get);
76+
} catch (final URISyntaxException e) {
8777
throw new RuntimeException(
8878
new UncheckedText(
8979
new FormattedText(
@@ -103,19 +93,8 @@ public String delete(final String path, final AccessToken token, final Iterable<
10393
builder.setParameters(new ListOf<>(parameters));
10494
final HttpUriRequest delete = new HttpDelete(builder.build());
10595
delete.addHeader(new AuthorizationHeader(token.accessToken()));
106-
final HttpResponse response = this.client.execute(delete);
107-
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
108-
throw new FinapiException(HttpStatus.SC_OK, response);
109-
}
110-
final String responseBody = new UncheckedText(
111-
new TextOf(
112-
new InputOf(response.getEntity().getContent()),
113-
StandardCharsets.UTF_8
114-
)
115-
).asString();
116-
LOGGER.info("Response body was: {}", responseBody);
117-
return responseBody;
118-
} catch (final IOException | URISyntaxException e) {
96+
return this.execute(delete);
97+
} catch (final URISyntaxException e) {
11998
throw new IllegalStateException(
12099
new UncheckedText(
121100
new FormattedText(
@@ -142,29 +121,9 @@ public HttpPost post(final String path) {
142121
*/
143122
@Override
144123
public String post(final String path, final HttpEntity entity, final int expected) {
145-
try {
146-
final HttpPost post = new HttpPost(this.endpoint + path);
147-
post.setEntity(entity);
148-
final HttpResponse response = this.client.execute(post);
149-
if (response.getStatusLine().getStatusCode() != expected) {
150-
throw new FinapiException(expected, response);
151-
}
152-
return new UncheckedText(
153-
new TextOf(
154-
new InputOf(response.getEntity().getContent()),
155-
StandardCharsets.UTF_8
156-
)
157-
).asString();
158-
} catch (final IOException e) {
159-
throw new IllegalStateException(
160-
new UncheckedText(
161-
new FormattedText(
162-
"Couldn't post to '%s'",
163-
path
164-
)
165-
).asString()
166-
);
167-
}
124+
final HttpPost post = new HttpPost(this.endpoint + path);
125+
post.setEntity(entity);
126+
return this.execute(post, expected);
168127
}
169128

170129
@Override
@@ -176,67 +135,50 @@ public HttpPost post(final String path, final AccessToken token) {
176135

177136
@Override
178137
public String post(final String path, final AccessToken token, final int expected) {
179-
try {
180-
final HttpUriRequest post = new HttpPost(this.endpoint + path);
181-
post.addHeader(new AuthorizationHeader(token.accessToken()));
182-
final HttpResponse response = this.client.execute(post);
183-
if (response.getStatusLine().getStatusCode() != expected) {
184-
throw new FinapiException(expected, response);
185-
}
186-
return new UncheckedText(
187-
new TextOf(
188-
new InputOf(response.getEntity().getContent()),
189-
StandardCharsets.UTF_8
190-
)
191-
).asString();
192-
} catch (final IOException e) {
193-
throw new IllegalStateException(
194-
new UncheckedText(
195-
new FormattedText(
196-
"Couldn't post to '%s'",
197-
path
198-
)
199-
).asString()
200-
);
201-
}
138+
final HttpUriRequest post = new HttpPost(this.endpoint + path);
139+
post.addHeader(new AuthorizationHeader(token.accessToken()));
140+
return this.execute(post, expected);
202141
}
203142

204143
@Override
205144
public String post(final String path, final AccessToken token, final HttpEntity entity, final int expected) {
206-
try {
207-
final HttpPost post = new HttpPost(this.endpoint + path);
208-
post.addHeader(new AuthorizationHeader(token.accessToken()));
209-
post.setEntity(entity);
210-
final HttpResponse response = this.client.execute(post);
211-
if (response.getStatusLine().getStatusCode() != expected) {
212-
throw new FinapiException(expected, response);
213-
}
214-
return new UncheckedText(
215-
new TextOf(
216-
new InputOf(response.getEntity().getContent()),
217-
StandardCharsets.UTF_8
218-
)
219-
).asString();
220-
} catch (final IOException e) {
221-
throw new IllegalStateException(
222-
new UncheckedText(
223-
new FormattedText(
224-
"Couldn't post to '%s'",
225-
path
226-
)
227-
).asString()
228-
);
229-
}
145+
final HttpPost post = new HttpPost(this.endpoint + path);
146+
post.addHeader(new AuthorizationHeader(token.accessToken()));
147+
post.setEntity(entity);
148+
return this.execute(post, expected);
230149
}
231150

232151
@Override
233152
public String patch(final String path, final AccessToken token, final HttpEntity entity, final int expected) {
153+
final HttpPatch patch = new HttpPatch(this.endpoint + path);
154+
patch.addHeader(new AuthorizationHeader(token.accessToken()));
155+
patch.setEntity(entity);
156+
return this.execute(patch, expected);
157+
}
158+
159+
@Override
160+
public String patch(final String path, final AccessToken token, final Jsonable body) {
161+
return this.patch(
162+
path, token,
163+
new StringEntity(
164+
body.asString(),
165+
ContentType.APPLICATION_JSON
166+
),
167+
HttpStatus.SC_OK
168+
);
169+
}
170+
171+
private String execute(final HttpUriRequest request) {
172+
return this.execute(request, HttpStatus.SC_OK);
173+
}
174+
175+
private String execute(final HttpUriRequest request, final int expected) {
234176
try {
235-
final HttpPatch patch = new HttpPatch(this.endpoint + path);
236-
patch.addHeader(new AuthorizationHeader(token.accessToken()));
237-
patch.setEntity(entity);
238-
final HttpResponse response = this.client.execute(patch);
239-
if (response.getStatusLine().getStatusCode() != expected) {
177+
final HttpResponse response = this.client.execute(request);
178+
final int statusCode = response.getStatusLine().getStatusCode();
179+
if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
180+
throw new FinapiAuthenticationException(response);
181+
} else if (statusCode != expected) {
240182
throw new FinapiException(expected, response);
241183
}
242184
return new UncheckedText(
@@ -246,29 +188,18 @@ public String patch(final String path, final AccessToken token, final HttpEntity
246188
)
247189
).asString();
248190
} catch (final IOException e) {
249-
throw new IllegalStateException(
191+
throw new RuntimeException(
250192
new UncheckedText(
251193
new FormattedText(
252-
"Couldn't post to '%s'",
253-
path
194+
"Couldn't get '%s'",
195+
request.getURI()
254196
)
255-
).asString()
197+
).asString(),
198+
e
256199
);
257200
}
258201
}
259202

260-
@Override
261-
public String patch(final String path, final AccessToken token, final Jsonable body) {
262-
return this.patch(
263-
path, token,
264-
new StringEntity(
265-
body.asString(),
266-
ContentType.APPLICATION_JSON
267-
),
268-
HttpStatus.SC_OK
269-
);
270-
}
271-
272203
@SuppressWarnings("staticfree")
273204
private static final class AuthorizationHeader implements Header {
274205

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2018 Roman Proshin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.proshin.finapi.exception;
17+
18+
import java.io.IOException;
19+
import java.nio.charset.StandardCharsets;
20+
import java.util.function.Supplier;
21+
import org.apache.http.HttpResponse;
22+
import org.cactoos.io.InputOf;
23+
import org.cactoos.text.TextOf;
24+
import org.cactoos.text.UncheckedText;
25+
import org.json.JSONObject;
26+
27+
public final class FinapiAuthenticationException extends RuntimeException {
28+
29+
@SuppressWarnings("staticfree")
30+
private static final long serialVersionUID = -2536176269994901299L;
31+
32+
@SuppressWarnings("TransientFieldNotInitialized")
33+
private final transient String requestId;
34+
35+
@SuppressWarnings("nullfree")
36+
public FinapiAuthenticationException(final HttpResponse response) {
37+
this(
38+
((Supplier<String>) () -> {
39+
try {
40+
final JSONObject jsonObject = new JSONObject(
41+
new UncheckedText(
42+
new TextOf(
43+
new InputOf(response.getEntity().getContent()),
44+
StandardCharsets.UTF_8
45+
)
46+
).asString()
47+
);
48+
return String.format("%s: %s",
49+
jsonObject.getString("error"), jsonObject.getString("error_description")
50+
);
51+
} catch (final IOException e) {
52+
throw new RuntimeException("Couldn't read the response body", e);
53+
}
54+
}
55+
).get(),
56+
response.getFirstHeader("X-Request-Id").getValue()
57+
);
58+
}
59+
60+
public FinapiAuthenticationException(final String message, final String requestId) {
61+
super(message);
62+
this.requestId = requestId;
63+
}
64+
65+
public String requestId() {
66+
return this.requestId;
67+
}
68+
}

src/main/java/org/proshin/finapi/exception/FinapiException.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public final class FinapiException extends RuntimeException {
3636

3737
@SuppressWarnings("TransientFieldNotInitialized")
3838
private final transient JSONObject origin;
39+
@SuppressWarnings("TransientFieldNotInitialized")
40+
private final transient String requestId;
3941
@SuppressWarnings({"TransientFieldNotInitialized", "OptionalUsedAsFieldOrParameterType"})
4042
private final transient Optional<String> location;
4143

@@ -61,15 +63,22 @@ public FinapiException(final int expected, final HttpResponse response) {
6163
}
6264
}
6365
).get(),
66+
response.getFirstHeader("X-Request-Id").getValue(),
6467
Optional.ofNullable(response.getFirstHeader("Location"))
6568
.map(NameValuePair::getValue)
6669
.orElse(null)
6770
);
6871
}
6972

70-
public FinapiException(final String message, final JSONObject origin, final String location) {
73+
public FinapiException(
74+
final String message,
75+
final JSONObject origin,
76+
final String requestId,
77+
final String location) {
78+
7179
super(message);
7280
this.origin = origin;
81+
this.requestId = requestId;
7382
this.location = Optional.ofNullable(location);
7483
}
7584

@@ -85,7 +94,7 @@ public OffsetDateTime date() {
8594
}
8695

8796
public String requestId() {
88-
return this.origin.getString("requestId");
97+
return this.requestId;
8998
}
9099

91100
public String endpoint() {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2019 Roman Proshin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.proshin.finapi.endpoint;
17+
18+
import org.apache.http.HttpStatus;
19+
import org.apache.http.entity.ContentType;
20+
import org.apache.http.entity.StringEntity;
21+
import static org.assertj.core.api.Assertions.fail;
22+
import org.junit.jupiter.api.Test;
23+
import org.mockserver.model.HttpRequest;
24+
import org.mockserver.model.HttpResponse;
25+
import org.mockserver.model.JsonBody;
26+
import org.proshin.finapi.TestWithMockedEndpoint;
27+
import org.proshin.finapi.exception.FinapiAuthenticationException;
28+
29+
final class FpEndpointTest extends TestWithMockedEndpoint {
30+
31+
@Test
32+
void testAuthenticationException() {
33+
final String body = '{' +
34+
" \"bankId\": 277672," +
35+
'}';
36+
this.server()
37+
.when(
38+
HttpRequest.request("/api/v1/bankConnections")
39+
.withMethod("POST")
40+
.withBody(new JsonBody(body))
41+
).respond(
42+
HttpResponse.response('{' +
43+
" \"error\": \"unauthorized\"," +
44+
" \"error_description\": \"An Authentication object was not found in the SecurityContext\"" +
45+
'}')
46+
.withStatusCode(HttpStatus.SC_UNAUTHORIZED)
47+
.withHeader("x-request-id", "request-id")
48+
);
49+
50+
try {
51+
this.endpoint()
52+
.post(
53+
"/api/v1/bankConnections",
54+
new StringEntity(body, ContentType.APPLICATION_JSON),
55+
HttpStatus.SC_CREATED
56+
);
57+
} catch (final FinapiAuthenticationException e) {
58+
// do nothing
59+
return;
60+
}
61+
fail("Should never be reached");
62+
}
63+
}

0 commit comments

Comments
 (0)