Add preliminary support for uploading media.

Reviewed in http://codereview.appspot.com/4515144/
This commit is contained in:
Joe Gregorio
2011-05-26 21:36:34 -04:00
parent e98c232b1c
commit 922b78c082
2 changed files with 64 additions and 12 deletions

View File

@@ -29,17 +29,21 @@ import re
import uritemplate
import urllib
import urlparse
import mimetypes
try:
from urlparse import parse_qsl
except ImportError:
from cgi import parse_qsl
from http import HttpRequest
from anyjson import simplejson
from model import JsonModel
from errors import UnknownLinkType
from email.mime.multipart import MIMEMultipart
from email.mime.nonmultipart import MIMENonMultipart
from errors import HttpError
from errors import InvalidJsonError
from errors import UnknownLinkType
from http import HttpRequest
from model import JsonModel
URITEMPLATE = re.compile('{[^}]*}')
VARNAME = re.compile('[a-zA-Z0-9_-]+')
@@ -52,6 +56,11 @@ STACK_QUERY_PARAMETERS = ['trace', 'fields', 'pp', 'prettyPrint', 'userIp',
'userip', 'strict']
def _write_headers(self):
# Utility no-op method for multipart media handling
pass
def key2param(key):
"""Converts key names into parameter names.
@@ -250,6 +259,11 @@ def createResource(http, baseUrl, model, requestBuilder,
'type': 'object',
'required': True,
}
methodDesc['parameters']['media_body'] = {
'description': 'The filename of the media request body.',
'type': 'string',
'required': False,
}
argmap = {} # Map from method parameter name to query parameter name
required_params = [] # Required parameters
@@ -310,6 +324,7 @@ def createResource(http, baseUrl, model, requestBuilder,
'Parameter "%s" value "%s" is not an allowed value in "%s"' %
(name, kwargs[name], str(enums)))
media_filename = kwargs.pop('media_body', None)
actual_query_params = {}
actual_path_params = {}
for key, value in kwargs.iteritems():
@@ -332,16 +347,49 @@ def createResource(http, baseUrl, model, requestBuilder,
headers, params, query, body = self._model.request(headers,
actual_path_params, actual_query_params, body_value)
# TODO(ade) This exists to fix a bug in V1 of the Buzz discovery
# document. Base URLs should not contain any path elements. If they do
# then urlparse.urljoin will strip them out This results in an incorrect
# URL which returns a 404
url_result = urlparse.urlsplit(self._baseUrl)
new_base_url = url_result[0] + '://' + url_result[1]
expanded_url = uritemplate.expand(pathUrl, params)
url = urlparse.urljoin(self._baseUrl,
url_result[2] + expanded_url + query)
url = urlparse.urljoin(self._baseUrl, expanded_url + query)
if media_filename:
(media_mime_type, encoding) = mimetypes.guess_type(media_filename)
if media_mime_type is None:
raise UnknownFileType(media_filename)
# modify the path to prepend '/upload'
parsed = list(urlparse.urlparse(url))
parsed[2] = '/upload' + parsed[2]
url = urlparse.urlunparse(parsed)
if body is None:
headers['content-type'] = media_mime_type
# make the body the contents of the file
f = file(media_filename, 'rb')
body = f.read()
f.close()
else:
msgRoot = MIMEMultipart('related')
# msgRoot should not write out it's own headers
setattr(msgRoot, '_write_headers', lambda self: None)
# attach the body as one part
msg = MIMENonMultipart(*headers['content-type'].split('/'))
msg.set_payload(body)
msgRoot.attach(msg)
# attach the media as the second part
msg = MIMENonMultipart(*media_mime_type.split('/'))
msg['Content-Transfer-Encoding'] = 'binary'
f = file(media_filename, 'rb')
msg.set_payload(f.read())
f.close()
msgRoot.attach(msg)
body = msgRoot.as_string()
# must appear after the call to as_string() to get the right boundary
headers['content-type'] = ('multipart/related; '
'boundary="%s"') % msgRoot.get_boundary()
logging.info('URL being requested: %s' % url)
return self._requestBuilder(self._http,
@@ -358,6 +406,8 @@ def createResource(http, baseUrl, model, requestBuilder,
for arg in argmap.iterkeys():
if arg in STACK_QUERY_PARAMETERS:
continue
if arg == 'media_body':
continue
repeated = ''
if arg in repeated_params:
repeated = ' (repeated)'

View File

@@ -98,10 +98,12 @@ class DecoratorTests(unittest.TestCase):
debug=True)
self.app = TestApp(application)
users.get_current_user = UserMock
self.httplib2_orig = httplib2.Http
httplib2.Http = Http2Mock
def tearDown(self):
self.testbed.deactivate()
httplib2.Http = self.httplib2_orig
def test_required(self):
# An initial request to an oauth_required decorated path should be a