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 02a0c8e

Browse filesBrowse files
chore(gae): restore 'firenotes' samples (GoogleCloudPlatform#13344)
* chore: restore 'firenotes' samples * chore(gae): fix headers * chore(gae): delete README.md * chore(gae): add a note to app.yaml that this example is designed for Python 2.7 which is not supported * chore(gae): add comments that these samples are for illustration purposes only * chore(gae): update note for Python 2.7 and GAE first-generation End of Support.
1 parent bb70e5f commit 02a0c8e
Copy full SHA for 02a0c8e

File tree

Expand file treeCollapse file tree

12 files changed

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

12 files changed

+617
-0
lines changed
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright 2021 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+
# This code is designed for Python 2.7 and
16+
# the App Engine first-generation Runtime which has reached End of Support.
17+
18+
runtime: python27
19+
api_version: 1
20+
threadsafe: true
21+
service: backend
22+
23+
handlers:
24+
- url: /.*
25+
script: main.app
26+
27+
env_variables:
28+
GAE_USE_SOCKETS_HTTPLIB : 'true'
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2016 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+
from google.appengine.ext import vendor
16+
17+
# Add any libraries installed in the "lib" folder.
18+
vendor.add("lib")
+36Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2021 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+
indexes:
16+
17+
# AUTOGENERATED
18+
19+
# This index.yaml is automatically updated whenever the dev_appserver
20+
# detects that a new type of query is run. If you want to manage the
21+
# index.yaml file manually, remove the above marker line (the line
22+
# saying "# AUTOGENERATED"). If you want to manage some indexes
23+
# manually, move them above the marker line. The index.yaml file is
24+
# automatically uploaded to the admin console when you next deploy
25+
# your application using appcfg.py.
26+
27+
- kind: Note
28+
ancestor: yes
29+
properties:
30+
- name: created
31+
32+
- kind: Note
33+
ancestor: yes
34+
properties:
35+
- name: created
36+
direction: desc
+137Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Copyright 2016 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 logging
16+
import os
17+
18+
from flask import Flask, jsonify, request
19+
import flask_cors
20+
from google.appengine.ext import ndb
21+
import google.auth.transport.requests
22+
import google.oauth2.id_token
23+
import requests_toolbelt.adapters.appengine
24+
25+
# Use the App Engine Requests adapter. This makes sure that Requests uses
26+
# URLFetch.
27+
requests_toolbelt.adapters.appengine.monkeypatch()
28+
HTTP_REQUEST = google.auth.transport.requests.Request()
29+
30+
app = Flask(__name__)
31+
flask_cors.CORS(app)
32+
33+
34+
class Note(ndb.Model):
35+
"""NDB model class for a user's note.
36+
37+
Key is user id from decrypted token.
38+
"""
39+
40+
friendly_id = ndb.StringProperty()
41+
message = ndb.TextProperty()
42+
created = ndb.DateTimeProperty(auto_now_add=True)
43+
44+
45+
# [START gae_python_query_database]
46+
# This code is for illustration purposes only.
47+
48+
def query_database(user_id):
49+
"""Fetches all notes associated with user_id.
50+
51+
Notes are ordered them by date created, with most recent note added
52+
first.
53+
"""
54+
ancestor_key = ndb.Key(Note, user_id)
55+
query = Note.query(ancestor=ancestor_key).order(-Note.created)
56+
notes = query.fetch()
57+
58+
note_messages = []
59+
60+
for note in notes:
61+
note_messages.append(
62+
{
63+
"friendly_id": note.friendly_id,
64+
"message": note.message,
65+
"created": note.created,
66+
}
67+
)
68+
69+
return note_messages
70+
71+
72+
# [END gae_python_query_database]
73+
74+
75+
@app.route("/notes", methods=["GET"])
76+
def list_notes():
77+
"""Returns a list of notes added by the current Firebase user."""
78+
79+
# Verify Firebase auth.
80+
# [START gae_python_verify_token]
81+
# This code is for illustration purposes only.
82+
83+
id_token = request.headers["Authorization"].split(" ").pop()
84+
claims = google.oauth2.id_token.verify_firebase_token(
85+
id_token, HTTP_REQUEST, audience=os.environ.get("GOOGLE_CLOUD_PROJECT")
86+
)
87+
if not claims:
88+
return "Unauthorized", 401
89+
# [END gae_python_verify_token]
90+
91+
notes = query_database(claims["sub"])
92+
93+
return jsonify(notes)
94+
95+
96+
@app.route("/notes", methods=["POST", "PUT"])
97+
def add_note():
98+
"""
99+
Adds a note to the user's notebook. The request should be in this format:
100+
101+
{
102+
"message": "note message."
103+
}
104+
"""
105+
106+
# Verify Firebase auth.
107+
id_token = request.headers["Authorization"].split(" ").pop()
108+
claims = google.oauth2.id_token.verify_firebase_token(
109+
id_token, HTTP_REQUEST, audience=os.environ.get("GOOGLE_CLOUD_PROJECT")
110+
)
111+
if not claims:
112+
return "Unauthorized", 401
113+
114+
# [START gae_python_create_entity]
115+
# This code is for illustration purposes only.
116+
117+
data = request.get_json()
118+
119+
# Populates note properties according to the model,
120+
# with the user ID as the key name.
121+
note = Note(parent=ndb.Key(Note, claims["sub"]), message=data["message"])
122+
123+
# Some providers do not provide one of these so either can be used.
124+
note.friendly_id = claims.get("name", claims.get("email", "Unknown"))
125+
# [END gae_python_create_entity]
126+
127+
# Stores note in database.
128+
note.put()
129+
130+
return "OK", 200
131+
132+
133+
@app.errorhandler(500)
134+
def server_error(e):
135+
# Log the error and stacktrace.
136+
logging.exception("An error occurred during a request.")
137+
return "An internal error occurred.", 500
+99Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Copyright 2016 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 json
16+
17+
from google.appengine.ext import ndb
18+
import jwt
19+
import mock
20+
import pytest
21+
22+
23+
@pytest.fixture
24+
def app():
25+
# Remove any existing pyjwt handlers, as firebase_helper will register
26+
# its own.
27+
try:
28+
jwt.unregister_algorithm("RS256")
29+
except KeyError:
30+
pass
31+
32+
import main
33+
34+
main.app.testing = True
35+
return main.app.test_client()
36+
37+
38+
@pytest.fixture
39+
def mock_token():
40+
patch = mock.patch("google.oauth2.id_token.verify_firebase_token")
41+
with patch as mock_verify:
42+
yield mock_verify
43+
44+
45+
@pytest.fixture
46+
def test_data():
47+
from main import Note
48+
49+
ancestor_key = ndb.Key(Note, "123")
50+
notes = [
51+
Note(parent=ancestor_key, message="1"),
52+
Note(parent=ancestor_key, message="2"),
53+
]
54+
ndb.put_multi(notes)
55+
yield
56+
57+
58+
def test_list_notes_with_mock_token(testbed, app, mock_token, test_data):
59+
mock_token.return_value = {"sub": "123"}
60+
61+
r = app.get("/notes", headers={"Authorization": "Bearer 123"})
62+
assert r.status_code == 200
63+
64+
data = json.loads(r.data)
65+
assert len(data) == 2
66+
assert data[0]["message"] == "2"
67+
68+
69+
def test_list_notes_with_bad_mock_token(testbed, app, mock_token):
70+
mock_token.return_value = None
71+
72+
r = app.get("/notes", headers={"Authorization": "Bearer 123"})
73+
assert r.status_code == 401
74+
75+
76+
def test_add_note_with_mock_token(testbed, app, mock_token):
77+
mock_token.return_value = {"sub": "123"}
78+
79+
r = app.post(
80+
"/notes",
81+
data=json.dumps({"message": "Hello, world!"}),
82+
content_type="application/json",
83+
headers={"Authorization": "Bearer 123"},
84+
)
85+
86+
assert r.status_code == 200
87+
88+
from main import Note
89+
90+
results = Note.query().fetch()
91+
assert len(results) == 1
92+
assert results[0].message == "Hello, world!"
93+
94+
95+
def test_add_note_with_bad_mock_token(testbed, app, mock_token):
96+
mock_token.return_value = None
97+
98+
r = app.post("/notes", headers={"Authorization": "Bearer 123"})
99+
assert r.status_code == 401
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# pin pytest to 4.6.11 for Python2.
2+
pytest==4.6.11; python_version < '3.0'
3+
pytest==8.3.2; python_version >= '3.0'
4+
mock==3.0.5; python_version < '3.0'
5+
mock==5.1.0; python_version >= '3.0'
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Flask==1.1.4; python_version < '3.0'
2+
Flask==3.0.0; python_version > '3.0'
3+
pyjwt==1.7.1; python_version < '3.0'
4+
flask-cors==3.0.10
5+
google-auth==2.17.3; python_version < '3.0'
6+
google-auth==2.17.3; python_version > '3.0'
7+
requests==2.27.1
8+
requests-toolbelt==0.10.1
9+
Werkzeug==1.0.1; python_version < '3.0'
10+
Werkzeug==3.0.3; python_version > '3.0'
+34Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2021 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+
# This code is for illustration purposes only.
16+
17+
# This code is designed for Python 2.7 and
18+
# the App Engine first-generation Runtime which has reached End of Support.
19+
20+
runtime: python27
21+
api_version: 1
22+
service: default
23+
threadsafe: true
24+
25+
handlers:
26+
27+
# root
28+
- url: /
29+
static_files: index.html
30+
upload: index.html
31+
32+
- url: /(.+)
33+
static_files: \1
34+
upload: (.+)

0 commit comments

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