Skip to content

Commit 1c70957

Browse files
committed
Added path to validation errors
1 parent 93f7586 commit 1c70957

25 files changed

+173
-79
lines changed

src/main/java/graphql/validation/AbstractRule.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
import graphql.language.Node;
1313
import graphql.language.OperationDefinition;
1414
import graphql.language.SelectionSet;
15+
import graphql.language.SourceLocation;
1516
import graphql.language.TypeName;
1617
import graphql.language.VariableDefinition;
1718
import graphql.language.VariableReference;
1819

20+
import java.util.ArrayList;
1921
import java.util.List;
2022

2123
@Internal
@@ -51,8 +53,16 @@ public void setValidationUtil(ValidationUtil validationUtil) {
5153
this.validationUtil = validationUtil;
5254
}
5355

54-
public void addError(ValidationError error) {
55-
validationErrorCollector.addError(error);
56+
public void addError(ValidationErrorType validationErrorType, List<? extends Node> locations, String description) {
57+
List<SourceLocation> locationList = new ArrayList<>();
58+
for (Node node : locations) {
59+
locationList.add(node.getSourceLocation());
60+
}
61+
validationErrorCollector.addError(new ValidationError(validationErrorType, locationList, description, getPath()));
62+
}
63+
64+
public void addError(ValidationErrorType validationErrorType, SourceLocation location, String description) {
65+
validationErrorCollector.addError(new ValidationError(validationErrorType, location, description, getPath()));
5666
}
5767

5868
public List<ValidationError> getErrors() {
@@ -64,6 +74,10 @@ public ValidationContext getValidationContext() {
6474
return validationContext;
6575
}
6676

77+
protected List<Object> getPath() {
78+
return validationContext.getPath();
79+
}
80+
6781
public void checkArgument(Argument argument) {
6882

6983
}
@@ -120,5 +134,8 @@ public void documentFinished(Document document) {
120134

121135
}
122136

123-
137+
@Override
138+
public String toString() {
139+
return "Rule{" + validationContext + "}";
140+
}
124141
}

src/main/java/graphql/validation/ErrorFactory.java

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/main/java/graphql/validation/TraversalContext.java

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

33

4-
import java.util.ArrayList;
5-
import java.util.List;
6-
74
import graphql.Assert;
85
import graphql.Internal;
96
import graphql.execution.TypeFromAST;
@@ -39,6 +36,9 @@
3936
import graphql.schema.GraphQLUnmodifiedType;
4037
import graphql.schema.SchemaUtil;
4138

39+
import java.util.ArrayList;
40+
import java.util.List;
41+
4242
import static graphql.introspection.Introspection.SchemaMetaFieldDef;
4343
import static graphql.introspection.Introspection.TypeMetaFieldDef;
4444
import static graphql.introspection.Introspection.TypeNameMetaFieldDef;
@@ -50,6 +50,7 @@ public class TraversalContext implements DocumentVisitor {
5050
final List<GraphQLCompositeType> parentTypeStack = new ArrayList<>();
5151
final List<GraphQLInputType> inputTypeStack = new ArrayList<>();
5252
final List<GraphQLFieldDefinition> fieldDefStack = new ArrayList<>();
53+
final List<String> nameStack = new ArrayList<>();
5354
GraphQLDirective directive;
5455
GraphQLArgument argument;
5556

@@ -96,6 +97,7 @@ private void enterImpl(SelectionSet selectionSet) {
9697
}
9798

9899
private void enterImpl(Field field) {
100+
enterName(field.getName());
99101
GraphQLCompositeType parentType = getParentType();
100102
GraphQLFieldDefinition fieldDefinition = null;
101103
if (parentType != null) {
@@ -133,6 +135,7 @@ private void enterImpl(InlineFragment inlineFragment) {
133135
}
134136

135137
private void enterImpl(FragmentDefinition fragmentDefinition) {
138+
enterName(fragmentDefinition.getName());
136139
GraphQLType type = schema.getType(fragmentDefinition.getTypeCondition().getName());
137140
addOutputType((GraphQLOutputType) type);
138141
}
@@ -190,13 +193,15 @@ public void leave(Node node, List<Node> ancestors) {
190193
} else if (node instanceof SelectionSet) {
191194
parentTypeStack.remove(parentTypeStack.size() - 1);
192195
} else if (node instanceof Field) {
196+
leaveName(((Field) node).getName());
193197
fieldDefStack.remove(fieldDefStack.size() - 1);
194198
outputTypeStack.remove(outputTypeStack.size() - 1);
195199
} else if (node instanceof Directive) {
196200
directive = null;
197201
} else if (node instanceof InlineFragment) {
198202
outputTypeStack.remove(outputTypeStack.size() - 1);
199203
} else if (node instanceof FragmentDefinition) {
204+
leaveName(((FragmentDefinition) node).getName());
200205
outputTypeStack.remove(outputTypeStack.size() - 1);
201206
} else if (node instanceof VariableDefinition) {
202207
inputTypeStack.remove(inputTypeStack.size() - 1);
@@ -210,6 +215,21 @@ public void leave(Node node, List<Node> ancestors) {
210215
}
211216
}
212217

218+
private void enterName(String name) {
219+
if (!isEmpty(name)) {
220+
nameStack.add(name);
221+
}
222+
}
223+
224+
private void leaveName(String name) {
225+
if (!isEmpty(name)) {
226+
nameStack.remove(nameStack.size() - 1);
227+
}
228+
}
229+
230+
private boolean isEmpty(String name) {
231+
return name == null || name.isEmpty();
232+
}
213233

214234
private GraphQLNullableType getNullableType(GraphQLType type) {
215235
return (GraphQLNullableType) (type instanceof GraphQLNonNull ? ((GraphQLNonNull) type).getWrappedType() : type);
@@ -256,6 +276,13 @@ public GraphQLFieldDefinition getFieldDef() {
256276
return lastElement(fieldDefStack);
257277
}
258278

279+
public List<Object> getPath() {
280+
if (nameStack.isEmpty()) {
281+
return null;
282+
}
283+
return new ArrayList<>(nameStack);
284+
}
285+
259286
private void addFieldDef(GraphQLFieldDefinition fieldDefinition) {
260287
fieldDefStack.add(fieldDefinition);
261288
}

src/main/java/graphql/validation/ValidationContext.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import graphql.schema.GraphQLSchema;
1515

1616
import java.util.LinkedHashMap;
17+
import java.util.List;
1718
import java.util.Map;
1819

1920
@Internal
@@ -82,4 +83,12 @@ public GraphQLOutputType getOutputType() {
8283
}
8384

8485

86+
public List<Object> getPath() {
87+
return traversalContext.getPath();
88+
}
89+
90+
@Override
91+
public String toString() {
92+
return "ValidationContext{" + getPath() + "}";
93+
}
8594
}

src/main/java/graphql/validation/ValidationError.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import graphql.language.SourceLocation;
88

99
import java.util.ArrayList;
10+
import java.util.Collections;
1011
import java.util.List;
1112

1213
public class ValidationError implements GraphQLError {
@@ -15,25 +16,35 @@ public class ValidationError implements GraphQLError {
1516
private final List<SourceLocation> locations = new ArrayList<>();
1617
private final String description;
1718
private final ValidationErrorType validationErrorType;
19+
private final List<Object> path;
1820

1921
public ValidationError(ValidationErrorType validationErrorType) {
2022
this(validationErrorType, (SourceLocation) null, null);
2123
}
2224

2325
public ValidationError(ValidationErrorType validationErrorType, SourceLocation sourceLocation, String description) {
24-
this.validationErrorType = validationErrorType;
25-
if (sourceLocation != null)
26-
this.locations.add(sourceLocation);
27-
this.description = description;
28-
this.message = mkMessage(validationErrorType, description);
26+
this(validationErrorType, nullOrList(sourceLocation), description, null);
27+
}
28+
29+
public ValidationError(ValidationErrorType validationErrorType, SourceLocation sourceLocation, String description, List<Object> path) {
30+
this(validationErrorType, nullOrList(sourceLocation), description, path);
2931
}
3032

3133
public ValidationError(ValidationErrorType validationErrorType, List<SourceLocation> sourceLocations, String description) {
34+
this(validationErrorType, sourceLocations, description, null);
35+
}
36+
37+
public ValidationError(ValidationErrorType validationErrorType, List<SourceLocation> sourceLocations, String description, List<Object> path) {
3238
this.validationErrorType = validationErrorType;
3339
if (sourceLocations != null)
3440
this.locations.addAll(sourceLocations);
3541
this.description = description;
3642
this.message = mkMessage(validationErrorType, description);
43+
this.path = path;
44+
}
45+
46+
private static List<SourceLocation> nullOrList(SourceLocation sourceLocation) {
47+
return sourceLocation == null ? null : Collections.singletonList(sourceLocation);
3748
}
3849

3950
private String mkMessage(ValidationErrorType validationErrorType, String description) {
@@ -58,6 +69,11 @@ public List<SourceLocation> getLocations() {
5869
return locations;
5970
}
6071

72+
@Override
73+
public List<Object> getPath() {
74+
return path;
75+
}
76+
6177
@Override
6278
public ErrorType getErrorType() {
6379
return ErrorType.ValidationError;
@@ -68,6 +84,7 @@ public ErrorType getErrorType() {
6884
public String toString() {
6985
return "ValidationError{" +
7086
"validationErrorType=" + validationErrorType +
87+
", path=" + path +
7188
", message=" + message +
7289
", locations=" + locations +
7390
", description='" + description + '\'' +

src/main/java/graphql/validation/rules/ArgumentsOfCorrectType.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import graphql.validation.AbstractRule;
77
import graphql.validation.ArgumentValidationUtil;
88
import graphql.validation.ValidationContext;
9-
import graphql.validation.ValidationError;
109
import graphql.validation.ValidationErrorCollector;
1110
import graphql.validation.ValidationErrorType;
1211

@@ -22,7 +21,7 @@ public void checkArgument(Argument argument) {
2221
if (fieldArgument == null) return;
2322
ArgumentValidationUtil validationUtil = new ArgumentValidationUtil(argument);
2423
if (!validationUtil.isValidLiteralValue(argument.getValue(), fieldArgument.getType(), getValidationContext().getSchema())) {
25-
addError(new ValidationError(ValidationErrorType.WrongType, argument.getSourceLocation(), validationUtil.getMessage()));
24+
addError(ValidationErrorType.WrongType, argument.getSourceLocation(), validationUtil.getMessage());
2625
}
2726
}
2827
}

src/main/java/graphql/validation/rules/FieldsOnCorrectType.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import graphql.schema.GraphQLFieldDefinition;
77
import graphql.validation.AbstractRule;
88
import graphql.validation.ValidationContext;
9-
import graphql.validation.ValidationError;
109
import graphql.validation.ValidationErrorCollector;
1110
import graphql.validation.ValidationErrorType;
1211

@@ -26,7 +25,7 @@ public void checkField(Field field) {
2625
GraphQLFieldDefinition fieldDef = getValidationContext().getFieldDef();
2726
if (fieldDef == null) {
2827
String message = String.format("Field '%s' in type '%s' is undefined", field.getName(), parentType.getName());
29-
addError(new ValidationError(ValidationErrorType.FieldUndefined, field.getSourceLocation(), message));
28+
addError(ValidationErrorType.FieldUndefined, field.getSourceLocation(), message);
3029
}
3130

3231
}

src/main/java/graphql/validation/rules/FragmentsOnCompositeType.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import graphql.schema.GraphQLType;
88
import graphql.validation.AbstractRule;
99
import graphql.validation.ValidationContext;
10-
import graphql.validation.ValidationError;
1110
import graphql.validation.ValidationErrorCollector;
1211
import graphql.validation.ValidationErrorType;
1312

@@ -27,7 +26,7 @@ public void checkInlineFragment(InlineFragment inlineFragment) {
2726
if (type == null) return;
2827
if (!(type instanceof GraphQLCompositeType)) {
2928
String message = "Inline fragment type condition is invalid, must be on Object/Interface/Union";
30-
addError(new ValidationError(ValidationErrorType.InlineFragmentTypeConditionInvalid, inlineFragment.getSourceLocation(), message));
29+
addError(ValidationErrorType.InlineFragmentTypeConditionInvalid, inlineFragment.getSourceLocation(), message);
3130
}
3231
}
3332

@@ -37,7 +36,7 @@ public void checkFragmentDefinition(FragmentDefinition fragmentDefinition) {
3736
if (type == null) return;
3837
if (!(type instanceof GraphQLCompositeType)) {
3938
String message = "Fragment type condition is invalid, must be on Object/Interface/Union";
40-
addError(new ValidationError(ValidationErrorType.InlineFragmentTypeConditionInvalid, fragmentDefinition.getSourceLocation(), message));
39+
addError(ValidationErrorType.InlineFragmentTypeConditionInvalid, fragmentDefinition.getSourceLocation(), message);
4140
}
4241
}
4342
}

src/main/java/graphql/validation/rules/KnownArgumentNames.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import graphql.schema.GraphQLFieldDefinition;
77
import graphql.validation.AbstractRule;
88
import graphql.validation.ValidationContext;
9-
import graphql.validation.ValidationError;
109
import graphql.validation.ValidationErrorCollector;
1110
import graphql.validation.ValidationErrorType;
1211

@@ -25,7 +24,7 @@ public void checkArgument(Argument argument) {
2524
GraphQLArgument directiveArgument = directiveDef.getArgument(argument.getName());
2625
if (directiveArgument == null) {
2726
String message = String.format("Unknown directive argument %s", argument.getName());
28-
addError(new ValidationError(ValidationErrorType.UnknownDirective, argument.getSourceLocation(), message));
27+
addError(ValidationErrorType.UnknownDirective, argument.getSourceLocation(), message);
2928
}
3029

3130
return;
@@ -36,7 +35,7 @@ public void checkArgument(Argument argument) {
3635
GraphQLArgument fieldArgument = fieldDef.getArgument(argument.getName());
3736
if (fieldArgument == null) {
3837
String message = String.format("Unknown field argument %s", argument.getName());
39-
addError(new ValidationError(ValidationErrorType.UnknownArgument, argument.getSourceLocation(), message));
38+
addError(ValidationErrorType.UnknownArgument, argument.getSourceLocation(), message);
4039
}
4140
}
4241
}

src/main/java/graphql/validation/rules/KnownDirectives.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import graphql.schema.GraphQLDirective;
1414
import graphql.validation.AbstractRule;
1515
import graphql.validation.ValidationContext;
16-
import graphql.validation.ValidationError;
1716
import graphql.validation.ValidationErrorCollector;
1817
import graphql.validation.ValidationErrorType;
1918

@@ -31,14 +30,14 @@ public void checkDirective(Directive directive, List<Node> ancestors) {
3130
GraphQLDirective graphQLDirective = getValidationContext().getSchema().getDirective(directive.getName());
3231
if (graphQLDirective == null) {
3332
String message = String.format("Unknown directive %s", directive.getName());
34-
addError(new ValidationError(ValidationErrorType.UnknownDirective, directive.getSourceLocation(), message));
33+
addError(ValidationErrorType.UnknownDirective, directive.getSourceLocation(), message);
3534
return;
3635
}
3736

3837
Node ancestor = ancestors.get(ancestors.size() - 1);
3938
if (hasInvalidLocation(graphQLDirective, ancestor)) {
4039
String message = String.format("Directive %s not allowed here", directive.getName());
41-
addError(new ValidationError(ValidationErrorType.MisplacedDirective, directive.getSourceLocation(), message));
40+
addError(ValidationErrorType.MisplacedDirective, directive.getSourceLocation(), message);
4241
}
4342
}
4443

0 commit comments

Comments
 (0)