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

Fix null query parameter handling in generated RestEasy Reactive clients #25870

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
Expand Up @@ -2325,20 +2325,22 @@ private ResultHandle addQueryParam(MethodInfo jandexMethod, BytecodeCreator meth
AssignableResultHandle result = methodCreator.createVariable(WebTarget.class);
BranchResult isValueNull = methodCreator.ifNull(webTarget);
BytecodeCreator notNullValue = isValueNull.falseBranch();
BranchResult isParamNull = notNullValue.ifNull(queryParamHandle);
BytecodeCreator notNullParam = isParamNull.falseBranch();
if (isMap(type, index)) {
var resolvesTypes = resolveMapTypes(type, index, jandexMethod);
var keyType = resolvesTypes.getKey();
if (!ResteasyReactiveDotNames.STRING.equals(keyType.name())) {
throw new IllegalArgumentException(
"Map parameter types must have String keys. Offending method is: " + jandexMethod);
}
notNullValue.assign(result, webTarget);
notNullParam.assign(result, webTarget);
// Loop through the keys
ResultHandle keySet = notNullValue.invokeInterfaceMethod(ofMethod(Map.class, "keySet", Set.class),
ResultHandle keySet = notNullParam.invokeInterfaceMethod(ofMethod(Map.class, "keySet", Set.class),
queryParamHandle);
ResultHandle keysIterator = notNullValue.invokeInterfaceMethod(
ResultHandle keysIterator = notNullParam.invokeInterfaceMethod(
ofMethod(Set.class, "iterator", Iterator.class), keySet);
BytecodeCreator loopCreator = notNullValue.whileLoop(c -> iteratorHasNext(c, keysIterator)).block();
BytecodeCreator loopCreator = notNullParam.whileLoop(c -> iteratorHasNext(c, keysIterator)).block();
ResultHandle key = loopCreator.invokeInterfaceMethod(
ofMethod(Iterator.class, "next", Object.class), keysIterator);
// get the value and convert
Expand Down Expand Up @@ -2373,7 +2375,7 @@ private ResultHandle addQueryParam(MethodInfo jandexMethod, BytecodeCreator meth
String componentType = null;
if (type.kind() == Type.Kind.ARRAY) {
componentType = type.asArrayType().component().name().toString();
paramArray = notNullValue.checkCast(queryParamHandle, Object[].class);
paramArray = notNullParam.checkCast(queryParamHandle, Object[].class);
} else if (isCollection(type, index)) {
if (type.kind() == PARAMETERIZED_TYPE) {
Type paramType = type.asParameterizedType().arguments().get(0);
Expand All @@ -2384,21 +2386,24 @@ private ResultHandle addQueryParam(MethodInfo jandexMethod, BytecodeCreator meth
if (componentType == null) {
componentType = DotNames.OBJECT.toString();
}
paramArray = notNullValue.invokeStaticMethod(
paramArray = notNullParam.invokeStaticMethod(
MethodDescriptor.ofMethod(ToObjectArray.class, "collection", Object[].class, Collection.class),
queryParamHandle);
} else {
componentType = type.name().toString();
paramArray = notNullValue.invokeStaticMethod(
paramArray = notNullParam.invokeStaticMethod(
MethodDescriptor.ofMethod(ToObjectArray.class, "value", Object[].class, Object.class),
queryParamHandle);
}

addQueryParamToWebTarget(notNullValue, notNullValue.load(paramName), webTarget, client, genericType,
addQueryParamToWebTarget(notNullParam, notNullParam.load(paramName), webTarget, client, genericType,
paramAnnotations, paramIndex,
paramArray, componentType, result);
}

BytecodeCreator nullParam = isParamNull.trueBranch();
nullParam.assign(result, webTarget);

BytecodeCreator nullValue = isValueNull.trueBranch();
nullValue.assign(result, nullValue.loadNull());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package io.quarkus.rest.client.reactive.queries;

import static org.assertj.core.api.Assertions.assertThat;

import java.net.URI;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;

import org.eclipse.microprofile.rest.client.RestClientBuilder;
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 QueryTest {
@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar.addClasses(Resource.class));

@TestHTTPResource
URI baseUri;

@Test
void testQuery() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.sendQuery("bar")).isEqualTo("bar");
}

@Test
void testNullQuery() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.sendQuery(null)).isNull();
}

@Test
void testQueryMap() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.sendQueryMap(Map.of("a", "1", "b", "2"))).isEqualTo("a=1,b=2");
}

@Test
void testNullQueryMap() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.sendQueryMap(null)).isEqualTo("none");
}

@Test
void testQueryCollection() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.sendQueryCollection(List.of("a", "b"))).isEqualTo("a,b");
}

@Test
void testNullQueryCollection() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.sendQueryCollection(null)).isEqualTo("none");
}

@Test
void testQueryArray() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.sendQueryArray(new String[] { "a", "b" })).isEqualTo("a,b");
}

@Test
void testNullQueryArray() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.sendQueryArray(null)).isEqualTo("none");
}

@Test
void testQueriesWithSubresource() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.querySub("bar", "bar2").send("bar3", "bar4")).isEqualTo("bar:bar2:bar3:bar4");
}

@Test
void testNullQueriesWithSubresource() {
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.querySub("bar", null).send(null, "bar4")).isEqualTo("bar:null:null:bar4");
}

@Path("/")
@ApplicationScoped
public static class Resource {
@GET
public String returnQueryValue(@QueryParam("foo") String query) {
return query;
}

@Path("map")
@GET
public String returnQueryMap(@Context UriInfo uriInfo) {
if (uriInfo.getQueryParameters().isEmpty()) {
return "none";
}
return uriInfo.getQueryParameters().entrySet().stream()
.flatMap(entry -> entry.getValue().stream()
.map(value -> new AbstractMap.SimpleEntry(entry.getKey(), value)))
.map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
.sorted()
.collect(Collectors.joining(","));
}

@Path("collection")
@GET
public String returnQueryCollection(@QueryParam("foo") List<String> query) {
if (query.isEmpty()) {
return "none";
}
return String.join(",", query);
}

@Path("array")
@GET
public String returnQueryArray(@QueryParam("foo") String[] query) {
if (query.length == 0) {
return "none";
}
return String.join(",", query);
}

@Path("2")
@GET
public String returnQueryValue2(@QueryParam("foo") String query, @QueryParam("foo2") String query2,
@QueryParam("foo3") String query3, @QueryParam("foo4") String query4) {
return query + ":" + query2 + ":" + query3 + ":" + query4;
}
}

public interface Client {

@GET
String sendQuery(@QueryParam("foo") String query);

@Path("map")
@GET
String sendQueryMap(@RestQuery Map<String, String> query);

@Path("collection")
@GET
String sendQueryCollection(@QueryParam("foo") List<String> query);

@Path("array")
@GET
String sendQueryArray(@QueryParam("foo") String[] query);

@Path("2")
SubClient querySub(@QueryParam("foo") String query, @QueryParam("foo2") String query2);
}

public interface SubClient {

@GET
String send(@QueryParam("foo3") String query3, @QueryParam("foo4") String query4);
}

}