First pass at adding in pagination

This commit is contained in:
Joe Gregorio
2010-08-25 23:49:30 -04:00
parent af7a127466
commit 6d5e94f0ce
10 changed files with 366 additions and 39 deletions

View File

@@ -3,3 +3,6 @@ pep8:
test:
python runtests.py
skeletons:
python discovery_extras.py tests/data/buzz.json tests/data/latitude.json tests/data/moderator.json

View File

@@ -0,0 +1,89 @@
{
"data": {
"buzz": {
"v1": {
"baseUrl": "https://www.googleapis.com/",
"resources": {
"activities": {
"methods": {
"delete": {},
"get": {},
"insert": {},
"list": {
"next": {
"type": "uri",
"location": ["links", "next", 0, "href"]
}
},
"search": {
"next": {
"type": "uri",
"location": ["links", "next", 0, "href"]
}
},
"update": {}
}
},
"comments": {
"methods": {
"delete": {},
"get": {},
"insert": {},
"list": {},
"update": {}
}
},
"feeds": {
"methods": {
"delete": {},
"insert": {},
"list": {},
"update": {}
}
},
"groups": {
"methods": {
"delete": {},
"get": {},
"insert": {},
"list": {
"next": {
"type": "uri",
"location": ["links", "next", 0, "href"]
}
},
"update": {}
}
},
"people": {
"methods": {
"delete": {},
"get": {},
"liked": {},
"list": {},
"relatedToUri": {},
"reshared": {},
"search": {},
"update": {}
}
},
"photos": {
"methods": {
"insert": {}
}
},
"related": {
"methods": {
"list": {}
}
},
"search": {
"methods": {
"extractPeople": {}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,26 @@
{
"data": {
"latitude": {
"v1": {
"baseUrl": "https://www.googleapis.com/",
"resources": {
"currentLocation": {
"methods": {
"delete": {},
"get": {},
"insert": {}
}
},
"location": {
"methods": {
"delete": {},
"get": {},
"insert": {},
"list": {}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,60 @@
{
"data": {
"moderator": {
"v1": {
"baseUrl": "https://www.googleapis.com/",
"resources": {
"profiles": {
"methods": {
"get": {},
"update": {}
}
},
"responses": {
"methods": {
"insert": {},
"list": {}
}
},
"series": {
"methods": {
"get": {},
"insert": {},
"list": {},
"update": {}
}
},
"submissions": {
"methods": {
"get": {},
"insert": {},
"list": {}
}
},
"tags": {
"methods": {
"delete": {},
"insert": {},
"list": {}
}
},
"topics": {
"methods": {
"get": {},
"insert": {},
"list": {}
}
},
"votes": {
"methods": {
"get": {},
"insert": {},
"list": {},
"update": {}
}
}
}
}
}
}
}

View File

@@ -23,17 +23,21 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)'
import httplib2
import logging
import os
import re
import simplejson
import urlparse
import uritemplate
import urlparse
class HttpError(Exception):
pass
DISCOVERY_URI = 'http://www.googleapis.com/discovery/0.1/describe\
{?api,apiVersion}'
class UnknownLinkType(Exception):
pass
DISCOVERY_URI = ('http://www.googleapis.com/discovery/0.1/describe'
'{?api,apiVersion}')
def key2method(key):
@@ -107,10 +111,10 @@ class JsonModel(object):
raise HttpError(simplejson.loads(content)['error'])
def build(service, version, http=httplib2.Http(),
def build(serviceName, version, http=httplib2.Http(),
discoveryServiceUrl=DISCOVERY_URI, auth=None, model=JsonModel()):
params = {
'api': service,
'api': serviceName,
'apiVersion': version
}
@@ -118,7 +122,14 @@ def build(service, version, http=httplib2.Http(),
logging.info('URL being requested: %s' % requested_url)
resp, content = http.request(requested_url)
d = simplejson.loads(content)
service = d['data'][service][version]
service = d['data'][serviceName][version]
fn = os.path.join(os.path.dirname(__file__), "contrib", serviceName, "future.json")
f = file(fn, "r")
d = simplejson.load(f)
f.close()
future = d['data'][serviceName][version]['resources']
base = service['baseUrl']
resources = service['resources']
@@ -130,21 +141,21 @@ def build(service, version, http=httplib2.Http(),
self._baseUrl = base
self._model = model
def createMethod(theclass, methodName, methodDesc):
def createMethod(theclass, methodName, methodDesc, futureDesc):
def method(self, **kwargs):
return createResource(self._http, self._baseUrl, self._model,
methodName, methodDesc)
methodName, methodDesc, futureDesc)
setattr(method, '__doc__', 'A description of how to use this function')
setattr(theclass, methodName, method)
for methodName, methodDesc in resources.iteritems():
createMethod(Service, methodName, methodDesc)
createMethod(Service, methodName, methodDesc, future[methodName])
return Service()
def createResource(http, baseUrl, model, resourceName, resourceDesc):
def createResource(http, baseUrl, model, resourceName, resourceDesc, futureDesc):
class Resource(object):
"""A class for interacting with a resource."""
@@ -154,7 +165,7 @@ def createResource(http, baseUrl, model, resourceName, resourceDesc):
self._baseUrl = baseUrl
self._model = model
def createMethod(theclass, methodName, methodDesc):
def createMethod(theclass, methodName, methodDesc, futureDesc):
pathUrl = methodDesc['pathUrl']
pathUrl = re.sub(r'\{', r'{+', pathUrl)
httpMethod = methodDesc['httpMethod']
@@ -207,7 +218,7 @@ def createResource(http, baseUrl, model, resourceName, resourceDesc):
headers = {}
headers, params, query, body = self._model.request(headers, actual_path_params, actual_query_params, body_value)
expanded_url = uritemplate.expand(pathUrl, params)
expanded_url = uritemplate.expand(pathUrl, params)
url = urlparse.urljoin(self._baseUrl, expanded_url + query)
logging.info('URL being requested: %s' % url)
@@ -218,12 +229,53 @@ def createResource(http, baseUrl, model, resourceName, resourceDesc):
docs = ['A description of how to use this function\n\n']
for arg in argmap.iterkeys():
docs.append('%s - A parameter\n' % arg)
required = ""
if arg in required_params:
required = " (required)"
docs.append('%s - A parameter%s\n' % (arg, required))
setattr(method, '__doc__', ''.join(docs))
setattr(theclass, methodName, method)
def createNextMethod(theclass, methodName, methodDesc):
def method(self, previous):
"""
Takes a single argument, 'body', which is the results
from the last call, and returns the next set of items
in the collection.
Returns None if there are no more items in
the collection.
"""
if methodDesc['type'] != 'uri':
raise UnknownLinkType(methodDesc['type'])
try:
p = previous
for key in methodDesc['location']:
p = p[key]
url = p
except KeyError:
return None
headers = {}
headers, params, query, body = self._model.request(headers, {}, {}, None)
logging.info('URL being requested: %s' % url)
resp, content = self._http.request(url, method='GET', headers=headers)
return self._model.response(resp, content)
setattr(theclass, methodName, method)
# Add basic methods to Resource
for methodName, methodDesc in resourceDesc['methods'].iteritems():
createMethod(Resource, methodName, methodDesc)
createMethod(Resource, methodName, methodDesc, futureDesc['methods'].get(methodName, {}))
# Add <m>_next() methods to Resource
for methodName, methodDesc in futureDesc['methods'].iteritems():
if 'next' in methodDesc and methodName in resourceDesc['methods']:
createNextMethod(Resource, methodName + "_next", methodDesc['next'])
return Resource()

52
discovery_extras.py Normal file
View File

@@ -0,0 +1,52 @@
# Copyright (C) 2010 Google Inc.
#
# 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.
"""Generate a skeleton discovery extras document.
For the given API, retrieve the discovery document,
strip out the guts of each method description
and put :
"""
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
import os
import os.path
import simplejson
import sys
def main():
for filename in sys.argv[1:]:
f = file(filename, "r")
dis = simplejson.load(f)
f.close()
data = dis['data']
api = data[data.keys()[0]]
version = api[api.keys()[0]]
resources = version['resources']
for res_name, res_desc in resources.iteritems():
methods = res_desc['methods']
for method_name, method_desc in methods.iteritems():
methods[method_name] = {}
path, basename = os.path.split(filename)
newfilename = os.path.join(path, "skel-" + basename)
f = file(newfilename, "w")
simplejson.dump(dis, f, sort_keys=True, indent=2 * ' ')
f.close()
if __name__ == '__main__':
main()

View File

@@ -17,14 +17,14 @@ headers = {'user-agent': 'google-api-client-python-buzz-cmdline/1.0',
consumer_key = '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'
'?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'
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'
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)
client = oauth.Client(consumer)

View File

@@ -497,6 +497,26 @@
}
}
},
"relatedToUri": {
"pathUrl": "buzz/v1/people/{userId}/@related",
"rpcName": "buzz.people.relatedToUri",
"httpMethod": "POST",
"methodType": "rest",
"parameters": {
"alt": {
"parameterType": "query",
"required": false
},
"uri": {
"parameterType": "query",
"required": false
},
"hl": {
"parameterType": "query",
"required": false
}
}
},
"reshared": {
"pathUrl": "buzz/v1/activities/{userId}/{scope}/{postId}/{groupId}",
"rpcName": "buzz.people.reshared",
@@ -722,15 +742,16 @@
}
}
},
"insert": {
"pathUrl": "buzz/v1/people/{userId}/@groups",
"rpcName": "buzz.groups.insert",
"httpMethod": "POST",
"update": {
"pathUrl": "buzz/v1/people/{userId}/@groups/{groupId}/@self",
"rpcName": "buzz.groups.update",
"httpMethod": "PUT",
"methodType": "rest",
"parameters": {
"alt": {
"parameterType": "query",
"required": false
"groupId": {
"parameterType": "path",
"pattern": "[^/]+",
"required": true
},
"userId": {
"parameterType": "path",
@@ -743,16 +764,15 @@
}
}
},
"update": {
"pathUrl": "buzz/v1/people/{userId}/@groups/{groupId}/@self",
"rpcName": "buzz.groups.update",
"httpMethod": "PUT",
"insert": {
"pathUrl": "buzz/v1/people/{userId}/@groups",
"rpcName": "buzz.groups.insert",
"httpMethod": "POST",
"methodType": "rest",
"parameters": {
"groupId": {
"parameterType": "path",
"pattern": "[^/]+",
"required": true
"alt": {
"parameterType": "query",
"required": false
},
"userId": {
"parameterType": "path",

View File

@@ -1,6 +1,19 @@
#!/usr/bin/python2.4
#
# Copyright 2010 Google Inc. All Rights Reserved.
# Copyright 2010 Google Inc.
#
# 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.
"""Discovery document tests

View File

@@ -1,10 +1,22 @@
#!/usr/bin/python2.4
#
# Copyright 2010 Google Inc. All Rights Reserved.
# Copyright 2010 Google Inc.
#
# 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.
"""Discovery document tests
"""JSON Model tests
Unit tests for objects created from discovery documents.
Unit tests for the JSON model.
"""
__author__ = 'jcgregorio@google.com (Joe Gregorio)'