Skip to content

Commit afa072c

Browse files
committed
Merge branch 'http-tasks' of https://github.com/GoogleCloudPlatform/python-docs-samples into http-tasks
2 parents 4cf79b3 + ff2429b commit afa072c

File tree

93 files changed

+8658
-201
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+8658
-201
lines changed

MAC_SETUP.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ test their code.
3434
3535
```
3636
eval "$(pyenv init -)"
37-
eval "$(pyenv virtualenv-init -)"`
37+
eval "$(pyenv virtualenv-init -)"
3838
```
3939
4040
Note that this also works with ZSH.

appengine/flexible/tasks/create_app_engine_queue_task.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ def create_task(project, queue, location, payload=None, in_seconds=None):
2222
# [START cloud_tasks_appengine_create_task]
2323
"""Create a task for a given queue with an arbitrary payload."""
2424

25-
from google.cloud import tasks
25+
from google.cloud import tasks_v2
2626
from google.protobuf import timestamp_pb2
2727

2828
# Create a client.
29-
client = tasks.CloudTasksClient()
29+
client = tasks_v2.CloudTasksClient()
3030

3131
# TODO(developer): Uncomment these lines and replace with your values.
3232
# project = 'my-project-id'
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
Flask==1.0.2
22
gunicorn==19.9.0
3-
google-cloud-tasks==0.5.0
3+
google-cloud-tasks==0.6.0

appengine/standard_python37/bigquery/main.py

+33-9
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@
1515
# [START gae_python37_bigquery]
1616
import concurrent.futures
1717

18-
from flask import Flask, render_template
18+
import flask
1919
from google.cloud import bigquery
2020

2121

22-
app = Flask(__name__)
22+
app = flask.Flask(__name__)
2323
bigquery_client = bigquery.Client()
2424

2525

26-
@app.route('/')
26+
@app.route("/")
2727
def main():
28-
query_job = bigquery_client.query("""
28+
query_job = bigquery_client.query(
29+
"""
2930
SELECT
3031
CONCAT(
3132
'https://stackoverflow.com/questions/',
@@ -35,20 +36,43 @@ def main():
3536
WHERE tags like '%google-bigquery%'
3637
ORDER BY view_count DESC
3738
LIMIT 10
38-
""")
39+
"""
40+
)
41+
42+
return flask.redirect(
43+
flask.url_for(
44+
"results",
45+
project_id=query_job.project,
46+
job_id=query_job.job_id,
47+
location=query_job.location,
48+
)
49+
)
50+
51+
52+
@app.route("/results")
53+
def results():
54+
project_id = flask.request.args.get("project_id")
55+
job_id = flask.request.args.get("job_id")
56+
location = flask.request.args.get("location")
57+
58+
query_job = bigquery_client.get_job(
59+
job_id,
60+
project=project_id,
61+
location=location,
62+
)
3963

4064
try:
4165
# Set a timeout because queries could take longer than one minute.
4266
results = query_job.result(timeout=30)
4367
except concurrent.futures.TimeoutError:
44-
return render_template('timeout.html', job_id=query_job.job_id)
68+
return flask.render_template("timeout.html", job_id=query_job.job_id)
4569

46-
return render_template('query_result.html', results=results)
70+
return flask.render_template("query_result.html", results=results)
4771

4872

49-
if __name__ == '__main__':
73+
if __name__ == "__main__":
5074
# This is used when running locally only. When deploying to Google App
5175
# Engine, a webserver process such as Gunicorn will serve the app. This
5276
# can be configured by adding an `entrypoint` to app.yaml.
53-
app.run(host='127.0.0.1', port=8080, debug=True)
77+
app.run(host="127.0.0.1", port=8080, debug=True)
5478
# [END gae_python37_bigquery]

appengine/standard_python37/bigquery/main_test.py

+31-7
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,46 @@ def flask_client():
2828

2929

3030
def test_main(flask_client):
31-
r = flask_client.get('/')
31+
r = flask_client.get("/")
32+
assert r.status_code == 302
33+
assert "/results" in r.headers.get("location", "")
34+
35+
36+
def test_results(flask_client, monkeypatch):
37+
import main
38+
39+
fake_job = mock.create_autospec(bigquery.QueryJob)
40+
fake_rows = [("example1.com", "42"), ("example2.com", "38")]
41+
fake_job.result.return_value = fake_rows
42+
43+
def fake_get_job(self, job_id, **kwargs):
44+
return fake_job
45+
46+
monkeypatch.setattr(main.bigquery.Client, "get_job", fake_get_job)
47+
48+
r = flask_client.get(
49+
"/results?project_id=123&job_id=456&location=my_location"
50+
)
51+
response_body = r.data.decode("utf-8")
52+
3253
assert r.status_code == 200
33-
assert 'Query Result' in r.data.decode('utf-8')
54+
assert "Query Result" in response_body # verifies header
55+
assert "example2.com" in response_body
56+
assert "42" in response_body
3457

3558

36-
def test_main_timeout(flask_client, monkeypatch):
59+
def test_results_timeout(flask_client, monkeypatch):
3760
import main
3861

3962
fake_job = mock.create_autospec(bigquery.QueryJob)
4063
fake_job.result.side_effect = concurrent.futures.TimeoutError()
4164

42-
def fake_query(query):
65+
def fake_get_job(self, job_id, **kwargs):
4366
return fake_job
4467

45-
monkeypatch.setattr(main.bigquery_client, 'query', fake_query)
68+
monkeypatch.setattr(main.bigquery.Client, "get_job", fake_get_job)
69+
70+
r = flask_client.get("/results", follow_redirects=True)
4671

47-
r = flask_client.get('/')
4872
assert r.status_code == 200
49-
assert 'Query Timeout' in r.data.decode('utf-8')
73+
assert "Query Timeout" in r.data.decode("utf-8")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Python 3 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. The push auth service account must have Service Account Token Creator Role assigned, which can be done in the Cloud Console [IAM & admin](https://console.cloud.google.com/iam-admin/iam) UI. `--push-auth-token-audience` is optional. If set, remember to modify the audience field check in `main.py` (line 88).
17+
18+
$ gcloud pubsub topics create [your-topic-name]
19+
$ gcloud beta 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+
--push-auth-service-account=[your-service-account-email] \
25+
--push-auth-token-audience=example.com
26+
27+
3. Update the environment variables in ``app.yaml``.
28+
29+
## Running locally
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 "localhost: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 400 BAD REQUEST
62+
Content-Type: text/html; charset=utf-8
63+
Content-Length: 58
64+
Server: Werkzeug/0.15.2 Python/3.7.3
65+
Date: Sat, 06 Apr 2019 04:56:12 GMT
66+
67+
Invalid token: 'NoneType' object has no attribute 'split'
68+
69+
The simulated push request fails because it does not have a Cloud Pub/Sub-generated JWT in the "Authorization" header.
70+
71+
## Running on App Engine
72+
73+
Note: Not all the files in the current directory are needed to run your code on App Engine. Specifically, `main_test.py` and the `data` directory, which contains a mocked private key file and a mocked public certs file, are for testing purposes only. They SHOULD NOT be included in when deploying your app. When your app is up and running, Cloud Pub/Sub creates tokens using a private key, then the Google Auth Python library takes care of verifying and decoding the token using Google's public certs, to confirm that the push requests indeed come from Cloud Pub/Sub.
74+
75+
In the current directory, deploy using `gcloud`:
76+
77+
$ gcloud app deploy app.yaml
78+
79+
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
runtime: python37
2+
3+
#[START env]
4+
env_variables:
5+
PUBSUB_TOPIC: your-topic
6+
# This token is used to verify that requests originate from your
7+
# application. It can be any sufficiently random string.
8+
PUBSUB_VERIFICATION_TOKEN: 1234abc
9+
#[END env]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEpAIBAAKCAQEA4ej0p7bQ7L/r4rVGUz9RN4VQWoej1Bg1mYWIDYslvKrk1gpj
3+
7wZgkdmM7oVK2OfgrSj/FCTkInKPqaCR0gD7K80q+mLBrN3PUkDrJQZpvRZIff3/
4+
xmVU1WeruQLFJjnFb2dqu0s/FY/2kWiJtBCakXvXEOb7zfbINuayL+MSsCGSdVYs
5+
SliS5qQpgyDap+8b5fpXZVJkq92hrcNtbkg7hCYUJczt8n9hcCTJCfUpApvaFQ18
6+
pe+zpyl4+WzkP66I28hniMQyUlA1hBiskT7qiouq0m8IOodhv2fagSZKjOTTU2xk
7+
SBc//fy3ZpsL7WqgsZS7Q+0VRK8gKfqkxg5OYQIDAQABAoIBAQDGGHzQxGKX+ANk
8+
nQi53v/c6632dJKYXVJC+PDAz4+bzU800Y+n/bOYsWf/kCp94XcG4Lgsdd0Gx+Zq
9+
HD9CI1IcqqBRR2AFscsmmX6YzPLTuEKBGMW8twaYy3utlFxElMwoUEsrSWRcCA1y
10+
nHSDzTt871c7nxCXHxuZ6Nm/XCL7Bg8uidRTSC1sQrQyKgTPhtQdYrPQ4WZ1A4J9
11+
IisyDYmZodSNZe5P+LTJ6M1SCgH8KH9ZGIxv3diMwzNNpk3kxJc9yCnja4mjiGE2
12+
YCNusSycU5IhZwVeCTlhQGcNeV/skfg64xkiJE34c2y2ttFbdwBTPixStGaF09nU
13+
Z422D40BAoGBAPvVyRRsC3BF+qZdaSMFwI1yiXY7vQw5+JZh01tD28NuYdRFzjcJ
14+
vzT2n8LFpj5ZfZFvSMLMVEFVMgQvWnN0O6xdXvGov6qlRUSGaH9u+TCPNnIldjMP
15+
B8+xTwFMqI7uQr54wBB+Poq7dVRP+0oHb0NYAwUBXoEuvYo3c/nDoRcZAoGBAOWl
16+
aLHjMv4CJbArzT8sPfic/8waSiLV9Ixs3Re5YREUTtnLq7LoymqB57UXJB3BNz/2
17+
eCueuW71avlWlRtE/wXASj5jx6y5mIrlV4nZbVuyYff0QlcG+fgb6pcJQuO9DxMI
18+
aqFGrWP3zye+LK87a6iR76dS9vRU+bHZpSVvGMKJAoGAFGt3TIKeQtJJyqeUWNSk
19+
klORNdcOMymYMIlqG+JatXQD1rR6ThgqOt8sgRyJqFCVT++YFMOAqXOBBLnaObZZ
20+
CFbh1fJ66BlSjoXff0W+SuOx5HuJJAa5+WtFHrPajwxeuRcNa8jwxUsB7n41wADu
21+
UqWWSRedVBg4Ijbw3nWwYDECgYB0pLew4z4bVuvdt+HgnJA9n0EuYowVdadpTEJg
22+
soBjNHV4msLzdNqbjrAqgz6M/n8Ztg8D2PNHMNDNJPVHjJwcR7duSTA6w2p/4k28
23+
bvvk/45Ta3XmzlxZcZSOct3O31Cw0i2XDVc018IY5be8qendDYM08icNo7vQYkRH
24+
504kQQKBgQDjx60zpz8ozvm1XAj0wVhi7GwXe+5lTxiLi9Fxq721WDxPMiHDW2XL
25+
YXfFVy/9/GIMvEiGYdmarK1NW+VhWl1DC5xhDg0kvMfxplt4tynoq1uTsQTY31Mx
26+
BeF5CT/JuNYk3bEBF0H/Q3VGO1/ggVS+YezdFbLWIRoMnLj6XCFEGg==
27+
-----END RSA PRIVATE KEY-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDIzCCAgugAwIBAgIJAMfISuBQ5m+5MA0GCSqGSIb3DQEBBQUAMBUxEzARBgNV
3+
BAMTCnVuaXQtdGVzdHMwHhcNMTExMjA2MTYyNjAyWhcNMjExMjAzMTYyNjAyWjAV
4+
MRMwEQYDVQQDEwp1bml0LXRlc3RzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
5+
CgKCAQEA4ej0p7bQ7L/r4rVGUz9RN4VQWoej1Bg1mYWIDYslvKrk1gpj7wZgkdmM
6+
7oVK2OfgrSj/FCTkInKPqaCR0gD7K80q+mLBrN3PUkDrJQZpvRZIff3/xmVU1Wer
7+
uQLFJjnFb2dqu0s/FY/2kWiJtBCakXvXEOb7zfbINuayL+MSsCGSdVYsSliS5qQp
8+
gyDap+8b5fpXZVJkq92hrcNtbkg7hCYUJczt8n9hcCTJCfUpApvaFQ18pe+zpyl4
9+
+WzkP66I28hniMQyUlA1hBiskT7qiouq0m8IOodhv2fagSZKjOTTU2xkSBc//fy3
10+
ZpsL7WqgsZS7Q+0VRK8gKfqkxg5OYQIDAQABo3YwdDAdBgNVHQ4EFgQU2RQ8yO+O
11+
gN8oVW2SW7RLrfYd9jEwRQYDVR0jBD4wPIAU2RQ8yO+OgN8oVW2SW7RLrfYd9jGh
12+
GaQXMBUxEzARBgNVBAMTCnVuaXQtdGVzdHOCCQDHyErgUOZvuTAMBgNVHRMEBTAD
13+
AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBRv+M/6+FiVu7KXNjFI5pSN17OcW5QUtPr
14+
odJMlWrJBtynn/TA1oJlYu3yV5clc/71Vr/AxuX5xGP+IXL32YDF9lTUJXG/uUGk
15+
+JETpKmQviPbRsvzYhz4pf6ZIOZMc3/GIcNq92ECbseGO+yAgyWUVKMmZM0HqXC9
16+
ovNslqe0M8C1sLm1zAR5z/h/litE7/8O2ietija3Q/qtl2TOXJdCA6sgjJX2WUql
17+
ybrC55ct18NKf3qhpcEkGQvFU40rVYApJpi98DiZPYFdx1oBDp/f4uZ3ojpxRVFT
18+
cDwcJLfNRCPUhormsY7fDS9xSyThiHsW9mjJYdcaKQkwYZ0F11yB
19+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)