From 3ad4dd639da1f829d6858f78b0f4032d48608a4b Mon Sep 17 00:00:00 2001 From: Tomek Sroka Date: Tue, 5 Mar 2019 11:58:21 -0800 Subject: [PATCH 1/4] Add support for visiting fragments in QueryVisitior. --- .../analysis/NodeVisitorWithTypeTracking.java | 5 +- .../java/graphql/analysis/QueryVisitor.java | 4 ++ .../QueryVisitorFragmentEnvironment.java | 13 +++++ .../QueryVisitorFragmentEnvironmentImpl.java | 49 +++++++++++++++++++ .../analysis/QueryTransformerTest.groovy | 14 +++++- .../analysis/QueryTraversalTest.groovy | 49 +++++++++++++++++++ 6 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 src/main/java/graphql/analysis/QueryVisitorFragmentEnvironment.java create mode 100644 src/main/java/graphql/analysis/QueryVisitorFragmentEnvironmentImpl.java diff --git a/src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java b/src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java index d74e5cf63e..00838a6cf8 100644 --- a/src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java +++ b/src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java @@ -89,10 +89,13 @@ public TraversalControl visitFragmentDefinition(FragmentDefinition node, Travers return TraversalControl.ABORT; } + QueryVisitorFragmentEnvironment fragmentEnvironment = new QueryVisitorFragmentEnvironmentImpl(node, context); + if (context.getVar(NodeTraverser.LeaveOrEnter.class) == LEAVE) { + postOrderCallback.visitFragment(fragmentEnvironment); return TraversalControl.CONTINUE; } - + preOrderCallback.visitFragment(fragmentEnvironment); QueryTraversalContext parentEnv = context.getVarFromParents(QueryTraversalContext.class); GraphQLCompositeType typeCondition = (GraphQLCompositeType) schema.getType(node.getTypeCondition().getName()); diff --git a/src/main/java/graphql/analysis/QueryVisitor.java b/src/main/java/graphql/analysis/QueryVisitor.java index 9a39344b54..40797b8636 100644 --- a/src/main/java/graphql/analysis/QueryVisitor.java +++ b/src/main/java/graphql/analysis/QueryVisitor.java @@ -16,4 +16,8 @@ public interface QueryVisitor { void visitFragmentSpread(QueryVisitorFragmentSpreadEnvironment queryVisitorFragmentSpreadEnvironment); + default void visitFragment(QueryVisitorFragmentEnvironment queryVisitorFragmentEnvironment) { + + } + } diff --git a/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironment.java b/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironment.java new file mode 100644 index 0000000000..72a4449d1c --- /dev/null +++ b/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironment.java @@ -0,0 +1,13 @@ +package graphql.analysis; + +import graphql.PublicApi; +import graphql.language.FragmentDefinition; +import graphql.language.Node; +import graphql.util.TraverserContext; + +@PublicApi +public interface QueryVisitorFragmentEnvironment { + FragmentDefinition getFragmentDefinition(); + + TraverserContext getTraverserContext(); +} diff --git a/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironmentImpl.java b/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironmentImpl.java new file mode 100644 index 0000000000..c460393f34 --- /dev/null +++ b/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironmentImpl.java @@ -0,0 +1,49 @@ +package graphql.analysis; + +import graphql.Internal; +import graphql.language.FragmentDefinition; +import graphql.language.Node; +import graphql.util.TraverserContext; + +import java.util.Objects; + +@Internal +public class QueryVisitorFragmentEnvironmentImpl implements QueryVisitorFragmentEnvironment { + + private final FragmentDefinition fragmentDefinition; + private final TraverserContext traverserContext; + + + public QueryVisitorFragmentEnvironmentImpl(FragmentDefinition fragmentDefinition, TraverserContext traverserContext) { + this.fragmentDefinition = fragmentDefinition; + this.traverserContext = traverserContext; + } + + @Override + public FragmentDefinition getFragmentDefinition() { + return fragmentDefinition; + } + + @Override + public TraverserContext getTraverserContext() { + return traverserContext; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + QueryVisitorFragmentEnvironmentImpl that = (QueryVisitorFragmentEnvironmentImpl) o; + return Objects.equals(fragmentDefinition, that.fragmentDefinition); + } + + @Override + public int hashCode() { + return Objects.hash(fragmentDefinition); + } +} + diff --git a/src/test/groovy/graphql/analysis/QueryTransformerTest.groovy b/src/test/groovy/graphql/analysis/QueryTransformerTest.groovy index 99afbffb41..b241bb72fb 100644 --- a/src/test/groovy/graphql/analysis/QueryTransformerTest.groovy +++ b/src/test/groovy/graphql/analysis/QueryTransformerTest.groovy @@ -5,6 +5,7 @@ import graphql.language.Document import graphql.language.Field import graphql.language.NodeUtil import graphql.language.SelectionSet +import graphql.language.TypeName import graphql.parser.Parser import graphql.schema.GraphQLSchema import spock.lang.Specification @@ -207,7 +208,7 @@ class QueryTransformerTest extends Specification { 0 * _ } - def "named fragment is traversed if it is a root and can be transformed"() { + def "fragment is traversed if it is a root and can be transformed"() { def query = TestUtil.parseQuery(''' { root { @@ -241,6 +242,15 @@ class QueryTransformerTest extends Specification { }) } } + + @Override + void visitFragment(QueryVisitorFragmentEnvironment env) { + def changed = env.fragmentDefinition.transform({ builder -> + builder.typeCondition(TypeName.newTypeName("newTypeName").build()) + .name("newFragName") + }) + changeNode(env.traverserContext, changed) + } } @@ -248,6 +258,6 @@ class QueryTransformerTest extends Specification { def newFragment = queryTransformer.transform(visitor) then: printAstCompact(newFragment) == - "fragment frag on Root {fooA {midA {newChild1 newChild2}}}" + "fragment newFragName on newTypeName {fooA {midA {newChild1 newChild2}}}" } } diff --git a/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy b/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy index e3aa19e183..0bf4f8726e 100644 --- a/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy +++ b/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy @@ -7,6 +7,7 @@ import graphql.language.FragmentDefinition import graphql.language.FragmentSpread import graphql.language.InlineFragment import graphql.language.NodeTraverser +import graphql.language.NodeUtil import graphql.parser.Parser import graphql.schema.GraphQLNonNull import graphql.schema.GraphQLObjectType @@ -302,6 +303,54 @@ class QueryTraversalTest extends Specification { } + + def "test preOrder and postOrder order for fragments"() { + given: + def schema = TestUtil.schema(""" + type Query{ + foo: Foo + bar: String + } + type Foo { + subFoo: String + } + """) + def visitor = Mock(QueryVisitor) + def query = createQuery(""" + { + ...F1 + } + + fragment F1 on Query { + foo { + subFoo + } + } + """) + + def fragments = NodeUtil.getFragmentsByName(query) + + QueryTraversal queryTraversal = QueryTraversal.newQueryTraversal() + .schema(schema) + .root(fragments["F1"]) + .rootParentType(schema.getQueryType()) + .fragmentsByName(fragments) + .variables([:]) + .build() + + when: + queryTraversal.visitPreOrder(visitor) + + then: + 1 * visitor.visitFragment({ QueryVisitorFragmentEnvironment env -> env.fragmentDefinition == fragments["F1"] }) + + when: + queryTraversal.visitPostOrder(visitor) + + then: + 1 * visitor.visitFragment({ QueryVisitorFragmentEnvironment env -> env.fragmentDefinition == fragments["F1"] }) + } + def "works for mutations()"() { given: def schema = TestUtil.schema(""" From 48394db5f25e837a34d4974cfe789bea8b828fb2 Mon Sep 17 00:00:00 2001 From: Tomek Sroka Date: Mon, 11 Mar 2019 13:33:01 -0700 Subject: [PATCH 2/4] Add support for visiting fragments in QueryVisitior. PR feedback --- .../graphql/analysis/NodeVisitorWithTypeTracking.java | 6 +++--- src/main/java/graphql/analysis/QueryVisitor.java | 2 +- ...ava => QueryVisitorFragmentDefinitionEnvironment.java} | 2 +- ...=> QueryVisitorFragmentDefinitionEnvironmentImpl.java} | 6 +++--- .../groovy/graphql/analysis/QueryTransformerTest.groovy | 4 ++-- .../groovy/graphql/analysis/QueryTraversalTest.groovy | 8 ++++---- 6 files changed, 14 insertions(+), 14 deletions(-) rename src/main/java/graphql/analysis/{QueryVisitorFragmentEnvironment.java => QueryVisitorFragmentDefinitionEnvironment.java} (82%) rename src/main/java/graphql/analysis/{QueryVisitorFragmentEnvironmentImpl.java => QueryVisitorFragmentDefinitionEnvironmentImpl.java} (74%) diff --git a/src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java b/src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java index 00838a6cf8..a817963145 100644 --- a/src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java +++ b/src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java @@ -89,13 +89,13 @@ public TraversalControl visitFragmentDefinition(FragmentDefinition node, Travers return TraversalControl.ABORT; } - QueryVisitorFragmentEnvironment fragmentEnvironment = new QueryVisitorFragmentEnvironmentImpl(node, context); + QueryVisitorFragmentDefinitionEnvironment fragmentEnvironment = new QueryVisitorFragmentDefinitionEnvironmentImpl(node, context); if (context.getVar(NodeTraverser.LeaveOrEnter.class) == LEAVE) { - postOrderCallback.visitFragment(fragmentEnvironment); + postOrderCallback.visitFragmentDefinition(fragmentEnvironment); return TraversalControl.CONTINUE; } - preOrderCallback.visitFragment(fragmentEnvironment); + preOrderCallback.visitFragmentDefinition(fragmentEnvironment); QueryTraversalContext parentEnv = context.getVarFromParents(QueryTraversalContext.class); GraphQLCompositeType typeCondition = (GraphQLCompositeType) schema.getType(node.getTypeCondition().getName()); diff --git a/src/main/java/graphql/analysis/QueryVisitor.java b/src/main/java/graphql/analysis/QueryVisitor.java index 40797b8636..be95bac4f1 100644 --- a/src/main/java/graphql/analysis/QueryVisitor.java +++ b/src/main/java/graphql/analysis/QueryVisitor.java @@ -16,7 +16,7 @@ public interface QueryVisitor { void visitFragmentSpread(QueryVisitorFragmentSpreadEnvironment queryVisitorFragmentSpreadEnvironment); - default void visitFragment(QueryVisitorFragmentEnvironment queryVisitorFragmentEnvironment) { + default void visitFragmentDefinition(QueryVisitorFragmentDefinitionEnvironment queryVisitorFragmentDefinitionEnvironment) { } diff --git a/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironment.java b/src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironment.java similarity index 82% rename from src/main/java/graphql/analysis/QueryVisitorFragmentEnvironment.java rename to src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironment.java index 72a4449d1c..2d2b51e092 100644 --- a/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironment.java +++ b/src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironment.java @@ -6,7 +6,7 @@ import graphql.util.TraverserContext; @PublicApi -public interface QueryVisitorFragmentEnvironment { +public interface QueryVisitorFragmentDefinitionEnvironment { FragmentDefinition getFragmentDefinition(); TraverserContext getTraverserContext(); diff --git a/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironmentImpl.java b/src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironmentImpl.java similarity index 74% rename from src/main/java/graphql/analysis/QueryVisitorFragmentEnvironmentImpl.java rename to src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironmentImpl.java index c460393f34..d87be32c15 100644 --- a/src/main/java/graphql/analysis/QueryVisitorFragmentEnvironmentImpl.java +++ b/src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironmentImpl.java @@ -8,13 +8,13 @@ import java.util.Objects; @Internal -public class QueryVisitorFragmentEnvironmentImpl implements QueryVisitorFragmentEnvironment { +public class QueryVisitorFragmentDefinitionEnvironmentImpl implements QueryVisitorFragmentDefinitionEnvironment { private final FragmentDefinition fragmentDefinition; private final TraverserContext traverserContext; - public QueryVisitorFragmentEnvironmentImpl(FragmentDefinition fragmentDefinition, TraverserContext traverserContext) { + public QueryVisitorFragmentDefinitionEnvironmentImpl(FragmentDefinition fragmentDefinition, TraverserContext traverserContext) { this.fragmentDefinition = fragmentDefinition; this.traverserContext = traverserContext; } @@ -37,7 +37,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - QueryVisitorFragmentEnvironmentImpl that = (QueryVisitorFragmentEnvironmentImpl) o; + QueryVisitorFragmentDefinitionEnvironmentImpl that = (QueryVisitorFragmentDefinitionEnvironmentImpl) o; return Objects.equals(fragmentDefinition, that.fragmentDefinition); } diff --git a/src/test/groovy/graphql/analysis/QueryTransformerTest.groovy b/src/test/groovy/graphql/analysis/QueryTransformerTest.groovy index b241bb72fb..1382c50966 100644 --- a/src/test/groovy/graphql/analysis/QueryTransformerTest.groovy +++ b/src/test/groovy/graphql/analysis/QueryTransformerTest.groovy @@ -208,7 +208,7 @@ class QueryTransformerTest extends Specification { 0 * _ } - def "fragment is traversed if it is a root and can be transformed"() { + def "fragment definition is traversed if it is a root and can be transformed"() { def query = TestUtil.parseQuery(''' { root { @@ -244,7 +244,7 @@ class QueryTransformerTest extends Specification { } @Override - void visitFragment(QueryVisitorFragmentEnvironment env) { + void visitFragmentDefinition(QueryVisitorFragmentDefinitionEnvironment env) { def changed = env.fragmentDefinition.transform({ builder -> builder.typeCondition(TypeName.newTypeName("newTypeName").build()) .name("newFragName") diff --git a/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy b/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy index 0bf4f8726e..6e06ee187c 100644 --- a/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy +++ b/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy @@ -21,7 +21,7 @@ import static graphql.schema.GraphQLList.list import static graphql.schema.GraphQLNonNull.nonNull import static java.util.Collections.emptyMap -class QueryTraversalTest extends Specification { +class QueryTrqaversalTest extends Specification { Document createQuery(String query) { @@ -304,7 +304,7 @@ class QueryTraversalTest extends Specification { } - def "test preOrder and postOrder order for fragments"() { + def "test preOrder and postOrder order for fragment definitions"() { given: def schema = TestUtil.schema(""" type Query{ @@ -342,13 +342,13 @@ class QueryTraversalTest extends Specification { queryTraversal.visitPreOrder(visitor) then: - 1 * visitor.visitFragment({ QueryVisitorFragmentEnvironment env -> env.fragmentDefinition == fragments["F1"] }) + 1 * visitor.visitFragmentDefinition({ QueryVisitorFragmentDefinitionEnvironment env -> env.fragmentDefinition == fragments["F1"] }) when: queryTraversal.visitPostOrder(visitor) then: - 1 * visitor.visitFragment({ QueryVisitorFragmentEnvironment env -> env.fragmentDefinition == fragments["F1"] }) + 1 * visitor.visitFragmentDefinition({ QueryVisitorFragmentDefinitionEnvironment env -> env.fragmentDefinition == fragments["F1"] }) } def "works for mutations()"() { From 81004f54535120d9f7f6e6b4e38888e351ac1d37 Mon Sep 17 00:00:00 2001 From: Tomek Sroka Date: Mon, 11 Mar 2019 13:34:44 -0700 Subject: [PATCH 3/4] Add support for visiting fragments in QueryVisitior. PR feedback --- .../QueryVisitorFragmentDefinitionEnvironmentImpl.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironmentImpl.java b/src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironmentImpl.java index d87be32c15..da1349f7b9 100644 --- a/src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironmentImpl.java +++ b/src/main/java/graphql/analysis/QueryVisitorFragmentDefinitionEnvironmentImpl.java @@ -45,5 +45,12 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(fragmentDefinition); } + + @Override + public String toString() { + return "QueryVisitorFragmentDefinitionEnvironmentImpl{" + + "fragmentDefinition=" + fragmentDefinition + + '}'; + } } From 8b58e3e8b11b20f365cfdd87fc189d6e382f577c Mon Sep 17 00:00:00 2001 From: Tomek Sroka Date: Mon, 11 Mar 2019 15:33:06 -0700 Subject: [PATCH 4/4] Add support for visiting fragments in QueryVisitior. PR feedback --- src/test/groovy/graphql/analysis/QueryTraversalTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy b/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy index 6e06ee187c..608d115ec8 100644 --- a/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy +++ b/src/test/groovy/graphql/analysis/QueryTraversalTest.groovy @@ -21,7 +21,7 @@ import static graphql.schema.GraphQLList.list import static graphql.schema.GraphQLNonNull.nonNull import static java.util.Collections.emptyMap -class QueryTrqaversalTest extends Specification { +class QueryTraversalTest extends Specification { Document createQuery(String query) {