From d55f45ea8e0ec5f9d21f18b118824ae4a377ae22 Mon Sep 17 00:00:00 2001 From: Dheeraj Kumar Ketireddy Date: Tue, 26 May 2026 14:22:44 +0530 Subject: [PATCH 1/3] feat: rich `filters` + `pql` on work-item list endpoints, add `list_workspace` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the new external API's structured filtering capability on every work-item list method in the SDK. - WorkItemQueryParams now accepts `filters: dict[str, Any] | None` (the existing `pql: str | None` field is unchanged). A small `_prepare_work_item_params` helper JSON-encodes `filters` into the `filters=` query string the API expects. - New `WorkItems.list_workspace(workspace_slug, params)` calling `GET /workspaces//work-items/` — paginated, total_results, spans every project the caller can view. - `WorkItems.list` and `WorkItems.list_archived` now route through `_prepare_work_item_params`, so `filters` and `pql` work uniformly. - `Cycles.list_work_items` and `Modules.list_work_items` accept `WorkItemQueryParams` (typed path) with the same serialization, and still accept a plain `Mapping[str, Any]` for backwards compatibility. - Real-API unit tests for `filters` on `list`, plus `list_workspace` with and without `filters`. - Bump version to 0.2.13. Backend: makeplane/plane-ee#7376 Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 48 ++++++++++++++++++- plane/api/cycles.py | 26 ++++++---- plane/api/modules.py | 17 +++++-- plane/api/work_items/base.py | 89 +++++++++++++++++++++++++++++------ plane/models/query_params.py | 22 ++++++++- pyproject.toml | 2 +- tests/unit/test_work_items.py | 71 ++++++++++++++++++++++++++++ 7 files changed, 245 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index d5aacf5..47a91ab 100644 --- a/README.md +++ b/README.md @@ -748,9 +748,55 @@ The SDK provides comprehensive Pydantic v2 models for all API operations. - `BaseQueryParams` - Base query parameters - `PaginatedQueryParams` - Pagination support (per_page, page) -- `WorkItemQueryParams` - Work item specific queries (expand, order_by, etc.) +- `WorkItemQueryParams` - Work item specific queries (expand, order_by, `filters`, `pql`, etc.) - `RetrieveQueryParams` - Retrieve operations (expand, fields, etc.) +#### Filtering work items + +`WorkItemQueryParams` accepts two filter inputs that map to the same backend filter engine: + +- **`filters`** — a structured filter expression (dict). Supports nested + `and` / `or` / `not` groups and field operators (`__in`, `__gte`, + `__range`, `__icontains`, etc.). The SDK JSON-encodes this into the + `filters=` query parameter. +- **`pql`** — a Plane Query Language string. Human-readable alternative + with the same expressive power. + +```python +from plane.models.query_params import WorkItemQueryParams + +# Project-scoped, structured filters +client.work_items.list( + "my-workspace", + "project-id", + params=WorkItemQueryParams( + filters={"and": [ + {"priority": "urgent"}, + {"state_group__in": ["unstarted", "started"]}, + ]}, + order_by="-created_at", + per_page=50, + ), +) + +# Project-scoped, PQL +client.work_items.list( + "my-workspace", + "project-id", + params=WorkItemQueryParams(pql='priority = "urgent" AND assignee = currentUser()'), +) + +# Workspace-scoped — spans every project the caller can view, with +# per-project authorization honored server-side +client.work_items.list_workspace( + "my-workspace", + params=WorkItemQueryParams(filters={"priority": "urgent"}), +) +``` + +The same `filters` and `pql` query parameters also work on `list_archived`, +`cycles.list_work_items`, and `modules.list_work_items`. + ### Response Models Paginated responses follow the pattern `PaginatedResponse` and include: diff --git a/plane/api/cycles.py b/plane/api/cycles.py index 44edda0..48ecc4e 100644 --- a/plane/api/cycles.py +++ b/plane/api/cycles.py @@ -10,7 +10,9 @@ TransferCycleWorkItemsRequest, UpdateCycle, ) +from ..models.query_params import WorkItemQueryParams from .base_resource import BaseResource +from .work_items.base import _prepare_work_item_params class Cycles(BaseResource): @@ -137,18 +139,28 @@ def list_work_items( workspace_slug: str, project_id: str, cycle_id: str, - params: Mapping[str, Any] | None = None, + params: WorkItemQueryParams | Mapping[str, Any] | None = None, ) -> PaginatedCycleWorkItemResponse: """List work items in a cycle. + Supports the same ``filters`` and ``pql`` query parameters as + :meth:`WorkItems.list`. + Args: workspace_slug: The workspace slug identifier project_id: UUID of the project cycle_id: UUID of the cycle - params: Optional query parameters + params: Optional query parameters. Prefer ``WorkItemQueryParams``; + a plain mapping is also accepted for backwards compatibility + and is passed through unchanged. """ + if isinstance(params, WorkItemQueryParams): + query_params: Mapping[str, Any] | None = _prepare_work_item_params(params) + else: + query_params = params response = self._get( - f"{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/cycle-issues", params=params + f"{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/cycle-issues", + params=query_params, ) return PaginatedCycleWorkItemResponse.model_validate(response) @@ -180,9 +192,7 @@ def archive(self, workspace_slug: str, project_id: str, cycle_id: str) -> bool: project_id: UUID of the project cycle_id: UUID of the cycle """ - self._post( - f"{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/archive", {} - ) + self._post(f"{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/archive", {}) return True def unarchive(self, workspace_slug: str, project_id: str, cycle_id: str) -> bool: @@ -193,7 +203,5 @@ def unarchive(self, workspace_slug: str, project_id: str, cycle_id: str) -> bool project_id: UUID of the project cycle_id: UUID of the cycle """ - self._delete( - f"{workspace_slug}/projects/{project_id}/archived-cycles/{cycle_id}/unarchive" - ) + self._delete(f"{workspace_slug}/projects/{project_id}/archived-cycles/{cycle_id}/unarchive") return True diff --git a/plane/api/modules.py b/plane/api/modules.py index ba8b87c..4009dc1 100644 --- a/plane/api/modules.py +++ b/plane/api/modules.py @@ -9,7 +9,9 @@ PaginatedModuleWorkItemResponse, UpdateModule, ) +from ..models.query_params import WorkItemQueryParams from .base_resource import BaseResource +from .work_items.base import _prepare_work_item_params class Modules(BaseResource): @@ -136,19 +138,28 @@ def list_work_items( workspace_slug: str, project_id: str, module_id: str, - params: Mapping[str, Any] | None = None, + params: WorkItemQueryParams | Mapping[str, Any] | None = None, ) -> PaginatedModuleWorkItemResponse: """List work items in a module. + Supports the same ``filters`` and ``pql`` query parameters as + :meth:`WorkItems.list`. + Args: workspace_slug: The workspace slug identifier project_id: UUID of the project module_id: UUID of the module - params: Optional query parameters + params: Optional query parameters. Prefer ``WorkItemQueryParams``; + a plain mapping is also accepted for backwards compatibility + and is passed through unchanged. """ + if isinstance(params, WorkItemQueryParams): + query_params: Mapping[str, Any] | None = _prepare_work_item_params(params) + else: + query_params = params response = self._get( f"{workspace_slug}/projects/{project_id}/modules/{module_id}/module-issues", - params=params, + params=query_params, ) return PaginatedModuleWorkItemResponse.model_validate(response) diff --git a/plane/api/work_items/base.py b/plane/api/work_items/base.py index 1a9d82b..9355df1 100644 --- a/plane/api/work_items/base.py +++ b/plane/api/work_items/base.py @@ -1,5 +1,6 @@ from __future__ import annotations +import json from typing import Any from ...models.query_params import RetrieveQueryParams, WorkItemQueryParams @@ -23,6 +24,21 @@ from .work_logs import WorkLogs +def _prepare_work_item_params(params: WorkItemQueryParams | None) -> dict[str, Any] | None: + """Serialize WorkItemQueryParams for use as HTTP query params. + + The ``filters`` field is a structured object but the API expects it as a + JSON string in a single ``filters=`` query parameter. Everything else is + passed through as-is by ``requests``' query-string encoder. + """ + if params is None: + return None + payload = params.model_dump(exclude_none=True) + if "filters" in payload and isinstance(payload["filters"], dict): + payload["filters"] = json.dumps(payload["filters"], separators=(",", ":")) + return payload + + class WorkItems(BaseResource): def __init__(self, config: Any) -> None: super().__init__(config, "/workspaces/") @@ -157,23 +173,67 @@ def list( project_id: UUID of the project params: Optional query parameters for filtering, ordering, and pagination - Example: - from plane.models.schemas import WorkItemQueryParams + Example:: - # List work items with filters + from plane.models.query_params import WorkItemQueryParams + + # PQL filter (human-readable) + work_items = client.work_items.list( + "my-workspace", + "project-id", + params=WorkItemQueryParams(pql='priority = "urgent"'), + ) + + # Structured `filters` (JSON-encoded into the query string) work_items = client.work_items.list( "my-workspace", "project-id", params=WorkItemQueryParams( - priority="high", - state="state-id", - expand="assignees,labels" - ) + filters={"and": [ + {"priority": "urgent"}, + {"state_group__in": ["unstarted", "started"]}, + ]}, + ), + ) + """ + response = self._get( + f"{workspace_slug}/projects/{project_id}/work-items", + params=_prepare_work_item_params(params), + ) + return PaginatedWorkItemResponse.model_validate(response) + + def list_workspace( + self, + workspace_slug: str, + params: WorkItemQueryParams | None = None, + ) -> PaginatedWorkItemResponse: + """List work items across an entire workspace. + + Returns a paginated envelope of work items the caller can view, + spanning every project in the workspace (per-project authorization + and conditional grants are honored server-side). + + Args: + workspace_slug: The workspace slug identifier + params: Optional query parameters — supports ``filters``, ``pql``, + ``order_by``, ``cursor``, ``per_page``, ``fields``, ``expand``. + + Example:: + + from plane.models.query_params import WorkItemQueryParams + + results = client.work_items.list_workspace( + "my-workspace", + params=WorkItemQueryParams( + filters={"priority": "urgent"}, + order_by="-created_at", + per_page=50, + ), ) """ - query_params = params.model_dump(exclude_none=True) if params else None response = self._get( - f"{workspace_slug}/projects/{project_id}/work-items", params=query_params + f"{workspace_slug}/work-items", + params=_prepare_work_item_params(params), ) return PaginatedWorkItemResponse.model_validate(response) @@ -247,14 +307,17 @@ def list_archived( ) -> PaginatedWorkItemResponse: """List archived work items in a project. + Supports the same ``filters`` and ``pql`` query parameters as + :meth:`list`. + Args: workspace_slug: The workspace slug identifier project_id: UUID of the project params: Optional query parameters for filtering, ordering, and pagination """ - query_params = params.model_dump(exclude_none=True) if params else None response = self._get( - f"{workspace_slug}/projects/{project_id}/archived-work-items", params=query_params + f"{workspace_slug}/projects/{project_id}/archived-work-items", + params=_prepare_work_item_params(params), ) return PaginatedWorkItemResponse.model_validate(response) @@ -286,6 +349,4 @@ def unarchive(self, workspace_slug: str, project_id: str, work_item_id: str) -> Returns: None (HTTP 204 No Content) """ - self._delete( - f"{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/unarchive" - ) + self._delete(f"{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/unarchive") diff --git a/plane/models/query_params.py b/plane/models/query_params.py index 56040a5..49fa24b 100644 --- a/plane/models/query_params.py +++ b/plane/models/query_params.py @@ -1,5 +1,7 @@ """Query parameter DTOs for list/retrieve endpoints.""" +from typing import Any + from pydantic import BaseModel, ConfigDict, Field @@ -58,12 +60,28 @@ class WorkItemQueryParams(PaginatedQueryParams): - fields: Comma-separated fields to include - order_by: Field to order by (prefix with '-' for descending) - per_page: Number of results per page (1-100) - - pql: PQL filters + - pql: Plane Query Language expression for structured filtering + - filters: JSON-serializable filter expression for structured filtering """ model_config = ConfigDict(extra="ignore", populate_by_name=True) - pql: str | None = Field(None, description="PQL filters") + pql: str | None = Field( + None, + description=( + "Plane Query Language expression. Human-readable alternative to " + '`filters`. Example: `priority = "urgent" AND assignee = currentUser()`.' + ), + ) + filters: dict[str, Any] | None = Field( + None, + description=( + "Structured filter expression. Supports nested `and`/`or`/`not` groups " + "and field comparisons with operators like `__in`, `__gte`, `__range`, " + "`__isnull`, `__icontains`, etc. JSON-encoded into the `filters=` " + "query param by the client." + ), + ) class RetrieveQueryParams(BaseQueryParams): diff --git a/pyproject.toml b/pyproject.toml index 6e7fcae..d3f1c71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plane-sdk" -version = "0.2.12" +version = "0.2.13" description = "Python SDK for Plane API" readme = "README.md" requires-python = ">=3.10" diff --git a/tests/unit/test_work_items.py b/tests/unit/test_work_items.py index 6759029..07916f0 100644 --- a/tests/unit/test_work_items.py +++ b/tests/unit/test_work_items.py @@ -78,6 +78,77 @@ def test_list_work_items_with_pql_filter( if created_item_2 is not None: client.work_items.delete(workspace_slug, project.id, created_item_2.id) + def test_list_work_items_with_filters( + self, client: PlaneClient, workspace_slug: str, project: Project + ) -> None: + """Test listing work items with a structured `filters` query parameter.""" + created_high = None + created_low = None + + try: + created_high = client.work_items.create( + workspace_slug, + project.id, + CreateWorkItem(name="filters-urgent-item", priority="urgent"), + ) + created_low = client.work_items.create( + workspace_slug, + project.id, + CreateWorkItem(name="filters-low-item", priority="low"), + ) + + params = WorkItemQueryParams(filters={"priority": "urgent"}) + response = client.work_items.list(workspace_slug, project.id, params=params) + assert response is not None + assert isinstance(response.results, list) + result_ids = [item.id for item in response.results] + assert created_high.id in result_ids + assert created_low.id not in result_ids + finally: + if created_high is not None: + client.work_items.delete(workspace_slug, project.id, created_high.id) + if created_low is not None: + client.work_items.delete(workspace_slug, project.id, created_low.id) + + def test_list_workspace_work_items( + self, client: PlaneClient, workspace_slug: str, project: Project + ) -> None: + """`list_workspace` returns a paginated envelope spanning the workspace.""" + response = client.work_items.list_workspace(workspace_slug) + assert response is not None + assert hasattr(response, "results") + assert hasattr(response, "total_results") + assert isinstance(response.results, list) + + def test_list_workspace_work_items_with_filters( + self, client: PlaneClient, workspace_slug: str, project: Project + ) -> None: + """`list_workspace` honors `filters` and reduces the result set.""" + created = None + try: + created = client.work_items.create( + workspace_slug, + project.id, + CreateWorkItem(name="workspace-list-urgent-item", priority="urgent"), + ) + unfiltered = client.work_items.list_workspace(workspace_slug) + filtered = client.work_items.list_workspace( + workspace_slug, + params=WorkItemQueryParams(filters={"priority": "urgent"}), + ) + assert filtered is not None + assert filtered.total_results <= unfiltered.total_results + assert created.id in [item.id for item in filtered.results] or ( + # If the workspace has many urgent items, the created one may be + # on a later page — accept either presence on this page or that + # at least one urgent item is returned overall. + filtered.total_results + > 0 + ) + finally: + if created is not None: + client.work_items.delete(workspace_slug, project.id, created.id) + def test_search_work_items(self, client: PlaneClient, workspace_slug: str) -> None: """Test searching work items.""" response = client.work_items.search(workspace_slug, "test") From 0ddae26f42e8d45823ee8dcbe8ba039d7b2cedc2 Mon Sep 17 00:00:00 2001 From: Dheeraj Kumar Ketireddy Date: Tue, 26 May 2026 14:46:03 +0530 Subject: [PATCH 2/3] test: tighten `test_list_workspace_work_items_with_filters` assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the `or filtered.total_results > 0` fallback (which could pass even when filtering was a no-op) and assert directly that every returned item has `priority == "urgent"` — that's the property the test is meant to prove. Addresses CodeRabbit review. --- tests/unit/test_work_items.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/unit/test_work_items.py b/tests/unit/test_work_items.py index 07916f0..53a2328 100644 --- a/tests/unit/test_work_items.py +++ b/tests/unit/test_work_items.py @@ -138,13 +138,11 @@ def test_list_workspace_work_items_with_filters( ) assert filtered is not None assert filtered.total_results <= unfiltered.total_results - assert created.id in [item.id for item in filtered.results] or ( - # If the workspace has many urgent items, the created one may be - # on a later page — accept either presence on this page or that - # at least one urgent item is returned overall. - filtered.total_results - > 0 - ) + # Every returned item must satisfy the filter — this is the + # assertion that actually proves filtering worked. + assert len(filtered.results) > 0 + for item in filtered.results: + assert item.priority == "urgent" finally: if created is not None: client.work_items.delete(workspace_slug, project.id, created.id) From 9b34a715f8083a18ee794dafd5a9acefeaa17a2f Mon Sep 17 00:00:00 2001 From: Dheeraj Kumar Ketireddy Date: Tue, 26 May 2026 14:49:09 +0530 Subject: [PATCH 3/3] fix: JSON-encode `filters` for the Mapping input path too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot caught a real bug: `Cycles.list_work_items` / `Modules.list_work_items` accept a plain `Mapping[str, Any]` for BC, but when callers passed `filters` as a dict through that path the SDK forwarded it to `requests` unchanged. The API expects `filters` as a JSON-encoded string, so the request would produce an invalid query parameter. - Extend `prepare_work_item_params` (renamed from `_prepare_work_item_params` since it's now used across resources, no longer private to the work_items module) to accept either a `WorkItemQueryParams` DTO or a plain mapping, and normalise `filters` to a JSON string in both paths. - Simplify `Cycles.list_work_items` and `Modules.list_work_items` to use the unified helper — drops the `isinstance` branch. - Fix README: `PaginatedQueryParams` is cursor-based (cursor, per_page), not (per_page, page) — that combination has never existed in the model. Addresses Copilot review on PR #37. --- README.md | 2 +- plane/api/cycles.py | 15 ++++++--------- plane/api/modules.py | 15 ++++++--------- plane/api/work_items/base.py | 26 +++++++++++++++++--------- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 47a91ab..ecadafb 100644 --- a/README.md +++ b/README.md @@ -747,7 +747,7 @@ The SDK provides comprehensive Pydantic v2 models for all API operations. ### Query Parameters - `BaseQueryParams` - Base query parameters -- `PaginatedQueryParams` - Pagination support (per_page, page) +- `PaginatedQueryParams` - Cursor-based pagination support (cursor, per_page) - `WorkItemQueryParams` - Work item specific queries (expand, order_by, `filters`, `pql`, etc.) - `RetrieveQueryParams` - Retrieve operations (expand, fields, etc.) diff --git a/plane/api/cycles.py b/plane/api/cycles.py index 48ecc4e..4ffb998 100644 --- a/plane/api/cycles.py +++ b/plane/api/cycles.py @@ -12,7 +12,7 @@ ) from ..models.query_params import WorkItemQueryParams from .base_resource import BaseResource -from .work_items.base import _prepare_work_item_params +from .work_items.base import prepare_work_item_params class Cycles(BaseResource): @@ -144,23 +144,20 @@ def list_work_items( """List work items in a cycle. Supports the same ``filters`` and ``pql`` query parameters as - :meth:`WorkItems.list`. + :meth:`WorkItems.list`. ``filters`` is JSON-encoded into the query + string for both the DTO and the mapping path, so callers can pass + a dict either way. Args: workspace_slug: The workspace slug identifier project_id: UUID of the project cycle_id: UUID of the cycle params: Optional query parameters. Prefer ``WorkItemQueryParams``; - a plain mapping is also accepted for backwards compatibility - and is passed through unchanged. + a plain mapping is also accepted for backwards compatibility. """ - if isinstance(params, WorkItemQueryParams): - query_params: Mapping[str, Any] | None = _prepare_work_item_params(params) - else: - query_params = params response = self._get( f"{workspace_slug}/projects/{project_id}/cycles/{cycle_id}/cycle-issues", - params=query_params, + params=prepare_work_item_params(params), ) return PaginatedCycleWorkItemResponse.model_validate(response) diff --git a/plane/api/modules.py b/plane/api/modules.py index 4009dc1..19751a8 100644 --- a/plane/api/modules.py +++ b/plane/api/modules.py @@ -11,7 +11,7 @@ ) from ..models.query_params import WorkItemQueryParams from .base_resource import BaseResource -from .work_items.base import _prepare_work_item_params +from .work_items.base import prepare_work_item_params class Modules(BaseResource): @@ -143,23 +143,20 @@ def list_work_items( """List work items in a module. Supports the same ``filters`` and ``pql`` query parameters as - :meth:`WorkItems.list`. + :meth:`WorkItems.list`. ``filters`` is JSON-encoded into the query + string for both the DTO and the mapping path, so callers can pass + a dict either way. Args: workspace_slug: The workspace slug identifier project_id: UUID of the project module_id: UUID of the module params: Optional query parameters. Prefer ``WorkItemQueryParams``; - a plain mapping is also accepted for backwards compatibility - and is passed through unchanged. + a plain mapping is also accepted for backwards compatibility. """ - if isinstance(params, WorkItemQueryParams): - query_params: Mapping[str, Any] | None = _prepare_work_item_params(params) - else: - query_params = params response = self._get( f"{workspace_slug}/projects/{project_id}/modules/{module_id}/module-issues", - params=query_params, + params=prepare_work_item_params(params), ) return PaginatedModuleWorkItemResponse.model_validate(response) diff --git a/plane/api/work_items/base.py b/plane/api/work_items/base.py index 9355df1..6b7697e 100644 --- a/plane/api/work_items/base.py +++ b/plane/api/work_items/base.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +from collections.abc import Mapping from typing import Any from ...models.query_params import RetrieveQueryParams, WorkItemQueryParams @@ -24,16 +25,23 @@ from .work_logs import WorkLogs -def _prepare_work_item_params(params: WorkItemQueryParams | None) -> dict[str, Any] | None: - """Serialize WorkItemQueryParams for use as HTTP query params. +def prepare_work_item_params( + params: WorkItemQueryParams | Mapping[str, Any] | None, +) -> dict[str, Any] | None: + """Serialize work-item query params for use as HTTP query params. - The ``filters`` field is a structured object but the API expects it as a - JSON string in a single ``filters=`` query parameter. Everything else is - passed through as-is by ``requests``' query-string encoder. + Accepts either a :class:`WorkItemQueryParams` DTO or a plain mapping, + and normalises the ``filters`` field: the API expects it as a JSON + string in a single ``filters=`` query parameter, but callers are free + to pass it as a dict for ergonomics. Everything else is passed through + as-is by ``requests``' query-string encoder. """ if params is None: return None - payload = params.model_dump(exclude_none=True) + if isinstance(params, WorkItemQueryParams): + payload: dict[str, Any] = params.model_dump(exclude_none=True) + else: + payload = {k: v for k, v in params.items() if v is not None} if "filters" in payload and isinstance(payload["filters"], dict): payload["filters"] = json.dumps(payload["filters"], separators=(",", ":")) return payload @@ -198,7 +206,7 @@ def list( """ response = self._get( f"{workspace_slug}/projects/{project_id}/work-items", - params=_prepare_work_item_params(params), + params=prepare_work_item_params(params), ) return PaginatedWorkItemResponse.model_validate(response) @@ -233,7 +241,7 @@ def list_workspace( """ response = self._get( f"{workspace_slug}/work-items", - params=_prepare_work_item_params(params), + params=prepare_work_item_params(params), ) return PaginatedWorkItemResponse.model_validate(response) @@ -317,7 +325,7 @@ def list_archived( """ response = self._get( f"{workspace_slug}/projects/{project_id}/archived-work-items", - params=_prepare_work_item_params(params), + params=prepare_work_item_params(params), ) return PaginatedWorkItemResponse.model_validate(response)