From 7a12cba4d59512d77cd9c4a1ee799038101e96de Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 27 Mar 2026 09:18:16 -0400 Subject: [PATCH] feat: add 'cancelled' task status; fix 500 on invalid status/priority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TaskStatus enum was missing 'cancelled' — the LLM tried to use it and hit TaskStatus("cancelled") raising ValueError → 500. Added the value, a migration to extend the task_status Postgres enum, and proper 400 validation guards on both create and update task routes. Co-Authored-By: Claude Sonnet 4.6 --- .../0031_add_cancelled_task_status.py | 18 ++++++++++++++ src/fabledassistant/models/note.py | 1 + src/fabledassistant/routes/tasks.py | 24 +++++++++++++++---- 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 alembic/versions/0031_add_cancelled_task_status.py diff --git a/alembic/versions/0031_add_cancelled_task_status.py b/alembic/versions/0031_add_cancelled_task_status.py new file mode 100644 index 0000000..7194f02 --- /dev/null +++ b/alembic/versions/0031_add_cancelled_task_status.py @@ -0,0 +1,18 @@ +"""Add 'cancelled' value to task_status enum.""" + +from alembic import op + +revision = "0031" +down_revision = "0030" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.execute("ALTER TYPE task_status ADD VALUE IF NOT EXISTS 'cancelled'") + + +def downgrade() -> None: + # PostgreSQL does not support removing enum values without a full type rebuild. + # Downgrade is a no-op; the value simply becomes unused. + pass diff --git a/src/fabledassistant/models/note.py b/src/fabledassistant/models/note.py index f317a27..f1957dd 100644 --- a/src/fabledassistant/models/note.py +++ b/src/fabledassistant/models/note.py @@ -13,6 +13,7 @@ class TaskStatus(str, enum.Enum): todo = "todo" in_progress = "in_progress" done = "done" + cancelled = "cancelled" class TaskPriority(str, enum.Enum): diff --git a/src/fabledassistant/routes/tasks.py b/src/fabledassistant/routes/tasks.py index c5b7a0d..a483d5c 100644 --- a/src/fabledassistant/routes/tasks.py +++ b/src/fabledassistant/routes/tasks.py @@ -71,10 +71,14 @@ async def create_task_route(): if isinstance(due_date, tuple): return due_date - status = TaskStatus(data["status"]).value if "status" in data else TaskStatus.todo.value - priority = ( - TaskPriority(data["priority"]).value if "priority" in data else TaskPriority.none.value - ) + try: + status = TaskStatus(data["status"]).value if "status" in data else TaskStatus.todo.value + except ValueError: + return jsonify({"error": f"Invalid status: {data['status']}"}), 400 + try: + priority = TaskPriority(data["priority"]).value if "priority" in data else TaskPriority.none.value + except ValueError: + return jsonify({"error": f"Invalid priority: {data['priority']}"}), 400 project_id = data.get("project_id") if project_id is None and data.get("project"): @@ -123,9 +127,19 @@ async def update_task_route(task_id: int): uid = get_current_user_id() data = await request.get_json() fields = {} - for key in ("title", "status", "priority"): + for key in ("title",): if key in data: fields[key] = data[key] + if "status" in data: + try: + fields["status"] = TaskStatus(data["status"]).value + except ValueError: + return jsonify({"error": f"Invalid status: {data['status']}"}), 400 + if "priority" in data: + try: + fields["priority"] = TaskPriority(data["priority"]).value + except ValueError: + return jsonify({"error": f"Invalid priority: {data['priority']}"}), 400 # Accept both "body" and "description" (prefer body) if "body" in data: