diff --git a/docs/src/main/asciidoc/rest-client.adoc b/docs/src/main/asciidoc/rest-client.adoc index cb6183b5e314b..1b717a9ab9478 100644 --- a/docs/src/main/asciidoc/rest-client.adoc +++ b/docs/src/main/asciidoc/rest-client.adoc @@ -157,6 +157,7 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MultivaluedMap; import java.util.Map; import java.util.Set; +import java util.Optional; @Path("/extensions") @RegisterRestClient(configKey = "extensions-api") @@ -168,6 +169,9 @@ public interface ExtensionsService { @GET Set getByName(@RestQuery String name); <1> + @GET + Set getByOptionalName(@RestQuery Optional name); + @GET Set getByFilter(@RestQuery Map filter); <2> diff --git a/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java b/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java index ba00737128b1c..41528da8079c1 100644 --- a/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java +++ b/extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java @@ -17,6 +17,7 @@ import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.MAP; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.MULTI; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.OBJECT; +import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.OPTIONAL; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.PART_TYPE_NAME; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.REST_FORM_PARAM; import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.REST_MULTI; @@ -2861,6 +2862,19 @@ private ResultHandle addQueryParam(MethodInfo jandexMethod, BytecodeCreator meth paramArray = notNullParam.invokeStaticMethod( MethodDescriptor.ofMethod(ToObjectArray.class, "collection", Object[].class, Collection.class), queryParamHandle); + } else if (isOptional(type, index)) { + if (type.kind() == PARAMETERIZED_TYPE) { + Type paramType = type.asParameterizedType().arguments().get(0); + if ((paramType.kind() == CLASS) || (paramType.kind() == PARAMETERIZED_TYPE)) { + componentType = paramType.name().toString(); + } + } + if (componentType == null) { + componentType = DotNames.OBJECT.toString(); + } + paramArray = notNullParam.invokeStaticMethod( + MethodDescriptor.ofMethod(ToObjectArray.class, "optional", Object[].class, Optional.class), + queryParamHandle); } else { componentType = type.name().toString(); paramArray = notNullParam.invokeStaticMethod( @@ -2930,6 +2944,10 @@ private boolean isMap(Type type, IndexView index) { return isAssignableFrom(MAP, type.name(), index); } + private boolean isOptional(Type type, IndexView index) { + return isAssignableFrom(OPTIONAL, type.name(), index); + } + private void addHeaderParam(BytecodeCreator invoBuilderEnricher, AssignableResultHandle invocationBuilder, String paramName, ResultHandle headerParamHandle, String paramType, ResultHandle client, ResultHandle genericType, ResultHandle annotations) { diff --git a/extensions/resteasy-reactive/rest-client-jaxrs/runtime/src/main/java/io/quarkus/jaxrs/client/reactive/runtime/ToObjectArray.java b/extensions/resteasy-reactive/rest-client-jaxrs/runtime/src/main/java/io/quarkus/jaxrs/client/reactive/runtime/ToObjectArray.java index 6dc6c53e074bf..4f4d2f614fbc7 100644 --- a/extensions/resteasy-reactive/rest-client-jaxrs/runtime/src/main/java/io/quarkus/jaxrs/client/reactive/runtime/ToObjectArray.java +++ b/extensions/resteasy-reactive/rest-client-jaxrs/runtime/src/main/java/io/quarkus/jaxrs/client/reactive/runtime/ToObjectArray.java @@ -1,6 +1,7 @@ package io.quarkus.jaxrs.client.reactive.runtime; import java.util.Collection; +import java.util.Optional; /** * used by query param handling mechanism, in generated code @@ -16,6 +17,10 @@ public static Object[] value(Object value) { return new Object[] { value }; } + public static Object[] optional(Optional optional) { + return optional.isPresent() ? new Object[] { optional.get() } : new Object[] {}; + } + private ToObjectArray() { } } diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/OptionalQueryParamsTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/OptionalQueryParamsTest.java new file mode 100644 index 0000000000000..19407ccc476fa --- /dev/null +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/OptionalQueryParamsTest.java @@ -0,0 +1,63 @@ +package io.quarkus.rest.client.reactive; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URI; +import java.util.*; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.UriInfo; + +import org.jboss.resteasy.reactive.RestQuery; +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 OptionalQueryParamsTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar.addClasses(Resource.class, Client.class)); + + @TestHTTPResource + URI baseUri; + + @Test + void testQueryParamsWithOptionals() { + Client client = QuarkusRestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class); + String responseForEmptyOptional = client.optional(Optional.empty()); + assertThat(responseForEmptyOptional).isNullOrEmpty(); + + String responseForValueOptional = client.optional(Optional.of("value")); + assertThat(responseForValueOptional).isEqualTo("parameter=value"); + } + + @Path("optional") + public static class Resource { + + @Path("query") + @GET + public String queryParams(@Context UriInfo uriInfo) { + + var entries = new ArrayList(uriInfo.getQueryParameters().size()); + for (var entry : uriInfo.getQueryParameters().entrySet()) { + entries.add(entry.getKey() + "=" + String.join(",", entry.getValue())); + } + return String.join(";", entries); + + } + } + + @Path("optional") + public interface Client { + + @Path("query") + @GET + String optional(@RestQuery Optional parameter); + + } +}