diff --git a/aws_lambda_powertools/utilities/data_classes/cloud_watch_alarm_event.py b/aws_lambda_powertools/utilities/data_classes/cloud_watch_alarm_event.py index 7604a38bf63..8fae6a1fd83 100644 --- a/aws_lambda_powertools/utilities/data_classes/cloud_watch_alarm_event.py +++ b/aws_lambda_powertools/utilities/data_classes/cloud_watch_alarm_event.py @@ -90,8 +90,15 @@ def return_data(self) -> bool: return self["returnData"] @property - def metric_stat(self) -> CloudWatchAlarmMetricStat: - return CloudWatchAlarmMetricStat(self["metricStat"]) + def metric_stat(self) -> CloudWatchAlarmMetricStat | None: + """ + Optional metric stat of the alarm metric. + + Metric math alarms list expression entries alongside metric entries, and an + expression entry has no metricStat. + """ + metric_stat = self.get("metricStat") + return CloudWatchAlarmMetricStat(metric_stat) if metric_stat else None class CloudWatchAlarmMetricStat(DictWrapper): diff --git a/tests/unit/data_classes/required_dependencies/test_cloud_watch_alarm_event.py b/tests/unit/data_classes/required_dependencies/test_cloud_watch_alarm_event.py index c73aa9f6b8b..56a3675f6b7 100644 --- a/tests/unit/data_classes/required_dependencies/test_cloud_watch_alarm_event.py +++ b/tests/unit/data_classes/required_dependencies/test_cloud_watch_alarm_event.py @@ -57,6 +57,23 @@ def test_cloud_watch_alarm_event_single_metric(): assert isinstance(metric_1.metric_stat.metric, dict) +def test_cloud_watch_alarm_metric_stat_is_none_for_expression_entries(): + raw_event = load_event("cloudWatchAlarmEventSingleMetric.json") + parsed_event = CloudWatchAlarmEvent(raw_event) + metrics = parsed_event.alarm_data.configuration.metrics + + # GIVEN a metric math alarm, whose expression entries carry no metricStat key + expression_metric = metrics[0] + assert expression_metric.expression is not None + assert "metricStat" not in raw_event["alarmData"]["configuration"]["metrics"][0] + + # THEN metric_stat must return None like expression and label do, not raise KeyError + assert expression_metric.metric_stat is None + + # AND iterating every metric's metric_stat must be safe + assert [metric.metric_stat is not None for metric in metrics] == [False, True, True] + + def test_cloud_watch_alarm_event_composite_metric(): raw_event = load_event("cloudWatchAlarmEventCompositeMetric.json") parsed_event = CloudWatchAlarmEvent(raw_event)