Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 16ab4c2

Browse filesBrowse files
feat(storage): Add delete_source_objects optional parameter to compose API (#17163)
Add an optional `delete_source_objects` boolean parameter to the `Blob.compose` method in `google-cloud-storage`. When this parameter is set to `True`, it includes the `deleteSourceObjects` field in the request body to the GCS JSON API, which triggers the deletion of source objects upon successful composition. Unit and system tests have been added and verified to pass. --- *PR created automatically by Jules for task [8742736712890332678](https://jules.google.com/task/8742736712890332678) started by @nidhiii-27* --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: nidhiii-27 <224584462+nidhiii-27@users.noreply.github.com>
1 parent 9ba049a commit 16ab4c2
Copy full SHA for 16ab4c2

3 files changed

+66Lines changed: 66 additions & 0 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎packages/google-cloud-storage/google/cloud/storage/blob.py‎

Copy file name to clipboardExpand all lines: packages/google-cloud-storage/google/cloud/storage/blob.py
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3849,6 +3849,7 @@ def compose(
38493849
if_metageneration_match=None,
38503850
if_source_generation_match=None,
38513851
retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
3852+
delete_source_objects=None,
38523853
):
38533854
"""Concatenate source blobs into this one.
38543855
@@ -3908,6 +3909,11 @@ def compose(
39083909
Change the value to ``DEFAULT_RETRY`` or another `google.api_core.retry.Retry` object
39093910
to enable retries regardless of generation precondition setting.
39103911
See [Configuring Retries](https://cloud.google.com/python/docs/reference/storage/latest/retry_timeout).
3912+
3913+
:type delete_source_objects: bool
3914+
:param delete_source_objects:
3915+
(Optional) If True, the source objects will be deleted after a
3916+
successful composition.
39113917
"""
39123918
with create_trace_span(name="Storage.Blob.compose"):
39133919
sources_len = len(sources)
@@ -3964,6 +3970,9 @@ def compose(
39643970
"destination": self._properties.copy(),
39653971
}
39663972

3973+
if delete_source_objects is not None:
3974+
request["deleteSourceObjects"] = delete_source_objects
3975+
39673976
if self.user_project is not None:
39683977
query_params["userProject"] = self.user_project
39693978

Collapse file

‎packages/google-cloud-storage/tests/system/test_blob.py‎

Copy file name to clipboardExpand all lines: packages/google-cloud-storage/tests/system/test_blob.py
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,3 +1209,23 @@ def test_blob_download_as_bytes_single_shot_download(
12091209

12101210
result_single_shot_download = blob.download_as_bytes(single_shot_download=True)
12111211
assert result_single_shot_download == payload
1212+
1213+
1214+
def test_blob_compose_delete_source_objects(shared_bucket, blobs_to_delete):
1215+
payload_1 = b"AAA\n"
1216+
source_1 = shared_bucket.blob("source-1-delete")
1217+
source_1.upload_from_string(payload_1)
1218+
blobs_to_delete.append(source_1)
1219+
1220+
payload_2 = b"BBB\n"
1221+
source_2 = shared_bucket.blob("source-2-delete")
1222+
source_2.upload_from_string(payload_2)
1223+
blobs_to_delete.append(source_2)
1224+
1225+
destination = shared_bucket.blob("destination-delete")
1226+
destination.compose([source_1, source_2], delete_source_objects=True)
1227+
blobs_to_delete.append(destination)
1228+
1229+
assert destination.download_as_bytes() == payload_1 + payload_2
1230+
assert not source_1.exists()
1231+
assert not source_2.exists()
Collapse file

‎packages/google-cloud-storage/tests/unit/test_blob.py‎

Copy file name to clipboardExpand all lines: packages/google-cloud-storage/tests/unit/test_blob.py
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4874,6 +4874,43 @@ def test_compose_w_metageneration_match(self):
48744874
_target_object=destination,
48754875
)
48764876

4877+
def test_compose_w_delete_source_objects(self):
4878+
source_1_name = "source-1"
4879+
source_2_name = "source-2"
4880+
destination_name = "destination"
4881+
delete_source_objects = True
4882+
api_response = {}
4883+
client = mock.Mock(spec=["_post_resource"])
4884+
client._post_resource.return_value = api_response
4885+
bucket = _Bucket(client=client)
4886+
source_1 = self._make_one(source_1_name, bucket=bucket)
4887+
source_2 = self._make_one(source_2_name, bucket=bucket)
4888+
destination = self._make_one(destination_name, bucket=bucket)
4889+
4890+
destination.compose(
4891+
sources=[source_1, source_2],
4892+
delete_source_objects=delete_source_objects,
4893+
)
4894+
4895+
expected_path = f"/b/name/o/{destination_name}/compose"
4896+
expected_data = {
4897+
"sourceObjects": [
4898+
{"name": source_1.name, "generation": source_1.generation},
4899+
{"name": source_2.name, "generation": source_2.generation},
4900+
],
4901+
"destination": {},
4902+
"deleteSourceObjects": delete_source_objects,
4903+
}
4904+
expected_query_params = {}
4905+
client._post_resource.assert_called_once_with(
4906+
expected_path,
4907+
expected_data,
4908+
query_params=expected_query_params,
4909+
timeout=self._get_default_timeout(),
4910+
retry=DEFAULT_RETRY_IF_GENERATION_SPECIFIED,
4911+
_target_object=destination,
4912+
)
4913+
48774914
def test_rewrite_w_response_wo_resource(self):
48784915
source_name = "source"
48794916
dest_name = "dest"

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.