Fokko closed pull request #2824: [AIRFLOW-1867] Fix sendgrid py3k bug; add
sandbox mode
URL: https://github.com/apache/incubator-airflow/pull/2824
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/airflow/contrib/utils/sendgrid.py
b/airflow/contrib/utils/sendgrid.py
index ceb3718ff5..1b932bcb9b 100644
--- a/airflow/contrib/utils/sendgrid.py
+++ b/airflow/contrib/utils/sendgrid.py
@@ -27,16 +27,16 @@
import os
import sendgrid
-from sendgrid.helpers.mail import Attachment, Content, Email, Mail, \
- Personalization, CustomArg, Category
+from sendgrid.helpers.mail import (
+ Attachment, Content, Email, Mail, Personalization, CustomArg, Category,
+ MailSettings, SandBoxMode)
from airflow.utils.email import get_email_address_list
from airflow.utils.log.logging_mixin import LoggingMixin
-def send_email(to, subject, html_content, files=None,
- dryrun=False, cc=None, bcc=None,
- mime_subtype='mixed', **kwargs):
+def send_email(to, subject, html_content, files=None, dryrun=False, cc=None,
+ bcc=None, mime_subtype='mixed', sandbox_mode=False, **kwargs):
"""
Send an email with html content using sendgrid.
@@ -50,11 +50,18 @@ def send_email(to, subject, html_content, files=None,
SENDGRID_MAIL_FROM={your-mail-from}
SENDGRID_API_KEY={your-sendgrid-api-key}.
"""
+ if files is None:
+ files = []
+
mail = Mail()
from_email = kwargs.get('from_email') or
os.environ.get('SENDGRID_MAIL_FROM')
from_name = kwargs.get('from_name') or
os.environ.get('SENDGRID_MAIL_SENDER')
mail.from_email = Email(from_email, from_name)
mail.subject = subject
+ mail.mail_settings = MailSettings()
+
+ if sandbox_mode:
+ mail.mail_settings.sandbox_mode = SandBoxMode(enable=True)
# Add the recipient list of to emails.
personalization = Personalization()
@@ -84,15 +91,18 @@ def send_email(to, subject, html_content, files=None,
mail.add_category(Category(cat))
# Add email attachment.
- for fname in files or []:
+ for fname in files:
basename = os.path.basename(fname)
+
attachment = Attachment()
+ attachment.type = mimetypes.guess_type(basename)[0]
+ attachment.filename = basename
+ attachment.disposition = "attachment"
+ attachment.content_id = '<{0}>'.format(basename)
+
with open(fname, "rb") as f:
- attachment.content = str(base64.b64encode(f.read()), 'utf-8')
- attachment.type = mimetypes.guess_type(basename)[0]
- attachment.filename = basename
- attachment.disposition = "attachment"
- attachment.content_id = '<%s>' % basename
+ attachment.content = base64.b64encode(f.read()).decode('utf-8')
+
mail.add_attachment(attachment)
_post_sendgrid_mail(mail.get())
@@ -103,8 +113,8 @@ def _post_sendgrid_mail(mail_data):
response = sg.client.mail.send.post(request_body=mail_data)
# 2xx status code.
if response.status_code >= 200 and response.status_code < 300:
- log.info('Email with subject %s is successfully sent to recipients:
%s' %
- (mail_data['subject'], mail_data['personalizations']))
+ log.info('Email with subject %s is successfully sent to recipients:
%s',
+ mail_data['subject'], mail_data['personalizations'])
else:
- log.warning('Failed to send out email with subject %s, status code:
%s' %
- (mail_data['subject'], response.status_code))
+ log.warning('Failed to send out email with subject %s, status code:
%s',
+ mail_data['subject'], response.status_code)
diff --git a/tests/contrib/utils/test_sendgrid.py
b/tests/contrib/utils/test_sendgrid.py
index 6710076c2d..d12c597d70 100644
--- a/tests/contrib/utils/test_sendgrid.py
+++ b/tests/contrib/utils/test_sendgrid.py
@@ -20,6 +20,8 @@
import copy
import unittest
+import tempfile
+import os
from airflow.contrib.utils.sendgrid import send_email
@@ -41,58 +43,78 @@ def setUp(self):
self.cc = ['[email protected]', '[email protected]']
self.bcc = ['[email protected]', '[email protected]']
self.expected_mail_data = {
- 'content': [{'type': u'text/html', 'value': '<b>Foo</b> bar'}],
+ 'content': [{'type': u'text/html', 'value': self.html_content}],
'personalizations': [
{'cc': [{'email': '[email protected]'}, {'email':
'[email protected]'}],
'to': [{'email': '[email protected]'}, {'email': '[email protected]'}],
'bcc': [{'email': '[email protected]'}, {'email':
'[email protected]'}]}],
'from': {'email': u'[email protected]'},
- 'subject': 'sendgrid-send-email unit test'}
+ 'subject': 'sendgrid-send-email unit test',
+ 'mail_settings': {},
+ }
self.personalization_custom_args = {'arg1': 'val1', 'arg2': 'val2'}
self.categories = ['cat1', 'cat2']
# extras
self.expected_mail_data_extras = copy.deepcopy(self.expected_mail_data)
- self.expected_mail_data_extras['personalizations'][0]['custom_args'] =
\
- self.personalization_custom_args
+ self.expected_mail_data_extras['personalizations'][0]['custom_args'] =
(
+ self.personalization_custom_args)
self.expected_mail_data_extras['categories'] = self.categories
- self.expected_mail_data_extras['from'] = \
- {'name': 'Foo', 'email': '[email protected]'}
+ self.expected_mail_data_extras['from'] = {
+ 'name': 'Foo',
+ 'email': '[email protected]',
+ }
# sender
self.expected_mail_data_sender = copy.deepcopy(self.expected_mail_data)
- self.expected_mail_data_sender['from'] = \
- {'name': 'Foo Bar', 'email': '[email protected]'}
+ self.expected_mail_data_sender['from'] = {
+ 'name': 'Foo Bar',
+ 'email': '[email protected]',
+ }
- # Test the right email is constructed.
-
- @mock.patch('os.environ.get')
+ # Test the right email is constructed.
+ @mock.patch('os.environ', dict(os.environ,
SENDGRID_MAIL_FROM='[email protected]'))
@mock.patch('airflow.contrib.utils.sendgrid._post_sendgrid_mail')
- def test_send_email_sendgrid_correct_email(self, mock_post, mock_get):
- def get_return(var):
- return {'SENDGRID_MAIL_FROM': '[email protected]'}.get(var)
+ def test_send_email_sendgrid_correct_email(self, mock_post):
+ with tempfile.NamedTemporaryFile(mode='wt', suffix='.txt') as f:
+ f.write('this is some test data')
+ f.flush()
+
+ filename = os.path.basename(f.name)
+ expected_mail_data = dict(
+ self.expected_mail_data,
+ attachments=[{
+ 'content': 'dGhpcyBpcyBzb21lIHRlc3QgZGF0YQ==',
+ 'content_id': '<{0}>'.format(filename),
+ 'disposition': 'attachment',
+ 'filename': filename,
+ 'type': 'text/plain',
+ }],
+ )
- mock_get.side_effect = get_return
- send_email(self.to, self.subject, self.html_content, cc=self.cc,
bcc=self.bcc)
- mock_post.assert_called_with(self.expected_mail_data)
+ send_email(self.to,
+ self.subject,
+ self.html_content,
+ cc=self.cc,
+ bcc=self.bcc,
+ files=[f.name])
+ mock_post.assert_called_with(expected_mail_data)
# Test the right email is constructed.
- @mock.patch('os.environ.get')
+ @mock.patch(
+ 'os.environ',
+ dict(os.environ,
+ SENDGRID_MAIL_FROM='[email protected]',
+ SENDGRID_MAIL_SENDER='Foo')
+ )
@mock.patch('airflow.contrib.utils.sendgrid._post_sendgrid_mail')
- def test_send_email_sendgrid_correct_email_extras(self, mock_post,
mock_get):
- def get_return(var):
- return {'SENDGRID_MAIL_FROM': '[email protected]',
- 'SENDGRID_MAIL_SENDER': 'Foo'}.get(var)
-
- mock_get.side_effect = get_return
+ def test_send_email_sendgrid_correct_email_extras(self, mock_post):
send_email(self.to, self.subject, self.html_content, cc=self.cc,
bcc=self.bcc,
personalization_custom_args=self.personalization_custom_args,
categories=self.categories)
mock_post.assert_called_with(self.expected_mail_data_extras)
- @mock.patch('os.environ.get')
+ @mock.patch('os.environ', {})
@mock.patch('airflow.contrib.utils.sendgrid._post_sendgrid_mail')
- def test_send_email_sendgrid_sender(self, mock_post, mock_get):
-
- mock_get.return_value = None
+ def test_send_email_sendgrid_sender(self, mock_post):
send_email(self.to, self.subject, self.html_content, cc=self.cc,
bcc=self.bcc,
from_email='[email protected]', from_name='Foo Bar')
mock_post.assert_called_with(self.expected_mail_data_sender)
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services