1818
1919use crate :: {
2020 handlers:: http:: rbac:: RBACError ,
21- parseable:: PARSEABLE ,
22- storage:: { object_storage:: dashboard_path, ObjectStorageError } ,
23- users:: dashboards:: { Dashboard , CURRENT_DASHBOARD_VERSION , DASHBOARDS } ,
24- utils:: { actix:: extract_session_key_from_req, get_hash, get_user_from_request} ,
21+ storage:: ObjectStorageError ,
22+ users:: dashboards:: { validate_dashboard_id, Dashboard , Tile , DASHBOARDS } ,
23+ utils:: { get_hash, get_user_from_request} ,
2524} ;
2625use actix_web:: {
2726 http:: header:: ContentType ,
2827 web:: { self , Json , Path } ,
2928 HttpRequest , HttpResponse , Responder ,
3029} ;
31- use bytes:: Bytes ;
32- use rand:: distributions:: DistString ;
33-
34- use chrono:: Utc ;
3530use http:: StatusCode ;
3631use serde_json:: Error as SerdeError ;
3732
38- pub async fn list ( req : HttpRequest ) -> Result < impl Responder , DashboardError > {
39- let key =
40- extract_session_key_from_req ( & req) . map_err ( |e| DashboardError :: Custom ( e. to_string ( ) ) ) ?;
41- let dashboards = DASHBOARDS . list_dashboards ( & key) . await ;
33+ pub async fn list_dashboards ( ) -> Result < impl Responder , DashboardError > {
34+ let dashboards = DASHBOARDS . list_dashboards ( ) . await ;
35+ let dashboard_summaries = dashboards
36+ . iter ( )
37+ . map ( |dashboard| dashboard. to_summary ( ) )
38+ . collect :: < Vec < _ > > ( ) ;
4239
43- Ok ( ( web:: Json ( dashboards ) , StatusCode :: OK ) )
40+ Ok ( ( web:: Json ( dashboard_summaries ) , StatusCode :: OK ) )
4441}
4542
46- pub async fn get (
47- req : HttpRequest ,
48- dashboard_id : Path < String > ,
49- ) -> Result < impl Responder , DashboardError > {
50- let user_id = get_user_from_request ( & req) ?;
51- let dashboard_id = dashboard_id. into_inner ( ) ;
43+ pub async fn get_dashboard ( dashboard_id : Path < String > ) -> Result < impl Responder , DashboardError > {
44+ let dashboard_id = validate_dashboard_id ( dashboard_id. into_inner ( ) ) ?;
5245
53- if let Some ( dashboard) = DASHBOARDS
54- . get_dashboard ( & dashboard_id, & get_hash ( & user_id ) )
46+ let dashboard = DASHBOARDS
47+ . get_dashboard ( dashboard_id)
5548 . await
56- {
57- return Ok ( ( web:: Json ( dashboard) , StatusCode :: OK ) ) ;
58- }
49+ . ok_or_else ( || DashboardError :: Metadata ( "Dashboard does not exist" ) ) ?;
5950
60- Err ( DashboardError :: Metadata ( "Dashboard does not exist" ) )
51+ Ok ( ( web :: Json ( dashboard ) , StatusCode :: OK ) )
6152}
6253
63- pub async fn post (
54+ pub async fn create_dashboard (
6455 req : HttpRequest ,
6556 Json ( mut dashboard) : Json < Dashboard > ,
6657) -> Result < impl Responder , DashboardError > {
67- let mut user_id = get_user_from_request ( & req) ?;
68- user_id = get_hash ( & user_id) ;
69- let dashboard_id = get_hash ( Utc :: now ( ) . timestamp_micros ( ) . to_string ( ) . as_str ( ) ) ;
70- dashboard. dashboard_id = Some ( dashboard_id. clone ( ) ) ;
71- dashboard. version = Some ( CURRENT_DASHBOARD_VERSION . to_string ( ) ) ;
72-
73- dashboard. user_id = Some ( user_id. clone ( ) ) ;
74- for tile in dashboard. tiles . iter_mut ( ) {
75- tile. tile_id = Some ( get_hash (
76- format ! (
77- "{}{}" ,
78- rand:: distributions:: Alphanumeric . sample_string( & mut rand:: thread_rng( ) , 8 ) ,
79- Utc :: now( ) . timestamp_micros( )
80- )
81- . as_str ( ) ,
82- ) ) ;
58+ if dashboard. title . is_empty ( ) {
59+ return Err ( DashboardError :: Metadata ( "Title must be provided" ) ) ;
8360 }
84- DASHBOARDS . update ( & dashboard) . await ;
85-
86- let path = dashboard_path ( & user_id, & format ! ( "{}.json" , dashboard_id) ) ;
8761
88- let store = PARSEABLE . storage . get_object_store ( ) ;
89- let dashboard_bytes = serde_json:: to_vec ( & dashboard) ?;
90- store
91- . put_object ( & path, Bytes :: from ( dashboard_bytes) )
92- . await ?;
62+ let user_id = get_hash ( & get_user_from_request ( & req) ?) ;
9363
64+ DASHBOARDS . create ( & user_id, & mut dashboard) . await ?;
9465 Ok ( ( web:: Json ( dashboard) , StatusCode :: OK ) )
9566}
9667
97- pub async fn update (
68+ pub async fn update_dashboard (
9869 req : HttpRequest ,
9970 dashboard_id : Path < String > ,
10071 Json ( mut dashboard) : Json < Dashboard > ,
10172) -> Result < impl Responder , DashboardError > {
102- let mut user_id = get_user_from_request ( & req) ?;
103- user_id = get_hash ( & user_id) ;
104- let dashboard_id = dashboard_id. into_inner ( ) ;
73+ let user_id = get_hash ( & get_user_from_request ( & req) ?) ;
74+ let dashboard_id = validate_dashboard_id ( dashboard_id. into_inner ( ) ) ?;
10575
106- if DASHBOARDS
107- . get_dashboard ( & dashboard_id, & user_id)
108- . await
109- . is_none ( )
110- {
111- return Err ( DashboardError :: Metadata ( "Dashboard does not exist" ) ) ;
112- }
113- dashboard. dashboard_id = Some ( dashboard_id. to_string ( ) ) ;
114- dashboard. user_id = Some ( user_id. clone ( ) ) ;
115- dashboard. version = Some ( CURRENT_DASHBOARD_VERSION . to_string ( ) ) ;
116- for tile in dashboard. tiles . iter_mut ( ) {
117- if tile. tile_id . is_none ( ) {
118- tile. tile_id = Some ( get_hash ( Utc :: now ( ) . timestamp_micros ( ) . to_string ( ) . as_str ( ) ) ) ;
76+ // Validate all tiles have valid IDs
77+ if let Some ( tiles) = & dashboard. tiles {
78+ if tiles. iter ( ) . any ( |tile| tile. tile_id . is_nil ( ) ) {
79+ return Err ( DashboardError :: Metadata ( "Tile ID must be provided" ) ) ;
11980 }
12081 }
121- DASHBOARDS . update ( & dashboard) . await ;
12282
123- let path = dashboard_path ( & user_id, & format ! ( "{}.json" , dashboard_id) ) ;
83+ // Check if tile_id are unique
84+ if let Some ( tiles) = & dashboard. tiles {
85+ let unique_tiles: Vec < _ > = tiles
86+ . iter ( )
87+ . map ( |tile| tile. tile_id )
88+ . collect :: < std:: collections:: HashSet < _ > > ( )
89+ . into_iter ( )
90+ . collect ( ) ;
91+
92+ if unique_tiles. len ( ) != tiles. len ( ) {
93+ return Err ( DashboardError :: Metadata ( "Tile IDs must be unique" ) ) ;
94+ }
95+ }
12496
125- let store = PARSEABLE . storage . get_object_store ( ) ;
126- let dashboard_bytes = serde_json:: to_vec ( & dashboard) ?;
127- store
128- . put_object ( & path, Bytes :: from ( dashboard_bytes) )
97+ DASHBOARDS
98+ . update ( & user_id, dashboard_id, & mut dashboard)
12999 . await ?;
130100
131101 Ok ( ( web:: Json ( dashboard) , StatusCode :: OK ) )
132102}
133103
134- pub async fn delete (
104+ pub async fn delete_dashboard (
135105 req : HttpRequest ,
136106 dashboard_id : Path < String > ,
137107) -> Result < HttpResponse , DashboardError > {
138- let mut user_id = get_user_from_request ( & req) ?;
139- user_id = get_hash ( & user_id) ;
140- let dashboard_id = dashboard_id. into_inner ( ) ;
141- if DASHBOARDS
142- . get_dashboard ( & dashboard_id, & user_id)
108+ let user_id = get_hash ( & get_user_from_request ( & req) ?) ;
109+ let dashboard_id = validate_dashboard_id ( dashboard_id. into_inner ( ) ) ?;
110+
111+ DASHBOARDS . delete_dashboard ( & user_id, dashboard_id) . await ?;
112+
113+ Ok ( HttpResponse :: Ok ( ) . finish ( ) )
114+ }
115+
116+ pub async fn add_tile (
117+ req : HttpRequest ,
118+ dashboard_id : Path < String > ,
119+ Json ( tile) : Json < Tile > ,
120+ ) -> Result < impl Responder , DashboardError > {
121+ if tile. tile_id . is_nil ( ) {
122+ return Err ( DashboardError :: Metadata ( "Tile ID must be provided" ) ) ;
123+ }
124+
125+ let user_id = get_hash ( & get_user_from_request ( & req) ?) ;
126+ let dashboard_id = validate_dashboard_id ( dashboard_id. into_inner ( ) ) ?;
127+
128+ let mut dashboard = DASHBOARDS
129+ . get_dashboard_by_user ( dashboard_id, & user_id)
143130 . await
144- . is_none ( )
145- {
146- return Err ( DashboardError :: Metadata ( "Dashboard does not exist" ) ) ;
131+ . ok_or ( DashboardError :: Unauthorized ) ?;
132+
133+ let tiles = dashboard. tiles . get_or_insert_with ( Vec :: new) ;
134+
135+ // check if the tile already exists
136+ if tiles. iter ( ) . any ( |t| t. tile_id == tile. tile_id ) {
137+ return Err ( DashboardError :: Metadata ( "Tile already exists" ) ) ;
147138 }
148- let path = dashboard_path ( & user_id, & format ! ( "{}.json" , dashboard_id) ) ;
149- let store = PARSEABLE . storage . get_object_store ( ) ;
150- store. delete_object ( & path) . await ?;
139+ tiles. push ( tile) ;
151140
152- DASHBOARDS . delete_dashboard ( & dashboard_id) . await ;
141+ DASHBOARDS
142+ . update ( & user_id, dashboard_id, & mut dashboard)
143+ . await ?;
153144
154- Ok ( HttpResponse :: Ok ( ) . finish ( ) )
145+ Ok ( ( web :: Json ( dashboard ) , StatusCode :: OK ) )
155146}
156147
157148#[ derive( Debug , thiserror:: Error ) ]
@@ -166,6 +157,8 @@ pub enum DashboardError {
166157 UserDoesNotExist ( #[ from] RBACError ) ,
167158 #[ error( "Error: {0}" ) ]
168159 Custom ( String ) ,
160+ #[ error( "Dashboard does not exist or is not accessible" ) ]
161+ Unauthorized ,
169162}
170163
171164impl actix_web:: ResponseError for DashboardError {
@@ -176,6 +169,7 @@ impl actix_web::ResponseError for DashboardError {
176169 Self :: Metadata ( _) => StatusCode :: BAD_REQUEST ,
177170 Self :: UserDoesNotExist ( _) => StatusCode :: NOT_FOUND ,
178171 Self :: Custom ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
172+ Self :: Unauthorized => StatusCode :: UNAUTHORIZED ,
179173 }
180174 }
181175
0 commit comments