Skip to content

Commit fba637d

Browse files
[Storage] Support metadata on upload_blob_from_url API (#37696)
1 parent 3b0da18 commit fba637d

File tree

7 files changed

+90
-6
lines changed

7 files changed

+90
-6
lines changed

sdk/storage/azure-storage-blob/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## 12.24.0b1 (Unreleased)
44

55
### Features Added
6-
6+
- Added support for passing metadata to `upload_blob_from_url` via the new `metadata` keyword.
77

88

99
## 12.23.1 (2024-09-25)

sdk/storage/azure-storage-blob/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/storage/azure-storage-blob",
5-
"Tag": "python/storage/azure-storage-blob_fad0e99de3"
5+
"Tag": "python/storage/azure-storage-blob_7df5687d1f"
66
}

sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,12 @@ def get_account_information(self, **kwargs: Any) -> Dict[str, str]:
320320
process_storage_error(error)
321321

322322
@distributed_trace
323-
def upload_blob_from_url(self, source_url: str, **kwargs: Any) -> Dict[str, Any]:
323+
def upload_blob_from_url(
324+
self, source_url: str,
325+
*,
326+
metadata: Optional[Dict[str, str]] = None,
327+
**kwargs: Any
328+
) -> Dict[str, Any]:
324329
"""
325330
Creates a new Block Blob where the content of the blob is read from a given URL.
326331
The content of an existing blob is overwritten with the new blob.
@@ -337,6 +342,8 @@ def upload_blob_from_url(self, source_url: str, **kwargs: Any) -> Dict[str, Any]
337342
https://myaccount.blob.core.windows.net/mycontainer/myblob?snapshot=<DateTime>
338343
339344
https://otheraccount.blob.core.windows.net/mycontainer/myblob?sastoken
345+
:keyword dict(str, str) metadata:
346+
Name-value pairs associated with the blob as metadata.
340347
:keyword bool overwrite: Whether the blob to be uploaded should overwrite the current data.
341348
If True, upload_blob will overwrite the existing data. If set to False, the
342349
operation will fail with ResourceExistsError.
@@ -422,6 +429,7 @@ def upload_blob_from_url(self, source_url: str, **kwargs: Any) -> Dict[str, Any]
422429
raise ValueError("Customer provided encryption key must be used over HTTPS.")
423430
options = _upload_blob_from_url_options(
424431
source_url=source_url,
432+
metadata=metadata,
425433
**kwargs)
426434
try:
427435
return cast(Dict[str, Any], self._client.block_blob.put_blob_from_url(**options))

sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client_helpers.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ def _upload_blob_options( # pylint:disable=too-many-statements
188188
raise ValueError(f"Unsupported BlobType: {blob_type}")
189189
return kwargs
190190

191-
def _upload_blob_from_url_options(source_url: str, **kwargs: Any ) -> Dict[str, Any]:
191+
def _upload_blob_from_url_options(source_url: str, **kwargs: Any) -> Dict[str, Any]:
192+
metadata = kwargs.pop('metadata', None)
193+
headers = kwargs.pop('headers', {})
194+
headers.update(add_metadata_headers(metadata))
192195
source_url = _encode_source_url(source_url=source_url)
193196
tier = kwargs.pop('standard_blob_tier', None)
194197
overwrite = kwargs.pop('overwrite', False)
@@ -222,7 +225,8 @@ def _upload_blob_from_url_options(source_url: str, **kwargs: Any ) -> Dict[str,
222225
'tier': tier.value if tier else None,
223226
'source_modified_access_conditions': get_source_conditions(kwargs),
224227
'cpk_info': cpk_info,
225-
'cpk_scope_info': get_cpk_scope_info(kwargs)
228+
'cpk_scope_info': get_cpk_scope_info(kwargs),
229+
'headers': headers,
226230
}
227231
options.update(kwargs)
228232
if not overwrite and not _any_conditions(**options): # pylint: disable=protected-access

sdk/storage/azure-storage-blob/azure/storage/blob/aio/_blob_client_async.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,12 @@ async def get_account_information(self, **kwargs: Any) -> Dict[str, str]:
310310
process_storage_error(error)
311311

312312
@distributed_trace_async
313-
async def upload_blob_from_url(self, source_url: str, **kwargs: Any) -> Dict[str, Any]:
313+
async def upload_blob_from_url(
314+
self, source_url: str,
315+
*,
316+
metadata: Optional[Dict[str, str]] = None,
317+
**kwargs: Any
318+
) -> Dict[str, Any]:
314319
"""
315320
Creates a new Block Blob where the content of the blob is read from a given URL.
316321
The content of an existing blob is overwritten with the new blob.
@@ -327,6 +332,8 @@ async def upload_blob_from_url(self, source_url: str, **kwargs: Any) -> Dict[str
327332
https://myaccount.blob.core.windows.net/mycontainer/myblob?snapshot=<DateTime>
328333
329334
https://otheraccount.blob.core.windows.net/mycontainer/myblob?sastoken
335+
:keyword dict(str, str) metadata:
336+
Name-value pairs associated with the blob as metadata.
330337
:keyword bool overwrite: Whether the blob to be uploaded should overwrite the current data.
331338
If True, upload_blob will overwrite the existing data. If set to False, the
332339
operation will fail with ResourceExistsError.
@@ -412,6 +419,7 @@ async def upload_blob_from_url(self, source_url: str, **kwargs: Any) -> Dict[str
412419
raise ValueError("Customer provided encryption key must be used over HTTPS.")
413420
options = _upload_blob_from_url_options(
414421
source_url=source_url,
422+
metadata=metadata,
415423
**kwargs)
416424
try:
417425
return cast(Dict[str, Any], await self._client.block_blob.put_blob_from_url(**options))

sdk/storage/azure-storage-blob/tests/test_block_blob.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,38 @@ def test_upload_blob_from_url_with_standard_tier_specified(self, **kwargs):
197197
# Assert
198198
assert new_blob_properties.blob_tier == blob_tier
199199

200+
@BlobPreparer()
201+
@recorded_by_proxy
202+
def test_upload_blob_from_url_with_metadata(self, **kwargs):
203+
storage_account_name = kwargs.pop("storage_account_name")
204+
storage_account_key = kwargs.pop("storage_account_key")
205+
206+
# Arrange
207+
self._setup(storage_account_name, storage_account_key, container_name="testcontainer")
208+
blob = self._create_blob()
209+
self.bsc.get_blob_client(self.container_name, blob.blob_name)
210+
sas = self.generate_sas(
211+
generate_blob_sas,
212+
account_name=storage_account_name,
213+
account_key=storage_account_key,
214+
container_name=self.container_name,
215+
blob_name=blob.blob_name,
216+
permission=BlobSasPermissions(read=True),
217+
expiry=datetime.utcnow() + timedelta(hours=1)
218+
)
219+
# Act
220+
source_blob = '{0}/{1}/{2}?{3}'.format(
221+
self.account_url(storage_account_name, "blob"), self.container_name, blob.blob_name, sas)
222+
223+
blob_name = self.get_resource_name("blobcopy")
224+
new_blob = self.bsc.get_blob_client(self.container_name, blob_name)
225+
new_blob.upload_blob_from_url(source_blob, metadata={'blobdata': 'data1'})
226+
227+
new_blob_properties = new_blob.get_blob_properties()
228+
229+
# Assert
230+
assert new_blob_properties.metadata == {'blobdata': 'data1'}
231+
200232
@BlobPreparer()
201233
@recorded_by_proxy
202234
def test_upload_blob_from_url_with_cold_tier_specified(self, **kwargs):

sdk/storage/azure-storage-blob/tests/test_block_blob_async.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,38 @@ async def test_upload_blob_from_url_with_standard_tier_specified(self, **kwargs)
220220
# Assert
221221
assert new_blob_properties.blob_tier == blob_tier
222222

223+
@BlobPreparer()
224+
@recorded_by_proxy_async
225+
async def test_upload_blob_from_url_with_metadata(self, **kwargs):
226+
storage_account_name = kwargs.pop("storage_account_name")
227+
storage_account_key = kwargs.pop("storage_account_key")
228+
229+
# Arrange
230+
await self._setup(storage_account_name, storage_account_key, container_name="testcontainer")
231+
blob = await self._create_blob()
232+
self.bsc.get_blob_client(self.container_name, blob.blob_name)
233+
sas = self.generate_sas(
234+
generate_blob_sas,
235+
account_name=storage_account_name,
236+
account_key=storage_account_key,
237+
container_name=self.container_name,
238+
blob_name=blob.blob_name,
239+
permission=BlobSasPermissions(read=True),
240+
expiry=datetime.utcnow() + timedelta(hours=1)
241+
)
242+
# Act
243+
source_blob = '{0}/{1}/{2}?{3}'.format(
244+
self.account_url(storage_account_name, "blob"), self.container_name, blob.blob_name, sas)
245+
246+
blob_name = self.get_resource_name("blobcopy")
247+
new_blob = self.bsc.get_blob_client(self.container_name, blob_name)
248+
await new_blob.upload_blob_from_url(source_blob, metadata={'blobdata': 'data1'})
249+
250+
new_blob_properties = await new_blob.get_blob_properties()
251+
252+
# Assert
253+
assert new_blob_properties.metadata == {'blobdata': 'data1'}
254+
223255
@BlobPreparer()
224256
@recorded_by_proxy_async
225257
async def test_upload_blob_from_url_with_cold_tier_specified(self, **kwargs):

0 commit comments

Comments
 (0)