1
1
from asyncio import sleep
2
- from typing import Any , Dict , List , NamedTuple
2
+ from typing import Any , AsyncGenerator , Dict , List , NamedTuple
3
3
4
4
import pytest
5
5
from graphql .error import GraphQLError
15
15
)
16
16
from graphql .execution .execute import DeferredFragmentRecord
17
17
from graphql .language import DocumentNode , parse
18
- from graphql .pyutils import Path
18
+ from graphql .pyutils import Path , is_awaitable
19
19
from graphql .type import (
20
20
GraphQLField ,
21
21
GraphQLID ,
26
26
GraphQLString ,
27
27
)
28
28
29
+
30
+ def resolve_null_sync (_obj , _info ) -> None :
31
+ """A resolver returning a null value synchronously."""
32
+ return
33
+
34
+
35
+ async def resolve_null_async (_obj , _info ) -> None :
36
+ """A resolver returning a null value asynchronously."""
37
+ return
38
+
39
+
29
40
friend_type = GraphQLObjectType (
30
- "Friend" , {"id" : GraphQLField (GraphQLID ), "name" : GraphQLField (GraphQLString )}
41
+ "Friend" ,
42
+ {
43
+ "id" : GraphQLField (GraphQLID ),
44
+ "name" : GraphQLField (GraphQLString ),
45
+ "asyncNonNullErrorField" : GraphQLField (
46
+ GraphQLNonNull (GraphQLString ), resolve = resolve_null_async
47
+ ),
48
+ },
31
49
)
32
50
33
51
34
52
class Friend (NamedTuple ):
35
- name : str
36
53
id : int
54
+ name : str
37
55
38
56
39
- friends = [Friend ("Han" , 2 ), Friend ("Leia" , 3 ), Friend ("C-3PO" , 4 )]
57
+ friends = [Friend (2 , "Han" ), Friend (3 , "Leia" ), Friend (4 , "C-3PO" )]
40
58
41
59
42
60
async def resolve_slow (_obj , _info ) -> str :
@@ -50,14 +68,10 @@ async def resolve_bad(_obj, _info) -> str:
50
68
raise RuntimeError ("bad" )
51
69
52
70
53
- def resolve_null_sync (_obj , _info ) -> None :
54
- """Simulate a resolver returning a null value synchronously."""
55
- return
56
-
57
-
58
- async def resolve_null_async (_obj , _info ) -> None :
59
- """Simulate a resolver returning a null value asynchronously."""
60
- return
71
+ async def resolve_friends_async (_obj , _info ) -> AsyncGenerator [Friend , None ]:
72
+ """A slow async generator yielding the first friend."""
73
+ await sleep (0 )
74
+ yield friends [0 ]
61
75
62
76
63
77
hero_type = GraphQLObjectType (
@@ -76,10 +90,13 @@ async def resolve_null_async(_obj, _info) -> None:
76
90
"friends" : GraphQLField (
77
91
GraphQLList (friend_type ), resolve = lambda _obj , _info : friends
78
92
),
93
+ "asyncFriends" : GraphQLField (
94
+ GraphQLList (friend_type ), resolve = resolve_friends_async
95
+ ),
79
96
},
80
97
)
81
98
82
- hero = Friend ("Luke" , 1 )
99
+ hero = Friend (1 , "Luke" )
83
100
84
101
query = GraphQLObjectType (
85
102
"Query" , {"hero" : GraphQLField (hero_type , resolve = lambda _obj , _info : hero )}
@@ -90,6 +107,8 @@ async def resolve_null_async(_obj, _info) -> None:
90
107
91
108
async def complete (document : DocumentNode , root_value : Any = None ) -> Any :
92
109
result = experimental_execute_incrementally (schema , document , root_value )
110
+ if is_awaitable (result ):
111
+ result = await result
93
112
94
113
if isinstance (result , ExperimentalIncrementalExecutionResults ):
95
114
results : List [Any ] = [result .initial_result .formatted ]
@@ -882,6 +901,38 @@ async def returns_payloads_from_synchronous_data_in_correct_order():
882
901
},
883
902
]
884
903
904
+ @pytest .mark .asyncio ()
905
+ async def filters_deferred_payloads_when_list_item_from_async_iterable_nulled ():
906
+ document = parse (
907
+ """
908
+ query {
909
+ hero {
910
+ asyncFriends {
911
+ asyncNonNullErrorField
912
+ ...NameFragment @defer
913
+ }
914
+ }
915
+ }
916
+ fragment NameFragment on Friend {
917
+ name
918
+ }
919
+ """
920
+ )
921
+
922
+ result = await complete (document )
923
+
924
+ assert result == {
925
+ "data" : {"hero" : {"asyncFriends" : [None ]}},
926
+ "errors" : [
927
+ {
928
+ "message" : "Cannot return null for non-nullable field"
929
+ " Friend.asyncNonNullErrorField." ,
930
+ "locations" : [{"line" : 5 , "column" : 19 }],
931
+ "path" : ["hero" , "asyncFriends" , 0 , "asyncNonNullErrorField" ],
932
+ }
933
+ ],
934
+ }
935
+
885
936
@pytest .mark .asyncio ()
886
937
async def original_execute_function_throws_error_if_deferred_and_all_is_sync ():
887
938
document = parse (
@@ -918,7 +969,8 @@ async def original_execute_function_throws_error_if_deferred_and_not_all_is_sync
918
969
[
919
970
{
920
971
"message" : "Executing this GraphQL operation would unexpectedly"
921
- " produce multiple payloads (due to @defer or @stream directive)"
972
+ " produce multiple payloads"
973
+ " (due to @defer or @stream directive)"
922
974
}
923
975
],
924
976
)
0 commit comments