"""Job APIs: submit (enqueue), query status, list.""" from __future__ import annotations from typing import Optional from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Query, status from asf_domain import ( Job, JobCreate, JobUpdate, JobStatus, JobType, ) from ...repositories import NotFoundError, TenantMismatchError from .deps import job_service from ...services import JobService router = APIRouter() @router.get("", response_model=list[Job], summary="List jobs (tenant scoped)") async def list_jobs( project_id: Optional[UUID] = Query(None), status: Optional[JobStatus] = Query(None), type: Optional[JobType] = Query(None), limit: int = Query(50, ge=1, le=500), offset: int = Query(0, ge=0), svc: JobService = Depends(job_service), ) -> list[Job]: return await svc.list( project_id=project_id, status=status, type=type, limit=limit, offset=offset ) @router.post( "", response_model=Job, status_code=status.HTTP_202_ACCEPTED, summary="Submit a new asynchronous job. The job record is persisted and the work is enqueued on RabbitMQ.", ) async def submit_job( body: JobCreate, svc: JobService = Depends(job_service), ) -> Job: try: return await svc.submit(body) except NotFoundError as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc @router.get("/{job_id}", response_model=Job, summary="Get job by id") async def get_job( job_id: UUID, svc: JobService = Depends(job_service), ) -> Job: j = await svc.get(job_id) if j is None: raise HTTPException(status_code=404, detail=f"Job {job_id} not found") return j @router.patch( "/{job_id}", response_model=Job, summary="Update job status / progress / result (used by workers and, in future, cancel operations).", ) async def update_job( job_id: UUID, body: JobUpdate, svc: JobService = Depends(job_service), ) -> Job: try: return await svc.update(job_id, body) except (NotFoundError, TenantMismatchError) as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc