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 = ['foo...@foo.com', 'bar...@bar.com']
         self.bcc = ['foo-...@foo.com', 'bar-...@bar.com']
         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': 'foo...@foo.com'}, {'email': 
'bar...@bar.com'}],
                  'to': [{'email': 'f...@foo.com'}, {'email': 'b...@bar.com'}],
                  'bcc': [{'email': 'foo-...@foo.com'}, {'email': 
'bar-...@bar.com'}]}],
             'from': {'email': u'f...@bar.com'},
-            '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': 'f...@bar.com'}
+        self.expected_mail_data_extras['from'] = {
+            'name': 'Foo',
+            'email': 'f...@bar.com',
+        }
         # sender
         self.expected_mail_data_sender = copy.deepcopy(self.expected_mail_data)
-        self.expected_mail_data_sender['from'] = \
-            {'name': 'Foo Bar', 'email': 'f...@foo.bar'}
+        self.expected_mail_data_sender['from'] = {
+            'name': 'Foo Bar',
+            'email': 'f...@foo.bar',
+        }
 
-        # 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='f...@bar.com'))
     @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': 'f...@bar.com'}.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='f...@bar.com',
+             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': 'f...@bar.com',
-                    '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='f...@foo.bar', 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:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to