From 13ce6218ae7f3d38a6b42dfefba5ce2c4c2a597c Mon Sep 17 00:00:00 2001 From: ogt Date: Thu, 9 Jul 2026 19:46:14 +0800 Subject: [PATCH] fix(api): degrade recent event readback failures --- apps/api/src/api/v1/platform/events.py | 31 +++++++++++++++---- .../test_platform_events_project_context.py | 27 ++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/apps/api/src/api/v1/platform/events.py b/apps/api/src/api/v1/platform/events.py index f7e6b6e69..4424a8131 100644 --- a/apps/api/src/api/v1/platform/events.py +++ b/apps/api/src/api/v1/platform/events.py @@ -118,6 +118,8 @@ class RecentEventsResponse(BaseModel): events: list[ChannelEventItem] total: int limit: int + source_status: str = "ready" + source_error: str | None = None class ChannelEventDossierItem(BaseModel): @@ -667,9 +669,26 @@ async def list_recent_events( ), limit: int = Query(20, ge=1, le=100, description="最多返回筆數"), ) -> dict[str, Any]: - return await list_recent_channel_events( - project_id=project_id, - channel_type=channel_type, - provider_prefix=provider_prefix, - limit=limit, - ) + try: + return await list_recent_channel_events( + project_id=project_id, + channel_type=channel_type, + provider_prefix=provider_prefix, + limit=limit, + ) + except Exception as exc: + logger.warning( + "recent_channel_events_source_unavailable", + project_id=project_id or "awoooi", + channel_type=channel_type, + provider_prefix=provider_prefix, + limit=limit, + error=exc.__class__.__name__, + ) + return { + "events": [], + "total": 0, + "limit": max(1, min(limit, 100)), + "source_status": "source_unavailable", + "source_error": exc.__class__.__name__, + } diff --git a/apps/api/tests/test_platform_events_project_context.py b/apps/api/tests/test_platform_events_project_context.py index 7b3dc95a7..f0caf5211 100644 --- a/apps/api/tests/test_platform_events_project_context.py +++ b/apps/api/tests/test_platform_events_project_context.py @@ -10,6 +10,33 @@ from src.core.context import ( ) +@pytest.mark.asyncio +async def test_recent_events_degrades_when_source_unavailable(monkeypatch): + async def fake_list_recent_channel_events(**kwargs): + raise RuntimeError("db unavailable") + + monkeypatch.setattr( + events, + "list_recent_channel_events", + fake_list_recent_channel_events, + ) + + response = await events.list_recent_events( + project_id="awoooi", + channel_type=None, + provider_prefix="alertmanager", + limit=20, + ) + + assert response == { + "events": [], + "total": 0, + "limit": 20, + "source_status": "source_unavailable", + "source_error": "RuntimeError", + } + + @pytest.mark.asyncio async def test_source_correlation_review_uses_body_project_context(monkeypatch): observed: dict[str, object] = {}