Skip to content

Commit 2e502b1

Browse files
authored
Gae python version update (#3699)
1 parent ec30b6a commit 2e502b1

File tree

106 files changed

+2615
-0
lines changed

Some content is hidden

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

106 files changed

+2615
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
runtime: python38
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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 gae_python38_bigquery]
16+
import concurrent.futures
17+
18+
import flask
19+
from google.cloud import bigquery
20+
21+
22+
app = flask.Flask(__name__)
23+
bigquery_client = bigquery.Client()
24+
25+
26+
@app.route("/")
27+
def main():
28+
query_job = bigquery_client.query(
29+
"""
30+
SELECT
31+
CONCAT(
32+
'https://stackoverflow.com/questions/',
33+
CAST(id as STRING)) as url,
34+
view_count
35+
FROM `bigquery-public-data.stackoverflow.posts_questions`
36+
WHERE tags like '%google-bigquery%'
37+
ORDER BY view_count DESC
38+
LIMIT 10
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+
)
63+
64+
try:
65+
# Set a timeout because queries could take longer than one minute.
66+
results = query_job.result(timeout=30)
67+
except concurrent.futures.TimeoutError:
68+
return flask.render_template("timeout.html", job_id=query_job.job_id)
69+
70+
return flask.render_template("query_result.html", results=results)
71+
72+
73+
if __name__ == "__main__":
74+
# This is used when running locally only. When deploying to Google App
75+
# Engine, a webserver process such as Gunicorn will serve the app. This
76+
# can be configured by adding an `entrypoint` to app.yaml.
77+
app.run(host="127.0.0.1", port=8080, debug=True)
78+
# [END gae_python38_bigquery]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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 concurrent.futures
16+
from unittest import mock
17+
18+
from google.cloud import bigquery
19+
import pytest
20+
21+
22+
@pytest.fixture
23+
def flask_client():
24+
import main
25+
26+
main.app.testing = True
27+
return main.app.test_client()
28+
29+
30+
def test_main(flask_client):
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+
53+
assert r.status_code == 200
54+
assert "Query Result" in response_body # verifies header
55+
assert "example2.com" in response_body
56+
assert "42" in response_body
57+
58+
59+
def test_results_timeout(flask_client, monkeypatch):
60+
import main
61+
62+
fake_job = mock.create_autospec(bigquery.QueryJob)
63+
fake_job.result.side_effect = concurrent.futures.TimeoutError()
64+
65+
def fake_get_job(self, job_id, **kwargs):
66+
return fake_job
67+
68+
monkeypatch.setattr(main.bigquery.Client, "get_job", fake_get_job)
69+
70+
r = flask_client.get("/results", follow_redirects=True)
71+
72+
assert r.status_code == 200
73+
assert "Query Timeout" in r.data.decode("utf-8")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest==5.3.2
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
google-cloud-bigquery==1.24.0
2+
Flask==1.1.2
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE html>
2+
{#
3+
Copyright 2019 Google LLC
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+
https://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+
<meta charset="utf-8" />
18+
<title>Query Result</title>
19+
20+
<table>
21+
<tr>
22+
<th>URL</th>
23+
<th>View Count</th>
24+
</tr>
25+
{% for result in results %}
26+
<tr>
27+
<td>{{ result[0] }}</td>
28+
<td>{{ result[1] }}</td>
29+
</tr>
30+
{% endfor %}
31+
</table>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!DOCTYPE html>
2+
{#
3+
Copyright 2019 Google LLC
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+
https://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+
<meta charset="utf-8" />
18+
<title>Query Timeout</title>
19+
20+
<p>Query job {{ job_id }} timed out.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
runtime: python38
2+
3+
handlers:
4+
# This configures Google App Engine to serve the files in the app's static
5+
# directory.
6+
- url: /static
7+
static_dir: static
8+
9+
# This handler routes all requests not caught above to your main app. It is
10+
# required when static routes are defined, but can be omitted (along with
11+
# the entire handlers section) when there are no static files defined.
12+
- url: /.*
13+
script: auto
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 gae_python38_render_template]
16+
import datetime
17+
18+
from flask import Flask, render_template
19+
20+
app = Flask(__name__)
21+
22+
23+
@app.route('/')
24+
def root():
25+
# For the sake of example, use static information to inflate the template.
26+
# This will be replaced with real information in later steps.
27+
dummy_times = [datetime.datetime(2018, 1, 1, 10, 0, 0),
28+
datetime.datetime(2018, 1, 2, 10, 30, 0),
29+
datetime.datetime(2018, 1, 3, 11, 0, 0),
30+
]
31+
32+
return render_template('index.html', times=dummy_times)
33+
34+
35+
if __name__ == '__main__':
36+
# This is used when running locally only. When deploying to Google App
37+
# Engine, a webserver process such as Gunicorn will serve the app. This
38+
# can be configured by adding an `entrypoint` to app.yaml.
39+
# Flask's development server will automatically serve static files in
40+
# the "static" directory. See:
41+
# http://flask.pocoo.org/docs/1.0/quickstart/#static-files. Once deployed,
42+
# App Engine itself will serve those files as configured in app.yaml.
43+
app.run(host='127.0.0.1', port=8080, debug=True)
44+
# [START gae_python38_render_template]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
import main
16+
17+
18+
def test_index():
19+
main.app.testing = True
20+
client = main.app.test_client()
21+
22+
r = client.get('/')
23+
assert r.status_code == 200
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest==5.3.2
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Flask==1.1.2
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright 2018, Google LLC
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+
16+
// [START gae_python38_log]
17+
'use strict';
18+
19+
window.addEventListener('load', function () {
20+
21+
console.log("Hello World!");
22+
23+
});
24+
// [END gae_python38_log]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
body {
2+
font-family: "helvetica", sans-serif;
3+
text-align: center;
4+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>Datastore and Firebase Auth Example</title>
5+
<script src="{{ url_for('static', filename='script.js') }}"></script>
6+
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
7+
</head>
8+
<body>
9+
10+
<h1>Datastore and Firebase Auth Example</h1>
11+
12+
<h2>Last 10 visits</h2>
13+
{% for time in times %}
14+
<p>{{ time }}</p>
15+
{% endfor %}
16+
17+
</body>
18+
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
runtime: python38
2+
3+
handlers:
4+
# This configures Google App Engine to serve the files in the app's static
5+
# directory.
6+
- url: /static
7+
static_dir: static
8+
9+
# This handler routes all requests not caught above to your main app. It is
10+
# required when static routes are defined, but can be omitted (along with
11+
# the entire handlers section) when there are no static files defined.
12+
- url: /.*
13+
script: auto

0 commit comments

Comments
 (0)