Skip to content

fix: accept UUID strings for WorkItemDetail labels and assignees (Plane v1.3.0+)#29

Open
vdaluz wants to merge 1 commit intomakeplane:mainfrom
vdaluz:fix/work-item-detail-labels-assignees-type
Open

fix: accept UUID strings for WorkItemDetail labels and assignees (Plane v1.3.0+)#29
vdaluz wants to merge 1 commit intomakeplane:mainfrom
vdaluz:fix/work-item-detail-labels-assignees-type

Conversation

@vdaluz
Copy link
Copy Markdown

@vdaluz vdaluz commented Apr 10, 2026

Problem

Plane v1.3.0 changed the API to return labels and assignees as UUID strings instead of expanded objects, even on full (non-sparse) responses. WorkItemDetail typed both as required non-optional lists of objects:

assignees: list[UserLite]
labels: list[Label]

This causes a ValidationError on every retrieve_work_item / retrieve_work_item_by_identifier call against a Plane v1.3.0+ instance:

pydantic_core._pydantic_core.ValidationError: 1 validation error for WorkItemDetail
labels.0
  Input should be a valid dictionary or instance of Label
  [type=model_type, input_value='f6a24a78-a275-4fd1-...', input_type=str]

Fix

Change both fields to accept either UUID strings or expanded objects, and default to [] for sparse responses (consistent with WorkItemExpand and the approach in #28):

assignees: list[str] | list[UserLite] = []
labels: list[str] | list[Label] = []

Relation to #28

PR #28 fixes ValidationError when labels/assignees are absent from a sparse response. This PR fixes ValidationError when they are present as strings — a different code path, same field. The two fixes are complementary; combining them (union type + default []) handles all cases.

Tests

8 new unit tests in tests/unit/test_work_item_detail_types.py:

  • UUID strings for labels only, assignees only, both together
  • Expanded objects (pre-v1.3.0 behavior) still parse correctly
  • Empty lists accepted
  • Fields omitted entirely default to []
  • name remains required (no over-relaxation)

All 8 pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced work item details to support flexible assignee and label inputs, accepting either ID strings or full object data, improving API compatibility.
  • Tests

    • Added comprehensive unit tests validating backward and forward compatibility for assignee and label handling.

Plane v1.3.0 changed the API to return labels and assignees as UUID
strings instead of expanded objects. WorkItemDetail typed these as
list[Label] and list[UserLite] respectively, causing a Pydantic
ValidationError on any retrieve_work_item call against a v1.3.0+ instance.

Change both fields to list[str] | list[T] = [] so they accept either
representation, and default to [] for sparse responses (consistent with
WorkItemExpand and the fix in makeplane#28).

Adds 8 unit tests covering: UUID strings, expanded objects, empty lists,
omitted fields, and that name remains required.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

The WorkItemDetail model is updated to accept assignees and labels fields as either UUID strings or full objects, with both fields now optional and defaulting to empty lists. A comprehensive test suite validates backward compatibility with the new string-based input format and existing object-based inputs.

Changes

Cohort / File(s) Summary
WorkItemDetail Model Updates
plane/models/work_items.py
Modified assignees and labels fields to accept union types (list[str] | list[UserLite] and list[str] | list[Label]), with empty list defaults making both fields optional.
WorkItemDetail Type Validation Tests
tests/unit/test_work_item_detail_types.py
New test suite validating WorkItemDetail parsing with string UUID inputs, object-form inputs, mixed scenarios, and default empty list behavior when fields are omitted.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 With flexible whiskers and union type cheer,
Strings dance with objects, both welcome here!
No longer required, just empty and free,
Backward compatibility flows gracefully! 🌸

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: accepting UUID strings for WorkItemDetail labels and assignees to support Plane v1.3.0+ API changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/unit/test_work_item_detail_types.py (1)

58-92: Strengthen object-form tests with type assertions.

These two tests currently validate count only. Adding element-type checks will catch parser regressions where dict/object coercion changes unexpectedly.

Suggested test hardening
 import pytest
 from pydantic import ValidationError
 
+from plane.models.labels import Label
+from plane.models.users import UserLite
 from plane.models.work_items import WorkItemDetail
@@
     def test_labels_as_objects(self) -> None:
@@
         item = WorkItemDetail(**data)
         assert len(item.labels) == 1
+        assert isinstance(item.labels[0], Label)
@@
     def test_assignees_as_objects(self) -> None:
@@
         item = WorkItemDetail(**data)
         assert len(item.assignees) == 1
+        assert isinstance(item.assignees[0], UserLite)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/test_work_item_detail_types.py` around lines 58 - 92, Tests
test_labels_as_objects and test_assignees_as_objects only assert lengths;
strengthen them by asserting the parsed elements are proper model instances (not
raw dicts) and expose expected attributes: for WorkItemDetail -> item.labels[0],
assert it's not a dict (e.g., not isinstance(..., dict)) and has attributes like
id and name/color; for item.assignees[0], assert it's not a dict and has id and
display_name (and optional avatar/is_bot) to catch regressions where dicts are
returned instead of parsed objects.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/unit/test_work_item_detail_types.py`:
- Around line 58-92: Tests test_labels_as_objects and test_assignees_as_objects
only assert lengths; strengthen them by asserting the parsed elements are proper
model instances (not raw dicts) and expose expected attributes: for
WorkItemDetail -> item.labels[0], assert it's not a dict (e.g., not
isinstance(..., dict)) and has attributes like id and name/color; for
item.assignees[0], assert it's not a dict and has id and display_name (and
optional avatar/is_bot) to catch regressions where dicts are returned instead of
parsed objects.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0402f7cb-fa2f-4ed2-b315-c75be3cd46e3

📥 Commits

Reviewing files that changed from the base of the PR and between d6c824d and 5fb4e25.

📒 Files selected for processing (2)
  • plane/models/work_items.py
  • tests/unit/test_work_item_detail_types.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant