From dd73fa606b049437eb8db288a77a390073983ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Vajner?= Date: Wed, 30 Dec 2020 17:22:01 +0100 Subject: [PATCH 1/8] fix(#495): character encoding issue with Playground --- .../src/main/resources/templates/playground.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground-spring-boot-autoconfigure/src/main/resources/templates/playground.html b/playground-spring-boot-autoconfigure/src/main/resources/templates/playground.html index be52603b..b7586ce9 100644 --- a/playground-spring-boot-autoconfigure/src/main/resources/templates/playground.html +++ b/playground-spring-boot-autoconfigure/src/main/resources/templates/playground.html @@ -2,7 +2,7 @@ - + From 46066b5bb9e1e881eec5b816120c1bb358d2b262 Mon Sep 17 00:00:00 2001 From: devopsix <58369032+devopsix@users.noreply.github.com> Date: Sat, 2 Jan 2021 15:07:38 +0100 Subject: [PATCH 2/8] Add a configuration property for ignoring abstract classes when detecting GraphQL interface implementations. --- .../GraphQLAnnotationsAutoConfiguration.java | 8 ++- .../GraphQLAnnotationsProperties.java | 7 ++ ...eAbstractInterfaceImplementationsTest.java | 64 +++++++++++++++++++ .../GraphQLInterfaceQueryTest.java | 23 +++++++ .../test/interfaces/AbstractVehicle.java | 22 +++++++ .../annotations/test/interfaces/Car.java | 22 ++++--- .../annotations/test/interfaces/Truck.java | 2 + 7 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/GraphQLInterfaceQueryIgnoreAbstractInterfaceImplementationsTest.java create mode 100644 graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/AbstractVehicle.java diff --git a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/main/java/graphql/kickstart/graphql/annotations/GraphQLAnnotationsAutoConfiguration.java b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/main/java/graphql/kickstart/graphql/annotations/GraphQLAnnotationsAutoConfiguration.java index 02a9e059..61fe0dfe 100644 --- a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/main/java/graphql/kickstart/graphql/annotations/GraphQLAnnotationsAutoConfiguration.java +++ b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/main/java/graphql/kickstart/graphql/annotations/GraphQLAnnotationsAutoConfiguration.java @@ -17,10 +17,12 @@ import graphql.schema.GraphQLSchema; import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.reflections.Reflections; @@ -179,11 +181,15 @@ private void registerGraphQLInterfaceImplementations( final Reflections reflections, final AnnotationsSchemaCreator.Builder builder ) { + Predicate> implementationQualifiesForInclusion = + type -> !(graphQLAnnotationsProperties.isIgnoreAbstractInterfaceImplementations() + && Modifier.isAbstract(type.getModifiers())); reflections.getMethodsAnnotatedWith(GraphQLField.class).stream() .map(Method::getDeclaringClass) .filter(Class::isInterface) .forEach(graphQLInterface -> - reflections.getSubTypesOf(graphQLInterface) + reflections.getSubTypesOf(graphQLInterface).stream() + .filter(implementationQualifiesForInclusion) .forEach(implementation -> { log.info("Registering {} as an implementation of GraphQL interface {}", implementation, diff --git a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/main/java/graphql/kickstart/graphql/annotations/GraphQLAnnotationsProperties.java b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/main/java/graphql/kickstart/graphql/annotations/GraphQLAnnotationsProperties.java index 6460c057..36c4fc25 100644 --- a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/main/java/graphql/kickstart/graphql/annotations/GraphQLAnnotationsProperties.java +++ b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/main/java/graphql/kickstart/graphql/annotations/GraphQLAnnotationsProperties.java @@ -27,4 +27,11 @@ public class GraphQLAnnotationsProperties { */ @Builder.Default private boolean alwaysPrettify = true; + + /** + * If set to true abstract classes implementing a GraphQL interface will not be added to the schema. + * Defaults to false for backward compatibility. + */ + @Builder.Default + private boolean ignoreAbstractInterfaceImplementations = false; } diff --git a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/GraphQLInterfaceQueryIgnoreAbstractInterfaceImplementationsTest.java b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/GraphQLInterfaceQueryIgnoreAbstractInterfaceImplementationsTest.java new file mode 100644 index 00000000..2c24b6bf --- /dev/null +++ b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/GraphQLInterfaceQueryIgnoreAbstractInterfaceImplementationsTest.java @@ -0,0 +1,64 @@ +package graphql.kickstart.graphql.annotations; + +import com.graphql.spring.boot.test.GraphQLResponse; +import com.graphql.spring.boot.test.GraphQLTestTemplate; +import graphql.kickstart.graphql.annotations.test.interfaces.Car; +import graphql.kickstart.graphql.annotations.test.interfaces.Truck; +import graphql.schema.GraphQLNamedType; +import graphql.schema.GraphQLScalarType; +import graphql.schema.GraphQLSchema; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.io.IOException; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Testing interface handling (ignore abstract implementations).") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = "graphql.annotations.ignore-abstract-interface-implementations=true") +@ActiveProfiles({"test", "interface-test"}) +class GraphQLInterfaceQueryIgnoreAbstractInterfaceImplementationsTest { + + @Autowired + private GraphQLTestTemplate graphQLTestTemplate; + + @Autowired + private GraphQLSchema graphQLSchema; + + @Test + @DisplayName("Assert that GraphQL interfaces and their implementations are registered correctly.") + void testInterfaceQuery() throws IOException { + // WHEN + final GraphQLResponse actual = graphQLTestTemplate + .postForResource("queries/test-interface-query.graphql"); + // THEN + assertThat(actual.get("$.data.vehicles[0]", Car.class)) + .usingRecursiveComparison().ignoringAllOverriddenEquals() + .isEqualTo(Car.builder().numberOfSeats(4).registrationNumber("ABC-123").build()); + assertThat(actual.get("$.data.vehicles[1]", Truck.class)) + .usingRecursiveComparison().ignoringAllOverriddenEquals() + .isEqualTo(Truck.builder().cargoWeightCapacity(12).registrationNumber("CBA-321").build()); + } + + @Test + @DisplayName("Assert that abstract GraphQL interface implementations are excluded from the schema.") + void testInterfaceImplementationDetection() { + // THEN + Set vehicleDomainTypes = graphQLSchema.getAllTypesAsList().stream() + .filter(type -> !(type instanceof GraphQLScalarType)) + .map(GraphQLNamedType::getName) + .filter(name -> !name.startsWith("__")) + .filter(name -> !"PageInfo".equals(name)) + .collect(Collectors.toSet()); + // Must not contain "AbstractVehicle" + assertThat(vehicleDomainTypes) + .containsExactlyInAnyOrder("InterfaceQuery", "Vehicle", "Car", "Truck"); + } +} + diff --git a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/GraphQLInterfaceQueryTest.java b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/GraphQLInterfaceQueryTest.java index e7aaf680..9ed3039b 100644 --- a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/GraphQLInterfaceQueryTest.java +++ b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/GraphQLInterfaceQueryTest.java @@ -7,6 +7,11 @@ import graphql.kickstart.graphql.annotations.test.interfaces.Car; import graphql.kickstart.graphql.annotations.test.interfaces.Truck; import java.io.IOException; +import java.util.Set; +import java.util.stream.Collectors; +import graphql.schema.GraphQLNamedType; +import graphql.schema.GraphQLScalarType; +import graphql.schema.GraphQLSchema; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -21,6 +26,9 @@ class GraphQLInterfaceQueryTest { @Autowired private GraphQLTestTemplate graphQLTestTemplate; + @Autowired + private GraphQLSchema graphQLSchema; + @Test @DisplayName("Assert that GraphQL interfaces and their implementations are registered correctly.") void testInterfaceQuery() throws IOException { @@ -35,5 +43,20 @@ void testInterfaceQuery() throws IOException { .usingRecursiveComparison().ignoringAllOverriddenEquals() .isEqualTo(Truck.builder().cargoWeightCapacity(12).registrationNumber("CBA-321").build()); } + + @Test + @DisplayName("Assert that abstract GraphQL interface implementations are added to the schema.") + void testInterfaceImplementationDetection() { + // THEN + Set vehicleDomainTypes = graphQLSchema.getAllTypesAsList().stream() + .filter(type -> !(type instanceof GraphQLScalarType)) + .map(GraphQLNamedType::getName) + .filter(name -> !name.startsWith("__")) + .filter(name -> !"PageInfo".equals(name)) + .collect(Collectors.toSet()); + // Should contain "AbstractVehicle" + assertThat(vehicleDomainTypes) + .containsExactlyInAnyOrder("InterfaceQuery", "Vehicle", "AbstractVehicle", "Car", "Truck"); + } } diff --git a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/AbstractVehicle.java b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/AbstractVehicle.java new file mode 100644 index 00000000..dad3a501 --- /dev/null +++ b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/AbstractVehicle.java @@ -0,0 +1,22 @@ +package graphql.kickstart.graphql.annotations.test.interfaces; + +import graphql.annotations.annotationTypes.GraphQLField; +import graphql.annotations.annotationTypes.GraphQLNonNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +public abstract class AbstractVehicle implements Vehicle { + + /** + * Note that you have to repeat the annotations from the interface method! + */ + @GraphQLField + @GraphQLNonNull + private String registrationNumber; +} diff --git a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/Car.java b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/Car.java index 0f684229..5c836a5c 100644 --- a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/Car.java +++ b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/Car.java @@ -3,24 +3,26 @@ import graphql.annotations.annotationTypes.GraphQLField; import graphql.annotations.annotationTypes.GraphQLNonNull; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; @Data -@Builder +@SuperBuilder @AllArgsConstructor @NoArgsConstructor -public class Car implements Vehicle { - - /** - * Note that you have to repeat the annotations from the interface method! - */ - @GraphQLField - @GraphQLNonNull - private String registrationNumber; +@EqualsAndHashCode(callSuper = true) +// “implements Vehicle” has to be repeated here although already inherited from AbstractVehicle +// because otherwise GraphQL-Java Annotations would not find this class. +public class Car extends AbstractVehicle implements Vehicle { @GraphQLField @GraphQLNonNull private int numberOfSeats; + + public Car(String registrationNumber, int numberOfSeats) { + super(registrationNumber); + this.numberOfSeats = numberOfSeats; + } } diff --git a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/Truck.java b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/Truck.java index 9bc43003..f1f7fbf8 100644 --- a/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/Truck.java +++ b/graphql-kickstart-spring-boot-autoconfigure-graphql-annotations/src/test/java/graphql/kickstart/graphql/annotations/test/interfaces/Truck.java @@ -11,6 +11,8 @@ @Builder @NoArgsConstructor @AllArgsConstructor +// Truck intentionally does not extend AbstractVehicle in order to have one inheritance +// hierarchy free from abstract classes. public class Truck implements Vehicle { /** From 5113cf38c818a180ddc9f083e3017bfbb58397df Mon Sep 17 00:00:00 2001 From: devopsix <58369032+devopsix@users.noreply.github.com> Date: Fri, 1 Jan 2021 21:01:18 +0100 Subject: [PATCH 3/8] Describe use of graphql-kickstart-spring-boot-starter-graphql-annotations dependency. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8c0913d2..f48ed9ee 100644 --- a/README.md +++ b/README.md @@ -473,6 +473,9 @@ the classpath. Use the `schemaLocationPattern` property to customize this patter https://github.com/Enigmatis/graphql-java-annotations +The GraphQL Annotations library is used instead of GraphQL Java Tools if the `graphql-spring-boot-starter` +dependency is replaced by `graphql-kickstart-spring-boot-starter-graphql-annotations`. + The schema will be built using the GraphQL Annotations library in a code-first approach - instead of writing it manually, the schema will be constructed based on the Java code. Please see the documentation of the GraphQL Annotations library for a detailed documentation of the available From 0ad60dc3927c0185c4467613798a7f998104e9f4 Mon Sep 17 00:00:00 2001 From: oliemansm Date: Mon, 4 Jan 2021 15:24:17 +0100 Subject: [PATCH 4/8] Fix failing build issue in PR on JDK15 --- build.gradle | 4 ++++ .../graphiql/boot/GraphiQLProperties.java | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 68e22a84..d115776d 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,10 @@ sonarqube { } } +jacoco { + toolVersion = "0.8.7.202012290703" +} + subprojects { apply plugin: 'idea' apply plugin: 'jacoco' diff --git a/graphiql-spring-boot-autoconfigure/src/main/java/graphql/kickstart/graphiql/boot/GraphiQLProperties.java b/graphiql-spring-boot-autoconfigure/src/main/java/graphql/kickstart/graphiql/boot/GraphiQLProperties.java index 58f16e68..85b69d7a 100644 --- a/graphiql-spring-boot-autoconfigure/src/main/java/graphql/kickstart/graphiql/boot/GraphiQLProperties.java +++ b/graphiql-spring-boot-autoconfigure/src/main/java/graphql/kickstart/graphiql/boot/GraphiQLProperties.java @@ -32,12 +32,27 @@ static class CodeMirror { @Data static class Props { - private Variables variables = new Variables(); + private GraphiQLVariables variables = new GraphiQLVariables(); + /** + * See https://github.com/graphql/graphiql/tree/main/packages/graphiql#props + */ @Data - static class Variables { + static class GraphiQLVariables { + private String query; + private String variables; + private String headers; + private String operationName; + private String response; + private String defaultQuery; + private boolean defaultVariableEditorOpen; + private boolean defaultSecondaryEditorOpen; private String editorTheme; + private boolean readOnly; + private boolean docsExplorerOpen; + private boolean headerEditorEnabled; + private boolean shouldPersistHeaders; } } From 980473db2cdc904c38aee0f6bc258a94b4636478 Mon Sep 17 00:00:00 2001 From: oliemansm Date: Mon, 4 Jan 2021 15:31:27 +0100 Subject: [PATCH 5/8] Fix failing build issue in PR on JDK15 --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index d115776d..de10701d 100644 --- a/build.gradle +++ b/build.gradle @@ -37,10 +37,6 @@ sonarqube { } } -jacoco { - toolVersion = "0.8.7.202012290703" -} - subprojects { apply plugin: 'idea' apply plugin: 'jacoco' @@ -92,6 +88,10 @@ subprojects { } } + jacoco { + toolVersion = "0.8.7.202012290703" + } + jacocoTestReport { reports { xml.enabled = true From 621c71e8b1f9214e11a6e1c8f571f981877ad92d Mon Sep 17 00:00:00 2001 From: oliemansm Date: Mon, 4 Jan 2021 15:39:49 +0100 Subject: [PATCH 6/8] Snapshot repository for jacoco --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index de10701d..eaca3afd 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,7 @@ subprojects { maven { url "https://dl.bintray.com/graphql-java-kickstart/releases" } maven { url "https://repo.spring.io/libs-milestone" } maven { url "https://oss.jfrog.org/artifactory/oss-snapshot-local" } + maven { url "https://oss.sonatype.org/content/repositories/snapshots" } } dependencyManagement { From 698ed3953ed32fa1023ee9b054f1c79b5054ef90 Mon Sep 17 00:00:00 2001 From: oliemansm Date: Mon, 4 Jan 2021 15:42:57 +0100 Subject: [PATCH 7/8] Snapshot repository for jacoco --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index eaca3afd..48d5d3f0 100644 --- a/build.gradle +++ b/build.gradle @@ -56,8 +56,8 @@ subprojects { jcenter() maven { url "https://dl.bintray.com/graphql-java-kickstart/releases" } maven { url "https://repo.spring.io/libs-milestone" } - maven { url "https://oss.jfrog.org/artifactory/oss-snapshot-local" } maven { url "https://oss.sonatype.org/content/repositories/snapshots" } + maven { url "https://oss.jfrog.org/artifactory/oss-snapshot-local" } } dependencyManagement { From 68359c2f1222f8255123bd7e5196f4af9bdebd08 Mon Sep 17 00:00:00 2001 From: oliemansm Date: Mon, 4 Jan 2021 15:46:25 +0100 Subject: [PATCH 8/8] Snapshot repository for jacoco --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 48d5d3f0..128bd07a 100644 --- a/build.gradle +++ b/build.gradle @@ -90,7 +90,7 @@ subprojects { } jacoco { - toolVersion = "0.8.7.202012290703" + toolVersion = "0.8.7-SNAPSHOT" } jacocoTestReport {