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

Support raw collections in RESTEasy Reactive server and client #31931

Merged
merged 1 commit into from
Mar 17, 2023
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 @@ -941,7 +941,7 @@ A more full example of generated client (with sub-resource) can is at the bottom
MethodDescriptor handleHeaderDescriptor = MethodDescriptor.ofMethod(name,
method.getName() + "$$" + methodIndex + "$$handleHeader$$" + paramIdx,
Invocation.Builder.class,
Invocation.Builder.class, param.type);
Invocation.Builder.class, param.declaredType);
MethodCreator handleHeaderMethod = classContext.classCreator.getMethodCreator(
handleHeaderDescriptor).setModifiers(Modifier.PRIVATE);

Expand Down Expand Up @@ -1358,7 +1358,7 @@ private void handleSubResourceMethod(List<JaxrsClientReactiveEnricherBuildItem>
subMethod.getName() + "$$" + subMethodIndex + "$$handleHeader$$param"
+ inheritedParamIndex + "$" + subParamField.paramIndex,
Invocation.Builder.class,
Invocation.Builder.class, param.type);
Invocation.Builder.class, param.declaredType);
MethodCreator handleHeaderMethod = subContext.classCreator.getMethodCreator(
handleHeaderDescriptor).setModifiers(Modifier.PRIVATE);

Expand Down Expand Up @@ -1464,7 +1464,7 @@ private void handleSubResourceMethod(List<JaxrsClientReactiveEnricherBuildItem>
MethodDescriptor handleHeaderDescriptor = MethodDescriptor.ofMethod(subName,
subMethod.getName() + "$$" + subMethodIndex + "$$handleHeader$$" + paramIdx,
Invocation.Builder.class,
Invocation.Builder.class, param.type);
Invocation.Builder.class, param.declaredType);
MethodCreator handleHeaderMethod = subContext.classCreator.getMethodCreator(
handleHeaderDescriptor).setModifiers(Modifier.PRIVATE);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;

import jakarta.inject.Inject;
import jakarta.json.JsonArray;
Expand Down Expand Up @@ -143,6 +147,42 @@ public Response filters(@Context HttpHeaders headers, @RestHeader("filter-reques
return Response.ok().header("filter-request", header).build();
}

@GET
@Path("header-param-list")
public Object headerParamWithList(@HeaderParam("header") List list) {
return collectionToString(list);
}

@GET
@Path("rest-header-list")
public Object restHeaderWithList(@RestHeader List header) {
return collectionToString(header);
}

@GET
@Path("header-param-set")
public Object headerParamWithSet(@HeaderParam("header") Set list) {
return collectionToString(list);
}

@GET
@Path("rest-header-set")
public Object restHeaderWithSet(@RestHeader Set header) {
return collectionToString(header);
}

@GET
@Path("header-param-sorted-set")
public Object headerParamWithSortedSet(@HeaderParam("header") SortedSet list) {
return collectionToString(list);
}

@GET
@Path("rest-header-sorted-set")
public String restHeaderWithSortedSet(@RestHeader SortedSet header) {
return collectionToString(header);
}

@GET
@Path("feature-filters")
public Response featureFilters(@Context HttpHeaders headers) {
Expand Down Expand Up @@ -380,4 +420,8 @@ public String simplifiedResourceInfo(@Context SimpleResourceInfo simplifiedResou
public String bigDecimalConverter(BigDecimal val) {
return val.toString();
}

private String collectionToString(Collection list) {
return (String) list.stream().map(Object::toString).collect(Collectors.joining(", "));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
Expand Down Expand Up @@ -313,6 +315,34 @@ public void testHeaderParamInCtor() {
.then().body(Matchers.is(emptyString()));
}

@ParameterizedTest
@ValueSource(strings = {
"rest-header-list",
"rest-header-set",
"rest-header-sorted-set"
})
public void testRestHeaderUsingCollection(String path) {
RestAssured.with().header("header", "a", "b")
.get("/simple/" + path)
.then()
.statusCode(HttpStatus.SC_OK)
.body(Matchers.equalTo("a, b"));
}

@ParameterizedTest
@ValueSource(strings = {
"header-param-list",
"header-param-set",
"header-param-sorted-set"
})
public void testHeaderParamUsingCollection(String path) {
RestAssured.with().header("header", "a", "b")
.get("/simple/" + path)
.then()
.statusCode(HttpStatus.SC_OK)
.body(Matchers.equalTo("a, b"));
}

@Test
public void testFormMap() {
RestAssured
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
import static org.assertj.core.api.Assertions.assertThat;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
Expand Down Expand Up @@ -40,6 +47,15 @@ void testNullHeaders() {
assertThat(client.cookieSub("bar", null).send(null, "bar4", "dummy")).isEqualTo("bar:null:null:bar4:X-My-Header/dummy");
}

@Test
void testHeadersWithCollections() {
String expected = "a, b, c, d";
Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
assertThat(client.headersList(List.of("a", "b"), List.of("c", "d"))).isEqualTo(expected);
assertThat(client.headersSet(Set.of("a", "b"), Set.of("c", "d"))).isEqualTo(expected);
assertThat(client.headersSet(new TreeSet(List.of("a", "b")), new TreeSet(List.of("c", "d")))).isEqualTo(expected);
}

@Path("/")
@ApplicationScoped
public static class Resource {
Expand All @@ -51,12 +67,50 @@ public String returnHeaders(@HeaderParam("foo") String header, @HeaderParam("foo
return header + ":" + header2 + ":" + header3 + ":" + header4 + ":" + myHeaderName + "/"
+ headers.getHeaderString("X-My-Header");
}

@GET
@Path("/headers-list")
public String headersList(@HeaderParam("foo") List foo, @RestHeader List header) {
return joiningCollections(foo, header);
}

@GET
@Path("/headers-set")
public String headersSet(@HeaderParam("foo") Set foo, @RestHeader Set header) {
return joiningCollections(foo, header);
}

@GET
@Path("/headers-sorted-set")
public String headersSortedSet(@HeaderParam("foo") SortedSet foo, @RestHeader SortedSet header) {
return joiningCollections(foo, header);
}

private String joiningCollections(Collection... collections) {
List<String> allHeaders = new ArrayList<>();
for (Collection collection : collections) {
collection.forEach(v -> allHeaders.add((String) v));
}
return allHeaders.stream().collect(Collectors.joining(", "));
}
}

public interface Client {

@Path("/")
SubClient cookieSub(@HeaderParam("foo") String cookie, @HeaderParam("foo2") String cookie2);

@GET
@Path("/headers-list")
String headersList(@HeaderParam("foo") List foo, @RestHeader List header);

@GET
@Path("/headers-set")
String headersSet(@HeaderParam("foo") Set foo, @RestHeader Set header);

@GET
@Path("/headers-sorted-set")
String headersSortedSet(@HeaderParam("foo") SortedSet foo, @RestHeader SortedSet header);
}

public interface SubClient {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.rest.client.reactive.runtime;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -81,11 +82,13 @@ public void filter(ResteasyReactiveClientRequestContext requestContext) {
}
}

private static List<String> castToListOfStrings(List<Object> values) {
private static List<String> castToListOfStrings(Collection<Object> values) {
List<String> result = new ArrayList<>();
for (Object value : values) {
if (value instanceof String) {
result.add((String) value);
} else if (value instanceof Collection) {
result.addAll(castToListOfStrings((Collection<Object>) value));
} else {
result.add(String.valueOf(value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1359,13 +1359,28 @@ && isParameterContainerType(paramType.asClassType())) {
elementType = paramType.name().toString();
handleTemporalParam(builder, paramType.name(), anns, currentMethodInfo);
typeHandled = true;
} else if (paramType.name().equals(LIST) && (type == ParameterType.QUERY)) { // RESTEasy Classic handles the non-generic List type
} else if (paramType.name().equals(LIST) && (type == ParameterType.QUERY
|| type == ParameterType.HEADER)) { // RESTEasy Classic handles the non-generic List type
elementType = String.class.getName();
typeHandled = true;
builder.setSingle(false);
if (convertible) {
handleListParam(existingConverters, errorLocation, hasRuntimeConverters, builder, elementType);
}
} else if (paramType.name().equals(SET) && type == ParameterType.HEADER) { // RESTEasy Classic handles the non-generic Set type
elementType = String.class.getName();
typeHandled = true;
builder.setSingle(false);
if (convertible) {
handleSetParam(existingConverters, errorLocation, hasRuntimeConverters, builder, elementType);
}
} else if (paramType.name().equals(SORTED_SET) && type == ParameterType.HEADER) { // RESTEasy Classic handles the non-generic SortedSet type
elementType = String.class.getName();
typeHandled = true;
builder.setSingle(false);
if (convertible) {
handleSortedSetParam(existingConverters, errorLocation, hasRuntimeConverters, builder, elementType);
}
} else if (paramType.kind() == Kind.ARRAY) {
ArrayType at = paramType.asArrayType();
typeHandled = true;
Expand Down