From 1a22331f52ffc7f55d02c0db9f1b861d30724c2d Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 25 May 2021 16:39:25 +0200 Subject: [PATCH] Hibernate Validator - Fix container element constraints detection Fixes #17452 --- .../HibernateValidatorProcessor.java | 22 +++++ .../test/ContainerElementConstraintsTest.java | 95 +++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ContainerElementConstraintsTest.java diff --git a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java index df559c92d389e..236adeca95480 100644 --- a/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java +++ b/extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java @@ -276,6 +276,28 @@ public void build(HibernateValidatorRecorder recorder, RecorderContext recorderC } else if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) { contributeClass(classNamesToBeValidated, indexView, annotation.target().asClass().name()); // no need for reflection in the case of a class level constraint + } else if (annotation.target().kind() == AnnotationTarget.Kind.TYPE) { + // container element constraints + AnnotationTarget enclosingTarget = annotation.target().asType().enclosingTarget(); + if (enclosingTarget.kind() == AnnotationTarget.Kind.FIELD) { + contributeClass(classNamesToBeValidated, indexView, enclosingTarget.asField().declaringClass().name()); + reflectiveFields.produce(new ReflectiveFieldBuildItem(enclosingTarget.asField())); + if (annotation.target().asType().target() != null) { + contributeClassMarkedForCascadingValidation(classNamesToBeValidated, indexView, + consideredAnnotation, + annotation.target().asType().target()); + } + } else if (enclosingTarget.kind() == AnnotationTarget.Kind.METHOD) { + contributeClass(classNamesToBeValidated, indexView, enclosingTarget.asMethod().declaringClass().name()); + reflectiveMethods.produce(new ReflectiveMethodBuildItem(enclosingTarget.asMethod())); + if (annotation.target().asType().target() != null) { + contributeClassMarkedForCascadingValidation(classNamesToBeValidated, indexView, + consideredAnnotation, + annotation.target().asType().target()); + } + contributeMethodsWithInheritedValidation(methodsWithInheritedValidation, indexView, + enclosingTarget.asMethod()); + } } } } diff --git a/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ContainerElementConstraintsTest.java b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ContainerElementConstraintsTest.java new file mode 100644 index 0000000000000..47771618c4154 --- /dev/null +++ b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ContainerElementConstraintsTest.java @@ -0,0 +1,95 @@ +package io.quarkus.hibernate.validator.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.validation.ValidatorFactory; +import javax.validation.constraints.NotBlank; + +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 io.quarkus.test.QuarkusUnitTest; + +public class ContainerElementConstraintsTest { + + @Inject + ValidatorFactory validatorFactory; + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap + .create(JavaArchive.class).addClasses(TestBean.class)); + + @Test + public void testContainerElementConstraint() { + assertThat(validatorFactory.getValidator().validate(new TestBean())).hasSize(1); + } + + @Test + public void testNestedContainerElementConstraint() { + assertThat(validatorFactory.getValidator().validate(new NestedTestBean())).hasSize(1); + } + + @Test + public void testMethodParameterContainerElementConstraint() throws NoSuchMethodException, SecurityException { + Map> invalidMap = new HashMap<>(); + invalidMap.put("key", Collections.singletonList("")); + + assertThat(validatorFactory.getValidator().forExecutables().validateParameters(new MethodParameterTestBean(), + MethodParameterTestBean.class.getMethod("test", Map.class), new Object[] { invalidMap })).hasSize(1); + } + + @Test + public void testMethodReturnValueContainerElementConstraint() throws NoSuchMethodException, SecurityException { + Map> invalidMap = new HashMap<>(); + invalidMap.put("key", Collections.singletonList("")); + + assertThat(validatorFactory.getValidator().forExecutables().validateReturnValue(new MethodReturnValueTestBean(), + MethodReturnValueTestBean.class.getMethod("test"), invalidMap)).hasSize(1); + } + + static class TestBean { + + public Map constrainedMap; + + public TestBean() { + Map invalidMap = new HashMap<>(); + invalidMap.put("key", ""); + + this.constrainedMap = invalidMap; + } + } + + static class NestedTestBean { + + public Map> constrainedMap; + + public NestedTestBean() { + Map> invalidMap = new HashMap<>(); + invalidMap.put("key", Collections.singletonList("")); + + this.constrainedMap = invalidMap; + } + } + + static class MethodParameterTestBean { + + public void test(Map> constrainedMap) { + // do nothing + } + } + + static class MethodReturnValueTestBean { + + public Map> test() { + return null; + } + } +}