summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Welleck <awellec@us.ibm.com>2016-02-23 15:02:03 -0600
committerAndrew Welleck <awellec@us.ibm.com>2016-06-09 13:51:26 -0500
commit439330cb9caa88d455ee0770d32ad7bc77db0b2a (patch)
tree97f36b30f6724b50e61bef1df4f38eceb4fbade2
parent3a6c14981dbd26e12b7d77ebfbb501607557b97c (diff)
downloadpython-swiftclient-439330cb9caa88d455ee0770d32ad7bc77db0b2a.tar.gz
Query string functionality for containers
Added functionality for arbitrary query strings to be passed into container functions. Additionally a minor typo correction in the README. Added unit tests for query string functionality. Closes-Bug: #1542459 Change-Id: Ica2cb3ea439632588388e748d8d2e944e9ed4fa4
-rw-r--r--README.rst2
-rw-r--r--swiftclient/client.py34
-rw-r--r--tests/unit/test_swiftclient.py31
3 files changed, 57 insertions, 10 deletions
diff --git a/README.rst b/README.rst
index 3677406..cd5efc3 100644
--- a/README.rst
+++ b/README.rst
@@ -17,7 +17,7 @@ in the `OpenStack wiki`__.
__ http://docs.openstack.org/infra/manual/developers.html
-This code is based on original the client previously included with
+This code is based on the original client previously included with
`OpenStack's Swift`__ The python-swiftclient is licensed under the
Apache License like the rest of OpenStack.
diff --git a/swiftclient/client.py b/swiftclient/client.py
index 807cb3d..f23f917 100644
--- a/swiftclient/client.py
+++ b/swiftclient/client.py
@@ -804,7 +804,8 @@ def post_account(url, token, headers, http_conn=None, response_dict=None,
def get_container(url, token, container, marker=None, limit=None,
prefix=None, delimiter=None, end_marker=None,
path=None, http_conn=None,
- full_listing=False, service_token=None, headers=None):
+ full_listing=False, service_token=None, headers=None,
+ query_string=None):
"""
Get a listing of objects for the container.
@@ -823,6 +824,7 @@ def get_container(url, token, container, marker=None, limit=None,
of 10000 listings
:param service_token: service auth token
:param headers: additional headers to include in the request
+ :param query_string: if set will be appended with '?' to generated path
:returns: a tuple of (response headers, a list of objects) The response
headers will be a dict and all header names will be lowercase.
:raises ClientException: HTTP GET request failed
@@ -866,6 +868,8 @@ def get_container(url, token, container, marker=None, limit=None,
qs += '&end_marker=%s' % quote(end_marker)
if path:
qs += '&path=%s' % quote(path)
+ if query_string:
+ qs += '&%s' % query_string.lstrip('?')
if service_token:
headers['X-Service-Token'] = service_token
method = 'GET'
@@ -927,7 +931,7 @@ def head_container(url, token, container, http_conn=None, headers=None,
def put_container(url, token, container, headers=None, http_conn=None,
- response_dict=None, service_token=None):
+ response_dict=None, service_token=None, query_string=None):
"""
Create a container
@@ -940,6 +944,7 @@ def put_container(url, token, container, headers=None, http_conn=None,
:param response_dict: an optional dictionary into which to place
the response - status, reason and headers
:param service_token: service auth token
+ :param query_string: if set will be appended with '?' to generated path
:raises ClientException: HTTP PUT request failed
"""
if http_conn:
@@ -955,6 +960,8 @@ def put_container(url, token, container, headers=None, http_conn=None,
headers['X-Service-Token'] = service_token
if 'content-length' not in (k.lower() for k in headers):
headers['Content-Length'] = '0'
+ if query_string:
+ path += '?' + query_string.lstrip('?')
conn.request(method, path, '', headers)
resp = conn.getresponse()
body = resp.read()
@@ -1008,7 +1015,8 @@ def post_container(url, token, container, headers, http_conn=None,
def delete_container(url, token, container, http_conn=None,
- response_dict=None, service_token=None):
+ response_dict=None, service_token=None,
+ query_string=None):
"""
Delete a container
@@ -1020,6 +1028,7 @@ def delete_container(url, token, container, http_conn=None,
:param response_dict: an optional dictionary into which to place
the response - status, reason and headers
:param service_token: service auth token
+ :param query_string: if set will be appended with '?' to generated path
:raises ClientException: HTTP DELETE request failed
"""
if http_conn:
@@ -1030,6 +1039,8 @@ def delete_container(url, token, container, http_conn=None,
headers = {'X-Auth-Token': token}
if service_token:
headers['X-Service-Token'] = service_token
+ if query_string:
+ path += '?' + query_string.lstrip('?')
method = 'DELETE'
conn.request(method, path, '', headers)
resp = conn.getresponse()
@@ -1592,7 +1603,7 @@ class Connection(object):
def get_container(self, container, marker=None, limit=None, prefix=None,
delimiter=None, end_marker=None, path=None,
- full_listing=False, headers=None):
+ full_listing=False, headers=None, query_string=None):
"""Wrapper for :func:`get_container`"""
# TODO(unknown): With full_listing=True this will restart the entire
# listing with each retry. Need to make a better version that just
@@ -1600,22 +1611,27 @@ class Connection(object):
return self._retry(None, get_container, container, marker=marker,
limit=limit, prefix=prefix, delimiter=delimiter,
end_marker=end_marker, path=path,
- full_listing=full_listing, headers=headers)
+ full_listing=full_listing, headers=headers,
+ query_string=query_string)
- def put_container(self, container, headers=None, response_dict=None):
+ def put_container(self, container, headers=None, response_dict=None,
+ query_string=None):
"""Wrapper for :func:`put_container`"""
return self._retry(None, put_container, container, headers=headers,
- response_dict=response_dict)
+ response_dict=response_dict,
+ query_string=query_string)
def post_container(self, container, headers, response_dict=None):
"""Wrapper for :func:`post_container`"""
return self._retry(None, post_container, container, headers,
response_dict=response_dict)
- def delete_container(self, container, response_dict=None):
+ def delete_container(self, container, response_dict=None,
+ query_string=None):
"""Wrapper for :func:`delete_container`"""
return self._retry(None, delete_container, container,
- response_dict=response_dict)
+ response_dict=response_dict,
+ query_string=query_string)
def head_object(self, container, obj, headers=None):
"""Wrapper for :func:`head_object`"""
diff --git a/tests/unit/test_swiftclient.py b/tests/unit/test_swiftclient.py
index a51fe1a..dda37fd 100644
--- a/tests/unit/test_swiftclient.py
+++ b/tests/unit/test_swiftclient.py
@@ -746,6 +746,16 @@ class TestGetContainer(MockHttpTest):
}),
])
+ def test_query_string(self):
+ c.http_connection = self.fake_http_connection(
+ 200, query_string="format=json&hello=20", body=b'[]')
+ c.get_container('http://www.test.com', 'asdf', 'asdf',
+ query_string="hello=20")
+ self.assertRequests([
+ ('GET', '/asdf?format=json&hello=20', '', {
+ 'x-auth-token': 'asdf'}),
+ ])
+
class TestHeadContainer(MockHttpTest):
@@ -805,6 +815,17 @@ class TestPutContainer(MockHttpTest):
'content-length': '0'}),
])
+ def test_query_string(self):
+ c.http_connection = self.fake_http_connection(200,
+ query_string="hello=20")
+ c.put_container('http://www.test.com', 'asdf', 'asdf',
+ query_string="hello=20")
+ for req in self.iter_request_log():
+ self.assertEqual(req['method'], 'PUT')
+ self.assertEqual(req['parsed_path'].path, '/asdf')
+ self.assertEqual(req['parsed_path'].query, 'hello=20')
+ self.assertEqual(req['headers']['x-auth-token'], 'asdf')
+
class TestDeleteContainer(MockHttpTest):
@@ -817,6 +838,16 @@ class TestDeleteContainer(MockHttpTest):
'x-auth-token': 'token'}),
])
+ def test_query_string(self):
+ c.http_connection = self.fake_http_connection(200,
+ query_string="hello=20")
+ c.delete_container('http://www.test.com', 'token', 'container',
+ query_string="hello=20")
+ self.assertRequests([
+ ('DELETE', 'http://www.test.com/container?hello=20', '', {
+ 'x-auth-token': 'token'})
+ ])
+
class TestGetObject(MockHttpTest):