Skip to content

Commit

Permalink
use ClientLogger bean, if present
Browse files Browse the repository at this point in the history
adds integration tests for ClientLogger
  • Loading branch information
mickroll committed Jun 9, 2023
1 parent 5ed7597 commit 09af39f
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 10 deletions.
2 changes: 2 additions & 0 deletions docs/src/main/asciidoc/rest-client-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,8 @@ quarkus.rest-client.logging.body-limit=50
quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG
----

TIP: REST Client Reactive uses a default `ClientLogger` implementation. You can change it by providing a custom `ClientLogger` instance through CDI or when programmatically creating your client.

== Mocking the client for tests
If you use a client injected with the `@RestClient` annotation, you can easily mock it for tests.
You can do it with Mockito's `@InjectMock` or with `QuarkusMock`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jakarta.ws.rs.core.MediaType;

import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.resteasy.reactive.client.api.ClientLogger;
import org.jboss.resteasy.reactive.client.impl.ClientBuilderImpl;
import org.jboss.resteasy.reactive.client.impl.WebTargetImpl;
import org.jboss.resteasy.reactive.server.jackson.JacksonBasicMessageBodyReader;
Expand Down Expand Up @@ -74,7 +75,10 @@ private ClientBuilderImpl registerJacksonProviders(ClientBuilderImpl clientBuild
.registerMessageBodyWriter(new ClientJacksonMessageBodyWriter(newObjectMapper), Object.class,
HANDLED_MEDIA_TYPES, true, PROVIDER_PRIORITY);
}

InstanceHandle<ClientLogger> clientLogger = arcContainer.instance(ClientLogger.class);
if (clientLogger.isAvailable()) {
clientBuilder.clientLogger(clientLogger.get());
}
}
return clientBuilder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
import org.eclipse.microprofile.rest.client.spi.RestClientBuilderListener;
import org.jboss.resteasy.reactive.client.api.ClientLogger;

import io.quarkus.rest.client.reactive.runtime.QuarkusRestClientBuilderImpl;
import io.quarkus.rest.client.reactive.runtime.RestClientBuilderImpl;
Expand Down Expand Up @@ -250,6 +251,14 @@ static QuarkusRestClientBuilder newBuilder() {
*/
QuarkusRestClientBuilder httpClientOptions(HttpClientOptions httpClientOptions);

/**
* Specifies the client logger to use.
*
* @param clientLogger the client logger to use.
* @return the current builder
*/
QuarkusRestClientBuilder clientLogger(ClientLogger clientLogger);

/**
* Based on the configured QuarkusRestClientBuilder, creates a new instance of the given REST interface to invoke API calls
* against.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
import org.jboss.resteasy.reactive.client.api.ClientLogger;

import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
import io.quarkus.rest.client.reactive.runtime.context.ClientHeadersFactoryContextResolver;
Expand Down Expand Up @@ -217,6 +218,12 @@ public QuarkusRestClientBuilder httpClientOptions(HttpClientOptions httpClientOp
return this;
}

@Override
public QuarkusRestClientBuilder clientLogger(ClientLogger clientLogger) {
proxy.clientLogger(clientLogger);
return this;
}

@Override
public <T> T build(Class<T> clazz) throws IllegalStateException, RestClientDefinitionException {
return proxy.build(clazz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;
import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper;
import org.jboss.resteasy.reactive.client.api.ClientLogger;
import org.jboss.resteasy.reactive.client.api.InvalidRestClientDefinitionException;
import org.jboss.resteasy.reactive.client.api.LoggingScope;
import org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties;
Expand Down Expand Up @@ -65,6 +66,8 @@ public class RestClientBuilderImpl implements RestClientBuilder {
private String proxyPassword;
private String nonProxyHosts;

private ClientLogger clientLogger;

@Override
public RestClientBuilderImpl baseUrl(URL url) {
try {
Expand Down Expand Up @@ -156,6 +159,11 @@ public RestClientBuilderImpl nonProxyHosts(String nonProxyHosts) {
return this;
}

public RestClientBuilderImpl clientLogger(ClientLogger clientLogger) {
this.clientLogger = clientLogger;
return this;
}

@Override
public RestClientBuilderImpl executorService(ExecutorService executor) {
throw new IllegalArgumentException("Specifying executor service is not supported. " +
Expand Down Expand Up @@ -322,6 +330,14 @@ public <T> T build(Class<T> aClass) throws IllegalStateException, RestClientDefi
Integer loggingBodySize = logging != null ? logging.bodyLimit : 100;
clientBuilder.loggingScope(loggingScope);
clientBuilder.loggingBodySize(loggingBodySize);
if (clientLogger != null) {
clientBuilder.clientLogger(clientLogger);
} else {
InstanceHandle<ClientLogger> clientLoggerInstance = Arc.container().instance(ClientLogger.class);
if (clientLoggerInstance.isAvailable()) {
clientBuilder.clientLogger(clientLoggerInstance.get());
}
}

clientBuilder.multiQueryParamMode(toMultiQueryParamMode(queryParamStyle));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public class ClientCallingResource {
private static final String[] RESPONSES = { "cortland", "lobo", "golden delicious" };
private final AtomicInteger count = new AtomicInteger(0);

@RestClient
ClientWithClientLogger clientWithClientLogger;

@RestClient
ClientWithExceptionMapper clientWithExceptionMapper;

Expand All @@ -59,13 +62,57 @@ public class ClientCallingResource {
@Inject
InMemorySpanExporter inMemorySpanExporter;

@Inject
MyClientLogger globalClientLogger;

void init(@Observes Router router) {
router.post().handler(BodyHandler.create());

router.get("/unprocessable").handler(rc -> rc.response().setStatusCode(422).end("the entity was unprocessable"));

router.get("/client-logger").handler(rc -> {
rc.response().end("Hello World!");
});

router.post("/call-client-with-global-client-logger").blockingHandler(rc -> {
String url = rc.body().asString();
ClientWithClientLogger client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.build(ClientWithClientLogger.class);
globalClientLogger.reset();
client.call();
if (globalClientLogger.wasUsed()) {
success(rc, "global client logger was used");
} else {
fail(rc, "global client logger was not used");
}
});

router.post("/call-client-with-explicit-client-logger").blockingHandler(rc -> {
String url = rc.body().asString();
MyClientLogger explicitClientLogger = new MyClientLogger();
ClientWithClientLogger client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.clientLogger(explicitClientLogger)
.build(ClientWithClientLogger.class);
client.call();
if (explicitClientLogger.wasUsed()) {
success(rc, "explicit client logger was used");
} else {
fail(rc, "explicit client logger was not used");
}
});

router.post("/call-cdi-client-with-global-client-logger").blockingHandler(rc -> {
globalClientLogger.reset();
clientWithClientLogger.call();
if (globalClientLogger.wasUsed()) {
success(rc, "global client logger was used");
} else {
fail(rc, "global client logger was not used");
}
});

router.post("/call-client-with-exception-mapper").blockingHandler(rc -> {
String url = rc.getBody().toString();
String url = rc.body().asString();
ClientWithExceptionMapper client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.register(MyResponseExceptionMapper.class)
.build(ClientWithExceptionMapper.class);
Expand All @@ -81,7 +128,7 @@ void init(@Observes Router router) {
});

router.route("/call-client").blockingHandler(rc -> {
String url = rc.getBody().toString();
String url = rc.body().asString();
AppleClient client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.build(AppleClient.class);
Uni<Apple> apple1 = Uni.createFrom().item(client.swapApple(new Apple("lobo")));
Expand Down Expand Up @@ -110,7 +157,7 @@ void init(@Observes Router router) {
});

router.route("/call-client-retry").blockingHandler(rc -> {
String url = rc.getBody().toString();
String url = rc.body().asString();
AppleClient client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url + "/does-not-exist"))
.build(AppleClient.class);
AtomicInteger count = new AtomicInteger(0);
Expand All @@ -120,29 +167,29 @@ void init(@Observes Router router) {
});

router.post("/hello").handler(rc -> rc.response().putHeader("content-type", MediaType.TEXT_PLAIN)
.end("Hello, " + (rc.getBodyAsString()).repeat(getCount(rc))));
.end("Hello, " + (rc.body().asString()).repeat(getCount(rc))));

router.post("/hello/fromMessage").handler(rc -> rc.response().putHeader("content-type", MediaType.TEXT_PLAIN)
.end(rc.body().asJsonObject().getString("message")));

router.route("/call-hello-client").blockingHandler(rc -> {
String url = rc.getBody().toString();
String url = rc.body().asString();
HelloClient client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.build(HelloClient.class);
String greeting = client.greeting("John", 2);
rc.response().end(greeting);
});

router.route("/call-hello-client-trace").blockingHandler(rc -> {
String url = rc.getBody().toString();
String url = rc.body().asString();
HelloClient client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.build(HelloClient.class);
String greeting = client.greeting("Mary", 3);
rc.response().end(greeting);
});

router.route("/call-helloFromMessage-client").blockingHandler(rc -> {
String url = rc.getBody().toString();
String url = rc.body().asString();
HelloClient client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.build(HelloClient.class);
String greeting = client.fromMessage(new HelloClient.Message("Hello world"));
Expand All @@ -153,15 +200,15 @@ void init(@Observes Router router) {
.end(getParam(rc)));

router.route("/call-params-client-with-param-first").blockingHandler(rc -> {
String url = rc.getBody().toString();
String url = rc.body().asString();
ParamClient client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.build(ParamClient.class);
String result = client.getParam(Param.FIRST);
rc.response().end(result);
});

router.route("/rest-response").blockingHandler(rc -> {
String url = rc.getBody().toString();
String url = rc.body().asString();
RestResponseClient client = QuarkusRestClientBuilder.newBuilder().baseUri(URI.create(url))
.property("microprofile.rest.client.disable.default.mapper", true)
.build(RestResponseClient.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.quarkus.it.rest.client.main;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@Path("/client-logger")
@RegisterRestClient(configKey = "w-client-logger")
public interface ClientWithClientLogger {
@GET
String call();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.quarkus.it.rest.client.main;

import java.util.concurrent.atomic.AtomicBoolean;

import jakarta.enterprise.context.ApplicationScoped;

import org.jboss.resteasy.reactive.client.api.ClientLogger;

import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpClientResponse;

@ApplicationScoped
public class MyClientLogger implements ClientLogger {
public final AtomicBoolean used = new AtomicBoolean(false);

@Override
public void setBodySize(int bodySize) {
}

@Override
public void logResponse(HttpClientResponse response, boolean redirect) {
used.set(true);
}

@Override
public void logRequest(HttpClientRequest request, Buffer body, boolean omitBody) {
used.set(true);
}

public void reset() {
used.set(false);
}

public boolean wasUsed() {
return used.get();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
w-client-logger/mp-rest/url=${test.url}
w-exception-mapper/mp-rest/url=${test.url}
w-fault-tolerance/mp-rest/url=${test.url}
io.quarkus.it.rest.client.main.ParamClient/mp-rest/url=${test.url}
io.quarkus.it.rest.client.multipart.MultipartClient/mp-rest/url=${test.url}
# global client logging scope
quarkus.rest-client.logging.scope=request-response
# Self-Signed client
quarkus.rest-client.self-signed.trust-store=${self-signed.trust-store}
quarkus.rest-client.self-signed.trust-store-password=${self-signed.trust-store-password}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,27 @@ void shouldRetryOnFailure() {
.body(equalTo("4"));
}

@Test
void shouldLogWithExplicitLogger() {
RestAssured.with().body(baseUrl).post("/call-client-with-explicit-client-logger")
.then()
.statusCode(200);
}

@Test
void shouldLogWithGlobalLogger() {
RestAssured.with().body(baseUrl).post("/call-client-with-global-client-logger")
.then()
.statusCode(200);
}

@Test
void shouldLogCdiWithGlobalLogger() {
RestAssured.with().body(baseUrl).post("/call-cdi-client-with-global-client-logger")
.then()
.statusCode(200);
}

@Test
void shouldMapException() {
RestAssured.with().body(baseUrl).post("/call-client-with-exception-mapper")
Expand Down

0 comments on commit 09af39f

Please sign in to comment.