Skip to content

Commit

Permalink
#687 AWS Sender Email API (#144)
Browse files Browse the repository at this point in the history
* addes todos

* saving

* saving

* added management commands for list setting and confirm settings

* saving

* confirm setting active

* added issue as todo

* clean aws sender records
  • Loading branch information
KlemenSpruk authored Oct 13, 2023
1 parent 69efef6 commit 30eab3e
Show file tree
Hide file tree
Showing 10 changed files with 617 additions and 16 deletions.
117 changes: 110 additions & 7 deletions django_project_base/base/event.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import copy
import datetime
from abc import ABC, abstractmethod

Expand All @@ -6,7 +7,7 @@
from django.shortcuts import get_object_or_404
from rest_registration.settings import registration_settings

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 @@ -45,16 +46,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 @@ -117,3 +145,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

0 comments on commit 30eab3e

Please sign in to comment.