diff --git a/backend/conftest.py b/backend/conftest.py
index 9958c992b3..a5eaf9192e 100644
--- a/backend/conftest.py
+++ b/backend/conftest.py
@@ -75,22 +75,6 @@ def change_azure_account_to_test_name(settings):
settings.AZURE_STORAGE_ACCOUNT_NAME = "pytest-fakestorageaccount"
-class TestEmailBackend:
- ALL_EMAIL_BACKEND_CALLS = []
-
- def __init__(self, *args, **kwargs) -> None:
- pass
-
- def send_email(self, **kwargs):
- TestEmailBackend.ALL_EMAIL_BACKEND_CALLS.append(kwargs)
-
-
-@pytest.fixture
-def sent_emails():
- TestEmailBackend.ALL_EMAIL_BACKEND_CALLS = []
- yield TestEmailBackend.ALL_EMAIL_BACKEND_CALLS
-
-
@pytest.fixture
def image_file():
def wrapper(filename: str = "test.jpg"):
diff --git a/backend/notifications/aws.py b/backend/notifications/aws.py
deleted file mode 100644
index 716fc369a3..0000000000
--- a/backend/notifications/aws.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import typing
-
-import boto3
-from django.conf import settings
-
-from newsletters.exporter import Endpoint
-from users.models import User
-
-
-def _get_client():
- return boto3.client("pinpoint", region_name="eu-central-1")
-
-
-def chunks(arr, n):
- for i in range(0, len(arr), n):
- yield arr[i : i + n] # noqa
-
-
-def send_endpoints_to_pinpoint(endpoints: typing.Iterable[Endpoint]):
- # batch only supports 100 at the time
- endpoint_chunks = chunks(list(endpoints), 100)
-
- for endpoints_chunk in endpoint_chunks:
- data = {"Item": [endpoint.to_item() for endpoint in endpoints_chunk]}
-
- client = _get_client()
- client.update_endpoints_batch(
- ApplicationId=settings.PINPOINT_APPLICATION_ID, EndpointBatchRequest=data
- )
-
-
-def send_notification(
- template_name: str,
- users: typing.List[User],
- substitutions: typing.Dict[str, typing.List[str]],
-):
- client = _get_client()
- client.send_users_messages(
- ApplicationId=settings.PINPOINT_APPLICATION_ID,
- SendUsersMessageRequest={
- "MessageConfiguration": {
- "EmailMessage": {
- "FromAddress": "noreply@pycon.it",
- "Substitutions": substitutions,
- }
- },
- "TemplateConfiguration": {"EmailTemplate": {"Name": template_name}},
- "Users": {str(user.id): {} for user in users},
- },
- )
-
- # TODO: validate that it has been sent correctly
diff --git a/backend/notifications/backends/__init__.py b/backend/notifications/backends/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/backend/notifications/backends/base.py b/backend/notifications/backends/base.py
deleted file mode 100644
index 8961ce6a66..0000000000
--- a/backend/notifications/backends/base.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Dict, List, Optional
-
-from notifications.templates import EmailTemplate
-
-
-class EmailBackend:
- def __init__(self, environment: Optional[str] = None) -> None:
- self.environment = environment
-
- def send_email(
- self,
- *,
- template: EmailTemplate,
- subject: str,
- from_: str,
- to: str,
- variables: Optional[Dict[str, str]] = None,
- reply_to: List[str] = None,
- ) -> str:
- raise NotImplementedError()
diff --git a/backend/notifications/backends/local.py b/backend/notifications/backends/local.py
deleted file mode 100644
index 3a1319a8aa..0000000000
--- a/backend/notifications/backends/local.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from typing import Dict, List, Optional
-from uuid import uuid4
-
-from notifications.templates import EmailTemplate
-
-from notifications.backends.base import EmailBackend
-
-
-class LocalEmailBackend(EmailBackend):
- def __init__(self, environment: Optional[str] = None) -> None:
- super().__init__(environment=environment)
-
- def send_email(
- self,
- *,
- template: EmailTemplate,
- subject: str,
- from_: str,
- to: str,
- variables: Optional[Dict[str, str]] = None,
- reply_to: List[str] = None,
- ) -> str:
- reply_to = reply_to or []
-
- print("=== Email sending ===")
- print(f"Template: {template}")
- print(f"From: {from_}")
- print(f"To: {to}")
- print(f"Subject: {subject}")
- print(f"Variables: {str(variables)}")
- print(f"Reply to: {str(reply_to)}")
- print("=== End Email sending ===")
-
- return f"messageid-{uuid4()}"
diff --git a/backend/notifications/backends/ses.py b/backend/notifications/backends/ses.py
deleted file mode 100644
index c4a7e961c0..0000000000
--- a/backend/notifications/backends/ses.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import json
-from typing import Any, Dict, List, Optional
-import html
-import boto3
-from notifications.templates import EmailTemplate
-from notifications.emails import SafeString
-from notifications.backends.base import EmailBackend
-
-
-class SESEmailBackend(EmailBackend):
- def __init__(self, environment: str) -> None:
- super().__init__(environment)
- self.ses = boto3.client("ses")
-
- def send_email(
- self,
- *,
- template: EmailTemplate,
- subject: str,
- from_: str,
- to: str,
- variables: Optional[Dict[str, str]] = None,
- reply_to: List[str] = None,
- ) -> str:
- reply_to = reply_to or []
-
- variables = self.encode_vars({"subject": subject, **(variables or {})})
- response = self.ses.send_templated_email(
- Source=from_,
- Destination={"ToAddresses": [to]},
- Template=f"pythonit-{self.environment}-{template}",
- TemplateData=json.dumps(variables),
- ReplyToAddresses=reply_to,
- ConfigurationSetName="primary",
- )
- return response["MessageId"]
-
- def encode_vars(self, variables: dict[str, Any]) -> dict[str, Any]:
- vars = dict()
-
- for key, value in variables.items():
- if isinstance(value, str) and not isinstance(value, SafeString):
- value = html.escape(value)
-
- vars[key] = value
-
- return vars
diff --git a/backend/notifications/backends/tests/__init__.py b/backend/notifications/backends/tests/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/backend/notifications/backends/tests/test_base.py b/backend/notifications/backends/tests/test_base.py
deleted file mode 100644
index 34521e1996..0000000000
--- a/backend/notifications/backends/tests/test_base.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import pytest
-from notifications.backends.base import EmailBackend
-
-
-def test_base_backend():
- base = EmailBackend()
- with pytest.raises(NotImplementedError):
- base.send_email(
- template="template",
- subject="subject",
- from_="from",
- to="to",
- variables={"key": "value"},
- reply_to=["reply"],
- )
diff --git a/backend/notifications/backends/tests/test_local.py b/backend/notifications/backends/tests/test_local.py
deleted file mode 100644
index efba1c99b9..0000000000
--- a/backend/notifications/backends/tests/test_local.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from unittest.mock import patch
-
-from notifications.backends.local import LocalEmailBackend
-from notifications.templates import EmailTemplate
-
-
-def test_local_email_just_logs():
- with patch("notifications.backends.local.print") as mock_logger:
- LocalEmailBackend().send_email(
- template=EmailTemplate.RESET_PASSWORD,
- from_="from@from.it",
- to="hello@world.it",
- subject="Hello World!",
- variables={"a": "b", "c": "d"},
- reply_to=["test@placeholder.com"],
- )
-
- assert mock_logger.call_count == 8
- mock_logger.assert_any_call("=== Email sending ===")
- mock_logger.assert_any_call(f"Template: {str(EmailTemplate.RESET_PASSWORD)}")
- mock_logger.assert_any_call("From: from@from.it")
- mock_logger.assert_any_call("To: hello@world.it")
- mock_logger.assert_any_call("Subject: Hello World!")
- mock_logger.assert_any_call("Variables: {'a': 'b', 'c': 'd'}")
- mock_logger.assert_any_call("Reply to: ['test@placeholder.com']")
- mock_logger.assert_any_call("=== End Email sending ===")
diff --git a/backend/notifications/backends/tests/test_ses.py b/backend/notifications/backends/tests/test_ses.py
deleted file mode 100644
index a9a4a77796..0000000000
--- a/backend/notifications/backends/tests/test_ses.py
+++ /dev/null
@@ -1,142 +0,0 @@
-from unittest.mock import patch
-from notifications.emails import mark_safe
-from notifications.backends.ses import SESEmailBackend
-from notifications.templates import EmailTemplate
-
-
-def test_send_email_via_ses():
- with patch("notifications.backends.ses.boto3") as mock_boto:
- mock_boto.client.return_value.send_templated_email.return_value = {
- "MessageId": "msg-id-123"
- }
- message_id = SESEmailBackend("production").send_email(
- template=EmailTemplate.RESET_PASSWORD,
- subject="Subject",
- from_="test@email.it",
- to="destination@email.it",
- variables={"a": "b", "c": "d"},
- )
-
- assert message_id == "msg-id-123"
-
- mock_boto.client.return_value.send_templated_email.assert_called_once_with(
- Source="test@email.it",
- Destination={"ToAddresses": ["destination@email.it"]},
- Template="pythonit-production-reset-password",
- TemplateData='{"subject": "Subject", "a": "b", "c": "d"}',
- ReplyToAddresses=[],
- ConfigurationSetName="primary",
- )
-
-
-def test_send_email_without_variables():
- with patch("notifications.backends.ses.boto3") as mock_boto:
- SESEmailBackend("production").send_email(
- template=EmailTemplate.RESET_PASSWORD,
- subject="Subject",
- from_="test@email.it",
- to="destination@email.it",
- )
-
- mock_boto.client.return_value.send_templated_email.assert_called_once_with(
- Source="test@email.it",
- Destination={"ToAddresses": ["destination@email.it"]},
- Template="pythonit-production-reset-password",
- TemplateData='{"subject": "Subject"}',
- ReplyToAddresses=[],
- ConfigurationSetName="primary",
- )
-
-
-def test_send_email_with_reply_to():
- with patch("notifications.backends.ses.boto3") as mock_boto:
- SESEmailBackend("production").send_email(
- template=EmailTemplate.RESET_PASSWORD,
- subject="Subject",
- from_="test@email.it",
- to="destination@email.it",
- reply_to=[
- "test1@placeholder.com",
- "test2@placeholder.com",
- ],
- )
-
- mock_boto.client.return_value.send_templated_email.assert_called_once_with(
- Source="test@email.it",
- Destination={"ToAddresses": ["destination@email.it"]},
- Template="pythonit-production-reset-password",
- TemplateData='{"subject": "Subject"}',
- ReplyToAddresses=[
- "test1@placeholder.com",
- "test2@placeholder.com",
- ],
- ConfigurationSetName="primary",
- )
-
-
-def test_variables_are_html_encoded():
- with patch("notifications.backends.ses.boto3") as mock_boto:
- SESEmailBackend("production").send_email(
- template=EmailTemplate.RESET_PASSWORD,
- subject="Subject",
- from_="test@email.it",
- to="destination@email.it",
- variables={
- "a": 'link',
- },
- )
-
- mock_boto.client.return_value.send_templated_email.assert_called_once_with(
- Source="test@email.it",
- Destination={"ToAddresses": ["destination@email.it"]},
- Template="pythonit-production-reset-password",
- TemplateData='{"subject": "Subject", "a": "<a href="https://google.it">link</a>"}',
- ReplyToAddresses=[],
- ConfigurationSetName="primary",
- )
-
-
-def test_safe_string_variables_are_not_encoded():
- with patch("notifications.backends.ses.boto3") as mock_boto:
- SESEmailBackend("production").send_email(
- template=EmailTemplate.RESET_PASSWORD,
- subject="Subject",
- from_="test@email.it",
- to="destination@email.it",
- variables={
- "safe": mark_safe('link'),
- "not_safe": 'link',
- },
- )
-
- mock_boto.client.return_value.send_templated_email.assert_called_once_with(
- Source="test@email.it",
- Destination={"ToAddresses": ["destination@email.it"]},
- Template="pythonit-production-reset-password",
- TemplateData='{"subject": "Subject", "safe": "link", "not_safe": "<a href="https://google.it">link</a>"}',
- ReplyToAddresses=[],
- ConfigurationSetName="primary",
- )
-
-
-def test_non_string_variables_are_not_encoded():
- with patch("notifications.backends.ses.boto3") as mock_boto:
- SESEmailBackend("production").send_email(
- template=EmailTemplate.RESET_PASSWORD,
- subject="Subject",
- from_="test@email.it",
- to="destination@email.it",
- variables={
- "value": 1,
- "boolean": False,
- },
- )
-
- mock_boto.client.return_value.send_templated_email.assert_called_once_with(
- Source="test@email.it",
- Destination={"ToAddresses": ["destination@email.it"]},
- Template="pythonit-production-reset-password",
- TemplateData='{"subject": "Subject", "value": 1, "boolean": false}',
- ReplyToAddresses=[],
- ConfigurationSetName="primary",
- )
diff --git a/backend/notifications/emails.py b/backend/notifications/emails.py
deleted file mode 100644
index 220f4c0d95..0000000000
--- a/backend/notifications/emails.py
+++ /dev/null
@@ -1,65 +0,0 @@
-from typing import List, Optional
-
-from django.conf import settings
-from notifications.templates import EmailTemplate
-
-
-from dataclasses import dataclass
-import importlib
-
-from .backends.base import EmailBackend
-
-EMAIL_BACKEND_CACHE: dict[str, EmailBackend] = {}
-
-
-def get_email_backend(backend_path: str, **options: dict[str, str]) -> EmailBackend:
- global EMAIL_BACKEND_CACHE
-
- instance = EMAIL_BACKEND_CACHE.get(backend_path, None)
-
- if not instance:
- module_path, class_name = backend_path.rsplit(".", 1)
- module = importlib.import_module(module_path)
- class_impl = getattr(module, class_name)
- instance = class_impl(**options)
- EMAIL_BACKEND_CACHE[backend_path] = instance
-
- return instance
-
-
-@dataclass
-class SafeString(str):
- original_str: str
-
- def __str__(self) -> str:
- return self.original_str
-
-
-def mark_safe(string: str) -> SafeString:
- if string is None:
- raise ValueError("string cannot be None")
-
- return SafeString(string)
-
-
-def send_email(
- *,
- template: EmailTemplate,
- to: str,
- subject: str,
- from_: Optional[str] = None,
- variables: Optional[dict[str, str]] = None,
- reply_to: List[str] = None,
-):
- from_ = from_ or settings.DEFAULT_FROM_EMAIL
- backend = get_email_backend(
- settings.PYTHONIT_EMAIL_BACKEND, environment=settings.ENVIRONMENT
- )
- backend.send_email(
- template=template,
- from_=from_,
- to=to,
- subject=subject,
- variables=variables,
- reply_to=reply_to,
- )
diff --git a/backend/notifications/templates.py b/backend/notifications/templates.py
deleted file mode 100644
index f482e79779..0000000000
--- a/backend/notifications/templates.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from enum import Enum
-
-
-class EmailTemplate(str, Enum):
- # Users
- RESET_PASSWORD = "reset-password"
-
- def __str__(self) -> str:
- return str.__str__(self)
diff --git a/backend/notifications/tests/test_emails.py b/backend/notifications/tests/test_emails.py
deleted file mode 100644
index 4f27468508..0000000000
--- a/backend/notifications/tests/test_emails.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from unittest.mock import patch
-
-from notifications.emails import SafeString, get_email_backend, mark_safe
-import pytest
-
-
-class FakeBackend:
- pass
-
-
-class FakeBackendWithEnv:
- def __init__(self, a: int, b: int) -> None:
- self.a = a
- self.b = b
-
-
-def test_get_email_backend():
- loaded_backend = get_email_backend("notifications.tests.test_emails.FakeBackend")
- assert "notifications.tests.test_emails.FakeBackend" in str(type(loaded_backend))
-
-
-def test_loading_same_backend_uses_cache():
- loaded_backend = get_email_backend("notifications.tests.test_emails.FakeBackend")
- assert "notifications.tests.test_emails.FakeBackend" in str(type(loaded_backend))
-
- with patch("notifications.emails.importlib.import_module") as mock:
- loaded_backend = get_email_backend(
- "notifications.tests.test_emails.FakeBackend"
- )
-
- assert "notifications.tests.test_emails.FakeBackend" in str(type(loaded_backend))
- assert not mock.called
-
- mock.reset_mock()
-
- with patch("notifications.emails.importlib.import_module") as mock:
- get_email_backend("notifications.tests.test_emails.FakeBackend2")
-
- assert mock.called
-
-
-def test_get_email_backend_with_envs():
- loaded_backend = get_email_backend(
- "notifications.tests.test_emails.FakeBackendWithEnv", a=1, b=2
- )
- assert loaded_backend.a == 1
- assert loaded_backend.b == 2
-
-
-def test_mark_safe():
- safe_string = mark_safe("abc")
-
- assert isinstance(safe_string, SafeString)
- assert str(safe_string) == "abc"
-
-
-def test_mark_safe_empty_string():
- safe_string = mark_safe("")
-
- assert isinstance(safe_string, SafeString)
- assert str(safe_string) == ""
-
-
-def test_mark_safe_with_none_fails():
- with pytest.raises(ValueError):
- mark_safe(None)
diff --git a/backend/pycon/settings/base.py b/backend/pycon/settings/base.py
index 9a5b505b40..4e299a6a6b 100644
--- a/backend/pycon/settings/base.py
+++ b/backend/pycon/settings/base.py
@@ -286,19 +286,12 @@
QUERY_INSPECT_LOG_TRACEBACKS = True
QUERY_INSPECT_TRACEBACK_ROOTS = [root(".")]
-PINPOINT_APPLICATION_ID = env("PINPOINT_APPLICATION_ID", default="")
-
MAILCHIMP_SECRET_KEY = env("MAILCHIMP_SECRET_KEY", default="")
MAILCHIMP_DC = env("MAILCHIMP_DC", default="us3")
MAILCHIMP_LIST_ID = env("MAILCHIMP_LIST_ID", default="")
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
-PYTHONIT_EMAIL_BACKEND = env(
- "PYTHONIT_EMAIL_BACKEND",
- default="notifications.backends.local.LocalEmailBackend",
-)
-
SPEAKERS_EMAIL_ADDRESS = env("SPEAKERS_EMAIL_ADDRESS", default="")
VOLUNTEERS_PUSH_NOTIFICATIONS_IOS_ARN = env(
diff --git a/backend/pycon/settings/test.py b/backend/pycon/settings/test.py
index bad943d26e..3d1fb00529 100644
--- a/backend/pycon/settings/test.py
+++ b/backend/pycon/settings/test.py
@@ -34,7 +34,6 @@
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
}
}
-PYTHONIT_EMAIL_BACKEND = "conftest.TestEmailBackend"
CELERY_TASK_ALWAYS_EAGER = True
CELERY_TASK_EAGER_PROPAGATES = True
diff --git a/infrastructure/applications/pycon_backend/main.tf b/infrastructure/applications/pycon_backend/main.tf
index 60fbb125bc..663b7b5130 100644
--- a/infrastructure/applications/pycon_backend/main.tf
+++ b/infrastructure/applications/pycon_backend/main.tf
@@ -94,12 +94,10 @@ module "lambda" {
AWS_REGION_NAME = aws_s3_bucket.backend_media.region
SPEAKERS_EMAIL_ADDRESS = module.secrets.value.speakers_email_address
EMAIL_BACKEND = "django_ses.SESBackend"
- PYTHONIT_EMAIL_BACKEND = "notifications.backends.ses.SESEmailBackend"
FRONTEND_URL = "https://pycon.it"
PRETIX_API = "https://tickets.pycon.it/api/v1/"
AWS_S3_CUSTOM_DOMAIN = local.cdn_url
PRETIX_API_TOKEN = module.common_secrets.value.pretix_api_token
- PINPOINT_APPLICATION_ID = module.secrets.value.pinpoint_application_id
MAILCHIMP_SECRET_KEY = module.common_secrets.value.mailchimp_secret_key
MAILCHIMP_DC = module.common_secrets.value.mailchimp_dc
MAILCHIMP_LIST_ID = module.common_secrets.value.mailchimp_list_id
diff --git a/infrastructure/applications/pycon_backend/worker.tf b/infrastructure/applications/pycon_backend/worker.tf
index 718c060f01..8746f573ac 100644
--- a/infrastructure/applications/pycon_backend/worker.tf
+++ b/infrastructure/applications/pycon_backend/worker.tf
@@ -60,10 +60,6 @@ locals {
name = "EMAIL_BACKEND",
value = "django_ses.SESBackend"
},
- {
- name = "PYTHONIT_EMAIL_BACKEND",
- value = "notifications.backends.ses.SESEmailBackend"
- },
{
name = "FRONTEND_URL",
value = "https://pycon.it"
@@ -80,10 +76,6 @@ locals {
name = "PRETIX_API_TOKEN",
value = module.common_secrets.value.pretix_api_token
},
- {
- name = "PINPOINT_APPLICATION_ID",
- value = module.secrets.value.pinpoint_application_id
- },
{
name = "MAILCHIMP_SECRET_KEY",
value = module.common_secrets.value.mailchimp_secret_key