Skip to content

Commit

Permalink
feat: Implement skipping of not Rest parameters indexing processing u…
Browse files Browse the repository at this point in the history
…sing new build item

[The implementation brings a new build item that extension can produce in order to allow Rest resource methods with parameters that are not related to Rest]
  • Loading branch information
poldinik committed Apr 29, 2024
1 parent 6b171d0 commit c14cf3f
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
import io.quarkus.resteasy.reactive.server.spi.NonBlockingReturnTypeBuildItem;
import io.quarkus.resteasy.reactive.server.spi.PreExceptionMapperHandlerBuildItem;
import io.quarkus.resteasy.reactive.server.spi.ResumeOn404BuildItem;
import io.quarkus.resteasy.reactive.server.spi.AllowNotRestParametersBuildItem;
import io.quarkus.resteasy.reactive.spi.CustomExceptionMapperBuildItem;
import io.quarkus.resteasy.reactive.spi.DynamicFeatureBuildItem;
import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem;
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 {
}
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,18 @@ 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 @@ -1678,6 +1694,8 @@ public boolean handleMultipartForReturnType(AdditionalWriters additionalWriters,
private Function<ClassInfo, Supplier<Boolean>> isDisabledCreator = null;

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

private boolean skipNotRestParameters = false;

public B setMultipartReturnTypeIndexerExtension(MultipartReturnTypeIndexerExtension multipartReturnTypeHandler) {
this.multipartReturnTypeIndexerExtension = multipartReturnTypeHandler;
Expand Down Expand Up @@ -1801,6 +1819,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

0 comments on commit c14cf3f

Please sign in to comment.