From 1aff10f92ca2bb57980983b5da3e7e9f3583868a Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 6 Nov 2023 13:24:10 +0200 Subject: [PATCH] Handle generic types for ParamConverter in REST Client Fixes: #36639 --- .../JaxrsClientReactiveProcessor.java | 4 +- .../converter/GenericsConverterTest.java | 127 ++++++++++++++++++ 2 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/converter/GenericsConverterTest.java diff --git a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index bd86f0b3c6a22..b96c574255bfa 100644 --- a/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/jaxrs-client-reactive/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -2652,7 +2652,7 @@ private ResultHandle addQueryParam(MethodInfo jandexMethod, BytecodeCreator meth if (isCollection(valueType, index)) { if (valueType.kind() == PARAMETERIZED_TYPE) { Type paramType = valueType.asParameterizedType().arguments().get(0); - if (paramType.kind() == CLASS) { + if ((paramType.kind() == CLASS) || (paramType.kind() == PARAMETERIZED_TYPE)) { componentType = paramType.name().toString(); } } @@ -2679,7 +2679,7 @@ private ResultHandle addQueryParam(MethodInfo jandexMethod, BytecodeCreator meth } else if (isCollection(type, index)) { if (type.kind() == PARAMETERIZED_TYPE) { Type paramType = type.asParameterizedType().arguments().get(0); - if (paramType.kind() == CLASS) { + if ((paramType.kind() == CLASS) || (paramType.kind() == PARAMETERIZED_TYPE)) { componentType = paramType.name().toString(); } } diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/converter/GenericsConverterTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/converter/GenericsConverterTest.java new file mode 100644 index 0000000000000..61a8ad069bae6 --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/converter/GenericsConverterTest.java @@ -0,0 +1,127 @@ +package io.quarkus.rest.client.reactive.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.net.URI; +import java.util.List; +import java.util.stream.Collectors; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.ext.ParamConverter; +import jakarta.ws.rs.ext.ParamConverterProvider; +import jakarta.ws.rs.ext.Provider; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; + +public class GenericsConverterTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest(); + + @TestHTTPResource + URI baseUri; + + @Test + void testSingle() { + TestClient client = RestClientBuilder.newBuilder().baseUri(baseUri) + .build(TestClient.class); + var result = client.wrapper(new WrapperClass<>(StatusEnum.ACTIVE)); + assertEquals("ACTIVE", result); + } + + @Test + void testList() { + TestClient client = RestClientBuilder.newBuilder().baseUri(baseUri) + .build(TestClient.class); + var result = client + .wrapperList(List.of(new WrapperClass<>(StatusEnum.ACTIVE), new WrapperClass<>(StatusEnum.INACTIVE))); + assertEquals("ACTIVE,INACTIVE", result); + } + + public enum StatusEnum { + + ACTIVE, + INACTIVE + } + + public static class WrapperClass> { + + private final E value; + + public WrapperClass(final E value) { + this.value = value; + } + + public E getValue() { + return value; + } + } + + public static class WrapperClassParamConverter implements ParamConverter> { + + @Override + public WrapperClass fromString(String value) { + return new WrapperClass<>(Enum.valueOf(StatusEnum.class, value)); + } + + @Override + public String toString(WrapperClass wrapperClass) { + return wrapperClass != null ? wrapperClass.getValue().toString() : null; + } + + } + + @Provider + public static class WrapperClassParamConverterProvider implements ParamConverterProvider { + + @Override + @SuppressWarnings("unchecked") + public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { + if (WrapperClass.class.isAssignableFrom(rawType)) { + return (ParamConverter) new WrapperClassParamConverter(); + } + + return null; + } + } + + @Path("/test") + public static class TestResource { + + @GET + @Path("/single") + public String wrapper(@QueryParam("wrapper") final WrapperClass wrapper) { + return wrapper.getValue().toString(); + } + + @GET + @Path("/list") + public String wrapperList(@QueryParam("wrapperList") final List> wrapperList) { + return wrapperList.stream().map(WrapperClass::getValue).map(Enum::name).collect(Collectors.joining(",")); + } + + } + + @Path("/test") + public interface TestClient { + + @GET + @Path("/single") + String wrapper(@QueryParam("wrapper") final WrapperClass wrapper); + + @GET + @Path("/list") + String wrapperList(@QueryParam("wrapperList") final List> wrapperList); + + } + +}