feat: add filter fields to WorkItemQueryParams#24
feat: add filter fields to WorkItemQueryParams#24lifeiscontent wants to merge 1 commit intomakeplane:mainfrom
Conversation
Add assignee_id__in, state_id__in, state_group__in, priority__in, label_id__in, created_by_id__in, is_draft, and is_archived fields to WorkItemQueryParams to support server-side filtering of work items. Add model_serializer to convert list fields to comma-separated strings for compatibility with django-filters BaseInFilter.
📝 WalkthroughWalkthroughAdded eight new optional filter fields to Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip You can validate your CodeRabbit configuration file in your editor.If your editor has YAML language server, you can enable auto-completion and validation by adding |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
plane/models/query_params.py (1)
98-105: Implementation is correct; optional improvement for empty list handling.The wrap serializer correctly post-processes the default serialization to convert lists to comma-separated strings for django-filters compatibility. The
handler(self)signature is valid in Pydantic v2.Minor consideration: an empty list
[]will serialize to"", producing a query parameter like?assignee_id__in=. If this is undesired, filter out empty strings:Optional: exclude empty list results
`@model_serializer`(mode="wrap") def _serialize(self, handler): # type: ignore[no-untyped-def] """Serialize list fields as comma-separated strings for django-filters.""" data = handler(self) for key, value in data.items(): if isinstance(value, list): data[key] = ",".join(str(v) for v in value) + return {k: v for k, v in data.items() if v != ""} - return data🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plane/models/query_params.py` around lines 98 - 105, The current _serialize method converts list fields to comma-separated strings but leaves empty lists as an empty string (e.g., ?assignee_id__in=); update _serialize (the model_serializer wrapper that calls handler(self)) to skip or remove keys whose value is an empty list so they are not serialized as empty strings—detect list values in the data dict after handler(self) and if the list is empty, delete data[key] (or set it to None/omit it) instead of converting to ",".join, while still converting non-empty lists as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@plane/models/query_params.py`:
- Around line 98-105: The current _serialize method converts list fields to
comma-separated strings but leaves empty lists as an empty string (e.g.,
?assignee_id__in=); update _serialize (the model_serializer wrapper that calls
handler(self)) to skip or remove keys whose value is an empty list so they are
not serialized as empty strings—detect list values in the data dict after
handler(self) and if the list is empty, delete data[key] (or set it to None/omit
it) instead of converting to ",".join, while still converting non-empty lists as
before.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 53d91f60-ecbd-42b1-a54d-e0c3e241f99c
📒 Files selected for processing (1)
plane/models/query_params.py
There was a problem hiding this comment.
Pull request overview
Adds first-class server-side filtering to the SDK’s WorkItemQueryParams, aligning the Python client with Plane’s REST API filtering capabilities and avoiding client-side filtering workarounds.
Changes:
- Added filter fields to
WorkItemQueryParams(assignee/state/priority/labels/creator + draft/archived flags). - Added a Pydantic
model_serializerto serialize list filters as comma-separated strings for django-filtersBaseInFiltercompatibility.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @model_serializer(mode="wrap") | ||
| def _serialize(self, handler): # type: ignore[no-untyped-def] | ||
| """Serialize list fields as comma-separated strings for django-filters.""" |
There was a problem hiding this comment.
The @model_serializer is currently untyped and relies on # type: ignore[no-untyped-def], which conflicts with the repo’s strict mypy config. Please type the serializer signature (e.g., using Pydantic’s SerializerFunctionWrapHandler and optional SerializationInfo) so we can remove the type ignore and keep type safety.
| state_group__in: list[str] | None = Field( | ||
| None, | ||
| description="Filter by state groups (backlog, unstarted, started, completed, cancelled)", | ||
| ) | ||
| priority__in: list[str] | None = Field( | ||
| None, | ||
| description="Filter by priority levels (urgent, high, medium, low, none)", | ||
| ) |
There was a problem hiding this comment.
state_group__in and priority__in are typed as list[str], but the codebase already defines constrained Literal types (GroupEnum, PriorityEnum) used across models. Consider typing these as list[GroupEnum] | None and list[PriorityEnum] | None to prevent invalid values at compile/validation time and keep consistency with the rest of the SDK.
| """Serialize list fields as comma-separated strings for django-filters.""" | ||
| data = handler(self) | ||
| for key, value in data.items(): | ||
| if isinstance(value, list): |
There was a problem hiding this comment.
The model-level serializer converts any list-valued field into a comma-separated string. That’s fine for the current __in filters, but it will also affect any future list fields added to WorkItemQueryParams (even if the API expects repeated query params instead of CSV). Consider limiting this conversion to the known *__in keys (or an explicit allowlist) to avoid surprising serialization changes later.
| if isinstance(value, list): | |
| if key.endswith("__in") and isinstance(value, list): |
| @model_serializer(mode="wrap") | ||
| def _serialize(self, handler): # type: ignore[no-untyped-def] | ||
| """Serialize list fields as comma-separated strings for django-filters.""" | ||
| data = handler(self) | ||
| for key, value in data.items(): | ||
| if isinstance(value, list): | ||
| data[key] = ",".join(str(v) for v in value) | ||
| return data |
There was a problem hiding this comment.
New serialization behavior (converting list filters to comma-separated strings) isn’t covered by tests. Please add a unit test that asserts WorkItemQueryParams(assignee_id__in=[...]).model_dump(exclude_none=True) produces a comma-separated string, since this behavior is critical for server-side filtering to work with requests query encoding.
Summary
Adds server-side filtering support to
WorkItemQueryParams, enabling callers to filter work items by assignee, state, priority, labels, and more.Changes
WorkItemQueryParams:assignee_id__in— filter by assignee UUIDsstate_id__in— filter by state UUIDsstate_group__in— filter by state group (backlog, unstarted, started, completed, cancelled)priority__in— filter by priority (urgent, high, medium, low, none)label_id__in— filter by label UUIDscreated_by_id__in— filter by creator UUIDsis_draft— filter by draft statusis_archived— filter by archived statusmodel_serializerto convert list fields to comma-separated strings for compatibility with django-filtersBaseInFilter.Context
The Plane REST API (via
IssueFilterSetin django-filters) supports all of these query parameters, butWorkItemQueryParamshad no filter fields — only pagination/ordering. This meant SDK users (including the MCP server) could not filter work items server-side and had to fetch all items and filter client-side.Companion PR
Summary by CodeRabbit