From ef098ae15dd1194495e47c0b0eea828e559dfa92 Mon Sep 17 00:00:00 2001 From: MikaKerman Date: Tue, 4 Feb 2025 15:57:35 +0200 Subject: [PATCH 1/6] Refactor alert sending and grouping logic in data monitoring alerts - Moved alert grouping logic from integration to data monitoring alerts - Added support for grouping all alerts when threshold is met - Updated alert sending to handle individual alerts instead of bulk sending - Simplified alert processing and added type hints - Deprecated bulk alert sending methods in base integration --- .../alerts/data_monitoring_alerts.py | 54 ++++++++++++------- .../alerts/integrations/base_integration.py | 2 + 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py b/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py index eb5ccaeb4..2531926c4 100644 --- a/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py +++ b/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py @@ -7,6 +7,7 @@ from elementary.config.config import Config from elementary.monitor.alerts.alerts_groups import GroupedByTableAlerts +from elementary.monitor.alerts.alerts_groups.alerts_group import AlertsGroup from elementary.monitor.alerts.alerts_groups.base_alerts_group import BaseAlertsGroup from elementary.monitor.alerts.grouping_type import GroupingType from elementary.monitor.alerts.model_alert import ModelAlertModel @@ -182,8 +183,10 @@ def _format_alerts( ModelAlertModel, SourceFreshnessAlertModel, GroupedByTableAlerts, + BaseAlertsGroup, ] ]: + group_all_alerts = len(alerts) >= self.config.group_alerts_threshold # type: ignore[arg-type] formatted_alerts = [] grouped_by_table_alerts = [] model_ids_to_alerts_map = defaultdict(lambda: []) @@ -204,6 +207,10 @@ def _format_alerts( disable_samples=self.disable_samples, env=self.config.specified_env, ) + if group_all_alerts: + formatted_alerts.append(formatted_alert) + continue + try: grouping_type = GroupingType(group_alerts_by) if grouping_type == GroupingType.BY_TABLE: @@ -218,24 +225,27 @@ def _format_alerts( f"Failed to extract value as a group-by config: '{group_alerts_by}'. Allowed Values: {list(GroupingType.__members__.keys())} Ignoring it for now and default grouping strategy will be used" ) - for alerts_by_model in model_ids_to_alerts_map.values(): - grouped_by_table_alerts.append( - GroupedByTableAlerts( - alerts=alerts_by_model, - env=self.config.specified_env, + if group_all_alerts: + return [AlertsGroup(alerts=formatted_alerts, env=self.config.specified_env)] + + else: + for alerts_by_model in model_ids_to_alerts_map.values(): + grouped_by_table_alerts.append( + GroupedByTableAlerts( + alerts=alerts_by_model, env=self.config.specified_env + ) ) - ) - self.execution_properties["had_group_by_table"] = ( - len(grouped_by_table_alerts) > 0 - ) - self.execution_properties["had_group_by_alert"] = len(formatted_alerts) > 0 + self.execution_properties["had_group_by_table"] = ( + len(grouped_by_table_alerts) > 0 + ) + self.execution_properties["had_group_by_alert"] = len(formatted_alerts) > 0 - all_alerts = formatted_alerts + grouped_by_table_alerts - return sorted( - all_alerts, - key=lambda alert: alert.detected_at or datetime.max, - ) + all_alerts = formatted_alerts + grouped_by_table_alerts + return sorted( + all_alerts, + key=lambda alert: alert.detected_at or datetime.max, + ) def _send_test_message(self): self.alerts_integration.send_test_message( @@ -250,6 +260,7 @@ def _send_alerts( ModelAlertModel, SourceFreshnessAlertModel, GroupedByTableAlerts, + BaseAlertsGroup, ] ], ): @@ -257,12 +268,17 @@ def _send_alerts( self.execution_properties["sent_alert_count"] = self.sent_alert_count return - sent_successfully_alerts = [] + sent_successfully_alerts: List[ + Union[ + TestAlertModel, + ModelAlertModel, + SourceFreshnessAlertModel, + ] + ] = [] with alive_bar(len(alerts), title="Sending alerts") as bar: - for alert, sent_successfully in self.alerts_integration.send_alerts( - alerts, self.config.group_alerts_threshold - ): + for alert in alerts: + sent_successfully = self.alerts_integration.send_alert(alert) bar() if sent_successfully: if isinstance(alert, BaseAlertsGroup): diff --git a/elementary/monitor/data_monitoring/alerts/integrations/base_integration.py b/elementary/monitor/data_monitoring/alerts/integrations/base_integration.py index 1b076b4ec..e4781b5ab 100644 --- a/elementary/monitor/data_monitoring/alerts/integrations/base_integration.py +++ b/elementary/monitor/data_monitoring/alerts/integrations/base_integration.py @@ -129,6 +129,7 @@ def _group_alerts( AlertsGroup, ] ]: + # Deprecated: the grouping logic is now handled outside of the integration, and the integration only sends the alerts if not alerts: return [] @@ -176,6 +177,7 @@ def send_alerts( None, None, ]: + # Deprecated: the grouping logic is now handled outside of the integration, and the integration only sends the alerts grouped_alerts = self._group_alerts(alerts, group_alerts_threshold) for alert in grouped_alerts: if isinstance(alert, BaseAlertsGroup): From a44e9c0e3b913cff0937be67ee21f6d85008d36d Mon Sep 17 00:00:00 2001 From: MikaKerman Date: Tue, 4 Feb 2025 16:01:23 +0200 Subject: [PATCH 2/6] Add base messaging integration classes and exceptions - Created base abstract class for messaging integrations with generic support - Implemented `BaseMessagingIntegration` with abstract methods for sending messages and checking reply support - Added custom exceptions for messaging integration errors - Introduced `MessageSendResult` generic model to track message sending context --- .../messaging_integrations/__init__.py | 0 .../base_messaging_integration.py | 49 +++++++++++++++++++ .../messaging_integrations/exceptions.py | 6 +++ 3 files changed, 55 insertions(+) create mode 100644 elementary/messages/messaging_integrations/__init__.py create mode 100644 elementary/messages/messaging_integrations/base_messaging_integration.py create mode 100644 elementary/messages/messaging_integrations/exceptions.py diff --git a/elementary/messages/messaging_integrations/__init__.py b/elementary/messages/messaging_integrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/elementary/messages/messaging_integrations/base_messaging_integration.py b/elementary/messages/messaging_integrations/base_messaging_integration.py new file mode 100644 index 000000000..6e35a3104 --- /dev/null +++ b/elementary/messages/messaging_integrations/base_messaging_integration.py @@ -0,0 +1,49 @@ +from abc import ABC, abstractmethod +from datetime import datetime +from typing import Generic, Optional, TypeVar + +from pydantic import BaseModel + +from elementary.messages.message_body import MessageBody +from elementary.messages.messaging_integrations.exceptions import ( + MessageIntegrationReplyNotSupportedError, +) +from elementary.utils.log import get_logger + +logger = get_logger(__name__) + + +T = TypeVar("T") + + +class MessageSendResult(BaseModel, Generic[T]): + timestamp: datetime + message_context: Optional[T] = None + + +DestinationType = TypeVar("DestinationType") +MessageContextType = TypeVar("MessageContextType") + + +class BaseMessagingIntegration(ABC, Generic[DestinationType, MessageContextType]): + @abstractmethod + def send_message( + self, + destination: DestinationType, + message_body: MessageBody, + ) -> MessageSendResult[MessageContextType]: + raise NotImplementedError + + @abstractmethod + def supports_reply(self) -> bool: + raise NotImplementedError + + def reply_to_message( + self, + destination: DestinationType, + message_context: MessageContextType, + message_body: MessageBody, + ) -> MessageSendResult[MessageContextType]: + if not self.supports_reply(): + raise MessageIntegrationReplyNotSupportedError + raise NotImplementedError diff --git a/elementary/messages/messaging_integrations/exceptions.py b/elementary/messages/messaging_integrations/exceptions.py new file mode 100644 index 000000000..dc1272423 --- /dev/null +++ b/elementary/messages/messaging_integrations/exceptions.py @@ -0,0 +1,6 @@ +class MessagingIntegrationError(Exception): + pass + + +class MessageIntegrationReplyNotSupportedError(MessagingIntegrationError): + pass From 9cc51ce02e86d9e08903d1aab1ae76e8859e8f4d Mon Sep 17 00:00:00 2001 From: MikaKerman Date: Tue, 4 Feb 2025 17:38:57 +0200 Subject: [PATCH 3/6] Add Teams webhook messaging integration - Implemented `TeamsWebhookMessagingIntegration` for sending messages to Microsoft Teams - Created support for sending Adaptive Cards via webhook - Added error handling and logging for webhook message sending - Implemented method to check reply support (not supported for Teams) --- .../messaging_integrations/teams_webhook.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 elementary/messages/messaging_integrations/teams_webhook.py diff --git a/elementary/messages/messaging_integrations/teams_webhook.py b/elementary/messages/messaging_integrations/teams_webhook.py new file mode 100644 index 000000000..0f0ae1360 --- /dev/null +++ b/elementary/messages/messaging_integrations/teams_webhook.py @@ -0,0 +1,82 @@ +from datetime import datetime +from typing import Optional + +import requests +from pydantic import BaseModel + +from elementary.messages.formats.adaptive_cards import format_adaptive_card +from elementary.messages.message_body import MessageBody +from elementary.messages.messaging_integrations.base_messaging_integration import ( + BaseMessagingIntegration, + MessageSendResult, +) +from elementary.messages.messaging_integrations.exceptions import ( + MessageIntegrationReplyNotSupportedError, + MessagingIntegrationError, +) +from elementary.utils.log import get_logger + +logger = get_logger(__name__) + + +class ChannelWebhook(BaseModel): + webhook: str + channel: Optional[str] = None + + +def send_adaptive_card(webhook_url: str, card: dict) -> requests.Response: + """Sends an Adaptive Card to the specified webhook URL.""" + payload = { + "type": "message", + "attachments": [ + { + "contentType": "application/vnd.microsoft.card.adaptive", + "contentUrl": None, + "content": card, + } + ], + } + + response = requests.post( + webhook_url, + json=payload, + headers={"Content-Type": "application/json"}, + ) + response.raise_for_status() + if response.status_code == 202: + logger.debug("Got 202 response from Teams webhook, assuming success") + return response + + +class TeamsWebhookMessagingIntegration( + BaseMessagingIntegration[ChannelWebhook, ChannelWebhook] +): + def send_message( + self, + destination: ChannelWebhook, + message_body: MessageBody, + ) -> MessageSendResult[ChannelWebhook]: + card = format_adaptive_card(message_body) + try: + send_adaptive_card(destination.webhook, card) + return MessageSendResult( + message_context=destination, + timestamp=datetime.utcnow(), + ) + except requests.RequestException as e: + raise MessagingIntegrationError( + "Failed to send message to Teams webhook" + ) from e + + def supports_reply(self) -> bool: + return False + + def reply_to_message( + self, + destination: ChannelWebhook, + message_context: ChannelWebhook, + message_body: MessageBody, + ) -> MessageSendResult[ChannelWebhook]: + raise MessageIntegrationReplyNotSupportedError( + "Teams webhook message integration does not support replying to messages" + ) From 420f155c0ac747c2e4cdfa302a7b4484827059e8 Mon Sep 17 00:00:00 2001 From: MikaKerman Date: Tue, 4 Feb 2025 17:47:32 +0200 Subject: [PATCH 4/6] Add comprehensive README for Elementary Messaging Integration System - Created detailed documentation explaining the new messaging integration architecture - Described key components like `BaseMessagingIntegration` and migration strategy - Outlined implementation guidelines for new messaging platform integrations - Highlighted current and future support for messaging platforms --- .../messages/messaging_integrations/README.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 elementary/messages/messaging_integrations/README.md diff --git a/elementary/messages/messaging_integrations/README.md b/elementary/messages/messaging_integrations/README.md new file mode 100644 index 000000000..869865da7 --- /dev/null +++ b/elementary/messages/messaging_integrations/README.md @@ -0,0 +1,58 @@ +# Elementary Messaging Integration System + +## Overview + +The Elementary Messaging Integration system provides a flexible and extensible framework for sending alerts and messages to various messaging platforms (e.g., Slack, Teams). The system is designed to support a gradual migration from the legacy integration system to a more generic messaging-based approach. + +## Architecture + +### BaseMessagingIntegration + +The core of the new messaging system is the `BaseMessagingIntegration` abstract class. This class defines the contract that all messaging integrations must follow: + +- `send_message()`: Send a message to a specific destination +- `supports_reply()`: Check if the integration supports message threading/replies +- `reply_to_message()`: Reply to an existing message (if supported) + +### Key Components + +1. **MessageBody**: A platform-agnostic representation of a message +2. **MessageSendResult**: Contains information about a sent message, including timestamp and platform-specific context +3. **DestinationType**: Generic type representing the destination for a message (e.g., webhook URL, channel) +4. **MessageContextType**: Generic type for platform-specific message context + +## Migration Strategy + +The system currently supports both: + +- Legacy `BaseIntegration` implementations (e.g., Slack) +- New `BaseMessagingIntegration` implementations (e.g., Teams) + +This dual support allows for a gradual migration path where: + +1. New integrations are implemented using `BaseMessagingIntegration` +2. Existing integrations can be migrated one at a time +3. The legacy `BaseIntegration` will eventually be deprecated + +## Implementing a New Integration + +To add a new messaging platform integration: + +1. Create a new class that extends `BaseMessagingIntegration` +2. Implement the required abstract methods: + ```python + def send_message(self, destination: DestinationType, message_body: MessageBody) -> MessageSendResult + def supports_reply(self) -> bool + def reply_to_message(self, destination, message_context, message_body) -> MessageSendResult # if supported + ``` +3. Update the `Integrations` factory class to support the new integration + +## Current Implementations + +- **Teams**: Uses the new `BaseMessagingIntegration` system with webhook support +- **Slack**: Currently uses the legacy `BaseIntegration` system (planned for migration) + +## Future Improvements + +1. Complete migration of Slack to `BaseMessagingIntegration` +2. Add support for more messaging platforms From e197ac6d321421d1a3aa60fa014cb722c48abe73 Mon Sep 17 00:00:00 2001 From: MikaKerman Date: Tue, 4 Feb 2025 17:50:48 +0200 Subject: [PATCH 5/6] Add support for new messaging integration in data monitoring alerts - Updated `DataMonitoringAlerts` to support both legacy and new messaging integrations - Implemented dual-path alert sending logic for BaseIntegration and BaseMessagingIntegration - Added `_send_message()` method to handle new messaging integration message sending - Created `get_health_check_message()` for test message support in new integrations - Updated `Integrations` class to support gradual migration to new messaging system - Added destination resolution method for messaging integrations --- .../alerts/data_monitoring_alerts.py | 97 +++++++++++++++++-- .../alerts/integrations/integrations.py | 37 +++++-- 2 files changed, 114 insertions(+), 20 deletions(-) diff --git a/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py b/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py index 2531926c4..6bf42d156 100644 --- a/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py +++ b/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py @@ -6,9 +6,19 @@ from alive_progress import alive_bar from elementary.config.config import Config +from elementary.messages.block_builders import TextLineBlock +from elementary.messages.blocks import HeaderBlock, LinesBlock +from elementary.messages.message_body import MessageBody +from elementary.messages.messaging_integrations.base_messaging_integration import ( + BaseMessagingIntegration, + MessageSendResult, +) +from elementary.messages.messaging_integrations.exceptions import ( + MessagingIntegrationError, +) +from elementary.monitor.alerts.alert_messages.builder import AlertMessageBuilder from elementary.monitor.alerts.alerts_groups import GroupedByTableAlerts from elementary.monitor.alerts.alerts_groups.alerts_group import AlertsGroup -from elementary.monitor.alerts.alerts_groups.base_alerts_group import BaseAlertsGroup from elementary.monitor.alerts.grouping_type import GroupingType from elementary.monitor.alerts.model_alert import ModelAlertModel from elementary.monitor.alerts.source_freshness_alert import SourceFreshnessAlertModel @@ -32,7 +42,26 @@ logger = get_logger(__name__) +def get_health_check_message() -> MessageBody: + return MessageBody( + blocks=[ + HeaderBlock(text="Elementary monitor ran successfully"), + LinesBlock( + lines=[ + TextLineBlock( + text=f"Elementary monitor ran successfully on {datetime.now().strftime('%Y-%m-%d %H:%M')}" + ), + ] + ), + ] + ) + + class DataMonitoringAlerts(DataMonitoring): + # The alerts_integration field now supports both the legacy BaseIntegration and the new BaseMessagingIntegration + # This dual support allows for a gradual migration from the old integration system to the new messaging system + alerts_integration: Union[BaseIntegration, BaseMessagingIntegration] + def __init__( self, config: Config, @@ -61,7 +90,9 @@ def __init__( self.override_config_defaults = override_config self.alerts_integration = self._get_integration_client() - def _get_integration_client(self) -> BaseIntegration: + def _get_integration_client( + self, + ) -> Union[BaseIntegration, BaseMessagingIntegration]: return Integrations.get_integration( config=self.config, tracking=self.tracking, @@ -183,7 +214,7 @@ def _format_alerts( ModelAlertModel, SourceFreshnessAlertModel, GroupedByTableAlerts, - BaseAlertsGroup, + AlertsGroup, ] ]: group_all_alerts = len(alerts) >= self.config.group_alerts_threshold # type: ignore[arg-type] @@ -247,11 +278,57 @@ def _format_alerts( key=lambda alert: alert.detected_at or datetime.max, ) - def _send_test_message(self): - self.alerts_integration.send_test_message( - channel_name=self.config.slack_channel_name + def _send_message( + self, integration: BaseMessagingIntegration, message_body: MessageBody + ) -> MessageSendResult: + destination = Integrations.get_destination( + integration=integration, config=self.config + ) + return integration.send_message( + destination=destination, message_body=message_body ) + def _send_test_message(self): + if isinstance(self.alerts_integration, BaseIntegration): + self.alerts_integration.send_test_message( + channel_name=self.config.slack_channel_name + ) + else: + test_message = get_health_check_message() + return self._send_message( + integration=self.alerts_integration, message_body=test_message + ) + + def _send_alert( + self, + alert: Union[ + TestAlertModel, + ModelAlertModel, + SourceFreshnessAlertModel, + GroupedByTableAlerts, + AlertsGroup, + ], + ): + # Support both legacy BaseIntegration and new BaseMessagingIntegration + # BaseIntegration will be deprecated in favor of BaseMessagingIntegration + if isinstance(self.alerts_integration, BaseIntegration): + return self.alerts_integration.send_alert(alert) + else: + # New messaging integration path - converts alerts to message bodies + alert_message_builder = AlertMessageBuilder() + alert_message_body = alert_message_builder.build( + alert=alert, + ) + try: + self._send_message( + integration=self.alerts_integration, + message_body=alert_message_body, + ) + return True + except MessagingIntegrationError: + logger.error(f"Could not send the alert - {type(alert)}.") + return False + def _send_alerts( self, alerts: List[ @@ -260,7 +337,7 @@ def _send_alerts( ModelAlertModel, SourceFreshnessAlertModel, GroupedByTableAlerts, - BaseAlertsGroup, + AlertsGroup, ] ], ): @@ -278,15 +355,15 @@ def _send_alerts( with alive_bar(len(alerts), title="Sending alerts") as bar: for alert in alerts: - sent_successfully = self.alerts_integration.send_alert(alert) + sent_successfully = self._send_alert(alert) bar() if sent_successfully: - if isinstance(alert, BaseAlertsGroup): + if isinstance(alert, AlertsGroup): sent_successfully_alerts.extend(alert.alerts) else: sent_successfully_alerts.append(alert) else: - if isinstance(alert, BaseAlertsGroup): + if isinstance(alert, AlertsGroup): for inner_alert in alert.alerts: logger.error( f"Could not send the alert - {inner_alert.id}. Full alert: {json.dumps(inner_alert.data)}" diff --git a/elementary/monitor/data_monitoring/alerts/integrations/integrations.py b/elementary/monitor/data_monitoring/alerts/integrations/integrations.py index 6f1f976d4..8a37b5bdf 100644 --- a/elementary/monitor/data_monitoring/alerts/integrations/integrations.py +++ b/elementary/monitor/data_monitoring/alerts/integrations/integrations.py @@ -1,16 +1,20 @@ -from typing import Optional +from typing import Any, Optional, Union from elementary.config.config import Config from elementary.exceptions.exceptions import Error +from elementary.messages.messaging_integrations.base_messaging_integration import ( + BaseMessagingIntegration, +) +from elementary.messages.messaging_integrations.teams_webhook import ( + ChannelWebhook, + TeamsWebhookMessagingIntegration, +) from elementary.monitor.data_monitoring.alerts.integrations.base_integration import ( BaseIntegration, ) from elementary.monitor.data_monitoring.alerts.integrations.slack.slack import ( SlackIntegration, ) -from elementary.monitor.data_monitoring.alerts.integrations.teams.teams import ( - TeamsIntegration, -) from elementary.tracking.tracking_interface import Tracking @@ -29,7 +33,11 @@ def get_integration( config: Config, tracking: Optional[Tracking] = None, override_config_defaults: bool = False, - ) -> BaseIntegration: + ) -> Union[BaseIntegration, BaseMessagingIntegration]: + # Factory method that returns either a legacy BaseIntegration or new BaseMessagingIntegration + # This allows for a gradual migration from the old integration system to the new messaging system + # - Slack currently uses the legacy BaseIntegration + # - Teams uses the new BaseMessagingIntegration if config.has_slack: return SlackIntegration( config=config, @@ -37,10 +45,19 @@ def get_integration( override_config_defaults=override_config_defaults, ) elif config.has_teams: - return TeamsIntegration( - config=config, - tracking=tracking, - override_config_defaults=override_config_defaults, - ) + return TeamsWebhookMessagingIntegration() else: raise UnsupportedAlertIntegrationError + + @staticmethod + def get_destination(integration: BaseMessagingIntegration, config: Config) -> Any: + # Helper method to get the appropriate destination for BaseMessagingIntegration implementations + # Each messaging integration type may have different destination requirements + # Currently supports Teams webhook destinations + if ( + isinstance(integration, TeamsWebhookMessagingIntegration) + and config.has_teams + and config.teams_webhook + ): + return ChannelWebhook(webhook=config.teams_webhook) + return None From 0fab5f50c8694fc68bb507ef7e681623ddeae01a Mon Sep 17 00:00:00 2001 From: MikaKerman Date: Thu, 6 Feb 2025 18:04:54 +0200 Subject: [PATCH 6/6] Rename message_body parameter to body in messaging integration methods - Updated parameter name from `message_body` to `body` in base messaging integration methods - Synchronized parameter naming across `BaseMessagingIntegration`, `TeamsWebhookMessagingIntegration`, and `DataMonitoringAlerts` - Simplified method signatures and improved consistency in messaging integration code - Updated README example to reflect new parameter naming convention --- elementary/messages/messaging_integrations/README.md | 2 +- .../base_messaging_integration.py | 4 ++-- .../messages/messaging_integrations/teams_webhook.py | 6 +++--- .../data_monitoring/alerts/data_monitoring_alerts.py | 10 ++++------ 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/elementary/messages/messaging_integrations/README.md b/elementary/messages/messaging_integrations/README.md index 869865da7..4fad946bf 100644 --- a/elementary/messages/messaging_integrations/README.md +++ b/elementary/messages/messaging_integrations/README.md @@ -41,7 +41,7 @@ To add a new messaging platform integration: 1. Create a new class that extends `BaseMessagingIntegration` 2. Implement the required abstract methods: ```python - def send_message(self, destination: DestinationType, message_body: MessageBody) -> MessageSendResult + def send_message(self, destination: DestinationType, body: MessageBody) -> MessageSendResult def supports_reply(self) -> bool def reply_to_message(self, destination, message_context, message_body) -> MessageSendResult # if supported ``` diff --git a/elementary/messages/messaging_integrations/base_messaging_integration.py b/elementary/messages/messaging_integrations/base_messaging_integration.py index 6e35a3104..169f18433 100644 --- a/elementary/messages/messaging_integrations/base_messaging_integration.py +++ b/elementary/messages/messaging_integrations/base_messaging_integration.py @@ -30,7 +30,7 @@ class BaseMessagingIntegration(ABC, Generic[DestinationType, MessageContextType] def send_message( self, destination: DestinationType, - message_body: MessageBody, + body: MessageBody, ) -> MessageSendResult[MessageContextType]: raise NotImplementedError @@ -42,7 +42,7 @@ def reply_to_message( self, destination: DestinationType, message_context: MessageContextType, - message_body: MessageBody, + body: MessageBody, ) -> MessageSendResult[MessageContextType]: if not self.supports_reply(): raise MessageIntegrationReplyNotSupportedError diff --git a/elementary/messages/messaging_integrations/teams_webhook.py b/elementary/messages/messaging_integrations/teams_webhook.py index 0f0ae1360..d1ced03b5 100644 --- a/elementary/messages/messaging_integrations/teams_webhook.py +++ b/elementary/messages/messaging_integrations/teams_webhook.py @@ -54,9 +54,9 @@ class TeamsWebhookMessagingIntegration( def send_message( self, destination: ChannelWebhook, - message_body: MessageBody, + body: MessageBody, ) -> MessageSendResult[ChannelWebhook]: - card = format_adaptive_card(message_body) + card = format_adaptive_card(body) try: send_adaptive_card(destination.webhook, card) return MessageSendResult( @@ -75,7 +75,7 @@ def reply_to_message( self, destination: ChannelWebhook, message_context: ChannelWebhook, - message_body: MessageBody, + body: MessageBody, ) -> MessageSendResult[ChannelWebhook]: raise MessageIntegrationReplyNotSupportedError( "Teams webhook message integration does not support replying to messages" diff --git a/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py b/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py index 6bf42d156..496dba5a8 100644 --- a/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py +++ b/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py @@ -279,14 +279,12 @@ def _format_alerts( ) def _send_message( - self, integration: BaseMessagingIntegration, message_body: MessageBody + self, integration: BaseMessagingIntegration, body: MessageBody ) -> MessageSendResult: destination = Integrations.get_destination( integration=integration, config=self.config ) - return integration.send_message( - destination=destination, message_body=message_body - ) + return integration.send_message(destination=destination, body=body) def _send_test_message(self): if isinstance(self.alerts_integration, BaseIntegration): @@ -296,7 +294,7 @@ def _send_test_message(self): else: test_message = get_health_check_message() return self._send_message( - integration=self.alerts_integration, message_body=test_message + integration=self.alerts_integration, body=test_message ) def _send_alert( @@ -322,7 +320,7 @@ def _send_alert( try: self._send_message( integration=self.alerts_integration, - message_body=alert_message_body, + body=alert_message_body, ) return True except MessagingIntegrationError: