|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import json |
3 | 4 | from typing import Any |
4 | 5 |
|
5 | 6 | from ...models.query_params import RetrieveQueryParams, WorkItemQueryParams |
|
23 | 24 | from .work_logs import WorkLogs |
24 | 25 |
|
25 | 26 |
|
| 27 | +def _prepare_work_item_params(params: WorkItemQueryParams | None) -> dict[str, Any] | None: |
| 28 | + """Serialize WorkItemQueryParams for use as HTTP query params. |
| 29 | +
|
| 30 | + The ``filters`` field is a structured object but the API expects it as a |
| 31 | + JSON string in a single ``filters=`` query parameter. Everything else is |
| 32 | + passed through as-is by ``requests``' query-string encoder. |
| 33 | + """ |
| 34 | + if params is None: |
| 35 | + return None |
| 36 | + payload = params.model_dump(exclude_none=True) |
| 37 | + if "filters" in payload and isinstance(payload["filters"], dict): |
| 38 | + payload["filters"] = json.dumps(payload["filters"], separators=(",", ":")) |
| 39 | + return payload |
| 40 | + |
| 41 | + |
26 | 42 | class WorkItems(BaseResource): |
27 | 43 | def __init__(self, config: Any) -> None: |
28 | 44 | super().__init__(config, "/workspaces/") |
@@ -157,23 +173,67 @@ def list( |
157 | 173 | project_id: UUID of the project |
158 | 174 | params: Optional query parameters for filtering, ordering, and pagination |
159 | 175 |
|
160 | | - Example: |
161 | | - from plane.models.schemas import WorkItemQueryParams |
| 176 | + Example:: |
162 | 177 |
|
163 | | - # List work items with filters |
| 178 | + from plane.models.query_params import WorkItemQueryParams |
| 179 | +
|
| 180 | + # PQL filter (human-readable) |
| 181 | + work_items = client.work_items.list( |
| 182 | + "my-workspace", |
| 183 | + "project-id", |
| 184 | + params=WorkItemQueryParams(pql='priority = "urgent"'), |
| 185 | + ) |
| 186 | +
|
| 187 | + # Structured `filters` (JSON-encoded into the query string) |
164 | 188 | work_items = client.work_items.list( |
165 | 189 | "my-workspace", |
166 | 190 | "project-id", |
167 | 191 | params=WorkItemQueryParams( |
168 | | - priority="high", |
169 | | - state="state-id", |
170 | | - expand="assignees,labels" |
171 | | - ) |
| 192 | + filters={"and": [ |
| 193 | + {"priority": "urgent"}, |
| 194 | + {"state_group__in": ["unstarted", "started"]}, |
| 195 | + ]}, |
| 196 | + ), |
| 197 | + ) |
| 198 | + """ |
| 199 | + response = self._get( |
| 200 | + f"{workspace_slug}/projects/{project_id}/work-items", |
| 201 | + params=_prepare_work_item_params(params), |
| 202 | + ) |
| 203 | + return PaginatedWorkItemResponse.model_validate(response) |
| 204 | + |
| 205 | + def list_workspace( |
| 206 | + self, |
| 207 | + workspace_slug: str, |
| 208 | + params: WorkItemQueryParams | None = None, |
| 209 | + ) -> PaginatedWorkItemResponse: |
| 210 | + """List work items across an entire workspace. |
| 211 | +
|
| 212 | + Returns a paginated envelope of work items the caller can view, |
| 213 | + spanning every project in the workspace (per-project authorization |
| 214 | + and conditional grants are honored server-side). |
| 215 | +
|
| 216 | + Args: |
| 217 | + workspace_slug: The workspace slug identifier |
| 218 | + params: Optional query parameters — supports ``filters``, ``pql``, |
| 219 | + ``order_by``, ``cursor``, ``per_page``, ``fields``, ``expand``. |
| 220 | +
|
| 221 | + Example:: |
| 222 | +
|
| 223 | + from plane.models.query_params import WorkItemQueryParams |
| 224 | +
|
| 225 | + results = client.work_items.list_workspace( |
| 226 | + "my-workspace", |
| 227 | + params=WorkItemQueryParams( |
| 228 | + filters={"priority": "urgent"}, |
| 229 | + order_by="-created_at", |
| 230 | + per_page=50, |
| 231 | + ), |
172 | 232 | ) |
173 | 233 | """ |
174 | | - query_params = params.model_dump(exclude_none=True) if params else None |
175 | 234 | response = self._get( |
176 | | - f"{workspace_slug}/projects/{project_id}/work-items", params=query_params |
| 235 | + f"{workspace_slug}/work-items", |
| 236 | + params=_prepare_work_item_params(params), |
177 | 237 | ) |
178 | 238 | return PaginatedWorkItemResponse.model_validate(response) |
179 | 239 |
|
@@ -247,14 +307,17 @@ def list_archived( |
247 | 307 | ) -> PaginatedWorkItemResponse: |
248 | 308 | """List archived work items in a project. |
249 | 309 |
|
| 310 | + Supports the same ``filters`` and ``pql`` query parameters as |
| 311 | + :meth:`list`. |
| 312 | +
|
250 | 313 | Args: |
251 | 314 | workspace_slug: The workspace slug identifier |
252 | 315 | project_id: UUID of the project |
253 | 316 | params: Optional query parameters for filtering, ordering, and pagination |
254 | 317 | """ |
255 | | - query_params = params.model_dump(exclude_none=True) if params else None |
256 | 318 | response = self._get( |
257 | | - f"{workspace_slug}/projects/{project_id}/archived-work-items", params=query_params |
| 319 | + f"{workspace_slug}/projects/{project_id}/archived-work-items", |
| 320 | + params=_prepare_work_item_params(params), |
258 | 321 | ) |
259 | 322 | return PaginatedWorkItemResponse.model_validate(response) |
260 | 323 |
|
@@ -286,6 +349,4 @@ def unarchive(self, workspace_slug: str, project_id: str, work_item_id: str) -> |
286 | 349 | Returns: |
287 | 350 | None (HTTP 204 No Content) |
288 | 351 | """ |
289 | | - self._delete( |
290 | | - f"{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/unarchive" |
291 | | - ) |
| 352 | + self._delete(f"{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/unarchive") |
0 commit comments