pipeline.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  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. dry_run=payload.dry_run,
  29. )
  30. @router.get("/runs/{run_id}")
  31. def pipeline_run_detail(run_id: str) -> dict:
  32. result = get_run(run_id)
  33. if result is None:
  34. raise HTTPException(status_code=404, detail="pipeline run not found")
  35. return result
  36. @router.get("/runs/{run_id}/steps")
  37. def pipeline_run_steps(run_id: str) -> dict:
  38. result = get_run(run_id)
  39. if result is None:
  40. raise HTTPException(status_code=404, detail="pipeline run not found")
  41. return {"items": result["steps"]}
  42. @router.post("/runs/{run_id}/resume", status_code=202)
  43. def resume_pipeline_run(run_id: str) -> dict:
  44. if not resume_run(run_id):
  45. raise HTTPException(status_code=404, detail="pipeline run not found or not resumable")
  46. result = get_run(run_id)
  47. assert result is not None
  48. return {"accepted": True, "run_id": run_id, "status": result["status"]}
  49. @router.post("/runs/{run_id}/cancel", status_code=202)
  50. def cancel_pipeline_run(run_id: str) -> dict:
  51. if not cancel_run(run_id):
  52. raise HTTPException(status_code=404, detail="pipeline run not found")
  53. result = get_run(run_id)
  54. assert result is not None
  55. return {"accepted": True, "run_id": run_id, "status": result["status"]}
  56. @router.post("/runs/{run_id}/steps/{step_key}/retry", status_code=202)
  57. def retry_pipeline_run_step(run_id: str, step_key: str) -> dict:
  58. if not retry_step(run_id, step_key):
  59. raise HTTPException(
  60. status_code=409,
  61. detail="step is not the first failed step or run is not resumable",
  62. )
  63. return {
  64. "accepted": True,
  65. "run_id": run_id,
  66. "step_key": step_key,
  67. "status": "queued",
  68. }
  69. @router.get("/health")
  70. def health() -> dict:
  71. return pipeline_health()