Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Twilio Email #882

Merged
merged 1 commit into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions sendgrid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
Modules to help with common tasks.
"""

from .version import __version__
from .sendgrid import SendGridAPIClient # noqa
from .helpers.mail import * # noqa
from .helpers.endpoints import * # noqa
# from .helpers.inbound import * # noqa
from .helpers.mail import * # noqa
from .helpers.stats import * # noqa
from .sendgrid import SendGridAPIClient # noqa
from .twilio_email import TwilioEmailAPIClient # noqa
from .version import __version__
62 changes: 62 additions & 0 deletions sendgrid/base_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import python_http_client


class BaseInterface(object):
def __init__(self, auth, host, impersonate_subuser):
"""
Construct the Twilio SendGrid v3 API object.
Note that the underlying client is being set up during initialization,
therefore changing attributes in runtime will not affect HTTP client
behaviour.

:param auth: the authorization header
:type auth: string
:param impersonate_subuser: the subuser to impersonate. Will be passed
by "On-Behalf-Of" header by underlying
client. See
https://sendgrid.com/docs/User_Guide/Settings/subusers.html
for more details
:type impersonate_subuser: string
:param host: base URL for API calls
:type host: string
"""
from . import __version__
self.auth = auth
self.host = host
self.impersonate_subuser = impersonate_subuser
self.version = __version__
self.useragent = 'sendgrid/{};python'.format(self.version)

self.client = python_http_client.Client(
host=self.host,
request_headers=self._default_headers,
version=3)

@property
def _default_headers(self):
"""Set the default header for a Twilio SendGrid v3 API call"""
headers = {
"Authorization": self.auth,
"User-Agent": self.useragent,
"Accept": 'application/json'
}
if self.impersonate_subuser:
headers['On-Behalf-Of'] = self.impersonate_subuser

return headers

def reset_request_headers(self):
self.client.request_headers = self._default_headers

def send(self, message):
"""Make a Twilio SendGrid v3 API request with the request body generated by
the Mail object

:param message: The Twilio SendGrid v3 API request body generated by the Mail
object
:type message: Mail
"""
if not isinstance(message, dict):
message = message.get()

return self.client.mail.send.post(request_body=message)
56 changes: 9 additions & 47 deletions sendgrid/sendgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@

import os

import python_http_client
from .base_interface import BaseInterface


class SendGridAPIClient(object):
class SendGridAPIClient(BaseInterface):
"""The Twilio SendGrid API Client.

Use this object to interact with the v3 API. For example:
sg = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
Use this object to interact with the v3 API. For example:
mail_client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
...
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())
response = mail_client.send(mail)

For examples and detailed use instructions, see
https://github.com/sendgrid/sendgrid-python
Expand All @@ -40,8 +40,8 @@ def __init__(
therefore changing attributes in runtime will not affect HTTP client
behaviour.

:param api_key: Twilio SendGrid API key to use. If not provided, key will be
read from environment variable "SENDGRID_API_KEY"
:param api_key: Twilio SendGrid API key to use. If not provided, value
will be read from environment variable "SENDGRID_API_KEY"
:type api_key: string
:param impersonate_subuser: the subuser to impersonate. Will be passed
by "On-Behalf-Of" header by underlying
Expand All @@ -52,45 +52,7 @@ def __init__(
:param host: base URL for API calls
:type host: string
"""
from . import __version__
self.api_key = api_key or os.environ.get('SENDGRID_API_KEY')
self.impersonate_subuser = impersonate_subuser
self.host = host
self.version = __version__
self.useragent = 'sendgrid/{};python'.format(self.version)
auth = 'Bearer {}'.format(self.api_key)

self.client = python_http_client.Client(
host=self.host,
request_headers=self._default_headers,
version=3)

@property
def _default_headers(self):
"""Set the default header for a Twilio SendGrid v3 API call"""
headers = {
"Authorization": 'Bearer {}'.format(self.api_key),
"User-Agent": self.useragent,
"Accept": 'application/json'
}
if self.impersonate_subuser:
headers['On-Behalf-Of'] = self.impersonate_subuser

return headers

def reset_request_headers(self):

self.client.request_headers = self._default_headers

def send(self, message):
"""Make a Twilio SendGrid v3 API request with the request body generated by
the Mail object

:param message: The Twilio SendGrid v3 API request body generated by the Mail
object
:type message: Mail
"""
if isinstance(message, dict):
response = self.client.mail.send.post(request_body=message)
else:
response = self.client.mail.send.post(request_body=message.get())
return response
super(SendGridAPIClient, self).__init__(auth, host, impersonate_subuser)
73 changes: 73 additions & 0 deletions sendgrid/twilio_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""
This library allows you to quickly and easily use the Twilio Email Web API v3 via Python.

For more information on this library, see the README on GitHub.
https://github.com/sendgrid/sendgrid-python
For more information on the Twilio SendGrid v3 API, see the v3 docs:
http://sendgrid.com/docs/API_Reference/api_v3.html
For the user guide, code examples, and more, visit the main docs page:
http://sendgrid.com/docs/index.html

This file provides the Twilio Email API Client.
"""
import os
from base64 import b64encode

from .base_interface import BaseInterface


class TwilioEmailAPIClient(BaseInterface):
"""The Twilio Email API Client.

Use this object to interact with the v3 API. For example:
mail_client = sendgrid.TwilioEmailAPIClient(os.environ.get('TWILIO_API_KEY'),
os.environ.get('TWILIO_API_SECRET'))
...
mail = Mail(from_email, subject, to_email, content)
response = mail_client.send(mail)

For examples and detailed use instructions, see
https://github.com/sendgrid/sendgrid-python
"""

def __init__(
self,
username=None,
password=None,
host='https://email.twilio.com',
impersonate_subuser=None):
"""
Construct the Twilio Email v3 API object.
Note that the underlying client is being set up during initialization,
therefore changing attributes in runtime will not affect HTTP client
behaviour.

:param username: Twilio Email API key SID or Account SID to use. If not
provided, value will be read from the environment
variable "TWILIO_API_KEY" or "TWILIO_ACCOUNT_SID"
:type username: string
:param password: Twilio Email API key secret or Account Auth Token to
use. If not provided, value will be read from the
environment variable "TWILIO_API_SECRET" or
"TWILIO_AUTH_TOKEN"
:type password: string
:param impersonate_subuser: the subuser to impersonate. Will be passed
by "On-Behalf-Of" header by underlying
client. See
https://sendgrid.com/docs/User_Guide/Settings/subusers.html
for more details
:type impersonate_subuser: string
:param host: base URL for API calls
:type host: string
"""
self.username = username or \
os.environ.get('TWILIO_API_KEY') or \
os.environ.get('TWILIO_ACCOUNT_SID')

self.password = password or \
os.environ.get('TWILIO_API_SECRET') or \
os.environ.get('TWILIO_AUTH_TOKEN')

auth = 'Basic ' + b64encode('{}:{}'.format(self.username, self.password).encode()).decode()

super(TwilioEmailAPIClient, self).__init__(auth, host, impersonate_subuser)
18 changes: 5 additions & 13 deletions test/test_sendgrid.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import sendgrid
from sendgrid.helpers.endpoints.ip.unassigned import unassigned
from sendgrid.helpers.mail import *
import os
import datetime
import os
import unittest

import sendgrid
from sendgrid.helpers.endpoints.ip.unassigned import unassigned

host = "http://localhost:4010"


Expand All @@ -18,12 +18,9 @@ def setUpClass(cls):
os.path.dirname(__file__)), '/..')
cls.sg = sendgrid.SendGridAPIClient(host=host)
cls.devnull = open(os.devnull, 'w')
prism_cmd = None

def test_api_key_init(self):
self.assertEqual(self.sg.api_key, os.environ.get('SENDGRID_API_KEY'))
# Support the previous naming convention for API keys
self.assertEqual(self.sg.api_key, self.sg.api_key)
my_sendgrid = sendgrid.SendGridAPIClient(api_key="THISISMYKEY")
self.assertEqual(my_sendgrid.api_key, "THISISMYKEY")

Expand Down Expand Up @@ -2305,7 +2302,7 @@ def test_whitelabel_links__link_id__subuser_post(self):

def test_license_year(self):
LICENSE_FILE = 'LICENSE.md'
copyright_line=''
copyright_line = ''
with open(LICENSE_FILE, 'r') as f:
for line in f:
if line.startswith('Copyright'):
Expand All @@ -2314,8 +2311,3 @@ def test_license_year(self):
self.assertEqual(
'Copyright (C) %s, Twilio SendGrid, Inc. <[email protected]>' % datetime.datetime.now().year,
copyright_line)

# @classmethod
# def tearDownClass(cls):
# cls.p.kill()
# print("Prism Shut Down")
37 changes: 37 additions & 0 deletions test/test_twilio_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import os
import unittest

from sendgrid import TwilioEmailAPIClient


class UnitTests(unittest.TestCase):

@classmethod
def setUpClass(cls):
os.environ['TWILIO_API_KEY'] = 'api-key'
os.environ['TWILIO_API_SECRET'] = 'api-secret'
os.environ['TWILIO_ACCOUNT_SID'] = 'account-sid'
os.environ['TWILIO_AUTH_TOKEN'] = 'auth-token'

def test_init_key_over_token(self):
mail_client = TwilioEmailAPIClient()

self.assertEqual(mail_client.username, 'api-key')
self.assertEqual(mail_client.password, 'api-secret')
self.assertEqual(mail_client.host, 'https://email.twilio.com')

def test_init_token(self):
del os.environ['TWILIO_API_KEY']
del os.environ['TWILIO_API_SECRET']

mail_client = TwilioEmailAPIClient()

self.assertEqual(mail_client.username, 'account-sid')
self.assertEqual(mail_client.password, 'auth-token')

def test_init_args(self):
mail_client = TwilioEmailAPIClient('username', 'password')

self.assertEqual(mail_client.username, 'username')
self.assertEqual(mail_client.password, 'password')
self.assertEqual(mail_client.auth, 'Basic dXNlcm5hbWU6cGFzc3dvcmQ=')
6 changes: 4 additions & 2 deletions use_cases/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ This directory provides examples for specific use cases of this library. Please
* [Integrate with Slack Events API](slack_event_api_integration.md)
* [Legacy Templates](legacy_templates.md)

### Working with SMS
* [Send a SMS Message](sms.md)
# Twilio Use Cases
* [Twilio Setup](twilio-setup.md)
* [Send an Email With Twilio Email (Pilot)](twilio-email.md)
* [Send an SMS Message](sms.md)

### Troubleshooting
* [Error Handling](error_handling.md)
Expand Down
50 changes: 9 additions & 41 deletions use_cases/sms.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,12 @@
Following are the steps to add Twilio SMS to your app:
First, follow the [Twilio Setup](twilio-setup.md) guide for creating a Twilio account and setting up environment variables with the proper credentials.

## 1. Obtain a Free Twilio Account

Sign up for a free Twilio account [here](https://www.twilio.com/try-twilio?source=sendgrid-python).

## 2. Update Your Environment Variables

You can obtain your Account Sid and Auth Token from [twilio.com/console](https://twilio.com/console).

### Mac
Then, install the Twilio Helper Library.

```bash
echo "export TWILIO_ACCOUNT_SID='YOUR_TWILIO_ACCOUNT_SID'" > twilio.env
echo "export TWILIO_AUTH_TOKEN='YOUR_TWILIO_AUTH_TOKEN'" >> twilio.env
echo "twilio.env" >> .gitignore
source ./twilio.env
pip install twilio
```

### Windows

Temporarily set the environment variable (accessible only during the current CLI session):

```bash
set TWILIO_ACCOUNT_SID=YOUR_TWILIO_ACCOUNT_SID
set TWILIO_AUTH_TOKEN=YOUR_TWILIO_AUTH_TOKEN
```

Permanently set the environment variable (accessible in all subsequent CLI sessions):

```bash
setx TWILIO_ACCOUNT_SID "YOUR_TWILIO_ACCOUNT_SID"
setx TWILIO_AUTH_TOKEN "YOUR_TWILIO_AUTH_TOKEN"
```

## 3. Install the Twilio Helper Library

`pip install twilio`

Then, you can execute the following code.
Finally, send a message.

```python
import os
Expand All @@ -50,12 +19,11 @@ to_number ='+15558675310'
body = "Join Earth's mightiest heroes. Like Kevin Bacon."
client = Client(account_sid, auth_token)

message = client.messages \
.create(
body=body,
from_=from_number,
to=to_number
)
message = client.messages.create(
body=body,
from_=from_number,
to=to_number
)

print(message.sid)
```
Expand Down
Loading