feat(awooop): link telegram alerts to incident runs
Some checks failed
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / tests (push) Failing after 1m58s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped

This commit is contained in:
Your Name
2026-05-17 22:17:21 +08:00
parent 3aabceb234
commit 6868a9a93d
8 changed files with 163 additions and 15 deletions

View File

@@ -141,11 +141,13 @@ async def list_runs(
project_id: str | None,
state: str | None,
remediation_status: str | None,
incident_id: str | None,
page: int,
per_page: int,
) -> dict[str, Any]:
"""列出 runs支援 project_id、state、remediation_status filter 與分頁。"""
"""列出 runs支援 project_id、state、remediation_status、incident_id filter 與分頁。"""
_validate_remediation_status_filter(remediation_status)
_validate_incident_id_filter(incident_id)
async with get_db_context("awoooi") as db:
stmt = select(AwoooPRunState).order_by(AwoooPRunState.created_at.desc())
@@ -155,7 +157,7 @@ async def list_runs(
stmt = stmt.where(AwoooPRunState.state == state)
offset = (page - 1) * per_page
if remediation_status:
if remediation_status or incident_id:
result = await db.execute(stmt)
candidate_rows = list(result.scalars().all())
context_limit = _list_filter_context_limit(len(candidate_rows))
@@ -176,6 +178,10 @@ async def list_runs(
remediation_summaries.get(row.run_id),
remediation_status,
)
and _remediation_summary_matches_incident_id(
remediation_summaries.get(row.run_id),
incident_id,
)
]
total = len(filtered_rows)
rows = filtered_rows[offset : offset + per_page]
@@ -521,14 +527,36 @@ def _validate_remediation_status_filter(value: str | None) -> None:
)
def _validate_incident_id_filter(value: str | None) -> None:
if value is None:
return
if not _INCIDENT_ID_RE.fullmatch(value):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="incident_id 格式錯誤,必須是 INC-YYYYMMDD-XXXX",
)
def _remediation_summary_matches_status(
summary: dict[str, Any] | None,
remediation_status: str,
remediation_status: str | None,
) -> bool:
if remediation_status is None:
return True
status_value = str((summary or {}).get("status") or "no_evidence")
return status_value == remediation_status
def _remediation_summary_matches_incident_id(
summary: dict[str, Any] | None,
incident_id: str | None,
) -> bool:
if incident_id is None:
return True
incident_ids = (summary or {}).get("incident_ids")
return isinstance(incident_ids, list) and incident_id in incident_ids
async def _build_run_remediation_summaries(
*,
runs: list[AwoooPRunState],