Skip to content

Commit

Permalink
Merge pull request #27193 from Sgitario/panache_total
Browse files Browse the repository at this point in the history
Panache Rest Data: Generate "/count" endpoint
  • Loading branch information
geoand committed Aug 9, 2022
2 parents a0daaf8 + 54e3d7d commit 17240b6
Show file tree
Hide file tree
Showing 19 changed files with 312 additions and 0 deletions.
6 changes: 6 additions & 0 deletions docs/src/main/asciidoc/rest-data-panache.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,12 @@ public class PeopleResourceJaxRs { // The actual class name is going to be uniqu
// ... build a response with page links and return a 200 response with a list
}
@GET
@Path("/count")
public long count() {
return resource.count();
}
@Transactional
@POST
@Consumes("application/json")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,12 @@ public interface DataAccessImplementor {
* @return int page count.
*/
ResultHandle pageCount(BytecodeCreator creator, ResultHandle page);

/**
* return the total number of entities.
*
* @param creator Bytecode creator that should be used for implementation.
* @return long entities count.
*/
ResultHandle count(BytecodeCreator creator);
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,12 @@ public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) {
page);
return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), query);
}

/**
* Implements <code>Entity.count()</code>
*/
@Override
public ResultHandle count(BytecodeCreator creator) {
return creator.invokeStaticMethod(ofMethod(entityClassName, "count", long.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) {
return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), query);
}

/**
* Implements <code>repository.count()</code>
*/
@Override
public ResultHandle count(BytecodeCreator creator) {
return creator.invokeInterfaceMethod(ofMethod(PanacheRepositoryBase.class, "count", long.class),
getRepositoryInstance(creator));
}

/**
* Implements getting repository from Arc container.
* <code>Arc.container().instance(Repository.class, new Annotation[0]).get()</code>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ String implement(ClassOutput classOutput, DataAccessImplementor dataAccessImplem
classCreator.addAnnotation(ApplicationScoped.class);
implementList(classCreator, dataAccessImplementor);
implementListPageCount(classCreator, dataAccessImplementor);
implementCount(classCreator, dataAccessImplementor);
implementGet(classCreator, dataAccessImplementor);
implementAdd(classCreator, dataAccessImplementor);
implementUpdate(classCreator, dataAccessImplementor, entityType);
Expand Down Expand Up @@ -90,6 +91,15 @@ private void implementListPageCount(ClassCreator classCreator, DataAccessImpleme
methodCreator.close();
}

/**
* Generate count method.
*/
private void implementCount(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) {
MethodCreator methodCreator = classCreator.getMethodCreator("count", long.class);
methodCreator.returnValue(dataAccessImplementor.count(methodCreator));
methodCreator.close();
}

private void implementGet(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) {
MethodCreator methodCreator = classCreator.getMethodCreator("get", Object.class, Object.class);
ResultHandle id = methodCreator.getMethodParam(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;

public abstract class AbstractCountMethodTest {

@Test
void shouldGetTotalNumberOfEntities() {
given().get("/collections/count")
.then().statusCode(HttpStatus.SC_OK)
.and().body(is(equalTo("2")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment.entity;

import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.hibernate.orm.rest.data.panache.deployment.AbstractCountMethodTest;
import io.quarkus.test.QuarkusUnitTest;

class PanacheEntityResourceCountMethodTest extends AbstractCountMethodTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Collection.class, CollectionsResource.class, AbstractEntity.class, AbstractItem.class,
Item.class, ItemsResource.class,
EmptyListItem.class, EmptyListItemsResource.class)
.addAsResource("application.properties")
.addAsResource("import.sql"));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment.repository;

import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.hibernate.orm.rest.data.panache.deployment.AbstractCountMethodTest;
import io.quarkus.test.QuarkusUnitTest;

class PanacheRepositoryResourceCountMethodTest extends AbstractCountMethodTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Collection.class, CollectionsResource.class, CollectionsRepository.class,
AbstractEntity.class, AbstractItem.class, Item.class, ItemsResource.class,
ItemsRepository.class, EmptyListItem.class, EmptyListItemsRepository.class,
EmptyListItemsResource.class)
.addAsResource("application.properties")
.addAsResource("import.sql"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,12 @@ public interface DataAccessImplementor {
* @return int page count.
*/
ResultHandle pageCount(BytecodeCreator creator, ResultHandle page);

/**
* return the total number of entities.
*
* @param creator Bytecode creator that should be used for implementation.
* @return long entities count.
*/
ResultHandle count(BytecodeCreator creator);
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,12 @@ public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) {
page);
return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", Uni.class), query);
}

/**
* Implements <code>Entity.count()</code>
*/
@Override
public ResultHandle count(BytecodeCreator creator) {
return creator.invokeStaticMethod(ofMethod(entityClassName, "count", Uni.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) {
return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", Uni.class), query);
}

/**
* Implements <code>repository.count()</code>
*/
@Override
public ResultHandle count(BytecodeCreator creator) {
return creator.invokeInterfaceMethod(ofMethod(PanacheRepositoryBase.class, "count", Uni.class),
getRepositoryInstance(creator));
}

/**
* Implements getting repository from Arc container.
* <code>Arc.container().instance(Repository.class, new Annotation[0]).get()</code>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ String implement(ClassOutput classOutput, DataAccessImplementor dataAccessImplem

classCreator.addAnnotation(ApplicationScoped.class);
implementList(classCreator, dataAccessImplementor);
implementCount(classCreator, dataAccessImplementor);
implementListPageCount(classCreator, dataAccessImplementor);
implementGet(classCreator, dataAccessImplementor);
implementAdd(classCreator, dataAccessImplementor);
Expand All @@ -79,6 +80,15 @@ private void implementList(ClassCreator classCreator, DataAccessImplementor data
methodCreator.close();
}

/**
* Generate count method.
*/
private void implementCount(ClassCreator classCreator, DataAccessImplementor dataAccessImplementor) {
MethodCreator methodCreator = classCreator.getMethodCreator("count", Uni.class);
methodCreator.returnValue(dataAccessImplementor.count(methodCreator));
methodCreator.close();
}

/**
* Generate list page count method.
* This method is used when building page URLs for list operation response and is not exposed to a user.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.hibernate.reactive.rest.data.panache.deployment;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;

public abstract class AbstractCountMethodTest {

@Test
void shouldGetTotalNumberOfEntities() {
given().get("/collections/count")
.then().statusCode(HttpStatus.SC_OK)
.and().body(is(equalTo("2")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.hibernate.reactive.rest.data.panache.deployment.entity;

import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.hibernate.reactive.rest.data.panache.deployment.AbstractCountMethodTest;
import io.quarkus.test.QuarkusUnitTest;

class PanacheEntityResourceCountMethodTest extends AbstractCountMethodTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Collection.class, CollectionsResource.class, AbstractEntity.class, AbstractItem.class,
Item.class, ItemsResource.class,
EmptyListItem.class, EmptyListItemsResource.class)
.addAsResource("application.properties")
.addAsResource("import.sql"));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.quarkus.hibernate.reactive.rest.data.panache.deployment.repository;

import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.hibernate.reactive.rest.data.panache.deployment.AbstractCountMethodTest;
import io.quarkus.test.QuarkusUnitTest;

class PanacheRepositoryResourceCountMethodTest extends AbstractCountMethodTest {

@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Collection.class, CollectionsResource.class, CollectionsRepository.class,
AbstractEntity.class, AbstractItem.class, Item.class, ItemsResource.class,
ItemsRepository.class, EmptyListItem.class, EmptyListItemsRepository.class,
EmptyListItemsResource.class)
.addAsResource("application.properties")
.addAsResource("import.sql"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.quarkus.gizmo.FieldCreator;
import io.quarkus.gizmo.FieldDescriptor;
import io.quarkus.rest.data.panache.deployment.methods.AddMethodImplementor;
import io.quarkus.rest.data.panache.deployment.methods.CountMethodImplementor;
import io.quarkus.rest.data.panache.deployment.methods.DeleteMethodImplementor;
import io.quarkus.rest.data.panache.deployment.methods.GetMethodImplementor;
import io.quarkus.rest.data.panache.deployment.methods.ListMethodImplementor;
Expand All @@ -39,6 +40,7 @@ class JaxRsResourceImplementor {
JaxRsResourceImplementor(boolean withValidation, boolean isResteasyClassic, boolean isReactivePanache) {
this.methodImplementors = Arrays.asList(new GetMethodImplementor(isResteasyClassic, isReactivePanache),
new ListMethodImplementor(isResteasyClassic, isReactivePanache),
new CountMethodImplementor(isResteasyClassic, isReactivePanache),
new AddMethodImplementor(withValidation, isResteasyClassic, isReactivePanache),
new UpdateMethodImplementor(withValidation, isResteasyClassic, isReactivePanache),
new DeleteMethodImplementor(isResteasyClassic, isReactivePanache),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package io.quarkus.rest.data.panache.deployment.methods;

import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
import static io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator.ofType;

import javax.ws.rs.core.Response;

import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.FieldDescriptor;
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.gizmo.TryBlock;
import io.quarkus.rest.data.panache.RestDataResource;
import io.quarkus.rest.data.panache.deployment.ResourceMetadata;
import io.quarkus.rest.data.panache.deployment.properties.ResourceProperties;
import io.quarkus.rest.data.panache.deployment.utils.SignatureMethodCreator;
import io.quarkus.rest.data.panache.deployment.utils.UniImplementor;
import io.smallrye.mutiny.Uni;

public final class CountMethodImplementor extends StandardMethodImplementor {

private static final String METHOD_NAME = "count";

private static final String RESOURCE_METHOD_NAME = "count";

private static final String EXCEPTION_MESSAGE = "Failed to count the entities";

private static final String REL = "count";

public CountMethodImplementor(boolean isResteasyClassic, boolean isReactivePanache) {
super(isResteasyClassic, isReactivePanache);
}

/**
* Generate JAX-RS GET method.
*
* The RESTEasy Classic version exposes {@link RestDataResource#count()}.
*
* <pre>
* {@code
* &#64;GET
* &#64;Path("/count")
* public long count() {
* try {
* return resource.count();
* } catch (Throwable t) {
* throw new RestDataPanacheException(t);
* }
* }
* }
* </pre>
*
* The RESTEasy Reactive version exposes {@link io.quarkus.rest.data.panache.ReactiveRestDataResource#count()}
* and the generated code looks more or less like this:
*
* <pre>
* {@code
* &#64;GET
* &#64;Path("/count")
* &#64;LinkResource(rel = "count", entityClassName = "com.example.Entity")
* public Uni<Long> count() {
* try {
* return resource.count();
* } catch (Throwable t) {
* throw new RestDataPanacheException(t);
* }
* }
* }
* </pre>
*/
@Override
protected void implementInternal(ClassCreator classCreator, ResourceMetadata resourceMetadata,
ResourceProperties resourceProperties, FieldDescriptor resourceField) {
// Method parameters: sort strings, page index, page size, uri info
MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(RESOURCE_METHOD_NAME, classCreator,
isNotReactivePanache() ? ofType(Response.class) : ofType(Uni.class, Long.class));

// Add method annotations
addGetAnnotation(methodCreator);
addProducesAnnotation(methodCreator, APPLICATION_JSON);
addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), RESOURCE_METHOD_NAME));
if (!isResteasyClassic()) {
// We only add the Links annotation in Resteasy Reactive because Resteasy Classic ignores the REL parameter:
// it always uses "list" for GET methods, so it interferes with the list implementation.
addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL);
}

ResultHandle resource = methodCreator.readInstanceField(resourceField, methodCreator.getThis());

if (isNotReactivePanache()) {
TryBlock tryBlock = implementTryBlock(methodCreator, EXCEPTION_MESSAGE);
ResultHandle count = tryBlock.invokeVirtualMethod(
ofMethod(resourceMetadata.getResourceClass(), METHOD_NAME, long.class),
resource);

// Return response
tryBlock.returnValue(responseImplementor.ok(tryBlock, count));
tryBlock.close();
} else {
ResultHandle uniCount = methodCreator.invokeVirtualMethod(
ofMethod(resourceMetadata.getResourceClass(), METHOD_NAME, Uni.class),
resource);
methodCreator.returnValue(UniImplementor.map(methodCreator, uniCount, EXCEPTION_MESSAGE,
(countBody, count) -> countBody.returnValue(responseImplementor.ok(countBody, count))));
}

methodCreator.close();
}

@Override
protected String getResourceMethodName() {
return RESOURCE_METHOD_NAME;
}
}

0 comments on commit 17240b6

Please sign in to comment.