Ejemplo n.º 1
0
    def email_user(self,
                   subject,
                   context,
                   text_template,
                   html_template=None,
                   tag='Default'):
        from django.template.loader import render_to_string

        context['user'] = self
        text_message = render_to_string(text_template, context)
        if html_template:
            html_message = render_to_string(html_template, context)
        else:
            html_message = None

        # send_mail(subject, text_message, settings.DEFAULT_FROM_EMAIL, [self.email], fail_silently=False,
        #           html_message=html_message)

        message = AnymailMessage(
            subject=subject,
            body=text_message,
            to=["%s <%s>" % (self.full_name or self.username, self.email)],
            tags=[tag],
        )
        if html_message:
            message.attach_alternative(html_message, 'text/html')
        message.track_clicks = True
        message.send()
    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            # no metadata, send_at, track_clicks support
            tags=["tag 1"],  # max one tag
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertTrue(message.anymail_status.status.issubset({'queued', 'sent'}))
Ejemplo n.º 3
0
    def send_mail(self):
        """Function to send email"""
	return_var = None
        send_status = None
	plain_text = self.get_plain_template()
	html_text = self.get_html_template()

        # send email
        text_content = plain_text
        html_content = html_text
        msg = AnymailMessage(subject=self.email_subject, body=text_content, from_email=self.email_from, to=self.email_to, reply_to=self.reply_to)
        msg.attach_alternative(html_content, "text/html")
        send_status = msg.send()
        status = msg.anymail_status  # available after sending
        return_var = status.message_id  # e.g., '<*****@*****.**>'

	"""
        logger.info("###START EMAIL HTML###")
        logger.info(html_text)
        logger.info("###END EMAIL HTML###")
	"""

        logger.info("email inviata a " + str(self.email_to) + " | stato invio: " + str(send_status) + " (1=ok) | msg_id: " + str(return_var))

        return return_var
    def handle_prison(self, credit_notice_email, path, date, **options):
        call_command(
            'create_prisoner_credit_notices',
            path, credit_notice_email.prison.nomis_id,
            date=date, **options
        )
        if not path.exists():
            if self.verbosity:
                self.stdout.write('Nothing to send to %s' % credit_notice_email)
            return

        template_context = prepare_context()
        text_body = template_loader.get_template('credit/prisoner-notice-email.txt').render(template_context)
        html_body = template_loader.get_template('credit/prisoner-notice-email.html').render(template_context)
        email = AnymailMessage(
            subject=str(self.subject),
            body=text_body.strip('\n'),
            from_email=self.from_address,
            to=[credit_notice_email.email],
            tags=['prisoner-notice'],
        )
        email.attach_alternative(html_body, 'text/html')
        email.attach_file(str(path), mimetype='application/pdf')

        if self.verbosity:
            self.stdout.write('Sending prisoner notice email to %s' % credit_notice_email)
        email.send()
Ejemplo n.º 5
0
    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email='"Test From, with comma" <*****@*****.**>',
            to=["*****@*****.**", '"Recipient 2, OK?" <*****@*****.**>'],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=['"Reply, with comma" <*****@*****.**>'],  # v3 only supports single reply-to
            headers={"X-Anymail-Test": "value", "X-Anymail-Count": 3},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=True,
            track_opens=True,
            # esp_extra={'asm': {'group_id': 1}},  # this breaks activity feed if you don't have an asm group
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SendGrid always queues
    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test FILES",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=True,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SendGrid always queues
    def test_all_options(self):
        send_at = datetime.now() + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1"],  # SparkPost only supports single tags
            track_clicks=True,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SparkPost always queues
    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email='"Test From, with comma" <*****@*****.**>',
            to=["*****@*****.**", '"Recipient 2, OK?" <*****@*****.**>'],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=['"Reply, with comma" <*****@*****.**>'],  # v3 only supports single reply-to
            headers={"X-Anymail-Test": "value", "X-Anymail-Count": 3},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=True,
            track_opens=True,
            # esp_extra={'asm': {'group_id': 1}},  # this breaks activity feed if you don't have an asm group
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SendGrid always queues
Ejemplo n.º 9
0
    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail Mailjet all-options integration test",
            body="This is the text body",
            from_email='"Test Sender, Inc." <*****@*****.**>',
            to=[
                '*****@*****.**',
                '"Recipient, 2nd" <*****@*****.**>'
            ],
            cc=['*****@*****.**', 'Copy 2 <*****@*****.**>'],
            bcc=[
                '*****@*****.**',
                'Blind Copy 2 <*****@*****.**>'
            ],
            reply_to=['"Reply, To" <*****@*****.**>'
                      ],  # Mailjet only supports single reply_to
            headers={"X-Anymail-Test": "value"},
            metadata={
                "meta1": "simple string",
                "meta2": 2
            },
            tags=["tag 1"],  # Mailjet only allows a single tag
            track_clicks=True,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid, "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'sent'})
    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email='"Test Sender, Inc." <*****@*****.**>',
            to=['*****@*****.**', '"Recipient, 2nd" <*****@*****.**>'],
            cc=['*****@*****.**', 'Copy 2 <*****@*****.**>'],
            bcc=['*****@*****.**', 'Blind Copy 2 <*****@*****.**>'],
            reply_to=['*****@*****.**', '"Reply, 2nd" <*****@*****.**>'],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            tags=["tag 1"],  # Mailjet only allows a single tag
            track_clicks=True,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'sent'})
Ejemplo n.º 11
0
def messages_for_recipients(recipients_and_contexts, template_name):
    """
    Creates the messages to the recipients using the templates

    Args:
        recipients_and_contexts (list of (str, dict)): list of users and their contexts as a dict
        template_name (str): name of the template, this should match a directory in mail/templates

    Yields:
        EmailMultiAlternatives: email message with rendered content
    """
    with mail.get_connection(
            settings.NOTIFICATION_EMAIL_BACKEND) as connection:
        for recipient, context in recipients_and_contexts:
            subject, text_body, html_body = render_email_templates(
                template_name, context)
            msg = AnymailMessage(
                subject=subject,
                body=text_body,
                to=[recipient],
                from_email=settings.MAILGUN_FROM_EMAIL,
                connection=connection,
            )
            msg.attach_alternative(html_body, "text/html")
            yield msg
    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail Amazon SES all-options integration test",
            body="This is the text body",
            from_email='"Test From" <*****@*****.**>',
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},
            metadata={"meta1": "simple_string", "meta2": 2},
            tags=["Re-engagement", "Cohort 12/2017"],
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.attach_alternative(
            "Amazon SES SendRawEmail actually supports multiple alternative parts",
            "text/x-note-for-email-geeks")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})
Ejemplo n.º 13
0
    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            # no metadata, send_at, track_clicks support
            tags=["tag 1"],  # max one tag
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid, "text/html")

        message.send()
        self.assertTrue(
            message.anymail_status.status.issubset({'queued', 'sent'}))
Ejemplo n.º 14
0
    def handle_prison(self, credit_notice_email, path, date, **options):
        call_command('create_prisoner_credit_notices',
                     path,
                     credit_notice_email.prison.nomis_id,
                     date=date,
                     **options)
        if not path.exists():
            if self.verbosity:
                self.stdout.write('Nothing to send to %s' %
                                  credit_notice_email)
            return

        template_context = {
            'static_url': urljoin(settings.SITE_URL, settings.STATIC_URL),
        }
        text_body = template_loader.get_template(
            'credit/prisoner-notice-email.txt').render(template_context)
        html_body = template_loader.get_template(
            'credit/prisoner-notice-email.html').render(template_context)
        email = AnymailMessage(
            subject=self.subject,
            body=text_body.strip('\n'),
            from_email=self.from_address,
            to=[credit_notice_email.email],
            tags=['prisoner-notice'],
        )
        email.attach_alternative(html_body, 'text/html')
        email.attach_file(str(path), mimetype='application/pdf')

        if self.verbosity:
            self.stdout.write('Sending prisoner notice email to %s' %
                              credit_notice_email)
        email.send()
    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail Postmark all-options integration test",
            body="This is the text body",
            # Postmark accepts multiple from_email addresses, but truncates to the first on their end
            from_email="Test From <*****@*****.**>, [email protected]",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            # no metadata, send_at, track_clicks support
            tags=["tag 1"],  # max one tag
            track_opens=True,
            track_clicks=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'sent'})
Ejemplo n.º 16
0
    def flag_for_review(self, approval_request):
        """
        Mark this hosting provider as in need of review by admins.
        Sends an notification via email to admins.
        """

        #  notify_admin_via_email(approval_request)
        provider = approval_request.hostingprovider
        link_path = reverse(
            "greenweb_admin:accounts_hostingprovider_change", args=[provider.id]
        )
        link_url = f"{settings.SITE_URL}{link_path}"
        ctx = {
            "approval_request": approval_request,
            "provider": provider,
            "link_url": link_url,
        }
        notification_subject = (
            f"TGWF: {approval_request.hostingprovider} - "
            "has been updated and needs a review"
        )

        notification_email_copy = render_to_string("flag_for_review_text.txt", ctx)
        notification_email_html = render_to_string("flag_for_review_text.html", ctx)

        msg = AnymailMessage(
            subject=notification_subject,
            body=notification_email_copy,
            to=["*****@*****.**"],
        )

        msg.attach_alternative(notification_email_html, "text/html")
        msg.send()
def email_export_xlsx(*, object_type, user, session, endpoint_path, filters,
                      export_description, attachment_name):
    if not get_language():
        language = getattr(settings, 'LANGUAGE_CODE', 'en')
        activate(language)

    if object_type == 'credits':
        export_message = gettext(
            'Attached are the credits you exported from ‘%(service_name)s’.')
    elif object_type == 'disbursements':
        export_message = (gettext(
            'Attached are the bank transfer and cheque disbursements you exported from ‘%(service_name)s’.'
        ) + ' ' + gettext('You can’t see cash or postal orders here.'))
    elif object_type == 'senders':
        export_message = gettext(
            'Attached is a list of payment sources you exported from ‘%(service_name)s’.'
        )
    elif object_type == 'prisoners':
        export_message = gettext(
            'Attached is a list of prisoners you exported from ‘%(service_name)s’.'
        )
    else:
        raise NotImplementedError(f'Cannot export {object_type}')

    api_session = get_api_session_with_session(user, session)
    generated_at = timezone.now()
    object_list = convert_date_fields(
        retrieve_all_pages_for_path(api_session, endpoint_path, **filters))

    serialiser = ObjectListSerialiser.serialiser_for(object_type)
    workbook = serialiser.make_workbook(object_list)
    output = save_virtual_workbook(workbook)

    attachment_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'

    template_context = prepare_context({
        'export_description': export_description,
        'generated_at': generated_at,
        'export_message': export_message % {
            'service_name': gettext('Prisoner money intelligence')
        }
    })

    subject = '%s - %s' % (gettext('Prisoner money intelligence'),
                           gettext('Exported data'))
    text_body = template_loader.get_template(
        'security/email/export.txt').render(template_context)
    html_body = template_loader.get_template(
        'security/email/export.html').render(template_context)
    email = AnymailMessage(
        subject=subject,
        body=text_body.strip('\n'),
        from_email=default_from_address(),
        to=[user.email],
        tags=['export'],
    )
    email.attach_alternative(html_body, mimetype='text/html')
    email.attach(attachment_name, output, mimetype=attachment_type)
    email.send()
Ejemplo n.º 18
0
    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        send_at_timestamp = mktime(send_at.timetuple())  # python3: send_at.timestamp()
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=False,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,3", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path(), domain=MAILGUN_TEST_DOMAIN)
        message.attach_alternative(
            "<div>This is the <i>html</i> body <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # Mailgun always queues
        message_id = message.anymail_status.message_id

        events = self.fetch_mailgun_events(message_id, event="accepted")
        if events is None:
            self.skipTest("No Mailgun 'accepted' event after 30sec -- can't complete this test")
            return

        event = events.pop()
        self.assertCountEqual(event["tags"], ["tag 1", "tag 2"])  # don't care about order
        self.assertEqual(event["user-variables"],
                         {"meta1": "simple string", "meta2": "2"})  # all metadata values become strings

        self.assertEqual(event["message"]["scheduled-for"], send_at_timestamp)
        self.assertCountEqual(event["message"]["recipients"],
                              ['*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**',
                               '*****@*****.**', '*****@*****.**'])  # don't care about order

        headers = event["message"]["headers"]
        self.assertEqual(headers["from"], "Test From <*****@*****.**>")
        self.assertEqual(headers["to"], "[email protected], Recipient 2 <*****@*****.**>")
        self.assertEqual(headers["subject"], "Anymail all-options integration test")

        attachments = event["message"]["attachments"]
        self.assertEqual(len(attachments), 2)  # because inline image shouldn't be an attachment
        self.assertEqual(attachments[0]["filename"], "attachment1.txt")
        self.assertEqual(attachments[0]["content-type"], "text/plain")
        self.assertEqual(attachments[1]["filename"], "attachment2.csv")
        self.assertEqual(attachments[1]["content-type"], "text/csv")
    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        send_at_timestamp = mktime(send_at.timetuple())  # python3: send_at.timestamp()
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=False,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,3", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path(), domain=MAILGUN_TEST_DOMAIN)
        message.attach_alternative(
            "<div>This is the <i>html</i> body <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # Mailgun always queues
        message_id = message.anymail_status.message_id

        events = self.fetch_mailgun_events(message_id, event="accepted")
        if events is None:
            self.skipTest("No Mailgun 'accepted' event after 30sec -- can't complete this test")
            return

        event = events.pop()
        self.assertCountEqual(event["tags"], ["tag 1", "tag 2"])  # don't care about order
        self.assertEqual(event["user-variables"],
                         {"meta1": "simple string", "meta2": "2"})  # all metadata values become strings

        self.assertEqual(event["message"]["scheduled-for"], send_at_timestamp)
        self.assertCountEqual(event["message"]["recipients"],
                              ['*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**',
                               '*****@*****.**', '*****@*****.**'])  # don't care about order

        headers = event["message"]["headers"]
        self.assertEqual(headers["from"], "Test From <*****@*****.**>")
        self.assertEqual(headers["to"], "[email protected], Recipient 2 <*****@*****.**>")
        self.assertEqual(headers["subject"], "Anymail all-options integration test")

        attachments = event["message"]["attachments"]
        self.assertEqual(len(attachments), 2)  # because inline image shouldn't be an attachment
        self.assertEqual(attachments[0]["filename"], "attachment1.txt")
        self.assertEqual(attachments[0]["content-type"], "text/plain")
        self.assertEqual(attachments[1]["filename"], "attachment2.csv")
        self.assertEqual(attachments[1]["content-type"], "text/csv")
Ejemplo n.º 20
0
def prepare_email(template,
                  user=None,
                  context=None,
                  to=None,
                  language=None,
                  **kwargs):
    context = dict(context) if context else {}

    context.update({
        'site_name': settings.SITE_NAME,
        'hostname': settings.HOSTNAME,
    })

    if user:
        context.update({
            'user': user,
            'user_display_name': user.get_full_name(),
        })

    if not to:
        if not user:
            raise Exception(
                'Do not know who to send the email to, no "user" or "to" field'
            )
        to = [user.email]

    if isinstance(to, str):
        to = [to]

    if user and not language:
        language = user.language

    subject, text_content, html_content = prepare_email_content(
        template, context, language)

    from_email = formataddr((settings.SITE_NAME, settings.DEFAULT_FROM_EMAIL))

    message_kwargs = {
        'subject': subject,
        'body': text_content,
        'to': to,
        'from_email': from_email,
        'track_clicks': False,
        'track_opens': False,
        **kwargs,
    }

    email = AnymailMessage(**message_kwargs)

    if html_content:
        email.attach_alternative(html_content, 'text/html')

    return email
Ejemplo n.º 21
0
def prepare_email(from_address, to, subject, text_template, html_template, template_context, anymail_tags):
    if not get_language():
        language = getattr(settings, 'LANGUAGE_CODE', 'en')
        activate(language)

    text_body = loader.get_template(text_template).render(template_context)
    email = AnymailMessage(
        subject=subject,
        body=text_body.strip('\n'),
        from_email=from_address,
        to=to
    )
    if anymail_tags:
        email.tags = list(map(force_text, anymail_tags))
    if html_template:
        html_body = loader.get_template(html_template).render(template_context)
        email.attach_alternative(html_body, 'text/html')
    return email
Ejemplo n.º 22
0
def send_email(subject,
               body,
               recipient_list,
               from_email=settings.DEFAULT_FROM_EMAIL,
               html_body=None,
               tags=None,
               fail_silently=False):
    message = AnymailMessage(subject=subject, body=body, to=recipient_list)

    message.from_email = from_email

    if html_body:
        message.attach_alternative(html_body, 'text/html')

    # Tags can then be found and filtered in the ESP's analytics dashboard
    if tags:
        message.tags = tags

    message.send(fail_silently=fail_silently)
Ejemplo n.º 23
0
 def send(self, to, *args, **kwargs):
     """
     Overrides djoser.email.PasswordResetEmail#send to use our mail API.
     """
     context = self.get_context_data()
     context.update(self.context)
     with django_mail.get_connection(
             settings.NOTIFICATION_EMAIL_BACKEND) as connection:
         subject, text_body, html_body = render_email_templates(
             "password_reset", context)
         msg = AnymailMessage(
             subject=subject,
             body=text_body,
             to=to,
             from_email=settings.MAILGUN_FROM_EMAIL,
             connection=connection,
         )
         msg.attach_alternative(html_body, "text/html")
         send_messages([msg])
Ejemplo n.º 24
0
def build_message(connection,
                  template_name,
                  recipient,
                  context,
                  metadata=None):
    """
    Creates a message object

    Args:
        connection: An instance of the email backend class (return value of django.core.mail.get_connection)
        template_name (str): name of the template, this should match a directory in mail/templates
        recipient (str): Recipient email address
        context (dict or None): A dict of context variables
        metadata (EmailMetadata or None): An object containing extra data to attach to the message

    Returns:
        django.core.mail.EmailMultiAlternatives: email message with rendered content
    """
    subject, text_body, html_body = render_email_templates(
        template_name, context or {})
    msg = AnymailMessage(
        subject=subject,
        body=text_body,
        to=[recipient],
        from_email=settings.MAILGUN_FROM_EMAIL,
        connection=connection,
        headers={"Reply-To": settings.MITXPRO_REPLY_TO_ADDRESS},
    )
    esp_extra = {}
    if metadata:
        if metadata.tags:
            esp_extra.update({"o:tag": metadata.tags})
        if metadata.user_variables:
            esp_extra.update({
                "v:{}".format(k): v
                for k, v in metadata.user_variables.items()
            })
    if esp_extra:
        msg.esp_extra = esp_extra
    msg.attach_alternative(html_body, "text/html")
    return msg
    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail SendinBlue all-options integration test",
            body="This is the text body",
            from_email='"Test From, with comma" <*****@*****.**>',
            to=["*****@*****.**", '"Recipient 2, OK?" <*****@*****.**>'],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=['"Reply, with comma" <*****@*****.**>'],  # SendinBlue API v3 only supports single reply-to
            headers={"X-Anymail-Test": "value", "X-Anymail-Count": 3},

            metadata={"meta1": "simple string", "meta2": 2},
            tags=["tag 1", "tag 2"],
        )
        message.attach_alternative('<p>HTML content</p>', "text/html")  # SendinBlue requires an HTML body

        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SendinBlue always queues
        self.assertRegex(message.anymail_status.message_id, r'\<.+@.+\>')
Ejemplo n.º 26
0
def send_csv(prison, date, batches, csv_contents, total, count):
    prison_name = prison.get('short_name') or prison['name']
    some_batch = batches[0]
    now = timezone.localtime(timezone.now())
    csv_name = 'payment_%s_%s.csv' % (
        prison['cms_establishment_code'],
        now.strftime('%Y%m%d_%H%M%S'),
    )
    zipped_csv = io.BytesIO()
    with zipfile.ZipFile(zipped_csv, 'w') as z:
        z.writestr(csv_name, csv_contents)
    template_context = prepare_context({
        'prison_name': prison_name,
        'date': date,
        'total': total,
        'count': count,
    })
    text_body = template_loader.get_template(
        'bank_admin/emails/private-estate.txt').render(template_context)
    html_body = template_loader.get_template(
        'bank_admin/emails/private-estate.html').render(template_context)
    email = AnymailMessage(
        subject=
        'Credits received from "Send money to someone in prison" for %s on %s'
        % (
            prison_name,
            date.strftime('%d/%m/%Y'),
        ),
        body=text_body.strip('\n'),
        from_email=default_from_address(),
        to=some_batch['remittance_emails'],
        tags=['private-csv'],
    )
    email.attach_alternative(html_body, mimetype='text/html')
    email.attach(csv_name + '.zip',
                 zipped_csv.getvalue(),
                 mimetype='application/zip')
    email.send()
    logger.info('Sent private estate batch for %s' % prison_name)
Ejemplo n.º 27
0
    def send(self):
        headers = {}
        reply_to = delivery_settings.get('reply_to', False)
        if reply_to:
            headers['Reply-To'] = self.from_address

        message = EmailMessage(subject=self.subject,
                               body=self.text,
                               from_email=self.from_address,
                               to=[self.to_address],
                               headers=headers)

        if self.html:
            message.attach_alternative(self.html, "text/html")

        sent = message.send()
        if settings.EMAIL_BACKEND.startswith('anymail'):
            status = message.anymail_status
            results = status.status
            recipient = status.recipients.get(self.from_address, None)
            msg_bits = ','.join(results) if results else 'STATUS UNKNOWN'
            log_message = '{}: {} | {}'.format(
                status.message_id or 'NO_ID',
                recipient.status.upper() if recipient else 'UNKNOWN', msg_bits)

            is_success = results.issubset({'queued', 'sent'
                                           }) if results else False
        else:
            log_message = 'Sent by {}'.format(settings.EMAIL_BACKEND)

            is_success = bool(sent)

        MessageLog.objects.log(self, is_success, log_message)

        if is_success:
            self.delete()

        return is_success
Ejemplo n.º 28
0
class TemplateEmail:
    def __init__(self,
                 *,
                 template: str,
                 subject: str,
                 from_email: str,
                 to: List[str],
                 context_data: dict = {}) -> None:
        if template not in TEMPLATE_PATHS:
            raise ValueError(f'"{template}" is not registered.')

        template_path = TEMPLATE_PATHS[template]
        text_content = render_to_string(template_path, {
            **{
                "email_format": "txt"
            },
            **context_data
        })
        html_content = render_to_string(template_path, {
            **{
                "email_format": "html"
            },
            **context_data
        })

        if template.startswith("recruitment."):
            to.append(RECRUITMENT_EMAIL)

        self.message = AnymailMessage(subject=subject,
                                      from_email=from_email,
                                      to=to,
                                      body=text_content)
        self.message.attach_alternative(html_content, "text/html")

    def send(self):
        self.message.send()
Ejemplo n.º 29
0
def prepare_email(template, user=None, extra_context=None, to=None):
    context = {
        'site_name': settings.SITE_NAME,
    }

    if extra_context is not None:
        context.update(extra_context)

    if user is not None:
        context.update({
            'user': user,
            'user_display_name': user.get_full_name(),
        })

    if to is None:
        if user is None:
            raise Exception('Do not know who to send the email to, no "user" or "to" field')
        to = user.email

    email = AnymailMessage(
        subject=render_to_string('{}.subject.jinja2'.format(template), context).replace('\n', ''),
        body=render_to_string('{}.text.jinja2'.format(template), context),
        to=[to],
        from_email=settings.DEFAULT_FROM_EMAIL,
        track_clicks=False,
        track_opens=False
    )

    try:
        html_template = get_template('{}.html.jinja2'.format(template))
        html_content = html_template.render(context)
        email.attach_alternative(html_content, 'text/html')
    except TemplateDoesNotExist:
        pass

    return email
class SendGridBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """SendGrid API integration tests

    These tests run against the **live** SendGrid API, using the
    environment variable `SENDGRID_TEST_API_KEY` as the API key
    If those variables are not set, these tests won't run.

    SendGrid doesn't offer a test mode -- it tries to send everything
    you ask. To avoid stacking up a pile of undeliverable @example.com
    emails, the tests use SendGrid's "sink domain" @sink.sendgrid.net.
    https://support.sendgrid.com/hc/en-us/articles/201995663-Safely-Test-Your-Sending-Speed

    """

    def setUp(self):
        super(SendGridBackendIntegrationTests, self).setUp()
        self.message = AnymailMessage('Anymail SendGrid integration test', 'Text content',
                                      '*****@*****.**', ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the SendGrid send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients['*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # SendGrid always queues
        self.assertRegex(message_id, r'\<.+@example\.com\>')  # should use from_email's domain
        self.assertEqual(anymail_status.status, {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test FILES",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=True,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SendGrid always queues

    def test_merge_data(self):
        message = AnymailMessage(
            subject="Anymail merge_data test: %value%",
            body="This body includes merge data: %value%",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            merge_data={
                '*****@*****.**': {'value': 'one'},
                '*****@*****.**': {'value': 'two'},
            },
            esp_extra={
                'merge_field_format': '%{}%',
            },
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(recipient_status['*****@*****.**'].status, 'queued')
        self.assertEqual(recipient_status['*****@*****.**'].status, 'queued')

    @override_settings(ANYMAIL_SENDGRID_API_KEY="Hey, that's not an API key!")
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 400)
        # Make sure the exception message includes SendGrid's response:
        self.assertIn("authorization grant is invalid", str(err))
class SparkPostBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """SparkPost API integration tests

    These tests run against the **live** SparkPost API, using the
    environment variable `SPARKPOST_TEST_API_KEY` as the API key
    If that variable is not set, these tests won't run.

    SparkPost doesn't offer a test mode -- it tries to send everything
    you ask. To avoid stacking up a pile of undeliverable @example.com
    emails, the tests use SparkPost's "sink domain" @*.sink.sparkpostmail.com.
    https://support.sparkpost.com/customer/en/portal/articles/2361300-how-to-test-integrations

    SparkPost also doesn't support arbitrary senders (so no [email protected]).
    We've set up @test-sp.anymail.info as a validated sending domain for these tests.

    """

    def setUp(self):
        super(SparkPostBackendIntegrationTests, self).setUp()
        self.message = AnymailMessage('Anymail SparkPost integration test', 'Text content',
                                      '*****@*****.**', ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the SparkPost send status and transmission id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients['*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # SparkPost always queues
        self.assertRegex(message_id, r'.+')  # this is actually the transmission_id; should be non-blank
        self.assertEqual(anymail_status.status, {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        send_at = datetime.now() + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1"],  # SparkPost only supports single tags
            track_clicks=True,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SparkPost always queues

    def test_merge_data(self):
        message = AnymailMessage(
            subject="Anymail merge_data test: {{ value }}",
            body="This body includes merge data: {{ value }}\n"
                 "And global merge data: {{ global }}",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            merge_data={
                '*****@*****.**': {'value': 'one'},
                '*****@*****.**': {'value': 'two'},
            },
            merge_global_data={
                'global': 'global_value'
            },
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(recipient_status['*****@*****.**'].status, 'queued')
        self.assertEqual(recipient_status['*****@*****.**'].status, 'queued')

    def test_stored_template(self):
        message = AnymailMessage(
            template_id='test-template',  # a real template in our SparkPost test account
            to=["*****@*****.**"],
            merge_data={
                '*****@*****.**': {
                    'name': "Test Recipient",
                }
            },
            merge_global_data={
                'order': '12345',
            },
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(recipient_status['*****@*****.**'].status, 'queued')

    @override_settings(ANYMAIL_SPARKPOST_API_KEY="Hey, that's not an API key!")
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 403)
        # Make sure the exception message includes SparkPost's response:
        self.assertIn("Forbidden", str(err))
class SendGridBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """SendGrid v3 API integration tests

    These tests run against the **live** SendGrid API, using the
    environment variable `SENDGRID_TEST_API_KEY` as the API key
    If those variables are not set, these tests won't run.

    The SEND_DEFAULTS above force SendGrid's v3 sandbox mode, which avoids sending mail.
    (Sandbox sends also don't show in the activity feed, so disable that for live debugging.)

    The tests also use SendGrid's "sink domain" @sink.sendgrid.net for recipient addresses.
    https://support.sendgrid.com/hc/en-us/articles/201995663-Safely-Test-Your-Sending-Speed

    """

    def setUp(self):
        super(SendGridBackendIntegrationTests, self).setUp()
        self.message = AnymailMessage('Anymail SendGrid integration test', 'Text content',
                                      '*****@*****.**', ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the SendGrid send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients['*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # SendGrid always queues
        self.assertRegex(message_id, r'\<.+@example\.com\>')  # should use from_email's domain
        self.assertEqual(anymail_status.status, {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email='"Test From, with comma" <*****@*****.**>',
            to=["*****@*****.**", '"Recipient 2, OK?" <*****@*****.**>'],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=['"Reply, with comma" <*****@*****.**>'],  # v3 only supports single reply-to
            headers={"X-Anymail-Test": "value", "X-Anymail-Count": 3},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=True,
            track_opens=True,
            # esp_extra={'asm': {'group_id': 1}},  # this breaks activity feed if you don't have an asm group
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SendGrid always queues

    def test_merge_data(self):
        message = AnymailMessage(
            subject="Anymail merge_data test: %field%",
            body="This body includes merge data: %field%",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            reply_to=['"Merge data in reply name: %field%" <*****@*****.**>'],
            merge_data={
                '*****@*****.**': {'field': 'one'},
                '*****@*****.**': {'field': 'two'},
            },
            esp_extra={
                'merge_field_format': '%{}%',
            },
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(recipient_status['*****@*****.**'].status, 'queued')
        self.assertEqual(recipient_status['*****@*****.**'].status, 'queued')

    @unittest.skipUnless(SENDGRID_TEST_TEMPLATE_ID,
                         "Set the SENDGRID_TEST_TEMPLATE_ID environment variable "
                         "to a template in your SendGrid account to test stored templates")
    def test_stored_template(self):
        message = AnymailMessage(
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**"],
            template_id=SENDGRID_TEST_TEMPLATE_ID,
            # The test template in the Anymail Test account has a substitution "-field-":
            merge_global_data={
                'field': 'value from merge_global_data',
            },
            esp_extra={
                'merge_field_format': '-{}-',
            },
        )
        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})

    @override_settings(ANYMAIL_SENDGRID_API_KEY="Hey, that's not an API key!")
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
        # Make sure the exception message includes SendGrid's response:
        self.assertIn("authorization grant is invalid", str(err))
class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """Mailgun API integration tests

    These tests run against the **live** Mailgun API, using the
    environment variable `MAILGUN_TEST_API_KEY` as the API key
    and `MAILGUN_TEST_DOMAIN` as the sender domain.
    If those variables are not set, these tests won't run.

    """
    def setUp(self):
        super(MailgunBackendIntegrationTests, self).setUp()
        self.message = AnymailMessage('Anymail Mailgun integration test',
                                      'Text content', '*****@*****.**',
                                      ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def fetch_mailgun_events(self,
                             message_id,
                             event=None,
                             initial_delay=2,
                             retry_delay=2,
                             max_retries=5):
        """Return list of Mailgun events related to message_id"""
        url = "https://api.mailgun.net/v3/%s/events" % MAILGUN_TEST_DOMAIN
        auth = ("api", MAILGUN_TEST_API_KEY)

        # Despite the docs, Mailgun's events API actually expects the message-id
        # without the <...> brackets (so, not exactly "as returned by the messages API")
        # https://documentation.mailgun.com/api-events.html#filter-field
        params = {'message-id': message_id[1:-1]}  # strip <...>
        if event is not None:
            params['event'] = event

        # It can take a few seconds for the events to show up
        # in Mailgun's logs, so retry a few times if necessary:
        sleep(initial_delay)
        response = None
        for retry in range(max_retries):
            if retry > 0:
                sleep(retry_delay)
            response = requests.get(url, auth=auth, params=params)
            if 200 == response.status_code:
                items = response.json()["items"]
                if len(items) > 0:
                    return items
                # else no matching events found yet, so try again after delay
            elif 500 <= response.status_code < 600:
                # server error (hopefully transient); try again after delay
                pass
            elif 403 == response.status_code:
                # "forbidden": this may be related to API throttling; try again after delay
                pass
            else:
                response.raise_for_status()
        # Max retries exceeded:
        if response is not None and 200 != response.status_code:
            logging.warning("Ignoring Mailgun events API error %d:\n%s" %
                            (response.status_code, response.text))
        return None

    def test_simple_send(self):
        # Example of getting the Mailgun send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients[
            '*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # Mailgun always queues
        self.assertGreater(len(message_id),
                           0)  # don't know what it'll be, but it should exist

        self.assertEqual(anymail_status.status,
                         {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        send_at_timestamp = mktime(
            send_at.timetuple())  # python3: send_at.timestamp()
        message = AnymailMessage(
            subject="Anymail Mailgun all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>, [email protected]",
            to=[
                "*****@*****.**", "Recipient 2 <*****@*****.**>"
            ],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=[
                "*****@*****.**",
                "Blind Copy 2 <*****@*****.**>"
            ],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},
            metadata={
                "meta1": "simple string",
                "meta2": 2
            },
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=False,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")
        message.attach("vedhæftet fil.csv", "ID,Name\n1,3", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path(),
                                               domain=MAILGUN_TEST_DOMAIN)
        message.attach_alternative(
            "<div>This is the <i>html</i> body <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status,
                         {'queued'})  # Mailgun always queues
        message_id = message.anymail_status.message_id

        events = self.fetch_mailgun_events(message_id, event="accepted")
        if events is None:
            self.skipTest(
                "No Mailgun 'accepted' event after 30sec -- can't complete this test"
            )
            return

        event = events.pop()
        self.assertCountEqual(event["tags"],
                              ["tag 1", "tag 2"])  # don't care about order
        self.assertEqual(event["user-variables"], {
            "meta1": "simple string",
            "meta2": "2"
        })  # all metadata values become strings

        self.assertEqual(event["message"]["scheduled-for"], send_at_timestamp)
        self.assertIn(event["recipient"], [
            '*****@*****.**', '*****@*****.**',
            '*****@*****.**', '*****@*****.**',
            '*****@*****.**', '*****@*****.**'
        ])

        headers = event["message"]["headers"]
        self.assertEqual(
            headers["from"],
            "Test From <*****@*****.**>, [email protected]")
        self.assertEqual(
            headers["to"],
            "[email protected], Recipient 2 <*****@*****.**>")
        self.assertEqual(headers["subject"],
                         "Anymail Mailgun all-options integration test")

        attachments = event["message"]["attachments"]
        self.assertEqual(len(attachments),
                         2)  # because inline image shouldn't be an attachment
        self.assertEqual(attachments[0]["filename"], "attachment1.txt")
        self.assertEqual(attachments[0]["content-type"], "text/plain")
        self.assertEqual(attachments[1]["filename"], "vedhæftet fil.csv")
        self.assertEqual(attachments[1]["content-type"], "text/csv")

        # No other fields are verifiable from the event data.
        # (We could try fetching the message from event["storage"]["url"]
        # to verify content and other headers.)

    # As of Anymail 0.10, this test is no longer possible, because
    # Anymail now raises AnymailInvalidAddress without even calling Mailgun
    # def test_invalid_from(self):
    #     self.message.from_email = 'webmaster'
    #     with self.assertRaises(AnymailAPIError) as cm:
    #         self.message.send()
    #     err = cm.exception
    #     self.assertEqual(err.status_code, 400)
    #     self.assertIn("'from' parameter is not a valid address", str(err))

    @override_settings(
        ANYMAIL={
            'MAILGUN_API_KEY': "Hey, that's not an API key",
            'MAILGUN_SENDER_DOMAIN': MAILGUN_TEST_DOMAIN,
            'MAILGUN_SEND_DEFAULTS': {
                'esp_extra': {
                    'o:testmode': 'yes'
                }
            }
        })
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
class MailjetBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """Mailjet API integration tests

    These tests run against the **live** Mailjet API, using the
    environment variables `MAILJET_TEST_API_KEY` and `MAILJET_TEST_SECRET_KEY`
    as the API key and API secret key, respectively.
    If those variables are not set, these tests won't run.

    Mailjet doesn't (in v3.0) offer a test/sandbox mode -- it tries to send everything
    you ask.

    Mailjet also doesn't support unverified senders (so no [email protected]).
    We've set up @test-mj.anymail.info as a validated sending domain for these tests.

    """

    def setUp(self):
        super(MailjetBackendIntegrationTests, self).setUp()
        self.message = AnymailMessage('Anymail Mailjet integration test', 'Text content',
                                      '*****@*****.**', ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the Mailjet send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients['*****@*****.**'].message_id

        self.assertEqual(sent_status, 'sent')
        self.assertRegex(message_id, r'.+')
        self.assertEqual(anymail_status.status, {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email='"Test Sender, Inc." <*****@*****.**>',
            to=['*****@*****.**', '"Recipient, 2nd" <*****@*****.**>'],
            cc=['*****@*****.**', 'Copy 2 <*****@*****.**>'],
            bcc=['*****@*****.**', 'Blind Copy 2 <*****@*****.**>'],
            reply_to=['*****@*****.**', '"Reply, 2nd" <*****@*****.**>'],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            tags=["tag 1"],  # Mailjet only allows a single tag
            track_clicks=True,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'sent'})

    def test_merge_data(self):
        message = AnymailMessage(
            subject="Anymail merge_data test",  # Mailjet doesn't support merge fields in the subject
            body="This body includes merge data: [[var:value]]\n"
                 "And global merge data: [[var:global]]",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            merge_data={
                '*****@*****.**': {'value': 'one'},
                '*****@*****.**': {'value': 'two'},
            },
            merge_global_data={
                'global': 'global_value'
            },
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(recipient_status['*****@*****.**'].status, 'sent')
        self.assertEqual(recipient_status['*****@*****.**'].status, 'sent')

    def test_stored_template(self):
        message = AnymailMessage(
            template_id='176375',  # ID of the real template named 'test-template' in our Mailjet test account
            to=["*****@*****.**"],
            merge_data={
                '*****@*****.**': {
                    'name': "Test Recipient",
                }
            },
            merge_global_data={
                'order': '12345',
            },
        )
        message.from_email = None  # use the template's sender email/name
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(recipient_status['*****@*****.**'].status, 'sent')

    @override_settings(ANYMAIL_MAILJET_API_KEY="Hey, that's not an API key!")
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
        self.assertIn("Invalid Mailjet API key or secret", str(err))
class PostmarkBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """Postmark API integration tests

    These tests run against the **live** Postmark API, but using a
    test key that's not capable of sending actual email.
    """

    def setUp(self):
        super(PostmarkBackendIntegrationTests, self).setUp()
        self.message = AnymailMessage('Anymail Postmark integration test', 'Text content',
                                      '*****@*****.**', ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the SendGrid send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients['*****@*****.**'].message_id

        self.assertEqual(sent_status, 'sent')
        self.assertGreater(len(message_id), 0)  # non-empty string
        self.assertEqual(anymail_status.status, {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            # no metadata, send_at, track_clicks support
            tags=["tag 1"],  # max one tag
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'sent'})

    def test_invalid_from(self):
        self.message.from_email = 'webmaster@localhost'  # Django's default From
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 422)
        self.assertIn("Invalid 'From' address", str(err))

    @override_settings(ANYMAIL_POSTMARK_SERVER_TOKEN="Hey, that's not a server token!")
    def test_invalid_server_token(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
        # Make sure the exception message includes Postmark's response:
        self.assertIn("Bad or missing Server API token", str(err))
Ejemplo n.º 36
0
class SendinBlueBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """SendinBlue v3 API integration tests

    SendinBlue doesn't have sandbox so these tests run
    against the **live** SendinBlue API, using the
    environment variable `SENDINBLUE_TEST_API_KEY` as the API key
    If those variables are not set, these tests won't run.

    https://developers.sendinblue.com/docs/faq#section-how-can-i-test-the-api-

    """
    def setUp(self):
        super(SendinBlueBackendIntegrationTests, self).setUp()

        self.message = AnymailMessage('Anymail SendinBlue integration test',
                                      'Text content',
                                      '*****@*****.**',
                                      ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the SendinBlue send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients[
            '*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # SendinBlue always queues
        self.assertRegex(
            message_id, r'\<.+@.+\>'
        )  # Message-ID can be [email protected] or .sendinblue.com
        self.assertEqual(anymail_status.status,
                         {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail SendinBlue all-options integration test",
            body="This is the text body",
            from_email='"Test From, with comma" <*****@*****.**>',
            to=[
                "*****@*****.**",
                '"Recipient 2, OK?" <*****@*****.**>'
            ],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=[
                "*****@*****.**",
                "Blind Copy 2 <*****@*****.**>"
            ],
            reply_to=['"Reply, with comma" <*****@*****.**>'
                      ],  # SendinBlue API v3 only supports single reply-to
            headers={
                "X-Anymail-Test": "value",
                "X-Anymail-Count": 3
            },
            metadata={
                "meta1": "simple string",
                "meta2": 2
            },
            tags=["tag 1"],  # SendinBlue only supports single tags
        )
        message.attach_alternative(
            '<p>HTML content</p>',
            "text/html")  # SendinBlue requires an HTML body

        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")

        message.send()
        self.assertEqual(message.anymail_status.status,
                         {'queued'})  # SendinBlue always queues
        self.assertRegex(message.anymail_status.message_id, r'\<.+@.+\>')

    def test_template(self):
        message = AnymailMessage(
            template_id=
            1,  # There is a template with this id in the Anymail test account
            to=[
                "*****@*****.**"
            ],  # SendinBlue doesn't allow recipient display names with templates
            reply_to=["*****@*****.**"],
            tags=["using-template"],
            headers={"X-Anymail-Test": "group: A, variation: C"},
            merge_global_data={
                # The Anymail test template includes `%SHIP_DATE%` and `%ORDER_ID%` variables
                "SHIP_DATE": "yesterday",
                "ORDER_ID": "12345",
            },
            metadata={
                "customer-id": "ZXK9123",
                "meta2": 2
            },
        )
        message.from_email = None  # Required for SendinBlue templates

        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")

        message.send()
        self.assertEqual(message.anymail_status.status,
                         {'queued'})  # SendinBlue always queues
        self.assertRegex(message.anymail_status.message_id, r'\<.+@.+\>')

    @override_settings(ANYMAIL_SENDINBLUE_API_KEY="Hey, that's not an API key!"
                       )
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
        # Make sure the exception message includes SendinBlue's response:
        self.assertIn("Key not found", str(err))
class MailgunBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """Mailgun API integration tests

    These tests run against the **live** Mailgun API, using the
    environment variable `MAILGUN_TEST_API_KEY` as the API key
    and `MAILGUN_TEST_DOMAIN` as the sender domain.
    If those variables are not set, these tests won't run.

    """

    def setUp(self):
        super(MailgunBackendIntegrationTests, self).setUp()
        self.message = AnymailMessage('Anymail integration test', 'Text content',
                                      '*****@*****.**', ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def fetch_mailgun_events(self, message_id, event=None,
                             initial_delay=2, retry_delay=2, max_retries=5):
        """Return list of Mailgun events related to message_id"""
        url = "https://api.mailgun.net/v3/%s/events" % MAILGUN_TEST_DOMAIN
        auth = ("api", MAILGUN_TEST_API_KEY)

        # Despite the docs, Mailgun's events API actually expects the message-id
        # without the <...> brackets (so, not exactly "as returned by the messages API")
        # https://documentation.mailgun.com/api-events.html#filter-field
        params = {'message-id': message_id[1:-1]}  # strip <...>
        if event is not None:
            params['event'] = event

        # It can take a few seconds for the events to show up
        # in Mailgun's logs, so retry a few times if necessary:
        sleep(initial_delay)
        response = None
        for retry in range(max_retries):
            if retry > 0:
                sleep(retry_delay)
            response = requests.get(url, auth=auth, params=params)
            if 200 == response.status_code:
                items = response.json()["items"]
                if len(items) > 0:
                    return items
                # else no matching events found yet, so try again after delay
            elif 500 <= response.status_code < 600:
                # server error (hopefully transient); try again after delay
                pass
            elif 403 == response.status_code:
                # "forbidden": this may be related to API throttling; try again after delay
                pass
            else:
                response.raise_for_status()
        # Max retries exceeded:
        if response is not None and 200 != response.status_code:
            logging.warning("Ignoring Mailgun events API error %d:\n%s"
                            % (response.status_code, response.text))
        return None

    def test_simple_send(self):
        # Example of getting the Mailgun send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients['*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # Mailgun always queues
        self.assertGreater(len(message_id), 0)  # don't know what it'll be, but it should exist

        self.assertEqual(anymail_status.status, {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        send_at_timestamp = mktime(send_at.timetuple())  # python3: send_at.timestamp()
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            metadata={"meta1": "simple string", "meta2": 2},
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=False,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,3", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path(), domain=MAILGUN_TEST_DOMAIN)
        message.attach_alternative(
            "<div>This is the <i>html</i> body <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # Mailgun always queues
        message_id = message.anymail_status.message_id

        events = self.fetch_mailgun_events(message_id, event="accepted")
        if events is None:
            self.skipTest("No Mailgun 'accepted' event after 30sec -- can't complete this test")
            return

        event = events.pop()
        self.assertCountEqual(event["tags"], ["tag 1", "tag 2"])  # don't care about order
        self.assertEqual(event["user-variables"],
                         {"meta1": "simple string", "meta2": "2"})  # all metadata values become strings

        self.assertEqual(event["message"]["scheduled-for"], send_at_timestamp)
        self.assertCountEqual(event["message"]["recipients"],
                              ['*****@*****.**', '*****@*****.**', '*****@*****.**', '*****@*****.**',
                               '*****@*****.**', '*****@*****.**'])  # don't care about order

        headers = event["message"]["headers"]
        self.assertEqual(headers["from"], "Test From <*****@*****.**>")
        self.assertEqual(headers["to"], "[email protected], Recipient 2 <*****@*****.**>")
        self.assertEqual(headers["subject"], "Anymail all-options integration test")

        attachments = event["message"]["attachments"]
        self.assertEqual(len(attachments), 2)  # because inline image shouldn't be an attachment
        self.assertEqual(attachments[0]["filename"], "attachment1.txt")
        self.assertEqual(attachments[0]["content-type"], "text/plain")
        self.assertEqual(attachments[1]["filename"], "attachment2.csv")
        self.assertEqual(attachments[1]["content-type"], "text/csv")

        # No other fields are verifiable from the event data.
        # (We could try fetching the message from event["storage"]["url"]
        # to verify content and other headers.)

    def test_invalid_from(self):
        self.message.from_email = 'webmaster'
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 400)
        self.assertIn("'from' parameter is not a valid address", str(err))

    @override_settings(ANYMAIL_MAILGUN_API_KEY="Hey, that's not an API key!")
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
class SendGridBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
    """SendGrid v3 API integration tests

    These tests run against the **live** SendGrid API, using the
    environment variable `ANYMAIL_TEST_SENDGRID_API_KEY` as the API key
    If those variables are not set, these tests won't run.

    The SEND_DEFAULTS above force SendGrid's v3 sandbox mode, which avoids sending mail.
    (Sandbox sends also don't show in the activity feed, so disable that for live debugging.)

    The tests also use SendGrid's "sink domain" @sink.sendgrid.net for recipient addresses.
    https://support.sendgrid.com/hc/en-us/articles/201995663-Safely-Test-Your-Sending-Speed

    """
    def setUp(self):
        super().setUp()
        self.message = AnymailMessage('Anymail SendGrid integration test',
                                      'Text content', '*****@*****.**',
                                      ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the SendGrid send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients[
            '*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # SendGrid always queues
        self.assertUUIDIsValid(
            message_id)  # Anymail generates a UUID tracking id
        self.assertEqual(anymail_status.status,
                         {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        send_at = datetime.now().replace(microsecond=0) + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email='"Test From, with comma" <*****@*****.**>',
            to=[
                "*****@*****.**",
                '"Recipient 2, OK?" <*****@*****.**>'
            ],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=[
                "*****@*****.**",
                "Blind Copy 2 <*****@*****.**>"
            ],
            reply_to=['"Reply, with comma" <*****@*****.**>'
                      ],  # v3 only supports single reply-to
            headers={
                "X-Anymail-Test": "value",
                "X-Anymail-Count": 3
            },
            metadata={
                "meta1": "simple string",
                "meta2": 2
            },
            send_at=send_at,
            tags=["tag 1", "tag 2"],
            track_clicks=True,
            track_opens=True,
            # esp_extra={'asm': {'group_id': 1}},  # this breaks activity feed if you don't have an asm group
        )
        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid, "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status,
                         {'queued'})  # SendGrid always queues

    def test_merge_data(self):
        message = AnymailMessage(
            subject="Anymail merge_data test: %field%",
            body="This body includes merge data: %field%",
            from_email="Test From <*****@*****.**>",
            to=[
                "*****@*****.**", "Recipient 2 <*****@*****.**>"
            ],
            reply_to=[
                '"Merge data in reply name: %field%" <*****@*****.**>'
            ],
            merge_data={
                '*****@*****.**': {
                    'field': 'one'
                },
                '*****@*****.**': {
                    'field': 'two'
                },
            },
            esp_extra={
                'merge_field_format': '%{}%',
            },
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(recipient_status['*****@*****.**'].status,
                         'queued')
        self.assertEqual(recipient_status['*****@*****.**'].status,
                         'queued')

    @unittest.skipUnless(
        ANYMAIL_TEST_SENDGRID_TEMPLATE_ID,
        "Set the ANYMAIL_TEST_SENDGRID_TEMPLATE_ID environment variable "
        "to a template in your SendGrid account to test stored templates")
    def test_stored_template(self):
        message = AnymailMessage(
            from_email="Test From <*****@*****.**>",
            to=["*****@*****.**"],
            # Anymail's live test template has merge fields "name", "order_no", and "dept"...
            template_id=ANYMAIL_TEST_SENDGRID_TEMPLATE_ID,
            merge_data={
                '*****@*****.**': {
                    'name': "Test Recipient",
                    'order_no': "12345",
                },
            },
            merge_global_data={'dept': "Fulfillment"},
        )
        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})

    @override_settings(ANYMAIL_SENDGRID_API_KEY="Hey, that's not an API key!")
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
        # Make sure the exception message includes SendGrid's response:
        self.assertIn("authorization grant is invalid", str(err))
class SparkPostBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
    """SparkPost API integration tests

    These tests run against the **live** SparkPost API, using the
    environment variable `ANYMAIL_TEST_SPARKPOST_API_KEY` as the API key
    If that variable is not set, these tests won't run.

    SparkPost doesn't offer a test mode -- it tries to send everything
    you ask. To avoid stacking up a pile of undeliverable @example.com
    emails, the tests use SparkPost's "sink domain" @*.sink.sparkpostmail.com.
    https://www.sparkpost.com/docs/faq/using-sink-server/

    SparkPost also doesn't support arbitrary senders (so no [email protected]).
    We've set up @test-sp.anymail.info as a validated sending domain for these tests.
    """
    def setUp(self):
        super().setUp()
        self.message = AnymailMessage('Anymail SparkPost integration test',
                                      'Text content',
                                      '*****@*****.**',
                                      ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the SparkPost send status and transmission id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients[
            '*****@*****.**'].status
        message_id = anymail_status.recipients[
            '*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # SparkPost always queues
        self.assertRegex(
            message_id,
            r'.+')  # this is actually the transmission_id; should be non-blank
        self.assertEqual(anymail_status.status,
                         {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        send_at = datetime.now() + timedelta(minutes=2)
        message = AnymailMessage(
            subject="Anymail all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            to=[
                "*****@*****.**",
                "Recipient 2 <*****@*****.**>"
            ],
            # Limit the live b/cc's to avoid running through our small monthly allowance:
            # cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            # bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            cc=["Copy To <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},
            metadata={
                "meta1": "simple string",
                "meta2": 2
            },
            send_at=send_at,
            tags=["tag 1"],  # SparkPost only supports single tags
            track_clicks=True,
            track_opens=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid, "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status,
                         {'queued'})  # SparkPost always queues

    def test_merge_data(self):
        message = AnymailMessage(
            subject="Anymail merge_data test: {{ value }}",
            body="This body includes merge data: {{ value }}\n"
            "And global merge data: {{ global }}",
            from_email="Test From <*****@*****.**>",
            to=[
                "*****@*****.**",
                "Recipient 2 <*****@*****.**>"
            ],
            merge_data={
                '*****@*****.**': {
                    'value': 'one'
                },
                '*****@*****.**': {
                    'value': 'two'
                },
            },
            merge_global_data={'global': 'global_value'},
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(
            recipient_status['*****@*****.**'].status,
            'queued')
        self.assertEqual(
            recipient_status['*****@*****.**'].status,
            'queued')

    def test_stored_template(self):
        message = AnymailMessage(
            template_id=
            'test-template',  # a real template in our SparkPost test account
            to=["*****@*****.**"],
            merge_data={
                '*****@*****.**': {
                    'name': "Test Recipient",
                }
            },
            merge_global_data={
                'order': '12345',
            },
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(
            recipient_status['*****@*****.**'].status,
            'queued')

    @override_settings(ANYMAIL_SPARKPOST_API_KEY="Hey, that's not an API key!")
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 403)
        # Make sure the exception message includes SparkPost's response:
        self.assertIn("Forbidden", str(err))
class AmazonSESBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
    """Amazon SES API integration tests

    These tests run against the **live** Amazon SES API, using the environment
    variables `AMAZON_SES_TEST_ACCESS_KEY_ID` and `AMAZON_SES_TEST_SECRET_ACCESS_KEY`
    as AWS credentials. If those variables are not set, these tests won't run.
    (You can also set the environment variable `AMAZON_SES_TEST_REGION_NAME`
    to test SES using a region other than the default "us-east-1".)

    Amazon SES doesn't offer a test mode -- it tries to send everything you ask.
    To avoid stacking up a pile of undeliverable @example.com
    emails, the tests use Amazon's @simulator.amazonses.com addresses.
    https://docs.aws.amazon.com/ses/latest/DeveloperGuide/mailbox-simulator.html

    Amazon SES also doesn't support arbitrary senders (so no [email protected]).
    We've set up @test-ses.anymail.info as a validated sending domain for these tests.
    You may need to change the from_email to your own address when testing.

    """
    def setUp(self):
        super().setUp()
        self.message = AnymailMessage('Anymail Amazon SES integration test',
                                      'Text content',
                                      '*****@*****.**',
                                      ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

        # boto3 relies on GC to close connections. Python 3 warns about unclosed ssl.SSLSocket during cleanup.
        # We don't care. (It may be a false positive, or it may be a botocore problem, but it's not *our* problem.)
        # https://github.com/boto/boto3/issues/454#issuecomment-586033745
        # Filter in TestCase.setUp because unittest resets the warning filters for each test.
        # https://stackoverflow.com/a/26620811/647002
        warnings.filterwarnings("ignore",
                                message=r"unclosed <ssl\.SSLSocket",
                                category=ResourceWarning)

    def test_simple_send(self):
        # Example of getting the Amazon SES send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients[
            '*****@*****.**'].status
        message_id = anymail_status.recipients[
            '*****@*****.**'].message_id

        self.assertEqual(
            sent_status,
            'queued')  # Amazon SES always queues (or raises an error)
        self.assertRegex(
            message_id,
            r'[0-9a-f-]+')  # Amazon SES message ids are groups of hex chars
        self.assertEqual(anymail_status.status,
                         {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail Amazon SES all-options integration test",
            body="This is the text body",
            from_email='"Test From" <*****@*****.**>',
            to=[
                "*****@*****.**",
                "Recipient 2 <*****@*****.**>"
            ],
            cc=[
                "*****@*****.**",
                "Copy 2 <*****@*****.**>"
            ],
            bcc=[
                "*****@*****.**",
                "Blind Copy 2 <*****@*****.**>"
            ],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},
            metadata={
                "meta1": "simple_string",
                "meta2": 2
            },
            tags=["Re-engagement", "Cohort 12/2017"],
        )
        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid, "text/html")

        message.attach_alternative(
            "Amazon SES SendRawEmail actually supports multiple alternative parts",
            "text/x-note-for-email-geeks")

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})

    def test_stored_template(self):
        # Using a template created like this:
        # boto3.client('ses').create_template(Template={
        #     "TemplateName": "TestTemplate",
        #     "SubjectPart": "Your order {{order}} shipped",
        #     "HtmlPart": "<h1>Dear {{name}}:</h1><p>Your order {{order}} shipped {{ship_date}}.</p>",
        #     "TextPart": "Dear {{name}}:\r\nYour order {{order}} shipped {{ship_date}}."
        # })
        message = AnymailMessage(
            template_id='TestTemplate',
            from_email='"Test From" <*****@*****.**>',
            to=[
                "First Recipient <*****@*****.**>",
                "*****@*****.**"
            ],
            merge_data={
                '*****@*****.**': {
                    'order': 12345,
                    'name': "Test Recipient"
                },
                '*****@*****.**': {
                    'order': 6789
                },
            },
            merge_global_data={
                'name': "Customer",  # default
                'ship_date': "today"
            },
        )
        message.send()
        recipient_status = message.anymail_status.recipients
        self.assertEqual(
            recipient_status['*****@*****.**'].status,
            'queued')
        self.assertRegex(
            recipient_status['*****@*****.**'].message_id,
            r'[0-9a-f-]+')
        self.assertEqual(
            recipient_status['*****@*****.**'].status,
            'queued')
        self.assertRegex(
            recipient_status['*****@*****.**'].message_id,
            r'[0-9a-f-]+')

    @override_settings(
        ANYMAIL={
            "AMAZON_SES_CLIENT_PARAMS": {
                "aws_access_key_id": "test-invalid-access-key-id",
                "aws_secret_access_key": "test-invalid-secret-access-key",
                "region_name": AMAZON_SES_TEST_REGION_NAME,
            }
        })
    def test_invalid_aws_credentials(self):
        # Make sure the exception message includes AWS's response:
        with self.assertRaisesMessage(
                AnymailAPIError,
                "The security token included in the request is invalid"):
            self.message.send()
Ejemplo n.º 41
0
class SendinBlueBackendIntegrationTests(AnymailTestMixin, SimpleTestCase):
    """SendinBlue v3 API integration tests

    SendinBlue doesn't have sandbox so these tests run
    against the **live** SendinBlue API, using the
    environment variable `ANYMAIL_TEST_SENDINBLUE_API_KEY` as the API key
    If those variables are not set, these tests won't run.

    https://developers.sendinblue.com/docs/faq#section-how-can-i-test-the-api-

    """
    def setUp(self):
        super().setUp()

        self.message = AnymailMessage('Anymail SendinBlue integration test',
                                      'Text content',
                                      '*****@*****.**',
                                      ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the SendinBlue send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients[
            '*****@*****.**'].message_id

        self.assertEqual(sent_status, 'queued')  # SendinBlue always queues
        self.assertRegex(
            message_id, r'\<.+@.+\>'
        )  # Message-ID can be [email protected] or .sendinblue.com
        self.assertEqual(anymail_status.status,
                         {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail SendinBlue all-options integration test",
            body="This is the text body",
            from_email='"Test From, with comma" <*****@*****.**>',
            to=[
                "*****@*****.**",
                '"Recipient 2, OK?" <*****@*****.**>'
            ],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=[
                "*****@*****.**",
                "Blind Copy 2 <*****@*****.**>"
            ],
            reply_to=['"Reply, with comma" <*****@*****.**>'
                      ],  # SendinBlue API v3 only supports single reply-to
            headers={
                "X-Anymail-Test": "value",
                "X-Anymail-Count": 3
            },
            metadata={
                "meta1": "simple string",
                "meta2": 2
            },
            tags=["tag 1", "tag 2"],
        )
        message.attach_alternative(
            '<p>HTML content</p>',
            "text/html")  # SendinBlue requires an HTML body

        message.attach("attachment1.txt", "Here is some\ntext for you",
                       "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")

        message.send()
        self.assertEqual(message.anymail_status.status,
                         {'queued'})  # SendinBlue always queues
        self.assertRegex(message.anymail_status.message_id, r'\<.+@.+\>')

    def test_template(self):
        message = AnymailMessage(
            template_id=
            5,  # There is a *new-style* template with this id in the Anymail test account
            from_email=
            'Sender <*****@*****.**>',  # Override template sender
            to=["Recipient <*****@*****.**>"
                ],  # No batch send (so max one recipient suggested)
            reply_to=["Do not reply <*****@*****.**>"],
            tags=["using-template"],
            headers={"X-Anymail-Test": "group: A, variation: C"},
            merge_global_data={
                # The Anymail test template includes `{{ params.SHIP_DATE }}`
                # and `{{ params.ORDER_ID }}` substitutions
                "SHIP_DATE": "yesterday",
                "ORDER_ID": "12345",
            },
            metadata={
                "customer-id": "ZXK9123",
                "meta2": 2
            },
        )

        # Normal attachments don't work with Sendinblue templates:
        #   message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        # If you can host the attachment content on some publicly-accessible URL,
        # this *non-portable* alternative allows sending attachments with templates:
        message.esp_extra = {
            'attachment': [{
                'name':
                'attachment1.txt',
                # URL where Sendinblue can download the attachment content while sending:
                'url':
                'https://raw.githubusercontent.com/anymail/django-anymail/main/AUTHORS.txt',
            }]
        }

        message.send()
        self.assertEqual(message.anymail_status.status,
                         {'queued'})  # SendinBlue always queues
        self.assertRegex(message.anymail_status.message_id, r'\<.+@.+\>')

    @override_settings(ANYMAIL_SENDINBLUE_API_KEY="Hey, that's not an API key!"
                       )
    def test_invalid_api_key(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
        # Make sure the exception message includes SendinBlue's response:
        self.assertIn("Key not found", str(err))
class PostmarkBackendIntegrationTests(SimpleTestCase, AnymailTestMixin):
    """Postmark API integration tests

    These tests run against the **live** Postmark API, but using a
    test key that's not capable of sending actual email.
    """

    def setUp(self):
        super(PostmarkBackendIntegrationTests, self).setUp()
        self.message = AnymailMessage('Anymail Postmark integration test', 'Text content',
                                      '*****@*****.**', ['*****@*****.**'])
        self.message.attach_alternative('<p>HTML content</p>', "text/html")

    def test_simple_send(self):
        # Example of getting the Postmark send status and message id from the message
        sent_count = self.message.send()
        self.assertEqual(sent_count, 1)

        anymail_status = self.message.anymail_status
        sent_status = anymail_status.recipients['*****@*****.**'].status
        message_id = anymail_status.recipients['*****@*****.**'].message_id

        self.assertEqual(sent_status, 'sent')
        self.assertGreater(len(message_id), 0)  # non-empty string
        self.assertEqual(anymail_status.status, {sent_status})  # set of all recipient statuses
        self.assertEqual(anymail_status.message_id, message_id)

    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail Postmark all-options integration test",
            body="This is the text body",
            # Postmark accepts multiple from_email addresses, but truncates to the first on their end
            from_email="Test From <*****@*****.**>, [email protected]",
            to=["*****@*****.**", "Recipient 2 <*****@*****.**>"],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=["*****@*****.**", "Blind Copy 2 <*****@*****.**>"],
            reply_to=["*****@*****.**", "Reply 2 <*****@*****.**>"],
            headers={"X-Anymail-Test": "value"},

            # no metadata, send_at, track_clicks support
            tags=["tag 1"],  # max one tag
            track_opens=True,
            track_clicks=True,
        )
        message.attach("attachment1.txt", "Here is some\ntext for you", "text/plain")
        message.attach("attachment2.csv", "ID,Name\n1,Amy Lina", "text/csv")
        cid = message.attach_inline_image_file(sample_image_path())
        message.attach_alternative(
            "<p><b>HTML:</b> with <a href='http://example.com'>link</a>"
            "and image: <img src='cid:%s'></div>" % cid,
            "text/html")

        message.send()
        self.assertEqual(message.anymail_status.status, {'sent'})

    def test_invalid_from(self):
        self.message.from_email = 'webmaster@localhost'  # Django's default From
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 422)
        self.assertIn("Invalid 'From' address", str(err))

    @override_settings(ANYMAIL_POSTMARK_SERVER_TOKEN="Hey, that's not a server token!")
    def test_invalid_server_token(self):
        with self.assertRaises(AnymailAPIError) as cm:
            self.message.send()
        err = cm.exception
        self.assertEqual(err.status_code, 401)
        # Make sure the exception message includes Postmark's response:
        self.assertIn("Please verify that you are using a valid token", str(err))