Skip to content

fix(kanban): validator falls back to global columns when project has no specifics#605

Open
MacJediWizard wants to merge 1 commit intoDRYTRIX:mainfrom
MacJediWizard:fix/upstream-kanban-validator-fallback
Open

fix(kanban): validator falls back to global columns when project has no specifics#605
MacJediWizard wants to merge 1 commit intoDRYTRIX:mainfrom
MacJediWizard:fix/upstream-kanban-validator-fallback

Conversation

@MacJediWizard
Copy link
Copy Markdown

Bug

PUT /api/tasks/<id>/status returns 400 Invalid status whenever the task belongs to a project that has no project-specific kanban_columns rows AND the user drops it into a globally-configured column other than todo, in_progress, review, or done. "On Hold" was the path that surfaced this in production for me.

Reproduction

  1. A project has zero rows in kanban_columns with that project_id.
  2. The instance has 5 globals (project_id IS NULL): todo, in_progress, review, done, on_hold.
  3. The kanban UI renders the 5 globals as drop targets for that project's tasks.
  4. The user drops a task into "On Hold". Frontend sends status="on_hold".
  5. app/routes/tasks.py:1519 calls KanbanColumn.get_valid_status_keys(project_id=task.project_id).
  6. get_active_columns(project_id=<id>) filters strictly on project_id and returns [].
  7. get_valid_status_keys falls back to the hardcoded list ["todo", "in_progress", "review", "done", "cancelled"].
  8. The hardcoded list is missing on_hold (and includes cancelled, which isn't a configured column in the default seed). on_hold not in list → 400.

Live HTTP-log fingerprint:

14:56:21  PUT /api/tasks/<id>/status -> 200    (drop to one of the four hardcoded keys)
14:56:31  PUT /api/tasks/<id>/status -> 400    (drop to On Hold)
14:56:55  PUT /api/tasks/<id>/status -> 200    (drop back)
14:56:58  PUT /api/tasks/<id>/status -> 400    (On Hold again)

The on_hold column is part of an instance's configured globals — KanbanColumn.initialize_default_columns doesn't seed it, but the Settings UI's /kanban/columns/<id>/toggle etc. lets admins add/enable it, after which it persists as a global row.

Fix

app/models/kanban_column.pyget_valid_status_keys now falls back to the configured global columns when the project has no project-specific columns. The kanban UI already renders the globals in that case, so the validator must accept the same set. The hardcoded list is only used as a last-ditch fallback when even the globals table is empty (covers the table-not-yet-seeded path during a fresh migration).

     @classmethod
     def get_valid_status_keys(cls, project_id=None):
-        """..."""
         columns = cls.get_active_columns(project_id=project_id)
+        if not columns and project_id is not None:
+            columns = cls.get_active_columns(project_id=None)
         if not columns:
             return ["todo", "in_progress", "review", "done", "cancelled"]
         return [col.key for col in columns]

Diff size

app/models/kanban_column.py | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)

Pure validator change. No schema change. No behavioural change to the column data. The only API behaviour change is that a status that the kanban UI is already rendering and the user is already targeting is now also accepted by the API, eliminating the 400.

Tested against v5.5.2.

…no specifics

PUT /api/tasks/<id>/status returns 400 "Invalid status" whenever the
task belongs to a project that has no project-specific kanban_columns
rows AND the user drops it into a configured global column other than
the four hardcoded fallback keys.

Reproduction:

  1. Project has no project-specific kanban_columns rows.
  2. The instance has 5 globals (project_id IS NULL): todo, in_progress,
     review, done, on_hold.
  3. The kanban UI renders the 5 globals as drop targets for that
     project's tasks.
  4. User drops a task into "On Hold". Frontend sends status="on_hold".
  5. app/routes/tasks.py:1519 calls
       KanbanColumn.get_valid_status_keys(project_id=task.project_id)
     with the project's id.
  6. get_active_columns(project_id=<id>) filters strictly on project_id
     and returns [].
  7. get_valid_status_keys then falls back to the hardcoded list
       ["todo", "in_progress", "review", "done", "cancelled"]
     which is missing "on_hold" (and includes "cancelled", which isn't
     even a configured column).
  8. "on_hold" is not in that list -> 400.

Drops to the four hardcoded keys all returned 200; only "On Hold"
failed, exactly matching the live 200/400 alternation observed in
production logs.

Fix: when there are no project-specific columns, fall back to the
configured global columns from the database (which is the set the UI
is already rendering). The hardcoded list is only used as a last-ditch
fallback when even the globals table is empty - this preserves the
table-not-yet-seeded safety net during fresh migrations.

Pure validator change; no schema change, no behavioural change beyond
accepting the statuses the UI is already offering.
@evilguy4000
Copy link
Copy Markdown
Collaborator

@MacJediWizard Can you change the branch to develop?

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.

2 participants