Skip to content

Commit 2eeaa6b

Browse files
committed
Started a branch for the oneOf directive support
1 parent 7f229de commit 2eeaa6b

File tree

7 files changed

+126
-2
lines changed

7 files changed

+126
-2
lines changed

src/main/java/graphql/Directives.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static graphql.introspection.Introspection.DirectiveLocation.FRAGMENT_SPREAD;
1616
import static graphql.introspection.Introspection.DirectiveLocation.INLINE_FRAGMENT;
1717
import static graphql.introspection.Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION;
18+
import static graphql.introspection.Introspection.DirectiveLocation.INPUT_OBJECT;
1819
import static graphql.introspection.Introspection.DirectiveLocation.SCALAR;
1920
import static graphql.language.DirectiveLocation.newDirectiveLocation;
2021
import static graphql.language.InputValueDefinition.newInputValueDefinition;
@@ -31,10 +32,12 @@ public class Directives {
3132

3233
private static final String SPECIFIED_BY = "specifiedBy";
3334
private static final String DEPRECATED = "deprecated";
35+
private static final String ONE_OF = "oneOf";
3436

3537
public static final String NO_LONGER_SUPPORTED = "No longer supported";
3638
public static final DirectiveDefinition DEPRECATED_DIRECTIVE_DEFINITION;
3739
public static final DirectiveDefinition SPECIFIED_BY_DIRECTIVE_DEFINITION;
40+
public static final DirectiveDefinition ONE_OF_DIRECTIVE_DEFINITION;
3841

3942

4043
static {
@@ -65,6 +68,12 @@ public class Directives {
6568
.type(newNonNullType(newTypeName().name("String").build()).build())
6669
.build())
6770
.build();
71+
72+
ONE_OF_DIRECTIVE_DEFINITION = DirectiveDefinition.newDirectiveDefinition()
73+
.name(ONE_OF)
74+
.directiveLocation(newDirectiveLocation().name(INPUT_OBJECT.name()).build())
75+
.description(createDescription("This directive is used to indicate an Input Object is a OneOf Input Object."))
76+
.build();
6877
}
6978

7079
public static final GraphQLDirective IncludeDirective = GraphQLDirective.newDirective()
@@ -119,6 +128,13 @@ public class Directives {
119128
.definition(SPECIFIED_BY_DIRECTIVE_DEFINITION)
120129
.build();
121130

131+
public static final GraphQLDirective OneOfDirective = GraphQLDirective.newDirective()
132+
.name(ONE_OF)
133+
.description("This directive is used to indicate an Input Object is a OneOf Input Object.")
134+
.validLocations(INPUT_OBJECT)
135+
.definition(ONE_OF_DIRECTIVE_DEFINITION)
136+
.build();
137+
122138
private static Description createDescription(String s) {
123139
return new Description(s, null, false);
124140
}

src/main/java/graphql/introspection/Introspection.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,14 @@ private static String printDefaultValue(InputValueWithState inputValueWithState,
399399
return null;
400400
};
401401

402+
private static final IntrospectionDataFetcher<?> isOneOfFetcher = environment -> {
403+
Object type = environment.getSource();
404+
if (type instanceof GraphQLInputObjectType) {
405+
return ((GraphQLInputObjectType) type).isOneOf();
406+
}
407+
return null;
408+
};
409+
402410
public static final GraphQLObjectType __Type = newObject()
403411
.name("__Type")
404412
.field(newFieldDefinition()
@@ -440,6 +448,9 @@ private static String printDefaultValue(InputValueWithState inputValueWithState,
440448
.field(newFieldDefinition()
441449
.name("ofType")
442450
.type(typeRef("__Type")))
451+
.field(newFieldDefinition()
452+
.name("isOneOf")
453+
.type(GraphQLBoolean))
443454
.field(newFieldDefinition()
444455
.name("specifiedByURL")
445456
.type(GraphQLString))
@@ -460,6 +471,7 @@ private static String printDefaultValue(InputValueWithState inputValueWithState,
460471
register(__Type, "enumValues", enumValuesTypesFetcher);
461472
register(__Type, "inputFields", inputFieldsFetcher);
462473
register(__Type, "ofType", OfTypeFetcher);
474+
register(__Type, "isOneOf", isOneOfFetcher);
463475
register(__Type, "specifiedByURL", specifiedByUrlDataFetcher);
464476
register(__Type, "specifiedByUrl", specifiedByUrlDataFetcher); // note that this field is deprecated
465477
}

src/main/java/graphql/schema/GraphQLInputObjectType.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.google.common.collect.ImmutableList;
44
import com.google.common.collect.ImmutableMap;
5+
import graphql.Directives;
56
import graphql.DirectivesUtil;
67
import graphql.Internal;
78
import graphql.PublicApi;
@@ -34,6 +35,8 @@
3435
public class GraphQLInputObjectType implements GraphQLNamedInputType, GraphQLUnmodifiedType, GraphQLNullableType, GraphQLInputFieldsContainer, GraphQLDirectiveContainer {
3536

3637
private final String name;
38+
39+
private final boolean isOneOf;
3740
private final String description;
3841
private final ImmutableMap<String, GraphQLInputObjectField> fieldMap;
3942
private final InputObjectTypeDefinition definition;
@@ -60,18 +63,37 @@ private GraphQLInputObjectType(String name,
6063
this.extensionDefinitions = ImmutableList.copyOf(extensionDefinitions);
6164
this.directives = new DirectivesUtil.DirectivesHolder(directives, appliedDirectives);
6265
this.fieldMap = buildDefinitionMap(fields);
66+
this.isOneOf = hasOneOf(directives, appliedDirectives);
6367
}
6468

6569
private ImmutableMap<String, GraphQLInputObjectField> buildDefinitionMap(List<GraphQLInputObjectField> fieldDefinitions) {
6670
return ImmutableMap.copyOf(FpKit.getByName(fieldDefinitions, GraphQLInputObjectField::getName,
6771
(fld1, fld2) -> assertShouldNeverHappen("Duplicated definition for field '%s' in type '%s'", fld1.getName(), this.name)));
6872
}
6973

74+
private boolean hasOneOf(List<GraphQLDirective> directives, List<GraphQLAppliedDirective> appliedDirectives) {
75+
if (appliedDirectives.stream().anyMatch(d -> Directives.OneOfDirective.getName().equals(d.getName()))) {
76+
return true;
77+
}
78+
// eventually GraphQLDirective as applied directive goes away
79+
return directives.stream().anyMatch(d -> Directives.OneOfDirective.getName().equals(d.getName()));
80+
}
81+
7082
@Override
7183
public String getName() {
7284
return name;
7385
}
7486

87+
88+
/**
89+
* An Input Object is considered a OneOf Input Object if it has the `@oneOf` directive applied to it.
90+
*
91+
* @return true if it's a OneOf Input Object
92+
*/
93+
public boolean isOneOf() {
94+
return isOneOf;
95+
}
96+
7597
public String getDescription() {
7698
return description;
7799
}

src/main/java/graphql/schema/GraphQLSchema.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,9 @@ private GraphQLSchema buildImpl() {
917917
if (additionalDirectives.stream().noneMatch(d -> d.getName().equals(Directives.SpecifiedByDirective.getName()))) {
918918
additionalDirectives.add(Directives.SpecifiedByDirective);
919919
}
920+
if (additionalDirectives.stream().noneMatch(d -> d.getName().equals(Directives.OneOfDirective.getName()))) {
921+
additionalDirectives.add(Directives.OneOfDirective);
922+
}
920923

921924
// quick build - no traversing
922925
final GraphQLSchema partiallyBuiltSchema = new GraphQLSchema(this);

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ public class DirectiveInfo {
2222
Directives.IncludeDirective,
2323
Directives.SkipDirective,
2424
Directives.DeprecatedDirective,
25-
Directives.SpecifiedByDirective);
25+
Directives.SpecifiedByDirective,
26+
Directives.OneOfDirective
27+
);
2628

2729
/**
2830
* A map from directive name to directive which provided by specification
@@ -31,7 +33,9 @@ public class DirectiveInfo {
3133
Directives.IncludeDirective.getName(), Directives.IncludeDirective,
3234
Directives.SkipDirective.getName(), Directives.SkipDirective,
3335
Directives.DeprecatedDirective.getName(), Directives.DeprecatedDirective,
34-
Directives.SpecifiedByDirective.getName(), Directives.SpecifiedByDirective);
36+
Directives.SpecifiedByDirective.getName(), Directives.SpecifiedByDirective,
37+
Directives.OneOfDirective.getName(), Directives.OneOfDirective
38+
);
3539

3640
/**
3741
* Returns true if a directive with provided directiveName has been defined in graphql specification

src/test/groovy/graphql/introspection/IntrospectionTest.groovy

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,4 +622,45 @@ class IntrospectionTest extends Specification {
622622
parseExecutionResult(allTrueExecutionResult).every()
623623
allTrueExecutionResult.data["__schema"]["types"].find{it["name"] == "Query"}["fields"].find{it["name"] == "tenDimensionalList"}["type"]["ofType"]["ofType"]["ofType"]["ofType"]["ofType"]["ofType"]["ofType"]["ofType"] == null // typeRefFragmentDepth is 7
624624
}
625+
626+
627+
def "introspection for oneOf support"() {
628+
def spec = '''
629+
630+
type Query {
631+
oneOfNamedField(arg : OneOfInputType) : Enum
632+
namedField(arg : InputType) : Enum
633+
}
634+
enum Enum {
635+
RED
636+
BLUE
637+
}
638+
input InputType {
639+
inputField : String
640+
}
641+
input OneOfInputType @oneOf {
642+
inputFieldA : String
643+
inputFieldB : String
644+
}
645+
'''
646+
647+
when:
648+
def graphQL = TestUtil.graphQL(spec).build()
649+
def executionResult = graphQL.execute(IntrospectionQuery.INTROSPECTION_QUERY)
650+
651+
then:
652+
executionResult.errors.isEmpty()
653+
654+
def types = executionResult.data['__schema']['types'] as List
655+
656+
def inputType = types.find { it['name'] == 'InputType' }
657+
inputType["isOneOf"] == false
658+
659+
def oneOfInputType = types.find { it['name'] == 'OneOfInputType' }
660+
oneOfInputType["isOneOf"] == true
661+
662+
def queryType = types.find { it['name'] == 'Query' }
663+
oneOfInputType["isOneOf"] == null
664+
}
665+
625666
}

src/test/groovy/graphql/schema/GraphQLInputObjectTypeTest.groovy

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

3+
import graphql.Directives
34
import graphql.GraphQLContext
45
import graphql.StarWarsSchema
56
import graphql.language.ObjectValue
@@ -79,4 +80,29 @@ class GraphQLInputObjectTypeTest extends Specification {
7980
expect:
8081
validationUtil.isValidLiteralValue(objectValue.build(), inputObjectType, schema, graphQLContext, Locale.ENGLISH)
8182
}
83+
84+
def "can detect one of support"() {
85+
when:
86+
def inputObjectType = newInputObject().name("TestInputObjectType")
87+
.field(newInputObjectField().name("NAME").type(GraphQLInt))
88+
.build()
89+
then:
90+
!inputObjectType.isOneOf()
91+
92+
when:
93+
inputObjectType = newInputObject().name("TestInputObjectType")
94+
.field(newInputObjectField().name("NAME").type(GraphQLInt))
95+
.withDirective(Directives.OneOfDirective)
96+
.build()
97+
then:
98+
inputObjectType.isOneOf()
99+
100+
when:
101+
inputObjectType = newInputObject().name("TestInputObjectType")
102+
.field(newInputObjectField().name("NAME").type(GraphQLInt))
103+
.withAppliedDirective(Directives.OneOfDirective.toAppliedDirective())
104+
.build()
105+
then:
106+
inputObjectType.isOneOf()
107+
}
82108
}

0 commit comments

Comments
 (0)