@@ -52,12 +52,16 @@ pub struct Tile {
52
52
pub other_fields : Option < serde_json:: Map < String , Value > > ,
53
53
}
54
54
#[ derive( Debug , Serialize , Deserialize , Default , Clone ) ]
55
+ #[ serde( rename_all = "camelCase" ) ]
55
56
pub struct Dashboard {
56
57
pub version : Option < String > ,
57
58
pub title : String ,
58
59
pub author : Option < String > ,
59
60
pub dashboard_id : Option < Ulid > ,
61
+ pub created : Option < DateTime < Utc > > ,
60
62
pub modified : Option < DateTime < Utc > > ,
63
+ pub tags : Option < Vec < String > > ,
64
+ pub is_favorite : Option < bool > , // whether the dashboard is marked as favorite, default is false
61
65
dashboard_type : Option < DashboardType > ,
62
66
pub tiles : Option < Vec < Tile > > ,
63
67
}
@@ -77,6 +81,11 @@ impl Dashboard {
77
81
if self . tiles . is_none ( ) {
78
82
self . tiles = Some ( Vec :: new ( ) ) ;
79
83
}
84
+
85
+ // if is_favorite is None, set it to false, else set it to the current value
86
+ self . is_favorite = Some (
87
+ self . is_favorite . unwrap_or ( false ) , // default to false if not set
88
+ ) ;
80
89
}
81
90
82
91
/// create a summary of the dashboard
@@ -96,6 +105,13 @@ impl Dashboard {
96
105
) ;
97
106
}
98
107
108
+ if let Some ( created) = & self . created {
109
+ map. insert (
110
+ "created" . to_string ( ) ,
111
+ serde_json:: Value :: String ( created. to_string ( ) ) ,
112
+ ) ;
113
+ }
114
+
99
115
if let Some ( modified) = & self . modified {
100
116
map. insert (
101
117
"modified" . to_string ( ) ,
@@ -110,6 +126,22 @@ impl Dashboard {
110
126
) ;
111
127
}
112
128
129
+ if let Some ( tags) = & self . tags {
130
+ map. insert (
131
+ "tags" . to_string ( ) ,
132
+ serde_json:: Value :: Array (
133
+ tags. iter ( )
134
+ . map ( |tag| serde_json:: Value :: String ( tag. clone ( ) ) )
135
+ . collect ( ) ,
136
+ ) ,
137
+ ) ;
138
+ }
139
+
140
+ map. insert (
141
+ "is_favorite" . to_string ( ) ,
142
+ serde_json:: Value :: Bool ( self . is_favorite . unwrap_or ( false ) ) ,
143
+ ) ;
144
+
113
145
map
114
146
}
115
147
}
@@ -175,6 +207,16 @@ impl Dashboards {
175
207
let dashboard_id = dashboard
176
208
. dashboard_id
177
209
. ok_or ( DashboardError :: Metadata ( "Dashboard ID must be provided" ) ) ?;
210
+
211
+ // ensure the dashboard has unique title
212
+ let dashboards = self . 0 . read ( ) . await ;
213
+ let has_duplicate = dashboards
214
+ . iter ( )
215
+ . any ( |d| d. title == dashboard. title && d. dashboard_id != dashboard. dashboard_id ) ;
216
+
217
+ if has_duplicate {
218
+ return Err ( DashboardError :: Metadata ( "Dashboard title must be unique" ) ) ;
219
+ }
178
220
let path = dashboard_path ( user_id, & format ! ( "{dashboard_id}.json" ) ) ;
179
221
180
222
let store = PARSEABLE . storage . get_object_store ( ) ;
@@ -194,6 +236,7 @@ impl Dashboards {
194
236
user_id : & str ,
195
237
dashboard : & mut Dashboard ,
196
238
) -> Result < ( ) , DashboardError > {
239
+ dashboard. created = Some ( Utc :: now ( ) ) ;
197
240
dashboard. set_metadata ( user_id, None ) ;
198
241
199
242
self . save_dashboard ( user_id, dashboard) . await ?;
@@ -211,10 +254,12 @@ impl Dashboards {
211
254
dashboard_id : Ulid ,
212
255
dashboard : & mut Dashboard ,
213
256
) -> Result < ( ) , DashboardError > {
214
- self . ensure_dashboard_ownership ( dashboard_id, user_id)
257
+ let existing_dashboard = self
258
+ . ensure_dashboard_ownership ( dashboard_id, user_id)
215
259
. await ?;
216
260
217
261
dashboard. set_metadata ( user_id, Some ( dashboard_id) ) ;
262
+ dashboard. created = existing_dashboard. created ;
218
263
self . save_dashboard ( user_id, dashboard) . await ?;
219
264
220
265
let mut dashboards = self . 0 . write ( ) . await ;
@@ -288,6 +333,20 @@ impl Dashboards {
288
333
self . 0 . read ( ) . await . clone ( )
289
334
}
290
335
336
+ /// List tags from all dashboards
337
+ /// This function returns a list of unique tags from all dashboards
338
+ pub async fn list_tags ( & self ) -> Vec < String > {
339
+ let dashboards = self . 0 . read ( ) . await ;
340
+ let mut tags = dashboards
341
+ . iter ( )
342
+ . filter_map ( |d| d. tags . as_ref ( ) )
343
+ . flat_map ( |t| t. iter ( ) . cloned ( ) )
344
+ . collect :: < Vec < String > > ( ) ;
345
+ tags. sort ( ) ;
346
+ tags. dedup ( ) ;
347
+ tags
348
+ }
349
+
291
350
/// Ensure the user is the owner of the dashboard
292
351
/// This function is called when updating or deleting a dashboard
293
352
/// check if the user is the owner of the dashboard
@@ -296,10 +355,13 @@ impl Dashboards {
296
355
& self ,
297
356
dashboard_id : Ulid ,
298
357
user_id : & str ,
299
- ) -> Result < ( ) , DashboardError > {
358
+ ) -> Result < Dashboard , DashboardError > {
300
359
self . get_dashboard_by_user ( dashboard_id, user_id)
301
360
. await
302
- . ok_or_else ( || DashboardError :: Unauthorized )
303
- . map ( |_| ( ) )
361
+ . ok_or_else ( || {
362
+ DashboardError :: Metadata (
363
+ "Dashboard does not exist or you do not have permission to access it" ,
364
+ )
365
+ } )
304
366
}
305
367
}
0 commit comments