Skip to content

Commit 6676984

Browse files
committed
improve query traversal:
- make parent type more clear: output type vs unmodified type - indicate if typename introspection field or not - every fields container is a also a composite type and every composite type is also an output type
1 parent 8d02fc7 commit 6676984

13 files changed

Lines changed: 199 additions & 60 deletions

src/main/java/graphql/analysis/MaxQueryComplexityInstrumentation.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ public InstrumentationContext<List<ValidationError>> beginValidation(Instrumenta
5959
@Override
6060
public void visitField(QueryVisitorFieldEnvironment env) {
6161
int childsComplexity = 0;
62-
QueryVisitorFieldEnvironment thisNodeAsParent = new QueryVisitorFieldEnvironmentImpl(env.getField(), env.getFieldDefinition(), env.getParentType(), env.getParentEnvironment(), env.getArguments(), env.getSelectionSetContainer());
63-
if (valuesByParent.containsKey(thisNodeAsParent)) {
64-
childsComplexity = valuesByParent.get(thisNodeAsParent).stream().mapToInt(Integer::intValue).sum();
62+
if (valuesByParent.containsKey(env)) {
63+
childsComplexity = valuesByParent.get(env).stream().mapToInt(Integer::intValue).sum();
6564
}
6665
int value = calculateComplexity(env, childsComplexity);
6766
valuesByParent.putIfAbsent(env.getParentEnvironment(), new ArrayList<>());
@@ -101,16 +100,16 @@ private int calculateComplexity(QueryVisitorFieldEnvironment QueryVisitorFieldEn
101100
return fieldComplexityCalculator.calculate(fieldComplexityEnvironment, childsComplexity);
102101
}
103102

104-
private FieldComplexityEnvironment convertEnv(QueryVisitorFieldEnvironment QueryVisitorFieldEnvironment) {
103+
private FieldComplexityEnvironment convertEnv(QueryVisitorFieldEnvironment queryVisitorFieldEnvironment) {
105104
FieldComplexityEnvironment parentEnv = null;
106-
if (QueryVisitorFieldEnvironment.getParentEnvironment() != null) {
107-
parentEnv = convertEnv(QueryVisitorFieldEnvironment.getParentEnvironment());
105+
if (queryVisitorFieldEnvironment.getParentEnvironment() != null) {
106+
parentEnv = convertEnv(queryVisitorFieldEnvironment.getParentEnvironment());
108107
}
109108
return new FieldComplexityEnvironment(
110-
QueryVisitorFieldEnvironment.getField(),
111-
QueryVisitorFieldEnvironment.getFieldDefinition(),
112-
QueryVisitorFieldEnvironment.getParentType(),
113-
QueryVisitorFieldEnvironment.getArguments(),
109+
queryVisitorFieldEnvironment.getField(),
110+
queryVisitorFieldEnvironment.getFieldDefinition(),
111+
queryVisitorFieldEnvironment.getUnmodifiedParentType(),
112+
queryVisitorFieldEnvironment.getArguments(),
114113
parentEnv
115114
);
116115
}

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import graphql.language.TypeName;
2020
import graphql.schema.GraphQLCompositeType;
2121
import graphql.schema.GraphQLFieldDefinition;
22+
import graphql.schema.GraphQLFieldsContainer;
2223
import graphql.schema.GraphQLObjectType;
2324
import graphql.schema.GraphQLSchema;
2425
import graphql.schema.GraphQLUnmodifiedType;
@@ -167,7 +168,7 @@ private List<Node> childrenOf(Node selection) {
167168

168169
private void visitImpl(QueryVisitor visitFieldCallback, boolean preOrder) {
169170
Map<Class<?>, Object> rootVars = new LinkedHashMap<>();
170-
rootVars.put(QueryTraversalContext.class, new QueryTraversalContext(rootParentType, null, null));
171+
rootVars.put(QueryTraversalContext.class, new QueryTraversalContext(rootParentType, rootParentType, null, null));
171172

172173
QueryVisitor noOp = new QueryVisitorStub();
173174
QueryVisitor preOrderCallback = preOrder ? visitFieldCallback : noOp;
@@ -211,10 +212,10 @@ public TraversalControl visitInlineFragment(InlineFragment inlineFragment, Trave
211212
TypeName typeCondition = inlineFragment.getTypeCondition();
212213
fragmentCondition = (GraphQLCompositeType) schema.getType(typeCondition.getName());
213214
} else {
214-
fragmentCondition = parentEnv.getType();
215+
fragmentCondition = parentEnv.getRawType();
215216
}
216217
// for unions we only have other fragments inside
217-
context.setVar(QueryTraversalContext.class, new QueryTraversalContext(fragmentCondition, parentEnv.getEnvironment(), inlineFragment));
218+
context.setVar(QueryTraversalContext.class, new QueryTraversalContext(fragmentCondition, fragmentCondition, parentEnv.getEnvironment(), inlineFragment));
218219
return TraversalControl.CONTINUE;
219220
}
220221

@@ -242,7 +243,7 @@ public TraversalControl visitFragmentSpread(FragmentSpread fragmentSpread, Trave
242243
GraphQLCompositeType typeCondition = (GraphQLCompositeType) schema.getType(fragmentDefinition.getTypeCondition().getName());
243244

244245
context
245-
.setVar(QueryTraversalContext.class, new QueryTraversalContext(typeCondition, parentEnv.getEnvironment(), fragmentDefinition));
246+
.setVar(QueryTraversalContext.class, new QueryTraversalContext(typeCondition, typeCondition, parentEnv.getEnvironment(), fragmentDefinition));
246247
return TraversalControl.CONTINUE;
247248
}
248249

@@ -252,9 +253,18 @@ public TraversalControl visitField(Field field, TraverserContext<Node> context)
252253
.getParentContext()
253254
.getVar(QueryTraversalContext.class);
254255

255-
GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(schema, parentEnv.getType(), field.getName());
256+
GraphQLFieldDefinition fieldDefinition = Introspection.getFieldDef(schema, parentEnv.getRawType(), field.getName());
257+
boolean isTypeNameIntrospectionField = fieldDefinition == Introspection.TypeNameMetaFieldDef;
258+
GraphQLFieldsContainer fieldsContainer = !isTypeNameIntrospectionField ? (GraphQLFieldsContainer) unwrapAll(parentEnv.getOutputType()) : null;
256259
Map<String, Object> argumentValues = valuesResolver.getArgumentValues(schema.getFieldVisibility(), fieldDefinition.getArguments(), field.getArguments(), variables);
257-
QueryVisitorFieldEnvironment environment = new QueryVisitorFieldEnvironmentImpl(field, fieldDefinition, parentEnv.getType(), parentEnv.getEnvironment(), argumentValues, parentEnv.getSelectionSetContainer());
260+
QueryVisitorFieldEnvironment environment = new QueryVisitorFieldEnvironmentImpl(isTypeNameIntrospectionField,
261+
field,
262+
fieldDefinition,
263+
parentEnv.getOutputType(),
264+
fieldsContainer,
265+
parentEnv.getEnvironment(),
266+
argumentValues,
267+
parentEnv.getSelectionSetContainer());
258268

259269
LeaveOrEnter leaveOrEnter = context.getVar(LeaveOrEnter.class);
260270
if (leaveOrEnter == LEAVE) {
@@ -269,8 +279,8 @@ public TraversalControl visitField(Field field, TraverserContext<Node> context)
269279

270280
GraphQLUnmodifiedType unmodifiedType = unwrapAll(fieldDefinition.getType());
271281
QueryTraversalContext fieldEnv = (unmodifiedType instanceof GraphQLCompositeType)
272-
? new QueryTraversalContext((GraphQLCompositeType) unmodifiedType, environment, field)
273-
: new QueryTraversalContext(null, environment, field);// Terminal (scalar) node, EMPTY FRAME
282+
? new QueryTraversalContext(fieldDefinition.getType(), (GraphQLCompositeType) unmodifiedType, environment, field)
283+
: new QueryTraversalContext(null, null, environment, field);// Terminal (scalar) node, EMPTY FRAME
274284

275285

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

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,37 @@
33
import graphql.Internal;
44
import graphql.language.SelectionSetContainer;
55
import graphql.schema.GraphQLCompositeType;
6+
import graphql.schema.GraphQLOutputType;
67

78
/**
89
* QueryTraversal helper class that maintains traversal context as
910
* the query traversal algorithm traverses down the Selection AST
1011
*/
1112
@Internal
1213
class QueryTraversalContext {
13-
14-
private final GraphQLCompositeType type;
14+
15+
private final GraphQLOutputType outputType;
16+
private final GraphQLCompositeType rawType;
1517
private final QueryVisitorFieldEnvironment environment;
1618
private final SelectionSetContainer selectionSetContainer;
1719

18-
QueryTraversalContext(GraphQLCompositeType type, QueryVisitorFieldEnvironment environment, SelectionSetContainer selectionSetContainer) {
19-
this.type = type;
20+
QueryTraversalContext(GraphQLOutputType outputType,
21+
GraphQLCompositeType rawType
22+
,
23+
QueryVisitorFieldEnvironment environment,
24+
SelectionSetContainer selectionSetContainer) {
25+
this.outputType = outputType;
26+
this.rawType = rawType;
2027
this.environment = environment;
2128
this.selectionSetContainer = selectionSetContainer;
2229
}
2330

24-
public GraphQLCompositeType getType() {
25-
return type;
31+
public GraphQLOutputType getOutputType() {
32+
return outputType;
33+
}
34+
35+
public GraphQLCompositeType getRawType() {
36+
return rawType;
2637
}
2738

2839
public QueryVisitorFieldEnvironment getEnvironment() {

src/main/java/graphql/analysis/QueryVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
@PublicApi
1111
public interface QueryVisitor {
1212

13-
void visitField(QueryVisitorFieldEnvironment QueryVisitorFieldEnvironment);
13+
void visitField(QueryVisitorFieldEnvironment queryVisitorFieldEnvironment);
1414

1515
void visitInlineFragment(QueryVisitorInlineFragmentEnvironment queryVisitorInlineFragmentEnvironment);
1616

src/main/java/graphql/analysis/QueryVisitorFieldEnvironment.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,32 @@
33
import graphql.PublicApi;
44
import graphql.language.Field;
55
import graphql.language.SelectionSetContainer;
6-
import graphql.schema.GraphQLCompositeType;
76
import graphql.schema.GraphQLFieldDefinition;
7+
import graphql.schema.GraphQLFieldsContainer;
8+
import graphql.schema.GraphQLOutputType;
89

910
import java.util.Map;
1011

1112
@PublicApi
1213
public interface QueryVisitorFieldEnvironment {
1314

15+
/**
16+
* @return true if the current field is __typename
17+
*/
18+
boolean isTypeNameIntrospectionField();
19+
1420
Field getField();
1521

1622
GraphQLFieldDefinition getFieldDefinition();
1723

18-
GraphQLCompositeType getParentType();
24+
GraphQLOutputType getParentType();
25+
26+
/**
27+
* @return the unmodified fields container fot the current type
28+
*
29+
* @throws IllegalStateException if the current field is __typename see {@link #isTypeNameIntrospectionField()}
30+
*/
31+
GraphQLFieldsContainer getUnmodifiedParentType();
1932

2033
QueryVisitorFieldEnvironment getParentEnvironment();
2134

src/main/java/graphql/analysis/QueryVisitorFieldEnvironmentImpl.java

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,38 @@
33
import graphql.Internal;
44
import graphql.language.Field;
55
import graphql.language.SelectionSetContainer;
6-
import graphql.schema.GraphQLCompositeType;
76
import graphql.schema.GraphQLFieldDefinition;
7+
import graphql.schema.GraphQLFieldsContainer;
8+
import graphql.schema.GraphQLOutputType;
89

910
import java.util.Map;
11+
import java.util.Objects;
1012

1113
@Internal
1214
public class QueryVisitorFieldEnvironmentImpl implements QueryVisitorFieldEnvironment {
15+
16+
private final boolean typeNameIntrospectionField;
1317
private final Field field;
1418
private final GraphQLFieldDefinition fieldDefinition;
15-
private final GraphQLCompositeType parentType;
19+
private final GraphQLOutputType parentType;
20+
private final GraphQLFieldsContainer unmodifiedParentType;
1621
private final Map<String, Object> arguments;
1722
private final QueryVisitorFieldEnvironment parentEnvironment;
1823
private final SelectionSetContainer selectionSetContainer;
1924

20-
public QueryVisitorFieldEnvironmentImpl(Field field,
25+
public QueryVisitorFieldEnvironmentImpl(boolean typeNameIntrospectionField,
26+
Field field,
2127
GraphQLFieldDefinition fieldDefinition,
22-
GraphQLCompositeType parentType,
28+
GraphQLOutputType parentType,
29+
GraphQLFieldsContainer unmodifiedParentType,
2330
QueryVisitorFieldEnvironment parentEnvironment,
2431
Map<String, Object> arguments,
2532
SelectionSetContainer selectionSetContainer) {
33+
this.typeNameIntrospectionField = typeNameIntrospectionField;
2634
this.field = field;
2735
this.fieldDefinition = fieldDefinition;
2836
this.parentType = parentType;
37+
this.unmodifiedParentType = unmodifiedParentType;
2938
this.parentEnvironment = parentEnvironment;
3039
this.arguments = arguments;
3140
this.selectionSetContainer = selectionSetContainer;
@@ -42,7 +51,7 @@ public GraphQLFieldDefinition getFieldDefinition() {
4251
}
4352

4453
@Override
45-
public GraphQLCompositeType getParentType() {
54+
public GraphQLOutputType getParentType() {
4655
return parentType;
4756
}
4857

@@ -61,30 +70,38 @@ public SelectionSetContainer getSelectionSetContainer() {
6170
return selectionSetContainer;
6271
}
6372

73+
@Override
74+
public GraphQLFieldsContainer getUnmodifiedParentType() {
75+
if (isTypeNameIntrospectionField()) {
76+
throw new IllegalStateException("introspection field __typename doesn't have a fields container");
77+
}
78+
return unmodifiedParentType;
79+
}
80+
81+
@Override
82+
public boolean isTypeNameIntrospectionField() {
83+
return typeNameIntrospectionField;
84+
}
85+
6486
@Override
6587
public boolean equals(Object o) {
6688
if (this == o) return true;
6789
if (o == null || getClass() != o.getClass()) return false;
68-
6990
QueryVisitorFieldEnvironmentImpl that = (QueryVisitorFieldEnvironmentImpl) o;
70-
71-
if (field != null ? !field.equals(that.field) : that.field != null) return false;
72-
if (fieldDefinition != null ? !fieldDefinition.equals(that.fieldDefinition) : that.fieldDefinition != null)
73-
return false;
74-
if (parentType != null ? !parentType.equals(that.parentType) : that.parentType != null) return false;
75-
if (parentEnvironment != null ? !parentEnvironment.equals(that.parentEnvironment) : that.parentEnvironment != null)
76-
return false;
77-
return arguments != null ? arguments.equals(that.arguments) : that.arguments == null;
91+
return typeNameIntrospectionField == that.typeNameIntrospectionField &&
92+
Objects.equals(field, that.field) &&
93+
Objects.equals(fieldDefinition, that.fieldDefinition) &&
94+
Objects.equals(parentType, that.parentType) &&
95+
Objects.equals(unmodifiedParentType, that.unmodifiedParentType) &&
96+
Objects.equals(arguments, that.arguments) &&
97+
Objects.equals(parentEnvironment, that.parentEnvironment) &&
98+
Objects.equals(selectionSetContainer, that.selectionSetContainer);
7899
}
79100

80101
@Override
81102
public int hashCode() {
82-
int result = field != null ? field.hashCode() : 0;
83-
result = 31 * result + (fieldDefinition != null ? fieldDefinition.hashCode() : 0);
84-
result = 31 * result + (parentType != null ? parentType.hashCode() : 0);
85-
result = 31 * result + (parentEnvironment != null ? parentEnvironment.hashCode() : 0);
86-
result = 31 * result + (arguments != null ? arguments.hashCode() : 0);
87-
return result;
103+
104+
return Objects.hash(typeNameIntrospectionField, field, fieldDefinition, parentType, unmodifiedParentType, arguments, parentEnvironment, selectionSetContainer);
88105
}
89106

90107
@Override

src/main/java/graphql/analysis/QueryVisitorStub.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class QueryVisitorStub implements QueryVisitor {
77

88

99
@Override
10-
public void visitField(QueryVisitorFieldEnvironment QueryVisitorFieldEnvironment) {
10+
public void visitField(QueryVisitorFieldEnvironment queryVisitorFieldEnvironment) {
1111

1212
}
1313

src/main/java/graphql/execution/instrumentation/fieldvalidation/FieldValidationSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public GraphQLFieldDefinition getFieldDefinition() {
106106

107107
@Override
108108
public GraphQLCompositeType getParentType() {
109-
return traversalEnv.getParentType();
109+
return traversalEnv.getUnmodifiedParentType();
110110
}
111111

112112
@Override

src/main/java/graphql/introspection/Introspection.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44
import graphql.Assert;
5+
import graphql.PublicApi;
56
import graphql.language.AstPrinter;
67
import graphql.language.AstValueHelper;
78
import graphql.schema.DataFetcher;
@@ -40,6 +41,7 @@
4041
import static graphql.schema.GraphQLObjectType.newObject;
4142
import static graphql.schema.GraphQLTypeReference.typeRef;
4243

44+
@PublicApi
4345
public class Introspection {
4446

4547
public enum TypeKind {
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package graphql.schema;
22

33

4-
public interface GraphQLCompositeType extends GraphQLType {
4+
import graphql.PublicApi;
5+
6+
@PublicApi
7+
public interface GraphQLCompositeType extends GraphQLOutputType {
58
}

0 commit comments

Comments
 (0)