-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add repeatable directives #1915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
| import graphql.language.InlineFragment; | ||
| import graphql.language.Node; | ||
| import graphql.language.OperationDefinition; | ||
| import graphql.schema.GraphQLDirective; | ||
| import graphql.validation.AbstractRule; | ||
| import graphql.validation.ValidationContext; | ||
| import graphql.validation.ValidationErrorCollector; | ||
|
|
@@ -57,17 +58,23 @@ public void checkOperationDefinition(OperationDefinition operationDefinition) { | |
| } | ||
|
|
||
| private void checkDirectivesUniqueness(Node<?> directivesContainer, List<Directive> directives) { | ||
| Set<String> names = new LinkedHashSet<>(); | ||
| directives.forEach(directive -> { | ||
| String name = directive.getName(); | ||
| if (names.contains(name)) { | ||
| Set<String> directiveNames = new LinkedHashSet<>(); | ||
|
|
||
| for (Directive directive : directives) { | ||
| String directiveName = directive.getName(); | ||
| GraphQLDirective graphQLDirective = getValidationContext().getSchema().getDirective(directiveName); | ||
|
|
||
| if (graphQLDirective == null) continue; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please use fully formed more lines : yes - more readable : also yes
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. by using the GraphQL java code formatter it will be automatically handled. |
||
| if (graphQLDirective.getDefinition() != null && graphQLDirective.getDefinition().isRepeatable()) continue; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we will want to transfer the AST isRepeatable into the runtime That is inside SchemaGenerator related code we transfer these booleans over The AST elements are for reading SDL - the GraphqlXXX class are aimed as the runtime equivalents with more logic in them and more state rules.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for your guidance |
||
|
|
||
| if (directiveNames.contains(directiveName)) { | ||
| addError(ValidationErrorType.DuplicateDirectiveName, | ||
| directive.getSourceLocation(), | ||
| duplicateDirectiveNameMessage(name, directivesContainer.getClass().getSimpleName())); | ||
| duplicateDirectiveNameMessage(directiveName, directivesContainer.getClass().getSimpleName())); | ||
| } else { | ||
| names.add(name); | ||
| directiveNames.add(directiveName); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| private String duplicateDirectiveNameMessage(String directiveName, String location) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| package graphql.execution.directives | ||
|
|
||
| import graphql.TestUtil | ||
| import graphql.language.Directive | ||
| import graphql.language.Field | ||
| import graphql.language.OperationDefinition | ||
| import graphql.language.StringValue | ||
| import graphql.validation.Validator | ||
| import spock.lang.Specification | ||
|
|
||
| class RepeatableDirectivesTest extends Specification { | ||
|
|
||
| def sdl = ''' | ||
| directive @repeatableDirective(arg: String) repeatable on FIELD | ||
|
|
||
| directive @nonRepeatableDirective on FIELD | ||
|
|
||
| type Query { | ||
| namedField: String | ||
| } | ||
| ''' | ||
|
|
||
| def schema = TestUtil.schema(sdl) | ||
|
|
||
|
|
||
| def "repeatableDirectives"() { | ||
| def spec = ''' | ||
| query { | ||
| f1: namedField @repeatableDirective @repeatableDirective | ||
| f2: namedField @repeatableDirective | ||
| f3: namedField @nonRepeatableDirective | ||
| } | ||
| ''' | ||
|
|
||
| when: | ||
| def document = TestUtil.parseQuery(spec) | ||
| def validator = new Validator(); | ||
| def validationErrors = validator.validateDocument(schema, document); | ||
|
|
||
| then: | ||
| validationErrors.size() == 0 | ||
| } | ||
|
|
||
| def "nonRepeatableDirective"() { | ||
|
|
||
| def spec = ''' | ||
| query { | ||
| namedField @nonRepeatableDirective @nonRepeatableDirective | ||
| } | ||
| ''' | ||
|
|
||
| when: | ||
| def document = TestUtil.parseQuery(spec) | ||
| def validator = new Validator(); | ||
| def validationErrors = validator.validateDocument(schema, document); | ||
|
|
||
| then: | ||
| validationErrors.size() == 1 | ||
| validationErrors[0].message == "Validation error of type DuplicateDirectiveName: Directives must be uniquely named within a location. The directive 'nonRepeatableDirective' used on a 'Field' is not unique. @ 'namedField'" | ||
| } | ||
|
|
||
| def "getRepeatableDirectivesInfo"() { | ||
|
|
||
| def spec = ''' | ||
| query { | ||
| namedField @repeatableDirective(arg: "value1") @repeatableDirective(arg: "value2") | ||
| } | ||
| ''' | ||
|
|
||
| when: | ||
| def document = TestUtil.parseQuery(spec) | ||
| def validator = new Validator(); | ||
| def validationErrors = validator.validateDocument(schema, document); | ||
|
|
||
| OperationDefinition operationDefinition = document.getDefinitions()[0] | ||
| Field field = operationDefinition.getSelectionSet().getSelections()[0] | ||
| List<Directive> directives = field.getDirectives() | ||
|
|
||
| then: | ||
| validationErrors.size() == 0 | ||
| directives.size() == 2 | ||
| ((StringValue) directives[0].getArgument("arg").getValue()).getValue() == "value1" | ||
| ((StringValue) directives[1].getArgument("arg").getValue()).getValue() == "value2" | ||
| } | ||
|
|
||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice catch