task-queue-system/app/api.py

69 lines
No EOL
2.4 KiB
Python

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from app.db import Base, engine, SessionLocal
from app.schemas import CreateJobRequest, JobInfo, TaskInfo
from app.services import create_job, get_job_with_tasks
from app.models import Job, Task
from typing import List
from uuid import UUID
import re, idna
app = FastAPI(title="Task Queue API", version="0.3.0")
async def get_session() -> AsyncSession:
async with SessionLocal() as session:
yield session
@app.on_event("startup")
async def on_startup():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
DOMAIN_RE = re.compile(r"^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z0-9-]{1,63})+$")
def _validate_domain(domain: str) -> str:
try:
_ = idna.encode(domain).decode()
except Exception:
raise HTTPException(status_code=400, detail="Invalid domain (IDNA)")
if not DOMAIN_RE.match(domain):
raise HTTPException(status_code=400, detail="Invalid domain format")
return domain
@app.post("/jobs", response_model=JobInfo)
async def create_job_endpoint(req: CreateJobRequest, session: AsyncSession = Depends(get_session)):
if not req.job_type:
raise HTTPException(status_code=400, detail="job_type is required")
domain = _validate_domain(req.domain.strip().lower())
job = await create_job(session, req.job_type, domain, req.payload)
job = await get_job_with_tasks(session, job.id)
return _job_to_schema(job)
@app.get("/jobs/{job_id}", response_model=JobInfo)
async def get_job_endpoint(job_id: UUID, session: AsyncSession = Depends(get_session)):
job = await get_job_with_tasks(session, job_id)
if not job:
raise HTTPException(status_code=404, detail="Job not found")
return _job_to_schema(job)
def _job_to_schema(job: Job) -> JobInfo:
tasks: List[TaskInfo] = [
TaskInfo(
id=t.id,
name=t.name,
status=t.status.value if hasattr(t.status, "value") else str(t.status),
retries=t.retries,
max_retries=t.max_retries,
result=t.result,
error=t.error,
)
for t in job.tasks
]
return JobInfo(
id=job.id,
job_type=job.job_type,
status=job.status.value if hasattr(job.status, "value") else str(job.status),
domain=job.domain,
payload=job.payload,
tasks=tasks,
)