From b5f2ab14c92ab5bd39efb44ca76500afddcc6420 Mon Sep 17 00:00:00 2001 From: bbaker Date: Thu, 1 May 2025 18:45:57 +1000 Subject: [PATCH 1/9] A generalised configuration mechanism --- src/main/java/graphql/GraphQL.java | 10 ++ .../java/graphql/GraphQLConfiguration.java | 150 ++++++++++++++++++ .../java/graphql/parser/ParserOptions.java | 2 +- .../config/GraphQLConfigurationTest.groovy | 42 +++++ 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 src/main/java/graphql/GraphQLConfiguration.java create mode 100644 src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index 5d8dfc87d..470d25c96 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -84,6 +84,16 @@ @PublicApi public class GraphQL { + /** + * This allows you to control specific aspects of the GraphQL system + * including some JVM wide settings and some per execution setttings + * + * @return a GraphQL object + */ + public static GraphQLConfiguration configuration() { + return GraphQLConfiguration.INSTANCE; + } + private final GraphQLSchema graphQLSchema; private final ExecutionStrategy queryStrategy; private final ExecutionStrategy mutationStrategy; diff --git a/src/main/java/graphql/GraphQLConfiguration.java b/src/main/java/graphql/GraphQLConfiguration.java new file mode 100644 index 000000000..e186309be --- /dev/null +++ b/src/main/java/graphql/GraphQLConfiguration.java @@ -0,0 +1,150 @@ +package graphql; + +import graphql.parser.ParserOptions; + +/** + * This allows you to control specific aspects of the GraphQL system + * including some JVM wide settings and some per execution settings + * as well as experimental ones + */ +public class GraphQLConfiguration { + static final GraphQLConfiguration INSTANCE = new GraphQLConfiguration(); + static final ParserCfg PARSER_CFG = new ParserCfg(); + static final IncrementalSupportCfg INCREMENTAL_SUPPORT_CFG = new IncrementalSupportCfg(); + + private GraphQLConfiguration() { + } + + public ParserCfg parser() { + return PARSER_CFG; + } + + @ExperimentalApi + public IncrementalSupportCfg incrementalSupport() { + return INCREMENTAL_SUPPORT_CFG; + } + + public static class ParserCfg { + + private ParserCfg() { + } + + /** + * By default, the Parser will not capture ignored characters. A static holds this default + * value in a JVM wide basis options object. + *

+ * Significant memory savings can be made if we do NOT capture ignored characters, + * especially in SDL parsing. + * + * @return the static default JVM value + * + * @see graphql.language.IgnoredChar + * @see graphql.language.SourceLocation + */ + public ParserOptions getDefaultParserOptions() { + return ParserOptions.getDefaultParserOptions(); + } + + /** + * By default, the Parser will not capture ignored characters. A static holds this default + * value in a JVM wide basis options object. + *

+ * Significant memory savings can be made if we do NOT capture ignored characters, + * especially in SDL parsing. So we have set this to false by default. + *

+ * This static can be set to true to allow the behavior of version 16.x or before. + * + * @param options - the new default JVM parser options + * + * @see graphql.language.IgnoredChar + * @see graphql.language.SourceLocation + */ + public ParserCfg setDefaultParserOptions(ParserOptions options) { + ParserOptions.setDefaultParserOptions(options); + return this; + } + + + /** + * By default, for operation parsing, the Parser will not capture ignored characters, and it will not capture line comments into AST + * elements . A static holds this default value for operation parsing in a JVM wide basis options object. + * + * @return the static default JVM value for operation parsing + * + * @see graphql.language.IgnoredChar + * @see graphql.language.SourceLocation + */ + public ParserOptions getDefaultOperationParserOptions() { + return ParserOptions.getDefaultOperationParserOptions(); + } + + /** + * By default, the Parser will not capture ignored characters or line comments. A static holds this default + * value in a JVM wide basis options object for operation parsing. + *

+ * This static can be set to true to allow the behavior of version 16.x or before. + * + * @param options - the new default JVM parser options for operation parsing + * + * @see graphql.language.IgnoredChar + * @see graphql.language.SourceLocation + */ + public ParserCfg setDefaultOperationParserOptions(ParserOptions options) { + ParserOptions.setDefaultOperationParserOptions(options); + return this; + } + + /** + * By default, for SDL parsing, the Parser will not capture ignored characters, but it will capture line comments into AST + * elements. The SDL default options allow unlimited tokens and whitespace, since a DOS attack vector is + * not commonly available via schema SDL parsing. + *

+ * A static holds this default value for SDL parsing in a JVM wide basis options object. + * + * @return the static default JVM value for SDL parsing + * + * @see graphql.language.IgnoredChar + * @see graphql.language.SourceLocation + * @see graphql.schema.idl.SchemaParser + */ + public ParserOptions getDefaultSdlParserOptions() { + return ParserOptions.getDefaultSdlParserOptions(); + } + + /** + * By default, for SDL parsing, the Parser will not capture ignored characters, but it will capture line comments into AST + * elements . A static holds this default value for operation parsing in a JVM wide basis options object. + *

+ * This static can be set to true to allow the behavior of version 16.x or before. + * + * @param options - the new default JVM parser options for SDL parsing + * + * @see graphql.language.IgnoredChar + * @see graphql.language.SourceLocation + */ + public ParserCfg setDefaultSdlParserOptions(ParserOptions options) { + ParserOptions.setDefaultSdlParserOptions(options); + return this; + } + } + + + public static class IncrementalSupportCfg { + private IncrementalSupportCfg() { + } + + @ExperimentalApi + public boolean isIncrementalSupportEnabled(GraphQLContext graphQLContext) { + return graphQLContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT); + } + + /** + * This controls whether @defer and @stream behaviour is enabled for this execution. + */ + @ExperimentalApi + public IncrementalSupportCfg enableIncrementalSupport(GraphQLContext.Builder graphqlContext, boolean enable) { + graphqlContext.put(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT, enable); + return this; + } + } +} diff --git a/src/main/java/graphql/parser/ParserOptions.java b/src/main/java/graphql/parser/ParserOptions.java index 0adfb73f6..2e0429442 100644 --- a/src/main/java/graphql/parser/ParserOptions.java +++ b/src/main/java/graphql/parser/ParserOptions.java @@ -175,7 +175,7 @@ public static ParserOptions getDefaultSdlParserOptions() { * * This static can be set to true to allow the behavior of version 16.x or before. * - * @param options - the new default JVM parser options for operation parsing + * @param options - the new default JVM parser options for SDL parsing * * @see graphql.language.IgnoredChar * @see graphql.language.SourceLocation diff --git a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy b/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy new file mode 100644 index 000000000..66fa67a8e --- /dev/null +++ b/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy @@ -0,0 +1,42 @@ +package graphql.config + +import graphql.GraphQL +import graphql.GraphQLContext +import spock.lang.Specification + +import static graphql.parser.ParserOptions.newParserOptions + +class GraphQLConfigurationTest extends Specification { + + def "can set parser configurations"() { + when: + def parserOptions = newParserOptions().maxRuleDepth(99).build() + GraphQL.configuration().parser().setDefaultParserOptions(parserOptions) + def defaultParserOptions = GraphQL.configuration().parser().getDefaultParserOptions() + + then: + defaultParserOptions.getMaxRuleDepth() == 99 + } + + def "can set defer configuration"() { + when: + def builder = GraphQLContext.newContext() + GraphQL.configuration().incrementalSupport().enableIncrementalSupport(builder, true) + + then: + GraphQL.configuration().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + + when: + builder = GraphQLContext.newContext() + GraphQL.configuration().incrementalSupport().enableIncrementalSupport(builder, false) + + then: + !GraphQL.configuration().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + + when: + builder = GraphQLContext.newContext() + + then: + !GraphQL.configuration().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + } +} From 516b62513f5d85a80851adbc152cda9120a21342 Mon Sep 17 00:00:00 2001 From: bbaker Date: Thu, 1 May 2025 18:59:21 +1000 Subject: [PATCH 2/9] A generalised configuration mechanism - added property data fetcher config --- .../java/graphql/GraphQLConfiguration.java | 48 +++++++++++++++++++ .../config/GraphQLConfigurationTest.groovy | 17 +++++++ 2 files changed, 65 insertions(+) diff --git a/src/main/java/graphql/GraphQLConfiguration.java b/src/main/java/graphql/GraphQLConfiguration.java index e186309be..2614fbd3d 100644 --- a/src/main/java/graphql/GraphQLConfiguration.java +++ b/src/main/java/graphql/GraphQLConfiguration.java @@ -1,6 +1,7 @@ package graphql; import graphql.parser.ParserOptions; +import graphql.schema.PropertyDataFetcherHelper; /** * This allows you to control specific aspects of the GraphQL system @@ -11,6 +12,7 @@ public class GraphQLConfiguration { static final GraphQLConfiguration INSTANCE = new GraphQLConfiguration(); static final ParserCfg PARSER_CFG = new ParserCfg(); static final IncrementalSupportCfg INCREMENTAL_SUPPORT_CFG = new IncrementalSupportCfg(); + static final PropertyDataFetcherCfg PROPERTY_DATA_FETCHER_CFG = new PropertyDataFetcherCfg(); private GraphQLConfiguration() { } @@ -19,6 +21,10 @@ public ParserCfg parser() { return PARSER_CFG; } + public PropertyDataFetcherCfg propertyDataFetcher() { + return PROPERTY_DATA_FETCHER_CFG; + } + @ExperimentalApi public IncrementalSupportCfg incrementalSupport() { return INCREMENTAL_SUPPORT_CFG; @@ -128,6 +134,48 @@ public ParserCfg setDefaultSdlParserOptions(ParserOptions options) { } } + public static class PropertyDataFetcherCfg { + private PropertyDataFetcherCfg() { + } + + /** + * PropertyDataFetcher caches the methods and fields that map from a class to a property for runtime performance reasons + * as well as negative misses. + *

+ * However during development you might be using an assistance tool like JRebel to allow you to tweak your code base and this + * caching may interfere with this. So you can call this method to clear the cache. A JRebel plugin could + * be developed to do just that. + */ + @SuppressWarnings("unused") + public PropertyDataFetcherCfg clearReflectionCache() { + PropertyDataFetcherHelper.clearReflectionCache(); + return this; + } + + /** + * This can be used to control whether PropertyDataFetcher will use {@link java.lang.reflect.Method#setAccessible(boolean)} to gain access to property + * values. By default, it PropertyDataFetcher WILL use setAccessible. + * + * @param flag whether to use setAccessible + * + * @return the previous value of the flag + */ + public boolean setUseSetAccessible(boolean flag) { + return PropertyDataFetcherHelper.setUseSetAccessible(flag); + } + + /** + * This can be used to control whether PropertyDataFetcher will cache negative lookups for a property for performance reasons. By default it PropertyDataFetcher WILL cache misses. + * + * @param flag whether to cache misses + * + * @return the previous value of the flag + */ + public boolean setUseNegativeCache(boolean flag) { + return PropertyDataFetcherHelper.setUseNegativeCache(flag); + } + } + public static class IncrementalSupportCfg { private IncrementalSupportCfg() { diff --git a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy b/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy index 66fa67a8e..787bb7e13 100644 --- a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy +++ b/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy @@ -18,6 +18,23 @@ class GraphQLConfigurationTest extends Specification { defaultParserOptions.getMaxRuleDepth() == 99 } + def "can set property data fetcher config"() { + when: + def prevValue = GraphQL.configuration().propertyDataFetcher().setUseNegativeCache(false) + then: + prevValue + + when: + prevValue = GraphQL.configuration().propertyDataFetcher().setUseNegativeCache(false) + then: + ! prevValue + + when: + prevValue = GraphQL.configuration().propertyDataFetcher().setUseNegativeCache(true) + then: + ! prevValue + } + def "can set defer configuration"() { when: def builder = GraphQLContext.newContext() From 5700fe858c5f07b692349a2178c1f646afcd971e Mon Sep 17 00:00:00 2001 From: bbaker Date: Thu, 1 May 2025 22:27:02 +1000 Subject: [PATCH 3/9] A generalised configuration mechanism - tweaked name and cleanups in tests --- src/main/java/graphql/GraphQL.java | 4 +-- .../config/GraphQLConfigurationTest.groovy | 28 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index 470d25c96..7f073b2a4 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -86,11 +86,11 @@ public class GraphQL { /** * This allows you to control specific aspects of the GraphQL system - * including some JVM wide settings and some per execution setttings + * including some JVM wide settings and some per execution settings * * @return a GraphQL object */ - public static GraphQLConfiguration configuration() { + public static GraphQLConfiguration config() { return GraphQLConfiguration.INSTANCE; } diff --git a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy b/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy index 787bb7e13..61746aff3 100644 --- a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy +++ b/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy @@ -2,17 +2,25 @@ package graphql.config import graphql.GraphQL import graphql.GraphQLContext +import graphql.parser.ParserOptions import spock.lang.Specification import static graphql.parser.ParserOptions.newParserOptions class GraphQLConfigurationTest extends Specification { + def startingParserOptions = ParserOptions.getDefaultParserOptions() + + void cleanup() { + ParserOptions.setDefaultParserOptions(startingParserOptions) + GraphQL.config().propertyDataFetcher().setUseNegativeCache(true) + } + def "can set parser configurations"() { when: def parserOptions = newParserOptions().maxRuleDepth(99).build() - GraphQL.configuration().parser().setDefaultParserOptions(parserOptions) - def defaultParserOptions = GraphQL.configuration().parser().getDefaultParserOptions() + GraphQL.config().parser().setDefaultParserOptions(parserOptions) + def defaultParserOptions = GraphQL.config().parser().getDefaultParserOptions() then: defaultParserOptions.getMaxRuleDepth() == 99 @@ -20,17 +28,17 @@ class GraphQLConfigurationTest extends Specification { def "can set property data fetcher config"() { when: - def prevValue = GraphQL.configuration().propertyDataFetcher().setUseNegativeCache(false) + def prevValue = GraphQL.config().propertyDataFetcher().setUseNegativeCache(false) then: prevValue when: - prevValue = GraphQL.configuration().propertyDataFetcher().setUseNegativeCache(false) + prevValue = GraphQL.config().propertyDataFetcher().setUseNegativeCache(false) then: ! prevValue when: - prevValue = GraphQL.configuration().propertyDataFetcher().setUseNegativeCache(true) + prevValue = GraphQL.config().propertyDataFetcher().setUseNegativeCache(true) then: ! prevValue } @@ -38,22 +46,22 @@ class GraphQLConfigurationTest extends Specification { def "can set defer configuration"() { when: def builder = GraphQLContext.newContext() - GraphQL.configuration().incrementalSupport().enableIncrementalSupport(builder, true) + GraphQL.config().incrementalSupport().enableIncrementalSupport(builder, true) then: - GraphQL.configuration().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + GraphQL.config().incrementalSupport().isIncrementalSupportEnabled(builder.build()) when: builder = GraphQLContext.newContext() - GraphQL.configuration().incrementalSupport().enableIncrementalSupport(builder, false) + GraphQL.config().incrementalSupport().enableIncrementalSupport(builder, false) then: - !GraphQL.configuration().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + !GraphQL.config().incrementalSupport().isIncrementalSupportEnabled(builder.build()) when: builder = GraphQLContext.newContext() then: - !GraphQL.configuration().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + !GraphQL.config().incrementalSupport().isIncrementalSupportEnabled(builder.build()) } } From fb67c76a68b640f7bc94e805c1c1a547bc14bd3a Mon Sep 17 00:00:00 2001 From: bbaker Date: Sat, 3 May 2025 17:00:00 +1000 Subject: [PATCH 4/9] A generalised configuration mechanism - changed it to have graphql context wrapping mechanism --- src/main/java/graphql/GraphQL.java | 26 ++- .../java/graphql/GraphQLConfiguration.java | 159 +++++++++++++++--- src/main/java/graphql/GraphQLContext.java | 9 + .../config/GraphQLConfigurationTest.groovy | 90 ++++++++-- 4 files changed, 239 insertions(+), 45 deletions(-) diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index 7f073b2a4..5a1f06a7a 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -86,12 +86,30 @@ public class GraphQL { /** * This allows you to control specific aspects of the GraphQL system - * including some JVM wide settings and some per execution settings + * including some JVM wide settings * - * @return a GraphQL object + * @return a {@link GraphQLConfiguration} object */ - public static GraphQLConfiguration config() { - return GraphQLConfiguration.INSTANCE; + public static GraphQLConfiguration configure() { + return new GraphQLConfiguration(); + } + + /** + * This allows you to control specific per execution aspects of the GraphQL system + * + * @return a {@link GraphQLConfiguration.GraphQLContextConfiguration} object + */ + public static GraphQLConfiguration.GraphQLContextConfiguration configure(GraphQLContext graphQLContext) { + return new GraphQLConfiguration.GraphQLContextConfiguration(graphQLContext); + } + + /** + * This allows you to control specific per execution aspects of the GraphQL system + * + * @return a {@link GraphQLConfiguration.GraphQLContextConfiguration} object + */ + public static GraphQLConfiguration.GraphQLContextConfiguration configure(GraphQLContext.Builder graphQLContextBuilder) { + return new GraphQLConfiguration.GraphQLContextConfiguration(graphQLContextBuilder); } private final GraphQLSchema graphQLSchema; diff --git a/src/main/java/graphql/GraphQLConfiguration.java b/src/main/java/graphql/GraphQLConfiguration.java index 2614fbd3d..5a1f0380d 100644 --- a/src/main/java/graphql/GraphQLConfiguration.java +++ b/src/main/java/graphql/GraphQLConfiguration.java @@ -1,38 +1,61 @@ package graphql; +import graphql.introspection.GoodFaithIntrospection; import graphql.parser.ParserOptions; import graphql.schema.PropertyDataFetcherHelper; +import static graphql.Assert.assertNotNull; + /** * This allows you to control specific aspects of the GraphQL system * including some JVM wide settings and some per execution settings - * as well as experimental ones + * as well as experimental ones. */ public class GraphQLConfiguration { - static final GraphQLConfiguration INSTANCE = new GraphQLConfiguration(); - static final ParserCfg PARSER_CFG = new ParserCfg(); - static final IncrementalSupportCfg INCREMENTAL_SUPPORT_CFG = new IncrementalSupportCfg(); - static final PropertyDataFetcherCfg PROPERTY_DATA_FETCHER_CFG = new PropertyDataFetcherCfg(); + GraphQLConfiguration() { + } - private GraphQLConfiguration() { + /** + * @return an element that allows you to control JVM wide parsing configuration + */ + public ParserCfg parsing() { + return new ParserCfg(this); } - public ParserCfg parser() { - return PARSER_CFG; + /** + * @return an element that allows you to control JVM wide {@link graphql.schema.PropertyDataFetcher} configuration + */ + public PropertyDataFetcherCfg propertyDataFetching() { + return new PropertyDataFetcherCfg(this); } - public PropertyDataFetcherCfg propertyDataFetcher() { - return PROPERTY_DATA_FETCHER_CFG; + /** + * @return an element that allows you to control JVM wide configuration + * of {@link graphql.introspection.GoodFaithIntrospection} + */ + public GoodFaithIntrospectionCfg goodFaithIntrospection() { + return new GoodFaithIntrospectionCfg(this); } - @ExperimentalApi - public IncrementalSupportCfg incrementalSupport() { - return INCREMENTAL_SUPPORT_CFG; + private static class BaseCfg { + protected final GraphQLConfiguration configuration; + + private BaseCfg(GraphQLConfiguration configuration) { + this.configuration = configuration; + } + + /** + * @return an element that allows you to chain multiple configuration elements + */ + public GraphQLConfiguration then() { + return configuration; + } } - public static class ParserCfg { + public static class ParserCfg extends BaseCfg { - private ParserCfg() { + private ParserCfg(GraphQLConfiguration configuration) { + super(configuration); } /** @@ -134,8 +157,9 @@ public ParserCfg setDefaultSdlParserOptions(ParserOptions options) { } } - public static class PropertyDataFetcherCfg { - private PropertyDataFetcherCfg() { + public static class PropertyDataFetcherCfg extends BaseCfg { + private PropertyDataFetcherCfg(GraphQLConfiguration configuration) { + super(configuration); } /** @@ -176,22 +200,109 @@ public boolean setUseNegativeCache(boolean flag) { } } + public static class GoodFaithIntrospectionCfg extends BaseCfg { + private GoodFaithIntrospectionCfg(GraphQLConfiguration configuration) { + super(configuration); + } - public static class IncrementalSupportCfg { - private IncrementalSupportCfg() { + /** + * @return true if good faith introspection is enabled + */ + public boolean isEnabledJvmWide() { + return GoodFaithIntrospection.isEnabledJvmWide(); } - @ExperimentalApi - public boolean isIncrementalSupportEnabled(GraphQLContext graphQLContext) { - return graphQLContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT); + /** + * This allows you to disable good faith introspection, which is on by default. + * + * @param enabled the desired state + * + * @return the previous state + */ + public GoodFaithIntrospectionCfg enabledJvmWide(boolean enabled) { + GoodFaithIntrospection.enabledJvmWide(enabled); + return this; + } + } + + /* + * =============================================== + * Per GraphqlContext code down here + * =============================================== + */ + + public static class GraphQLContextConfiguration { + // it will be one or the other types of GraphQLContext + private final GraphQLContext graphQLContext; + private final GraphQLContext.Builder graphQLContextBuilder; + + GraphQLContextConfiguration(GraphQLContext graphQLContext) { + this.graphQLContext = graphQLContext; + this.graphQLContextBuilder = null; + } + + GraphQLContextConfiguration(GraphQLContext.Builder graphQLContextBuilder) { + this.graphQLContextBuilder = graphQLContextBuilder; + this.graphQLContext = null; + } + + /** + * @return an element that allows you to control incremental support, that is @defer configuration + */ + public IncrementalSupportCfg incrementalSupport() { + return new IncrementalSupportCfg(this); + } + + private void put(String named, Object value) { + if (graphQLContext != null) { + graphQLContext.put(named, value); + } else { + assertNotNull(graphQLContextBuilder).put(named, value); + } + } + + private boolean getBoolean(String named) { + if (graphQLContext != null) { + return graphQLContext.getBoolean(named); + } else { + return assertNotNull(graphQLContextBuilder).getBoolean(named); + } + } + } + + private static class BaseContextCfg { + protected final GraphQLContextConfiguration contextConfig; + + private BaseContextCfg(GraphQLContextConfiguration contextConfig) { + this.contextConfig = contextConfig; + } + + /** + * @return an element that allows you to chain multiple configuration elements + */ + public GraphQLContextConfiguration then() { + return contextConfig; + } + } + + public static class IncrementalSupportCfg extends BaseContextCfg { + private IncrementalSupportCfg(GraphQLContextConfiguration contextConfig) { + super(contextConfig); + } + + /** + * @return true if @defer and @stream behaviour is enabled for this execution. + */ + public boolean isIncrementalSupportEnabled() { + return contextConfig.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT); } /** * This controls whether @defer and @stream behaviour is enabled for this execution. */ @ExperimentalApi - public IncrementalSupportCfg enableIncrementalSupport(GraphQLContext.Builder graphqlContext, boolean enable) { - graphqlContext.put(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT, enable); + public IncrementalSupportCfg enableIncrementalSupport(boolean enable) { + contextConfig.put(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT, enable); return this; } } diff --git a/src/main/java/graphql/GraphQLContext.java b/src/main/java/graphql/GraphQLContext.java index 48f79ce77..64ec5c406 100644 --- a/src/main/java/graphql/GraphQLContext.java +++ b/src/main/java/graphql/GraphQLContext.java @@ -326,6 +326,15 @@ public Builder put( ); } + public Object get(Object key) { + return map.get(key); + } + + public boolean getBoolean(Object key) { + return Boolean.parseBoolean(String.valueOf(get(key))); + } + + public Builder of( Object key1, Object value1 ) { diff --git a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy b/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy index 61746aff3..c0ef69046 100644 --- a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy +++ b/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy @@ -1,8 +1,11 @@ package graphql.config +import graphql.ExperimentalApi import graphql.GraphQL import graphql.GraphQLContext +import graphql.introspection.GoodFaithIntrospection import graphql.parser.ParserOptions +import graphql.schema.PropertyDataFetcherHelper import spock.lang.Specification import static graphql.parser.ParserOptions.newParserOptions @@ -10,17 +13,20 @@ import static graphql.parser.ParserOptions.newParserOptions class GraphQLConfigurationTest extends Specification { def startingParserOptions = ParserOptions.getDefaultParserOptions() + def startingState = GoodFaithIntrospection.isEnabledJvmWide() void cleanup() { + // JVM wide so other tests can be affected ParserOptions.setDefaultParserOptions(startingParserOptions) - GraphQL.config().propertyDataFetcher().setUseNegativeCache(true) + PropertyDataFetcherHelper.setUseNegativeCache(true) + GoodFaithIntrospection.enabledJvmWide(startingState) } def "can set parser configurations"() { when: def parserOptions = newParserOptions().maxRuleDepth(99).build() - GraphQL.config().parser().setDefaultParserOptions(parserOptions) - def defaultParserOptions = GraphQL.config().parser().getDefaultParserOptions() + GraphQL.configure().parsing().setDefaultParserOptions(parserOptions) + def defaultParserOptions = GraphQL.configure().parsing().getDefaultParserOptions() then: defaultParserOptions.getMaxRuleDepth() == 99 @@ -28,40 +34,90 @@ class GraphQLConfigurationTest extends Specification { def "can set property data fetcher config"() { when: - def prevValue = GraphQL.config().propertyDataFetcher().setUseNegativeCache(false) + def prevValue = GraphQL.configure().propertyDataFetching().setUseNegativeCache(false) then: prevValue when: - prevValue = GraphQL.config().propertyDataFetcher().setUseNegativeCache(false) + prevValue = GraphQL.configure().propertyDataFetching().setUseNegativeCache(false) then: - ! prevValue + !prevValue when: - prevValue = GraphQL.config().propertyDataFetcher().setUseNegativeCache(true) + prevValue = GraphQL.configure().propertyDataFetching().setUseNegativeCache(true) then: - ! prevValue + !prevValue } - def "can set defer configuration"() { + def "can set good faith settings"() { when: - def builder = GraphQLContext.newContext() - GraphQL.config().incrementalSupport().enableIncrementalSupport(builder, true) + GraphQL.configure().goodFaithIntrospection().enabledJvmWide(false) then: - GraphQL.config().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + !GraphQL.configure().goodFaithIntrospection().isEnabledJvmWide() when: - builder = GraphQLContext.newContext() - GraphQL.config().incrementalSupport().enableIncrementalSupport(builder, false) + GraphQL.configure().goodFaithIntrospection().enabledJvmWide(true) then: - !GraphQL.config().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + GraphQL.configure().goodFaithIntrospection().isEnabledJvmWide() + // showing chaining when: - builder = GraphQLContext.newContext() + GraphQL.configure().goodFaithIntrospection() + .enabledJvmWide(true) + .then().goodFaithIntrospection() + .enabledJvmWide(false) then: - !GraphQL.config().incrementalSupport().isIncrementalSupportEnabled(builder.build()) + !GraphQL.configure().goodFaithIntrospection().isEnabledJvmWide() + } + + def "can set defer configuration on graphql context objects"() { + when: + def graphqlContextBuilder = GraphQLContext.newContext() + GraphQL.configure(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(true) + + then: + graphqlContextBuilder.build().get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == true + GraphQL.configure(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() + + when: + graphqlContextBuilder = GraphQLContext.newContext() + GraphQL.configure(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(false) + + then: + graphqlContextBuilder.build().get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false + !GraphQL.configure(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() + + when: + def graphqlContext = GraphQLContext.newContext().build() + GraphQL.configure(graphqlContext).incrementalSupport().enableIncrementalSupport(true) + + then: + graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == true + GraphQL.configure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + + when: + graphqlContext = GraphQLContext.newContext().build() + GraphQL.configure(graphqlContext).incrementalSupport().enableIncrementalSupport(false) + + then: + graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false + !GraphQL.configure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + + when: + graphqlContext = GraphQLContext.newContext().build() + // just to show we we can navigate the DSL + GraphQL.configure(graphqlContext) + .incrementalSupport() + .enableIncrementalSupport(false) + .enableIncrementalSupport(true) + .then().incrementalSupport() + .enableIncrementalSupport(false) + + then: + graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false + !GraphQL.configure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() } } From b6313b1b08191cdbc6321a2f85b1225de14ee7d4 Mon Sep 17 00:00:00 2001 From: bbaker Date: Sun, 4 May 2025 10:21:36 +1000 Subject: [PATCH 5/9] A generalised configuration mechanism - changed it to have graphql context wrapping mechanism - renamed as extraordinary --- src/main/java/graphql/GraphQL.java | 36 ++++++++++----- ...=> GraphQLExtraordinaryConfiguration.java} | 22 ++++++---- ...phQLExtraordinaryConfigurationTest.groovy} | 44 +++++++++---------- 3 files changed, 59 insertions(+), 43 deletions(-) rename src/main/java/graphql/{GraphQLConfiguration.java => GraphQLExtraordinaryConfiguration.java} (93%) rename src/test/groovy/graphql/config/{GraphQLConfigurationTest.groovy => GraphQLExtraordinaryConfigurationTest.groovy} (55%) diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index 5a1f06a7a..2cf7e1241 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -85,31 +85,43 @@ public class GraphQL { /** - * This allows you to control specific aspects of the GraphQL system + * This allows you to control "extraordinary" aspects of the GraphQL system * including some JVM wide settings + *

+ * This is named extraordinary because in general we don't expect you to + * have to make ths configuration by default, but you can opt into certain features + * or disable them if you want to. * - * @return a {@link GraphQLConfiguration} object + * @return a {@link GraphQLExtraordinaryConfiguration} object */ - public static GraphQLConfiguration configure() { - return new GraphQLConfiguration(); + public static GraphQLExtraordinaryConfiguration extraordinaryConfigure() { + return new GraphQLExtraordinaryConfiguration(); } /** - * This allows you to control specific per execution aspects of the GraphQL system + * This allows you to control "extraordinary" per execution aspects of the GraphQL system + *

+ * This is named extraordinary because in general we don't expect you to + * have to make ths configuration by default, but you can opt into certain features + * or disable them if you want to. * - * @return a {@link GraphQLConfiguration.GraphQLContextConfiguration} object + * @return a {@link GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration} object */ - public static GraphQLConfiguration.GraphQLContextConfiguration configure(GraphQLContext graphQLContext) { - return new GraphQLConfiguration.GraphQLContextConfiguration(graphQLContext); + public static GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration extraordinaryConfigure(GraphQLContext graphQLContext) { + return new GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration(graphQLContext); } /** - * This allows you to control specific per execution aspects of the GraphQL system + * This allows you to control "extraordinary" per execution aspects of the GraphQL system + *

+ * This is named extraordinary because in general we don't expect you to + * have to make ths configuration by default, but you can opt into certain features + * or disable them if you want to. * - * @return a {@link GraphQLConfiguration.GraphQLContextConfiguration} object + * @return a {@link GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration} object */ - public static GraphQLConfiguration.GraphQLContextConfiguration configure(GraphQLContext.Builder graphQLContextBuilder) { - return new GraphQLConfiguration.GraphQLContextConfiguration(graphQLContextBuilder); + public static GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration extraordinaryConfigure(GraphQLContext.Builder graphQLContextBuilder) { + return new GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration(graphQLContextBuilder); } private final GraphQLSchema graphQLSchema; diff --git a/src/main/java/graphql/GraphQLConfiguration.java b/src/main/java/graphql/GraphQLExtraordinaryConfiguration.java similarity index 93% rename from src/main/java/graphql/GraphQLConfiguration.java rename to src/main/java/graphql/GraphQLExtraordinaryConfiguration.java index 5a1f0380d..32be0485d 100644 --- a/src/main/java/graphql/GraphQLConfiguration.java +++ b/src/main/java/graphql/GraphQLExtraordinaryConfiguration.java @@ -7,12 +7,16 @@ import static graphql.Assert.assertNotNull; /** - * This allows you to control specific aspects of the GraphQL system + * This allows you to control "extraordinary" aspects of the GraphQL system * including some JVM wide settings and some per execution settings * as well as experimental ones. + *

+ * This is named extraordinary because in general we don't expect you to + * have to make ths configuration by default but you can opt into certain features + * or disable them if you want to. */ -public class GraphQLConfiguration { - GraphQLConfiguration() { +public class GraphQLExtraordinaryConfiguration { + GraphQLExtraordinaryConfiguration() { } /** @@ -38,23 +42,23 @@ public GoodFaithIntrospectionCfg goodFaithIntrospection() { } private static class BaseCfg { - protected final GraphQLConfiguration configuration; + protected final GraphQLExtraordinaryConfiguration configuration; - private BaseCfg(GraphQLConfiguration configuration) { + private BaseCfg(GraphQLExtraordinaryConfiguration configuration) { this.configuration = configuration; } /** * @return an element that allows you to chain multiple configuration elements */ - public GraphQLConfiguration then() { + public GraphQLExtraordinaryConfiguration then() { return configuration; } } public static class ParserCfg extends BaseCfg { - private ParserCfg(GraphQLConfiguration configuration) { + private ParserCfg(GraphQLExtraordinaryConfiguration configuration) { super(configuration); } @@ -158,7 +162,7 @@ public ParserCfg setDefaultSdlParserOptions(ParserOptions options) { } public static class PropertyDataFetcherCfg extends BaseCfg { - private PropertyDataFetcherCfg(GraphQLConfiguration configuration) { + private PropertyDataFetcherCfg(GraphQLExtraordinaryConfiguration configuration) { super(configuration); } @@ -201,7 +205,7 @@ public boolean setUseNegativeCache(boolean flag) { } public static class GoodFaithIntrospectionCfg extends BaseCfg { - private GoodFaithIntrospectionCfg(GraphQLConfiguration configuration) { + private GoodFaithIntrospectionCfg(GraphQLExtraordinaryConfiguration configuration) { super(configuration); } diff --git a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy b/src/test/groovy/graphql/config/GraphQLExtraordinaryConfigurationTest.groovy similarity index 55% rename from src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy rename to src/test/groovy/graphql/config/GraphQLExtraordinaryConfigurationTest.groovy index c0ef69046..84987d241 100644 --- a/src/test/groovy/graphql/config/GraphQLConfigurationTest.groovy +++ b/src/test/groovy/graphql/config/GraphQLExtraordinaryConfigurationTest.groovy @@ -10,7 +10,7 @@ import spock.lang.Specification import static graphql.parser.ParserOptions.newParserOptions -class GraphQLConfigurationTest extends Specification { +class GraphQLExtraordinaryConfigurationTest extends Specification { def startingParserOptions = ParserOptions.getDefaultParserOptions() def startingState = GoodFaithIntrospection.isEnabledJvmWide() @@ -25,8 +25,8 @@ class GraphQLConfigurationTest extends Specification { def "can set parser configurations"() { when: def parserOptions = newParserOptions().maxRuleDepth(99).build() - GraphQL.configure().parsing().setDefaultParserOptions(parserOptions) - def defaultParserOptions = GraphQL.configure().parsing().getDefaultParserOptions() + GraphQL.extraordinaryConfigure().parsing().setDefaultParserOptions(parserOptions) + def defaultParserOptions = GraphQL.extraordinaryConfigure().parsing().getDefaultParserOptions() then: defaultParserOptions.getMaxRuleDepth() == 99 @@ -34,82 +34,82 @@ class GraphQLConfigurationTest extends Specification { def "can set property data fetcher config"() { when: - def prevValue = GraphQL.configure().propertyDataFetching().setUseNegativeCache(false) + def prevValue = GraphQL.extraordinaryConfigure().propertyDataFetching().setUseNegativeCache(false) then: prevValue when: - prevValue = GraphQL.configure().propertyDataFetching().setUseNegativeCache(false) + prevValue = GraphQL.extraordinaryConfigure().propertyDataFetching().setUseNegativeCache(false) then: !prevValue when: - prevValue = GraphQL.configure().propertyDataFetching().setUseNegativeCache(true) + prevValue = GraphQL.extraordinaryConfigure().propertyDataFetching().setUseNegativeCache(true) then: !prevValue } def "can set good faith settings"() { when: - GraphQL.configure().goodFaithIntrospection().enabledJvmWide(false) + GraphQL.extraordinaryConfigure().goodFaithIntrospection().enabledJvmWide(false) then: - !GraphQL.configure().goodFaithIntrospection().isEnabledJvmWide() + !GraphQL.extraordinaryConfigure().goodFaithIntrospection().isEnabledJvmWide() when: - GraphQL.configure().goodFaithIntrospection().enabledJvmWide(true) + GraphQL.extraordinaryConfigure().goodFaithIntrospection().enabledJvmWide(true) then: - GraphQL.configure().goodFaithIntrospection().isEnabledJvmWide() + GraphQL.extraordinaryConfigure().goodFaithIntrospection().isEnabledJvmWide() // showing chaining when: - GraphQL.configure().goodFaithIntrospection() + GraphQL.extraordinaryConfigure().goodFaithIntrospection() .enabledJvmWide(true) .then().goodFaithIntrospection() .enabledJvmWide(false) then: - !GraphQL.configure().goodFaithIntrospection().isEnabledJvmWide() + !GraphQL.extraordinaryConfigure().goodFaithIntrospection().isEnabledJvmWide() } def "can set defer configuration on graphql context objects"() { when: def graphqlContextBuilder = GraphQLContext.newContext() - GraphQL.configure(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(true) + GraphQL.extraordinaryConfigure(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(true) then: graphqlContextBuilder.build().get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == true - GraphQL.configure(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() + GraphQL.extraordinaryConfigure(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() when: graphqlContextBuilder = GraphQLContext.newContext() - GraphQL.configure(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(false) + GraphQL.extraordinaryConfigure(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(false) then: graphqlContextBuilder.build().get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false - !GraphQL.configure(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() + !GraphQL.extraordinaryConfigure(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() when: def graphqlContext = GraphQLContext.newContext().build() - GraphQL.configure(graphqlContext).incrementalSupport().enableIncrementalSupport(true) + GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().enableIncrementalSupport(true) then: graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == true - GraphQL.configure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() when: graphqlContext = GraphQLContext.newContext().build() - GraphQL.configure(graphqlContext).incrementalSupport().enableIncrementalSupport(false) + GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().enableIncrementalSupport(false) then: graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false - !GraphQL.configure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + !GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() when: graphqlContext = GraphQLContext.newContext().build() // just to show we we can navigate the DSL - GraphQL.configure(graphqlContext) + GraphQL.extraordinaryConfigure(graphqlContext) .incrementalSupport() .enableIncrementalSupport(false) .enableIncrementalSupport(true) @@ -118,6 +118,6 @@ class GraphQLConfigurationTest extends Specification { then: graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false - !GraphQL.configure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + !GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() } } From f4b3c3f0007d78550728b9cdacf3e5850b821670 Mon Sep 17 00:00:00 2001 From: bbaker Date: Mon, 5 May 2025 15:43:04 +1000 Subject: [PATCH 6/9] A generalised configuration mechanism - changed it to have graphql context wrapping mechanism - renamed as unusual --- src/main/java/graphql/GraphQL.java | 30 ++++++------- ....java => GraphQLUnusualConfiguration.java} | 22 +++++----- ...=> GraphQLUnusualConfigurationTest.groovy} | 44 +++++++++---------- 3 files changed, 48 insertions(+), 48 deletions(-) rename src/main/java/graphql/{GraphQLExtraordinaryConfiguration.java => GraphQLUnusualConfiguration.java} (93%) rename src/test/groovy/graphql/config/{GraphQLExtraordinaryConfigurationTest.groovy => GraphQLUnusualConfigurationTest.groovy} (56%) diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index 2cf7e1241..5db9374a4 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -85,43 +85,43 @@ public class GraphQL { /** - * This allows you to control "extraordinary" aspects of the GraphQL system + * This allows you to control "unusual" aspects of the GraphQL system * including some JVM wide settings *

- * This is named extraordinary because in general we don't expect you to + * This is named unusual because in general we don't expect you to * have to make ths configuration by default, but you can opt into certain features * or disable them if you want to. * - * @return a {@link GraphQLExtraordinaryConfiguration} object + * @return a {@link GraphQLUnusualConfiguration} object */ - public static GraphQLExtraordinaryConfiguration extraordinaryConfigure() { - return new GraphQLExtraordinaryConfiguration(); + public static GraphQLUnusualConfiguration unusualConfiguration() { + return new GraphQLUnusualConfiguration(); } /** - * This allows you to control "extraordinary" per execution aspects of the GraphQL system + * This allows you to control "unusual" per execution aspects of the GraphQL system *

- * This is named extraordinary because in general we don't expect you to + * This is named unusual because in general we don't expect you to * have to make ths configuration by default, but you can opt into certain features * or disable them if you want to. * - * @return a {@link GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration} object + * @return a {@link GraphQLUnusualConfiguration.GraphQLContextConfiguration} object */ - public static GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration extraordinaryConfigure(GraphQLContext graphQLContext) { - return new GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration(graphQLContext); + public static GraphQLUnusualConfiguration.GraphQLContextConfiguration unusualConfiguration(GraphQLContext graphQLContext) { + return new GraphQLUnusualConfiguration.GraphQLContextConfiguration(graphQLContext); } /** - * This allows you to control "extraordinary" per execution aspects of the GraphQL system + * This allows you to control "unusual" per execution aspects of the GraphQL system *

- * This is named extraordinary because in general we don't expect you to + * This is named unusual because in general we don't expect you to * have to make ths configuration by default, but you can opt into certain features * or disable them if you want to. * - * @return a {@link GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration} object + * @return a {@link GraphQLUnusualConfiguration.GraphQLContextConfiguration} object */ - public static GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration extraordinaryConfigure(GraphQLContext.Builder graphQLContextBuilder) { - return new GraphQLExtraordinaryConfiguration.GraphQLContextConfiguration(graphQLContextBuilder); + public static GraphQLUnusualConfiguration.GraphQLContextConfiguration unusualConfiguration(GraphQLContext.Builder graphQLContextBuilder) { + return new GraphQLUnusualConfiguration.GraphQLContextConfiguration(graphQLContextBuilder); } private final GraphQLSchema graphQLSchema; diff --git a/src/main/java/graphql/GraphQLExtraordinaryConfiguration.java b/src/main/java/graphql/GraphQLUnusualConfiguration.java similarity index 93% rename from src/main/java/graphql/GraphQLExtraordinaryConfiguration.java rename to src/main/java/graphql/GraphQLUnusualConfiguration.java index 32be0485d..8698175cc 100644 --- a/src/main/java/graphql/GraphQLExtraordinaryConfiguration.java +++ b/src/main/java/graphql/GraphQLUnusualConfiguration.java @@ -7,16 +7,16 @@ import static graphql.Assert.assertNotNull; /** - * This allows you to control "extraordinary" aspects of the GraphQL system + * This allows you to control "unusual" aspects of the GraphQL system * including some JVM wide settings and some per execution settings * as well as experimental ones. *

- * This is named extraordinary because in general we don't expect you to - * have to make ths configuration by default but you can opt into certain features + * This is named unusual because in general we don't expect you to + * have to make ths configuration by default, but you can opt into certain features * or disable them if you want to. */ -public class GraphQLExtraordinaryConfiguration { - GraphQLExtraordinaryConfiguration() { +public class GraphQLUnusualConfiguration { + GraphQLUnusualConfiguration() { } /** @@ -42,23 +42,23 @@ public GoodFaithIntrospectionCfg goodFaithIntrospection() { } private static class BaseCfg { - protected final GraphQLExtraordinaryConfiguration configuration; + protected final GraphQLUnusualConfiguration configuration; - private BaseCfg(GraphQLExtraordinaryConfiguration configuration) { + private BaseCfg(GraphQLUnusualConfiguration configuration) { this.configuration = configuration; } /** * @return an element that allows you to chain multiple configuration elements */ - public GraphQLExtraordinaryConfiguration then() { + public GraphQLUnusualConfiguration then() { return configuration; } } public static class ParserCfg extends BaseCfg { - private ParserCfg(GraphQLExtraordinaryConfiguration configuration) { + private ParserCfg(GraphQLUnusualConfiguration configuration) { super(configuration); } @@ -162,7 +162,7 @@ public ParserCfg setDefaultSdlParserOptions(ParserOptions options) { } public static class PropertyDataFetcherCfg extends BaseCfg { - private PropertyDataFetcherCfg(GraphQLExtraordinaryConfiguration configuration) { + private PropertyDataFetcherCfg(GraphQLUnusualConfiguration configuration) { super(configuration); } @@ -205,7 +205,7 @@ public boolean setUseNegativeCache(boolean flag) { } public static class GoodFaithIntrospectionCfg extends BaseCfg { - private GoodFaithIntrospectionCfg(GraphQLExtraordinaryConfiguration configuration) { + private GoodFaithIntrospectionCfg(GraphQLUnusualConfiguration configuration) { super(configuration); } diff --git a/src/test/groovy/graphql/config/GraphQLExtraordinaryConfigurationTest.groovy b/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy similarity index 56% rename from src/test/groovy/graphql/config/GraphQLExtraordinaryConfigurationTest.groovy rename to src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy index 84987d241..3052ee673 100644 --- a/src/test/groovy/graphql/config/GraphQLExtraordinaryConfigurationTest.groovy +++ b/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy @@ -10,7 +10,7 @@ import spock.lang.Specification import static graphql.parser.ParserOptions.newParserOptions -class GraphQLExtraordinaryConfigurationTest extends Specification { +class GraphQLUnusualConfigurationTest extends Specification { def startingParserOptions = ParserOptions.getDefaultParserOptions() def startingState = GoodFaithIntrospection.isEnabledJvmWide() @@ -25,8 +25,8 @@ class GraphQLExtraordinaryConfigurationTest extends Specification { def "can set parser configurations"() { when: def parserOptions = newParserOptions().maxRuleDepth(99).build() - GraphQL.extraordinaryConfigure().parsing().setDefaultParserOptions(parserOptions) - def defaultParserOptions = GraphQL.extraordinaryConfigure().parsing().getDefaultParserOptions() + GraphQL.unusualConfiguration().parsing().setDefaultParserOptions(parserOptions) + def defaultParserOptions = GraphQL.unusualConfiguration().parsing().getDefaultParserOptions() then: defaultParserOptions.getMaxRuleDepth() == 99 @@ -34,82 +34,82 @@ class GraphQLExtraordinaryConfigurationTest extends Specification { def "can set property data fetcher config"() { when: - def prevValue = GraphQL.extraordinaryConfigure().propertyDataFetching().setUseNegativeCache(false) + def prevValue = GraphQL.unusualConfiguration().propertyDataFetching().setUseNegativeCache(false) then: prevValue when: - prevValue = GraphQL.extraordinaryConfigure().propertyDataFetching().setUseNegativeCache(false) + prevValue = GraphQL.unusualConfiguration().propertyDataFetching().setUseNegativeCache(false) then: !prevValue when: - prevValue = GraphQL.extraordinaryConfigure().propertyDataFetching().setUseNegativeCache(true) + prevValue = GraphQL.unusualConfiguration().propertyDataFetching().setUseNegativeCache(true) then: !prevValue } def "can set good faith settings"() { when: - GraphQL.extraordinaryConfigure().goodFaithIntrospection().enabledJvmWide(false) + GraphQL.unusualConfiguration().goodFaithIntrospection().enabledJvmWide(false) then: - !GraphQL.extraordinaryConfigure().goodFaithIntrospection().isEnabledJvmWide() + !GraphQL.unusualConfiguration().goodFaithIntrospection().isEnabledJvmWide() when: - GraphQL.extraordinaryConfigure().goodFaithIntrospection().enabledJvmWide(true) + GraphQL.unusualConfiguration().goodFaithIntrospection().enabledJvmWide(true) then: - GraphQL.extraordinaryConfigure().goodFaithIntrospection().isEnabledJvmWide() + GraphQL.unusualConfiguration().goodFaithIntrospection().isEnabledJvmWide() // showing chaining when: - GraphQL.extraordinaryConfigure().goodFaithIntrospection() + GraphQL.unusualConfiguration().goodFaithIntrospection() .enabledJvmWide(true) .then().goodFaithIntrospection() .enabledJvmWide(false) then: - !GraphQL.extraordinaryConfigure().goodFaithIntrospection().isEnabledJvmWide() + !GraphQL.unusualConfiguration().goodFaithIntrospection().isEnabledJvmWide() } def "can set defer configuration on graphql context objects"() { when: def graphqlContextBuilder = GraphQLContext.newContext() - GraphQL.extraordinaryConfigure(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(true) + GraphQL.unusualConfiguration(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(true) then: graphqlContextBuilder.build().get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == true - GraphQL.extraordinaryConfigure(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() + GraphQL.unusualConfiguration(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() when: graphqlContextBuilder = GraphQLContext.newContext() - GraphQL.extraordinaryConfigure(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(false) + GraphQL.unusualConfiguration(graphqlContextBuilder).incrementalSupport().enableIncrementalSupport(false) then: graphqlContextBuilder.build().get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false - !GraphQL.extraordinaryConfigure(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() + !GraphQL.unusualConfiguration(graphqlContextBuilder).incrementalSupport().isIncrementalSupportEnabled() when: def graphqlContext = GraphQLContext.newContext().build() - GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().enableIncrementalSupport(true) + GraphQL.unusualConfiguration(graphqlContext).incrementalSupport().enableIncrementalSupport(true) then: graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == true - GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + GraphQL.unusualConfiguration(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() when: graphqlContext = GraphQLContext.newContext().build() - GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().enableIncrementalSupport(false) + GraphQL.unusualConfiguration(graphqlContext).incrementalSupport().enableIncrementalSupport(false) then: graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false - !GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + !GraphQL.unusualConfiguration(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() when: graphqlContext = GraphQLContext.newContext().build() // just to show we we can navigate the DSL - GraphQL.extraordinaryConfigure(graphqlContext) + GraphQL.unusualConfiguration(graphqlContext) .incrementalSupport() .enableIncrementalSupport(false) .enableIncrementalSupport(true) @@ -118,6 +118,6 @@ class GraphQLExtraordinaryConfigurationTest extends Specification { then: graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false - !GraphQL.extraordinaryConfigure(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + !GraphQL.unusualConfiguration(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() } } From 2f9728e712d7e756f27dc63024be41e46f14ef1c Mon Sep 17 00:00:00 2001 From: bbaker Date: Tue, 6 May 2025 14:58:49 +1000 Subject: [PATCH 7/9] A generalised configuration mechanism - changed it to have graphql context wrapping mechanism - renamed as config --- .../graphql/GraphQLUnusualConfiguration.java | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/java/graphql/GraphQLUnusualConfiguration.java b/src/main/java/graphql/GraphQLUnusualConfiguration.java index 8698175cc..3fbd24a39 100644 --- a/src/main/java/graphql/GraphQLUnusualConfiguration.java +++ b/src/main/java/graphql/GraphQLUnusualConfiguration.java @@ -22,29 +22,29 @@ public class GraphQLUnusualConfiguration { /** * @return an element that allows you to control JVM wide parsing configuration */ - public ParserCfg parsing() { - return new ParserCfg(this); + public ParserConfig parsing() { + return new ParserConfig(this); } /** * @return an element that allows you to control JVM wide {@link graphql.schema.PropertyDataFetcher} configuration */ - public PropertyDataFetcherCfg propertyDataFetching() { - return new PropertyDataFetcherCfg(this); + public PropertyDataFetcherConfig propertyDataFetching() { + return new PropertyDataFetcherConfig(this); } /** * @return an element that allows you to control JVM wide configuration * of {@link graphql.introspection.GoodFaithIntrospection} */ - public GoodFaithIntrospectionCfg goodFaithIntrospection() { - return new GoodFaithIntrospectionCfg(this); + public GoodFaithIntrospectionConfig goodFaithIntrospection() { + return new GoodFaithIntrospectionConfig(this); } - private static class BaseCfg { + private static class BaseConfig { protected final GraphQLUnusualConfiguration configuration; - private BaseCfg(GraphQLUnusualConfiguration configuration) { + private BaseConfig(GraphQLUnusualConfiguration configuration) { this.configuration = configuration; } @@ -56,9 +56,9 @@ public GraphQLUnusualConfiguration then() { } } - public static class ParserCfg extends BaseCfg { + public static class ParserConfig extends BaseConfig { - private ParserCfg(GraphQLUnusualConfiguration configuration) { + private ParserConfig(GraphQLUnusualConfiguration configuration) { super(configuration); } @@ -92,7 +92,7 @@ public ParserOptions getDefaultParserOptions() { * @see graphql.language.IgnoredChar * @see graphql.language.SourceLocation */ - public ParserCfg setDefaultParserOptions(ParserOptions options) { + public ParserConfig setDefaultParserOptions(ParserOptions options) { ParserOptions.setDefaultParserOptions(options); return this; } @@ -122,7 +122,7 @@ public ParserOptions getDefaultOperationParserOptions() { * @see graphql.language.IgnoredChar * @see graphql.language.SourceLocation */ - public ParserCfg setDefaultOperationParserOptions(ParserOptions options) { + public ParserConfig setDefaultOperationParserOptions(ParserOptions options) { ParserOptions.setDefaultOperationParserOptions(options); return this; } @@ -155,14 +155,14 @@ public ParserOptions getDefaultSdlParserOptions() { * @see graphql.language.IgnoredChar * @see graphql.language.SourceLocation */ - public ParserCfg setDefaultSdlParserOptions(ParserOptions options) { + public ParserConfig setDefaultSdlParserOptions(ParserOptions options) { ParserOptions.setDefaultSdlParserOptions(options); return this; } } - public static class PropertyDataFetcherCfg extends BaseCfg { - private PropertyDataFetcherCfg(GraphQLUnusualConfiguration configuration) { + public static class PropertyDataFetcherConfig extends BaseConfig { + private PropertyDataFetcherConfig(GraphQLUnusualConfiguration configuration) { super(configuration); } @@ -175,7 +175,7 @@ private PropertyDataFetcherCfg(GraphQLUnusualConfiguration configuration) { * be developed to do just that. */ @SuppressWarnings("unused") - public PropertyDataFetcherCfg clearReflectionCache() { + public PropertyDataFetcherConfig clearReflectionCache() { PropertyDataFetcherHelper.clearReflectionCache(); return this; } @@ -204,8 +204,8 @@ public boolean setUseNegativeCache(boolean flag) { } } - public static class GoodFaithIntrospectionCfg extends BaseCfg { - private GoodFaithIntrospectionCfg(GraphQLUnusualConfiguration configuration) { + public static class GoodFaithIntrospectionConfig extends BaseConfig { + private GoodFaithIntrospectionConfig(GraphQLUnusualConfiguration configuration) { super(configuration); } @@ -223,7 +223,7 @@ public boolean isEnabledJvmWide() { * * @return the previous state */ - public GoodFaithIntrospectionCfg enabledJvmWide(boolean enabled) { + public GoodFaithIntrospectionConfig enabledJvmWide(boolean enabled) { GoodFaithIntrospection.enabledJvmWide(enabled); return this; } @@ -253,8 +253,8 @@ public static class GraphQLContextConfiguration { /** * @return an element that allows you to control incremental support, that is @defer configuration */ - public IncrementalSupportCfg incrementalSupport() { - return new IncrementalSupportCfg(this); + public IncrementalSupportConfig incrementalSupport() { + return new IncrementalSupportConfig(this); } private void put(String named, Object value) { @@ -274,10 +274,10 @@ private boolean getBoolean(String named) { } } - private static class BaseContextCfg { + private static class BaseContextConfig { protected final GraphQLContextConfiguration contextConfig; - private BaseContextCfg(GraphQLContextConfiguration contextConfig) { + private BaseContextConfig(GraphQLContextConfiguration contextConfig) { this.contextConfig = contextConfig; } @@ -289,8 +289,8 @@ public GraphQLContextConfiguration then() { } } - public static class IncrementalSupportCfg extends BaseContextCfg { - private IncrementalSupportCfg(GraphQLContextConfiguration contextConfig) { + public static class IncrementalSupportConfig extends BaseContextConfig { + private IncrementalSupportConfig(GraphQLContextConfiguration contextConfig) { super(contextConfig); } @@ -305,7 +305,7 @@ public boolean isIncrementalSupportEnabled() { * This controls whether @defer and @stream behaviour is enabled for this execution. */ @ExperimentalApi - public IncrementalSupportCfg enableIncrementalSupport(boolean enable) { + public IncrementalSupportConfig enableIncrementalSupport(boolean enable) { contextConfig.put(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT, enable); return this; } From bdb351ab7da8d406a98b6a040dadf3a7be8551b6 Mon Sep 17 00:00:00 2001 From: bbaker Date: Mon, 19 May 2025 12:33:28 +1000 Subject: [PATCH 8/9] Added data loader config --- .../graphql/GraphQLUnusualConfiguration.java | 83 +++++++++++++++++++ .../DataLoaderDispatchingContextKeys.java | 4 +- .../GraphQLUnusualConfigurationTest.groovy | 64 +++++++++++++- 3 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/main/java/graphql/GraphQLUnusualConfiguration.java b/src/main/java/graphql/GraphQLUnusualConfiguration.java index 3fbd24a39..1f99b5848 100644 --- a/src/main/java/graphql/GraphQLUnusualConfiguration.java +++ b/src/main/java/graphql/GraphQLUnusualConfiguration.java @@ -1,10 +1,16 @@ package graphql; +import graphql.execution.instrumentation.dataloader.DelayedDataLoaderDispatcherExecutorFactory; import graphql.introspection.GoodFaithIntrospection; import graphql.parser.ParserOptions; import graphql.schema.PropertyDataFetcherHelper; +import java.time.Duration; + import static graphql.Assert.assertNotNull; +import static graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys.DELAYED_DATA_LOADER_BATCH_WINDOW_SIZE_NANO_SECONDS; +import static graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys.DELAYED_DATA_LOADER_DISPATCHING_EXECUTOR_FACTORY; +import static graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING; /** * This allows you to control "unusual" aspects of the GraphQL system @@ -235,6 +241,7 @@ public GoodFaithIntrospectionConfig enabledJvmWide(boolean enabled) { * =============================================== */ + @SuppressWarnings("DataFlowIssue") public static class GraphQLContextConfiguration { // it will be one or the other types of GraphQLContext private final GraphQLContext graphQLContext; @@ -257,6 +264,14 @@ public IncrementalSupportConfig incrementalSupport() { return new IncrementalSupportConfig(this); } + /** + * @return an element that allows you to precisely control {@link org.dataloader.DataLoader} behavior + * in graphql-java. + */ + public DataloaderConfig dataloaderConfig() { + return new DataloaderConfig(this); + } + private void put(String named, Object value) { if (graphQLContext != null) { graphQLContext.put(named, value); @@ -272,6 +287,15 @@ private boolean getBoolean(String named) { return assertNotNull(graphQLContextBuilder).getBoolean(named); } } + + private T get(String named) { + if (graphQLContext != null) { + return graphQLContext.get(named); + } else { + //noinspection unchecked + return (T) assertNotNull(graphQLContextBuilder).get(named); + } + } } private static class BaseContextConfig { @@ -310,4 +334,63 @@ public IncrementalSupportConfig enableIncrementalSupport(boolean enable) { return this; } } + + public static class DataloaderConfig extends BaseContextConfig { + private DataloaderConfig(GraphQLContextConfiguration contextConfig) { + super(contextConfig); + } + + /** + * @return true if @defer and @stream behaviour is enabled for this execution. + */ + public boolean isDataLoaderChainingEnabled() { + return contextConfig.getBoolean(ENABLE_DATA_LOADER_CHAINING); + } + + /** + * Enables the ability that chained DataLoaders are dispatched automatically. + */ + @ExperimentalApi + public DataloaderConfig enableDataLoaderChaining(boolean enable) { + contextConfig.put(ENABLE_DATA_LOADER_CHAINING, enable); + return this; + } + + /** + * @return the batch window duration size for delayed DataLoaders. + */ + public Duration delayedDataLoaderBatchWindowSize() { + Long d = contextConfig.get(DELAYED_DATA_LOADER_BATCH_WINDOW_SIZE_NANO_SECONDS); + return d != null ? Duration.ofNanos(d) : null; + } + + /** + * Sets the batch window duration size for delayed DataLoaders. + * That is for DataLoaders, that are not batched as part of the normal per level + * dispatching, because they were created after the level was already dispatched. + */ + @ExperimentalApi + public DataloaderConfig delayedDataLoaderBatchWindowSize(Duration batchWindowSize) { + contextConfig.put(DELAYED_DATA_LOADER_BATCH_WINDOW_SIZE_NANO_SECONDS, batchWindowSize.toNanos()); + return this; + } + + /** + * @return the instance of {@link DelayedDataLoaderDispatcherExecutorFactory} that is used to create the + * {@link java.util.concurrent.ScheduledExecutorService} for the delayed DataLoader dispatching. + */ + public DelayedDataLoaderDispatcherExecutorFactory delayedDataLoaderExecutorFactory() { + return contextConfig.get(DELAYED_DATA_LOADER_DISPATCHING_EXECUTOR_FACTORY); + } + + /** + * Sets the instance of {@link DelayedDataLoaderDispatcherExecutorFactory} that is used to create the + * {@link java.util.concurrent.ScheduledExecutorService} for the delayed DataLoader dispatching. + */ + @ExperimentalApi + public DataloaderConfig delayedDataLoaderExecutorFactory(DelayedDataLoaderDispatcherExecutorFactory delayedDataLoaderDispatcherExecutorFactory) { + contextConfig.put(DELAYED_DATA_LOADER_DISPATCHING_EXECUTOR_FACTORY, delayedDataLoaderDispatcherExecutorFactory); + return this; + } + } } diff --git a/src/main/java/graphql/execution/instrumentation/dataloader/DataLoaderDispatchingContextKeys.java b/src/main/java/graphql/execution/instrumentation/dataloader/DataLoaderDispatchingContextKeys.java index 2aea2460e..e85322526 100644 --- a/src/main/java/graphql/execution/instrumentation/dataloader/DataLoaderDispatchingContextKeys.java +++ b/src/main/java/graphql/execution/instrumentation/dataloader/DataLoaderDispatchingContextKeys.java @@ -1,8 +1,8 @@ package graphql.execution.instrumentation.dataloader; -import graphql.ExperimentalApi; import graphql.GraphQLContext; +import graphql.Internal; import org.jspecify.annotations.NullMarked; import java.time.Duration; @@ -10,7 +10,7 @@ /** * GraphQLContext keys related to DataLoader dispatching. */ -@ExperimentalApi +@Internal @NullMarked public final class DataLoaderDispatchingContextKeys { private DataLoaderDispatchingContextKeys() { diff --git a/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy b/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy index 3052ee673..fe4f557f4 100644 --- a/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy +++ b/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy @@ -3,11 +3,15 @@ package graphql.config import graphql.ExperimentalApi import graphql.GraphQL import graphql.GraphQLContext +import graphql.execution.instrumentation.dataloader.DataLoaderDispatchingContextKeys +import graphql.execution.instrumentation.dataloader.DelayedDataLoaderDispatcherExecutorFactory import graphql.introspection.GoodFaithIntrospection import graphql.parser.ParserOptions import graphql.schema.PropertyDataFetcherHelper import spock.lang.Specification +import java.time.Duration + import static graphql.parser.ParserOptions.newParserOptions class GraphQLUnusualConfigurationTest extends Specification { @@ -118,6 +122,64 @@ class GraphQLUnusualConfigurationTest extends Specification { then: graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT) == false - !GraphQL.unusualConfiguration(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + !GraphQL.unusualConfiguration(graphqlContext).incrementalSupport().isIncrementalSupportEnabled() + } + + def "can set data loader chaining config for enablement"() { + when: + def graphqlContextBuilder = GraphQLContext.newContext() + GraphQL.unusualConfiguration(graphqlContextBuilder).dataloaderConfig().enableDataLoaderChaining(true) + + then: + graphqlContextBuilder.build().get(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING) == true + GraphQL.unusualConfiguration(graphqlContextBuilder).dataloaderConfig().isDataLoaderChainingEnabled() + + + when: + def graphqlContext = GraphQLContext.newContext().build() + GraphQL.unusualConfiguration(graphqlContext).dataloaderConfig().enableDataLoaderChaining(true) + + then: + graphqlContext.get(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING) == true + GraphQL.unusualConfiguration(graphqlContext).dataloaderConfig().isDataLoaderChainingEnabled() + } + + def "can set data loader chaining config for extra config"() { + when: + def graphqlContext = GraphQLContext.newContext().build() + GraphQL.unusualConfiguration(graphqlContext).dataloaderConfig().delayedDataLoaderBatchWindowSize(Duration.ofMillis(10)) + + then: + graphqlContext.get(DataLoaderDispatchingContextKeys.DELAYED_DATA_LOADER_BATCH_WINDOW_SIZE_NANO_SECONDS) == Duration.ofMillis(10).toNanos() + GraphQL.unusualConfiguration(graphqlContext).dataloaderConfig().delayedDataLoaderBatchWindowSize() == Duration.ofMillis(10) + + when: + DelayedDataLoaderDispatcherExecutorFactory factory = {} + graphqlContext = GraphQLContext.newContext().build() + GraphQL.unusualConfiguration(graphqlContext).dataloaderConfig().delayedDataLoaderExecutorFactory(factory) + + then: + graphqlContext.get(DataLoaderDispatchingContextKeys.DELAYED_DATA_LOADER_DISPATCHING_EXECUTOR_FACTORY) == factory + GraphQL.unusualConfiguration(graphqlContext).dataloaderConfig().delayedDataLoaderExecutorFactory() == factory + + when: + graphqlContext = GraphQLContext.newContext().build() + // just to show we we can navigate the DSL + GraphQL.unusualConfiguration(graphqlContext) + .incrementalSupport() + .enableIncrementalSupport(false) + .enableIncrementalSupport(true) + .then() + .dataloaderConfig() + .enableDataLoaderChaining(true) + .then() + .dataloaderConfig() + .delayedDataLoaderBatchWindowSize(Duration.ofMillis(10)) + .delayedDataLoaderExecutorFactory(factory) + + then: + graphqlContext.get(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING) == true + graphqlContext.get(DataLoaderDispatchingContextKeys.DELAYED_DATA_LOADER_BATCH_WINDOW_SIZE_NANO_SECONDS) == Duration.ofMillis(10).toNanos() + graphqlContext.get(DataLoaderDispatchingContextKeys.DELAYED_DATA_LOADER_DISPATCHING_EXECUTOR_FACTORY) == factory } } From 3c5c911485cfd3dc829db45536cb96dbf25f8d54 Mon Sep 17 00:00:00 2001 From: bbaker Date: Mon, 19 May 2025 14:45:04 +1000 Subject: [PATCH 9/9] Added ExecutionInput support --- src/main/java/graphql/ExecutionInput.java | 8 ++++++ src/main/java/graphql/GraphQL.java | 26 +++++++++++++++++++ .../GraphQLUnusualConfigurationTest.groovy | 25 ++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/src/main/java/graphql/ExecutionInput.java b/src/main/java/graphql/ExecutionInput.java index 59efc4f48..7b0a02b2e 100644 --- a/src/main/java/graphql/ExecutionInput.java +++ b/src/main/java/graphql/ExecutionInput.java @@ -222,6 +222,14 @@ public static class Builder { private ExecutionId executionId; private AtomicBoolean cancelled = new AtomicBoolean(false); + /** + * Package level access to the graphql context + * @return shhh but it's the graphql context + */ + GraphQLContext graphQLContext() { + return graphQLContext; + } + public Builder query(String query) { this.query = assertNotNull(query, () -> "query can't be null"); return this; diff --git a/src/main/java/graphql/GraphQL.java b/src/main/java/graphql/GraphQL.java index ce4abd32b..d4d8fb3b6 100644 --- a/src/main/java/graphql/GraphQL.java +++ b/src/main/java/graphql/GraphQL.java @@ -99,6 +99,32 @@ public static GraphQLUnusualConfiguration unusualConfiguration() { return new GraphQLUnusualConfiguration(); } + /** + * This allows you to control "unusual" per execution aspects of the GraphQL system + *

+ * This is named unusual because in general we don't expect you to + * have to make ths configuration by default, but you can opt into certain features + * or disable them if you want to. + * + * @return a {@link GraphQLUnusualConfiguration.GraphQLContextConfiguration} object + */ + public static GraphQLUnusualConfiguration.GraphQLContextConfiguration unusualConfiguration(ExecutionInput executionInput) { + return new GraphQLUnusualConfiguration.GraphQLContextConfiguration(executionInput.getGraphQLContext()); + } + + /** + * This allows you to control "unusual" per execution aspects of the GraphQL system + *

+ * This is named unusual because in general we don't expect you to + * have to make ths configuration by default, but you can opt into certain features + * or disable them if you want to. + * + * @return a {@link GraphQLUnusualConfiguration.GraphQLContextConfiguration} object + */ + public static GraphQLUnusualConfiguration.GraphQLContextConfiguration unusualConfiguration(ExecutionInput.Builder executionInputBuilder) { + return new GraphQLUnusualConfiguration.GraphQLContextConfiguration(executionInputBuilder.graphQLContext()); + } + /** * This allows you to control "unusual" per execution aspects of the GraphQL system *

diff --git a/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy b/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy index fe4f557f4..765a608c0 100644 --- a/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy +++ b/src/test/groovy/graphql/config/GraphQLUnusualConfigurationTest.groovy @@ -1,5 +1,6 @@ package graphql.config +import graphql.ExecutionInput import graphql.ExperimentalApi import graphql.GraphQL import graphql.GraphQLContext @@ -182,4 +183,28 @@ class GraphQLUnusualConfigurationTest extends Specification { graphqlContext.get(DataLoaderDispatchingContextKeys.DELAYED_DATA_LOADER_BATCH_WINDOW_SIZE_NANO_SECONDS) == Duration.ofMillis(10).toNanos() graphqlContext.get(DataLoaderDispatchingContextKeys.DELAYED_DATA_LOADER_DISPATCHING_EXECUTOR_FACTORY) == factory } + + def "we can access via the ExecutionInput"() { + when: + def eiBuilder = ExecutionInput.newExecutionInput("query q {f}") + + GraphQL.unusualConfiguration(eiBuilder) + .dataloaderConfig() + .enableDataLoaderChaining(true) + + def ei = eiBuilder.build() + + then: + ei.getGraphQLContext().get(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING) == true + + when: + ei = ExecutionInput.newExecutionInput("query q {f}").build() + + GraphQL.unusualConfiguration(ei) + .dataloaderConfig() + .enableDataLoaderChaining(true) + + then: + ei.getGraphQLContext().get(DataLoaderDispatchingContextKeys.ENABLE_DATA_LOADER_CHAINING) == true + } }