Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly create REST Client template path when @Url is used #45010

Merged
merged 3 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package io.quarkus.micrometer.deployment.binder;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.search.Search;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.quarkus.rest.client.reactive.Url;
import io.quarkus.test.QuarkusUnitTest;

public class RestClientUriParameterTest {

final static SimpleMeterRegistry registry = new SimpleMeterRegistry();

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.withApplicationRoot(
jar -> jar.addClasses(Resource.class, Client.class))
.overrideConfigKey("quarkus.rest-client.\"client\".url", "http://does-not-exist.io");

@RestClient
Client client;

@ConfigProperty(name = "quarkus.http.test-port")
Integer testPort;

@BeforeAll
static void setRegistry() {
Metrics.addRegistry(registry);
}

@AfterAll()
static void removeRegistry() {
Metrics.removeRegistry(registry);
}

@Test
public void testOverride() {
String result = client.getById("http://localhost:" + testPort, "bar");
assertEquals("bar", result);

Timer clientTimer = registry.find("http.client.requests").timer();
assertNotNull(clientTimer);
assertEquals("/example/{id}", clientTimer.getId().getTag("uri"));
}

private Search getMeter(String name) {
return registry.find(name);
}

@Path("/example")
@RegisterRestClient(baseUri = "http://dummy")
public interface Client {

@GET
@Path("/{id}")
String getById(@Url String baseUri, @PathParam("id") String id);
}

@Path("/example")
public static class Resource {

@RestClient
Client client;

@GET
@Path("/{id}")
@Produces(MediaType.TEXT_PLAIN)
public String example() {
return "bar";
}

@GET
@Path("/call")
@Produces(MediaType.TEXT_PLAIN)
public String call() {
return client.getById("http://localhost:8080", "1");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ public class UriTagWithHttpRootTest {
@Inject
MeterRegistry registry;

@Test
public void testClient() throws InterruptedException {
when().get("/ping/one").then().statusCode(200);
Util.waitForMeters(registry.find("http.server.requests").timers(), 1);
Util.waitForMeters(registry.find("http.client.requests").timers(), 1);
Assertions.assertEquals(1, registry.find("http.client.requests").tag("uri", "/pong/{message}").timers().size());
}

@Test
public void testRequestUris() throws Exception {
RestAssured.basePath = "/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -955,8 +955,7 @@ A more full example of generated client (with sub-resource) can is at the bottom
classContext.constructor.getThis(),
baseTarget));
if (observabilityIntegrationNeeded) {
String templatePath = MULTIPLE_SLASH_PATTERN.matcher(restClientInterface.getPath() + method.getPath())
.replaceAll("/");
String templatePath = templatePath(restClientInterface, method);
classContext.constructor.invokeVirtualMethod(
MethodDescriptor.ofMethod(WebTargetImpl.class, "setPreClientSendHandler", void.class,
ClientRestHandler.class),
Expand Down Expand Up @@ -1012,11 +1011,25 @@ A more full example of generated client (with sub-resource) can is at the bottom
+ jandexMethod.name());
}

ResultHandle newInputTarget = methodParamNotNull.invokeVirtualMethod(
MethodDescriptor.ofMethod(WebTargetImpl.class, "withNewUri", WebTargetImpl.class,
java.net.URI.class),
methodParamNotNull.readInstanceField(inputTargetField, methodParamNotNull.getThis()),
newUri);
ResultHandle newInputTarget;
if (observabilityIntegrationNeeded) {
// we need to apply the ClientObservabilityHandler to the inputTarget field without altering it
newInputTarget = methodParamNotNull.invokeVirtualMethod(
MethodDescriptor.ofMethod(WebTargetImpl.class, "withNewUri", WebTargetImpl.class,
java.net.URI.class, ClientRestHandler.class),
methodParamNotNull.readInstanceField(inputTargetField, methodParamNotNull.getThis()),
newUri,
methodParamNotNull.newInstance(
MethodDescriptor.ofConstructor(ClientObservabilityHandler.class, String.class),
methodParamNotNull.load(templatePath(restClientInterface, method))));
} else {
// just read the inputTarget field and call withNewUri on it
newInputTarget = methodParamNotNull.invokeVirtualMethod(
MethodDescriptor.ofMethod(WebTargetImpl.class, "withNewUri", WebTargetImpl.class,
java.net.URI.class),
methodParamNotNull.readInstanceField(inputTargetField, methodParamNotNull.getThis()),
newUri);
}
ResultHandle newBaseTarget = methodParamNotNull.invokeVirtualMethod(
baseTargetProducer.getMethodDescriptor(),
methodParamNotNull.getThis(), newInputTarget);
Expand Down Expand Up @@ -1247,6 +1260,11 @@ A more full example of generated client (with sub-resource) can is at the bottom

}

private String templatePath(RestClientInterface restClientInterface, ResourceMethod method) {
return MULTIPLE_SLASH_PATTERN.matcher(restClientInterface.getPath() + method.getPath())
.replaceAll("/");
}

/**
* The @Encoded annotation is only supported in path/query/matrix/form params.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ public WebTargetImpl withNewUri(URI uri) {
return newInstance(client, UriBuilder.fromUri(uri), configuration);
}

@SuppressWarnings("unused") // this is used in the REST Client to support @BaseUrl and observability is enabled
public WebTargetImpl withNewUri(URI uri, ClientRestHandler preClientSendHandler) {
return newInstance(client, UriBuilder.fromUri(uri), configuration, preClientSendHandler);
}

@SuppressWarnings("unused")
public WebTargetImpl queryParams(MultivaluedMap<String, Object> parameters)
throws IllegalArgumentException, NullPointerException {
Expand Down Expand Up @@ -297,6 +302,12 @@ public WebTargetImpl queryParamNoTemplate(String name, Object... values) throws

protected WebTargetImpl newInstance(HttpClient client, UriBuilder uriBuilder,
ConfigurationImpl configuration) {
return newInstance(client, uriBuilder, configuration, preClientSendHandler);
}

protected WebTargetImpl newInstance(HttpClient client, UriBuilder uriBuilder,
ConfigurationImpl configuration,
ClientRestHandler preClientSendHandler) {
WebTargetImpl result = new WebTargetImpl(restClient, client, uriBuilder, configuration,
handlerChain.setPreClientSendHandler(preClientSendHandler),
requestContext);
Expand Down
Loading