Skip to content

Commit d702f49

Browse files
authored
Merge pull request #46826 from FroMage/rest-client-duplicate-exception
Make response available on error in rest client
2 parents c1bccf6 + 3b277e7 commit d702f49

File tree

6 files changed

+146
-187
lines changed

6 files changed

+146
-187
lines changed

independent-projects/resteasy-reactive/client/runtime/pom.xml

+12
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,17 @@
6464
<artifactId>jboss-logging</artifactId>
6565
</dependency>
6666

67+
<dependency>
68+
<groupId>org.wiremock</groupId>
69+
<artifactId>wiremock-standalone</artifactId>
70+
<scope>test</scope>
71+
<exclusions>
72+
<exclusion>
73+
<groupId>commons-logging</groupId>
74+
<artifactId>commons-logging</artifactId>
75+
</exclusion>
76+
</exclusions>
77+
</dependency>
78+
6779
</dependencies>
6880
</project>

independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/api/WebClientApplicationException.java

-183
This file was deleted.

independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.jboss.resteasy.reactive.client.spi.FieldFiller;
2424
import org.jboss.resteasy.reactive.client.spi.MultipartResponseData;
2525
import org.jboss.resteasy.reactive.common.jaxrs.ResponseImpl;
26+
import org.jboss.resteasy.reactive.common.jaxrs.StatusTypeImpl;
2627

2728
import io.netty.handler.codec.http.multipart.Attribute;
2829
import io.netty.handler.codec.http.multipart.FileUpload;
@@ -38,10 +39,19 @@ public void handle(RestClientRequestContext context) throws Exception {
3839
public static ResponseImpl mapToResponse(RestClientRequestContext context,
3940
boolean parseContent)
4041
throws IOException {
42+
ClientResponseContextImpl responseContext = context.getOrCreateClientResponseContext();
43+
Response.StatusType statusType = new StatusTypeImpl(responseContext.getStatus(), responseContext.getReasonPhrase());
44+
return mapToResponse(context, statusType, parseContent);
45+
}
46+
47+
public static ResponseImpl mapToResponse(RestClientRequestContext context,
48+
Response.StatusType effectiveResponseStatus,
49+
boolean parseContent)
50+
throws IOException {
4151
Map<Class<?>, MultipartResponseData> multipartDataMap = context.getMultipartResponsesData();
4252
ClientResponseContextImpl responseContext = context.getOrCreateClientResponseContext();
4353
ClientResponseBuilderImpl builder = new ClientResponseBuilderImpl();
44-
builder.status(responseContext.getStatus(), responseContext.getReasonPhrase());
54+
builder.status(effectiveResponseStatus);
4555
builder.setAllHeaders(responseContext.getHeaders());
4656
builder.invocationState(context);
4757
InputStream entityStream = responseContext.getEntityStream();

independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientSetResponseEntityRestHandler.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import jakarta.ws.rs.core.Response;
99
import jakarta.ws.rs.core.Response.StatusType;
1010

11+
import org.jboss.resteasy.reactive.ClientWebApplicationException;
1112
import org.jboss.resteasy.reactive.RestResponse;
12-
import org.jboss.resteasy.reactive.client.api.WebClientApplicationException;
1313
import org.jboss.resteasy.reactive.client.impl.ClientRequestContextImpl;
1414
import org.jboss.resteasy.reactive.client.impl.ClientResponseContextImpl;
1515
import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext;
@@ -28,8 +28,8 @@ public void handle(RestClientRequestContext context) throws Exception {
2828
if (context.isCheckSuccessfulFamily()) {
2929
StatusType effectiveResponseStatus = determineEffectiveResponseStatus(context, requestContext);
3030
if (Response.Status.Family.familyOf(effectiveResponseStatus.getStatusCode()) != Response.Status.Family.SUCCESSFUL) {
31-
throw new WebClientApplicationException(effectiveResponseStatus.getStatusCode(),
32-
effectiveResponseStatus.getReasonPhrase());
31+
Response response = ClientResponseCompleteRestHandler.mapToResponse(context, effectiveResponseStatus, true);
32+
throw new ClientWebApplicationException(response);
3333
}
3434
}
3535

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package org.jboss.resteasy.reactive.client.handlers;
2+
3+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
4+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
5+
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
import static org.junit.jupiter.api.Assertions.assertNotNull;
8+
import static org.junit.jupiter.api.Assertions.assertTrue;
9+
import static org.junit.jupiter.api.Assertions.fail;
10+
11+
import java.util.Map;
12+
13+
import jakarta.ws.rs.WebApplicationException;
14+
import jakarta.ws.rs.client.Client;
15+
import jakarta.ws.rs.client.ClientBuilder;
16+
import jakarta.ws.rs.core.HttpHeaders;
17+
import jakarta.ws.rs.core.MediaType;
18+
import jakarta.ws.rs.core.Response;
19+
import jakarta.ws.rs.core.UriBuilder;
20+
21+
import org.junit.jupiter.api.AfterAll;
22+
import org.junit.jupiter.api.BeforeAll;
23+
import org.junit.jupiter.api.Test;
24+
25+
import com.github.tomakehurst.wiremock.WireMockServer;
26+
27+
import wiremock.com.fasterxml.jackson.core.JsonProcessingException;
28+
import wiremock.com.fasterxml.jackson.core.type.TypeReference;
29+
import wiremock.com.fasterxml.jackson.databind.ObjectMapper;
30+
31+
class ClientResponseCompleteRestHandlerTest {
32+
33+
private static final int MOCK_SERVER_PORT = 9300;
34+
private static final WireMockServer wireMockServer = new WireMockServer(MOCK_SERVER_PORT);
35+
private static final ObjectMapper objectMapper = new ObjectMapper();
36+
private static final Client httpClient = ClientBuilder.newBuilder().build();
37+
38+
@BeforeAll
39+
static void start() {
40+
wireMockServer.stubFor(get(urlPathEqualTo("/get-400"))
41+
.willReturn(aResponse().withStatus(400).withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
42+
.withBody("{\"error\": true, \"message\": \"Invalid parameter\"}")));
43+
wireMockServer.stubFor(get(urlPathEqualTo("/get-500"))
44+
.willReturn(aResponse().withStatus(500).withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
45+
.withBody("{\"error\": true, \"message\": \"Unexpected error.\"}")));
46+
wireMockServer.start();
47+
}
48+
49+
@AfterAll
50+
static void stop() {
51+
wireMockServer.stop();
52+
}
53+
54+
private String generateURL(String path) {
55+
return UriBuilder.fromUri("http://localhost:" + MOCK_SERVER_PORT).path(path).build().toString();
56+
}
57+
58+
@Test
59+
void testResponseOnClientError() throws Exception {
60+
try {
61+
httpClient.target(generateURL("/get-400")).request().get(String.class);
62+
fail("Should have thrown an exception");
63+
} catch (WebApplicationException e) {
64+
Response response = e.getResponse();
65+
checkResponse(response);
66+
Map<String, Object> parsedResponseContent = readAndParseResponse(response);
67+
assertEquals("Invalid parameter", parsedResponseContent.get("message"));
68+
}
69+
70+
// Test when we manually handle the response.
71+
try (Response response = httpClient.target(generateURL("/get-400")).request().get()) {
72+
checkResponse(response);
73+
Map<String, Object> parsedResponseContent = readAndParseResponse(response);
74+
assertEquals("Invalid parameter", parsedResponseContent.get("message"));
75+
}
76+
}
77+
78+
@Test
79+
void testResponseOnServerError() throws Exception {
80+
try {
81+
httpClient.target(generateURL("/get-500")).request().get(String.class);
82+
fail("Should have thrown an exception");
83+
} catch (WebApplicationException e) {
84+
Response response = e.getResponse();
85+
checkResponse(response);
86+
Map<String, Object> parsedResponseContent = readAndParseResponse(response);
87+
assertEquals("Unexpected error.", parsedResponseContent.get("message"));
88+
}
89+
90+
// Test when we manually handle the response.
91+
try (Response response = httpClient.target(generateURL("/get-500")).request().get()) {
92+
checkResponse(response);
93+
Map<String, Object> parsedResponseContent = readAndParseResponse(response);
94+
assertEquals("Unexpected error.", parsedResponseContent.get("message"));
95+
}
96+
}
97+
98+
private void checkResponse(Response response) {
99+
assertNotNull(response);
100+
assertEquals(MediaType.APPLICATION_JSON, response.getHeaderString(HttpHeaders.CONTENT_TYPE));
101+
assertTrue(response.hasEntity());
102+
response.bufferEntity();
103+
}
104+
105+
private Map<String, Object> readAndParseResponse(Response response) throws JsonProcessingException {
106+
String responseContent = response.readEntity(String.class);
107+
return objectMapper.readValue(responseContent,
108+
new TypeReference<>() {
109+
});
110+
}
111+
112+
}

independent-projects/resteasy-reactive/pom.xml

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
<smallrye-mutiny-vertx-core.version>3.18.1</smallrye-mutiny-vertx-core.version>
7171
<reactive-streams.version>1.0.4</reactive-streams.version>
7272
<mockito.version>5.16.0</mockito.version>
73+
<wiremock.version>3.11.0</wiremock.version>
7374
<mutiny-zero.version>1.1.1</mutiny-zero.version>
7475

7576
<!-- Forbidden API checks -->
@@ -337,6 +338,13 @@
337338
<scope>test</scope>
338339
</dependency>
339340

341+
<dependency>
342+
<groupId>org.wiremock</groupId>
343+
<artifactId>wiremock-standalone</artifactId>
344+
<version>${wiremock.version}</version>
345+
<scope>test</scope>
346+
</dependency>
347+
340348
<dependency>
341349
<groupId>io.rest-assured</groupId>
342350
<artifactId>rest-assured</artifactId>

0 commit comments

Comments
 (0)