1- from functools import reduce
2- from typing import List
1+ from typing import List , Optional
32
43from elementary .monitor .data_monitoring .schema import (
5- FilterSchema ,
64 FiltersSchema ,
7- ResourceTypeFilterSchema ,
8- StatusFilterSchema ,
5+ ResourceType ,
6+ Status ,
97)
108from elementary .monitor .fetchers .alerts .schema .pending_alerts import (
119 AlertTypes ,
1614logger = get_logger (__name__ )
1715
1816
17+ def get_string_ends (input_string : str , splitter : str ) -> List [str ]:
18+ parts = input_string .split (splitter )
19+ result = []
20+
21+ for i in range (len (parts )):
22+ result .append (splitter .join (parts [i :]))
23+
24+ return result
25+
26+
27+ def _get_alert_node_name (alert : PendingAlertSchema ) -> Optional [str ]:
28+ alert_node_name = None
29+ alert_type = AlertTypes (alert .type )
30+ if alert_type is AlertTypes .TEST :
31+ alert_node_name = alert .data .test_name # type: ignore[union-attr]
32+ elif alert_type is AlertTypes .MODEL or alert_type is AlertTypes .SOURCE_FRESHNESS :
33+ alert_node_name = alert .data .model_unique_id
34+ else :
35+ raise ValueError (f"Unexpected alert type: { alert_type } " )
36+ return alert_node_name
37+
38+
39+ def apply_filters_schema_on_alert (
40+ alert : PendingAlertSchema , filters_schema : FiltersSchema
41+ ) -> bool :
42+ tags = alert .data .tags or []
43+ models = (
44+ [
45+ alert .data .model_unique_id ,
46+ * get_string_ends (alert .data .model_unique_id , "." ),
47+ ]
48+ if alert .data .model_unique_id
49+ else []
50+ )
51+ owners = alert .data .unified_owners or []
52+ status = Status (alert .data .status )
53+ resource_type = ResourceType (alert .data .resource_type )
54+
55+ alert_node_name = _get_alert_node_name (alert )
56+ node_names = (
57+ [alert_node_name , * get_string_ends (alert_node_name , "." )]
58+ if alert_node_name
59+ else []
60+ )
61+
62+ return filters_schema .apply (
63+ tags = tags ,
64+ models = models ,
65+ owners = owners ,
66+ statuses = [status ],
67+ resource_types = [resource_type ],
68+ node_names = node_names ,
69+ )
70+
71+
1972def filter_alerts (
2073 alerts : List [PendingAlertSchema ],
2174 alerts_filter : FiltersSchema = FiltersSchema (),
@@ -29,188 +82,6 @@ def filter_alerts(
2982 logger .warning ("Invalid filter for alerts: %s" , alerts_filter .selector )
3083 return [] # type: ignore[return-value]
3184
32- # If the filter is empty, we want to return all of the alerts
33- filtered_alerts = alerts
34- filtered_alerts = _filter_alerts_by_tags (filtered_alerts , alerts_filter .tags )
35- filtered_alerts = _filter_alerts_by_models (filtered_alerts , alerts_filter .models )
36- filtered_alerts = _filter_alerts_by_owners (filtered_alerts , alerts_filter .owners )
37- filtered_alerts = _filter_alerts_by_statuses (
38- filtered_alerts , alerts_filter .statuses
39- )
40- filtered_alerts = _filter_alerts_by_resource_types (
41- filtered_alerts , alerts_filter .resource_types
42- )
43- if alerts_filter .node_names :
44- filtered_alerts = _filter_alerts_by_node_names (
45- filtered_alerts , alerts_filter .node_names
46- )
47-
48- return filtered_alerts
49-
50-
51- def _find_common_alerts (
52- first_alerts : List [PendingAlertSchema ],
53- second_alerts : List [PendingAlertSchema ],
54- ) -> List [PendingAlertSchema ]:
55- first_alert_ids = [alert .id for alert in first_alerts ]
56- second_alert_ids = [alert .id for alert in second_alerts ]
57- common_alert_ids = list (set (first_alert_ids ) & set (second_alert_ids ))
58-
59- common_alerts = []
60- # To handle dedupping common alerts
61- alert_ids_already_handled = []
62-
63- for alert in [* first_alerts , * second_alerts ]:
64- if alert .id in common_alert_ids and alert .id not in alert_ids_already_handled :
65- common_alerts .append (alert )
66- alert_ids_already_handled .append (alert .id )
67- return common_alerts
68-
69-
70- def _filter_alerts_by_tags (
71- alerts : List [PendingAlertSchema ],
72- tags_filters : List [FilterSchema ],
73- ) -> List [PendingAlertSchema ]:
74- if not tags_filters :
75- return [* alerts ]
76-
77- grouped_filtered_alerts_by_tags = []
78-
79- # OR filter for each tags_filter's values
80- for tags_filter in tags_filters :
81- filtered_alerts_by_tags = []
82- for alert in alerts :
83- if any (tag in (alert .data .tags or []) for tag in tags_filter .values ):
84- filtered_alerts_by_tags .append (alert )
85- grouped_filtered_alerts_by_tags .append (filtered_alerts_by_tags )
86-
87- # AND filter between all tags_filters
88- return reduce (_find_common_alerts , grouped_filtered_alerts_by_tags )
89-
90-
91- def _filter_alerts_by_owners (
92- alerts : List [PendingAlertSchema ],
93- owners_filters : List [FilterSchema ],
94- ) -> List [PendingAlertSchema ]:
95- if not owners_filters :
96- return [* alerts ]
97-
98- grouped_filtered_alerts_by_owners = []
99-
100- # OR filter for each owners_filter's values
101- for owners_filter in owners_filters :
102- filtered_alerts_by_owners = []
103- for alert in alerts :
104- if any (
105- owner in alert .data .unified_owners for owner in owners_filter .values
106- ):
107- filtered_alerts_by_owners .append (alert )
108- grouped_filtered_alerts_by_owners .append (filtered_alerts_by_owners )
109-
110- # AND filter between all owners_filters
111- return reduce (_find_common_alerts , grouped_filtered_alerts_by_owners )
112-
113-
114- def _filter_alerts_by_models (
115- alerts : List [PendingAlertSchema ],
116- models_filters : List [FilterSchema ],
117- ) -> List [PendingAlertSchema ]:
118- if not models_filters :
119- return [* alerts ]
120-
121- grouped_filtered_alerts_by_models = []
122-
123- # OR filter for each models_filter's values
124- for models_filter in models_filters :
125- filtered_alerts_by_models = []
126- for alert in alerts :
127- if any (
128- (
129- alert .data .model_unique_id
130- and alert .data .model_unique_id .endswith (model )
131- )
132- for model in models_filter .values
133- ):
134- filtered_alerts_by_models .append (alert )
135- grouped_filtered_alerts_by_models .append (filtered_alerts_by_models )
136-
137- # AND filter between all models_filters
138- return reduce (_find_common_alerts , grouped_filtered_alerts_by_models )
139-
140-
141- def _filter_alerts_by_node_names (
142- alerts : List [PendingAlertSchema ],
143- node_names_filters : List [str ],
144- ) -> List [PendingAlertSchema ]:
145- if not node_names_filters :
146- return [* alerts ]
147-
148- filtered_alerts = []
149- for alert in alerts :
150- alert_node_name = None
151- alert_type = AlertTypes (alert .type )
152- if alert_type is AlertTypes .TEST :
153- alert_node_name = alert .data .test_name # type: ignore[union-attr]
154- elif (
155- alert_type is AlertTypes .MODEL or alert_type is AlertTypes .SOURCE_FRESHNESS
156- ):
157- alert_node_name = alert .data .model_unique_id
158- else :
159- # Shouldn't happen
160- raise Exception (f"Unexpected alert type: { type (alert )} " )
161-
162- if alert_node_name :
163- for node_name in node_names_filters :
164- if alert_node_name .endswith (node_name ) or node_name .endswith (
165- alert_node_name
166- ):
167- filtered_alerts .append (alert )
168- break
169- return filtered_alerts # type: ignore[return-value]
170-
171-
172- def _filter_alerts_by_statuses (
173- alerts : List [PendingAlertSchema ],
174- statuses_filters : List [StatusFilterSchema ],
175- ) -> List [PendingAlertSchema ]:
176- if not statuses_filters :
177- return [* alerts ]
178-
179- grouped_filtered_alerts_by_statuses = []
180-
181- # OR filter for each statuses_filter's values
182- for statuses_filter in statuses_filters :
183- filtered_alerts_by_statuses = []
184- for alert in alerts :
185- if any (status == alert .data .status for status in statuses_filter .values ):
186- filtered_alerts_by_statuses .append (alert )
187- grouped_filtered_alerts_by_statuses .append (filtered_alerts_by_statuses )
188-
189- # AND filter between all statuses_filters
190- return reduce (_find_common_alerts , grouped_filtered_alerts_by_statuses )
191-
192-
193- def _filter_alerts_by_resource_types (
194- alerts : List [PendingAlertSchema ],
195- resource_types_filters : List [ResourceTypeFilterSchema ],
196- ) -> List [PendingAlertSchema ]:
197- if not resource_types_filters :
198- return [* alerts ]
199-
200- grouped_filtered_alerts_by_resource_types = []
201-
202- # OR filter for each resource_types_filter's values
203- for resource_types_filter in resource_types_filters :
204- filtered_alerts_by_resource_types = []
205- for alert in alerts :
206- if any (
207- resource_type == alert .data .resource_type .value
208- for resource_type in resource_types_filter .values
209- ):
210- filtered_alerts_by_resource_types .append (alert )
211- grouped_filtered_alerts_by_resource_types .append (
212- filtered_alerts_by_resource_types
213- )
214-
215- # AND filter between all resource_types_filters
216- return reduce (_find_common_alerts , grouped_filtered_alerts_by_resource_types )
85+ return [
86+ alert for alert in alerts if apply_filters_schema_on_alert (alert , alerts_filter )
87+ ]
0 commit comments