Skip to content

Commit 0e75b75

Browse files
committed
Upped to graphql-java 13.0 and more work in applies to!
1 parent 0a8faf7 commit 0e75b75

13 files changed

+335
-57
lines changed

TODO.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,25 @@ the name Constraint especially for the @directives rules
3737
Does this makes sense? What can we do to make this more powerful?
3838

3939
Hibernate Validator uses a form of this to do message interpolation
40-
40+
41+
Could this be the inter argument validation
42+
43+
```graphql
44+
field( first : Int, after : ID, last : Int, before : ID) : ObjType
45+
@Expression( expr="""
46+
${ (! empty first && empty last) || (!empty last && empty first) }
47+
""")
48+
```
49+
50+
is this powerful or not?
51+
52+
# Allow scripting
53+
54+
The Hibernate project allows scripting
55+
56+
```graphql
57+
@Script(lang : "javascript", script : "_this.startDate.before(_this.endDate)")
58+
59+
```
60+
61+
Could we do something similar

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ repositories {
3838

3939

4040
dependencies {
41-
compile "com.graphql-java:graphql-java:12.0"
41+
compile "com.graphql-java:graphql-java:13.0"
4242
compile "javax.validation:validation-api:2.0.1.Final"
4343
//compile "org.hibernate.validator:hibernate-validator:6.0.17.Final"
4444
compile "org.hibernate.validator:hibernate-validator:6.1.0.Alpha5"

src/main/java/graphql/validation/directives/AbstractDirectiveValidationRule.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import graphql.schema.GraphQLInputObjectType;
1212
import graphql.schema.GraphQLInputType;
1313
import graphql.schema.GraphQLScalarType;
14+
import graphql.schema.GraphQLTypeUtil;
1415
import graphql.validation.rules.ValidationRuleEnvironment;
1516
import graphql.validation.util.Util;
1617

@@ -39,10 +40,32 @@ public String getName() {
3940
}
4041

4142
@Override
42-
public boolean appliesToType(GraphQLArgument argument, GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
43-
return appliesToType(Util.unwrapNonNull(argument.getType()));
43+
public boolean appliesTo(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
44+
return false;
4445
}
4546

47+
@Override
48+
public boolean appliesTo(GraphQLArgument argument, GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
49+
50+
return DirectivesAndTypeWalker.isSuitable(argument, (inputType, directive) -> {
51+
boolean hasNamedDirective = directive.getName().equals(this.getName());
52+
if (hasNamedDirective) {
53+
inputType = Util.unwrapNonNull(inputType);
54+
boolean appliesToType = appliesToType(inputType);
55+
if (appliesToType) {
56+
return true;
57+
}
58+
// if they have a @Directive on there BUT it cant handle that type
59+
// then is a really bad situation
60+
String argType = GraphQLTypeUtil.simplePrint(inputType);
61+
Assert.assertShouldNeverHappen("The directive rule '%s' cannot be placed on elements of type '%s'", this.getName(), argType);
62+
}
63+
return false;
64+
});
65+
}
66+
67+
abstract protected boolean appliesToType(GraphQLInputType inputType);
68+
4669
/**
4770
* Returns true of the input type is one of the specified scalar types, regardless of non null ness
4871
*

src/main/java/graphql/validation/directives/DirectiveValidationRule.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
import graphql.GraphQLError;
44
import graphql.PublicSpi;
5-
import graphql.schema.GraphQLInputType;
65
import graphql.validation.rules.ValidationRule;
76
import graphql.validation.rules.ValidationRuleEnvironment;
87

98
import java.util.Collections;
109
import java.util.List;
1110

11+
/**
12+
* A DirectiveValidationRule is a specialised form of validation rule
13+
* that assumes it is backed by a SDL directive on fields, field arguments
14+
* or input type fields.
15+
*/
1216
@PublicSpi
1317
public interface DirectiveValidationRule extends ValidationRule {
1418

@@ -20,8 +24,6 @@ public interface DirectiveValidationRule extends ValidationRule {
2024

2125
List<String> getApplicableTypeNames();
2226

23-
boolean appliesToType(GraphQLInputType inputType);
24-
2527
@Override
2628
default List<GraphQLError> runValidation(ValidationRuleEnvironment ruleEnvironment) {
2729
return Collections.emptyList();

src/main/java/graphql/validation/directives/DirectiveValidationRules.java

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,25 @@ public static Builder newDirectiveValidationRules() {
9191
}
9292

9393
@Override
94-
public boolean appliesToType(GraphQLArgument argument, GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
95-
return argument.getDirectives().stream().anyMatch(d -> oneOfOurs(d, argument));
96-
}
97-
98-
private boolean oneOfOurs(GraphQLDirective directive, GraphQLArgument argument) {
99-
DirectiveValidationRule rule = directiveRules.get(directive.getName());
100-
if (rule != null) {
101-
return rule.appliesToType(argument.getType());
94+
public boolean appliesTo(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
95+
for (DirectiveValidationRule directiveRule : directiveRules.values()) {
96+
boolean applies = directiveRule.appliesTo(fieldDefinition, fieldsContainer);
97+
if (applies) {
98+
return true;
99+
}
102100
}
103101
return false;
104102
}
105103

106-
private void assertDirectiveOnTheRightType(DirectiveValidationRule directiveRule, GraphQLInputType inputType) {
107-
boolean applicable = directiveRule.appliesToType(inputType);
108-
if (!applicable) {
109-
String argType = GraphQLTypeUtil.simplePrint(inputType);
110-
Assert.assertShouldNeverHappen("The directive %s cannot be placed on arguments of type %s", directiveRule.getName(), argType);
104+
@Override
105+
public boolean appliesTo(GraphQLArgument argument, GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
106+
for (DirectiveValidationRule directiveRule : directiveRules.values()) {
107+
boolean applies = directiveRule.appliesTo(argument, fieldDefinition, fieldsContainer);
108+
if (applies) {
109+
return true;
110+
}
111111
}
112+
return false;
112113
}
113114

114115
@SuppressWarnings("unchecked")
@@ -118,9 +119,6 @@ public List<GraphQLError> runValidation(ValidationRuleEnvironment ruleEnvironmen
118119
GraphQLArgument argument = ruleEnvironment.getArgument();
119120
Object validatedValue = ruleEnvironment.getValidatedValue();
120121
List<GraphQLDirective> directives = argument.getDirectives();
121-
if (directives.isEmpty()) {
122-
return Collections.emptyList();
123-
}
124122

125123
//
126124
// all the directives validation code does NOT care for NULL ness since the graphql engine covers that.
@@ -140,10 +138,6 @@ private List<GraphQLError> runValidationImpl(ValidationRuleEnvironment ruleEnvir
140138
if (validationRule == null) {
141139
continue;
142140
}
143-
//
144-
// double check that directive is in fact on an element it can handle
145-
//
146-
assertDirectiveOnTheRightType(validationRule, inputType);
147141

148142
ruleEnvironment = ruleEnvironment.transform(b -> b.context(GraphQLDirective.class, directive));
149143
//
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package graphql.validation.directives;
2+
3+
import graphql.schema.GraphQLArgument;
4+
import graphql.schema.GraphQLDirective;
5+
import graphql.schema.GraphQLDirectiveContainer;
6+
import graphql.schema.GraphQLInputObjectField;
7+
import graphql.schema.GraphQLInputObjectType;
8+
import graphql.schema.GraphQLInputType;
9+
import graphql.schema.GraphQLList;
10+
import graphql.validation.util.Util;
11+
12+
import java.util.List;
13+
import java.util.function.BiFunction;
14+
15+
public class DirectivesAndTypeWalker {
16+
17+
public static boolean isSuitable(GraphQLArgument argument, BiFunction<GraphQLInputType, GraphQLDirective, Boolean> isSutiable) {
18+
GraphQLInputType inputType = argument.getType();
19+
List<GraphQLDirective> directives = argument.getDirectives();
20+
return walkInputType(inputType, directives, isSutiable);
21+
}
22+
23+
private static boolean walkInputType(GraphQLInputType inputType, List<GraphQLDirective> directives, BiFunction<GraphQLInputType, GraphQLDirective, Boolean> isSuitable) {
24+
GraphQLInputType unwrappedInputType = Util.unwrapNonNull(inputType);
25+
for (GraphQLDirective directive : directives) {
26+
if (isSuitable.apply(unwrappedInputType, directive)) {
27+
return true;
28+
}
29+
}
30+
if (unwrappedInputType instanceof GraphQLInputObjectType) {
31+
GraphQLInputObjectType inputObjType = (GraphQLInputObjectType) unwrappedInputType;
32+
for (GraphQLInputObjectField inputField : inputObjType.getFieldDefinitions()) {
33+
inputType = inputField.getType();
34+
directives = inputField.getDirectives();
35+
36+
if (walkInputType(inputType, directives, isSuitable)) {
37+
return true;
38+
}
39+
}
40+
}
41+
if (unwrappedInputType instanceof GraphQLList) {
42+
GraphQLInputType innerListType = Util.unwrapOneAndAllNonNull(unwrappedInputType);
43+
if (innerListType instanceof GraphQLDirectiveContainer) {
44+
directives = ((GraphQLDirectiveContainer) innerListType).getDirectives();
45+
if (walkInputType(innerListType, directives, isSuitable)) {
46+
return true;
47+
}
48+
}
49+
}
50+
return false;
51+
}
52+
53+
}

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
import java.util.List;
1212
import java.util.stream.Collectors;
1313

14-
import static graphql.validation.rules.ArgumentCoordinates.newArgumentCoordinates;
15-
1614
/**
1715
* {@link PossibleValidationRules} is a simple holder of possible rules
1816
* and you can then pass it field and arguments and narrow down the list of actual rules
@@ -31,7 +29,7 @@ private PossibleValidationRules(Builder builder) {
3129
this.onValidationErrorStrategy = builder.onValidationErrorStrategy;
3230
}
3331

34-
public static Builder newValidationRuleCandidates() {
32+
public static Builder newPossibleRules() {
3533
return new Builder();
3634
}
3735

@@ -47,21 +45,32 @@ public OnValidationErrorStrategy getOnValidationErrorStrategy() {
4745
return onValidationErrorStrategy;
4846
}
4947

50-
public ValidationRules getRulesFor(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
48+
public ValidationRules buildRulesFor(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
5149
ValidationRules.Builder rulesBuilder = ValidationRules.newValidationRules();
50+
51+
ValidationCoordinates fieldCoordinates = ValidationCoordinates.newCoordinates(fieldsContainer, fieldDefinition);
52+
List<ValidationRule> fieldRules = getRulesFor(fieldDefinition, fieldsContainer);
53+
rulesBuilder.addRules(fieldCoordinates, fieldRules);
54+
5255
for (GraphQLArgument fieldArg : fieldDefinition.getArguments()) {
53-
ArgumentCoordinates argumentCoordinates = newArgumentCoordinates(fieldsContainer, fieldDefinition, fieldArg);
56+
ValidationCoordinates validationCoordinates = ValidationCoordinates.newCoordinates(fieldsContainer, fieldDefinition, fieldArg);
5457

5558
List<ValidationRule> rules = getRulesFor(fieldArg, fieldDefinition, fieldsContainer);
56-
rulesBuilder.addRules(argumentCoordinates, rules);
59+
rulesBuilder.addRules(validationCoordinates, rules);
5760
}
5861

5962
return rulesBuilder.build();
6063
}
6164

6265
public List<ValidationRule> getRulesFor(GraphQLArgument fieldArg, GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
6366
return rules.stream()
64-
.filter(rule -> rule.appliesToType(fieldArg, fieldDefinition, fieldsContainer))
67+
.filter(rule -> rule.appliesTo(fieldArg, fieldDefinition, fieldsContainer))
68+
.collect(Collectors.toList());
69+
}
70+
71+
public List<ValidationRule> getRulesFor(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {
72+
return rules.stream()
73+
.filter(rule -> rule.appliesTo(fieldDefinition, fieldsContainer))
6574
.collect(Collectors.toList());
6675
}
6776

src/main/java/graphql/validation/rules/ArgumentCoordinates.java renamed to src/main/java/graphql/validation/rules/ValidationCoordinates.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99
import java.util.StringJoiner;
1010

1111
@PublicApi
12-
public class ArgumentCoordinates {
12+
public class ValidationCoordinates {
1313

1414
private final String containerType;
1515
private final String fieldName;
1616
private final String argName;
1717

18-
public ArgumentCoordinates(String containerType, String fieldName, String argName) {
18+
public ValidationCoordinates(String containerType, String fieldName, String argName) {
1919
this.containerType = Objects.requireNonNull(containerType);
2020
this.fieldName = Objects.requireNonNull(fieldName);
21-
this.argName = Objects.requireNonNull(argName);
21+
this.argName = argName;
2222
}
2323

2424
public String getContainerType() {
@@ -42,7 +42,7 @@ public boolean equals(Object o) {
4242
return false;
4343
}
4444

45-
ArgumentCoordinates that = (ArgumentCoordinates) o;
45+
ValidationCoordinates that = (ValidationCoordinates) o;
4646

4747
return Objects.equals(this.getContainerType(), that.getContainerType()) && Objects.equals(this.getFieldName(), that.getFieldName()) && Objects.equals(this.getArgName(), that.getArgName());
4848
}
@@ -62,19 +62,35 @@ public String toString() {
6262
}
6363

6464

65-
public static ArgumentCoordinates newArgumentCoordinates(GraphQLFieldsContainer fieldsContainer, GraphQLFieldDefinition fieldDefinition, GraphQLArgument fieldArg) {
66-
return new ArgumentCoordinates(
65+
public static ValidationCoordinates newCoordinates(GraphQLFieldsContainer fieldsContainer, GraphQLFieldDefinition fieldDefinition, GraphQLArgument fieldArg) {
66+
return new ValidationCoordinates(
6767
fieldsContainer.getName(),
6868
fieldDefinition.getName(),
6969
fieldArg.getName()
7070
);
7171
}
7272

73-
public static ArgumentCoordinates newArgumentCoordinates(String fieldsContainer, String fieldDefinition, String fieldArg) {
74-
return new ArgumentCoordinates(
73+
public static ValidationCoordinates newCoordinates(GraphQLFieldsContainer fieldsContainer, GraphQLFieldDefinition fieldDefinition) {
74+
return new ValidationCoordinates(
75+
fieldsContainer.getName(),
76+
fieldDefinition.getName(),
77+
null
78+
);
79+
}
80+
81+
public static ValidationCoordinates newCoordinates(String fieldsContainer, String fieldDefinition, String fieldArg) {
82+
return new ValidationCoordinates(
7583
fieldsContainer,
7684
fieldDefinition,
7785
fieldArg
7886
);
7987
}
88+
89+
public static ValidationCoordinates newCoordinates(String fieldsContainer, String fieldDefinition) {
90+
return new ValidationCoordinates(
91+
fieldsContainer,
92+
fieldDefinition,
93+
null
94+
);
95+
}
8096
}

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,33 @@
1515
@PublicSpi
1616
public interface ValidationRule {
1717

18-
boolean appliesToType(GraphQLArgument argument, GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer);
18+
/**
19+
* This is called to work out if this rule applies to a specified field
20+
*
21+
* @param fieldDefinition the field to check
22+
* @param fieldsContainer the field container
23+
*
24+
* @return true if this rule applies to the field
25+
*/
26+
boolean appliesTo(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer);
1927

28+
/**
29+
* This is called to work out if this rule applies to the argument of a specified field
30+
*
31+
* @param argument the argument to check
32+
* @param fieldDefinition the field to check
33+
* @param fieldsContainer the field container
34+
*
35+
* @return true if this rule applies to the argument of the field field
36+
*/
37+
boolean appliesTo(GraphQLArgument argument, GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer);
38+
39+
/**
40+
* This is called to runs the rule
41+
*
42+
* @param ruleEnvironment the rule environment
43+
*
44+
* @return a non null list of errors where emptyList() means its valid
45+
*/
2046
List<GraphQLError> runValidation(ValidationRuleEnvironment ruleEnvironment);
2147
}

0 commit comments

Comments
 (0)