Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#687 AWS Sender Email API #144

Merged
merged 11 commits into from
Oct 13, 2023
117 changes: 110 additions & 7 deletions django_project_base/base/event.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import copy
import datetime
from abc import ABC, abstractmethod

import swapper
from django.conf import settings
from django.shortcuts import get_object_or_404

from django_project_base.constants import EMAIL_SENDER_ID_SETTING_NAME
from django_project_base.constants import EMAIL_SENDER_ID_SETTING_NAME, SMS_SENDER_ID_SETTING_NAME


class UserModel:
Expand Down Expand Up @@ -44,16 +45,43 @@ def trigger_changed(self, old_state=None, new_state=None, payload=None, **kwargs
super().trigger_changed(old_state, new_state, payload, **kwargs)

if new_state.name == EMAIL_SENDER_ID_SETTING_NAME:
# TODO: THIS IS NOLY FOR AWS FOR NOW
# TODO: THIS IS ONLY FOR AWS FOR NOW
from django_project_base.aws.ses import AwsSes

if not old_state:
AwsSes.add_sender_email(new_state.python_value)
return
if old_state.python_value != new_state.python_value:
AwsSes.remove_sender_email(old_state.python_value) if old_state.python_value else None
AwsSes.add_sender_email(new_state.python_value)
return
elif (old_state.python_value != new_state.python_value) or (
new_state.python_value
and new_state.pending_value
and new_state.python_pending_value != new_state.python_value
):
AwsSes.add_sender_email(new_state.pending_value)

project_settings_manager = swapper.load_model("django_project_base", "ProjectSettings").objects
for sender in set(AwsSes.list_sender_emails()) - (
set(
project_settings_manager.objects.filter(name=EMAIL_SENDER_ID_SETTING_NAME).values_list(
"value", flat=True
)
)
| set(
project_settings_manager.objects.filter(name=EMAIL_SENDER_ID_SETTING_NAME).values_list(
"pending_value", flat=True
)
)
):
AwsSes.remove_sender_email(sender)


class SmsSenderChangedEvent(ProjectSettingChangedEvent):
def trigger(self, payload=None, **kwargs):
super().trigger(payload, **kwargs)

def trigger_changed(self, old_state=None, new_state=None, payload=None, **kwargs):
super().trigger_changed(old_state, new_state, payload, **kwargs)

if new_state.name == SMS_SENDER_ID_SETTING_NAME:
pass


class UserInviteFoundEvent(BaseEvent):
Expand Down Expand Up @@ -114,3 +142,78 @@ def trigger(self, payload=None, **kwargs):
UserInviteFoundEvent(self.user).trigger(payload=invite, request=payload)
return
payload.session.pop("invite-pk", None)


class ProjectSettingConfirmedEvent(BaseEvent):
def trigger_changed(self, old_state=None, new_state=None, payload=None, **kwargs):
super().trigger_changed(old_state=old_state, new_state=new_state, payload=payload, **kwargs)

def trigger(self, payload=None, **kwargs):
super().trigger(payload, **kwargs)
if not payload:
return
from django_project_base.aws.ses import AwsSes

def confirm(item):
item.value = copy.copy(item.python_pending_value)
item.pending_value = None
item.save(update_fields=["value", "pending_value"])

# not self.user Event is trigerred from management command
if payload.name == EMAIL_SENDER_ID_SETTING_NAME and (
payload.python_pending_value in AwsSes.list_verified_sender_emails() or not self.user
):
confirm(payload)
if payload.name == SMS_SENDER_ID_SETTING_NAME:
if not self.user:
confirm(payload)


class ProjectSettingActionRequiredEvent(BaseEvent):
def trigger_changed(self, old_state=None, new_state=None, payload=None, **kwargs):
super().trigger_changed(old_state=old_state, new_state=new_state, payload=payload, **kwargs)

def trigger(self, payload=None, **kwargs):
super().trigger(payload, **kwargs)
if not payload:
return

# if to := getattr(settings, "ADMINS", getattr(settings, "MANAGERS", [])):
# # TODO: SEND THIS AS SYSTEM MSG WHEN PR IS MERGED
# EMailNotificationWithListOfEmails(
# message=DjangoProjectBaseMessage(
# subject=gettext"Project settings action required"),
# body=f"{gettext('Action required for setting')} {payload.name} in project {payload.project.name}",
# footer="",
# content_type=DjangoProjectBaseMessage.HTML,
# ),
# recipients=to,
# project=None,
# user=None,
# ).send()


class ProjectSettingPendingResetEvent(BaseEvent):
def trigger_changed(self, old_state=None, new_state=None, payload=None, **kwargs):
super().trigger_changed(old_state=old_state, new_state=new_state, payload=payload, **kwargs)

def trigger(self, payload=None, **kwargs):
super().trigger(payload, **kwargs)
if not payload:
return
from django_project_base.aws.ses import AwsSes

pending_value = copy.copy(payload.python_pending_value)
payload.pending_value = None
payload.save(update_fields=["pending_value"])

if payload.name == EMAIL_SENDER_ID_SETTING_NAME:
if pending_value in AwsSes.list_sender_emails():
AwsSes.remove_sender_email(pending_value)
AwsSes.add_sender_email(payload.python_value)
if payload.python_value not in AwsSes.list_verified_sender_emails():
payload.action_required = True
payload.save(update_fields=["action_required"])
if payload.name == SMS_SENDER_ID_SETTING_NAME:
payload.action_required = True
payload.save(update_fields=["action_required"])
13 changes: 13 additions & 0 deletions django_project_base/base/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ class BaseProjectSettings(models.Model):
def python_value(self):
return self.value_validators[self.value_type](self.value)

@property
def python_pending_value(self):
return self.value_validators[self.value_type](self.pending_value)

def clean(self):
validator = self.value_validators[self.value_type]
try:
Expand All @@ -276,10 +280,19 @@ def clean(self):
swapper.get_model_name("django_project_base", "Project"), on_delete=models.CASCADE, null=False
)

pending_value = models.CharField(max_length=320, null=True, blank=True, verbose_name=_("Pending value"))
action_required = models.BooleanField(default=False, null=True, blank=True)

def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.full_clean()
validator = self.value_validators[self.value_type]
self.value = validator(self.value)
if self.pending_value:
self.pending_value = validator(self.pending_value)
if self.action_required:
from django_project_base.base.event import ProjectSettingActionRequiredEvent

ProjectSettingActionRequiredEvent(user=None).trigger(payload=self)
super().save(force_insert, force_update, using, update_fields)

def delete(self, using=None, keep_parents=False):
Expand Down
Empty file.
Empty file.
36 changes: 36 additions & 0 deletions django_project_base/management/commands/confirm_setting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import swapper
from django.core.management.base import BaseCommand
from django.shortcuts import get_object_or_404

from django_project_base.base.event import ProjectSettingConfirmedEvent


class Command(BaseCommand):
help = "Confirms project setting. Example: python manage.py confirm_setting 2 email-sender-id"

def add_arguments(self, parser) -> None:
parser.add_argument("project-id", type=str, help="Project identifier")
parser.add_argument("setting-name", type=str, help="Setting name")

def handle(self, *args, **options):
project = get_object_or_404(swapper.load_model("django_project_base", "Project"), pk=str(options["project-id"]))
setting = get_object_or_404(
swapper.load_model("django_project_base", "ProjectSettings"),
project=project,
name=str(options["setting-name"]),
)
ProjectSettingConfirmedEvent(user=None).trigger(payload=setting)
# TODO: send email when owner is known
# # TODO: SEND THIS AS SYSTEM MSG WHEN PR IS MERGED
# SystemEMailNotification(
# message=DjangoProjectBaseMessage(
# subject=f"{__('Project setting confirmed')}",
# body=f"{__('Setting')} {setting.name} {__('in project')} "
# f"{project.name} {__('has been confirmed and is now active.')}",
# footer="",
# content_type=DjangoProjectBaseMessage.PLAIN_TEXT,
# ),
# recipients=[], # TODO: find project owner
# user=None, # TODO: find project owner
# ).send()
return "ok"
35 changes: 35 additions & 0 deletions django_project_base/management/commands/list_pending_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import swapper
from django.core.management.base import BaseCommand


class Command(BaseCommand):
help = "Lists pending project settings. Example: python manage.py list_pending_settings"

def handle(self, *args, **options):
result = dict()
for project in swapper.load_model("django_project_base", "Project").objects.all():
for setting in (
swapper.load_model("django_project_base", "ProjectSettings")
.objects.filter(project=project, pending_value__isnull=False)
.exclude(pending_value="")
):
if project.name not in result:
result[project.name] = {}
result[project.name][setting.name] = {
"value": setting.python_value,
"pending_value": setting.python_pending_value,
}
# if to := getattr(settings, "ADMINS", getattr(settings, "MANAGERS", [])):
# # TODO: SEND THIS AS SYSTEM MSG WHEN PR IS MERGED
# EMailNotificationWithListOfEmails(
# message=DjangoProjectBaseMessage(
# subject=_("Pending settings report"),
# body=json.dumps(result),
# footer="",
# content_type=DjangoProjectBaseMessage.HTML,
# ),
# recipients=to,
# project=None,
# user=None,
# ).send()
self.stdout.write(self.style.WARNING(result))
Loading
Loading