Skip to content

Fix/sonar reliability#149

Merged
HardMax71 merged 6 commits intomainfrom
fix/sonar-reliability
Feb 8, 2026
Merged

Fix/sonar reliability#149
HardMax71 merged 6 commits intomainfrom
fix/sonar-reliability

Conversation

@HardMax71
Copy link
Owner

@HardMax71 HardMax71 commented Feb 8, 2026


Summary by cubic

Standardized query parameter annotations and explicit error responses across auth, events, executions, sagas, DLQ, notifications, and admin APIs. Enforced notification action_url as a required string; updated OpenAPI and regenerated frontend SDK/types.

  • Refactors

    • Switched to typing.Annotated for Query/Header params; added endpoint docstrings and parameter descriptions.
    • Introduced shared ErrorResponse and declared 401/403/404/500 responses where relevant (e.g., login, execute, saga, events).
    • Removed broad try/except in admin/events and admin/settings; use direct domain-to-Pydantic conversion.
    • Regenerated OpenAPI and frontend SDK/types; adjusted frontend auth store for verifyToken shape.
  • Migration

    • action_url is now required when creating notifications and always returned as a string.
    • Pass a meaningful URL (e.g., /api/v1/executions/{id}/result or /api/v1/notifications) when creating notifications.

Written for commit cf5cc8c. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Notifications now always include an action link; delivery channels (SSE, Slack, UI) surface this link.
    • Token verification endpoint exposes dedicated response types in the API.
  • Bug Fixes

    • Admin event replay/browse/reporting endpoints return clearer not-found and error responses.
  • Chores

    • OpenAPI and client SDK updated with richer error schemas, clearer parameter descriptions/validation, and enhanced API documentation.

@coderabbitai
Copy link

coderabbitai bot commented Feb 8, 2026

📝 Walkthrough

Walkthrough

Refactors many FastAPI routes to use typing.Annotated for Query metadata, adds a shared ErrorResponse schema and OpenAPI response mappings, makes notification.action_url non-optional and threads it through NotificationService and tests, and simplifies admin events replay/error handling (explicit checks, replay correlation and conditional background enqueue).

Changes

Cohort / File(s) Summary
FastAPI route refactor & OpenAPI responses
backend/app/api/routes/admin/events.py, backend/app/api/routes/admin/users.py, backend/app/api/routes/dlq.py, backend/app/api/routes/events.py, backend/app/api/routes/execution.py, backend/app/api/routes/notifications.py, backend/app/api/routes/replay.py, backend/app/api/routes/saga.py, backend/app/api/routes/saved_scripts.py, backend/app/api/routes/user_settings.py, backend/app/api/routes/auth.py, backend/app/api/routes/admin/settings.py
Converted many Query(...) params to Annotated[..., Query(...)], added Annotated imports, added short docstrings across handlers, and expanded OpenAPI responses mappings to reference new ErrorResponse (various 400/403/404/422/500 usages).
Admin events: control flow & replay
backend/app/api/routes/admin/events.py
Removed broad try/except wrappers, replaced with direct service calls and explicit 404/500 checks; added Annotated parameter typings; introduced local replay_correlation_id and replay_filter, conditional background enqueueing, and standardized EventReplay responses.
DLQ endpoints: typing & docs
backend/app/api/routes/dlq.py
Added Annotated-wrapped Query params with descriptions, return type annotations (e.g., DLQMessagesResponse), docstrings, and explicit 404 responses for individual message endpoints.
Notification model & service
backend/app/db/docs/notification.py, backend/app/domain/notification/models.py, backend/app/schemas_pydantic/notification.py, backend/app/services/notification_service.py
Made action_url non-optional (str, default ""), added action_url: str param to NotificationService.create_notification, passed concrete action URLs for system/execution notifications, and adjusted delivery logic to treat presence via bool(action_url).
Tests: notification callsites updated
backend/tests/e2e/notifications/test_notification_sse.py, backend/tests/e2e/services/notifications/test_notification_service.py
Updated tests to pass the new action_url argument and adjusted assertions where paths changed.
Schemas / OpenAPI / frontend typings
backend/app/schemas_pydantic/common.py, docs/reference/openapi.json, frontend/src/lib/api/index.ts, frontend/src/lib/api/sdk.gen.ts, frontend/src/lib/api/types.gen.ts
Added ErrorResponse Pydantic/type definitions; updated OpenAPI and generated frontend types to include ErrorResponse in error maps; changed NotificationResponse.action_url to string and made read_at optional in generated types; tightened SDK function error typings.
Admin settings: simplified conversions
backend/app/api/routes/admin/settings.py
Removed mapper helpers, use direct model_dump/model_validate conversions, and added explicit error response mappings for 400/422/500.
Minor docstring/typing updates
backend/app/api/routes/grafana_alerts.py, other route files listed above
Added/standardized docstrings and minor Annotated/Query adjustments without behavioral changes.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant API as AdminEventsAPI
  participant Service as AdminEventsService
  participant BG as BackgroundTasks
  participant Queue as ReplayQueue

  Client->>API: POST /admin/events/replay (EventReplayRequest)
  Note right of API: build replay_filter\ngenerate replay_correlation_id
  API->>Service: create_replay_session(replay_filter, correlation_id)
  Service-->>API: replay_session_id
  API->>Service: start_replay(replay_session_id, dry_run)
  Service-->>API: replay_result (success or error)
  alt success and enqueue requested
    API->>BG: add_task(enqueue_replay, replay_session_id, correlation_id)
    BG->>Queue: enqueue(replay_session_id, correlation_id)
  end
  API-->>Client: EventReplayResponse (session_id, status)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • fix: event bus #128 — modifies notification_service.py and notification payload handling; directly related to the new action_url parameter and downstream changes.
  • fix: moving from svelte4 to svelte5 #145 — changes action_url from optional to non-null across schemas; strongly related to the notification model/type updates here.
  • chore: type fixes #74 — touches admin events/replay endpoints and response/serialization patterns similar to this PR.

Poem

🐰 I hopped through routes and Annotated lines,

Action links fixed, no None in the signs,
Errors now named in the API view,
Replays queued tidy — a scrub and a chew,
A rabbit cheers the tidy refactor, hooray and dine!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Fix/sonar reliability' is vague and does not clearly convey the main changes in the pull request, which involve standardizing FastAPI parameters, removing broad error handling, adding error schemas, and making notification URLs required. Consider using a more descriptive title such as 'Improve error handling and API documentation across routes' or 'Standardize FastAPI parameters and add explicit error responses' to better reflect the scope of changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 95.50% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/sonar-reliability

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@backend/app/db/docs/notification.py`:
- Line 31: Existing DB documents with action_url:null will raise Pydantic v2
validation errors because action_url is declared as a non-nullable str; to fix,
update the document model field declaration for action_url in
backend/app/db/docs/notification.py to accept None (e.g., change action_url: str
= "" to action_url: str | None = None or use Optional[str] = None) so Beanie can
load legacy records, or alternatively implement a field validator on the same
model to coerce None -> ""; refer to the action_url field in the notification
document class when making this change.
🧹 Nitpick comments (3)
backend/app/api/routes/admin/events.py (2)

55-58: Missing lower-bound constraint on hours.

hours allows zero or negative values since only le=168 is specified. Consider adding ge=1 for consistency with similar parameters elsewhere in this PR (e.g., older_than_hours in replay.py).

Suggested fix
-    hours: Annotated[int, Query(le=168)] = 24,
+    hours: Annotated[int, Query(ge=1, le=168)] = 24,

64-67: Missing lower-bound constraint on limit for export endpoints.

Both CSV and JSON export endpoints allow limit ≤ 0. Adding ge=1 would match the convention used in other route files.

Suggested fix (apply to both CSV line 67 and JSON line 92)
-    limit: Annotated[int, Query(le=50000)] = 10000,
+    limit: Annotated[int, Query(ge=1, le=50000)] = 10000,
backend/app/services/notification_service.py (1)

303-312: Consider extracting hardcoded API paths to constants.

"/api/v1/notifications" and "/api/v1/executions/{id}/result" (lines 308, 454, 476, 506) are scattered as string literals. If routes change, these become stale silently. A shared module of URL builders or constants would centralize them.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 14 files

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/reference/openapi.json (1)

11157-11199: ⚠️ Potential issue | 🟡 Minor

Clarify whether action_url should be required or optional—the schema and implementation are inconsistent.

The OpenAPI schema marks action_url as required, but the internal models (Notification, DomainNotification, NotificationDocument) all default action_url to an empty string. The code includes defensive null checks (e.g., in NotificationCenter.svelte and notification_service.py), suggesting the field is expected to be empty in some cases. Either enforce the requirement at creation time (require producers to set a non-empty value) or update the API schema to reflect that action_url is optional. No explicit backfilling mechanism was found for legacy data.

🤖 Fix all issues with AI agents
In `@backend/app/api/routes/admin/events.py`:
- Around line 67-70: The export endpoints allow zero or negative limits because
the Query for the limit parameter lacks a lower bound; update the limit
Annotated parameter in both export_events_csv and export_events_json to include
a ge=1 constraint (i.e., use Query(le=50000, ge=1)) so clients cannot pass 0 or
negative values; change the limit parameter declaration (the variable named
limit using Query) in both functions accordingly.
- Around line 54-61: The hours query parameter in get_event_stats currently only
enforces an upper bound (le=168) and allows zero or negative values; update the
Query for the hours parameter in the get_event_stats route to include a minimum
(e.g., ge=1) and adjust the description accordingly so it reads something like
"Lookback window in hours (1-168)"; touch the Annotated[int, Query(...)]
declaration for hours to add ge=1 and update the description text so validation
prevents 0 or negative inputs.
- Around line 193-202: The delete handler in this router (async def
delete_event) currently raises HTTPException(status_code=500) when the
service.delete_event result is falsy; change this to raise a 404 to match the
equivalent logic in backend/app/api/routes/events.py (where a missing event
returns 404) — update the HTTPException to use status_code=404 and a descriptive
detail like "Event not found" (ensure the check of deleted from
AdminEventsService.delete_event remains the same but the exception and message
are corrected for consistency).

In `@backend/app/api/routes/admin/users.py`:
- Around line 42-45: The limit query parameter in the users admin route is
missing a lower bound; update the Annotated type for limit in the function
signature (the parameter named limit in backend/app/api/routes/admin/users.py)
to include ge=1 alongside le=1000 (i.e., use Query(ge=1, le=1000)) so it matches
the bounds used in other endpoints like dlq.py and execution.py and prevents
zero/negative values.
🧹 Nitpick comments (4)
backend/app/api/routes/replay.py (1)

32-39: Consider adding ErrorResponse mappings for session-based endpoints.

Other route files in this PR (e.g., saga.py, saved_scripts.py) add responses={404: {"model": ErrorResponse}} for endpoints that look up resources by ID. The replay session endpoints (start, pause, resume, cancel, get by session_id) don't have these mappings, which is inconsistent with the rest of the PR.

Also applies to: 42-49, 52-56, 59-63, 79-82

backend/app/api/routes/auth.py (1)

261-293: Dead except block in verify_token.

The auth_service.get_current_user(request) call at Line 250 is outside the try block, so any authentication failure raises before entering the try. The code inside the try (Lines 262–277) only performs logging and constructs a return value — it won't raise a meaningful exception. This makes the except block effectively unreachable.

Consider removing the try/except wrapper here, or moving the get_current_user call inside the try if the intent is to catch auth failures at this level.

♻️ Suggested simplification
-    try:
-        logger.info(
-            "Token verification successful",
-            extra={
-                "username": current_user.username,
-                "client_ip": get_client_ip(request),
-                "user_agent": request.headers.get("user-agent"),
-            },
-        )
-        csrf_token = request.cookies.get("csrf_token", "")
-
-        return TokenValidationResponse(
-            valid=True,
-            username=current_user.username,
-            role="admin" if current_user.is_superuser else "user",
-            csrf_token=csrf_token,
-        )
-
-    except Exception as e:
-        logger.error(
-            "Token verification failed",
-            extra={
-                "client_ip": get_client_ip(request),
-                "user_agent": request.headers.get("user-agent"),
-                "error_type": type(e).__name__,
-                "error_detail": str(e),
-            },
-        )
-        raise HTTPException(
-            status_code=401,
-            detail="Invalid token",
-            headers={"WWW-Authenticate": "Bearer"},
-        ) from e
+    logger.info(
+        "Token verification successful",
+        extra={
+            "username": current_user.username,
+            "client_ip": get_client_ip(request),
+            "user_agent": request.headers.get("user-agent"),
+        },
+    )
+    csrf_token = request.cookies.get("csrf_token", "")
+
+    return TokenValidationResponse(
+        valid=True,
+        username=current_user.username,
+        role="admin" if current_user.is_superuser else "user",
+        csrf_token=csrf_token,
+    )
backend/app/api/routes/execution.py (1)

316-320: Bare except Exception swallows all errors including programming bugs.

The catch-all here converts any exception (including unexpected bugs like AttributeError, TypeError) into a generic 500 response, making debugging harder. Consider catching only expected exceptions (e.g., from the K8s client) and letting unexpected ones propagate to the global error handler.

That said, this is pre-existing behavior and the PR only adds the ErrorResponse annotation — so this is not a regression.

backend/app/api/routes/admin/events.py (1)

144-157: String-matching on exception messages is fragile.

The checks "No events found" in msg and "Too many events" in msg couple this handler to the exact wording of the service-layer exceptions. If the message text changes, these conditions silently stop matching and the raise on line 157 propagates an unhandled ValueError as a 500.

Consider using distinct exception types (e.g., EventsNotFoundError, TooManyEventsError) or structured error codes in the service layer instead.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 8, 2026

@HardMax71 HardMax71 merged commit 4964338 into main Feb 8, 2026
18 checks passed
@HardMax71 HardMax71 deleted the fix/sonar-reliability branch February 8, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant