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

chore(gae): restore 'firenotes' samples #13344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 1 appengine/standard/firebase/firenotes/backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib
28 changes: 28 additions & 0 deletions 28 appengine/standard/firebase/firenotes/backend/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This code is designed for Python 2.7 and
# the App Engine first-generation Runtime which has reached End of Support.

runtime: python27
api_version: 1
threadsafe: true
service: backend

handlers:
- url: /.*
script: main.app

env_variables:
GAE_USE_SOCKETS_HTTPLIB : 'true'
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2016 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from google.appengine.ext import vendor

# Add any libraries installed in the "lib" folder.
vendor.add("lib")
36 changes: 36 additions & 0 deletions 36 appengine/standard/firebase/firenotes/backend/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

indexes:

# AUTOGENERATED

# This index.yaml is automatically updated whenever the dev_appserver
# detects that a new type of query is run. If you want to manage the
# index.yaml file manually, remove the above marker line (the line
# saying "# AUTOGENERATED"). If you want to manage some indexes
# manually, move them above the marker line. The index.yaml file is
# automatically uploaded to the admin console when you next deploy
# your application using appcfg.py.

- kind: Note
ancestor: yes
properties:
- name: created

- kind: Note
ancestor: yes
properties:
- name: created
direction: desc
137 changes: 137 additions & 0 deletions 137 appengine/standard/firebase/firenotes/backend/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright 2016 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import os

from flask import Flask, jsonify, request
import flask_cors
from google.appengine.ext import ndb
import google.auth.transport.requests
import google.oauth2.id_token
import requests_toolbelt.adapters.appengine

# Use the App Engine Requests adapter. This makes sure that Requests uses
# URLFetch.
requests_toolbelt.adapters.appengine.monkeypatch()
HTTP_REQUEST = google.auth.transport.requests.Request()

app = Flask(__name__)
flask_cors.CORS(app)


class Note(ndb.Model):
"""NDB model class for a user's note.

Key is user id from decrypted token.
"""

friendly_id = ndb.StringProperty()
message = ndb.TextProperty()
created = ndb.DateTimeProperty(auto_now_add=True)


# [START gae_python_query_database]
# This code is for illustration purposes only.

def query_database(user_id):
"""Fetches all notes associated with user_id.

Notes are ordered them by date created, with most recent note added
first.
"""
ancestor_key = ndb.Key(Note, user_id)
query = Note.query(ancestor=ancestor_key).order(-Note.created)
notes = query.fetch()

note_messages = []

for note in notes:
note_messages.append(
{
"friendly_id": note.friendly_id,
"message": note.message,
"created": note.created,
}
)

return note_messages


# [END gae_python_query_database]


@app.route("/notes", methods=["GET"])
def list_notes():
"""Returns a list of notes added by the current Firebase user."""

# Verify Firebase auth.
# [START gae_python_verify_token]
# This code is for illustration purposes only.

id_token = request.headers["Authorization"].split(" ").pop()
claims = google.oauth2.id_token.verify_firebase_token(
id_token, HTTP_REQUEST, audience=os.environ.get("GOOGLE_CLOUD_PROJECT")
)
if not claims:
return "Unauthorized", 401
# [END gae_python_verify_token]

notes = query_database(claims["sub"])

return jsonify(notes)


@app.route("/notes", methods=["POST", "PUT"])
def add_note():
"""
Adds a note to the user's notebook. The request should be in this format:

{
"message": "note message."
}
"""

# Verify Firebase auth.
id_token = request.headers["Authorization"].split(" ").pop()
claims = google.oauth2.id_token.verify_firebase_token(
id_token, HTTP_REQUEST, audience=os.environ.get("GOOGLE_CLOUD_PROJECT")
)
if not claims:
return "Unauthorized", 401

# [START gae_python_create_entity]
# This code is for illustration purposes only.

data = request.get_json()

# Populates note properties according to the model,
# with the user ID as the key name.
note = Note(parent=ndb.Key(Note, claims["sub"]), message=data["message"])

# Some providers do not provide one of these so either can be used.
note.friendly_id = claims.get("name", claims.get("email", "Unknown"))
# [END gae_python_create_entity]

# Stores note in database.
note.put()

return "OK", 200


@app.errorhandler(500)
def server_error(e):
# Log the error and stacktrace.
logging.exception("An error occurred during a request.")
return "An internal error occurred.", 500
99 changes: 99 additions & 0 deletions 99 appengine/standard/firebase/firenotes/backend/main_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2016 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json

from google.appengine.ext import ndb
import jwt
import mock
import pytest


@pytest.fixture
def app():
# Remove any existing pyjwt handlers, as firebase_helper will register
# its own.
try:
jwt.unregister_algorithm("RS256")
except KeyError:
pass

import main

main.app.testing = True
return main.app.test_client()


@pytest.fixture
def mock_token():
patch = mock.patch("google.oauth2.id_token.verify_firebase_token")
with patch as mock_verify:
yield mock_verify


@pytest.fixture
def test_data():
from main import Note

ancestor_key = ndb.Key(Note, "123")
notes = [
Note(parent=ancestor_key, message="1"),
Note(parent=ancestor_key, message="2"),
]
ndb.put_multi(notes)
yield


def test_list_notes_with_mock_token(testbed, app, mock_token, test_data):
mock_token.return_value = {"sub": "123"}

r = app.get("/notes", headers={"Authorization": "Bearer 123"})
assert r.status_code == 200

data = json.loads(r.data)
assert len(data) == 2
assert data[0]["message"] == "2"


def test_list_notes_with_bad_mock_token(testbed, app, mock_token):
mock_token.return_value = None

r = app.get("/notes", headers={"Authorization": "Bearer 123"})
assert r.status_code == 401


def test_add_note_with_mock_token(testbed, app, mock_token):
mock_token.return_value = {"sub": "123"}

r = app.post(
"/notes",
data=json.dumps({"message": "Hello, world!"}),
content_type="application/json",
headers={"Authorization": "Bearer 123"},
)

assert r.status_code == 200

from main import Note

results = Note.query().fetch()
assert len(results) == 1
assert results[0].message == "Hello, world!"


def test_add_note_with_bad_mock_token(testbed, app, mock_token):
mock_token.return_value = None

r = app.post("/notes", headers={"Authorization": "Bearer 123"})
assert r.status_code == 401
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# pin pytest to 4.6.11 for Python2.
pytest==4.6.11; python_version < '3.0'
pytest==8.3.2; python_version >= '3.0'
mock==3.0.5; python_version < '3.0'
mock==5.1.0; python_version >= '3.0'
10 changes: 10 additions & 0 deletions 10 appengine/standard/firebase/firenotes/backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Flask==1.1.4; python_version < '3.0'
Flask==3.0.0; python_version > '3.0'
pyjwt==1.7.1; python_version < '3.0'
flask-cors==3.0.10
google-auth==2.17.3; python_version < '3.0'
google-auth==2.17.3; python_version > '3.0'
requests==2.27.1
requests-toolbelt==0.10.1
Werkzeug==1.0.1; python_version < '3.0'
Werkzeug==3.0.3; python_version > '3.0'
34 changes: 34 additions & 0 deletions 34 appengine/standard/firebase/firenotes/frontend/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This code is for illustration purposes only.

# This code is designed for Python 2.7 and
# the App Engine first-generation Runtime which has reached End of Support.

runtime: python27
api_version: 1
service: default
threadsafe: true

handlers:

# root
- url: /
static_files: index.html
upload: index.html

- url: /(.+)
static_files: \1
upload: (.+)
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.