Skip to content

Commit 656607f

Browse files
committed
Merge pull request #117 from GoogleCloudPlatform/appengine-storage-standards
Bringing App Engine storage sample up to standards
2 parents 5a1ffb0 + 1aed5cc commit 656607f

File tree

9 files changed

+64
-142
lines changed

9 files changed

+64
-142
lines changed

appengine/bigquery/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib

appengine/storage/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib

appengine/storage/app.yaml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,5 @@ threadsafe: yes
33
api_version: 1
44

55
handlers:
6-
- url: /
7-
static_files: index.html
8-
upload: index.html
9-
10-
- url: /favicon.ico
11-
static_files: favicon.ico
12-
upload: favicon.ico
13-
146
- url: .*
157
script: main.app
16-
17-
libraries:
18-
- name: jinja2
19-
version: latest

appengine/storage/appengine_config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from google.appengine.ext import vendor
2+
3+
# Add any libraries installed in the "lib" folder.
4+
vendor.add('lib')

appengine/storage/index.html

Lines changed: 0 additions & 16 deletions
This file was deleted.

appengine/storage/listing.html

Lines changed: 0 additions & 21 deletions
This file was deleted.

appengine/storage/main.py

Lines changed: 22 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -14,109 +14,38 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17-
"""Present formatted listings for Google Cloud Storage buckets.
18-
This Google App Engine application takes a bucket name in the URL path and uses
19-
the Google Cloud Storage JSON API and Google's Python client library to list
20-
the bucket's contents.
21-
For example, if this app is invoked with the URI
22-
http://bucket-list.appspot.com/foo, it would extract the bucket name 'foo' and
23-
issue a request to GCS for its contents. The app formats the listing into an
24-
XML document, which is prepended with a reference to an XSLT style sheet for
25-
human readable presentation.
26-
For more information:
27-
Google APIs Client Library for Python:
28-
<https://code.google.com/p/google-api-python-client/>
29-
Google Cloud Storage JSON API:
30-
<https://developers.google.com/storage/docs/json_api/>
31-
Using OAuth 2.0 for Server to Server Applications:
32-
<https://developers.google.com/accounts/docs/OAuth2ServiceAccount>
33-
App Identity Python API Overview:
34-
<https://code.google.com/appengine/docs/python/appidentity/overview.html>
3517
"""
18+
Sample Google App Engine application that lists the objects in a Google Cloud
19+
Storage bucket.
3620
37-
import os
21+
For more information about Cloud Storage, see README.md in /storage.
22+
For more information about Google App Engine, see README.md in /appengine.
23+
"""
3824

39-
from apiclient.discovery import build as build_service
40-
from google.appengine.ext import webapp
41-
from google.appengine.ext.webapp.util import login_required
42-
import httplib2
43-
import jinja2
44-
from oauth2client.client import OAuth2WebServerFlow
25+
import json
4526

46-
# NOTE: You must provide a client ID and secret with access to the GCS JSON
47-
# API.
48-
# You can acquire a client ID and secret from the Google Developers Console.
49-
# <https://developers.google.com/console#:access>
50-
CLIENT_ID = ''
51-
CLIENT_SECRET = ''
52-
SCOPE = 'https://www.googleapis.com/auth/devstorage.read_only'
53-
USER_AGENT = 'app-engine-bucket-lister'
27+
from googleapiclient import discovery
28+
from oauth2client.client import GoogleCredentials
29+
import webapp2
5430

55-
# Since we don't plan to use all object attributes, we pass a fields argument
56-
# to specify what the server should return.
57-
FIELDS = 'items(name,media(timeCreated,hash,length))'
5831

32+
# The bucket that will be used to list objects.
33+
BUCKET_NAME = '<your-bucket-name>'
5934

60-
def GetBucketName(path):
61-
bucket = path[1:] # Trim the preceding slash
62-
if bucket[-1] == '/':
63-
# Trim final slash, if necessary.
64-
bucket = bucket[:-1]
65-
return bucket
35+
credentials = GoogleCredentials.get_application_default()
36+
storage = discovery.build('storage', 'v1', credentials=credentials)
6637

6738

68-
class MainHandler(webapp.RequestHandler):
69-
@login_required
39+
class MainPage(webapp2.RequestHandler):
7040
def get(self):
71-
callback = self.request.host_url + '/oauth2callback'
72-
flow = OAuth2WebServerFlow(
73-
client_id=CLIENT_ID,
74-
client_secret=CLIENT_SECRET,
75-
redirect_uri=callback,
76-
access_type='online',
77-
scope=SCOPE,
78-
user_agent=USER_AGENT)
79-
80-
bucket = GetBucketName(self.request.path)
81-
step2_url = flow.step1_get_authorize_url()
82-
# Add state to remember which bucket to list.
83-
self.redirect(step2_url + '&state=%s' % bucket)
84-
85-
86-
class AuthHandler(webapp.RequestHandler):
87-
@login_required
88-
def get(self):
89-
callback = self.request.host_url + '/oauth2callback'
90-
flow = OAuth2WebServerFlow(
91-
client_id=CLIENT_ID,
92-
client_secret=CLIENT_SECRET,
93-
redirect_uri=callback,
94-
scope=SCOPE,
95-
user_agent=USER_AGENT)
96-
97-
# Exchange the code (in self.request.params) for an access token.
98-
credentials = flow.step2_exchange(self.request.params)
99-
http = credentials.authorize(httplib2.Http())
100-
101-
bucket = self.request.get('state')
102-
storage = build_service('storage', 'v1beta1', http=http)
103-
list_response = storage.objects().list(bucket=bucket,
104-
fields=FIELDS).execute()
105-
template_values = {
106-
'items': list_response['items'], 'bucket_name': bucket}
41+
response = storage.objects().list(bucket=BUCKET_NAME).execute()
10742

108-
# We use a templating engine to format our output. For more
109-
# information:
110-
# <http://jinja.pocoo.org/docs/>
111-
jinja_env = jinja2.Environment(
112-
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
113-
template = jinja_env.get_template('listing.html')
114-
self.response.out.write(template.render(template_values))
43+
self.response.write(
44+
'<h3>Objects.list raw response:</h3>'
45+
'<pre>{}</pre>'.format(
46+
json.dumps(response, sort_keys=True, indent=2)))
11547

11648

117-
app = webapp.WSGIApplication(
118-
[
119-
('/oauth2callback', AuthHandler),
120-
('/..*', MainHandler)
121-
],
122-
debug=True)
49+
app = webapp2.WSGIApplication([
50+
('/', MainPage)
51+
], debug=True)

appengine/storage/main_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
import re
15+
16+
import tests
17+
import webtest
18+
19+
from . import main
20+
21+
22+
class TestStorageSample(tests.AppEngineTestbedCase):
23+
24+
def setUp(self):
25+
super(TestStorageSample, self).setUp()
26+
self.app = webtest.TestApp(main.app)
27+
main.BUCKET_NAME = self.bucket_name
28+
29+
def test_get(self):
30+
response = self.app.get('/')
31+
32+
self.assertEqual(response.status_int, 200)
33+
self.assertRegexpMatches(
34+
response.body,
35+
re.compile(r'.*.*items.*etag.*', re.DOTALL))

appengine/storage/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
google-api-python-client

0 commit comments

Comments
 (0)