16
16
*
17
17
*/
18
18
19
+ use std:: collections:: HashMap ;
20
+
19
21
use crate :: {
20
22
handlers:: http:: rbac:: RBACError ,
21
23
storage:: ObjectStorageError ,
@@ -68,37 +70,83 @@ pub async fn create_dashboard(
68
70
pub async fn update_dashboard (
69
71
req : HttpRequest ,
70
72
dashboard_id : Path < String > ,
71
- Json ( mut dashboard) : Json < Dashboard > ,
73
+ Json ( dashboard) : Json < Dashboard > ,
72
74
) -> Result < impl Responder , DashboardError > {
73
75
let user_id = get_hash ( & get_user_from_request ( & req) ?) ;
74
76
let dashboard_id = validate_dashboard_id ( dashboard_id. into_inner ( ) ) ?;
77
+ let mut existing_dashboard = DASHBOARDS
78
+ . get_dashboard_by_user ( dashboard_id, & user_id)
79
+ . await
80
+ . ok_or ( DashboardError :: Metadata (
81
+ "Dashboard does not exist or user is not authorized" ,
82
+ ) ) ?;
75
83
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" ) ) ;
80
- }
84
+ let query_map = web:: Query :: < HashMap < String , String > > :: from_query ( req. query_string ( ) )
85
+ . map_err ( |_| DashboardError :: InvalidQueryParameter ) ?;
86
+
87
+ // Validate: either query params OR body, not both
88
+ let has_query_params = !query_map. is_empty ( ) ;
89
+ let has_body_update = dashboard. title != existing_dashboard. title || dashboard. tiles . is_some ( ) ;
90
+
91
+ if has_query_params && has_body_update {
92
+ return Err ( DashboardError :: Metadata (
93
+ "Cannot use both query parameters and request body for updates" ,
94
+ ) ) ;
81
95
}
82
96
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" ) ) ;
97
+ let mut final_dashboard = if has_query_params {
98
+ // Apply partial updates from query parameters
99
+ if let Some ( is_favorite) = query_map. get ( "is_favorite" ) {
100
+ existing_dashboard. is_favorite = Some ( is_favorite == "true" ) ;
101
+ }
102
+ if let Some ( tags) = query_map. get ( "tags" ) {
103
+ let parsed_tags: Vec < String > = tags
104
+ . split ( ',' )
105
+ . map ( |s| s. trim ( ) )
106
+ . filter ( |s| !s. is_empty ( ) )
107
+ . map ( |s| s. to_string ( ) )
108
+ . collect ( ) ;
109
+ existing_dashboard. tags = if parsed_tags. is_empty ( ) {
110
+ None
111
+ } else {
112
+ Some ( parsed_tags)
113
+ } ;
114
+ }
115
+ if let Some ( rename_to) = query_map. get ( "rename_to" ) {
116
+ let trimmed = rename_to. trim ( ) ;
117
+ if trimmed. is_empty ( ) {
118
+ return Err ( DashboardError :: Metadata ( "Rename to cannot be empty" ) ) ;
119
+ }
120
+ existing_dashboard. title = trimmed. to_string ( ) ;
121
+ }
122
+ existing_dashboard
123
+ } else {
124
+ if let Some ( tiles) = & dashboard. tiles {
125
+ if tiles. iter ( ) . any ( |tile| tile. tile_id . is_nil ( ) ) {
126
+ return Err ( DashboardError :: Metadata ( "Tile ID must be provided" ) ) ;
127
+ }
128
+
129
+ // Check if tile_id are unique
130
+ let unique_tiles: Vec < _ > = tiles
131
+ . iter ( )
132
+ . map ( |tile| tile. tile_id )
133
+ . collect :: < std:: collections:: HashSet < _ > > ( )
134
+ . into_iter ( )
135
+ . collect ( ) ;
136
+
137
+ if unique_tiles. len ( ) != tiles. len ( ) {
138
+ return Err ( DashboardError :: Metadata ( "Tile IDs must be unique" ) ) ;
139
+ }
94
140
}
95
- }
141
+
142
+ dashboard
143
+ } ;
96
144
97
145
DASHBOARDS
98
- . update ( & user_id, dashboard_id, & mut dashboard )
146
+ . update ( & user_id, dashboard_id, & mut final_dashboard )
99
147
. await ?;
100
148
101
- Ok ( ( web:: Json ( dashboard ) , StatusCode :: OK ) )
149
+ Ok ( ( web:: Json ( final_dashboard ) , StatusCode :: OK ) )
102
150
}
103
151
104
152
pub async fn delete_dashboard (
@@ -164,6 +212,8 @@ pub enum DashboardError {
164
212
Custom ( String ) ,
165
213
#[ error( "Dashboard does not exist or is not accessible" ) ]
166
214
Unauthorized ,
215
+ #[ error( "Invalid query parameter" ) ]
216
+ InvalidQueryParameter ,
167
217
}
168
218
169
219
impl actix_web:: ResponseError for DashboardError {
@@ -175,6 +225,7 @@ impl actix_web::ResponseError for DashboardError {
175
225
Self :: UserDoesNotExist ( _) => StatusCode :: NOT_FOUND ,
176
226
Self :: Custom ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
177
227
Self :: Unauthorized => StatusCode :: UNAUTHORIZED ,
228
+ Self :: InvalidQueryParameter => StatusCode :: BAD_REQUEST ,
178
229
}
179
230
}
180
231
0 commit comments