diff --git a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs index e68e86c532..02f90c9a7b 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -4,7 +4,7 @@ use crate::{ generated::clients::BlobClient as GeneratedBlobClient, generated::models::{ - BlobClientDownloadResult, BlobClientGetPropertiesResult, + BlobClientDownloadResult, BlobClientGetPropertiesResult, BlobClientStartCopyFromUrlResult, BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult, BlockBlobClientUploadResult, }, @@ -12,9 +12,9 @@ use crate::{ pipeline::StorageHeadersPolicy, BlobClientDeleteOptions, BlobClientDownloadOptions, BlobClientGetPropertiesOptions, BlobClientOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, - BlobClientSetTierOptions, BlockBlobClientCommitBlockListOptions, - BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions, - BlockBlobClientUploadOptions, + BlobClientSetTierOptions, BlobClientStartCopyFromUrlOptions, + BlockBlobClientCommitBlockListOptions, BlockBlobClientGetBlockListOptions, + BlockBlobClientStageBlockOptions, BlockBlobClientUploadOptions, }; use azure_core::{ credentials::TokenCredential, @@ -250,4 +250,23 @@ impl BlobClient { ) -> Result> { self.client.set_tier(tier, options).await } + + /// Copies a blob or an internet resource to a new blob. + /// + /// # Arguments + /// + /// * `copy_source` - A URL of up to 2 KB in length that specifies a file or blob. + /// The value should be URL-encoded as it would appear in a request URI. + /// If the source is in another account, the source must either be public + /// or must be authenticated via a shared access signature. If the source + /// is public, no authentication is required. + /// Example: https://myaccount.blob.core.windows.net/mycontainer/myblob + /// * `options` - Optional configuration for the request. + pub async fn start_copy_from_url( + &self, + copy_source: &str, + options: Option>, + ) -> Result> { + self.client.start_copy_from_url(copy_source, options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs index 17ae16cf10..1c975e3251 100644 --- a/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs @@ -1387,7 +1387,7 @@ impl BlobClient { path = path.replace("{blobName}", &self.blob_name); path = path.replace("{containerName}", &self.container_name); url = url.join(&path)?; - url.query_pairs_mut().append_pair("comp", "copy"); + // url.query_pairs_mut().append_pair("comp", "copy"); if let Some(timeout) = options.timeout { url.query_pairs_mut() .append_pair("timeout", &timeout.to_string()); diff --git a/sdk/storage/azure_storage_blob/src/lib.rs b/sdk/storage/azure_storage_blob/src/lib.rs index 75460580b8..e3d4107e69 100644 --- a/sdk/storage/azure_storage_blob/src/lib.rs +++ b/sdk/storage/azure_storage_blob/src/lib.rs @@ -18,17 +18,18 @@ pub use crate::generated::clients::{ pub use crate::generated::models::{ BlobClientDeleteOptions, BlobClientDownloadOptions, BlobClientGetPropertiesOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTierOptions, - BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, - BlobContainerClientGetPropertiesOptions, BlobContainerClientSetMetadataOptions, - BlobServiceClientGetPropertiesOptions, BlockBlobClientCommitBlockListOptions, - BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions, - BlockBlobClientUploadOptions, + BlobClientStartCopyFromUrlOptions, BlobContainerClientCreateOptions, + BlobContainerClientDeleteOptions, BlobContainerClientGetPropertiesOptions, + BlobContainerClientSetMetadataOptions, BlobServiceClientGetPropertiesOptions, + BlockBlobClientCommitBlockListOptions, BlockBlobClientGetBlockListOptions, + BlockBlobClientStageBlockOptions, BlockBlobClientUploadOptions, }; pub mod models { pub use crate::generated::models::{ AccessTier, ArchiveStatus, BlobClientDownloadResult, BlobClientDownloadResultHeaders, BlobClientGetPropertiesResult, BlobClientGetPropertiesResultHeaders, + BlobClientStartCopyFromUrlResult, BlobClientStartCopyFromUrlResultHeaders, BlobContainerClientGetPropertiesResult, BlobContainerClientGetPropertiesResultHeaders, BlobImmutabilityPolicyMode, BlobType, BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult, BlockBlobClientUploadResult, BlockList, BlockListType, diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 284d990cb6..f180df91a7 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -9,7 +9,8 @@ use azure_core_test::{recorded, TestContext}; use azure_storage_blob::{ models::{ AccessTier, BlobClientDownloadResultHeaders, BlobClientGetPropertiesResultHeaders, - BlockListType, BlockLookupList, LeaseState, + BlobClientStartCopyFromUrlResultHeaders, BlockListType, BlockLookupList, CopyStatus, + LeaseState, }, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlockBlobClientUploadOptions, }; @@ -426,3 +427,36 @@ async fn test_set_access_tier(ctx: TestContext) -> Result<(), Box> { container_client.delete_container(None).await?; Ok(()) } + +#[recorded::test] +async fn test_start_copy_from_url(ctx: TestContext) -> Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, true).await?; + let source_blob_client = container_client.blob_client(get_blob_name(recording)); + create_test_blob(&source_blob_client).await?; + + let blob_client = container_client.blob_client("destination_blob".to_string()); + let source_url = format!( + "{}{}/{}", + source_blob_client.endpoint().as_str(), + source_blob_client.container_name(), + source_blob_client.blob_name() + ); + let response = blob_client.start_copy_from_url(&source_url, None).await?; + let (_, _, source_content) = source_blob_client.download(None).await?.deconstruct(); + let (_, _, copied_content) = blob_client.download(None).await?.deconstruct(); + + // Assert + let copy_status = response.copy_status()?; + let copy_id = response.copy_id()?; + assert_eq!(CopyStatus::Success, copy_status.unwrap()); + assert!(copy_id.is_some()); + assert_eq!( + source_content.collect().await?, + copied_content.collect().await? + ); + + container_client.delete_container(None).await?; + Ok(()) +}