18
18
19
19
use crate :: {
20
20
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} ,
25
24
} ;
26
25
use actix_web:: {
27
26
http:: header:: ContentType ,
28
27
web:: { self , Json , Path } ,
29
28
HttpRequest , HttpResponse , Responder ,
30
29
} ;
31
- use bytes:: Bytes ;
32
- use rand:: distributions:: DistString ;
33
-
34
- use chrono:: Utc ;
35
30
use http:: StatusCode ;
36
31
use serde_json:: Error as SerdeError ;
37
32
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 < _ > > ( ) ;
42
39
43
- Ok ( ( web:: Json ( dashboards ) , StatusCode :: OK ) )
40
+ Ok ( ( web:: Json ( dashboard_summaries ) , StatusCode :: OK ) )
44
41
}
45
42
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 ( ) ) ?;
52
45
53
- if let Some ( dashboard) = DASHBOARDS
54
- . get_dashboard ( & dashboard_id, & get_hash ( & user_id ) )
46
+ let dashboard = DASHBOARDS
47
+ . get_dashboard ( dashboard_id)
55
48
. await
56
- {
57
- return Ok ( ( web:: Json ( dashboard) , StatusCode :: OK ) ) ;
58
- }
49
+ . ok_or_else ( || DashboardError :: Metadata ( "Dashboard does not exist" ) ) ?;
59
50
60
- Err ( DashboardError :: Metadata ( "Dashboard does not exist" ) )
51
+ Ok ( ( web :: Json ( dashboard ) , StatusCode :: OK ) )
61
52
}
62
53
63
- pub async fn post (
54
+ pub async fn create_dashboard (
64
55
req : HttpRequest ,
65
56
Json ( mut dashboard) : Json < Dashboard > ,
66
57
) -> 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" ) ) ;
83
60
}
84
- DASHBOARDS . update ( & dashboard) . await ;
85
-
86
- let path = dashboard_path ( & user_id, & format ! ( "{}.json" , dashboard_id) ) ;
87
61
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) ?) ;
93
63
64
+ DASHBOARDS . create ( & user_id, & mut dashboard) . await ?;
94
65
Ok ( ( web:: Json ( dashboard) , StatusCode :: OK ) )
95
66
}
96
67
97
- pub async fn update (
68
+ pub async fn update_dashboard (
98
69
req : HttpRequest ,
99
70
dashboard_id : Path < String > ,
100
71
Json ( mut dashboard) : Json < Dashboard > ,
101
72
) -> 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 ( ) ) ?;
105
75
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" ) ) ;
119
80
}
120
81
}
121
- DASHBOARDS . update ( & dashboard) . await ;
122
82
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
+ }
124
96
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)
129
99
. await ?;
130
100
131
101
Ok ( ( web:: Json ( dashboard) , StatusCode :: OK ) )
132
102
}
133
103
134
- pub async fn delete (
104
+ pub async fn delete_dashboard (
135
105
req : HttpRequest ,
136
106
dashboard_id : Path < String > ,
137
107
) -> 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)
143
130
. 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" ) ) ;
147
138
}
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) ;
151
140
152
- DASHBOARDS . delete_dashboard ( & dashboard_id) . await ;
141
+ DASHBOARDS
142
+ . update ( & user_id, dashboard_id, & mut dashboard)
143
+ . await ?;
153
144
154
- Ok ( HttpResponse :: Ok ( ) . finish ( ) )
145
+ Ok ( ( web :: Json ( dashboard ) , StatusCode :: OK ) )
155
146
}
156
147
157
148
#[ derive( Debug , thiserror:: Error ) ]
@@ -166,6 +157,8 @@ pub enum DashboardError {
166
157
UserDoesNotExist ( #[ from] RBACError ) ,
167
158
#[ error( "Error: {0}" ) ]
168
159
Custom ( String ) ,
160
+ #[ error( "Dashboard does not exist or is not accessible" ) ]
161
+ Unauthorized ,
169
162
}
170
163
171
164
impl actix_web:: ResponseError for DashboardError {
@@ -176,6 +169,7 @@ impl actix_web::ResponseError for DashboardError {
176
169
Self :: Metadata ( _) => StatusCode :: BAD_REQUEST ,
177
170
Self :: UserDoesNotExist ( _) => StatusCode :: NOT_FOUND ,
178
171
Self :: Custom ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
172
+ Self :: Unauthorized => StatusCode :: UNAUTHORIZED ,
179
173
}
180
174
}
181
175
0 commit comments