LinkDesk/backend/docs/custom-task-status-update-e...

6.6 KiB

Custom Task Status Update Endpoint

Overview

This document describes the implementation of the PUT endpoint for updating custom task statuses in a project.

Endpoint: PUT /projects/{project_id}/task-statuses/{status_id}

Requirements Implemented:

  • 2.1: Support updating name
  • 2.2: Support updating color
  • 2.3: Support updating is_default flag
  • 5.2: If setting as default, unset other default statuses

Implementation Details

Endpoint Signature

@router.put("/{project_id}/task-statuses/{status_id}")
async def update_custom_task_status(
    project_id: int,
    status_id: str,
    status_update: dict,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_coordinator_or_admin)
):

Request Body Schema

Uses CustomTaskStatusUpdate schema:

{
    "name": "string (optional)",        # New status name (1-50 chars)
    "color": "string (optional)",       # Hex color code (e.g., #FF5733)
    "is_default": "boolean (optional)"  # Set as default status
}

Response Schema

Returns CustomTaskStatusResponse:

{
    "message": "string",
    "status": {
        "id": "string",
        "name": "string",
        "color": "string",
        "order": "integer",
        "is_default": "boolean"
    },
    "all_statuses": {
        "statuses": [...],           # All custom statuses
        "system_statuses": [...],    # System statuses
        "default_status_id": "string"
    }
}

Features

1. Name Update (Requirement 2.1)

  • Validates name uniqueness within project
  • Checks against other custom statuses
  • Checks against system status names
  • Returns 409 Conflict if name already exists
# Validate name uniqueness if name is being changed
if status_update.name is not None and status_update.name != status_to_update.get('name'):
    # Check against other custom statuses
    existing_names = [
        s.get('name', '').lower() 
        for i, s in enumerate(custom_statuses_data) 
        if isinstance(s, dict) and i != status_index
    ]
    
    # Check against system statuses
    system_names = [s['name'].lower() for s in SYSTEM_TASK_STATUSES]
    
    if status_update.name.lower() in existing_names:
        raise HTTPException(status_code=409, detail="Name already exists")
    
    if status_update.name.lower() in system_names:
        raise HTTPException(status_code=409, detail="Conflicts with system status")

2. Color Update (Requirement 2.2)

  • Accepts hex color codes (e.g., #FF5733)
  • Validates color format via schema
  • Updates color independently of other fields
# Update color if provided
if status_update.color is not None:
    status_to_update['color'] = status_update.color

3. Default Status Management (Requirement 2.3, 5.2)

  • When setting a status as default, automatically unsets all other defaults
  • Ensures only one default status exists at a time
  • Allows unsetting default status
# Handle is_default flag
if status_update.is_default is not None:
    if status_update.is_default:
        # If setting as default, unset other default statuses
        for status_data in custom_statuses_data:
            if isinstance(status_data, dict):
                status_data['is_default'] = False
        
        # Set this status as default
        status_to_update['is_default'] = True
    else:
        # Just unset this status as default
        status_to_update['is_default'] = False

4. JSON Column Updates

Uses flag_modified to ensure SQLAlchemy detects changes to JSON columns:

# Update the status in the list
custom_statuses_data[status_index] = status_to_update
db_project.custom_task_statuses = custom_statuses_data

# Use flag_modified for JSON column updates
flag_modified(db_project, 'custom_task_statuses')

db.commit()

Error Handling

404 Not Found

  • Project doesn't exist
  • Status ID not found in project

409 Conflict

  • Status name already exists in project
  • Status name conflicts with system status

403 Forbidden

  • User is not coordinator or admin

422 Unprocessable Entity

  • Invalid request body format
  • Invalid color format
  • Invalid name length

Example Usage

Update Status Name

curl -X PUT "http://localhost:8000/projects/1/task-statuses/custom_abc123" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "New Status Name"}'

Update Status Color

curl -X PUT "http://localhost:8000/projects/1/task-statuses/custom_abc123" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"color": "#00FF00"}'

Update Both Name and Color

curl -X PUT "http://localhost:8000/projects/1/task-statuses/custom_abc123" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "Updated Status", "color": "#0000FF"}'

Set as Default Status

curl -X PUT "http://localhost:8000/projects/1/task-statuses/custom_abc123" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"is_default": true}'

Testing

To test this endpoint:

  1. Start the backend server:

    cd backend
    uvicorn main:app --reload
    
  2. Create a custom status first (if needed):

    curl -X POST "http://localhost:8000/projects/1/task-statuses" \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{"name": "Test Status", "color": "#FF5733"}'
    
  3. Update the status using the examples above

  4. Verify changes by getting all statuses:

    curl -X GET "http://localhost:8000/projects/1/task-statuses" \
      -H "Authorization: Bearer <token>"
    

Integration with Frontend

The frontend can use this endpoint to:

  1. Update status names when users edit them
  2. Change status colors via color picker
  3. Set/unset default status via toggle or button
  4. Update multiple fields at once

The response includes the updated status and all statuses, allowing the frontend to update its state in a single request.

Database Schema

The custom task statuses are stored in the projects table as a JSON column:

custom_task_statuses JSON  -- Array of status objects

Each status object has the structure:

{
    "id": "custom_abc123",
    "name": "Status Name",
    "color": "#FF5733",
    "order": 0,
    "is_default": false
}
  • GET /projects/{project_id}/task-statuses - Get all statuses
  • POST /projects/{project_id}/task-statuses - Create new status
  • DELETE /projects/{project_id}/task-statuses/{status_id} - Delete status (to be implemented)
  • PATCH /projects/{project_id}/task-statuses/reorder - Reorder statuses (to be implemented)