Added support for URLs with many parameters e.g. max-liked, max-results and max-comments. I'm not checking in the functional tests I'm using since I still haven't fixed the problem of getting back a usable list of JSON objects for pagination.
This commit is contained in:
@@ -22,6 +22,7 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)'
|
|||||||
|
|
||||||
|
|
||||||
import httplib2
|
import httplib2
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
import simplejson
|
import simplejson
|
||||||
import urlparse
|
import urlparse
|
||||||
@@ -77,17 +78,21 @@ def key2param(key):
|
|||||||
|
|
||||||
class JsonModel(object):
|
class JsonModel(object):
|
||||||
|
|
||||||
def request(self, headers, params):
|
def request(self, headers, path_params, query_params, body_value):
|
||||||
model = params.get('body', None)
|
query = self.build_query(query_params)
|
||||||
query = '?alt=json&prettyprint=true'
|
|
||||||
headers['accept'] = 'application/json'
|
headers['accept'] = 'application/json'
|
||||||
if model == None:
|
if body_value is None:
|
||||||
return (headers, params, query, None)
|
return (headers, path_params, query, None)
|
||||||
else:
|
else:
|
||||||
model = {'data': model}
|
model = {'data': body_value}
|
||||||
headers['content-type'] = 'application/json'
|
headers['content-type'] = 'application/json'
|
||||||
del params['body']
|
return (headers, path_params, query, simplejson.dumps(model))
|
||||||
return (headers, params, query, simplejson.dumps(model))
|
|
||||||
|
def build_query(self, params):
|
||||||
|
query = '?alt=json&prettyprint=true'
|
||||||
|
for key,value in params.iteritems():
|
||||||
|
query += '&%s=%s' % (key, value)
|
||||||
|
return query
|
||||||
|
|
||||||
def response(self, resp, content):
|
def response(self, resp, content):
|
||||||
# Error handling is TBD, for example, do we retry
|
# Error handling is TBD, for example, do we retry
|
||||||
@@ -95,6 +100,7 @@ class JsonModel(object):
|
|||||||
if resp.status < 300:
|
if resp.status < 300:
|
||||||
return simplejson.loads(content)['data']
|
return simplejson.loads(content)['data']
|
||||||
else:
|
else:
|
||||||
|
logging.debug('Content from bad request was: %s' % content)
|
||||||
if resp['content-type'] != 'application/json':
|
if resp['content-type'] != 'application/json':
|
||||||
raise HttpError('%d %s' % (resp.status, resp.reason))
|
raise HttpError('%d %s' % (resp.status, resp.reason))
|
||||||
else:
|
else:
|
||||||
@@ -107,7 +113,10 @@ def build(service, version, http=httplib2.Http(),
|
|||||||
'api': service,
|
'api': service,
|
||||||
'apiVersion': version
|
'apiVersion': version
|
||||||
}
|
}
|
||||||
resp, content = http.request(uritemplate.expand(discoveryServiceUrl, params))
|
|
||||||
|
requested_url = uritemplate.expand(discoveryServiceUrl, params)
|
||||||
|
logging.info('URL being requested: %s' % requested_url)
|
||||||
|
resp, content = http.request(requested_url)
|
||||||
d = simplejson.loads(content)
|
d = simplejson.loads(content)
|
||||||
service = d['data'][service][version]
|
service = d['data'][service][version]
|
||||||
base = service['baseUrl']
|
base = service['baseUrl']
|
||||||
@@ -149,45 +158,59 @@ def createResource(http, baseUrl, model, resourceName, resourceDesc):
|
|||||||
pathUrl = methodDesc['pathUrl']
|
pathUrl = methodDesc['pathUrl']
|
||||||
pathUrl = re.sub(r'\{', r'{+', pathUrl)
|
pathUrl = re.sub(r'\{', r'{+', pathUrl)
|
||||||
httpMethod = methodDesc['httpMethod']
|
httpMethod = methodDesc['httpMethod']
|
||||||
args = methodDesc['parameters'].keys()
|
|
||||||
|
|
||||||
required = [] # Required parameters
|
argmap = {}
|
||||||
pattern = {} # Parameters the must match a regex
|
if httpMethod in ['PUT', 'POST']:
|
||||||
|
argmap['body'] = 'body'
|
||||||
|
|
||||||
|
|
||||||
|
required_params = [] # Required parameters
|
||||||
|
pattern_params = {} # Parameters that must match a regex
|
||||||
|
query_params = [] # Parameters that will be used in the query string
|
||||||
|
path_params = {} # Parameters that will be used in the base URL
|
||||||
for arg, desc in methodDesc['parameters'].iteritems():
|
for arg, desc in methodDesc['parameters'].iteritems():
|
||||||
param = key2param(arg)
|
param = key2param(arg)
|
||||||
if desc.get('pattern', ''):
|
argmap[param] = arg
|
||||||
pattern[param] = desc['pattern']
|
|
||||||
if desc.get('required', False):
|
|
||||||
required.append(param)
|
|
||||||
|
|
||||||
if httpMethod in ['PUT', 'POST']:
|
if desc.get('pattern', ''):
|
||||||
args.append('body')
|
pattern_params[param] = desc['pattern']
|
||||||
argmap = dict([(key2param(key), key) for key in args])
|
if desc.get('required', False):
|
||||||
|
required_params.append(param)
|
||||||
|
if desc.get('parameterType') == 'query':
|
||||||
|
query_params.append(param)
|
||||||
|
if desc.get('parameterType') == 'path':
|
||||||
|
path_params[param]=param
|
||||||
|
|
||||||
def method(self, **kwargs):
|
def method(self, **kwargs):
|
||||||
for name in kwargs.iterkeys():
|
for name in kwargs.iterkeys():
|
||||||
if name not in argmap:
|
if name not in argmap:
|
||||||
raise TypeError('Got an unexpected keyword argument "%s"' % name)
|
raise TypeError('Got an unexpected keyword argument "%s"' % name)
|
||||||
|
|
||||||
for name in required:
|
for name in required_params:
|
||||||
if name not in kwargs:
|
if name not in kwargs:
|
||||||
raise TypeError('Missing required parameter "%s"' % name)
|
raise TypeError('Missing required parameter "%s"' % name)
|
||||||
|
|
||||||
for name, regex in pattern.iteritems():
|
for name, regex in pattern_params.iteritems():
|
||||||
if name in kwargs:
|
if name in kwargs:
|
||||||
if re.match(regex, kwargs[name]) is None:
|
if re.match(regex, kwargs[name]) is None:
|
||||||
raise TypeError('Parameter "%s" value "%s" does not match the pattern "%s"' % (name, kwargs[name], regex))
|
raise TypeError('Parameter "%s" value "%s" does not match the pattern "%s"' % (name, kwargs[name], regex))
|
||||||
|
|
||||||
params = {}
|
actual_query_params = {}
|
||||||
|
actual_path_params = {}
|
||||||
for key, value in kwargs.iteritems():
|
for key, value in kwargs.iteritems():
|
||||||
params[argmap[key]] = value
|
if key in query_params:
|
||||||
|
actual_query_params[argmap[key]] = value
|
||||||
|
if key in path_params:
|
||||||
|
actual_path_params[argmap[key]] = value
|
||||||
|
body_value = kwargs.get('body', None)
|
||||||
|
|
||||||
headers = {}
|
headers = {}
|
||||||
headers, params, query, body = self._model.request(headers, params)
|
headers, params, query, body = self._model.request(headers, actual_path_params, actual_query_params, body_value)
|
||||||
|
|
||||||
url = urlparse.urljoin(self._baseUrl,
|
expanded_url = uritemplate.expand(pathUrl, params)
|
||||||
uritemplate.expand(pathUrl, params) + query)
|
url = urlparse.urljoin(self._baseUrl, expanded_url + query)
|
||||||
|
|
||||||
|
logging.info('URL being requested: %s' % url)
|
||||||
resp, content = self._http.request(
|
resp, content = self._http.request(
|
||||||
url, method=httpMethod, headers=headers, body=body)
|
url, method=httpMethod, headers=headers, body=body)
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class Discovery(unittest.TestCase):
|
|||||||
except TypeError, e:
|
except TypeError, e:
|
||||||
self.assertTrue('unexpected' in str(e))
|
self.assertTrue('unexpected' in str(e))
|
||||||
|
|
||||||
def test_resources(self):
|
def test_buzz_resources(self):
|
||||||
self.http = HttpMock('buzz.json', {'status': '200'})
|
self.http = HttpMock('buzz.json', {'status': '200'})
|
||||||
buzz = build('buzz', 'v1', self.http)
|
buzz = build('buzz', 'v1', self.http)
|
||||||
self.assertTrue(getattr(buzz, 'activities'))
|
self.assertTrue(getattr(buzz, 'activities'))
|
||||||
|
|||||||
@@ -19,9 +19,11 @@ class Model(unittest.TestCase):
|
|||||||
model = JsonModel()
|
model = JsonModel()
|
||||||
|
|
||||||
headers = {}
|
headers = {}
|
||||||
params = {}
|
path_params = {}
|
||||||
|
query_params = {}
|
||||||
|
body = None
|
||||||
|
|
||||||
headers, params, query, body = model.request(headers, params)
|
headers, params, query, body = model.request(headers, path_params, query_params, body)
|
||||||
|
|
||||||
self.assertEqual(headers['accept'], 'application/json')
|
self.assertEqual(headers['accept'], 'application/json')
|
||||||
self.assertTrue('content-type' not in headers)
|
self.assertTrue('content-type' not in headers)
|
||||||
@@ -32,9 +34,11 @@ class Model(unittest.TestCase):
|
|||||||
model = JsonModel()
|
model = JsonModel()
|
||||||
|
|
||||||
headers = {}
|
headers = {}
|
||||||
params = {'body': {}}
|
path_params = {}
|
||||||
|
query_params = {}
|
||||||
|
body = {}
|
||||||
|
|
||||||
headers, params, query, body = model.request(headers, params)
|
headers, params, query, body = model.request(headers, path_params, query_params, body)
|
||||||
|
|
||||||
self.assertEqual(headers['accept'], 'application/json')
|
self.assertEqual(headers['accept'], 'application/json')
|
||||||
self.assertEqual(headers['content-type'], 'application/json')
|
self.assertEqual(headers['content-type'], 'application/json')
|
||||||
|
|||||||
Reference in New Issue
Block a user