Skip to content

Commit ad2b7b0

Browse files
committed
More work on @OneOf directive support
1 parent 2eeaa6b commit ad2b7b0

7 files changed

Lines changed: 104 additions & 2 deletions

File tree

src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import static graphql.Directives.DEPRECATED_DIRECTIVE_DEFINITION;
8383
import static graphql.Directives.IncludeDirective;
8484
import static graphql.Directives.NO_LONGER_SUPPORTED;
85+
import static graphql.Directives.ONE_OF_DIRECTIVE_DEFINITION;
8586
import static graphql.Directives.SPECIFIED_BY_DIRECTIVE_DEFINITION;
8687
import static graphql.Directives.SkipDirective;
8788
import static graphql.Directives.SpecifiedByDirective;
@@ -1079,6 +1080,7 @@ void addDirectivesIncludedByDefault(TypeDefinitionRegistry typeRegistry) {
10791080
// we synthesize this into the type registry - no need for them to add it
10801081
typeRegistry.add(DEPRECATED_DIRECTIVE_DEFINITION);
10811082
typeRegistry.add(SPECIFIED_BY_DIRECTIVE_DEFINITION);
1083+
typeRegistry.add(ONE_OF_DIRECTIVE_DEFINITION);
10821084
}
10831085

10841086
private Optional<OperationTypeDefinition> getOperationNamed(String name, Map<String, OperationTypeDefinition> operationTypeDefs) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package graphql.schema.validation;
2+
3+
import graphql.schema.GraphQLInputObjectField;
4+
import graphql.schema.GraphQLInputObjectType;
5+
import graphql.schema.GraphQLSchemaElement;
6+
import graphql.schema.GraphQLTypeUtil;
7+
import graphql.schema.GraphQLTypeVisitorStub;
8+
import graphql.util.TraversalControl;
9+
import graphql.util.TraverserContext;
10+
11+
import static java.lang.String.format;
12+
13+
/*
14+
* Spec: If the Input Object is a OneOf Input Object then:
15+
* The type of the input field must be nullable.
16+
* The input field must not have a default value.
17+
*/
18+
public class OneOfInputObjectRules extends GraphQLTypeVisitorStub {
19+
20+
@Override
21+
public TraversalControl visitGraphQLInputObjectField(GraphQLInputObjectField inputObjectField, TraverserContext<GraphQLSchemaElement> context) {
22+
GraphQLInputObjectType inputObjectType = (GraphQLInputObjectType) context.getParentNode();
23+
if (!inputObjectType.isOneOf()) {
24+
return TraversalControl.CONTINUE;
25+
}
26+
SchemaValidationErrorCollector errorCollector = context.getVarFromParents(SchemaValidationErrorCollector.class);
27+
// error messages take from the reference implementation
28+
if (inputObjectField.hasSetDefaultValue()) {
29+
String message = format("OneOf input field %s.%s cannot have a default value.", inputObjectType.getName(), inputObjectField.getName());
30+
errorCollector.addError(new SchemaValidationError(SchemaValidationErrorType.OneOfDefaultValueOnField, message));
31+
}
32+
33+
if (GraphQLTypeUtil.isNonNull(inputObjectField.getType())) {
34+
String message = format("OneOf input field %s.%s must be nullable.", inputObjectType.getName(), inputObjectField.getName());
35+
errorCollector.addError(new SchemaValidationError(SchemaValidationErrorType.OneOfNonNullableField, message));
36+
}
37+
return TraversalControl.CONTINUE;
38+
}
39+
}

src/main/java/graphql/schema/validation/SchemaValidationErrorType.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ public enum SchemaValidationErrorType implements SchemaValidationErrorClassifica
2020
InvalidAppliedDirective,
2121
OutputTypeUsedInInputTypeContext,
2222
InputTypeUsedInOutputTypeContext,
23+
OneOfDefaultValueOnField,
24+
OneOfNonNullableField
2325
}

src/main/java/graphql/schema/validation/SchemaValidator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public SchemaValidator() {
2525
rules.add(new AppliedDirectivesAreValid());
2626
rules.add(new AppliedDirectiveArgumentsAreValid());
2727
rules.add(new InputAndOutputTypesUsedAppropriately());
28+
rules.add(new OneOfInputObjectRules());
2829
}
2930

3031
public List<GraphQLTypeVisitor> getRules() {

src/test/groovy/graphql/schema/idl/SchemaGeneratorTest.groovy

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,4 +2536,26 @@ class SchemaGeneratorTest extends Specification {
25362536
newSchema.getDirectives().findAll { it.name == "skip" }.size() == 1
25372537
newSchema.getDirectives().findAll { it.name == "include" }.size() == 1
25382538
}
2539+
2540+
def "oneOf directive is available implicitly"() {
2541+
def sdl = '''
2542+
type Query {
2543+
f(arg : OneOfInputType) : String
2544+
}
2545+
2546+
input OneOfInputType @oneOf {
2547+
a : String
2548+
b : String
2549+
}
2550+
'''
2551+
2552+
when:
2553+
def schema = TestUtil.schema(sdl)
2554+
then:
2555+
schema.getDirectives().findAll { it.name == "oneOf" }.size() == 1
2556+
2557+
GraphQLInputObjectType inputObjectType = schema.getTypeAs("OneOfInputType")
2558+
inputObjectType.isOneOf()
2559+
inputObjectType.hasAppliedDirective("oneOf")
2560+
}
25392561
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package graphql.schema.validation
2+
3+
import graphql.TestUtil
4+
import graphql.schema.idl.SchemaGenerator
5+
import graphql.schema.idl.SchemaParser
6+
import spock.lang.Specification
7+
8+
class OneOfInputObjectRulesTest extends Specification {
9+
10+
def "oneOf fields must be the right shape"() {
11+
12+
def sdl = """
13+
type Query {
14+
f(arg : OneOfInputType) : String
15+
}
16+
17+
input OneOfInputType @oneOf {
18+
ok : String
19+
badNonNull : String!
20+
badDefaulted : String = "default"
21+
}
22+
"""
23+
24+
when:
25+
def registry = new SchemaParser().parse(sdl)
26+
new SchemaGenerator().makeExecutableSchema(registry, TestUtil.getMockRuntimeWiring())
27+
28+
then:
29+
def schemaProblem = thrown(InvalidSchemaException)
30+
schemaProblem.errors.size() == 2
31+
schemaProblem.errors[0].description == "OneOf input field OneOfInputType.badNonNull must be nullable."
32+
schemaProblem.errors[0].classification == SchemaValidationErrorType.OneOfNonNullableField
33+
schemaProblem.errors[1].description == "OneOf input field OneOfInputType.badDefaulted cannot have a default value."
34+
schemaProblem.errors[1].classification == SchemaValidationErrorType.OneOfDefaultValueOnField
35+
}
36+
}

src/test/groovy/graphql/schema/validation/SchemaValidatorTest.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ class SchemaValidatorTest extends Specification {
1111
def validator = new SchemaValidator()
1212
def rules = validator.rules
1313
then:
14-
rules.size() == 7
14+
rules.size() == 8
1515
rules[0] instanceof NoUnbrokenInputCycles
1616
rules[1] instanceof TypesImplementInterfaces
1717
rules[2] instanceof TypeAndFieldRule
1818
rules[3] instanceof DefaultValuesAreValid
1919
rules[4] instanceof AppliedDirectivesAreValid
2020
rules[5] instanceof AppliedDirectiveArgumentsAreValid
2121
rules[6] instanceof InputAndOutputTypesUsedAppropriately
22+
rules[7] instanceof OneOfInputObjectRules
2223
}
23-
2424
}

0 commit comments

Comments
 (0)