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 c8dd7a0

Browse filesBrowse files
authored
feat: expose DELETE_OBJECT in AsyncGrpcClient (#1718)
Expose `DELETE_OBJECT` in `AsyncGrpcClient`
1 parent 08bc708 commit c8dd7a0
Copy full SHA for c8dd7a0

File tree

Expand file treeCollapse file tree

3 files changed

+129
-1
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+129
-1
lines changed
Open diff view settings
Collapse file

‎google/cloud/storage/asyncio/async_grpc_client.py‎

Copy file name to clipboardExpand all lines: google/cloud/storage/asyncio/async_grpc_client.py
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,55 @@ def grpc_client(self):
106106
google.cloud._storage_v2.StorageAsyncClient: The configured GAPIC client.
107107
"""
108108
return self._grpc_client
109+
110+
async def delete_object(
111+
self,
112+
bucket_name,
113+
object_name,
114+
generation=None,
115+
if_generation_match=None,
116+
if_generation_not_match=None,
117+
if_metageneration_match=None,
118+
if_metageneration_not_match=None,
119+
**kwargs,
120+
):
121+
"""Deletes an object and its metadata.
122+
123+
:type bucket_name: str
124+
:param bucket_name: The name of the bucket in which the object resides.
125+
126+
:type object_name: str
127+
:param object_name: The name of the object to delete.
128+
129+
:type generation: int
130+
:param generation:
131+
(Optional) If present, permanently deletes a specific generation
132+
of an object.
133+
134+
:type if_generation_match: int
135+
:param if_generation_match: (Optional)
136+
137+
:type if_generation_not_match: int
138+
:param if_generation_not_match: (Optional)
139+
140+
:type if_metageneration_match: int
141+
:param if_metageneration_match: (Optional)
142+
143+
:type if_metageneration_not_match: int
144+
:param if_metageneration_not_match: (Optional)
145+
146+
147+
"""
148+
# The gRPC API requires the bucket name to be in the format "projects/_/buckets/bucket_name"
149+
bucket_path = f"projects/_/buckets/{bucket_name}"
150+
request = storage_v2.DeleteObjectRequest(
151+
bucket=bucket_path,
152+
object=object_name,
153+
generation=generation,
154+
if_generation_match=if_generation_match,
155+
if_generation_not_match=if_generation_not_match,
156+
if_metageneration_match=if_metageneration_match,
157+
if_metageneration_not_match=if_metageneration_not_match,
158+
**kwargs,
159+
)
160+
await self._grpc_client.delete_object(request=request)
Collapse file

‎tests/system/test_zonal.py‎

Copy file name to clipboardExpand all lines: tests/system/test_zonal.py
+32-1Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from google.cloud.storage.asyncio.async_multi_range_downloader import (
2020
AsyncMultiRangeDownloader,
2121
)
22-
from google.api_core.exceptions import FailedPrecondition
22+
from google.api_core.exceptions import FailedPrecondition, NotFound
2323

2424

2525
pytestmark = pytest.mark.skipif(
@@ -570,3 +570,34 @@ async def _run():
570570
blobs_to_delete.append(storage_client.bucket(_ZONAL_BUCKET).blob(object_name))
571571

572572
event_loop.run_until_complete(_run())
573+
574+
575+
def test_delete_object_using_grpc_client(event_loop, grpc_client_direct):
576+
"""
577+
Test that a new writer when specifies `None` overrides the existing object.
578+
"""
579+
object_name = f"test_append_with_generation-{uuid.uuid4()}"
580+
581+
async def _run():
582+
writer = AsyncAppendableObjectWriter(
583+
grpc_client_direct, _ZONAL_BUCKET, object_name, generation=0
584+
)
585+
586+
# Empty object is created.
587+
await writer.open()
588+
await writer.append(b"some_bytes")
589+
await writer.close()
590+
591+
await grpc_client_direct.delete_object(_ZONAL_BUCKET, object_name)
592+
593+
# trying to get raises raises 404.
594+
with pytest.raises(NotFound):
595+
# TODO: Remove this once GET_OBJECT is exposed in `AsyncGrpcClient`
596+
await grpc_client_direct._grpc_client.get_object(
597+
bucket=f"projects/_/buckets/{_ZONAL_BUCKET}", object_=object_name
598+
)
599+
# cleanup
600+
del writer
601+
gc.collect()
602+
603+
event_loop.run_until_complete(_run())
Collapse file

‎tests/unit/asyncio/test_async_grpc_client.py‎

Copy file name to clipboardExpand all lines: tests/unit/asyncio/test_async_grpc_client.py
+45Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
from unittest import mock
16+
import pytest
1617
from google.auth import credentials as auth_credentials
1718
from google.auth.credentials import AnonymousCredentials
1819
from google.api_core import client_info as client_info_lib
@@ -185,6 +186,7 @@ def test_grpc_client_with_anon_creds(self, mock_grpc_gapic_client):
185186
credentials=anonymous_creds,
186187
options=expected_options,
187188
)
189+
mock_transport_cls.assert_called_once_with(channel=channel_sentinel)
188190

189191
@mock.patch("google.cloud._storage_v2.StorageAsyncClient")
190192
def test_user_agent_with_custom_client_info(self, mock_async_storage_client):
@@ -209,3 +211,46 @@ def test_user_agent_with_custom_client_info(self, mock_async_storage_client):
209211
agent_version = f"gcloud-python/{__version__}"
210212
expected_user_agent = f"custom-app/1.0 {agent_version} "
211213
assert client_info.user_agent == expected_user_agent
214+
215+
@mock.patch("google.cloud._storage_v2.StorageAsyncClient")
216+
@pytest.mark.asyncio
217+
async def test_delete_object(self, mock_async_storage_client):
218+
# Arrange
219+
mock_transport_cls = mock.MagicMock()
220+
mock_async_storage_client.get_transport_class.return_value = mock_transport_cls
221+
mock_gapic_client = mock.AsyncMock()
222+
mock_async_storage_client.return_value = mock_gapic_client
223+
224+
client = async_grpc_client.AsyncGrpcClient(
225+
credentials=_make_credentials(spec=AnonymousCredentials)
226+
)
227+
228+
bucket_name = "bucket"
229+
object_name = "object"
230+
generation = 123
231+
if_generation_match = 456
232+
if_generation_not_match = 789
233+
if_metageneration_match = 111
234+
if_metageneration_not_match = 222
235+
236+
# Act
237+
await client.delete_object(
238+
bucket_name,
239+
object_name,
240+
generation=generation,
241+
if_generation_match=if_generation_match,
242+
if_generation_not_match=if_generation_not_match,
243+
if_metageneration_match=if_metageneration_match,
244+
if_metageneration_not_match=if_metageneration_not_match,
245+
)
246+
247+
# Assert
248+
call_args, call_kwargs = mock_gapic_client.delete_object.call_args
249+
request = call_kwargs["request"]
250+
assert request.bucket == "projects/_/buckets/bucket"
251+
assert request.object == "object"
252+
assert request.generation == generation
253+
assert request.if_generation_match == if_generation_match
254+
assert request.if_generation_not_match == if_generation_not_match
255+
assert request.if_metageneration_match == if_metageneration_match
256+
assert request.if_metageneration_not_match == if_metageneration_not_match

0 commit comments

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