- Implemented tests for ScheduledTaskRepository covering task creation, retrieval, filtering, and status updates. - Developed tests for SchedulerService including task creation, cancellation, user task retrieval, and maintenance jobs. - Created tests for TaskHandlerRegistry to validate task execution for various types, including credit recharge and sound playback. - Ensured proper error handling and edge cases in task execution scenarios. - Added fixtures and mocks to facilitate isolated testing of services and repositories.
228 lines
8.1 KiB
Python
228 lines
8.1 KiB
Python
"""API endpoints for scheduled task management."""
|
|
|
|
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
|
|
from app.core.database import get_db
|
|
from app.core.dependencies import (
|
|
get_admin_user,
|
|
get_current_active_user,
|
|
)
|
|
from app.models.scheduled_task import RecurrenceType, ScheduledTask, TaskStatus, TaskType
|
|
from app.models.user import User
|
|
from app.schemas.scheduler import (
|
|
ScheduledTaskCreate,
|
|
ScheduledTaskResponse,
|
|
ScheduledTaskUpdate,
|
|
)
|
|
from app.services.scheduler import SchedulerService
|
|
|
|
router = APIRouter(prefix="/scheduler")
|
|
|
|
|
|
def get_scheduler_service() -> SchedulerService:
|
|
"""Get the global scheduler service instance."""
|
|
from app.main import get_global_scheduler_service
|
|
return get_global_scheduler_service()
|
|
|
|
|
|
@router.post("/tasks", response_model=ScheduledTaskResponse)
|
|
async def create_task(
|
|
task_data: ScheduledTaskCreate,
|
|
current_user: User = Depends(get_current_active_user),
|
|
scheduler_service: SchedulerService = Depends(get_scheduler_service),
|
|
) -> ScheduledTask:
|
|
"""Create a new scheduled task."""
|
|
try:
|
|
task = await scheduler_service.create_task(
|
|
name=task_data.name,
|
|
task_type=task_data.task_type,
|
|
scheduled_at=task_data.scheduled_at,
|
|
parameters=task_data.parameters,
|
|
user_id=current_user.id,
|
|
timezone=task_data.timezone,
|
|
recurrence_type=task_data.recurrence_type,
|
|
cron_expression=task_data.cron_expression,
|
|
recurrence_count=task_data.recurrence_count,
|
|
expires_at=task_data.expires_at,
|
|
)
|
|
return task
|
|
except Exception as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
|
|
|
|
@router.get("/tasks", response_model=List[ScheduledTaskResponse])
|
|
async def get_user_tasks(
|
|
status: Optional[TaskStatus] = Query(None, description="Filter by task status"),
|
|
task_type: Optional[TaskType] = Query(None, description="Filter by task type"),
|
|
limit: Optional[int] = Query(50, description="Maximum number of tasks to return"),
|
|
offset: Optional[int] = Query(0, description="Number of tasks to skip"),
|
|
current_user: User = Depends(get_current_active_user),
|
|
scheduler_service: SchedulerService = Depends(get_scheduler_service),
|
|
) -> List[ScheduledTask]:
|
|
"""Get user's scheduled tasks."""
|
|
return await scheduler_service.get_user_tasks(
|
|
user_id=current_user.id,
|
|
status=status,
|
|
task_type=task_type,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
|
|
|
|
@router.get("/tasks/{task_id}", response_model=ScheduledTaskResponse)
|
|
async def get_task(
|
|
task_id: int,
|
|
current_user: User = Depends(get_current_active_user),
|
|
db_session: AsyncSession = Depends(get_db),
|
|
) -> ScheduledTask:
|
|
"""Get a specific scheduled task."""
|
|
from app.repositories.scheduled_task import ScheduledTaskRepository
|
|
|
|
repo = ScheduledTaskRepository(db_session)
|
|
task = await repo.get_by_id(task_id)
|
|
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
# Check if user owns the task or is admin
|
|
if task.user_id != current_user.id and not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Access denied")
|
|
|
|
return task
|
|
|
|
|
|
@router.patch("/tasks/{task_id}", response_model=ScheduledTaskResponse)
|
|
async def update_task(
|
|
task_id: int,
|
|
task_update: ScheduledTaskUpdate,
|
|
current_user: User = Depends(get_current_active_user),
|
|
db_session: AsyncSession = Depends(get_db),
|
|
) -> ScheduledTask:
|
|
"""Update a scheduled task."""
|
|
from app.repositories.scheduled_task import ScheduledTaskRepository
|
|
|
|
repo = ScheduledTaskRepository(db_session)
|
|
task = await repo.get_by_id(task_id)
|
|
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
# Check if user owns the task or is admin
|
|
if task.user_id != current_user.id and not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Access denied")
|
|
|
|
# Update task fields
|
|
update_data = task_update.model_dump(exclude_unset=True)
|
|
for field, value in update_data.items():
|
|
setattr(task, field, value)
|
|
|
|
updated_task = await repo.update(task)
|
|
return updated_task
|
|
|
|
|
|
@router.delete("/tasks/{task_id}")
|
|
async def cancel_task(
|
|
task_id: int,
|
|
current_user: User = Depends(get_current_active_user),
|
|
scheduler_service: SchedulerService = Depends(get_scheduler_service),
|
|
db_session: AsyncSession = Depends(get_db),
|
|
) -> dict:
|
|
"""Cancel a scheduled task."""
|
|
from app.repositories.scheduled_task import ScheduledTaskRepository
|
|
|
|
repo = ScheduledTaskRepository(db_session)
|
|
task = await repo.get_by_id(task_id)
|
|
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
# Check if user owns the task or is admin
|
|
if task.user_id != current_user.id and not current_user.is_admin:
|
|
raise HTTPException(status_code=403, detail="Access denied")
|
|
|
|
success = await scheduler_service.cancel_task(task_id)
|
|
if not success:
|
|
raise HTTPException(status_code=400, detail="Failed to cancel task")
|
|
|
|
return {"message": "Task cancelled successfully"}
|
|
|
|
|
|
# Admin-only endpoints
|
|
@router.get("/admin/tasks", response_model=List[ScheduledTaskResponse])
|
|
async def get_all_tasks(
|
|
status: Optional[TaskStatus] = Query(None, description="Filter by task status"),
|
|
task_type: Optional[TaskType] = Query(None, description="Filter by task type"),
|
|
limit: Optional[int] = Query(100, description="Maximum number of tasks to return"),
|
|
offset: Optional[int] = Query(0, description="Number of tasks to skip"),
|
|
current_user: User = Depends(get_admin_user),
|
|
db_session: AsyncSession = Depends(get_db),
|
|
) -> List[ScheduledTask]:
|
|
"""Get all scheduled tasks (admin only)."""
|
|
from app.repositories.scheduled_task import ScheduledTaskRepository
|
|
|
|
repo = ScheduledTaskRepository(db_session)
|
|
|
|
# Get all tasks with pagination and filtering
|
|
from sqlmodel import select
|
|
|
|
statement = select(ScheduledTask)
|
|
|
|
if status:
|
|
statement = statement.where(ScheduledTask.status == status)
|
|
|
|
if task_type:
|
|
statement = statement.where(ScheduledTask.task_type == task_type)
|
|
|
|
statement = statement.order_by(ScheduledTask.scheduled_at.desc())
|
|
|
|
if offset:
|
|
statement = statement.offset(offset)
|
|
|
|
if limit:
|
|
statement = statement.limit(limit)
|
|
|
|
result = await db_session.exec(statement)
|
|
return list(result.all())
|
|
|
|
|
|
@router.get("/admin/system-tasks", response_model=List[ScheduledTaskResponse])
|
|
async def get_system_tasks(
|
|
status: Optional[TaskStatus] = Query(None, description="Filter by task status"),
|
|
task_type: Optional[TaskType] = Query(None, description="Filter by task type"),
|
|
current_user: User = Depends(get_admin_user),
|
|
db_session: AsyncSession = Depends(get_db),
|
|
) -> List[ScheduledTask]:
|
|
"""Get system tasks (admin only)."""
|
|
from app.repositories.scheduled_task import ScheduledTaskRepository
|
|
|
|
repo = ScheduledTaskRepository(db_session)
|
|
return await repo.get_system_tasks(status=status, task_type=task_type)
|
|
|
|
|
|
@router.post("/admin/system-tasks", response_model=ScheduledTaskResponse)
|
|
async def create_system_task(
|
|
task_data: ScheduledTaskCreate,
|
|
current_user: User = Depends(get_admin_user),
|
|
scheduler_service: SchedulerService = Depends(get_scheduler_service),
|
|
) -> ScheduledTask:
|
|
"""Create a system task (admin only)."""
|
|
try:
|
|
task = await scheduler_service.create_task(
|
|
name=task_data.name,
|
|
task_type=task_data.task_type,
|
|
scheduled_at=task_data.scheduled_at,
|
|
parameters=task_data.parameters,
|
|
user_id=None, # System task
|
|
timezone=task_data.timezone,
|
|
recurrence_type=task_data.recurrence_type,
|
|
cron_expression=task_data.cron_expression,
|
|
recurrence_count=task_data.recurrence_count,
|
|
expires_at=task_data.expires_at,
|
|
)
|
|
return task
|
|
except Exception as e:
|
|
raise HTTPException(status_code=400, detail=str(e)) |