Coverage for src/lexigram/graphql/pagination/types.py: 100%
48 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
1"""Data structures used for GraphQL pagination.
3This module defines the dataclasses representing edges, page info,
4connections and input/request objects. They were previously defined in the
5package root but have been moved here for a cleaner API surface.
6"""
8from __future__ import annotations
10from dataclasses import dataclass
11from typing import Generic, TypeVar
13T = TypeVar("T")
16@dataclass
17class Edge(Generic[T]):
18 """An edge in a connection."""
20 node: T
21 cursor: str
24@dataclass
25class PageInfo:
26 """Pagination information."""
28 has_next_page: bool
29 has_previous_page: bool
30 start_cursor: str | None = None
31 end_cursor: str | None = None
34@dataclass
35class CursorConnection(Generic[T]):
36 """A connection for cursor-based pagination."""
38 edges: list[Edge[T]]
39 page_info: PageInfo
42@dataclass
43class CursorPaginationInput:
44 """Input for cursor-based pagination.
46 Attributes:
47 first: Number of items to return (forward pagination).
48 after: Cursor to start after (forward pagination).
49 last: Number of items to return (backward pagination).
50 before: Cursor to start before (backward pagination).
51 """
53 first: int | None = None
54 after: str | None = None
55 last: int | None = None
56 before: str | None = None
58 def validate(self) -> None:
59 """Validate pagination input.
61 Raises:
62 ValueError: If the input is invalid.
63 """
64 if self.first is not None and self.first < 0:
65 raise ValueError("first must be non-negative")
66 if self.last is not None and self.last < 0:
67 raise ValueError("last must be non-negative")
68 if self.first is not None and self.last is not None:
69 raise ValueError("Cannot specify both first and last")
70 if self.after is not None and self.before is not None:
71 raise ValueError("Cannot specify both after and before")
74@dataclass
75class OffsetPaginationInput:
76 """Input for offset-based pagination.
78 Attributes:
79 offset: Number of items to skip.
80 limit: Maximum number of items to return.
81 """
83 offset: int = 0
84 limit: int = 10
86 def validate(self) -> None:
87 """Validate pagination input.
89 Raises:
90 ValueError: If the input is invalid.
91 """
92 if self.offset < 0:
93 raise ValueError("offset must be non-negative")
94 if self.limit < 0:
95 raise ValueError("limit must be non-negative")
98@dataclass
99class PaginationResult(Generic[T]):
100 """Result of pagination operation.
102 Attributes:
103 items: The paginated items.
104 total_count: Total number of items available.
105 has_next: Whether there are more items after this page.
106 has_previous: Whether there are items before this page.
107 """
109 items: list[T]
110 total_count: int
111 has_next: bool = False
112 has_previous: bool = False