From 9f54a4304324fbdab7266675d9eb6c2350997757 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Lapersonne Date: Mon, 4 May 2026 18:09:26 +0200 Subject: [PATCH] refactor: improve errors from API management (stephanebouget/github-security-alerts/#32) Errors management and display can be improved in both CLI and GUI. Errors returned by GitHub API were opaque: no HTTP code, no body. Thus some errors were hidden, even if the origin was the API results. RepoAlerts has been updated to bring the error message to the frontend. HTTP errors management has been reviews with better JSON processing with serde. Add in the GUI a new badge for error cases (red). Closes stephanebouget/github-security-alerts#32 Assisted-by: Claude Sonnet 4.6 (OpenCode, LLMProxy) Signed-off-by: Pierre-Yves Lapersonne --- src-tauri/src/alerts.rs | 72 ++++++++++++++----- src-tauri/src/models.rs | 1 + src/app/core/services/tauri/tauri.service.ts | 1 + .../alerts-list/alerts-list.component.html | 12 ++-- .../alerts-list/alerts-list.component.scss | 6 ++ 5 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src-tauri/src/alerts.rs b/src-tauri/src/alerts.rs index c7ca5b6..12eadc0 100644 --- a/src-tauri/src/alerts.rs +++ b/src-tauri/src/alerts.rs @@ -54,45 +54,81 @@ pub async fn get_github_security_alerts(app: tauri::AppHandle) -> Result { - if response.status() == 422 { - // 422 Unprocessable Entity usually means Dependabot is not enabled - eprintln!("Dependabot not enabled for {}", repo); + let status = response.status(); + + if status == 422 { + // 422 Unprocessable Entity: Dependabot not enabled on this repo + eprintln!("[{}] Dependabot not enabled (HTTP 422)", repo); repo_alerts.push(RepoAlerts { name: repo, alerts: 0, dependabot_enabled: false, + error: None, + }); + } else if !status.is_success() { + // Any other non-2xx: capture the raw body for a useful error message + let body = response.text().await.unwrap_or_default(); + let msg = format!("HTTP {} — {}", status.as_u16(), body); + eprintln!("[{}] GitHub API error: {}", repo, msg); + repo_alerts.push(RepoAlerts { + name: repo, + alerts: 0, + dependabot_enabled: false, + error: Some(msg), }); } else { - match response.json::>().await { - Ok(alerts) => { - let open_alerts = alerts.iter() - .filter(|a| a.state == "open") - .count(); - total_alerts += open_alerts; - repo_alerts.push(RepoAlerts { - name: repo, - alerts: open_alerts, - dependabot_enabled: true, - }); + // 2xx: try to parse the JSON body + // Read raw bytes first so we can log them if parsing fails + match response.bytes().await { + Ok(bytes) => { + match serde_json::from_slice::>(&bytes) { + Ok(alerts) => { + let open_alerts = alerts.iter() + .filter(|a| a.state == "open") + .count(); + total_alerts += open_alerts; + repo_alerts.push(RepoAlerts { + name: repo, + alerts: open_alerts, + dependabot_enabled: true, + error: None, + }); + } + Err(e) => { + // Log the raw body so we can see what GitHub actually returned + let raw = String::from_utf8_lossy(&bytes); + let msg = format!("JSON parse error: {} — body: {}", e, raw); + eprintln!("[{}] {}", repo, msg); + repo_alerts.push(RepoAlerts { + name: repo, + alerts: 0, + dependabot_enabled: false, + error: Some(format!("JSON parse error: {}", e)), + }); + } + } } Err(e) => { - eprintln!("Failed to parse alerts for {}: {}", repo, e); - // If parsing fails, assume Dependabot is not enabled + let msg = format!("Error reading response body: {}", e); + eprintln!("[{}] {}", repo, msg); repo_alerts.push(RepoAlerts { name: repo, alerts: 0, dependabot_enabled: false, + error: Some(msg), }); } } } } Err(e) => { - eprintln!("Failed to fetch alerts for {}: {}", repo, e); + let msg = format!("Network error: {}", e); + eprintln!("[{}] {}", repo, msg); repo_alerts.push(RepoAlerts { name: repo, alerts: 0, dependabot_enabled: false, + error: Some(msg), }); } } @@ -102,4 +138,4 @@ pub async fn get_github_security_alerts(app: tauri::AppHandle) -> Result, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/app/core/services/tauri/tauri.service.ts b/src/app/core/services/tauri/tauri.service.ts index b60d23a..fd8f699 100644 --- a/src/app/core/services/tauri/tauri.service.ts +++ b/src/app/core/services/tauri/tauri.service.ts @@ -16,6 +16,7 @@ export interface RepoAlerts { name: string; alerts: number; dependabot_enabled: boolean; + error?: string; } export interface AlertsResponse { diff --git a/src/app/shared/components/alerts-list/alerts-list.component.html b/src/app/shared/components/alerts-list/alerts-list.component.html index 8f3070b..7190390 100644 --- a/src/app/shared/components/alerts-list/alerts-list.component.html +++ b/src/app/shared/components/alerts-list/alerts-list.component.html @@ -60,13 +60,15 @@
- - + + + {{ repo.alerts }}
diff --git a/src/app/shared/components/alerts-list/alerts-list.component.scss b/src/app/shared/components/alerts-list/alerts-list.component.scss index d49bd71..c32e0d5 100644 --- a/src/app/shared/components/alerts-list/alerts-list.component.scss +++ b/src/app/shared/components/alerts-list/alerts-list.component.scss @@ -179,6 +179,12 @@ color: colors.$text-secondary; cursor: help; } + + &.error { + background: rgba(colors.$danger-color, 0.1); + color: colors.$danger-color; + cursor: help; + } } }