Skip to content

Commit 63f5433

Browse files
📝 Add docstrings to codex/create-json-dashboards-and-configure-alerts (#132)
Docstrings generation was requested by @shayancoin. * #76 (comment) The following files were modified: * `backend/api/main.py` * `backend/api/metrics.py` * `backend/api/routes_observability.py` * `backend/tests/test_observability_metrics.py` * `frontend/src/app/reportWebVitals.ts` Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 310e008 commit 63f5433

File tree

5 files changed

+93
-6
lines changed

5 files changed

+93
-6
lines changed

‎backend/api/main.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ async def _ensure_sqlite_tables() -> None:
5757

5858

5959
def _resolve_route_label(request: Request) -> str:
60+
"""
61+
Resolve the label to use for the incoming request's route.
62+
63+
Parameters:
64+
request (Request): The incoming Starlette/FastAPI request.
65+
66+
Returns:
67+
str: The route path from the request scope if present, otherwise the request URL path, or "unknown" if neither is available.
68+
"""
6069
route = request.scope.get("route")
6170
if route and hasattr(route, "path") and route.path:
6271
return route.path
@@ -65,6 +74,21 @@ def _resolve_route_label(request: Request) -> str:
6574

6675
class PrometheusInstrumentationMiddleware(BaseHTTPMiddleware):
6776
async def dispatch(self, request: Request, call_next): # type: ignore[override]
77+
"""
78+
Instrument incoming HTTP requests with Prometheus metrics and forward them to the next handler.
79+
80+
The middleware skips instrumentation for the "/metrics" path. For other requests it measures request duration, calls the downstream handler, and records an HTTP metric with service="backend", the resolved route label, HTTP method, status code, and duration in seconds. If the downstream handler raises an exception, the middleware records the metric with status "500" and re-raises the exception.
81+
82+
Parameters:
83+
request (Request): The incoming Starlette/FastAPI request.
84+
call_next (Callable): The next ASGI/Starlette request handler to call.
85+
86+
Returns:
87+
Response: The response returned by the downstream handler.
88+
89+
Raises:
90+
Exception: Re-raises any exception raised by the downstream handler after recording the metric.
91+
"""
6892
if request.url.path == "/metrics":
6993
return await call_next(request)
7094

‎backend/api/metrics.py‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@ def observe_http_request(
7575

7676

7777
def observe_lcp(*, app: str, seconds: float) -> None:
78-
"""Record a Largest Contentful Paint measurement in seconds."""
78+
"""
79+
Record the Largest Contentful Paint (LCP) value for an application.
80+
81+
Parameters:
82+
app (str): Application identifier used as the `app` label.
83+
seconds (float): LCP measurement in seconds.
84+
"""
7985

8086
web_vitals_lcp.labels(app=app).observe(seconds)
81-

‎backend/api/routes_observability.py‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,18 @@ class WebVitalPayload(BaseModel):
2121

2222
@router.post("/web-vitals", status_code=status.HTTP_202_ACCEPTED)
2323
async def ingest_web_vitals(payload: WebVitalPayload) -> dict[str, bool]:
24-
"""Ingest Web Vital measurements emitted by the frontend."""
24+
"""
25+
Process frontend web-vital measurements and record LCP metrics for observability.
26+
27+
Parameters:
28+
payload (WebVitalPayload): Web vital data from the frontend. Only metrics with `name == "LCP"` are accepted.
29+
30+
Returns:
31+
result (dict[str, bool]): A dictionary with {"ok": True} on successful ingestion.
32+
33+
Raises:
34+
HTTPException: If `payload.name` is not "LCP" (400 Bad Request with detail "Only LCP metrics are supported at this time").
35+
"""
2536

2637
if payload.name != "LCP":
2738
raise HTTPException(
@@ -32,4 +43,4 @@ async def ingest_web_vitals(payload: WebVitalPayload) -> dict[str, bool]:
3243
# web-vitals reports timings in milliseconds; convert to seconds for Prometheus
3344
seconds = payload.value / 1000.0
3445
observe_lcp(app=payload.app, seconds=seconds)
35-
return {"ok": True}
46+
return {"ok": True}

‎backend/tests/test_observability_metrics.py‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99

1010

1111
def _get_sample(name: str, labels: dict[str, str]) -> float:
12+
"""
13+
Retrieve a Prometheus metric sample value for the given metric name and labels.
14+
15+
Parameters:
16+
name (str): Metric name to query.
17+
labels (dict[str, str]): Label set to match the metric sample.
18+
19+
Returns:
20+
float: The sampled metric value, or 0.0 if no matching sample exists.
21+
"""
1222
value = REGISTRY.get_sample_value(name, labels)
1323
return float(value) if value is not None else 0.0
1424

@@ -27,6 +37,12 @@ def test_lcp_ingest_records_histogram() -> None:
2737

2838

2939
def test_lcp_ingest_rejects_unsupported_metrics() -> None:
40+
"""
41+
Verifies that the web-vitals endpoint rejects unsupported metric types.
42+
43+
Sends a POST request containing a CLS metric and asserts the response status is 400 with
44+
detail "Only LCP metrics are supported at this time".
45+
"""
3046
response = client.post(
3147
"/observability/web-vitals",
3248
json={"name": "CLS", "value": 0.04},
@@ -38,6 +54,11 @@ def test_lcp_ingest_rejects_unsupported_metrics() -> None:
3854

3955

4056
def test_http_metrics_recorded_for_requests() -> None:
57+
"""
58+
Verifies that a GET request to /healthcheck increments the Prometheus `http_requests_total` metric for the expected route labels.
59+
60+
Reads the `http_requests_total` sample for service="backend", route="/healthcheck", method="GET", status="200" before and after performing the request and asserts the metric value increases by 1.
61+
"""
4162
route_labels = {
4263
"service": "backend",
4364
"route": "/healthcheck",
@@ -50,4 +71,4 @@ def test_http_metrics_recorded_for_requests() -> None:
5071

5172
assert resp.status_code == 200
5273
after = _get_sample("http_requests_total", route_labels)
53-
assert after == before + 1
74+
assert after == before + 1

‎frontend/src/app/reportWebVitals.ts‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import type { Metric } from 'next/app';
33
const DEFAULT_ENDPOINT = '/observability/web-vitals';
44
const APP_LABEL = process.env.NEXT_PUBLIC_WEB_VITALS_APP ?? 'frontend';
55

6+
/**
7+
* Builds the full URL used to report web-vital metrics.
8+
*
9+
* Uses NEXT_PUBLIC_BACKEND_PROTOCOL, NEXT_PUBLIC_BACKEND_HOST, NEXT_PUBLIC_BACKEND_PORT, and
10+
* NEXT_PUBLIC_WEB_VITALS_ENDPOINT environment variables when present; otherwise falls back to the
11+
* current window.location values and configured defaults. When executed outside the browser
12+
* (no `window`), returns the default endpoint path.
13+
*
14+
* @returns The full endpoint URL string, or the default endpoint path when running server-side.
15+
*/
616
function buildEndpoint(): string {
717
if (typeof window === 'undefined') {
818
return DEFAULT_ENDPOINT;
@@ -17,6 +27,14 @@ function buildEndpoint(): string {
1727
return `${protocol}//${host}${portSegment}${endpoint}`;
1828
}
1929

30+
/**
31+
* Transmits a JSON payload to the given endpoint using a best-effort, fire-and-forget approach.
32+
*
33+
* Attempts to use `navigator.sendBeacon` with a JSON Blob; if that fails or is unavailable, falls back to a POST `fetch` with `keepalive`. Network errors are swallowed.
34+
*
35+
* @param endpoint - Destination URL for the metric
36+
* @param body - JSON-stringified payload to send
37+
*/
2038
function sendMetric(endpoint: string, body: string) {
2139
if (typeof navigator !== 'undefined' && 'sendBeacon' in navigator) {
2240
const blob = new Blob([body], { type: 'application/json' });
@@ -38,6 +56,14 @@ function sendMetric(endpoint: string, body: string) {
3856
});
3957
}
4058

59+
/**
60+
* Sends the page's Largest Contentful Paint (LCP) metric to the configured observability endpoint.
61+
*
62+
* Only metrics with `metric.name === 'LCP'` are transmitted. The outgoing payload is JSON and includes
63+
* the metric's `name`, `value`, `id`, and the application label.
64+
*
65+
* @param metric - The web-vitals `Metric` to evaluate and report (only LCP is reported)
66+
*/
4167
export function reportWebVitals(metric: Metric) {
4268
if (metric.name !== 'LCP') {
4369
return;
@@ -52,4 +78,4 @@ export function reportWebVitals(metric: Metric) {
5278
});
5379

5480
sendMetric(endpoint, body);
55-
}
81+
}

0 commit comments

Comments
 (0)