Skip to content

Commit 1d1c36c

Browse files
committed
refactor code and add system tests
1 parent d96977a commit 1d1c36c

8 files changed

Lines changed: 271 additions & 231 deletions

File tree

packages/google-cloud-storage/google/cloud/storage/_grpc_conversions.py

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from google.protobuf import field_mask_pb2
1615
from google.protobuf import timestamp_pb2
1716

1817
from google.cloud import _storage_v2
@@ -109,36 +108,3 @@ def blob_to_proto(blob):
109108
resource_params["contexts"] = _storage_v2.ObjectContexts(custom=custom_contexts)
110109

111110
return _storage_v2.Object(**resource_params)
112-
113-
114-
def get_update_mask(blob, changes):
115-
"""Generates a FieldMask for gRPC update operations."""
116-
# Map REST property names to GCS V2 Object proto field names.
117-
property_to_proto_field = {
118-
"cacheControl": "cache_control",
119-
"contentDisposition": "content_disposition",
120-
"contentEncoding": "content_encoding",
121-
"contentLanguage": "content_language",
122-
"contentType": "content_type",
123-
"metadata": "metadata",
124-
"eventBasedHold": "event_based_hold",
125-
"temporaryHold": "temporary_hold",
126-
"kmsKeyName": "kms_key",
127-
"customTime": "custom_time",
128-
"retention": "retention",
129-
}
130-
paths = []
131-
for change in changes:
132-
if change == "contexts":
133-
contexts = getattr(blob, "contexts", None)
134-
if not (contexts and contexts.custom):
135-
paths.append("contexts.custom")
136-
else:
137-
for key in contexts.custom:
138-
paths.append(f"contexts.custom.{key}")
139-
else:
140-
proto_field = property_to_proto_field.get(change)
141-
if proto_field:
142-
paths.append(proto_field)
143-
144-
return field_mask_pb2.FieldMask(paths=paths)

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,38 @@ def test_blob_patch_metadata(
554554
assert blob.metadata == {"foo": "Foo"}
555555

556556

557+
def test_blob_contexts_crud(
558+
shared_bucket,
559+
blobs_to_delete,
560+
file_data,
561+
service_account,
562+
):
563+
from google.cloud.storage.blob import ObjectContexts, ObjectCustomContextPayload
564+
565+
filename = file_data["logo"]["path"]
566+
blob_name = os.path.basename(filename)
567+
568+
blob = shared_bucket.blob(blob_name)
569+
blob.upload_from_filename(filename)
570+
blobs_to_delete.append(blob)
571+
572+
custom = {"foo": ObjectCustomContextPayload(value="bar")}
573+
blob.contexts = ObjectContexts(blob, custom=custom)
574+
blob.patch()
575+
blob.reload()
576+
assert "foo" in blob.contexts.custom
577+
assert blob.contexts.custom["foo"].value == "bar"
578+
assert blob.contexts.custom["foo"].create_time is not None
579+
assert blob.contexts.custom["foo"].update_time is not None
580+
581+
# Ensure that context keys can be deleted by setting equal to None.
582+
new_custom = {"foo": None}
583+
blob.contexts = ObjectContexts(blob, custom=new_custom)
584+
blob.patch()
585+
blob.reload()
586+
assert "foo" not in blob.contexts.custom
587+
588+
557589
def test_blob_direct_write_and_read_into_file(
558590
shared_bucket,
559591
blobs_to_delete,

packages/google-cloud-storage/tests/system/test_bucket.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,35 @@ def test_bucket_list_blobs_w_match_glob(
731731
assert [blob.name for blob in blobs] == expected_names
732732

733733

734+
@_helpers.retry_failures
735+
def test_bucket_list_blobs_w_filter(
736+
storage_client,
737+
buckets_to_delete,
738+
blobs_to_delete,
739+
):
740+
from google.cloud.storage.blob import ObjectContexts, ObjectCustomContextPayload
741+
742+
bucket_name = _helpers.unique_name("w-filter")
743+
bucket = _helpers.retry_429_503(storage_client.create_bucket)(bucket_name)
744+
buckets_to_delete.append(bucket)
745+
746+
payload = b"helloworld"
747+
blob_names = ["foo", "bar", "baz"]
748+
for name in blob_names:
749+
blob = bucket.blob(name)
750+
blob.upload_from_string(payload)
751+
if name == "bar":
752+
custom = {"target": ObjectCustomContextPayload(value="match")}
753+
blob.contexts = ObjectContexts(blob, custom=custom)
754+
blob.patch()
755+
blobs_to_delete.append(blob)
756+
757+
# List with filter matching only 'bar'
758+
blob_iter = bucket.list_blobs(filter_='contexts."target"="match"')
759+
blobs = list(blob_iter)
760+
assert [blob.name for blob in blobs] == ["bar"]
761+
762+
734763
def test_bucket_list_blobs_include_managed_folders(
735764
storage_client,
736765
buckets_to_delete,

packages/google-cloud-storage/tests/unit/asyncio/test_async_write_object_stream.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from google.cloud import _storage_v2
2323
from google.cloud.storage import Blob, Bucket
24+
from google.cloud.storage.blob import ObjectContexts, ObjectCustomContextPayload
2425
from google.cloud.storage.asyncio.async_write_object_stream import (
2526
_AsyncWriteObjectStream,
2627
)
@@ -198,6 +199,9 @@ async def test_open_new_object_with_blob_sync_attrs(
198199
"retain_until_time": retain_until_time,
199200
}
200201

202+
payload = ObjectCustomContextPayload(value="context-value")
203+
mock_blob.contexts = ObjectContexts(mock_blob, custom={"context-key": payload})
204+
201205
stream = _AsyncWriteObjectStream(mock_client, BUCKET, OBJECT, blob=mock_blob)
202206
await stream.open()
203207

@@ -226,6 +230,9 @@ async def test_open_new_object_with_blob_sync_attrs(
226230
retain_until_time.timestamp()
227231
)
228232

233+
assert "context-key" in resource.contexts.custom
234+
assert resource.contexts.custom["context-key"].value == "context-value"
235+
229236
@pytest.mark.asyncio
230237
async def test_open_already_open_raises(self, mock_client):
231238
stream = _AsyncWriteObjectStream(mock_client, BUCKET, OBJECT)

packages/google-cloud-storage/tests/unit/test__grpc_conversions.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -162,35 +162,3 @@ def test_blob_to_proto_contexts():
162162
assert int(proto.contexts.custom["key"].create_time.timestamp()) == int(
163163
create_time.timestamp()
164164
)
165-
166-
167-
def test_get_update_mask_contexts():
168-
blob = mock.Mock(spec=["contexts"])
169-
from google.cloud.storage.blob import ObjectContexts, ObjectCustomContextPayload
170-
171-
# Partial updates
172-
blob.contexts = ObjectContexts(
173-
blob,
174-
custom={
175-
"k1": ObjectCustomContextPayload(value="v1"),
176-
"k2": ObjectCustomContextPayload(value="v2"),
177-
},
178-
)
179-
mask = _grpc_conversions.get_update_mask(blob, ["contexts"])
180-
assert "contexts.custom.k1" in mask.paths
181-
assert "contexts.custom.k2" in mask.paths
182-
assert len(mask.paths) == 2
183-
184-
# Clear all
185-
blob.contexts = None
186-
mask = _grpc_conversions.get_update_mask(blob, ["contexts"])
187-
assert "contexts.custom" in mask.paths
188-
assert len(mask.paths) == 1
189-
190-
# Mixed
191-
mask = _grpc_conversions.get_update_mask(
192-
blob, ["contexts", "metadata", "customTime"]
193-
)
194-
assert "contexts.custom" in mask.paths
195-
assert "metadata" in mask.paths
196-
assert "custom_time" in mask.paths

0 commit comments

Comments
 (0)