Skip to content

Commit 6523ef3

Browse files
FroMagegsmet
authored andcommitted
Make sure that we test context propagation between rest server and client
There's a change in Vert.x 4.5.14+ that stops copying context locals from the parent to the child duplicated contexts, so we have to do it via nested contexts (where we manually copy the locals). This is required for things like propagating HR transactions/sessions. From @gsmet: Apparently, 3.15 is not affected but it's nice to have a test. Related to quarkusio#49163 and quarkusio#49133
1 parent bdc9929 commit 6523ef3

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package io.quarkus.rest.client.reactive;
2+
3+
import static io.restassured.RestAssured.when;
4+
import static org.hamcrest.Matchers.is;
5+
6+
import java.io.IOException;
7+
import java.util.Optional;
8+
9+
import jakarta.ws.rs.GET;
10+
import jakarta.ws.rs.Path;
11+
import jakarta.ws.rs.client.ClientRequestContext;
12+
import jakarta.ws.rs.client.ClientRequestFilter;
13+
import jakarta.ws.rs.client.ClientResponseContext;
14+
import jakarta.ws.rs.client.ClientResponseFilter;
15+
16+
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
17+
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
18+
import org.eclipse.microprofile.rest.client.inject.RestClient;
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.RegisterExtension;
21+
22+
import io.quarkus.test.QuarkusUnitTest;
23+
import io.smallrye.common.vertx.ContextLocals;
24+
import io.smallrye.mutiny.Uni;
25+
import io.vertx.core.Context;
26+
import io.vertx.core.Vertx;
27+
28+
public class ContextLocalPropagationTest {
29+
30+
@RegisterExtension
31+
static final QuarkusUnitTest config = new QuarkusUnitTest()
32+
.withApplicationRoot((jar) -> jar.addClasses(Resource.class, Client.class))
33+
.overrideRuntimeConfigKey("quarkus.rest-client.client.url",
34+
"http://localhost:${quarkus.http.test-port:8081}");
35+
36+
@Test
37+
void testClientFilterSeesParentContext() {
38+
when().get("test/invokeClient")
39+
.then()
40+
.statusCode(200)
41+
.body(is("test/foo/bar"));
42+
}
43+
44+
@Test
45+
void testClientUniInheritsParentContext() {
46+
when().get("test/client-inherits-context")
47+
.then()
48+
.statusCode(200)
49+
.body(is("test/set-in-parent"));
50+
}
51+
52+
@Path("test")
53+
public static class Resource {
54+
55+
private final Client client;
56+
57+
public Resource(@RestClient Client client) {
58+
this.client = client;
59+
}
60+
61+
@Path("invokeClient")
62+
@GET
63+
public String invokeClient() {
64+
var result = client.get();
65+
Optional<String> fromRequest = ContextLocals.get("fromRequest");
66+
Optional<String> fromResponse = ContextLocals.get("fromResponse");
67+
return result + "/" + fromRequest.orElse("none") + "/" + fromResponse.orElse("none");
68+
}
69+
70+
@Path("client-inherits-context")
71+
@GET
72+
public Uni<String> clientInheritsContext() {
73+
ContextLocals.put("parent-value", "set-in-parent");
74+
return client.getUni()
75+
.map(result -> result + "/" + ContextLocals.get("parent-value").orElse("none"));
76+
}
77+
78+
@Path("toClient")
79+
@GET
80+
public String toClient() {
81+
return "test";
82+
}
83+
84+
@Path("toClient2")
85+
@GET
86+
public String toClient2() {
87+
return "test";
88+
}
89+
}
90+
91+
@Path("test")
92+
@RegisterRestClient(configKey = "client")
93+
@RegisterProvider(RequestFilter.class)
94+
@RegisterProvider(ResponseFilter.class)
95+
public interface Client {
96+
97+
@GET
98+
@Path("toClient")
99+
String get();
100+
101+
@GET
102+
@Path("toClient2")
103+
Uni<String> getUni();
104+
}
105+
106+
public static class RequestFilter implements ClientRequestFilter {
107+
108+
@Override
109+
public void filter(ClientRequestContext requestContext) throws IOException {
110+
RestClientContextUtil.putLocal("fromRequest", "foo");
111+
}
112+
}
113+
114+
public static class ResponseFilter implements ClientResponseFilter {
115+
116+
@Override
117+
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
118+
throws IOException {
119+
RestClientContextUtil.putLocal("fromResponse", "bar");
120+
}
121+
}
122+
123+
public static final class RestClientContextUtil {
124+
125+
private RestClientContextUtil() {
126+
}
127+
128+
public static void putLocal(Object key, Object value) {
129+
determineRestClientContext().putLocal(key, value);
130+
}
131+
132+
private static Context determineRestClientContext() {
133+
// In an ideal world, this would always be populated, however because we never
134+
// defined a proper execution model for the REST Client handlers, currently we are
135+
// in a situation where request filters could be run on the calling context
136+
// and not the client's purpose built context.
137+
// We will need a proper solution soon, but as we need to have a proper way to
138+
// set contextual information in Quarkus 3.20 (LTS), we can't risk breaking
139+
// client code everywhere, so for now we will tell people to check the context
140+
Optional<Object> maybeParentContext = ContextLocals.get("__PARENT_CONTEXT__");
141+
Context effectiveContext;
142+
if (maybeParentContext.isPresent()) {
143+
effectiveContext = (Context) maybeParentContext.get();
144+
} else {
145+
effectiveContext = Vertx.currentContext();
146+
}
147+
return effectiveContext;
148+
}
149+
}
150+
}

0 commit comments

Comments
 (0)