Skip to content

Commit

Permalink
Support directives on variable definitions
Browse files Browse the repository at this point in the history
This adds language support for directives on variable definitions
following graphql/graphql-spec#510.

This is **only** language support so servers don't choke when
receiving queries using this feature.
  • Loading branch information
anthonymedinawork committed Jan 28, 2020
1 parent 055e735 commit c182772
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Unreleased
- Added `ResolveInfo.get_directive_arguments`
- Added `ResolveInfo.selected_fields` and `utilities.selected_fields`.

- Support directives on variable definitions: this adds language support for directives on variable definitions following [graphql/graphql-spec#510](https://github.com/graphql/graphql-spec/pull/510). This is **only** language support so servers don't choke when receiving queries using this feature.

### Fixed

- Introducing `pyproject.toml` broke Cython integration through build isolation. This is now fixed by always requiring cython as a a build dependency (through `build-system.requires`) and only enabling support behind the `PY_GQL_USE_CYTHON` env variable.
Expand Down
13 changes: 11 additions & 2 deletions py_gql/lang/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,20 +232,29 @@ def __init__(
self.loc = loc


class VariableDefinition(Node):
__slots__ = ("source", "loc", "variable", "type", "default_value")
class VariableDefinition(SupportDirectives, Node):
__slots__ = (
"source",
"loc",
"variable",
"type",
"default_value",
"directives",
)

def __init__(
self,
variable: Variable,
type: Type,
default_value=None, # type: Optional[Value]
directives=None, # type: Optional[List[Directive]]
source: Optional[str] = None,
loc: Optional[Tuple[int, int]] = None,
):
self.variable = variable
self.type = type
self.default_value = default_value
self.directives = directives or [] # type: List[Directive]
self.source = source
self.loc = loc

Expand Down
4 changes: 3 additions & 1 deletion py_gql/lang/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"FRAGMENT_DEFINITION",
"FRAGMENT_SPREAD",
"INLINE_FRAGMENT",
"VARIABLE_DEFINITION",
# Type System Definitions
"SCHEMA",
"SCALAR",
Expand Down Expand Up @@ -553,7 +554,7 @@ def parse_variable_definitions(self) -> List[_ast.VariableDefinition]:

def parse_variable_definition(self) -> _ast.VariableDefinition:
"""
VariableDefinition : Variable : Type DefaultValue?
VariableDefinition : Variable : Type DefaultValue? Directives[Const]?
"""
start = self.peek()
return _ast.VariableDefinition(
Expand All @@ -566,6 +567,7 @@ def parse_variable_definition(self) -> _ast.VariableDefinition:
if self.skip(Equals)
else None
),
directives=self.parse_directives(True),
loc=self._loc(start),
source=self._source,
)
Expand Down
15 changes: 11 additions & 4 deletions py_gql/lang/printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,17 @@ def print_operation_definition(self, node: _ast.OperationDefinition) -> str:
)

def print_variable_definition(self, node: _ast.VariableDefinition) -> str:
return "%s: %s%s" % (
self.print_variable(node.variable),
self(node.type),
_wrap(" = ", self(node.default_value)),
return _join(
[
"%s: %s%s"
% (
self.print_variable(node.variable),
self(node.type),
_wrap(" = ", self(node.default_value)),
),
self.print_directives(node),
],
" ",
)

def print_variable_definitions(
Expand Down
27 changes: 27 additions & 0 deletions tests/test_lang/test_ast_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,30 @@ def test_custom_indentation_object():
"""
)
)


def test_variable_definitions():
assert (
print_ast(
parse(
"""
query Query(
$a: ComplexType,
$b: Boolean = false,
$c: String @foo,
$d: Int! = 42 @bar(value: 42)
) {
id
}
""",
)
)
== dedent(
"""
query Query($a: ComplexType, $b: Boolean = false, $c: String @foo, \
$d: Int! = 42 @bar(value: 42)) {
id
}
"""
)
)
57 changes: 57 additions & 0 deletions tests/test_lang/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,3 +467,60 @@ def test_it_parses_inline_fragment_without_type():
],
),
)


def test_it_parses_variable_definition_with_directives():
assert_node_equal(
parse("query ($foo: Int @bar @baz) { foo }", no_location=True),
_ast.Document(
definitions=[
_ast.OperationDefinition(
"query",
_ast.SelectionSet(
selections=[_ast.Field(name=_ast.Name(value="foo"),)],
),
variable_definitions=[
_ast.VariableDefinition(
variable=_ast.Variable(
name=_ast.Name(value="foo"),
),
type=_ast.NamedType(name=_ast.Name(value="Int"),),
directives=[
_ast.Directive(name=_ast.Name(value="bar")),
_ast.Directive(name=_ast.Name(value="baz")),
],
)
],
)
],
),
)


def test_it_parses_variable_definition_with_default_and_directives():
assert_node_equal(
parse("query ($foo: Int = 42 @bar @baz) { foo }", no_location=True),
_ast.Document(
definitions=[
_ast.OperationDefinition(
"query",
_ast.SelectionSet(
selections=[_ast.Field(name=_ast.Name(value="foo"),)],
),
variable_definitions=[
_ast.VariableDefinition(
variable=_ast.Variable(
name=_ast.Name(value="foo"),
),
type=_ast.NamedType(name=_ast.Name(value="Int"),),
default_value=_ast.IntValue(value="42"),
directives=[
_ast.Directive(name=_ast.Name(value="bar")),
_ast.Directive(name=_ast.Name(value="baz")),
],
)
],
)
],
),
)

0 comments on commit c182772

Please sign in to comment.