Coverage for src/lexigram/graphql/schema/types.py: 81%

79 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 04:37 +0800

1"""GraphQL type helpers. 

2 

3This module provides type aliases and helpers for defining 

4GraphQL types using Strawberry. 

5""" 

6 

7from __future__ import annotations 

8 

9from typing import Any, Generic, TypeVar, cast 

10 

11import strawberry 

12 

13T = TypeVar("T") 

14 

15 

16# Re-export Strawberry type decorators for convenience 

17ObjectType: Any = strawberry.type 

18InputType: Any = strawberry.input 

19InterfaceType: Any = strawberry.interface 

20EnumType: Any = strawberry.enum 

21ScalarType: Any = strawberry.scalar 

22 

23 

24def union_type(*types: Any, name: str | None = None) -> Any: 

25 """Create a GraphQL union type. 

26 

27 Args: 

28 types: Types to include in the union. 

29 name: Optional name for the union. 

30 

31 Returns: 

32 Union type annotation. 

33 

34 Example: 

35 ```python 

36 SearchResult = union_type(User, Post, Comment, name="SearchResult") 

37 

38 @strawberry.type 

39 class Query: 

40 @strawberry.field 

41 def search(self, query: str) -> list[SearchResult]: 

42 ... 

43 ``` 

44 """ 

45 return strawberry.union(name or "Union", types=types) # type: ignore[call-arg] 

46 

47 

48# Generic Connection class - NOT a Strawberry type, just a type hint 

49# Use create_connection_type() to create concrete Connection types 

50class Connection(Generic[T]): 

51 """GraphQL Connection type template for cursor-based pagination. 

52 

53 This is a generic template. Use create_connection_type() to create 

54 concrete Connection types for your node types. 

55 

56 Attributes: 

57 edges: List of edges containing nodes. 

58 page_info: Pagination information. 

59 total_count: Total number of items. 

60 """ 

61 

62 class Edge: 

63 """Connection edge template.""" 

64 

65 node: Any 

66 cursor: str 

67 

68 class PageInfo: 

69 """Pagination information.""" 

70 

71 has_next_page: bool 

72 has_previous_page: bool 

73 start_cursor: str | None 

74 end_cursor: str | None 

75 

76 edges: list[Any] 

77 page_info: Any # Will be PageInfo type 

78 total_count: int = 0 

79 

80 

81# Concrete PageInfo type for use in typed connections 

82@strawberry.type 

83class ConnectionPageInfo: 

84 """Pagination information for connections.""" 

85 

86 has_next_page: bool 

87 has_previous_page: bool 

88 start_cursor: str | None = None 

89 end_cursor: str | None = None 

90 

91 

92@strawberry.type 

93class PagedResult(Generic[T]): 

94 """Simple offset-based pagination result. 

95 

96 Attributes: 

97 items: List of items in the current page. 

98 total: Total number of items. 

99 page: Current page number (1-indexed). 

100 page_size: Number of items per page. 

101 has_next: Whether there are more pages. 

102 has_previous: Whether there are previous pages. 

103 """ 

104 

105 items: list[T] 

106 total: int 

107 page: int 

108 page_size: int 

109 has_next: bool = False 

110 has_previous: bool = False 

111 

112 @property 

113 def total_pages(self) -> int: 

114 """Calculate total number of pages.""" 

115 if self.page_size <= 0: 

116 return 0 

117 return (self.total + self.page_size - 1) // self.page_size 

118 

119 

120@strawberry.input 

121class PaginationInput: 

122 """Input for pagination parameters. 

123 

124 Attributes: 

125 page: Page number (1-indexed). 

126 page_size: Number of items per page. 

127 """ 

128 

129 page: int = 1 

130 page_size: int = 20 

131 

132 

133@strawberry.input 

134class CursorPaginationInput: 

135 """Input for cursor-based pagination. 

136 

137 Attributes: 

138 first: Number of items to fetch from the start. 

139 after: Cursor to fetch items after. 

140 last: Number of items to fetch from the end. 

141 before: Cursor to fetch items before. 

142 """ 

143 

144 first: int | None = None 

145 after: str | None = None 

146 last: int | None = None 

147 before: str | None = None 

148 

149 

150@strawberry.input 

151class SortInput: 

152 """Input for sorting. 

153 

154 Attributes: 

155 field: Field to sort by. 

156 direction: Sort direction (ASC or DESC). 

157 """ 

158 

159 field: str 

160 direction: str = "ASC" 

161 

162 

163@strawberry.type 

164class MutationResult(Generic[T]): 

165 """Standard mutation result type. 

166 

167 Attributes: 

168 success: Whether the mutation succeeded. 

169 data: Result data if successful. 

170 errors: Error messages if failed. 

171 """ 

172 

173 success: bool 

174 data: T | None = None 

175 errors: list[str] = strawberry.field(default_factory=list) 

176 

177 

178@strawberry.type 

179class DeleteResult: 

180 """Result of a delete mutation. 

181 

182 Attributes: 

183 success: Whether deletion succeeded. 

184 id: ID of deleted item. 

185 message: Optional message. 

186 """ 

187 

188 success: bool 

189 id: str | None = None 

190 message: str | None = None 

191 

192 

193def create_connection_type( 

194 node_type: type[T], 

195 name: str | None = None, 

196) -> type[Connection[T]]: 

197 """Create a Connection type for a specific node type. 

198 

199 Args: 

200 node_type: The node type for the connection. 

201 name: Optional name prefix for the connection type. 

202 

203 Returns: 

204 A Connection type class. 

205 """ 

206 type_name = name or node_type.__name__ 

207 

208 @strawberry.type(name=f"{type_name}Edge") 

209 class TypedEdge: 

210 node: Any # node_type used at runtime; annotate as Any for typing 

211 cursor: str 

212 

213 @strawberry.type(name=f"{type_name}Connection") 

214 class TypedConnection: 

215 edges: list[TypedEdge] 

216 page_info: ConnectionPageInfo 

217 total_count: int = 0 

218 

219 # type so callers and mypy treat this as Connection[T]. 

220 return cast("type[Connection[T]]", TypedConnection) 

221 

222 

223__all__ = [ 

224 "Connection", 

225 "ConnectionPageInfo", 

226 "CursorPaginationInput", 

227 "DeleteResult", 

228 "EnumType", 

229 "InputType", 

230 "InterfaceType", 

231 "MutationResult", 

232 "ObjectType", 

233 "PagedResult", 

234 "PaginationInput", 

235 "ScalarType", 

236 "SortInput", 

237 "create_connection_type", 

238 "union_type", 

239]