Переглянути джерело

fix(api): move dyn welcome email to Domain post_save signal, add test

Signals are connected in AppConfig.ready(), as recommended in
https://docs.djangoproject.com/en/2.2/ref/applications/#django.apps.AppConfig.ready
Peter Thomassen 5 роки тому
батько
коміт
8dfbf7f4fc

+ 1 - 1
api/api/settings.py

@@ -40,7 +40,7 @@ INSTALLED_APPS = (
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'rest_framework',
-    'desecapi',
+    'desecapi.apps.AppConfig',
     'corsheaders',
     'djcelery_email',
 )

+ 8 - 0
api/desecapi/apps.py

@@ -0,0 +1,8 @@
+from django.apps import AppConfig as DjangoAppConfig
+
+
+class AppConfig(DjangoAppConfig):
+    name = 'desecapi'
+
+    def ready(self):
+        from desecapi import signals  # connect signals

+ 25 - 0
api/desecapi/signals.py

@@ -0,0 +1,25 @@
+from django.core.mail import EmailMessage
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from django.template.loader import get_template
+
+from desecapi import models
+
+
+@receiver(post_save, sender=models.Domain, dispatch_uid=__name__)
+def domain_handler(sender, instance, created, raw, using, update_fields, **kwargs):
+    if instance.is_locally_registrable:
+        content_tmpl = get_template('emails/domain-dyndns/content.txt')
+        subject_tmpl = get_template('emails/domain-dyndns/subject.txt')
+        from_tmpl = get_template('emails/from.txt')
+        context = {
+            'domain': instance.name,
+            'url': f'https://update.{instance.parent_domain_name}/',
+            'username': instance.name,
+            'password': models.Token.objects.create(user=instance.owner, name='dyndns')
+        }
+        email = EmailMessage(subject_tmpl.render(context),
+                             content_tmpl.render(context),
+                             from_tmpl.render(context),
+                             [instance.owner.email])
+        email.send()

+ 2 - 2
api/desecapi/tests/test_domains.py

@@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
 from psl_dns.exceptions import UnsupportedRule
 from rest_framework import status
 
-from desecapi.models import Domain
+from desecapi.models import Domain, Token
 from desecapi.pdns_change_tracker import PDNSChangeTracker
 from desecapi.tests.base import DesecTestCase, DomainOwnerTestCase, PublicSuffixMockMixin
 
@@ -398,7 +398,7 @@ class AutoDelegationDomainOwnerTests(DomainOwnerTestCase):
                 self.assertEqual(len(mail.outbox), i + 1)
                 email = str(mail.outbox[0].message())
                 self.assertTrue(name in email)
-                self.assertTrue(self.token.key in email)
+                self.assertTrue(any(token.key in email for token in Token.objects.filter(user=self.owner).all()))
                 self.assertFalse(self.user.plain_password in email)
 
     def test_domain_limit(self):

+ 7 - 8
api/desecapi/tests/test_user_management.py

@@ -25,7 +25,7 @@ from rest_framework.reverse import reverse
 from rest_framework.test import APIClient
 
 from api import settings
-from desecapi.models import Domain, User, Captcha
+from desecapi.models import Domain, User, Captcha, Token
 from desecapi.serializers import AuthenticatedActionSerializer
 from desecapi.tests.base import DesecTestCase, PublicSuffixMockMixin
 
@@ -249,16 +249,15 @@ class UserManagementTestCase(DesecTestCase, PublicSuffixMockMixin):
             status_code=status.HTTP_200_OK
         )
 
-    def assertRegistrationWithDomainVerificationSuccessResponse(self, response, domain=None):
+    def assertRegistrationWithDomainVerificationSuccessResponse(self, response, domain=None, email=None):
         if domain and self.has_local_suffix(domain):
+            body = self.assertEmailSent('', body_contains=domain, recipient=email)
+            self.assertTrue(any(token.key in body for token in Token.objects.filter(user__email=email).all()))
             text = 'Success! Here is the password'
         else:
+            self.assertNoEmailSent()
             text = 'Success! Please check the docs for the next steps'
-        return self.assertContains(
-            response=response,
-            text=text,
-            status_code=status.HTTP_200_OK
-        )
+        self.assertContains(response=response, text=text, status_code=status.HTTP_200_OK)
 
     def assertResetPasswordSuccessResponse(self, response):
         return self.assertContains(
@@ -403,7 +402,7 @@ class UserManagementTestCase(DesecTestCase, PublicSuffixMockMixin):
             cm = self.requests_desec_domain_creation(domain)
         with self.assertPdnsRequests(cm[:-1]):
             response = self.client.verify(confirmation_link)
-        self.assertRegistrationWithDomainVerificationSuccessResponse(response, domain)
+        self.assertRegistrationWithDomainVerificationSuccessResponse(response, domain, email)
         self.assertTrue(User.objects.get(email=email).is_active)
         self.assertPassword(email, password)
         self.assertTrue(Domain.objects.filter(name=domain, owner__email=email).exists())

+ 0 - 17
api/desecapi/views.py

@@ -81,23 +81,6 @@ class DomainList(generics.ListCreateAPIView):
         # TODO this line raises if the local public suffix is not in our database!
         PDNSChangeTracker.track(lambda: self.auto_delegate(domain))
 
-        # Send dyn email
-        if domain.is_locally_registrable:
-            content_tmpl = get_template('emails/domain-dyndns/content.txt')
-            subject_tmpl = get_template('emails/domain-dyndns/subject.txt')
-            from_tmpl = get_template('emails/from.txt')
-            context = {
-                'domain': domain.name,
-                'url': f'https://update.{domain.parent_domain_name}/',
-                'username': domain.name,
-                'password': self.request.auth.key
-            }
-            email = EmailMessage(subject_tmpl.render(context),
-                                 content_tmpl.render(context),
-                                 from_tmpl.render(context),
-                                 [self.request.user.email])
-            email.send()
-
     @staticmethod
     def auto_delegate(domain: models.Domain):
         if domain.is_locally_registrable: