From 44a681fe04a21ea9159c0071b2214784ef2f823b Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 24 Feb 2024 14:46:44 +1100 Subject: [PATCH 01/29] Demonstrate schema extension is not printed --- src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy b/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy index c0c83ddca8..dd793e9e69 100644 --- a/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy +++ b/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy @@ -1345,6 +1345,9 @@ type Query { def "can print a schema as AST elements"() { def sdl = ''' directive @directive1 on SCALAR + directive @schemaDirective on SCHEMA + extend schema @schemaDirective + type Query { foo : String } @@ -1449,6 +1452,7 @@ directive @deprecated( ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION directive @directive1 on SCALAR +directive @schemaDirective on SCHEMA "Directs the executor to include this field or fragment only when the `if` argument is true" directive @include( @@ -1534,6 +1538,8 @@ extend input Input { extend input Input { faz: String } + +extend schema @schemaDirective ''' when: From 6922064a62ba6430b3d75c90ce487aca5bbef06d Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 24 Feb 2024 14:50:47 +1100 Subject: [PATCH 02/29] Move directive definition location --- src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy b/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy index dd793e9e69..dd679a88c5 100644 --- a/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy +++ b/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy @@ -1452,7 +1452,6 @@ directive @deprecated( ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION directive @directive1 on SCALAR -directive @schemaDirective on SCHEMA "Directs the executor to include this field or fragment only when the `if` argument is true" directive @include( @@ -1463,6 +1462,8 @@ directive @include( "Indicates an Input Object is a OneOf Input Object." directive @oneOf on INPUT_OBJECT +directive @schemaDirective on SCHEMA + "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip( "Skipped when true." From 4f053fa8c792b2193c14e40de32e9ef6cc76ea6c Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:56:27 +1000 Subject: [PATCH 03/29] Update readme badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e065da305d..893dd2f6f7 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is a [GraphQL](https://github.com/graphql/graphql-spec) Java implementation Latest build in Maven central: https://repo1.maven.org/maven2/com/graphql-java/graphql-java/ [![Build](https://github.com/graphql-java/graphql-java/actions/workflows/master.yml/badge.svg)](https://github.com/graphql-java/graphql-java/actions/workflows/master.yml) -[![Latest Release](https://img.shields.io/maven-central/v/com.graphql-java/graphql-java?versionPrefix=21.)](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java/) +[![Latest Release](https://img.shields.io/maven-central/v/com.graphql-java/graphql-java?versionPrefix=22.)](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java/) [![Latest Snapshot](https://img.shields.io/maven-central/v/com.graphql-java/graphql-java?label=maven-central%20snapshot&versionPrefix=0)](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java/) [![MIT licensed](https://img.shields.io/badge/license-MIT-green)](https://github.com/graphql-java/graphql-java/blob/master/LICENSE.md) From b3e5a057d6028bf35b42e00ca80f5edd4d4aa3f9 Mon Sep 17 00:00:00 2001 From: Franklin Wang Date: Thu, 18 Apr 2024 11:29:14 +1000 Subject: [PATCH 04/29] Always include incremental props even if null --- src/main/java/graphql/incremental/DeferPayload.java | 10 +++------- src/main/java/graphql/incremental/StreamPayload.java | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/main/java/graphql/incremental/DeferPayload.java b/src/main/java/graphql/incremental/DeferPayload.java index 58e5ac0994..8d6047de3a 100644 --- a/src/main/java/graphql/incremental/DeferPayload.java +++ b/src/main/java/graphql/incremental/DeferPayload.java @@ -22,12 +22,12 @@ private DeferPayload(Object data, List path, String label, List the type to cast the result to + * @return the resolved data */ @Nullable public T getData() { - //noinspection unchecked + // noinspection unchecked return (T) this.data; } @@ -37,11 +37,7 @@ public T getData() { @Override public Map toSpecification() { Map map = new LinkedHashMap<>(super.toSpecification()); - - if (data != null) { - map.put("data", data); - } - + map.put("data", data); return map; } diff --git a/src/main/java/graphql/incremental/StreamPayload.java b/src/main/java/graphql/incremental/StreamPayload.java index e8bdfcf85c..88e1e7b543 100644 --- a/src/main/java/graphql/incremental/StreamPayload.java +++ b/src/main/java/graphql/incremental/StreamPayload.java @@ -21,12 +21,12 @@ private StreamPayload(List items, List path, String label, List< } /** - * @return the resolved list of items * @param the type to cast the result to + * @return the resolved list of items */ @Nullable public List getItems() { - //noinspection unchecked + // noinspection unchecked return (List) this.items; } @@ -36,11 +36,7 @@ public List getItems() { @Override public Map toSpecification() { Map map = new LinkedHashMap<>(super.toSpecification()); - - if (items != null) { - map.put("items", items); - } - + map.put("items", items); return map; } From ef6bb78f370807cd1b3712074aad6c2cf04e06a3 Mon Sep 17 00:00:00 2001 From: Franklin Wang Date: Thu, 18 Apr 2024 14:10:33 +1000 Subject: [PATCH 05/29] Fix tests --- ...eferExecutionSupportIntegrationTest.groovy | 44 ++++++++++--------- .../incremental/DeferredCallTest.groovy | 1 + 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/test/groovy/graphql/execution/incremental/DeferExecutionSupportIntegrationTest.groovy b/src/test/groovy/graphql/execution/incremental/DeferExecutionSupportIntegrationTest.groovy index e7844cc750..11e6e2d1f8 100644 --- a/src/test/groovy/graphql/execution/incremental/DeferExecutionSupportIntegrationTest.groovy +++ b/src/test/groovy/graphql/execution/incremental/DeferExecutionSupportIntegrationTest.groovy @@ -90,9 +90,10 @@ class DeferExecutionSupportIntegrationTest extends Specification { private static DataFetcher resolve(Object value, Integer sleepMs, boolean allowMultipleCalls) { return new DataFetcher() { boolean executed = false + @Override Object get(DataFetchingEnvironment environment) throws Exception { - if(executed && !allowMultipleCalls) { + if (executed && !allowMultipleCalls) { throw new IllegalStateException("This data fetcher can run only once") } executed = true @@ -298,7 +299,7 @@ class DeferExecutionSupportIntegrationTest extends Specification { }) def indexOfId3 = Iterables.indexOf(incrementalResults, { - it.incremental[0] == [path: ["post3"], label:"defer-id3", data: [id3: "3"]] + it.incremental[0] == [path: ["post3"], label: "defer-id3", data: [id3: "3"]] }) // Assert that both post3 and id3 are present @@ -349,7 +350,7 @@ class DeferExecutionSupportIntegrationTest extends Specification { def incrementalResults = getIncrementalResults(initialResult) then: - if(type == "Post") { + if (type == "Post") { assert incrementalResults == [ [ hasNext : false, @@ -481,8 +482,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { hasNext : false, incremental: [ [ - path : ["post"], - data : [summary: "A summary"] + path: ["post"], + data: [summary: "A summary"] ] ] ] @@ -519,8 +520,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { hasNext : false, incremental: [ [ - path : ["post"], - data : [resolvesToNull: null] + path: ["post"], + data: [resolvesToNull: null] ] ] ] @@ -560,8 +561,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { hasNext : false, incremental: [ [ - path : ["post"], - data : [summary: "A summary", text: "The full text"] + path: ["post"], + data: [summary: "A summary", text: "The full text"] ] ] ] @@ -739,8 +740,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { incremental: [ [ label: "summary-defer", - path: ["post"], - data: [summary: "A summary"] + path : ["post"], + data : [summary: "A summary"] ] ] ], @@ -749,8 +750,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { incremental: [ [ label: "text-defer", - path: ["post"], - data: [text: "The full text"] + path : ["post"], + data : [text: "The full text"] ] ] ] @@ -1211,8 +1212,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { hasNext : false, incremental: [ [ - path : ["post"], - data : [text: "The full text"], + path: ["post"], + data: [text: "The full text"], ] ] ] @@ -1270,8 +1271,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { hasNext : false, incremental: [ [ - path : ["post"], - data : [text: "The full text"], + path: ["post"], + data: [text: "The full text"], ] ] ] @@ -1329,8 +1330,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { hasNext : false, incremental: [ [ - path : ["post"], - data : [text: "The full text"], + path: ["post"], + data: [text: "The full text"], ] ] ] @@ -1371,6 +1372,7 @@ class DeferExecutionSupportIntegrationTest extends Specification { hasNext : true, incremental: [ [ + data : null, path : ["post"], errors: [ [ @@ -1388,8 +1390,8 @@ class DeferExecutionSupportIntegrationTest extends Specification { hasNext : false, incremental: [ [ - path : ["post"], - data : [text: "The full text"], + path: ["post"], + data: [text: "The full text"], ] ] ] diff --git a/src/test/groovy/graphql/execution/incremental/DeferredCallTest.groovy b/src/test/groovy/graphql/execution/incremental/DeferredCallTest.groovy index 252d6ca449..77da81298a 100644 --- a/src/test/groovy/graphql/execution/incremental/DeferredCallTest.groovy +++ b/src/test/groovy/graphql/execution/incremental/DeferredCallTest.groovy @@ -69,6 +69,7 @@ class DeferredCallTest extends Specification { then: deferPayload.toSpecification() == [ + data : null, path : ["path"], label : "my-label", errors: [ From dbcf871c601d31a7e1b65be5a84012dfbaa4028e Mon Sep 17 00:00:00 2001 From: Franklin Wang Date: Thu, 18 Apr 2024 14:15:15 +1000 Subject: [PATCH 06/29] Additional tests --- .../incremental/DeferPayloadTest.groovy | 21 +++++++++++++++++++ .../incremental/StreamPayloadTest.groovy | 20 ++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/test/groovy/graphql/incremental/DeferPayloadTest.groovy create mode 100644 src/test/groovy/graphql/incremental/StreamPayloadTest.groovy diff --git a/src/test/groovy/graphql/incremental/DeferPayloadTest.groovy b/src/test/groovy/graphql/incremental/DeferPayloadTest.groovy new file mode 100644 index 0000000000..90d6a0899c --- /dev/null +++ b/src/test/groovy/graphql/incremental/DeferPayloadTest.groovy @@ -0,0 +1,21 @@ +package graphql.incremental + + +import spock.lang.Specification + +class DeferPayloadTest extends Specification { + def "null data is included"() { + def payload = DeferPayload.newDeferredItem() + .data(null) + .build() + + when: + def spec = payload.toSpecification() + + then: + spec == [ + data : null, + path : null, + ] + } +} diff --git a/src/test/groovy/graphql/incremental/StreamPayloadTest.groovy b/src/test/groovy/graphql/incremental/StreamPayloadTest.groovy new file mode 100644 index 0000000000..bee0d88631 --- /dev/null +++ b/src/test/groovy/graphql/incremental/StreamPayloadTest.groovy @@ -0,0 +1,20 @@ +package graphql.incremental + +import spock.lang.Specification + +class StreamPayloadTest extends Specification { + def "null data is included"() { + def payload = StreamPayload.newStreamedItem() + .items(null) + .build() + + when: + def spec = payload.toSpecification() + + then: + spec == [ + items: null, + path : null, + ] + } +} From f3f59c9e69205ff71927e591d569cbee02853c2a Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Thu, 18 Apr 2024 20:46:17 +1000 Subject: [PATCH 07/29] Added field fetching method for Expedia --- .../graphql/execution/ExecutionStrategy.java | 4 +- .../ChainedInstrumentation.java | 43 +++++++++++- .../FieldFetchingInstrumentationContext.java | 68 +++++++++++++++++++ .../instrumentation/Instrumentation.java | 19 ++++++ .../NoContextChainedInstrumentation.java | 5 ++ .../SimplePerformantInstrumentation.java | 1 - 6 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 src/main/java/graphql/execution/instrumentation/FieldFetchingInstrumentationContext.java diff --git a/src/main/java/graphql/execution/ExecutionStrategy.java b/src/main/java/graphql/execution/ExecutionStrategy.java index 5e749360a5..b81a8f18ba 100644 --- a/src/main/java/graphql/execution/ExecutionStrategy.java +++ b/src/main/java/graphql/execution/ExecutionStrategy.java @@ -17,6 +17,7 @@ import graphql.execution.directives.QueryDirectivesImpl; import graphql.execution.incremental.DeferredExecutionSupport; import graphql.execution.instrumentation.ExecuteObjectInstrumentationContext; +import graphql.execution.instrumentation.FieldFetchingInstrumentationContext; import graphql.execution.instrumentation.Instrumentation; import graphql.execution.instrumentation.InstrumentationContext; import graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters; @@ -487,7 +488,7 @@ Async.CombinedBuilder getAsyncFieldValueInfo( Instrumentation instrumentation = executionContext.getInstrumentation(); InstrumentationFieldFetchParameters instrumentationFieldFetchParams = new InstrumentationFieldFetchParameters(executionContext, dataFetchingEnvironment, parameters, dataFetcher instanceof TrivialDataFetcher); - InstrumentationContext fetchCtx = nonNullCtx(instrumentation.beginFieldFetch(instrumentationFieldFetchParams, + FieldFetchingInstrumentationContext fetchCtx = FieldFetchingInstrumentationContext.nonNullCtx(instrumentation.beginFieldFetching(instrumentationFieldFetchParams, executionContext.getInstrumentationState()) ); @@ -496,6 +497,7 @@ Async.CombinedBuilder getAsyncFieldValueInfo( Object fetchedObject = invokeDataFetcher(executionContext, parameters, fieldDef, dataFetchingEnvironment, dataFetcher); executionContext.getDataLoaderDispatcherStrategy().fieldFetched(executionContext, parameters, dataFetcher, fetchedObject); fetchCtx.onDispatched(); + fetchCtx.onFetchedValue(fetchedObject); if (fetchedObject instanceof CompletableFuture) { @SuppressWarnings("unchecked") CompletableFuture fetchedValue = (CompletableFuture) fetchedObject; diff --git a/src/main/java/graphql/execution/instrumentation/ChainedInstrumentation.java b/src/main/java/graphql/execution/instrumentation/ChainedInstrumentation.java index 6decb929cd..f0577aeab0 100644 --- a/src/main/java/graphql/execution/instrumentation/ChainedInstrumentation.java +++ b/src/main/java/graphql/execution/instrumentation/ChainedInstrumentation.java @@ -1,7 +1,6 @@ package graphql.execution.instrumentation; import com.google.common.collect.ImmutableList; -import graphql.Assert; import graphql.ExecutionInput; import graphql.ExecutionResult; import graphql.ExperimentalApi; @@ -159,7 +158,7 @@ public ExecutionStrategyInstrumentationContext beginExecutionStrategy(Instrument } BiFunction mapper = (instrumentation, specificState) -> instrumentation.beginExecuteObject(parameters, specificState); ChainedInstrumentationState chainedInstrumentationState = (ChainedInstrumentationState) state; - if (instrumentations.size() == 1) { + if (instrumentations.size() == 1) { return mapper.apply(instrumentations.get(0), chainedInstrumentationState.getState(0)); } return new ChainedExecuteObjectInstrumentationContext(chainedMapAndDropNulls(chainedInstrumentationState, mapper)); @@ -182,11 +181,26 @@ public InstrumentationContext beginSubscribedFieldEvent(Instrum return chainedCtx(state, (instrumentation, specificState) -> instrumentation.beginFieldExecution(parameters, specificState)); } + @SuppressWarnings("deprecation") @Override public InstrumentationContext beginFieldFetch(InstrumentationFieldFetchParameters parameters, InstrumentationState state) { return chainedCtx(state, (instrumentation, specificState) -> instrumentation.beginFieldFetch(parameters, specificState)); } + @Override + public FieldFetchingInstrumentationContext beginFieldFetching(InstrumentationFieldFetchParameters parameters, InstrumentationState state) { + if (instrumentations.isEmpty()) { + return FieldFetchingInstrumentationContext.NOOP; + } + BiFunction mapper = (instrumentation, specificState) -> instrumentation.beginFieldFetching(parameters, specificState); + ChainedInstrumentationState chainedInstrumentationState = (ChainedInstrumentationState) state; + if (instrumentations.size() == 1) { + return mapper.apply(instrumentations.get(0), chainedInstrumentationState.getState(0)); + } + ImmutableList objects = chainedMapAndDropNulls(chainedInstrumentationState, mapper); + return new ChainedFieldFetchingInstrumentationContext(objects); + } + @Override public @Nullable InstrumentationContext beginFieldCompletion(InstrumentationFieldCompleteParameters parameters, InstrumentationState state) { return chainedCtx(state, (instrumentation, specificState) -> instrumentation.beginFieldCompletion(parameters, specificState)); @@ -344,8 +358,33 @@ public void onFieldValuesException() { } } + private static class ChainedFieldFetchingInstrumentationContext implements FieldFetchingInstrumentationContext { + + private final ImmutableList contexts; + + ChainedFieldFetchingInstrumentationContext(ImmutableList contexts) { + this.contexts = contexts; + } + + @Override + public void onDispatched() { + contexts.forEach(FieldFetchingInstrumentationContext::onDispatched); + } + + @Override + public void onFetchedValue(Object fetchedValue) { + contexts.forEach(context -> context.onFetchedValue(fetchedValue)); + } + + @Override + public void onCompleted(Object result, Throwable t) { + contexts.forEach(context -> context.onCompleted(result, t)); + } + } + private static class ChainedDeferredExecutionStrategyInstrumentationContext implements InstrumentationContext { + private final List> contexts; ChainedDeferredExecutionStrategyInstrumentationContext(List> contexts) { diff --git a/src/main/java/graphql/execution/instrumentation/FieldFetchingInstrumentationContext.java b/src/main/java/graphql/execution/instrumentation/FieldFetchingInstrumentationContext.java new file mode 100644 index 0000000000..d8e51269be --- /dev/null +++ b/src/main/java/graphql/execution/instrumentation/FieldFetchingInstrumentationContext.java @@ -0,0 +1,68 @@ +package graphql.execution.instrumentation; + +import graphql.Internal; +import graphql.PublicSpi; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@PublicSpi +public interface FieldFetchingInstrumentationContext extends InstrumentationContext { + + @Internal + FieldFetchingInstrumentationContext NOOP = new FieldFetchingInstrumentationContext() { + @Override + public void onDispatched() { + } + + @Override + public void onCompleted(Object result, Throwable t) { + } + + @Override + public void onFetchedValue(Object fetchedValue) { + } + }; + + /** + * This creates a no-op {@link InstrumentationContext} if the one pass in is null + * + * @param nullableContext a {@link InstrumentationContext} that can be null + * + * @return a non null {@link InstrumentationContext} that maybe a no-op + */ + @NotNull + @Internal + static FieldFetchingInstrumentationContext nonNullCtx(FieldFetchingInstrumentationContext nullableContext) { + return nullableContext == null ? NOOP : nullableContext; + } + + @Internal + static FieldFetchingInstrumentationContext adapter(@Nullable InstrumentationContext context) { + if (context == null) { + return null; + } + return new FieldFetchingInstrumentationContext() { + @Override + public void onDispatched() { + context.onDispatched(); + } + + @Override + public void onCompleted(Object result, Throwable t) { + context.onCompleted(result, t); + } + + @Override + public void onFetchedValue(Object fetchedValue) { + } + }; + } + + /** + * This is called back with value fetched for the field. + * + * @param fetchedValue a value that a field's {@link graphql.schema.DataFetcher} returned + */ + default void onFetchedValue(Object fetchedValue) { + } +} diff --git a/src/main/java/graphql/execution/instrumentation/Instrumentation.java b/src/main/java/graphql/execution/instrumentation/Instrumentation.java index 977422e565..11819347ad 100644 --- a/src/main/java/graphql/execution/instrumentation/Instrumentation.java +++ b/src/main/java/graphql/execution/instrumentation/Instrumentation.java @@ -196,12 +196,31 @@ default InstrumentationContext beginFieldExecution(InstrumentationFieldP * @param state the state created during the call to {@link #createStateAsync(InstrumentationCreateStateParameters)} * * @return a nullable {@link InstrumentationContext} object that will be called back when the step ends (assuming it's not null) + * + * @deprecated use {@link #beginFieldFetching(InstrumentationFieldFetchParameters, InstrumentationState)} instead */ + @Deprecated(since = "2024-04-18") @Nullable default InstrumentationContext beginFieldFetch(InstrumentationFieldFetchParameters parameters, InstrumentationState state) { return noOp(); } + /** + * This is called just before a field {@link DataFetcher} is invoked. The {@link FieldFetchingInstrumentationContext#onFetchedValue(Object)} + * callback will be invoked once a value is returned by a {@link DataFetcher} but perhaps before + * its value is completed if it's a {@link CompletableFuture} value. + * + * @param parameters the parameters to this step + * @param state the state created during the call to {@link #createStateAsync(InstrumentationCreateStateParameters)} + * + * @return a nullable {@link InstrumentationContext} object that will be called back when the step ends (assuming it's not null) + */ + @Nullable + default FieldFetchingInstrumentationContext beginFieldFetching(InstrumentationFieldFetchParameters parameters, InstrumentationState state) { + InstrumentationContext ctx = beginFieldFetch(parameters, state); + return FieldFetchingInstrumentationContext.adapter(ctx); + } + /** * This is called just before the complete field is started. * diff --git a/src/main/java/graphql/execution/instrumentation/NoContextChainedInstrumentation.java b/src/main/java/graphql/execution/instrumentation/NoContextChainedInstrumentation.java index 1928df84f0..8d8d3cd956 100644 --- a/src/main/java/graphql/execution/instrumentation/NoContextChainedInstrumentation.java +++ b/src/main/java/graphql/execution/instrumentation/NoContextChainedInstrumentation.java @@ -98,6 +98,11 @@ public InstrumentationContext beginFieldFetch(InstrumentationFieldFetchP return runAll(state, (instrumentation, specificState) -> instrumentation.beginFieldFetch(parameters, specificState)); } + @Override + public FieldFetchingInstrumentationContext beginFieldFetching(InstrumentationFieldFetchParameters parameters, InstrumentationState state) { + return runAll(state, (instrumentation, specificState) -> instrumentation.beginFieldFetching(parameters, specificState)); + } + @Override public @Nullable InstrumentationContext beginFieldCompletion(InstrumentationFieldCompleteParameters parameters, InstrumentationState state) { return runAll(state, (instrumentation, specificState) -> instrumentation.beginFieldCompletion(parameters, specificState)); diff --git a/src/main/java/graphql/execution/instrumentation/SimplePerformantInstrumentation.java b/src/main/java/graphql/execution/instrumentation/SimplePerformantInstrumentation.java index dfffa5b729..b46cedf9c7 100644 --- a/src/main/java/graphql/execution/instrumentation/SimplePerformantInstrumentation.java +++ b/src/main/java/graphql/execution/instrumentation/SimplePerformantInstrumentation.java @@ -102,7 +102,6 @@ public class SimplePerformantInstrumentation implements Instrumentation { return noOp(); } - @Override public @Nullable InstrumentationContext beginFieldCompletion(InstrumentationFieldCompleteParameters parameters, InstrumentationState state) { return noOp(); From 57968f9e20226524668142b36bddf2982b5122d8 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Fri, 19 Apr 2024 11:14:23 +1000 Subject: [PATCH 08/29] Added field fetching method for Expedia - updated testing instrumentations to show new methods is called and it delegates ok as well via Legacy one --- .../instrumentation/ModernTestingInstrumentation.groovy | 4 ++-- .../TestingFieldFetchingInstrumentationContext.groovy | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/test/groovy/graphql/execution/instrumentation/TestingFieldFetchingInstrumentationContext.groovy diff --git a/src/test/groovy/graphql/execution/instrumentation/ModernTestingInstrumentation.groovy b/src/test/groovy/graphql/execution/instrumentation/ModernTestingInstrumentation.groovy index 5d6fbb1d8e..822d08f5a6 100644 --- a/src/test/groovy/graphql/execution/instrumentation/ModernTestingInstrumentation.groovy +++ b/src/test/groovy/graphql/execution/instrumentation/ModernTestingInstrumentation.groovy @@ -86,9 +86,9 @@ class ModernTestingInstrumentation implements Instrumentation { } @Override - InstrumentationContext beginFieldFetch(InstrumentationFieldFetchParameters parameters, InstrumentationState state) { + FieldFetchingInstrumentationContext beginFieldFetching(InstrumentationFieldFetchParameters parameters, InstrumentationState state) { assert state == instrumentationState - return new TestingInstrumentContext("fetch-$parameters.field.name", executionList, throwableList, useOnDispatch) + return new TestingFieldFetchingInstrumentationContext("fetch-$parameters.field.name", executionList, throwableList, useOnDispatch) } @Override diff --git a/src/test/groovy/graphql/execution/instrumentation/TestingFieldFetchingInstrumentationContext.groovy b/src/test/groovy/graphql/execution/instrumentation/TestingFieldFetchingInstrumentationContext.groovy new file mode 100644 index 0000000000..02cbf9d928 --- /dev/null +++ b/src/test/groovy/graphql/execution/instrumentation/TestingFieldFetchingInstrumentationContext.groovy @@ -0,0 +1,9 @@ +package graphql.execution.instrumentation + +class TestingFieldFetchingInstrumentationContext extends TestingInstrumentContext> implements FieldFetchingInstrumentationContext { + + TestingFieldFetchingInstrumentationContext(Object op, Object executionList, Object throwableList, Boolean useOnDispatch) { + super(op, executionList, throwableList, useOnDispatch) + } +} + From 190bf6489431906a453cf525f17e28e663bdc705 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Sat, 27 Apr 2024 22:22:16 +1000 Subject: [PATCH 09/29] Added a getBoolean onto GraphQLContext.java --- src/main/java/graphql/GraphQLContext.java | 39 ++++++++++++++++--- .../java/graphql/execution/Execution.java | 2 +- .../graphql/execution/ExecutionStrategy.java | 4 +- .../introspection/GoodFaithIntrospection.java | 2 +- .../graphql/introspection/Introspection.java | 2 +- .../groovy/graphql/GraphQLContextTest.groovy | 16 ++++++++ 6 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/main/java/graphql/GraphQLContext.java b/src/main/java/graphql/GraphQLContext.java index 8b913919d3..dfc3ed634f 100644 --- a/src/main/java/graphql/GraphQLContext.java +++ b/src/main/java/graphql/GraphQLContext.java @@ -96,6 +96,33 @@ public Optional getOrEmpty(Object key) { return Optional.ofNullable(t); } + /** + * This returns true if the value at the specified key is equal to + * {@link Boolean#TRUE} + * + * @param key the key to look up + * + * @return true if the value is equal to {@link Boolean#TRUE} + */ + public Boolean getBoolean(Object key) { + Object val = map.get(assertNotNull(key)); + return Boolean.TRUE.equals(val); + } + + /** + * This returns true if the value at the specified key is equal to + * {@link Boolean#TRUE} or the default value if the key is missing + * + * @param key the key to look up + * @param defaultValue the value to use if the key is not present + * + * @return true if the value is equal to {@link Boolean#TRUE} + */ + public Boolean getBoolean(Object key, Boolean defaultValue) { + Object val = map.getOrDefault(assertNotNull(key), defaultValue); + return Boolean.TRUE.equals(val); + } + /** * Returns true if the context contains a value for that key * @@ -177,11 +204,11 @@ public GraphQLContext putAll(Consumer contextBuilderCons * Attempts to compute a mapping for the specified key and its * current mapped value (or null if there is no current mapping). * - * @param key key with which the specified value is to be associated + * @param key key with which the specified value is to be associated * @param remappingFunction the function to compute a value + * @param for two * * @return the new value associated with the specified key, or null if none - * @param for two */ public T compute(Object key, BiFunction remappingFunction) { assertNotNull(remappingFunction); @@ -192,11 +219,11 @@ public T compute(Object key, BiFunction rema * If the specified key is not already associated with a value (or is mapped to null), * attempts to compute its value using the given mapping function and enters it into this map unless null. * - * @param key key with which the specified value is to be associated + * @param key key with which the specified value is to be associated * @param mappingFunction the function to compute a value + * @param for two * * @return the current (existing or computed) value associated with the specified key, or null if the computed value is null - * @param for two */ public T computeIfAbsent(Object key, Function mappingFunction) { @@ -207,11 +234,11 @@ public T computeIfAbsent(Object key, Function mappingFu * If the value for the specified key is present and non-null, * attempts to compute a new mapping given the key and its current mapped value. * - * @param key key with which the specified value is to be associated + * @param key key with which the specified value is to be associated * @param remappingFunction the function to compute a value + * @param for two * * @return the new value associated with the specified key, or null if none - * @param for two */ public T computeIfPresent(Object key, BiFunction remappingFunction) { diff --git a/src/main/java/graphql/execution/Execution.java b/src/main/java/graphql/execution/Execution.java index 584449553a..a3fff5a8e8 100644 --- a/src/main/java/graphql/execution/Execution.java +++ b/src/main/java/graphql/execution/Execution.java @@ -154,7 +154,7 @@ private CompletableFuture executeOperation(ExecutionContext exe collectorParameters, operationDefinition.getSelectionSet(), Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> (Boolean) graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) + .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) .orElse(false) ); diff --git a/src/main/java/graphql/execution/ExecutionStrategy.java b/src/main/java/graphql/execution/ExecutionStrategy.java index 5e749360a5..427c9fa92e 100644 --- a/src/main/java/graphql/execution/ExecutionStrategy.java +++ b/src/main/java/graphql/execution/ExecutionStrategy.java @@ -298,7 +298,7 @@ DeferredExecutionSupport createDeferredExecutionSupport(ExecutionContext executi MergedSelectionSet fields = parameters.getFields(); return Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> (Boolean) graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) + .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) .orElse(false) ? new DeferredExecutionSupport.DeferredExecutionSupportImpl( fields, @@ -948,7 +948,7 @@ protected void handleValueException(CompletableFuture overallResult, Thro collectorParameters, parameters.getField(), Optional.ofNullable(executionContext.getGraphQLContext()) - .map(graphqlContext -> (Boolean) graphqlContext.get(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) + .map(graphqlContext -> graphqlContext.getBoolean(ExperimentalApi.ENABLE_INCREMENTAL_SUPPORT)) .orElse(false) ); diff --git a/src/main/java/graphql/introspection/GoodFaithIntrospection.java b/src/main/java/graphql/introspection/GoodFaithIntrospection.java index bd7285cbd1..ae7da12569 100644 --- a/src/main/java/graphql/introspection/GoodFaithIntrospection.java +++ b/src/main/java/graphql/introspection/GoodFaithIntrospection.java @@ -134,7 +134,7 @@ private static boolean isIntrospectionEnabled(GraphQLContext graphQlContext) { if (!isEnabledJvmWide()) { return false; } - return !graphQlContext.getOrDefault(GOOD_FAITH_INTROSPECTION_DISABLED, false); + return !graphQlContext.getBoolean(GOOD_FAITH_INTROSPECTION_DISABLED, false); } public static class BadFaithIntrospectionError implements GraphQLError { diff --git a/src/main/java/graphql/introspection/Introspection.java b/src/main/java/graphql/introspection/Introspection.java index ced6cbf818..4aea32bda4 100644 --- a/src/main/java/graphql/introspection/Introspection.java +++ b/src/main/java/graphql/introspection/Introspection.java @@ -144,7 +144,7 @@ private static boolean isIntrospectionEnabled(GraphQLContext graphQlContext) { if (!isEnabledJvmWide()) { return false; } - return !graphQlContext.getOrDefault(INTROSPECTION_DISABLED, false); + return !graphQlContext.getBoolean(INTROSPECTION_DISABLED, false); } private static final Map> introspectionDataFetchers = new LinkedHashMap<>(); diff --git a/src/test/groovy/graphql/GraphQLContextTest.groovy b/src/test/groovy/graphql/GraphQLContextTest.groovy index 8eebb17653..d411143db6 100644 --- a/src/test/groovy/graphql/GraphQLContextTest.groovy +++ b/src/test/groovy/graphql/GraphQLContextTest.groovy @@ -276,4 +276,20 @@ class GraphQLContextTest extends Specification { executionResult.errors.isEmpty() executionResult.data == [field: "ctx1value"] } + + def "boolean getters work"() { + when: + def context = GraphQLContext.newContext().of( + "f", false, + "t", true, + "notABool", "true" + ).build() + + then: + !context.getBoolean("f") + context.getBoolean("t") + !context.getBoolean("notABool") + !context.getBoolean("missing") + context.getBoolean("missing", true) + } } From 2cf240d67e235d7c0ecf7f068a698a972aef1c17 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Sun, 28 Apr 2024 12:20:13 +1000 Subject: [PATCH 10/29] Now does not overwrite default dara fetcher unless its nonnull See # 3431 --- .../graphql/schema/idl/RuntimeWiring.java | 26 +++++++++++- .../schema/idl/SchemaGeneratorHelper.java | 2 +- .../graphql/schema/idl/TypeRuntimeWiring.java | 3 ++ .../schema/idl/RuntimeWiringTest.groovy | 41 +++++++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/main/java/graphql/schema/idl/RuntimeWiring.java b/src/main/java/graphql/schema/idl/RuntimeWiring.java index 0a95adce08..941ee9534c 100644 --- a/src/main/java/graphql/schema/idl/RuntimeWiring.java +++ b/src/main/java/graphql/schema/idl/RuntimeWiring.java @@ -116,10 +116,31 @@ public Map> getDataFetchers() { return dataFetchers; } + /** + * This is deprecated because the name has the wrong plural case. + * + * @param typeName the type for fetch a map of per field data fetchers for + * + * @return a map of field data fetchers for a type + * + * @deprecated See {@link #getDataFetchersForType(String)} + */ + @Deprecated(since = "2024-04-28") public Map getDataFetcherForType(String typeName) { return dataFetchers.computeIfAbsent(typeName, k -> new LinkedHashMap<>()); } + /** + * This returns a map of the data fetchers per field on that named type. + * + * @param typeName the type for fetch a map of per field data fetchers for + * + * @return a map of field data fetchers for a type + */ + public Map getDataFetchersForType(String typeName) { + return dataFetchers.computeIfAbsent(typeName, k -> new LinkedHashMap<>()); + } + public DataFetcher getDefaultDataFetcherForType(String typeName) { return defaultDataFetchers.get(typeName); } @@ -284,7 +305,10 @@ public Builder type(TypeRuntimeWiring typeRuntimeWiring) { } typeDataFetchers.putAll(typeRuntimeWiring.getFieldDataFetchers()); - defaultDataFetchers.put(typeName, typeRuntimeWiring.getDefaultDataFetcher()); + DataFetcher defaultDataFetcher = typeRuntimeWiring.getDefaultDataFetcher(); + if (defaultDataFetcher != null) { + defaultDataFetchers.put(typeName, defaultDataFetcher); + } TypeResolver typeResolver = typeRuntimeWiring.getTypeResolver(); if (typeResolver != null) { diff --git a/src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java b/src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java index aa4878efb4..0b4e74c45b 100644 --- a/src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java +++ b/src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java @@ -839,7 +839,7 @@ private DataFetcherFactory buildDataFetcherFactory(BuildContext buildCtx, dataFetcher = wiringFactory.getDataFetcher(wiringEnvironment); assertNotNull(dataFetcher, () -> "The WiringFactory indicated it provides a data fetcher but then returned null"); } else { - dataFetcher = runtimeWiring.getDataFetcherForType(parentTypeName).get(fieldName); + dataFetcher = runtimeWiring.getDataFetchersForType(parentTypeName).get(fieldName); if (dataFetcher == null) { dataFetcher = runtimeWiring.getDefaultDataFetcherForType(parentTypeName); if (dataFetcher == null) { diff --git a/src/main/java/graphql/schema/idl/TypeRuntimeWiring.java b/src/main/java/graphql/schema/idl/TypeRuntimeWiring.java index 3480a5d6b0..5e2e333cef 100644 --- a/src/main/java/graphql/schema/idl/TypeRuntimeWiring.java +++ b/src/main/java/graphql/schema/idl/TypeRuntimeWiring.java @@ -184,6 +184,9 @@ private void assertFieldStrictly(String fieldName) { */ public Builder defaultDataFetcher(DataFetcher dataFetcher) { assertNotNull(dataFetcher); + if (strictMode && defaultDataFetcher != null) { + throw new StrictModeWiringException(format("The type %s has already has a default data fetcher defined", typeName)); + } defaultDataFetcher = dataFetcher; return this; } diff --git a/src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy b/src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy index bd3c225d2a..a74e654983 100644 --- a/src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy +++ b/src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy @@ -195,6 +195,7 @@ class RuntimeWiringTest extends Specification { def "strict mode can stop certain redefinitions"() { DataFetcher DF1 = env -> "x" + DataFetcher DF2 = env -> "x" TypeResolver TR1 = env -> null EnumValuesProvider EVP1 = name -> null @@ -236,5 +237,45 @@ class RuntimeWiringTest extends Specification { def e4 = thrown(StrictModeWiringException) e4.message == "The scalar String is already defined" + when: + TypeRuntimeWiring.newTypeWiring("Foo") + .strictMode() + .defaultDataFetcher(DF1) + .defaultDataFetcher(DF2) + + then: + def e5 = thrown(StrictModeWiringException) + e5.message == "The type Foo has already has a default data fetcher defined" + } + + def "dont over default data fetchers if they are null"() { + + DataFetcher DF1 = env -> "x" + DataFetcher DF2 = env -> "x" + DataFetcher DEFAULT_DF = env -> null + DataFetcher DEFAULT_DF2 = env -> null + + when: + def runtimeWiring = RuntimeWiring.newRuntimeWiring() + .type(TypeRuntimeWiring.newTypeWiring("Foo").defaultDataFetcher(DEFAULT_DF)) + .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("foo", DF1)) + .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("bar", DF2)) + .build() + + then: + runtimeWiring.getDefaultDataFetcherForType("Foo") == DEFAULT_DF + + when: + runtimeWiring = RuntimeWiring.newRuntimeWiring() + .type(TypeRuntimeWiring.newTypeWiring("Foo").defaultDataFetcher(DEFAULT_DF)) + .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("foo", DF1)) + .type(TypeRuntimeWiring.newTypeWiring("Foo").dataFetcher("bar", DF2)) + // we can specifically overwrite it later + .type(TypeRuntimeWiring.newTypeWiring("Foo").defaultDataFetcher(DEFAULT_DF2)) + .build() + + then: + runtimeWiring.getDefaultDataFetcherForType("Foo") == DEFAULT_DF2 + } } From 70c796336a9fd05ab49a58c73c2a40a130e98a04 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Mon, 29 Apr 2024 15:14:48 +1000 Subject: [PATCH 11/29] Update src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy Co-authored-by: dondonz <13839920+dondonz@users.noreply.github.com> --- src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy b/src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy index a74e654983..907d89a1bf 100644 --- a/src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy +++ b/src/test/groovy/graphql/schema/idl/RuntimeWiringTest.groovy @@ -248,7 +248,7 @@ class RuntimeWiringTest extends Specification { e5.message == "The type Foo has already has a default data fetcher defined" } - def "dont over default data fetchers if they are null"() { + def "overwrite default data fetchers if they are null"() { DataFetcher DF1 = env -> "x" DataFetcher DF2 = env -> "x" From 3ef087fcbbeae3d070221fd192b3016b138904b3 Mon Sep 17 00:00:00 2001 From: Brad Baker Date: Mon, 29 Apr 2024 16:26:43 +1000 Subject: [PATCH 12/29] getBoolean returns a boolean not a Boolean --- src/main/java/graphql/GraphQLContext.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/graphql/GraphQLContext.java b/src/main/java/graphql/GraphQLContext.java index dfc3ed634f..48f79ce774 100644 --- a/src/main/java/graphql/GraphQLContext.java +++ b/src/main/java/graphql/GraphQLContext.java @@ -104,7 +104,7 @@ public Optional getOrEmpty(Object key) { * * @return true if the value is equal to {@link Boolean#TRUE} */ - public Boolean getBoolean(Object key) { + public boolean getBoolean(Object key) { Object val = map.get(assertNotNull(key)); return Boolean.TRUE.equals(val); } @@ -118,7 +118,7 @@ public Boolean getBoolean(Object key) { * * @return true if the value is equal to {@link Boolean#TRUE} */ - public Boolean getBoolean(Object key, Boolean defaultValue) { + public boolean getBoolean(Object key, Boolean defaultValue) { Object val = map.getOrDefault(assertNotNull(key), defaultValue); return Boolean.TRUE.equals(val); } From f715621e23d755a75d7fbd38cdd0519a6316ef40 Mon Sep 17 00:00:00 2001 From: Salzian Date: Mon, 29 Apr 2024 17:01:13 +0200 Subject: [PATCH 13/29] Added some nullability annotations in DataFetchingEnvironment & sub-classes --- src/main/java/graphql/schema/DataFetchingEnvironment.java | 7 +++++++ .../java/graphql/schema/DataFetchingEnvironmentImpl.java | 8 +++++--- .../graphql/schema/DelegatingDataFetchingEnvironment.java | 8 +++++--- .../schema/DelegatingDataFetchingEnvironmentTest.groovy | 2 ++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/main/java/graphql/schema/DataFetchingEnvironment.java b/src/main/java/graphql/schema/DataFetchingEnvironment.java index ce46ad75c9..3c19f03329 100644 --- a/src/main/java/graphql/schema/DataFetchingEnvironment.java +++ b/src/main/java/graphql/schema/DataFetchingEnvironment.java @@ -13,6 +13,8 @@ import graphql.language.OperationDefinition; import org.dataloader.DataLoader; import org.dataloader.DataLoaderRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Locale; @@ -37,6 +39,7 @@ public interface DataFetchingEnvironment extends IntrospectionDataFetchingEnviro * * @return can be null for the root query, otherwise it is never null */ + @Nullable T getSource(); /** @@ -61,6 +64,7 @@ public interface DataFetchingEnvironment extends IntrospectionDataFetchingEnviro * * @return the named argument or null if it's not present */ + @Nullable T getArgument(String name); /** @@ -97,6 +101,7 @@ public interface DataFetchingEnvironment extends IntrospectionDataFetchingEnviro * * @return can NOT be null */ + @NotNull GraphQLContext getGraphQlContext(); /** @@ -114,6 +119,7 @@ public interface DataFetchingEnvironment extends IntrospectionDataFetchingEnviro * * @return can be null if no field context objects are passed back by previous parent fields */ + @Nullable T getLocalContext(); /** @@ -228,6 +234,7 @@ public interface DataFetchingEnvironment extends IntrospectionDataFetchingEnviro * * @see org.dataloader.DataLoaderRegistry#getDataLoader(String) */ + @Nullable DataLoader getDataLoader(String dataLoaderName); /** diff --git a/src/main/java/graphql/schema/DataFetchingEnvironmentImpl.java b/src/main/java/graphql/schema/DataFetchingEnvironmentImpl.java index 4377330e48..440ec86159 100644 --- a/src/main/java/graphql/schema/DataFetchingEnvironmentImpl.java +++ b/src/main/java/graphql/schema/DataFetchingEnvironmentImpl.java @@ -17,6 +17,8 @@ import graphql.language.OperationDefinition; import org.dataloader.DataLoader; import org.dataloader.DataLoaderRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Locale; @@ -129,12 +131,12 @@ public T getContext() { } @Override - public GraphQLContext getGraphQlContext() { + public @NotNull GraphQLContext getGraphQlContext() { return graphQLContext; } @Override - public T getLocalContext() { + public @Nullable T getLocalContext() { return (T) localContext; } @@ -204,7 +206,7 @@ public ExecutionStepInfo getExecutionStepInfo() { } @Override - public DataLoader getDataLoader(String dataLoaderName) { + public @Nullable DataLoader getDataLoader(String dataLoaderName) { return dataLoaderRegistry.getDataLoader(dataLoaderName); } diff --git a/src/main/java/graphql/schema/DelegatingDataFetchingEnvironment.java b/src/main/java/graphql/schema/DelegatingDataFetchingEnvironment.java index 41d6795556..0509684647 100644 --- a/src/main/java/graphql/schema/DelegatingDataFetchingEnvironment.java +++ b/src/main/java/graphql/schema/DelegatingDataFetchingEnvironment.java @@ -12,6 +12,8 @@ import graphql.language.OperationDefinition; import org.dataloader.DataLoader; import org.dataloader.DataLoaderRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Locale; @@ -70,12 +72,12 @@ public T getContext() { } @Override - public GraphQLContext getGraphQlContext() { + public @NotNull GraphQLContext getGraphQlContext() { return delegateEnvironment.getGraphQlContext(); } @Override - public T getLocalContext() { + public @Nullable T getLocalContext() { return delegateEnvironment.getLocalContext(); } @@ -146,7 +148,7 @@ public QueryDirectives getQueryDirectives() { } @Override - public DataLoader getDataLoader(String dataLoaderName) { + public @Nullable DataLoader getDataLoader(String dataLoaderName) { return delegateEnvironment.getDataLoader(dataLoaderName); } diff --git a/src/test/groovy/graphql/schema/DelegatingDataFetchingEnvironmentTest.groovy b/src/test/groovy/graphql/schema/DelegatingDataFetchingEnvironmentTest.groovy index 687a0f62ae..29133b21bd 100644 --- a/src/test/groovy/graphql/schema/DelegatingDataFetchingEnvironmentTest.groovy +++ b/src/test/groovy/graphql/schema/DelegatingDataFetchingEnvironmentTest.groovy @@ -1,6 +1,7 @@ package graphql.schema import graphql.GraphQLContext +import org.jetbrains.annotations.NotNull import spock.lang.Specification class DelegatingDataFetchingEnvironmentTest extends Specification { @@ -22,6 +23,7 @@ class DelegatingDataFetchingEnvironmentTest extends Specification { when: def delegatingDFE = new DelegatingDataFetchingEnvironment(dfe) { + @NotNull @Override GraphQLContext getGraphQlContext() { return GraphQLContext.of(["key": "overriddenContext"]) From fd526cec60ffeacecaac521a27fd90cdb41bef2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:51:19 +0000 Subject: [PATCH 14/29] Bump org.testng:testng from 7.10.1 to 7.10.2 Bumps [org.testng:testng](https://github.com/testng-team/testng) from 7.10.1 to 7.10.2. - [Release notes](https://github.com/testng-team/testng/releases) - [Changelog](https://github.com/testng-team/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/testng-team/testng/compare/7.10.1...7.10.2) --- updated-dependencies: - dependency-name: org.testng:testng dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3750da10cb..919b01740e 100644 --- a/build.gradle +++ b/build.gradle @@ -118,7 +118,7 @@ dependencies { testImplementation 'org.reactivestreams:reactive-streams-tck:' + reactiveStreamsVersion testImplementation "io.reactivex.rxjava2:rxjava:2.2.21" - testImplementation 'org.testng:testng:7.10.1' // use for reactive streams test inheritance + testImplementation 'org.testng:testng:7.10.2' // use for reactive streams test inheritance testImplementation 'org.openjdk.jmh:jmh-core:1.37' testAnnotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.37' From 01f789712f417f2ac0c9b559ee5492051f1d971d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:51:24 +0000 Subject: [PATCH 15/29] Bump net.bytebuddy:byte-buddy from 1.14.13 to 1.14.14 Bumps [net.bytebuddy:byte-buddy](https://github.com/raphw/byte-buddy) from 1.14.13 to 1.14.14. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.13...byte-buddy-1.14.14) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- agent/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/build.gradle b/agent/build.gradle index 1cc23ecb3c..2e2430cb46 100644 --- a/agent/build.gradle +++ b/agent/build.gradle @@ -6,7 +6,7 @@ plugins { } dependencies { - implementation("net.bytebuddy:byte-buddy:1.14.13") + implementation("net.bytebuddy:byte-buddy:1.14.14") // graphql-java itself implementation(rootProject) } From e67729a4a2c969ceb222ce1f294686b66be7357e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:51:34 +0000 Subject: [PATCH 16/29] Bump net.bytebuddy:byte-buddy-agent from 1.14.13 to 1.14.14 Bumps [net.bytebuddy:byte-buddy-agent](https://github.com/raphw/byte-buddy) from 1.14.13 to 1.14.14. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.13...byte-buddy-1.14.14) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- agent-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent-test/build.gradle b/agent-test/build.gradle index 280aed1bac..30ebd04262 100644 --- a/agent-test/build.gradle +++ b/agent-test/build.gradle @@ -4,7 +4,7 @@ plugins { dependencies { implementation(rootProject) - implementation("net.bytebuddy:byte-buddy-agent:1.14.13") + implementation("net.bytebuddy:byte-buddy-agent:1.14.14") testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' From ae47bd184e8ec9fd33a1c7f8f7c988100851f244 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Wed, 1 May 2024 08:06:16 +1000 Subject: [PATCH 17/29] Add schema extension printing via AST and add tests --- .../graphql/schema/idl/SchemaPrinter.java | 69 +++-- .../schema/idl/SchemaPrinterTest.groovy | 289 +++++++++++++++++- 2 files changed, 335 insertions(+), 23 deletions(-) diff --git a/src/main/java/graphql/schema/idl/SchemaPrinter.java b/src/main/java/graphql/schema/idl/SchemaPrinter.java index 9ac97f6a2e..c51e4a78e4 100644 --- a/src/main/java/graphql/schema/idl/SchemaPrinter.java +++ b/src/main/java/graphql/schema/idl/SchemaPrinter.java @@ -19,6 +19,7 @@ import graphql.language.ObjectTypeDefinition; import graphql.language.ScalarTypeDefinition; import graphql.language.SchemaDefinition; +import graphql.language.SchemaExtensionDefinition; import graphql.language.TypeDefinition; import graphql.language.UnionTypeDefinition; import graphql.schema.DefaultGraphqlTypeComparatorRegistry; @@ -283,7 +284,7 @@ public Options includeDirectiveDefinitions(boolean flag) { /** * This is a Predicate that decides whether a directive definition is printed. * - * @param includeDirectiveDefinition the predicate to decide of a directive defintion is printed + * @param includeDirectiveDefinition the predicate to decide of a directive definition is printed * * @return new instance of options */ @@ -482,7 +483,7 @@ public SchemaPrinter(Options options) { /** * This can print an in memory GraphQL IDL document back to a logical schema definition. - * If you want to turn a Introspection query result into a Document (and then into a printed + * If you want to turn an Introspection query result into a Document (and then into a printed * schema) then use {@link graphql.introspection.IntrospectionResultToSchema#createSchemaDefinition(java.util.Map)} * first to get the {@link graphql.language.Document} and then print that. * @@ -773,6 +774,17 @@ private boolean shouldPrintAsAst(TypeDefinition definition) { return options.isUseAstDefinitions() && definition != null; } + /** + * This will return true if the options say to use the AST and we have an AST element + * + * @param definition the AST schema definition + * + * @return true if we should print using AST nodes + */ + private boolean shouldPrintAsAst(SchemaDefinition definition) { + return options.isUseAstDefinitions() && definition != null; + } + /** * This will print out a runtime graphql schema element using its contained AST type definition. This * must be guarded by a called to {@link #shouldPrintAsAst(TypeDefinition)} @@ -792,6 +804,25 @@ private void printAsAst(PrintWriter out, TypeDefinition definition, List extensions) { + out.printf("%s\n", AstPrinter.printAst(definition)); + if (extensions != null) { + for (SchemaExtensionDefinition extension : extensions) { + out.printf("\n%s\n", AstPrinter.printAst(extension)); + } + } + out.print('\n'); + } + + private static String printAst(InputValueWithState value, GraphQLInputType type) { return AstPrinter.printAst(ValuesResolver.valueToLiteral(value, type, GraphQLContext.getDefault(), Locale.getDefault())); } @@ -803,7 +834,7 @@ private SchemaElementPrinter schemaPrinter() { GraphQLObjectType subscriptionType = schema.getSubscriptionType(); // when serializing a GraphQL schema using the type system language, a - // schema definition should be omitted if only uses the default root type names. + // schema definition should be omitted only if it uses the default root type names. boolean needsSchemaPrinted = options.isIncludeSchemaDefinition(); if (!needsSchemaPrinted) { @@ -819,21 +850,25 @@ private SchemaElementPrinter schemaPrinter() { } if (needsSchemaPrinted) { - if (hasAstDefinitionComments(schema) || hasDescription(schema)) { - out.print(printComments(schema, "")); - } - List directives = DirectivesUtil.toAppliedDirectives(schema.getSchemaAppliedDirectives(), schema.getSchemaDirectives()); - out.format("schema %s{\n", directivesString(GraphQLSchemaElement.class, directives)); - if (queryType != null) { - out.format(" query: %s\n", queryType.getName()); - } - if (mutationType != null) { - out.format(" mutation: %s\n", mutationType.getName()); - } - if (subscriptionType != null) { - out.format(" subscription: %s\n", subscriptionType.getName()); + if (shouldPrintAsAst(schema.getDefinition())) { + printAsAst(out, schema.getDefinition(), schema.getExtensionDefinitions()); + } else { + if (hasAstDefinitionComments(schema) || hasDescription(schema)) { + out.print(printComments(schema, "")); + } + List directives = DirectivesUtil.toAppliedDirectives(schema.getSchemaAppliedDirectives(), schema.getSchemaDirectives()); + out.format("schema %s{\n", directivesString(GraphQLSchemaElement.class, directives)); + if (queryType != null) { + out.format(" query: %s\n", queryType.getName()); + } + if (mutationType != null) { + out.format(" mutation: %s\n", mutationType.getName()); + } + if (subscriptionType != null) { + out.format(" subscription: %s\n", subscriptionType.getName()); + } + out.format("}\n\n"); } - out.format("}\n\n"); } }; } diff --git a/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy b/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy index d138793eeb..87119a1216 100644 --- a/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy +++ b/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy @@ -1342,11 +1342,292 @@ type Query { ''' } + def "can print extend schema block when AST printing enabled"() { + def sdl = ''' + directive @schemaDirective on SCHEMA + + """ + My schema block description + """ + schema { + mutation: MyMutation + } + + extend schema @schemaDirective { + query: MyQuery + } + + extend schema { + subscription: MySubscription + } + + type MyQuery { + foo: String + } + + type MyMutation { + pizza: String + } + + type MySubscription { + chippies: String + } + ''' + + when: + def runtimeWiring = newRuntimeWiring().build() + + def options = SchemaGenerator.Options.defaultOptions() + def types = new SchemaParser().parse(sdl) + GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(options, types, runtimeWiring) + + def printOptions = defaultOptions() + .includeScalarTypes(true) + .useAstDefinitions(true) + .includeSchemaDefinition(true) + def result = new SchemaPrinter(printOptions).print(schema) + + then: + result == '''""" +My schema block description +""" +schema { + mutation: MyMutation +} + +extend schema @schemaDirective { + query: MyQuery +} + +extend schema { + subscription: MySubscription +} + +"Marks the field, argument, input field or enum value as deprecated" +directive @deprecated( + "The reason for the deprecation" + reason: String = "No longer supported" + ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION + +"Directs the executor to include this field or fragment only when the `if` argument is true" +directive @include( + "Included when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Indicates an Input Object is a OneOf Input Object." +directive @oneOf on INPUT_OBJECT + +directive @schemaDirective on SCHEMA + +"Directs the executor to skip this field or fragment when the `if` argument is true." +directive @skip( + "Skipped when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Exposes a URL that specifies the behaviour of this scalar." +directive @specifiedBy( + "The URL that specifies the behaviour of this scalar." + url: String! + ) on SCALAR + +type MyMutation { + pizza: String +} + +type MyQuery { + foo: String +} + +type MySubscription { + chippies: String +} +''' + } + + def "will not print extend schema block when AST printing not enabled"() { + def sdl = ''' + directive @schemaDirective on SCHEMA + + """ + My schema block description + """ + schema { + mutation: MyMutation + } + + extend schema @schemaDirective { + query: MyQuery + } + + type MyQuery { + foo: String + } + + type MyMutation { + pizza: String + } + ''' + + when: + def runtimeWiring = newRuntimeWiring().build() + + def options = SchemaGenerator.Options.defaultOptions() + def types = new SchemaParser().parse(sdl) + GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(options, types, runtimeWiring) + + def printOptions = defaultOptions() + .includeScalarTypes(true) + .useAstDefinitions(false) + .includeSchemaDefinition(true) + def result = new SchemaPrinter(printOptions).print(schema) + + then: + result == '''"My schema block description" +schema @schemaDirective{ + query: MyQuery + mutation: MyMutation +} + +"Marks the field, argument, input field or enum value as deprecated" +directive @deprecated( + "The reason for the deprecation" + reason: String = "No longer supported" + ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION + +"Directs the executor to include this field or fragment only when the `if` argument is true" +directive @include( + "Included when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Indicates an Input Object is a OneOf Input Object." +directive @oneOf on INPUT_OBJECT + +directive @schemaDirective on SCHEMA + +"Directs the executor to skip this field or fragment when the `if` argument is true." +directive @skip( + "Skipped when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Exposes a URL that specifies the behaviour of this scalar." +directive @specifiedBy( + "The URL that specifies the behaviour of this scalar." + url: String! + ) on SCALAR + +type MyMutation { + pizza: String +} + +type MyQuery { + foo: String +} +''' + } + + def "can parse and print extend schema description"() { + // DZ TODO: this is failing at the moment + def sdl = ''' + directive @schemaDirective on SCHEMA + + """ + My schema block description + """ + schema { + mutation: MyMutation + } + + """ + My extend schema description + """ + extend schema @schemaDirective { + query: MyQuery + } + + type MyQuery { + foo: String + } + + type MyMutation { + pizza: String + } + ''' + + when: + def runtimeWiring = newRuntimeWiring().build() + + def options = SchemaGenerator.Options.defaultOptions() + def types = new SchemaParser().parse(sdl) + GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(options, types, runtimeWiring) + + def printOptions = defaultOptions() + .includeScalarTypes(true) + .useAstDefinitions(true) + .includeSchemaDefinition(true) + def result = new SchemaPrinter(printOptions).print(schema) + + then: + result == '''""" +My schema block description +""" +schema { + mutation: MyMutation +} + +""" +My extend schema description +""" +extend schema @schemaDirective { + query: MyQuery +} + +"Marks the field, argument, input field or enum value as deprecated" +directive @deprecated( + "The reason for the deprecation" + reason: String = "No longer supported" + ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION + +"Directs the executor to include this field or fragment only when the `if` argument is true" +directive @include( + "Included when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Indicates an Input Object is a OneOf Input Object." +directive @oneOf on INPUT_OBJECT + +directive @schemaDirective on SCHEMA + +"Directs the executor to skip this field or fragment when the `if` argument is true." +directive @skip( + "Skipped when true." + if: Boolean! + ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"Exposes a URL that specifies the behaviour of this scalar." +directive @specifiedBy( + "The URL that specifies the behaviour of this scalar." + url: String! + ) on SCALAR + +type MyMutation { + pizza: String +} + +type MyQuery { + foo: String +} +''' + } + + def "can print a schema as AST elements"() { def sdl = ''' directive @directive1 on SCALAR - directive @schemaDirective on SCHEMA - extend schema @schemaDirective type Query { foo : String @@ -1462,8 +1743,6 @@ directive @include( "Indicates an Input Object is a OneOf Input Object." directive @oneOf on INPUT_OBJECT -directive @schemaDirective on SCHEMA - "Directs the executor to skip this field or fragment when the `if` argument is true." directive @skip( "Skipped when true." @@ -1539,8 +1818,6 @@ extend input Input { extend input Input { faz: String } - -extend schema @schemaDirective ''' when: From 32c87b56bff819f7cc55d55b0317db6cd469c9cb Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Thu, 2 May 2024 07:15:07 +1000 Subject: [PATCH 18/29] Tidy up --- .../schema/idl/SchemaPrinterTest.groovy | 98 ------------------- 1 file changed, 98 deletions(-) diff --git a/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy b/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy index 87119a1216..2802f72808 100644 --- a/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy +++ b/src/test/groovy/graphql/schema/idl/SchemaPrinterTest.groovy @@ -1382,7 +1382,6 @@ type Query { GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(options, types, runtimeWiring) def printOptions = defaultOptions() - .includeScalarTypes(true) .useAstDefinitions(true) .includeSchemaDefinition(true) def result = new SchemaPrinter(printOptions).print(schema) @@ -1478,7 +1477,6 @@ type MySubscription { GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(options, types, runtimeWiring) def printOptions = defaultOptions() - .includeScalarTypes(true) .useAstDefinitions(false) .includeSchemaDefinition(true) def result = new SchemaPrinter(printOptions).print(schema) @@ -1529,102 +1527,6 @@ type MyQuery { ''' } - def "can parse and print extend schema description"() { - // DZ TODO: this is failing at the moment - def sdl = ''' - directive @schemaDirective on SCHEMA - - """ - My schema block description - """ - schema { - mutation: MyMutation - } - - """ - My extend schema description - """ - extend schema @schemaDirective { - query: MyQuery - } - - type MyQuery { - foo: String - } - - type MyMutation { - pizza: String - } - ''' - - when: - def runtimeWiring = newRuntimeWiring().build() - - def options = SchemaGenerator.Options.defaultOptions() - def types = new SchemaParser().parse(sdl) - GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(options, types, runtimeWiring) - - def printOptions = defaultOptions() - .includeScalarTypes(true) - .useAstDefinitions(true) - .includeSchemaDefinition(true) - def result = new SchemaPrinter(printOptions).print(schema) - - then: - result == '''""" -My schema block description -""" -schema { - mutation: MyMutation -} - -""" -My extend schema description -""" -extend schema @schemaDirective { - query: MyQuery -} - -"Marks the field, argument, input field or enum value as deprecated" -directive @deprecated( - "The reason for the deprecation" - reason: String = "No longer supported" - ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION - -"Directs the executor to include this field or fragment only when the `if` argument is true" -directive @include( - "Included when true." - if: Boolean! - ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - -"Indicates an Input Object is a OneOf Input Object." -directive @oneOf on INPUT_OBJECT - -directive @schemaDirective on SCHEMA - -"Directs the executor to skip this field or fragment when the `if` argument is true." -directive @skip( - "Skipped when true." - if: Boolean! - ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - -"Exposes a URL that specifies the behaviour of this scalar." -directive @specifiedBy( - "The URL that specifies the behaviour of this scalar." - url: String! - ) on SCALAR - -type MyMutation { - pizza: String -} - -type MyQuery { - foo: String -} -''' - } - - def "can print a schema as AST elements"() { def sdl = ''' directive @directive1 on SCALAR From 2a8b1b9ca08250aa04ce04ecaaaffdc9fa6c5e35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 16:23:39 +0000 Subject: [PATCH 19/29] Bump com.fasterxml.jackson.core:jackson-databind from 2.17.0 to 2.17.1 Bumps [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) from 2.17.0 to 2.17.1. - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 919b01740e..7cd75a9398 100644 --- a/build.gradle +++ b/build.gradle @@ -111,7 +111,7 @@ dependencies { testImplementation 'org.codehaus.groovy:groovy-json:3.0.21' testImplementation 'com.google.code.gson:gson:2.10.1' testImplementation 'org.eclipse.jetty:jetty-server:11.0.20' - testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0' + testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' testImplementation 'org.awaitility:awaitility-groovy:4.2.0' testImplementation 'com.github.javafaker:javafaker:1.0.2' From b177520bb7bb21c20e1bf91d4da344aba9018b96 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Thu, 9 May 2024 08:11:41 +1000 Subject: [PATCH 20/29] Add @deprecated validation for input type fields --- ...ecatedInputObjectAndArgumentsAreValid.java | 37 +++++++++ .../validation/SchemaValidationErrorType.java | 5 +- .../schema/validation/SchemaValidator.java | 1 + ...InputObjectAndArgumentsAreValidTest.groovy | 79 +++++++++++++++++++ .../validation/SchemaValidatorTest.groovy | 3 +- 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java create mode 100644 src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy diff --git a/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java b/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java new file mode 100644 index 0000000000..3645103880 --- /dev/null +++ b/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java @@ -0,0 +1,37 @@ +package graphql.schema.validation; + +import graphql.Internal; +import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLInputObjectField; +import graphql.schema.GraphQLInputObjectType; +import graphql.schema.GraphQLSchemaElement; +import graphql.schema.GraphQLTypeUtil; +import graphql.schema.GraphQLTypeVisitorStub; +import graphql.util.TraversalControl; +import graphql.util.TraverserContext; + +import static java.lang.String.format; + +/* + From the spec: + The @deprecated directive must not appear on required (non-null without a default) arguments + or input object field definitions. + */ +@Internal +public class DeprecatedInputObjectAndArgumentsAreValid extends GraphQLTypeVisitorStub { + + @Override + public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField inputObjectField, TraverserContext context) { + // There can only be at most one @deprecated, because it is not a repeatable directive + GraphQLAppliedDirective deprecatedDirective = inputObjectField.getAppliedDirective("deprecated"); + + if (deprecatedDirective != null && GraphQLTypeUtil.isNonNull(inputObjectField.getType()) && !inputObjectField.hasSetDefaultValue()) { + GraphQLInputObjectType inputObjectType = (GraphQLInputObjectType) context.getParentNode(); + SchemaValidationErrorCollector errorCollector = context.getVarFromParents(SchemaValidationErrorCollector.class); + String message = format("Required input field %s.%s cannot be deprecated.", inputObjectType.getName(), inputObjectField.getName()); + errorCollector.addError(new SchemaValidationError(SchemaValidationErrorType.RequiredInputFieldCannotBeDeprecated, message)); + } + return TraversalControl.CONTINUE; + } + +} diff --git a/src/main/java/graphql/schema/validation/SchemaValidationErrorType.java b/src/main/java/graphql/schema/validation/SchemaValidationErrorType.java index 08209bb6b2..b8392b18d7 100644 --- a/src/main/java/graphql/schema/validation/SchemaValidationErrorType.java +++ b/src/main/java/graphql/schema/validation/SchemaValidationErrorType.java @@ -21,5 +21,8 @@ public enum SchemaValidationErrorType implements SchemaValidationErrorClassifica OutputTypeUsedInInputTypeContext, InputTypeUsedInOutputTypeContext, OneOfDefaultValueOnField, - OneOfNonNullableField + OneOfNonNullableField, + RequiredInputFieldCannotBeDeprecated, + RequiredFieldArgumentCannotBeDeprecated, + RequiredDirectiveArgumentCannotBeDeprecated } diff --git a/src/main/java/graphql/schema/validation/SchemaValidator.java b/src/main/java/graphql/schema/validation/SchemaValidator.java index 0d8d7f271b..1f676fb77e 100644 --- a/src/main/java/graphql/schema/validation/SchemaValidator.java +++ b/src/main/java/graphql/schema/validation/SchemaValidator.java @@ -26,6 +26,7 @@ public SchemaValidator() { rules.add(new AppliedDirectiveArgumentsAreValid()); rules.add(new InputAndOutputTypesUsedAppropriately()); rules.add(new OneOfInputObjectRules()); + rules.add(new DeprecatedInputObjectAndArgumentsAreValid()); } public List getRules() { diff --git a/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy b/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy new file mode 100644 index 0000000000..e91330d332 --- /dev/null +++ b/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy @@ -0,0 +1,79 @@ +package graphql.schema.validation + +import graphql.TestUtil +import spock.lang.Specification + +class DeprecatedInputObjectAndArgumentsAreValidTest extends Specification { + + def "required input field cannot be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(pizzaInfo: PizzaInfo!): String + } + + input PizzaInfo { + name: String! + pineapples: Boolean! @deprecated(reason: "Don't need this input field") + } + ''' + + when: + TestUtil.schema(sdl) + + then: + def schemaProblem = thrown(InvalidSchemaException) + schemaProblem.getErrors().size() == 1 + schemaProblem.getErrors().first().description == "Required input field PizzaInfo.pineapples cannot be deprecated." + } + + def "nullable input field can be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(pizzaInfo: PizzaInfo!): String + } + + input PizzaInfo { + name: String! + pineapples: Boolean @deprecated(reason: "Don't need this input field") + } + ''' + + when: + TestUtil.schema(sdl) + + then: + noExceptionThrown() + } + + def "non-nullable input field with default value can be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(pizzaInfo: PizzaInfo!): String + } + + input PizzaInfo { + name: String! + pineapples: Boolean! = false @deprecated(reason: "Don't need this input field") + } + ''' + + when: + TestUtil.schema(sdl) + + then: + noExceptionThrown() + } + +} diff --git a/src/test/groovy/graphql/schema/validation/SchemaValidatorTest.groovy b/src/test/groovy/graphql/schema/validation/SchemaValidatorTest.groovy index 6efd8acbe8..706542df8d 100644 --- a/src/test/groovy/graphql/schema/validation/SchemaValidatorTest.groovy +++ b/src/test/groovy/graphql/schema/validation/SchemaValidatorTest.groovy @@ -11,7 +11,7 @@ class SchemaValidatorTest extends Specification { def validator = new SchemaValidator() def rules = validator.rules then: - rules.size() == 8 + rules.size() == 9 rules[0] instanceof NoUnbrokenInputCycles rules[1] instanceof TypesImplementInterfaces rules[2] instanceof TypeAndFieldRule @@ -20,5 +20,6 @@ class SchemaValidatorTest extends Specification { rules[5] instanceof AppliedDirectiveArgumentsAreValid rules[6] instanceof InputAndOutputTypesUsedAppropriately rules[7] instanceof OneOfInputObjectRules + rules[8] instanceof DeprecatedInputObjectAndArgumentsAreValid } } From 07dd80717a30d1da2a4e163ea567e5fc60959996 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 12 May 2024 18:10:41 +1000 Subject: [PATCH 21/29] Add validation for directive and field arguments --- ...ecatedInputObjectAndArgumentsAreValid.java | 28 ++- ...InputObjectAndArgumentsAreValidTest.groovy | 219 +++++++++++++++++- 2 files changed, 245 insertions(+), 2 deletions(-) diff --git a/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java b/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java index 3645103880..be62620d31 100644 --- a/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java +++ b/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java @@ -2,6 +2,9 @@ import graphql.Internal; import graphql.schema.GraphQLAppliedDirective; +import graphql.schema.GraphQLArgument; +import graphql.schema.GraphQLDirective; +import graphql.schema.GraphQLFieldDefinition; import graphql.schema.GraphQLInputObjectField; import graphql.schema.GraphQLInputObjectType; import graphql.schema.GraphQLSchemaElement; @@ -28,10 +31,33 @@ public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField inp if (deprecatedDirective != null && GraphQLTypeUtil.isNonNull(inputObjectField.getType()) && !inputObjectField.hasSetDefaultValue()) { GraphQLInputObjectType inputObjectType = (GraphQLInputObjectType) context.getParentNode(); SchemaValidationErrorCollector errorCollector = context.getVarFromParents(SchemaValidationErrorCollector.class); - String message = format("Required input field %s.%s cannot be deprecated.", inputObjectType.getName(), inputObjectField.getName()); + String message = format("Required input field '%s.%s' cannot be deprecated.", inputObjectType.getName(), inputObjectField.getName()); errorCollector.addError(new SchemaValidationError(SchemaValidationErrorType.RequiredInputFieldCannotBeDeprecated, message)); } return TraversalControl.CONTINUE; } + // An argument can appear as either a field argument or a directive argument. This visitor will visit both field arguments and directive arguments. + // An applied directive's argument cannot be deprecated. + @Override + public TraversalControl visitGraphQLArgument(GraphQLArgument argument, TraverserContext context) { + // There can only be at most one @deprecated, because it is not a repeatable directive + GraphQLAppliedDirective deprecatedDirective = argument.getAppliedDirective("deprecated"); + + if (deprecatedDirective != null && GraphQLTypeUtil.isNonNull(argument.getType()) && !argument.hasSetDefaultValue()) { + if (context.getParentNode() instanceof GraphQLFieldDefinition) { + GraphQLFieldDefinition fieldDefinition = (GraphQLFieldDefinition) context.getParentNode(); + SchemaValidationErrorCollector errorCollector = context.getVarFromParents(SchemaValidationErrorCollector.class); + String message = format("Required argument '%s' on field '%s' cannot be deprecated.", argument.getName(), fieldDefinition.getName()); + errorCollector.addError(new SchemaValidationError(SchemaValidationErrorType.RequiredFieldArgumentCannotBeDeprecated, message)); + } else if (context.getParentNode() instanceof GraphQLDirective) { + GraphQLDirective directive = (GraphQLDirective) context.getParentNode(); + SchemaValidationErrorCollector errorCollector = context.getVarFromParents(SchemaValidationErrorCollector.class); + String message = format("Required argument '%s' on directive '%s' cannot be deprecated.", argument.getName(), directive.getName()); + errorCollector.addError(new SchemaValidationError(SchemaValidationErrorType.RequiredDirectiveArgumentCannotBeDeprecated, message)); + } + } + return TraversalControl.CONTINUE; + } + } diff --git a/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy b/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy index e91330d332..b65bd7c0f6 100644 --- a/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy +++ b/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy @@ -18,6 +18,7 @@ class DeprecatedInputObjectAndArgumentsAreValidTest extends Specification { input PizzaInfo { name: String! pineapples: Boolean! @deprecated(reason: "Don't need this input field") + spicy: Boolean @deprecated(reason: "Don't need this nullable input field") } ''' @@ -27,7 +28,59 @@ class DeprecatedInputObjectAndArgumentsAreValidTest extends Specification { then: def schemaProblem = thrown(InvalidSchemaException) schemaProblem.getErrors().size() == 1 - schemaProblem.getErrors().first().description == "Required input field PizzaInfo.pineapples cannot be deprecated." + schemaProblem.getErrors().first().description == "Required input field 'PizzaInfo.pineapples' cannot be deprecated." + } + + def "multiple required input fields cannot be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(pizzaInfo: PizzaInfo!): String + } + + input PizzaInfo { + name: String! + pineapples: Boolean! @deprecated(reason: "Don't need this input field") + spicy: Boolean! @deprecated(reason: "Don't need this input field") + } + ''' + + when: + TestUtil.schema(sdl) + + then: + def schemaProblem = thrown(InvalidSchemaException) + schemaProblem.getErrors().size() == 2 + schemaProblem.getErrors()[0].description == "Required input field 'PizzaInfo.pineapples' cannot be deprecated." + schemaProblem.getErrors()[1].description == "Required input field 'PizzaInfo.spicy' cannot be deprecated." + } + + def "required input field list cannot be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(pizzaInfos: [PizzaInfo]!): String + } + + input PizzaInfo { + name: String! + pineapples: Boolean! @deprecated(reason: "Don't need this input field") + } + ''' + + when: + TestUtil.schema(sdl) + + then: + def schemaProblem = thrown(InvalidSchemaException) + schemaProblem.getErrors().size() == 1 + schemaProblem.getErrors().first().description == "Required input field 'PizzaInfo.pineapples' cannot be deprecated." } def "nullable input field can be deprecated"() { @@ -76,4 +129,168 @@ class DeprecatedInputObjectAndArgumentsAreValidTest extends Specification { noExceptionThrown() } + def "required field argument cannot be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(name: String!, pineapples: Boolean! @deprecated(reason: "Don't need this field argument")): String + } + ''' + + when: + TestUtil.schema(sdl) + + then: + def schemaProblem = thrown(InvalidSchemaException) + schemaProblem.getErrors().size() == 1 + schemaProblem.getErrors().first().description == "Required argument 'pineapples' on field 'updatePizza' cannot be deprecated." + } + + def "multiple required field arguments cannot be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(name: String! @deprecated(reason: "yeah nah"), pineapples: Boolean! @deprecated(reason: "Don't need this field argument")): String + } + ''' + + when: + TestUtil.schema(sdl) + + then: + def schemaProblem = thrown(InvalidSchemaException) + schemaProblem.getErrors().size() == 2 + schemaProblem.getErrors()[0].description == "Required argument 'name' on field 'updatePizza' cannot be deprecated." + schemaProblem.getErrors()[1].description == "Required argument 'pineapples' on field 'updatePizza' cannot be deprecated." + } + + def "nullable field argument can be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(name: String!, pineapples: Boolean @deprecated(reason: "Don't need this field argument")): String + } + ''' + + when: + TestUtil.schema(sdl) + + then: + noExceptionThrown() + } + + def "non-nullable field argument with default value can be deprecated"() { + def sdl = ''' + type Query { + pizza(name: String!): String + } + + type Mutation { + updatePizza(name: String!, pineapples: Boolean! = false @deprecated(reason: "Don't need this field argument")): String + } + ''' + + when: + TestUtil.schema(sdl) + + then: + noExceptionThrown() + } + + def "required directive argument cannot be deprecated"() { + def sdl = ''' + directive @pizzaDirective(name: String!, likesPineapples: Boolean! @deprecated(reason: "Don't need this directive argument")) on FIELD_DEFINITION + + type Query { + pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: true) + } + + type Mutation { + updatePizza(name: String!, pineapples: Boolean!): String + } + ''' + + when: + TestUtil.schema(sdl) + + then: + def schemaProblem = thrown(InvalidSchemaException) + schemaProblem.getErrors().size() == 1 + schemaProblem.getErrors().first().description == "Required argument 'likesPineapples' on directive 'pizzaDirective' cannot be deprecated." + } + + def "multiple required directive arguments cannot be deprecated"() { + def sdl = ''' + directive @pizzaDirective(name: String! @deprecated, likesPineapples: Boolean! @deprecated(reason: "Don't need this directive argument")) on FIELD_DEFINITION + + type Query { + pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: true) + } + + type Mutation { + updatePizza(name: String!, pineapples: Boolean!): String + } + ''' + + when: + TestUtil.schema(sdl) + + then: + def schemaProblem = thrown(InvalidSchemaException) + schemaProblem.getErrors().size() == 2 + schemaProblem.getErrors()[0].description == "Required argument 'name' on directive 'pizzaDirective' cannot be deprecated." + schemaProblem.getErrors()[1].description == "Required argument 'likesPineapples' on directive 'pizzaDirective' cannot be deprecated." + } + + def "nullable directive argument can be deprecated"() { + def sdl = ''' + directive @pizzaDirective(name: String!, likesPineapples: Boolean @deprecated(reason: "Don't need this directive argument")) on FIELD_DEFINITION + + type Query { + pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: true) + } + + type Mutation { + updatePizza(name: String!, pineapples: Boolean!): String + } + + ''' + + when: + TestUtil.schema(sdl) + + then: + noExceptionThrown() + } + + def "non-nullable directive argument with default value can be deprecated"() { + def sdl = ''' + directive @pizzaDirective(name: String!, likesPineapples: Boolean! = false @deprecated(reason: "Don't need this directive argument")) on FIELD_DEFINITION + + type Query { + pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: true) + } + + type Mutation { + updatePizza(name: String!, pineapples: Boolean!): String + } + + ''' + + when: + TestUtil.schema(sdl) + + then: + noExceptionThrown() + } + } From 1ce7d9929d8ac24a729809189ceaaa2f54b816a5 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 12 May 2024 18:14:14 +1000 Subject: [PATCH 22/29] He does not like pineapples --- .../DeprecatedInputObjectAndArgumentsAreValidTest.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy b/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy index b65bd7c0f6..056f134db4 100644 --- a/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy +++ b/src/test/groovy/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValidTest.groovy @@ -211,7 +211,7 @@ class DeprecatedInputObjectAndArgumentsAreValidTest extends Specification { directive @pizzaDirective(name: String!, likesPineapples: Boolean! @deprecated(reason: "Don't need this directive argument")) on FIELD_DEFINITION type Query { - pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: true) + pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: false) } type Mutation { @@ -233,7 +233,7 @@ class DeprecatedInputObjectAndArgumentsAreValidTest extends Specification { directive @pizzaDirective(name: String! @deprecated, likesPineapples: Boolean! @deprecated(reason: "Don't need this directive argument")) on FIELD_DEFINITION type Query { - pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: true) + pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: false) } type Mutation { @@ -256,7 +256,7 @@ class DeprecatedInputObjectAndArgumentsAreValidTest extends Specification { directive @pizzaDirective(name: String!, likesPineapples: Boolean @deprecated(reason: "Don't need this directive argument")) on FIELD_DEFINITION type Query { - pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: true) + pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: false) } type Mutation { @@ -277,7 +277,7 @@ class DeprecatedInputObjectAndArgumentsAreValidTest extends Specification { directive @pizzaDirective(name: String!, likesPineapples: Boolean! = false @deprecated(reason: "Don't need this directive argument")) on FIELD_DEFINITION type Query { - pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: true) + pizza(name: String!): String @pizzaDirective(name: "Stefano", likesPineapples: false) } type Mutation { From 291a3d7d425c1a27878e8200382170f42826d06b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 17:00:39 +0000 Subject: [PATCH 23/29] Bump net.bytebuddy:byte-buddy-agent from 1.14.14 to 1.14.15 Bumps [net.bytebuddy:byte-buddy-agent](https://github.com/raphw/byte-buddy) from 1.14.14 to 1.14.15. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.14...byte-buddy-1.14.15) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- agent-test/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent-test/build.gradle b/agent-test/build.gradle index 30ebd04262..2ce1e1892c 100644 --- a/agent-test/build.gradle +++ b/agent-test/build.gradle @@ -4,7 +4,7 @@ plugins { dependencies { implementation(rootProject) - implementation("net.bytebuddy:byte-buddy-agent:1.14.14") + implementation("net.bytebuddy:byte-buddy-agent:1.14.15") testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' From 370859279cfd1b26ffeec556d70d1670fcfe9ecb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 17:00:44 +0000 Subject: [PATCH 24/29] Bump net.bytebuddy:byte-buddy from 1.14.14 to 1.14.15 Bumps [net.bytebuddy:byte-buddy](https://github.com/raphw/byte-buddy) from 1.14.14 to 1.14.15. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.14...byte-buddy-1.14.15) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- agent/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/build.gradle b/agent/build.gradle index 2e2430cb46..c81c0e4d7c 100644 --- a/agent/build.gradle +++ b/agent/build.gradle @@ -6,7 +6,7 @@ plugins { } dependencies { - implementation("net.bytebuddy:byte-buddy:1.14.14") + implementation("net.bytebuddy:byte-buddy:1.14.15") // graphql-java itself implementation(rootProject) } From c2906a4da22ea54a092f9c4d528ad71dba14ee66 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Wed, 15 May 2024 07:48:32 +1000 Subject: [PATCH 25/29] Stop string spread --- .../DeprecatedInputObjectAndArgumentsAreValid.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java b/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java index be62620d31..ce619223cc 100644 --- a/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java +++ b/src/main/java/graphql/schema/validation/DeprecatedInputObjectAndArgumentsAreValid.java @@ -1,5 +1,6 @@ package graphql.schema.validation; +import graphql.Directives; import graphql.Internal; import graphql.schema.GraphQLAppliedDirective; import graphql.schema.GraphQLArgument; @@ -26,7 +27,7 @@ public class DeprecatedInputObjectAndArgumentsAreValid extends GraphQLTypeVisito @Override public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField inputObjectField, TraverserContext context) { // There can only be at most one @deprecated, because it is not a repeatable directive - GraphQLAppliedDirective deprecatedDirective = inputObjectField.getAppliedDirective("deprecated"); + GraphQLAppliedDirective deprecatedDirective = inputObjectField.getAppliedDirective(Directives.DEPRECATED_DIRECTIVE_DEFINITION.getName()); if (deprecatedDirective != null && GraphQLTypeUtil.isNonNull(inputObjectField.getType()) && !inputObjectField.hasSetDefaultValue()) { GraphQLInputObjectType inputObjectType = (GraphQLInputObjectType) context.getParentNode(); @@ -42,7 +43,7 @@ public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField inp @Override public TraversalControl visitGraphQLArgument(GraphQLArgument argument, TraverserContext context) { // There can only be at most one @deprecated, because it is not a repeatable directive - GraphQLAppliedDirective deprecatedDirective = argument.getAppliedDirective("deprecated"); + GraphQLAppliedDirective deprecatedDirective = argument.getAppliedDirective(Directives.DEPRECATED_DIRECTIVE_DEFINITION.getName()); if (deprecatedDirective != null && GraphQLTypeUtil.isNonNull(argument.getType()) && !argument.hasSetDefaultValue()) { if (context.getParentNode() instanceof GraphQLFieldDefinition) { From 7ced541661e8585291f4028bbc57fda46d02d28b Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Mon, 20 May 2024 07:50:12 +1000 Subject: [PATCH 26/29] Get ready for next release --- .github/workflows/pull_request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5e90decd21..ef52a4f3cb 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -7,10 +7,10 @@ on: pull_request: branches: - master + - 22.x + - 21.x - 20.x - 19.x - - 18.x - - 17.x jobs: buildAndTest: runs-on: ubuntu-latest From bbb1898ff6791e01223e304541a49b808d93a5c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 16:04:44 +0000 Subject: [PATCH 27/29] Bump org.eclipse.jetty:jetty-server from 11.0.20 to 11.0.21 Bumps org.eclipse.jetty:jetty-server from 11.0.20 to 11.0.21. --- updated-dependencies: - dependency-name: org.eclipse.jetty:jetty-server dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7cd75a9398..47f2c92328 100644 --- a/build.gradle +++ b/build.gradle @@ -110,7 +110,7 @@ dependencies { testImplementation 'org.codehaus.groovy:groovy:3.0.21' testImplementation 'org.codehaus.groovy:groovy-json:3.0.21' testImplementation 'com.google.code.gson:gson:2.10.1' - testImplementation 'org.eclipse.jetty:jetty-server:11.0.20' + testImplementation 'org.eclipse.jetty:jetty-server:11.0.21' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' testImplementation 'org.awaitility:awaitility-groovy:4.2.0' testImplementation 'com.github.javafaker:javafaker:1.0.2' From ff713160bc7613d876b612b87e4cd1c2158090e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 16:49:34 +0000 Subject: [PATCH 28/29] Bump google-github-actions/auth from 2.1.2 to 2.1.3 Bumps [google-github-actions/auth](https://github.com/google-github-actions/auth) from 2.1.2 to 2.1.3. - [Release notes](https://github.com/google-github-actions/auth/releases) - [Changelog](https://github.com/google-github-actions/auth/blob/main/CHANGELOG.md) - [Commits](https://github.com/google-github-actions/auth/compare/v2.1.2...v2.1.3) --- updated-dependencies: - dependency-name: google-github-actions/auth dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/invoke_test_runner.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/invoke_test_runner.yml b/.github/workflows/invoke_test_runner.yml index 16b6e15c61..61be8d20da 100644 --- a/.github/workflows/invoke_test_runner.yml +++ b/.github/workflows/invoke_test_runner.yml @@ -50,7 +50,7 @@ jobs: - id: 'auth' name: 'Authenticate to Google Cloud' - uses: google-github-actions/auth@v2.1.2 + uses: google-github-actions/auth@v2.1.3 with: credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} From d50ed88489ceb22a6cdb39fe161de14f1e09175b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 23:27:41 +0000 Subject: [PATCH 29/29] Bump com.google.code.gson:gson from 2.10.1 to 2.11.0 Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.10.1 to 2.11.0. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-parent-2.10.1...gson-parent-2.11.0) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 47f2c92328..b3fdb18952 100644 --- a/build.gradle +++ b/build.gradle @@ -109,7 +109,7 @@ dependencies { testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' testImplementation 'org.codehaus.groovy:groovy:3.0.21' testImplementation 'org.codehaus.groovy:groovy-json:3.0.21' - testImplementation 'com.google.code.gson:gson:2.10.1' + testImplementation 'com.google.code.gson:gson:2.11.0' testImplementation 'org.eclipse.jetty:jetty-server:11.0.21' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' testImplementation 'org.awaitility:awaitility-groovy:4.2.0'