pipeline.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. from __future__ import annotations
  2. from fastapi import APIRouter, HTTPException, Query
  3. from api.schemas.pipeline import CreatePipelineRunBody
  4. from api.services.pipeline import (
  5. cancel_run,
  6. create_run,
  7. get_run,
  8. list_runs,
  9. pipeline_health,
  10. resume_run,
  11. retry_step,
  12. )
  13. router = APIRouter(prefix="/api/pipeline", tags=["pipeline"])
  14. @router.get("/runs")
  15. def pipeline_runs(
  16. limit: int = Query(default=50, ge=1, le=200),
  17. status: str | None = None,
  18. biz_dt: str | None = Query(default=None, pattern=r"^\d{8}$"),
  19. ) -> dict:
  20. return {"items": list_runs(limit=limit, status=status, biz_dt=biz_dt)}
  21. @router.post("/runs", status_code=202)
  22. def create_pipeline_run(body: CreatePipelineRunBody | None = None) -> dict:
  23. payload = body or CreatePipelineRunBody()
  24. return create_run(
  25. biz_dt=payload.biz_dt,
  26. source="api",
  27. reason=payload.reason,
  28. )
  29. @router.get("/runs/{run_id}")
  30. def pipeline_run_detail(run_id: str) -> dict:
  31. result = get_run(run_id)
  32. if result is None:
  33. raise HTTPException(status_code=404, detail="pipeline run not found")
  34. return result
  35. @router.get("/runs/{run_id}/steps")
  36. def pipeline_run_steps(run_id: str) -> dict:
  37. result = get_run(run_id)
  38. if result is None:
  39. raise HTTPException(status_code=404, detail="pipeline run not found")
  40. return {"items": result["steps"]}
  41. @router.post("/runs/{run_id}/resume", status_code=202)
  42. def resume_pipeline_run(run_id: str) -> dict:
  43. if not resume_run(run_id):
  44. raise HTTPException(status_code=404, detail="pipeline run not found or not resumable")
  45. result = get_run(run_id)
  46. assert result is not None
  47. return {"accepted": True, "run_id": run_id, "status": result["status"]}
  48. @router.post("/runs/{run_id}/cancel", status_code=202)
  49. def cancel_pipeline_run(run_id: str) -> dict:
  50. if not cancel_run(run_id):
  51. raise HTTPException(status_code=404, detail="pipeline run not found")
  52. result = get_run(run_id)
  53. assert result is not None
  54. return {"accepted": True, "run_id": run_id, "status": result["status"]}
  55. @router.post("/runs/{run_id}/steps/{step_key}/retry", status_code=202)
  56. def retry_pipeline_run_step(run_id: str, step_key: str) -> dict:
  57. if not retry_step(run_id, step_key):
  58. raise HTTPException(
  59. status_code=409,
  60. detail="step is not the first failed step or run is not resumable",
  61. )
  62. return {
  63. "accepted": True,
  64. "run_id": run_id,
  65. "step_key": step_key,
  66. "status": "queued",
  67. }
  68. @router.get("/health")
  69. def health() -> dict:
  70. return pipeline_health()