@@ -31,28 +31,63 @@ use actix_web::{
3131use bytes:: Bytes ;
3232use ulid:: Ulid ;
3333
34- use crate :: alerts:: { ALERTS , AlertConfig , AlertError , AlertRequest , AlertState } ;
34+ use crate :: alerts:: { ALERTS , AlertConfig , AlertError , AlertRequest , AlertState , Severity } ;
3535
3636// GET /alerts
3737/// User needs at least a read access to the stream(s) that is being referenced in an alert
3838/// Read all alerts then return alerts which satisfy the condition
39+ /// Supports pagination with optional query parameters:
40+ /// - tags: comma-separated list of tags to filter alerts
41+ /// - offset: number of alerts to skip (default: 0)
42+ /// - limit: maximum number of alerts to return (default: 100, max: 1000)
3943pub async fn list ( req : HttpRequest ) -> Result < impl Responder , AlertError > {
4044 let session_key = extract_session_key_from_req ( & req) ?;
4145 let query_map = web:: Query :: < HashMap < String , String > > :: from_query ( req. query_string ( ) )
42- . map_err ( |_| AlertError :: InvalidQueryParameter ) ?;
46+ . map_err ( |_| AlertError :: InvalidQueryParameter ( "malformed query parameters" . to_string ( ) ) ) ?;
47+
4348 let mut tags_list = Vec :: new ( ) ;
49+ let mut offset = 0usize ;
50+ let mut limit = 100usize ; // Default limit
51+ const MAX_LIMIT : usize = 1000 ; // Maximum allowed limit
52+
53+ // Parse query parameters
4454 if !query_map. is_empty ( ) {
55+ // Parse tags parameter
4556 if let Some ( tags) = query_map. get ( "tags" ) {
4657 tags_list = tags
4758 . split ( ',' )
4859 . map ( |s| s. trim ( ) . to_string ( ) )
4960 . filter ( |s| !s. is_empty ( ) )
5061 . collect ( ) ;
5162 if tags_list. is_empty ( ) {
52- return Err ( AlertError :: InvalidQueryParameter ) ;
63+ return Err ( AlertError :: InvalidQueryParameter (
64+ "empty tags not allowed with query param tags" . to_string ( ) ,
65+ ) ) ;
66+ }
67+ }
68+
69+ // Parse offset parameter
70+ if let Some ( offset_str) = query_map. get ( "offset" ) {
71+ offset = offset_str. parse ( ) . map_err ( |_| {
72+ AlertError :: InvalidQueryParameter ( "offset is not a valid number" . to_string ( ) )
73+ } ) ?;
74+ }
75+
76+ // Parse limit parameter
77+ if let Some ( limit_str) = query_map. get ( "limit" ) {
78+ limit = limit_str. parse ( ) . map_err ( |_| {
79+ AlertError :: InvalidQueryParameter ( "limit is not a valid number" . to_string ( ) )
80+ } ) ?;
81+
82+ // Validate limit bounds
83+ if limit == 0 || limit > MAX_LIMIT {
84+ return Err ( AlertError :: InvalidQueryParameter (
85+ "limit should be between 1 and 1000" . to_string ( ) ,
86+ ) ) ;
5387 }
5488 }
5589 }
90+
5691 let guard = ALERTS . read ( ) . await ;
5792 let alerts = if let Some ( alerts) = guard. as_ref ( ) {
5893 alerts
@@ -61,11 +96,51 @@ pub async fn list(req: HttpRequest) -> Result<impl Responder, AlertError> {
6196 } ;
6297
6398 let alerts = alerts. list_alerts_for_user ( session_key, tags_list) . await ?;
64- let alerts_summary = alerts
99+ let mut alerts_summary = alerts
65100 . iter ( )
66101 . map ( |alert| alert. to_summary ( ) )
67102 . collect :: < Vec < _ > > ( ) ;
68- Ok ( web:: Json ( alerts_summary) )
103+
104+ // Sort by state priority (Triggered > Silenced > Resolved) then by severity (Critical > High > Medium > Low)
105+ alerts_summary. sort_by ( |a, b| {
106+ // Parse state and severity from JSON values back to enums
107+ let state_a = a
108+ . get ( "state" )
109+ . and_then ( |v| v. as_str ( ) )
110+ . and_then ( |s| s. parse :: < AlertState > ( ) . ok ( ) )
111+ . unwrap_or ( AlertState :: Resolved ) ; // Default to lowest priority
112+
113+ let state_b = b
114+ . get ( "state" )
115+ . and_then ( |v| v. as_str ( ) )
116+ . and_then ( |s| s. parse :: < AlertState > ( ) . ok ( ) )
117+ . unwrap_or ( AlertState :: Resolved ) ;
118+
119+ let severity_a = a
120+ . get ( "severity" )
121+ . and_then ( |v| v. as_str ( ) )
122+ . and_then ( |s| s. parse :: < Severity > ( ) . ok ( ) )
123+ . unwrap_or ( Severity :: Low ) ; // Default to lowest priority
124+
125+ let severity_b = b
126+ . get ( "severity" )
127+ . and_then ( |v| v. as_str ( ) )
128+ . and_then ( |s| s. parse :: < Severity > ( ) . ok ( ) )
129+ . unwrap_or ( Severity :: Low ) ;
130+
131+ // First sort by state, then by severity
132+ state_a
133+ . cmp ( & state_b)
134+ . then_with ( || severity_a. cmp ( & severity_b) )
135+ } ) ;
136+
137+ let paginated_alerts = alerts_summary
138+ . into_iter ( )
139+ . skip ( offset)
140+ . take ( limit)
141+ . collect :: < Vec < _ > > ( ) ;
142+
143+ Ok ( web:: Json ( paginated_alerts) )
69144}
70145
71146// POST /alerts
0 commit comments