Skip to content

Commit

Permalink
Add 'update' API wrapper for buckets/blobs (googleapis#3714, googleap…
Browse files Browse the repository at this point in the history
…is#3715)

Turns out some properties (i.e., 'labels', see googleapis#3711) behave differently under 'patch semantics'[1], which makes 'update' useful.

[1] https://cloud.google.com/storage/docs/json_api/v1/how-tos/performance#patch

Closes googleapis#7311
  • Loading branch information
tseaver authored and landrito committed Aug 22, 2017
1 parent 87dc3bc commit d2fb8aa
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 0 deletions.
16 changes: 16 additions & 0 deletions storage/google/cloud/storage/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,22 @@ def patch(self, client=None):
query_params={'projection': 'full'}, _target_object=self)
self._set_properties(api_response)

def update(self, client=None):
"""Sends all properties in a PUT request.
Updates the ``_properties`` with the response from the backend.
:type client: :class:`~google.cloud.storage.client.Client` or
``NoneType``
:param client: the client to use. If not passed, falls back to the
``client`` stored on the current object.
"""
client = self._require_client(client)
api_response = client._connection.api_request(
method='PUT', path=self.path, data=self._properties,
query_params={'projection': 'full'}, _target_object=self)
self._set_properties(api_response)


def _scalar_property(fieldname):
"""Create a property descriptor around the :class:`_PropertyMixin` helpers.
Expand Down
3 changes: 3 additions & 0 deletions storage/google/cloud/storage/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ def cors(self):
>>> policies[1]['maxAgeSeconds'] = 3600
>>> del policies[0]
>>> bucket.cors = policies
>>> bucket.update()
:setter: Set CORS policies for this bucket.
:getter: Gets the CORS policies for this bucket.
Expand Down Expand Up @@ -595,6 +596,7 @@ def labels(self):
>>> labels['new_key'] = 'some-label'
>>> del labels['old_key']
>>> bucket.labels = labels
>>> bucket.update()
:setter: Set labels for this bucket.
:getter: Gets the labels for this bucket.
Expand Down Expand Up @@ -663,6 +665,7 @@ def lifecycle_rules(self):
>>> rules[1]['rule']['action']['type'] = 'Delete'
>>> del rules[0]
>>> bucket.lifecycle_rules = rules
>>> bucket.update()
:setter: Set lifestyle rules for this bucket.
:getter: Gets the lifestyle rules for this bucket.
Expand Down
20 changes: 20 additions & 0 deletions storage/tests/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,26 @@ def test_list_buckets(self):
if bucket.name in buckets_to_create]
self.assertEqual(len(created_buckets), len(buckets_to_create))

def test_bucket_update_labels(self):
bucket_name = 'update-labels' + unique_resource_id('-')
bucket = retry_429(Config.CLIENT.create_bucket)(bucket_name)
self.case_buckets_to_delete.append(bucket_name)
self.assertTrue(bucket.exists())

updated_labels = {'test-label': 'label-value'}
bucket.labels = updated_labels
bucket.update()
self.assertEqual(bucket.labels, updated_labels)

new_labels = {'another-label': 'another-value'}
bucket.labels = new_labels
bucket.update()
self.assertEqual(bucket.labels, new_labels)

bucket.labels = {}
bucket.update()
self.assertEqual(bucket.labels, {})


class TestStorageFiles(unittest.TestCase):

Expand Down
20 changes: 20 additions & 0 deletions storage/tests/unit/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,26 @@ def test_patch(self):
# Make sure changes get reset by patch().
self.assertEqual(derived._changes, set())

def test_update(self):
connection = _Connection({'foo': 'Foo'})
client = _Client(connection)
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']) # Update sends 'baz' anyway.
derived.update(client=client)
self.assertEqual(derived._properties, {'foo': 'Foo'})
kw = connection._requested
self.assertEqual(len(kw), 1)
self.assertEqual(kw[0]['method'], 'PUT')
self.assertEqual(kw[0]['path'], '/path')
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
self.assertEqual(kw[0]['data'], {'bar': BAR, 'baz': BAZ})
# Make sure changes get reset by patch().
self.assertEqual(derived._changes, set())


class Test__scalar_property(unittest.TestCase):

Expand Down

0 comments on commit d2fb8aa

Please sign in to comment.