diff --git a/elementary/messages/messaging_integrations/base_messaging_integration.py b/elementary/messages/messaging_integrations/base_messaging_integration.py index 0bc118fd0..888234ce8 100644 --- a/elementary/messages/messaging_integrations/base_messaging_integration.py +++ b/elementary/messages/messaging_integrations/base_messaging_integration.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod from datetime import datetime -from typing import Generic, Optional, TypeVar +from typing import Any, Generic, Optional, TypeVar from pydantic import BaseModel @@ -27,6 +27,10 @@ class MessageSendResult(BaseModel, Generic[T]): class BaseMessagingIntegration(ABC, Generic[DestinationType, MessageContextType]): + @abstractmethod + def parse_message_context(self, context: dict[str, Any]) -> MessageContextType: + raise NotImplementedError + @abstractmethod def send_message( self, diff --git a/elementary/messages/messaging_integrations/file_system.py b/elementary/messages/messaging_integrations/file_system.py index 173f3e9b5..2fa1f0332 100644 --- a/elementary/messages/messaging_integrations/file_system.py +++ b/elementary/messages/messaging_integrations/file_system.py @@ -1,6 +1,7 @@ import json from datetime import datetime from pathlib import Path +from typing import Any from elementary.messages.message_body import MessageBody from elementary.messages.messaging_integrations.base_messaging_integration import ( @@ -36,6 +37,9 @@ def __init__(self, directory: str, create_if_missing: bool = True) -> None: f"Directory {self.directory} does not exist and create_if_missing is False" ) + def parse_message_context(self, context: dict[str, Any]) -> EmptyMessageContext: + return EmptyMessageContext(**context) + def supports_reply(self) -> bool: return False diff --git a/elementary/messages/messaging_integrations/mapped.py b/elementary/messages/messaging_integrations/mapped.py index cd0e247e3..83e89e1bb 100644 --- a/elementary/messages/messaging_integrations/mapped.py +++ b/elementary/messages/messaging_integrations/mapped.py @@ -1,4 +1,4 @@ -from typing import Generic, Mapping +from typing import Any, Generic, Mapping from elementary.messages.message_body import MessageBody from elementary.messages.messaging_integrations.base_messaging_integration import ( @@ -18,8 +18,14 @@ class MappedMessagingIntegration( def __init__( self, mapping: Mapping[str, BaseMessagingIntegration[None, MessageContextType]] ): + if len(mapping) == 0: + raise MessagingIntegrationError("Mapping cannot be empty") self._mapping = mapping + def parse_message_context(self, context: dict[str, Any]) -> MessageContextType: + sample_integration = next(iter(self._mapping.values())) + return sample_integration.parse_message_context(context) + def send_message( self, destination: str, body: MessageBody ) -> MessageSendResult[MessageContextType]: diff --git a/elementary/messages/messaging_integrations/slack_web.py b/elementary/messages/messaging_integrations/slack_web.py index 721b43c2c..17cb5955e 100644 --- a/elementary/messages/messaging_integrations/slack_web.py +++ b/elementary/messages/messaging_integrations/slack_web.py @@ -1,5 +1,5 @@ import json -from typing import Dict, Iterator, Optional +from typing import Any, Dict, Iterator, Optional from pydantic import BaseModel from ratelimit import limits, sleep_and_retry @@ -53,6 +53,9 @@ def from_token( client.retry_handlers.append(RateLimitErrorRetryHandler(max_retry_count=5)) return cls(client, tracking) + def parse_message_context(self, context: dict[str, Any]) -> SlackWebMessageContext: + return SlackWebMessageContext(**context) + def supports_reply(self) -> bool: return True diff --git a/elementary/messages/messaging_integrations/slack_webhook.py b/elementary/messages/messaging_integrations/slack_webhook.py index fdcbd59b9..32624f0b2 100644 --- a/elementary/messages/messaging_integrations/slack_webhook.py +++ b/elementary/messages/messaging_integrations/slack_webhook.py @@ -1,6 +1,6 @@ from datetime import datetime from http import HTTPStatus -from typing import Optional +from typing import Any, Optional from ratelimit import limits, sleep_and_retry from slack_sdk import WebhookClient @@ -43,6 +43,9 @@ def from_url( client.retry_handlers.append(RateLimitErrorRetryHandler(max_retry_count=5)) return cls(client, tracking) + def parse_message_context(self, context: dict[str, Any]) -> EmptyMessageContext: + return EmptyMessageContext(**context) + @sleep_and_retry @limits(calls=1, period=ONE_SECOND) def _send_message(self, formatted_message: FormattedBlockKitMessage) -> None: diff --git a/elementary/messages/messaging_integrations/teams_webhook.py b/elementary/messages/messaging_integrations/teams_webhook.py index 5a644c667..2b038bcf6 100644 --- a/elementary/messages/messaging_integrations/teams_webhook.py +++ b/elementary/messages/messaging_integrations/teams_webhook.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Optional +from typing import Any, Optional import requests from typing_extensions import TypeAlias @@ -53,6 +53,9 @@ class TeamsWebhookMessagingIntegration( def __init__(self, url: str) -> None: self.url = url + def parse_message_context(self, context: dict[str, Any]) -> EmptyMessageContext: + return EmptyMessageContext(**context) + def send_message( self, destination: None, diff --git a/tests/unit/messages/messaging_integrations/test_mapped.py b/tests/unit/messages/messaging_integrations/test_mapped.py index a4375aa21..f3fb43085 100644 --- a/tests/unit/messages/messaging_integrations/test_mapped.py +++ b/tests/unit/messages/messaging_integrations/test_mapped.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import Dict, List +from typing import Any, Dict, List from unittest.mock import MagicMock import pytest @@ -38,6 +38,9 @@ def __init__(self, supports_reply: bool = True, supports_actions: bool = False): message_context=MockMessageContext(id="test_id"), ) + def parse_message_context(self, context: dict[str, Any]) -> MockMessageContext: + return MockMessageContext(**context) + def send_message( self, destination: None, body: MessageBody ) -> MessageSendResult[MockMessageContext]: