Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/java/graphql/DirectivesUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import graphql.language.Directive;
import graphql.language.DirectiveDefinition;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLDirective;
import graphql.util.FpKit;
Expand All @@ -14,6 +16,7 @@

import static graphql.Assert.assertNotNull;
import static graphql.collect.ImmutableKit.emptyList;
import static java.util.stream.Collectors.toList;

@Internal
public class DirectivesUtil {
Expand Down Expand Up @@ -149,4 +152,12 @@ public List<GraphQLDirective> getDirectives(String directiveName) {
return allDirectivesByName.getOrDefault(directiveName, emptyList());
}
}

public static List<Directive> nonRepeatableDirectivesOnly(Map<String, DirectiveDefinition> directiveDefinitionMap, List<Directive> directives) {
return directives.stream().filter(directive -> {
String directiveName = directive.getName();
DirectiveDefinition directiveDefinition = directiveDefinitionMap.get(directiveName);
return directiveDefinition == null || !directiveDefinition.isRepeatable();
}).collect(toList());
}
}
10 changes: 1 addition & 9 deletions src/main/java/graphql/schema/idl/SchemaTypeChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.function.Function;
import java.util.function.Predicate;

import static graphql.DirectivesUtil.nonRepeatableDirectivesOnly;
import static graphql.introspection.Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION;
import static java.util.stream.Collectors.toList;

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


private List<Directive> nonRepeatableDirectivesOnly(Map<String, DirectiveDefinition> directiveDefinitionMap, List<Directive> directives) {
return directives.stream().filter(directive -> {
String directiveName = directive.getName();
DirectiveDefinition directiveDefinition = directiveDefinitionMap.get(directiveName);
return directiveDefinition == null || !directiveDefinition.isRepeatable();
}).collect(toList());
}

private void checkObjTypeFields(List<GraphQLError> errors, ObjectTypeDefinition typeDefinition, List<FieldDefinition> fieldDefinitions, Map<String, DirectiveDefinition> directiveDefinitionMap) {
// field unique ness
checkNamedUniqueness(errors, fieldDefinitions, FieldDefinition::getName,
Expand Down
22 changes: 12 additions & 10 deletions src/main/java/graphql/schema/idl/SchemaTypeExtensionsChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import graphql.Internal;
import graphql.language.Argument;
import graphql.language.Directive;
import graphql.language.DirectiveDefinition;
import graphql.language.EnumTypeDefinition;
import graphql.language.EnumValueDefinition;
import graphql.language.FieldDefinition;
Expand Down Expand Up @@ -32,6 +33,7 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static graphql.DirectivesUtil.nonRepeatableDirectivesOnly;
import static graphql.schema.idl.SchemaTypeChecker.checkNamedUniqueness;
import static graphql.util.FpKit.mergeFirst;

Expand All @@ -43,12 +45,13 @@
class SchemaTypeExtensionsChecker {

void checkTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry) {
checkObjectTypeExtensions(errors, typeRegistry);
checkInterfaceTypeExtensions(errors, typeRegistry);
Map<String, DirectiveDefinition> directiveDefinitionMap = typeRegistry.getDirectiveDefinitions();
checkObjectTypeExtensions(errors, typeRegistry,directiveDefinitionMap);
checkInterfaceTypeExtensions(errors, typeRegistry,directiveDefinitionMap);
checkUnionTypeExtensions(errors, typeRegistry);
checkEnumTypeExtensions(errors, typeRegistry);
checkScalarTypeExtensions(errors, typeRegistry);
checkInputObjectTypeExtensions(errors, typeRegistry);
checkInputObjectTypeExtensions(errors, typeRegistry,directiveDefinitionMap);
}


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

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

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


/*
* Interface type extensions have the potential to be invalid if incorrectly defined.
*
Expand All @@ -111,7 +113,7 @@ private void checkObjectTypeExtensions(List<GraphQLError> errors, TypeDefinition
* 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).
* Any directives provided must not already apply to the original Interface type.
*/
private void checkInterfaceTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry) {
private void checkInterfaceTypeExtensions(List<GraphQLError> errors, TypeDefinitionRegistry typeRegistry,Map<String, DirectiveDefinition> directiveDefinitionMap) {
typeRegistry.interfaceTypeExtensions()
.forEach((name, extensions) -> {
checkTypeExtensionHasCorrespondingType(errors, typeRegistry, name, extensions, InterfaceTypeDefinition.class);
Expand All @@ -128,7 +130,7 @@ private void checkInterfaceTypeExtensions(List<GraphQLError> errors, TypeDefinit
(namedField, inputValueDefinition) -> new NonUniqueArgumentError(extension, fld, name)));

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

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

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

inputValueDefinitions.forEach(fld -> fld.getDirectives().forEach(directive ->
Expand Down
28 changes: 28 additions & 0 deletions src/test/groovy/graphql/Issue2114.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package graphql

import graphql.schema.idl.RuntimeWiring
import graphql.schema.idl.SchemaGenerator
import graphql.schema.idl.SchemaParser
import spock.lang.Specification

class Issue2114 extends Specification {

def "allow use of repeatable directives on extensions"() {
given:
def spec = "type Query {}" +
"directive @IamRepeatable repeatable on FIELD_DEFINITION" +
" extend type Query { " +
" test: String" +
" @IamRepeatable" +
" @IamRepeatable" +
"}"

when:
def registry = new SchemaParser().parse(spec)
def graphQLSchema = new SchemaGenerator().makeExecutableSchema(registry, RuntimeWiring.newRuntimeWiring().build())

then:
graphQLSchema != null
}
}