Skip to content

Commit 694857f

Browse files
committed
Moved to new Expression constraint and now has context on what we are validating
1 parent a276925 commit 694857f

16 files changed

+215
-121
lines changed

src/main/java/graphql/validation/constraints/DirectiveConstraints.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import graphql.schema.GraphQLArgument;
55
import graphql.schema.GraphQLFieldDefinition;
66
import graphql.schema.GraphQLFieldsContainer;
7-
import graphql.validation.constraints.standard.ArgumentsConstraint;
7+
import graphql.validation.constraints.standard.ExpressionConstraint;
88
import graphql.validation.constraints.standard.AssertFalseConstraint;
99
import graphql.validation.constraints.standard.AssertTrueConstraint;
1010
import graphql.validation.constraints.standard.DecimalMaxConstraint;
@@ -44,7 +44,7 @@ public class DirectiveConstraints {
4444
* These are the standard directive rules that come with the system
4545
*/
4646
public final static List<DirectiveConstraint> STANDARD_CONSTRAINTS = Arrays.asList(
47-
new ArgumentsConstraint(),
47+
new ExpressionConstraint(),
4848
new AssertFalseConstraint(),
4949
new AssertTrueConstraint(),
5050
new DecimalMaxConstraint(),

src/main/java/graphql/validation/constraints/standard/ArgumentsConstraint.java renamed to src/main/java/graphql/validation/constraints/standard/ExpressionConstraint.java

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
import java.util.Collections;
1414
import java.util.List;
1515
import java.util.Map;
16+
import java.util.function.Supplier;
1617

17-
public class ArgumentsConstraint extends AbstractDirectiveConstraint {
18+
public class ExpressionConstraint extends AbstractDirectiveConstraint {
1819

19-
public ArgumentsConstraint() {
20-
super("Arguments");
20+
public ExpressionConstraint() {
21+
super("Expression");
2122
}
2223

2324

@@ -29,19 +30,19 @@ public Documentation getDocumentation() {
2930
.description("The provided expression must evaluate to true.")
3031

3132
.example("drivers( first : Int, after : String!, last : Int, before : String) \n" +
32-
" : DriverConnection @Arguments(expression : \"${args.containsOneOf('first','last') }\"")
33+
" : DriverConnection @Expression(value : \"${args.containsOneOf('first','last') }\"")
3334

34-
.applicableTypeNames("Output Fields")
35+
.applicableTypeNames("All Types and Scalars")
3536

36-
.directiveSDL("directive @Arguments(expression : String!, message : String = \"%s\") " +
37-
"on FIELD_DEFINITION",
37+
.directiveSDL("directive @Expression(value : String!, message : String = \"%s\") " +
38+
"on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION",
3839
getMessageTemplate())
3940
.build();
4041
}
4142

4243
@Override
4344
public boolean appliesToType(GraphQLInputType inputType) {
44-
return false;
45+
return true;
4546
}
4647

4748
@Override
@@ -53,29 +54,38 @@ public boolean appliesTo(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsCo
5354
protected List<GraphQLError> runConstraint(ValidationEnvironment validationEnvironment) {
5455
GraphQLFieldDefinition fieldDefinition = validationEnvironment.getFieldDefinition();
5556
GraphQLDirective directive = validationEnvironment.getContextObject(GraphQLDirective.class);
56-
String expression = curlyBraces(getStrArg(directive, "expression"));
57+
String expression = helpWithCurlyBraces(getStrArg(directive, "value"));
58+
59+
Object argument = validationEnvironment.getArgument();
60+
Object validatedValue = validationEnvironment.getValidatedValue();
61+
Map<String, Object> argumentValues = validationEnvironment.getArgumentValues();
5762

5863
Map<String, Object> variables = mkMap(
59-
"fieldDefinition", fieldDefinition,
60-
"args", validationEnvironment.getArgumentValues()
64+
"validatedValue", validatedValue,
65+
66+
"gqlField", fieldDefinition,
67+
"gqlArgument", argument,
68+
69+
"args", argumentValues, // short hand
70+
"arguments", argumentValues
6171
);
6272
ELSupport elSupport = new ELSupport(validationEnvironment.getLocale());
6373
boolean isOK = elSupport.evaluateBoolean(expression, variables);
6474

6575
if (!isOK) {
66-
return mkError(validationEnvironment, directive, mkMessageParams(null, validationEnvironment,
67-
"expression", expression));
76+
return mkError(validationEnvironment, directive, mkMessageParams(validatedValue, validationEnvironment,
77+
"value", expression));
6878

6979
}
7080
return Collections.emptyList();
7181
}
7282

73-
private String curlyBraces(String expression) {
83+
private String helpWithCurlyBraces(String expression) {
7484
expression = expression.trim();
7585
if (!expression.startsWith("${") && !expression.startsWith("#{")) {
7686
expression = "${" + expression;
7787
}
78-
if (!expression.startsWith("}")) {
88+
if (!expression.endsWith("}")) {
7989
expression = expression + "}";
8090
}
8191
return expression;

src/main/java/graphql/validation/constraints/standard/NotEmptyRule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public boolean appliesToType(GraphQLInputType inputType) {
4242
@Override
4343
protected List<GraphQLError> runConstraint(ValidationEnvironment validationEnvironment) {
4444
Object validatedValue = validationEnvironment.getValidatedValue();
45-
GraphQLInputType argumentType = validationEnvironment.getFieldOrArgumentType();
45+
GraphQLInputType argumentType = validationEnvironment.getValidatedType();
4646

4747
GraphQLDirective directive = validationEnvironment.getContextObject(GraphQLDirective.class);
4848
int size = getStringOrObjectOrMapLength(argumentType, validatedValue);

src/main/java/graphql/validation/constraints/standard/SizeConstraint.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public boolean appliesToType(GraphQLInputType inputType) {
4545
@Override
4646
protected List<GraphQLError> runConstraint(ValidationEnvironment validationEnvironment) {
4747
Object validatedValue = validationEnvironment.getValidatedValue();
48-
GraphQLInputType argType = validationEnvironment.getFieldOrArgumentType();
48+
GraphQLInputType argType = validationEnvironment.getValidatedType();
4949

5050
GraphQLDirective directive = validationEnvironment.getContextObject(GraphQLDirective.class);
5151
int min = getIntArg(directive, "min");

src/main/java/graphql/validation/el/BetterMapELResolver.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,17 @@ public Object invoke(ELContext context, Object base, Object method, Class<?>[] p
3737
}
3838

3939
if (base instanceof Map) {
40-
context.setPropertyResolved(true);
4140
Map map = (Map) base;
4241
if ("containsOneOf" .equals(method)) {
42+
context.setPropertyResolved(true);
4343
return containsOneOf(map, Arrays.asList(params));
4444
}
4545
if ("containsAllOf" .equals(method)) {
46+
context.setPropertyResolved(true);
4647
return containsAllOf(map, Arrays.asList(params));
4748
}
4849
}
49-
return null;
50+
// delegate back to underlying Map resolver
51+
return super.invoke(context, base, method, paramTypes, params);
5052
}
51-
5253
}

src/main/java/graphql/validation/el/ELSupport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ private <T> T evaluateImpl(String expression, Map<String, Object> variables, Cla
7373
bindVariable(context, entry.getKey(), entry.getValue());
7474
}
7575
ValueExpression result = expressionFactory.createValueExpression(context, expression, expectedResultClass);
76-
return (T) result.getValue(context);
76+
Object value = result.getValue(context);
77+
return (T) value;
7778
}
7879

7980

src/main/java/graphql/validation/interpolation/ResourceBundleMessageInterpolator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public class ResourceBundleMessageInterpolator implements MessageInterpolator {
6868
*/
6969
@SuppressWarnings("unused")
7070
protected ErrorClassification buildErrorClassification(String messageTemplate, Map<String, Object> messageParams, ValidationEnvironment validationEnvironment) {
71-
ExecutionPath fieldOrArgumentPath = validationEnvironment.getFieldOrArgumentPath();
71+
ExecutionPath fieldOrArgumentPath = validationEnvironment.getValidatedPath();
7272
GraphQLDirective directive = validationEnvironment.getContextObject(GraphQLDirective.class);
7373
return new ValidationErrorType(fieldOrArgumentPath, directive);
7474
}
@@ -192,7 +192,7 @@ public ValidationErrorType(ExecutionPath fieldOrArgumentPath, GraphQLDirective d
192192
public Object toSpecification(GraphQLError error) {
193193
Map<String, Object> map = new LinkedHashMap<>();
194194
map.put("type", "ExtendedValidationError");
195-
map.put("fieldOrArgumentPath", fieldOrArgumentPath);
195+
map.put("validatedPath", fieldOrArgumentPath);
196196
if (directive != null) {
197197
map.put("constraint", "@" + directive.getName());
198198
}

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

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,52 @@
2222
@PublicApi
2323
public class ValidationEnvironment {
2424

25+
/**
26+
* The type of element being validated
27+
*/
28+
public enum ValidatedElement {
29+
/**
30+
* A output field is being validated
31+
*/
32+
FIELD,
33+
/**
34+
* An argument on a graphql output field is being validated
35+
*/
36+
ARGUMENT,
37+
/**
38+
* A input type field is being validated
39+
*/
40+
INPUT_OBJECT_FIELD
41+
}
42+
2543
private final GraphQLFieldsContainer fieldsContainer;
2644
private final GraphQLFieldDefinition fieldDefinition;
2745
private final GraphQLArgument argument;
28-
private final GraphQLInputType fieldOrArgumentType;
29-
private final Object validatedValue;
30-
private final ExecutionPath fieldOrArgumentPath;
3146
private final ExecutionPath executionPath;
47+
private final ExecutionPath validatedPath;
3248
private final SourceLocation location;
3349
private final MessageInterpolator interpolator;
3450
private final Map<Class, Object> contextMap;
3551
private final Locale locale;
3652
private final Map<String, Object> argumentValues;
53+
private final Object validatedValue;
54+
private final GraphQLInputType validatedType;
55+
private final ValidatedElement validatedElement;
3756

3857
private ValidationEnvironment(Builder builder) {
3958
this.argument = builder.argument;
4059
this.argumentValues = Collections.unmodifiableMap(builder.argumentValues);
4160
this.contextMap = Collections.unmodifiableMap(builder.contextMap);
42-
this.executionPath = builder.executionPath;
4361
this.fieldDefinition = builder.fieldDefinition;
44-
this.fieldOrArgumentPath = builder.fieldOrArgumentPath;
45-
this.fieldOrArgumentType = builder.fieldOrArgumentType;
62+
this.executionPath = builder.executionPath;
63+
this.validatedPath = builder.validatedPath;
64+
this.validatedType = builder.validatedType;
4665
this.fieldsContainer = builder.fieldsContainer;
4766
this.interpolator = builder.interpolator;
4867
this.locale = builder.locale;
4968
this.location = builder.location;
5069
this.validatedValue = builder.validatedValue;
70+
this.validatedElement = builder.validatedElement;
5171
}
5272

5373
public static Builder newValidationEnvironment() {
@@ -71,20 +91,20 @@ public GraphQLArgument getArgument() {
7191
return argument;
7292
}
7393

74-
public ExecutionPath getExecutionPath() {
75-
return executionPath;
76-
}
77-
7894
public SourceLocation getLocation() {
7995
return location;
8096
}
8197

82-
public ExecutionPath getFieldOrArgumentPath() {
83-
return fieldOrArgumentPath;
98+
public ExecutionPath getValidatedPath() {
99+
return validatedPath;
84100
}
85101

86-
public GraphQLInputType getFieldOrArgumentType() {
87-
return fieldOrArgumentType;
102+
public ExecutionPath getExecutionPath() {
103+
return executionPath;
104+
}
105+
106+
public GraphQLInputType getValidatedType() {
107+
return validatedType;
88108
}
89109

90110
public Object getValidatedValue() {
@@ -103,6 +123,10 @@ public Locale getLocale() {
103123
return locale;
104124
}
105125

126+
public ValidatedElement getValidatedElement() {
127+
return validatedElement;
128+
}
129+
106130
public ValidationEnvironment transform(Consumer<Builder> builderConsumer) {
107131
Builder builder = newValidationEnvironment().validationEnvironment(this);
108132
builderConsumer.accept(builder);
@@ -113,38 +137,47 @@ public static class Builder {
113137
private final Map<Class, Object> contextMap = new HashMap<>();
114138
private GraphQLArgument argument;
115139
private Map<String, Object> argumentValues = new HashMap<>();
116-
private ExecutionPath executionPath;
117140
private GraphQLFieldDefinition fieldDefinition;
118-
private ExecutionPath fieldOrArgumentPath = ExecutionPath.rootPath();
119-
private GraphQLInputType fieldOrArgumentType;
141+
private ExecutionPath validatedPath = ExecutionPath.rootPath();
142+
private ExecutionPath executionPath;
120143
private GraphQLFieldsContainer fieldsContainer;
121144
private MessageInterpolator interpolator;
122145
private Locale locale;
123146
private SourceLocation location;
124147
private Object validatedValue;
148+
private GraphQLInputType validatedType;
149+
private ValidatedElement validatedElement;
125150

126151
public Builder validationEnvironment(ValidationEnvironment validationEnvironment) {
127152
this.argument = validationEnvironment.argument;
128153
this.argumentValues = validationEnvironment.argumentValues;
129154
this.contextMap.putAll(validationEnvironment.contextMap);
130-
this.executionPath = validationEnvironment.executionPath;
131155
this.fieldDefinition = validationEnvironment.fieldDefinition;
132-
this.fieldOrArgumentPath = validationEnvironment.fieldOrArgumentPath;
133-
this.fieldOrArgumentType = validationEnvironment.fieldOrArgumentType;
156+
this.executionPath = validationEnvironment.executionPath;
157+
this.validatedPath = validationEnvironment.validatedPath;
158+
this.validatedType = validationEnvironment.validatedType;
134159
this.fieldsContainer = validationEnvironment.fieldsContainer;
135160
this.interpolator = validationEnvironment.interpolator;
136161
this.locale = validationEnvironment.locale;
137162
this.location = validationEnvironment.location;
138163
this.validatedValue = validationEnvironment.validatedValue;
164+
this.validatedElement = validationEnvironment.validatedElement;
139165
return this;
140166
}
141167

142168
public Builder dataFetchingEnvironment(DataFetchingEnvironment dataFetchingEnvironment) {
143169
fieldsContainer(dataFetchingEnvironment.getExecutionStepInfo().getFieldContainer());
144170
fieldDefinition(dataFetchingEnvironment.getFieldDefinition());
145171
executionPath(dataFetchingEnvironment.getExecutionStepInfo().getPath());
172+
validatedPath(dataFetchingEnvironment.getExecutionStepInfo().getPath());
146173
location(dataFetchingEnvironment.getField().getSourceLocation());
147174
argumentValues(dataFetchingEnvironment.getArguments());
175+
validatedElement(ValidatedElement.FIELD);
176+
return this;
177+
}
178+
179+
public Builder argument(GraphQLArgument argument) {
180+
this.argument = argument;
148181
return this;
149182
}
150183

@@ -158,30 +191,23 @@ public Builder fieldsContainer(GraphQLFieldsContainer fieldsContainer) {
158191
return this;
159192
}
160193

161-
public Builder fieldDefinition(GraphQLFieldDefinition fieldDefinition) {
162-
this.fieldDefinition = fieldDefinition;
163-
return this;
164-
}
165-
166194
public Builder executionPath(ExecutionPath executionPath) {
167195
this.executionPath = executionPath;
168196
return this;
169197
}
170198

171-
public Builder argument(GraphQLArgument argument) {
172-
this.argument = argument;
173-
fieldOrArgumentType(argument.getType());
174-
fieldOrArgumentPath(ExecutionPath.rootPath().segment(argument.getName()));
199+
public Builder fieldDefinition(GraphQLFieldDefinition fieldDefinition) {
200+
this.fieldDefinition = fieldDefinition;
175201
return this;
176202
}
177203

178-
public Builder fieldOrArgumentPath(ExecutionPath fieldOrArgumentPath) {
179-
this.fieldOrArgumentPath = fieldOrArgumentPath;
204+
public Builder validatedElement(ValidatedElement validatedElement) {
205+
this.validatedElement = validatedElement;
180206
return this;
181207
}
182208

183-
public Builder fieldOrArgumentType(GraphQLInputType fieldOrArgumentType) {
184-
this.fieldOrArgumentType = fieldOrArgumentType;
209+
public Builder validatedType(GraphQLInputType validatedType) {
210+
this.validatedType = validatedType;
185211
return this;
186212
}
187213

@@ -190,6 +216,11 @@ public Builder validatedValue(Object validatedValue) {
190216
return this;
191217
}
192218

219+
public Builder validatedPath(ExecutionPath validatedPath) {
220+
this.validatedPath = validatedPath;
221+
return this;
222+
}
223+
193224
public Builder argumentValues(Map<String, Object> argumentValues) {
194225
this.argumentValues = argumentValues;
195226
return this;

0 commit comments

Comments
 (0)