8000 [RFC] Allow directives on variable definitions · graphql-python/graphql-core@f2567ab · GitHub
[go: up one dir, main page]

Skip to content

Commit f2567ab

Browse files
committed
[RFC] Allow directives on variable definitions
Replicates graphql/graphql-js@1d7efa9
1 parent c585e34 commit f2567ab

File tree

14 files changed

+45
-12
lines changed

14 files changed

+45
-12
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ a query language for APIs created by Facebook.
1313

1414
The current version 1.0.0rc2 of GraphQL-core-next is up-to-date with GraphQL.js
1515
version 14.0.0rc2. All parts of the API are covered by an extensive test
16-
suite of currently 1547 unit tests.
16+
suite of currently 1549 unit tests.
1717

1818

1919
## Documentation

graphql/language/ast.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,12 @@ class OperationDefinitionNode(ExecutableDefinitionNode):
159159

160160

161161
class VariableDefinitionNode(Node):
162-
__slots__ = 'variable', 'type', 'default_value'
162+
__slots__ = 'variable', 'type', 'default_value', 'directives'
163163

164164
variable: 'VariableNode'
165165
type: 'TypeNode'
166166
default_value: Optional['ValueNode']
167+
directives: Optional[List['DirectiveNode']]
167168

168169

169170
class SelectionSetNode(Node):

graphql/language/directive_locations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class DirectiveLocation(Enum):
1313
FIELD = 'field'
1414
FRAGMENT_DEFINITION = 'fragment definition'
1515
FRAGMENT_SPREAD = 'fragment spread'
16+
VARIABLE_DEFINITION = 'variable definition'
1617
INLINE_FRAGMENT = 'inline fragment'
1718

1819
# Type System Definitions

graphql/language/parser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,14 @@ def parse_variable_definitions(lexer: Lexer) -> List[VariableDefinitionNode]:
169169

170170

171171
def parse_variable_definition(lexer: Lexer) -> VariableDefinitionNode:
172-
"""VariableDefinition: Variable: Type DefaultValue?"""
172+
"""VariableDefinition: Variable: Type DefaultValue? Directives[Const]?"""
173173
start = lexer.token
174174
return VariableDefinitionNode(
175175
variable=parse_variable(lexer),
176176
type=expect(lexer, TokenKind.COLON) and parse_type_reference(lexer),
177177
default_value=parse_value_literal(lexer, True)
178178
if skip(lexer, TokenKind.EQUALS) else None,
179+
directives=parse_directives(lexer, True),
179180
loc=loc(lexer, start))
180181

181182

graphql/language/printer.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def leave_operation_definition(self, node, *_args):
5050
) else selection_set
5151

5252
def leave_variable_definition(self, node, *_args):
53-
return f"{node.variable}: {node.type}{wrap(' = ', node.default_value)}"
53+
return (f"{node.variable}: {node.type}"
54+
f"{wrap(' = ', node.default_value)}"
55+
f"{wrap(' ', ' '.join(node.directives))}")
5456

5557
def leave_selection_set(self, node, *_args):
5658
return block(node.selections)

graphql/language/visitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
'document': ('definitions',),
2828
'operation_definition': (
2929
'name', 'variable_definitions', 'directives', 'selection_set'),
30-
'variable_definition': ('variable', 'type', 'default_value'),
30+
'variable_definition': ('variable', 'type', 'default_value', 'directives'),
3131
'variable': ('name',),
3232
'selection_set': ('selections',),
3333
'field': ('alias', 'name', 'arguments', 'directives', 'selection_set'),

graphql/type/introspection.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ def print_value(value: Any, type_: GraphQLInputType) -> str:
106106
'INLINE_FRAGMENT': GraphQLEnumValue(
107107
DirectiveLocation.INLINE_FRAGMENT,
108108
description='Location adjacent to an inline fragment.'),
109+
'VARIABLE_DEFINITION': GraphQLEnumValue(
110+
DirectiveLocation.VARIABLE_DEFINITION,
111+
description='Location adjacent to a variable definition.'),
109112
'SCHEMA': GraphQLEnumValue(
110113
DirectiveLocation.SCHEMA,
111114
description='Location adjacent to a schema definition.'),

graphql/validation/rules/known_directives.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def enter_directive(
6969
'fragment_spread': DirectiveLocation.FRAGMENT_SPREAD,
7070
'inline_fragment': DirectiveLocation.INLINE_FRAGMENT,
7171
'fragment_definition': DirectiveLocation.FRAGMENT_DEFINITION,
72+
'variable_definition': DirectiveLocation.VARIABLE_DEFINITION,
7273
'schema_definition': DirectiveLocation.SCHEMA,
7374
'schema_extension': DirectiveLocation.SCHEMA,
7475
'scalar_type_definition': DirectiveLocation.SCALAR,

tests/language/test_parser.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ def parses_constant_default_values():
8282
'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }',
8383
'Unexpected $', (1, 37))
8484

85+
def parses_variable_definition_directives():
86+
parse('query Foo($x: Boolean = false @bar) { field }')
87+
8588
def does_not_accept_fragments_named_on():
8689
assert_syntax_error(
8790
'fragment on on on { on }', "Unexpected Name 'on'", (1, 10))

tests/language/test_printer.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ def correctly_prints_query_operation_with_artifacts():
4848
}
4949
""")
5050

51+
def correcty_prints_query_operation_with_variable_directive():
52+
query_ast_with_variable_directive = parse(
53+
'query ($foo: TestType = {a: 123}'
54+
' @testDirective(if: true) @test) { id }')
55+
assert print_ast(query_ast_with_variable_directive) == dedent("""
56+
query ($foo: TestType = {a: 123} @testDirective(if: true) @test) {
57+
id
58+
}
59+
""")
60+
5161
def correctly_prints_mutation_operation_with_artifacts():
5262
mutation_ast_with_artifacts = parse(
5363
'mutation ($foo: TestType) @testDirective { id, name }')

tests/type/test_introspection.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,11 @@ def executes_an_introspection_query():
656656
'name': 'INLINE_FRAGMENT',
657657
'isDeprecated': False,
658658
'deprecationReason': None
659-
}, {
659+
}, {
660+
'name': 'VARIABLE_DEFINITION',
661+
'isDeprecated': False,
662+
'deprecationReason': None
663+
}, {
660664
'name': 'SCHEMA',
661665
'isDeprecated': False,
662666
'deprecationReason': None

tests/utilities/test_schema_printer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,9 @@ def prints_introspection_schema():
576576
"""Location adjacent to an inline fragment."""
577577
INLINE_FRAGMENT
578578
579+
"""Location adjacent to a variable definition."""
580+
VARIABLE_DEFINITION
581+
579582
"""Location adjacent to a schema definition."""
580583
SCHEMA
581584

tests/validation/harness.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,10 @@ def raise_type_error(message):
196196
locations=[DirectiveLocation.FRAGMENT_SPREAD]),
197197
GraphQLDirective(
198198
name='onInlineFragment',
199-
locations=[DirectiveLocation.INLINE_FRAGMENT])],
199+
locations=[DirectiveLocation.INLINE_FRAGMENT]),
200+
GraphQLDirective(
201+
name='onVariableDefinition',
202+
locations=[DirectiveLocation.VARIABLE_DEFINITION])],
200203
types=[Cat, Dog, Human, Alien])
201204

202205

tests/validation/test_known_directives.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ def with_many_unknown_directives():
9898

9999
def with_well_placed_directives():
100100
expect_passes_rule(KnownDirectivesRule, """
101-
query Foo @onQuery{
102-
name @include(if: true)
101+
query Foo($var: Boolean @onVariableDefinition) @onQuery {
102+
name @include(if: $var)
103103
...Frag @include(if: true)
104104
skippedField @skip(if: true)
105105
...SkippedFrag @skip(if: true)
@@ -112,16 +112,17 @@ def with_well_placed_directives():
112112

113113
def with_misplaced_directives():
114114
expect_fails_rule(KnownDirectivesRule, """
115-
query Foo @include(if: true) {
116-
name @onQuery
115+
query Foo($var: Boolean @onField) @include(if: true) {
116+
name @onQuery @include(if: $var)
117117
...Frag @onQuery
118118
}
119119
120120
mutation Bar @onQuery {
121121
someField
122122
}
123123
""", [
124-
misplaced_directive('include', 'query', 2, 23),
124+
misplaced_directive('onField', 'variable definition', 2, 37),
125+
misplaced_directive('include', 'query', 2, 47),
125126
misplaced_directive('onQuery', 'field', 3, 20),
126127
misplaced_directive('onQuery', 'fragment spread', 4, 23),
127128
misplaced_directive('onQuery', 'mutation', 7, 26),

0 commit comments

Comments
 (0)
0