diff --git a/graphql/execution/tests/test_execute_schema.py b/graphql/execution/tests/test_execute_schema.py index 0c7e1033..621c4ae7 100644 --- a/graphql/execution/tests/test_execute_schema.py +++ b/graphql/execution/tests/test_execute_schema.py @@ -1,5 +1,7 @@ # type: ignore +from itertools import starmap, repeat +from typing import Union from graphql.execution import execute from graphql.language.parser import parse from graphql.type import ( @@ -51,6 +53,7 @@ def test_executes_using_a_schema(): { "id": GraphQLField(GraphQLNonNull(GraphQLString)), "isPublished": GraphQLField(GraphQLBoolean), + "topic": GraphQLField(GraphQLString), "author": GraphQLField(BlogAuthor), "title": GraphQLField(GraphQLString), "body": GraphQLField(GraphQLString), @@ -58,29 +61,36 @@ def test_executes_using_a_schema(): }, ) + def _resolve_article(obj, info, id, topic): + return Article(id, topic) + + def _resolve_feed(*_): + return list(starmap(Article, zip(range(1, 10 + 1), repeat("food")))) + BlogQuery = GraphQLObjectType( "Query", { "article": GraphQLField( BlogArticle, - args={"id": GraphQLArgument(GraphQLID)}, - resolver=lambda obj, info, **args: Article(args["id"]), - ), - "feed": GraphQLField( - GraphQLList(BlogArticle), - resolver=lambda *_: map(Article, range(1, 10 + 1)), + args={ + "id": GraphQLArgument(GraphQLID), + "topic": GraphQLArgument(GraphQLNonNull(GraphQLString)), + }, + resolver=_resolve_article, ), + "feed": GraphQLField(GraphQLList(BlogArticle), resolver=_resolve_feed), }, ) BlogSchema = GraphQLSchema(BlogQuery) class Article(object): - def __init__(self, id): - # type: (int) -> None + def __init__(self, id, topic): + # type: (int, Union[str, None]) -> None self.id = id self.isPublished = True self.author = Author() + self.topic = "My topic is {}".format(topic or "null") self.title = "My Article {}".format(id) self.body = "This is a post" self.hidden = "This data is not exposed in the schema" @@ -97,7 +107,7 @@ def pic(self, width, height): @property def recentArticle(self): # type: () -> Article - return Article(1) + return Article(1, "food") class Pic(object): def __init__(self, uid, width, height): @@ -112,7 +122,7 @@ def __init__(self, uid, width, height): id, title }, - article(id: "1") { + article(id: "1", topic: null) { ...articleFields, author { id, @@ -132,6 +142,7 @@ def __init__(self, uid, width, height): fragment articleFields on Article { id, isPublished, + topic, title, body, hidden, @@ -159,6 +170,7 @@ def __init__(self, uid, width, height): "article": { "id": "1", "isPublished": True, + "topic": "My topic is null", "title": "My Article 1", "body": "This is a post", "author": { @@ -168,6 +180,7 @@ def __init__(self, uid, width, height): "recentArticle": { "id": "1", "isPublished": True, + "topic": "My topic is food", "title": "My Article 1", "body": "This is a post", "keywords": ["foo", "bar", "1", "true", None], diff --git a/graphql/execution/tests/test_mutations.py b/graphql/execution/tests/test_mutations.py index d7ec17bc..b19088ca 100644 --- a/graphql/execution/tests/test_mutations.py +++ b/graphql/execution/tests/test_mutations.py @@ -107,7 +107,16 @@ def assert_evaluate_mutations_serially(executor=None): }, fifth: immediatelyChangeTheNumber(newNumber: 5) { theNumber - } + }, + sixth: immediatelyChangeTheNumber(newNumber: null) { + theNumber + }, + seventh: immediatelyChangeTheNumber(newNumber: 100) { + theNumber + }, + eighth: immediatelyChangeTheNumber(newNumber: null) { + theNumber + }, }""" ast = parse(doc) result = execute(schema, ast, Root(6), operation_name="M", executor=executor) @@ -118,6 +127,9 @@ def assert_evaluate_mutations_serially(executor=None): "third": {"theNumber": 3}, "fourth": {"theNumber": 4}, "fifth": {"theNumber": 5}, + "sixth": {"theNumber": None}, + "seventh": {"theNumber": 100}, + "eighth": {"theNumber": None}, } diff --git a/graphql/execution/tests/test_resolve.py b/graphql/execution/tests/test_resolve.py index d6788f38..a9e97ea2 100644 --- a/graphql/execution/tests/test_resolve.py +++ b/graphql/execution/tests/test_resolve.py @@ -49,6 +49,27 @@ def _test_schema(test_field): ) +def test_explicit_null_is_passed_to_resolver(): + def resolver(_, __, maybe_string): + return 'maybe_string is "{}"'.format(maybe_string or "null") + + schema = _test_schema( + GraphQLField( + GraphQLString, + args=OrderedDict([("maybe_string", GraphQLArgument(GraphQLString))]), + resolver=resolver, + ) + ) + + result = graphql(schema, '{ test(maybe_string: "Cool") }') + assert not result.errors + assert result.data == {"test": 'maybe_string is "Cool"'} + + result = graphql(schema, "{ test(maybe_string: null) }") + assert not result.errors + assert result.data == {"test": 'maybe_string is "null"'} + + def test_default_function_accesses_properties(): # type: () -> None schema = _test_schema(GraphQLField(GraphQLString)) diff --git a/graphql/execution/values.py b/graphql/execution/values.py index def1820e..a5cd7662 100644 --- a/graphql/execution/values.py +++ b/graphql/execution/values.py @@ -134,15 +134,16 @@ def get_argument_values( continue else: - value = value_from_ast(arg_ast.value, arg_type, variables) # type: ignore - if value is None: + arg_name = arg_def.out_name or name # type: ignore + arg_ast_value = arg_ast.value # type: ignore + value = value_from_ast(arg_ast_value, arg_type, variables) # type: ignore + if value is None and not isinstance(arg_ast_value, ast.NullValue): if arg_def.default_value is not Undefined: - value = arg_def.default_value - result[arg_def.out_name or name] = value + result[arg_name] = arg_def.default_value + else: + result[arg_name] = None else: - # We use out_name as the output name for the - # dict if exists - result[arg_def.out_name or name] = value + result[arg_name] = value return result