Skip to content

Commit 7483fd2

Browse files
author
Jon Wayne Parrott
committed
Adding mailgun sample
1 parent f2e19a5 commit 7483fd2

File tree

10 files changed

+216
-0
lines changed

10 files changed

+216
-0
lines changed

appengine/mailgun/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib

appengine/mailgun/README.md

Whitespace-only changes.

appengine/mailgun/__init__.py

Whitespace-only changes.

appengine/mailgun/app.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
runtime: python27
2+
threadsafe: yes
3+
api_version: 1
4+
5+
handlers:
6+
- url: .*
7+
script: main.app

appengine/mailgun/appengine_config.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2015 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from google.appengine.ext import vendor
16+
17+
# Add any libraries installed in the "lib" folder.
18+
vendor.add('lib')

appengine/mailgun/main.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2015 Google Inc.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
"""
18+
Sample Google App Engine application that sends mail using Mailgun.
19+
"""
20+
from urllib import urlencode
21+
22+
import httplib2
23+
import webapp2
24+
25+
26+
# Your Mailgun Domain Name
27+
MAILGUN_DOMAIN_NAME = 'your-mailgun-domain-name'
28+
# Your Mailgun API key
29+
MAILGUN_API_KEY = 'your-mailgun-api-key'
30+
31+
32+
# [START simple_message]
33+
def send_simple_message(recipient):
34+
http = httplib2.Http()
35+
http.add_credentials('api', MAILGUN_API_KEY)
36+
37+
url = 'https://api.mailgun.net/v3/{}/messages'.format(MAILGUN_DOMAIN_NAME)
38+
data = {
39+
'from': 'Example Sender <mailgun@{}>'.format(MAILGUN_DOMAIN_NAME),
40+
'to': recipient,
41+
'subject': 'This is an example email from Mailgun',
42+
'text': 'Test message from Mailgun'
43+
}
44+
45+
resp, content = http.request(url, 'POST', urlencode(data))
46+
47+
if resp.status != 200:
48+
raise RuntimeError(
49+
'Mailgun API error: {} {}'.format(resp.status, content))
50+
# [END simple_message]
51+
52+
53+
# [START complex_message]
54+
def send_complex_message(recipient):
55+
http = httplib2.Http()
56+
http.add_credentials('api', MAILGUN_API_KEY)
57+
58+
url = 'https://api.mailgun.net/v3/{}/messages'.format(MAILGUN_DOMAIN_NAME)
59+
data = {
60+
'from': 'Example Sender <mailgun@{}>'.format(MAILGUN_DOMAIN_NAME),
61+
'to': recipient,
62+
'subject': 'This is an example email from Mailgun',
63+
'text': 'Test message from Mailgun',
64+
'html': '<html>HTML <strong>version</strong> of the body</html>'
65+
}
66+
67+
resp, content = http.request(url, 'POST', urlencode(data))
68+
69+
if resp.status != 200:
70+
raise RuntimeError(
71+
'Mailgun API error: {} {}'.format(resp.status, content))
72+
# [END complex_message]
73+
74+
75+
class MainPage(webapp2.RequestHandler):
76+
def get(self):
77+
self.response.content_type = 'text/html'
78+
self.response.write("""
79+
<!doctype html>
80+
<html><body>
81+
<form method="POST">
82+
<input type="text" name="recipient" placeholder="Enter recipient email">
83+
<input type="submit" name="submit" value="Send simple email">
84+
<input type="submit" name="submit" value="Send complex email">
85+
</form>
86+
</body></html>
87+
""")
88+
89+
def post(self):
90+
recipient = self.request.get('recipient')
91+
action = self.request.get('submit')
92+
93+
if action == 'Send simple email':
94+
send_simple_message(recipient)
95+
else:
96+
send_complex_message(recipient)
97+
98+
self.response.write('Mail sent')
99+
100+
101+
app = webapp2.WSGIApplication([
102+
('/', MainPage)
103+
], debug=True)

appengine/mailgun/main_test.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright 2015 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from tests import AppEngineTestbedCase, Http2Mock
16+
import webtest
17+
18+
from . import main
19+
20+
21+
class TestMailgunHandlers(AppEngineTestbedCase):
22+
def setUp(self):
23+
super(TestMailgunHandlers, self).setUp()
24+
25+
self.app = webtest.TestApp(main.app)
26+
27+
def test_get(self):
28+
response = self.app.get('/')
29+
self.assertEqual(response.status_int, 200)
30+
31+
def test_post(self):
32+
http = Http2Mock(responses=[{}])
33+
34+
with http:
35+
response = self.app.post('/', {
36+
'recipient': '[email protected]',
37+
'submit': 'Send simple email'})
38+
39+
self.assertEqual(response.status_int, 200)
40+
41+
http = Http2Mock(responses=[{}])
42+
43+
with http:
44+
response = self.app.post('/', {
45+
'recipient': '[email protected]',
46+
'submit': 'Send complex email'})
47+
48+
self.assertEqual(response.status_int, 200)
49+
50+
http = Http2Mock(responses=[{'status': 500, 'body': 'Test error'}])
51+
52+
with http, self.assertRaises(Exception):
53+
self.app.post('/', {
54+
'recipient': '[email protected]',
55+
'submit': 'Send simple email'})

appengine/mailgun/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
httplib2

tests/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
AppEngineTestbedCase,
1717
capture_stdout,
1818
CloudBaseTest,
19+
Http2Mock,
1920
RESOURCE_PATH)
2021

2122

2223
__all__ = [
2324
'AppEngineTestbedCase',
2425
'capture_stdout',
2526
'CloudBaseTest',
27+
'Http2Mock',
2628
'RESOURCE_PATH'
2729
]

tests/utils.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import tempfile
2222
import unittest
2323

24+
import httplib2
2425
from nose.plugins.skip import SkipTest
2526
from six.moves import cStringIO
2627

@@ -135,3 +136,31 @@ def capture_stdout():
135136
yield fake_stdout
136137
finally:
137138
sys.stdout = old_stdout
139+
140+
141+
class Http2Mock(object):
142+
"""Mock httplib2.Http"""
143+
144+
def __init__(self, responses):
145+
self.responses = responses
146+
147+
def add_credentials(self, user, pwd):
148+
self.credentials = (user, pwd)
149+
150+
def request(self, token_uri, method, body, headers=None, *args, **kwargs):
151+
response = self.responses.pop(0)
152+
self.status = response.get('status', 200)
153+
self.body = response.get('body', '')
154+
self.headers = response.get('headers', '')
155+
return (self, self.body)
156+
157+
def __enter__(self):
158+
self.httplib2_orig = httplib2.Http
159+
httplib2.Http = self
160+
return self
161+
162+
def __exit__(self, exc_type, exc_value, traceback):
163+
httplib2.Http = self.httplib2_orig
164+
165+
def __call__(self, *args, **kwargs):
166+
return self

0 commit comments

Comments
 (0)