diff --git a/plane/api/work_items/base.py b/plane/api/work_items/base.py index 0b7c730..92a48cb 100644 --- a/plane/api/work_items/base.py +++ b/plane/api/work_items/base.py @@ -8,6 +8,7 @@ RetrieveQueryParams, WorkItemCountQueryParams, WorkItemQueryParams, + WorkItemSearchQueryParams, ) from ...models.work_items import ( AdvancedSearchResult, @@ -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: diff --git a/plane/models/__init__.py b/plane/models/__init__.py index 7b3c069..af4d879 100644 --- a/plane/models/__init__.py +++ b/plane/models/__init__.py @@ -25,6 +25,7 @@ ProjectLiteListQueryParams, RetrieveQueryParams, WorkItemQueryParams, + WorkItemSearchQueryParams, ) __all__ = [ @@ -54,6 +55,7 @@ "ProjectLiteListQueryParams", "RetrieveQueryParams", "WorkItemQueryParams", + "WorkItemSearchQueryParams", ] diff --git a/plane/models/query_params.py b/plane/models/query_params.py index c8458d2..9e73cb8 100644 --- a/plane/models/query_params.py +++ b/plane/models/query_params.py @@ -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. @@ -358,4 +375,5 @@ class WorkItemCountQueryParams(BaseModel): "WorkItemCountGroupBy", "WorkItemCountQueryParams", "WorkItemQueryParams", + "WorkItemSearchQueryParams", ] diff --git a/tests/unit/test_work_items.py b/tests/unit/test_work_items.py index 2bbc9c2..1f939fc 100644 --- a/tests/unit/test_work_items.py +++ b/tests/unit/test_work_items.py @@ -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, @@ -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)