Skip to content

Commit

Permalink
Merge pull request #128 from sendgrid/v3_beta
Browse files Browse the repository at this point in the history
V3 beta
  • Loading branch information
thinkingserious committed Sep 29, 2015
2 parents 298aeab + 6a41d33 commit 394f7bc
Show file tree
Hide file tree
Showing 25 changed files with 903 additions and 188 deletions.
3 changes: 3 additions & 0 deletions .env_sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SENDGRID_API_KEY=your_sendgrid_api_key
SENDGRID_USERNAME=your_sendgrid_username
SENDGRID_PASSWORD=your_sendgrid_password
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ sdist
*.egg
*.egg-info
*.pyc
.idea/
venv/
.idea
.env
.python-version
.tox/
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ notifications:
Build %{build_number}</a> on branch <i>%{branch}</i> by %{author}: <strong>%{message}</strong>
<a href="https://github.com/sendgrid/docs/commits/%{commit}">View on GitHub</a>'
format: html
notify: true
notify: false
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
# Change Log
All notable changes to this project will be documented in this file.

## [1.4.3] - 2015-10-22
## [1.5.3] - 2015-09-29
### Added
- Refactored tests and added Tox support
- Framework for Web API v3 endpoints
- Web API v3 endpionts: apikeys, ASM groups and ASM suppressions

### Fixed
- Python 3 Fix [#126](https://github.com/sendgrid/sendgrid-python/issues/126)

## [1.4.3] - 2015-09-22
### Fixed
- Reply To header now supports friendly name [#110](https://github.com/sendgrid/sendgrid-python/issues/110)

Expand Down
108 changes: 103 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Announcements

For users of our `Web API v3 endpoints`_, we have begun integrating v3 endpoints into this library. As part of this process we have implemented a test automation tool, TOX_. We are also updating and enhancing the core library code.

In no particular order, we have implemented a few of the v3 endpoints already and would appreciate your feedback. Please feel free to submit issues and pull requests on the `v3_beta branch`_.
In no particular order, we have implemented a `few of the v3`_ endpoints already and would appreciate your feedback.

Thank you for your continued support!

Expand Down Expand Up @@ -232,6 +232,76 @@ add_content_id
message.add_attachment('image.png', open('./image.png', 'rb'))
message.add_content_id('image.png', 'ID_IN_HTML')
message.set_html('<html><body>TEXT BEFORE IMAGE<img src="cid:ID_IN_HTML"></img>AFTER IMAGE</body></html>')
WEB API v3
----------

.. _APIKeysAnchor:

`APIKeys`_
~~~~~~~~~~

List all API Keys belonging to the authenticated user.

.. code:: python
client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
status, msg = client.apikeys.get()
`Advanced Suppression Manager (ASM)`_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Advanced Suppression Manager gives your recipients more control over the types of emails they want to receive by letting them opt out of messages from a certain type of email.

More information_.

.. _information: https://sendgrid.com/docs/API_Reference/Web_API_v3/Advanced_Suppression_Manager/index.html

ASM Groups
~~~~~~~~~~

Retrieve all suppression groups associated with the user.

.. code:: python
client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
status, msg = client.asm_groups.get()
Get a single record.

.. code:: python
status, msg = client.asm_groups.get(record_id)
ASM Suppressions
~~~~~~~~~~~~~~~~

Suppressions are email addresses that can be added to groups to prevent certain types of emails from being delivered to those addresses.

Add recipient addresses to the suppressions list for a given group.

.. code:: python
client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
group_id = <group_id_number> # If no group_id_number, the emails will be added to the global suppression group
emails = ['[email protected]', '[email protected]']
status, msg = client.asm_suppressions.post(group_id, emails)
Get suppressed addresses for a given group.

.. code:: python
status, msg = client.asm_suppressions.get(<group_id>)
Get suppression groups associated with a given recipient address.

.. code:: python
status, msg = client.asm_suppressions.get(None,<email_address>)
Delete a recipient email from the suppressions list for a group.

status, msg = client.asm_suppressions.delete(<group_id>,<email_address>)

SendGrid's `X-SMTPAPI`_
-----------------------
Expand Down Expand Up @@ -380,20 +450,48 @@ set_asm_group_id
Using Templates from the Template Engine
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code:: python


message.add_filter('templates', 'enable', '1')
message.add_filter('templates', 'template_id', 'TEMPLATE-ALPHA-NUMERIC-ID')

Tests
~~~~~

**Prerequisites:**

- Mac OS X Prerequisite:

.. code:: python
xcode-select --install
- Install pyenv and tox

.. code:: python
brew update
brew install pyenv
pip install tox
- Add `eval "$(pyenv init -)"` to your profile after installing tox, you only need to do this once.

.. code:: python
pyenv install 2.6.9
pyenv install 2.7.8
pyenv install 3.2.6
**Run the tests:**

.. code:: python
virtualenv venv
source venv/bin/activate
source venv/bin/activate #or . ./activate.sh
python setup.py install
python test/__init__.py
pyenv local 3.2.6 2.7.8 2.6.9
pyenv rehash
tox
Deploying
~~~~~~~~~
Expand All @@ -419,4 +517,4 @@ MIT License
.. _Filter: http://sendgrid.com/docs/API_Reference/SMTP_API/apps.html
.. _`Web API v3 endpoints`: https://sendgrid.com/docs/API_Reference/Web_API_v3/index.html
.. _TOX: https://testrun.org/tox/latest/
.. _`v3_beta branch`: https://github.com/sendgrid/sendgrid-python/tree/v3_beta
.. _`few of the v3`: APIKeysAnchor_
4 changes: 4 additions & 0 deletions activate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Use this to activate the virtual environment, use the following to execute in current shell
# . ./activate
source venv/bin/activate
19 changes: 19 additions & 0 deletions example_v2_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import sendgrid
import os
if os.path.exists('.env'):
for line in open('.env'):
var = line.strip().split('=')
if len(var) == 2:
os.environ[var[0]] = var[1]

sg = sendgrid.SendGridClient(os.environ.get('SENDGRID_USERNAME'), os.environ.get('SENDGRID_PASSWORD'))

message = sendgrid.Mail()
message.add_to('Elmer Thomas <[email protected]>')
message.set_subject('Testing from the Python library')
message.set_html('<b>This was a successful test!</b>')
message.set_text('This was a successful test!')
message.set_from('Elmer Thomas <[email protected]>')
status, msg = sg.send(message)
print status
print msg
76 changes: 76 additions & 0 deletions example_v3_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import sendgrid
import json

import os
if os.path.exists('.env'):
for line in open('.env'):
var = line.strip().split('=')
if len(var) == 2:
os.environ[var[0]] = var[1]



client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))

status, msg = client.asm_suppressions.delete(67,'[email protected]')
print status
print msg

"""
status, msg = client.asm_suppressions.post(60, ['[email protected]', '[email protected]'])
print status
print msg
status, msg = client.asm_suppressions.get(None,'[email protected]')
print status
print msg
status, msg = client.asm_groups.get([66,67,50])
print status
print msg
name = "My Amazing API Key"
status, msg = client.apikeys.post(name)
msg = json.loads(msg)
api_key_id = msg['api_key_id']
print status
print msg
name = "My NEW API Key 3000"
status, msg = client.apikeys.patch(api_key_id, name)
print status
print msg
status, msg = client.apikeys.delete(api_key_id)
print status
status, msg = client.apikeys.get()
print status
print msg
# Get a list of all valid API Keys from your account
status, msg = client.apikeys.get()
print status
print msg
# Create a new API Key
name = "My API Key 10"
status, msg = client.apikeys.post(name)
print status
print msg
# Delete an API Key with a given api_key_id
api_key_id = "zc0r5sW5TTuBQGsMPMUx0A"
status, msg = client.apikeys.delete(api_key_id)
print status
print msg
# Update the name of an API Key, given an api_key_id
api_key_id = "API_KEY"
name = "My API Key 3"
status, msg = client.apikeys.patch(api_key_id, name)
print status
print msg
"""
3 changes: 3 additions & 0 deletions sendgrid/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from .version import __version__
from .sendgrid import SendGridClient
from .exceptions import SendGridError, SendGridClientError, SendGridServerError
#v2 API
from .message import Mail
#v3 API
from .client import SendGridAPIClient
94 changes: 94 additions & 0 deletions sendgrid/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import json
from .version import __version__
from socket import timeout
try:
import urllib.request as urllib_request
from urllib.parse import urlencode
from urllib.error import HTTPError
except ImportError: # Python 2
import urllib2 as urllib_request
from urllib2 import HTTPError
from urllib import urlencode

from .exceptions import SendGridClientError, SendGridServerError
from .resources.apikeys import APIKeys
from .resources.asm_groups import ASMGroups
from .resources.asm_suppressions import ASMSuppressions

class SendGridAPIClient(object):

"""SendGrid API."""

def __init__(self, apikey, **opts):
"""
Construct SendGrid API object.
Args:
apikey: SendGrid API key
opts: You can pass in host or proxies
"""
self._apikey = apikey
self.useragent = 'sendgrid/' + __version__ + ';python_v3'
self.host = opts.get('host', 'https://api.sendgrid.com')
# urllib cannot connect to SSL servers using proxies
self.proxies = opts.get('proxies', None)

self.apikeys = APIKeys(self)
self.asm_groups = ASMGroups(self)
self.asm_suppressions = ASMSuppressions(self)

@property
def apikey(self):
return self._apikey

@apikey.setter
def apikey(self, value):
self._apikey = value

def _build_request(self, url, json_header=False, method='GET', data=None):
if self.proxies:
proxy_support = urllib_request.ProxyHandler(self.proxies)
opener = urllib_request.build_opener(proxy_support)
urllib_request.install_opener(opener)
req = urllib_request.Request(url)
req.get_method = lambda: method
req.add_header('User-Agent', self.useragent)
req.add_header('Authorization', 'Bearer ' + self.apikey)
if json_header:
req.add_header('Content-Type', 'application/json')
try:
if data:
response = urllib_request.urlopen(req, json.dumps(data))
else:
response = urllib_request.urlopen(req, timeout=10)
except HTTPError as e:
if 400 <= e.code < 500:
raise SendGridClientError(e.code, e.read())
elif 500 <= e.code < 600:
raise SendGridServerError(e.code, e.read())
else:
assert False
except timeout as e:
raise SendGridClientError(408, 'Request timeout')
body = response.read()
return response.getcode(), body

def get(self, api):
url = self.host + api.endpoint
response, body = self._build_request(url, False, 'GET')
return response, body

def post(self, api, data):
url = self.host + api.endpoint
response, body = self._build_request(url, True, 'POST', data)
return response, body

def delete(self, api):
url = self.host + api.endpoint
response, body = self._build_request(url, False, 'DELETE')
return response, body

def patch(self, api, data):
url = self.host + api.endpoint
response, body = self._build_request(url, True, 'PATCH', data)
return response, body
Loading

0 comments on commit 394f7bc

Please sign in to comment.