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

Allow not JAX-RS parameters within resource methods #40031

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 @@ -183,6 +183,7 @@
import io.quarkus.resteasy.reactive.server.runtime.security.EagerSecurityHandler;
import io.quarkus.resteasy.reactive.server.runtime.security.EagerSecurityInterceptorHandler;
import io.quarkus.resteasy.reactive.server.runtime.security.SecurityContextOverrideHandler;
import io.quarkus.resteasy.reactive.server.spi.AllowNotRestParametersBuildItem;
import io.quarkus.resteasy.reactive.server.spi.AnnotationsTransformerBuildItem;
import io.quarkus.resteasy.reactive.server.spi.ContextTypeBuildItem;
import io.quarkus.resteasy.reactive.server.spi.HandlerConfigurationProviderBuildItem;
Expand Down Expand Up @@ -413,8 +414,8 @@ public void setupEndpoints(ApplicationIndexBuildItem applicationIndexBuildItem,
List<ContextTypeBuildItem> contextTypeBuildItems,
CompiledJavaVersionBuildItem compiledJavaVersionBuildItem,
ResourceInterceptorsBuildItem resourceInterceptorsBuildItem,
Capabilities capabilities)
throws NoSuchMethodException {
Capabilities capabilities,
Optional<AllowNotRestParametersBuildItem> allowNotRestParametersBuildItem) {

if (!resourceScanningResultBuildItem.isPresent()) {
// no detected @Path, bail out
Expand Down Expand Up @@ -634,6 +635,8 @@ public Supplier<Boolean> apply(ClassInfo classInfo) {
}
});

serverEndpointIndexerBuilder.skipNotRestParameters(allowNotRestParametersBuildItem.isPresent());

if (!serverDefaultProducesHandlers.isEmpty()) {
List<DefaultProducesHandler> handlers = new ArrayList<>(serverDefaultProducesHandlers.size());
for (ServerDefaultProducesHandlerBuildItem bi : serverDefaultProducesHandlers) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.quarkus.resteasy.reactive.server.test.resource.basic;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

import java.util.function.Consumer;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildContext;
import io.quarkus.builder.BuildStep;
import io.quarkus.resteasy.reactive.server.spi.AllowNotRestParametersBuildItem;
import io.quarkus.resteasy.reactive.server.test.resource.basic.resource.MixedParameterResource;
import io.quarkus.test.QuarkusUnitTest;

/**
* @tpSubChapter Resources
* @tpChapter Integration tests
* @tpTestCaseDetails Test resources with not all method parameters related to RESTEasy.
*/
@DisplayName("Allow Not RESTEasy Method Parameters")
public class AllowNotResteasyParametersTest {

@RegisterExtension
static QuarkusUnitTest quarkusUnitTest = new QuarkusUnitTest()
.addBuildChainCustomizer(new Consumer<BuildChainBuilder>() {
@Override
public void accept(BuildChainBuilder buildChainBuilder) {
buildChainBuilder.addBuildStep(new BuildStep() {
@Override
public void execute(BuildContext context) {
context.produce(new AllowNotRestParametersBuildItem());
}
}).produces(AllowNotRestParametersBuildItem.class).build();
}
})
.withApplicationRoot((jar) -> jar
.addClass(MixedParameterResource.class));

@Test
@DisplayName("Test Resource Method with one param not related to RESTEasy")
public void shouldOkEvenNotResteasyParameterPresence() {
given()
.body("value")
.post("/" + MixedParameterResource.class.getSimpleName() + "/mixed?foo=bar")
.then().statusCode(200).body(is("bar.value"));
}

@Test
@DisplayName("Test Resource Method with only one param not related to RESTEasy")
public void shouldOkEvenNotResteasySingleParameterPresence() {
given()
.body("value")
.get("/" + MixedParameterResource.class.getSimpleName() + "/single")
.then().statusCode(200);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.resteasy.reactive.server.test.resource.basic.resource;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

@Path("/MixedParameterResource")
public class MixedParameterResource {

@POST
@Path("/mixed")
public String mixedParam(@OtherAnnotation MyOtherObject otherObject, @QueryParam("foo") String query, String body) {
return query.concat(".").concat(body);
}

@GET
@Path("/single")
public String singleParam(@OtherAnnotation MyOtherObject otherObject) {
return "ok";
}

public static class MyOtherObject {
String param;

public String getParam() {
return param;
}

public void setParam(String param) {
this.param = param;
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface OtherAnnotation {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.resteasy.reactive.server.spi;

import io.quarkus.builder.item.SimpleBuildItem;

/**
* A build item which extensions can generate when they want to allow RESTEasy Reactive methods
* with parameters that are annotated with not REST annotations. This allows RESTEasy Reactive to let mixed parameters coexist
* within resource methods signature
*/
public final class AllowNotRestParametersBuildItem extends SimpleBuildItem {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little Javadoc on what this done would be great

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.HTTP_HEADERS;
import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.INSTANT;
import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.INTEGER;
import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.JAX_RS_ANNOTATIONS_FOR_FIELDS;
import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.LIST;
import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.LOCAL_DATE;
import static org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames.LOCAL_DATE_TIME;
Expand Down Expand Up @@ -237,6 +238,8 @@ public abstract class EndpointIndexer<T extends EndpointIndexer<T, PARAM, METHOD
private final Function<ClassInfo, Supplier<Boolean>> isDisabledCreator;

private final Predicate<Map<DotName, AnnotationInstance>> skipMethodParameter;
private final boolean skipNotRestParameters;

private SerializerScanningResult serializerScanningResult;

protected EndpointIndexer(Builder<T, ?, METHOD> builder) {
Expand All @@ -262,6 +265,7 @@ protected EndpointIndexer(Builder<T, ?, METHOD> builder) {
this.targetJavaVersion = builder.targetJavaVersion;
this.isDisabledCreator = builder.isDisabledCreator;
this.skipMethodParameter = builder.skipMethodParameter;
this.skipNotRestParameters = builder.skipNotRestParameters;
}

public Optional<ResourceClass> createEndpoints(ClassInfo classInfo, boolean considerApplication) {
Expand Down Expand Up @@ -576,7 +580,7 @@ private ResourceMethod createResourceMethod(ClassInfo currentClassInfo, ClassInf
Type paramType = currentMethodInfo.parameterType(i);
String errorLocation = "method " + currentMethodInfo + " on class " + currentMethodInfo.declaringClass();

if (skipParameter(anns)) {
if (skipParameter(anns) || skipNotRestParameters(skipNotRestParameters).apply(anns)) {
parameterResult = createIndexedParam()
.setCurrentClassInfo(currentClassInfo)
.setActualEndpointInfo(actualEndpointInfo)
Expand Down Expand Up @@ -782,6 +786,19 @@ protected void warnAboutMissUsedBodyParameter(DotName httpMethod, MethodInfo met
+ methodInfo.declaringClass().name() + "#" + methodInfo + "'");
}

private Function<Map<DotName, AnnotationInstance>, Boolean> skipNotRestParameters(boolean skipAllNotMethodParameter) {
return new Function<Map<DotName, AnnotationInstance>, Boolean>() {
@Override
public Boolean apply(Map<DotName, AnnotationInstance> anns) {
if (skipAllNotMethodParameter) {
return anns.size() > 0
&& JAX_RS_ANNOTATIONS_FOR_FIELDS.stream().noneMatch(dotName -> anns.containsKey(dotName));
}
return false;
}
};
}

protected boolean skipParameter(Map<DotName, AnnotationInstance> anns) {
return skipMethodParameter != null && skipMethodParameter.test(anns);
}
Expand Down Expand Up @@ -1679,6 +1696,8 @@ public boolean handleMultipartForReturnType(AdditionalWriters additionalWriters,

private Predicate<Map<DotName, AnnotationInstance>> skipMethodParameter = null;

private boolean skipNotRestParameters = false;

public B setMultipartReturnTypeIndexerExtension(MultipartReturnTypeIndexerExtension multipartReturnTypeHandler) {
this.multipartReturnTypeIndexerExtension = multipartReturnTypeHandler;
return (B) this;
Expand Down Expand Up @@ -1801,6 +1820,11 @@ public B setSkipMethodParameter(
return (B) this;
}

public B skipNotRestParameters(boolean skipNotRestParameters) {
this.skipNotRestParameters = skipNotRestParameters;
return (B) this;
}

public abstract T build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz,
addHandlers(handlers, clazz, method, info, HandlerChainCustomizer.Phase.RESOLVE_METHOD_PARAMETERS);
for (int i = 0; i < parameters.length; i++) {
ServerMethodParameter param = (ServerMethodParameter) parameters[i];
if (param.parameterType.equals(ParameterType.SKIPPED))
continue;
ParameterExtractor extractor = parameterExtractor(pathParameterIndexes, locatableResource, param);
ParameterConverter converter = null;
ParamConverterProviders paramConverterProviders = info.getParamConverterProviders();
Expand Down
Loading