From 4b40e34c3e224edab5a61cfdae52f6cf9ef1ecf5 Mon Sep 17 00:00:00 2001 From: iLan Epstein Date: Thu, 23 Feb 2023 12:41:13 -0800 Subject: [PATCH 1/4] Initial reply_to_list implementation --- sendgrid/helpers/mail/mail.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sendgrid/helpers/mail/mail.py b/sendgrid/helpers/mail/mail.py index ba21f789..2b58b58d 100644 --- a/sendgrid/helpers/mail/mail.py +++ b/sendgrid/helpers/mail/mail.py @@ -60,6 +60,7 @@ def __init__( self._ip_pool_name = None self._mail_settings = None self._reply_to = None + self._reply_to_list = None self._send_at = None self._subject = None self._template_id = None @@ -695,6 +696,33 @@ def reply_to(self, value): value = ReplyTo(value[0], value[1]) self._reply_to = value + @property + def reply_to_list(self): + """A list of ReplyTo email addresses + + :rtype: list(ReplyTo), tuple + """ + return self._reply_to_list + + @reply_to_list.setter + def reply_to_list(self, value): + """A list of ReplyTo email addresses + + :param value: A list of ReplyTo email addresses + :type value: list(ReplyTo), tuple + """ + if isinstance(value, list): + for reply in value: + if isinstance(reply, ReplyTo): + if not isinstance(reply.email, str): + raise ValueError('You must provide an email for each entry in a reply_to_list') + reply = ReplyTo(reply.email, reply.name) + else: + raise ValueError( + 'Please use a list of ReplyTos for a reply_to_list.' + ) + self._reply_to_list = value + @property def contents(self): """The contents of the email @@ -981,6 +1009,7 @@ def get(self): 'mail_settings': self._get_or_none(self.mail_settings), 'tracking_settings': self._get_or_none(self.tracking_settings), 'reply_to': self._get_or_none(self.reply_to), + 'reply_to_list': [r.get() for r in self.reply_to_list or []], } return {key: value for key, value in mail.items() From e222fd05fb8d87fd80dc030a4a58152ff6e135d7 Mon Sep 17 00:00:00 2001 From: iLan Epstein Date: Thu, 23 Feb 2023 14:07:32 -0800 Subject: [PATCH 2/4] Add tests --- test/unit/test_mail_helpers.py | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/test/unit/test_mail_helpers.py b/test/unit/test_mail_helpers.py index a7d08d89..c00307d4 100644 --- a/test/unit/test_mail_helpers.py +++ b/test/unit/test_mail_helpers.py @@ -235,6 +235,58 @@ def test_send_a_single_email_to_multiple_recipients(self): }''') ) + def test_send_a_single_email_with_multiple_reply_to_addresses(self): + from sendgrid.helpers.mail import (Mail, From, ReplyTo, To, Subject, + PlainTextContent, HtmlContent) + self.maxDiff = None + message = Mail( + from_email=From('test+from@example.com', 'Example From Name'), + to_emails=To('test+to0@example.com', 'Example To Name'), + subject=Subject('Sending with SendGrid is Fun'), + plain_text_content=PlainTextContent('and easy to do anywhere, even with Python'), + html_content=HtmlContent('and easy to do anywhere, even with Python')) + + message.reply_to_list = [ReplyTo(email = 'test+reply_to_1@example.com'), ReplyTo(email = 'test+reply_to_2@example.com')] + + self.assertEqual( + message.get(), + json.loads(r'''{ + "content": [ + { + "type": "text/plain", + "value": "and easy to do anywhere, even with Python" + }, + { + "type": "text/html", + "value": "and easy to do anywhere, even with Python" + } + ], + "from": { + "email": "test+from@example.com", + "name": "Example From Name" + }, + "personalizations": [ + { + "to": [ + { + "email": "test+to0@example.com", + "name": "Example To Name" + } + ] + } + ], + "reply_to_list": [ + { + "email": "test+reply_to_1@example.com" + }, + { + "email": "test+reply_to_2@example.com" + } + ], + "subject": "Sending with SendGrid is Fun" + }''') + ) + def test_multiple_emails_to_multiple_recipients(self): from sendgrid.helpers.mail import (Mail, From, To, Subject, PlainTextContent, HtmlContent, @@ -568,6 +620,44 @@ def test_value_error_is_raised_on_to_emails_set_to_list_of_lists(self): 'and easy to do anywhere, even with Python'), html_content=HtmlContent( 'and easy to do anywhere, even with Python')) + + def test_value_error_is_raised_on_to_emails_set_to_reply_to_list_of_strs(self): + from sendgrid.helpers.mail import (PlainTextContent, HtmlContent) + self.maxDiff = None + to_emails = [ + ('test+to0@example.com', 'Example To Name 0'), + ('test+to1@example.com', 'Example To Name 1') + ] + + mail = Mail( + from_email=From('test+from@example.com', 'Example From Name'), + to_emails=to_emails, + subject=Subject('Sending with SendGrid is Fun'), + plain_text_content=PlainTextContent( + 'and easy to do anywhere, even with Python'), + html_content=HtmlContent( + 'and easy to do anywhere, even with Python')) + with self.assertRaises(ValueError): + mail.reply_to_list = ['test+reply_to0@example.com', 'test+reply_to1@example.com'] + + def test_value_error_is_raised_on_to_emails_set_to_reply_to_list_of_tuples(self): + from sendgrid.helpers.mail import (PlainTextContent, HtmlContent) + self.maxDiff = None + to_emails = [ + ('test+to0@example.com', 'Example To Name 0'), + ('test+to1@example.com', 'Example To Name 1') + ] + + mail = Mail( + from_email=From('test+from@example.com', 'Example From Name'), + to_emails=to_emails, + subject=Subject('Sending with SendGrid is Fun'), + plain_text_content=PlainTextContent( + 'and easy to do anywhere, even with Python'), + html_content=HtmlContent( + 'and easy to do anywhere, even with Python')) + with self.assertRaises(ValueError): + mail.reply_to_list = [('test+reply_to@example.com', 'Test Name')] def test_error_is_not_raised_on_to_emails_set_to_list_of_tuples(self): from sendgrid.helpers.mail import (PlainTextContent, HtmlContent) From 068dd66604ebf429a31e80159a869de905f23a10 Mon Sep 17 00:00:00 2001 From: iLan Epstein Date: Thu, 23 Feb 2023 14:14:50 -0800 Subject: [PATCH 3/4] Add use case --- ..._email_with_multiple_reply_to_addresses.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 use_cases/send_a_single_email_with_multiple_reply_to_addresses.md diff --git a/use_cases/send_a_single_email_with_multiple_reply_to_addresses.md b/use_cases/send_a_single_email_with_multiple_reply_to_addresses.md new file mode 100644 index 00000000..55d6adf1 --- /dev/null +++ b/use_cases/send_a_single_email_with_multiple_reply_to_addresses.md @@ -0,0 +1,29 @@ +```python +import os +from sendgrid import SendGridAPIClient +from sendgrid.helpers.mail import Mail + +message = Mail( + from_email='from_email@example.com', + to_emails='to@example.com', + subject='Sending with Twilio SendGrid is Fun', + html_content='and easy to do anywhere, even with Python') +message.reply_to_list = [ + ReplyTo( + email='reply-to-1@example.com', + name="Reply To Name 1", + ), + ReplyTo( + email='reply-to-2@example.com', + name="Reply To Name 2", + ) +] +try: + sendgrid_client = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY')) + response = sendgrid_client.send(message) + print(response.status_code) + print(response.body) + print(response.headers) +except Exception as e: + print(e) +``` From a1d4e170f2a0a808804b39a00d2346fe4bcbc83f Mon Sep 17 00:00:00 2001 From: iLan Epstein Date: Tue, 21 Mar 2023 11:15:27 -0700 Subject: [PATCH 4/4] Remove unnecessary line and move assignment out of for loop (PR Feedback) --- sendgrid/helpers/mail/mail.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sendgrid/helpers/mail/mail.py b/sendgrid/helpers/mail/mail.py index 2b58b58d..2472ad7d 100644 --- a/sendgrid/helpers/mail/mail.py +++ b/sendgrid/helpers/mail/mail.py @@ -716,12 +716,11 @@ def reply_to_list(self, value): if isinstance(reply, ReplyTo): if not isinstance(reply.email, str): raise ValueError('You must provide an email for each entry in a reply_to_list') - reply = ReplyTo(reply.email, reply.name) else: raise ValueError( 'Please use a list of ReplyTos for a reply_to_list.' ) - self._reply_to_list = value + self._reply_to_list = value @property def contents(self):