diff --git a/.hgignore b/.hgignore index 6b95477..1c08454 100644 --- a/.hgignore +++ b/.hgignore @@ -5,3 +5,4 @@ syntax: glob */.git/* .gitignore samples/cmdline/*.dat +htmlcov/* diff --git a/apiclient/discovery.py b/apiclient/discovery.py index cbba1dc..9eec549 100644 --- a/apiclient/discovery.py +++ b/apiclient/discovery.py @@ -102,7 +102,7 @@ class JsonModel(object): return simplejson.loads(content)['data'] else: logging.debug('Content from bad request was: %s' % content) - if resp['content-type'] != 'application/json': + if resp.get('content-type', '') != 'application/json': raise HttpError('%d %s' % (resp.status, resp.reason)) else: raise HttpError(simplejson.loads(content)['error']) @@ -263,7 +263,7 @@ def createResource(http, baseUrl, model, resourceName, resourceDesc, for key in methodDesc['location']: p = p[key] url = p - except KeyError: + except (KeyError, TypeError): return None headers = {} diff --git a/apiclient/http.py b/apiclient/http.py new file mode 100644 index 0000000..16591fb --- /dev/null +++ b/apiclient/http.py @@ -0,0 +1,34 @@ +# Copyright 2010 Google Inc. All Rights Reserved. + +"""One-line documentation for http module. + +A detailed description of http. +""" + +__author__ = 'jcgregorio@google.com (Joe Gregorio)' + + +class HttpRequest(object): + """Encapsulate an HTTP request. + """ + + def __init__(self, http, uri, method="GET", body=None, headers=None, postproc=None): + self.uri = uri + self.method = method + self.body = body + self.headers = headers or {} + self.http = http + self.postproc = postproc + + def execute(self, http=None): + """Execute the request. + + If an http object is passed in it is used instead of the + httplib2.Http object that the request was constructed with. + """ + if http is None: + http = self.http + resp, content = http.request(self.uri, self.method, + body=self.body, + headers=self.headers) + return self.postproc(resp, content) diff --git a/functional_tests/test_services.py b/functional_tests/test_services.py index 5a9fa23..e82ec24 100644 --- a/functional_tests/test_services.py +++ b/functional_tests/test_services.py @@ -24,8 +24,12 @@ class BuzzFunctionalTest(unittest.TestCase): def test_can_get_buzz_activities_with_many_params(self): buzz = build('buzz', 'v1') max_results = 2 - activities = buzz.activities().list(userId='googlebuzz', scope='@self', - max_comments=max_results*2 ,max_liked=max_results*3, - max_results=max_results).execute()['items'] + actcol = buzz.activities() + activities = actcol.list(userId='googlebuzz', scope='@self', + max_comments=max_results*2 ,max_liked=max_results*3, + max_results=max_results).execute()['items'] activity_count = len(activities) self.assertEquals(max_results, activity_count) + + activities = actcol.list_next(activities) + self.assertEquals(activities, None) # Public streams don't have next links diff --git a/samples/cmdline/buzz.py b/samples/cmdline/buzz.py index 60c7d50..b692be5 100644 --- a/samples/cmdline/buzz.py +++ b/samples/cmdline/buzz.py @@ -28,8 +28,11 @@ def main(): p = build("buzz", "v1", http=http) activities = p.activities() - activitylist = activities.list(scope='@self', userId='@me').execute() + activitylist = activities.list(max_results='2', scope='@self', userId='@me').execute() print activitylist['items'][0]['title'] + activitylist = activities.list_next(activitylist).execute() + print activitylist['items'][0]['title'] + activities.insert(userId='@me', body={ 'title': 'Testing insert', 'object': { diff --git a/tests/test_discovery.py b/tests/test_discovery.py index 539796f..c44f1e4 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -22,7 +22,7 @@ Unit tests for objects created from discovery documents. __author__ = 'jcgregorio@google.com (Joe Gregorio)' -from apiclient.discovery import build +from apiclient.discovery import build, key2param import httplib2 import os import unittest @@ -41,6 +41,12 @@ class HttpMock(object): return httplib2.Response(self.headers), self.data +class Utilities(unittest.TestCase): + def test_key2param(self): + self.assertEqual('max_results', key2param('max-results')) + self.assertEqual('x007_bond', key2param('007-bond')) + + class Discovery(unittest.TestCase): def test_method_error_checking(self): self.http = HttpMock('buzz.json', {'status': '200'}) @@ -86,6 +92,23 @@ class Discovery(unittest.TestCase): self.assertTrue(getattr(buzz, 'comments')) self.assertTrue(getattr(buzz, 'related')) + def test_auth(self): + self.http = HttpMock('buzz.json', {'status': '200'}) + buzz = build('buzz', 'v1', self.http) + auth = buzz.auth_discovery() + self.assertTrue('request' in auth) + + +class Next(unittest.TestCase): + def test_next(self): + self.http = HttpMock('buzz.json', {'status': '200'}) + buzz = build('buzz', 'v1', self.http) + activities = {'links': + {'next': + [{'href': 'http://www.googleapis.com/next-link'}]}} + request = buzz.activities().list_next(activities) + self.assertEqual(request.uri, 'http://www.googleapis.com/next-link') + if __name__ == '__main__': unittest.main() diff --git a/tests/test_json_model.py b/tests/test_json_model.py index 8049568..79b63ab 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -21,9 +21,10 @@ Unit tests for the JSON model. __author__ = 'jcgregorio@google.com (Joe Gregorio)' -from apiclient.discovery import JsonModel +from apiclient.discovery import JsonModel, HttpError import os import unittest +import httplib2 # Python 2.5 requires different modules try: @@ -82,6 +83,47 @@ class Model(unittest.TestCase): self.assertEqual(query_dict['bar'], [u'\N{COMET}'.encode('utf-8')]) self.assertEqual(body, '{"data": {}}') + def test_user_agent(self): + model = JsonModel() + + headers = {'user-agent': 'my-test-app/1.23.4'} + path_params = {} + query_params = {} + body = {} + + headers, params, query, body = model.request(headers, path_params, query_params, body) + + self.assertEqual(headers['user-agent'], 'my-test-app/1.23.4 google-api-python-client/1.0') + + def test_bad_response(self): + model = JsonModel() + resp = httplib2.Response({'status': '401'}) + resp.reason = 'Unauthorized' + content = '{"error": "not authorized"}' + + try: + content = model.response(resp, content) + self.fail('Should have thrown an exception') + except HttpError, e: + self.assertTrue('Unauthorized' in str(e)) + + resp['content-type'] = 'application/json' + + try: + content = model.response(resp, content) + self.fail('Should have thrown an exception') + except HttpError, e: + self.assertTrue('not authorized' in str(e)) + + + def test_good_response(self): + model = JsonModel() + resp = httplib2.Response({'status': '200'}) + resp.reason = 'OK' + content = '{"data": "is good"}' + + content = model.response(resp, content) + self.assertEqual(content, 'is good') if __name__ == '__main__': unittest.main()