Skip to content

Commit 94a5495

Browse files
refactor and add comments
1 parent cb9d135 commit 94a5495

File tree

1 file changed

+70
-18
lines changed

1 file changed

+70
-18
lines changed

src/users/dashboards.rs

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,21 @@ pub static DASHBOARDS: Lazy<Dashboards> = Lazy::new(Dashboards::default);
3333
pub const CURRENT_DASHBOARD_VERSION: &str = "v1";
3434

3535
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
36+
/// type of dashboard
37+
/// Dashboard is the default type
38+
/// Report is a type of dashboard that is used for reporting
3639
pub enum DashboardType {
40+
/// Dashboard is the default type
3741
#[default]
3842
Dashboard,
43+
/// Report is a type of dashboard that is used for reporting
3944
Report,
4045
}
4146

4247
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
4348
pub struct Tile {
4449
pub tile_id: Ulid,
50+
/// all other fields are variable and can be added as needed
4551
#[serde(flatten)]
4652
pub other_fields: Option<serde_json::Map<String, Value>>,
4753
}
@@ -57,14 +63,22 @@ pub struct Dashboard {
5763
}
5864

5965
impl Dashboard {
66+
/// set metadata for the dashboard
67+
/// add author, dashboard_id, version, modified, and dashboard_type
68+
/// if dashboard_id is None, generate a new one
6069
pub fn set_metadata(&mut self, user_id: &str, dashboard_id: Option<Ulid>) {
6170
self.author = Some(user_id.to_string());
6271
self.dashboard_id = dashboard_id.or_else(|| Some(Ulid::new()));
6372
self.version = Some(CURRENT_DASHBOARD_VERSION.to_string());
6473
self.modified = Some(Utc::now());
6574
self.dashboard_type = Some(DashboardType::Dashboard);
75+
if self.tiles.is_none() {
76+
self.tiles = Some(Vec::new());
77+
}
6678
}
6779

80+
/// create a summary of the dashboard
81+
/// used for listing dashboards
6882
pub fn to_summary(&self) -> serde_json::Map<String, serde_json::Value> {
6983
let mut map = serde_json::Map::new();
7084

@@ -98,15 +112,21 @@ impl Dashboard {
98112
}
99113
}
100114

115+
/// Validate the dashboard ID
116+
/// Check if the dashboard ID is a valid ULID
117+
/// If the dashboard ID is not valid, return an error
101118
pub fn validate_dashboard_id(dashboard_id: String) -> Result<Ulid, DashboardError> {
102119
Ulid::from_string(&dashboard_id)
103-
.map_err(|_| DashboardError::Metadata("Dashboard ID must be provided"))
120+
.map_err(|_| DashboardError::Metadata("Invalid dashboard ID format - must be a valid ULID"))
104121
}
105122

106123
#[derive(Default, Debug)]
107124
pub struct Dashboards(RwLock<Vec<Dashboard>>);
108125

109126
impl Dashboards {
127+
/// Load all dashboards from the object store
128+
/// and store them in memory
129+
/// This function is called on server start
110130
pub async fn load(&self) -> anyhow::Result<()> {
111131
let mut this = vec![];
112132
let store = PARSEABLE.storage.get_object_store();
@@ -118,10 +138,21 @@ impl Dashboards {
118138
continue;
119139
}
120140

121-
let dashboard_value = serde_json::from_slice::<serde_json::Value>(&dashboard)?;
122-
if let Ok(dashboard) = serde_json::from_value::<Dashboard>(dashboard_value) {
141+
let dashboard_value = match serde_json::from_slice::<serde_json::Value>(&dashboard)
142+
{
143+
Ok(value) => value,
144+
Err(err) => {
145+
tracing::warn!("Failed to parse dashboard JSON: {}", err);
146+
continue;
147+
}
148+
};
149+
150+
if let Ok(dashboard) = serde_json::from_value::<Dashboard>(dashboard_value.clone())
151+
{
123152
this.retain(|d: &Dashboard| d.dashboard_id != dashboard.dashboard_id);
124153
this.push(dashboard);
154+
} else {
155+
tracing::warn!("Failed to deserialize dashboard: {:?}", dashboard_value);
125156
}
126157
}
127158
}
@@ -132,6 +163,8 @@ impl Dashboards {
132163
Ok(())
133164
}
134165

166+
/// Save the dashboard to the object store
167+
/// This function is called when creating or updating a dashboard
135168
async fn save_dashboard(
136169
&self,
137170
user_id: &str,
@@ -151,6 +184,9 @@ impl Dashboards {
151184
Ok(())
152185
}
153186

187+
/// Create a new dashboard
188+
/// This function is called when creating a new dashboard
189+
/// add dashboard in memory and save it to the object store
154190
pub async fn create(
155191
&self,
156192
user_id: &str,
@@ -164,19 +200,17 @@ impl Dashboards {
164200
Ok(())
165201
}
166202

203+
/// Update an existing dashboard
204+
/// This function is called when updating a dashboard
205+
/// update dashboard in memory and save it to the object store
167206
pub async fn update(
168207
&self,
169208
user_id: &str,
170209
dashboard_id: Ulid,
171210
dashboard: &mut Dashboard,
172211
) -> Result<(), DashboardError> {
173-
if self
174-
.get_dashboard_by_user(dashboard_id, user_id)
175-
.await
176-
.is_none()
177-
{
178-
return Err(DashboardError::Unauthorized);
179-
}
212+
self.ensure_dashboard_ownership(dashboard_id, user_id)
213+
.await?;
180214

181215
dashboard.set_metadata(user_id, Some(dashboard_id));
182216
self.save_dashboard(user_id, dashboard).await?;
@@ -188,18 +222,16 @@ impl Dashboards {
188222
Ok(())
189223
}
190224

225+
/// Delete a dashboard
226+
/// This function is called when deleting a dashboard
227+
/// delete dashboard in memory and from the object store
191228
pub async fn delete_dashboard(
192229
&self,
193230
user_id: &str,
194231
dashboard_id: Ulid,
195232
) -> Result<(), DashboardError> {
196-
if self
197-
.get_dashboard_by_user(dashboard_id, user_id)
198-
.await
199-
.is_none()
200-
{
201-
return Err(DashboardError::Unauthorized);
202-
}
233+
self.ensure_dashboard_ownership(dashboard_id, user_id)
234+
.await?;
203235

204236
let path = dashboard_path(user_id, &format!("{}.json", dashboard_id));
205237
let store = PARSEABLE.storage.get_object_store();
@@ -208,12 +240,14 @@ impl Dashboards {
208240
self.0.write().await.retain(|d| {
209241
d.dashboard_id
210242
.as_ref()
211-
.is_some_and(|id| *id == dashboard_id)
243+
.map_or(true, |id| *id != dashboard_id)
212244
});
213245

214246
Ok(())
215247
}
216248

249+
/// Get a dashboard by ID
250+
/// fetch dashboard from memory
217251
pub async fn get_dashboard(&self, dashboard_id: Ulid) -> Option<Dashboard> {
218252
self.0
219253
.read()
@@ -227,6 +261,8 @@ impl Dashboards {
227261
.cloned()
228262
}
229263

264+
/// Get a dashboard by ID and user ID
265+
/// fetch dashboard from memory
230266
pub async fn get_dashboard_by_user(
231267
&self,
232268
dashboard_id: Ulid,
@@ -245,7 +281,23 @@ impl Dashboards {
245281
.cloned()
246282
}
247283

284+
/// List all dashboards
285+
/// fetch all dashboards from memory
248286
pub async fn list_dashboards(&self) -> Vec<Dashboard> {
249287
self.0.read().await.clone()
250288
}
289+
290+
/// Ensure the user is the owner of the dashboard
291+
/// This function is called when updating or deleting a dashboard
292+
/// check if the user is the owner of the dashboard
293+
/// if the user is not the owner, return an error
294+
async fn ensure_dashboard_ownership(
295+
&self,
296+
dashboard_id: Ulid,
297+
user_id: &str,
298+
) -> Result<Dashboard, DashboardError> {
299+
self.get_dashboard_by_user(dashboard_id, user_id)
300+
.await
301+
.ok_or(DashboardError::Unauthorized)
302+
}
251303
}

0 commit comments

Comments
 (0)