From 542684a632d2fd50e2dcb4b00371bc2fc4c845a4 Mon Sep 17 00:00:00 2001 From: Isaac Mercieca Date: Thu, 25 May 2023 16:31:42 +0200 Subject: [PATCH 1/3] fixed bug with nested embeddable --- .../schema/impl/GraphQLJpaSchemaBuilder.java | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java b/schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java index 9bbf5d21a..12c7ea758 100644 --- a/schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java +++ b/schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java @@ -968,6 +968,7 @@ private GraphQLType getEmbeddableType(EmbeddableType embeddableType, boolean if (embeddableInputCache.containsKey(embeddableType.getJavaType())) { return embeddableInputCache.get(embeddableType.getJavaType()); } + graphQLType = GraphQLInputObjectType .newInputObject() @@ -981,10 +982,31 @@ private GraphQLType getEmbeddableType(EmbeddableType embeddableType, boolean .getAttributes() .stream() .filter(this::isNotIgnored) - .map(this::getInputObjectField) + .map(attribute -> { + if (isEmbeddable(attribute)) { + EmbeddableType nestedEmbeddableType = (EmbeddableType) ( + (SingularAttribute) attribute + ).getType(); + return GraphQLInputObjectField + .newInputObjectField() + .name(attribute.getName()) + .description(getSchemaDescription(nestedEmbeddableType)) + .type( + (GraphQLInputType) getEmbeddableType( + nestedEmbeddableType, + input, + searchByIdArg + ) + ) + .build(); + } else { + return getInputObjectField(attribute); + } + }) .collect(Collectors.toList()) ) .build(); + embeddableInputCache.put(embeddableType.getJavaType(), (GraphQLInputObjectType) graphQLType); return graphQLType; } @@ -994,8 +1016,10 @@ private GraphQLType getEmbeddableType(EmbeddableType embeddableType, boolean if (embeddableOutputCache.containsKey(embeddableType.getJavaType())) { return embeddableOutputCache.get(embeddableType.getJavaType()); } + String embeddableTypeName = namingStrategy.singularize(embeddableType.getJavaType().getSimpleName()) + "EmbeddableType"; + graphQLType = GraphQLObjectType .newObject() @@ -1006,12 +1030,35 @@ private GraphQLType getEmbeddableType(EmbeddableType embeddableType, boolean .getAttributes() .stream() .filter(this::isNotIgnored) - .map(this::getObjectField) + .map(attribute -> { + if (isEmbeddable(attribute)) { + EmbeddableType nestedEmbeddableType = (EmbeddableType) ( + (SingularAttribute) attribute + ).getType(); + + return GraphQLFieldDefinition + .newFieldDefinition() + .name(attribute.getName()) + .description(getSchemaDescription(nestedEmbeddableType)) + .type( + (GraphQLOutputType) getEmbeddableType( + nestedEmbeddableType, + input, + searchByIdArg + ) + ) + .build(); + } else { + return getObjectField(attribute); + } + }) .collect(Collectors.toList()) ) .build(); + embeddableOutputCache.putIfAbsent(embeddableType.getJavaType(), (GraphQLObjectType) graphQLType); } + return graphQLType; } From 1bdaded684537d5450294d70813445defac722f7 Mon Sep 17 00:00:00 2001 From: Isaac Mercieca Date: Fri, 26 May 2023 09:21:56 +0200 Subject: [PATCH 2/3] updated `getEmbeddableType` so that the description for the nested embeddable --- .../jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java b/schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java index 12c7ea758..7794488ea 100644 --- a/schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java +++ b/schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaSchemaBuilder.java @@ -990,7 +990,7 @@ private GraphQLType getEmbeddableType(EmbeddableType embeddableType, boolean return GraphQLInputObjectField .newInputObjectField() .name(attribute.getName()) - .description(getSchemaDescription(nestedEmbeddableType)) + .description(getSchemaDescription(attribute)) .type( (GraphQLInputType) getEmbeddableType( nestedEmbeddableType, @@ -1039,7 +1039,7 @@ private GraphQLType getEmbeddableType(EmbeddableType embeddableType, boolean return GraphQLFieldDefinition .newFieldDefinition() .name(attribute.getName()) - .description(getSchemaDescription(nestedEmbeddableType)) + .description(getSchemaDescription(attribute)) .type( (GraphQLOutputType) getEmbeddableType( nestedEmbeddableType, From 49a98c222fe346ae674e0633ac8ef8cfc6abaf96 Mon Sep 17 00:00:00 2001 From: Isaac Mercieca Date: Thu, 8 Jun 2023 12:22:27 +0200 Subject: [PATCH 3/3] added a test under `BooksSchemaBuildTest` to assert schema support for a nested embeddable --- .../query/schema/BooksSchemaBuildTest.java | 26 +++++++++++++++-- .../jpa/query/schema/model/book/Address.java | 28 +++++++++++++++++++ .../jpa/query/schema/model/book/Author.java | 6 ++++ .../jpa/query/schema/model/book/Zipcode.java | 18 ++++++++++++ 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Address.java create mode 100644 tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Zipcode.java diff --git a/schema/src/test/java/com/introproventures/graphql/jpa/query/schema/BooksSchemaBuildTest.java b/schema/src/test/java/com/introproventures/graphql/jpa/query/schema/BooksSchemaBuildTest.java index d0957c6fa..b6d074ec0 100644 --- a/schema/src/test/java/com/introproventures/graphql/jpa/query/schema/BooksSchemaBuildTest.java +++ b/schema/src/test/java/com/introproventures/graphql/jpa/query/schema/BooksSchemaBuildTest.java @@ -22,6 +22,7 @@ import com.introproventures.graphql.jpa.query.AbstractSpringBootTestSupport; import com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaSchemaBuilder; +import com.introproventures.graphql.jpa.query.schema.model.book.Address; import com.introproventures.graphql.jpa.query.schema.model.book.Author; import com.introproventures.graphql.jpa.query.schema.model.book.Book; import com.introproventures.graphql.jpa.query.schema.model.book_superclass.SuperAuthor; @@ -154,6 +155,23 @@ public void correctlyDerivesToOneOptionalFromGivenEntities() { .isEqualTo(Boolean.TRUE); } + @Test + public void correctlyDerivesNestedEmbeddableFromGivenEntities() { + //when + GraphQLSchema schema = builder.build(); + + // then + assertThat(schema).describedAs("Ensure the schema is generated").isNotNull(); + + //then + assertThat(getFieldForType(Author.Fields.homeAddress, "Author", schema)) + .isPresent() + .get() + .extracting(it -> getField(Address.Fields.zipcode, it).isPresent()) + .describedAs("Ensure that a nested embeddable can be queried on") + .isEqualTo(Boolean.TRUE); + } + @Test public void shouldBuildSchemaWithStringArrayAsStringListType() { //given @@ -193,9 +211,11 @@ public void testBuildSchema() { } private Optional getFieldForType(String fieldName, String type, GraphQLSchema schema) { - return schema - .getQueryType() - .getFieldDefinition(type) + return getField(fieldName, schema.getQueryType().getFieldDefinition(type)); + } + + private Optional getField(String fieldName, GraphQLFieldDefinition fieldDefinition) { + return fieldDefinition .getType() .getChildren() .stream() diff --git a/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Address.java b/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Address.java new file mode 100644 index 000000000..04524a943 --- /dev/null +++ b/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Address.java @@ -0,0 +1,28 @@ +package com.introproventures.graphql.jpa.query.schema.model.book; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import java.io.Serializable; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.FieldNameConstants; + +@Getter +@Setter +@ToString +@FieldNameConstants +@Embeddable +public class Address implements Serializable { + + String street; + + String state; + + String country; + + String city; + + @Embedded + Zipcode zipcode; +} diff --git a/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Author.java b/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Author.java index 1145150df..f0a2dbd70 100644 --- a/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Author.java +++ b/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Author.java @@ -19,6 +19,7 @@ import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; +import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -33,11 +34,13 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import lombok.experimental.FieldNameConstants; @Entity @Getter @Setter @ToString +@FieldNameConstants @EqualsAndHashCode(exclude = { "books", "phoneNumbers" }) // Fixes NPE in Hibernate when initializing loaded collections #1 public class Author { @@ -57,4 +60,7 @@ public class Author { @Enumerated(EnumType.STRING) Genre genre; + + @Embedded + Address homeAddress; } diff --git a/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Zipcode.java b/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Zipcode.java new file mode 100644 index 000000000..545ce0962 --- /dev/null +++ b/tests/models/books/src/main/java/com/introproventures/graphql/jpa/query/schema/model/book/Zipcode.java @@ -0,0 +1,18 @@ +package com.introproventures.graphql.jpa.query.schema.model.book; + +import jakarta.persistence.Embeddable; +import java.io.Serializable; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +@Embeddable +public class Zipcode implements Serializable { + + String mainCode; + + String codeSuffix; +}