From bc2fdecf5668e1292791889cb67694b507b64401 Mon Sep 17 00:00:00 2001 From: Devdutt Shenoi Date: Fri, 7 Feb 2025 23:05:22 +0530 Subject: [PATCH] refactor: dashboards * generate ids if not provided * set default value * improve error response(404) * improve method signature --- src/handlers/http/users/dashboards.rs | 59 +++++++++------------------ src/users/dashboards.rs | 39 ++++++++++++++---- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/handlers/http/users/dashboards.rs b/src/handlers/http/users/dashboards.rs index f95de4559..bc2e5677d 100644 --- a/src/handlers/http/users/dashboards.rs +++ b/src/handlers/http/users/dashboards.rs @@ -16,25 +16,23 @@ * */ -use crate::{ - handlers::http::rbac::RBACError, - option::CONFIG, - storage::{object_storage::dashboard_path, ObjectStorageError}, - users::dashboards::{Dashboard, CURRENT_DASHBOARD_VERSION, DASHBOARDS}, - utils::{get_hash, get_user_from_request}, -}; use actix_web::{ http::header::ContentType, web::{self, Json, Path}, HttpRequest, HttpResponse, Responder, }; use bytes::Bytes; -use rand::distributions::DistString; - -use chrono::Utc; use http::StatusCode; use serde_json::Error as SerdeError; +use crate::{ + handlers::http::rbac::RBACError, + option::CONFIG, + storage::{object_storage::dashboard_path, ObjectStorageError}, + users::dashboards::{Dashboard, DASHBOARDS}, + utils::{get_hash, get_user_from_request}, +}; + pub async fn list(req: HttpRequest) -> Result { let user_id = get_user_from_request(&req)?; let dashboards = DASHBOARDS.list_dashboards_by_user(&get_hash(&user_id)); @@ -49,11 +47,11 @@ pub async fn get( let user_id = get_user_from_request(&req)?; let dashboard_id = dashboard_id.into_inner(); - if let Some(dashboard) = DASHBOARDS.get_dashboard(&dashboard_id, &get_hash(&user_id)) { + if let Some(dashboard) = DASHBOARDS.get(&dashboard_id, &get_hash(&user_id)) { return Ok((web::Json(dashboard), StatusCode::OK)); } - Err(DashboardError::Metadata("Dashboard does not exist")) + Err(DashboardError::DashboardDoesNotExist) } pub async fn post( @@ -62,24 +60,10 @@ pub async fn post( ) -> Result { let mut user_id = get_user_from_request(&req)?; user_id = get_hash(&user_id); - 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(user_id.clone()); - for tile in dashboard.tiles.iter_mut() { - tile.tile_id = Some(get_hash( - format!( - "{}{}", - rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 8), - Utc::now().timestamp_micros() - ) - .as_str(), - )); - } DASHBOARDS.update(&dashboard); - let path = dashboard_path(&user_id, &format!("{}.json", dashboard_id)); + let path = dashboard_path(&user_id, &format!("{}.json", dashboard.dashboard_id)); let store = CONFIG.storage().get_object_store(); let dashboard_bytes = serde_json::to_vec(&dashboard)?; @@ -99,17 +83,11 @@ pub async fn update( user_id = get_hash(&user_id); let dashboard_id = dashboard_id.into_inner(); - if DASHBOARDS.get_dashboard(&dashboard_id, &user_id).is_none() { - return Err(DashboardError::Metadata("Dashboard does not exist")); + if DASHBOARDS.get(&dashboard_id, &user_id).is_none() { + return Err(DashboardError::DashboardDoesNotExist); } - dashboard.dashboard_id = Some(dashboard_id.to_string()); + dashboard.dashboard_id = 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() { - tile.tile_id = Some(get_hash(Utc::now().timestamp_micros().to_string().as_str())); - } - } DASHBOARDS.update(&dashboard); let path = dashboard_path(&user_id, &format!("{}.json", dashboard_id)); @@ -130,14 +108,14 @@ pub async fn delete( let mut user_id = get_user_from_request(&req)?; user_id = get_hash(&user_id); let dashboard_id = dashboard_id.into_inner(); - if DASHBOARDS.get_dashboard(&dashboard_id, &user_id).is_none() { - return Err(DashboardError::Metadata("Dashboard does not exist")); + if DASHBOARDS.get(&dashboard_id, &user_id).is_none() { + return Err(DashboardError::DashboardDoesNotExist); } let path = dashboard_path(&user_id, &format!("{}.json", dashboard_id)); let store = CONFIG.storage().get_object_store(); store.delete_object(&path).await?; - DASHBOARDS.delete_dashboard(&dashboard_id); + DASHBOARDS.delete(&dashboard_id); Ok(HttpResponse::Ok().finish()) } @@ -150,6 +128,8 @@ pub enum DashboardError { Serde(#[from] SerdeError), #[error("Cannot perform this operation: {0}")] Metadata(&'static str), + #[error("Dashboard does not exist")] + DashboardDoesNotExist, #[error("User does not exist")] UserDoesNotExist(#[from] RBACError), } @@ -160,6 +140,7 @@ impl actix_web::ResponseError for DashboardError { Self::ObjectStorage(_) => StatusCode::INTERNAL_SERVER_ERROR, Self::Serde(_) => StatusCode::BAD_REQUEST, Self::Metadata(_) => StatusCode::BAD_REQUEST, + Self::DashboardDoesNotExist => StatusCode::NOT_FOUND, Self::UserDoesNotExist(_) => StatusCode::NOT_FOUND, } } diff --git a/src/users/dashboards.rs b/src/users/dashboards.rs index 46b3bbcf2..1740a1baa 100644 --- a/src/users/dashboards.rs +++ b/src/users/dashboards.rs @@ -18,7 +18,9 @@ use std::sync::RwLock; +use chrono::Utc; use once_cell::sync::Lazy; +use rand::distributions::DistString; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -32,10 +34,22 @@ use super::TimeFilter; pub static DASHBOARDS: Lazy = Lazy::new(Dashboards::default); pub const CURRENT_DASHBOARD_VERSION: &str = "v3"; +fn gen_tile_id() -> String { + get_hash( + format!( + "{}{}", + rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 8), + Utc::now().timestamp_micros() + ) + .as_str(), + ) +} + #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct Tiles { name: String, - pub tile_id: Option, + #[serde(default = "gen_tile_id")] + pub tile_id: String, description: String, query: String, order: Option, @@ -95,12 +109,22 @@ pub struct TickConfig { unit: String, } +fn default_version() -> String { + CURRENT_DASHBOARD_VERSION.to_owned() +} + +fn gen_dashboard_id() -> String { + get_hash(Utc::now().timestamp_micros().to_string().as_str()) +} + #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct Dashboard { - pub version: Option, + #[serde(default = "default_version")] + pub version: String, name: String, description: String, - pub dashboard_id: Option, + #[serde(default = "gen_dashboard_id")] + pub dashboard_id: String, pub user_id: Option, pub time_filter: Option, refresh_interval: u64, @@ -184,19 +208,18 @@ impl Dashboards { s.push(dashboard.clone()); } - pub fn delete_dashboard(&self, dashboard_id: &str) { + pub fn delete(&self, dashboard_id: &str) { let mut s = self.0.write().expect(LOCK_EXPECT); - s.retain(|d| d.dashboard_id != Some(dashboard_id.to_string())); + s.retain(|d| d.dashboard_id != dashboard_id); } - pub fn get_dashboard(&self, dashboard_id: &str, user_id: &str) -> Option { + pub fn get(&self, dashboard_id: &str, user_id: &str) -> Option { self.0 .read() .expect(LOCK_EXPECT) .iter() .find(|d| { - d.dashboard_id == Some(dashboard_id.to_string()) - && d.user_id == Some(user_id.to_string()) + d.dashboard_id == dashboard_id && d.user_id.as_ref().is_some_and(|id| id == user_id) }) .cloned() }