Skip to content

Commit

Permalink
Fix user methods requiring a session in Panache REST Data with Reactive
Browse files Browse the repository at this point in the history
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 #34432
  • Loading branch information
Sgitario committed Jul 7, 2023
1 parent 86d5083 commit 72a264c
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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();
}
Expand All @@ -132,15 +130,13 @@ 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();
}

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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 72a264c

Please sign in to comment.