Skip to content

Commit 69543d4

Browse files
author
Ace Nassri
authored
Add missing GCF samples (other than tutorials) (#1614)
* Add GCP API call sample Change-Id: I2362e4ee8031fd3ea6880ffcec5eb253615df5ba * Add log retrieval sample + a few import tweaks Change-Id: Ieb2b5b3421bd6821bc6d5626b7986c23e9d82350 * Move log retrieval sample Change-Id: I0fb311114bf8c3554e2f052ca00f1dd9eef1cf69 * Add XML parsing sample Change-Id: I5ed6d6dc3792e28b12162c47c7e6f5c974a7477f * Add multipart sample Change-Id: I1a17e1c72bea6e7a8d18b9ed5910db09bedb753d * Add docstrings Change-Id: I3d99531c68b5ca3272cb6208777f3ab9bde8f9bd * Add signed URL sample Change-Id: I06c2a5cb46fd2cb497af7ba9e02e43a4e97de972 * Fix nits Change-Id: Ic20ff72602ea3b6b1fdc361d87aea7dfb742150f * Add requirements.txt files Change-Id: Ie8682514d99aa6f82786a76719535efd58a5c1bf * Build fix: downgrade 'futures' Change-Id: Ief7e96a43948af12de32627b20031c95953ade09 * Fix failing test + s/Exception/runtimeError/ Change-Id: Idc9854ba256271081078c15f7e62d3fd6bbec449 * Remove incorrect lint commits + actually fix lint Change-Id: Ib1b6f8eb47511fe9b19ed6d773b4f9c6ef0dc8ed * Shrink requirements.txt files Change-Id: I8fe4f70869deeb3aac0f9c6016f5c5e45a172731 * Add missing package Change-Id: Iba2165bd2af99306feb47b7e29640ebf7d8f3e95 * Remove add'l pkgs Change-Id: I585f2a6a31cf5e23a54eb02fb366a8685601ea23 * Address Andrew's comments Change-Id: Ibb5a518f60d06c7637cc39ab54e81a234ea188ec * Fix minor python{2->3} bugs Change-Id: If71dba3d86954e8e0bf7168638c5c73eb40fe599
1 parent b4619bf commit 69543d4

File tree

8 files changed

+226
-14
lines changed

8 files changed

+226
-14
lines changed

functions/http/main.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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 functions_http_signed_url]
16+
from datetime import datetime, timedelta
17+
# [END functions_http_signed_url]
18+
19+
# [START functions_http_xml]
20+
import json
21+
# [END functions_http_xml]
22+
23+
# [START functions_http_form_data]
24+
import os
25+
import tempfile
26+
# [END functions_http_form_data]
27+
28+
# [START functions_http_signed_url]
29+
from flask import abort
30+
from google.cloud import storage
31+
# [END functions_http_signed_url]
32+
33+
# [START functions_http_form_data]
34+
from werkzeug.utils import secure_filename
35+
# [END functions_http_form_data]
36+
37+
# [START functions_http_xml]
38+
import xmltodict
39+
# [END functions_http_xml]
40+
41+
42+
# [START functions_http_xml]
43+
44+
def parse_xml(request):
45+
""" Parses a document of type 'text/xml'
46+
Args:
47+
request (flask.Request): The request object.
48+
Returns:
49+
The response text, or any set of values that can be turned into a
50+
Response object using `make_response`
51+
<http://flask.pocoo.org/docs/0.12/api/#flask.Flask.make_response>.
52+
"""
53+
data = xmltodict.parse(request.data)
54+
return json.dumps(data, indent=2)
55+
# [END functions_http_xml]
56+
57+
58+
# [START functions_http_form_data]
59+
60+
# Helper function that computes the filepath to save files to
61+
def get_file_path(filename):
62+
# Note: tempfile.gettempdir() points to an in-memory file system
63+
# on GCF. Thus, any files in it must fit in the instance's memory.
64+
file_name = secure_filename(filename)
65+
return os.path.join(tempfile.gettempdir(), file_name)
66+
67+
68+
def parse_multipart(request):
69+
""" Parses a 'multipart/form-data' upload request
70+
Args:
71+
request (flask.Request): The request object.
72+
Returns:
73+
The response text, or any set of values that can be turned into a
74+
Response object using `make_response`
75+
<http://flask.pocoo.org/docs/0.12/api/#flask.Flask.make_response>.
76+
"""
77+
78+
# This code will process each non-file field in the form
79+
fields = {}
80+
data = request.form.to_dict()
81+
for field in data:
82+
fields[field] = data[field]
83+
print('Processed field: %s' % field)
84+
85+
# This code will process each file uploaded
86+
files = request.files.to_dict()
87+
for file_name, file in files.items():
88+
file.save(get_file_path(file_name))
89+
print('Processed file: %s' % file_name)
90+
91+
# Clear temporary directory
92+
for file_name in files:
93+
file_path = get_file_path(file_name)
94+
os.remove(file_path)
95+
96+
return "Done!"
97+
# [END functions_http_form_data]
98+
99+
100+
# [START functions_http_signed_url]
101+
storage_client = storage.Client()
102+
103+
104+
def get_signed_url(request):
105+
if request.method != 'POST':
106+
return abort(405)
107+
108+
request_json = request.get_json()
109+
110+
# Get a reference to the destination file in GCS
111+
file_name = request_json['filename']
112+
file = storage_client.bucket('my-bucket').blob(file_name)
113+
114+
# Create a temporary upload URL
115+
expires_at_ms = datetime.now() + timedelta(seconds=30)
116+
url = file.generate_signed_url(expires_at_ms,
117+
content_type=request_json['contentType'])
118+
119+
return url
120+
# [END functions_http_signed_url]

functions/http/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
google-cloud-storage==1.10.0
2+
xmltodict==0.11.0

functions/log/main.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,18 @@
1515
# [START functions_log_helloworld]
1616
import logging
1717

18+
# [END functions_log_helloworld]
19+
20+
# [START functions_log_retrieve]
21+
import os
22+
# [END functions_log_retrieve]
23+
24+
# [START functions_logs_retrieve]
25+
import google.cloud.logging as cloud_logging
26+
# [END functions_logs_retrieve]
27+
1828

29+
# [START functions_log_helloworld]
1930
def hello_world(data, context):
2031
"""Background Cloud Function.
2132
Args:
@@ -25,3 +36,33 @@ def hello_world(data, context):
2536
print('Hello, stdout!')
2637
logging.warn('Hello, logging handler!')
2738
# [END functions_log_helloworld]
39+
40+
41+
# [START functions_log_retrieve]
42+
cloud_client = cloud_logging.Client()
43+
log_name = 'cloudfunctions.googleapis.com%2Fcloud-functions'
44+
cloud_logger = cloud_client.logger(log_name.format(os.getenv('GCP_PROJECT')))
45+
46+
47+
def get_log_entries(request):
48+
"""
49+
HTTP Cloud Function that displays log entries from Cloud Functions.
50+
Args:
51+
request (flask.Request): The request object.
52+
Returns:
53+
The response text, or any set of values that can be turned into a
54+
Response object using `make_response`
55+
<http://flask.pocoo.org/docs/0.12/api/#flask.Flask.make_response>.
56+
"""
57+
""""""
58+
59+
all_entries = cloud_logger.list_entries(page_size=10)
60+
entries = next(all_entries.pages)
61+
62+
for entry in entries:
63+
timestamp = entry.timestamp.isoformat()
64+
print('* {}: {}'.format
65+
(timestamp, entry.payload))
66+
67+
return 'Done!'
68+
# [END functions_log_retrieve]

functions/log/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
google-cloud-logging==1.6.0

functions/tips/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
# Google Cloud Functions - Tips sample
44

5+
## Running locally
6+
Set the `GCP_PROJECT` variable to the ID of the GCP project to use.
7+
8+
## More info
59
See:
610

7-
* [Cloud Functions Tips tutorial][tutorial]
811
* [Cloud Functions Tips sample source code][code]
912

10-
[tutorial]: https://cloud.google.com/functions/docs/bestpractices/tips
1113
[code]: main.py

functions/tips/main.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,26 @@
1414

1515
# [START functions_tips_infinite_retries]
1616
from datetime import datetime
17+
# [END functions_tips_infinite_retries]
18+
19+
# [START functions_tips_gcp_apis]
20+
import os
21+
# [END functions_tips_gcp_apis]
1722

23+
# [START functions_tips_infinite_retries]
1824
# The 'python-dateutil' package must be included in requirements.txt.
1925
from dateutil import parser
2026

2127
# [END functions_tips_infinite_retries]
28+
29+
# [START functions_tips_retry]
30+
from google.cloud import error_reporting
31+
# [END functions_tips_retry]
32+
33+
# [START functions_tips_gcp_apis]
34+
from google.cloud import pubsub_v1
35+
# [END functions_tips_gcp_apis]
36+
2237
# [START functions_tips_connection_pooling]
2338
import requests
2439

@@ -89,6 +104,38 @@ def connection_pooling(request):
89104
# [END functions_tips_connection_pooling]
90105

91106

107+
# [START functions_tips_gcp_apis]
108+
109+
# Create a global Pub/Sub client to avoid unneeded network activity
110+
pubsub = pubsub_v1.PublisherClient()
111+
112+
113+
def gcp_api_call(request):
114+
"""
115+
HTTP Cloud Function that uses a cached client library instance to
116+
reduce the number of connections required per function invocation.
117+
Args:
118+
request (flask.Request): The request object.
119+
Returns:
120+
The response text, or any set of values that can be turned into a
121+
Response object using `make_response`
122+
<http://flask.pocoo.org/docs/0.12/api/#flask.Flask.make_response>.
123+
"""
124+
125+
project = os.getenv('GCP_PROJECT')
126+
request_json = request.get_json()
127+
128+
topic_name = request_json['topic']
129+
topic_path = pubsub.topic_path(project, topic_name)
130+
131+
# Process the request
132+
data = 'Test message'.encode('utf-8')
133+
pubsub.publish(topic_path, data=data)
134+
135+
return '1 message published'
136+
# [END functions_tips_gcp_apis]
137+
138+
92139
# [START functions_tips_infinite_retries]
93140
def avoid_infinite_retries(data, context):
94141
"""Background Cloud Function that only executes within a certain
@@ -119,6 +166,9 @@ def avoid_infinite_retries(data, context):
119166

120167

121168
# [START functions_tips_retry]
169+
error_client = error_reporting.Client()
170+
171+
122172
def retry_or_not(data, context):
123173
"""Background Cloud Function that demonstrates how to toggle retries.
124174
@@ -129,9 +179,6 @@ def retry_or_not(data, context):
129179
None; output is written to Stackdriver Logging
130180
"""
131181

132-
from google import cloud
133-
error_client = cloud.error_reporting.Client()
134-
135182
if data.data.get('retry'):
136183
try_again = True
137184
else:

functions/tips/main_test.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,15 @@ def test_avoid_infinite_retries(capsys):
7272

7373

7474
def test_retry_or_not():
75-
with patch('google.cloud') as cloud_mock:
76-
77-
error_client = MagicMock()
78-
79-
cloud_mock.error_reporting = MagicMock(
80-
Client=MagicMock(return_value=error_client))
75+
with patch('main.error_client') as error_client_mock:
76+
error_client_mock.report_exception = MagicMock()
8177

8278
event = Mock(data={})
8379
main.retry_or_not(event, None)
84-
assert error_client.report_exception.call_count == 1
80+
assert error_client_mock.report_exception.call_count == 1
8581

8682
event.data = {'retry': True}
8783
with pytest.raises(RuntimeError):
8884
main.retry_or_not(event, None)
8985

90-
assert error_client.report_exception.call_count == 2
86+
assert error_client_mock.report_exception.call_count == 2

functions/tips/requirements.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
google-cloud-error-reporting==0.30.0
2-
python-dateutil==2.7.3
2+
google-cloud-pubsub==0.35.4
3+
python-dateutil==2.7.3
4+
requests==2.18.1
5+
xmltodict==0.11.0

0 commit comments

Comments
 (0)