From 0f99cc65f7c8a7c2d386722687e099b511e21439 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Wed, 16 Jul 2025 12:19:22 -0700 Subject: [PATCH 01/11] Some sort of SAS support --- .../src/clients/blob_client.rs | 26 ++++++++++++ .../src/generated/clients/blob_client.rs | 42 +++++++++++++++++-- .../azure_storage_blob/tests/blob_client.rs | 31 +++++++++++--- 3 files changed, 89 insertions(+), 10 deletions(-) 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 1c2564d279..afd4f340ee 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -83,6 +83,32 @@ impl BlobClient { }) } + pub fn new_no_credential( + endpoint: &str, + container_name: String, + blob_name: String, + options: Option, + ) -> Result { + let mut options = options.unwrap_or_default(); + + let storage_headers_policy = Arc::new(StorageHeadersPolicy); + options + .client_options + .per_call_policies + .push(storage_headers_policy); + + let client = GeneratedBlobClient::new_no_credential( + endpoint, + container_name.clone(), + blob_name.clone(), + Some(options), + )?; + Ok(Self { + endpoint: endpoint.parse()?, + client, + }) + } + /// Returns a new instance of AppendBlobClient. /// /// # Arguments 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 9dac2880bf..68cdbe5da7 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 @@ -102,6 +102,35 @@ impl BlobClient { }) } + pub fn new_no_credential( + endpoint: &str, + container_name: String, + blob_name: String, + options: Option, + ) -> Result { + let options = options.unwrap_or_default(); + let endpoint = Url::parse(endpoint)?; + if !endpoint.scheme().starts_with("http") { + return Err(azure_core::Error::message( + azure_core::error::ErrorKind::Other, + format!("{endpoint} must use http(s)"), + )); + } + Ok(Self { + blob_name, + container_name, + endpoint, + version: options.version, + pipeline: Pipeline::new( + option_env!("CARGO_PKG_NAME"), + option_env!("CARGO_PKG_VERSION"), + options.client_options, + Vec::default(), + Vec::default(), + ), + }) + } + /// Returns the Url associated with this client. pub fn endpoint(&self) -> &Url { &self.endpoint @@ -770,10 +799,15 @@ impl BlobClient { let options = options.unwrap_or_default(); let ctx = Context::with_context(&options.method_options.context); let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + { + let mut path_segments = url.path_segments_mut().expect("Cannot be base"); + path_segments.push(&self.container_name); + path_segments.push(&self.blob_name); + } if let Some(snapshot) = options.snapshot { url.query_pairs_mut().append_pair("snapshot", &snapshot); } diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 5d5818b2f9..225608ef39 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -6,12 +6,15 @@ use azure_core::{ Bytes, }; use azure_core_test::{recorded, TestContext}; -use azure_storage_blob::models::{ - AccessTier, BlobClientAcquireLeaseResultHeaders, BlobClientChangeLeaseResultHeaders, - BlobClientDownloadOptions, BlobClientDownloadResultHeaders, BlobClientGetPropertiesOptions, - BlobClientGetPropertiesResultHeaders, BlobClientSetMetadataOptions, - BlobClientSetPropertiesOptions, BlobClientSetTierOptions, BlockBlobClientUploadOptions, - LeaseState, +use azure_storage_blob::{ + models::{ + AccessTier, BlobClientAcquireLeaseResultHeaders, BlobClientChangeLeaseResultHeaders, + BlobClientDownloadOptions, BlobClientDownloadResultHeaders, BlobClientGetPropertiesOptions, + BlobClientGetPropertiesResultHeaders, BlobClientSetMetadataOptions, + BlobClientSetPropertiesOptions, BlobClientSetTierOptions, BlockBlobClientUploadOptions, + LeaseState, + }, + BlobClient, }; use azure_storage_blob_test::{create_test_blob, get_blob_name, get_container_client}; use std::{collections::HashMap, error::Error, time::Duration}; @@ -423,3 +426,19 @@ async fn test_leased_blob_operations(ctx: TestContext) -> Result<(), Box Result<(), Box> { + // SAS + let endpoint = "endpoint_with_sas"; + let container_name = "acontainer108f32e8"; + let blob_name = "goodbye.txt"; + let sas_blob_client = + BlobClient::new_no_credential(endpoint, container_name.into(), blob_name.into(), None)?; + + let blob_properties = sas_blob_client.get_properties(None).await?; + let content_length = blob_properties.content_length()?; + assert_eq!(11, content_length.unwrap()); + + Ok(()) +} From cdd65fbd021cf35826a4bb89e4ff046d40d6e986 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 21 Jul 2025 15:06:56 -0700 Subject: [PATCH 02/11] Refactor based on PR feedback --- .../src/clients/blob_client.rs | 36 +++++++++++++------ .../src/generated/clients/blob_client.rs | 11 ++++-- .../azure_storage_blob/tests/blob_client.rs | 8 ++--- 3 files changed, 37 insertions(+), 18 deletions(-) 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 afd4f340ee..7e12904ad7 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -83,12 +83,7 @@ impl BlobClient { }) } - pub fn new_no_credential( - endpoint: &str, - container_name: String, - blob_name: String, - options: Option, - ) -> Result { + pub fn from_blob_url(blob_url: &str, options: Option) -> Result { let mut options = options.unwrap_or_default(); let storage_headers_policy = Arc::new(StorageHeadersPolicy); @@ -97,14 +92,35 @@ impl BlobClient { .per_call_policies .push(storage_headers_policy); + // TODO: We would abstract this out to a helper that is fancier and can handle edge-cases, which Url crate may not have been designed to handle + // Parse out container name and blob name, then remove them from the path + let mut container_name = ""; + let mut blob_name = ""; + let mut url = Url::parse(blob_url)?; + // URL BEFORE: https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig + // println!("URL BEFORE: {}", url.clone()); + + let url_for_path_segments = url.clone(); + if let Some(mut segments) = url_for_path_segments.path_segments() { + container_name = segments.next().unwrap(); + blob_name = segments.next().unwrap(); + } + url.set_path(""); + // URL AFTER NULLED PATH: https://vincenttranstock.blob.core.windows.net/?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig + // println!("URL AFTER NULLED PATH: {}", url.clone()); + + // Therefore, given the current design, the endpoint passed to the generated client will have query parameters on it. let client = GeneratedBlobClient::new_no_credential( - endpoint, - container_name.clone(), - blob_name.clone(), + url.as_str(), + container_name.to_string(), + blob_name.to_string(), Some(options), )?; + + // Consequently that means currently endpoint() will have all the query parameters in it. + // We could strip it out here before saving on this client, but that would mean a mismatch of endpoint values between our client and generated. Ok(Self { - endpoint: endpoint.parse()?, + endpoint: url, client, }) } 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 68cdbe5da7..b0de2925b1 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 @@ -803,11 +803,16 @@ impl BlobClient { // path = path.replace("{blobName}", &self.blob_name); // path = path.replace("{containerName}", &self.container_name); // url = url.join(&path)?; + + // request uri before: https://vincenttranstock.blob.core.windows.net/?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig= + // println!("request uri before: {}", url); { - let mut path_segments = url.path_segments_mut().expect("Cannot be base"); - path_segments.push(&self.container_name); - path_segments.push(&self.blob_name); + url.path_segments_mut() + .expect("Cannot be base") + .extend([&self.container_name, &self.blob_name]); } + // request uri after: https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig= + // println!("request uri after: {}", url); if let Some(snapshot) = options.snapshot { url.query_pairs_mut().append_pair("snapshot", &snapshot); } diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 225608ef39..595022af60 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -430,11 +430,9 @@ async fn test_leased_blob_operations(ctx: TestContext) -> Result<(), Box Result<(), Box> { // SAS - let endpoint = "endpoint_with_sas"; - let container_name = "acontainer108f32e8"; - let blob_name = "goodbye.txt"; - let sas_blob_client = - BlobClient::new_no_credential(endpoint, container_name.into(), blob_name.into(), None)?; + let blob_url = "https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig=e7QwWlj42eY%2FqDMSdSppRqSSqlkinW00%2Bymu03LAuek%3D"; + + let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?; let blob_properties = sas_blob_client.get_properties(None).await?; let content_length = blob_properties.content_length()?; From 19338b0fdab0425bf74a0db2f8dd7cc4f6af75e3 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 21 Jul 2025 15:14:38 -0700 Subject: [PATCH 03/11] nit --- sdk/storage/azure_storage_blob/tests/blob_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 595022af60..cdff30225c 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -430,7 +430,7 @@ async fn test_leased_blob_operations(ctx: TestContext) -> Result<(), Box Result<(), Box> { // SAS - let blob_url = "https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig=e7QwWlj42eY%2FqDMSdSppRqSSqlkinW00%2Bymu03LAuek%3D"; + let blob_url = ""; let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?; From 1a674512e3430d8b9871a84fd217069c8f548d09 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Fri, 25 Jul 2025 14:11:21 -0700 Subject: [PATCH 04/11] Refactor SAS --- .../src/clients/blob_client.rs | 32 ++++++++----------- .../src/generated/clients/blob_client.rs | 2 +- sdk/storage/azure_storage_blob/src/lib.rs | 1 - .../azure_storage_blob/tests/blob_client.rs | 4 +-- 4 files changed, 17 insertions(+), 22 deletions(-) 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 7e12904ad7..9016bf0bf4 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -92,32 +92,28 @@ impl BlobClient { .per_call_policies .push(storage_headers_policy); - // TODO: We would abstract this out to a helper that is fancier and can handle edge-cases, which Url crate may not have been designed to handle - // Parse out container name and blob name, then remove them from the path - let mut container_name = ""; - let mut blob_name = ""; + // Input URL: https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig let mut url = Url::parse(blob_url)?; - // URL BEFORE: https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig - // println!("URL BEFORE: {}", url.clone()); - let url_for_path_segments = url.clone(); - if let Some(mut segments) = url_for_path_segments.path_segments() { - container_name = segments.next().unwrap(); - blob_name = segments.next().unwrap(); - } + let mut segments = url.path_segments().expect("Failed to get path segments"); + let container_name = segments + .next() + .expect("Failed to parse container_name") + .to_string(); + let blob_name = segments.collect::>().join("/"); + url.set_path(""); - // URL AFTER NULLED PATH: https://vincenttranstock.blob.core.windows.net/?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig - // println!("URL AFTER NULLED PATH: {}", url.clone()); + // After parsing & clear path: https://vincenttranstock.blob.core.windows.net/?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig - // Therefore, given the current design, the endpoint passed to the generated client will have query parameters on it. - let client = GeneratedBlobClient::new_no_credential( + // Therefore, given the current design, the endpoint passed to the generated client will have query parameters (i.e. SAS info) on it. + let client = GeneratedBlobClient::with_no_credential( url.as_str(), - container_name.to_string(), - blob_name.to_string(), + container_name, + blob_name, Some(options), )?; - // Consequently that means currently endpoint() will have all the query parameters in it. + // Consequently that means currently endpoint() will have all the query parameters (i.e. SAS info) in it. // We could strip it out here before saving on this client, but that would mean a mismatch of endpoint values between our client and generated. Ok(Self { endpoint: url, 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 b0de2925b1..3cf2d75690 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 @@ -102,7 +102,7 @@ impl BlobClient { }) } - pub fn new_no_credential( + pub fn with_no_credential( endpoint: &str, container_name: String, blob_name: String, diff --git a/sdk/storage/azure_storage_blob/src/lib.rs b/sdk/storage/azure_storage_blob/src/lib.rs index 49ae35a0f3..b177bd5f83 100644 --- a/sdk/storage/azure_storage_blob/src/lib.rs +++ b/sdk/storage/azure_storage_blob/src/lib.rs @@ -2,7 +2,6 @@ // // Licensed under the MIT License. See License.txt in the project root for license information. // Code generated by Microsoft (R) Rust Code Generator. DO NOT EDIT. - #![doc = include_str!("../README.md")] #![allow(dead_code)] #![allow(unused_imports)] diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index cdff30225c..5ff6ac9886 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -428,9 +428,9 @@ async fn test_leased_blob_operations(ctx: TestContext) -> Result<(), Box Result<(), Box> { +async fn test_sas(ctx: TestContext) -> Result<(), Box> { // SAS - let blob_url = ""; + let blob_url = ""; let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?; From 574942792bf83b8c5312cc4ca7d11dbf397fb162 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Fri, 15 Aug 2025 14:01:08 -0700 Subject: [PATCH 05/11] Comment everything out that is causing build errors --- .../src/clients/blob_client.rs | 436 ++- .../src/clients/blob_container_client.rs | 12 +- .../src/generated/clients/blob_client.rs | 3172 ++++++++--------- .../clients/blob_container_client.rs | 20 +- .../azure_storage_blob/tests/blob_client.rs | 818 ++--- .../azure_storage_blob_test/src/lib.rs | 34 +- 6 files changed, 2233 insertions(+), 2259 deletions(-) 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 7010982168..41c6da23a3 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -32,7 +32,7 @@ use std::sync::Arc; /// A client to interact with a specific Azure storage blob, although that blob may not yet exist. pub struct BlobClient { - pub(super) endpoint: Url, + pub(super) blob_url: Url, pub(super) client: GeneratedBlobClient, } @@ -69,7 +69,7 @@ impl BlobClient { Some(options), )?; Ok(Self { - endpoint: endpoint.parse()?, + blob_url: client.blob_url().clone(), client, }) } @@ -83,71 +83,51 @@ impl BlobClient { .per_call_policies .push(storage_headers_policy); - // Input URL: https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig - let mut url = Url::parse(blob_url)?; + let url = Url::parse(blob_url)?; + let client = GeneratedBlobClient::with_no_credential(url.as_str(), Some(options))?; - let mut segments = url.path_segments().expect("Failed to get path segments"); - let container_name = segments - .next() - .expect("Failed to parse container_name") - .to_string(); - let blob_name = segments.collect::>().join("/"); - - url.set_path(""); - // After parsing & clear path: https://vincenttranstock.blob.core.windows.net/?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig - - // Therefore, given the current design, the endpoint passed to the generated client will have query parameters (i.e. SAS info) on it. - let client = GeneratedBlobClient::with_no_credential( - url.as_str(), - container_name, - blob_name, - Some(options), - )?; - - // Consequently that means currently endpoint() will have all the query parameters (i.e. SAS info) in it. - // We could strip it out here before saving on this client, but that would mean a mismatch of endpoint values between our client and generated. Ok(Self { - endpoint: url, + blob_url: url, client, }) } - /// Returns a new instance of AppendBlobClient. - /// - /// # Arguments - /// - pub fn append_blob_client(&self) -> AppendBlobClient { - AppendBlobClient { - endpoint: self.client.endpoint.clone(), - client: self.client.get_append_blob_client(), - } - } - - /// Returns a new instance of BlockBlobClient. - /// - /// # Arguments - /// - pub fn block_blob_client(&self) -> BlockBlobClient { - BlockBlobClient { - endpoint: self.client.endpoint.clone(), - client: self.client.get_block_blob_client(), - } - } - - /// Returns a new instance of PageBlobClient. - /// - /// # Arguments - /// - pub fn page_blob_client(&self) -> PageBlobClient { - PageBlobClient { - endpoint: self.client.endpoint.clone(), - client: self.client.get_page_blob_client(), - } - } + // /// Returns a new instance of AppendBlobClient. + // /// + // /// # Arguments + // /// + // pub fn append_blob_client(&self) -> AppendBlobClient { + // AppendBlobClient { + // endpoint: self.client.endpoint.clone(), + // client: self.client.get_append_blob_client(), + // } + // } + + // /// Returns a new instance of BlockBlobClient. + // /// + // /// # Arguments + // /// + // pub fn block_blob_client(&self) -> BlockBlobClient { + // BlockBlobClient { + // endpoint: self.client.endpoint.clone(), + // client: self.client.get_block_blob_client(), + // } + // } + + // /// Returns a new instance of PageBlobClient. + // /// + // /// # Arguments + // /// + // pub fn page_blob_client(&self) -> PageBlobClient { + // PageBlobClient { + // endpoint: self.client.endpoint.clone(), + // client: self.client.get_page_blob_client(), + // } + // } /// Gets the endpoint of the Storage account this client is connected to. - pub fn endpoint(&self) -> &Url { - &self.endpoint + pub fn blob_url(&self) -> &Url { + &self.blob_url } /// Gets the container name of the Storage account this client is connected to. @@ -173,173 +153,173 @@ impl BlobClient { self.client.get_properties(options).await } - /// Sets system properties on the blob. - /// - /// # Arguments - /// - /// * `options` - Optional configuration for the request. - pub async fn set_properties( - &self, - options: Option>, - ) -> Result> { - self.client.set_properties(options).await - } - - /// Downloads a blob from the service, including its metadata and properties. - /// - /// * `options` - Optional configuration for the request. - pub async fn download( - &self, - options: Option>, - ) -> Result> { - self.client.download(options).await - } - - /// Creates a new blob from a data source. - /// - /// # Arguments - /// - /// * `data` - The blob data to upload. - /// * `overwrite` - Whether the blob to be uploaded should overwrite the current data. If True, `upload_blob` will overwrite the existing data. - /// If False, the operation will fail with ResourceExistsError. - /// * `content_length` - Total length of the blob data to be uploaded. - /// * `options` - Optional configuration for the request. - pub async fn upload( - &self, - data: RequestContent, - overwrite: bool, - content_length: u64, - options: Option>, - ) -> Result> { - let mut options = options.unwrap_or_default(); - - if !overwrite { - options.if_none_match = Some(String::from("*")); - } - - let block_blob_client = self.client.get_block_blob_client(); - - block_blob_client - .upload(data, content_length, Some(options)) - .await - } - - /// Sets user-defined metadata for the specified blob as one or more name-value pairs. Each call to this operation - /// replaces all existing metadata attached to the blob. To remove all metadata from the blob, call this operation with - /// no metadata headers. - /// - /// # Arguments - /// - /// * `options` - Optional configuration for the request. - pub async fn set_metadata( - &self, - options: Option>, - ) -> Result> { - self.client.set_metadata(options).await - } - - /// Deletes the blob. - /// - /// # Arguments - /// - /// * `options` - Optional configuration for the request. - pub async fn delete( - &self, - options: Option>, - ) -> Result> { - self.client.delete(options).await - } - - /// Sets the tier on a blob. Standard tiers are only applicable for Block blobs, while Premium tiers are only applicable - /// for Page blobs. - /// - /// # Arguments - /// - /// * `tier` - The tier to be set on the blob. - /// * `options` - Optional configuration for the request. - pub async fn set_tier( - &self, - tier: AccessTier, - options: Option>, - ) -> Result> { - self.client.set_tier(tier, options).await - } - - /// Requests a new lease on a blob. The lease lock duration can be 15 to 60 seconds, or can be infinite. - /// - /// # Arguments - /// - /// * `duration` - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A - /// non-infinite lease can be between 15 and 60 seconds. - /// * `options` - Optional configuration for the request. - pub async fn acquire_lease( - &self, - duration: i32, - options: Option>, - ) -> Result> { - self.client.acquire_lease(duration, options).await - } - - /// Ends a lease and ensures that another client can't acquire a new lease until the current lease - /// period has expired. - /// - /// # Arguments - /// - /// * `options` - Optional configuration for the request. - pub async fn break_lease( - &self, - options: Option>, - ) -> Result> { - self.client.break_lease(options).await - } - - /// Changes the ID of an existing lease to the proposed lease ID. - /// - /// # Arguments - /// - /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the - /// lease ID must match. - /// * `proposed_lease_id` - The proposed lease ID for the blob. - /// * `options` - Optional configuration for the request. - pub async fn change_lease( - &self, - lease_id: String, - proposed_lease_id: String, - options: Option>, - ) -> Result> { - self.client - .change_lease(lease_id, proposed_lease_id, options) - .await - } - - /// Frees the lease so that another client can immediately acquire a lease - /// against the blob as soon as the release is complete. - /// - /// # Arguments - /// - /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the - /// lease ID must match. - /// * `options` - Optional configuration for the request. - pub async fn release_lease( - &self, - lease_id: String, - options: Option>, - ) -> Result> { - self.client.release_lease(lease_id, options).await - } - - /// Renews the lease on a blob. - /// - /// # Arguments - /// - /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the - /// lease ID must match. - /// * `options` - Optional configuration for the request. - pub async fn renew_lease( - &self, - lease_id: String, - options: Option>, - ) -> Result> { - self.client.renew_lease(lease_id, options).await - } + // /// Sets system properties on the blob. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional configuration for the request. + // pub async fn set_properties( + // &self, + // options: Option>, + // ) -> Result> { + // self.client.set_properties(options).await + // } + + // /// Downloads a blob from the service, including its metadata and properties. + // /// + // /// * `options` - Optional configuration for the request. + // pub async fn download( + // &self, + // options: Option>, + // ) -> Result> { + // self.client.download(options).await + // } + + // /// Creates a new blob from a data source. + // /// + // /// # Arguments + // /// + // /// * `data` - The blob data to upload. + // /// * `overwrite` - Whether the blob to be uploaded should overwrite the current data. If True, `upload_blob` will overwrite the existing data. + // /// If False, the operation will fail with ResourceExistsError. + // /// * `content_length` - Total length of the blob data to be uploaded. + // /// * `options` - Optional configuration for the request. + // pub async fn upload( + // &self, + // data: RequestContent, + // overwrite: bool, + // content_length: u64, + // options: Option>, + // ) -> Result> { + // let mut options = options.unwrap_or_default(); + + // if !overwrite { + // options.if_none_match = Some(String::from("*")); + // } + + // let block_blob_client = self.client.get_block_blob_client(); + + // block_blob_client + // .upload(data, content_length, Some(options)) + // .await + // } + + // /// Sets user-defined metadata for the specified blob as one or more name-value pairs. Each call to this operation + // /// replaces all existing metadata attached to the blob. To remove all metadata from the blob, call this operation with + // /// no metadata headers. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional configuration for the request. + // pub async fn set_metadata( + // &self, + // options: Option>, + // ) -> Result> { + // self.client.set_metadata(options).await + // } + + // /// Deletes the blob. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional configuration for the request. + // pub async fn delete( + // &self, + // options: Option>, + // ) -> Result> { + // self.client.delete(options).await + // } + + // /// Sets the tier on a blob. Standard tiers are only applicable for Block blobs, while Premium tiers are only applicable + // /// for Page blobs. + // /// + // /// # Arguments + // /// + // /// * `tier` - The tier to be set on the blob. + // /// * `options` - Optional configuration for the request. + // pub async fn set_tier( + // &self, + // tier: AccessTier, + // options: Option>, + // ) -> Result> { + // self.client.set_tier(tier, options).await + // } + + // /// Requests a new lease on a blob. The lease lock duration can be 15 to 60 seconds, or can be infinite. + // /// + // /// # Arguments + // /// + // /// * `duration` - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + // /// non-infinite lease can be between 15 and 60 seconds. + // /// * `options` - Optional configuration for the request. + // pub async fn acquire_lease( + // &self, + // duration: i32, + // options: Option>, + // ) -> Result> { + // self.client.acquire_lease(duration, options).await + // } + + // /// Ends a lease and ensures that another client can't acquire a new lease until the current lease + // /// period has expired. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional configuration for the request. + // pub async fn break_lease( + // &self, + // options: Option>, + // ) -> Result> { + // self.client.break_lease(options).await + // } + + // /// Changes the ID of an existing lease to the proposed lease ID. + // /// + // /// # Arguments + // /// + // /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + // /// lease ID must match. + // /// * `proposed_lease_id` - The proposed lease ID for the blob. + // /// * `options` - Optional configuration for the request. + // pub async fn change_lease( + // &self, + // lease_id: String, + // proposed_lease_id: String, + // options: Option>, + // ) -> Result> { + // self.client + // .change_lease(lease_id, proposed_lease_id, options) + // .await + // } + + // /// Frees the lease so that another client can immediately acquire a lease + // /// against the blob as soon as the release is complete. + // /// + // /// # Arguments + // /// + // /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + // /// lease ID must match. + // /// * `options` - Optional configuration for the request. + // pub async fn release_lease( + // &self, + // lease_id: String, + // options: Option>, + // ) -> Result> { + // self.client.release_lease(lease_id, options).await + // } + + // /// Renews the lease on a blob. + // /// + // /// # Arguments + // /// + // /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + // /// lease ID must match. + // /// * `options` - Optional configuration for the request. + // pub async fn renew_lease( + // &self, + // lease_id: String, + // options: Option>, + // ) -> Result> { + // self.client.renew_lease(lease_id, options).await + // } } diff --git a/sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs b/sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs index 312a85c08a..f4efd825a9 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs @@ -76,12 +76,12 @@ impl BlobContainerClient { /// # Arguments /// /// * `blob_name` - The name of the blob. - pub fn blob_client(&self, blob_name: String) -> BlobClient { - BlobClient { - endpoint: self.client.endpoint.clone(), - client: self.client.get_blob_client(blob_name), - } - } + // pub fn blob_client(&self, blob_name: String) -> BlobClient { + // BlobClient { + // endpoint: self.client.endpoint.clone(), + // client: self.client.get_blob_client(blob_name), + // } + // } /// Gets the endpoint of the Storage account this client is connected to. pub fn endpoint(&self) -> &Url { 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 896f947ad1..afdf608f8b 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 @@ -40,11 +40,10 @@ use azure_core::{ }; use std::sync::Arc; -#[tracing::client] pub struct BlobClient { pub(crate) blob_name: String, pub(crate) container_name: String, - pub(crate) endpoint: Url, + pub(crate) blob_url: Url, pub(crate) pipeline: Pipeline, pub(crate) version: String, } @@ -69,7 +68,6 @@ impl BlobClient { /// * `container_name` - The name of the container. /// * `blob_name` - The name of the blob. /// * `options` - Optional configuration for the client. - #[tracing::new("azure_storage_blob")] pub fn new( endpoint: &str, credential: Arc, @@ -85,7 +83,13 @@ impl BlobClient { format!("{endpoint} must use http(s)"), )); } - endpoint.set_query(None); + + // In regular ctor, since struct now holds blob_url, we have to build the blob_url given endpoint + container_name + blob_name + endpoint + .path_segments_mut() + .expect("Cannot be base") + .extend([&container_name, &blob_name]); + let auth_policy: Arc = Arc::new(BearerTokenCredentialPolicy::new( credential, vec!["https://storage.azure.com/.default"], @@ -93,7 +97,7 @@ impl BlobClient { Ok(Self { blob_name, container_name, - endpoint, + blob_url: endpoint, version: options.version, pipeline: Pipeline::new( option_env!("CARGO_PKG_NAME"), @@ -105,24 +109,29 @@ impl BlobClient { }) } - pub fn with_no_credential( - endpoint: &str, - container_name: String, - blob_name: String, - options: Option, - ) -> Result { + pub fn with_no_credential(blob_url: &str, options: Option) -> Result { let options = options.unwrap_or_default(); - let endpoint = Url::parse(endpoint)?; - if !endpoint.scheme().starts_with("http") { + let blob_url = Url::parse(blob_url)?; + if !blob_url.scheme().starts_with("http") { return Err(azure_core::Error::message( azure_core::error::ErrorKind::Other, - format!("{endpoint} must use http(s)"), + format!("{blob_url} must use http(s)"), )); } + + let mut segments = blob_url + .path_segments() + .expect("Failed to get path segments"); + let container_name = segments + .next() + .expect("Failed to parse container_name") + .to_string(); + let blob_name = segments.collect::>().join("/"); + Ok(Self { blob_name, container_name, - endpoint, + blob_url, version: options.version, pipeline: Pipeline::new( option_env!("CARGO_PKG_NAME"), @@ -135,292 +144,798 @@ impl BlobClient { } /// Returns the Url associated with this client. - pub fn endpoint(&self) -> &Url { - &self.endpoint + pub fn blob_url(&self) -> &Url { + &self.blob_url } - /// The Abort Copy From URL operation aborts a pending Copy From URL operation, and leaves a destination blob with zero length - /// and full metadata. - /// - /// # Arguments - /// - /// * `copy_id` - The copy identifier provided in the x-ms-copy-id header of the original Copy Blob operation. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.abortCopyFromUrl")] - pub async fn abort_copy_from_url( - &self, - copy_id: &str, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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") - .append_key_only("copyid"); - url.query_pairs_mut().append_pair("copyid", copy_id); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - request.insert_header("x-ms-copy-action", "abort"); - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Abort Copy From URL operation aborts a pending Copy From URL operation, and leaves a destination blob with zero length + // /// and full metadata. + // /// + // /// # Arguments + // /// + // /// * `copy_id` - The copy identifier provided in the x-ms-copy-id header of the original Copy Blob operation. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.abortCopyFromUrl")] + // pub async fn abort_copy_from_url( + // &self, + // copy_id: &str, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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") + // .append_key_only("copyid"); + // url.query_pairs_mut().append_pair("copyid", copy_id); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // request.insert_header("x-ms-copy-action", "abort"); + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// The Acquire Lease operation requests a new lease on a blob. The lease lock duration can be 15 to 60 seconds, or can be - /// infinite. - /// - /// # Arguments - /// - /// * `duration` - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A - /// non-infinite lease can be between 15 and 60 seconds. A lease duration cannot be changed using renew or change. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.acquireLease")] - pub async fn acquire_lease( - &self, - duration: i32, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - url.query_pairs_mut() - .append_key_only("acquire") - .append_pair("comp", "lease"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - request.insert_header("x-ms-lease-action", "acquire"); - request.insert_header("x-ms-lease-duration", duration.to_string()); - if let Some(proposed_lease_id) = options.proposed_lease_id { - request.insert_header("x-ms-proposed-lease-id", proposed_lease_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Acquire Lease operation requests a new lease on a blob. The lease lock duration can be 15 to 60 seconds, or can be + // /// infinite. + // /// + // /// # Arguments + // /// + // /// * `duration` - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + // /// non-infinite lease can be between 15 and 60 seconds. A lease duration cannot be changed using renew or change. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.acquireLease")] + // pub async fn acquire_lease( + // &self, + // duration: i32, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + // url.query_pairs_mut() + // .append_key_only("acquire") + // .append_pair("comp", "lease"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // request.insert_header("x-ms-lease-action", "acquire"); + // request.insert_header("x-ms-lease-duration", duration.to_string()); + // if let Some(proposed_lease_id) = options.proposed_lease_id { + // request.insert_header("x-ms-proposed-lease-id", proposed_lease_id); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// The Break Lease operation ends a lease and ensures that another client can't acquire a new lease until the current lease - /// period has expired. - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.breakLease")] - pub async fn break_lease( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - url.query_pairs_mut() - .append_key_only("break") - .append_pair("comp", "lease"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - request.insert_header("x-ms-lease-action", "break"); - if let Some(break_period) = options.break_period { - request.insert_header("x-ms-lease-break-period", break_period.to_string()); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Break Lease operation ends a lease and ensures that another client can't acquire a new lease until the current lease + // /// period has expired. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.breakLease")] + // pub async fn break_lease( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + // url.query_pairs_mut() + // .append_key_only("break") + // .append_pair("comp", "lease"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // request.insert_header("x-ms-lease-action", "break"); + // if let Some(break_period) = options.break_period { + // request.insert_header("x-ms-lease-break-period", break_period.to_string()); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// The Change Lease operation is used to change the ID of an existing lease. - /// - /// # Arguments - /// - /// * `lease_id` - Required. A lease ID for the source path. If specified, the source path must have an active lease and the - /// lease ID must match. - /// * `proposed_lease_id` - Required. The proposed lease ID for the container. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.changeLease")] - pub async fn change_lease( - &self, - lease_id: String, - proposed_lease_id: String, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - url.query_pairs_mut() - .append_key_only("change") - .append_pair("comp", "lease"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - request.insert_header("x-ms-lease-action", "change"); - request.insert_header("x-ms-lease-id", lease_id); - request.insert_header("x-ms-proposed-lease-id", proposed_lease_id); - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Change Lease operation is used to change the ID of an existing lease. + // /// + // /// # Arguments + // /// + // /// * `lease_id` - Required. A lease ID for the source path. If specified, the source path must have an active lease and the + // /// lease ID must match. + // /// * `proposed_lease_id` - Required. The proposed lease ID for the container. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.changeLease")] + // pub async fn change_lease( + // &self, + // lease_id: String, + // proposed_lease_id: String, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + // url.query_pairs_mut() + // .append_key_only("change") + // .append_pair("comp", "lease"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // request.insert_header("x-ms-lease-action", "change"); + // request.insert_header("x-ms-lease-id", lease_id); + // request.insert_header("x-ms-proposed-lease-id", proposed_lease_id); + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } + + // /// The Copy From URL operation copies a blob or an internet resource to a new blob. It will not return a response until the + // /// copy is complete. + // /// + // /// # Arguments + // /// + // /// * `copy_source` - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that + // /// specifies a page blob snapshot. The value should be URL-encoded as it would appear in a request URI. The source blob must + // /// either be public or must be authenticated via a shared access signature. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.copyFromUrl")] + // pub async fn copy_from_url( + // &self, + // copy_source: String, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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") + // .append_key_only("sync"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(tier) = options.tier { + // request.insert_header("x-ms-access-tier", tier.to_string()); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // request.insert_header("x-ms-copy-source", copy_source); + // if let Some(copy_source_authorization) = options.copy_source_authorization { + // request.insert_header("x-ms-copy-source-authorization", copy_source_authorization); + // } + // if let Some(copy_source_tags) = options.copy_source_tags { + // request.insert_header("x-ms-copy-source-tag-option", copy_source_tags.to_string()); + // } + // if let Some(encryption_scope) = options.encryption_scope { + // request.insert_header("x-ms-encryption-scope", encryption_scope); + // } + // if let Some(file_request_intent) = options.file_request_intent { + // request.insert_header("x-ms-file-request-intent", file_request_intent.to_string()); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(immutability_policy_mode) = options.immutability_policy_mode { + // request.insert_header( + // "x-ms-immutability-policy-mode", + // immutability_policy_mode.to_string(), + // ); + // } + // if let Some(immutability_policy_expiry) = options.immutability_policy_expiry { + // request.insert_header( + // "x-ms-immutability-policy-until-date", + // to_rfc7231(&immutability_policy_expiry), + // ); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // if let Some(legal_hold) = options.legal_hold { + // request.insert_header("x-ms-legal-hold", legal_hold.to_string()); + // } + // if let Some(metadata) = options.metadata { + // for (k, v) in &metadata { + // request.insert_header(format!("x-ms-meta-{k}"), v); + // } + // } + // request.insert_header("x-ms-requires-sync", "true"); + // if let Some(source_content_md5) = options.source_content_md5 { + // request.insert_header("x-ms-source-content-md5", encode(source_content_md5)); + // } + // if let Some(source_if_match) = options.source_if_match { + // request.insert_header("x-ms-source-if-match", source_if_match); + // } + // if let Some(source_if_modified_since) = options.source_if_modified_since { + // request.insert_header( + // "x-ms-source-if-modified-since", + // to_rfc7231(&source_if_modified_since), + // ); + // } + // if let Some(source_if_none_match) = options.source_if_none_match { + // request.insert_header("x-ms-source-if-none-match", source_if_none_match); + // } + // if let Some(source_if_unmodified_since) = options.source_if_unmodified_since { + // request.insert_header( + // "x-ms-source-if-unmodified-since", + // to_rfc7231(&source_if_unmodified_since), + // ); + // } + // if let Some(blob_tags_string) = options.blob_tags_string { + // request.insert_header("x-ms-tags", blob_tags_string); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } + + // /// The Create Snapshot operation creates a read-only snapshot of a blob + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.createSnapshot")] + // pub async fn create_snapshot( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "snapshot"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(encryption_algorithm) = options.encryption_algorithm { + // request.insert_header( + // "x-ms-encryption-algorithm", + // encryption_algorithm.to_string(), + // ); + // } + // if let Some(encryption_key) = options.encryption_key { + // request.insert_header("x-ms-encryption-key", encryption_key); + // } + // if let Some(encryption_key_sha256) = options.encryption_key_sha256 { + // request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); + // } + // if let Some(encryption_scope) = options.encryption_scope { + // request.insert_header("x-ms-encryption-scope", encryption_scope); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // if let Some(metadata) = options.metadata { + // for (k, v) in &metadata { + // request.insert_header(format!("x-ms-meta-{k}"), v); + // } + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } + + // /// If the storage account's soft delete feature is disabled then, when a blob is deleted, it is permanently removed from + // /// the storage account. If the storage account's soft delete feature is enabled, then, when a blob is deleted, it is marked + // /// for deletion and becomes inaccessible immediately. However, the blob service retains the blob or snapshot for the number + // /// of days specified by the DeleteRetentionPolicy section of [Storage service properties] (Set-Blob-Service-Properties.md). + // /// After the specified number of days has passed, the blob's data is permanently removed from the storage account. Note that + // /// you continue to be charged for the soft-deleted blob's storage until it is permanently removed. Use the List Blobs API + // /// and specify the \"include=deleted\" query parameter to discover which blobs and snapshots have been soft deleted. You + // /// can then use the Undelete Blob API to restore a soft-deleted blob. All other operations on a soft-deleted blob or snapshot + // /// causes the service to return an HTTP status code of 404 (ResourceNotFound). + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.delete")] + // pub async fn delete( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + // if let Some(blob_delete_type) = options.blob_delete_type { + // url.query_pairs_mut() + // .append_pair("deletetype", blob_delete_type.as_ref()); + // } + // if let Some(snapshot) = options.snapshot { + // url.query_pairs_mut().append_pair("snapshot", &snapshot); + // } + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // if let Some(version_id) = options.version_id { + // url.query_pairs_mut().append_pair("versionid", &version_id); + // } + // let mut request = Request::new(url, Method::Delete); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(delete_snapshots) = options.delete_snapshots { + // request.insert_header("x-ms-delete-snapshots", delete_snapshots.to_string()); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// The Copy From URL operation copies a blob or an internet resource to a new blob. It will not return a response until the - /// copy is complete. + // /// The Delete Immutability Policy operation deletes the immutability policy on the blob. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.deleteImmutabilityPolicy")] + // pub async fn delete_immutability_policy( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "immutabilityPolicies"); + // if let Some(snapshot) = options.snapshot { + // url.query_pairs_mut().append_pair("snapshot", &snapshot); + // } + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // if let Some(version_id) = options.version_id { + // url.query_pairs_mut().append_pair("versionid", &version_id); + // } + // let mut request = Request::new(url, Method::Delete); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } + + // /// The Download operation reads or downloads a blob from the system, including its metadata and properties. You can also + // /// call Download to read a snapshot. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.download")] + // pub async fn download( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + // if let Some(snapshot) = options.snapshot { + // url.query_pairs_mut().append_pair("snapshot", &snapshot); + // } + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // if let Some(version_id) = options.version_id { + // url.query_pairs_mut().append_pair("versionid", &version_id); + // } + // let mut request = Request::new(url, Method::Get); + // request.insert_header("accept", "application/octet-stream"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(range) = options.range { + // request.insert_header("range", range); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(encryption_algorithm) = options.encryption_algorithm { + // request.insert_header( + // "x-ms-encryption-algorithm", + // encryption_algorithm.to_string(), + // ); + // } + // if let Some(encryption_key) = options.encryption_key { + // request.insert_header("x-ms-encryption-key", encryption_key); + // } + // if let Some(encryption_key_sha256) = options.encryption_key_sha256 { + // request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // if let Some(range_get_content_crc64) = options.range_get_content_crc64 { + // request.insert_header( + // "x-ms-range-get-content-crc64", + // range_get_content_crc64.to_string(), + // ); + // } + // if let Some(range_get_content_md5) = options.range_get_content_md5 { + // request.insert_header( + // "x-ms-range-get-content-md5", + // range_get_content_md5.to_string(), + // ); + // } + // if let Some(structured_body_type) = options.structured_body_type { + // request.insert_header("x-ms-structured-body", structured_body_type); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } + + // /// Returns the sku name and account kind + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.getAccountInfo")] + // pub async fn get_account_info( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + // url.query_pairs_mut() + // .append_key_only("blob") + // .append_pair("comp", "properties") + // .append_pair("restype", "account"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Get); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } + + /// Returns a new instance of AppendBlobClient. + // #[tracing::subclient] + // pub fn get_append_blob_client(&self) -> AppendBlobClient { + // AppendBlobClient { + // blob_name: self.blob_name.clone(), + // container_name: self.container_name.clone(), + // endpoint: self.endpoint.clone(), + // pipeline: self.pipeline.clone(), + // version: self.version.clone(), + // } + // } + + // /// Returns a new instance of BlockBlobClient. + // #[tracing::subclient] + // pub fn get_block_blob_client(&self) -> BlockBlobClient { + // BlockBlobClient { + // blob_name: self.blob_name.clone(), + // container_name: self.container_name.clone(), + // endpoint: self.endpoint.clone(), + // pipeline: self.pipeline.clone(), + // version: self.version.clone(), + // } + // } + + // /// Returns a new instance of PageBlobClient. + // #[tracing::subclient] + // pub fn get_page_blob_client(&self) -> PageBlobClient { + // PageBlobClient { + // blob_name: self.blob_name.clone(), + // container_name: self.container_name.clone(), + // endpoint: self.endpoint.clone(), + // pipeline: self.pipeline.clone(), + // version: self.version.clone(), + // } + // } + + /// The Get Properties operation returns all user-defined metadata, standard HTTP properties, and system properties for the + /// blob. It does not return the content of the blob. /// /// # Arguments /// - /// * `copy_source` - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that - /// specifies a page blob snapshot. The value should be URL-encoded as it would appear in a request URI. The source blob must - /// either be public or must be authenticated via a shared access signature. /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.copyFromUrl")] - pub async fn copy_from_url( + pub async fn get_properties( &self, - copy_source: String, - options: Option>, - ) -> Result> { + options: Option>, + ) -> Result> { let options = options.unwrap_or_default(); let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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") - .append_key_only("sync"); + let mut url = self.blob_url.clone(); + if let Some(snapshot) = options.snapshot { + url.query_pairs_mut().append_pair("snapshot", &snapshot); + } if let Some(timeout) = options.timeout { url.query_pairs_mut() .append_pair("timeout", &timeout.to_string()); } - let mut request = Request::new(url, Method::Put); + if let Some(version_id) = options.version_id { + url.query_pairs_mut().append_pair("versionid", &version_id); + } + let mut request = Request::new(url, Method::Head); request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); if let Some(if_match) = options.if_match { request.insert_header("if-match", if_match); } @@ -433,76 +948,27 @@ impl BlobClient { if let Some(if_unmodified_since) = options.if_unmodified_since { request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); } - if let Some(tier) = options.tier { - request.insert_header("x-ms-access-tier", tier.to_string()); - } if let Some(client_request_id) = options.client_request_id { request.insert_header("x-ms-client-request-id", client_request_id); } - request.insert_header("x-ms-copy-source", copy_source); - if let Some(copy_source_authorization) = options.copy_source_authorization { - request.insert_header("x-ms-copy-source-authorization", copy_source_authorization); - } - if let Some(copy_source_tags) = options.copy_source_tags { - request.insert_header("x-ms-copy-source-tag-option", copy_source_tags.to_string()); + if let Some(encryption_algorithm) = options.encryption_algorithm { + request.insert_header( + "x-ms-encryption-algorithm", + encryption_algorithm.to_string(), + ); } - if let Some(encryption_scope) = options.encryption_scope { - request.insert_header("x-ms-encryption-scope", encryption_scope); + if let Some(encryption_key) = options.encryption_key { + request.insert_header("x-ms-encryption-key", encryption_key); } - if let Some(file_request_intent) = options.file_request_intent { - request.insert_header("x-ms-file-request-intent", file_request_intent.to_string()); + if let Some(encryption_key_sha256) = options.encryption_key_sha256 { + request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); } if let Some(if_tags) = options.if_tags { request.insert_header("x-ms-if-tags", if_tags); } - if let Some(immutability_policy_mode) = options.immutability_policy_mode { - request.insert_header( - "x-ms-immutability-policy-mode", - immutability_policy_mode.to_string(), - ); - } - if let Some(immutability_policy_expiry) = options.immutability_policy_expiry { - request.insert_header( - "x-ms-immutability-policy-until-date", - to_rfc7231(&immutability_policy_expiry), - ); - } if let Some(lease_id) = options.lease_id { request.insert_header("x-ms-lease-id", lease_id); } - if let Some(legal_hold) = options.legal_hold { - request.insert_header("x-ms-legal-hold", legal_hold.to_string()); - } - if let Some(metadata) = options.metadata { - for (k, v) in &metadata { - request.insert_header(format!("x-ms-meta-{k}"), v); - } - } - request.insert_header("x-ms-requires-sync", "true"); - if let Some(source_content_md5) = options.source_content_md5 { - request.insert_header("x-ms-source-content-md5", encode(source_content_md5)); - } - if let Some(source_if_match) = options.source_if_match { - request.insert_header("x-ms-source-if-match", source_if_match); - } - if let Some(source_if_modified_since) = options.source_if_modified_since { - request.insert_header( - "x-ms-source-if-modified-since", - to_rfc7231(&source_if_modified_since), - ); - } - if let Some(source_if_none_match) = options.source_if_none_match { - request.insert_header("x-ms-source-if-none-match", source_if_none_match); - } - if let Some(source_if_unmodified_since) = options.source_if_unmodified_since { - request.insert_header( - "x-ms-source-if-unmodified-since", - to_rfc7231(&source_if_unmodified_since), - ); - } - if let Some(blob_tags_string) = options.blob_tags_string { - request.insert_header("x-ms-tags", blob_tags_string); - } request.insert_header("x-ms-version", &self.version); let rsp = self.pipeline.send(&ctx, &mut request).await?; if !rsp.status().is_success() { @@ -517,1265 +983,793 @@ impl BlobClient { Ok(rsp.into()) } - /// The Create Snapshot operation creates a read-only snapshot of a blob - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.createSnapshot")] - pub async fn create_snapshot( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "snapshot"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(encryption_algorithm) = options.encryption_algorithm { - request.insert_header( - "x-ms-encryption-algorithm", - encryption_algorithm.to_string(), - ); - } - if let Some(encryption_key) = options.encryption_key { - request.insert_header("x-ms-encryption-key", encryption_key); - } - if let Some(encryption_key_sha256) = options.encryption_key_sha256 { - request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); - } - if let Some(encryption_scope) = options.encryption_scope { - request.insert_header("x-ms-encryption-scope", encryption_scope); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - if let Some(metadata) = options.metadata { - for (k, v) in &metadata { - request.insert_header(format!("x-ms-meta-{k}"), v); - } - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Get Blob Tags operation enables users to get tags on a blob. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.getTags")] + // pub async fn get_tags( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "tags"); + // if let Some(snapshot) = options.snapshot { + // url.query_pairs_mut().append_pair("snapshot", &snapshot); + // } + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // if let Some(version_id) = options.version_id { + // url.query_pairs_mut().append_pair("versionid", &version_id); + // } + // let mut request = Request::new(url, Method::Get); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// If the storage account's soft delete feature is disabled then, when a blob is deleted, it is permanently removed from - /// the storage account. If the storage account's soft delete feature is enabled, then, when a blob is deleted, it is marked - /// for deletion and becomes inaccessible immediately. However, the blob service retains the blob or snapshot for the number - /// of days specified by the DeleteRetentionPolicy section of [Storage service properties] (Set-Blob-Service-Properties.md). - /// After the specified number of days has passed, the blob's data is permanently removed from the storage account. Note that - /// you continue to be charged for the soft-deleted blob's storage until it is permanently removed. Use the List Blobs API - /// and specify the \"include=deleted\" query parameter to discover which blobs and snapshots have been soft deleted. You - /// can then use the Undelete Blob API to restore a soft-deleted blob. All other operations on a soft-deleted blob or snapshot - /// causes the service to return an HTTP status code of 404 (ResourceNotFound). - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.delete")] - pub async fn delete( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - if let Some(blob_delete_type) = options.blob_delete_type { - url.query_pairs_mut() - .append_pair("deletetype", blob_delete_type.as_ref()); - } - if let Some(snapshot) = options.snapshot { - url.query_pairs_mut().append_pair("snapshot", &snapshot); - } - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Delete); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(delete_snapshots) = options.delete_snapshots { - request.insert_header("x-ms-delete-snapshots", delete_snapshots.to_string()); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Release Lease operation frees the lease if it's no longer needed, so that another client can immediately acquire a + // /// lease against the blob. + // /// + // /// # Arguments + // /// + // /// * `lease_id` - Required. A lease ID for the source path. If specified, the source path must have an active lease and the + // /// lease ID must match. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.releaseLease")] + // pub async fn release_lease( + // &self, + // lease_id: String, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "lease") + // .append_key_only("release"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // request.insert_header("x-ms-lease-action", "release"); + // request.insert_header("x-ms-lease-id", lease_id); + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// The Delete Immutability Policy operation deletes the immutability policy on the blob. - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.deleteImmutabilityPolicy")] - pub async fn delete_immutability_policy( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "immutabilityPolicies"); - if let Some(snapshot) = options.snapshot { - url.query_pairs_mut().append_pair("snapshot", &snapshot); - } - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Delete); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Renew Lease operation renews an existing lease. + // /// + // /// # Arguments + // /// + // /// * `lease_id` - Required. A lease ID for the source path. If specified, the source path must have an active lease and the + // /// lease ID must match. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.renewLease")] + // pub async fn renew_lease( + // &self, + // lease_id: String, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "lease") + // .append_key_only("renew"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // request.insert_header("x-ms-lease-action", "renew"); + // request.insert_header("x-ms-lease-id", lease_id); + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// The Download operation reads or downloads a blob from the system, including its metadata and properties. You can also - /// call Download to read a snapshot. - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.download")] - pub async fn download( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - if let Some(snapshot) = options.snapshot { - url.query_pairs_mut().append_pair("snapshot", &snapshot); - } - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Get); - request.insert_header("accept", "application/octet-stream"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(range) = options.range { - request.insert_header("range", range); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(encryption_algorithm) = options.encryption_algorithm { - request.insert_header( - "x-ms-encryption-algorithm", - encryption_algorithm.to_string(), - ); - } - if let Some(encryption_key) = options.encryption_key { - request.insert_header("x-ms-encryption-key", encryption_key); - } - if let Some(encryption_key_sha256) = options.encryption_key_sha256 { - request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - if let Some(range_get_content_crc64) = options.range_get_content_crc64 { - request.insert_header( - "x-ms-range-get-content-crc64", - range_get_content_crc64.to_string(), - ); - } - if let Some(range_get_content_md5) = options.range_get_content_md5 { - request.insert_header( - "x-ms-range-get-content-md5", - range_get_content_md5.to_string(), - ); - } - if let Some(structured_body_type) = options.structured_body_type { - request.insert_header("x-ms-structured-body", structured_body_type); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// Set the expiration time of a blob + // /// + // /// # Arguments + // /// + // /// * `expiry_options` - Required. Indicates mode of the expiry time + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.setExpiry")] + // pub async fn set_expiry( + // &self, + // expiry_options: BlobExpiryOptions, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "expiry"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // request.insert_header("x-ms-expiry-option", expiry_options.to_string()); + // if let Some(expires_on) = options.expires_on { + // request.insert_header("x-ms-expiry-time", to_rfc7231(&expires_on)); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// Returns the sku name and account kind - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.getAccountInfo")] - pub async fn get_account_info( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - url.query_pairs_mut() - .append_key_only("blob") - .append_pair("comp", "properties") - .append_pair("restype", "account"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Get); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// Set the immutability policy of a blob + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.setImmutabilityPolicy")] + // pub async fn set_immutability_policy( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "immutabilityPolicies"); + // if let Some(snapshot) = options.snapshot { + // url.query_pairs_mut().append_pair("snapshot", &snapshot); + // } + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // if let Some(version_id) = options.version_id { + // url.query_pairs_mut().append_pair("versionid", &version_id); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(immutability_policy_mode) = options.immutability_policy_mode { + // request.insert_header( + // "x-ms-immutability-policy-mode", + // immutability_policy_mode.to_string(), + // ); + // } + // if let Some(immutability_policy_expiry) = options.immutability_policy_expiry { + // request.insert_header( + // "x-ms-immutability-policy-until-date", + // to_rfc7231(&immutability_policy_expiry), + // ); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// Returns a new instance of AppendBlobClient. - #[tracing::subclient] - pub fn get_append_blob_client(&self) -> AppendBlobClient { - AppendBlobClient { - blob_name: self.blob_name.clone(), - container_name: self.container_name.clone(), - endpoint: self.endpoint.clone(), - pipeline: self.pipeline.clone(), - version: self.version.clone(), - } - } + // /// The Set Legal Hold operation sets a legal hold on the blob. + // /// + // /// # Arguments + // /// + // /// * `legal_hold` - Required. Specifies the legal hold status to set on the blob. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.setLegalHold")] + // pub async fn set_legal_hold( + // &self, + // legal_hold: bool, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "legalhold"); + // if let Some(snapshot) = options.snapshot { + // url.query_pairs_mut().append_pair("snapshot", &snapshot); + // } + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // if let Some(version_id) = options.version_id { + // url.query_pairs_mut().append_pair("versionid", &version_id); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // request.insert_header("x-ms-legal-hold", legal_hold.to_string()); + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// Returns a new instance of BlockBlobClient. - #[tracing::subclient] - pub fn get_block_blob_client(&self) -> BlockBlobClient { - BlockBlobClient { - blob_name: self.blob_name.clone(), - container_name: self.container_name.clone(), - endpoint: self.endpoint.clone(), - pipeline: self.pipeline.clone(), - version: self.version.clone(), - } - } + // /// The Set Metadata operation sets user-defined metadata for the specified blob as one or more name-value pairs. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.setMetadata")] + // pub async fn set_metadata( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "metadata"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(encryption_algorithm) = options.encryption_algorithm { + // request.insert_header( + // "x-ms-encryption-algorithm", + // encryption_algorithm.to_string(), + // ); + // } + // if let Some(encryption_key) = options.encryption_key { + // request.insert_header("x-ms-encryption-key", encryption_key); + // } + // if let Some(encryption_key_sha256) = options.encryption_key_sha256 { + // request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); + // } + // if let Some(encryption_scope) = options.encryption_scope { + // request.insert_header("x-ms-encryption-scope", encryption_scope); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // if let Some(metadata) = options.metadata { + // for (k, v) in &metadata { + // request.insert_header(format!("x-ms-meta-{k}"), v); + // } + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// Returns a new instance of PageBlobClient. - #[tracing::subclient] - pub fn get_page_blob_client(&self) -> PageBlobClient { - PageBlobClient { - blob_name: self.blob_name.clone(), - container_name: self.container_name.clone(), - endpoint: self.endpoint.clone(), - pipeline: self.pipeline.clone(), - version: self.version.clone(), - } - } + // /// The Set HTTP Headers operation sets system properties on the blob. + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.setProperties")] + // pub async fn set_properties( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + // url.query_pairs_mut() + // .append_key_only("SetHTTPHeaders") + // .append_pair("comp", "properties"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(blob_cache_control) = options.blob_cache_control { + // request.insert_header("x-ms-blob-cache-control", blob_cache_control); + // } + // if let Some(blob_content_disposition) = options.blob_content_disposition { + // request.insert_header("x-ms-blob-content-disposition", blob_content_disposition); + // } + // if let Some(blob_content_encoding) = options.blob_content_encoding { + // request.insert_header("x-ms-blob-content-encoding", blob_content_encoding); + // } + // if let Some(blob_content_language) = options.blob_content_language { + // request.insert_header("x-ms-blob-content-language", blob_content_language); + // } + // if let Some(blob_content_md5) = options.blob_content_md5 { + // request.insert_header("x-ms-blob-content-md5", encode(blob_content_md5)); + // } + // if let Some(blob_content_type) = options.blob_content_type { + // request.insert_header("x-ms-blob-content-type", blob_content_type); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// The Get Properties operation returns all user-defined metadata, standard HTTP properties, and system properties for the - /// blob. It does not return the content of the blob. - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.getProperties")] - pub async fn get_properties( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - // let mut path = String::from("{containerName}/{blobName}"); - // path = path.replace("{blobName}", &self.blob_name); - // path = path.replace("{containerName}", &self.container_name); - // url = url.join(&path)?; + // /// The Set Tags operation enables users to set tags on a blob. + // /// + // /// # Arguments + // /// + // /// * `tags` - The blob tags. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.setTags")] + // pub async fn set_tags( + // &self, + // tags: RequestContent, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "tags"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // if let Some(version_id) = options.version_id { + // url.query_pairs_mut().append_pair("versionid", &version_id); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // if let Some(transactional_content_md5) = options.transactional_content_md5 { + // request.insert_header("content-md5", encode(transactional_content_md5)); + // } + // request.insert_header("content-type", "application/xml"); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(transactional_content_crc64) = options.transactional_content_crc64 { + // request.insert_header("x-ms-content-crc64", encode(transactional_content_crc64)); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // request.insert_header("x-ms-version", &self.version); + // request.set_body(tags); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - // request uri before: https://vincenttranstock.blob.core.windows.net/?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig= - // println!("request uri before: {}", url); - { - url.path_segments_mut() - .expect("Cannot be base") - .extend([&self.container_name, &self.blob_name]); - } - // request uri after: https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig= - // println!("request uri after: {}", url); - if let Some(snapshot) = options.snapshot { - url.query_pairs_mut().append_pair("snapshot", &snapshot); - } - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Head); - request.insert_header("accept", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(encryption_algorithm) = options.encryption_algorithm { - request.insert_header( - "x-ms-encryption-algorithm", - encryption_algorithm.to_string(), - ); - } - if let Some(encryption_key) = options.encryption_key { - request.insert_header("x-ms-encryption-key", encryption_key); - } - if let Some(encryption_key_sha256) = options.encryption_key_sha256 { - request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// The Get Blob Tags operation enables users to get tags on a blob. - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.getTags")] - pub async fn get_tags( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "tags"); - if let Some(snapshot) = options.snapshot { - url.query_pairs_mut().append_pair("snapshot", &snapshot); - } - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Get); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// The Release Lease operation frees the lease if it's no longer needed, so that another client can immediately acquire a - /// lease against the blob. - /// - /// # Arguments - /// - /// * `lease_id` - Required. A lease ID for the source path. If specified, the source path must have an active lease and the - /// lease ID must match. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.releaseLease")] - pub async fn release_lease( - &self, - lease_id: String, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "lease") - .append_key_only("release"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - request.insert_header("x-ms-lease-action", "release"); - request.insert_header("x-ms-lease-id", lease_id); - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// The Renew Lease operation renews an existing lease. - /// - /// # Arguments - /// - /// * `lease_id` - Required. A lease ID for the source path. If specified, the source path must have an active lease and the - /// lease ID must match. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.renewLease")] - pub async fn renew_lease( - &self, - lease_id: String, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "lease") - .append_key_only("renew"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - request.insert_header("x-ms-lease-action", "renew"); - request.insert_header("x-ms-lease-id", lease_id); - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// Set the expiration time of a blob - /// - /// # Arguments - /// - /// * `expiry_options` - Required. Indicates mode of the expiry time - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.setExpiry")] - pub async fn set_expiry( - &self, - expiry_options: BlobExpiryOptions, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "expiry"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - request.insert_header("x-ms-expiry-option", expiry_options.to_string()); - if let Some(expires_on) = options.expires_on { - request.insert_header("x-ms-expiry-time", to_rfc7231(&expires_on)); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// Set the immutability policy of a blob - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.setImmutabilityPolicy")] - pub async fn set_immutability_policy( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "immutabilityPolicies"); - if let Some(snapshot) = options.snapshot { - url.query_pairs_mut().append_pair("snapshot", &snapshot); - } - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(immutability_policy_mode) = options.immutability_policy_mode { - request.insert_header( - "x-ms-immutability-policy-mode", - immutability_policy_mode.to_string(), - ); - } - if let Some(immutability_policy_expiry) = options.immutability_policy_expiry { - request.insert_header( - "x-ms-immutability-policy-until-date", - to_rfc7231(&immutability_policy_expiry), - ); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// The Set Legal Hold operation sets a legal hold on the blob. - /// - /// # Arguments - /// - /// * `legal_hold` - Required. Specifies the legal hold status to set on the blob. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.setLegalHold")] - pub async fn set_legal_hold( - &self, - legal_hold: bool, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "legalhold"); - if let Some(snapshot) = options.snapshot { - url.query_pairs_mut().append_pair("snapshot", &snapshot); - } - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - request.insert_header("x-ms-legal-hold", legal_hold.to_string()); - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// The Set Metadata operation sets user-defined metadata for the specified blob as one or more name-value pairs. - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.setMetadata")] - pub async fn set_metadata( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "metadata"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(encryption_algorithm) = options.encryption_algorithm { - request.insert_header( - "x-ms-encryption-algorithm", - encryption_algorithm.to_string(), - ); - } - if let Some(encryption_key) = options.encryption_key { - request.insert_header("x-ms-encryption-key", encryption_key); - } - if let Some(encryption_key_sha256) = options.encryption_key_sha256 { - request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); - } - if let Some(encryption_scope) = options.encryption_scope { - request.insert_header("x-ms-encryption-scope", encryption_scope); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - if let Some(metadata) = options.metadata { - for (k, v) in &metadata { - request.insert_header(format!("x-ms-meta-{k}"), v); - } - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// The Set HTTP Headers operation sets system properties on the blob. - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.setProperties")] - pub async fn set_properties( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - url.query_pairs_mut() - .append_key_only("SetHTTPHeaders") - .append_pair("comp", "properties"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(blob_cache_control) = options.blob_cache_control { - request.insert_header("x-ms-blob-cache-control", blob_cache_control); - } - if let Some(blob_content_disposition) = options.blob_content_disposition { - request.insert_header("x-ms-blob-content-disposition", blob_content_disposition); - } - if let Some(blob_content_encoding) = options.blob_content_encoding { - request.insert_header("x-ms-blob-content-encoding", blob_content_encoding); - } - if let Some(blob_content_language) = options.blob_content_language { - request.insert_header("x-ms-blob-content-language", blob_content_language); - } - if let Some(blob_content_md5) = options.blob_content_md5 { - request.insert_header("x-ms-blob-content-md5", encode(blob_content_md5)); - } - if let Some(blob_content_type) = options.blob_content_type { - request.insert_header("x-ms-blob-content-type", blob_content_type); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// The Set Tags operation enables users to set tags on a blob. - /// - /// # Arguments - /// - /// * `tags` - The blob tags. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.setTags")] - pub async fn set_tags( - &self, - tags: RequestContent, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "tags"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - if let Some(transactional_content_md5) = options.transactional_content_md5 { - request.insert_header("content-md5", encode(transactional_content_md5)); - } - request.insert_header("content-type", "application/xml"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(transactional_content_crc64) = options.transactional_content_crc64 { - request.insert_header("x-ms-content-crc64", encode(transactional_content_crc64)); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - request.insert_header("x-ms-version", &self.version); - request.set_body(tags); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - - /// The Set Tier operation sets the tier on a block blob. The operation is allowed on a page blob or block blob, but not on - /// an append blob. A block blob's tier determines Hot/Cool/Archive storage type. This operation does not update the blob's - /// ETag. - /// - /// # Arguments - /// - /// * `tier` - Indicates the tier to be set on the blob. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.setTier")] - pub async fn set_tier( - &self, - tier: AccessTier, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "tier"); - if let Some(snapshot) = options.snapshot { - url.query_pairs_mut().append_pair("snapshot", &snapshot); - } - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - if let Some(version_id) = options.version_id { - url.query_pairs_mut().append_pair("versionid", &version_id); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - request.insert_header("x-ms-access-tier", tier.to_string()); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - if let Some(rehydrate_priority) = options.rehydrate_priority { - request.insert_header("x-ms-rehydrate-priority", rehydrate_priority.to_string()); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Set Tier operation sets the tier on a block blob. The operation is allowed on a page blob or block blob, but not on + // /// an append blob. A block blob's tier determines Hot/Cool/Archive storage type. This operation does not update the blob's + // /// ETag. + // /// + // /// # Arguments + // /// + // /// * `tier` - Indicates the tier to be set on the blob. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.setTier")] + // pub async fn set_tier( + // &self, + // tier: AccessTier, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "tier"); + // if let Some(snapshot) = options.snapshot { + // url.query_pairs_mut().append_pair("snapshot", &snapshot); + // } + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // if let Some(version_id) = options.version_id { + // url.query_pairs_mut().append_pair("versionid", &version_id); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // request.insert_header("x-ms-access-tier", tier.to_string()); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // if let Some(rehydrate_priority) = options.rehydrate_priority { + // request.insert_header("x-ms-rehydrate-priority", rehydrate_priority.to_string()); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// The Start Copy From URL operation copies a blob or an internet resource to a new blob. - /// - /// # Arguments - /// - /// * `copy_source` - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that - /// specifies a page blob snapshot. The value should be URL-encoded as it would appear in a request URI. The source blob must - /// either be public or must be authenticated via a shared access signature. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.startCopyFromUrl")] - pub async fn start_copy_from_url( - &self, - copy_source: String, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(tier) = options.tier { - request.insert_header("x-ms-access-tier", tier.to_string()); - } - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - request.insert_header("x-ms-copy-source", copy_source); - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(immutability_policy_mode) = options.immutability_policy_mode { - request.insert_header( - "x-ms-immutability-policy-mode", - immutability_policy_mode.to_string(), - ); - } - if let Some(immutability_policy_expiry) = options.immutability_policy_expiry { - request.insert_header( - "x-ms-immutability-policy-until-date", - to_rfc7231(&immutability_policy_expiry), - ); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - if let Some(legal_hold) = options.legal_hold { - request.insert_header("x-ms-legal-hold", legal_hold.to_string()); - } - if let Some(metadata) = options.metadata { - for (k, v) in &metadata { - request.insert_header(format!("x-ms-meta-{k}"), v); - } - } - if let Some(rehydrate_priority) = options.rehydrate_priority { - request.insert_header("x-ms-rehydrate-priority", rehydrate_priority.to_string()); - } - request.insert_header("x-ms-requires-sync", "true"); - if let Some(seal_blob) = options.seal_blob { - request.insert_header("x-ms-seal-blob", seal_blob.to_string()); - } - if let Some(source_if_match) = options.source_if_match { - request.insert_header("x-ms-source-if-match", source_if_match); - } - if let Some(source_if_modified_since) = options.source_if_modified_since { - request.insert_header( - "x-ms-source-if-modified-since", - to_rfc7231(&source_if_modified_since), - ); - } - if let Some(source_if_none_match) = options.source_if_none_match { - request.insert_header("x-ms-source-if-none-match", source_if_none_match); - } - if let Some(source_if_tags) = options.source_if_tags { - request.insert_header("x-ms-source-if-tags", source_if_tags); - } - if let Some(source_if_unmodified_since) = options.source_if_unmodified_since { - request.insert_header( - "x-ms-source-if-unmodified-since", - to_rfc7231(&source_if_unmodified_since), - ); - } - if let Some(blob_tags_string) = options.blob_tags_string { - request.insert_header("x-ms-tags", blob_tags_string); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// The Start Copy From URL operation copies a blob or an internet resource to a new blob. + // /// + // /// # Arguments + // /// + // /// * `copy_source` - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that + // /// specifies a page blob snapshot. The value should be URL-encoded as it would appear in a request URI. The source blob must + // /// either be public or must be authenticated via a shared access signature. + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.startCopyFromUrl")] + // pub async fn start_copy_from_url( + // &self, + // copy_source: String, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // path = path.replace("{blobName}", &self.blob_name); + // path = path.replace("{containerName}", &self.container_name); + // url = url.join(&path)?; + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(if_match) = options.if_match { + // request.insert_header("if-match", if_match); + // } + // if let Some(if_modified_since) = options.if_modified_since { + // request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + // } + // if let Some(if_none_match) = options.if_none_match { + // request.insert_header("if-none-match", if_none_match); + // } + // if let Some(if_unmodified_since) = options.if_unmodified_since { + // request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + // } + // if let Some(tier) = options.tier { + // request.insert_header("x-ms-access-tier", tier.to_string()); + // } + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // request.insert_header("x-ms-copy-source", copy_source); + // if let Some(if_tags) = options.if_tags { + // request.insert_header("x-ms-if-tags", if_tags); + // } + // if let Some(immutability_policy_mode) = options.immutability_policy_mode { + // request.insert_header( + // "x-ms-immutability-policy-mode", + // immutability_policy_mode.to_string(), + // ); + // } + // if let Some(immutability_policy_expiry) = options.immutability_policy_expiry { + // request.insert_header( + // "x-ms-immutability-policy-until-date", + // to_rfc7231(&immutability_policy_expiry), + // ); + // } + // if let Some(lease_id) = options.lease_id { + // request.insert_header("x-ms-lease-id", lease_id); + // } + // if let Some(legal_hold) = options.legal_hold { + // request.insert_header("x-ms-legal-hold", legal_hold.to_string()); + // } + // if let Some(metadata) = options.metadata { + // for (k, v) in &metadata { + // request.insert_header(format!("x-ms-meta-{k}"), v); + // } + // } + // if let Some(rehydrate_priority) = options.rehydrate_priority { + // request.insert_header("x-ms-rehydrate-priority", rehydrate_priority.to_string()); + // } + // request.insert_header("x-ms-requires-sync", "true"); + // if let Some(seal_blob) = options.seal_blob { + // request.insert_header("x-ms-seal-blob", seal_blob.to_string()); + // } + // if let Some(source_if_match) = options.source_if_match { + // request.insert_header("x-ms-source-if-match", source_if_match); + // } + // if let Some(source_if_modified_since) = options.source_if_modified_since { + // request.insert_header( + // "x-ms-source-if-modified-since", + // to_rfc7231(&source_if_modified_since), + // ); + // } + // if let Some(source_if_none_match) = options.source_if_none_match { + // request.insert_header("x-ms-source-if-none-match", source_if_none_match); + // } + // if let Some(source_if_tags) = options.source_if_tags { + // request.insert_header("x-ms-source-if-tags", source_if_tags); + // } + // if let Some(source_if_unmodified_since) = options.source_if_unmodified_since { + // request.insert_header( + // "x-ms-source-if-unmodified-since", + // to_rfc7231(&source_if_unmodified_since), + // ); + // } + // if let Some(blob_tags_string) = options.blob_tags_string { + // request.insert_header("x-ms-tags", blob_tags_string); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } - /// Undelete a blob that was previously soft deleted - /// - /// # Arguments - /// - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.undelete")] - pub async fn undelete( - &self, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - 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", "undelete"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-type", "application/xml"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } + // /// Undelete a blob that was previously soft deleted + // /// + // /// # Arguments + // /// + // /// * `options` - Optional parameters for the request. + // #[tracing::function("Storage.Blob.Container.Blob.undelete")] + // pub async fn undelete( + // &self, + // options: Option>, + // ) -> Result> { + // let options = options.unwrap_or_default(); + // let ctx = Context::with_context(&options.method_options.context); + // let mut url = self.endpoint.clone(); + // let mut path = String::from("{containerName}/{blobName}"); + // 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", "undelete"); + // if let Some(timeout) = options.timeout { + // url.query_pairs_mut() + // .append_pair("timeout", &timeout.to_string()); + // } + // let mut request = Request::new(url, Method::Put); + // request.insert_header("accept", "application/xml"); + // request.insert_header("content-type", "application/xml"); + // if let Some(client_request_id) = options.client_request_id { + // request.insert_header("x-ms-client-request-id", client_request_id); + // } + // request.insert_header("x-ms-version", &self.version); + // let rsp = self.pipeline.send(&ctx, &mut request).await?; + // if !rsp.status().is_success() { + // let status = rsp.status(); + // let http_error = HttpError::new(rsp).await; + // let error_kind = ErrorKind::http_response( + // status, + // http_error.error_code().map(std::borrow::ToOwned::to_owned), + // ); + // return Err(Error::new(error_kind, http_error)); + // } + // Ok(rsp.into()) + // } } impl Default for BlobClientOptions { diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/blob_container_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/blob_container_client.rs index 0871c272cd..c1883a1b24 100644 --- a/sdk/storage/azure_storage_blob/src/generated/clients/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/src/generated/clients/blob_container_client.rs @@ -527,16 +527,16 @@ impl BlobContainerClient { /// # Arguments /// /// * `blob_name` - The name of the blob. - #[tracing::subclient] - pub fn get_blob_client(&self, blob_name: String) -> BlobClient { - BlobClient { - blob_name, - container_name: self.container_name.clone(), - endpoint: self.endpoint.clone(), - pipeline: self.pipeline.clone(), - version: self.version.clone(), - } - } + // #[tracing::subclient] + // pub fn get_blob_client(&self, blob_name: String) -> BlobClient { + // BlobClient { + // blob_name, + // container_name: self.container_name.clone(), + // endpoint: self.endpoint.clone(), + // pipeline: self.pipeline.clone(), + // version: self.version.clone(), + // } + // } /// returns all user-defined metadata and system properties for the specified container. The data returned does not include /// the container's list of blobs diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 5ff6ac9886..6e50866f54 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -16,427 +16,427 @@ use azure_storage_blob::{ }, BlobClient, }; -use azure_storage_blob_test::{create_test_blob, get_blob_name, get_container_client}; +// use azure_storage_blob_test::{create_test_blob, get_blob_name, get_container_client}; use std::{collections::HashMap, error::Error, time::Duration}; use tokio::time; -#[recorded::test] -async fn test_get_blob_properties(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - let recording = ctx.recording(); - let container_client = get_container_client(recording, false).await?; - let blob_client = container_client.blob_client(get_blob_name(recording)); - - // Invalid Container Scenario - let response = blob_client.get_properties(None).await; - - // Assert - let error = response.unwrap_err().http_status(); - assert_eq!(StatusCode::NotFound, error.unwrap()); - - container_client.create_container(None).await?; - create_test_blob(&blob_client).await?; - - // No Option Scenario - let response = blob_client.get_properties(None).await?; - - // Assert - let lease_state = response.lease_state()?; - let content_length = response.content_length()?; - let etag = response.etag()?; - let creation_time = response.creation_time()?; - - assert_eq!(LeaseState::Available, lease_state.unwrap()); - assert_eq!(17, content_length.unwrap()); - assert!(etag.is_some()); - assert!(creation_time.is_some()); - - container_client.delete_container(None).await?; - Ok(()) -} - -#[recorded::test] -async fn test_set_blob_properties(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - let recording = ctx.recording(); - let container_client = get_container_client(recording, true).await?; - let blob_client = container_client.blob_client(get_blob_name(recording)); - create_test_blob(&blob_client).await?; - - // Set Content Settings - let set_properties_options = BlobClientSetPropertiesOptions { - blob_content_language: Some("spanish".to_string()), - blob_content_disposition: Some("inline".to_string()), - ..Default::default() - }; - blob_client - .set_properties(Some(set_properties_options)) - .await?; - - // Assert - let response = blob_client.get_properties(None).await?; - let content_language = response.content_language()?; - let content_disposition = response.content_disposition()?; - - assert_eq!("spanish".to_string(), content_language.unwrap()); - assert_eq!("inline".to_string(), content_disposition.unwrap()); - - container_client.delete_container(None).await?; - Ok(()) -} - -#[recorded::test] -async fn test_upload_blob(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - let recording = ctx.recording(); - let container_client = get_container_client(recording, true).await?; - let blob_client = container_client.blob_client(get_blob_name(recording)); - - let data = b"hello rusty world"; - - // No Overwrite Scenario - blob_client - .upload( - RequestContent::from(data.to_vec()), - false, - u64::try_from(data.len())?, - None, - ) - .await?; - - // Assert - let response = blob_client.download(None).await?; - let content_length = response.content_length()?; - let (status_code, _, response_body) = response.deconstruct(); - assert!(status_code.is_success()); - assert_eq!(17, content_length.unwrap()); - assert_eq!(Bytes::from_static(data), response_body.collect().await?); - - // Overwrite Scenarios - let new_data = b"hello overwritten rusty world"; - - // Error Case (overwrite=false/none) - let response = blob_client - .upload( - RequestContent::from(new_data.to_vec()), - false, - u64::try_from(new_data.len())?, - None, - ) - .await; - - // Assert - assert!(response.is_err()); - let error = response.unwrap_err().http_status(); - assert_eq!(StatusCode::Conflict, error.unwrap()); - - // Working Case (overwrite=true) - let overwrite_response = blob_client - .upload( - RequestContent::from(new_data.to_vec()), - true, - u64::try_from(new_data.len())?, - None, - ) - .await?; - let response = blob_client.download(None).await?; - let content_length = response.content_length()?; - - // Assert - assert_eq!(overwrite_response.status(), StatusCode::Created); - let (status_code, _, response_body) = response.deconstruct(); - assert!(status_code.is_success()); - assert_eq!(29, content_length.unwrap()); - assert_eq!(Bytes::from_static(new_data), response_body.collect().await?); - - container_client.delete_container(None).await?; - Ok(()) -} - -#[recorded::test] -async fn test_delete_blob(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - let recording = ctx.recording(); - let container_client = get_container_client(recording, true).await?; - let blob_client = container_client.blob_client(get_blob_name(recording)); - create_test_blob(&blob_client).await?; - - // Existence Check - blob_client.get_properties(None).await?; - - blob_client.delete(None).await?; - - let response = blob_client.download(None).await; - - // Assert - let error = response.unwrap_err().http_status(); - assert_eq!(StatusCode::NotFound, error.unwrap()); - - container_client.delete_container(None).await?; - Ok(()) -} - -#[recorded::test] -async fn test_download_blob(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - let recording = ctx.recording(); - let container_client = get_container_client(recording, true).await?; - let blob_client = container_client.blob_client(get_blob_name(recording)); - let data = b"hello rusty world"; - - blob_client - .upload( - RequestContent::from(data.to_vec()), - false, - u64::try_from(data.len())?, - None, - ) - .await?; - let response = blob_client.download(None).await?; - - // Assert - let content_length = response.content_length()?; - let (status_code, _, response_body) = response.deconstruct(); - assert!(status_code.is_success()); - assert_eq!(17, content_length.unwrap()); - assert_eq!( - b"hello rusty world".to_vec(), - response_body.collect().await? - ); - - container_client.delete_container(None).await?; - Ok(()) -} - -#[recorded::test] -async fn test_set_blob_metadata(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - - let recording = ctx.recording(); - let container_client = get_container_client(recording, true).await?; - let blob_client = container_client.blob_client(get_blob_name(recording)); - let data = b"hello rusty world"; - - // Upload Blob With Metadata - let initial_metadata = HashMap::from([("initial".to_string(), "metadata".to_string())]); - - let options_with_metadata = BlockBlobClientUploadOptions { - metadata: Some(initial_metadata.clone()), - ..Default::default() - }; - blob_client - .upload( - RequestContent::from(data.to_vec()), - false, - u64::try_from(data.len())?, - Some(options_with_metadata), - ) - .await?; - // Assert - let response = blob_client.get_properties(None).await?; - let response_metadata = response.metadata()?; - assert_eq!(initial_metadata, response_metadata); - - // Set Metadata With Values - let update_metadata = HashMap::from([("updated".to_string(), "values".to_string())]); - let set_metadata_options = BlobClientSetMetadataOptions { - metadata: Some(update_metadata.clone()), - ..Default::default() - }; - blob_client.set_metadata(Some(set_metadata_options)).await?; - // Assert - let response = blob_client.get_properties(None).await?; - let response_metadata = response.metadata()?; - assert_eq!(update_metadata, response_metadata); - - // Set Metadata No Values (Clear Metadata) - blob_client.set_metadata(None).await?; - // Assert - let response = blob_client.get_properties(None).await?; - let response_metadata = response.metadata()?; - assert_eq!(HashMap::new(), response_metadata); - - Ok(()) -} - -#[recorded::test] -async fn test_set_access_tier(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - let recording = ctx.recording(); - let container_client = get_container_client(recording, true).await?; - let blob_client = container_client.blob_client(get_blob_name(recording)); - create_test_blob(&blob_client).await?; - - let original_response = blob_client.get_properties(None).await?; - let og_access_tier = original_response.access_tier()?; - assert_eq!(AccessTier::Hot.to_string(), og_access_tier.unwrap()); - - // Set Standard Blob Tier (Cold) - blob_client.set_tier(AccessTier::Cold, None).await?; - let response = blob_client.get_properties(None).await?; - - // Assert - let access_tier = response.access_tier()?; - assert_eq!(AccessTier::Cold.to_string(), access_tier.unwrap()); - - container_client.delete_container(None).await?; - Ok(()) -} - -#[recorded::test] -async fn test_blob_lease_operations(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - let recording = ctx.recording(); - let container_client = get_container_client(recording, true).await?; - let blob_name = get_blob_name(recording); - let blob_client = container_client.blob_client(blob_name.clone()); - let other_blob_client = container_client.blob_client(blob_name); - create_test_blob(&blob_client).await?; - - // Acquire Lease - let acquire_response = blob_client.acquire_lease(15, None).await?; - let lease_id = acquire_response.lease_id()?.unwrap(); - let other_acquire_response = other_blob_client.acquire_lease(15, None).await; - // Assert - let error = other_acquire_response.unwrap_err().http_status(); - assert_eq!(StatusCode::Conflict, error.unwrap()); - - // Change Lease - let proposed_lease_id = "00000000-1111-2222-3333-444444444444".to_string(); - let change_lease_response = blob_client - .change_lease(lease_id, proposed_lease_id.clone(), None) - .await?; - // Assert - let lease_id = change_lease_response.lease_id()?.unwrap(); - assert_eq!(proposed_lease_id.clone().to_string(), lease_id); - - // Sleep until lease expires - time::sleep(Duration::from_secs(15)).await; - - // Renew Lease - blob_client - .renew_lease(proposed_lease_id.clone(), None) - .await?; - let other_acquire_response = other_blob_client.acquire_lease(15, None).await; - // Assert - let error = other_acquire_response.unwrap_err().http_status(); - assert_eq!(StatusCode::Conflict, error.unwrap()); - - // Break Lease - blob_client.break_lease(None).await?; - let other_acquire_response = other_blob_client.acquire_lease(15, None).await; - // Assert - let error = other_acquire_response.unwrap_err().http_status(); - assert_eq!(StatusCode::Conflict, error.unwrap()); - - // Release Lease - blob_client - .release_lease(proposed_lease_id.clone(), None) - .await?; - other_blob_client.acquire_lease(15, None).await?; - - container_client.delete_container(None).await?; - Ok(()) -} - -#[recorded::test] -async fn test_leased_blob_operations(ctx: TestContext) -> Result<(), Box> { - // Recording Setup - let recording = ctx.recording(); - let container_client = get_container_client(recording, true).await?; - let blob_name = get_blob_name(recording); - let blob_client = container_client.blob_client(blob_name.clone()); - create_test_blob(&blob_client).await?; - let acquire_response = blob_client.acquire_lease(-1, None).await?; - let lease_id = acquire_response.lease_id()?.unwrap(); - - // Set Properties, Set Metadata, Set Access Tier - let set_properties_options = BlobClientSetPropertiesOptions { - blob_content_language: Some("spanish".to_string()), - blob_content_disposition: Some("inline".to_string()), - lease_id: Some(lease_id.clone()), - ..Default::default() - }; - blob_client - .set_properties(Some(set_properties_options)) - .await?; - - let update_metadata = HashMap::from([("updated".to_string(), "values".to_string())]); - let set_metadata_options = BlobClientSetMetadataOptions { - metadata: Some(update_metadata.clone()), - lease_id: Some(lease_id.clone()), - ..Default::default() - }; - blob_client.set_metadata(Some(set_metadata_options)).await?; - - let set_tier_options = BlobClientSetTierOptions { - lease_id: Some(lease_id.clone()), - ..Default::default() - }; - blob_client - .set_tier(AccessTier::Cold, Some(set_tier_options)) - .await?; - - // Assert - let get_properties_options = BlobClientGetPropertiesOptions { - lease_id: Some(lease_id.clone()), - ..Default::default() - }; - let response = blob_client - .get_properties(Some(get_properties_options)) - .await?; - let content_language = response.content_language()?; - let content_disposition = response.content_disposition()?; - let response_metadata = response.metadata()?; - let access_tier = response.access_tier()?; - - assert_eq!("spanish".to_string(), content_language.unwrap()); - assert_eq!("inline".to_string(), content_disposition.unwrap()); - assert_eq!(update_metadata, response_metadata); - assert_eq!(AccessTier::Cold.to_string(), access_tier.unwrap()); - - // Overwrite Upload - let data = b"overruled!"; - let upload_options = BlockBlobClientUploadOptions { - lease_id: Some(lease_id.clone()), - ..Default::default() - }; - blob_client - .upload( - RequestContent::from(data.to_vec()), - true, - u64::try_from(data.len())?, - Some(upload_options), - ) - .await?; - - // Assert - let download_options = BlobClientDownloadOptions { - lease_id: Some(lease_id.clone()), - ..Default::default() - }; - let response = blob_client.download(Some(download_options)).await?; - let content_length = response.content_length()?; - let (status_code, _, response_body) = response.deconstruct(); - assert!(status_code.is_success()); - assert_eq!(10, content_length.unwrap()); - assert_eq!(data.to_vec(), response_body.collect().await?); - - blob_client.break_lease(None).await?; - container_client.delete_container(None).await?; - Ok(()) -} +// #[recorded::test] +// async fn test_get_blob_properties(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, false).await?; +// let blob_client = container_client.blob_client(get_blob_name(recording)); + +// // Invalid Container Scenario +// let response = blob_client.get_properties(None).await; + +// // Assert +// let error = response.unwrap_err().http_status(); +// assert_eq!(StatusCode::NotFound, error.unwrap()); + +// container_client.create_container(None).await?; +// create_test_blob(&blob_client).await?; + +// // No Option Scenario +// let response = blob_client.get_properties(None).await?; + +// // Assert +// let lease_state = response.lease_state()?; +// let content_length = response.content_length()?; +// let etag = response.etag()?; +// let creation_time = response.creation_time()?; + +// assert_eq!(LeaseState::Available, lease_state.unwrap()); +// assert_eq!(17, content_length.unwrap()); +// assert!(etag.is_some()); +// assert!(creation_time.is_some()); + +// container_client.delete_container(None).await?; +// Ok(()) +// } + +// #[recorded::test] +// async fn test_set_blob_properties(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, true).await?; +// let blob_client = container_client.blob_client(get_blob_name(recording)); +// create_test_blob(&blob_client).await?; + +// // Set Content Settings +// let set_properties_options = BlobClientSetPropertiesOptions { +// blob_content_language: Some("spanish".to_string()), +// blob_content_disposition: Some("inline".to_string()), +// ..Default::default() +// }; +// blob_client +// .set_properties(Some(set_properties_options)) +// .await?; + +// // Assert +// let response = blob_client.get_properties(None).await?; +// let content_language = response.content_language()?; +// let content_disposition = response.content_disposition()?; + +// assert_eq!("spanish".to_string(), content_language.unwrap()); +// assert_eq!("inline".to_string(), content_disposition.unwrap()); + +// container_client.delete_container(None).await?; +// Ok(()) +// } + +// #[recorded::test] +// async fn test_upload_blob(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, true).await?; +// let blob_client = container_client.blob_client(get_blob_name(recording)); + +// let data = b"hello rusty world"; + +// // No Overwrite Scenario +// blob_client +// .upload( +// RequestContent::from(data.to_vec()), +// false, +// u64::try_from(data.len())?, +// None, +// ) +// .await?; + +// // Assert +// let response = blob_client.download(None).await?; +// let content_length = response.content_length()?; +// let (status_code, _, response_body) = response.deconstruct(); +// assert!(status_code.is_success()); +// assert_eq!(17, content_length.unwrap()); +// assert_eq!(Bytes::from_static(data), response_body.collect().await?); + +// // Overwrite Scenarios +// let new_data = b"hello overwritten rusty world"; + +// // Error Case (overwrite=false/none) +// let response = blob_client +// .upload( +// RequestContent::from(new_data.to_vec()), +// false, +// u64::try_from(new_data.len())?, +// None, +// ) +// .await; + +// // Assert +// assert!(response.is_err()); +// let error = response.unwrap_err().http_status(); +// assert_eq!(StatusCode::Conflict, error.unwrap()); + +// // Working Case (overwrite=true) +// let overwrite_response = blob_client +// .upload( +// RequestContent::from(new_data.to_vec()), +// true, +// u64::try_from(new_data.len())?, +// None, +// ) +// .await?; +// let response = blob_client.download(None).await?; +// let content_length = response.content_length()?; + +// // Assert +// assert_eq!(overwrite_response.status(), StatusCode::Created); +// let (status_code, _, response_body) = response.deconstruct(); +// assert!(status_code.is_success()); +// assert_eq!(29, content_length.unwrap()); +// assert_eq!(Bytes::from_static(new_data), response_body.collect().await?); + +// container_client.delete_container(None).await?; +// Ok(()) +// } + +// #[recorded::test] +// async fn test_delete_blob(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, true).await?; +// let blob_client = container_client.blob_client(get_blob_name(recording)); +// create_test_blob(&blob_client).await?; + +// // Existence Check +// blob_client.get_properties(None).await?; + +// blob_client.delete(None).await?; + +// let response = blob_client.download(None).await; + +// // Assert +// let error = response.unwrap_err().http_status(); +// assert_eq!(StatusCode::NotFound, error.unwrap()); + +// container_client.delete_container(None).await?; +// Ok(()) +// } + +// #[recorded::test] +// async fn test_download_blob(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, true).await?; +// let blob_client = container_client.blob_client(get_blob_name(recording)); +// let data = b"hello rusty world"; + +// blob_client +// .upload( +// RequestContent::from(data.to_vec()), +// false, +// u64::try_from(data.len())?, +// None, +// ) +// .await?; +// let response = blob_client.download(None).await?; + +// // Assert +// let content_length = response.content_length()?; +// let (status_code, _, response_body) = response.deconstruct(); +// assert!(status_code.is_success()); +// assert_eq!(17, content_length.unwrap()); +// assert_eq!( +// b"hello rusty world".to_vec(), +// response_body.collect().await? +// ); + +// container_client.delete_container(None).await?; +// Ok(()) +// } + +// #[recorded::test] +// async fn test_set_blob_metadata(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup + +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, true).await?; +// let blob_client = container_client.blob_client(get_blob_name(recording)); +// let data = b"hello rusty world"; + +// // Upload Blob With Metadata +// let initial_metadata = HashMap::from([("initial".to_string(), "metadata".to_string())]); + +// let options_with_metadata = BlockBlobClientUploadOptions { +// metadata: Some(initial_metadata.clone()), +// ..Default::default() +// }; +// blob_client +// .upload( +// RequestContent::from(data.to_vec()), +// false, +// u64::try_from(data.len())?, +// Some(options_with_metadata), +// ) +// .await?; +// // Assert +// let response = blob_client.get_properties(None).await?; +// let response_metadata = response.metadata()?; +// assert_eq!(initial_metadata, response_metadata); + +// // Set Metadata With Values +// let update_metadata = HashMap::from([("updated".to_string(), "values".to_string())]); +// let set_metadata_options = BlobClientSetMetadataOptions { +// metadata: Some(update_metadata.clone()), +// ..Default::default() +// }; +// blob_client.set_metadata(Some(set_metadata_options)).await?; +// // Assert +// let response = blob_client.get_properties(None).await?; +// let response_metadata = response.metadata()?; +// assert_eq!(update_metadata, response_metadata); + +// // Set Metadata No Values (Clear Metadata) +// blob_client.set_metadata(None).await?; +// // Assert +// let response = blob_client.get_properties(None).await?; +// let response_metadata = response.metadata()?; +// assert_eq!(HashMap::new(), response_metadata); + +// Ok(()) +// } + +// #[recorded::test] +// async fn test_set_access_tier(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, true).await?; +// let blob_client = container_client.blob_client(get_blob_name(recording)); +// create_test_blob(&blob_client).await?; + +// let original_response = blob_client.get_properties(None).await?; +// let og_access_tier = original_response.access_tier()?; +// assert_eq!(AccessTier::Hot.to_string(), og_access_tier.unwrap()); + +// // Set Standard Blob Tier (Cold) +// blob_client.set_tier(AccessTier::Cold, None).await?; +// let response = blob_client.get_properties(None).await?; + +// // Assert +// let access_tier = response.access_tier()?; +// assert_eq!(AccessTier::Cold.to_string(), access_tier.unwrap()); + +// container_client.delete_container(None).await?; +// Ok(()) +// } + +// #[recorded::test] +// async fn test_blob_lease_operations(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, true).await?; +// let blob_name = get_blob_name(recording); +// let blob_client = container_client.blob_client(blob_name.clone()); +// let other_blob_client = container_client.blob_client(blob_name); +// create_test_blob(&blob_client).await?; + +// // Acquire Lease +// let acquire_response = blob_client.acquire_lease(15, None).await?; +// let lease_id = acquire_response.lease_id()?.unwrap(); +// let other_acquire_response = other_blob_client.acquire_lease(15, None).await; +// // Assert +// let error = other_acquire_response.unwrap_err().http_status(); +// assert_eq!(StatusCode::Conflict, error.unwrap()); + +// // Change Lease +// let proposed_lease_id = "00000000-1111-2222-3333-444444444444".to_string(); +// let change_lease_response = blob_client +// .change_lease(lease_id, proposed_lease_id.clone(), None) +// .await?; +// // Assert +// let lease_id = change_lease_response.lease_id()?.unwrap(); +// assert_eq!(proposed_lease_id.clone().to_string(), lease_id); + +// // Sleep until lease expires +// time::sleep(Duration::from_secs(15)).await; + +// // Renew Lease +// blob_client +// .renew_lease(proposed_lease_id.clone(), None) +// .await?; +// let other_acquire_response = other_blob_client.acquire_lease(15, None).await; +// // Assert +// let error = other_acquire_response.unwrap_err().http_status(); +// assert_eq!(StatusCode::Conflict, error.unwrap()); + +// // Break Lease +// blob_client.break_lease(None).await?; +// let other_acquire_response = other_blob_client.acquire_lease(15, None).await; +// // Assert +// let error = other_acquire_response.unwrap_err().http_status(); +// assert_eq!(StatusCode::Conflict, error.unwrap()); + +// // Release Lease +// blob_client +// .release_lease(proposed_lease_id.clone(), None) +// .await?; +// other_blob_client.acquire_lease(15, None).await?; + +// container_client.delete_container(None).await?; +// Ok(()) +// } + +// #[recorded::test] +// async fn test_leased_blob_operations(ctx: TestContext) -> Result<(), Box> { +// // Recording Setup +// let recording = ctx.recording(); +// let container_client = get_container_client(recording, true).await?; +// let blob_name = get_blob_name(recording); +// let blob_client = container_client.blob_client(blob_name.clone()); +// create_test_blob(&blob_client).await?; +// let acquire_response = blob_client.acquire_lease(-1, None).await?; +// let lease_id = acquire_response.lease_id()?.unwrap(); + +// // Set Properties, Set Metadata, Set Access Tier +// let set_properties_options = BlobClientSetPropertiesOptions { +// blob_content_language: Some("spanish".to_string()), +// blob_content_disposition: Some("inline".to_string()), +// lease_id: Some(lease_id.clone()), +// ..Default::default() +// }; +// blob_client +// .set_properties(Some(set_properties_options)) +// .await?; + +// let update_metadata = HashMap::from([("updated".to_string(), "values".to_string())]); +// let set_metadata_options = BlobClientSetMetadataOptions { +// metadata: Some(update_metadata.clone()), +// lease_id: Some(lease_id.clone()), +// ..Default::default() +// }; +// blob_client.set_metadata(Some(set_metadata_options)).await?; + +// let set_tier_options = BlobClientSetTierOptions { +// lease_id: Some(lease_id.clone()), +// ..Default::default() +// }; +// blob_client +// .set_tier(AccessTier::Cold, Some(set_tier_options)) +// .await?; + +// // Assert +// let get_properties_options = BlobClientGetPropertiesOptions { +// lease_id: Some(lease_id.clone()), +// ..Default::default() +// }; +// let response = blob_client +// .get_properties(Some(get_properties_options)) +// .await?; +// let content_language = response.content_language()?; +// let content_disposition = response.content_disposition()?; +// let response_metadata = response.metadata()?; +// let access_tier = response.access_tier()?; + +// assert_eq!("spanish".to_string(), content_language.unwrap()); +// assert_eq!("inline".to_string(), content_disposition.unwrap()); +// assert_eq!(update_metadata, response_metadata); +// assert_eq!(AccessTier::Cold.to_string(), access_tier.unwrap()); + +// // Overwrite Upload +// let data = b"overruled!"; +// let upload_options = BlockBlobClientUploadOptions { +// lease_id: Some(lease_id.clone()), +// ..Default::default() +// }; +// blob_client +// .upload( +// RequestContent::from(data.to_vec()), +// true, +// u64::try_from(data.len())?, +// Some(upload_options), +// ) +// .await?; + +// // Assert +// let download_options = BlobClientDownloadOptions { +// lease_id: Some(lease_id.clone()), +// ..Default::default() +// }; +// let response = blob_client.download(Some(download_options)).await?; +// let content_length = response.content_length()?; +// let (status_code, _, response_body) = response.deconstruct(); +// assert!(status_code.is_success()); +// assert_eq!(10, content_length.unwrap()); +// assert_eq!(data.to_vec(), response_body.collect().await?); + +// blob_client.break_lease(None).await?; +// container_client.delete_container(None).await?; +// Ok(()) +// } #[recorded::test] async fn test_sas(ctx: TestContext) -> Result<(), Box> { // SAS - let blob_url = ""; + let blob_url = "https://ruststoragedev.blob.core.windows.net/container0vgpjc2p/blob1ear0rva?sp=r&st=2025-08-15T20:44:04Z&se=2025-08-16T04:59:04Z&spr=https&sv=2024-11-04&sr=b&sig=9gRXYd8CUZXEjzR7nnOQ%2Bf16XnLjHeb6a4dKd%2FCElwY%3D"; let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?; let blob_properties = sas_blob_client.get_properties(None).await?; let content_length = blob_properties.content_length()?; - assert_eq!(11, content_length.unwrap()); + assert_eq!(17, content_length.unwrap()); Ok(()) } diff --git a/sdk/storage/azure_storage_blob_test/src/lib.rs b/sdk/storage/azure_storage_blob_test/src/lib.rs index cb6b164896..edde841714 100644 --- a/sdk/storage/azure_storage_blob_test/src/lib.rs +++ b/sdk/storage/azure_storage_blob_test/src/lib.rs @@ -95,20 +95,20 @@ pub async fn get_container_client( Ok(container_client) } -/// Creates a test blob with no options, containing the data "b'hello rusty world'" with content length 17. -/// -/// # Arguments -/// -/// * `blob_client` - A reference to a BlobClient instance. -pub async fn create_test_blob( - blob_client: &BlobClient, -) -> Result> { - blob_client - .upload( - RequestContent::from(b"hello rusty world".to_vec()), - true, - 17, - None, - ) - .await -} +// / Creates a test blob with no options, containing the data "b'hello rusty world'" with content length 17. +// / +// / # Arguments +// / +// / * `blob_client` - A reference to a BlobClient instance. +// pub async fn create_test_blob( +// blob_client: &BlobClient, +// ) -> Result> { +// blob_client +// .upload( +// RequestContent::from(b"hello rusty world".to_vec()), +// true, +// 17, +// None, +// ) +// .await +// } From 49e2b99a7b2720cad5300028373eef449a11784f Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Fri, 15 Aug 2025 14:01:43 -0700 Subject: [PATCH 06/11] nit --- sdk/storage/azure_storage_blob/tests/blob_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 6e50866f54..dd328acf8d 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -430,7 +430,7 @@ use tokio::time; #[recorded::test] async fn test_sas(ctx: TestContext) -> Result<(), Box> { // SAS - let blob_url = "https://ruststoragedev.blob.core.windows.net/container0vgpjc2p/blob1ear0rva?sp=r&st=2025-08-15T20:44:04Z&se=2025-08-16T04:59:04Z&spr=https&sv=2024-11-04&sr=b&sig=9gRXYd8CUZXEjzR7nnOQ%2Bf16XnLjHeb6a4dKd%2FCElwY%3D"; + let blob_url = ""; let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?; From cc25f02dae16caf2a9a42a98d297cff6a2f58fe4 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Fri, 15 Aug 2025 14:08:30 -0700 Subject: [PATCH 07/11] Adjust blob_client --- sdk/storage/azure_storage_blob/src/clients/blob_client.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 41c6da23a3..747e0749ad 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -32,7 +32,6 @@ use std::sync::Arc; /// A client to interact with a specific Azure storage blob, although that blob may not yet exist. pub struct BlobClient { - pub(super) blob_url: Url, pub(super) client: GeneratedBlobClient, } @@ -69,7 +68,6 @@ impl BlobClient { Some(options), )?; Ok(Self { - blob_url: client.blob_url().clone(), client, }) } @@ -87,7 +85,6 @@ impl BlobClient { let client = GeneratedBlobClient::with_no_credential(url.as_str(), Some(options))?; Ok(Self { - blob_url: url, client, }) } @@ -127,7 +124,7 @@ impl BlobClient { /// Gets the endpoint of the Storage account this client is connected to. pub fn blob_url(&self) -> &Url { - &self.blob_url + &self.client.blob_url } /// Gets the container name of the Storage account this client is connected to. From 61a6606c1427e5c41e648d36ea7a9fa8cea34758 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Fri, 15 Aug 2025 14:58:19 -0700 Subject: [PATCH 08/11] More changes --- .../src/clients/blob_client.rs | 116 +++++++++++++++--- .../src/generated/clients/blob_client.rs | 55 +-------- .../azure_storage_blob/tests/blob_client.rs | 24 +++- 3 files changed, 127 insertions(+), 68 deletions(-) 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 747e0749ad..c7997c294a 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -24,7 +24,7 @@ use azure_core::{ credentials::TokenCredential, http::{ policies::{BearerTokenCredentialPolicy, Policy}, - NoFormat, RequestContent, Response, Url, XmlFormat, + NoFormat, Pipeline, RequestContent, Response, Url, XmlFormat, }, Bytes, Result, }; @@ -35,6 +35,88 @@ pub struct BlobClient { pub(super) client: GeneratedBlobClient, } +impl GeneratedBlobClient { + fn from_url_with_credential( + blob_url: &str, + credential: Arc, + options: Option, + ) -> Result { + let options = options.unwrap_or_default(); + let blob_url = Url::parse(blob_url)?; + if !blob_url.scheme().starts_with("http") { + return Err(azure_core::Error::message( + azure_core::error::ErrorKind::Other, + format!("{blob_url} must use http(s)"), + )); + } + + let mut segments = blob_url + .path_segments() + .expect("Failed to get path segments"); + let container_name = segments + .next() + .expect("Failed to parse container_name") + .to_string(); + let blob_name = segments.collect::>().join("/"); + + let auth_policy: Arc = Arc::new(BearerTokenCredentialPolicy::new( + credential, + vec!["https://storage.azure.com/.default"], + )); + + Ok(Self { + blob_name, + container_name, + blob_url, + version: options.version, + pipeline: Pipeline::new( + option_env!("CARGO_PKG_NAME"), + option_env!("CARGO_PKG_VERSION"), + options.client_options, + Vec::default(), + vec![auth_policy], + ), + }) + } + + fn from_url_with_no_credential( + blob_url: &str, + options: Option, + ) -> Result { + let options = options.unwrap_or_default(); + let blob_url = Url::parse(blob_url)?; + if !blob_url.scheme().starts_with("http") { + return Err(azure_core::Error::message( + azure_core::error::ErrorKind::Other, + format!("{blob_url} must use http(s)"), + )); + } + + let mut segments = blob_url + .path_segments() + .expect("Failed to get path segments"); + let container_name = segments + .next() + .expect("Failed to parse container_name") + .to_string(); + let blob_name = segments.collect::>().join("/"); + + Ok(Self { + blob_name, + container_name, + blob_url, + version: options.version, + pipeline: Pipeline::new( + option_env!("CARGO_PKG_NAME"), + option_env!("CARGO_PKG_VERSION"), + options.client_options, + Vec::default(), + Vec::default(), + ), + }) + } +} + impl BlobClient { /// Creates a new BlobClient, using Entra ID authentication. /// @@ -60,16 +142,22 @@ impl BlobClient { .per_call_policies .push(storage_headers_policy); - let client = GeneratedBlobClient::new( - endpoint, - credential.clone(), - container_name.clone(), - blob_name.clone(), - Some(options), - )?; - Ok(Self { - client, - }) + let mut url = Url::parse(endpoint)?; + if !url.scheme().starts_with("http") { + return Err(azure_core::Error::message( + azure_core::error::ErrorKind::Other, + format!("{url} must use http(s)"), + )); + } + + // In regular ctor, since struct now holds blob_url, we have to build the blob_url given endpoint + container_name + blob_name + url.path_segments_mut() + .expect("Cannot be base") + .extend([&container_name, &blob_name]); + + let client = + GeneratedBlobClient::from_url_with_credential(url.as_str(), credential, Some(options))?; + Ok(Self { client }) } pub fn from_blob_url(blob_url: &str, options: Option) -> Result { @@ -82,11 +170,9 @@ impl BlobClient { .push(storage_headers_policy); let url = Url::parse(blob_url)?; - let client = GeneratedBlobClient::with_no_credential(url.as_str(), Some(options))?; + let client = GeneratedBlobClient::from_url_with_no_credential(url.as_str(), Some(options))?; - Ok(Self { - client, - }) + Ok(Self { client }) } // /// Returns a new instance of AppendBlobClient. 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 afdf608f8b..d2155b546f 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 @@ -58,16 +58,7 @@ pub struct BlobClientOptions { } impl BlobClient { - /// Creates a new BlobClient, using Entra ID authentication. - /// - /// # Arguments - /// - /// * `endpoint` - Service host - /// * `credential` - An implementation of [`TokenCredential`](azure_core::credentials::TokenCredential) that can provide an - /// Entra ID token to use when authenticating. - /// * `container_name` - The name of the container. - /// * `blob_name` - The name of the blob. - /// * `options` - Optional configuration for the client. + // UNUSED UNUSED UNUSED UNUSED UNUSED UNUSED UNUSED UNUSED pub fn new( endpoint: &str, credential: Arc, @@ -83,13 +74,7 @@ impl BlobClient { format!("{endpoint} must use http(s)"), )); } - - // In regular ctor, since struct now holds blob_url, we have to build the blob_url given endpoint + container_name + blob_name - endpoint - .path_segments_mut() - .expect("Cannot be base") - .extend([&container_name, &blob_name]); - + endpoint.set_query(None); let auth_policy: Arc = Arc::new(BearerTokenCredentialPolicy::new( credential, vec!["https://storage.azure.com/.default"], @@ -97,7 +82,7 @@ impl BlobClient { Ok(Self { blob_name, container_name, - blob_url: endpoint, + blob_url: endpoint, // This is straight up wrong (does not fold in blob/container to url, but need to fix compiler) version: options.version, pipeline: Pipeline::new( option_env!("CARGO_PKG_NAME"), @@ -109,40 +94,6 @@ impl BlobClient { }) } - pub fn with_no_credential(blob_url: &str, options: Option) -> Result { - let options = options.unwrap_or_default(); - let blob_url = Url::parse(blob_url)?; - if !blob_url.scheme().starts_with("http") { - return Err(azure_core::Error::message( - azure_core::error::ErrorKind::Other, - format!("{blob_url} must use http(s)"), - )); - } - - let mut segments = blob_url - .path_segments() - .expect("Failed to get path segments"); - let container_name = segments - .next() - .expect("Failed to parse container_name") - .to_string(); - let blob_name = segments.collect::>().join("/"); - - Ok(Self { - blob_name, - container_name, - blob_url, - version: options.version, - pipeline: Pipeline::new( - option_env!("CARGO_PKG_NAME"), - option_env!("CARGO_PKG_VERSION"), - options.client_options, - Vec::default(), - Vec::default(), - ), - }) - } - /// Returns the Url associated with this client. pub fn blob_url(&self) -> &Url { &self.blob_url diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index dd328acf8d..7792a67e02 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -430,7 +430,7 @@ use tokio::time; #[recorded::test] async fn test_sas(ctx: TestContext) -> Result<(), Box> { // SAS - let blob_url = ""; + let blob_url = "SAS URL HERE"; let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?; @@ -440,3 +440,25 @@ async fn test_sas(ctx: TestContext) -> Result<(), Box> { Ok(()) } + +#[recorded::test] +async fn test_regular(ctx: TestContext) -> Result<(), Box> { + let recording = ctx.recording(); + let endpoint = "https://ruststoragedev.blob.core.windows.net/"; + let container_name = "container0vgpjc2p"; + let blob_name = "blob1ear0rva"; + let credential = recording.credential(); + let blob_client = BlobClient::new( + endpoint, + container_name.into(), + blob_name.into(), + credential, + None, + )?; + + let blob_properties = blob_client.get_properties(None).await?; + let content_length = blob_properties.content_length()?; + assert_eq!(17, content_length.unwrap()); + + Ok(()) +} From 051a57d60933e4dcc67a9ed2868758d997c6f738 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 25 Aug 2025 18:02:39 -0700 Subject: [PATCH 09/11] Working parser PoC" --- .../azure_storage_blob/tests/blob_client.rs | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 7792a67e02..749654897f 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -430,7 +430,7 @@ use tokio::time; #[recorded::test] async fn test_sas(ctx: TestContext) -> Result<(), Box> { // SAS - let blob_url = "SAS URL HERE"; + let blob_url = "SAS URL"; let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?; @@ -441,24 +441,24 @@ async fn test_sas(ctx: TestContext) -> Result<(), Box> { Ok(()) } -#[recorded::test] -async fn test_regular(ctx: TestContext) -> Result<(), Box> { - let recording = ctx.recording(); - let endpoint = "https://ruststoragedev.blob.core.windows.net/"; - let container_name = "container0vgpjc2p"; - let blob_name = "blob1ear0rva"; - let credential = recording.credential(); - let blob_client = BlobClient::new( - endpoint, - container_name.into(), - blob_name.into(), - credential, - None, - )?; - - let blob_properties = blob_client.get_properties(None).await?; - let content_length = blob_properties.content_length()?; - assert_eq!(17, content_length.unwrap()); +// #[recorded::test] +// async fn test_regular(ctx: TestContext) -> Result<(), Box> { +// let recording = ctx.recording(); +// let endpoint = "https://ruststoragedev.blob.core.windows.net/"; +// let container_name = "container0vgpjc2p"; +// let blob_name = "s?cuffed?snapshot"; +// let credential = recording.credential(); +// let blob_client = BlobClient::new( +// endpoint, +// container_name.into(), +// blob_name.into(), +// credential, +// None, +// )?; + +// let blob_properties = blob_client.get_properties(None).await?; +// let content_length = blob_properties.content_length()?; +// assert_eq!(17, content_length.unwrap()); - Ok(()) -} +// Ok(()) +// } From a053f88718ab8597ca51d6edce8680f56e356e08 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 25 Aug 2025 18:05:49 -0700 Subject: [PATCH 10/11] nit --- .../src/clients/blob_client.rs | 140 +++++++++--------- 1 file changed, 72 insertions(+), 68 deletions(-) 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 c7997c294a..72ab7e494f 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -30,89 +30,94 @@ use azure_core::{ }; use std::sync::Arc; +pub fn url_encode(s: impl AsRef<[u8]>) -> String { + url::form_urlencoded::byte_serialize(s.as_ref()).collect::() +} + +pub fn rebuild_blob_url(input: &str) -> Option { + // Extract storage account + let storage_account = input + .split(".blob") + .next() + .or_else(|| input.split(".dfs").next())? + .split("//") + .last()?; + + // Extract container name + let path_segments = input.split('/').collect::>(); + let container_name = path_segments.get(3)?; + + // Find final '?' that has '=' after it + let mut query_start_index = None; + for (i, c) in input.char_indices().rev() { + if c == '?' && input[i + 1..].contains('=') { + query_start_index = Some(i); + break; + } + } + + let query_start = query_start_index?; + let query_string = &input[query_start..]; + + // Extract blob name + let container_pos = input.find(container_name)? + container_name.len() + 1; + let blob_raw = &input[container_pos..query_start]; + let blob_name = url_encode(blob_raw); + + // Rebuild the URL + let rebuilt_url = format!( + "https://{}.blob.core.windows.net/{}/{}{}", + storage_account, container_name, blob_name, query_string + ); + + Some(rebuilt_url) +} + /// A client to interact with a specific Azure storage blob, although that blob may not yet exist. pub struct BlobClient { pub(super) client: GeneratedBlobClient, } impl GeneratedBlobClient { - fn from_url_with_credential( + fn from_url( blob_url: &str, - credential: Arc, + credential: Option>, options: Option, ) -> Result { let options = options.unwrap_or_default(); - let blob_url = Url::parse(blob_url)?; - if !blob_url.scheme().starts_with("http") { - return Err(azure_core::Error::message( - azure_core::error::ErrorKind::Other, - format!("{blob_url} must use http(s)"), - )); - } - - let mut segments = blob_url - .path_segments() - .expect("Failed to get path segments"); - let container_name = segments - .next() - .expect("Failed to parse container_name") - .to_string(); - let blob_name = segments.collect::>().join("/"); - - let auth_policy: Arc = Arc::new(BearerTokenCredentialPolicy::new( - credential, - vec!["https://storage.azure.com/.default"], - )); - - Ok(Self { - blob_name, - container_name, - blob_url, - version: options.version, - pipeline: Pipeline::new( + let blob_url = rebuild_blob_url(blob_url).expect("Something exploded in building the URL."); + + println!("{}", blob_url.clone()); + + let pipeline = match credential { + Some(cred) => { + let auth_policy: Arc = Arc::new(BearerTokenCredentialPolicy::new( + cred, + vec!["https://storage.azure.com/.default"], + )); + Pipeline::new( + option_env!("CARGO_PKG_NAME"), + option_env!("CARGO_PKG_VERSION"), + options.client_options, + Vec::default(), + vec![auth_policy], + ) + } + None => Pipeline::new( option_env!("CARGO_PKG_NAME"), option_env!("CARGO_PKG_VERSION"), options.client_options, Vec::default(), - vec![auth_policy], + Vec::default(), ), - }) - } - - fn from_url_with_no_credential( - blob_url: &str, - options: Option, - ) -> Result { - let options = options.unwrap_or_default(); - let blob_url = Url::parse(blob_url)?; - if !blob_url.scheme().starts_with("http") { - return Err(azure_core::Error::message( - azure_core::error::ErrorKind::Other, - format!("{blob_url} must use http(s)"), - )); - } - - let mut segments = blob_url - .path_segments() - .expect("Failed to get path segments"); - let container_name = segments - .next() - .expect("Failed to parse container_name") - .to_string(); - let blob_name = segments.collect::>().join("/"); + }; Ok(Self { - blob_name, - container_name, - blob_url, + blob_name: "dummy".into(), + container_name: "dummy".into(), + blob_url: Url::parse(&blob_url)?, version: options.version, - pipeline: Pipeline::new( - option_env!("CARGO_PKG_NAME"), - option_env!("CARGO_PKG_VERSION"), - options.client_options, - Vec::default(), - Vec::default(), - ), + pipeline, }) } } @@ -155,8 +160,7 @@ impl BlobClient { .expect("Cannot be base") .extend([&container_name, &blob_name]); - let client = - GeneratedBlobClient::from_url_with_credential(url.as_str(), credential, Some(options))?; + let client = GeneratedBlobClient::from_url(url.as_str(), Some(credential), Some(options))?; Ok(Self { client }) } @@ -170,7 +174,7 @@ impl BlobClient { .push(storage_headers_policy); let url = Url::parse(blob_url)?; - let client = GeneratedBlobClient::from_url_with_no_credential(url.as_str(), Some(options))?; + let client = GeneratedBlobClient::from_url(url.as_str(), None, Some(options))?; Ok(Self { client }) } From dca1081227ad55453574df55ecaf4d39f919d23f Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Tue, 26 Aug 2025 12:09:36 -0700 Subject: [PATCH 11/11] Re_factor --- .../src/clients/blob_client.rs | 49 +------------------ .../azure_storage_blob/tests/blob_client.rs | 42 ++++++++-------- 2 files changed, 23 insertions(+), 68 deletions(-) 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 72ab7e494f..1bc3f9501e 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -30,49 +30,6 @@ use azure_core::{ }; use std::sync::Arc; -pub fn url_encode(s: impl AsRef<[u8]>) -> String { - url::form_urlencoded::byte_serialize(s.as_ref()).collect::() -} - -pub fn rebuild_blob_url(input: &str) -> Option { - // Extract storage account - let storage_account = input - .split(".blob") - .next() - .or_else(|| input.split(".dfs").next())? - .split("//") - .last()?; - - // Extract container name - let path_segments = input.split('/').collect::>(); - let container_name = path_segments.get(3)?; - - // Find final '?' that has '=' after it - let mut query_start_index = None; - for (i, c) in input.char_indices().rev() { - if c == '?' && input[i + 1..].contains('=') { - query_start_index = Some(i); - break; - } - } - - let query_start = query_start_index?; - let query_string = &input[query_start..]; - - // Extract blob name - let container_pos = input.find(container_name)? + container_name.len() + 1; - let blob_raw = &input[container_pos..query_start]; - let blob_name = url_encode(blob_raw); - - // Rebuild the URL - let rebuilt_url = format!( - "https://{}.blob.core.windows.net/{}/{}{}", - storage_account, container_name, blob_name, query_string - ); - - Some(rebuilt_url) -} - /// A client to interact with a specific Azure storage blob, although that blob may not yet exist. pub struct BlobClient { pub(super) client: GeneratedBlobClient, @@ -85,9 +42,6 @@ impl GeneratedBlobClient { options: Option, ) -> Result { let options = options.unwrap_or_default(); - let blob_url = rebuild_blob_url(blob_url).expect("Something exploded in building the URL."); - - println!("{}", blob_url.clone()); let pipeline = match credential { Some(cred) => { @@ -115,7 +69,7 @@ impl GeneratedBlobClient { Ok(Self { blob_name: "dummy".into(), container_name: "dummy".into(), - blob_url: Url::parse(&blob_url)?, + blob_url: Url::parse(blob_url)?, version: options.version, pipeline, }) @@ -156,6 +110,7 @@ impl BlobClient { } // In regular ctor, since struct now holds blob_url, we have to build the blob_url given endpoint + container_name + blob_name + // This is a URL tool so automatically encodes :D url.path_segments_mut() .expect("Cannot be base") .extend([&container_name, &blob_name]); diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 749654897f..df8ffdb6d8 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -430,7 +430,7 @@ use tokio::time; #[recorded::test] async fn test_sas(ctx: TestContext) -> Result<(), Box> { // SAS - let blob_url = "SAS URL"; + let blob_url = ""; let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?; @@ -441,24 +441,24 @@ async fn test_sas(ctx: TestContext) -> Result<(), Box> { Ok(()) } -// #[recorded::test] -// async fn test_regular(ctx: TestContext) -> Result<(), Box> { -// let recording = ctx.recording(); -// let endpoint = "https://ruststoragedev.blob.core.windows.net/"; -// let container_name = "container0vgpjc2p"; -// let blob_name = "s?cuffed?snapshot"; -// let credential = recording.credential(); -// let blob_client = BlobClient::new( -// endpoint, -// container_name.into(), -// blob_name.into(), -// credential, -// None, -// )?; - -// let blob_properties = blob_client.get_properties(None).await?; -// let content_length = blob_properties.content_length()?; -// assert_eq!(17, content_length.unwrap()); +#[recorded::test] +async fn test_regular(ctx: TestContext) -> Result<(), Box> { + let recording = ctx.recording(); + let endpoint = "https://ruststoragedev.blob.core.windows.net/"; + let container_name = "container0vgpjc2p"; + let blob_name = "s?cuffed?snapshot"; + let credential = recording.credential(); + let blob_client = BlobClient::new( + endpoint, + container_name.into(), + blob_name.into(), + credential, + None, + )?; + + let blob_properties = blob_client.get_properties(None).await?; + let content_length = blob_properties.content_length()?; + assert_eq!(17, content_length.unwrap()); -// Ok(()) -// } + Ok(()) +}