diff --git a/google/cloud/storage/bucket.py b/google/cloud/storage/bucket.py index 78d8c5fc2..228c0e2aa 100644 --- a/google/cloud/storage/bucket.py +++ b/google/cloud/storage/bucket.py @@ -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. @@ -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 diff --git a/tests/unit/test_bucket.py b/tests/unit/test_bucket.py index 29320367a..27bd94f1a 100644 --- a/tests/unit/test_bucket.py +++ b/tests/unit/test_bucket.py @@ -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"