Skip to content
This repository was archived by the owner on Feb 27, 2023. It is now read-only.

Commit 9850d4f

Browse files
committed
Merge remote-tracking branch 'upstream/master' into defer-support-exploration
2 parents 7d318d7 + 5be7647 commit 9850d4f

90 files changed

Lines changed: 2863 additions & 194 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/main/java/graphql/Scalars.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import java.math.BigDecimal;
1515
import java.math.BigInteger;
16+
import java.util.UUID;
1617

1718
import static graphql.Assert.assertShouldNeverHappen;
1819

@@ -274,6 +275,9 @@ private String convertImpl(Object input) {
274275
if (input instanceof Long) {
275276
return String.valueOf(input);
276277
}
278+
if (input instanceof UUID) {
279+
return String.valueOf(input);
280+
}
277281
return null;
278282

279283
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package graphql.analysis;
2+
3+
import graphql.Internal;
4+
import graphql.language.Field;
5+
import graphql.language.FragmentDefinition;
6+
import graphql.language.FragmentSpread;
7+
import graphql.language.InlineFragment;
8+
import graphql.language.Node;
9+
import graphql.language.NodeTraverser;
10+
import graphql.language.NodeVisitorStub;
11+
import graphql.language.Selection;
12+
import graphql.language.SelectionSet;
13+
import graphql.util.TraversalControl;
14+
import graphql.util.TraverserContext;
15+
16+
import java.util.Collections;
17+
import java.util.List;
18+
import java.util.Map;
19+
20+
/**
21+
* QueryTraversal helper class responsible for obtaining Selection
22+
* nodes from selection parent.
23+
* Uses double dispatch in order to avoid reflection based {@code instanceof} check.
24+
*/
25+
@Internal
26+
public class ChildrenOfSelectionProvider extends NodeVisitorStub {
27+
28+
private Map<String, FragmentDefinition> fragmentDefinitionMap;
29+
30+
public ChildrenOfSelectionProvider(Map<String, FragmentDefinition> fragmentDefinitionMap) {
31+
this.fragmentDefinitionMap = fragmentDefinitionMap;
32+
}
33+
34+
@Override
35+
public TraversalControl visitInlineFragment(InlineFragment node, TraverserContext<Node> context) {
36+
getSelectionSetChildren(node.getSelectionSet(), context);
37+
return TraversalControl.CONTINUE;
38+
}
39+
40+
@Override
41+
public TraversalControl visitFragmentSpread(FragmentSpread fragmentSpread, TraverserContext<Node> context) {
42+
getSelectionSetChildren(fragmentDefinitionMap.get(fragmentSpread.getName()).getSelectionSet(), context);
43+
return TraversalControl.CONTINUE;
44+
}
45+
46+
@Override
47+
public TraversalControl visitField(Field node, TraverserContext<Node> context) {
48+
getSelectionSetChildren(node.getSelectionSet(), context);
49+
return TraversalControl.CONTINUE;
50+
}
51+
52+
@Override
53+
public TraversalControl visitSelectionSet(SelectionSet node, TraverserContext<Node> context) {
54+
context.setResult(node.getSelections());
55+
return TraversalControl.CONTINUE;
56+
}
57+
58+
private void getSelectionSetChildren(SelectionSet node, TraverserContext<Node> context) {
59+
if (node == null) {
60+
context.setResult(Collections.emptyList());
61+
} else {
62+
context.setResult(node.getSelections());
63+
}
64+
}
65+
66+
public List<Node> getSelections(Selection selection) {
67+
return NodeTraverser.oneVisitWithResult(selection, this);
68+
}
69+
}

src/main/java/graphql/analysis/QueryVisitor.java renamed to src/main/java/graphql/analysis/FieldVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import graphql.Internal;
44

55
@Internal
6-
public interface QueryVisitor {
6+
public interface FieldVisitor {
77

88
void visitField(QueryVisitorEnvironment queryVisitorEnvironment);
99

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

Lines changed: 118 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
import graphql.language.FragmentDefinition;
1010
import graphql.language.FragmentSpread;
1111
import graphql.language.InlineFragment;
12+
import graphql.language.Node;
13+
import graphql.language.NodeTraverser;
14+
import graphql.language.NodeTraverser.LeaveOrEnter;
1215
import graphql.language.NodeUtil;
16+
import graphql.language.NodeVisitorStub;
1317
import graphql.language.OperationDefinition;
1418
import graphql.language.Selection;
1519
import graphql.language.SelectionSet;
@@ -20,56 +24,61 @@
2024
import graphql.schema.GraphQLSchema;
2125
import graphql.schema.GraphQLUnmodifiedType;
2226
import graphql.schema.SchemaUtil;
27+
import graphql.util.TraversalControl;
28+
import graphql.util.TraverserContext;
2329

2430
import java.util.LinkedHashMap;
31+
import java.util.List;
2532
import java.util.Map;
2633

2734
import static graphql.Assert.assertNotNull;
2835
import static graphql.Assert.assertShouldNeverHappen;
36+
import static graphql.language.NodeTraverser.LeaveOrEnter.LEAVE;
2937

3038
@Internal
3139
public class QueryTraversal {
3240

33-
3441
private final OperationDefinition operationDefinition;
3542
private final GraphQLSchema schema;
36-
private Map<String, FragmentDefinition> fragmentsByName = new LinkedHashMap<>();
43+
private final Map<String, FragmentDefinition> fragmentsByName;
3744
private final Map<String, Object> variables;
3845

3946
private final ConditionalNodes conditionalNodes = new ConditionalNodes();
40-
4147
private final ValuesResolver valuesResolver = new ValuesResolver();
4248
private final SchemaUtil schemaUtil = new SchemaUtil();
43-
49+
private final ChildrenOfSelectionProvider childrenOfSelectionProvider;
4450

4551
public QueryTraversal(GraphQLSchema schema,
4652
Document document,
4753
String operation,
4854
Map<String, Object> variables) {
4955
NodeUtil.GetOperationResult getOperationResult = NodeUtil.getOperation(document, operation);
56+
5057
this.operationDefinition = getOperationResult.operationDefinition;
5158
this.fragmentsByName = getOperationResult.fragmentsByName;
59+
this.childrenOfSelectionProvider = new ChildrenOfSelectionProvider(fragmentsByName);
5260
this.schema = schema;
5361
this.variables = variables;
5462
}
5563

56-
public void visitPostOrder(QueryVisitor visitor) {
57-
visitImpl(visitor, operationDefinition.getSelectionSet(), getRootType(), null, false);
64+
public void visitPostOrder(FieldVisitor visitor) {
65+
visitImpl(visitor, operationDefinition.getSelectionSet(), getRootType(), false);
5866
}
5967

60-
public void visitPreOrder(QueryVisitor visitor) {
61-
visitImpl(visitor, operationDefinition.getSelectionSet(), getRootType(), null, true);
68+
public void visitPreOrder(FieldVisitor visitor) {
69+
visitImpl(visitor, operationDefinition.getSelectionSet(), getRootType(), true);
6270
}
6371

6472
private GraphQLObjectType getRootType() {
65-
if (operationDefinition.getOperation() == OperationDefinition.Operation.MUTATION) {
66-
return assertNotNull(schema.getMutationType());
67-
} else if (operationDefinition.getOperation() == OperationDefinition.Operation.QUERY) {
68-
return assertNotNull(schema.getQueryType());
69-
} else if (operationDefinition.getOperation() == OperationDefinition.Operation.SUBSCRIPTION) {
70-
return assertNotNull(schema.getSubscriptionType());
71-
} else {
72-
return assertShouldNeverHappen();
73+
switch (operationDefinition.getOperation()) {
74+
case MUTATION:
75+
return assertNotNull(schema.getMutationType());
76+
case QUERY:
77+
return assertNotNull(schema.getQueryType());
78+
case SUBSCRIPTION:
79+
return assertNotNull(schema.getSubscriptionType());
80+
default:
81+
return assertShouldNeverHappen();
7382
}
7483
}
7584

@@ -89,69 +98,111 @@ public <T> T reducePreOrder(QueryReducer<T> queryReducer, T initialValue) {
8998
return (T) acc[0];
9099
}
91100

101+
private List<Node> childrenOf(Node selection) {
102+
return childrenOfSelectionProvider.getSelections((Selection) selection);
103+
}
92104

93-
private void visitImpl(QueryVisitor visitor, SelectionSet selectionSet, GraphQLCompositeType type, QueryVisitorEnvironment parent, boolean preOrder) {
105+
private void visitImpl(FieldVisitor visitFieldCallback, SelectionSet selectionSet, GraphQLCompositeType type, boolean preOrder) {
106+
Map<Class<?>, Object> rootVars = new LinkedHashMap<>();
107+
rootVars.put(QueryTraversalContext.class, new QueryTraversalContext(type, null));
94108

95-
for (Selection selection : selectionSet.getSelections()) {
96-
if (selection instanceof Field) {
97-
GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(schema, type, ((Field) selection).getName());
98-
visitField(visitor, (Field) selection, fieldDefinition, type, parent, preOrder);
99-
} else if (selection instanceof InlineFragment) {
100-
visitInlineFragment(visitor, (InlineFragment) selection, type, parent, preOrder);
101-
} else if (selection instanceof FragmentSpread) {
102-
visitFragmentSpread(visitor, (FragmentSpread) selection, parent, preOrder);
103-
}
104-
}
109+
FieldVisitor noOp = notUsed -> {
110+
};
111+
FieldVisitor preOrderCallback = preOrder ? visitFieldCallback : noOp;
112+
FieldVisitor postOrderCallback = !preOrder ? visitFieldCallback : noOp;
113+
114+
NodeTraverser nodeTraverser = new NodeTraverser(rootVars, this::childrenOf);
115+
nodeTraverser.depthFirst(new NodeVisitorImpl(preOrderCallback, postOrderCallback), selectionSet.getSelections());
105116
}
106117

107-
private void visitFragmentSpread(QueryVisitor visitor, FragmentSpread fragmentSpread, QueryVisitorEnvironment parent, boolean preOrder) {
108-
if (!conditionalNodes.shouldInclude(this.variables, fragmentSpread.getDirectives())) {
109-
return;
118+
private class NodeVisitorImpl extends NodeVisitorStub {
119+
120+
final FieldVisitor preOrderCallback;
121+
final FieldVisitor postOrderCallback;
122+
123+
NodeVisitorImpl(FieldVisitor preOrderCallback, FieldVisitor postOrderCallback) {
124+
this.preOrderCallback = preOrderCallback;
125+
this.postOrderCallback = postOrderCallback;
110126
}
111-
FragmentDefinition fragmentDefinition = fragmentsByName.get(fragmentSpread.getName());
112127

113-
if (!conditionalNodes.shouldInclude(variables, fragmentDefinition.getDirectives())) {
114-
return;
128+
@Override
129+
public TraversalControl visitInlineFragment(InlineFragment inlineFragment, TraverserContext<Node> context) {
130+
if (context.getVar(LeaveOrEnter.class) == LEAVE) {
131+
return TraversalControl.CONTINUE;
132+
}
133+
if (!conditionalNodes.shouldInclude(variables, inlineFragment.getDirectives()))
134+
return TraversalControl.ABORT;
135+
136+
// inline fragments are allowed not have type conditions, if so the parent type counts
137+
QueryTraversalContext parentEnv = context
138+
.getParentContext()
139+
.getVar(QueryTraversalContext.class);
140+
141+
GraphQLCompositeType fragmentCondition;
142+
if (inlineFragment.getTypeCondition() != null) {
143+
TypeName typeCondition = inlineFragment.getTypeCondition();
144+
fragmentCondition = (GraphQLCompositeType) schema.getType(typeCondition.getName());
145+
} else {
146+
fragmentCondition = parentEnv.getType();
147+
}
148+
// for unions we only have other fragments inside
149+
context.setVar(QueryTraversalContext.class, new QueryTraversalContext(fragmentCondition, parentEnv.getEnvironment()));
150+
return TraversalControl.CONTINUE;
115151
}
116-
GraphQLCompositeType typeCondition = (GraphQLCompositeType) schema.getType(fragmentDefinition.getTypeCondition().getName());
117-
visitImpl(visitor, fragmentDefinition.getSelectionSet(), typeCondition, parent, preOrder);
118-
}
119152

153+
@Override
154+
public TraversalControl visitFragmentSpread(FragmentSpread fragmentSpread, TraverserContext<Node> context) {
155+
if (context.getVar(LeaveOrEnter.class) == LEAVE) {
156+
return TraversalControl.CONTINUE;
157+
}
158+
if (!conditionalNodes.shouldInclude(variables, fragmentSpread.getDirectives()))
159+
return TraversalControl.ABORT;
120160

121-
private void visitInlineFragment(QueryVisitor visitor, InlineFragment inlineFragment, GraphQLCompositeType parentType, QueryVisitorEnvironment parent, boolean preOrder) {
122-
if (!conditionalNodes.shouldInclude(variables, inlineFragment.getDirectives())) {
123-
return;
124-
}
125-
// inline fragments are allowed not have type conditions, if so the parent type counts
126-
GraphQLCompositeType fragmentCondition;
127-
if (inlineFragment.getTypeCondition() != null) {
128-
TypeName typeCondition = inlineFragment.getTypeCondition();
129-
fragmentCondition = (GraphQLCompositeType) schema.getType(typeCondition.getName());
130-
} else {
131-
fragmentCondition = parentType;
132-
}
133-
// for unions we only have other fragments inside
134-
visitImpl(visitor, inlineFragment.getSelectionSet(), fragmentCondition, parent, preOrder);
135-
}
161+
FragmentDefinition fragmentDefinition = fragmentsByName.get(fragmentSpread.getName());
162+
if (!conditionalNodes.shouldInclude(variables, fragmentDefinition.getDirectives()))
163+
return TraversalControl.ABORT;
136164

137-
private void visitField(QueryVisitor visitor, Field field, GraphQLFieldDefinition fieldDefinition, GraphQLCompositeType parentType, QueryVisitorEnvironment parentEnv, boolean preOrder) {
138-
if (!conditionalNodes.shouldInclude(variables, field.getDirectives())) {
139-
return;
140-
}
141-
Map<String, Object> argumentValues = valuesResolver.getArgumentValues(schema.getFieldVisibility(), fieldDefinition.getArguments(), field.getArguments(), variables);
142-
if (preOrder) {
143-
visitor.visitField(new QueryVisitorEnvironment(field, fieldDefinition, parentType, parentEnv, argumentValues));
144-
}
145-
GraphQLUnmodifiedType unmodifiedType = schemaUtil.getUnmodifiedType(fieldDefinition.getType());
146-
if (unmodifiedType instanceof GraphQLCompositeType) {
147-
QueryVisitorEnvironment newParentEnvironment = new QueryVisitorEnvironment(field, fieldDefinition, parentType, parentEnv, argumentValues);
148-
visitImpl(visitor, field.getSelectionSet(), (GraphQLCompositeType) unmodifiedType, newParentEnvironment, preOrder);
149-
}
150-
if (!preOrder) {
151-
visitor.visitField(new QueryVisitorEnvironment(field, fieldDefinition, parentType, parentEnv, argumentValues));
165+
QueryTraversalContext parentEnv = context
166+
.getParentContext()
167+
.getVar(QueryTraversalContext.class);
168+
169+
GraphQLCompositeType typeCondition = (GraphQLCompositeType) schema.getType(fragmentDefinition.getTypeCondition().getName());
170+
171+
context
172+
.setVar(QueryTraversalContext.class, new QueryTraversalContext(typeCondition, parentEnv.getEnvironment()));
173+
return TraversalControl.CONTINUE;
152174
}
153175

154-
}
176+
@Override
177+
public TraversalControl visitField(Field field, TraverserContext<Node> context) {
178+
QueryTraversalContext parentEnv = context
179+
.getParentContext()
180+
.getVar(QueryTraversalContext.class);
181+
182+
GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(schema, parentEnv.getType(), field.getName());
183+
Map<String, Object> argumentValues = valuesResolver.getArgumentValues(schema.getFieldVisibility(), fieldDefinition.getArguments(), field.getArguments(), variables);
184+
QueryVisitorEnvironment environment = new QueryVisitorEnvironment(field, fieldDefinition, parentEnv.getType(), parentEnv.getEnvironment(), argumentValues);
155185

186+
LeaveOrEnter leaveOrEnter = context.getVar(LeaveOrEnter.class);
187+
if (leaveOrEnter == LEAVE) {
188+
postOrderCallback.visitField(environment);
189+
return TraversalControl.CONTINUE;
190+
}
191+
192+
if (!conditionalNodes.shouldInclude(variables, field.getDirectives()))
193+
return TraversalControl.ABORT;
194+
195+
preOrderCallback.visitField(environment);
196+
197+
GraphQLUnmodifiedType unmodifiedType = schemaUtil.getUnmodifiedType(fieldDefinition.getType());
198+
QueryTraversalContext fieldEnv = (unmodifiedType instanceof GraphQLCompositeType)
199+
? new QueryTraversalContext((GraphQLCompositeType) unmodifiedType, environment)
200+
: new QueryTraversalContext(null, environment);// Terminal (scalar) node, EMPTY FRAME
156201

202+
203+
context.setVar(QueryTraversalContext.class, fieldEnv);
204+
return TraversalControl.CONTINUE;
205+
}
206+
207+
}
157208
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package graphql.analysis;
2+
3+
import graphql.Internal;
4+
import graphql.schema.GraphQLCompositeType;
5+
6+
/**
7+
* QueryTraversal helper class that maintains traversal context as
8+
* the query traversal algorithm traverses down the Selection AST
9+
*/
10+
@Internal
11+
class QueryTraversalContext {
12+
13+
private final GraphQLCompositeType type;
14+
private final QueryVisitorEnvironment environment;
15+
16+
QueryTraversalContext(GraphQLCompositeType type, QueryVisitorEnvironment environment) {
17+
this.type = type;
18+
this.environment = environment;
19+
}
20+
21+
public GraphQLCompositeType getType() {
22+
return type;
23+
}
24+
25+
public QueryVisitorEnvironment getEnvironment() {
26+
return environment;
27+
}
28+
}

src/main/java/graphql/language/Argument.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package graphql.language;
22

33

4+
import graphql.util.TraversalControl;
5+
import graphql.util.TraverserContext;
6+
47
import java.util.ArrayList;
58
import java.util.List;
69

@@ -54,4 +57,8 @@ public String toString() {
5457
'}';
5558
}
5659

60+
@Override
61+
public TraversalControl accept(TraverserContext<Node> context, NodeVisitor visitor) {
62+
return visitor.visitArgument(this, context);
63+
}
5764
}

0 commit comments

Comments
 (0)