feat(db): wire jobs router to SQLAlchemy, add cancel endpoint
CI / basic-check (push) Has been cancelled
CI / basic-check (push) Has been cancelled
This commit is contained in:
@@ -1,19 +1,63 @@
|
|||||||
from fastapi import APIRouter, HTTPException
|
import json
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from app.schemas import EntityResponse, Job, ListResponse
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from app.services.store import JOBS
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.database import get_db
|
||||||
|
from app.models import JobModel
|
||||||
|
from app.schemas import EntityResponse, Job, JobStatus, ListResponse
|
||||||
|
|
||||||
router = APIRouter(prefix='/jobs', tags=['jobs'])
|
router = APIRouter(prefix='/jobs', tags=['jobs'])
|
||||||
|
|
||||||
|
|
||||||
|
def _to_job(m: JobModel) -> Job:
|
||||||
|
return Job(
|
||||||
|
id=m.id, type=m.type, status=m.status,
|
||||||
|
progress=m.progress, message=m.message, error=m.error,
|
||||||
|
payload=json.loads(m.payload_json or '{}'),
|
||||||
|
result=json.loads(m.result_json or 'null'),
|
||||||
|
created_at=m.created_at,
|
||||||
|
started_at=m.started_at,
|
||||||
|
finished_at=m.finished_at,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get('', response_model=ListResponse[Job])
|
@router.get('', response_model=ListResponse[Job])
|
||||||
def list_jobs() -> ListResponse[Job]:
|
def list_jobs(
|
||||||
return ListResponse(count=len(JOBS), items=JOBS)
|
status: JobStatus | None = Query(default=None),
|
||||||
|
type: str | None = Query(default=None),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
q = db.query(JobModel)
|
||||||
|
if status:
|
||||||
|
q = q.filter(JobModel.status == status)
|
||||||
|
if type:
|
||||||
|
q = q.filter(JobModel.type == type)
|
||||||
|
rows = q.order_by(JobModel.created_at.desc()).all()
|
||||||
|
return ListResponse(count=len(rows), items=[_to_job(r) for r in rows])
|
||||||
|
|
||||||
|
|
||||||
@router.get('/{job_id}', response_model=EntityResponse[Job])
|
@router.get('/{job_id}', response_model=EntityResponse[Job])
|
||||||
def get_job(job_id: str) -> EntityResponse[Job]:
|
def get_job(job_id: str, db: Session = Depends(get_db)):
|
||||||
for item in JOBS:
|
row = db.query(JobModel).filter(JobModel.id == job_id).first()
|
||||||
if item.id == job_id:
|
if not row:
|
||||||
return EntityResponse(item=item)
|
|
||||||
raise HTTPException(status_code=404, detail='Job not found')
|
raise HTTPException(status_code=404, detail='Job not found')
|
||||||
|
return EntityResponse(item=_to_job(row))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/{job_id}/cancel', response_model=EntityResponse[Job])
|
||||||
|
def cancel_job(job_id: str, db: Session = Depends(get_db)):
|
||||||
|
row = db.query(JobModel).filter(JobModel.id == job_id).first()
|
||||||
|
if not row:
|
||||||
|
raise HTTPException(status_code=404, detail='Job not found')
|
||||||
|
if row.status not in ('pending', 'running'):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=409,
|
||||||
|
detail=f'Cannot cancel job with status {row.status!r}',
|
||||||
|
)
|
||||||
|
row.status = 'cancelled'
|
||||||
|
row.finished_at = datetime.now(timezone.utc)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(row)
|
||||||
|
return EntityResponse(item=_to_job(row))
|
||||||
|
|||||||
Reference in New Issue
Block a user