From 72a264cbec4fa291ed15402b1b48a45a47e95f52 Mon Sep 17 00:00:00 2001
From: Jose <josecarvajalhilario@gmail.com>
Date: Fri, 7 Jul 2023 11:38:08 +0200
Subject: [PATCH] Fix user methods requiring a session in Panache REST Data
 with Reactive

These changes add the `@WithSessionOnDemand` annotation at class level when using Panache REST Data with Hibernate Reactive.

This annotation makes user methods that return an Uni class to work.

About tests, I've covered the following scenarios:
- user GET method that returns an Uni
- user POST method that returns an Uni (and requires a transaction)
- injecting the generated resource will also work

Fix https://github.com/quarkusio/quarkus/issues/34432
---
 .../deployment/ResourceImplementor.java       |  8 ++-----
 .../AbstractInjectResourcesMethodTest.java    | 10 +++++++++
 .../entity/CollectionsResource.java           | 22 +++++++++++++++++++
 .../deployment/entity/InjectionResource.java  | 11 ++++++++++
 .../PanacheEntityResourceGetMethodTest.java   | 13 +++++++++++
 .../PanacheEntityResourcePostMethodTest.java  | 13 +++++++++++
 .../repository/CollectionsResource.java       | 22 +++++++++++++++++++
 ...anacheRepositoryResourceGetMethodTest.java | 13 +++++++++++
 ...nacheRepositoryResourcePostMethodTest.java | 13 +++++++++++
 .../deployment/JaxRsResourceImplementor.java  |  4 ++++
 10 files changed, 123 insertions(+), 6 deletions(-)

diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/ResourceImplementor.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/ResourceImplementor.java
index 6fc0fb62e719d..2dcc24a7a3a41 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/ResourceImplementor.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/ResourceImplementor.java
@@ -20,7 +20,7 @@
 import io.quarkus.gizmo.MethodCreator;
 import io.quarkus.gizmo.MethodDescriptor;
 import io.quarkus.gizmo.ResultHandle;
-import io.quarkus.hibernate.reactive.panache.common.WithSession;
+import io.quarkus.hibernate.reactive.panache.common.WithSessionOnDemand;
 import io.quarkus.hibernate.reactive.panache.common.WithTransaction;
 import io.quarkus.panache.common.Page;
 import io.quarkus.panache.common.Sort;
@@ -62,6 +62,7 @@ String implement(ClassOutput classOutput, DataAccessImplementor dataAccessImplem
         // when injecting the resource in user beans:
         classCreator.addAnnotation(Alternative.class);
         classCreator.addAnnotation(Priority.class).add("value", Integer.MAX_VALUE);
+        classCreator.addAnnotation(WithSessionOnDemand.class);
 
         HibernateReactiveResourceMethodListenerImplementor resourceMethodListenerImplementor = new HibernateReactiveResourceMethodListenerImplementor(
                 classCreator, resourceMethodListeners);
@@ -82,7 +83,6 @@ String implement(ClassOutput classOutput, DataAccessImplementor dataAccessImplem
 
     private void implementList(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) {
         MethodCreator methodCreator = classCreator.getMethodCreator("list", Uni.class, Page.class, Sort.class);
-        methodCreator.addAnnotation(WithSession.class);
         ResultHandle page = methodCreator.getMethodParam(0);
         ResultHandle sort = methodCreator.getMethodParam(1);
         ResultHandle columns = methodCreator.invokeVirtualMethod(ofMethod(Sort.class, "getColumns", List.class), sort);
@@ -98,7 +98,6 @@ private void implementList(ClassCreator classCreator, DataAccessImplementor data
     private void implementListWithQuery(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) {
         MethodCreator methodCreator = classCreator.getMethodCreator("list", Uni.class, Page.class, Sort.class,
                 String.class, Map.class);
-        methodCreator.addAnnotation(WithSession.class);
         ResultHandle page = methodCreator.getMethodParam(0);
         ResultHandle sort = methodCreator.getMethodParam(1);
         ResultHandle query = methodCreator.getMethodParam(2);
@@ -120,7 +119,6 @@ private void implementListWithQuery(ClassCreator classCreator, DataAccessImpleme
      */
     private void implementCount(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) {
         MethodCreator methodCreator = classCreator.getMethodCreator("count", Uni.class);
-        methodCreator.addAnnotation(WithSession.class);
         methodCreator.returnValue(dataAccessImplementor.count(methodCreator));
         methodCreator.close();
     }
@@ -132,7 +130,6 @@ private void implementCount(ClassCreator classCreator, DataAccessImplementor dat
     private void implementListPageCount(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) {
         MethodCreator methodCreator = classCreator.getMethodCreator(Constants.PAGE_COUNT_METHOD_PREFIX + "list", Uni.class,
                 Page.class);
-        methodCreator.addAnnotation(WithSession.class);
         ResultHandle page = methodCreator.getMethodParam(0);
         methodCreator.returnValue(dataAccessImplementor.pageCount(methodCreator, page));
         methodCreator.close();
@@ -140,7 +137,6 @@ private void implementListPageCount(ClassCreator classCreator, DataAccessImpleme
 
     private void implementGet(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) {
         MethodCreator methodCreator = classCreator.getMethodCreator("get", Uni.class, Object.class);
-        methodCreator.addAnnotation(WithSession.class);
         ResultHandle id = methodCreator.getMethodParam(0);
         methodCreator.returnValue(dataAccessImplementor.findById(methodCreator, id));
         methodCreator.close();
diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/AbstractInjectResourcesMethodTest.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/AbstractInjectResourcesMethodTest.java
index 522d322d01d8b..0a1b32680ab70 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/AbstractInjectResourcesMethodTest.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/AbstractInjectResourcesMethodTest.java
@@ -2,6 +2,7 @@
 
 import static io.restassured.RestAssured.given;
 import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.is;
 
 import org.junit.jupiter.api.Test;
 
@@ -14,4 +15,13 @@ void shouldGetListOfItems() {
                 .then().statusCode(200)
                 .and().body("id", contains(1, 2));
     }
+
+    @Test
+    void shouldCollectionByName() {
+        given().accept("application/json")
+                .when().get("/call/resource/collectionByName/full collection")
+                .then().statusCode(200)
+                .and().body("id", is("full"))
+                .and().body("name", is("full collection"));
+    }
 }
diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/CollectionsResource.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/CollectionsResource.java
index bcf2595d487a9..8b2db9ab78106 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/CollectionsResource.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/CollectionsResource.java
@@ -1,8 +1,30 @@
 package io.quarkus.hibernate.reactive.rest.data.panache.deployment.entity;
 
+import java.util.Collections;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+
 import io.quarkus.hibernate.reactive.rest.data.panache.PanacheEntityResource;
 import io.quarkus.rest.data.panache.ResourceProperties;
+import io.smallrye.mutiny.Uni;
 
 @ResourceProperties(hal = true, paged = false, halCollectionName = "item-collections")
 public interface CollectionsResource extends PanacheEntityResource<Collection, String> {
+    @GET
+    @Path("/name/{name}")
+    default Uni<Collection> findByName(@PathParam("name") String name) {
+        return Collection.find("name = :name", Collections.singletonMap("name", name)).singleResult();
+    }
+
+    @POST
+    @Path("/name/{name}")
+    default Uni<Collection> addByName(@PathParam("name") String name) {
+        Collection collection = new Collection();
+        collection.id = name;
+        collection.name = name;
+        return Collection.persist(collection).onItem().transform(res -> collection);
+    }
 }
diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/InjectionResource.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/InjectionResource.java
index ef961bbd2922f..1addf66232756 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/InjectionResource.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/InjectionResource.java
@@ -5,6 +5,7 @@
 import jakarta.inject.Inject;
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.core.MediaType;
 
@@ -18,10 +19,20 @@ public class InjectionResource {
     @Inject
     ItemsResource itemsResource;
 
+    @Inject
+    CollectionsResource collectionsResource;
+
     @GET
     @Path("/items")
     @Produces(MediaType.APPLICATION_JSON)
     public Uni<List<Item>> items() {
         return itemsResource.list(new Page(5), Sort.by("id"));
     }
+
+    @GET
+    @Path("/collectionByName/{name}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Uni<Collection> collectionByName(@PathParam("name") String name) {
+        return collectionsResource.findByName(name);
+    }
 }
diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/PanacheEntityResourceGetMethodTest.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/PanacheEntityResourceGetMethodTest.java
index 536d893409eeb..6290512bb78d2 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/PanacheEntityResourceGetMethodTest.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/PanacheEntityResourceGetMethodTest.java
@@ -1,5 +1,9 @@
 package io.quarkus.hibernate.reactive.rest.data.panache.deployment.entity;
 
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import io.quarkus.hibernate.reactive.rest.data.panache.deployment.AbstractGetMethodTest;
@@ -15,4 +19,13 @@ class PanacheEntityResourceGetMethodTest extends AbstractGetMethodTest {
                             EmptyListItem.class, EmptyListItemsResource.class)
                     .addAsResource("application.properties")
                     .addAsResource("import.sql"));
+
+    @Test
+    void shouldCopyAdditionalMethodsAsResources() {
+        given().accept("application/json")
+                .when().get("/collections/name/full collection")
+                .then().statusCode(200)
+                .and().body("id", is("full"))
+                .and().body("name", is("full collection"));
+    }
 }
diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/PanacheEntityResourcePostMethodTest.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/PanacheEntityResourcePostMethodTest.java
index fea432427db38..d575e9c557c6d 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/PanacheEntityResourcePostMethodTest.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/entity/PanacheEntityResourcePostMethodTest.java
@@ -1,5 +1,9 @@
 package io.quarkus.hibernate.reactive.rest.data.panache.deployment.entity;
 
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import io.quarkus.hibernate.reactive.rest.data.panache.deployment.AbstractPostMethodTest;
@@ -14,4 +18,13 @@ class PanacheEntityResourcePostMethodTest extends AbstractPostMethodTest {
                             Item.class, ItemsResource.class)
                     .addAsResource("application.properties")
                     .addAsResource("import.sql"));
+
+    @Test
+    void shouldCopyAdditionalMethodsAsResources() {
+        given().accept("application/json")
+                .when().post("/collections/name/mycollection")
+                .then().statusCode(200)
+                .and().body("id", is("mycollection"))
+                .and().body("name", is("mycollection"));
+    }
 }
diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/CollectionsResource.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/CollectionsResource.java
index e24f791173789..137642ce48a84 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/CollectionsResource.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/CollectionsResource.java
@@ -1,5 +1,12 @@
 package io.quarkus.hibernate.reactive.rest.data.panache.deployment.repository;
 
+import java.util.Collections;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+
 import io.quarkus.hibernate.reactive.rest.data.panache.PanacheRepositoryResource;
 import io.quarkus.rest.data.panache.MethodProperties;
 import io.quarkus.rest.data.panache.ResourceProperties;
@@ -10,4 +17,19 @@ public interface CollectionsResource extends PanacheRepositoryResource<Collectio
 
     @MethodProperties(rolesAllowed = "admin")
     Uni<Boolean> delete(String name);
+
+    @GET
+    @Path("/name/{name}")
+    default Uni<Collection> findByName(@PathParam("name") String name) {
+        return Collection.find("name = :name", Collections.singletonMap("name", name)).singleResult();
+    }
+
+    @POST
+    @Path("/name/{name}")
+    default Uni<Collection> addByName(@PathParam("name") String name) {
+        Collection collection = new Collection();
+        collection.id = name;
+        collection.name = name;
+        return Collection.persist(collection).onItem().transform(res -> collection);
+    }
 }
diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourceGetMethodTest.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourceGetMethodTest.java
index 1554e52727a68..90932cfc7216a 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourceGetMethodTest.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourceGetMethodTest.java
@@ -1,5 +1,9 @@
 package io.quarkus.hibernate.reactive.rest.data.panache.deployment.repository;
 
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import io.quarkus.hibernate.reactive.rest.data.panache.deployment.AbstractGetMethodTest;
@@ -16,4 +20,13 @@ class PanacheRepositoryResourceGetMethodTest extends AbstractGetMethodTest {
                             EmptyListItemsResource.class)
                     .addAsResource("application.properties")
                     .addAsResource("import.sql"));
+
+    @Test
+    void shouldCopyAdditionalMethodsAsResources() {
+        given().accept("application/json")
+                .when().get("/collections/name/full collection")
+                .then().statusCode(200)
+                .and().body("id", is("full"))
+                .and().body("name", is("full collection"));
+    }
 }
diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourcePostMethodTest.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourcePostMethodTest.java
index 4835a1080e3cc..a4e39ce66abc3 100644
--- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourcePostMethodTest.java
+++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourcePostMethodTest.java
@@ -1,5 +1,9 @@
 package io.quarkus.hibernate.reactive.rest.data.panache.deployment.repository;
 
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import io.quarkus.hibernate.reactive.rest.data.panache.deployment.AbstractPostMethodTest;
@@ -15,4 +19,13 @@ class PanacheRepositoryResourcePostMethodTest extends AbstractPostMethodTest {
                             ItemsRepository.class)
                     .addAsResource("application.properties")
                     .addAsResource("import.sql"));
+
+    @Test
+    void shouldCopyAdditionalMethodsAsResources() {
+        given().accept("application/json")
+                .when().post("/collections/name/mycollection")
+                .then().statusCode(200)
+                .and().body("id", is("mycollection"))
+                .and().body("name", is("mycollection"));
+    }
 }
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 2a94eb832dabb..eb31ccdaccef8 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
@@ -37,6 +37,7 @@ class JaxRsResourceImplementor {
 
     private static final Logger LOGGER = Logger.getLogger(JaxRsResourceImplementor.class);
     private static final String OPENAPI_TAG_ANNOTATION = "org.eclipse.microprofile.openapi.annotations.tags.Tag";
+    private static final String WITH_SESSION_ON_DEMAND_ANNOTATION = "io.quarkus.hibernate.reactive.panache.common.WithSessionOnDemand";
 
     private final List<MethodImplementor> methodImplementors;
 
@@ -107,6 +108,9 @@ private void implementClassAnnotations(ClassCreator classCreator, ResourceMetada
                 classCreator.addAnnotation(classAnnotation);
             }
         }
+        if (capabilities.isPresent(Capability.HIBERNATE_REACTIVE)) {
+            classCreator.addAnnotation(WITH_SESSION_ON_DEMAND_ANNOTATION);
+        }
     }
 
     private FieldDescriptor implementResourceField(ClassCreator classCreator, ResourceMetadata resourceMetadata) {