From 978dccebaaddfcc3c53b71e2dda8d31797faacdd Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Mon, 1 Jan 2024 10:49:27 +0900 Subject: [PATCH 1/8] Fix test --- .../groovy/example/http/ExecutionResultJSONTesting.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/groovy/example/http/ExecutionResultJSONTesting.java b/src/test/groovy/example/http/ExecutionResultJSONTesting.java index 965ac68e4b..019eb17616 100644 --- a/src/test/groovy/example/http/ExecutionResultJSONTesting.java +++ b/src/test/groovy/example/http/ExecutionResultJSONTesting.java @@ -70,7 +70,11 @@ private void testGson(HttpServletResponse response, Object er) throws IOExceptio private ExecutionResult createER() { List errors = new ArrayList<>(); - errors.add(new ValidationError(ValidationErrorType.UnknownType, mkLocations(), "Test ValidationError")); // Retain as there is no alternative constructor for ValidationError + errors.add(ValidationError.newValidationError() + .validationErrorType(ValidationErrorType.UnknownType) + .sourceLocations(mkLocations()) + .description("Test ValidationError") + .build()); errors.add(new MissingRootTypeException("Mutations are not supported.", null)); errors.add(new InvalidSyntaxError(mkLocations(), "Not good syntax m'kay")); errors.add(new NonNullableFieldWasNullError(new NonNullableFieldWasNullException(mkExecutionInfo(), mkPath()))); From 7fb726238acf2a9314c4ff6e6de13932765a68e9 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:13:52 +0900 Subject: [PATCH 2/8] Fix Groovy tests --- .../graphql/validation/ValidationError.java | 1 - src/test/groovy/graphql/ErrorsTest.groovy | 18 +++++++++++++++--- .../groovy/graphql/GraphQLErrorTest.groovy | 6 +++++- .../graphql/GraphqlErrorHelperTest.groovy | 6 +++++- .../execution/DataFetcherResultTest.groovy | 2 +- ...tegyExceptionHandlingEquivalenceTest.groovy | 2 +- .../PreparsedDocumentEntryTest.groovy | 2 +- .../graphql/language/SerialisationTest.groovy | 12 ++++++++++-- 8 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/main/java/graphql/validation/ValidationError.java b/src/main/java/graphql/validation/ValidationError.java index b3b27d8477..04c1f88936 100644 --- a/src/main/java/graphql/validation/ValidationError.java +++ b/src/main/java/graphql/validation/ValidationError.java @@ -2,7 +2,6 @@ import com.google.common.collect.ImmutableMap; -import graphql.DeprecatedAt; import graphql.ErrorType; import graphql.GraphQLError; import graphql.GraphqlErrorHelper; diff --git a/src/test/groovy/graphql/ErrorsTest.groovy b/src/test/groovy/graphql/ErrorsTest.groovy index dfae441761..e346a26b47 100644 --- a/src/test/groovy/graphql/ErrorsTest.groovy +++ b/src/test/groovy/graphql/ErrorsTest.groovy @@ -43,9 +43,21 @@ class ErrorsTest extends Specification { def "ValidationError equals and hashcode works"() { expect: - def same1 = new ValidationError(ValidationErrorType.BadValueForDefaultArg, [src(15, 34), src(23, 567)], "bad ju ju") - def same2 = new ValidationError(ValidationErrorType.BadValueForDefaultArg, [src(15, 34), src(23, 567)], "bad ju ju") - def different1 = new ValidationError(ValidationErrorType.FieldsConflict, [src(15, 34), src(23, 567)], "bad ju ju") + def same1 = ValidationError.newValidationError() + .validationErrorType(ValidationErrorType.BadValueForDefaultArg) + .sourceLocations([src(15, 34), src(23, 567)]) + .description("bad ju ju") + .build() + def same2 = ValidationError.newValidationError() + .validationErrorType(ValidationErrorType.BadValueForDefaultArg) + .sourceLocations([src(15, 34), src(23, 567)]) + .description("bad ju ju") + .build() + def different1 = ValidationError.newValidationError() + .validationErrorType(ValidationErrorType.FieldsConflict) + .sourceLocations([src(15, 34), src(23, 567)]) + .description("bad ju ju") + .build() commonAssert(same1, same2, different1) } diff --git a/src/test/groovy/graphql/GraphQLErrorTest.groovy b/src/test/groovy/graphql/GraphQLErrorTest.groovy index 7560419be8..ca9fc1e8d7 100644 --- a/src/test/groovy/graphql/GraphQLErrorTest.groovy +++ b/src/test/groovy/graphql/GraphQLErrorTest.groovy @@ -25,7 +25,11 @@ class GraphQLErrorTest extends Specification { where: gError | expectedMap - new ValidationError(ValidationErrorType.UnknownType, mkLocations(), "Test ValidationError") | + ValidationError.newValidationError() + .validationErrorType(ValidationErrorType.UnknownType) + .sourceLocations(mkLocations()) + .description("Test ValidationError") + .build() | [ locations: [[line: 666, column: 999], [line: 333, column: 0]], message : "Test ValidationError", diff --git a/src/test/groovy/graphql/GraphqlErrorHelperTest.groovy b/src/test/groovy/graphql/GraphqlErrorHelperTest.groovy index 34684b6ce6..88bfecb444 100644 --- a/src/test/groovy/graphql/GraphqlErrorHelperTest.groovy +++ b/src/test/groovy/graphql/GraphqlErrorHelperTest.groovy @@ -65,7 +65,11 @@ class GraphqlErrorHelperTest extends Specification { def "can turn error classifications into extensions"() { - def validationErr = new ValidationError(ValidationErrorType.InvalidFragmentType, new SourceLocation(6, 9), "Things are not valid") + def validationErr = ValidationError.newValidationError() + .validationErrorType(ValidationErrorType.InvalidFragmentType) + .sourceLocation(new SourceLocation(6, 9)) + .description("Things are not valid") + .build() when: def specMap = GraphqlErrorHelper.toSpecification(validationErr) diff --git a/src/test/groovy/graphql/execution/DataFetcherResultTest.groovy b/src/test/groovy/graphql/execution/DataFetcherResultTest.groovy index a7bc5b7fa7..35fbfe2f1d 100644 --- a/src/test/groovy/graphql/execution/DataFetcherResultTest.groovy +++ b/src/test/groovy/graphql/execution/DataFetcherResultTest.groovy @@ -7,7 +7,7 @@ import spock.lang.Specification class DataFetcherResultTest extends Specification { - def error1 = new ValidationError(ValidationErrorType.DuplicateOperationName) + def error1 = ValidationError.newValidationError().validationErrorType(ValidationErrorType.DuplicateOperationName).build() def error2 = new InvalidSyntaxError([], "Boo") def "basic building"() { diff --git a/src/test/groovy/graphql/execution/ExecutionStrategyExceptionHandlingEquivalenceTest.groovy b/src/test/groovy/graphql/execution/ExecutionStrategyExceptionHandlingEquivalenceTest.groovy index af179821cb..2c8653491e 100644 --- a/src/test/groovy/graphql/execution/ExecutionStrategyExceptionHandlingEquivalenceTest.groovy +++ b/src/test/groovy/graphql/execution/ExecutionStrategyExceptionHandlingEquivalenceTest.groovy @@ -18,7 +18,7 @@ class ExecutionStrategyExceptionHandlingEquivalenceTest extends Specification { @Override InstrumentationContext beginFieldFetch(InstrumentationFieldFetchParameters parameters, InstrumentationState state) { - throw new AbortExecutionException([new ValidationError(ValidationErrorType.UnknownType)]) // Retain as there is no alternative constructor for ValidationError + throw new AbortExecutionException([ValidationError.newValidationError().validationErrorType(ValidationErrorType.UnknownType).build()]) } } diff --git a/src/test/groovy/graphql/execution/preparsed/PreparsedDocumentEntryTest.groovy b/src/test/groovy/graphql/execution/preparsed/PreparsedDocumentEntryTest.groovy index 96e5d0f2f3..4cfa7d0e15 100644 --- a/src/test/groovy/graphql/execution/preparsed/PreparsedDocumentEntryTest.groovy +++ b/src/test/groovy/graphql/execution/preparsed/PreparsedDocumentEntryTest.groovy @@ -33,7 +33,7 @@ class PreparsedDocumentEntryTest extends Specification { def "Ensure a non-null errors returns"() { given: def errors = [new InvalidSyntaxError(new SourceLocation(0, 0), "bang"), - new ValidationError(ValidationErrorType.InvalidSyntax)] + ValidationError.newValidationError().validationErrorType(ValidationErrorType.InvalidSyntax).build()] when: def docEntry = new PreparsedDocumentEntry(errors) diff --git a/src/test/groovy/graphql/language/SerialisationTest.groovy b/src/test/groovy/graphql/language/SerialisationTest.groovy index 9a78e90913..8bd4ae46af 100644 --- a/src/test/groovy/graphql/language/SerialisationTest.groovy +++ b/src/test/groovy/graphql/language/SerialisationTest.groovy @@ -112,7 +112,11 @@ class SerialisationTest extends Specification { when: GraphQLError syntaxError1 = new InvalidSyntaxError(srcLoc(1, 1), "Bad Syntax 1") - GraphQLError validationError2 = new ValidationError(ValidationErrorType.FieldUndefined, srcLoc(2, 2), "Bad Query 2") + GraphQLError validationError2 = ValidationError.newValidationError() + .validationErrorType(ValidationErrorType.FieldUndefined) + .sourceLocation(srcLoc(2, 2)) + .description("Bad Query 2") + .build() def originalEntry = new PreparsedDocumentEntry([syntaxError1, validationError2]) PreparsedDocumentEntry newEntry = serialisedDownAndBack(originalEntry) @@ -146,7 +150,11 @@ class SerialisationTest extends Specification { Document originalDoc = TestUtil.parseQuery(query) GraphQLError syntaxError1 = new InvalidSyntaxError(srcLoc(1, 1), "Bad Syntax 1") - GraphQLError validationError2 = new ValidationError(ValidationErrorType.FieldUndefined, srcLoc(2, 2), "Bad Query 2") + GraphQLError validationError2 = ValidationError.newValidationError() + .validationErrorType(ValidationErrorType.FieldUndefined) + .sourceLocation(srcLoc(2, 2)) + .description("Bad Query 2") + .build() def originalEntry = new PreparsedDocumentEntry(originalDoc, [syntaxError1, validationError2]) def originalAst = AstPrinter.printAst(originalEntry.getDocument()) PreparsedDocumentEntry newEntry = serialisedDownAndBack(originalEntry) From 39c40d2ef626689271a42a848272344589001b11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 16:45:28 +0000 Subject: [PATCH 3/8] Bump org.testng:testng from 7.8.0 to 7.9.0 Bumps [org.testng:testng](https://github.com/testng-team/testng) from 7.8.0 to 7.9.0. - [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.8.0...7.9.0) --- updated-dependencies: - dependency-name: org.testng:testng 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 c44c4b751d..0c5d98c68e 100644 --- a/build.gradle +++ b/build.gradle @@ -120,7 +120,7 @@ dependencies { testImplementation 'org.reactivestreams:reactive-streams-tck:' + reactiveStreamsVersion testImplementation "io.reactivex.rxjava2:rxjava:2.2.21" - testImplementation 'org.testng:testng:7.8.0' // use for reactive streams test inheritance + testImplementation 'org.testng:testng:7.9.0' // use for reactive streams test inheritance testImplementation 'org.openjdk.jmh:jmh-core:1.37' testAnnotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.37' From ea786d6d95aa435c52c9368deb4673f17649566b Mon Sep 17 00:00:00 2001 From: Felipe Reis Date: Mon, 8 Jan 2024 20:11:58 +1100 Subject: [PATCH 4/8] Refactor ENF Factory - Create an inner class to maintain state, so we don't need to pass variables around that much --- .../ExecutableNormalizedOperationFactory.java | 761 +++++++++--------- .../FieldCollectorNormalizedQueryParams.java | 136 ---- .../ExecutableNormalizedFieldTest.groovy | 3 +- ...tableNormalizedOperationFactoryTest.groovy | 192 ++--- ...ormalizedOperationToAstCompilerTest.groovy | 3 +- 5 files changed, 476 insertions(+), 619 deletions(-) delete mode 100644 src/main/java/graphql/normalized/FieldCollectorNormalizedQueryParams.java diff --git a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java index 1124c9b7ce..a635d58571 100644 --- a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java +++ b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java @@ -34,7 +34,6 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import graphql.schema.GraphQLType; -import graphql.schema.GraphQLTypeUtil; import graphql.schema.GraphQLUnionType; import graphql.schema.GraphQLUnmodifiedType; import graphql.schema.impl.SchemaUtil; @@ -65,6 +64,7 @@ */ @PublicApi public class ExecutableNormalizedOperationFactory { + public static class Options { private final GraphQLContext graphQLContext; private final Locale locale; @@ -91,6 +91,7 @@ public static Options defaultOptions() { * e.g. can be passed to {@link graphql.schema.Coercing} for parsing. * * @param locale the locale to use + * * @return new options object to use */ public Options locale(Locale locale) { @@ -103,6 +104,7 @@ public Options locale(Locale locale) { * Can be used to intercept input values e.g. using {@link graphql.execution.values.InputInterceptor}. * * @param graphQLContext the context to use + * * @return new options object to use */ public Options graphQLContext(GraphQLContext graphQLContext) { @@ -114,6 +116,7 @@ public Options graphQLContext(GraphQLContext graphQLContext) { * against malicious operations. * * @param maxChildrenDepth the max depth + * * @return new options object to use */ public Options maxChildrenDepth(int maxChildrenDepth) { @@ -122,6 +125,7 @@ public Options maxChildrenDepth(int maxChildrenDepth) { /** * @return context to use during operation parsing + * * @see #graphQLContext(GraphQLContext) */ public GraphQLContext getGraphQLContext() { @@ -130,6 +134,7 @@ public GraphQLContext getGraphQLContext() { /** * @return locale to use during operation parsing + * * @see #locale(Locale) */ public Locale getLocale() { @@ -138,6 +143,7 @@ public Locale getLocale() { /** * @return maximum children depth before aborting parsing + * * @see #maxChildrenDepth(int) */ public int getMaxChildrenDepth() { @@ -145,7 +151,8 @@ public int getMaxChildrenDepth() { } } - private final ConditionalNodes conditionalNodes = new ConditionalNodes(); + + private static final ConditionalNodes conditionalNodes = new ConditionalNodes(); /** * This will create a runtime representation of the graphql operation that would be executed @@ -165,12 +172,13 @@ public static ExecutableNormalizedOperation createExecutableNormalizedOperation( CoercedVariables coercedVariableValues ) { NodeUtil.GetOperationResult getOperationResult = NodeUtil.getOperation(document, operationName); - return new ExecutableNormalizedOperationFactory().createNormalizedQueryImpl(graphQLSchema, + + return createExecutableNormalizedOperation( + graphQLSchema, getOperationResult.operationDefinition, getOperationResult.fragmentsByName, - coercedVariableValues, - null, - Options.defaultOptions()); + coercedVariableValues + ); } /** @@ -188,12 +196,14 @@ public static ExecutableNormalizedOperation createExecutableNormalizedOperation( OperationDefinition operationDefinition, Map fragments, CoercedVariables coercedVariableValues) { - return new ExecutableNormalizedOperationFactory().createNormalizedQueryImpl(graphQLSchema, + return new ExecutableNormalizedOperationFactoryInner( + graphQLSchema, operationDefinition, fragments, coercedVariableValues, null, - Options.defaultOptions()); + Options.defaultOptions() + ).createNormalizedQueryImpl(); } /** @@ -267,20 +277,8 @@ public static ExecutableNormalizedOperation createExecutableNormalizedOperationW RawVariables rawVariables, Options options) { NodeUtil.GetOperationResult getOperationResult = NodeUtil.getOperation(document, operationName); + OperationDefinition operationDefinition = getOperationResult.operationDefinition; - return new ExecutableNormalizedOperationFactory().createExecutableNormalizedOperationImplWithRawVariables(graphQLSchema, - getOperationResult.operationDefinition, - getOperationResult.fragmentsByName, - rawVariables, - options - ); - } - - private ExecutableNormalizedOperation createExecutableNormalizedOperationImplWithRawVariables(GraphQLSchema graphQLSchema, - OperationDefinition operationDefinition, - Map fragments, - RawVariables rawVariables, - Options options) { List variableDefinitions = operationDefinition.getVariableDefinitions(); CoercedVariables coercedVariableValues = ValuesResolver.coerceVariableValues(graphQLSchema, variableDefinitions, @@ -292,437 +290,434 @@ private ExecutableNormalizedOperation createExecutableNormalizedOperationImplWit rawVariables, options.getGraphQLContext(), options.getLocale()); - return createNormalizedQueryImpl(graphQLSchema, + + return new ExecutableNormalizedOperationFactoryInner( + graphQLSchema, operationDefinition, - fragments, + getOperationResult.fragmentsByName, coercedVariableValues, normalizedVariableValues, - options); - } - - /** - * Creates a new ExecutableNormalizedOperation for the provided query - */ - private ExecutableNormalizedOperation createNormalizedQueryImpl(GraphQLSchema graphQLSchema, - OperationDefinition operationDefinition, - Map fragments, - CoercedVariables coercedVariableValues, - @Nullable Map normalizedVariableValues, - Options options) { - FieldCollectorNormalizedQueryParams parameters = FieldCollectorNormalizedQueryParams - .newParameters() - .fragments(fragments) - .schema(graphQLSchema) - .coercedVariables(coercedVariableValues.toMap()) - .normalizedVariables(normalizedVariableValues) - .build(); - - GraphQLObjectType rootType = SchemaUtil.getOperationRootType(graphQLSchema, operationDefinition); - - CollectNFResult collectFromOperationResult = collectFromOperation(parameters, operationDefinition, rootType); - - ImmutableListMultimap.Builder fieldToNormalizedField = ImmutableListMultimap.builder(); - ImmutableMap.Builder normalizedFieldToMergedField = ImmutableMap.builder(); - ImmutableMap.Builder normalizedFieldToQueryDirectives = ImmutableMap.builder(); - ImmutableListMultimap.Builder coordinatesToNormalizedFields = ImmutableListMultimap.builder(); - - BiConsumer captureMergedField = (enf, mergedFld) -> { - // QueryDirectivesImpl is a lazy object and only computes itself when asked for - QueryDirectives queryDirectives = new QueryDirectivesImpl(mergedFld, graphQLSchema, coercedVariableValues.toMap(), options.getGraphQLContext(), options.getLocale()); - normalizedFieldToQueryDirectives.put(enf, queryDirectives); - normalizedFieldToMergedField.put(enf, mergedFld); - }; - - for (ExecutableNormalizedField topLevel : collectFromOperationResult.children) { - ImmutableList fieldAndAstParents = collectFromOperationResult.normalizedFieldToAstFields.get(topLevel); - MergedField mergedField = newMergedField(fieldAndAstParents); - - captureMergedField.accept(topLevel, mergedField); - - updateFieldToNFMap(topLevel, fieldAndAstParents, fieldToNormalizedField); - updateCoordinatedToNFMap(coordinatesToNormalizedFields, topLevel); - - buildFieldWithChildren( - topLevel, - fieldAndAstParents, - parameters, - fieldToNormalizedField, - captureMergedField, - coordinatesToNormalizedFields, - 1, - options.getMaxChildrenDepth()); - } - for (FieldCollectorNormalizedQueryParams.PossibleMerger possibleMerger : parameters.getPossibleMergerList()) { - List childrenWithSameResultKey = possibleMerger.parent.getChildrenWithSameResultKey(possibleMerger.resultKey); - ENFMerger.merge(possibleMerger.parent, childrenWithSameResultKey, graphQLSchema); - } - return new ExecutableNormalizedOperation( - operationDefinition.getOperation(), - operationDefinition.getName(), - new ArrayList<>(collectFromOperationResult.children), - fieldToNormalizedField.build(), - normalizedFieldToMergedField.build(), - normalizedFieldToQueryDirectives.build(), - coordinatesToNormalizedFields.build() - ); + options + ).createNormalizedQueryImpl(); } - private void buildFieldWithChildren(ExecutableNormalizedField executableNormalizedField, - ImmutableList fieldAndAstParents, - FieldCollectorNormalizedQueryParams fieldCollectorNormalizedQueryParams, - ImmutableListMultimap.Builder fieldNormalizedField, - BiConsumer captureMergedField, - ImmutableListMultimap.Builder coordinatesToNormalizedFields, - int curLevel, - int maxLevel) { - if (curLevel > maxLevel) { - throw new AbortExecutionException("Maximum query depth exceeded " + curLevel + " > " + maxLevel); + private static class ExecutableNormalizedOperationFactoryInner { + private final GraphQLSchema graphQLSchema; + private final OperationDefinition operationDefinition; + private final Map fragments; + private final CoercedVariables coercedVariableValues; + private final @Nullable Map normalizedVariableValues; + private final Options options; + + private final List possibleMergerList = new ArrayList<>(); + + private final ImmutableListMultimap.Builder fieldToNormalizedField = ImmutableListMultimap.builder(); + private final ImmutableMap.Builder normalizedFieldToMergedField = ImmutableMap.builder(); + private final ImmutableMap.Builder normalizedFieldToQueryDirectives = ImmutableMap.builder(); + private final ImmutableListMultimap.Builder coordinatesToNormalizedFields = ImmutableListMultimap.builder(); + + private ExecutableNormalizedOperationFactoryInner( + GraphQLSchema graphQLSchema, + OperationDefinition operationDefinition, + Map fragments, + CoercedVariables coercedVariableValues, + @Nullable Map normalizedVariableValues, + Options options + ) { + this.graphQLSchema = graphQLSchema; + this.operationDefinition = operationDefinition; + this.fragments = fragments; + this.coercedVariableValues = coercedVariableValues; + this.normalizedVariableValues = normalizedVariableValues; + this.options = options; } - CollectNFResult nextLevel = collectFromMergedField(fieldCollectorNormalizedQueryParams, executableNormalizedField, fieldAndAstParents, curLevel + 1); + /** + * Creates a new ExecutableNormalizedOperation for the provided query + */ + private ExecutableNormalizedOperation createNormalizedQueryImpl() { + GraphQLObjectType rootType = SchemaUtil.getOperationRootType(graphQLSchema, operationDefinition); - for (ExecutableNormalizedField childENF : nextLevel.children) { - executableNormalizedField.addChild(childENF); - ImmutableList childFieldAndAstParents = nextLevel.normalizedFieldToAstFields.get(childENF); + CollectNFResult collectFromOperationResult = collectFromOperation(rootType); - MergedField mergedField = newMergedField(childFieldAndAstParents); - captureMergedField.accept(childENF, mergedField); + BiConsumer captureMergedField = (enf, mergedFld) -> { + // QueryDirectivesImpl is a lazy object and only computes itself when asked for + QueryDirectives queryDirectives = new QueryDirectivesImpl(mergedFld, graphQLSchema, coercedVariableValues.toMap(), options.getGraphQLContext(), options.getLocale()); + normalizedFieldToQueryDirectives.put(enf, queryDirectives); + normalizedFieldToMergedField.put(enf, mergedFld); + }; - updateFieldToNFMap(childENF, childFieldAndAstParents, fieldNormalizedField); - updateCoordinatedToNFMap(coordinatesToNormalizedFields, childENF); + for (ExecutableNormalizedField topLevel : collectFromOperationResult.children) { + ImmutableList fieldAndAstParents = collectFromOperationResult.normalizedFieldToAstFields.get(topLevel); + MergedField mergedField = newMergedField(fieldAndAstParents); - buildFieldWithChildren(childENF, - childFieldAndAstParents, - fieldCollectorNormalizedQueryParams, - fieldNormalizedField, - captureMergedField, - coordinatesToNormalizedFields, - curLevel + 1, - maxLevel); - } - } + captureMergedField.accept(topLevel, mergedField); - private static MergedField newMergedField(ImmutableList fieldAndAstParents) { - return MergedField.newMergedField(map(fieldAndAstParents, fieldAndAstParent -> fieldAndAstParent.field)).build(); - } + updateFieldToNFMap(topLevel, fieldAndAstParents); + updateCoordinatedToNFMap(topLevel); - private void updateFieldToNFMap(ExecutableNormalizedField executableNormalizedField, - ImmutableList mergedField, - ImmutableListMultimap.Builder fieldToNormalizedField) { - for (FieldAndAstParent astField : mergedField) { - fieldToNormalizedField.put(astField.field, executableNormalizedField); + buildFieldWithChildren( + topLevel, + fieldAndAstParents, + captureMergedField, + 1, + options.getMaxChildrenDepth()); + } + // getPossibleMergerList + for (PossibleMerger possibleMerger : possibleMergerList) { + List childrenWithSameResultKey = possibleMerger.parent.getChildrenWithSameResultKey(possibleMerger.resultKey); + ENFMerger.merge(possibleMerger.parent, childrenWithSameResultKey, graphQLSchema); + } + return new ExecutableNormalizedOperation( + operationDefinition.getOperation(), + operationDefinition.getName(), + new ArrayList<>(collectFromOperationResult.children), + fieldToNormalizedField.build(), + normalizedFieldToMergedField.build(), + normalizedFieldToQueryDirectives.build(), + coordinatesToNormalizedFields.build() + ); } - } - private void updateCoordinatedToNFMap(ImmutableListMultimap.Builder coordinatesToNormalizedFields, ExecutableNormalizedField topLevel) { - for (String objectType : topLevel.getObjectTypeNames()) { - FieldCoordinates coordinates = FieldCoordinates.coordinates(objectType, topLevel.getFieldName()); - coordinatesToNormalizedFields.put(coordinates, topLevel); - } - } - private static class FieldAndAstParent { - final Field field; - final GraphQLCompositeType astParentType; + private void buildFieldWithChildren(ExecutableNormalizedField executableNormalizedField, + ImmutableList fieldAndAstParents, + BiConsumer captureMergedField, + int curLevel, + int maxLevel) { + if (curLevel > maxLevel) { + throw new AbortExecutionException("Maximum query depth exceeded " + curLevel + " > " + maxLevel); + } - private FieldAndAstParent(Field field, GraphQLCompositeType astParentType) { - this.field = field; - this.astParentType = astParentType; - } - } + CollectNFResult nextLevel = collectFromMergedField(executableNormalizedField, fieldAndAstParents, curLevel + 1); + for (ExecutableNormalizedField childENF : nextLevel.children) { + executableNormalizedField.addChild(childENF); + ImmutableList childFieldAndAstParents = nextLevel.normalizedFieldToAstFields.get(childENF); - public static class CollectNFResult { - private final Collection children; - private final ImmutableListMultimap normalizedFieldToAstFields; + MergedField mergedField = newMergedField(childFieldAndAstParents); + captureMergedField.accept(childENF, mergedField); - public CollectNFResult(Collection children, ImmutableListMultimap normalizedFieldToAstFields) { - this.children = children; - this.normalizedFieldToAstFields = normalizedFieldToAstFields; - } - } + updateFieldToNFMap(childENF, childFieldAndAstParents); + updateCoordinatedToNFMap(childENF); + buildFieldWithChildren(childENF, + childFieldAndAstParents, + captureMergedField, + curLevel + 1, + maxLevel); + } + } - public CollectNFResult collectFromMergedField(FieldCollectorNormalizedQueryParams parameters, - ExecutableNormalizedField executableNormalizedField, - ImmutableList mergedField, - int level) { - List fieldDefs = executableNormalizedField.getFieldDefinitions(parameters.getGraphQLSchema()); - Set possibleObjects = resolvePossibleObjects(fieldDefs, parameters.getGraphQLSchema()); - if (possibleObjects.isEmpty()) { - return new CollectNFResult(ImmutableKit.emptyList(), ImmutableListMultimap.of()); + private static MergedField newMergedField(ImmutableList fieldAndAstParents) { + return MergedField.newMergedField(map(fieldAndAstParents, fieldAndAstParent -> fieldAndAstParent.field)).build(); } - List collectedFields = new ArrayList<>(); - for (FieldAndAstParent fieldAndAstParent : mergedField) { - if (fieldAndAstParent.field.getSelectionSet() == null) { - continue; + private void updateFieldToNFMap(ExecutableNormalizedField executableNormalizedField, + ImmutableList mergedField) { + for (FieldAndAstParent astField : mergedField) { + fieldToNormalizedField.put(astField.field, executableNormalizedField); } - GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(parameters.getGraphQLSchema(), fieldAndAstParent.astParentType, fieldAndAstParent.field.getName()); - GraphQLUnmodifiedType astParentType = unwrapAll(fieldDefinition.getType()); - this.collectFromSelectionSet(parameters, - fieldAndAstParent.field.getSelectionSet(), - collectedFields, - (GraphQLCompositeType) astParentType, - possibleObjects - ); } - Map> fieldsByName = fieldsByResultKey(collectedFields); - ImmutableList.Builder resultNFs = ImmutableList.builder(); - ImmutableListMultimap.Builder normalizedFieldToAstFields = ImmutableListMultimap.builder(); - - createNFs(resultNFs, parameters, fieldsByName, normalizedFieldToAstFields, level, executableNormalizedField); - return new CollectNFResult(resultNFs.build(), normalizedFieldToAstFields.build()); - } - - private Map> fieldsByResultKey(List collectedFields) { - Map> fieldsByName = new LinkedHashMap<>(); - for (CollectedField collectedField : collectedFields) { - fieldsByName.computeIfAbsent(collectedField.field.getResultKey(), ignored -> new ArrayList<>()).add(collectedField); + private void updateCoordinatedToNFMap(ExecutableNormalizedField topLevel) { + for (String objectType : topLevel.getObjectTypeNames()) { + FieldCoordinates coordinates = FieldCoordinates.coordinates(objectType, topLevel.getFieldName()); + coordinatesToNormalizedFields.put(coordinates, topLevel); + } } - return fieldsByName; - } - - public CollectNFResult collectFromOperation(FieldCollectorNormalizedQueryParams parameters, - OperationDefinition operationDefinition, - GraphQLObjectType rootType) { + public CollectNFResult collectFromMergedField(ExecutableNormalizedField executableNormalizedField, + ImmutableList mergedField, + int level) { + List fieldDefs = executableNormalizedField.getFieldDefinitions(graphQLSchema); + Set possibleObjects = resolvePossibleObjects(fieldDefs); + if (possibleObjects.isEmpty()) { + return new CollectNFResult(ImmutableKit.emptyList(), ImmutableListMultimap.of()); + } - Set possibleObjects = ImmutableSet.of(rootType); - List collectedFields = new ArrayList<>(); - collectFromSelectionSet(parameters, operationDefinition.getSelectionSet(), collectedFields, rootType, possibleObjects); - // group by result key - Map> fieldsByName = fieldsByResultKey(collectedFields); - ImmutableList.Builder resultNFs = ImmutableList.builder(); - ImmutableListMultimap.Builder normalizedFieldToAstFields = ImmutableListMultimap.builder(); + List collectedFields = new ArrayList<>(); + for (FieldAndAstParent fieldAndAstParent : mergedField) { + if (fieldAndAstParent.field.getSelectionSet() == null) { + continue; + } + GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(graphQLSchema, fieldAndAstParent.astParentType, fieldAndAstParent.field.getName()); + GraphQLUnmodifiedType astParentType = unwrapAll(fieldDefinition.getType()); + this.collectFromSelectionSet(fieldAndAstParent.field.getSelectionSet(), + collectedFields, + (GraphQLCompositeType) astParentType, + possibleObjects + ); + } + Map> fieldsByName = fieldsByResultKey(collectedFields); + ImmutableList.Builder resultNFs = ImmutableList.builder(); + ImmutableListMultimap.Builder normalizedFieldToAstFields = ImmutableListMultimap.builder(); - createNFs(resultNFs, parameters, fieldsByName, normalizedFieldToAstFields, 1, null); + createNFs(resultNFs, fieldsByName, normalizedFieldToAstFields, level, executableNormalizedField); - return new CollectNFResult(resultNFs.build(), normalizedFieldToAstFields.build()); - } + return new CollectNFResult(resultNFs.build(), normalizedFieldToAstFields.build()); + } - private void createNFs(ImmutableList.Builder nfListBuilder, - FieldCollectorNormalizedQueryParams parameters, - Map> fieldsByName, - ImmutableListMultimap.Builder normalizedFieldToAstFields, - int level, - ExecutableNormalizedField parent) { - for (String resultKey : fieldsByName.keySet()) { - List fieldsWithSameResultKey = fieldsByName.get(resultKey); - List commonParentsGroups = groupByCommonParents(fieldsWithSameResultKey); - for (CollectedFieldGroup fieldGroup : commonParentsGroups) { - ExecutableNormalizedField nf = createNF(parameters, fieldGroup, level, parent); - if (nf == null) { - continue; + private Map> fieldsByResultKey(List collectedFields) { + Map> fieldsByName = new LinkedHashMap<>(); + for (CollectedField collectedField : collectedFields) { + fieldsByName.computeIfAbsent(collectedField.field.getResultKey(), ignored -> new ArrayList<>()).add(collectedField); + } + return fieldsByName; + } + + public CollectNFResult collectFromOperation(GraphQLObjectType rootType) { + + + Set possibleObjects = ImmutableSet.of(rootType); + List collectedFields = new ArrayList<>(); + collectFromSelectionSet(operationDefinition.getSelectionSet(), collectedFields, rootType, possibleObjects); + // group by result key + Map> fieldsByName = fieldsByResultKey(collectedFields); + ImmutableList.Builder resultNFs = ImmutableList.builder(); + ImmutableListMultimap.Builder normalizedFieldToAstFields = ImmutableListMultimap.builder(); + + createNFs(resultNFs, fieldsByName, normalizedFieldToAstFields, 1, null); + + return new CollectNFResult(resultNFs.build(), normalizedFieldToAstFields.build()); + } + + private void createNFs(ImmutableList.Builder nfListBuilder, + Map> fieldsByName, + ImmutableListMultimap.Builder normalizedFieldToAstFields, + int level, + ExecutableNormalizedField parent) { + for (String resultKey : fieldsByName.keySet()) { + List fieldsWithSameResultKey = fieldsByName.get(resultKey); + List commonParentsGroups = groupByCommonParents(fieldsWithSameResultKey); + for (CollectedFieldGroup fieldGroup : commonParentsGroups) { + ExecutableNormalizedField nf = createNF(fieldGroup, level, parent); + if (nf == null) { + continue; + } + for (CollectedField collectedField : fieldGroup.fields) { + normalizedFieldToAstFields.put(nf, new FieldAndAstParent(collectedField.field, collectedField.astTypeCondition)); + } + nfListBuilder.add(nf); } - for (CollectedField collectedField : fieldGroup.fields) { - normalizedFieldToAstFields.put(nf, new FieldAndAstParent(collectedField.field, collectedField.astTypeCondition)); + if (commonParentsGroups.size() > 1) { + possibleMergerList.add(new PossibleMerger(parent, resultKey)); } - nfListBuilder.add(nf); - } - if (commonParentsGroups.size() > 1) { - parameters.addPossibleMergers(parent, resultKey); } } - } - - private ExecutableNormalizedField createNF(FieldCollectorNormalizedQueryParams parameters, - CollectedFieldGroup collectedFieldGroup, - int level, - ExecutableNormalizedField parent) { - Field field; - Set objectTypes = collectedFieldGroup.objectTypes; - field = collectedFieldGroup.fields.iterator().next().field; - String fieldName = field.getName(); - GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(parameters.getGraphQLSchema(), objectTypes.iterator().next(), fieldName); - - Map argumentValues = ValuesResolver.getArgumentValues(fieldDefinition.getArguments(), field.getArguments(), CoercedVariables.of(parameters.getCoercedVariableValues()), parameters.getGraphQLContext(), parameters.getLocale()); - Map normalizedArgumentValues = null; - if (parameters.getNormalizedVariableValues() != null) { - normalizedArgumentValues = ValuesResolver.getNormalizedArgumentValues(fieldDefinition.getArguments(), field.getArguments(), parameters.getNormalizedVariableValues()); - } - ImmutableList objectTypeNames = map(objectTypes, GraphQLObjectType::getName); - - return ExecutableNormalizedField.newNormalizedField() - .alias(field.getAlias()) - .resolvedArguments(argumentValues) - .normalizedArguments(normalizedArgumentValues) - .astArguments(field.getArguments()) - .objectTypeNames(objectTypeNames) - .fieldName(fieldName) - .level(level) - .parent(parent) - .build(); - } - private static class CollectedFieldGroup { - Set objectTypes; - Set fields; + private ExecutableNormalizedField createNF(CollectedFieldGroup collectedFieldGroup, + int level, + ExecutableNormalizedField parent) { + Field field; + Set objectTypes = collectedFieldGroup.objectTypes; + field = collectedFieldGroup.fields.iterator().next().field; + String fieldName = field.getName(); + GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(graphQLSchema, objectTypes.iterator().next(), fieldName); - public CollectedFieldGroup(Set fields, Set objectTypes) { - this.fields = fields; - this.objectTypes = objectTypes; + Map argumentValues = ValuesResolver.getArgumentValues(fieldDefinition.getArguments(), field.getArguments(), CoercedVariables.of(this.coercedVariableValues.toMap()), this.options.graphQLContext, this.options.locale); + Map normalizedArgumentValues = null; + if (this.normalizedVariableValues != null) { + normalizedArgumentValues = ValuesResolver.getNormalizedArgumentValues(fieldDefinition.getArguments(), field.getArguments(), this.normalizedVariableValues); + } + ImmutableList objectTypeNames = map(objectTypes, GraphQLObjectType::getName); + + return ExecutableNormalizedField.newNormalizedField() + .alias(field.getAlias()) + .resolvedArguments(argumentValues) + .normalizedArguments(normalizedArgumentValues) + .astArguments(field.getArguments()) + .objectTypeNames(objectTypeNames) + .fieldName(fieldName) + .level(level) + .parent(parent) + .build(); + } + + private static class CollectedFieldGroup { + Set objectTypes; + Set fields; + + public CollectedFieldGroup(Set fields, Set objectTypes) { + this.fields = fields; + this.objectTypes = objectTypes; + } } - } - private List groupByCommonParents(Collection fields) { - ImmutableSet.Builder objectTypes = ImmutableSet.builder(); - for (CollectedField collectedField : fields) { - objectTypes.addAll(collectedField.objectTypes); - } - Set allRelevantObjects = objectTypes.build(); - Map> groupByAstParent = groupingBy(fields, fieldAndType -> fieldAndType.astTypeCondition); - if (groupByAstParent.size() == 1) { - return singletonList(new CollectedFieldGroup(ImmutableSet.copyOf(fields), allRelevantObjects)); - } - ImmutableList.Builder result = ImmutableList.builder(); - for (GraphQLObjectType objectType : allRelevantObjects) { - Set relevantFields = filterSet(fields, field -> field.objectTypes.contains(objectType)); - result.add(new CollectedFieldGroup(relevantFields, singleton(objectType))); + private List groupByCommonParents(Collection fields) { + ImmutableSet.Builder objectTypes = ImmutableSet.builder(); + for (CollectedField collectedField : fields) { + objectTypes.addAll(collectedField.objectTypes); + } + Set allRelevantObjects = objectTypes.build(); + Map> groupByAstParent = groupingBy(fields, fieldAndType -> fieldAndType.astTypeCondition); + if (groupByAstParent.size() == 1) { + return singletonList(new CollectedFieldGroup(ImmutableSet.copyOf(fields), allRelevantObjects)); + } + ImmutableList.Builder result = ImmutableList.builder(); + for (GraphQLObjectType objectType : allRelevantObjects) { + Set relevantFields = filterSet(fields, field -> field.objectTypes.contains(objectType)); + result.add(new CollectedFieldGroup(relevantFields, singleton(objectType))); + } + return result.build(); + } + + private void collectFromSelectionSet(SelectionSet selectionSet, + List result, + GraphQLCompositeType astTypeCondition, + Set possibleObjects + ) { + for (Selection selection : selectionSet.getSelections()) { + if (selection instanceof Field) { + collectField(result, (Field) selection, possibleObjects, astTypeCondition); + } else if (selection instanceof InlineFragment) { + collectInlineFragment(result, (InlineFragment) selection, possibleObjects, astTypeCondition); + } else if (selection instanceof FragmentSpread) { + collectFragmentSpread(result, (FragmentSpread) selection, possibleObjects); + } + } } - return result.build(); - } + private void collectFragmentSpread(List result, + FragmentSpread fragmentSpread, + Set possibleObjects + ) { + if (!conditionalNodes.shouldInclude(fragmentSpread, + this.coercedVariableValues.toMap(), + this.graphQLSchema, + this.options.graphQLContext)) { + return; + } + FragmentDefinition fragmentDefinition = assertNotNull(this.fragments.get(fragmentSpread.getName())); - private void collectFromSelectionSet(FieldCollectorNormalizedQueryParams parameters, - SelectionSet selectionSet, - List result, - GraphQLCompositeType astTypeCondition, - Set possibleObjects - ) { - for (Selection selection : selectionSet.getSelections()) { - if (selection instanceof Field) { - collectField(parameters, result, (Field) selection, possibleObjects, astTypeCondition); - } else if (selection instanceof InlineFragment) { - collectInlineFragment(parameters, result, (InlineFragment) selection, possibleObjects, astTypeCondition); - } else if (selection instanceof FragmentSpread) { - collectFragmentSpread(parameters, result, (FragmentSpread) selection, possibleObjects); + if (!conditionalNodes.shouldInclude(fragmentDefinition, + this.coercedVariableValues.toMap(), + this.graphQLSchema, + this.options.graphQLContext)) { + return; } - } - } + GraphQLCompositeType newAstTypeCondition = (GraphQLCompositeType) assertNotNull(this.graphQLSchema.getType(fragmentDefinition.getTypeCondition().getName())); + Set newPossibleObjects = narrowDownPossibleObjects(possibleObjects, newAstTypeCondition); + collectFromSelectionSet(fragmentDefinition.getSelectionSet(), result, newAstTypeCondition, newPossibleObjects); + } + + private void collectInlineFragment(List result, + InlineFragment inlineFragment, + Set possibleObjects, + GraphQLCompositeType astTypeCondition + ) { + if (!conditionalNodes.shouldInclude(inlineFragment, this.coercedVariableValues.toMap(), this.graphQLSchema, this.options.graphQLContext)) { + return; + } + Set newPossibleObjects = possibleObjects; + GraphQLCompositeType newAstTypeCondition = astTypeCondition; - private static class CollectedField { - Field field; - Set objectTypes; - GraphQLCompositeType astTypeCondition; + if (inlineFragment.getTypeCondition() != null) { + newAstTypeCondition = (GraphQLCompositeType) this.graphQLSchema.getType(inlineFragment.getTypeCondition().getName()); + newPossibleObjects = narrowDownPossibleObjects(possibleObjects, newAstTypeCondition); - public CollectedField(Field field, Set objectTypes, GraphQLCompositeType astTypeCondition) { - this.field = field; - this.objectTypes = objectTypes; - this.astTypeCondition = astTypeCondition; + } + collectFromSelectionSet(inlineFragment.getSelectionSet(), result, newAstTypeCondition, newPossibleObjects); + } + + private void collectField(List result, + Field field, + Set possibleObjectTypes, + GraphQLCompositeType astTypeCondition + ) { + if (!conditionalNodes.shouldInclude(field, + this.coercedVariableValues.toMap(), + this.graphQLSchema, + this.options.graphQLContext)) { + return; + } + // this means there is actually no possible type for this field, and we are done + if (possibleObjectTypes.isEmpty()) { + return; + } + result.add(new CollectedField(field, possibleObjectTypes, astTypeCondition)); } - public boolean isAbstract() { - return GraphQLTypeUtil.isInterfaceOrUnion(astTypeCondition); - } + private Set narrowDownPossibleObjects(Set currentOnes, + GraphQLCompositeType typeCondition) { + + ImmutableSet resolvedTypeCondition = resolvePossibleObjects(typeCondition); + if (currentOnes.isEmpty()) { + return resolvedTypeCondition; + } - public boolean isConcrete() { - return GraphQLTypeUtil.isObjectType(astTypeCondition); + // Faster intersection, as either set often has a size of 1. + return intersection(currentOnes, resolvedTypeCondition); } - } - private void collectFragmentSpread(FieldCollectorNormalizedQueryParams parameters, - List result, - FragmentSpread fragmentSpread, - Set possibleObjects - ) { - if (!conditionalNodes.shouldInclude(fragmentSpread, - parameters.getCoercedVariableValues(), - parameters.getGraphQLSchema(), - parameters.getGraphQLContext())) { - return; - } - FragmentDefinition fragmentDefinition = assertNotNull(parameters.getFragmentsByName().get(fragmentSpread.getName())); - - if (!conditionalNodes.shouldInclude(fragmentDefinition, - parameters.getCoercedVariableValues(), - parameters.getGraphQLSchema(), - parameters.getGraphQLContext())) { - return; - } - GraphQLCompositeType newAstTypeCondition = (GraphQLCompositeType) assertNotNull(parameters.getGraphQLSchema().getType(fragmentDefinition.getTypeCondition().getName())); - Set newPossibleObjects = narrowDownPossibleObjects(possibleObjects, newAstTypeCondition, parameters.getGraphQLSchema()); - collectFromSelectionSet(parameters, fragmentDefinition.getSelectionSet(), result, newAstTypeCondition, newPossibleObjects); - } + private ImmutableSet resolvePossibleObjects(List defs) { + ImmutableSet.Builder builder = ImmutableSet.builder(); + for (GraphQLFieldDefinition def : defs) { + GraphQLUnmodifiedType outputType = unwrapAll(def.getType()); + if (outputType instanceof GraphQLCompositeType) { + builder.addAll(resolvePossibleObjects((GraphQLCompositeType) outputType)); + } + } - private void collectInlineFragment(FieldCollectorNormalizedQueryParams parameters, - List result, - InlineFragment inlineFragment, - Set possibleObjects, - GraphQLCompositeType astTypeCondition - ) { - if (!conditionalNodes.shouldInclude(inlineFragment, parameters.getCoercedVariableValues(), parameters.getGraphQLSchema(), parameters.getGraphQLContext())) { - return; + return builder.build(); } - Set newPossibleObjects = possibleObjects; - GraphQLCompositeType newAstTypeCondition = astTypeCondition; - - if (inlineFragment.getTypeCondition() != null) { - newAstTypeCondition = (GraphQLCompositeType) parameters.getGraphQLSchema().getType(inlineFragment.getTypeCondition().getName()); - newPossibleObjects = narrowDownPossibleObjects(possibleObjects, newAstTypeCondition, parameters.getGraphQLSchema()); + private ImmutableSet resolvePossibleObjects(GraphQLCompositeType type) { + if (type instanceof GraphQLObjectType) { + return ImmutableSet.of((GraphQLObjectType) type); + } else if (type instanceof GraphQLInterfaceType) { + return ImmutableSet.copyOf(graphQLSchema.getImplementations((GraphQLInterfaceType) type)); + } else if (type instanceof GraphQLUnionType) { + List unionTypes = ((GraphQLUnionType) type).getTypes(); + return ImmutableSet.copyOf(ImmutableKit.map(unionTypes, GraphQLObjectType.class::cast)); + } else { + return assertShouldNeverHappen(); + } } - collectFromSelectionSet(parameters, inlineFragment.getSelectionSet(), result, newAstTypeCondition, newPossibleObjects); - } - private void collectField(FieldCollectorNormalizedQueryParams parameters, - List result, - Field field, - Set possibleObjectTypes, - GraphQLCompositeType astTypeCondition - ) { - if (!conditionalNodes.shouldInclude(field, - parameters.getCoercedVariableValues(), - parameters.getGraphQLSchema(), - parameters.getGraphQLContext())) { - return; - } - // this means there is actually no possible type for this field, and we are done - if (possibleObjectTypes.isEmpty()) { - return; + private static class PossibleMerger { + ExecutableNormalizedField parent; + String resultKey; + + public PossibleMerger(ExecutableNormalizedField parent, String resultKey) { + this.parent = parent; + this.resultKey = resultKey; + } } - result.add(new CollectedField(field, possibleObjectTypes, astTypeCondition)); - } - private Set narrowDownPossibleObjects(Set currentOnes, - GraphQLCompositeType typeCondition, - GraphQLSchema graphQLSchema) { + private static class CollectedField { + Field field; + Set objectTypes; + GraphQLCompositeType astTypeCondition; - ImmutableSet resolvedTypeCondition = resolvePossibleObjects(typeCondition, graphQLSchema); - if (currentOnes.isEmpty()) { - return resolvedTypeCondition; + public CollectedField(Field field, Set objectTypes, GraphQLCompositeType astTypeCondition) { + this.field = field; + this.objectTypes = objectTypes; + this.astTypeCondition = astTypeCondition; + } } - // Faster intersection, as either set often has a size of 1. - return intersection(currentOnes, resolvedTypeCondition); - } - - private ImmutableSet resolvePossibleObjects(List defs, GraphQLSchema graphQLSchema) { - ImmutableSet.Builder builder = ImmutableSet.builder(); + public static class CollectNFResult { + private final Collection children; + private final ImmutableListMultimap normalizedFieldToAstFields; - for (GraphQLFieldDefinition def : defs) { - GraphQLUnmodifiedType outputType = unwrapAll(def.getType()); - if (outputType instanceof GraphQLCompositeType) { - builder.addAll(resolvePossibleObjects((GraphQLCompositeType) outputType, graphQLSchema)); + public CollectNFResult(Collection children, ImmutableListMultimap normalizedFieldToAstFields) { + this.children = children; + this.normalizedFieldToAstFields = normalizedFieldToAstFields; } } - return builder.build(); - } + private static class FieldAndAstParent { + final Field field; + final GraphQLCompositeType astParentType; - private ImmutableSet resolvePossibleObjects(GraphQLCompositeType type, GraphQLSchema graphQLSchema) { - if (type instanceof GraphQLObjectType) { - return ImmutableSet.of((GraphQLObjectType) type); - } else if (type instanceof GraphQLInterfaceType) { - return ImmutableSet.copyOf(graphQLSchema.getImplementations((GraphQLInterfaceType) type)); - } else if (type instanceof GraphQLUnionType) { - List unionTypes = ((GraphQLUnionType) type).getTypes(); - return ImmutableSet.copyOf(ImmutableKit.map(unionTypes, GraphQLObjectType.class::cast)); - } else { - return assertShouldNeverHappen(); + private FieldAndAstParent(Field field, GraphQLCompositeType astParentType) { + this.field = field; + this.astParentType = astParentType; + } } } + } diff --git a/src/main/java/graphql/normalized/FieldCollectorNormalizedQueryParams.java b/src/main/java/graphql/normalized/FieldCollectorNormalizedQueryParams.java deleted file mode 100644 index 8b4d03bf3e..0000000000 --- a/src/main/java/graphql/normalized/FieldCollectorNormalizedQueryParams.java +++ /dev/null @@ -1,136 +0,0 @@ -package graphql.normalized; - -import graphql.Assert; -import graphql.GraphQLContext; -import graphql.Internal; -import graphql.language.FragmentDefinition; -import graphql.schema.GraphQLSchema; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -@Internal -public class FieldCollectorNormalizedQueryParams { - private final GraphQLSchema graphQLSchema; - private final Map fragmentsByName; - private final Map coercedVariableValues; - private final Map normalizedVariableValues; - private final GraphQLContext graphQLContext; - private final Locale locale; - - private final List possibleMergerList = new ArrayList<>(); - - public static class PossibleMerger { - ExecutableNormalizedField parent; - String resultKey; - - public PossibleMerger(ExecutableNormalizedField parent, String resultKey) { - this.parent = parent; - this.resultKey = resultKey; - } - } - - public void addPossibleMergers(ExecutableNormalizedField parent, String resultKey) { - possibleMergerList.add(new PossibleMerger(parent, resultKey)); - } - - public List getPossibleMergerList() { - return possibleMergerList; - } - - public GraphQLSchema getGraphQLSchema() { - return graphQLSchema; - } - - public Map getFragmentsByName() { - return fragmentsByName; - } - - @NotNull - public Map getCoercedVariableValues() { - return coercedVariableValues; - } - - @Nullable - public Map getNormalizedVariableValues() { - return normalizedVariableValues; - } - - public GraphQLContext getGraphQLContext() { - return graphQLContext; - } - - public Locale getLocale() { - return locale; - } - - private FieldCollectorNormalizedQueryParams(Builder builder) { - this.fragmentsByName = builder.fragmentsByName; - this.graphQLSchema = builder.graphQLSchema; - this.coercedVariableValues = builder.coercedVariableValues; - this.normalizedVariableValues = builder.normalizedVariableValues; - this.graphQLContext = builder.graphQLContext; - this.locale = builder.locale; - } - - public static Builder newParameters() { - return new Builder(); - } - - public static class Builder { - private GraphQLSchema graphQLSchema; - private final Map fragmentsByName = new LinkedHashMap<>(); - private final Map coercedVariableValues = new LinkedHashMap<>(); - private Map normalizedVariableValues; - private GraphQLContext graphQLContext = GraphQLContext.getDefault(); - private Locale locale = Locale.getDefault(); - - /** - * @see FieldCollectorNormalizedQueryParams#newParameters() - */ - private Builder() { - - } - - public Builder schema(GraphQLSchema graphQLSchema) { - this.graphQLSchema = graphQLSchema; - return this; - } - - public Builder fragments(Map fragmentsByName) { - this.fragmentsByName.putAll(fragmentsByName); - return this; - } - - public Builder coercedVariables(Map coercedVariableValues) { - this.coercedVariableValues.putAll(coercedVariableValues); - return this; - } - - public Builder normalizedVariables(Map normalizedVariableValues) { - this.normalizedVariableValues = normalizedVariableValues; - return this; - } - - public Builder graphQLContext(GraphQLContext graphQLContext) { - this.graphQLContext = graphQLContext; - return this; - } - - public Builder locale(Locale locale) { - this.locale = locale; - return this; - } - - public FieldCollectorNormalizedQueryParams build() { - Assert.assertNotNull(graphQLSchema, () -> "You must provide a schema"); - return new FieldCollectorNormalizedQueryParams(this); - } - - } -} diff --git a/src/test/groovy/graphql/normalized/ExecutableNormalizedFieldTest.groovy b/src/test/groovy/graphql/normalized/ExecutableNormalizedFieldTest.groovy index dc3db5daa4..6debbb2ff2 100644 --- a/src/test/groovy/graphql/normalized/ExecutableNormalizedFieldTest.groovy +++ b/src/test/groovy/graphql/normalized/ExecutableNormalizedFieldTest.groovy @@ -48,8 +48,7 @@ class ExecutableNormalizedFieldTest extends Specification { """ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory normalizedOperationFactory = new ExecutableNormalizedOperationFactory() - def normalizedOperation = normalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + def normalizedOperation = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def pets = normalizedOperation.getTopLevelFields()[0] def allChildren = pets.getChildren() diff --git a/src/test/groovy/graphql/normalized/ExecutableNormalizedOperationFactoryTest.groovy b/src/test/groovy/graphql/normalized/ExecutableNormalizedOperationFactoryTest.groovy index 89ea656b81..7a67201bd9 100644 --- a/src/test/groovy/graphql/normalized/ExecutableNormalizedOperationFactoryTest.groovy +++ b/src/test/groovy/graphql/normalized/ExecutableNormalizedOperationFactoryTest.groovy @@ -112,8 +112,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -198,8 +198,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -278,8 +278,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -329,8 +329,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -372,8 +372,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -422,8 +422,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -485,8 +485,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -531,8 +531,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -575,8 +575,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -619,8 +619,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -651,8 +651,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -702,8 +702,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -752,8 +752,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -791,8 +791,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -835,8 +835,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - def dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -875,8 +875,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - def dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -923,8 +923,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - def dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -1026,8 +1026,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) def subFooField = (document.getDefinitions()[1] as FragmentDefinition).getSelectionSet().getSelections()[0] as Field - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def fieldToNormalizedField = tree.getFieldToNormalizedField() expect: @@ -1069,8 +1069,8 @@ type Dog implements Animal{ def petsField = (document.getDefinitions()[0] as OperationDefinition).getSelectionSet().getSelections()[0] as Field def idField = petsField.getSelectionSet().getSelections()[0] as Field - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def fieldToNormalizedField = tree.getFieldToNormalizedField() @@ -1118,8 +1118,8 @@ type Dog implements Animal{ def schemaField = selections[2] as Field def typeField = selections[3] as Field - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def fieldToNormalizedField = tree.getFieldToNormalizedField() expect: @@ -1175,8 +1175,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -1218,8 +1218,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTree(tree) expect: @@ -1246,8 +1246,8 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def normalizedFieldToMergedField = tree.getNormalizedFieldToMergedField() Traverser traverser = Traverser.depthFirst({ it.getChildren() }) List result = new ArrayList<>() @@ -1284,10 +1284,10 @@ type Dog implements Animal{ Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def coordinatesToNormalizedFields = tree.coordinatesToNormalizedFields then: @@ -1385,8 +1385,8 @@ schema { Document document = TestUtil.parseQuery(mutation) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -1435,7 +1435,7 @@ schema { assertValidQuery(graphQLSchema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + def variables = [ var1: [bar: 123], var2: [foo: "foo", input2: [bar: 123]] @@ -1443,7 +1443,7 @@ schema { // the normalized arg value should be the same regardless of how the value was provided def expectedNormalizedArgValue = [foo: new NormalizedInputValue("String", parseValue('"foo"')), input2: new NormalizedInputValue("Input2", [bar: new NormalizedInputValue("Int", parseValue("123"))])] when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) def topLevelField = tree.getTopLevelFields().get(0) def secondField = topLevelField.getChildren().get(0) def arg1 = secondField.getNormalizedArgument("arg1") @@ -1482,9 +1482,9 @@ schema { assertValidQuery(graphQLSchema, query) def document = TestUtil.parseQuery(query) - def dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.emptyVariables()) then: def topLevelField = tree.getTopLevelFields().get(0) @@ -1517,13 +1517,13 @@ schema { assertValidQuery(graphQLSchema, query) def document = TestUtil.parseQuery(query) - def dependencyGraph = new ExecutableNormalizedOperationFactory() + def variables = [ varIds : null, otherVar: null, ] when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) then: def topLevelField = tree.getTopLevelFields().get(0) @@ -1573,9 +1573,9 @@ schema { ] assertValidQuery(graphQLSchema, query, variables) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) def topLevelField = tree.getTopLevelFields().get(0) def arg1 = topLevelField.getNormalizedArgument("arg1") def arg2 = topLevelField.getNormalizedArgument("arg2") @@ -1626,9 +1626,9 @@ schema { ] assertValidQuery(graphQLSchema, query, variables) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) def topLevelField = tree.getTopLevelFields().get(0) def arg1 = topLevelField.getNormalizedArgument("arg1") def arg2 = topLevelField.getNormalizedArgument("arg2") @@ -1681,9 +1681,9 @@ schema { ''' assertValidQuery(graphQLSchema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.emptyVariables()) then: tree.normalizedFieldToMergedField.size() == 3 @@ -1739,9 +1739,9 @@ schema { ''' assertValidQuery(graphQLSchema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) then: @@ -1787,9 +1787,9 @@ schema { ''' assertValidQuery(graphQLSchema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) then: @@ -1863,9 +1863,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -1927,9 +1927,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -1984,9 +1984,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -2059,9 +2059,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -2121,9 +2121,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -2163,9 +2163,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -2206,9 +2206,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -2249,9 +2249,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -2324,9 +2324,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -2400,9 +2400,9 @@ schema { ''' assertValidQuery(schema, query) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, document, null, RawVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, schema) then: @@ -2462,9 +2462,9 @@ schema { def variables = ["true": Boolean.TRUE, "false": Boolean.FALSE] assertValidQuery(graphQLSchema, query, variables) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) println String.join("\n", printTree(tree)) def printedTree = printTree(tree) @@ -2519,9 +2519,9 @@ schema { def variables = [:] assertValidQuery(graphQLSchema, query, variables) Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() + when: - def tree = dependencyGraph.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(graphQLSchema, document, null, RawVariables.of(variables)) def printedTree = printTreeAndDirectives(tree) then: @@ -2584,8 +2584,8 @@ fragment personName on Person { Document document = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -2637,8 +2637,8 @@ fragment personName on Person { Document document = TestUtil.parseQuery(query) - def dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: @@ -2684,8 +2684,8 @@ fragment personName on Person { Document document = TestUtil.parseQuery(query) - def dependencyGraph = new ExecutableNormalizedOperationFactory() - def tree = dependencyGraph.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) + + def tree = ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, document, null, CoercedVariables.emptyVariables()) def printedTree = printTreeWithLevelInfo(tree, graphQLSchema) expect: diff --git a/src/test/groovy/graphql/normalized/ExecutableNormalizedOperationToAstCompilerTest.groovy b/src/test/groovy/graphql/normalized/ExecutableNormalizedOperationToAstCompilerTest.groovy index dd074ce7a8..e990d981a5 100644 --- a/src/test/groovy/graphql/normalized/ExecutableNormalizedOperationToAstCompilerTest.groovy +++ b/src/test/groovy/graphql/normalized/ExecutableNormalizedOperationToAstCompilerTest.groovy @@ -2140,8 +2140,7 @@ class ExecutableNormalizedOperationToAstCompilerTest extends Specification { assertValidQuery(schema, query, variables) Document originalDocument = TestUtil.parseQuery(query) - ExecutableNormalizedOperationFactory dependencyGraph = new ExecutableNormalizedOperationFactory() - return dependencyGraph.createExecutableNormalizedOperationWithRawVariables(schema, originalDocument, null, RawVariables.of(variables)) + return ExecutableNormalizedOperationFactory.createExecutableNormalizedOperationWithRawVariables(schema, originalDocument, null, RawVariables.of(variables)) } private List createNormalizedFields(GraphQLSchema schema, String query, Map variables = [:]) { From 1c96e10b9ca478b1ec336dd052a485536a9533f7 Mon Sep 17 00:00:00 2001 From: Felipe Reis Date: Mon, 8 Jan 2024 20:27:22 +1100 Subject: [PATCH 5/8] Convert captureMergedField into a class method --- .../ExecutableNormalizedOperationFactory.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java index a635d58571..3fc18873e9 100644 --- a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java +++ b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java @@ -341,18 +341,11 @@ private ExecutableNormalizedOperation createNormalizedQueryImpl() { CollectNFResult collectFromOperationResult = collectFromOperation(rootType); - BiConsumer captureMergedField = (enf, mergedFld) -> { - // QueryDirectivesImpl is a lazy object and only computes itself when asked for - QueryDirectives queryDirectives = new QueryDirectivesImpl(mergedFld, graphQLSchema, coercedVariableValues.toMap(), options.getGraphQLContext(), options.getLocale()); - normalizedFieldToQueryDirectives.put(enf, queryDirectives); - normalizedFieldToMergedField.put(enf, mergedFld); - }; - for (ExecutableNormalizedField topLevel : collectFromOperationResult.children) { ImmutableList fieldAndAstParents = collectFromOperationResult.normalizedFieldToAstFields.get(topLevel); MergedField mergedField = newMergedField(fieldAndAstParents); - captureMergedField.accept(topLevel, mergedField); + captureMergedField(topLevel, mergedField); updateFieldToNFMap(topLevel, fieldAndAstParents); updateCoordinatedToNFMap(topLevel); @@ -360,7 +353,6 @@ private ExecutableNormalizedOperation createNormalizedQueryImpl() { buildFieldWithChildren( topLevel, fieldAndAstParents, - captureMergedField, 1, options.getMaxChildrenDepth()); } @@ -380,10 +372,15 @@ private ExecutableNormalizedOperation createNormalizedQueryImpl() { ); } + private void captureMergedField(ExecutableNormalizedField enf, MergedField mergedFld) { + // QueryDirectivesImpl is a lazy object and only computes itself when asked for + QueryDirectives queryDirectives = new QueryDirectivesImpl(mergedFld, graphQLSchema, coercedVariableValues.toMap(), options.getGraphQLContext(), options.getLocale()); + normalizedFieldToQueryDirectives.put(enf, queryDirectives); + normalizedFieldToMergedField.put(enf, mergedFld); + } private void buildFieldWithChildren(ExecutableNormalizedField executableNormalizedField, ImmutableList fieldAndAstParents, - BiConsumer captureMergedField, int curLevel, int maxLevel) { if (curLevel > maxLevel) { @@ -397,14 +394,13 @@ private void buildFieldWithChildren(ExecutableNormalizedField executableNormaliz ImmutableList childFieldAndAstParents = nextLevel.normalizedFieldToAstFields.get(childENF); MergedField mergedField = newMergedField(childFieldAndAstParents); - captureMergedField.accept(childENF, mergedField); + captureMergedField(childENF, mergedField); updateFieldToNFMap(childENF, childFieldAndAstParents); updateCoordinatedToNFMap(childENF); buildFieldWithChildren(childENF, childFieldAndAstParents, - captureMergedField, curLevel + 1, maxLevel); } From f1ff2044236e407faac9b2a464dd9041ce0866d8 Mon Sep 17 00:00:00 2001 From: Felipe Reis Date: Mon, 8 Jan 2024 20:32:01 +1100 Subject: [PATCH 6/8] Marked the constructor of ENFFactory as deprecated --- .../normalized/ExecutableNormalizedOperationFactory.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java index 3fc18873e9..2f878fd3f9 100644 --- a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java +++ b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import graphql.DeprecatedAt; import graphql.GraphQLContext; import graphql.PublicApi; import graphql.collect.ImmutableKit; @@ -151,6 +152,14 @@ public int getMaxChildrenDepth() { } } + /** + * @deprecated the API surface of this class is all static methods, eventually this constructor will be made `private`. + */ + @Deprecated + @DeprecatedAt("2024-01-08") + public ExecutableNormalizedOperationFactory() { + + } private static final ConditionalNodes conditionalNodes = new ConditionalNodes(); From 64b887dab2246bbc446d12757e6bedb4d0068a11 Mon Sep 17 00:00:00 2001 From: Andreas Marek Date: Tue, 9 Jan 2024 11:23:57 +1000 Subject: [PATCH 7/8] make constructor private rename Inner to Impl --- .../ExecutableNormalizedOperationFactory.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java index 2f878fd3f9..36a07f4866 100644 --- a/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java +++ b/src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java @@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import graphql.DeprecatedAt; import graphql.GraphQLContext; import graphql.PublicApi; import graphql.collect.ImmutableKit; @@ -47,7 +46,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.function.BiConsumer; import static graphql.Assert.assertNotNull; import static graphql.Assert.assertShouldNeverHappen; @@ -152,12 +150,7 @@ public int getMaxChildrenDepth() { } } - /** - * @deprecated the API surface of this class is all static methods, eventually this constructor will be made `private`. - */ - @Deprecated - @DeprecatedAt("2024-01-08") - public ExecutableNormalizedOperationFactory() { + private ExecutableNormalizedOperationFactory() { } @@ -205,7 +198,7 @@ public static ExecutableNormalizedOperation createExecutableNormalizedOperation( OperationDefinition operationDefinition, Map fragments, CoercedVariables coercedVariableValues) { - return new ExecutableNormalizedOperationFactoryInner( + return new ExecutableNormalizedOperationFactoryImpl( graphQLSchema, operationDefinition, fragments, @@ -300,7 +293,7 @@ public static ExecutableNormalizedOperation createExecutableNormalizedOperationW options.getGraphQLContext(), options.getLocale()); - return new ExecutableNormalizedOperationFactoryInner( + return new ExecutableNormalizedOperationFactoryImpl( graphQLSchema, operationDefinition, getOperationResult.fragmentsByName, @@ -311,7 +304,7 @@ public static ExecutableNormalizedOperation createExecutableNormalizedOperationW } - private static class ExecutableNormalizedOperationFactoryInner { + private static class ExecutableNormalizedOperationFactoryImpl { private final GraphQLSchema graphQLSchema; private final OperationDefinition operationDefinition; private final Map fragments; @@ -326,7 +319,7 @@ private static class ExecutableNormalizedOperationFactoryInner { private final ImmutableMap.Builder normalizedFieldToQueryDirectives = ImmutableMap.builder(); private final ImmutableListMultimap.Builder coordinatesToNormalizedFields = ImmutableListMultimap.builder(); - private ExecutableNormalizedOperationFactoryInner( + private ExecutableNormalizedOperationFactoryImpl( GraphQLSchema graphQLSchema, OperationDefinition operationDefinition, Map fragments, From 86ac919abfde0f31cea8c7e0e103594781775509 Mon Sep 17 00:00:00 2001 From: Andreas Marek Date: Wed, 10 Jan 2024 08:56:48 +1000 Subject: [PATCH 8/8] upgrade gradle to 8.5 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bdc9a83b1e..3499ded5c1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists