Added TODO and did PEP 8 cleanup.
This commit is contained in:
4
Makefile
4
Makefile
@@ -1,2 +1,2 @@
|
|||||||
default:
|
pep8:
|
||||||
python discovery.py
|
find apiclient samples -name "*.py" | xargs pep8 --ignore=E111,E202
|
||||||
|
|||||||
21
TODO
Normal file
21
TODO
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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'}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user