From b3394f4b52e1e85622c2160d4224d2095a10e8fe Mon Sep 17 00:00:00 2001 From: Mike Fedosin Date: Thu, 22 Sep 2016 22:05:38 +0300 Subject: [PATCH] Allow to set content-type for uploading blobs Change-Id: I97446773544c05cb700385ab60ca7bd5f0a611b5 --- glareclient/common/http.py | 22 +++++++++----- glareclient/osc/v1/blobs.py | 10 +++++-- glareclient/tests/unit/osc/v1/test_blob.py | 32 +++++++++++++++++++++ glareclient/tests/unit/v1/test_artifacts.py | 17 +++++++++++ glareclient/tests/utils.py | 3 +- glareclient/v1/artifacts.py | 8 ++++-- 6 files changed, 78 insertions(+), 14 deletions(-) diff --git a/glareclient/common/http.py b/glareclient/common/http.py index 8cc1458..f86fa1a 100644 --- a/glareclient/common/http.py +++ b/glareclient/common/http.py @@ -62,21 +62,29 @@ def _chunk_body(body): def _set_request_params(kwargs_params): + """Handle the common parameters used to send the request.""" + data = kwargs_params.pop('data', None) params = copy.deepcopy(kwargs_params) headers = params.get('headers', {}) - content_type = headers.get('Content-Type', 'application/json') + content_type = headers.get('Content-Type') + stream = params.get("stream", False) - if data is not None and not isinstance(data, six.string_types): - if content_type.startswith('application/json'): - data = jsonutils.dumps(data) - if content_type == 'application/octet-stream': + if stream: + if data is not None: data = _chunk_body(data) + content_type = content_type or 'application/octet-stream' + elif data is not None and not isinstance(data, six.string_types): + try: + data = jsonutils.dumps(data) + except TypeError: + raise exc.HTTPBadRequest("json is malformed.") params['data'] = data - headers.update({'Content-Type': content_type}) + headers.update( + {'Content-Type': content_type or 'application/json'}) params['headers'] = headers - params['stream'] = content_type == 'application/octet-stream' + params['stream'] = stream return params diff --git a/glareclient/osc/v1/blobs.py b/glareclient/osc/v1/blobs.py index aa8fe3c..aeab68a 100644 --- a/glareclient/osc/v1/blobs.py +++ b/glareclient/osc/v1/blobs.py @@ -62,6 +62,12 @@ class UploadBlob(command.ShowOne): metavar='', help='Name of the blob field.' ) + parser.add_argument( + '--content-type', + metavar='', + default='application/octet-stream', + help='Content-type of the blob.' + ) parser.add_argument( '--progress', help='Show download progress bar.' @@ -83,8 +89,8 @@ class UploadBlob(command.ShowOne): blob = progressbar.VerboseFileWrapper(blob, file_size) client.artifacts.upload_blob(parsed_args.id, - parsed_args.blob_property, - blob, + parsed_args.blob_property, blob, + content_type=parsed_args.content_type, type_name=parsed_args.type_name) data = client.artifacts.get(parsed_args.id, diff --git a/glareclient/tests/unit/osc/v1/test_blob.py b/glareclient/tests/unit/osc/v1/test_blob.py index 90ead9c..a126f2a 100644 --- a/glareclient/tests/unit/osc/v1/test_blob.py +++ b/glareclient/tests/unit/osc/v1/test_blob.py @@ -135,6 +135,38 @@ class TestUploadBlob(TestBlobs): with testtools.ExpectedException(SystemExit): self.cmd.take_action(parsed_args) + def test_upload_with_custom_content_type(self): + exp_data = ('template', 'application/x-yaml', False, + '35d83e8eedfbdb87ff97d1f2761f8ebf', + '942854360eeec1335537702399c5aed940401602', + 'd8a7834fc6652f316322d80196f6dcf2' + '94417030e37c15412e4deb7a67a367dd', + 594, 'active', 'fake_url') + + mocked_get = { + "status": "active", + "url": "fake_url", + "md5": "35d83e8eedfbdb87ff97d1f2761f8ebf", + "sha1": "942854360eeec1335537702399c5aed940401602", + "sha256": "d8a7834fc6652f316322d80196f6dcf2" + "94417030e37c15412e4deb7a67a367dd", + "external": False, + "content_type": "application/x-yaml", + "size": 594} + self.app.client_manager.artifact.artifacts.get = \ + lambda id, type_name: {'template': mocked_get} + + arglist = ['tosca_templates', + 'fc15c365-d4f9-4b8b-a090-d9e230f1f6ba', + '--file', '/path/to/file', + '--content-type', 'application/x-yaml'] + verify = [('type_name', 'tosca_templates')] + + parsed_args = self.check_parser(self.cmd, arglist, verify) + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.COLUMNS, columns) + self.assertEqual(exp_data, data) + def test_upload_blob_with_blob_prop(self): exp_data = ('blob', 'application/octet-stream', False, '35d83e8eedfbdb87ff97d1f2761f8ebf', diff --git a/glareclient/tests/unit/v1/test_artifacts.py b/glareclient/tests/unit/v1/test_artifacts.py index 1e5ffcd..6b2a8d4 100644 --- a/glareclient/tests/unit/v1/test_artifacts.py +++ b/glareclient/tests/unit/v1/test_artifacts.py @@ -277,6 +277,23 @@ class TestController(testtools.TestCase): 'data')] self.assertEqual(expect, self.api.calls) + def test_upload_blob_custom_content_type(self): + art_id = '3a4560a1-e585-443e-9b39-553b46ec92a3' + self.controller.upload_blob(artifact_id=art_id, + type_name='sample_artifact', + blob_property='blob', + data='{"a":"b"}', + content_type='application/json',) + + exp_headers = { + 'Content-Type': 'application/json' + } + + expect = [('PUT', '/artifacts/sample_artifact/%s/blob' % art_id, + exp_headers, + {"a": "b"})] + self.assertEqual(expect, self.api.calls) + def test_download_blob(self): art_id = '3a4560a1-e585-443e-9b39-553b46ec92a3' self.controller.download_blob(artifact_id=art_id, diff --git a/glareclient/tests/utils.py b/glareclient/tests/utils.py index 0d090cf..a7335ed 100644 --- a/glareclient/tests/utils.py +++ b/glareclient/tests/utils.py @@ -26,7 +26,7 @@ class FakeAPI(object): self.calls = [] def _request(self, method, url, headers=None, data=None, - content_length=None): + content_length=None, **kwargs): call = build_call_record(method, sort_url_by_query_keys(url), headers or {}, data) if content_length is not None: @@ -210,5 +210,4 @@ def build_call_record(method, url, headers, data): data = json.loads(data) except ValueError: return (method, url, headers or {}, data) - data = [sorted(d.items()) for d in data] return (method, url, headers or {}, data) diff --git a/glareclient/v1/artifacts.py b/glareclient/v1/artifacts.py index e3ba88a..e04b6de 100644 --- a/glareclient/v1/artifacts.py +++ b/glareclient/v1/artifacts.py @@ -194,16 +194,18 @@ class Controller(object): url = '/artifacts/%s/%s' % (type_name, artifact_id) self.http_client.delete(url) - def upload_blob(self, artifact_id, blob_property, data, type_name=None): + def upload_blob(self, artifact_id, blob_property, data, type_name=None, + content_type=None): """Upload blob data. :param artifact_id: ID of the artifact to download a blob :param blob_property: blob property name """ + content_type = content_type or 'application/octet-stream' type_name = self._check_type_name(type_name) - hdrs = {'Content-Type': 'application/octet-stream'} + hdrs = {'Content-Type': content_type} url = '/artifacts/%s/%s/%s' % (type_name, artifact_id, blob_property) - self.http_client.put(url, headers=hdrs, data=data) + self.http_client.put(url, headers=hdrs, data=data, stream=True) def download_blob(self, artifact_id, blob_property, type_name=None, do_checksum=True):