Skip to content

Commit 5ab6b0d

Browse files
fix: preserve metadata value (#298)
Fixes #293
1 parent 3bf5c52 commit 5ab6b0d

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

google/cloud/storage/blob.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,8 @@ def _do_multipart_upload(
16081608
raise ValueError(msg)
16091609

16101610
transport = self._get_transport(client)
1611+
if "metadata" in self._properties and "metadata" not in self._changes:
1612+
self._changes.add("metadata")
16111613
info = self._get_upload_arguments(content_type)
16121614
headers, object_metadata, content_type = info
16131615

@@ -1775,6 +1777,8 @@ def _initiate_resumable_upload(
17751777
chunk_size = _DEFAULT_CHUNKSIZE
17761778

17771779
transport = self._get_transport(client)
1780+
if "metadata" in self._properties and "metadata" not in self._changes:
1781+
self._changes.add("metadata")
17781782
info = self._get_upload_arguments(content_type)
17791783
headers, object_metadata, content_type = info
17801784
if extra_headers is not None:

tests/unit/test_blob.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,12 +1828,17 @@ def _do_multipart_success(
18281828
if_metageneration_not_match=None,
18291829
kms_key_name=None,
18301830
timeout=None,
1831+
metadata=None,
18311832
):
18321833
from six.moves.urllib.parse import urlencode
18331834

18341835
bucket = _Bucket(name="w00t", user_project=user_project)
18351836
blob = self._make_one(u"blob-name", bucket=bucket, kms_key_name=kms_key_name)
18361837
self.assertIsNone(blob.chunk_size)
1838+
if metadata:
1839+
self.assertIsNone(blob.metadata)
1840+
blob._properties["metadata"] = metadata
1841+
self.assertEqual(len(blob._changes), 0)
18371842

18381843
# Create mocks to be checked for doing transport.
18391844
transport = self._mock_transport(http_client.OK, {})
@@ -1906,10 +1911,18 @@ def _do_multipart_success(
19061911

19071912
upload_url += "?" + urlencode(qs_params)
19081913

1914+
blob_data = b'{"name": "blob-name"}\r\n'
1915+
if metadata:
1916+
blob_data = (
1917+
b'{"name": "blob-name", "metadata": '
1918+
+ json.dumps(metadata).encode("utf-8")
1919+
+ b"}\r\n"
1920+
)
1921+
self.assertEqual(blob._changes, set(["metadata"]))
19091922
payload = (
19101923
b"--==0==\r\n"
19111924
+ b"content-type: application/json; charset=UTF-8\r\n\r\n"
1912-
+ b'{"name": "blob-name"}\r\n'
1925+
+ blob_data
19131926
+ b"--==0==\r\n"
19141927
+ b"content-type: application/xml\r\n\r\n"
19151928
+ data_read
@@ -1974,6 +1987,10 @@ def test__do_multipart_upload_with_generation_not_match(self, mock_get_boundary)
19741987
mock_get_boundary, if_generation_not_match=4, if_metageneration_not_match=4
19751988
)
19761989

1990+
@mock.patch(u"google.resumable_media._upload.get_boundary", return_value=b"==0==")
1991+
def test__do_multipart_upload_with_metadata(self, mock_get_boundary):
1992+
self._do_multipart_success(mock_get_boundary, metadata={"test": "test"})
1993+
19771994
def test__do_multipart_upload_bad_size(self):
19781995
blob = self._make_one(u"blob-name", bucket=None)
19791996

@@ -2006,14 +2023,20 @@ def _initiate_resumable_helper(
20062023
blob_chunk_size=786432,
20072024
kms_key_name=None,
20082025
timeout=None,
2026+
metadata=None,
20092027
):
20102028
from six.moves.urllib.parse import urlencode
20112029
from google.resumable_media.requests import ResumableUpload
20122030
from google.cloud.storage.blob import _DEFAULT_CHUNKSIZE
20132031

20142032
bucket = _Bucket(name="whammy", user_project=user_project)
20152033
blob = self._make_one(u"blob-name", bucket=bucket, kms_key_name=kms_key_name)
2016-
blob.metadata = {"rook": "takes knight"}
2034+
if metadata:
2035+
self.assertIsNone(blob.metadata)
2036+
blob._properties["metadata"] = metadata
2037+
self.assertEqual(len(blob._changes), 0)
2038+
else:
2039+
blob.metadata = {"rook": "takes knight"}
20172040
blob.chunk_size = blob_chunk_size
20182041
if blob_chunk_size is not None:
20192042
self.assertIsNotNone(blob.chunk_size)
@@ -2022,8 +2045,11 @@ def _initiate_resumable_helper(
20222045

20232046
# Need to make sure **same** dict is used because ``json.dumps()``
20242047
# will depend on the hash order.
2025-
object_metadata = blob._get_writable_metadata()
2026-
blob._get_writable_metadata = mock.Mock(return_value=object_metadata, spec=[])
2048+
if not metadata:
2049+
object_metadata = blob._get_writable_metadata()
2050+
blob._get_writable_metadata = mock.Mock(
2051+
return_value=object_metadata, spec=[]
2052+
)
20272053

20282054
# Create mocks to be checked for doing transport.
20292055
resumable_url = "http://test.invalid?upload_id=hey-you"
@@ -2107,6 +2133,8 @@ def _initiate_resumable_helper(
21072133
self.assertNotEqual(blob.chunk_size, chunk_size)
21082134
self.assertEqual(upload._chunk_size, chunk_size)
21092135
self.assertIs(upload._stream, stream)
2136+
if metadata:
2137+
self.assertEqual(blob._changes, set(["metadata"]))
21102138
if size is None:
21112139
self.assertIsNone(upload._total_bytes)
21122140
else:
@@ -2125,8 +2153,11 @@ def _initiate_resumable_helper(
21252153
# Make sure we never read from the stream.
21262154
self.assertEqual(stream.tell(), 0)
21272155

2128-
# Check the mocks.
2129-
blob._get_writable_metadata.assert_called_once_with()
2156+
if metadata:
2157+
object_metadata = {"name": u"blob-name", "metadata": metadata}
2158+
else:
2159+
# Check the mocks.
2160+
blob._get_writable_metadata.assert_called_once_with()
21302161
payload = json.dumps(object_metadata).encode("utf-8")
21312162
expected_headers = {
21322163
"content-type": "application/json; charset=UTF-8",
@@ -2144,6 +2175,9 @@ def _initiate_resumable_helper(
21442175
timeout=expected_timeout,
21452176
)
21462177

2178+
def test__initiate_resumable_upload_with_metadata(self):
2179+
self._initiate_resumable_helper(metadata={"test": "test"})
2180+
21472181
def test__initiate_resumable_upload_with_custom_timeout(self):
21482182
self._initiate_resumable_helper(timeout=9.58)
21492183

0 commit comments

Comments
 (0)