Skip to content

Commit e20aad5

Browse files
committed
Allow accessing headers from invalid responses
1 parent 46824f3 commit e20aad5

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

client/api/src/main/java/io/smallrye/graphql/client/InvalidResponseException.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.smallrye.graphql.client;
22

3+
import java.util.List;
4+
import java.util.Map;
5+
36
/**
47
* Marks a response that had unexpected contents and the client was unable to properly process it.
58
* This can be either due to an error on the server side (returning non-conformant responses),
@@ -8,12 +11,30 @@
811
*/
912
public class InvalidResponseException extends RuntimeException {
1013

14+
private final Map<String, List<String>> transportMeta;
15+
1116
public InvalidResponseException(String message) {
1217
super(message);
18+
this.transportMeta = null;
1319
}
1420

1521
public InvalidResponseException(String message, Throwable cause) {
1622
super(message, cause);
23+
this.transportMeta = null;
24+
}
25+
26+
public InvalidResponseException(String message, Throwable cause, Map<String, List<String>> transportMeta) {
27+
super(message, cause);
28+
this.transportMeta = transportMeta;
29+
}
30+
31+
/**
32+
* Get transport-specific metadata that came from the server with the invalid response.
33+
* For HTTP, these are the response headers. It can be null if headers aren't applicable, for example
34+
* if this is coming from a message received over a WebSocket connection.
35+
*/
36+
public Map<String, List<String>> getTransportMeta() {
37+
return transportMeta;
1738
}
1839

1940
}

client/implementation/src/main/java/io/smallrye/graphql/client/impl/ResponseReader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ public static ResponseImpl readFrom(String input, Map<String, List<String>> head
7979
if (jsonResponse == null) {
8080
throw new InvalidResponseException(
8181
"Unexpected response. Code=" + statusCode + ", message=\"" + statusMessage + "\", " +
82-
"body=\"" + input + "\"");
82+
"body=\"" + input + "\"",
83+
null, headers);
8384
}
8485
JsonObject data = null;
8586
if (jsonResponse.containsKey("data")) {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package io.smallrye.graphql.tests.client.dynamic.error;
2+
3+
import java.io.IOException;
4+
import java.net.URL;
5+
import java.util.concurrent.ExecutionException;
6+
7+
import jakarta.servlet.ServletException;
8+
import jakarta.servlet.annotation.WebServlet;
9+
import jakarta.servlet.http.HttpServlet;
10+
import jakarta.servlet.http.HttpServletRequest;
11+
import jakarta.servlet.http.HttpServletResponse;
12+
13+
import org.jboss.arquillian.container.test.api.Deployment;
14+
import org.jboss.arquillian.container.test.api.RunAsClient;
15+
import org.jboss.arquillian.junit.Arquillian;
16+
import org.jboss.arquillian.test.api.ArquillianResource;
17+
import org.jboss.shrinkwrap.api.ShrinkWrap;
18+
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
19+
import org.jboss.shrinkwrap.api.spec.WebArchive;
20+
import org.junit.Assert;
21+
import org.junit.Before;
22+
import org.junit.Test;
23+
import org.junit.runner.RunWith;
24+
25+
import io.smallrye.graphql.client.InvalidResponseException;
26+
import io.smallrye.graphql.client.vertx.dynamic.VertxDynamicGraphQLClient;
27+
import io.smallrye.graphql.client.vertx.dynamic.VertxDynamicGraphQLClientBuilder;
28+
29+
/**
30+
* Verify that the client allows access to HTTP response headers when an invalid response is received.
31+
* In this test, a servlet is used to return a 418 I'm a teapot response with a custom header.
32+
* The client then makes a request and verifies that the InvalidResponseException contains the custom header.
33+
*/
34+
@RunWith(Arquillian.class)
35+
@RunAsClient
36+
public class InvalidResponseTest {
37+
38+
@Deployment
39+
public static WebArchive deployment() {
40+
return ShrinkWrap.create(WebArchive.class, "invalid-response-test.war")
41+
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
42+
.addClasses(ErrorServlet.class);
43+
}
44+
45+
@WebServlet(urlPatterns = { "/graphql" })
46+
public static class ErrorServlet extends HttpServlet {
47+
@Override
48+
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
49+
resp.setStatus(418); // I'm a teapot
50+
resp.setHeader("Teapot-Color", "Yellow");
51+
}
52+
}
53+
54+
@ArquillianResource
55+
URL testingURL;
56+
57+
private static VertxDynamicGraphQLClient client;
58+
59+
@Before
60+
public void prepare() {
61+
client = (VertxDynamicGraphQLClient) new VertxDynamicGraphQLClientBuilder()
62+
.url(testingURL.toString() + "graphql")
63+
.build();
64+
}
65+
66+
@Test
67+
public void receiveImATeapotResponse() throws ExecutionException, InterruptedException {
68+
try {
69+
client.executeSync("{}");
70+
Assert.fail();
71+
} catch (InvalidResponseException ex) {
72+
Assert.assertEquals("Yellow", ex.getTransportMeta().get("Teapot-Color").get(0));
73+
}
74+
}
75+
76+
}

0 commit comments

Comments
 (0)