Merge "Create header property"
This commit is contained in:
@@ -11,7 +11,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from openstack import exceptions
|
||||
from openstack.object_store import object_store_service
|
||||
from openstack import resource
|
||||
from openstack import utils
|
||||
@@ -31,20 +30,20 @@ class Container(resource.Resource):
|
||||
|
||||
# Account data (when id=None)
|
||||
#: The transaction date and time.
|
||||
timestamp = resource.prop("x-timestamp")
|
||||
timestamp = resource.header("x-timestamp")
|
||||
#: The total number of bytes that are stored in Object Storage for
|
||||
#: the account.
|
||||
account_bytes_used = resource.prop("x-account-bytes-used")
|
||||
account_bytes_used = resource.header("x-account-bytes-used")
|
||||
#: The number of containers.
|
||||
account_container_count = resource.prop("x-account-container-count")
|
||||
account_container_count = resource.header("x-account-container-count")
|
||||
#: The number of objects in the account.
|
||||
account_object_count = resource.prop("x-account-object-count")
|
||||
account_object_count = resource.header("x-account-object-count")
|
||||
#: The secret key value for temporary URLs. If not set,
|
||||
#: this header is not returned by this operation.
|
||||
meta_temp_url_key = resource.prop("x-account-meta-temp-url-key")
|
||||
meta_temp_url_key = resource.header("x-account-meta-temp-url-key")
|
||||
#: A second secret key value for temporary URLs. If not set,
|
||||
#: this header is not returned by this operation.
|
||||
meta_temp_url_key_2 = resource.prop("x-account-meta-temp-url-key-2")
|
||||
meta_temp_url_key_2 = resource.header("x-account-meta-temp-url-key-2")
|
||||
|
||||
# Container body data (when id=None)
|
||||
#: The name of the container.
|
||||
@@ -57,9 +56,9 @@ class Container(resource.Resource):
|
||||
|
||||
# Container metadata (when id=name)
|
||||
#: The number of objects.
|
||||
object_count = resource.prop("x-container-object-count")
|
||||
object_count = resource.header("x-container-object-count")
|
||||
#: The count of bytes used in total.
|
||||
bytes_used = resource.prop("x-container-bytes-used")
|
||||
bytes_used = resource.header("x-container-bytes-used")
|
||||
|
||||
# Request headers (when id=None)
|
||||
#: If set to True, Object Storage queries all replicas to return the
|
||||
@@ -67,55 +66,38 @@ class Container(resource.Resource):
|
||||
#: faster after it finds one valid replica. Because setting this
|
||||
#: header to True is more expensive for the back end, use it only
|
||||
#: when it is absolutely needed.
|
||||
newest = resource.prop("x-newest", type=bool)
|
||||
newest = resource.header("x-newest", type=bool)
|
||||
|
||||
# Request headers (when id=name)
|
||||
#: The ACL that grants read access. If not set, this header is not
|
||||
#: returned by this operation.
|
||||
read_ACL = resource.prop("x-container-read")
|
||||
read_ACL = resource.header("x-container-read")
|
||||
#: The ACL that grants write access. If not set, this header is not
|
||||
#: returned by this operation.
|
||||
write_ACL = resource.prop("x-container-write")
|
||||
write_ACL = resource.header("x-container-write")
|
||||
#: The destination for container synchronization. If not set,
|
||||
#: this header is not returned by this operation.
|
||||
sync_to = resource.prop("x-container-sync-to")
|
||||
sync_to = resource.header("x-container-sync-to")
|
||||
#: The secret key for container synchronization. If not set,
|
||||
#: this header is not returned by this operation.
|
||||
sync_key = resource.prop("x-container-sync-key")
|
||||
sync_key = resource.header("x-container-sync-key")
|
||||
#: Enables versioning on this container. The value is the name
|
||||
#: of another container. You must UTF-8-encode and then URL-encode
|
||||
#: the name before you include it in the header. To disable
|
||||
#: versioning, set the header to an empty string.
|
||||
versions_location = resource.prop("x-versions-location")
|
||||
versions_location = resource.header("x-versions-location")
|
||||
#: Set to any value to disable versioning.
|
||||
remove_versions_location = resource.prop("x-remove-versions-location")
|
||||
remove_versions_location = resource.header("x-remove-versions-location")
|
||||
#: Changes the MIME type for the object.
|
||||
content_type = resource.prop("content-type")
|
||||
content_type = resource.header("content-type")
|
||||
#: If set to true, Object Storage guesses the content type based
|
||||
#: on the file extension and ignores the value sent in the
|
||||
#: Content-Type header, if present.
|
||||
detect_content_type = resource.prop("x-detect-content-type", type=bool)
|
||||
detect_content_type = resource.header("x-detect-content-type", type=bool)
|
||||
#: In combination with Expect: 100-Continue, specify an
|
||||
#: "If-None-Match: \*" header to query whether the server already
|
||||
#: has a copy of the object before any data is sent.
|
||||
if_none_match = resource.prop("if-none-match")
|
||||
|
||||
@classmethod
|
||||
def _do_create_update(cls, method, session, attrs, resource_id):
|
||||
"""Helper method to call put and post
|
||||
|
||||
The internals of create and update are the same exept for
|
||||
the session method.
|
||||
"""
|
||||
url = utils.urljoin(cls.base_path, resource_id)
|
||||
|
||||
# Only send actual headers, not potentially set body values.
|
||||
headers = attrs.copy()
|
||||
for val in ("name", "count", "bytes"):
|
||||
headers.pop(val, None)
|
||||
|
||||
return method(url, service=cls.service, accept=None,
|
||||
headers=headers).headers
|
||||
if_none_match = resource.header("if-none-match")
|
||||
|
||||
@classmethod
|
||||
def update_by_id(cls, session, resource_id, attrs, path_args=None):
|
||||
@@ -131,14 +113,11 @@ class Container(resource.Resource):
|
||||
class but is ignored for this method.
|
||||
|
||||
:return: A ``dict`` representing the response headers.
|
||||
:raises: :exc:`~openstack.exceptions.MethodNotSupported` if
|
||||
:data:`Resource.allow_update` is not set to ``True``.
|
||||
"""
|
||||
if not cls.allow_update:
|
||||
raise exceptions.MethodNotSupported('update')
|
||||
|
||||
return cls._do_create_update(session.post, session, attrs,
|
||||
resource_id)
|
||||
url = utils.urljoin(cls.base_path, resource_id)
|
||||
headers = attrs[resource.HEADERS]
|
||||
return session.post(url, service=cls.service, accept=None,
|
||||
headers=headers).headers
|
||||
|
||||
@classmethod
|
||||
def create_by_id(cls, session, attrs, resource_id=None):
|
||||
@@ -152,14 +131,11 @@ class Container(resource.Resource):
|
||||
the request. The default is ``None``.
|
||||
|
||||
:return: A ``dict`` representing the response headers.
|
||||
:raises: :exc:`~openstack.exceptions.MethodNotSupported` if
|
||||
:data:`Resource.allow_create` is not set to ``True``.
|
||||
"""
|
||||
if not cls.allow_create:
|
||||
raise exceptions.MethodNotSupported('create')
|
||||
|
||||
return cls._do_create_update(session.put, session, attrs,
|
||||
resource_id)
|
||||
url = utils.urljoin(cls.base_path, resource_id)
|
||||
headers = attrs[resource.HEADERS]
|
||||
return session.put(url, service=cls.service, accept=None,
|
||||
headers=headers).headers
|
||||
|
||||
def create(self, session):
|
||||
"""Create a Container from this instance.
|
||||
@@ -169,9 +145,8 @@ class Container(resource.Resource):
|
||||
|
||||
:return: This :class:`~openstack.object_store.v1.container.Container`
|
||||
instance.
|
||||
:raises: :exc:`~openstack.exceptions.MethodNotSupported` if
|
||||
:data:`Resource.allow_create` is not set to ``True``.
|
||||
"""
|
||||
self.create_by_id(session, self._attrs, self.id)
|
||||
resp = self.create_by_id(session, self._attrs, self.id)
|
||||
self.set_headers(resp)
|
||||
self._reset_dirty()
|
||||
return self
|
||||
|
@@ -11,7 +11,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from openstack import exceptions
|
||||
from openstack.object_store import object_store_service
|
||||
from openstack import resource
|
||||
from openstack import utils
|
||||
@@ -41,55 +40,55 @@ class Object(resource.Resource):
|
||||
|
||||
# Headers for HEAD and GET requests
|
||||
#: Authentication token.
|
||||
auth_token = resource.prop("x-auth-token")
|
||||
auth_token = resource.header("x-auth-token")
|
||||
#: If set to True, Object Storage queries all replicas to return
|
||||
#: the most recent one. If you omit this header, Object Storage
|
||||
#: responds faster after it finds one valid replica. Because
|
||||
#: setting this header to True is more expensive for the back end,
|
||||
#: use it only when it is absolutely needed.
|
||||
newest = resource.prop("x-newest", type=bool)
|
||||
newest = resource.header("x-newest", type=bool)
|
||||
#: TODO(briancurtin) there's a lot of content here...
|
||||
range = resource.prop("range", type=dict)
|
||||
range = resource.header("range", type=dict)
|
||||
#: See http://www.ietf.org/rfc/rfc2616.txt.
|
||||
if_match = resource.prop("if-match", type=dict)
|
||||
if_match = resource.header("if-match", type=dict)
|
||||
#: In combination with Expect: 100-Continue, specify an
|
||||
#: "If-None-Match: \*" header to query whether the server already
|
||||
#: has a copy of the object before any data is sent.
|
||||
if_none_match = resource.prop("if-none-match", type=dict)
|
||||
if_none_match = resource.header("if-none-match", type=dict)
|
||||
#: See http://www.ietf.org/rfc/rfc2616.txt.
|
||||
if_modified_since = resource.prop("if-modified-since", type=dict)
|
||||
if_modified_since = resource.header("if-modified-since", type=dict)
|
||||
#: See http://www.ietf.org/rfc/rfc2616.txt.
|
||||
if_unmodified_since = resource.prop("if-unmodified-since", type=dict)
|
||||
if_unmodified_since = resource.header("if-unmodified-since", type=dict)
|
||||
|
||||
# Query parameters
|
||||
#: Used with temporary URLs to sign the request. For more
|
||||
#: information about temporary URLs, see OpenStack Object Storage
|
||||
#: API v1 Reference.
|
||||
signature = resource.prop("signature")
|
||||
signature = resource.header("signature")
|
||||
#: Used with temporary URLs to specify the expiry time of the
|
||||
#: signature. For more information about temporary URLs, see
|
||||
#: OpenStack Object Storage API v1 Reference.
|
||||
expires = resource.prop("expires")
|
||||
expires = resource.header("expires")
|
||||
#: If you include the multipart-manifest=get query parameter and
|
||||
#: the object is a large object, the object contents are not
|
||||
#: returned. Instead, the manifest is returned in the
|
||||
#: X-Object-Manifest response header for dynamic large objects
|
||||
#: or in the response body for static large objects.
|
||||
multipart_manifest = resource.prop("multipart-manifest")
|
||||
multipart_manifest = resource.header("multipart-manifest")
|
||||
|
||||
# Response headers from HEAD and GET
|
||||
#: HEAD operations do not return content. However, in this
|
||||
#: operation the value in the Content-Length header is not the
|
||||
#: size of the response body. Instead it contains the size of
|
||||
#: the object, in bytes.
|
||||
content_length = resource.prop("content-length")
|
||||
content_length = resource.header("content-length")
|
||||
#: The MIME type of the object.
|
||||
content_type = resource.prop("content_type", alias="content-type")
|
||||
content_type = resource.header("content_type", alias="content-type")
|
||||
#: The type of ranges that the object accepts.
|
||||
accept_ranges = resource.prop("accept-ranges")
|
||||
accept_ranges = resource.header("accept-ranges")
|
||||
#: The date and time that the object was created or the last
|
||||
#: time that the metadata was changed.
|
||||
last_modified = resource.prop("last_modified", alias="last-modified")
|
||||
last_modified = resource.header("last_modified", alias="last-modified")
|
||||
#: For objects smaller than 5 GB, this value is the MD5 checksum
|
||||
#: of the object content. The value is not quoted.
|
||||
#: For manifest objects, this value is the MD5 checksum of the
|
||||
@@ -101,39 +100,39 @@ class Object(resource.Resource):
|
||||
#: the response body as it is received and compare this value
|
||||
#: with the one in the ETag header. If they differ, the content
|
||||
#: was corrupted, so retry the operation.
|
||||
etag = resource.prop("etag")
|
||||
etag = resource.header("etag")
|
||||
#: Set to True if this object is a static large object manifest object.
|
||||
is_static_large_object = resource.prop("x-static-large-object")
|
||||
is_static_large_object = resource.header("x-static-large-object")
|
||||
#: The transaction date and time.
|
||||
date = resource.prop("date")
|
||||
date = resource.header("date")
|
||||
#: If set, the value of the Content-Encoding metadata.
|
||||
#: If not set, this header is not returned by this operation.
|
||||
content_encoding = resource.prop("content-encoding")
|
||||
content_encoding = resource.header("content-encoding")
|
||||
#: If set, specifies the override behavior for the browser.
|
||||
#: For example, this header might specify that the browser use
|
||||
#: a download program to save this file rather than show the file,
|
||||
#: which is the default.
|
||||
#: If not set, this header is not returned by this operation.
|
||||
content_disposition = resource.prop("content-disposition")
|
||||
content_disposition = resource.header("content-disposition")
|
||||
#: If set, the time when the object will be deleted by the system
|
||||
#: in the format of a UNIX Epoch timestamp.
|
||||
#: If not set, this header is not returned by this operation.
|
||||
delete_at = resource.prop("x-delete-at", type=int)
|
||||
delete_at = resource.header("x-delete-at", type=int)
|
||||
#: If set, to this is a dynamic large object manifest object.
|
||||
#: The value is the container and object name prefix of the
|
||||
#: segment objects in the form container/prefix.
|
||||
object_manifest = resource.prop("x-object-manifest")
|
||||
object_manifest = resource.header("x-object-manifest")
|
||||
#: The UNIX timestamp of the transaction.
|
||||
timestamp = resource.prop("x-timestamp")
|
||||
timestamp = resource.header("x-timestamp")
|
||||
|
||||
# Headers for PUT and POST requests
|
||||
#: Set to chunked to enable chunked transfer encoding. If used,
|
||||
#: do not set the Content-Length header to a non-zero value.
|
||||
transfer_encoding = resource.prop("transfer-encoding")
|
||||
transfer_encoding = resource.header("transfer-encoding")
|
||||
#: If set to true, Object Storage guesses the content type based
|
||||
#: on the file extension and ignores the value sent in the
|
||||
#: Content-Type header, if present.
|
||||
detect_content_type = resource.prop("x-detect-content-type", type=bool)
|
||||
detect_content_type = resource.header("x-detect-content-type", type=bool)
|
||||
#: If set, this is the name of an object used to create the new
|
||||
#: object by copying the X-Copy-From object. The value is in form
|
||||
#: {container}/{object}. You must UTF-8-encode and then URL-encode
|
||||
@@ -141,16 +140,13 @@ class Object(resource.Resource):
|
||||
#: in the header.
|
||||
#: Using PUT with X-Copy-From has the same effect as using the
|
||||
#: COPY operation to copy an object.
|
||||
copy_from = resource.prop("x-copy-from")
|
||||
copy_from = resource.header("x-copy-from")
|
||||
#: Specifies the number of seconds after which the object is
|
||||
#: removed. Internally, the Object Storage system stores this
|
||||
#: value in the X-Delete-At metadata item.
|
||||
delete_after = resource.prop("x-delete-after", type=int)
|
||||
delete_after = resource.header("x-delete-after", type=int)
|
||||
|
||||
def get(self, session):
|
||||
if not self.allow_retrieve:
|
||||
raise exceptions.MethodNotSupported('retrieve')
|
||||
|
||||
# When joining the base_path part and the id part, base_path's
|
||||
# leading slash gets dropped off here. Putting an empty leading value
|
||||
# in front of it causes it to get joined and replaced.
|
||||
@@ -158,11 +154,7 @@ class Object(resource.Resource):
|
||||
|
||||
# Only send actual headers, not potentially set body values and
|
||||
# query parameters.
|
||||
headers = self._attrs.copy()
|
||||
for val in ("container", "name", "hash", "bytes", "signature",
|
||||
"expires", "multipart_manifest"):
|
||||
headers.pop(val, None)
|
||||
|
||||
headers = self.get_headers()
|
||||
resp = session.get(url, service=self.service, accept="bytes",
|
||||
headers=headers).content
|
||||
|
||||
@@ -170,9 +162,6 @@ class Object(resource.Resource):
|
||||
|
||||
def create(self, session, data=None):
|
||||
"""Create a remote resource from this instance."""
|
||||
if not self.allow_create:
|
||||
raise exceptions.MethodNotSupported('create')
|
||||
|
||||
url = utils.urljoin("", self.base_path % self, self.id)
|
||||
|
||||
if data is not None:
|
||||
@@ -181,5 +170,5 @@ class Object(resource.Resource):
|
||||
else:
|
||||
resp = session.post(url, service=self.service, data=None,
|
||||
accept=None).headers
|
||||
|
||||
self._attrs.update(resp)
|
||||
self.set_headers(resp)
|
||||
return self
|
||||
|
@@ -152,6 +152,52 @@ class prop(object):
|
||||
pass
|
||||
|
||||
|
||||
#: Key in attributes for header properties
|
||||
HEADERS = 'headers'
|
||||
|
||||
|
||||
class header(prop):
|
||||
"""A helper for defining header properties in a resource.
|
||||
|
||||
This property should be used for values passed in the header of a resource.
|
||||
Header values are stored in a special 'headers' attribute of a resource.
|
||||
Using this property will make it easier for users to access those values.
|
||||
For example, and object store container:
|
||||
|
||||
>>> class Container(Resource):
|
||||
... name = prop("name")
|
||||
... object_count = header("x-container-object-count")
|
||||
...
|
||||
>>> c = Container({name='pix'})
|
||||
>>> c.head(session)
|
||||
>>> print c["headers"]["x-container-object-count"]
|
||||
4
|
||||
>>> print c.object_count
|
||||
4
|
||||
|
||||
The first print shows accessing the header value without the property
|
||||
and the second print shows accessing the header with the property helper.
|
||||
"""
|
||||
|
||||
def _get_headers(self, instance):
|
||||
if instance is None:
|
||||
return None
|
||||
if HEADERS in instance:
|
||||
return instance[HEADERS]
|
||||
return None
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
headers = self._get_headers(instance)
|
||||
return super(header, self).__get__(headers, owner)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
headers = self._get_headers(instance)
|
||||
if headers is None:
|
||||
headers = instance._attrs[HEADERS] = {}
|
||||
headers[self.name] = value
|
||||
instance.set_headers(headers)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Resource(collections.MutableMapping):
|
||||
|
||||
@@ -397,6 +443,15 @@ class Resource(collections.MutableMapping):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def get_headers(self):
|
||||
if HEADERS in self._attrs:
|
||||
return self._attrs[HEADERS]
|
||||
return {}
|
||||
|
||||
def set_headers(self, values):
|
||||
self._attrs[HEADERS] = values
|
||||
self._dirty.add(HEADERS)
|
||||
|
||||
##
|
||||
# CRUD OPERATIONS
|
||||
##
|
||||
@@ -493,7 +548,7 @@ class Resource(collections.MutableMapping):
|
||||
body = body[cls.resource_key]
|
||||
|
||||
if include_headers:
|
||||
body.update(response.headers)
|
||||
body[HEADERS] = response.headers
|
||||
|
||||
return body
|
||||
|
||||
@@ -550,7 +605,7 @@ class Resource(collections.MutableMapping):
|
||||
a compound URL.
|
||||
See `How path_args are used`_ for details.
|
||||
|
||||
:return: A ``dict`` representing the headers.
|
||||
:return: A ``dict`` containing the headers.
|
||||
:raises: :exc:`~openstack.exceptions.MethodNotSupported` if
|
||||
:data:`Resource.allow_head` is not set to ``True``.
|
||||
"""
|
||||
@@ -565,7 +620,7 @@ class Resource(collections.MutableMapping):
|
||||
|
||||
data = session.head(url, service=cls.service, accept=None).headers
|
||||
|
||||
return data
|
||||
return {HEADERS: data}
|
||||
|
||||
@classmethod
|
||||
def head_by_id(cls, session, resource_id, path_args=None):
|
||||
|
@@ -69,7 +69,7 @@ LIST_EXAMPLE = [
|
||||
class TestAccount(testtools.TestCase):
|
||||
|
||||
def test_make_it(self):
|
||||
sot = container.Container.new(**ACCOUNT_EXAMPLE)
|
||||
sot = container.Container.new(**{'headers': ACCOUNT_EXAMPLE})
|
||||
self.assertIsNone(sot.id)
|
||||
self.assertEqual(ACCOUNT_EXAMPLE['x-timestamp'], sot.timestamp)
|
||||
self.assertEqual(ACCOUNT_EXAMPLE['x-account-bytes-used'],
|
||||
@@ -117,7 +117,7 @@ class TestContainer(testtools.TestCase):
|
||||
sot = container.Container(CONT_EXAMPLE)
|
||||
|
||||
# Update container with HEAD data
|
||||
sot._attrs.update(HEAD_EXAMPLE)
|
||||
sot._attrs.update({'headers': HEAD_EXAMPLE})
|
||||
|
||||
# Attributes from create
|
||||
self.assertEqual(CONT_EXAMPLE['name'], sot.id)
|
||||
|
@@ -13,7 +13,6 @@
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from openstack import exceptions
|
||||
from openstack.object_store.v1 import obj
|
||||
|
||||
|
||||
@@ -39,17 +38,19 @@ OBJ_EXAMPLE = {
|
||||
"content_type": "application/octet-stream"
|
||||
}
|
||||
|
||||
HEAD_EXAMPLE = {
|
||||
'content-length': '252466',
|
||||
DICT_EXAMPLE = {
|
||||
'container': CONTAINER_NAME,
|
||||
'name': OBJECT_NAME,
|
||||
'accept-ranges': 'bytes',
|
||||
'last-modified': 'Sun, 13 Jul 2014 18:41:04 GMT',
|
||||
'etag': '243f87b91224d85722564a80fd3cb1f1',
|
||||
'x-timestamp': '1405276863.31924',
|
||||
'date': 'Thu, 28 Aug 2014 14:41:59 GMT',
|
||||
'content-type': 'application/octet-stream',
|
||||
'id': 'tx5fb5ad4f4d0846c6b2bc7-0053ff3fb7'
|
||||
'headers': {
|
||||
'content-length': '252466',
|
||||
'accept-ranges': 'bytes',
|
||||
'last-modified': 'Sun, 13 Jul 2014 18:41:04 GMT',
|
||||
'etag': '243f87b91224d85722564a80fd3cb1f1',
|
||||
'x-timestamp': '1405276863.31924',
|
||||
'date': 'Thu, 28 Aug 2014 14:41:59 GMT',
|
||||
'content-type': 'application/octet-stream',
|
||||
'id': 'tx5fb5ad4f4d0846c6b2bc7-0053ff3fb7'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,17 +84,18 @@ class TestObject(testtools.TestCase):
|
||||
self.assertEqual(CONTAINER_NAME, sot.container)
|
||||
|
||||
def test_head(self):
|
||||
sot = obj.Object.existing(**HEAD_EXAMPLE)
|
||||
sot = obj.Object.existing(**DICT_EXAMPLE)
|
||||
|
||||
# Attributes from header
|
||||
self.assertEqual(HEAD_EXAMPLE['container'], sot.container)
|
||||
self.assertEqual(HEAD_EXAMPLE['content-length'], sot.content_length)
|
||||
self.assertEqual(HEAD_EXAMPLE['accept-ranges'], sot.accept_ranges)
|
||||
self.assertEqual(HEAD_EXAMPLE['last-modified'], sot.last_modified)
|
||||
self.assertEqual(HEAD_EXAMPLE['etag'], sot.etag)
|
||||
self.assertEqual(HEAD_EXAMPLE['x-timestamp'], sot.timestamp)
|
||||
self.assertEqual(HEAD_EXAMPLE['date'], sot.date)
|
||||
self.assertEqual(HEAD_EXAMPLE['content-type'], sot.content_type)
|
||||
self.assertEqual(DICT_EXAMPLE['container'], sot.container)
|
||||
headers = DICT_EXAMPLE['headers']
|
||||
self.assertEqual(headers['content-length'], sot.content_length)
|
||||
self.assertEqual(headers['accept-ranges'], sot.accept_ranges)
|
||||
self.assertEqual(headers['last-modified'], sot.last_modified)
|
||||
self.assertEqual(headers['etag'], sot.etag)
|
||||
self.assertEqual(headers['x-timestamp'], sot.timestamp)
|
||||
self.assertEqual(headers['date'], sot.date)
|
||||
self.assertEqual(headers['content-type'], sot.content_type)
|
||||
|
||||
def test_get(self):
|
||||
sot = obj.Object.new(container=CONTAINER_NAME, name=OBJECT_NAME)
|
||||
@@ -110,8 +112,3 @@ class TestObject(testtools.TestCase):
|
||||
self.sess.get.assert_called_with(url, service=sot.service,
|
||||
accept="bytes", headers=headers)
|
||||
self.assertEqual(rv, self.resp.content)
|
||||
|
||||
def test_cant_get(self):
|
||||
sot = obj.Object.new(container=CONTAINER_NAME, name=OBJECT_NAME)
|
||||
sot.allow_retrieve = False
|
||||
self.assertRaises(exceptions.MethodNotSupported, sot.get, self.sess)
|
||||
|
@@ -35,15 +35,14 @@ class TestObjectStoreProxy(test_proxy_base.TestProxyBase):
|
||||
|
||||
class Test_account_metadata(TestObjectStoreProxy):
|
||||
|
||||
@mock.patch("openstack.resource.Resource")
|
||||
def test_get_account_metadata(self, mock_resource):
|
||||
@mock.patch("openstack.resource.Resource.head")
|
||||
def test_get_account_metadata(self, mock_head):
|
||||
cont = container.Container()
|
||||
mock_resource = mock.MagicMock()
|
||||
mock_resource.head.return_value = cont
|
||||
mock_head.return_value = cont
|
||||
|
||||
result = self.proxy.get_account_metadata()
|
||||
|
||||
self.assertEqual(result, cont)
|
||||
self.assertEqual(cont, result)
|
||||
|
||||
def test_set_account_metadata(self):
|
||||
container = mock.MagicMock()
|
||||
|
@@ -176,6 +176,45 @@ class PropTests(base.TestCase):
|
||||
self.assertEqual(sot.attr, val)
|
||||
|
||||
|
||||
class HeaderTests(base.TestCase):
|
||||
class Test(resource.Resource):
|
||||
hey = resource.header("vocals")
|
||||
ho = resource.header("guitar")
|
||||
letsgo = resource.header("bass")
|
||||
|
||||
def test_get(self):
|
||||
val = "joey"
|
||||
args = {"vocals": val}
|
||||
sot = HeaderTests.Test({'headers': args})
|
||||
self.assertEqual(val, sot.hey)
|
||||
self.assertEqual(None, sot.ho)
|
||||
self.assertEqual(None, sot.letsgo)
|
||||
|
||||
def test_set_new(self):
|
||||
args = {"vocals": "joey", "bass": "deedee"}
|
||||
sot = HeaderTests.Test({'headers': args})
|
||||
sot._reset_dirty()
|
||||
sot.ho = "johnny"
|
||||
self.assertEqual("johnny", sot.ho)
|
||||
self.assertTrue(sot.is_dirty)
|
||||
|
||||
def test_set_old(self):
|
||||
args = {"vocals": "joey", "bass": "deedee"}
|
||||
sot = HeaderTests.Test({'headers': args})
|
||||
sot._reset_dirty()
|
||||
sot.letsgo = "cj"
|
||||
self.assertEqual("cj", sot.letsgo)
|
||||
self.assertTrue(sot.is_dirty)
|
||||
|
||||
def test_set_brand_new(self):
|
||||
sot = HeaderTests.Test({'headers': {}})
|
||||
sot._reset_dirty()
|
||||
sot.ho = "johnny"
|
||||
self.assertEqual("johnny", sot.ho)
|
||||
self.assertTrue(sot.is_dirty)
|
||||
self.assertEqual({'headers': {"guitar": "johnny"}}, sot)
|
||||
|
||||
|
||||
class ResourceTests(base.TestTransportBase):
|
||||
|
||||
TEST_URL = fakes.FakeAuthenticator.ENDPOINT
|
||||
@@ -352,7 +391,7 @@ class ResourceTests(base.TestTransportBase):
|
||||
|
||||
r_id = "my_id"
|
||||
resp = FakeResource2.head_data_by_id(sess, resource_id=r_id)
|
||||
self.assertEqual(resp, response_value)
|
||||
self.assertEqual({'headers': response_value}, resp)
|
||||
sess.head.assert_called_with(
|
||||
utils.urljoin(FakeResource2.base_path, r_id),
|
||||
service=FakeResource2.service,
|
||||
@@ -361,7 +400,7 @@ class ResourceTests(base.TestTransportBase):
|
||||
path_args = {"name": "my_name"}
|
||||
resp = FakeResource2.head_data_by_id(sess, resource_id=r_id,
|
||||
path_args=path_args)
|
||||
self.assertEqual(resp, response_value)
|
||||
self.assertEqual({'headers': response_value}, resp)
|
||||
sess.head.assert_called_with(
|
||||
utils.urljoin(FakeResource2.base_path % path_args, r_id),
|
||||
service=FakeResource2.service,
|
||||
@@ -504,8 +543,8 @@ class ResourceTests(base.TestTransportBase):
|
||||
**headers)
|
||||
|
||||
class FakeResource2(FakeResource):
|
||||
header1 = resource.prop("header1")
|
||||
header2 = resource.prop("header2")
|
||||
header1 = resource.header("header1")
|
||||
header2 = resource.header("header2")
|
||||
|
||||
obj = FakeResource2.get_by_id(self.session, fake_id,
|
||||
path_args=fake_arguments,
|
||||
@@ -515,8 +554,8 @@ class ResourceTests(base.TestTransportBase):
|
||||
self.assertEqual(fake_name, obj['name'])
|
||||
self.assertEqual(fake_attr1, obj['attr1'])
|
||||
self.assertEqual(fake_attr2, obj['attr2'])
|
||||
self.assertEqual(header1, obj['header1'])
|
||||
self.assertEqual(header2, obj['header2'])
|
||||
self.assertEqual(header1, obj['headers']['header1'])
|
||||
self.assertEqual(header2, obj['headers']['header2'])
|
||||
|
||||
self.assertEqual(fake_name, obj.name)
|
||||
self.assertEqual(fake_attr1, obj.first)
|
||||
@@ -526,20 +565,21 @@ class ResourceTests(base.TestTransportBase):
|
||||
|
||||
@httpretty.activate
|
||||
def test_head(self):
|
||||
class FakeResource2(FakeResource):
|
||||
header1 = resource.header("header1")
|
||||
header2 = resource.header("header2")
|
||||
|
||||
self.stub_url(httpretty.HEAD, path=[fake_path, fake_id],
|
||||
name=fake_name,
|
||||
attr1=fake_attr1,
|
||||
attr2=fake_attr2)
|
||||
obj = FakeResource.head_by_id(self.session, fake_id,
|
||||
path_args=fake_arguments)
|
||||
header1='one',
|
||||
header2='two')
|
||||
obj = FakeResource2.head_by_id(self.session, fake_id,
|
||||
path_args=fake_arguments)
|
||||
|
||||
self.assertEqual(fake_name, obj['name'])
|
||||
self.assertEqual(fake_attr1, obj['attr1'])
|
||||
self.assertEqual(fake_attr2, obj['attr2'])
|
||||
self.assertEqual('one', obj['headers']['header1'])
|
||||
self.assertEqual('two', obj['headers']['header2'])
|
||||
|
||||
self.assertEqual(fake_name, obj.name)
|
||||
self.assertEqual(fake_attr1, obj.first)
|
||||
self.assertEqual(fake_attr2, obj.second)
|
||||
self.assertEqual('one', obj.header1)
|
||||
self.assertEqual('two', obj.header2)
|
||||
|
||||
@httpretty.activate
|
||||
def test_update(self):
|
||||
|
Reference in New Issue
Block a user