Skip to content

Commit 800ea0d

Browse files
andimarekbbakerman
authored andcommitted
allow interfaces and unions as root parent type for traversal and transformations (graphql-java#1486)
* allow interfaces as root parent type for traversal and transformations * allow composite root parent types * add test case for starting in a selectionSet node * add test case for union selectionSet
1 parent 8ba2d1d commit 800ea0d

6 files changed

Lines changed: 299 additions & 26 deletions

File tree

src/main/java/graphql/analysis/NodeVisitorWithTypeTracking.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ public TraversalControl visitInlineFragment(InlineFragment inlineFragment, Trave
7676
TypeName typeCondition = inlineFragment.getTypeCondition();
7777
fragmentCondition = (GraphQLCompositeType) schema.getType(typeCondition.getName());
7878
} else {
79-
fragmentCondition = parentEnv.getRawType();
79+
fragmentCondition = parentEnv.getUnwrappedOutputType();
8080
}
8181
// for unions we only have other fragments inside
82-
context.setVar(QueryTraversalContext.class, new QueryTraversalContext(fragmentCondition, fragmentCondition, parentEnv.getEnvironment(), inlineFragment));
82+
context.setVar(QueryTraversalContext.class, new QueryTraversalContext(fragmentCondition, parentEnv.getEnvironment(), inlineFragment));
8383
return TraversalControl.CONTINUE;
8484
}
8585

@@ -100,7 +100,7 @@ public TraversalControl visitFragmentDefinition(FragmentDefinition node, Travers
100100
QueryTraversalContext parentEnv = context.getVarFromParents(QueryTraversalContext.class);
101101
GraphQLCompositeType typeCondition = (GraphQLCompositeType) schema.getType(node.getTypeCondition().getName());
102102
context
103-
.setVar(QueryTraversalContext.class, new QueryTraversalContext(typeCondition, typeCondition, parentEnv.getEnvironment(), node));
103+
.setVar(QueryTraversalContext.class, new QueryTraversalContext(typeCondition, parentEnv.getEnvironment(), node));
104104
return TraversalControl.CONTINUE;
105105
}
106106

@@ -129,15 +129,15 @@ public TraversalControl visitFragmentSpread(FragmentSpread fragmentSpread, Trave
129129
assertNotNull(typeCondition, "Invalid type condition '%s' in fragment '%s'", fragmentDefinition.getTypeCondition().getName(),
130130
fragmentDefinition.getName());
131131
context
132-
.setVar(QueryTraversalContext.class, new QueryTraversalContext(typeCondition, typeCondition, parentEnv.getEnvironment(), fragmentDefinition));
132+
.setVar(QueryTraversalContext.class, new QueryTraversalContext(typeCondition, parentEnv.getEnvironment(), fragmentDefinition));
133133
return TraversalControl.CONTINUE;
134134
}
135135

136136
@Override
137137
public TraversalControl visitField(Field field, TraverserContext<Node> context) {
138138
QueryTraversalContext parentEnv = context.getVarFromParents(QueryTraversalContext.class);
139139

140-
GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(schema, parentEnv.getRawType(), field.getName());
140+
GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(schema, (GraphQLCompositeType) unwrapAll(parentEnv.getOutputType()), field.getName());
141141
boolean isTypeNameIntrospectionField = fieldDefinition == Introspection.TypeNameMetaFieldDef;
142142
GraphQLFieldsContainer fieldsContainer = !isTypeNameIntrospectionField ? (GraphQLFieldsContainer) unwrapAll(parentEnv.getOutputType()) : null;
143143
GraphQLCodeRegistry codeRegistry = schema.getCodeRegistry();
@@ -166,8 +166,8 @@ public TraversalControl visitField(Field field, TraverserContext<Node> context)
166166

167167
GraphQLUnmodifiedType unmodifiedType = unwrapAll(fieldDefinition.getType());
168168
QueryTraversalContext fieldEnv = (unmodifiedType instanceof GraphQLCompositeType)
169-
? new QueryTraversalContext(fieldDefinition.getType(), (GraphQLCompositeType) unmodifiedType, environment, field)
170-
: new QueryTraversalContext(null, null, environment, field);// Terminal (scalar) node, EMPTY FRAME
169+
? new QueryTraversalContext(fieldDefinition.getType(), environment, field)
170+
: new QueryTraversalContext(null, environment, field);// Terminal (scalar) node, EMPTY FRAME
171171

172172

173173
context.setVar(QueryTraversalContext.class, fieldEnv);

src/main/java/graphql/analysis/QueryTransformer.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import graphql.language.FragmentDefinition;
55
import graphql.language.Node;
66
import graphql.language.NodeTraverser.LeaveOrEnter;
7-
import graphql.schema.GraphQLObjectType;
7+
import graphql.schema.GraphQLCompositeType;
88
import graphql.schema.GraphQLSchema;
99
import graphql.util.TraversalControl;
1010
import graphql.util.TraverserContext;
@@ -37,12 +37,12 @@ public class QueryTransformer {
3737
private final Map<String, FragmentDefinition> fragmentsByName;
3838
private final Map<String, Object> variables;
3939

40-
private final GraphQLObjectType rootParentType;
40+
private final GraphQLCompositeType rootParentType;
4141

4242

4343
private QueryTransformer(GraphQLSchema schema,
4444
Node root,
45-
GraphQLObjectType rootParentType,
45+
GraphQLCompositeType rootParentType,
4646
Map<String, FragmentDefinition> fragmentsByName,
4747
Map<String, Object> variables) {
4848
this.schema = assertNotNull(schema, "schema can't be null");
@@ -68,7 +68,7 @@ public Node transform(QueryVisitor queryVisitor) {
6868
NodeVisitorWithTypeTracking nodeVisitor = new NodeVisitorWithTypeTracking(queryVisitor, noOp, variables, schema, fragmentsByName);
6969

7070
Map<Class<?>, Object> rootVars = new LinkedHashMap<>();
71-
rootVars.put(QueryTraversalContext.class, new QueryTraversalContext(rootParentType, rootParentType, null, null));
71+
rootVars.put(QueryTraversalContext.class, new QueryTraversalContext(rootParentType, null, null));
7272

7373
TraverserVisitor<Node> nodeTraverserVisitor = new TraverserVisitor<Node>() {
7474

@@ -97,7 +97,7 @@ public static class Builder {
9797
private Map<String, Object> variables;
9898

9999
private Node root;
100-
private GraphQLObjectType rootParentType;
100+
private GraphQLCompositeType rootParentType;
101101
private Map<String, FragmentDefinition> fragmentsByName;
102102

103103

@@ -144,13 +144,13 @@ public Builder root(Node root) {
144144
*
145145
* @return this builder
146146
*/
147-
public Builder rootParentType(GraphQLObjectType rootParentType) {
147+
public Builder rootParentType(GraphQLCompositeType rootParentType) {
148148
this.rootParentType = rootParentType;
149149
return this;
150150
}
151151

152152
/**
153-
* Fragment by name map. Needs to be provided together with a {@link Builder#root(Node)} and {@link Builder#rootParentType(GraphQLObjectType)}
153+
* Fragment by name map. Needs to be provided together with a {@link Builder#root(Node)} and {@link Builder#rootParentType(GraphQLCompositeType)}
154154
*
155155
* @param fragmentsByName the map of fragments
156156
*
@@ -162,7 +162,7 @@ public Builder fragmentsByName(Map<String, FragmentDefinition> fragmentsByName)
162162
}
163163

164164
/**
165-
* @return a built {@link QueryTransformer} object
165+
166166
*/
167167
public QueryTransformer build() {
168168
return new QueryTransformer(schema, root, rootParentType, fragmentsByName, variables);

src/main/java/graphql/analysis/QueryTraversal.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import graphql.language.NodeTraverser;
99
import graphql.language.NodeUtil;
1010
import graphql.language.OperationDefinition;
11+
import graphql.schema.GraphQLCompositeType;
1112
import graphql.schema.GraphQLObjectType;
1213
import graphql.schema.GraphQLSchema;
1314

@@ -41,7 +42,7 @@ public class QueryTraversal {
4142
private final Map<String, FragmentDefinition> fragmentsByName;
4243
private final Map<String, Object> variables;
4344

44-
private final GraphQLObjectType rootParentType;
45+
private final GraphQLCompositeType rootParentType;
4546

4647
private QueryTraversal(GraphQLSchema schema,
4748
Document document,
@@ -58,7 +59,7 @@ private QueryTraversal(GraphQLSchema schema,
5859

5960
private QueryTraversal(GraphQLSchema schema,
6061
Node root,
61-
GraphQLObjectType rootParentType,
62+
GraphQLCompositeType rootParentType,
6263
Map<String, FragmentDefinition> fragmentsByName,
6364
Map<String, Object> variables) {
6465
this.schema = assertNotNull(schema, "schema can't be null");
@@ -159,7 +160,7 @@ private List<Node> childrenOf(Node<?> node) {
159160

160161
private Object visitImpl(QueryVisitor visitFieldCallback, Boolean preOrder) {
161162
Map<Class<?>, Object> rootVars = new LinkedHashMap<>();
162-
rootVars.put(QueryTraversalContext.class, new QueryTraversalContext(rootParentType, rootParentType, null, null));
163+
rootVars.put(QueryTraversalContext.class, new QueryTraversalContext(rootParentType, null, null));
163164

164165
QueryVisitor preOrderCallback;
165166
QueryVisitor postOrderCallback;
@@ -189,7 +190,7 @@ public static class Builder {
189190
private Map<String, Object> variables;
190191

191192
private Node root;
192-
private GraphQLObjectType rootParentType;
193+
private GraphQLCompositeType rootParentType;
193194
private Map<String, FragmentDefinition> fragmentsByName;
194195

195196

@@ -263,13 +264,13 @@ public Builder root(Node root) {
263264
*
264265
* @return this builder
265266
*/
266-
public Builder rootParentType(GraphQLObjectType rootParentType) {
267+
public Builder rootParentType(GraphQLCompositeType rootParentType) {
267268
this.rootParentType = rootParentType;
268269
return this;
269270
}
270271

271272
/**
272-
* Fragment by name map. Needs to be provided together with a {@link Builder#root(Node)} and {@link Builder#rootParentType(GraphQLObjectType)}
273+
* Fragment by name map. Needs to be provided together with a {@link Builder#root(Node)} and {@link Builder#rootParentType(GraphQLCompositeType)}
273274
*
274275
* @param fragmentsByName the map of fragments
275276
*

src/main/java/graphql/analysis/QueryTraversalContext.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import graphql.language.SelectionSetContainer;
55
import graphql.schema.GraphQLCompositeType;
66
import graphql.schema.GraphQLOutputType;
7+
import graphql.schema.GraphQLTypeUtil;
78

89
/**
910
* QueryTraversal helper class that maintains traversal context as
@@ -12,17 +13,15 @@
1213
@Internal
1314
class QueryTraversalContext {
1415

16+
// never used for scalars/enums, always a possibly wrapped composite type
1517
private final GraphQLOutputType outputType;
16-
private final GraphQLCompositeType rawType;
1718
private final QueryVisitorFieldEnvironment environment;
1819
private final SelectionSetContainer selectionSetContainer;
1920

2021
QueryTraversalContext(GraphQLOutputType outputType,
21-
GraphQLCompositeType rawType,
2222
QueryVisitorFieldEnvironment environment,
2323
SelectionSetContainer selectionSetContainer) {
2424
this.outputType = outputType;
25-
this.rawType = rawType;
2625
this.environment = environment;
2726
this.selectionSetContainer = selectionSetContainer;
2827
}
@@ -31,10 +30,11 @@ public GraphQLOutputType getOutputType() {
3130
return outputType;
3231
}
3332

34-
public GraphQLCompositeType getRawType() {
35-
return rawType;
33+
public GraphQLCompositeType getUnwrappedOutputType() {
34+
return (GraphQLCompositeType) GraphQLTypeUtil.unwrapAll(outputType);
3635
}
3736

37+
3838
public QueryVisitorFieldEnvironment getEnvironment() {
3939
return environment;
4040
}

0 commit comments

Comments
 (0)