Init Repo
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
# 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
|
||||
|
||||
```python
|
||||
@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:
|
||||
|
||||
```python
|
||||
{
|
||||
"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`:
|
||||
|
||||
```python
|
||||
{
|
||||
"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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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:
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
cd backend
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
2. Create a custom status first (if needed):
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
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:
|
||||
|
||||
```sql
|
||||
custom_task_statuses JSON -- Array of status objects
|
||||
```
|
||||
|
||||
Each status object has the structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "custom_abc123",
|
||||
"name": "Status Name",
|
||||
"color": "#FF5733",
|
||||
"order": 0,
|
||||
"is_default": false
|
||||
}
|
||||
```
|
||||
|
||||
## Related Endpoints
|
||||
|
||||
- `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)
|
||||
Reference in New Issue
Block a user