Coverage for src/lanraragi_api/api/category.py: 52%
29 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-22 23:19 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-22 23:19 +0000
1from typing import Any
3from lanraragi_api.api.base import BaseAPICall
4from lanraragi_api.entity.base import OperationResponse
5from lanraragi_api.entity.category import CategoryMetadata
8class CategoryAPI(BaseAPICall):
9 """Endpoints related to Categories.
11 Shared request and error behavior is documented on ``BaseAPICall``.
12 """
14 def get_all_categories(self) -> list[CategoryMetadata]:
15 """Get all the categories saved on the server.
17 Returns:
18 list[CategoryMetadata]: Metadata of every category on the server.
20 Raises:
21 APIHttpError: Any non-2xx status code returned by the server.
22 APIResponseDecodeError: If the response body is not a list of
23 objects matching ``CategoryMetadata``.
24 """
25 return self.request_model_list("GET", "/api/categories", CategoryMetadata)
27 def get_category(self, id: str) -> CategoryMetadata | None:
28 """Get the details of the specified category ID.
30 Args:
31 id: ID of the Category desired.
33 Returns:
34 CategoryMetadata | None: Details of the category, or None when the
35 server answers with 400.
37 Raises:
38 APIHttpError: Any status code other than 200 and 400.
40 Note:
41 The 400 response of the server is turned into None instead of
42 raising.
43 """
44 path = f"/api/categories/{id}"
45 resp = self.request("GET", path, expected_statuses={200, 400})
46 if resp.status_code == 400:
47 return None
48 return self.parse_model(
49 CategoryMetadata, self.parse_json_response(resp, path), path
50 )
52 def create_category(
53 self, name: str, search: str | None = None, pinned: bool | None = None
54 ) -> OperationResponse:
55 """Create a new Category.
57 Args:
58 name: Name of the Category.
59 search: Matching predicate, if creating a Dynamic Category.
60 Defaults to None.
61 pinned: Whether the created category will be pinned. Defaults to
62 None.
64 Returns:
65 OperationResponse: Result of the operation, with the ID of the
66 created Category in the extra ``category_id`` field.
68 Raises:
69 APIResponseDecodeError: If the response body is not valid JSON, or
70 does not match ``OperationResponse``.
71 APIOperationError: If the operation failed and raising is enabled.
73 Note:
74 A 400 response is returned in the operation result, with ``success``
75 set to 0, instead of raising.
76 """
77 return self.request_operation(
78 "PUT",
79 "/api/categories",
80 data={
81 "name": name,
82 "search": search,
83 "pinned": pinned,
84 },
85 )
87 def update_category(
88 self,
89 id: str,
90 name: str | None = None,
91 search: str | None = None,
92 pinned: bool | None = None,
93 ) -> OperationResponse:
94 """Modify a Category.
96 Args:
97 id: ID of the Category to update.
98 name: New name of the Category. Defaults to None.
99 search: Predicate. Trying to add a predicate to a category that
100 already contains Archives will give you an error. Defaults to
101 None.
102 pinned: Add this argument to pin the Category. If you don't, the
103 category will be unpinned on update. Defaults to None.
105 Returns:
106 OperationResponse: Result of the operation, with the ID of the
107 updated Category in the extra ``category_id`` field.
109 Raises:
110 APIResponseDecodeError: If the response body is not valid JSON, or
111 does not match ``OperationResponse``.
112 APIOperationError: If the operation failed and raising is enabled.
114 Note:
115 Failure responses such as 400 (error response) or 423 (Category
116 locked for modification) are returned in the operation result, with
117 ``success`` set to 0, instead of raising.
118 """
119 return self.request_operation(
120 "PUT",
121 f"/api/categories/{id}",
122 data={
123 "name": name,
124 "search": search,
125 "pinned": pinned,
126 },
127 )
129 def delete_category(self, id: str) -> OperationResponse:
130 """Remove a Category.
132 Args:
133 id: Category ID.
135 Returns:
136 OperationResponse: Result of the operation.
138 Raises:
139 APIResponseDecodeError: If the response body is not valid JSON, or
140 does not match ``OperationResponse``.
141 APIOperationError: If the operation failed and raising is enabled.
143 Note:
144 A 423 response, sent when the Category is locked for modification,
145 is returned in the operation result, with ``success`` set to 0,
146 instead of raising.
147 """
148 return self.request_operation("DELETE", f"/api/categories/{id}")
150 def add_archive_to_category(
151 self, category_id: str, archive_id: str
152 ) -> OperationResponse:
153 """Add the specified Archive ID (see Archive API) to the given Category.
155 Args:
156 category_id: Category ID to add the Archive to.
157 archive_id: Archive ID to add.
159 Returns:
160 OperationResponse: Result of the operation.
162 Raises:
163 APIResponseDecodeError: If the response body is not valid JSON, or
164 does not match ``OperationResponse``.
165 APIOperationError: If the operation failed and raising is enabled.
167 Note:
168 A 423 response, sent when the Category is locked for modification,
169 is returned in the operation result, with ``success`` set to 0,
170 instead of raising.
171 """
172 return self.request_operation(
173 "PUT", f"/api/categories/{category_id}/{archive_id}"
174 )
176 def remove_archive_from_category(
177 self, category_id: str, archive_id: str
178 ) -> OperationResponse:
179 """Remove an Archive ID from a Category.
181 Args:
182 category_id: Category ID.
183 archive_id: Archive ID.
185 Returns:
186 OperationResponse: Result of the operation.
188 Raises:
189 APIResponseDecodeError: If the response body is not valid JSON, or
190 does not match ``OperationResponse``.
191 APIOperationError: If the operation failed and raising is enabled.
193 Note:
194 A 423 response, sent when the Category is locked for modification,
195 is returned in the operation result, with ``success`` set to 0,
196 instead of raising.
197 """
198 return self.request_operation(
199 "DELETE", f"/api/categories/{category_id}/{archive_id}"
200 )
202 def get_bookmark_link(self) -> dict[Any, Any]:
203 """Retrieve the ID of the category linked to the bookmark feature.
205 Returns:
206 dict: Decoded response, with the linked category ID in
207 ``category_id``, as an empty string if no category is linked.
209 Raises:
210 APIHttpError: Any non-2xx status code returned by the server.
211 APIResponseDecodeError: If the response body is not valid JSON.
212 """
213 return self.request_json("GET", "/api/categories/bookmark_link")
215 def update_bookmark_link(self, id: str) -> OperationResponse:
216 """Link the bookmark feature to the specified static category.
218 This determines which category archives are added to when using the
219 bookmark button.
221 Args:
222 id: ID of the static category to link with the bookmark feature.
224 Returns:
225 OperationResponse: Result of the operation, with the ID of the
226 linked category in the extra ``category_id`` field.
228 Raises:
229 APIResponseDecodeError: If the response body is not valid JSON, or
230 does not match ``OperationResponse``.
231 APIOperationError: If the operation failed and raising is enabled.
233 Note:
234 Failure responses such as 400 (error response) or 404 (no category
235 with the given ID) are returned in the operation result, with
236 ``success`` set to 0, instead of raising.
237 """
238 return self.request_operation("PUT", f"/api/categories/bookmark_link/{id}")
240 def disable_bookmark_feature(self) -> OperationResponse:
241 """Disable the bookmark feature by removing the link to any category.
243 Returns:
244 OperationResponse: Result of the operation, with the ID of the
245 previously linked category in the extra ``category_id`` field,
246 as an empty string if no category was linked.
248 Raises:
249 APIResponseDecodeError: If the response body is not valid JSON, or
250 does not match ``OperationResponse``.
251 APIOperationError: If the operation failed and raising is enabled.
252 """
253 return self.request_operation("DELETE", "/api/categories/bookmark_link")