Skip to content

Commit 444bd54

Browse files
committed
conditional nodes ... not quite there yet
1 parent 02483dc commit 444bd54

File tree

4 files changed

+197
-42
lines changed

4 files changed

+197
-42
lines changed

src/main/java/graphql/execution/conditional/ConditionalNodes.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
import graphql.GraphQLContext;
55
import graphql.Internal;
66
import graphql.execution.CoercedVariables;
7-
import graphql.language.*;
7+
import graphql.language.Argument;
8+
import graphql.language.BooleanValue;
9+
import graphql.language.Directive;
10+
import graphql.language.DirectivesContainer;
11+
import graphql.language.NodeUtil;
12+
import graphql.language.VariableReference;
813
import graphql.schema.GraphQLSchema;
914
import org.jetbrains.annotations.Nullable;
1015

@@ -27,7 +32,7 @@ public Boolean shouldIncludeWithoutVariables(DirectivesContainer<?> element) {
2732
public boolean shouldInclude(DirectivesContainer<?> element,
2833
Map<String, Object> variables,
2934
GraphQLSchema graphQLSchema,
30-
GraphQLContext graphQLContext
35+
@Nullable GraphQLContext graphQLContext
3136
) {
3237
//
3338
// call the base @include / @skip first
@@ -94,6 +99,37 @@ public GraphQLContext getGraphQLContext() {
9499
return getDirectiveResult(variables, directives, IncludeDirective.getName(), true);
95100
}
96101

102+
public boolean containsSkipOrIncludeDirective(DirectivesContainer<?> directivesContainer) {
103+
return NodeUtil.findNodeByName(directivesContainer.getDirectives(), SkipDirective.getName()) != null ||
104+
NodeUtil.findNodeByName(directivesContainer.getDirectives(), IncludeDirective.getName()) != null;
105+
}
106+
107+
108+
public String getSkipVariableName(DirectivesContainer<?> directivesContainer) {
109+
Directive skipDirective = NodeUtil.findNodeByName(directivesContainer.getDirectives(), SkipDirective.getName());
110+
if (skipDirective == null) {
111+
return null;
112+
}
113+
Argument argument = skipDirective.getArgument("if");
114+
if (argument.getValue() instanceof VariableReference) {
115+
return ((VariableReference) argument.getValue()).getName();
116+
}
117+
return null;
118+
}
119+
120+
public String getIncludeVariableName(DirectivesContainer<?> directivesContainer) {
121+
Directive skipDirective = NodeUtil.findNodeByName(directivesContainer.getDirectives(), IncludeDirective.getName());
122+
if (skipDirective == null) {
123+
return null;
124+
}
125+
Argument argument = skipDirective.getArgument("if");
126+
if (argument.getValue() instanceof VariableReference) {
127+
return ((VariableReference) argument.getValue()).getName();
128+
}
129+
return null;
130+
}
131+
132+
97133
private @Nullable Boolean getDirectiveResult(Map<String, Object> variables, List<Directive> directives, String directiveName, boolean defaultValue) {
98134
Directive foundDirective = NodeUtil.findNodeByName(directives, directiveName);
99135
if (foundDirective != null) {
@@ -110,6 +146,9 @@ public GraphQLContext getGraphQLContext() {
110146
return ((BooleanValue) value).isValue();
111147
}
112148
if (value instanceof VariableReference && variables != null) {
149+
if (variables.get(((VariableReference) value).getName()) == null) {
150+
System.out.println("yeahhhhhhhhhhh");
151+
}
113152
return (boolean) variables.get(((VariableReference) value).getName());
114153
}
115154
return null;
Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,39 @@
11
package graphql.normalized.nf;
22

3+
import org.jetbrains.annotations.Nullable;
4+
35
import java.util.List;
6+
import java.util.Map;
47

58
public class NormalizedDocument {
69

7-
private final List<NormalizedOperation> normalizedOperations;
10+
private final List<NormalizedOperationWithAssumedSkipIncludeVariables> normalizedOperations;
811

9-
public NormalizedDocument(List<NormalizedOperation> normalizedOperations) {
12+
public NormalizedDocument(List<NormalizedOperationWithAssumedSkipIncludeVariables> normalizedOperations) {
1013
this.normalizedOperations = normalizedOperations;
1114
}
1215

13-
public List<NormalizedOperation> getNormalizedOperations() {
16+
public List<NormalizedOperationWithAssumedSkipIncludeVariables> getNormalizedOperations() {
1417
return normalizedOperations;
1518
}
19+
20+
public static class NormalizedOperationWithAssumedSkipIncludeVariables {
21+
22+
Map<String, Boolean> assumedSkipIncludeVariables;
23+
NormalizedOperation normalizedOperation;
24+
25+
public NormalizedOperationWithAssumedSkipIncludeVariables(@Nullable Map<String, Boolean> assumedSkipIncludeVariables, NormalizedOperation normalizedOperation) {
26+
this.assumedSkipIncludeVariables = assumedSkipIncludeVariables;
27+
this.normalizedOperation = normalizedOperation;
28+
}
29+
30+
public Map<String, Boolean> getAssumedSkipIncludeVariables() {
31+
return assumedSkipIncludeVariables;
32+
}
33+
34+
public NormalizedOperation getNormalizedOperation() {
35+
return normalizedOperation;
36+
}
37+
}
1638
}
1739

src/main/java/graphql/normalized/nf/NormalizedDocumentFactory.java

Lines changed: 78 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.ArrayList;
4040
import java.util.Collection;
4141
import java.util.LinkedHashMap;
42+
import java.util.LinkedHashSet;
4243
import java.util.List;
4344
import java.util.Locale;
4445
import java.util.Map;
@@ -250,6 +251,10 @@ private static class NormalizedDocumentFactoryImpl {
250251

251252
private final List<NormalizedField> rootEnfs = new ArrayList<>();
252253

254+
private final Set<String> skipIncludeVariableNames = new LinkedHashSet<>();
255+
256+
private Map<String, Boolean> assumedSkipIncludeVariableValues;
257+
253258
private NormalizedDocumentFactoryImpl(
254259
GraphQLSchema graphQLSchema,
255260
Document document,
@@ -265,45 +270,67 @@ private NormalizedDocumentFactoryImpl(
265270
* Creates a new NormalizedDocument for the provided query
266271
*/
267272
private NormalizedDocument createNormalizedQueryImpl() {
268-
List<NormalizedOperation> normalizedOperations = new ArrayList<>();
273+
List<NormalizedDocument.NormalizedOperationWithAssumedSkipIncludeVariables> normalizedOperations = new ArrayList<>();
269274
for (OperationDefinition operationDefinition : document.getDefinitionsOfType(OperationDefinition.class)) {
270-
this.rootEnfs.clear();
271-
this.fieldCount = 0;
272-
this.maxDepthSeen = 0;
273-
this.possibleMergerList.clear();
274-
;
275-
fieldToNormalizedField = ImmutableListMultimap.builder();
276-
normalizedFieldToMergedField = ImmutableMap.builder();
277-
normalizedFieldToQueryDirectives = ImmutableMap.builder();
278-
coordinatesToNormalizedFields = ImmutableListMultimap.builder();
279-
280-
buildNormalizedFieldsRecursively(null, operationDefinition, null, 0);
281-
282-
// TODO: handle possible mergers later
283-
for (PossibleMerger possibleMerger : possibleMergerList) {
284-
List<NormalizedField> childrenWithSameResultKey = possibleMerger.parent.getChildrenWithSameResultKey(possibleMerger.resultKey);
285-
NormalizedFieldsMerger.merge(possibleMerger.parent, childrenWithSameResultKey, graphQLSchema);
286-
}
287275

288-
NormalizedOperation normalizedOperation = new NormalizedOperation(
289-
operationDefinition.getOperation(),
290-
operationDefinition.getName(),
291-
rootEnfs,
292-
fieldToNormalizedField.build(),
293-
normalizedFieldToMergedField.build(),
294-
normalizedFieldToQueryDirectives.build(),
295-
coordinatesToNormalizedFields.build(),
296-
fieldCount,
297-
maxDepthSeen
298-
);
299-
normalizedOperations.add(normalizedOperation);
276+
assumedSkipIncludeVariableValues = null;
277+
skipIncludeVariableNames.clear();
278+
NormalizedOperation normalizedOperation = createNormalizedOperation(operationDefinition);
279+
280+
if (skipIncludeVariableNames.size() == 0) {
281+
normalizedOperations.add(new NormalizedDocument.NormalizedOperationWithAssumedSkipIncludeVariables(null, normalizedOperation));
282+
} else {
283+
int combinations = (int) Math.pow(2, skipIncludeVariableNames.size());
284+
for (int i = 0; i < combinations; i++) {
285+
assumedSkipIncludeVariableValues = new LinkedHashMap<>();
286+
int variableIndex = 0;
287+
for (String variableName : skipIncludeVariableNames) {
288+
assumedSkipIncludeVariableValues.put(variableName, (i & (1 << variableIndex++)) != 0);
289+
}
290+
291+
NormalizedOperation operationWithAssumedVariables = createNormalizedOperation(operationDefinition);
292+
normalizedOperations.add(new NormalizedDocument.NormalizedOperationWithAssumedSkipIncludeVariables(assumedSkipIncludeVariableValues, operationWithAssumedVariables));
293+
}
294+
}
300295
}
301296

302297
return new NormalizedDocument(
303298
normalizedOperations
304299
);
305300
}
306301

302+
private NormalizedOperation createNormalizedOperation(OperationDefinition operationDefinition) {
303+
this.rootEnfs.clear();
304+
this.fieldCount = 0;
305+
this.maxDepthSeen = 0;
306+
this.possibleMergerList.clear();
307+
fieldToNormalizedField = ImmutableListMultimap.builder();
308+
normalizedFieldToMergedField = ImmutableMap.builder();
309+
normalizedFieldToQueryDirectives = ImmutableMap.builder();
310+
coordinatesToNormalizedFields = ImmutableListMultimap.builder();
311+
312+
buildNormalizedFieldsRecursively(null, operationDefinition, null, 0);
313+
314+
for (PossibleMerger possibleMerger : possibleMergerList) {
315+
List<NormalizedField> childrenWithSameResultKey = possibleMerger.parent.getChildrenWithSameResultKey(possibleMerger.resultKey);
316+
NormalizedFieldsMerger.merge(possibleMerger.parent, childrenWithSameResultKey, graphQLSchema);
317+
}
318+
319+
NormalizedOperation normalizedOperation = new NormalizedOperation(
320+
operationDefinition.getOperation(),
321+
operationDefinition.getName(),
322+
rootEnfs,
323+
fieldToNormalizedField.build(),
324+
normalizedFieldToMergedField.build(),
325+
normalizedFieldToQueryDirectives.build(),
326+
coordinatesToNormalizedFields.build(),
327+
fieldCount,
328+
maxDepthSeen
329+
);
330+
return normalizedOperation;
331+
}
332+
333+
307334
private void captureMergedField(NormalizedField enf, MergedField mergedFld) {
308335
// // QueryDirectivesImpl is a lazy object and only computes itself when asked for
309336
// QueryDirectives queryDirectives = new QueryDirectivesImpl(mergedFld, graphQLSchema, coercedVariableValues.toMap(), options.getGraphQLContext(), options.getLocale());
@@ -557,12 +584,27 @@ private void collectField(List<CollectedField> result,
557584
Set<GraphQLObjectType> possibleObjectTypes,
558585
GraphQLCompositeType astTypeCondition
559586
) {
560-
// if (!conditionalNodes.shouldInclude(field,
561-
// this.coercedVariableValues.toMap(),
562-
// this.graphQLSchema,
563-
// this.options.graphQLContext)) {
564-
// return;
565-
// }
587+
Boolean shouldInclude;
588+
if (assumedSkipIncludeVariableValues == null) {
589+
if ((shouldInclude = conditionalNodes.shouldIncludeWithoutVariables(field)) == null) {
590+
591+
String skipVariableName = conditionalNodes.getSkipVariableName(field);
592+
String includeVariableName = conditionalNodes.getIncludeVariableName(field);
593+
if (skipVariableName != null) {
594+
skipIncludeVariableNames.add(skipVariableName);
595+
}
596+
if (includeVariableName != null) {
597+
skipIncludeVariableNames.add(includeVariableName);
598+
}
599+
}
600+
if (shouldInclude != null && !shouldInclude) {
601+
return;
602+
}
603+
} else {
604+
if (!conditionalNodes.shouldInclude(field, (Map) assumedSkipIncludeVariableValues, graphQLSchema, null)) {
605+
return;
606+
}
607+
}
566608
// this means there is actually no possible type for this field, and we are done
567609
if (possibleObjectTypes.isEmpty()) {
568610
return;

src/test/groovy/graphql/normalized/nf/NormalizedDocumentFactoryTest.groovy

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,56 @@ type Dog implements Animal{
116116
]
117117
}
118118

119+
def "document with skip/include with variables"() {
120+
String schema = """
121+
type Query{
122+
foo: Foo
123+
}
124+
type Foo {
125+
bar: Bar
126+
name: String
127+
}
128+
type Bar {
129+
baz: String
130+
}
131+
"""
132+
GraphQLSchema graphQLSchema = TestUtil.schema(schema)
133+
134+
String query = '''
135+
query ($skip: Boolean!, $include: Boolean!) {
136+
foo {
137+
name
138+
bar @skip(if: $skip) {
139+
baz @include(if: $include)
140+
}
141+
}
142+
}
143+
'''
144+
145+
146+
assertValidQuery(graphQLSchema, query, [skip: false, include: true])
147+
148+
Document document = TestUtil.parseQuery(query)
149+
def tree = NormalizedDocumentFactory.createNormalizedDocument(graphQLSchema, document)
150+
def printedTree = printDocumentWithLevelInfo(tree, graphQLSchema)
151+
152+
expect:
153+
printedTree == ['-Query.animal: Animal',
154+
'--[Bird, Cat, Dog].name: String',
155+
'--otherName: [Bird, Cat, Dog].name: String',
156+
'--Cat.friends: [Friend]',
157+
'---Friend.isCatOwner: Boolean',
158+
'---Friend.pets: [Pet]',
159+
'----Dog.name: String',
160+
'--Bird.friends: [Friend]',
161+
'---Friend.isBirdOwner: Boolean',
162+
'---Friend.name: String',
163+
'---Friend.pets: [Pet]',
164+
'----Cat.breed: String'
165+
]
166+
}
167+
168+
119169
private void assertValidQuery(GraphQLSchema graphQLSchema, String query, Map variables = [:]) {
120170
GraphQL graphQL = GraphQL.newGraphQL(graphQLSchema).build()
121171
def ei = ExecutionInput.newExecutionInput(query).variables(variables).build()
@@ -124,7 +174,9 @@ type Dog implements Animal{
124174

125175
static List<String> printDocumentWithLevelInfo(NormalizedDocument normalizedDocument, GraphQLSchema schema) {
126176
def result = []
127-
for (NormalizedOperation normalizedOperation : normalizedDocument.normalizedOperations) {
177+
for (NormalizedDocument.NormalizedOperationWithAssumedSkipIncludeVariables normalizedOperationWithAssumedSkipIncludeVariables : normalizedDocument.normalizedOperations) {
178+
NormalizedOperation normalizedOperation = normalizedOperationWithAssumedSkipIncludeVariables.normalizedOperation;
179+
result << "assumed variables: " + normalizedOperationWithAssumedSkipIncludeVariables.assumedSkipIncludeVariables
128180
Traverser<NormalizedField> traverser = Traverser.depthFirst({ it.getChildren() })
129181
traverser.traverse(normalizedOperation.getTopLevelFields(), new TraverserVisitorStub<NormalizedField>() {
130182
@Override

0 commit comments

Comments
 (0)