New media upload scheme that uses only /upload and the new uploadType parameter.

Committed TBR review at http://codereview.appspot.com/5704049/.
This commit is contained in:
Joe Gregorio
2012-03-02 15:55:52 -05:00
parent 853bcf3b49
commit de86044310
2 changed files with 23 additions and 9 deletions

View File

@@ -85,7 +85,9 @@ def _write_headers(self):
def _add_query_parameter(url, name, value): 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: Args:
url: string, url to add the query parameter to. url: string, url to add the query parameter to.
@@ -99,8 +101,8 @@ def _add_query_parameter(url, name, value):
return url return url
else: else:
parsed = list(urlparse.urlparse(url)) parsed = list(urlparse.urlparse(url))
q = parse_qsl(parsed[4]) q = dict(parse_qsl(parsed[4]))
q.append((name, value)) q[name] = value
parsed[4] = urllib.urlencode(q) parsed[4] = urllib.urlencode(q)
return urlparse.urlunparse(parsed) return urlparse.urlunparse(parsed)
@@ -333,8 +335,10 @@ def createResource(http, baseUrl, model, requestBuilder,
maxSize = 0 maxSize = 0
if 'mediaUpload' in methodDesc: if 'mediaUpload' in methodDesc:
mediaUpload = methodDesc['mediaUpload'] mediaUpload = methodDesc['mediaUpload']
mediaPathUrl = mediaUpload['protocols']['simple']['path'] # TODO(jcgregorio) Use URLs from discovery once it is updated.
mediaResumablePathUrl = mediaUpload['protocols']['resumable']['path'] parsed = list(urlparse.urlparse(baseUrl))
basePath = parsed[2]
mediaPathUrl = '/upload' + basePath + pathUrl
accept = mediaUpload['accept'] accept = mediaUpload['accept']
maxSize = _media_size_to_long(mediaUpload.get('maxSize', '')) 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) raise MediaUploadSizeError("Media larger than: %s" % maxSize)
# Use the media path uri for media uploads # Use the media path uri for media uploads
if media_upload.resumable(): expanded_url = uritemplate.expand(mediaPathUrl, params)
expanded_url = uritemplate.expand(mediaResumablePathUrl, params)
else:
expanded_url = uritemplate.expand(mediaPathUrl, params)
url = urlparse.urljoin(self._baseUrl, expanded_url + query) url = urlparse.urljoin(self._baseUrl, expanded_url + query)
if media_upload.resumable():
url = _add_query_parameter(url, 'uploadType', 'resumable')
if media_upload.resumable(): if media_upload.resumable():
# This is all we need to do for resumable, if the body exists it gets # 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 # This is a simple media upload
headers['content-type'] = media_upload.mimetype() headers['content-type'] = media_upload.mimetype()
body = media_upload.getbytes(0, media_upload.size()) body = media_upload.getbytes(0, media_upload.size())
url = _add_query_parameter(url, 'uploadType', 'media')
else: else:
# This is a multipart/related upload. # This is a multipart/related upload.
msgRoot = MIMEMultipart('related') msgRoot = MIMEMultipart('related')
@@ -530,6 +534,7 @@ def createResource(http, baseUrl, model, requestBuilder,
multipart_boundary = msgRoot.get_boundary() multipart_boundary = msgRoot.get_boundary()
headers['content-type'] = ('multipart/related; ' headers['content-type'] = ('multipart/related; '
'boundary="%s"') % multipart_boundary 'boundary="%s"') % multipart_boundary
url = _add_query_parameter(url, 'uploadType', 'multipart')
logging.info('URL being requested: %s' % url) logging.info('URL being requested: %s' % url)
return self._requestBuilder(self._http, return self._requestBuilder(self._http,

View File

@@ -312,6 +312,9 @@ class Discovery(unittest.TestCase):
request = zoo.animals().insert(media_body=datafile('small.png')) request = zoo.animals().insert(media_body=datafile('small.png'))
self.assertEquals('image/png', request.headers['content-type']) self.assertEquals('image/png', request.headers['content-type'])
self.assertEquals('PNG', request.body[1:4]) 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): def test_multipart_media_raise_correct_exceptions(self):
self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
@@ -337,6 +340,9 @@ class Discovery(unittest.TestCase):
self.assertTrue(request.headers['content-type'].startswith( self.assertTrue(request.headers['content-type'].startswith(
'multipart/related')) 'multipart/related'))
self.assertEquals('--==', request.body[0:4]) 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): def test_media_capable_method_without_media(self):
self.http = HttpMock(datafile('zoo.json'), {'status': '200'}) self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
@@ -452,6 +458,9 @@ class Discovery(unittest.TestCase):
media_upload = MediaFileUpload(datafile('small.png'), resumable=True) media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
request = zoo.animals().insert(media_body=media_upload, body=None) 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([ http = HttpMockSequence([
({'status': '200', ({'status': '200',