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:
-
Start the backend server:
cd backend uvicorn main:app --reload -
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"}' -
Update the status using the examples above
-
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:
- Update status names when users edit them
- Change status colors via color picker
- Set/unset default status via toggle or button
- 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
}
Related Endpoints
GET /projects/{project_id}/task-statuses- Get all statusesPOST /projects/{project_id}/task-statuses- Create new statusDELETE /projects/{project_id}/task-statuses/{status_id}- Delete status (to be implemented)PATCH /projects/{project_id}/task-statuses/reorder- Reorder statuses (to be implemented)