Repository: quarkusio/quarkus
Issue: Validation for REST endpoints does not return messages about the constraints violated

**Describe the bug**
After the changes in https://github.com/quarkusio/quarkus/pull/15447, now when using REST Panache or Spring Data REST quarkus extensions, we got 400 when there are constraint violations. 

The problem now is that the response does not return the constraints that have been violated. 

**Expected behavior**
After a REST violation, It's expected to receive something like:

```json
{
    "classViolations": [],
    "parameterViolations": [
        {
            "constraintType": "PARAMETER",
            "message": "Title may not be blank",
            "path": "addBook.book.title",
            "value": ""
        }
    ],
    "propertyViolations": [],
    "returnValueViolations": []
}
```

* If we're using JSON... *

**Actual behavior**
No message is returned.

**To Reproduce**
1- git clone https://github.com/Sgitario/beefy-scenarios
2- git checkout reproducer_15409
3- cd beefy-scenarios/602-spring-data-rest
4- mvn clean verify
There are some tests failing due to this issue.

**Environment (please complete the following information):**
 - Quarkus version or git rev: 999-SNAPSHOT


Changed files:
extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java
extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java
extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java
extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java
extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java
integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java
integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java
extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java

diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java
index 98d8c347b25..f5c6b67156f 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java
@@ -33,16 +33,20 @@ class JaxRsResourceImplementor {
 
     private static final Logger LOGGER = Logger.getLogger(JaxRsResourceImplementor.class);
 
-    private final List<MethodImplementor> METHOD_IMPLEMENTORS = Arrays.asList(
-            new GetMethodImplementor(),
-            new GetHalMethodImplementor(),
-            new ListMethodImplementor(),
-            new ListHalMethodImplementor(),
-            new AddMethodImplementor(),
-            new AddHalMethodImplementor(),
-            new UpdateMethodImplementor(),
-            new UpdateHalMethodImplementor(),
-            new DeleteMethodImplementor());
+    private final List<MethodImplementor> methodImplementors;
+
+    JaxRsResourceImplementor(boolean withValidation) {
+        this.methodImplementors = Arrays.asList(
+                new GetMethodImplementor(),
+                new GetHalMethodImplementor(),
+                new ListMethodImplementor(),
+                new ListHalMethodImplementor(),
+                new AddMethodImplementor(withValidation),
+                new AddHalMethodImplementor(withValidation),
+                new UpdateMethodImplementor(withValidation),
+                new UpdateHalMethodImplementor(withValidation),
+                new DeleteMethodImplementor());
+    }
 
     /**
      * Implement a JAX-RS resource with a following structure.
@@ -81,13 +85,14 @@ private void implementClassAnnotations(ClassCreator classCreator, ResourceProper
 
     private FieldDescriptor implementResourceField(ClassCreator classCreator, ResourceMetadata resourceMetadata) {
         FieldCreator resourceFieldCreator = classCreator.getFieldCreator("resource", resourceMetadata.getResourceClass());
-        resourceFieldCreator.setModifiers(resourceFieldCreator.getModifiers() & ~Modifier.PRIVATE).addAnnotation(Inject.class);
+        resourceFieldCreator.setModifiers(resourceFieldCreator.getModifiers() & ~Modifier.PRIVATE)
+                .addAnnotation(Inject.class);
         return resourceFieldCreator.getFieldDescriptor();
     }
 
     private void implementMethods(ClassCreator classCreator, ResourceMetadata resourceMetadata,
             ResourceProperties resourceProperties, FieldDescriptor resourceField) {
-        for (MethodImplementor methodImplementor : METHOD_IMPLEMENTORS) {
+        for (MethodImplementor methodImplementor : methodImplementors) {
             methodImplementor.implement(classCreator, resourceMetadata, resourceProperties, resourceField);
         }
     }
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java
index 082c361d05b..0e3574c7b80 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/RestDataProcessor.java
@@ -7,6 +7,8 @@
 
 import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
 import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
+import io.quarkus.deployment.Capabilities;
+import io.quarkus.deployment.Capability;
 import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
@@ -31,10 +33,10 @@ public class RestDataProcessor {
 
     @BuildStep
     void implementResources(CombinedIndexBuildItem index, List<RestDataResourceBuildItem> resourceBuildItems,
-            List<ResourcePropertiesBuildItem> resourcePropertiesBuildItems,
+            List<ResourcePropertiesBuildItem> resourcePropertiesBuildItems, Capabilities capabilities,
             BuildProducer<GeneratedBeanBuildItem> implementationsProducer) {
         ClassOutput classOutput = new GeneratedBeanGizmoAdaptor(implementationsProducer);
-        JaxRsResourceImplementor jaxRsResourceImplementor = new JaxRsResourceImplementor();
+        JaxRsResourceImplementor jaxRsResourceImplementor = new JaxRsResourceImplementor(hasValidatorCapability(capabilities));
         ResourcePropertiesProvider resourcePropertiesProvider = new ResourcePropertiesProvider(index.getIndex());
 
         for (RestDataResourceBuildItem resourceBuildItem : resourceBuildItems) {
@@ -80,4 +82,8 @@ private ResourceProperties getResourceProperties(ResourcePropertiesProvider reso
         }
         return resourcePropertiesProvider.getForInterface(resourceMetadata.getResourceInterface());
     }
+
+    private boolean hasValidatorCapability(Capabilities capabilities) {
+        return capabilities.isPresent(Capability.HIBERNATE_VALIDATOR);
+    }
 }
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java
index e41d2e373f9..28b7be0ee95 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java
@@ -2,6 +2,7 @@
 
 import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
 
+import javax.validation.Valid;
 import javax.ws.rs.core.Response;
 
 import io.quarkus.gizmo.ClassCreator;
@@ -22,6 +23,12 @@ public final class AddMethodImplementor extends StandardMethodImplementor {
 
     private static final String REL = "add";
 
+    private final boolean withValidation;
+
+    public AddMethodImplementor(boolean withValidation) {
+        this.withValidation = withValidation;
+    }
+
     /**
      * Generate JAX-RS POST method that exposes {@link RestDataResource#add(Object)}.
      * Generated code looks more or less like this:
@@ -67,6 +74,10 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
         addConsumesAnnotation(methodCreator, APPLICATION_JSON);
         addProducesAnnotation(methodCreator, APPLICATION_JSON);
         addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL);
+        // Add parameter annotations
+        if (withValidation) {
+            methodCreator.getParameterAnnotations(0).addAnnotation(Valid.class);
+        }
 
         ResultHandle resource = methodCreator.readInstanceField(resourceField, methodCreator.getThis());
         ResultHandle entityToSave = methodCreator.getMethodParam(0);
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java
index d5ffb4cd0c8..21335d65f46 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java
@@ -2,6 +2,7 @@
 
 import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
 
+import javax.validation.Valid;
 import javax.ws.rs.core.Response;
 
 import io.quarkus.gizmo.BranchResult;
@@ -26,6 +27,12 @@ public final class UpdateMethodImplementor extends StandardMethodImplementor {
 
     private static final String REL = "update";
 
+    private final boolean withValidation;
+
+    public UpdateMethodImplementor(boolean withValidation) {
+        this.withValidation = withValidation;
+    }
+
     /**
      * Generate JAX-RS UPDATE method that exposes {@link RestDataResource#update(Object, Object)}.
      * Expose {@link RestDataResource#update(Object, Object)} via JAX-RS method.
@@ -79,6 +86,10 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
         addConsumesAnnotation(methodCreator, APPLICATION_JSON);
         addProducesAnnotation(methodCreator, APPLICATION_JSON);
         addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL);
+        // Add parameter annotations
+        if (withValidation) {
+            methodCreator.getParameterAnnotations(1).addAnnotation(Valid.class);
+        }
 
         ResultHandle resource = methodCreator.readInstanceField(resourceField, methodCreator.getThis());
         ResultHandle id = methodCreator.getMethodParam(0);
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java
index fce46c72078..4e12f49b9ac 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/AddHalMethodImplementor.java
@@ -2,6 +2,7 @@
 
 import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
 
+import javax.validation.Valid;
 import javax.ws.rs.core.Response;
 
 import io.quarkus.gizmo.ClassCreator;
@@ -20,6 +21,12 @@ public final class AddHalMethodImplementor extends HalMethodImplementor {
 
     private static final String RESOURCE_METHOD_NAME = "add";
 
+    private final boolean withValidation;
+
+    public AddHalMethodImplementor(boolean withValidation) {
+        this.withValidation = withValidation;
+    }
+
     /**
      * Expose {@link RestDataResource#add(Object)} via HAL JAX-RS method.
      * Generated code looks more or less like this:
@@ -61,6 +68,10 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
         addPostAnnotation(methodCreator);
         addConsumesAnnotation(methodCreator, APPLICATION_JSON);
         addProducesAnnotation(methodCreator, APPLICATION_HAL_JSON);
+        // Add parameter annotations
+        if (withValidation) {
+            methodCreator.getParameterAnnotations(0).addAnnotation(Valid.class);
+        }
 
         ResultHandle resource = methodCreator.readInstanceField(resourceField, methodCreator.getThis());
         ResultHandle entityToSave = methodCreator.getMethodParam(0);
diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java
index ee55679932b..7c07580ce60 100644
--- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java
+++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/UpdateHalMethodImplementor.java
@@ -2,6 +2,7 @@
 
 import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
 
+import javax.validation.Valid;
 import javax.ws.rs.core.Response;
 
 import io.quarkus.gizmo.BranchResult;
@@ -24,6 +25,12 @@ public final class UpdateHalMethodImplementor extends HalMethodImplementor {
 
     private static final String RESOURCE_GET_METHOD_NAME = "get";
 
+    private final boolean withValidation;
+
+    public UpdateHalMethodImplementor(boolean withValidation) {
+        this.withValidation = withValidation;
+    }
+
     /**
      * Expose {@link RestDataResource#update(Object, Object)} via HAL JAX-RS method.
      * Generated code looks more or less like this:
@@ -72,6 +79,10 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res
         addPathParamAnnotation(methodCreator.getParameterAnnotations(0), "id");
         addConsumesAnnotation(methodCreator, APPLICATION_JSON);
         addProducesAnnotation(methodCreator, APPLICATION_HAL_JSON);
+        // Add parameter annotations
+        if (withValidation) {
+            methodCreator.getParameterAnnotations(1).addAnnotation(Valid.class);
+        }
 
         ResultHandle resource = methodCreator.readInstanceField(resourceField, methodCreator.getThis());
         ResultHandle id = methodCreator.getMethodParam(0);
diff --git a/integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java b/integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java
index df2f9f3db55..1e06b968175 100644
--- a/integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java
+++ b/integration-tests/hibernate-orm-rest-data-panache/src/test/java/io/quarkus/it/hibernate/orm/rest/data/panache/HibernateOrmRestDataPanacheTest.java
@@ -178,7 +178,9 @@ void shouldNotCreateBookWithBlankTitle() {
                 .and().contentType("application/json")
                 .and().body(book.toString())
                 .when().post("/books")
-                .then().statusCode(400);
+                .then().statusCode(400)
+                .and().body("parameterViolations[0].path", equalTo("add.arg0.title"))
+                .and().body("parameterViolations[0].message", equalTo("must not be blank"));
     }
 
     @Test
@@ -230,4 +232,24 @@ void shouldCreateUpdateAndDeleteBook() {
         when().delete(location)
                 .then().statusCode(204);
     }
+
+    @Test
+    void shouldNotUpdateBookWithBlankTitle() {
+        JsonObject author = Json.createObjectBuilder()
+                .add("id", DOSTOEVSKY_ID)
+                .add("name", DOSTOEVSKY_NAME)
+                .add("dob", DOSTOEVSKY_DOB)
+                .build();
+        JsonObject book = Json.createObjectBuilder()
+                .add("title", "")
+                .add("author", author)
+                .build();
+        given().accept("application/json")
+                .and().contentType("application/json")
+                .and().body(book.toString())
+                .when().put("/books/" + CRIME_AND_PUNISHMENT_ID)
+                .then().statusCode(400)
+                .and().body("parameterViolations[0].path", equalTo("update.arg1.title"))
+                .and().body("parameterViolations[0].message", equalTo("must not be blank"));
+    }
 }
diff --git a/integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java b/integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java
index 7631cfe405b..0a5325c28e8 100644
--- a/integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java
+++ b/integration-tests/spring-data-rest/src/test/java/io/quarkus/it/spring/data/rest/SpringDataRestTest.java
@@ -156,6 +156,26 @@ void shouldCreateAndDeleteBook() {
                 .then().statusCode(404);
     }
 
+    @Test
+    void shouldNotCreateBookWithBlankTitle() {
+        JsonObject author = Json.createObjectBuilder()
+                .add("id", DOSTOEVSKY_ID)
+                .add("name", DOSTOEVSKY_NAME)
+                .add("dob", DOSTOEVSKY_DOB)
+                .build();
+        JsonObject book = Json.createObjectBuilder()
+                .add("title", "")
+                .add("author", author)
+                .build();
+        given().accept("application/json")
+                .and().contentType("application/json")
+                .and().body(book.toString())
+                .when().post("/books")
+                .then().statusCode(400)
+                .and().body("parameterViolations[0].path", equalTo("add.arg0.title"))
+                .and().body("parameterViolations[0].message", equalTo("must not be blank"));
+    }
+
     @Test
     void shouldNotUpdateAuthor() {
         JsonObject author = Json.createObjectBuilder()
@@ -207,7 +227,7 @@ void shouldCreateUpdateAndDeleteBook() {
     }
 
     @Test
-    void shouldNotCreateBookWithBlankTitle() {
+    void shouldNotUpdateBookWithBlankTitle() {
         JsonObject author = Json.createObjectBuilder()
                 .add("id", DOSTOEVSKY_ID)
                 .add("name", DOSTOEVSKY_NAME)
@@ -220,7 +240,9 @@ void shouldNotCreateBookWithBlankTitle() {
         given().accept("application/json")
                 .and().contentType("application/json")
                 .and().body(book.toString())
-                .when().put("/books/100")
-                .then().statusCode(400);
+                .when().put("/books/" + CRIME_AND_PUNISHMENT_ID)
+                .then().statusCode(400)
+                .and().body("parameterViolations[0].path", equalTo("update.arg1.title"))
+                .and().body("parameterViolations[0].message", equalTo("must not be blank"));
     }
 }