From 19ebf08339263f0446235ddffbbab0e6d3f11699 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sun, 20 Mar 2022 17:52:44 +0100 Subject: [PATCH] fix: default value for argument should be Undefined (Issue #1394) and update function from_global_id exception handling (https://github.com/graphql-python/graphql-relay-py/commit/b217aefa8c9162f0c165a0646b545f4da37bcd76) --- .../snap_test_objectidentification.py | 2 +- graphene/relay/node.py | 2 ++ graphene/tests/issues/test_1394.py | 36 +++++++++++++++++++ graphene/types/argument.py | 3 +- graphene/types/tests/test_query.py | 6 ++-- graphene/types/tests/test_type_map.py | 7 ++-- graphene/utils/tests/test_deduplicator.py | 5 +-- 7 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 graphene/tests/issues/test_1394.py diff --git a/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py b/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py index 7bce5ba33..d7694e90b 100644 --- a/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py +++ b/examples/starwars_relay/tests/snapshots/snap_test_objectidentification.py @@ -49,7 +49,7 @@ name: String """The ships used by the faction.""" - ships(before: String = null, after: String = null, first: Int = null, last: Int = null): ShipConnection + ships(before: String, after: String, first: Int, last: Int): ShipConnection } """An object with an ID""" diff --git a/graphene/relay/node.py b/graphene/relay/node.py index c1316d226..dabcff6ce 100644 --- a/graphene/relay/node.py +++ b/graphene/relay/node.py @@ -86,6 +86,8 @@ def node_resolver(cls, only_type, root, info, id): def get_node_from_global_id(cls, info, global_id, only_type=None): try: _type, _id = cls.from_global_id(global_id) + if not _type: + raise ValueError("Invalid Global ID") except Exception as e: raise Exception( f'Unable to parse global ID "{global_id}". ' diff --git a/graphene/tests/issues/test_1394.py b/graphene/tests/issues/test_1394.py new file mode 100644 index 000000000..393743819 --- /dev/null +++ b/graphene/tests/issues/test_1394.py @@ -0,0 +1,36 @@ +from ...types import ObjectType, Schema, String, NonNull + + +class Query(ObjectType): + hello = String(input=NonNull(String)) + + def resolve_hello(self, info, input): + if input == "nothing": + return None + return f"Hello {input}!" + + +schema = Schema(query=Query) + + +def test_required_input_provided(): + """ + Test that a required argument works when provided. + """ + input_value = "Potato" + result = schema.execute('{ hello(input: "%s") }' % input_value) + assert not result.errors + assert result.data == {"hello": "Hello Potato!"} + + +def test_required_input_missing(): + """ + Test that a required argument raised an error if not provided. + """ + result = schema.execute("{ hello }") + assert result.errors + assert len(result.errors) == 1 + assert ( + result.errors[0].message + == "Field 'hello' argument 'input' of type 'String!' is required, but it was not provided." + ) diff --git a/graphene/types/argument.py b/graphene/types/argument.py index 71026d45b..f9dc843bf 100644 --- a/graphene/types/argument.py +++ b/graphene/types/argument.py @@ -1,4 +1,5 @@ from itertools import chain +from graphql import Undefined from .dynamic import Dynamic from .mountedtype import MountedType @@ -41,7 +42,7 @@ class Argument(MountedType): def __init__( self, type_, - default_value=None, + default_value=Undefined, description=None, name=None, required=False, diff --git a/graphene/types/tests/test_query.py b/graphene/types/tests/test_query.py index 2d3e4c730..e117754fe 100644 --- a/graphene/types/tests/test_query.py +++ b/graphene/types/tests/test_query.py @@ -229,11 +229,11 @@ def resolve_test(self, info, **args): result = test_schema.execute("{ test }", None) assert not result.errors - assert result.data == {"test": '[null,{"a_str":null,"a_int":null}]'} + assert result.data == {"test": "[null,{}]"} result = test_schema.execute('{ test(aStr: "String!") }', "Source!") assert not result.errors - assert result.data == {"test": '["Source!",{"a_str":"String!","a_int":null}]'} + assert result.data == {"test": '["Source!",{"a_str":"String!"}]'} result = test_schema.execute('{ test(aInt: -123, aStr: "String!") }', "Source!") assert not result.errors @@ -258,7 +258,7 @@ def resolve_test(self, info, **args): result = test_schema.execute("{ test }", None) assert not result.errors - assert result.data == {"test": '[null,{"a_input":null}]'} + assert result.data == {"test": "[null,{}]"} result = test_schema.execute('{ test(aInput: {aField: "String!"} ) }', "Source!") assert not result.errors diff --git a/graphene/types/tests/test_type_map.py b/graphene/types/tests/test_type_map.py index 12e7a1f44..f0c78e08c 100644 --- a/graphene/types/tests/test_type_map.py +++ b/graphene/types/tests/test_type_map.py @@ -1,3 +1,4 @@ +from graphql import Undefined from graphql.type import ( GraphQLArgument, GraphQLEnumType, @@ -244,7 +245,9 @@ class MyObjectType(ObjectType): foo_field = fields["fooBar"] assert isinstance(foo_field, GraphQLField) assert foo_field.args == { - "barFoo": GraphQLArgument(GraphQLString, default_value=None, out_name="bar_foo") + "barFoo": GraphQLArgument( + GraphQLString, default_value=Undefined, out_name="bar_foo" + ) } @@ -267,7 +270,7 @@ class MyObjectType(ObjectType): assert isinstance(foo_field, GraphQLField) assert foo_field.args == { "bar_foo": GraphQLArgument( - GraphQLString, default_value=None, out_name="bar_foo" + GraphQLString, default_value=Undefined, out_name="bar_foo" ) } diff --git a/graphene/utils/tests/test_deduplicator.py b/graphene/utils/tests/test_deduplicator.py index b845caf19..95a70e746 100644 --- a/graphene/utils/tests/test_deduplicator.py +++ b/graphene/utils/tests/test_deduplicator.py @@ -94,6 +94,7 @@ def test_does_not_modify_input(): ], "movies": { "1198359": { + "id": "1198359", "name": "King Arthur: Legend of the Sword", "synopsis": ( "When the child Arthur's father is murdered, Vortigern, " @@ -159,7 +160,7 @@ def resolve_events(_, info): "date": "2017-05-19", "movie": { "__typename": "Movie", - "id": "TW92aWU6Tm9uZQ==", + "id": "TW92aWU6MTE5ODM1OQ==", "name": "King Arthur: Legend of the Sword", "synopsis": ( "When the child Arthur's father is murdered, Vortigern, " @@ -172,7 +173,7 @@ def resolve_events(_, info): "__typename": "Event", "id": "RXZlbnQ6MjM0", "date": "2017-05-20", - "movie": {"__typename": "Movie", "id": "TW92aWU6Tm9uZQ=="}, + "movie": {"__typename": "Movie", "id": "TW92aWU6MTE5ODM1OQ=="}, }, ] }