Skip to content

Commit

Permalink
Bringing App Engine storage sample up to standards
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Wayne Parrott committed Sep 24, 2015
1 parent dc48bd1 commit b452ef8
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 142 deletions.
1 change: 1 addition & 0 deletions appengine/bigquery/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib
1 change: 1 addition & 0 deletions appengine/storage/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib
12 changes: 0 additions & 12 deletions appengine/storage/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,5 @@ threadsafe: yes
api_version: 1

handlers:
- url: /
static_files: index.html
upload: index.html

- url: /favicon.ico
static_files: favicon.ico
upload: favicon.ico

- url: .*
script: main.app

libraries:
- name: jinja2
version: latest
4 changes: 4 additions & 0 deletions appengine/storage/appengine_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from google.appengine.ext import vendor

# Add any libraries installed in the "lib" folder.
vendor.add('lib')

This comment has been minimized.

Copy link
@jerjou

jerjou Sep 24, 2015

Contributor

Can you update the README to have the install step to install requirements.txt to lib?

This comment has been minimized.

Copy link
@theacodes

theacodes via email Sep 24, 2015

Contributor
16 changes: 0 additions & 16 deletions appengine/storage/index.html

This file was deleted.

21 changes: 0 additions & 21 deletions appengine/storage/listing.html

This file was deleted.

115 changes: 22 additions & 93 deletions appengine/storage/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
<https://code.google.com/p/google-api-python-client/>
Google Cloud Storage JSON API:
<https://developers.google.com/storage/docs/json_api/>
Using OAuth 2.0 for Server to Server Applications:
<https://developers.google.com/accounts/docs/OAuth2ServiceAccount>
App Identity Python API Overview:
<https://code.google.com/appengine/docs/python/appidentity/overview.html>
"""
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.
# <https://developers.google.com/console#:access>
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 = 'verdant-future-95122'

This comment has been minimized.

Copy link
@jerjou

jerjou Sep 24, 2015

Contributor

This can be 'cloud-samples-tests-bucket'

This comment has been minimized.

Copy link
@theacodes

theacodes Sep 24, 2015

Contributor

Ah I forgot to change this back, it should be a placeholder like in bigquery. Will fix.


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:
# <http://jinja.pocoo.org/docs/>
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(
'<h3>Objects.list raw response:</h3>'
'<pre>{}</pre>'.format(
json.dumps(response, sort_keys=True, indent=2)))


app = webapp.WSGIApplication(
[
('/oauth2callback', AuthHandler),
('/..*', MainHandler)
],
debug=True)
app = webapp2.WSGIApplication([
('/', MainPage)
], debug=True)
35 changes: 35 additions & 0 deletions appengine/storage/main_test.py
Original file line number Diff line number Diff line change
@@ -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))
1 change: 1 addition & 0 deletions appengine/storage/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
google-api-python-client

0 comments on commit b452ef8

Please sign in to comment.