Skip to content

Commit 394f7bc

Browse files
Merge pull request #128 from sendgrid/v3_beta
V3 beta
2 parents 298aeab + 6a41d33 commit 394f7bc

25 files changed

+903
-188
lines changed

.env_sample

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SENDGRID_API_KEY=your_sendgrid_api_key
2+
SENDGRID_USERNAME=your_sendgrid_username
3+
SENDGRID_PASSWORD=your_sendgrid_password

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ sdist
88
*.egg
99
*.egg-info
1010
*.pyc
11-
.idea/
1211
venv/
12+
.idea
13+
.env
14+
.python-version
15+
.tox/

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ notifications:
1616
Build %{build_number}</a> on branch <i>%{branch}</i> by %{author}: <strong>%{message}</strong>
1717
<a href="https://github.com/sendgrid/docs/commits/%{commit}">View on GitHub</a>'
1818
format: html
19-
notify: true
19+
notify: false

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
# Change Log
22
All notable changes to this project will be documented in this file.
33

4-
## [1.4.3] - 2015-10-22
4+
## [1.5.3] - 2015-09-29
5+
### Added
6+
- Refactored tests and added Tox support
7+
- Framework for Web API v3 endpoints
8+
- Web API v3 endpionts: apikeys, ASM groups and ASM suppressions
9+
10+
### Fixed
11+
- Python 3 Fix [#126](https://github.com/sendgrid/sendgrid-python/issues/126)
12+
13+
## [1.4.3] - 2015-09-22
514
### Fixed
615
- Reply To header now supports friendly name [#110](https://github.com/sendgrid/sendgrid-python/issues/110)
716

README.rst

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Announcements
1717

1818
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.
1919

20-
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`_.
20+
In no particular order, we have implemented a `few of the v3`_ endpoints already and would appreciate your feedback.
2121

2222
Thank you for your continued support!
2323

@@ -232,6 +232,76 @@ add_content_id
232232
message.add_attachment('image.png', open('./image.png', 'rb'))
233233
message.add_content_id('image.png', 'ID_IN_HTML')
234234
message.set_html('<html><body>TEXT BEFORE IMAGE<img src="cid:ID_IN_HTML"></img>AFTER IMAGE</body></html>')
235+
236+
WEB API v3
237+
----------
238+
239+
.. _APIKeysAnchor:
240+
241+
`APIKeys`_
242+
~~~~~~~~~~
243+
244+
List all API Keys belonging to the authenticated user.
245+
246+
.. code:: python
247+
248+
client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
249+
status, msg = client.apikeys.get()
250+
251+
`Advanced Suppression Manager (ASM)`_
252+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
253+
254+
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.
255+
256+
More information_.
257+
258+
.. _information: https://sendgrid.com/docs/API_Reference/Web_API_v3/Advanced_Suppression_Manager/index.html
259+
260+
ASM Groups
261+
~~~~~~~~~~
262+
263+
Retrieve all suppression groups associated with the user.
264+
265+
.. code:: python
266+
267+
client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
268+
status, msg = client.asm_groups.get()
269+
270+
Get a single record.
271+
272+
.. code:: python
273+
274+
status, msg = client.asm_groups.get(record_id)
275+
276+
ASM Suppressions
277+
~~~~~~~~~~~~~~~~
278+
279+
Suppressions are email addresses that can be added to groups to prevent certain types of emails from being delivered to those addresses.
280+
281+
Add recipient addresses to the suppressions list for a given group.
282+
283+
.. code:: python
284+
285+
client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
286+
group_id = <group_id_number> # If no group_id_number, the emails will be added to the global suppression group
287+
emails = ['elmer+test@thinkingserious.com', 'elmer+test2@thinkingserious.com']
288+
status, msg = client.asm_suppressions.post(group_id, emails)
289+
290+
Get suppressed addresses for a given group.
291+
292+
.. code:: python
293+
294+
status, msg = client.asm_suppressions.get(<group_id>)
295+
296+
Get suppression groups associated with a given recipient address.
297+
298+
.. code:: python
299+
300+
status, msg = client.asm_suppressions.get(None,<email_address>)
301+
302+
Delete a recipient email from the suppressions list for a group.
303+
304+
status, msg = client.asm_suppressions.delete(<group_id>,<email_address>)
235305

236306
SendGrid's `X-SMTPAPI`_
237307
-----------------------
@@ -380,20 +450,48 @@ set_asm_group_id
380450
Using Templates from the Template Engine
381451
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
382452

383-
.. code:: python
453+
384454

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

388458
Tests
389459
~~~~~
390460

461+
**Prerequisites:**
462+
463+
- Mac OS X Prerequisite:
464+
465+
.. code:: python
466+
467+
xcode-select --install
468+
469+
- Install pyenv and tox
470+
471+
.. code:: python
472+
473+
brew update
474+
brew install pyenv
475+
pip install tox
476+
477+
- Add `eval "$(pyenv init -)"` to your profile after installing tox, you only need to do this once.
478+
479+
.. code:: python
480+
481+
pyenv install 2.6.9
482+
pyenv install 2.7.8
483+
pyenv install 3.2.6
484+
485+
**Run the tests:**
486+
391487
.. code:: python
392488
393489
virtualenv venv
394-
source venv/bin/activate
490+
source venv/bin/activate #or . ./activate.sh
395491
python setup.py install
396-
python test/__init__.py
492+
pyenv local 3.2.6 2.7.8 2.6.9
493+
pyenv rehash
494+
tox
397495
398496
Deploying
399497
~~~~~~~~~
@@ -419,4 +517,4 @@ MIT License
419517
.. _Filter: http://sendgrid.com/docs/API_Reference/SMTP_API/apps.html
420518
.. _`Web API v3 endpoints`: https://sendgrid.com/docs/API_Reference/Web_API_v3/index.html
421519
.. _TOX: https://testrun.org/tox/latest/
422-
.. _`v3_beta branch`: https://github.com/sendgrid/sendgrid-python/tree/v3_beta
520+
.. _`few of the v3`: APIKeysAnchor_

activate.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
# Use this to activate the virtual environment, use the following to execute in current shell
3+
# . ./activate
4+
source venv/bin/activate

example_v2_test.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import sendgrid
2+
import os
3+
if os.path.exists('.env'):
4+
for line in open('.env'):
5+
var = line.strip().split('=')
6+
if len(var) == 2:
7+
os.environ[var[0]] = var[1]
8+
9+
sg = sendgrid.SendGridClient(os.environ.get('SENDGRID_USERNAME'), os.environ.get('SENDGRID_PASSWORD'))
10+
11+
message = sendgrid.Mail()
12+
message.add_to('Elmer Thomas <elmer@thinkingserious.com>')
13+
message.set_subject('Testing from the Python library')
14+
message.set_html('<b>This was a successful test!</b>')
15+
message.set_text('This was a successful test!')
16+
message.set_from('Elmer Thomas <elmer@thinkingserious.com>')
17+
status, msg = sg.send(message)
18+
print status
19+
print msg

example_v3_test.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import sendgrid
2+
import json
3+
4+
import os
5+
if os.path.exists('.env'):
6+
for line in open('.env'):
7+
var = line.strip().split('=')
8+
if len(var) == 2:
9+
os.environ[var[0]] = var[1]
10+
11+
12+
13+
client = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
14+
15+
status, msg = client.asm_suppressions.delete(67,'elmer+test@thinkingserious.com')
16+
print status
17+
print msg
18+
19+
"""
20+
21+
status, msg = client.asm_suppressions.post(60, ['elmer+test@thinkingserious.com', 'elmer.thomas@yahoo.com'])
22+
print status
23+
print msg
24+
25+
26+
status, msg = client.asm_suppressions.get(None,'elmer.thomas@yahoo.com')
27+
print status
28+
print msg
29+
30+
status, msg = client.asm_groups.get([66,67,50])
31+
print status
32+
print msg
33+
34+
name = "My Amazing API Key"
35+
status, msg = client.apikeys.post(name)
36+
msg = json.loads(msg)
37+
api_key_id = msg['api_key_id']
38+
print status
39+
print msg
40+
41+
name = "My NEW API Key 3000"
42+
status, msg = client.apikeys.patch(api_key_id, name)
43+
print status
44+
print msg
45+
46+
status, msg = client.apikeys.delete(api_key_id)
47+
print status
48+
49+
status, msg = client.apikeys.get()
50+
print status
51+
print msg
52+
53+
# Get a list of all valid API Keys from your account
54+
status, msg = client.apikeys.get()
55+
print status
56+
print msg
57+
58+
# Create a new API Key
59+
name = "My API Key 10"
60+
status, msg = client.apikeys.post(name)
61+
print status
62+
print msg
63+
64+
# Delete an API Key with a given api_key_id
65+
api_key_id = "zc0r5sW5TTuBQGsMPMUx0A"
66+
status, msg = client.apikeys.delete(api_key_id)
67+
print status
68+
print msg
69+
70+
# Update the name of an API Key, given an api_key_id
71+
api_key_id = "API_KEY"
72+
name = "My API Key 3"
73+
status, msg = client.apikeys.patch(api_key_id, name)
74+
print status
75+
print msg
76+
"""

sendgrid/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
from .version import __version__
22
from .sendgrid import SendGridClient
33
from .exceptions import SendGridError, SendGridClientError, SendGridServerError
4+
#v2 API
45
from .message import Mail
6+
#v3 API
7+
from .client import SendGridAPIClient

sendgrid/client.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import json
2+
from .version import __version__
3+
from socket import timeout
4+
try:
5+
import urllib.request as urllib_request
6+
from urllib.parse import urlencode
7+
from urllib.error import HTTPError
8+
except ImportError: # Python 2
9+
import urllib2 as urllib_request
10+
from urllib2 import HTTPError
11+
from urllib import urlencode
12+
13+
from .exceptions import SendGridClientError, SendGridServerError
14+
from .resources.apikeys import APIKeys
15+
from .resources.asm_groups import ASMGroups
16+
from .resources.asm_suppressions import ASMSuppressions
17+
18+
class SendGridAPIClient(object):
19+
20+
"""SendGrid API."""
21+
22+
def __init__(self, apikey, **opts):
23+
"""
24+
Construct SendGrid API object.
25+
26+
Args:
27+
apikey: SendGrid API key
28+
opts: You can pass in host or proxies
29+
"""
30+
self._apikey = apikey
31+
self.useragent = 'sendgrid/' + __version__ + ';python_v3'
32+
self.host = opts.get('host', 'https://api.sendgrid.com')
33+
# urllib cannot connect to SSL servers using proxies
34+
self.proxies = opts.get('proxies', None)
35+
36+
self.apikeys = APIKeys(self)
37+
self.asm_groups = ASMGroups(self)
38+
self.asm_suppressions = ASMSuppressions(self)
39+
40+
@property
41+
def apikey(self):
42+
return self._apikey
43+
44+
@apikey.setter
45+
def apikey(self, value):
46+
self._apikey = value
47+
48+
def _build_request(self, url, json_header=False, method='GET', data=None):
49+
if self.proxies:
50+
proxy_support = urllib_request.ProxyHandler(self.proxies)
51+
opener = urllib_request.build_opener(proxy_support)
52+
urllib_request.install_opener(opener)
53+
req = urllib_request.Request(url)
54+
req.get_method = lambda: method
55+
req.add_header('User-Agent', self.useragent)
56+
req.add_header('Authorization', 'Bearer ' + self.apikey)
57+
if json_header:
58+
req.add_header('Content-Type', 'application/json')
59+
try:
60+
if data:
61+
response = urllib_request.urlopen(req, json.dumps(data))
62+
else:
63+
response = urllib_request.urlopen(req, timeout=10)
64+
except HTTPError as e:
65+
if 400 <= e.code < 500:
66+
raise SendGridClientError(e.code, e.read())
67+
elif 500 <= e.code < 600:
68+
raise SendGridServerError(e.code, e.read())
69+
else:
70+
assert False
71+
except timeout as e:
72+
raise SendGridClientError(408, 'Request timeout')
73+
body = response.read()
74+
return response.getcode(), body
75+
76+
def get(self, api):
77+
url = self.host + api.endpoint
78+
response, body = self._build_request(url, False, 'GET')
79+
return response, body
80+
81+
def post(self, api, data):
82+
url = self.host + api.endpoint
83+
response, body = self._build_request(url, True, 'POST', data)
84+
return response, body
85+
86+
def delete(self, api):
87+
url = self.host + api.endpoint
88+
response, body = self._build_request(url, False, 'DELETE')
89+
return response, body
90+
91+
def patch(self, api, data):
92+
url = self.host + api.endpoint
93+
response, body = self._build_request(url, True, 'PATCH', data)
94+
return response, body

0 commit comments

Comments
 (0)