From 1979e435c3031134dd29917925a92ac2d6313663 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 21 Aug 2024 18:10:01 +0200 Subject: [PATCH] Upgrade to GraphQL Java 22.2 This also documents the new subscription ordering feature as reported and requested in #949. Closes gh-1044 --- build.gradle | 2 +- .../modules/ROOT/pages/request-execution.adoc | 25 ++++------ .../CustomExecutionIdProvider.java | 27 ++++++++++ .../graphqlsource/GraphQlConfig.java | 33 ++++++++++++ .../reactivedatafetcher/GraphQlConfig.java | 50 +++++++++++++++++++ 5 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/graphqlsource/CustomExecutionIdProvider.java create mode 100644 spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/graphqlsource/GraphQlConfig.java create mode 100644 spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/reactivedatafetcher/GraphQlConfig.java diff --git a/build.gradle b/build.gradle index e8ae7472..3b3ca641 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ description = "Spring for GraphQL" ext { moduleProjects = [project(":spring-graphql"), project(":spring-graphql-test")] springFrameworkVersion = "6.1.11" - graphQlJavaVersion = "22.1" + graphQlJavaVersion = "22.2" springBootVersion = "3.3.0" } diff --git a/spring-graphql-docs/modules/ROOT/pages/request-execution.adoc b/spring-graphql-docs/modules/ROOT/pages/request-execution.adoc index 2672acc0..3a942604 100644 --- a/spring-graphql-docs/modules/ROOT/pages/request-execution.adoc +++ b/spring-graphql-docs/modules/ROOT/pages/request-execution.adoc @@ -28,19 +28,7 @@ and `DataFetcherExceptionResolver` and `SubscriptionExceptionResolver` beans fo xref:request-execution.adoc#execution.exceptions[exception resolution]. For further customizations, you can also declare a `GraphQlSourceBuilderCustomizer` bean, for example: -[source,java,indent=0,subs="verbatim,quotes"] ----- -@Configuration(proxyBeanMethods = false) -class GraphQlConfig { - - @Bean - public GraphQlSourceBuilderCustomizer sourceBuilderCustomizer() { - return (builder) -> - builder.configureGraphQl(graphQlBuilder -> - graphQlBuilder.executionIdProvider(new CustomExecutionIdProvider())); - } -} ----- +include-code::GraphQlConfig[] @@ -409,7 +397,7 @@ request is simple enough and did not require asynchronous data fetching. -[[execution.reactive-datafetcher]] +[[execution.reactivedatafetcher]] == Reactive `DataFetcher` The default `GraphQlSource` builder enables support for a `DataFetcher` to return `Mono` @@ -422,6 +410,15 @@ A reactive `DataFetcher` can rely on access to Reactor context propagated from t transport layer, such as from a WebFlux request handling, see xref:request-execution.adoc#execution.context.webflux[WebFlux Context]. +In the case of subscription requests, GraphQL Java will produce items as soon as they +are available and all their requested fields were fetched. Because this involves several +layers of asynchronous data fetching, items might be sent over the wire out of their +original order. If you wish GraphQL Java to buffer items and retain the original order, +you can do so by setting the `SubscriptionExecutionStrategy.KEEP_SUBSCRIPTION_EVENTS_ORDERED` +configuration flag in the `GraphQLContext`. This can be done, for example, with a custom +`Instrumentation`: + +include-code::GraphQlConfig[] [[execution.context]] diff --git a/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/graphqlsource/CustomExecutionIdProvider.java b/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/graphqlsource/CustomExecutionIdProvider.java new file mode 100644 index 00000000..84bd59a1 --- /dev/null +++ b/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/graphqlsource/CustomExecutionIdProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.docs.execution.graphqlsource; + +import graphql.execution.ExecutionId; +import graphql.execution.ExecutionIdProvider; + +class CustomExecutionIdProvider implements ExecutionIdProvider { + @Override + public ExecutionId provide(String query, String operationName, Object context) { + return null; + } +} diff --git a/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/graphqlsource/GraphQlConfig.java b/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/graphqlsource/GraphQlConfig.java new file mode 100644 index 00000000..3d3e7a39 --- /dev/null +++ b/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/graphqlsource/GraphQlConfig.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.docs.execution.graphqlsource; + +import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class GraphQlConfig { + + @Bean + public GraphQlSourceBuilderCustomizer sourceBuilderCustomizer() { + return (builder) -> + builder.configureGraphQl((graphQlBuilder) -> + graphQlBuilder.executionIdProvider(new CustomExecutionIdProvider())); + } + +} diff --git a/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/reactivedatafetcher/GraphQlConfig.java b/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/reactivedatafetcher/GraphQlConfig.java new file mode 100644 index 00000000..27295952 --- /dev/null +++ b/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/execution/reactivedatafetcher/GraphQlConfig.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.docs.execution.reactivedatafetcher; + +import graphql.ExecutionResult; +import graphql.execution.SubscriptionExecutionStrategy; +import graphql.execution.instrumentation.InstrumentationContext; +import graphql.execution.instrumentation.InstrumentationState; +import graphql.execution.instrumentation.SimpleInstrumentationContext; +import graphql.execution.instrumentation.SimplePerformantInstrumentation; +import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class GraphQlConfig { + + @Bean + public SubscriptionOrderInstrumentation subscriptionOrderInstrumentation() { + return new SubscriptionOrderInstrumentation(); + } + + static class SubscriptionOrderInstrumentation extends SimplePerformantInstrumentation { + + @Override + public InstrumentationContext beginExecution(InstrumentationExecutionParameters parameters, + InstrumentationState state) { + // Enable option for keeping subscription results in upstream order + parameters.getGraphQLContext().put(SubscriptionExecutionStrategy.KEEP_SUBSCRIPTION_EVENTS_ORDERED, true); + return SimpleInstrumentationContext.noOp(); + } + + } + +}