Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 62ef48d

Browse filesBrowse files
anguillanneufchenyumic
authored andcommitted
Pub/Sub Sample for App Engine Standard (GoogleCloudPlatform#1606)
* Example of Pub/Sub API Client Lib in GAE Standard * Added Pub/Sub in AppEngine Standard
1 parent f6ca863 commit 62ef48d
Copy full SHA for 62ef48d

File tree

Expand file treeCollapse file tree

7 files changed

+305
-0
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+305
-0
lines changed

‎appengine/standard/pubsub/README.md

Copy file name to clipboard
+77Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Python Google Cloud Pub/Sub sample for Google App Engine Standard Environment
2+
3+
[![Open in Cloud Shell][shell_img]][shell_link]
4+
5+
[shell_img]: http://gstatic.com/cloudssh/images/open-btn.png
6+
[shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=appengine/standard/pubsub/README.md
7+
8+
This demonstrates how to send and receive messages using [Google Cloud Pub/Sub](https://cloud.google.com/pubsub) on [Google App Engine Standard Environment](https://cloud.google.com/appengine/docs/standard/).
9+
10+
## Setup
11+
12+
Before you can run or deploy the sample, you will need to do the following:
13+
14+
1. Enable the Cloud Pub/Sub API in the [Google Developers Console](https://console.developers.google.com/project/_/apiui/apiview/pubsub/overview).
15+
16+
2. Create a topic and subscription.
17+
18+
$ gcloud pubsub topics create [your-topic-name]
19+
$ gcloud pubsub subscriptions create [your-subscription-name] \
20+
--topic [your-topic-name] \
21+
--push-endpoint \
22+
https://[your-app-id].appspot.com/_ah/push-handlers/receive_messages/token=[your-token] \
23+
--ack-deadline 30
24+
25+
3. Update the environment variables in ``app.yaml``.
26+
27+
## Running locally
28+
29+
Refer to the [top-level README](../README.md) for instructions on running and deploying.
30+
31+
When running locally, you can use the [Google Cloud SDK](https://cloud.google.com/sdk) to provide authentication to use Google Cloud APIs:
32+
33+
$ gcloud init
34+
35+
Install dependencies, preferably with a virtualenv:
36+
37+
$ virtualenv env
38+
$ source env/bin/activate
39+
$ pip install -r requirements.txt
40+
41+
Then set environment variables before starting your application:
42+
43+
$ export GOOGLE_CLOUD_PROJECT=[your-project-name]
44+
$ export PUBSUB_VERIFICATION_TOKEN=[your-verification-token]
45+
$ export PUBSUB_TOPIC=[your-topic]
46+
$ python main.py
47+
48+
### Simulating push notifications
49+
50+
The application can send messages locally, but it is not able to receive push messages locally. You can, however, simulate a push message by making an HTTP request to the local push notification endpoint. There is an included ``sample_message.json``. You can use
51+
``curl`` or [httpie](https://github.com/jkbrzt/httpie) to POST this:
52+
53+
$ curl -i --data @sample_message.json ":8080/_ah/push-handlers/receive_messages?token=[your-token]"
54+
55+
Or
56+
57+
$ http POST ":8080/_ah/push-handlers/receive_messages?token=[your-token]" < sample_message.json
58+
59+
Response:
60+
61+
HTTP/1.0 200 OK
62+
Content-Length: 2
63+
Content-Type: text/html; charset=utf-8
64+
Date: Mon, 10 Aug 2015 17:52:03 GMT
65+
Server: Werkzeug/0.10.4 Python/2.7.10
66+
67+
OK
68+
69+
After the request completes, you can refresh ``localhost:8080`` and see the message in the list of received messages.
70+
71+
## Running on App Engine
72+
73+
Deploy using `gcloud`:
74+
75+
gcloud app deploy app.yaml
76+
77+
You can now access the application at `https://your-app-id.appspot.com`. You can use the form to submit messages, but it's non-deterministic which instance of your application will receive the notification. You can send multiple messages and refresh the page to see the received message.

‎appengine/standard/pubsub/app.yaml

Copy file name to clipboard
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
runtime: python27
2+
api_version: 1
3+
threadsafe: yes
4+
5+
handlers:
6+
- url: /
7+
script: main.app
8+
9+
- url: /_ah/push-handlers/.*
10+
script: main.app
11+
login: admin
12+
13+
#[START env]
14+
env_variables:
15+
PUBSUB_TOPIC: your-topic
16+
# This token is used to verify that requests originate from your
17+
# application. It can be any sufficiently random string.
18+
PUBSUB_VERIFICATION_TOKEN: 1234abc
19+
#[END env]

‎appengine/standard/pubsub/main.py

Copy file name to clipboard
+94Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Copyright 2018 Google, LLC.
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+
# [START app]
16+
import base64
17+
import json
18+
import logging
19+
import os
20+
21+
from flask import current_app, Flask, render_template, request
22+
from googleapiclient.discovery import build
23+
24+
25+
app = Flask(__name__)
26+
27+
# Configure the following environment variables via app.yaml
28+
# This is used in the push request handler to veirfy that the request came from
29+
# pubsub and originated from a trusted source.
30+
app.config['PUBSUB_VERIFICATION_TOKEN'] = \
31+
os.environ['PUBSUB_VERIFICATION_TOKEN']
32+
app.config['PUBSUB_TOPIC'] = os.environ['PUBSUB_TOPIC']
33+
app.config['GCLOUD_PROJECT'] = os.environ['GOOGLE_CLOUD_PROJECT']
34+
35+
36+
# Global list to storage messages received by this instance.
37+
MESSAGES = []
38+
39+
40+
# [START index]
41+
@app.route('/', methods=['GET', 'POST'])
42+
def index():
43+
if request.method == 'GET':
44+
return render_template('index.html', messages=MESSAGES)
45+
46+
data = request.form.get('payload', 'Example payload').encode('utf-8')
47+
48+
service = build('pubsub', 'v1')
49+
topic_path = 'projects/{project_id}/topics/{topic}'.format(
50+
project_id=app.config['GCLOUD_PROJECT'],
51+
topic=app.config['PUBSUB_TOPIC']
52+
)
53+
service.projects().topics().publish(
54+
topic=topic_path, body={
55+
"messages": [{
56+
"data": base64.b64encode(data)
57+
}]
58+
}).execute()
59+
60+
return 'OK', 200
61+
# [END index]
62+
63+
64+
# [START push]
65+
@app.route('/_ah/push-handlers/receive_messages', methods=['POST'])
66+
def receive_messages_handler():
67+
if (request.args.get('token', '') !=
68+
current_app.config['PUBSUB_VERIFICATION_TOKEN']):
69+
return 'Invalid request', 400
70+
71+
envelope = json.loads(request.data.decode('utf-8'))
72+
payload = base64.b64decode(envelope['message']['data'])
73+
74+
MESSAGES.append(payload)
75+
76+
# Returning any 2xx status indicates successful receipt of the message.
77+
return 'OK', 200
78+
# [END push]
79+
80+
81+
@app.errorhandler(500)
82+
def server_error(e):
83+
logging.exception('An error occurred during a request.')
84+
return """
85+
An internal error occurred: <pre>{}</pre>
86+
See logs for full stacktrace.
87+
""".format(e), 500
88+
89+
90+
if __name__ == '__main__':
91+
# This is used when running locally. Gunicorn is used to run the
92+
# application on Google App Engine. See entrypoint in app.yaml.
93+
app.run(host='127.0.0.1', port=8080, debug=True)
94+
# [END app]
+70Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Copyright 2018 Google, LLC.
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+
import base64
16+
import json
17+
import os
18+
19+
import pytest
20+
21+
import main
22+
23+
24+
@pytest.fixture
25+
def client():
26+
main.app.testing = True
27+
return main.app.test_client()
28+
29+
30+
def test_index(client):
31+
r = client.get('/')
32+
assert r.status_code == 200
33+
34+
35+
def test_post_index(client):
36+
r = client.post('/', data={'payload': 'Test payload'})
37+
assert r.status_code == 200
38+
39+
40+
def test_push_endpoint(client):
41+
url = '/_ah/push-handlers/receive_messages?token=' + \
42+
os.environ['PUBSUB_VERIFICATION_TOKEN']
43+
44+
r = client.post(
45+
url,
46+
data=json.dumps({
47+
"message": {
48+
"data": base64.b64encode(
49+
u'Test message'.encode('utf-8')
50+
).decode('utf-8')
51+
}
52+
})
53+
)
54+
55+
assert r.status_code == 200
56+
57+
# Make sure the message is visible on the home page.
58+
r = client.get('/')
59+
assert r.status_code == 200
60+
assert 'Test message' in r.data.decode('utf-8')
61+
62+
63+
def test_push_endpoint_errors(client):
64+
# no token
65+
r = client.post('/_ah/push-handlers/receive_messages')
66+
assert r.status_code == 400
67+
68+
# invalid token
69+
r = client.post('/_ah/push-handlers/receive_messages?token=bad')
70+
assert r.status_code == 400
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Flask==0.12.2
2+
google-api-python-client==1.7.3
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"message": {
3+
"data": "SGVsbG8sIFdvcmxkIQ=="
4+
}
5+
}
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{#
2+
# Copyright 2015 Google Inc. All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#}
16+
<!doctype html>
17+
<html>
18+
<head>
19+
<title>Pub/Sub Python on Google App Engine Flexible Environment</title>
20+
</head>
21+
<body>
22+
<div>
23+
<p>Messages received by this instance:</p>
24+
<ul>
25+
{% for message in messages: %}
26+
<li>{{message}}</li>
27+
{% endfor %}
28+
</ul>
29+
<p><small>Note: because your application is likely running multiple instances, each instance will have a different list of messages.</small></p>
30+
</div>
31+
<!-- [START form] -->
32+
<form method="post">
33+
<textarea name="payload" placeholder="Enter message here"></textarea>
34+
<input type="submit">
35+
</form>
36+
<!-- [END form] -->
37+
</body>
38+
</html>

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.