Skip to content

Commit fa1ddbe

Browse files
fix: handle lazy connection type (#3941)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 2d92ecc commit fa1ddbe

File tree

3 files changed

+105
-7
lines changed

3 files changed

+105
-7
lines changed

RELEASE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Release type: patch
2+
3+
This release adds support for lazy types in ConnectionExtension.

strawberry/relay/fields.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,12 @@ def apply(self, field: StrawberryField) -> None:
261261
if isinstance(f_type, StrawberryOptional):
262262
f_type = f_type.of_type
263263

264+
if isinstance(f_type, LazyType):
265+
f_type = f_type.resolve_type()
266+
264267
type_origin = get_origin(f_type) if is_generic_alias(f_type) else f_type
268+
if isinstance(type_origin, LazyType):
269+
type_origin = type_origin.resolve_type()
265270

266271
if not isinstance(type_origin, type) or not issubclass(type_origin, Connection):
267272
raise RelayWrongAnnotationError(field.name, cast("type", field.origin))

tests/relay/test_connection.py

Lines changed: 97 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import sys
22
from collections.abc import Iterable
3-
from typing import Any, Optional
3+
from typing import Annotated, Any, Optional
44
from typing_extensions import Self
55

66
import pytest
77

88
import strawberry
99
from strawberry.permission import BasePermission
10-
from strawberry.relay import Connection, Node
11-
from strawberry.relay.types import ListConnection
10+
from strawberry.relay import Connection, Node, PageInfo, to_base64
11+
from strawberry.relay.types import Edge, ListConnection
1212
from strawberry.schema.config import StrawberryConfig
1313

1414

@@ -25,7 +25,7 @@ def resolve_nodes(
2525

2626

2727
@strawberry.type
28-
class UserConnection(Connection[User]):
28+
class EmptyUserConnection(Connection[User]):
2929
@classmethod
3030
def resolve_connection(
3131
cls,
@@ -42,6 +42,33 @@ def resolve_connection(
4242
return None
4343

4444

45+
@strawberry.type
46+
class UserConnection(Connection[User]):
47+
@classmethod
48+
def resolve_connection(
49+
cls,
50+
nodes: Iterable[User],
51+
*,
52+
info: Any,
53+
after: Optional[str] = None,
54+
before: Optional[str] = None,
55+
first: Optional[int] = None,
56+
last: Optional[int] = None,
57+
max_results: Optional[int] = None,
58+
**kwargs: Any,
59+
) -> Optional[Self]:
60+
user_node_id = to_base64(User, "1")
61+
return cls(
62+
page_info=PageInfo(
63+
has_next_page=False,
64+
has_previous_page=False,
65+
start_cursor=None,
66+
end_cursor=None,
67+
),
68+
edges=[Edge(cursor=user_node_id, node=User(id=user_node_id))],
69+
)
70+
71+
4572
class TestPermission(BasePermission):
4673
message = "Not allowed"
4774

@@ -52,7 +79,7 @@ def has_permission(self, source, info, **kwargs: Any):
5279
def test_nullable_connection_with_optional():
5380
@strawberry.type
5481
class Query:
55-
@strawberry.relay.connection(Optional[UserConnection])
82+
@strawberry.relay.connection(Optional[EmptyUserConnection])
5683
def users(self) -> Optional[list[User]]:
5784
return None
5885

@@ -74,14 +101,77 @@ def users(self) -> Optional[list[User]]:
74101
assert not result.errors
75102

76103

104+
def test_lazy_connection():
105+
@strawberry.type
106+
class Query:
107+
@strawberry.relay.connection(
108+
Optional[
109+
Annotated[
110+
"UserConnection", strawberry.lazy("tests.relay.test_connection")
111+
]
112+
]
113+
)
114+
def users(self) -> Optional[list[User]]:
115+
return None
116+
117+
schema = strawberry.Schema(query=Query)
118+
query = """
119+
query {
120+
users {
121+
edges {
122+
node {
123+
name
124+
}
125+
}
126+
}
127+
}
128+
"""
129+
130+
result = schema.execute_sync(query)
131+
assert result.data == {"users": {"edges": [{"node": {"name": "John"}}]}}
132+
assert not result.errors
133+
134+
135+
def test_lazy_optional_connection():
136+
@strawberry.type
137+
class Query:
138+
@strawberry.relay.connection(
139+
Optional[
140+
Annotated[
141+
"EmptyUserConnection",
142+
strawberry.lazy("tests.relay.test_connection"),
143+
]
144+
]
145+
)
146+
def users(self) -> Optional[list[User]]:
147+
return None
148+
149+
schema = strawberry.Schema(query=Query)
150+
query = """
151+
query {
152+
users {
153+
edges {
154+
node {
155+
name
156+
}
157+
}
158+
}
159+
}
160+
"""
161+
162+
result = schema.execute_sync(query)
163+
assert result.data == {"users": None}
164+
assert not result.errors
165+
166+
77167
@pytest.mark.skipif(
78168
sys.version_info < (3, 10),
79169
reason="pipe syntax for union is only available on python 3.10+",
80170
)
81171
def test_nullable_connection_with_pipe():
82172
@strawberry.type
83173
class Query:
84-
@strawberry.relay.connection(UserConnection | None)
174+
@strawberry.relay.connection(EmptyUserConnection | None)
85175
def users(self) -> list[User] | None:
86176
return None
87177

@@ -107,7 +197,7 @@ def test_nullable_connection_with_permission():
107197
@strawberry.type
108198
class Query:
109199
@strawberry.relay.connection(
110-
Optional[UserConnection], permission_classes=[TestPermission]
200+
Optional[EmptyUserConnection], permission_classes=[TestPermission]
111201
)
112202
def users(self) -> Optional[list[User]]: # pragma: no cover
113203
pytest.fail("Should not have been called...")

0 commit comments

Comments
 (0)