Skip to content

Commit

Permalink
fix: Input fields and Arguments can now be deprecated (#1472)
Browse files Browse the repository at this point in the history
Non-required InputFields and arguments now support deprecation via setting the `deprecation_reason` argument upon creation.
  • Loading branch information
vhutov authored Dec 10, 2022
1 parent d5dadb7 commit 19ea63b
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 8 deletions.
17 changes: 13 additions & 4 deletions graphene/types/argument.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,22 @@ class Argument(MountedType):
type (class for a graphene.UnmountedType): must be a class (not an instance) of an
unmounted graphene type (ex. scalar or object) which is used for the type of this
argument in the GraphQL schema.
required (bool): indicates this argument as not null in the graphql schema. Same behavior
required (optional, bool): indicates this argument as not null in the graphql schema. Same behavior
as graphene.NonNull. Default False.
name (str): the name of the GraphQL argument. Defaults to parameter name.
description (str): the description of the GraphQL argument in the schema.
default_value (Any): The value to be provided if the user does not set this argument in
name (optional, str): the name of the GraphQL argument. Defaults to parameter name.
description (optional, str): the description of the GraphQL argument in the schema.
default_value (optional, Any): The value to be provided if the user does not set this argument in
the operation.
deprecation_reason (optional, str): Setting this value indicates that the argument is
depreciated and may provide instruction or reason on how for clients to proceed. Cannot be
set if the argument is required (see spec).
"""

def __init__(
self,
type_,
default_value=Undefined,
deprecation_reason=None,
description=None,
name=None,
required=False,
Expand All @@ -51,12 +55,16 @@ def __init__(
super(Argument, self).__init__(_creation_counter=_creation_counter)

if required:
assert (
deprecation_reason is None
), f"Argument {name} is required, cannot deprecate it."
type_ = NonNull(type_)

self.name = name
self._type = type_
self.default_value = default_value
self.description = description
self.deprecation_reason = deprecation_reason

@property
def type(self):
Expand All @@ -68,6 +76,7 @@ def __eq__(self, other):
and self.type == other.type
and self.default_value == other.default_value
and self.description == other.description
and self.deprecation_reason == other.deprecation_reason
)


Expand Down
5 changes: 4 additions & 1 deletion graphene/types/inputfield.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ def __init__(
description=None,
required=False,
_creation_counter=None,
**extra_args
**extra_args,
):
super(InputField, self).__init__(_creation_counter=_creation_counter)
self.name = name
if required:
assert (
deprecation_reason is None
), f"InputField {name} is required, cannot deprecate it."
type_ = NonNull(type_)
self._type = type_
self.deprecation_reason = deprecation_reason
Expand Down
2 changes: 2 additions & 0 deletions graphene/types/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ def create_fields_for_type(self, graphene_type, is_input_type=False):
default_value=field.default_value,
out_name=name,
description=field.description,
deprecation_reason=field.deprecation_reason,
)
else:
args = {}
Expand All @@ -327,6 +328,7 @@ def create_fields_for_type(self, graphene_type, is_input_type=False):
out_name=arg_name,
description=arg.description,
default_value=arg.default_value,
deprecation_reason=arg.deprecation_reason,
)
subscribe = field.wrap_subscribe(
self.get_function_for_type(
Expand Down
40 changes: 38 additions & 2 deletions graphene/types/tests/test_argument.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,20 @@ def test_argument():


def test_argument_comparasion():
arg1 = Argument(String, name="Hey", description="Desc", default_value="default")
arg2 = Argument(String, name="Hey", description="Desc", default_value="default")
arg1 = Argument(
String,
name="Hey",
description="Desc",
default_value="default",
deprecation_reason="deprecated",
)
arg2 = Argument(
String,
name="Hey",
description="Desc",
default_value="default",
deprecation_reason="deprecated",
)

assert arg1 == arg2
assert arg1 != String()
Expand All @@ -40,6 +52,30 @@ def test_to_arguments():
}


def test_to_arguments_deprecated():
args = {"unmounted_arg": String(required=False, deprecation_reason="deprecated")}

my_args = to_arguments(args)
assert my_args == {
"unmounted_arg": Argument(
String, required=False, deprecation_reason="deprecated"
),
}


def test_to_arguments_required_deprecated():
args = {
"unmounted_arg": String(
required=True, name="arg", deprecation_reason="deprecated"
)
}

with raises(AssertionError) as exc_info:
to_arguments(args)

assert str(exc_info.value) == "Argument arg is required, cannot deprecate it."


def test_to_arguments_raises_if_field():
args = {"arg_string": Field(String)}

Expand Down
9 changes: 8 additions & 1 deletion graphene/types/tests/test_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,20 @@ def test_field_name_as_argument():

def test_field_source_argument_as_kw():
MyType = object()
field = Field(MyType, b=NonNull(True), c=Argument(None), a=NonNull(False))
deprecation_reason = "deprecated"
field = Field(
MyType,
b=NonNull(True),
c=Argument(None, deprecation_reason=deprecation_reason),
a=NonNull(False),
)
assert list(field.args) == ["b", "c", "a"]
assert isinstance(field.args["b"], Argument)
assert isinstance(field.args["b"].type, NonNull)
assert field.args["b"].type.of_type is True
assert isinstance(field.args["c"], Argument)
assert field.args["c"].type is None
assert field.args["c"].deprecation_reason == deprecation_reason
assert isinstance(field.args["a"], Argument)
assert isinstance(field.args["a"].type, NonNull)
assert field.args["a"].type.of_type is False
18 changes: 18 additions & 0 deletions graphene/types/tests/test_inputfield.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from functools import partial

from pytest import raises

from ..inputfield import InputField
from ..structures import NonNull
from .utils import MyLazyType
Expand All @@ -12,6 +14,22 @@ def test_inputfield_required():
assert field.type.of_type == MyType


def test_inputfield_deprecated():
MyType = object()
deprecation_reason = "deprecated"
field = InputField(MyType, required=False, deprecation_reason=deprecation_reason)
assert isinstance(field.type, type(MyType))
assert field.deprecation_reason == deprecation_reason


def test_inputfield_required_deprecated():
MyType = object()
with raises(AssertionError) as exc_info:
InputField(MyType, name="input", required=True, deprecation_reason="deprecated")

assert str(exc_info.value) == "InputField input is required, cannot deprecate it."


def test_inputfield_with_lazy_type():
MyType = object()
field = InputField(lambda: MyType)
Expand Down

0 comments on commit 19ea63b

Please sign in to comment.