Skip to content

Commit b01df74

Browse files
authored
allow usage of repeatable directives on extensions (#2115)
1 parent 43668fb commit b01df74

4 files changed

Lines changed: 52 additions & 19 deletions

File tree

src/main/java/graphql/DirectivesUtil.java

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

33
import com.google.common.collect.ImmutableList;
44
import com.google.common.collect.ImmutableMap;
5+
import graphql.language.Directive;
6+
import graphql.language.DirectiveDefinition;
57
import graphql.schema.GraphQLArgument;
68
import graphql.schema.GraphQLDirective;
79
import graphql.util.FpKit;
@@ -14,6 +16,7 @@
1416

1517
import static graphql.Assert.assertNotNull;
1618
import static graphql.collect.ImmutableKit.emptyList;
19+
import static java.util.stream.Collectors.toList;
1720

1821
@Internal
1922
public class DirectivesUtil {
@@ -149,4 +152,12 @@ public List<GraphQLDirective> getDirectives(String directiveName) {
149152
return allDirectivesByName.getOrDefault(directiveName, emptyList());
150153
}
151154
}
155+
156+
public static List<Directive> nonRepeatableDirectivesOnly(Map<String, DirectiveDefinition> directiveDefinitionMap, List<Directive> directives) {
157+
return directives.stream().filter(directive -> {
158+
String directiveName = directive.getName();
159+
DirectiveDefinition directiveDefinition = directiveDefinitionMap.get(directiveName);
160+
return directiveDefinition == null || !directiveDefinition.isRepeatable();
161+
}).collect(toList());
162+
}
152163
}

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.util.function.Function;
4343
import java.util.function.Predicate;
4444

45+
import static graphql.DirectivesUtil.nonRepeatableDirectivesOnly;
4546
import static graphql.introspection.Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION;
4647
import static java.util.stream.Collectors.toList;
4748

@@ -201,15 +202,6 @@ private void checkFieldsAreSensible(List<GraphQLError> errors, TypeDefinitionReg
201202
inputTypes.forEach(inputType -> checkInputValues(errors, inputType, inputType.getInputValueDefinitions(), INPUT_FIELD_DEFINITION, directiveDefinitionMap));
202203
}
203204

204-
205-
private List<Directive> nonRepeatableDirectivesOnly(Map<String, DirectiveDefinition> directiveDefinitionMap, List<Directive> directives) {
206-
return directives.stream().filter(directive -> {
207-
String directiveName = directive.getName();
208-
DirectiveDefinition directiveDefinition = directiveDefinitionMap.get(directiveName);
209-
return directiveDefinition == null || !directiveDefinition.isRepeatable();
210-
}).collect(toList());
211-
}
212-
213205
private void checkObjTypeFields(List<GraphQLError> errors, ObjectTypeDefinition typeDefinition, List<FieldDefinition> fieldDefinitions, Map<String, DirectiveDefinition> directiveDefinitionMap) {
214206
// field unique ness
215207
checkNamedUniqueness(errors, fieldDefinitions, FieldDefinition::getName,

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import graphql.Internal;
55
import graphql.language.Argument;
66
import graphql.language.Directive;
7+
import graphql.language.DirectiveDefinition;
78
import graphql.language.EnumTypeDefinition;
89
import graphql.language.EnumValueDefinition;
910
import graphql.language.FieldDefinition;
@@ -32,6 +33,7 @@
3233
import java.util.function.Consumer;
3334
import java.util.stream.Collectors;
3435

36+
import static graphql.DirectivesUtil.nonRepeatableDirectivesOnly;
3537
import static graphql.schema.idl.SchemaTypeChecker.checkNamedUniqueness;
3638
import static graphql.util.FpKit.mergeFirst;
3739

@@ -43,12 +45,13 @@
4345
class SchemaTypeExtensionsChecker {
4446

4547
void checkTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry) {
46-
checkObjectTypeExtensions(errors, typeRegistry);
47-
checkInterfaceTypeExtensions(errors, typeRegistry);
48+
Map<String, DirectiveDefinition> directiveDefinitionMap = typeRegistry.getDirectiveDefinitions();
49+
checkObjectTypeExtensions(errors, typeRegistry,directiveDefinitionMap);
50+
checkInterfaceTypeExtensions(errors, typeRegistry,directiveDefinitionMap);
4851
checkUnionTypeExtensions(errors, typeRegistry);
4952
checkEnumTypeExtensions(errors, typeRegistry);
5053
checkScalarTypeExtensions(errors, typeRegistry);
51-
checkInputObjectTypeExtensions(errors, typeRegistry);
54+
checkInputObjectTypeExtensions(errors, typeRegistry,directiveDefinitionMap);
5255
}
5356

5457

@@ -62,7 +65,7 @@ void checkTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeR
6265
* Any interfaces provided must not be already implemented by the original Object type.
6366
* The resulting extended object type must be a super-set of all interfaces it implements.
6467
*/
65-
private void checkObjectTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry) {
68+
private void checkObjectTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry,Map<String, DirectiveDefinition> directiveDefinitionMap) {
6669
typeRegistry.objectTypeExtensions()
6770
.forEach((name, extensions) -> {
6871
checkTypeExtensionHasCorrespondingType(errors, typeRegistry, name, extensions, ObjectTypeDefinition.class);
@@ -79,7 +82,7 @@ private void checkObjectTypeExtensions(List<GraphQLError> errors, TypeDefinition
7982
(namedField, inputValueDefinition) -> new NonUniqueArgumentError(extension, fld, name)));
8083

8184
// directive checks
82-
extension.getFieldDefinitions().forEach(fld -> checkNamedUniqueness(errors, fld.getDirectives(), Directive::getName,
85+
extension.getFieldDefinitions().forEach(fld -> checkNamedUniqueness(errors, nonRepeatableDirectivesOnly(directiveDefinitionMap, fld.getDirectives()), Directive::getName,
8386
(directiveName, directive) -> new NonUniqueDirectiveError(extension, fld, directiveName)));
8487

8588
fieldDefinitions.forEach(fld -> fld.getDirectives().forEach(directive ->
@@ -101,7 +104,6 @@ private void checkObjectTypeExtensions(List<GraphQLError> errors, TypeDefinition
101104
);
102105
}
103106

104-
105107
/*
106108
* Interface type extensions have the potential to be invalid if incorrectly defined.
107109
*
@@ -111,7 +113,7 @@ private void checkObjectTypeExtensions(List<GraphQLError> errors, TypeDefinition
111113
* Any Object type which implemented the original Interface type must also be a super-set of the fields of the Interface type extension (which may be due to Object type extension).
112114
* Any directives provided must not already apply to the original Interface type.
113115
*/
114-
private void checkInterfaceTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry) {
116+
private void checkInterfaceTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry,Map<String, DirectiveDefinition> directiveDefinitionMap) {
115117
typeRegistry.interfaceTypeExtensions()
116118
.forEach((name, extensions) -> {
117119
checkTypeExtensionHasCorrespondingType(errors, typeRegistry, name, extensions, InterfaceTypeDefinition.class);
@@ -128,7 +130,7 @@ private void checkInterfaceTypeExtensions(List<GraphQLError> errors, TypeDefinit
128130
(namedField, inputValueDefinition) -> new NonUniqueArgumentError(extension, fld, name)));
129131

130132
// directive checks
131-
extension.getFieldDefinitions().forEach(fld -> checkNamedUniqueness(errors, fld.getDirectives(), Directive::getName,
133+
extension.getFieldDefinitions().forEach(fld -> checkNamedUniqueness(errors, nonRepeatableDirectivesOnly(directiveDefinitionMap,fld.getDirectives()), Directive::getName,
132134
(directiveName, directive) -> new NonUniqueDirectiveError(extension, fld, directiveName)));
133135

134136
fieldDefinitions.forEach(fld -> fld.getDirectives().forEach(directive ->
@@ -242,7 +244,7 @@ private void checkScalarTypeExtensions(List<GraphQLError> errors, TypeDefinition
242244
* All fields of an Input Object type extension must not already be a field of the original Input Object.
243245
* Any directives provided must not already apply to the original Input Object type.
244246
*/
245-
private void checkInputObjectTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry) {
247+
private void checkInputObjectTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry,Map<String, DirectiveDefinition> directiveDefinitionMap) {
246248
typeRegistry.inputObjectTypeExtensions()
247249
.forEach((name, extensions) -> {
248250
checkTypeExtensionHasCorrespondingType(errors, typeRegistry, name, extensions, InputObjectTypeDefinition.class);
@@ -255,7 +257,7 @@ private void checkInputObjectTypeExtensions(List<GraphQLError> errors, TypeDefin
255257
(namedField, fieldDef) -> new NonUniqueNameError(extension, fieldDef));
256258

257259
// directive checks
258-
inputValueDefinitions.forEach(fld -> checkNamedUniqueness(errors, fld.getDirectives(), Directive::getName,
260+
inputValueDefinitions.forEach(fld -> checkNamedUniqueness(errors, nonRepeatableDirectivesOnly(directiveDefinitionMap, fld.getDirectives()), Directive::getName,
259261
(directiveName, directive) -> new NonUniqueDirectiveError(extension, fld, directiveName)));
260262

261263
inputValueDefinitions.forEach(fld -> fld.getDirectives().forEach(directive ->
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package graphql
2+
3+
import graphql.schema.idl.RuntimeWiring
4+
import graphql.schema.idl.SchemaGenerator
5+
import graphql.schema.idl.SchemaParser
6+
import spock.lang.Specification
7+
8+
class Issue2114 extends Specification {
9+
10+
def "allow use of repeatable directives on extensions"() {
11+
given:
12+
def spec = "type Query {}" +
13+
"directive @IamRepeatable repeatable on FIELD_DEFINITION" +
14+
" extend type Query { " +
15+
" test: String" +
16+
" @IamRepeatable" +
17+
" @IamRepeatable" +
18+
"}"
19+
20+
when:
21+
def registry = new SchemaParser().parse(spec)
22+
def graphQLSchema = new SchemaGenerator().makeExecutableSchema(registry, RuntimeWiring.newRuntimeWiring().build())
23+
24+
then:
25+
graphQLSchema != null
26+
}
27+
}
28+

0 commit comments

Comments
 (0)