You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2020/08/10 14:31:50 UTC
[airflow] branch master updated: Revert "Add Amazon SES hook
(#10004)" (#10276)
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/master by this push:
new 19bc97d Revert "Add Amazon SES hook (#10004)" (#10276)
19bc97d is described below
commit 19bc97d0ce436a6ec9d8e9a5adcd48c0a769d01f
Author: Jarek Potiuk <ja...@polidea.com>
AuthorDate: Mon Aug 10 16:30:40 2020 +0200
Revert "Add Amazon SES hook (#10004)" (#10276)
This reverts commit f06fe616e66256bdc53710de505c2c6b1bd21528.
---
airflow/providers/amazon/aws/hooks/ses.py | 100 ---------------------------
airflow/utils/email.py | 76 +++-----------------
docs/operators-and-hooks-ref.rst | 6 --
tests/providers/amazon/aws/hooks/test_ses.py | 78 ---------------------
tests/utils/test_email.py | 24 +------
5 files changed, 11 insertions(+), 273 deletions(-)
diff --git a/airflow/providers/amazon/aws/hooks/ses.py b/airflow/providers/amazon/aws/hooks/ses.py
deleted file mode 100644
index 74d3002..0000000
--- a/airflow/providers/amazon/aws/hooks/ses.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-"""
-This module contains AWS SES Hook
-"""
-from typing import Any, Dict, Iterable, List, Optional, Union
-
-from airflow.providers.amazon.aws.hooks.base_aws import AwsBaseHook
-from airflow.utils.email import build_mime_message
-
-
-class SESHook(AwsBaseHook):
- """
- Interact with Amazon Simple Email Service.
-
- Additional arguments (such as ``aws_conn_id``) may be specified and
- are passed down to the underlying AwsBaseHook.
-
- .. seealso::
- :class:`~airflow.providers.amazon.aws.hooks.base_aws.AwsBaseHook`
- """
-
- def __init__(self, *args, **kwargs) -> None:
- kwargs['client_type'] = 'ses'
- super().__init__(*args, **kwargs)
-
- def send_email( # pylint: disable=too-many-arguments
- self,
- mail_from: str,
- to: Union[str, Iterable[str]],
- subject: str,
- html_content: str,
- files: Optional[List[str]] = None,
- cc: Optional[Union[str, Iterable[str]]] = None,
- bcc: Optional[Union[str, Iterable[str]]] = None,
- mime_subtype: str = 'mixed',
- mime_charset: str = 'utf-8',
- reply_to: Optional[str] = None,
- return_path: Optional[str] = None,
- custom_headers: Optional[Dict[str, Any]] = None
- ) -> dict:
- """
- Send email using Amazon Simple Email Service
-
- :param mail_from: Email address to set as email's from
- :param to: List of email addresses to set as email's to
- :param subject: Email's subject
- :param html_content: Content of email in HTML format
- :param files: List of paths of files to be attached
- :param cc: List of email addresses to set as email's CC
- :param bcc: List of email addresses to set as email's BCC
- :param mime_subtype: Can be used to specify the subtype of the message. Default = mixed
- :param mime_charset: Email's charset. Default = UTF-8.
- :param return_path: The email address to which replies will be sent. By default, replies
- are sent to the original sender's email address.
- :param reply_to: The email address to which message bounces and complaints should be sent.
- "Return-Path" is sometimes called "envelope from," "envelope sender," or "MAIL FROM."
- :param custom_headers: Additional headers to add to the MIME message.
- No validations are run on these values and they should be able to be encoded.
- :return: Response from Amazon SES service with unique message identifier.
- """
- ses_client = self.get_conn()
-
- custom_headers = custom_headers or {}
- if reply_to:
- custom_headers['Reply-To'] = reply_to
- if return_path:
- custom_headers['Return-Path'] = return_path
-
- message, recipients = build_mime_message(
- mail_from=mail_from,
- to=to,
- subject=subject,
- html_content=html_content,
- files=files,
- cc=cc,
- bcc=bcc,
- mime_subtype=mime_subtype,
- mime_charset=mime_charset,
- custom_headers=custom_headers,
- )
-
- return ses_client.send_raw_email(
- Source=mail_from, Destinations=recipients, RawMessage={'Data': message.as_string()}
- )
diff --git a/airflow/utils/email.py b/airflow/utils/email.py
index 5c1b921..c7ac55e 100644
--- a/airflow/utils/email.py
+++ b/airflow/utils/email.py
@@ -24,7 +24,7 @@ from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formatdate
-from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
+from typing import Iterable, List, Union
from airflow.configuration import conf
from airflow.exceptions import AirflowConfigException
@@ -47,18 +47,10 @@ def send_email(to: Union[List[str], Iterable[str]], subject: str, html_content:
mime_subtype=mime_subtype, mime_charset=mime_charset, **kwargs)
-def send_email_smtp(
- to: Union[str, Iterable[str]],
- subject: str,
- html_content: str,
- files: Optional[List[str]] = None,
- dryrun: bool = False,
- cc: Optional[Union[str, Iterable[str]]] = None,
- bcc: Optional[Union[str, Iterable[str]]] = None,
- mime_subtype: str = 'mixed',
- mime_charset: str = 'utf-8',
- **kwargs,
-):
+def send_email_smtp(to, subject, html_content, files=None,
+ dryrun=False, cc=None, bcc=None,
+ mime_subtype='mixed', mime_charset='utf-8',
+ **kwargs):
"""
Send an email with html content
@@ -66,55 +58,11 @@ def send_email_smtp(
"""
smtp_mail_from = conf.get('smtp', 'SMTP_MAIL_FROM')
- msg, recipients = build_mime_message(
- mail_from=smtp_mail_from,
- to=to,
- subject=subject,
- html_content=html_content,
- files=files,
- cc=cc,
- bcc=bcc,
- mime_subtype=mime_subtype,
- mime_charset=mime_charset,
- )
-
- send_mime_email(e_from=smtp_mail_from, e_to=recipients, mime_msg=msg, dryrun=dryrun)
-
-
-def build_mime_message(
- mail_from: str,
- to: Union[str, Iterable[str]],
- subject: str,
- html_content: str,
- files: Optional[List[str]] = None,
- cc: Optional[Union[str, Iterable[str]]] = None,
- bcc: Optional[Union[str, Iterable[str]]] = None,
- mime_subtype: str = 'mixed',
- mime_charset: str = 'utf-8',
- custom_headers: Optional[Dict[str, Any]] = None,
-) -> Tuple[MIMEMultipart, List[str]]:
- """
- Build a MIME message that can be used to send an email and
- returns full list of recipients.
-
- :param mail_from: Email address to set as email's from
- :param to: List of email addresses to set as email's to
- :param subject: Email's subject
- :param html_content: Content of email in HTML format
- :param files: List of paths of files to be attached
- :param cc: List of email addresses to set as email's CC
- :param bcc: List of email addresses to set as email's BCC
- :param mime_subtype: Can be used to specify the subtype of the message. Default = mixed
- :param mime_charset: Email's charset. Default = UTF-8.
- :param custom_headers: Additional headers to add to the MIME message.
- No validations are run on these values and they should be able to be encoded.
- :return: Email as MIMEMultipart and list of recipients' addresses.
- """
to = get_email_address_list(to)
msg = MIMEMultipart(mime_subtype)
msg['Subject'] = subject
- msg['From'] = mail_from
+ msg['From'] = smtp_mail_from
msg['To'] = ", ".join(to)
recipients = to
if cc:
@@ -138,18 +86,14 @@ def build_mime_message(
file.read(),
Name=basename
)
- part['Content-Disposition'] = f'attachment; filename="{basename}"'
- part['Content-ID'] = f'<{basename}>'
+ part['Content-Disposition'] = 'attachment; filename="%s"' % basename
+ part['Content-ID'] = '<%s>' % basename
msg.attach(part)
- if custom_headers:
- for header_key, header_value in custom_headers.items():
- msg[header_key] = header_value
-
- return msg, recipients
+ send_mime_email(smtp_mail_from, recipients, msg, dryrun)
-def send_mime_email(e_from: str, e_to: List[str], mime_msg: MIMEMultipart, dryrun: bool = False) -> None:
+def send_mime_email(e_from, e_to, mime_msg, dryrun=False):
"""
Send MIME email.
"""
diff --git a/docs/operators-and-hooks-ref.rst b/docs/operators-and-hooks-ref.rst
index 500eec8..8e883c9 100644
--- a/docs/operators-and-hooks-ref.rst
+++ b/docs/operators-and-hooks-ref.rst
@@ -486,12 +486,6 @@ These integrations allow you to perform various operations within the Amazon Web
:mod:`airflow.providers.amazon.aws.sensors.sagemaker_transform`,
:mod:`airflow.providers.amazon.aws.sensors.sagemaker_tuning`
- * - `Amazon Simple Email Service (SES) <https://aws.amazon.com/ses/>`__
- -
- - :mod:`airflow.providers.amazon.aws.hooks.ses`
- -
- -
-
* - `Amazon Simple Notification Service (SNS) <https://aws.amazon.com/sns/>`__
-
- :mod:`airflow.providers.amazon.aws.hooks.sns`
diff --git a/tests/providers/amazon/aws/hooks/test_ses.py b/tests/providers/amazon/aws/hooks/test_ses.py
deleted file mode 100644
index 555755d..0000000
--- a/tests/providers/amazon/aws/hooks/test_ses.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import boto3
-import pytest
-from moto import mock_ses
-
-from airflow.providers.amazon.aws.hooks.ses import SESHook
-
-boto3.setup_default_session()
-
-
-@mock_ses
-def test_get_conn():
- hook = SESHook(aws_conn_id='aws_default')
- assert hook.get_conn() is not None
-
-
-@mock_ses
-@pytest.mark.parametrize('to',
- [
- 'to@domain.com',
- ['to1@domain.com', 'to2@domain.com'],
- 'to1@domain.com,to2@domain.com'
- ])
-@pytest.mark.parametrize('cc',
- [
- 'cc@domain.com',
- ['cc1@domain.com', 'cc2@domain.com'],
- 'cc1@domain.com,cc2@domain.com'
- ])
-@pytest.mark.parametrize('bcc',
- [
- 'bcc@domain.com',
- ['bcc1@domain.com', 'bcc2@domain.com'],
- 'bcc1@domain.com,bcc2@domain.com'
- ])
-def test_send_email(to, cc, bcc):
- # Given
- hook = SESHook()
- ses_client = hook.get_conn()
- mail_from = 'test_from@domain.com'
-
- # Amazon only allows to send emails from verified addresses,
- # then we need to validate the from address before sending the email,
- # otherwise this test would raise a `botocore.errorfactory.MessageRejected` exception
- ses_client.verify_email_identity(EmailAddress=mail_from)
-
- # When
- response = hook.send_email(
- mail_from=mail_from,
- to=to,
- subject='subject',
- html_content='<html>Test</html>',
- cc=cc,
- bcc=bcc,
- reply_to='reply_to@domain.com',
- return_path='return_path@domain.com',
- )
-
- # Then
- assert response is not None
- assert isinstance(response, dict)
- assert 'MessageId' in response
diff --git a/tests/utils/test_email.py b/tests/utils/test_email.py
index e5e6671..cde9f0a 100644
--- a/tests/utils/test_email.py
+++ b/tests/utils/test_email.py
@@ -26,7 +26,7 @@ import mock
from airflow import utils
from airflow.configuration import conf
-from airflow.utils.email import build_mime_message, get_email_address_list
+from airflow.utils.email import get_email_address_list
from tests.test_utils.config import conf_vars
EMAILS = ['test1@example.com', 'test2@example.com']
@@ -96,28 +96,6 @@ class TestEmail(unittest.TestCase):
cc=None, bcc=None, mime_charset='utf-8', mime_subtype='mixed')
self.assertFalse(mock_send_email.called)
- def test_build_mime_message(self):
- mail_from = 'from@example.com'
- mail_to = 'to@example.com'
- subject = 'test subject'
- html_content = '<html>Test</html>'
- custom_headers = {'Reply-To': 'reply_to@example.com'}
-
- msg, recipients = build_mime_message(
- mail_from=mail_from,
- to=mail_to,
- subject=subject,
- html_content=html_content,
- custom_headers=custom_headers,
- )
-
- self.assertIn('From', msg)
- self.assertIn('To', msg)
- self.assertIn('Subject', msg)
- self.assertIn('Reply-To', msg)
- self.assertListEqual([mail_to], recipients)
- self.assertEqual(msg['To'], ','.join(recipients))
-
@conf_vars({('smtp', 'SMTP_SSL'): 'False'})
class TestEmailSmtp(unittest.TestCase):