From bba9219429d11c91f9fa876683bfd5a622a18071 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 7 Sep 2021 22:31:26 +0300 Subject: [PATCH] Support RestResponse as a return type for reactive rest client methods Closes: #19966 --- .../ClientResponseCompleteRestHandler.java | 6 ++++- .../client/impl/AsyncInvokerImpl.java | 23 ++++++++++++++----- .../client/impl/RestClientRequestContext.java | 22 +++++++++++++++--- .../it/rest/client/main/AppleClient.java | 9 ++++++++ .../client/main/ClientCallingResource.java | 11 ++++++--- .../io/quarkus/it/rest/client/BasicTest.java | 4 ++-- 6 files changed, 60 insertions(+), 15 deletions(-) diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java index e10c4dcf01e806..7879d8e2e088ee 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java @@ -2,6 +2,7 @@ import java.io.IOException; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; import org.jboss.resteasy.reactive.client.impl.ClientResponseBuilderImpl; import org.jboss.resteasy.reactive.client.impl.ClientResponseContextImpl; import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext; @@ -22,7 +23,10 @@ public static ResponseImpl mapToResponse(RestClientRequestContext context, boole builder.status(responseContext.getStatus(), responseContext.getReasonPhrase()); builder.setAllHeaders(responseContext.getHeaders()); builder.invocationState(context); - if (context.isResponseTypeSpecified() && parseContent) { // this case means that a specific response type was requested + if (context.isResponseTypeSpecified() + // when we are returning a RestResponse, we don't want to do any parsing + && (Response.Status.Family.familyOf(context.getResponseStatus()) == Response.Status.Family.SUCCESSFUL) + && parseContent) { // this case means that a specific response type was requested Object entity = context.readEntity(responseContext.getEntityStream(), context.getResponseType(), responseContext.getMediaType(), diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/AsyncInvokerImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/AsyncInvokerImpl.java index 2b841872509957..b5ef7e9d2b3ec9 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/AsyncInvokerImpl.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/AsyncInvokerImpl.java @@ -16,6 +16,7 @@ import javax.ws.rs.client.InvocationCallback; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.Response; +import org.jboss.resteasy.reactive.RestResponse; import org.jboss.resteasy.reactive.common.jaxrs.ConfigurationImpl; import org.jboss.resteasy.reactive.common.util.types.Types; import org.jboss.resteasy.reactive.spi.ThreadSetupAction; @@ -280,12 +281,22 @@ private Type getInvocationCallbackType(InvocationCallback callback) { public CompletableFuture mapResponse(CompletableFuture res, Class responseType) { if (responseType.equals(Response.class)) { return (CompletableFuture) res; + } else if (responseType.equals(RestResponse.class)) { + return res.thenApply(new Function<>() { + @Override + public T apply(Response response) { + return (T) RestResponse.ResponseBuilder.create(response.getStatusInfo(), response.getEntity()) + .replaceAll(response.getHeaders()).build(); + } + }); + } else { + return res.thenApply(new Function<>() { + @Override + public T apply(Response response) { + return (T) response.getEntity(); + } + }); } - return res.thenApply(new Function() { - @Override - public T apply(Response response) { - return (T) response.getEntity(); - } - }); + } } diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java index 467166cdf2dc90..6ceb958ca79e48 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.URI; import java.util.Arrays; @@ -29,6 +30,7 @@ import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.WriterInterceptor; import org.jboss.resteasy.reactive.ClientWebApplicationException; +import org.jboss.resteasy.reactive.RestResponse; import org.jboss.resteasy.reactive.client.spi.ClientRestHandler; import org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext; import org.jboss.resteasy.reactive.common.core.Serialisers; @@ -102,9 +104,23 @@ public RestClientRequestContext(ClientImpl restClient, this.responseTypeSpecified = false; } else { this.responseType = responseType; - boolean isJaxResponse = responseType.getRawType().equals(Response.class); - this.checkSuccessfulFamily = !isJaxResponse; - this.responseTypeSpecified = !isJaxResponse; + if (responseType.getRawType().equals(Response.class)) { + this.checkSuccessfulFamily = false; + this.responseTypeSpecified = false; + } else if (responseType.getRawType().equals(RestResponse.class)) { + if (responseType.getType() instanceof ParameterizedType) { + ParameterizedType type = (ParameterizedType) responseType.getType(); + if (type.getActualTypeArguments().length == 1) { + Type restResponseType = type.getActualTypeArguments()[0]; + this.responseType = new GenericType<>(restResponseType); + } + } + this.checkSuccessfulFamily = false; + this.responseTypeSpecified = true; + } else { + this.checkSuccessfulFamily = true; + this.responseTypeSpecified = true; + } } this.registerBodyHandler = registerBodyHandler; this.result = new CompletableFuture<>(); diff --git a/integration-tests/resteasy-reactive-rest-client/src/main/java/io/quarkus/it/rest/client/main/AppleClient.java b/integration-tests/resteasy-reactive-rest-client/src/main/java/io/quarkus/it/rest/client/main/AppleClient.java index d03ae8cdc61f2e..608df268226a2d 100644 --- a/integration-tests/resteasy-reactive-rest-client/src/main/java/io/quarkus/it/rest/client/main/AppleClient.java +++ b/integration-tests/resteasy-reactive-rest-client/src/main/java/io/quarkus/it/rest/client/main/AppleClient.java @@ -9,6 +9,7 @@ import javax.ws.rs.core.MediaType; import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; +import org.jboss.resteasy.reactive.RestResponse; import io.smallrye.mutiny.Uni; @@ -54,4 +55,12 @@ public interface AppleClient { @POST @Produces(MediaType.APPLICATION_JSON) Uni uniStringApple(); + + @POST + @Produces(MediaType.APPLICATION_JSON) + RestResponse restResponseApple(); + + @POST + @Produces(MediaType.APPLICATION_JSON) + Uni> uniRestResponseApple(); } diff --git a/integration-tests/resteasy-reactive-rest-client/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java b/integration-tests/resteasy-reactive-rest-client/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java index 7dee0e453e1eea..49304c5c022d8f 100644 --- a/integration-tests/resteasy-reactive-rest-client/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java +++ b/integration-tests/resteasy-reactive-rest-client/src/main/java/io/quarkus/it/rest/client/main/ClientCallingResource.java @@ -3,6 +3,7 @@ import java.net.URI; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import java.util.stream.Collectors; import javax.enterprise.context.ApplicationScoped; @@ -12,6 +13,7 @@ import org.eclipse.microprofile.rest.client.RestClientBuilder; import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.resteasy.reactive.RestResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -74,12 +76,15 @@ void init(@Observes Router router) { Uni apple8 = Uni.createFrom().completionStage(client.completionStringApple()).onItem() .transform(this::toApple); Uni apple9 = client.uniStringApple().onItem().transform(this::toApple); - Uni.combine().all().unis(apple1, apple2, apple3, apple4, apple5, apple6, apple7, apple8, apple9).asTuple() + Uni apple10 = Uni.createFrom().item(client.restResponseApple().getEntity()); + Uni apple11 = client.uniRestResponseApple().onItem().transform(RestResponse::getEntity); + Uni.combine().all().unis(apple1, apple2, apple3, apple4, apple5, apple6, apple7, apple8, apple9, apple10, apple11) + .combinedWith(Function.identity()) .subscribe() - .with(tuple -> { + .with(list -> { try { rc.response().putHeader("content-type", "application/json") - .end(mapper.writeValueAsString(tuple.asList())); + .end(mapper.writeValueAsString(list)); } catch (JsonProcessingException e) { fail(rc, e.getMessage()); } diff --git a/integration-tests/resteasy-reactive-rest-client/src/test/java/io/quarkus/it/rest/client/BasicTest.java b/integration-tests/resteasy-reactive-rest-client/src/test/java/io/quarkus/it/rest/client/BasicTest.java index 78724c6e10cc52..f56ed8a8f4989d 100644 --- a/integration-tests/resteasy-reactive-rest-client/src/test/java/io/quarkus/it/rest/client/BasicTest.java +++ b/integration-tests/resteasy-reactive-rest-client/src/test/java/io/quarkus/it/rest/client/BasicTest.java @@ -49,11 +49,11 @@ void shouldMakeJsonRequest() { .statusCode(200) .contentType("application/json") .extract().body().jsonPath().getList(".", Map.class); - assertThat(results).hasSize(9).allSatisfy(m -> { + assertThat(results).hasSize(11).allSatisfy(m -> { assertThat(m).containsOnlyKeys("cultivar"); }); Map valueByCount = results.stream().collect(Collectors.groupingBy(m -> m.get("cultivar"), counting())); - assertThat(valueByCount).containsOnly(entry("cortland", 3L), entry("lobo", 3L), entry("golden delicious", 3L)); + assertThat(valueByCount).containsOnly(entry("cortland", 4L), entry("lobo", 4L), entry("golden delicious", 3L)); } @Test