Add samples for Cloud Tasks (#1068) · promiesun/python-docs-samples@356dcb1 · GitHub
Skip to content

Commit 356dcb1

Browse files
andrewsgJon Wayne Parrott
authored andcommitted
Add samples for Cloud Tasks (GoogleCloudPlatform#1068)
* Add samples for Cloud Tasks * Respond to tasks sample review * Update app engine queues samples * Address review feedback * Address review issues and convert pull queue sample to not use API key auth * Reform pull queues to match appengine queues changes to auth, command line input, readme * flake8 and fix comment
1 parent ec5bbad commit 356dcb1

12 files changed

Lines changed: 566 additions & 0 deletions

appengine/flexible/tasks/README.md

Lines changed: 87 additions & 0 deletions

appengine/flexible/tasks/app.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
runtime: python
2+
env: flex
3+
entrypoint: gunicorn -b :$PORT main:app
4+
5+
runtime_config:
6+
python_version: 3
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Copyright 2017 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 __future__ import print_function
16+
17+
import argparse
18+
import base64
19+
import datetime
20+
import json
21+
22+
from googleapiclient import discovery
23+
24+
25+
def seconds_from_now_to_rfc3339_datetime(seconds):
26+
"""Return an RFC 3339 datetime string for a number of seconds from now."""
27+
d = datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds)
28+
return d.isoformat('T') + 'Z'
29+
30+
31+
def create_task(project, queue, location, payload=None, in_seconds=None):
32+
"""Create a task for a given queue with an arbitrary payload."""
33+
34+
# Create a client.
35+
DISCOVERY_URL = (
36+
'https://cloudtasks.googleapis.com/$discovery/rest?version=v2beta2')
37+
client = discovery.build(
38+
'cloudtasks', 'v2beta2', discoveryServiceUrl=DISCOVERY_URL)
39+
40+
url = '/log_payload'
41+
body = {
42+
'task': {
43+
'app_engine_task_target': {
44+
'http_method': 'POST',
45+
'relative_url': url
46+
}
47+
}
48+
}
49+
50+
if payload is not None:
51+
# Payload is a string (unicode), and must be encoded for base64.
52+
# The finished request body is JSON, which requires unicode.
53+
body['task']['app_engine_task_target']['payload'] = base64.b64encode(
54+
payload.encode()).decode()
55+
56+
if in_seconds is not None:
57+
scheduled_time = seconds_from_now_to_rfc3339_datetime(in_seconds)
58+
body['task']['schedule_time'] = scheduled_time
59+
60+
queue_name = 'projects/{}/locations/{}/queues/{}'.format(
61+
project, location, queue)
62+
63+
print('Sending task {}'.format(json.dumps(body)))
64+
65+
response = client.projects().locations().queues().tasks().create(
66+
parent=queue_name, body=body).execute()
67+
68+
# By default CreateTaskRequest.responseView is BASIC, so not all
69+
# information is retrieved by default because some data, such as payloads,
70+
# might be desirable to return only when needed because of its large size
71+
# or because of the sensitivity of data that it contains.
72+
print('Created task {}'.format(response['name']))
73+
return response
74+
75+
76+
if __name__ == '__main__':
77+
parser = argparse.ArgumentParser(
78+
description=create_task.__doc__,
79+
formatter_class=argparse.RawDescriptionHelpFormatter)
80+
81+
parser.add_argument(
82+
'--project',
83+
help='Project of the queue to add the task to.'
84+
)
85+
86+
parser.add_argument(
87+
'--queue',
88+
help='ID (short name) of the queue to add the task to.'
89+
)
90+
91+
parser.add_argument(
92+
'--location',
93+
help='Location of the queue to add the task to.'
94+
)
95+
96+
parser.add_argument(
97+
'--payload',
98+
help='Optional payload to attach to the push queue.'
99+
)
100+
101+
parser.add_argument(
102+
'--in_seconds',
103+
help='The number of seconds from now to schedule task attempt.'
104+
)
105+
106+
args = parser.parse_args()
107+
108+
create_task(
109+
args.project, args.queue, args.location,
110+
args.payload, args.in_seconds)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2016 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+
import mock
16+
17+
import create_app_engine_queue_task
18+
19+
TEST_PROJECT = 'mock-project'
20+
TEST_LOCATION = 'us-central1'
21+
TEST_QUEUE = 'my-appengine-queue'
22+
23+
24+
@mock.patch('googleapiclient.discovery.build')
25+
def test_create_task(build):
26+
projects = build.return_value.projects.return_value
27+
locations = projects.locations.return_value
28+
create_function = locations.queues.return_value.tasks.return_value.create
29+
execute_function = create_function.return_value.execute
30+
execute_function.return_value = {'name': 'task_name'}
31+
create_app_engine_queue_task.create_task(
32+
TEST_PROJECT, TEST_QUEUE, TEST_LOCATION)
33+
assert execute_function.called

appengine/flexible/tasks/main.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright 2016 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+
"""App Engine app to serve as an endpoint for App Engine queue samples."""
16+
17+
import logging
18+
19+
from flask import Flask, request
20+
21+
app = Flask(__name__)
22+
23+
24+
@app.route('/log_payload', methods=['POST'])
25+
def log_payload():
26+
"""Log the request payload."""
27+
payload = request.data or "empty payload"
28+
logging.warn(payload)
29+
return 'Logged request payload: {}'.format(payload)
30+
31+
32+
@app.route('/')
33+
def hello():
34+
"""Basic index to verify app is serving."""
35+
return 'Hello World!'
36+
37+
38+
if __name__ == '__main__':
39+
# This is used when running locally. Gunicorn is used to run the
40+
# application on Google App Engine. See entrypoint in app.yaml.
41+
app.run(host='127.0.0.1', port=8080, debug=True)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright 2016 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+
import mock
16+
import pytest
17+
18+
19+
@pytest.fixture
20+
def app():
21+
import main
22+
main.app.testing = True
23+
return main.app.test_client()
24+
25+
26+
def test_index(app):
27+
r = app.get('/')
28+
assert r.status_code == 200
29+
30+
31+
@mock.patch('logging.warn')
32+
def test_log_payload(logging_mock, app):
33+
payload = 'hello'
34+
35+
r = app.post('/log_payload', payload)
36+
assert r.status_code == 200
37+
38+
assert logging_mock.called
39+
40+
41+
@mock.patch('logging.warn')
42+
def test_empty_payload(logging_mock, app):
43+
r = app.post('/log_payload')
44+
assert r.status_code == 200
45+
46+
assert logging_mock.called
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Flask==0.11.1
2+
google-api-python-client==1.6.0
3+
google-cloud-datastore==0.22.0
4+
gunicorn==19.6.0

tasks/README.md

Lines changed: 59 additions & 0 deletions

0 commit comments

Comments
 (0)