From a2e669915c97088d79e61b94c41f80dda3db0f2b Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Thu, 7 Mar 2024 09:51:52 -0500 Subject: [PATCH] fix: Ensure reactive context is propagated (#512) DefaultGraphQLInvocation and DefaultGraphQLExecutionInputCustomizer are updated to use Mono and Flux in their internal implementation in order to ensure that the Micronaut PropagationContext is carried appropriately through the invocation flow. Previous use of the bare io.micronaut.core.async.publisher.Publishers API in DefaultGraphQLExecutionInputCustomizer was causing the context not to be propagated as desired when including the Micrometer Context Propagation library. A test is added to verify the context propagation works as expected. Some additional cleanup is done throughout the test suite to reduce the scope of included Micronaut beans to the specific tests in order to make it easier to test different setups. * Avoid direct instantiation of DefaultApplicationContext Resolves #495 --- gradle/libs.versions.toml | 3 +- graphql/build.gradle | 2 + ...efaultGraphQLExecutionInputCustomizer.java | 4 +- .../graphql/DefaultGraphQLInvocation.java | 6 +- .../graphql/GraphQLConfigurationSpec.groovy | 46 ++++---- .../GraphQLContextPropagationSpec.groovy | 104 ++++++++++++++++++ .../graphql/GraphQLControllerSpec.groovy | 2 +- .../graphql/GraphQLFactory.groovy | 23 ---- .../graphql/GraphiQLConfigurationSpec.groovy | 55 ++++----- .../graphql/GraphiQLControllerSpec.groovy | 19 ++++ .../ws/GraphQLWsConfigurationSpec.groovy | 58 +++++----- .../GraphQLApolloWsConfigurationSpec.groovy | 68 ++++++------ graphql/src/test/resources/schema.graphqls | 4 + settings.gradle | 1 + 14 files changed, 256 insertions(+), 139 deletions(-) create mode 100644 graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLContextPropagationSpec.groovy delete mode 100644 graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLFactory.groovy create mode 100644 graphql/src/test/resources/schema.graphqls diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b9287c45..2132edd6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,6 @@ micronaut-platform = "4.1.2" micronaut-test = "4.0.0" groovy = "4.0.13" spock = "2.3-groovy-4.0" - graal-svm = "23.1.2" kotlin = '1.9.22' shadow = '8.0.0' @@ -14,6 +13,7 @@ micronaut-kotlin = "4.2.0" micronaut-security = "4.4.0" micronaut-serde = "2.7.1" micronaut-logging = "1.1.2" +micronaut-reactor = "3.2.1" managed-graphql-java = "21.3" managed-graphql-java-extended-scalars = "2023-01-24T02-11-56-babda5f" @@ -26,6 +26,7 @@ micronaut-core = { module = 'io.micronaut:micronaut-core-bom', version.ref = 'mi micronaut-kotlin = { module = "io.micronaut.kotlin:micronaut-kotlin-bom", version.ref = "micronaut-kotlin" } micronaut-security = { module = "io.micronaut.security:micronaut-security-bom", version.ref = "micronaut-security" } micronaut-serde = { module = "io.micronaut.serde:micronaut-serde-bom", version.ref = "micronaut-serde" } +micronaut-reactor = { module = "io.micronaut.reactor:micronaut-reactor-bom", version.ref = "micronaut-reactor" } managed-graphql-java = { module = 'com.graphql-java:graphql-java', version.ref = 'managed-graphql-java' } managed-graphql-java-extended-scalars = { module = 'com.graphql-java:graphql-java-extended-scalars', version.ref = 'managed-graphql-java-extended-scalars' } diff --git a/graphql/build.gradle b/graphql/build.gradle index 93d8868a..b3c6cf80 100644 --- a/graphql/build.gradle +++ b/graphql/build.gradle @@ -17,6 +17,8 @@ dependencies { testImplementation(mn.micronaut.http.server.netty) testImplementation(mn.micronaut.http.client) testRuntimeOnly(mn.snakeyaml) + testRuntimeOnly(mnReactor.micronaut.reactor) + testRuntimeOnly(mnReactor.micrometer.context.propagation) } micronautBuild { diff --git a/graphql/src/main/java/io/micronaut/configuration/graphql/DefaultGraphQLExecutionInputCustomizer.java b/graphql/src/main/java/io/micronaut/configuration/graphql/DefaultGraphQLExecutionInputCustomizer.java index 719a91d6..701c66e8 100644 --- a/graphql/src/main/java/io/micronaut/configuration/graphql/DefaultGraphQLExecutionInputCustomizer.java +++ b/graphql/src/main/java/io/micronaut/configuration/graphql/DefaultGraphQLExecutionInputCustomizer.java @@ -18,11 +18,11 @@ import graphql.ExecutionInput; import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.Nullable; -import io.micronaut.core.async.publisher.Publishers; import io.micronaut.http.HttpRequest; import io.micronaut.http.MutableHttpResponse; import jakarta.inject.Singleton; import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; /** * The default implementation for customizing GraphQL execution inputs. @@ -42,6 +42,6 @@ public Publisher customize( ExecutionInput executionInput, HttpRequest httpRequest, @Nullable MutableHttpResponse httpResponse) { - return Publishers.just(executionInput); + return Mono.just(executionInput); } } diff --git a/graphql/src/main/java/io/micronaut/configuration/graphql/DefaultGraphQLInvocation.java b/graphql/src/main/java/io/micronaut/configuration/graphql/DefaultGraphQLInvocation.java index b9cd3a8d..12f87b2e 100644 --- a/graphql/src/main/java/io/micronaut/configuration/graphql/DefaultGraphQLInvocation.java +++ b/graphql/src/main/java/io/micronaut/configuration/graphql/DefaultGraphQLInvocation.java @@ -20,13 +20,13 @@ import graphql.GraphQL; import io.micronaut.context.BeanProvider; import io.micronaut.core.annotation.Nullable; -import io.micronaut.core.async.publisher.Publishers; import io.micronaut.http.HttpRequest; import io.micronaut.http.MutableHttpResponse; import jakarta.inject.Singleton; import org.dataloader.DataLoaderRegistry; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import java.util.concurrent.CompletableFuture; @@ -81,11 +81,11 @@ public Publisher invoke( ExecutionInput executionInput = executionInputBuilder.build(); return Flux .from(graphQLExecutionInputCustomizer.customize(executionInput, httpRequest, httpResponse)) - .flatMap(customizedExecutionInput -> Publishers.fromCompletableFuture(() -> { + .flatMap(customizedExecutionInput -> Mono.fromFuture(() -> { try { return graphQL.executeAsync(customizedExecutionInput); } catch (Throwable e) { - CompletableFuture future = new CompletableFuture(); + CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(e); return future; } diff --git a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLConfigurationSpec.groovy b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLConfigurationSpec.groovy index 3bce31ca..45ffe174 100644 --- a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLConfigurationSpec.groovy +++ b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLConfigurationSpec.groovy @@ -17,11 +17,15 @@ package io.micronaut.configuration.graphql import graphql.GraphQL +import graphql.schema.GraphQLSchema import io.micronaut.context.ApplicationContext -import io.micronaut.context.DefaultApplicationContext +import io.micronaut.context.annotation.Bean +import io.micronaut.context.annotation.Factory +import io.micronaut.context.annotation.Requires import io.micronaut.context.env.Environment -import io.micronaut.context.env.PropertySource +import io.micronaut.core.util.StringUtils import io.micronaut.http.annotation.Controller +import jakarta.inject.Singleton import spock.lang.Specification /** @@ -33,11 +37,8 @@ class GraphQLConfigurationSpec extends Specification { void "test no graphql bean provided"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.factory": false] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName, + "graphql.factory": false], Environment.TEST) expect: !context.containsBean(GraphQLExecutionResultHandler) @@ -50,9 +51,7 @@ class GraphQLConfigurationSpec extends Specification { void "test graphql bean provided"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.registerSingleton(Mock(GraphQL)) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName], Environment.TEST) expect: context.containsBean(GraphQLExecutionResultHandler) @@ -66,11 +65,8 @@ class GraphQLConfigurationSpec extends Specification { void "test custom graphql path"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.path": "/custom-graphql"] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName, + "graphql.path": "/custom-graphql"], Environment.TEST) expect: context.containsBean(GraphQLExecutionResultHandler) @@ -85,11 +81,8 @@ class GraphQLConfigurationSpec extends Specification { void "test graphql disabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.enabled": false] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName, + "graphql.enabled": false], Environment.TEST) expect: !context.containsBean(GraphQLExecutionResultHandler) @@ -99,4 +92,17 @@ class GraphQLConfigurationSpec extends Specification { cleanup: context.close() } + + @Factory + static class GraphQLFactory { + + @Bean + @Singleton + @Requires(property = "graphql.factory", notEquals = StringUtils.FALSE) + @Requires(property = "spec.name", value = "GraphQLConfigurationSpec") + GraphQL graphQL() { + def schema = GraphQLSchema.newSchema().build() + GraphQL.newGraphQL(schema).build() + } + } } diff --git a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLContextPropagationSpec.groovy b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLContextPropagationSpec.groovy new file mode 100644 index 00000000..5791f1f5 --- /dev/null +++ b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLContextPropagationSpec.groovy @@ -0,0 +1,104 @@ +package io.micronaut.configuration.graphql + +import graphql.GraphQL +import graphql.schema.DataFetcher +import graphql.schema.DataFetchingEnvironment +import graphql.schema.GraphQLSchema +import graphql.schema.idl.RuntimeWiring +import graphql.schema.idl.SchemaGenerator +import graphql.schema.idl.SchemaParser +import graphql.schema.idl.TypeDefinitionRegistry +import io.micronaut.context.ApplicationContext +import io.micronaut.context.annotation.Bean +import io.micronaut.context.annotation.Factory +import io.micronaut.context.annotation.Requires +import io.micronaut.context.env.Environment +import io.micronaut.core.annotation.Nullable +import io.micronaut.core.io.ResourceResolver +import io.micronaut.http.HttpResponse +import io.micronaut.http.annotation.Body +import io.micronaut.http.annotation.Get +import io.micronaut.http.annotation.Post +import io.micronaut.http.annotation.QueryValue +import io.micronaut.http.client.annotation.Client +import io.micronaut.http.context.ServerRequestContext +import io.micronaut.runtime.server.EmbeddedServer +import jakarta.inject.Singleton +import spock.lang.AutoCleanup +import spock.lang.Specification + +import java.net.http.HttpRequest + +class GraphQLContextPropagationSpec extends Specification { + + @AutoCleanup + EmbeddedServer embeddedServer + + GraphQLClient graphQLClient + + def setup() { + embeddedServer = ApplicationContext.run( + EmbeddedServer, + ["spec.name": GraphQLContextPropagationSpec.simpleName]) + graphQLClient = embeddedServer.applicationContext.getBean(GraphQLClient) + } + + void "server request context is propagated to data fetcher"() { + when: + GraphQLResponseBody response = graphQLClient.hello("query { hello }") + + then: + response + response.getSpecification()["data"]["hello"] == "Hello World!" + } + + @Client("/graphql") + static interface GraphQLClient { + + @Get("{?query}") + GraphQLResponseBody hello(@QueryValue String query) + + } + + @Factory + static class GraphQLFactory { + + @Bean + @Singleton + @Requires(property = "spec.name", value = "GraphQLContextPropagationSpec") + GraphQL graphQL(ResourceResolver resourceResolver, HelloDataFetcher helloDataFetcher) { + + SchemaParser schemaParser = new SchemaParser() + SchemaGenerator schemaGenerator = new SchemaGenerator() + + TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry() + typeRegistry.merge(schemaParser.parse(new BufferedReader(new InputStreamReader( + resourceResolver.getResourceAsStream("classpath:schema.graphqls").get())))) + + RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring() + .type("Query", typeWiring -> typeWiring + .dataFetcher("hello", helloDataFetcher)) + .build() + + GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring) + + return GraphQL.newGraphQL(graphQLSchema).build() + } + } + + @Singleton + @Requires(property = "spec.name", value = "GraphQLContextPropagationSpec") + static class HelloDataFetcher implements DataFetcher { + + @Override + String get(DataFetchingEnvironment env) { + Optional request = ServerRequestContext.currentRequest() + assert request.isPresent() + String name = env.getArgument("name") + if (name == null || name.trim().length() == 0) { + name = "World" + } + return String.format("Hello %s!", name) + } + } +} diff --git a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLControllerSpec.groovy b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLControllerSpec.groovy index 26bc6b82..e9587f56 100644 --- a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLControllerSpec.groovy +++ b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLControllerSpec.groovy @@ -301,7 +301,7 @@ class GraphQLControllerSpec extends Specification { } @Factory - class GraphQLFactory { + static class GraphQLFactory { @Bean @Singleton diff --git a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLFactory.groovy b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLFactory.groovy deleted file mode 100644 index 070db825..00000000 --- a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphQLFactory.groovy +++ /dev/null @@ -1,23 +0,0 @@ -package io.micronaut.configuration.graphql - -import graphql.GraphQL -import graphql.schema.GraphQLSchema -import io.micronaut.context.annotation.Factory -import io.micronaut.context.annotation.Requires -import io.micronaut.core.util.StringUtils -import jakarta.inject.Singleton - -/** - * @author James Kleeh - * @since 1.0 - */ -@Factory -class GraphQLFactory { - - @Requires(property = "graphql.factory", notEquals = StringUtils.FALSE) - @Singleton - GraphQL graphQL() { - def schema = GraphQLSchema.newSchema().build() - GraphQL.newGraphQL(schema).build() - } -} diff --git a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphiQLConfigurationSpec.groovy b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphiQLConfigurationSpec.groovy index 1ad28646..4b851d6b 100644 --- a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphiQLConfigurationSpec.groovy +++ b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphiQLConfigurationSpec.groovy @@ -16,11 +16,15 @@ package io.micronaut.configuration.graphql +import graphql.GraphQL +import graphql.schema.GraphQLSchema import io.micronaut.context.ApplicationContext -import io.micronaut.context.DefaultApplicationContext +import io.micronaut.context.annotation.Bean +import io.micronaut.context.annotation.Factory +import io.micronaut.context.annotation.Requires import io.micronaut.context.env.Environment -import io.micronaut.context.env.PropertySource import io.micronaut.http.annotation.Controller +import jakarta.inject.Singleton import spock.lang.Specification /** @@ -31,8 +35,7 @@ class GraphiQLConfigurationSpec extends Specification { void "test graphiql disabled by default"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName], Environment.TEST) expect: !context.containsBean(GraphiQLController) @@ -43,11 +46,8 @@ class GraphiQLConfigurationSpec extends Specification { void "test graphiql enabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphiql.enabled": true] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName, + "graphql.graphiql.enabled": true], Environment.TEST) expect: context.containsBean(GraphiQLController) @@ -59,12 +59,9 @@ class GraphiQLConfigurationSpec extends Specification { void "test custom graphiql version"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphiql.enabled": true, - "graphql.graphiql.version" : "0.13.1"] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName, + "graphql.graphiql.enabled": true, + "graphql.graphiql.version" : "0.13.1"], Environment.TEST) expect: context.containsBean(GraphiQLController) @@ -76,12 +73,9 @@ class GraphiQLConfigurationSpec extends Specification { void "test custom graphiql path"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphiql.enabled": true, - "graphql.graphiql.path" : "/custom-graphiql"] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName, + "graphql.graphiql.enabled": true, + "graphql.graphiql.path" : "/custom-graphiql"], Environment.TEST) expect: context.containsBean(GraphiQLController) @@ -94,11 +88,8 @@ class GraphiQLConfigurationSpec extends Specification { void "test graphiql disabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphiql.enabled": false] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLConfigurationSpec.simpleName, + "graphql.graphiql.enabled": false], Environment.TEST) expect: !context.containsBean(GraphiQLController) @@ -106,4 +97,16 @@ class GraphiQLConfigurationSpec extends Specification { cleanup: context.close() } + + @Factory + static class GraphQLFactory { + + @Bean + @Singleton + @Requires(property = "spec.name", value = "GraphiQLConfigurationSpec") + GraphQL graphQL() { + def schema = GraphQLSchema.newSchema().build() + GraphQL.newGraphQL(schema).build() + } + } } diff --git a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphiQLControllerSpec.groovy b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphiQLControllerSpec.groovy index e856e984..50fffb15 100644 --- a/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphiQLControllerSpec.groovy +++ b/graphql/src/test/groovy/io/micronaut/configuration/graphql/GraphiQLControllerSpec.groovy @@ -16,13 +16,20 @@ package io.micronaut.configuration.graphql +import graphql.GraphQL +import graphql.schema.GraphQLSchema import io.micronaut.context.ApplicationContext +import io.micronaut.context.annotation.Bean +import io.micronaut.context.annotation.Factory +import io.micronaut.context.annotation.Requires import io.micronaut.context.env.Environment +import io.micronaut.core.util.StringUtils import io.micronaut.http.HttpRequest import io.micronaut.http.HttpResponse import io.micronaut.http.HttpStatus import io.micronaut.http.client.HttpClient import io.micronaut.runtime.server.EmbeddedServer +import jakarta.inject.Singleton import spock.lang.Specification import static io.micronaut.http.MediaType.TEXT_HTML @@ -75,4 +82,16 @@ class GraphiQLControllerSpec extends Specification { cleanup: embeddedServer.close() } + + @Factory + static class GraphQLFactory { + + @Bean + @Singleton + @Requires(property = "spec.name", value = "GraphiQLControllerSpec") + GraphQL graphQL() { + def schema = GraphQLSchema.newSchema().build() + GraphQL.newGraphQL(schema).build() + } + } } diff --git a/graphql/src/test/groovy/io/micronaut/configuration/graphql/ws/GraphQLWsConfigurationSpec.groovy b/graphql/src/test/groovy/io/micronaut/configuration/graphql/ws/GraphQLWsConfigurationSpec.groovy index 95185a8e..286ecb4d 100644 --- a/graphql/src/test/groovy/io/micronaut/configuration/graphql/ws/GraphQLWsConfigurationSpec.groovy +++ b/graphql/src/test/groovy/io/micronaut/configuration/graphql/ws/GraphQLWsConfigurationSpec.groovy @@ -1,11 +1,15 @@ package io.micronaut.configuration.graphql.ws - +import graphql.GraphQL +import graphql.schema.GraphQLSchema import io.micronaut.context.ApplicationContext -import io.micronaut.context.DefaultApplicationContext +import io.micronaut.context.annotation.Bean +import io.micronaut.context.annotation.Factory +import io.micronaut.context.annotation.Requires import io.micronaut.context.env.Environment -import io.micronaut.context.env.PropertySource +import io.micronaut.core.util.StringUtils import io.micronaut.websocket.annotation.ServerWebSocket +import jakarta.inject.Singleton import spock.lang.Specification import java.time.Duration @@ -14,8 +18,7 @@ class GraphQLWsConfigurationSpec extends Specification { void "test graphql websocket disabled by default"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLWsConfigurationSpec.simpleName], Environment.TEST) expect: !context.containsBean(GraphQLWsHandler) @@ -26,11 +29,8 @@ class GraphQLWsConfigurationSpec extends Specification { void "test graphql websocket enabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-ws.enabled": true] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLWsConfigurationSpec.simpleName, + "graphql.graphql-ws.enabled": true], Environment.TEST) expect: context.containsBean(GraphQLWsHandler) @@ -48,12 +48,9 @@ class GraphQLWsConfigurationSpec extends Specification { void "test graphql websocket enabled with custom connection timeout"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-ws.enabled" : true, - "graphql.graphql-ws.connection-init-wait-timeout": "30s"] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLWsConfigurationSpec.simpleName, + "graphql.graphql-ws.enabled" : true, + "graphql.graphql-ws.connection-init-wait-timeout": "30s"], Environment.TEST) expect: context.containsBean(GraphQLWsHandler) @@ -71,12 +68,9 @@ class GraphQLWsConfigurationSpec extends Specification { void "test custom path"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-ws.enabled": true, - "graphql.graphql-ws.path" : "/custom-graphql-ws"] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLWsConfigurationSpec.simpleName, + "graphql.graphql-ws.enabled": true, + "graphql.graphql-ws.path" : "/custom-graphql-ws"], Environment.TEST) expect: context.containsBean(GraphQLWsHandler) @@ -89,11 +83,8 @@ class GraphQLWsConfigurationSpec extends Specification { void "test graphql websocket disabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-ws.enabled": false] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLWsConfigurationSpec.simpleName, + "graphql.graphql-ws.enabled": false], Environment.TEST) expect: !context.containsBean(GraphQLWsHandler) @@ -101,4 +92,17 @@ class GraphQLWsConfigurationSpec extends Specification { cleanup: context.close() } + + @Factory + static class GraphQLFactory { + + @Bean + @Singleton + @Requires(property = "graphql.factory", notEquals = StringUtils.FALSE) + @Requires(property = "spec.name", value = "GraphQLWsConfigurationSpec") + GraphQL graphQL() { + def schema = GraphQLSchema.newSchema().build() + GraphQL.newGraphQL(schema).build() + } + } } diff --git a/graphql/src/test/groovy/io/micronaut/configuration/graphql/ws/apollo/GraphQLApolloWsConfigurationSpec.groovy b/graphql/src/test/groovy/io/micronaut/configuration/graphql/ws/apollo/GraphQLApolloWsConfigurationSpec.groovy index 35fcb0fc..b48fdd6c 100644 --- a/graphql/src/test/groovy/io/micronaut/configuration/graphql/ws/apollo/GraphQLApolloWsConfigurationSpec.groovy +++ b/graphql/src/test/groovy/io/micronaut/configuration/graphql/ws/apollo/GraphQLApolloWsConfigurationSpec.groovy @@ -1,12 +1,15 @@ package io.micronaut.configuration.graphql.ws.apollo +import graphql.GraphQL +import graphql.schema.GraphQLSchema import io.micronaut.context.ApplicationContext -import io.micronaut.context.DefaultApplicationContext +import io.micronaut.context.annotation.Bean +import io.micronaut.context.annotation.Factory +import io.micronaut.context.annotation.Requires import io.micronaut.context.env.Environment -import io.micronaut.context.env.PropertySource import io.micronaut.websocket.annotation.ServerWebSocket +import jakarta.inject.Singleton import spock.lang.Specification - /** * @author Gerard Klijs * @since 1.3 @@ -15,8 +18,7 @@ class GraphQLApolloWsConfigurationSpec extends Specification { void "test graphql websocket disabled by default"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLApolloWsConfigurationSpec.simpleName], Environment.TEST) expect: !context.containsBean(GraphQLApolloWsController) @@ -27,11 +29,8 @@ class GraphQLApolloWsConfigurationSpec extends Specification { void "test graphql websocket enabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-apollo-ws.enabled": true] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLApolloWsConfigurationSpec.simpleName, + "graphql.graphql-apollo-ws.enabled": true], Environment.TEST) expect: context.containsBean(GraphQLApolloWsController) @@ -50,12 +49,9 @@ class GraphQLApolloWsConfigurationSpec extends Specification { void "test custom path"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-apollo-ws.enabled": true, - "graphql.graphql-apollo-ws.path" : "/custom-graphql-ws"] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLApolloWsConfigurationSpec.simpleName, + "graphql.graphql-apollo-ws.enabled": true, + "graphql.graphql-apollo-ws.path" : "/custom-graphql-ws"], Environment.TEST) expect: context.containsBean(GraphQLApolloWsController) @@ -68,11 +64,8 @@ class GraphQLApolloWsConfigurationSpec extends Specification { void "test graphql websocket disabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-apollo-ws.enabled": false] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLApolloWsConfigurationSpec.simpleName, + "graphql.graphql-apollo-ws.enabled": false], Environment.TEST) expect: !context.containsBean(GraphQLApolloWsController) @@ -83,11 +76,8 @@ class GraphQLApolloWsConfigurationSpec extends Specification { void "test graphql websocket keepalive disabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-apollo-ws.keep-alive-enabled": false] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLApolloWsConfigurationSpec.simpleName, + "graphql.graphql-apollo-ws.keep-alive-enabled": false], Environment.TEST) expect: !context.getBean(GraphQLApolloWsConfiguration).enabled @@ -98,11 +88,8 @@ class GraphQLApolloWsConfigurationSpec extends Specification { void "test bean not created when graphql websocket keepalive disabled"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-apollo-ws.keep-alive-enabled": false] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLApolloWsConfigurationSpec.simpleName, + "graphql.graphql-apollo-ws.keep-alive-enabled": false], Environment.TEST) expect: !context.containsBean(GraphQLApolloWsKeepAlive) @@ -113,11 +100,8 @@ class GraphQLApolloWsConfigurationSpec extends Specification { void "test graphql websocket keepalive different interval"() { given: - ApplicationContext context = new DefaultApplicationContext(Environment.TEST) - context.environment.addPropertySource(PropertySource.of( - ["graphql.graphql-apollo-ws.keep-alive-interval": "1s"] - )) - context.start() + ApplicationContext context = ApplicationContext.run(["spec.name": GraphQLApolloWsConfigurationSpec.simpleName, + "graphql.graphql-apollo-ws.keep-alive-interval": "1s"], Environment.TEST) expect: context.getBean(GraphQLApolloWsConfiguration).keepAliveInterval == "1s" @@ -125,4 +109,16 @@ class GraphQLApolloWsConfigurationSpec extends Specification { cleanup: context.close() } + + @Factory + static class GraphQLFactory { + + @Bean + @Singleton + @Requires(property = "spec.name", value = "GraphQLApolloWsConfigurationSpec") + GraphQL graphQL() { + def schema = GraphQLSchema.newSchema().build() + GraphQL.newGraphQL(schema).build() + } + } } diff --git a/graphql/src/test/resources/schema.graphqls b/graphql/src/test/resources/schema.graphqls new file mode 100644 index 00000000..6fadd606 --- /dev/null +++ b/graphql/src/test/resources/schema.graphqls @@ -0,0 +1,4 @@ +type Query { + + hello(name: String): String! +} diff --git a/settings.gradle b/settings.gradle index bf2e1efe..4fc9ad1f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,6 +17,7 @@ micronautBuild { importMicronautCatalog("micronaut-kotlin") importMicronautCatalog("micronaut-security") importMicronautCatalog("micronaut-serde") + importMicronautCatalog("micronaut-reactor") } rootProject.name = 'graphql-parent'