?
TRCKR-2347
feature
Created: 2026-01-02 Updated: 2026-01-02
Relationships Loading...
Attachments
Loading...
Comments (2)
agent · 2026-01-02
## Implementation Complete ### Problem Different endpoints used different patterns for handling partial updates: - `issues.py` correctly used `model_fields_set` to distinguish null from omitted - `projects.py` and `milestones.py` used `if field is not None` which prevented clearing nullable fields - `sync.py` had a local `_filter_update_params()` that wasn't consistent with the other approaches ### Solution 1. **Created `shared/update_merge.py`** with: - `extract_update_fields(model)` - for Pydantic models (REST endpoints) - `extract_update_fields_from_dict(data, valid_fields)` - for raw dicts (sync) - `filter_update_params(entity_type, data)` - main entry point for sync - `VALID_UPDATE_FIELDS` dict with field sets for all entity types - `NON_UPDATEABLE_FIELDS` set for fields that should never be updated 2. **Updated `projects.py`** and **`milestones.py`** to use `model_fields_set` pattern (matching issues.py) 3. **Updated `sync.py`** to use the shared `filter_update_params()` function ### Testing - Added 27 comprehensive tests in `tests/test_shared_update_merge.py` covering: - Field set detection for Pydantic models - Null value preservation - Dict field filtering - Edge cases (empty string, empty list, boolean false, zero) - All entity types All sync update tests pass. Pre-existing test failures in other areas are unrelated. ### Files Changed - `shared/update_merge.py:1-186` - New utility module - `shared/__init__.py:18-24,72-77` - Added exports - `server/routes/projects.py:193-222` - Updated to use model_fields_set - `server/routes/milestones.py:156-172` - Updated to use model_fields_set - `server/routes/sync.py:31,38-47` - Replaced local implementation with shared utility - `tests/test_shared_update_merge.py:1-422` - New test file
triage-agent · 2026-01-02
## Feature Triage Complete **Status:** PARTIALLY IMPLEMENTED **Summary:** The codebase already has multiple separate implementations for null/unset handling: - Client store uses _UNSET sentinel pattern - Issues API uses Pydantic's model_fields_set - Sync endpoint uses _filter_update_params (doesn't distinguish null vs omitted) - CLI uses Click's ParameterSource **Gap:** No shared utility exists, and sync endpoint doesn't distinguish null from omitted. **Implementation Location:** - NEW: shared/update_merge.py - Central utility - MODIFY: server/routes/sync.py - Use shared utility - MODIFY: Projects/milestones endpoints - Adopt model_fields_set pattern - ADD: tests/test_shared_update_merge.py **Complexity:** Medium (8-12 files, 1 new file, ~200-300 lines) See attached triage report at /tmp/triage-TRCKR-2347.md