- Updated type hints from List/Optional to list/None for better readability and consistency across the codebase. - Refactored import statements for better organization and clarity. - Enhanced the ScheduledTaskBase schema to use modern type hints. - Cleaned up unnecessary comments and whitespace in various files. - Improved error handling and logging in task execution handlers. - Updated test cases to reflect changes in type hints and ensure compatibility with the new structure.
227 lines
7.9 KiB
Python
227 lines
7.9 KiB
Python
"""API endpoints for scheduled task management."""
|
|
|
|
|
|
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 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: TaskStatus | None = Query(None, description="Filter by task status"),
|
|
task_type: TaskType | None = Query(None, description="Filter by task type"),
|
|
limit: int | None = Query(50, description="Maximum number of tasks to return"),
|
|
offset: int | None = 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: TaskStatus | None = Query(None, description="Filter by task status"),
|
|
task_type: TaskType | None = Query(None, description="Filter by task type"),
|
|
limit: int | None = Query(100, description="Maximum number of tasks to return"),
|
|
offset: int | None = 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: TaskStatus | None = Query(None, description="Filter by task status"),
|
|
task_type: TaskType | None = 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))
|