-
-
diff --git a/appengine/storage/main.py b/appengine/storage/main.py
index 3f3471f997f..e952878ced8 100644
--- a/appengine/storage/main.py
+++ b/appengine/storage/main.py
@@ -14,109 +14,38 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Present formatted listings for Google Cloud Storage buckets.
-This Google App Engine application takes a bucket name in the URL path and uses
-the Google Cloud Storage JSON API and Google's Python client library to list
-the bucket's contents.
-For example, if this app is invoked with the URI
-http://bucket-list.appspot.com/foo, it would extract the bucket name 'foo' and
-issue a request to GCS for its contents. The app formats the listing into an
-XML document, which is prepended with a reference to an XSLT style sheet for
-human readable presentation.
-For more information:
-Google APIs Client Library for Python:
-
-Google Cloud Storage JSON API:
-
-Using OAuth 2.0 for Server to Server Applications:
-
-App Identity Python API Overview:
-
"""
+Sample Google App Engine application that lists the objects in a Google Cloud
+Storage bucket.
-import os
+For more information about Cloud Storage, see README.md in /storage.
+For more information about Google App Engine, see README.md in /appengine.
+"""
-from apiclient.discovery import build as build_service
-from google.appengine.ext import webapp
-from google.appengine.ext.webapp.util import login_required
-import httplib2
-import jinja2
-from oauth2client.client import OAuth2WebServerFlow
+import json
-# NOTE: You must provide a client ID and secret with access to the GCS JSON
-# API.
-# You can acquire a client ID and secret from the Google Developers Console.
-#
-CLIENT_ID = ''
-CLIENT_SECRET = ''
-SCOPE = 'https://www.googleapis.com/auth/devstorage.read_only'
-USER_AGENT = 'app-engine-bucket-lister'
+from googleapiclient import discovery
+from oauth2client.client import GoogleCredentials
+import webapp2
-# Since we don't plan to use all object attributes, we pass a fields argument
-# to specify what the server should return.
-FIELDS = 'items(name,media(timeCreated,hash,length))'
+# The bucket that will be used to list objects.
+BUCKET_NAME = ''
-def GetBucketName(path):
- bucket = path[1:] # Trim the preceding slash
- if bucket[-1] == '/':
- # Trim final slash, if necessary.
- bucket = bucket[:-1]
- return bucket
+credentials = GoogleCredentials.get_application_default()
+storage = discovery.build('storage', 'v1', credentials=credentials)
-class MainHandler(webapp.RequestHandler):
- @login_required
+class MainPage(webapp2.RequestHandler):
def get(self):
- callback = self.request.host_url + '/oauth2callback'
- flow = OAuth2WebServerFlow(
- client_id=CLIENT_ID,
- client_secret=CLIENT_SECRET,
- redirect_uri=callback,
- access_type='online',
- scope=SCOPE,
- user_agent=USER_AGENT)
-
- bucket = GetBucketName(self.request.path)
- step2_url = flow.step1_get_authorize_url()
- # Add state to remember which bucket to list.
- self.redirect(step2_url + '&state=%s' % bucket)
-
-
-class AuthHandler(webapp.RequestHandler):
- @login_required
- def get(self):
- callback = self.request.host_url + '/oauth2callback'
- flow = OAuth2WebServerFlow(
- client_id=CLIENT_ID,
- client_secret=CLIENT_SECRET,
- redirect_uri=callback,
- scope=SCOPE,
- user_agent=USER_AGENT)
-
- # Exchange the code (in self.request.params) for an access token.
- credentials = flow.step2_exchange(self.request.params)
- http = credentials.authorize(httplib2.Http())
-
- bucket = self.request.get('state')
- storage = build_service('storage', 'v1beta1', http=http)
- list_response = storage.objects().list(bucket=bucket,
- fields=FIELDS).execute()
- template_values = {
- 'items': list_response['items'], 'bucket_name': bucket}
+ response = storage.objects().list(bucket=BUCKET_NAME).execute()
- # We use a templating engine to format our output. For more
- # information:
- #
- jinja_env = jinja2.Environment(
- loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
- template = jinja_env.get_template('listing.html')
- self.response.out.write(template.render(template_values))
+ self.response.write(
+ '
Objects.list raw response:
'
+ '
{}
'.format(
+ json.dumps(response, sort_keys=True, indent=2)))
-app = webapp.WSGIApplication(
- [
- ('/oauth2callback', AuthHandler),
- ('/..*', MainHandler)
- ],
- debug=True)
+app = webapp2.WSGIApplication([
+ ('/', MainPage)
+], debug=True)
diff --git a/appengine/storage/main_test.py b/appengine/storage/main_test.py
new file mode 100644
index 00000000000..8648b8a1e6b
--- /dev/null
+++ b/appengine/storage/main_test.py
@@ -0,0 +1,35 @@
+# Copyright 2015 Google Inc. All rights reserved.
+#
+# 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 re
+
+import tests
+import webtest
+
+from . import main
+
+
+class TestStorageSample(tests.AppEngineTestbedCase):
+
+ def setUp(self):
+ super(TestStorageSample, self).setUp()
+ self.app = webtest.TestApp(main.app)
+ main.BUCKET_NAME = self.bucket_name
+
+ def test_get(self):
+ response = self.app.get('/')
+
+ self.assertEqual(response.status_int, 200)
+ self.assertRegexpMatches(
+ response.body,
+ re.compile(r'.*.*items.*etag.*', re.DOTALL))
diff --git a/appengine/storage/requirements.txt b/appengine/storage/requirements.txt
new file mode 100644
index 00000000000..d8055e00cd9
--- /dev/null
+++ b/appengine/storage/requirements.txt
@@ -0,0 +1 @@
+google-api-python-client