Add support for _next pagination with page tokens.
Reviewed in: http://codereview.appspot.com/4801047/
diff --git a/apiclient/discovery.py b/apiclient/discovery.py
index 2965f3c..8e14889 100644
--- a/apiclient/discovery.py
+++ b/apiclient/discovery.py
@@ -22,6 +22,7 @@
'build', 'build_from_document'
]
+import copy
import httplib2
import logging
import os
@@ -193,12 +194,13 @@
else:
future = {}
auth_discovery = {}
+ schema = service.get('schemas', {})
if model is None:
features = service.get('features', [])
model = JsonModel('dataWrapper' in features)
resource = createResource(http, base, model, requestBuilder, developerKey,
- service, future)
+ service, future, schema)
def auth_method():
"""Discovery information about the authentication the API uses."""
@@ -257,7 +259,7 @@
def createResource(http, baseUrl, model, requestBuilder,
- developerKey, resourceDesc, futureDesc):
+ developerKey, resourceDesc, futureDesc, schema):
class Resource(object):
"""A class for interacting with a resource."""
@@ -473,7 +475,10 @@
setattr(method, '__doc__', ''.join(docs))
setattr(theclass, methodName, method)
- def createNextMethod(theclass, methodName, methodDesc, futureDesc):
+ # This is a legacy method, as only Buzz and Moderator use the future.json
+ # functionality for generating _next methods. It will be kept around as long
+ # as those API versions are around, but no new APIs should depend upon it.
+ def createNextMethodFromFuture(theclass, methodName, methodDesc, futureDesc):
methodName = _fix_method_name(methodName)
methodId = methodDesc['id'] + '.next'
@@ -519,6 +524,49 @@
setattr(theclass, methodName, methodNext)
+
+ def createNextMethod(theclass, methodName, methodDesc, futureDesc):
+ methodName = _fix_method_name(methodName)
+ methodId = methodDesc['id'] + '.next'
+
+ def methodNext(self, previous_request, previous_response):
+ """Retrieves the next page of results.
+
+ Args:
+ previous_request: The request for the previous page.
+ previous_response: The response from the request for the previous page.
+
+ Returns:
+ A request object that you can call 'execute()' on to request the next
+ page. Returns None if there are no more items in the collection.
+ """
+ # Retrieve nextPageToken from previous_response
+ # Use as pageToken in previous_request to create new request.
+
+ if 'nextPageToken' not in previous_response:
+ return None
+
+ request = copy.copy(previous_request)
+
+ pageToken = previous_response['nextPageToken']
+ parsed = list(urlparse.urlparse(request.uri))
+ q = parse_qsl(parsed[4])
+
+ # Find and remove old 'pageToken' value from URI
+ newq = [(key, value) for (key, value) in q if key != 'pageToken']
+ newq.append(('pageToken', pageToken))
+ parsed[4] = urllib.urlencode(newq)
+ uri = urlparse.urlunparse(parsed)
+
+ request.uri = uri
+
+ logging.info('URL being requested: %s' % uri)
+
+ return request
+
+ setattr(theclass, methodName, methodNext)
+
+
# Add basic methods to Resource
if 'methods' in resourceDesc:
for methodName, methodDesc in resourceDesc['methods'].iteritems():
@@ -537,7 +585,7 @@
def methodResource(self):
return createResource(self._http, self._baseUrl, self._model,
self._requestBuilder, self._developerKey,
- methodDesc, futureDesc)
+ methodDesc, futureDesc, schema)
setattr(methodResource, '__doc__', 'A collection resource.')
setattr(methodResource, '__is_resource__', True)
@@ -554,8 +602,23 @@
if futureDesc and 'methods' in futureDesc:
for methodName, methodDesc in futureDesc['methods'].iteritems():
if 'next' in methodDesc and methodName in resourceDesc['methods']:
- createNextMethod(Resource, methodName + '_next',
+ createNextMethodFromFuture(Resource, methodName + '_next',
resourceDesc['methods'][methodName],
methodDesc['next'])
+ # Add _next() methods
+ # Look for response bodies in schema that contain nextPageToken, and methods
+ # that take a pageToken parameter.
+ if 'methods' in resourceDesc:
+ for methodName, methodDesc in resourceDesc['methods'].iteritems():
+ if 'response' in methodDesc:
+ responseSchema = methodDesc['response']
+ if '$ref' in responseSchema:
+ responseSchema = schema[responseSchema['$ref']]
+ hasNextPageToken = 'nextPageToken' in responseSchema['properties']
+ hasPageToken = 'pageToken' in methodDesc.get('parameters', {})
+ if hasNextPageToken and hasPageToken:
+ createNextMethod(Resource, methodName + '_next',
+ resourceDesc['methods'][methodName],
+ methodName)
return Resource()