Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#825: Allow passing explicit connection to '_PropertyMixin.{reload,patch}'. #852

Merged
merged 2 commits into from
May 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions gcloud/storage/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ class _PropertyMixin(object):
- path
"""

@property
def connection(self):
"""Abstract getter for the connection to use."""
raise NotImplementedError

@property
def path(self):

This comment was marked as spam.

"""Abstract getter for the object path."""
Expand All @@ -52,12 +47,19 @@ def __init__(self, name=None):
self._properties = {}
self._changes = set()

def reload(self):
"""Reload properties from Cloud Storage."""
def reload(self, connection=None):
"""Reload properties from Cloud Storage.

:type connection: :class:`gcloud.storage.connection.Connection`
:param connection: An explicit connection to use for the API request.
If not passed, use the connection assigned to
the object in its constructor.
"""
connection = _require_connection(connection)
# Pass only '?projection=noAcl' here because 'acl' and related
# are handled via custom endpoints.
query_params = {'projection': 'noAcl'}
api_response = self.connection.api_request(
api_response = connection.api_request(
method='GET', path=self.path, query_params=query_params)
self._set_properties(api_response)

Expand Down Expand Up @@ -89,16 +91,22 @@ def _set_properties(self, value):
# If the values are reset, the changes must as well.
self._changes = set()

def patch(self):
def patch(self, connection=None):
"""Sends all changed properties in a PATCH request.

Updates the ``_properties`` with the response from the backend.

:type connection: :class:`gcloud.storage.connection.Connection`
:param connection: An explicit connection to use for the API request.
If not passed, use the connection assigned to
the object in its constructor.
"""
connection = _require_connection(connection)
# Pass '?projection=full' here because 'PATCH' documented not
# to work properly w/ 'noAcl'.
update_properties = dict((key, self._properties[key])
for key in self._changes)
api_response = self.connection.api_request(
api_response = connection.api_request(
method='PATCH', path=self.path, data=update_properties,
query_params={'projection': 'full'})
self._set_properties(api_response)
Expand Down
2 changes: 1 addition & 1 deletion gcloud/storage/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def get_bucket(bucket_name, connection=None):
"""
connection = _require_connection(connection)
bucket = Bucket(bucket_name, connection=connection)
bucket.reload()
bucket.reload(connection=connection)
return bucket


Expand Down
61 changes: 47 additions & 14 deletions gcloud/storage/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,46 @@ def _getTargetClass(self):
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)

def _derivedClass(self, connection=None, path=None):
def _derivedClass(self, path=None):

class Derived(self._getTargetClass()):

@property
def connection(self):
return connection

@property
def path(self):
return path

return Derived

def test_connection_is_abstract(self):
mixin = self._makeOne()
self.assertRaises(NotImplementedError, lambda: mixin.connection)
def _monkey(self, connection):
from gcloud.storage._testing import _monkey_defaults
return _monkey_defaults(connection=connection)

def test_path_is_abstract(self):
mixin = self._makeOne()
self.assertRaises(NotImplementedError, lambda: mixin.path)

def test_reload(self):
def test_reload_w_implicit_connection(self):
connection = _Connection({'foo': 'Foo'})
derived = self._derivedClass('/path')()
# Make sure changes is not a set, so we can observe a change.
derived._changes = object()
with self._monkey(connection):
derived.reload()
self.assertEqual(derived._properties, {'foo': 'Foo'})
kw = connection._requested
self.assertEqual(len(kw), 1)
self.assertEqual(kw[0]['method'], 'GET')
self.assertEqual(kw[0]['path'], '/path')
self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'})
# Make sure changes get reset by reload.
self.assertEqual(derived._changes, set())

def test_reload_w_explicit_connection(self):
connection = _Connection({'foo': 'Foo'})
derived = self._derivedClass(connection, '/path')()
derived = self._derivedClass('/path')()
# Make sure changes is not a set, so we can observe a change.
derived._changes = object()
derived.reload()
derived.reload(connection)
self.assertEqual(derived._properties, {'foo': 'Foo'})
kw = connection._requested
self.assertEqual(len(kw), 1)
Expand All @@ -66,15 +78,36 @@ def test__patch_property(self):
derived._patch_property('foo', 'Foo')
self.assertEqual(derived._properties, {'foo': 'Foo'})

def test_patch(self):
def test_patch_w_implicit_connection(self):
connection = _Connection({'foo': 'Foo'})
derived = self._derivedClass('/path')()
# Make sure changes is non-empty, so we can observe a change.
BAR = object()
BAZ = object()
derived._properties = {'bar': BAR, 'baz': BAZ}
derived._changes = set(['bar']) # Ignore baz.
with self._monkey(connection):
derived.patch()
self.assertEqual(derived._properties, {'foo': 'Foo'})
kw = connection._requested
self.assertEqual(len(kw), 1)
self.assertEqual(kw[0]['method'], 'PATCH')
self.assertEqual(kw[0]['path'], '/path')
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
# Since changes does not include `baz`, we don't see it sent.
self.assertEqual(kw[0]['data'], {'bar': BAR})
# Make sure changes get reset by patch().
self.assertEqual(derived._changes, set())

def test_patch_w_explicit_connection(self):
connection = _Connection({'foo': 'Foo'})
derived = self._derivedClass(connection, '/path')()
derived = self._derivedClass('/path')()
# Make sure changes is non-empty, so we can observe a change.
BAR = object()
BAZ = object()
derived._properties = {'bar': BAR, 'baz': BAZ}
derived._changes = set(['bar']) # Ignore baz.
derived.patch()
derived.patch(connection)
self.assertEqual(derived._properties, {'foo': 'Foo'})
kw = connection._requested
self.assertEqual(len(kw), 1)
Expand Down