From a8f1995eb5bd625b1bbde8ff4f95e08b917f25c1 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 14 Oct 2024 09:24:29 +0300 Subject: [PATCH] Support @JsonView on request body Closes: #35871 --- .../ResteasyReactiveJacksonProcessor.java | 21 ++++-- .../test/JsonViewOnMethodParameterTest.java | 64 +++++++++++++++++++ 2 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/JsonViewOnMethodParameterTest.java diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java index c2a0ec0735be8..43ef5caad8bea 100644 --- a/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/ResteasyReactiveJacksonProcessor.java @@ -75,7 +75,11 @@ import io.quarkus.resteasy.reactive.jackson.runtime.mappers.NativeInvalidDefinitionExceptionMapper; import io.quarkus.resteasy.reactive.jackson.runtime.security.RolesAllowedConfigExpStorage; import io.quarkus.resteasy.reactive.jackson.runtime.security.SecurityCustomSerialization; -import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.*; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.BasicServerJacksonMessageBodyWriter; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.FullyFeaturedServerJacksonMessageBodyReader; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.FullyFeaturedServerJacksonMessageBodyWriter; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.GeneratedSerializersRegister; +import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.ServerJacksonMessageBodyReader; import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayMessageBodyReader; import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonArrayMessageBodyWriter; import io.quarkus.resteasy.reactive.jackson.runtime.serialisers.vertx.VertxJsonObjectMessageBodyReader; @@ -263,7 +267,7 @@ void handleJsonAnnotations(Optional resourceSca if ((jsonViews == null) || (jsonViews.length == 0)) { continue; } - recorder.recordJsonView(getTargetId(instance.target()), jsonViews[0].name().toString()); + recorder.recordJsonView(getTargetId(instance), jsonViews[0].name().toString()); } } if (resourceClass.annotationsMap().containsKey(CUSTOM_SERIALIZATION)) { @@ -290,7 +294,7 @@ void handleJsonAnnotations(Optional resourceSca ReflectiveClassBuildItem.builder(biFunctionType.name().toString()) .reason(getClass().getName()) .build()); - recorder.recordCustomSerialization(getTargetId(instance.target()), biFunctionType.name().toString()); + recorder.recordCustomSerialization(getTargetId(instance), biFunctionType.name().toString()); } } if (resourceClass.annotationsMap().containsKey(CUSTOM_DESERIALIZATION)) { @@ -317,7 +321,7 @@ void handleJsonAnnotations(Optional resourceSca ReflectiveClassBuildItem.builder(biFunctionType.name().toString()) .reason(getClass().getName()) .build()); - recorder.recordCustomDeserialization(getTargetId(instance.target()), biFunctionType.name().toString()); + recorder.recordCustomDeserialization(getTargetId(instance), biFunctionType.name().toString()); } } } @@ -641,15 +645,18 @@ private static boolean fieldTypeHasSecureFields(Type fieldType, IndexView indexV return false; } - private String getTargetId(AnnotationTarget target) { + private String getTargetId(AnnotationInstance instance) { + AnnotationTarget target = instance.target(); if (target.kind() == AnnotationTarget.Kind.CLASS) { return getClassId(target.asClass()); } else if (target.kind() == AnnotationTarget.Kind.METHOD) { return getMethodId(target.asMethod()); + } else if (target.kind() == AnnotationTarget.Kind.METHOD_PARAMETER) { + return getMethodId(target.asMethodParameter().method()); } - throw new UnsupportedOperationException("The `@CustomSerialization` and `@CustomDeserialization` annotations can only " - + "be used in methods or classes."); + throw new UnsupportedOperationException(String.format("The `%s` annotation can only " + + "be used in methods or classes.", instance.name())); } private String getClassId(ClassInfo classInfo) { diff --git a/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/JsonViewOnMethodParameterTest.java b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/JsonViewOnMethodParameterTest.java new file mode 100644 index 0000000000000..780d3a6bf73cb --- /dev/null +++ b/extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/JsonViewOnMethodParameterTest.java @@ -0,0 +1,64 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test; + +import static io.restassured.RestAssured.given; +import static jakarta.ws.rs.core.Response.Status.CREATED; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; + +import java.util.function.Supplier; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.jboss.resteasy.reactive.RestResponse; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.fasterxml.jackson.annotation.JsonView; + +import io.quarkus.test.QuarkusUnitTest; + +public class JsonViewOnMethodParameterTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(User.class, Views.class, Resource.class); + } + }); + + @Test + public void test() { + given().accept("application/json") + .contentType("application/json") + .body(""" + { + "id": 1, + "name": "Foo" + } + """) + .post("test") + .then() + .statusCode(201) + .body(not(containsString("1")), containsString("Foo")); + } + + @Path("test") + public static class Resource { + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public RestResponse create(@JsonView(Views.Public.class) User user) { + return RestResponse.status(CREATED, user); + } + } +}