From 8e20b31375fae8a28fa1069864c4fd5cd26873ba Mon Sep 17 00:00:00 2001 From: Nikhil Sinha <131262146+nikhilsinhaparseable@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:27:36 +0530 Subject: [PATCH] Dashboard graph configs (#938) enhancement: graph config for tiles in dashboards add fields - graph_type: enum - Default (default), Stacked, Percent orientation: enum - Horizontal (default), Vertical - migration from version v2 to v3 - fixed migration from v1 to v3 and null check --- server/src/handlers/http/users/dashboards.rs | 24 ++-- server/src/handlers/http/users/filters.rs | 20 ++-- server/src/users/dashboards.rs | 118 +++++++++++++++++-- server/src/users/filters.rs | 24 +++- 4 files changed, 152 insertions(+), 34 deletions(-) diff --git a/server/src/handlers/http/users/dashboards.rs b/server/src/handlers/http/users/dashboards.rs index 65a020c97..81945914e 100644 --- a/server/src/handlers/http/users/dashboards.rs +++ b/server/src/handlers/http/users/dashboards.rs @@ -52,12 +52,14 @@ pub async fn get(req: HttpRequest) -> Result { } pub async fn post(req: HttpRequest, body: Bytes) -> Result { - let user_id = get_user_from_request(&req)?; + let mut user_id = get_user_from_request(&req)?; + user_id = get_hash(&user_id); let mut dashboard: Dashboard = serde_json::from_slice(&body)?; let dashboard_id = get_hash(Utc::now().timestamp_micros().to_string().as_str()); dashboard.dashboard_id = Some(dashboard_id.clone()); dashboard.version = Some(CURRENT_DASHBOARD_VERSION.to_string()); - dashboard.user_id = Some(get_hash(&user_id)); + + dashboard.user_id = Some(user_id.clone()); for tile in dashboard.tiles.iter_mut() { tile.tile_id = Some(get_hash(Utc::now().timestamp_micros().to_string().as_str())); } @@ -75,19 +77,19 @@ pub async fn post(req: HttpRequest, body: Bytes) -> Result Result { - let user_id = get_user_from_request(&req)?; + let mut user_id = get_user_from_request(&req)?; + user_id = get_hash(&user_id); let dashboard_id = req .match_info() .get("dashboard_id") .ok_or(DashboardError::Metadata("No Dashboard Id Provided"))?; - if DASHBOARDS - .get_dashboard(dashboard_id, &get_hash(&user_id)) - .is_none() - { + + if DASHBOARDS.get_dashboard(dashboard_id, &user_id).is_none() { return Err(DashboardError::Metadata("Dashboard does not exist")); } let mut dashboard: Dashboard = serde_json::from_slice(&body)?; dashboard.dashboard_id = Some(dashboard_id.to_string()); + dashboard.user_id = Some(user_id.clone()); dashboard.version = Some(CURRENT_DASHBOARD_VERSION.to_string()); for tile in dashboard.tiles.iter_mut() { if tile.tile_id.is_none() { @@ -108,15 +110,13 @@ pub async fn update(req: HttpRequest, body: Bytes) -> Result Result { - let user_id = get_user_from_request(&req)?; + let mut user_id = get_user_from_request(&req)?; + user_id = get_hash(&user_id); let dashboard_id = req .match_info() .get("dashboard_id") .ok_or(DashboardError::Metadata("No Dashboard Id Provided"))?; - if DASHBOARDS - .get_dashboard(dashboard_id, &get_hash(&user_id)) - .is_none() - { + if DASHBOARDS.get_dashboard(dashboard_id, &user_id).is_none() { return Err(DashboardError::Metadata("Dashboard does not exist")); } let path = dashboard_path(&user_id, &format!("{}.json", dashboard_id)); diff --git a/server/src/handlers/http/users/filters.rs b/server/src/handlers/http/users/filters.rs index 5a924ea92..e8f00c901 100644 --- a/server/src/handlers/http/users/filters.rs +++ b/server/src/handlers/http/users/filters.rs @@ -50,11 +50,12 @@ pub async fn get(req: HttpRequest) -> Result { } pub async fn post(req: HttpRequest, body: Bytes) -> Result { - let user_id = get_user_from_request(&req)?; + let mut user_id = get_user_from_request(&req)?; + user_id = get_hash(&user_id); let mut filter: Filter = serde_json::from_slice(&body)?; let filter_id = get_hash(Utc::now().timestamp_micros().to_string().as_str()); filter.filter_id = Some(filter_id.clone()); - filter.user_id = Some(get_hash(&user_id)); + filter.user_id = Some(user_id.clone()); filter.version = Some(CURRENT_FILTER_VERSION.to_string()); FILTERS.update(&filter); @@ -71,17 +72,19 @@ pub async fn post(req: HttpRequest, body: Bytes) -> Result Result { - let user_id = get_user_from_request(&req)?; +pub async fn update(req: HttpRequest, body: Bytes) -> Result { + let mut user_id = get_user_from_request(&req)?; + user_id = get_hash(&user_id); let filter_id = req .match_info() .get("filter_id") .ok_or(FiltersError::Metadata("No Filter Id Provided"))?; - if FILTERS.get_filter(filter_id, &get_hash(&user_id)).is_none() { + if FILTERS.get_filter(filter_id, &user_id).is_none() { return Err(FiltersError::Metadata("Filter does not exist")); } let mut filter: Filter = serde_json::from_slice(&body)?; filter.filter_id = Some(filter_id.to_string()); + filter.user_id = Some(user_id.clone()); filter.version = Some(CURRENT_FILTER_VERSION.to_string()); FILTERS.update(&filter); @@ -95,17 +98,18 @@ pub async fn update(req: HttpRequest, body: Bytes) -> Result Result { - let user_id = get_user_from_request(&req)?; + let mut user_id = get_user_from_request(&req)?; + user_id = get_hash(&user_id); let filter_id = req .match_info() .get("filter_id") .ok_or(FiltersError::Metadata("No Filter Id Provided"))?; let filter = FILTERS - .get_filter(filter_id, &get_hash(&user_id)) + .get_filter(filter_id, &user_id) .ok_or(FiltersError::Metadata("Filter does not exist"))?; let path = filter_path( diff --git a/server/src/users/dashboards.rs b/server/src/users/dashboards.rs index fa9434614..72673031e 100644 --- a/server/src/users/dashboards.rs +++ b/server/src/users/dashboards.rs @@ -30,7 +30,7 @@ use crate::{ use super::TimeFilter; pub static DASHBOARDS: Lazy = Lazy::new(Dashboards::default); -pub const CURRENT_DASHBOARD_VERSION: &str = "v2"; +pub const CURRENT_DASHBOARD_VERSION: &str = "v3"; #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct Tiles { @@ -62,6 +62,25 @@ pub struct CircularChartConfig { pub struct GraphConfig { x_key: String, y_keys: Vec, + graph_type: Option, + orientation: Option, +} + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +#[serde(rename_all = "lowercase")] +pub enum GraphType { + #[default] + Default, + Stacked, + Percent, +} + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +#[serde(rename_all = "lowercase")] +pub enum Orientation { + #[default] + Horizontal, + Vertical, } #[derive(Debug, Serialize, Deserialize, Default, Clone)] @@ -96,7 +115,6 @@ impl Dashboards { let mut this = vec![]; let store = CONFIG.storage().get_object_store(); let dashboards = store.get_all_dashboards().await.unwrap_or_default(); - for dashboard in dashboards { if dashboard.is_empty() { continue; @@ -104,24 +122,55 @@ impl Dashboards { let mut dashboard_value = serde_json::from_slice::(&dashboard)?; if let Some(meta) = dashboard_value.clone().as_object() { let version = meta.get("version").and_then(|version| version.as_str()); - let user_id = meta.get("user_id").and_then(|user_id| user_id.as_str()); let dashboard_id = meta .get("dashboard_id") .and_then(|dashboard_id| dashboard_id.as_str()); - - if version == Some("v1") { - dashboard_value = migrate_v1_v2(dashboard_value); - if let (Some(user_id), Some(dashboard_id)) = (user_id, dashboard_id) { - let path = dashboard_path(user_id, &format!("{}.json", dashboard_id)); + match version { + Some("v1") => { + dashboard_value = migrate_v1_v2(dashboard_value); + dashboard_value = migrate_v2_v3(dashboard_value); + let user_id = dashboard_value + .as_object() + .unwrap() + .get("user_id") + .and_then(|user_id| user_id.as_str()); + let path = dashboard_path( + user_id.unwrap(), + &format!("{}.json", dashboard_id.unwrap()), + ); let dashboard_bytes = to_bytes(&dashboard_value); store.put_object(&path, dashboard_bytes.clone()).await?; if let Ok(dashboard) = serde_json::from_slice::(&dashboard_bytes) { + this.retain(|d: &Dashboard| d.dashboard_id != dashboard.dashboard_id); + this.push(dashboard); + } + } + Some("v2") => { + dashboard_value = migrate_v2_v3(dashboard_value); + let user_id = dashboard_value + .as_object() + .unwrap() + .get("user_id") + .and_then(|user_id| user_id.as_str()); + let path = dashboard_path( + user_id.unwrap(), + &format!("{}.json", dashboard_id.unwrap()), + ); + let dashboard_bytes = to_bytes(&dashboard_value); + store.put_object(&path, dashboard_bytes.clone()).await?; + if let Ok(dashboard) = serde_json::from_slice::(&dashboard_bytes) + { + this.retain(|d| d.dashboard_id != dashboard.dashboard_id); + this.push(dashboard); + } + } + _ => { + if let Ok(dashboard) = serde_json::from_slice::(&dashboard) { + this.retain(|d| d.dashboard_id != dashboard.dashboard_id); this.push(dashboard); } } - } else if let Ok(dashboard) = serde_json::from_slice::(&dashboard) { - this.push(dashboard); } } } @@ -176,6 +225,55 @@ fn migrate_v1_v2(mut dashboard_meta: Value) -> Value { "version".to_owned(), Value::String(CURRENT_DASHBOARD_VERSION.into()), ); + let tiles = dashboard_meta_map + .get_mut("tiles") + .unwrap() + .as_array_mut() + .unwrap(); + for tile in tiles.iter_mut() { + let tile_map = tile.as_object_mut().unwrap(); + let visualization = tile_map + .get_mut("visualization") + .unwrap() + .as_object_mut() + .unwrap(); + visualization.insert("tick_config".to_owned(), Value::Array(vec![])); + } + + dashboard_meta +} + +fn migrate_v2_v3(mut dashboard_meta: Value) -> Value { + let dashboard_meta_map = dashboard_meta.as_object_mut().unwrap(); + + dashboard_meta_map.insert( + "version".to_owned(), + Value::String(CURRENT_DASHBOARD_VERSION.into()), + ); + let tiles = dashboard_meta_map + .get_mut("tiles") + .unwrap() + .as_array_mut() + .unwrap(); + for tile in tiles { + let tile_map = tile.as_object_mut().unwrap(); + let visualization = tile_map + .get_mut("visualization") + .unwrap() + .as_object_mut() + .unwrap(); + if visualization.get("graph_config").is_some() + && !visualization.get("graph_config").unwrap().is_null() + { + let graph_config = visualization + .get_mut("graph_config") + .unwrap() + .as_object_mut() + .unwrap(); + graph_config.insert("orientation".to_owned(), Value::String("horizontal".into())); + graph_config.insert("graph_type".to_owned(), Value::String("default".into())); + } + } dashboard_meta } diff --git a/server/src/users/filters.rs b/server/src/users/filters.rs index 45d2421dc..016a76c8f 100644 --- a/server/src/users/filters.rs +++ b/server/src/users/filters.rs @@ -99,16 +99,32 @@ impl Filters { if let (Some(user_id), Some(stream_name), Some(filter_id)) = (user_id, stream_name, filter_id) { - let path = - filter_path(user_id, stream_name, &format!("{}.json", filter_id)); + let path = filter_path( + &get_hash(user_id), + stream_name, + &format!("{}.json", filter_id), + ); let filter_bytes = to_bytes(&filter_value); store.put_object(&path, filter_bytes.clone()).await?; if let Ok(filter) = serde_json::from_slice::(&filter_bytes) { + this.retain(|f: &Filter| f.filter_id != filter.filter_id); this.push(filter); } } - } else if let Ok(filter) = serde_json::from_slice::(&filter) { - this.push(filter); + } else if let (Some(user_id), Some(stream_name), Some(filter_id)) = + (user_id, stream_name, filter_id) + { + let path = filter_path( + &get_hash(user_id), + stream_name, + &format!("{}.json", filter_id), + ); + let filter_bytes = to_bytes(&filter_value); + store.put_object(&path, filter_bytes.clone()).await?; + if let Ok(filter) = serde_json::from_slice::(&filter) { + this.retain(|f: &Filter| f.filter_id != filter.filter_id); + this.push(filter); + } } } }