From de86044310a63bf7d6b3ad1127f3f9a568b5277a Mon Sep 17 00:00:00 2001 From: Joe Gregorio Date: Fri, 2 Mar 2012 15:55:52 -0500 Subject: [PATCH] New media upload scheme that uses only /upload and the new uploadType parameter. Committed TBR review at http://codereview.appspot.com/5704049/. --- apiclient/discovery.py | 23 ++++++++++++++--------- tests/test_discovery.py | 9 +++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/apiclient/discovery.py b/apiclient/discovery.py index 02ab99e..0c374ae 100644 --- a/apiclient/discovery.py +++ b/apiclient/discovery.py @@ -85,7 +85,9 @@ def _write_headers(self): def _add_query_parameter(url, name, value): - """Adds a query parameter to a url + """Adds a query parameter to a url. + + Replaces the current value if it already exists in the URL. Args: url: string, url to add the query parameter to. @@ -99,8 +101,8 @@ def _add_query_parameter(url, name, value): return url else: parsed = list(urlparse.urlparse(url)) - q = parse_qsl(parsed[4]) - q.append((name, value)) + q = dict(parse_qsl(parsed[4])) + q[name] = value parsed[4] = urllib.urlencode(q) return urlparse.urlunparse(parsed) @@ -333,8 +335,10 @@ def createResource(http, baseUrl, model, requestBuilder, maxSize = 0 if 'mediaUpload' in methodDesc: mediaUpload = methodDesc['mediaUpload'] - mediaPathUrl = mediaUpload['protocols']['simple']['path'] - mediaResumablePathUrl = mediaUpload['protocols']['resumable']['path'] + # TODO(jcgregorio) Use URLs from discovery once it is updated. + parsed = list(urlparse.urlparse(baseUrl)) + basePath = parsed[2] + mediaPathUrl = '/upload' + basePath + pathUrl accept = mediaUpload['accept'] maxSize = _media_size_to_long(mediaUpload.get('maxSize', '')) @@ -491,11 +495,10 @@ def createResource(http, baseUrl, model, requestBuilder, raise MediaUploadSizeError("Media larger than: %s" % maxSize) # Use the media path uri for media uploads - if media_upload.resumable(): - expanded_url = uritemplate.expand(mediaResumablePathUrl, params) - else: - expanded_url = uritemplate.expand(mediaPathUrl, params) + expanded_url = uritemplate.expand(mediaPathUrl, params) url = urlparse.urljoin(self._baseUrl, expanded_url + query) + if media_upload.resumable(): + url = _add_query_parameter(url, 'uploadType', 'resumable') if media_upload.resumable(): # This is all we need to do for resumable, if the body exists it gets @@ -507,6 +510,7 @@ def createResource(http, baseUrl, model, requestBuilder, # This is a simple media upload headers['content-type'] = media_upload.mimetype() body = media_upload.getbytes(0, media_upload.size()) + url = _add_query_parameter(url, 'uploadType', 'media') else: # This is a multipart/related upload. msgRoot = MIMEMultipart('related') @@ -530,6 +534,7 @@ def createResource(http, baseUrl, model, requestBuilder, multipart_boundary = msgRoot.get_boundary() headers['content-type'] = ('multipart/related; ' 'boundary="%s"') % multipart_boundary + url = _add_query_parameter(url, 'uploadType', 'multipart') logging.info('URL being requested: %s' % url) return self._requestBuilder(self._http, diff --git a/tests/test_discovery.py b/tests/test_discovery.py index 36d9c8a..7003876 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py @@ -312,6 +312,9 @@ class Discovery(unittest.TestCase): request = zoo.animals().insert(media_body=datafile('small.png')) self.assertEquals('image/png', request.headers['content-type']) self.assertEquals('PNG', request.body[1:4]) + self.assertEqual( + 'https://www.googleapis.com/upload/zoo/animals?uploadType=media&alt=json', + request.uri) def test_multipart_media_raise_correct_exceptions(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) @@ -337,6 +340,9 @@ class Discovery(unittest.TestCase): self.assertTrue(request.headers['content-type'].startswith( 'multipart/related')) self.assertEquals('--==', request.body[0:4]) + self.assertEqual( + 'https://www.googleapis.com/upload/zoo/animals?uploadType=multipart&alt=json', + request.uri) def test_media_capable_method_without_media(self): self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) @@ -452,6 +458,9 @@ class Discovery(unittest.TestCase): media_upload = MediaFileUpload(datafile('small.png'), resumable=True) request = zoo.animals().insert(media_body=media_upload, body=None) + self.assertEqual( + 'https://www.googleapis.com/upload/zoo/animals?uploadType=resumable&alt=json', + request.uri) http = HttpMockSequence([ ({'status': '200',