From e3b918b365ec0edaf53b83c12e9749d3cbb03699 Mon Sep 17 00:00:00 2001 From: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:52:09 +0200 Subject: [PATCH] fix(dashboard): Findings/SBOM filter by onboarded targets; accurate target findings_count The Findings and SBOM pages populated their target dropdown from the legacy `repositories` collection, so onboarded targets never appeared and their findings/SBOM couldn't be filtered by name (the data was there, keyed by the target id). Point both dropdowns at `onboarded_targets` via `fetch_targets`. Also refresh `OnboardedTarget.findings_count` at the end of `run_target`: the shared pipeline (Stage 7) increments the legacy `repositories` doc, which the unified path has none of, so the Targets page always showed 0. Set the accurate total (count of findings keyed by the target id) on the target itself. Co-Authored-By: Claude Opus 4.8 --- compliance-agent/src/pipeline/orchestrator.rs | 19 +++++++++++++++++++ compliance-dashboard/src/pages/findings.rs | 10 +++++----- compliance-dashboard/src/pages/sbom.rs | 18 +++++++++--------- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/compliance-agent/src/pipeline/orchestrator.rs b/compliance-agent/src/pipeline/orchestrator.rs index 4da7a3e..d7eed7e 100644 --- a/compliance-agent/src/pipeline/orchestrator.rs +++ b/compliance-agent/src/pipeline/orchestrator.rs @@ -461,6 +461,25 @@ impl PipelineOrchestrator { } }, ) .await?; + // Refresh the target's cached findings count. The shared pipeline + // (Stage 7) increments `repositories`, which the unified path does + // not use, so set the accurate total on the target itself. + let total = self + .db + .findings() + .count_documents(doc! { "repo_id": target_id }) + .await + .unwrap_or(*count as u64); + self.db + .onboarded_targets() + .update_one( + doc! { "_id": oid }, + doc! { "$set": { + "findings_count": total as i64, + "updated_at": mongodb::bson::DateTime::now(), + } }, + ) + .await?; } Err(e) => { tracing::error!(target_id, error = %e, "Unified scan pipeline failed"); diff --git a/compliance-dashboard/src/pages/findings.rs b/compliance-dashboard/src/pages/findings.rs index b7b546b..fda987f 100644 --- a/compliance-dashboard/src/pages/findings.rs +++ b/compliance-dashboard/src/pages/findings.rs @@ -20,7 +20,7 @@ pub fn FindingsPage() -> Element { let mut selected_ids = use_signal(Vec::::new); let repos = use_resource(|| async { - crate::infrastructure::repositories::fetch_repositories(1) + crate::infrastructure::onboarding::fetch_targets() .await .ok() }); @@ -86,14 +86,14 @@ pub fn FindingsPage() -> Element { } select { onchange: move |e| { repo_filter.set(e.value()); page.set(1); }, - option { value: "", "All Repositories" } + option { value: "", "All Targets" } { match &*repos.read() { Some(Some(resp)) => rsx! { - for repo in &resp.data { + for t in &resp.data { { - let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); - let name = repo.name.clone(); + let id = t.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string(); + let name = t.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string(); rsx! { option { value: "{id}", "{name}" } } diff --git a/compliance-dashboard/src/pages/sbom.rs b/compliance-dashboard/src/pages/sbom.rs index 14c1c46..bd1f6f7 100644 --- a/compliance-dashboard/src/pages/sbom.rs +++ b/compliance-dashboard/src/pages/sbom.rs @@ -28,9 +28,9 @@ pub fn SbomPage() -> Element { let mut diff_repo_a = use_signal(String::new); let mut diff_repo_b = use_signal(String::new); - // ── Repos for dropdowns ── + // ── Targets for dropdowns ── let repos = use_resource(|| async { - crate::infrastructure::repositories::fetch_repositories(1) + crate::infrastructure::onboarding::fetch_targets() .await .ok() }); @@ -114,14 +114,14 @@ pub fn SbomPage() -> Element { select { class: "sbom-filter-select", onchange: move |e| { repo_filter.set(e.value()); page.set(1); }, - option { value: "", "All Repositories" } + option { value: "", "All Targets" } { match &*repos.read() { Some(Some(resp)) => rsx! { for repo in &resp.data { { - let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); - let name = repo.name.clone(); + let id = repo.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string(); + let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string(); rsx! { option { value: "{id}", "{name}" } } } } @@ -476,8 +476,8 @@ pub fn SbomPage() -> Element { Some(Some(resp)) => rsx! { for repo in &resp.data { { - let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); - let name = repo.name.clone(); + let id = repo.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string(); + let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string(); rsx! { option { value: "{id}", "{name}" } } } } @@ -498,8 +498,8 @@ pub fn SbomPage() -> Element { Some(Some(resp)) => rsx! { for repo in &resp.data { { - let id = repo.id.as_ref().map(|id| id.to_hex()).unwrap_or_default(); - let name = repo.name.clone(); + let id = repo.get("_id").and_then(|o| o.get("$oid")).and_then(|s| s.as_str()).unwrap_or_default().to_string(); + let name = repo.get("name").and_then(|n| n.as_str()).unwrap_or_default().to_string(); rsx! { option { value: "{id}", "{name}" } } } } -- 2.54.0