Skip to content

Commit 7b823cc

Browse files
committed
More work on @OneOf directive support - added literal validation
1 parent ad2b7b0 commit 7b823cc

6 files changed

Lines changed: 99 additions & 4 deletions

File tree

src/main/java/graphql/Directives.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public class Directives {
7272
ONE_OF_DIRECTIVE_DEFINITION = DirectiveDefinition.newDirectiveDefinition()
7373
.name(ONE_OF)
7474
.directiveLocation(newDirectiveLocation().name(INPUT_OBJECT.name()).build())
75-
.description(createDescription("This directive is used to indicate an Input Object is a OneOf Input Object."))
75+
.description(createDescription("Indicates an Input Object is a OneOf Input Object."))
7676
.build();
7777
}
7878

@@ -130,7 +130,7 @@ public class Directives {
130130

131131
public static final GraphQLDirective OneOfDirective = GraphQLDirective.newDirective()
132132
.name(ONE_OF)
133-
.description("This directive is used to indicate an Input Object is a OneOf Input Object.")
133+
.description("Indicates an Input Object is a OneOf Input Object.")
134134
.validLocations(INPUT_OBJECT)
135135
.definition(ONE_OF_DIRECTIVE_DEFINITION)
136136
.build();

src/main/java/graphql/validation/ArgumentValidationUtil.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ protected void handleFieldNotValidError(Value<?> value, GraphQLType type, int in
9191
argumentNames.add(0, String.format("[%s]", index));
9292
}
9393

94+
@Override
95+
protected void handleOneOfNotOneFieldError(GraphQLInputObjectType type) {
96+
errMsgKey = "ArgumentValidationUtil.handleOneOfNotOneFieldError";
97+
arguments.add(type.getName());
98+
}
99+
100+
@Override
101+
protected void handleOneOfValueIsNullError(GraphQLInputObjectType type, ObjectField objectField) {
102+
errMsgKey = "ArgumentValidationUtil.handleOneOfValueIsNullError";
103+
arguments.add(type.getName() + "." + objectField.getName());
104+
}
105+
94106
public I18nMsg getMsgAndArgs() {
95107
StringBuilder argument = new StringBuilder(argumentName);
96108
for (String name : argumentNames) {

src/main/java/graphql/validation/ValidationUtil.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ protected void handleFieldNotValidError(ObjectField objectField, GraphQLInputObj
7777
protected void handleFieldNotValidError(Value<?> value, GraphQLType type, int index) {
7878
}
7979

80+
protected void handleOneOfNotOneFieldError(GraphQLInputObjectType type) {
81+
}
82+
83+
protected void handleOneOfValueIsNullError(GraphQLInputObjectType type, ObjectField objectField) {
84+
}
85+
8086
public boolean isValidLiteralValue(Value<?> value, GraphQLType type, GraphQLSchema schema, GraphQLContext graphQLContext, Locale locale) {
8187
if (value == null || value instanceof NullValue) {
8288
boolean valid = !(isNonNull(type));
@@ -154,11 +160,22 @@ boolean isValidLiteralValue(Value<?> value, GraphQLInputObjectType type, GraphQL
154160
handleFieldNotValidError(objectField, type);
155161
return false;
156162
}
157-
163+
}
164+
if (type.isOneOf()) {
165+
if (objectValue.getObjectFields().size() != 1) {
166+
handleOneOfNotOneFieldError(type);
167+
return false;
168+
}
169+
ObjectField objectField = objectValue.getObjectFields().get(0);
170+
if (objectField.getValue() instanceof NullValue) {
171+
handleOneOfValueIsNullError(type, objectField);
172+
return false;
173+
}
158174
}
159175
return true;
160176
}
161177

178+
162179
private Set<String> getMissingFields(GraphQLInputObjectType type, Map<String, ObjectField> objectFieldMap, GraphqlFieldVisibility fieldVisibility) {
163180
return fieldVisibility.getFieldDefinitions(type).stream()
164181
.filter(field -> isNonNull(field.getType()))

src/main/resources/i18n/Validation.properties

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,9 @@ ArgumentValidationUtil.handleNotObjectError=Validation error ({0}) : argument ''
9898
ArgumentValidationUtil.handleMissingFieldsError=Validation error ({0}) : argument ''{1}'' with value ''{2}'' is missing required fields ''{3}''
9999
# suppress inspection "UnusedProperty"
100100
ArgumentValidationUtil.handleExtraFieldError=Validation error ({0}) : argument ''{1}'' with value ''{2}'' contains a field not in ''{3}'': ''{4}''
101-
#
101+
# suppress inspection "UnusedProperty"
102+
# suppress inspection "UnusedMessageFormatParameter"
103+
ArgumentValidationUtil.handleOneOfNotOneFieldError=Validation error ({0}) : Exactly one key must be specified for OneOf type ''{3}''.
104+
# suppress inspection "UnusedProperty"
105+
# suppress inspection "UnusedMessageFormatParameter"
106+
ArgumentValidationUtil.handleOneOfValueIsNullError=Validation error ({0}) : OneOf type field ''{3}'' must be non-null.

src/test/groovy/graphql/validation/SpecValidationSchema.java

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

3+
import graphql.Directives;
34
import graphql.Scalars;
45
import graphql.schema.GraphQLArgument;
56
import graphql.schema.GraphQLCodeRegistry;
@@ -184,13 +185,28 @@ public class SpecValidationSchema {
184185
.validLocations(FIELD, FRAGMENT_SPREAD, FRAGMENT_DEFINITION, INLINE_FRAGMENT, QUERY)
185186
.build();
186187

188+
public static final GraphQLInputObjectType oneOfInputType = GraphQLInputObjectType.newInputObject()
189+
.name("oneOfInputType")
190+
.withAppliedDirective(Directives.OneOfDirective.toAppliedDirective())
191+
.field(GraphQLInputObjectField.newInputObjectField()
192+
.name("a")
193+
.type(GraphQLString))
194+
.field(GraphQLInputObjectField.newInputObjectField()
195+
.name("b")
196+
.type(GraphQLString))
197+
.build();
198+
199+
187200
public static final GraphQLObjectType queryRoot = GraphQLObjectType.newObject()
188201
.name("QueryRoot")
189202
.field(newFieldDefinition().name("dog").type(dog)
190203
.argument(newArgument().name("arg1").type(GraphQLString).build())
191204
.withDirective(dogDirective)
192205
)
193206
.field(newFieldDefinition().name("pet").type(pet))
207+
.field(newFieldDefinition().name("oneOfField").type(GraphQLString)
208+
.argument(newArgument().name("oneOfArg").type(oneOfInputType).build())
209+
)
194210
.build();
195211

196212
public static final GraphQLObjectType subscriptionRoot = GraphQLObjectType.newObject()

src/test/groovy/graphql/validation/rules/ArgumentsOfCorrectTypeTest.groovy

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,51 @@ class ArgumentsOfCorrectTypeTest extends Specification {
335335
validationErrors.get(0).message == "Validation error (WrongType@[dog/doesKnowCommand]) : argument 'dogCommand' with value 'EnumValue{name='PRETTY'}' is not a valid 'DogCommand' - Literal value not in allowable values for enum 'DogCommand' - 'EnumValue{name='PRETTY'}'"
336336
}
337337

338+
def "invalid oneOf input because of two keys"() {
339+
def query = """
340+
query oneOF {
341+
oneOfField(oneOfArg : { a : "x", b : "y" })
342+
}
343+
"""
344+
when:
345+
def validationErrors = validate(query)
346+
347+
then:
348+
!validationErrors.empty
349+
validationErrors.size() == 1
350+
validationErrors.get(0).getValidationErrorType() == ValidationErrorType.WrongType
351+
validationErrors.get(0).message == "Validation error (WrongType@[oneOfField]) : Exactly one key must be specified for OneOf type 'oneOfInputType'."
352+
}
353+
354+
def "invalid oneOf input because of null key"() {
355+
def query = """
356+
query oneOF {
357+
oneOfField(oneOfArg : { a : null })
358+
}
359+
"""
360+
when:
361+
def validationErrors = validate(query)
362+
363+
then:
364+
!validationErrors.empty
365+
validationErrors.size() == 1
366+
validationErrors.get(0).getValidationErrorType() == ValidationErrorType.WrongType
367+
validationErrors.get(0).message == "Validation error (WrongType@[oneOfField]) : OneOf type field 'oneOfInputType.a' must be non-null."
368+
}
369+
370+
def "valid oneOf input"() {
371+
def query = """
372+
query oneOF {
373+
oneOfField(oneOfArg : { a : "x" })
374+
}
375+
"""
376+
when:
377+
def validationErrors = validate(query)
378+
379+
then:
380+
validationErrors.empty
381+
}
382+
338383
static List<ValidationError> validate(String query) {
339384
def document = new Parser().parseDocument(query)
340385
return new Validator().validateDocument(SpecValidationSchema.specValidationSchema, document, Locale.ENGLISH)

0 commit comments

Comments
 (0)