Skip to content

Commit

Permalink
feat: add if*generation*match support for Bucket.rename_blob() (#141)
Browse files Browse the repository at this point in the history
* feat: add ifMetageneration*Match support, pt1

* fix unit tests, add test for helper

* fix unit tests

* add generation match args into more methods

* feat: add if*generation*Match support, pt2

* Lint fix.

* delete "more than one set "checks

* del excess import

* delete "more than one set" checks

* rename the helper; add error raising in case of wront parameters type

* feat: add if*generation*match support for Bucket.rename_blob()

* erase resolving conflicts caused blank line

Co-authored-by: Frank Natividad <frankyn@users.noreply.github.com>
  • Loading branch information
Gurov Ilya and frankyn committed Jun 9, 2020
1 parent dc01c59 commit f52efc8
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 5 deletions.
99 changes: 95 additions & 4 deletions google/cloud/storage/bucket.py
Expand Up @@ -1666,7 +1666,21 @@ def copy_blob(
new_blob._set_properties(copy_result)
return new_blob

def rename_blob(self, blob, new_name, client=None, timeout=_DEFAULT_TIMEOUT):
def rename_blob(
self,
blob,
new_name,
client=None,
timeout=_DEFAULT_TIMEOUT,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
if_source_generation_match=None,
if_source_generation_not_match=None,
if_source_metageneration_match=None,
if_source_metageneration_not_match=None,
):
"""Rename the given blob using copy and delete operations.
If :attr:`user_project` is set, bills the API request to that project.
Expand Down Expand Up @@ -1699,16 +1713,93 @@ def rename_blob(self, blob, new_name, client=None, timeout=_DEFAULT_TIMEOUT):
Can also be passed as a tuple (connect_timeout, read_timeout).
See :meth:`requests.Session.request` documentation for details.
:type if_generation_match: long
:param if_generation_match: (Optional) Makes the operation
conditional on whether the destination
object's current generation matches the
given value. Setting to 0 makes the
operation succeed only if there are no
live versions of the object.
:type if_generation_not_match: long
:param if_generation_not_match: (Optional) Makes the operation
conditional on whether the
destination object's current
generation does not match the given
value. If no live object exists,
the precondition fails. Setting to
0 makes the operation succeed only
if there is a live version
of the object.
:type if_metageneration_match: long
:param if_metageneration_match: (Optional) Makes the operation
conditional on whether the
destination object's current
metageneration matches the given
value.
:type if_metageneration_not_match: long
:param if_metageneration_not_match: (Optional) Makes the operation
conditional on whether the
destination object's current
metageneration does not match
the given value.
:type if_source_generation_match: long
:param if_source_generation_match: (Optional) Makes the operation
conditional on whether the source
object's generation matches the
given value.
:type if_source_generation_not_match: long
:param if_source_generation_not_match: (Optional) Makes the operation
conditional on whether the source
object's generation does not match
the given value.
:type if_source_metageneration_match: long
:param if_source_metageneration_match: (Optional) Makes the operation
conditional on whether the source
object's current metageneration
matches the given value.
:type if_source_metageneration_not_match: long
:param if_source_metageneration_not_match: (Optional) Makes the operation
conditional on whether the source
object's current metageneration
does not match the given value.
:rtype: :class:`Blob`
:returns: The newly-renamed blob.
"""
same_name = blob.name == new_name

new_blob = self.copy_blob(blob, self, new_name, client=client, timeout=timeout)
new_blob = self.copy_blob(
blob,
self,
new_name,
client=client,
timeout=timeout,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
if_source_generation_match=if_source_generation_match,
if_source_generation_not_match=if_source_generation_not_match,
if_source_metageneration_match=if_source_metageneration_match,
if_source_metageneration_not_match=if_source_metageneration_not_match,
)

if not same_name:
blob.delete(client=client, timeout=timeout)

blob.delete(
client=client,
timeout=timeout,
if_generation_match=if_generation_match,
if_generation_not_match=if_generation_not_match,
if_metageneration_match=if_metageneration_match,
if_metageneration_not_match=if_metageneration_not_match,
)
return new_blob

@property
Expand Down
58 changes: 57 additions & 1 deletion tests/unit/test_bucket.py
Expand Up @@ -1426,7 +1426,63 @@ def test_rename_blob(self):
self.assertEqual(kw["query_params"], {})
self.assertEqual(kw["timeout"], 42)

blob.delete.assert_called_once_with(client=client, timeout=42)
blob.delete.assert_called_once_with(
client=client,
timeout=42,
if_generation_match=None,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
)

def test_rename_blob_with_generation_match(self):
BUCKET_NAME = "BUCKET_NAME"
BLOB_NAME = "blob-name"
NEW_BLOB_NAME = "new-blob-name"
DATA = {"name": NEW_BLOB_NAME}
GENERATION_NUMBER = 6
METAGENERATION_NUMBER = 9

connection = _Connection(DATA)
client = _Client(connection)
bucket = self._make_one(client=client, name=BUCKET_NAME)
blob = self._make_blob(BUCKET_NAME, BLOB_NAME)

renamed_blob = bucket.rename_blob(
blob,
NEW_BLOB_NAME,
client=client,
timeout=42,
if_generation_match=GENERATION_NUMBER,
if_source_metageneration_not_match=METAGENERATION_NUMBER,
)

self.assertIs(renamed_blob.bucket, bucket)
self.assertEqual(renamed_blob.name, NEW_BLOB_NAME)

COPY_PATH = "/b/{}/o/{}/copyTo/b/{}/o/{}".format(
BUCKET_NAME, BLOB_NAME, BUCKET_NAME, NEW_BLOB_NAME
)
(kw,) = connection._requested
self.assertEqual(kw["method"], "POST")
self.assertEqual(kw["path"], COPY_PATH)
self.assertEqual(
kw["query_params"],
{
"ifGenerationMatch": GENERATION_NUMBER,
"ifSourceMetagenerationNotMatch": METAGENERATION_NUMBER,
},
)
self.assertEqual(kw["timeout"], 42)

blob.delete.assert_called_once_with(
client=client,
timeout=42,
if_generation_match=GENERATION_NUMBER,
if_generation_not_match=None,
if_metageneration_match=None,
if_metageneration_not_match=None,
)

def test_rename_blob_to_itself(self):
BUCKET_NAME = "BUCKET_NAME"
Expand Down

0 comments on commit f52efc8

Please sign in to comment.