Added TODO and did PEP 8 cleanup.

This commit is contained in:
Joe Gregorio
2010-08-18 15:21:06 -04:00
parent a4a7eb065c
commit 41cf797f87
5 changed files with 74 additions and 60 deletions

View File

@@ -1,2 +1,2 @@
default: pep8:
python discovery.py find apiclient samples -name "*.py" | xargs pep8 --ignore=E111,E202

21
TODO Normal file
View File

@@ -0,0 +1,21 @@
TODO
====
- Unit tests against copies of current discovery docs
- Flag required parameters
- Check the regex when accepting values
- OAuth cmdline sample should start local http server to catch response.
- 'Extra Discovery' for pagination
- Implement requests as Command objects, either for immediate
execution, or for batching.
- Requests for multiple APIs at one time.
- 2.x and 3.x compatible

View File

@@ -27,9 +27,12 @@ import simplejson
import urlparse import urlparse
import uritemplate import uritemplate
class HttpError(Exception): pass
DISCOVERY_URI = 'http://www.googleapis.com/discovery/0.1/describe{?api,apiVersion}' class HttpError(Exception):
pass
DISCOVERY_URI = 'http://www.googleapis.com/discovery/0.1/describe' +
'{?api,apiVersion}'
def key2method(key): def key2method(key):
@@ -73,6 +76,7 @@ def key2param(key):
class JsonModel(object): class JsonModel(object):
def request(self, headers, params): def request(self, headers, params):
model = params.get('body', None) model = params.get('body', None)
query = '?alt=json&prettyprint=true' query = '?alt=json&prettyprint=true'
@@ -80,13 +84,14 @@ class JsonModel(object):
if model == None: if model == None:
return (headers, params, query, None) return (headers, params, query, None)
else: else:
model = {'data': model } model = {'data': model}
headers['Content-Type'] = 'application/json' headers['Content-Type'] = 'application/json'
del params['body'] del params['body']
return (headers, params, query, simplejson.dumps(model)) return (headers, params, query, simplejson.dumps(model))
def response(self, resp, content): def response(self, resp, content):
# Error handling is TBD # Error handling is TBD, for example, do we retry
# for some operation/error combinations?
if resp.status < 300: if resp.status < 300:
return simplejson.loads(content)['data'] return simplejson.loads(content)['data']
else: else:
@@ -97,7 +102,7 @@ class JsonModel(object):
def build(service, version, http=httplib2.Http(), def build(service, version, http=httplib2.Http(),
discoveryServiceUrl = DISCOVERY_URI, auth = None, model = JsonModel()): discoveryServiceUrl=DISCOVERY_URI, auth=None, model=JsonModel()):
params = { params = {
'api': service, 'api': service,
'apiVersion': version 'apiVersion': version
@@ -117,6 +122,7 @@ def build(service, version, http=httplib2.Http(),
self._model = model self._model = model
def createMethod(theclass, methodName, methodDesc): def createMethod(theclass, methodName, methodDesc):
def method(self, **kwargs): def method(self, **kwargs):
return createResource(self._http, self._baseUrl, self._model, return createResource(self._http, self._baseUrl, self._model,
methodName, methodDesc) methodName, methodDesc)

View File

@@ -10,38 +10,14 @@ A detailed description of discovery.
__author__ = 'jcgregorio@google.com (Joe Gregorio)' __author__ = 'jcgregorio@google.com (Joe Gregorio)'
# TODO
# - Add normalize_ that converts max-results into MaxResults
#
# - Each 'resource' should be its own object accessible
# from the service object returned from discovery.
#
# - Methods can either execute immediately or return
# RestRequest objects which can be batched.
#
# - 'Body' parameter for non-GET requests
#
# - 2.x and 3.x compatible
# JS also has the idea of a TransportRequest and a Transport.
# The Transport has a doRequest() method that takes a request
# and a callback function.
#
# Discovery doc notes
# - Which parameters are optional vs mandatory
# - Is pattern a regex?
# - Inconsistent naming max-results vs userId
from apiclient.discovery import build from apiclient.discovery import build
import httplib2 import httplib2
import simplejson
import re
import oauth2 as oauth import oauth2 as oauth
import re
import simplejson
def oauth_wrap(consumer, token, http): def oauth_wrap(consumer, token, http):
""" """
@@ -51,7 +27,7 @@ def oauth_wrap(consumer, token, http):
Returns: Returns:
A modified instance of http that was passed in. A modified instance of http that was passed in.
Example: Example:
h = httplib2.Http() h = httplib2.Http()
@@ -61,14 +37,15 @@ def oauth_wrap(consumer, token, http):
subclass of httplib2.Authenication because subclass of httplib2.Authenication because
it never gets passed the absolute URI, which is it never gets passed the absolute URI, which is
needed for signing. So instead we have to overload needed for signing. So instead we have to overload
'request' with a closure that adds in the 'request' with a closure that adds in the
Authorization header and then calls the original version Authorization header and then calls the original version
of 'request()'. of 'request()'.
""" """
request_orig = http.request request_orig = http.request
signer = oauth.SignatureMethod_HMAC_SHA1() signer = oauth.SignatureMethod_HMAC_SHA1()
def new_request(uri, method="GET", body=None, headers=None, redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None): def new_request(uri, method="GET", body=None, headers=None,
redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None):
"""Modify the request headers to add the appropriate """Modify the request headers to add the appropriate
Authorization header.""" Authorization header."""
req = oauth.Request.from_consumer_and_token( req = oauth.Request.from_consumer_and_token(
@@ -78,19 +55,23 @@ def oauth_wrap(consumer, token, http):
headers = {} headers = {}
headers.update(req.to_header()) headers.update(req.to_header())
headers['user-agent'] = 'jcgregorio-test-client' headers['user-agent'] = 'jcgregorio-test-client'
return request_orig(uri, method, body, headers, redirections, connection_type) return request_orig(uri, method, body, headers,
redirections, connection_type)
http.request = new_request http.request = new_request
return http return http
def get_wrapped_http(): def get_wrapped_http():
f = open("oauth_token.dat", "r") f = open("oauth_token.dat", "r")
oauth_params = simplejson.loads(f.read()) oauth_params = simplejson.loads(f.read())
consumer = oauth.Consumer(oauth_params['consumer_key'], oauth_params['consumer_secret']) consumer = oauth.Consumer(
token = oauth.Token(oauth_params['oauth_token'], oauth_params['oauth_token_secret']) oauth_params['consumer_key'], oauth_params['consumer_secret'])
token = oauth.Token(
oauth_params['oauth_token'], oauth_params['oauth_token_secret'])
# Create a simple monkeypatch for httplib2.Http.request # Create a simple monkeypatch for httplib2.Http.request
# just adds in the oauth authorization header and then calls # just adds in the oauth authorization header and then calls
# the original request(). # the original request().
http = httplib2.Http() http = httplib2.Http()
@@ -99,14 +80,14 @@ def get_wrapped_http():
def main(): def main():
http = get_wrapped_http() http = get_wrapped_http()
p = build("buzz", "v1", http = http) p = build("buzz", "v1", http=http)
activities = p.activities() activities = p.activities()
activitylist = activities.list(scope='@self', userId='@me') activitylist = activities.list(scope='@self', userId='@me')
print activitylist['items'][0]['title'] print activitylist['items'][0]['title']
activities.insert(userId='@me', body={ activities.insert(userId='@me', body={
'title': 'Testing insert', 'title': 'Testing insert',
'object': { 'object': {
'content': u'Just a short note to show that insert is working. ☄', 'content': u'Just a short note to show that insert is working. ☄',
'type': 'note'} 'type': 'note'}
} }
) )

View File

@@ -9,7 +9,7 @@ try:
except ImportError: except ImportError:
from cgi import parse_qs, parse_qsl from cgi import parse_qs, parse_qsl
httplib2.debuglevel=4 httplib2.debuglevel = 4
headers = {"user-agent": "jcgregorio-buzz-client", headers = {"user-agent": "jcgregorio-buzz-client",
'content-type': 'application/x-www-form-urlencoded' 'content-type': 'application/x-www-form-urlencoded'
} }
@@ -17,18 +17,22 @@ headers = {"user-agent": "jcgregorio-buzz-client",
consumer_key = 'anonymous' consumer_key = 'anonymous'
consumer_secret = 'anonymous' consumer_secret = 'anonymous'
request_token_url = 'https://www.google.com/accounts/OAuthGetRequestToken?domain=anonymous&scope=https://www.googleapis.com/auth/buzz' request_token_url = 'https://www.google.com/accounts/OAuthGetRequestToken' +
access_token_url = 'https://www.google.com/accounts/OAuthGetAccessToken?domain=anonymous&scope=https://www.googleapis.com/auth/buzz' '?domain=anonymous&scope=https://www.googleapis.com/auth/buzz'
authorize_url = 'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?domain=anonymous&scope=https://www.googleapis.com/auth/buzz' access_token_url = 'https://www.google.com/accounts/OAuthGetAccessToken' +
'?domain=anonymous&scope=https://www.googleapis.com/auth/buzz'
authorize_url = 'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken' +
'?domain=anonymous&scope=https://www.googleapis.com/auth/buzz'
consumer = oauth.Consumer(consumer_key, consumer_secret) consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer) client = oauth.Client(consumer)
# Step 1: Get a request token. This is a temporary token that is used for # Step 1: Get a request token. This is a temporary token that is used for
# having the user authorize an access token and to sign the request to obtain # having the user authorize an access token and to sign the request to obtain
# said access token. # said access token.
resp, content = client.request(request_token_url, "POST", headers=headers, body="oauth_callback=oob") resp, content = client.request(request_token_url, "POST", headers=headers,
body="oauth_callback=oob")
if resp['status'] != '200': if resp['status'] != '200':
print content print content
raise Exception("Invalid response %s." % resp['status']) raise Exception("Invalid response %s." % resp['status'])
@@ -38,9 +42,9 @@ request_token = dict(parse_qsl(content))
print "Request Token:" print "Request Token:"
print " - oauth_token = %s" % request_token['oauth_token'] print " - oauth_token = %s" % request_token['oauth_token']
print " - oauth_token_secret = %s" % request_token['oauth_token_secret'] print " - oauth_token_secret = %s" % request_token['oauth_token_secret']
print print
# Step 2: Redirect to the provider. Since this is a CLI script we do not # Step 2: Redirect to the provider. Since this is a CLI script we do not
# redirect. In a web application you would redirect the user to the URL # redirect. In a web application you would redirect the user to the URL
# below. # below.
@@ -56,20 +60,21 @@ authorize_url = urlparse.urlunparse(url)
print "Go to the following link in your browser:" print "Go to the following link in your browser:"
print authorize_url print authorize_url
print print
# After the user has granted access to you, the consumer, the provider will # After the user has granted access to you, the consumer, the provider will
# redirect you to whatever URL you have told them to redirect to. You can # redirect you to whatever URL you have told them to redirect to. You can
# usually define this in the oauth_callback argument as well. # usually define this in the oauth_callback argument as well.
accepted = 'n' accepted = 'n'
while accepted.lower() == 'n': while accepted.lower() == 'n':
accepted = raw_input('Have you authorized me? (y/n) ') accepted = raw_input('Have you authorized me? (y/n) ')
oauth_verifier = raw_input('What is the PIN? ') oauth_verifier = raw_input('What is the PIN? ').strip()
# Step 3: Once the consumer has redirected the user back to the oauth_callback # Step 3: Once the consumer has redirected the user back to the oauth_callback
# URL you can request the access token the user has approved. You use the # URL you can request the access token the user has approved. You use the
# request token to sign this request. After this is done you throw away the # request token to sign this request. After this is done you throw away the
# request token and use the access token returned. You should store this # request token and use the access token returned. You should store this
# access token somewhere safe, like a database, for future use. # access token somewhere safe, like a database, for future use.
token = oauth.Token(request_token['oauth_token'], token = oauth.Token(request_token['oauth_token'],
request_token['oauth_token_secret']) request_token['oauth_token_secret'])
@@ -83,13 +88,14 @@ print "Access Token:"
print " - oauth_token = %s" % access_token['oauth_token'] print " - oauth_token = %s" % access_token['oauth_token']
print " - oauth_token_secret = %s" % access_token['oauth_token_secret'] print " - oauth_token_secret = %s" % access_token['oauth_token_secret']
print print
print "You may now access protected resources using the access tokens above." print "You may now access protected resources using the access tokens above."
print print
d = dict( d = dict(
consumer_key = 'anonymous', consumer_key='anonymous',
consumer_secret = 'anonymous' consumer_secret='anonymous'
) )
d.update(access_token) d.update(access_token)
f = open("oauth_token.dat", "w") f = open("oauth_token.dat", "w")