예제 #1
0
파일: email_core.py 프로젝트: entpy/mjg
    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
예제 #2
0
 def test_merge_data(self):
     message = AnymailMessage(
         subject=
         "Anymail Mailjet 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 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_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/master/AUTHORS.txt',
            }]
        }

        message.send()
        self.assertEqual(message.anymail_status.status, {'queued'})  # SendinBlue always queues
        self.assertRegex(message.anymail_status.message_id, r'\<.+@.+\>')
예제 #5
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
예제 #6
0
 def setUp(self):
     super().setUp()
     self.message = AnymailMessage('Anymail Mailjet integration test',
                                   'Text content',
                                   '*****@*****.**',
                                   ['*****@*****.**'])
     self.message.attach_alternative('<p>HTML content</p>', "text/html")
 def setUp(self):
     super().setUp()
     self.message = AnymailMessage('Anymail SparkPost integration test',
                                   'Text content',
                                   '*****@*****.**',
                                   ['*****@*****.**'])
     self.message.attach_alternative('<p>HTML content</p>', "text/html")
예제 #8
0
    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 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'}))
예제 #10
0
 def test_stored_template(self):
     message = AnymailMessage(
         template_id=
         'test-template',  # name of a real template named in Anymail's Mailgun test account
         subject=
         'Your order %recipient.order%',  # Mailgun templates don't define subject
         from_email=
         'Test From <*****@*****.**>',  # Mailgun templates don't define sender
         to=["*****@*****.**"],
         # metadata and merge_data must not have any conflicting keys when using template_id
         metadata={
             "meta1": "simple string",
             "meta2": 2
         },
         merge_data={'*****@*****.**': {
             'name': "Test Recipient",
         }},
         merge_global_data={
             'order': '12345',
         },
     )
     message.send()
     recipient_status = message.anymail_status.recipients
     self.assertEqual(recipient_status['*****@*****.**'].status,
                      'queued')
    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 }}\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_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')
    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_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-]+')
    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):
        self.set_mock_response(raw=self._mock_batch_response)
        message = AnymailMessage(
            from_email='*****@*****.**',
            template_id=
            1234567,  # Postmark only supports merge_data content in a template
            to=['*****@*****.**', 'Bob <*****@*****.**>'],
            merge_data={
                '*****@*****.**': {
                    'name': "Alice",
                    'group': "Developers"
                },
                '*****@*****.**': {
                    'name': "Bob"
                },  # and leave group undefined
                '*****@*****.**': {
                    'name': "Not a recipient for this message"
                },
            },
            merge_global_data={
                'group': "Users",
                'site': "ExampleCo"
            })
        message.send()

        self.assert_esp_called('/email/batchWithTemplates')
        data = self.get_api_call_json()
        messages = data["Messages"]
        self.assertEqual(len(messages), 2)
        self.assertEqual(
            messages[0], {
                "From": "*****@*****.**",
                "To": "*****@*****.**",
                "TemplateId": 1234567,
                "TemplateModel": {
                    "name": "Alice",
                    "group": "Developers",
                    "site": "ExampleCo"
                },
            })
        self.assertEqual(
            messages[1], {
                "From": "*****@*****.**",
                "To": "Bob <*****@*****.**>",
                "TemplateId": 1234567,
                "TemplateModel": {
                    "name": "Bob",
                    "group": "Users",
                    "site": "ExampleCo"
                },
            })

        recipients = message.anymail_status.recipients
        self.assertEqual(recipients['*****@*****.**'].status, 'sent')
        self.assertEqual(recipients['*****@*****.**'].message_id,
                         'b7bc2f4a-e38e-4336-af7d-e6c392c2f817')
        self.assertEqual(recipients['*****@*****.**'].status, 'sent')
        self.assertEqual(recipients['*****@*****.**'].message_id,
                         'e2ecbbfc-fe12-463d-b933-9fe22915106d')
예제 #18
0
    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")
예제 #19
0
 def test_send_anymail_message_without_template(self):
     # Make sure SendRawEmail is used for non-template_id messages
     message = AnymailMessage(from_email="*****@*****.**",
                              to=["*****@*****.**"],
                              subject="subject")
     message.send()
     self.assert_esp_not_called(operation_name="send_bulk_templated_email")
     self.get_send_params(operation_name="send_raw_email"
                          )  # fails if send_raw_email not called
    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_template_alias(self):
     # Anymail template_id can be either Postmark TemplateId or TemplateAlias
     message = AnymailMessage(
         from_email='*****@*****.**',
         to=['*****@*****.**'],
         template_id='welcome-message',
     )
     message.send()
     self.assert_esp_called('/email/withTemplate/')
     data = self.get_api_call_json()
     self.assertEqual(data['TemplateAlias'], 'welcome-message')
예제 #22
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
    def setUp(self):
        super(AmazonSESBackendIntegrationTests, self).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 might not be a real problem worth warning, but in any case it's not our problem.)
        # https://www.google.com/search?q=unittest+boto3+ResourceWarning+unclosed+ssl.SSLSocket
        # 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)
예제 #24
0
 def test_no_debug_logging(self):
     # Make sure it doesn't output anything when DEBUG_API_REQUESTS is not set
     message = AnymailMessage('Subject', 'Text Body', '*****@*****.**', ['*****@*****.**'])
     message._payload_init = dict(
         data="Request body",
         headers={
             "Content-Type": "text/plain",
             "Accept": "application/json",
         },
     )
     with self.assertPrints("", match="equal"):
         message.send()
 def test_template(self):
     message = AnymailMessage(
         from_email="*****@*****.**",
         to=["*****@*****.**", "Second Recipient <*****@*****.**>"],
         template_id=POSTMARK_TEST_TEMPLATE_ID,
         merge_data={
             "*****@*****.**": {"name": "Recipient 1", "order_no": "12345"},
             "*****@*****.**": {"order_no": "6789"},
         },
         merge_global_data={"name": "Valued Customer"},
     )
     message.send()
     self.assertEqual(message.anymail_status.status, {'sent'})
    def test_merge_data_no_template(self):
        # merge_data={} can be used to force batch sending without a template
        self.set_mock_response(raw=json.dumps(
            [{
                "ErrorCode": 0,
                "Message": "OK",
                "To": "*****@*****.**",
                "SubmittedAt": "2016-03-12T15:27:50.4468803-05:00",
                "MessageID": "b7bc2f4a-e38e-4336-af7d-e6c392c2f817",
            }, {
                "ErrorCode": 0,
                "Message": "OK",
                "To": "*****@*****.**",
                "SubmittedAt": "2016-03-12T15:27:50.4468803-05:00",
                "MessageID": "e2ecbbfc-fe12-463d-b933-9fe22915106d",
            }]).encode('utf-8'))

        message = AnymailMessage(
            from_email='*****@*****.**',
            to=['*****@*****.**', 'Bob <*****@*****.**>'],
            merge_data={},
            subject="Test batch send",
            body="Test body",
        )
        message.send()

        self.assert_esp_called('/email/batch')
        data = self.get_api_call_json()
        self.assertEqual(len(data), 2)
        self.assertEqual(
            data[0], {
                "From": "*****@*****.**",
                "To": "*****@*****.**",
                "Subject": "Test batch send",
                "TextBody": "Test body",
            })
        self.assertEqual(
            data[1], {
                "From": "*****@*****.**",
                "To": "Bob <*****@*****.**>",
                "Subject": "Test batch send",
                "TextBody": "Test body",
            })

        recipients = message.anymail_status.recipients
        self.assertEqual(recipients['*****@*****.**'].status, 'sent')
        self.assertEqual(recipients['*****@*****.**'].message_id,
                         'b7bc2f4a-e38e-4336-af7d-e6c392c2f817')
        self.assertEqual(recipients['*****@*****.**'].status, 'sent')
        self.assertEqual(recipients['*****@*****.**'].message_id,
                         'e2ecbbfc-fe12-463d-b933-9fe22915106d')
예제 #27
0
    def test_all_options(self):
        message = AnymailMessage(
            subject="Anymail Postal all-options integration test",
            body="This is the text body",
            from_email="Test From <*****@*****.**>",
            envelope_sender="*****@*****.**",
            to=[
                "*****@*****.**", "Recipient 2 <*****@*****.**>"
            ],
            cc=["*****@*****.**", "Copy 2 <*****@*****.**>"],
            bcc=[
                "*****@*****.**",
                "Blind Copy 2 <*****@*****.**>"
            ],
            reply_to=["*****@*****.**"],
            headers={"X-Anymail-Test": "value"},
            tags=["tag 1"],  # max one tag
        )
        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'})
        self.assertEqual(
            message.anymail_status.recipients['*****@*****.**'].status,
            'queued')
        self.assertEqual(
            message.anymail_status.recipients['*****@*****.**'].status,
            'queued')
        # distinct messages should have different message_ids:
        self.assertNotEqual(
            message.anymail_status.recipients['*****@*****.**'].
            message_id, message.anymail_status.
            recipients['*****@*****.**'].message_id)
예제 #28
0
    def send_email(self, subject, body, tag='Default'):
        try:
            validate_email(self.email)
        except:
            print('Invalid email: %s' % self.email)
            return

        message = AnymailMessage(
            subject=subject,
            body=body,
            to=["%s <%s>" % (self.full_name or self.username, self.email)],
            tags=[tag],
        )
        message.send()
예제 #29
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()
예제 #30
0
    def send_email(self, subject, body, tag='Default'):
        try:
            validate_email(self.email)
        except:
            print('Invalid email: %s' % self.email)
            return

        message = AnymailMessage(
            subject=subject,
            body=body,
            to=["%s <%s>" % (self.full_name or self.username, self.email)],
            tags=[tag],
        )
        message.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()
 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'})
 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'})
 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'})
 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')
예제 #36
0
def send_mail_on_comment(user_id, post_id, host):
	#
	# sends mail on comment post
	#
	user = User.objects.get(id=user_id)
	post = models.NewsPost.objects.get(id=post_id)

	msg = AnymailMessage(
		subject = 'News Point - new comment',
		body = '''
		You have a new comment on post "{}"
		View at {}/post/{}/
		'''.format(post.title, host, post_id),
		to = [user.email],
		)
	msg.send()
    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_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')
 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')
    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()
 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')
 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_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')
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))
 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")
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))
 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 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")
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))
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))
 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")
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(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))