From 8e0e4da1baff050969f3f9b5f07bea00b9c5eae5 Mon Sep 17 00:00:00 2001 From: Nikhil Sinha Date: Mon, 14 Jul 2025 00:15:47 -0700 Subject: [PATCH 1/2] fix: allow multiple tags in dashboards list_by_tag API --- src/handlers/http/modal/server.rs | 4 ++-- src/handlers/http/users/dashboards.rs | 20 ++++++++++++++------ src/users/dashboards.rs | 10 ++++++---- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/handlers/http/modal/server.rs b/src/handlers/http/modal/server.rs index 615af1d8e..5579be1dd 100644 --- a/src/handlers/http/modal/server.rs +++ b/src/handlers/http/modal/server.rs @@ -308,9 +308,9 @@ impl Server { ), ) .service( - web::resource("/list_by_tag/{tag}").route( + web::resource("/list_by_tag/{tags}").route( web::get() - .to(dashboards::list_dashboards_by_tag) + .to(dashboards::list_dashboards_by_tags) .authorize(Action::ListDashboard), ), ) diff --git a/src/handlers/http/users/dashboards.rs b/src/handlers/http/users/dashboards.rs index c3a774462..3ef25e2df 100644 --- a/src/handlers/http/users/dashboards.rs +++ b/src/handlers/http/users/dashboards.rs @@ -215,13 +215,21 @@ pub async fn list_tags() -> Result { Ok((web::Json(tags), StatusCode::OK)) } -pub async fn list_dashboards_by_tag(tag: Path) -> Result { - let tag = tag.into_inner(); - if tag.is_empty() { - return Err(DashboardError::Metadata("Tag cannot be empty")); +pub async fn list_dashboards_by_tags(tags: Path) -> Result { + let tags = tags.into_inner(); + if tags.is_empty() { + return Err(DashboardError::Metadata("Tags cannot be empty")); } - - let dashboards = DASHBOARDS.list_dashboards_by_tag(&tag).await; + // tags can be comma separated list of tags + let tags = tags + .split(',') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect::>(); + if tags.is_empty() { + return Err(DashboardError::Metadata("Tags cannot be empty")); + } + let dashboards = DASHBOARDS.list_dashboards_by_tags(tags).await; let dashboard_summaries = dashboards .iter() .map(|dashboard| dashboard.to_summary()) diff --git a/src/users/dashboards.rs b/src/users/dashboards.rs index 30a567efe..192f61f7a 100644 --- a/src/users/dashboards.rs +++ b/src/users/dashboards.rs @@ -376,14 +376,16 @@ impl Dashboards { /// List dashboards by tag /// This function returns a list of dashboards that have the specified tag - pub async fn list_dashboards_by_tag(&self, tag: &str) -> Vec { + pub async fn list_dashboards_by_tags(&self, tags: Vec) -> Vec { let dashboards = self.0.read().await; dashboards .iter() .filter(|d| { - d.tags - .as_ref() - .is_some_and(|tags| tags.contains(&tag.to_string())) + if let Some(dashboard_tags) = &d.tags { + !tags.is_empty() && dashboard_tags.iter().any(|tag| tags.contains(tag)) + } else { + false + } }) .cloned() .collect() From e30f9dcb917ae6110f50caac86ec067ffa2ef7a8 Mon Sep 17 00:00:00 2001 From: Nikhil Sinha Date: Mon, 14 Jul 2025 05:33:29 -0700 Subject: [PATCH 2/2] list by tags with dashboard listing api --- src/handlers/http/modal/server.rs | 7 ----- src/handlers/http/users/dashboards.rs | 40 ++++++++++++--------------- src/users/dashboards.rs | 7 +++-- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/src/handlers/http/modal/server.rs b/src/handlers/http/modal/server.rs index 5579be1dd..f9795c705 100644 --- a/src/handlers/http/modal/server.rs +++ b/src/handlers/http/modal/server.rs @@ -307,13 +307,6 @@ impl Server { .authorize(Action::ListDashboard), ), ) - .service( - web::resource("/list_by_tag/{tags}").route( - web::get() - .to(dashboards::list_dashboards_by_tags) - .authorize(Action::ListDashboard), - ), - ) .service( web::scope("/{dashboard_id}") .service( diff --git a/src/handlers/http/users/dashboards.rs b/src/handlers/http/users/dashboards.rs index 3ef25e2df..f0b0f66ef 100644 --- a/src/handlers/http/users/dashboards.rs +++ b/src/handlers/http/users/dashboards.rs @@ -44,6 +44,23 @@ pub async fn list_dashboards(req: HttpRequest) -> Result = tags + .split(',') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(); + if tags.is_empty() { + return Err(DashboardError::Metadata("Tags cannot be empty")); + } + let dashboards = DASHBOARDS.list_dashboards_by_tags(tags).await; + let dashboard_summaries = dashboards + .iter() + .map(|dashboard| dashboard.to_summary()) + .collect::>(); + return Ok((web::Json(dashboard_summaries), StatusCode::OK)); + } } let dashboards = DASHBOARDS.list_dashboards(dashboard_limit).await; let dashboard_summaries = dashboards @@ -215,29 +232,6 @@ pub async fn list_tags() -> Result { Ok((web::Json(tags), StatusCode::OK)) } -pub async fn list_dashboards_by_tags(tags: Path) -> Result { - let tags = tags.into_inner(); - if tags.is_empty() { - return Err(DashboardError::Metadata("Tags cannot be empty")); - } - // tags can be comma separated list of tags - let tags = tags - .split(',') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect::>(); - if tags.is_empty() { - return Err(DashboardError::Metadata("Tags cannot be empty")); - } - let dashboards = DASHBOARDS.list_dashboards_by_tags(tags).await; - let dashboard_summaries = dashboards - .iter() - .map(|dashboard| dashboard.to_summary()) - .collect::>(); - - Ok((web::Json(dashboard_summaries), StatusCode::OK)) -} - #[derive(Debug, thiserror::Error)] pub enum DashboardError { #[error("Failed to connect to storage: {0}")] diff --git a/src/users/dashboards.rs b/src/users/dashboards.rs index 192f61f7a..c67c0e1e7 100644 --- a/src/users/dashboards.rs +++ b/src/users/dashboards.rs @@ -375,14 +375,17 @@ impl Dashboards { } /// List dashboards by tag - /// This function returns a list of dashboards that have the specified tag + /// This function returns a list of dashboards that match any of the provided tags + /// If no tags are provided, it returns an empty list pub async fn list_dashboards_by_tags(&self, tags: Vec) -> Vec { let dashboards = self.0.read().await; dashboards .iter() .filter(|d| { if let Some(dashboard_tags) = &d.tags { - !tags.is_empty() && dashboard_tags.iter().any(|tag| tags.contains(tag)) + dashboard_tags + .iter() + .any(|dashboard_tag| tags.contains(dashboard_tag)) } else { false }