From bf8e3f5c56a164ac26782adf686a0c3ed3b95c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gero=20M=C3=BCller?= Date: Wed, 10 Jan 2024 08:57:02 +0100 Subject: [PATCH] Support ManyToOne queries in Panache REST resource --- .../PanacheEntityResourceGetMethodTest.java | 17 ++++++++ .../methods/ListMethodImplementor.java | 17 ++++++-- .../deployment/utils/EntityTypeUtils.java | 43 +++++++++++++++++++ .../utils/SignatureMethodCreator.java | 8 ++++ 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/entity/PanacheEntityResourceGetMethodTest.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/entity/PanacheEntityResourceGetMethodTest.java index 55f7197d40a19..8f28973b0a27e 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/entity/PanacheEntityResourceGetMethodTest.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/entity/PanacheEntityResourceGetMethodTest.java @@ -1,6 +1,7 @@ package io.quarkus.hibernate.orm.rest.data.panache.deployment.entity; import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import org.junit.jupiter.api.Test; @@ -29,4 +30,20 @@ void shouldCopyAdditionalMethodsAsResources() { .and().body("name", is("full collection")); } + @Test + void shouldReturnItemsForFullCollection() { + given().accept("application/json") + .when().get("/items?collection.id=full") + .then().statusCode(200) + .body("$", hasSize(2)); + } + + @Test + void shouldReturnNoItemsForEmptyCollection() { + given().accept("application/json") + .when().get("/items?collection.id=empty") + .then().statusCode(200) + .body("$", hasSize(0)); + } + } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java index 1b8ec8077590f..99abbdabdc13e 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java @@ -184,7 +184,11 @@ private void implementPaged(ClassCreator classCreator, ResourceMetadata resource parameters.add(param("size", int.class, intType())); parameters.add(param("uriInfo", UriInfo.class)); parameters.add(param("namedQuery", String.class)); - parameters.addAll(compatibleFieldsForQuery); + for (SignatureMethodCreator.Parameter param : compatibleFieldsForQuery) { + parameters.add(param( + param.getName().replace(".", "__"), + param.getClazz())); + } MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(getMethodName(), classCreator, isNotReactivePanache() ? responseType() : uniType(resourceMetadata.getEntityType()), parameters.toArray(new SignatureMethodCreator.Parameter[0])); @@ -271,7 +275,11 @@ private void implementNotPaged(ClassCreator classCreator, ResourceMetadata resou List parameters = new ArrayList<>(); parameters.add(param("sort", List.class, parameterizedType(classType(List.class), classType(String.class)))); parameters.add(param("namedQuery", String.class)); - parameters.addAll(compatibleFieldsForQuery); + for (SignatureMethodCreator.Parameter param : compatibleFieldsForQuery) { + parameters.add(param( + param.getName().replace(".", "__"), + param.getClazz())); + } MethodCreator methodCreator = SignatureMethodCreator.getMethodCreator(getMethodName(), classCreator, isNotReactivePanache() ? responseType() : uniType(resourceMetadata.getEntityType()), parameters.toArray(new SignatureMethodCreator.Parameter[0])); @@ -321,13 +329,14 @@ public ResultHandle list(BytecodeCreator creator, ResourceMetadata resourceMetad ResultHandle queryList = creator.newInstance(ofConstructor(ArrayList.class)); for (Map.Entry field : fieldValues.entrySet()) { String fieldName = field.getKey(); + String paramName = fieldName.replace(".", "__"); ResultHandle fieldValueFromQuery = field.getValue(); BytecodeCreator fieldValueFromQueryIsSet = creator.ifNotNull(fieldValueFromQuery).trueBranch(); fieldValueFromQueryIsSet.invokeInterfaceMethod(ofMethod(List.class, "add", boolean.class, Object.class), - queryList, fieldValueFromQueryIsSet.load(fieldName + "=:" + fieldName)); + queryList, fieldValueFromQueryIsSet.load(fieldName + "=:" + paramName)); fieldValueFromQueryIsSet.invokeInterfaceMethod( ofMethod(Map.class, "put", Object.class, Object.class, Object.class), - dataParams, fieldValueFromQueryIsSet.load(fieldName), fieldValueFromQuery); + dataParams, fieldValueFromQueryIsSet.load(paramName), fieldValueFromQuery); } /** diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/EntityTypeUtils.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/EntityTypeUtils.java index 17e1dbe5d82b4..cedede3c18971 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/EntityTypeUtils.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/EntityTypeUtils.java @@ -16,6 +16,10 @@ public final class EntityTypeUtils { + // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.5 + public static final int ACC_STATIC = 0x0008; + public static final int ACC_FINAL = 0x0010; + private EntityTypeUtils() { } @@ -25,7 +29,46 @@ public static Map getEntityFields(IndexView index, String entityTy ClassInfo currentEntityClass = index.getClassByName(entityTypeName); while (currentEntityClass != null) { for (FieldInfo field : currentEntityClass.fields()) { + // skip static fields + if ((field.flags() & ACC_STATIC) != 0) { + continue; + } + // skip final fields + if ((field.flags() & ACC_FINAL) != 0) { + continue; + } + // skip fields with Transient annotation + if (field.hasAnnotation(DotName.createSimple("jakarta.persistence.Transient"))) { + continue; + } + fields.put(field.name(), field.type()); + + // if the field is a ManyToOne relation, add the Id field of the relation to the fields map + if (field.type().kind() == Type.Kind.CLASS + && field.hasAnnotation(DotName.createSimple("jakarta.persistence.ManyToOne"))) { + // get the class info for the relation field + ClassInfo currentRelationClass = index.getClassByName(field.type().name()); + while (currentRelationClass != null) { + // get the field with Id annotation + FieldInfo relationIdField = currentRelationClass.fields().stream().filter((relationField) -> { + return relationField.hasAnnotation(DotName.createSimple("jakarta.persistence.Id")); + }).findFirst().orElse(null); + // if the field is not null, add it to the fields map + if (relationIdField != null) { + fields.put(field.name() + "." + relationIdField.name(), relationIdField.type()); + } + + // get the super class of the relation class + if (currentRelationClass.superName() != null) { + currentRelationClass = index.getClassByName(currentRelationClass.superName()); + } else { + currentRelationClass = null; + } + } + + } + } if (currentEntityClass.superName() != null) { diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/SignatureMethodCreator.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/SignatureMethodCreator.java index 20dddea276b35..402dc1a124834 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/SignatureMethodCreator.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/utils/SignatureMethodCreator.java @@ -72,6 +72,14 @@ public static class Parameter { public String getName() { return name; } + + public Type getType() { + return type; + } + + public Object getClazz() { + return clazz; + } } public static class ReturnType {