Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions plane/api/work_items/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
RetrieveQueryParams,
WorkItemCountQueryParams,
WorkItemQueryParams,
WorkItemSearchQueryParams,
)
from ...models.work_items import (
AdvancedSearchResult,
Expand Down Expand Up @@ -350,14 +351,16 @@ def search(
self,
workspace_slug: str,
query: str,
params: RetrieveQueryParams | None = None,
params: WorkItemSearchQueryParams | RetrieveQueryParams | None = None,
) -> WorkItemSearch:
"""Search work items.

Args:
workspace_slug: The workspace slug identifier
query: Search query string
params: Optional query parameters for expand, fields, etc.
params: Optional query parameters for expand, fields, limit, etc.
Pass :class:`WorkItemSearchQueryParams` to raise the result cap;
the API returns 10 results when ``limit`` is omitted.
"""
search_params = {"search": query}
if params:
Expand Down
2 changes: 2 additions & 0 deletions plane/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
ProjectLiteListQueryParams,
RetrieveQueryParams,
WorkItemQueryParams,
WorkItemSearchQueryParams,
)

__all__ = [
Expand Down Expand Up @@ -54,6 +55,7 @@
"ProjectLiteListQueryParams",
"RetrieveQueryParams",
"WorkItemQueryParams",
"WorkItemSearchQueryParams",
]


Expand Down
18 changes: 18 additions & 0 deletions plane/models/query_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ class RetrieveQueryParams(BaseQueryParams):
model_config = ConfigDict(extra="ignore", populate_by_name=True)


class WorkItemSearchQueryParams(BaseQueryParams):
"""Query parameters for the work item search endpoint.

Search is not cursor paginated; the API caps the result set with ``limit``
instead. When ``limit`` is omitted it returns 10 results, and the response
carries no total or truncation marker, so a caller cannot distinguish a
complete result set from a truncated one.
"""

model_config = ConfigDict(extra="ignore", populate_by_name=True)

limit: int | None = Field(
None,
description="Maximum number of results to return. The API returns 10 when omitted.",
)


class MemberQueryParams(BaseQueryParams):
"""Query parameters for workspace/project member list endpoints.

Expand Down Expand Up @@ -358,4 +375,5 @@ class WorkItemCountQueryParams(BaseModel):
"WorkItemCountGroupBy",
"WorkItemCountQueryParams",
"WorkItemQueryParams",
"WorkItemSearchQueryParams",
]
29 changes: 28 additions & 1 deletion tests/unit/test_work_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

from plane.client import PlaneClient
from plane.models.projects import Project
from plane.models.query_params import PaginatedQueryParams, WorkItemQueryParams
from plane.models.query_params import (
PaginatedQueryParams,
WorkItemQueryParams,
WorkItemSearchQueryParams,
)
from plane.models.work_items import (
AdvancedSearchWorkItem,
CreateWorkItem,
Expand Down Expand Up @@ -154,6 +158,29 @@ def test_search_work_items(self, client: PlaneClient, workspace_slug: str) -> No
assert hasattr(response, "issues")
assert isinstance(response.issues, list)

def test_search_work_items_respects_limit(
self, client: PlaneClient, workspace_slug: str
) -> None:
"""Test that search honors an explicit result limit.

Without ``limit`` the API returns at most 10 results and gives no
indication the set was truncated, so a caller cannot page past it.

The unlimited search runs first and the test skips when the workspace
has fewer than two matching work items, so the assertions cannot pass
vacuously against an empty result set.
"""
unlimited = client.work_items.search(workspace_slug, "e")
if len(unlimited.issues) < 2:
pytest.skip("workspace has too few matching work items to exercise the limit")

params = WorkItemSearchQueryParams(limit=1)
limited = client.work_items.search(workspace_slug, "e", params=params)
assert limited is not None
assert isinstance(limited.issues, list)
assert len(limited.issues) == 1
assert len(limited.issues) < len(unlimited.issues)

def test_advanced_search_work_items(self, client: PlaneClient, workspace_slug: str) -> None:
"""Test advanced search with query only."""
data = AdvancedSearchWorkItem(query="test", limit=10)
Expand Down