From 4e89dc9ff9e030a984c0fb3afab270a8e5f2bf55 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 2 Jun 2025 12:49:38 -0700 Subject: [PATCH 1/6] Clean regen against latest feature/blob-tsp commit --- .../src/generated/clients/append_blob_client.rs | 1 - .../azure_storage_blob/src/generated/clients/blob_client.rs | 1 - .../src/generated/clients/block_blob_client.rs | 1 - .../src/generated/clients/page_blob_client.rs | 3 --- sdk/storage/azure_storage_blob/tsp-location.yaml | 2 +- 5 files changed, 1 insertion(+), 7 deletions(-) diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/append_blob_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/append_blob_client.rs index a9bfe32a7e..6e052566f2 100644 --- a/sdk/storage/azure_storage_blob/src/generated/clients/append_blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/generated/clients/append_blob_client.rs @@ -334,7 +334,6 @@ impl AppendBlobClient { let mut request = Request::new(url, Method::Put); request.insert_header("accept", "application/json"); request.insert_header("content-length", content_length.to_string()); - request.insert_header("content-type", "application/xml"); if let Some(if_match) = options.if_match { request.insert_header("if-match", if_match); } 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 974e609213..59dc308c8e 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 @@ -801,7 +801,6 @@ impl BlobClient { } let mut request = Request::new(url, Method::Head); request.insert_header("accept", "application/json"); - request.insert_header("content-type", ""); if let Some(if_match) = options.if_match { request.insert_header("if-match", if_match); } diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/block_blob_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/block_blob_client.rs index 4c3380885c..8fa6879811 100644 --- a/sdk/storage/azure_storage_blob/src/generated/clients/block_blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/generated/clients/block_blob_client.rs @@ -304,7 +304,6 @@ impl BlockBlobClient { if let Some(transactional_content_md5) = options.transactional_content_md5 { request.insert_header("content-md5", base64::encode(transactional_content_md5)); } - request.insert_header("content-type", "application/xml"); if let Some(if_match) = options.if_match { request.insert_header("if-match", if_match); } diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/page_blob_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/page_blob_client.rs index 5e2ffc6ba9..158946f234 100644 --- a/sdk/storage/azure_storage_blob/src/generated/clients/page_blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/generated/clients/page_blob_client.rs @@ -123,7 +123,6 @@ impl PageBlobClient { let mut request = Request::new(url, Method::Put); request.insert_header("accept", "application/json"); request.insert_header("content-length", content_length.to_string()); - request.insert_header("content-type", "application/xml"); if let Some(if_match) = options.if_match { request.insert_header("if-match", if_match); } @@ -276,7 +275,6 @@ impl PageBlobClient { let mut request = Request::new(url, Method::Put); request.insert_header("accept", "application/json"); request.insert_header("content-length", content_length.to_string()); - request.insert_header("content-type", ""); if let Some(if_match) = options.if_match { request.insert_header("if-match", if_match); } @@ -805,7 +803,6 @@ impl PageBlobClient { let mut request = Request::new(url, Method::Put); request.insert_header("accept", "application/json"); request.insert_header("content-length", content_length.to_string()); - request.insert_header("content-type", "application/xml"); if let Some(if_match) = options.if_match { request.insert_header("if-match", if_match); } diff --git a/sdk/storage/azure_storage_blob/tsp-location.yaml b/sdk/storage/azure_storage_blob/tsp-location.yaml index 630396183b..04d816fc10 100644 --- a/sdk/storage/azure_storage_blob/tsp-location.yaml +++ b/sdk/storage/azure_storage_blob/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/storage/Microsoft.BlobStorage -commit: a0463864e021c00771eb01510e2eecd800ad05e2 +commit: f848853e5880d2b1e3375a6517aa59ada57d60d3 repo: Azure/azure-rest-api-specs additionalDirectories: From c2d1bddef4b9e3bd0507871748e08dfb7c3292f5 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 2 Jun 2025 13:59:50 -0700 Subject: [PATCH 2/6] get/set tags --- .../src/clients/blob_client.rs | 32 +++++- .../src/generated/clients/blob_client.rs | 9 +- .../src/generated/models/header_traits.rs | 61 ++--------- .../src/generated/models/pub_models.rs | 4 - sdk/storage/azure_storage_blob/src/lib.rs | 12 +-- .../azure_storage_blob/tests/blob_client.rs | 100 +++++++++++++++++- .../azure_storage_blob/tsp-location.yaml | 2 +- .../azure_storage_blob_test/src/lib.rs | 31 +++++- 8 files changed, 174 insertions(+), 77 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 cf0a42c751..3c974a55d1 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -10,9 +10,9 @@ use crate::{ }, models::{ AccessTier, BlobClientDeleteOptions, BlobClientDownloadOptions, - BlobClientGetPropertiesOptions, BlobClientSetMetadataOptions, - BlobClientSetPropertiesOptions, BlobClientSetTierOptions, - BlockBlobClientCommitBlockListOptions, BlockBlobClientUploadOptions, BlockList, + BlobClientGetPropertiesOptions, BlobClientGetTagsOptions, BlobClientSetMetadataOptions, + BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, BlobClientSetTierOptions, + BlobTags, BlockBlobClientCommitBlockListOptions, BlockBlobClientUploadOptions, BlockList, BlockListType, BlockLookupList, }, pipeline::StorageHeadersPolicy, @@ -211,4 +211,30 @@ impl BlobClient { ) -> Result> { self.client.set_tier(tier, options).await } + + /// Sets tags on a blob. Note that each call to this operation replaces all existing tags. To remove + /// all tags from the blob, call this operation with no tags specified. + /// + /// # Arguments + /// + /// * `options` - Optional configuration for the request. + pub async fn set_tags( + &self, + tags: RequestContent, + options: Option>, + ) -> Result> { + self.client.set_tags(tags, options).await + } + + /// Gets the tags on a blob. + /// + /// # Arguments + /// + /// * `options` - Optional configuration for the request. + pub async fn get_tags( + &self, + options: Option>, + ) -> Result> { + self.client.get_tags(options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs index 59dc308c8e..7f0acf0b8b 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 @@ -19,10 +19,9 @@ use crate::generated::{ BlobClientSetExpiryOptions, BlobClientSetExpiryResult, BlobClientSetImmutabilityPolicyOptions, BlobClientSetImmutabilityPolicyResult, BlobClientSetLegalHoldOptions, BlobClientSetLegalHoldResult, BlobClientSetMetadataOptions, - BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, BlobClientSetTagsResult, - BlobClientSetTierOptions, BlobClientStartCopyFromUrlOptions, - BlobClientStartCopyFromUrlResult, BlobClientUndeleteOptions, BlobClientUndeleteResult, - BlobExpiryOptions, BlobTags, + BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, BlobClientSetTierOptions, + BlobClientStartCopyFromUrlOptions, BlobClientStartCopyFromUrlResult, + BlobClientUndeleteOptions, BlobClientUndeleteResult, BlobExpiryOptions, BlobTags, }, }; use azure_core::{ @@ -1282,7 +1281,7 @@ impl BlobClient { &self, tags: RequestContent, options: Option>, - ) -> Result> { + ) -> Result> { let options = options.unwrap_or_default(); let ctx = Context::with_context(&options.method_options.context); let mut url = self.endpoint.clone(); diff --git a/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs b/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs index cb132ce3a1..b685391f5b 100644 --- a/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs +++ b/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs @@ -11,20 +11,20 @@ use super::{ BlobClientCreateSnapshotResult, BlobClientDeleteImmutabilityPolicyResult, BlobClientDownloadResult, BlobClientGetAccountInfoResult, BlobClientGetPropertiesResult, BlobClientReleaseLeaseResult, BlobClientRenewLeaseResult, BlobClientSetExpiryResult, - BlobClientSetImmutabilityPolicyResult, BlobClientSetLegalHoldResult, BlobClientSetTagsResult, + BlobClientSetImmutabilityPolicyResult, BlobClientSetLegalHoldResult, BlobClientStartCopyFromUrlResult, BlobClientUndeleteResult, BlobContainerClientAcquireLeaseResult, BlobContainerClientBreakLeaseResult, BlobContainerClientChangeLeaseResult, BlobContainerClientGetAccountInfoResult, BlobContainerClientGetPropertiesResult, BlobContainerClientReleaseLeaseResult, BlobContainerClientRenameResult, BlobContainerClientRenewLeaseResult, BlobContainerClientRestoreResult, BlobContainerClientSetAccessPolicyResult, - BlobImmutabilityPolicyMode, BlobServiceClientGetAccountInfoResult, BlobTags, BlobType, + BlobImmutabilityPolicyMode, BlobServiceClientGetAccountInfoResult, BlobType, BlockBlobClientCommitBlockListResult, BlockBlobClientPutBlobFromUrlResult, BlockBlobClientQueryResult, BlockBlobClientStageBlockFromUrlResult, BlockBlobClientStageBlockResult, BlockBlobClientUploadResult, BlockList, CopyStatus, - FilterBlobSegment, LeaseDuration, LeaseState, LeaseStatus, ListBlobsFlatSegmentResponse, - ListBlobsHierarchySegmentResponse, PageBlobClientClearPagesResult, - PageBlobClientCopyIncrementalResult, PageBlobClientCreateResult, PageBlobClientResizeResult, + FilterBlobSegment, LeaseDuration, LeaseState, LeaseStatus, ListBlobsHierarchySegmentResponse, + PageBlobClientClearPagesResult, PageBlobClientCopyIncrementalResult, + PageBlobClientCreateResult, PageBlobClientResizeResult, PageBlobClientUpdateSequenceNumberResult, PageBlobClientUploadPagesFromUrlResult, PageBlobClientUploadPagesResult, PageList, PublicAccessType, RehydratePriority, SignedIdentifier, SkuName, StorageServiceStats, UserDelegationKey, @@ -1380,18 +1380,6 @@ impl BlobClientSetLegalHoldResultHeaders for Response Result>; -} - -impl BlobClientSetTagsResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| date::parse_rfc7231(h.as_str())) - } -} - /// Provides access to typed response headers for `BlobClient::start_copy_from_url()` pub trait BlobClientStartCopyFromUrlResultHeaders: private::Sealed { fn date(&self) -> Result>; @@ -1558,7 +1546,6 @@ impl BlobContainerClientChangeLeaseResultHeaders /// Provides access to typed response headers for `BlobContainerClient::get_account_info()` pub trait BlobContainerClientGetAccountInfoResultHeaders: private::Sealed { - fn date(&self) -> Result>; fn account_kind(&self) -> Result>; fn is_hierarchical_namespace_enabled(&self) -> Result>; fn sku_name(&self) -> Result>; @@ -1567,11 +1554,6 @@ pub trait BlobContainerClientGetAccountInfoResultHeaders: private::Sealed { impl BlobContainerClientGetAccountInfoResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| date::parse_rfc7231(h.as_str())) - } - /// Identifies the account kind fn account_kind(&self) -> Result> { Headers::get_optional_as(self.headers(), &ACCOUNT_KIND) @@ -1823,18 +1805,6 @@ impl BlobServiceClientGetAccountInfoResultHeaders } } -/// Provides access to typed response headers for `BlobClient::get_tags()` -pub trait BlobTagsHeaders: private::Sealed { - fn date(&self) -> Result>; -} - -impl BlobTagsHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| date::parse_rfc7231(h.as_str())) - } -} - /// Provides access to typed response headers for `BlockBlobClient::commit_block_list()` pub trait BlockBlobClientCommitBlockListResultHeaders: private::Sealed { fn content_md5(&self) -> Result>>; @@ -2373,18 +2343,6 @@ impl FilterBlobSegmentHeaders for Response { } } -/// Provides access to typed response headers for `BlobContainerClient::list_blob_flat_segment()` -pub trait ListBlobsFlatSegmentResponseHeaders: private::Sealed { - fn date(&self) -> Result>; -} - -impl ListBlobsFlatSegmentResponseHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| date::parse_rfc7231(h.as_str())) - } -} - /// Provides access to typed response headers for `BlobContainerClient::list_blob_hierarchy_segment()` pub trait ListBlobsHierarchySegmentResponseHeaders: private::Sealed { fn date(&self) -> Result>; @@ -2858,16 +2816,16 @@ mod private { BlobClientGetAccountInfoResult, BlobClientGetPropertiesResult, BlobClientReleaseLeaseResult, BlobClientRenewLeaseResult, BlobClientSetExpiryResult, BlobClientSetImmutabilityPolicyResult, BlobClientSetLegalHoldResult, - BlobClientSetTagsResult, BlobClientStartCopyFromUrlResult, BlobClientUndeleteResult, + BlobClientStartCopyFromUrlResult, BlobClientUndeleteResult, BlobContainerClientAcquireLeaseResult, BlobContainerClientBreakLeaseResult, BlobContainerClientChangeLeaseResult, BlobContainerClientGetAccountInfoResult, BlobContainerClientGetPropertiesResult, BlobContainerClientReleaseLeaseResult, BlobContainerClientRenameResult, BlobContainerClientRenewLeaseResult, BlobContainerClientRestoreResult, BlobContainerClientSetAccessPolicyResult, - BlobServiceClientGetAccountInfoResult, BlobTags, BlockBlobClientCommitBlockListResult, + BlobServiceClientGetAccountInfoResult, BlockBlobClientCommitBlockListResult, BlockBlobClientPutBlobFromUrlResult, BlockBlobClientQueryResult, BlockBlobClientStageBlockFromUrlResult, BlockBlobClientStageBlockResult, - BlockBlobClientUploadResult, BlockList, FilterBlobSegment, ListBlobsFlatSegmentResponse, + BlockBlobClientUploadResult, BlockList, FilterBlobSegment, ListBlobsHierarchySegmentResponse, PageBlobClientClearPagesResult, PageBlobClientCopyIncrementalResult, PageBlobClientCreateResult, PageBlobClientResizeResult, PageBlobClientUpdateSequenceNumberResult, @@ -2897,7 +2855,6 @@ mod private { impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} - impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} @@ -2911,7 +2868,6 @@ mod private { impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} - impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} @@ -2920,7 +2876,6 @@ mod private { impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} - impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} diff --git a/sdk/storage/azure_storage_blob/src/generated/models/pub_models.rs b/sdk/storage/azure_storage_blob/src/generated/models/pub_models.rs index 221c9f984e..655516ac24 100644 --- a/sdk/storage/azure_storage_blob/src/generated/models/pub_models.rs +++ b/sdk/storage/azure_storage_blob/src/generated/models/pub_models.rs @@ -159,10 +159,6 @@ pub struct BlobClientSetImmutabilityPolicyResult; #[derive(SafeDebug)] pub struct BlobClientSetLegalHoldResult; -/// Contains results for `BlobClient::set_tags()` -#[derive(SafeDebug)] -pub struct BlobClientSetTagsResult; - /// Contains results for `BlobClient::start_copy_from_url()` #[derive(SafeDebug)] pub struct BlobClientStartCopyFromUrlResult; diff --git a/sdk/storage/azure_storage_blob/src/lib.rs b/sdk/storage/azure_storage_blob/src/lib.rs index 0e8fa822ae..041870ce13 100644 --- a/sdk/storage/azure_storage_blob/src/lib.rs +++ b/sdk/storage/azure_storage_blob/src/lib.rs @@ -18,13 +18,13 @@ pub mod models { AccessTier, ArchiveStatus, BlobClientDeleteOptions, BlobClientDownloadOptions, BlobClientDownloadResult, BlobClientDownloadResultHeaders, BlobClientGetPropertiesOptions, BlobClientGetPropertiesResult, BlobClientGetPropertiesResultHeaders, - BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTierOptions, - BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, - BlobContainerClientGetPropertiesOptions, BlobContainerClientGetPropertiesResult, - BlobContainerClientGetPropertiesResultHeaders, + BlobClientGetTagsOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, + BlobClientSetTagsOptions, BlobClientSetTierOptions, BlobContainerClientCreateOptions, + BlobContainerClientDeleteOptions, BlobContainerClientGetPropertiesOptions, + BlobContainerClientGetPropertiesResult, BlobContainerClientGetPropertiesResultHeaders, BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientSetMetadataOptions, - BlobImmutabilityPolicyMode, BlobServiceClientGetPropertiesOptions, BlobType, - BlockBlobClientCommitBlockListOptions, BlockBlobClientCommitBlockListResult, + BlobImmutabilityPolicyMode, BlobServiceClientGetPropertiesOptions, BlobTag, BlobTags, + BlobType, BlockBlobClientCommitBlockListOptions, BlockBlobClientCommitBlockListResult, BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions, BlockBlobClientStageBlockResult, BlockBlobClientUploadOptions, BlockBlobClientUploadResult, BlockList, BlockListType, BlockLookupList, CopyStatus, LeaseState, LeaseStatus, diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 16a9d3a2de..7091dcc715 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -8,10 +8,12 @@ use azure_core::{ use azure_core_test::{recorded, TestContext}; use azure_storage_blob::models::{ AccessTier, BlobClientDownloadResultHeaders, BlobClientGetPropertiesResultHeaders, - BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlockBlobClientUploadOptions, - LeaseState, + BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobTag, BlobTags, + BlockBlobClientUploadOptions, LeaseState, +}; +use azure_storage_blob_test::{ + create_test_blob, get_blob_name, get_container_client, test_blob_tag_equality, }; -use azure_storage_blob_test::{create_test_blob, get_blob_name, get_container_client}; use std::{collections::HashMap, error::Error}; #[recorded::test] @@ -276,3 +278,95 @@ async fn test_set_access_tier(ctx: TestContext) -> Result<(), Box> { container_client.delete_container(None).await?; Ok(()) } + +#[recorded::test] +async fn test_blob_tags(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 Tags with Tags Specified + let blob_tag_1 = BlobTag { + key: Some("hello".to_string()), + value: Some("world".to_string()), + }; + let blob_tag_2 = BlobTag { + key: Some("ferris".to_string()), + value: Some("crab".to_string()), + }; + let blob_tags = BlobTags { + blob_tag_set: Some(vec![blob_tag_1, blob_tag_2]), + }; + blob_client + .set_tags(RequestContent::try_from(blob_tags.clone())?, None) + .await?; + + // Assert + let response_tags = blob_client.get_tags(None).await?.into_body().await?; + assert!(test_blob_tag_equality(blob_tags, response_tags)); + + // Set Tags with No Tags (Clear Tags) + blob_client + .set_tags( + RequestContent::try_from(BlobTags { + blob_tag_set: Some(vec![]), + })?, + None, + ) + .await?; + + // Assert + let response_tags = blob_client.get_tags(None).await?.into_body().await?; + assert!(response_tags.blob_tag_set.is_none()); + + container_client.delete_container(None).await?; + Ok(()) +} + +#[recorded::test] +async fn test_blob_tags2(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 Tags with Tags Specified + let blob_tag_1 = BlobTag { + key: Some("hello".to_string()), + value: Some("world".to_string()), + }; + let blob_tag_2 = BlobTag { + key: Some("ferris".to_string()), + value: Some("crab".to_string()), + }; + let blob_tags = BlobTags { + blob_tag_set: Some(vec![blob_tag_1, blob_tag_2]), + }; + blob_client + .set_tags(RequestContent::try_from(blob_tags.clone())?, None) + .await?; + + // Assert + let response_tags = blob_client.get_tags(None).await?.into_body().await?; + assert!(test_blob_tag_equality(blob_tags, response_tags)); + + // Set Tags with No Tags (Clear Tags) + blob_client + .set_tags( + RequestContent::try_from(BlobTags { + blob_tag_set: Some(vec![]), + })?, + None, + ) + .await?; + + // Assert + let response_tags = blob_client.get_tags(None).await?.into_body().await?; + assert!(response_tags.blob_tag_set.is_none()); + + container_client.delete_container(None).await?; + Ok(()) +} diff --git a/sdk/storage/azure_storage_blob/tsp-location.yaml b/sdk/storage/azure_storage_blob/tsp-location.yaml index 04d816fc10..a9cea3edf0 100644 --- a/sdk/storage/azure_storage_blob/tsp-location.yaml +++ b/sdk/storage/azure_storage_blob/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/storage/Microsoft.BlobStorage -commit: f848853e5880d2b1e3375a6517aa59ada57d60d3 +commit: f037d99bdbc34d4fe00d1784abd10f4a43d9e475 repo: Azure/azure-rest-api-specs additionalDirectories: diff --git a/sdk/storage/azure_storage_blob_test/src/lib.rs b/sdk/storage/azure_storage_blob_test/src/lib.rs index d8fe702769..17567e7228 100644 --- a/sdk/storage/azure_storage_blob_test/src/lib.rs +++ b/sdk/storage/azure_storage_blob_test/src/lib.rs @@ -7,9 +7,11 @@ use azure_core::{ }; use azure_core_test::Recording; use azure_storage_blob::{ - models::BlockBlobClientUploadResult, BlobClient, BlobContainerClient, - BlobContainerClientOptions, BlobServiceClient, BlobServiceClientOptions, + models::{BlobTags, BlockBlobClientUploadResult}, + BlobClient, BlobContainerClient, BlobContainerClientOptions, BlobServiceClient, + BlobServiceClientOptions, }; +use std::collections::HashMap; /// Takes in a Recording instance and returns an instrumented options bag and endpoint. /// @@ -112,3 +114,28 @@ pub async fn create_test_blob( ) .await } + +/// Takes in two separate BlobTags instances and compares their contents to check for equality. +/// +/// # Arguments +/// +/// * `tags1` - The first BlobTags to be compared. +/// * `tags2` - The second BlobTags to be compared. +pub fn test_blob_tag_equality(tags1: BlobTags, tags2: BlobTags) -> bool { + let mut count_map = HashMap::new(); + // Iterate through first set of tags, populate HashMap + for blob_tag in tags1.blob_tag_set.unwrap() { + count_map.insert(blob_tag.key.unwrap(), blob_tag.value.unwrap()); + } + // Iterate through second set of tags + for blob_tag in tags2.blob_tag_set.unwrap() { + // If tag is not found, return false + if !count_map.contains_key(&blob_tag.key.clone().unwrap()) { + return false; + } else { + count_map.remove(&blob_tag.key.unwrap()); + } + } + // Ensure HashMap has been completely consumed + count_map.is_empty() +} From 1058418c8f77fb9ac6a067510d9d92f2601f017a Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 2 Jun 2025 17:46:15 -0700 Subject: [PATCH 3/6] get_account_info all clients --- sdk/storage/assets.json | 2 +- .../src/clients/blob_client.rs | 23 +++++++--- .../src/clients/blob_container_client.rs | 22 ++++++++-- .../src/clients/blob_service_client.rs | 19 +++++++- .../src/generated/models/header_traits.rs | 12 ------ sdk/storage/azure_storage_blob/src/lib.rs | 28 ++++++------ .../azure_storage_blob/tests/blob_client.rs | 43 ++++--------------- .../tests/blob_container_client.rs | 20 +++++++++ .../tests/blob_service_client.rs | 24 ++++++++++- .../azure_storage_blob/tsp-location.yaml | 2 +- 10 files changed, 124 insertions(+), 71 deletions(-) diff --git a/sdk/storage/assets.json b/sdk/storage/assets.json index f749f6b24a..deae74cc58 100644 --- a/sdk/storage/assets.json +++ b/sdk/storage/assets.json @@ -1,6 +1,6 @@ { "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "rust", - "Tag": "rust/azure_storage_blob_231754d877", + "Tag": "rust/azure_storage_blob_de70cafd7a", "TagPrefix": "rust/azure_storage_blob" } \ No newline at end of file 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 3c974a55d1..a49bb14de0 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -4,16 +4,16 @@ use crate::{ generated::clients::BlobClient as GeneratedBlobClient, generated::models::{ - BlobClientDownloadResult, BlobClientGetPropertiesResult, + BlobClientDownloadResult, BlobClientGetAccountInfoResult, BlobClientGetPropertiesResult, BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult, BlockBlobClientUploadResult, }, models::{ AccessTier, BlobClientDeleteOptions, BlobClientDownloadOptions, - BlobClientGetPropertiesOptions, BlobClientGetTagsOptions, BlobClientSetMetadataOptions, - BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, BlobClientSetTierOptions, - BlobTags, BlockBlobClientCommitBlockListOptions, BlockBlobClientUploadOptions, BlockList, - BlockListType, BlockLookupList, + BlobClientGetAccountInfoOptions, BlobClientGetPropertiesOptions, BlobClientGetTagsOptions, + BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, + BlobClientSetTierOptions, BlobTags, BlockBlobClientCommitBlockListOptions, + BlockBlobClientUploadOptions, BlockList, BlockListType, BlockLookupList, }, pipeline::StorageHeadersPolicy, BlobClientOptions, BlockBlobClient, @@ -237,4 +237,17 @@ impl BlobClient { ) -> Result> { self.client.get_tags(options).await } + + /// Gets information related to the Storage account in which the blob resides. + /// This includes the `sku_name` and `account_kind`. + /// + /// # Arguments + /// + /// * `options` - Optional configuration for the request. + pub async fn get_account_info( + &self, + options: Option>, + ) -> Result> { + self.client.get_account_info(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 d540205c80..823d7f8a0c 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 @@ -3,11 +3,14 @@ use crate::{ generated::clients::BlobContainerClient as GeneratedBlobContainerClient, - generated::models::BlobContainerClientGetPropertiesResult, + generated::models::{ + BlobContainerClientGetAccountInfoResult, BlobContainerClientGetPropertiesResult, + }, models::{ BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, - BlobContainerClientGetPropertiesOptions, BlobContainerClientListBlobFlatSegmentOptions, - BlobContainerClientSetMetadataOptions, ListBlobsFlatSegmentResponse, + BlobContainerClientGetAccountInfoOptions, BlobContainerClientGetPropertiesOptions, + BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientSetMetadataOptions, + ListBlobsFlatSegmentResponse, }, pipeline::StorageHeadersPolicy, BlobClient, BlobContainerClientOptions, @@ -157,4 +160,17 @@ impl BlobContainerClient { ) -> Result> { self.client.list_blob_flat_segment(options) } + + /// Gets information related to the Storage account in which the container resides. + /// This includes the `sku_name` and `account_kind`. + /// + /// # Arguments + /// + /// * `options` - Optional configuration for the request. + pub async fn get_account_info( + &self, + options: Option>, + ) -> Result> { + self.client.get_account_info(options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs b/sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs index 9f593c9ec7..1c111c8c81 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs @@ -3,7 +3,11 @@ use crate::{ generated::clients::BlobServiceClient as GeneratedBlobServiceClient, - models::{BlobServiceClientGetPropertiesOptions, StorageServiceProperties}, + generated::models::BlobServiceClientGetAccountInfoResult, + models::{ + BlobServiceClientGetAccountInfoOptions, BlobServiceClientGetPropertiesOptions, + StorageServiceProperties, + }, pipeline::StorageHeadersPolicy, BlobContainerClient, BlobServiceClientOptions, }; @@ -89,4 +93,17 @@ impl BlobServiceClient { ) -> Result> { self.client.get_properties(options).await } + + /// Gets information related to the Storage account. + /// This includes the `sku_name` and `account_kind`. + /// + /// # Arguments + /// + /// * `options` - Optional configuration for the request. + pub async fn get_account_info( + &self, + options: Option>, + ) -> Result> { + self.client.get_account_info(options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs b/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs index b685391f5b..09798f2fe8 100644 --- a/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs +++ b/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs @@ -901,18 +901,12 @@ impl BlobClientDownloadResultHeaders for Response { /// Provides access to typed response headers for `BlobClient::get_account_info()` pub trait BlobClientGetAccountInfoResultHeaders: private::Sealed { - fn date(&self) -> Result>; fn account_kind(&self) -> Result>; fn is_hierarchical_namespace_enabled(&self) -> Result>; fn sku_name(&self) -> Result>; } impl BlobClientGetAccountInfoResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| date::parse_rfc7231(h.as_str())) - } - /// Identifies the account kind fn account_kind(&self) -> Result> { Headers::get_optional_as(self.headers(), &ACCOUNT_KIND) @@ -1775,7 +1769,6 @@ impl BlobContainerClientSetAccessPolicyResultHeaders /// Provides access to typed response headers for `BlobServiceClient::get_account_info()` pub trait BlobServiceClientGetAccountInfoResultHeaders: private::Sealed { - fn date(&self) -> Result>; fn account_kind(&self) -> Result>; fn is_hierarchical_namespace_enabled(&self) -> Result>; fn sku_name(&self) -> Result>; @@ -1784,11 +1777,6 @@ pub trait BlobServiceClientGetAccountInfoResultHeaders: private::Sealed { impl BlobServiceClientGetAccountInfoResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| date::parse_rfc7231(h.as_str())) - } - /// Identifies the account kind fn account_kind(&self) -> Result> { Headers::get_optional_as(self.headers(), &ACCOUNT_KIND) diff --git a/sdk/storage/azure_storage_blob/src/lib.rs b/sdk/storage/azure_storage_blob/src/lib.rs index 041870ce13..78a5556255 100644 --- a/sdk/storage/azure_storage_blob/src/lib.rs +++ b/sdk/storage/azure_storage_blob/src/lib.rs @@ -15,20 +15,22 @@ pub use clients::*; pub mod models { pub use crate::generated::models::{ - AccessTier, ArchiveStatus, BlobClientDeleteOptions, BlobClientDownloadOptions, - BlobClientDownloadResult, BlobClientDownloadResultHeaders, BlobClientGetPropertiesOptions, - BlobClientGetPropertiesResult, BlobClientGetPropertiesResultHeaders, - BlobClientGetTagsOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, - BlobClientSetTagsOptions, BlobClientSetTierOptions, BlobContainerClientCreateOptions, - BlobContainerClientDeleteOptions, BlobContainerClientGetPropertiesOptions, - BlobContainerClientGetPropertiesResult, BlobContainerClientGetPropertiesResultHeaders, + AccessTier, AccountKind, ArchiveStatus, BlobClientDeleteOptions, BlobClientDownloadOptions, + BlobClientDownloadResultHeaders, BlobClientGetAccountInfoOptions, + BlobClientGetAccountInfoResultHeaders, BlobClientGetPropertiesOptions, + BlobClientGetPropertiesResultHeaders, BlobClientGetTagsOptions, + BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, + BlobClientSetTierOptions, BlobContainerClientCreateOptions, + BlobContainerClientDeleteOptions, BlobContainerClientGetAccountInfoOptions, + BlobContainerClientGetAccountInfoResultHeaders, BlobContainerClientGetPropertiesOptions, + BlobContainerClientGetPropertiesResultHeaders, BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientSetMetadataOptions, - BlobImmutabilityPolicyMode, BlobServiceClientGetPropertiesOptions, BlobTag, BlobTags, - BlobType, BlockBlobClientCommitBlockListOptions, BlockBlobClientCommitBlockListResult, + BlobImmutabilityPolicyMode, BlobServiceClientGetAccountInfoOptions, + BlobServiceClientGetAccountInfoResultHeaders, BlobServiceClientGetPropertiesOptions, + BlobTag, BlobTags, BlobType, BlockBlobClientCommitBlockListOptions, BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions, - BlockBlobClientStageBlockResult, BlockBlobClientUploadOptions, BlockBlobClientUploadResult, - BlockList, BlockListType, BlockLookupList, CopyStatus, LeaseState, LeaseStatus, - ListBlobsFlatSegmentResponse, PublicAccessType, RehydratePriority, - StorageServiceProperties, + BlockBlobClientUploadOptions, BlockBlobClientUploadResult, BlockList, BlockListType, + BlockLookupList, CopyStatus, LeaseState, LeaseStatus, ListBlobsFlatSegmentResponse, + PublicAccessType, RehydratePriority, StorageServiceProperties, }; } diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 7091dcc715..75ba28fd1f 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -7,7 +7,8 @@ use azure_core::{ }; use azure_core_test::{recorded, TestContext}; use azure_storage_blob::models::{ - AccessTier, BlobClientDownloadResultHeaders, BlobClientGetPropertiesResultHeaders, + AccessTier, AccountKind, BlobClientDownloadResultHeaders, + BlobClientGetAccountInfoResultHeaders, BlobClientGetPropertiesResultHeaders, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobTag, BlobTags, BlockBlobClientUploadOptions, LeaseState, }; @@ -326,47 +327,21 @@ async fn test_blob_tags(ctx: TestContext) -> Result<(), Box> { } #[recorded::test] -async fn test_blob_tags2(ctx: TestContext) -> Result<(), Box> { +async fn test_get_account_info(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 Tags with Tags Specified - let blob_tag_1 = BlobTag { - key: Some("hello".to_string()), - value: Some("world".to_string()), - }; - let blob_tag_2 = BlobTag { - key: Some("ferris".to_string()), - value: Some("crab".to_string()), - }; - let blob_tags = BlobTags { - blob_tag_set: Some(vec![blob_tag_1, blob_tag_2]), - }; - blob_client - .set_tags(RequestContent::try_from(blob_tags.clone())?, None) - .await?; + // Act + let response = blob_client.get_account_info(None).await?; // Assert - let response_tags = blob_client.get_tags(None).await?.into_body().await?; - assert!(test_blob_tag_equality(blob_tags, response_tags)); - - // Set Tags with No Tags (Clear Tags) - blob_client - .set_tags( - RequestContent::try_from(BlobTags { - blob_tag_set: Some(vec![]), - })?, - None, - ) - .await?; + let sku_name = response.sku_name()?; + let account_kind = response.account_kind()?; - // Assert - let response_tags = blob_client.get_tags(None).await?.into_body().await?; - assert!(response_tags.blob_tag_set.is_none()); + assert!(sku_name.is_some()); + assert_eq!(AccountKind::StorageV2, account_kind.unwrap()); - container_client.delete_container(None).await?; Ok(()) } diff --git a/sdk/storage/azure_storage_blob/tests/blob_container_client.rs b/sdk/storage/azure_storage_blob/tests/blob_container_client.rs index 43b8f7a45d..ed88822083 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_container_client.rs @@ -4,6 +4,7 @@ use azure_core::http::StatusCode; use azure_core_test::{recorded, TestContext}; use azure_storage_blob::models::{ + AccountKind, BlobContainerClientGetAccountInfoResultHeaders, BlobContainerClientGetPropertiesResultHeaders, BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientSetMetadataOptions, BlobType, LeaseState, }; @@ -204,3 +205,22 @@ async fn test_list_blobs_with_continuation(ctx: TestContext) -> Result<(), Box Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, true).await?; + + // Act + let response = container_client.get_account_info(None).await?; + + // Assert + let sku_name = response.sku_name()?; + let account_kind = response.account_kind()?; + + assert!(sku_name.is_some()); + assert_eq!(AccountKind::StorageV2, account_kind.unwrap()); + + Ok(()) +} diff --git a/sdk/storage/azure_storage_blob/tests/blob_service_client.rs b/sdk/storage/azure_storage_blob/tests/blob_service_client.rs index 0077ef903b..f13abcf085 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_service_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_service_client.rs @@ -2,7 +2,10 @@ // Licensed under the MIT License. use azure_core_test::{recorded, TestContext}; -use azure_storage_blob::models::BlobServiceClientGetPropertiesOptions; +use azure_storage_blob::models::{ + AccountKind, BlobServiceClientGetAccountInfoResultHeaders, + BlobServiceClientGetPropertiesOptions, +}; use azure_storage_blob_test::get_blob_service_client; use std::error::Error; @@ -22,3 +25,22 @@ async fn test_get_service_properties(ctx: TestContext) -> Result<(), Box Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let service_client = get_blob_service_client(recording)?; + + // Act + let response = service_client.get_account_info(None).await?; + + // Assert + let sku_name = response.sku_name()?; + let account_kind = response.account_kind()?; + + assert!(sku_name.is_some()); + assert_eq!(AccountKind::StorageV2, account_kind.unwrap()); + + Ok(()) +} diff --git a/sdk/storage/azure_storage_blob/tsp-location.yaml b/sdk/storage/azure_storage_blob/tsp-location.yaml index a9cea3edf0..f93cf84931 100644 --- a/sdk/storage/azure_storage_blob/tsp-location.yaml +++ b/sdk/storage/azure_storage_blob/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/storage/Microsoft.BlobStorage -commit: f037d99bdbc34d4fe00d1784abd10f4a43d9e475 +commit: d0e964026da6d5c9ad4992c5f6e7ffaf578e609d repo: Azure/azure-rest-api-specs additionalDirectories: From 0b9ac06cc8e19e7d4b7834d5a8409a872acff89f Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 2 Jun 2025 18:00:20 -0700 Subject: [PATCH 4/6] Re-record to fix broken tests from latest main generation --- sdk/storage/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/assets.json b/sdk/storage/assets.json index deae74cc58..0be37eab0e 100644 --- a/sdk/storage/assets.json +++ b/sdk/storage/assets.json @@ -1,6 +1,6 @@ { "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "rust", - "Tag": "rust/azure_storage_blob_de70cafd7a", + "Tag": "rust/azure_storage_blob_3efb7d4663", "TagPrefix": "rust/azure_storage_blob" } \ No newline at end of file From f827ea663f39379fd24c11da0c530795d18b16d1 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Wed, 4 Jun 2025 11:48:27 -0700 Subject: [PATCH 5/6] PR feedback --- .../src/clients/blob_client.rs | 14 ++++++------- .../src/clients/blob_container_client.rs | 6 ++---- .../src/clients/blob_service_client.rs | 5 ++--- sdk/storage/azure_storage_blob/src/lib.rs | 20 +++++++++++-------- 4 files changed, 22 insertions(+), 23 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 a49bb14de0..7e7e318ca4 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -3,17 +3,15 @@ use crate::{ generated::clients::BlobClient as GeneratedBlobClient, - generated::models::{ - BlobClientDownloadResult, BlobClientGetAccountInfoResult, BlobClientGetPropertiesResult, - BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult, - BlockBlobClientUploadResult, - }, models::{ - AccessTier, BlobClientDeleteOptions, BlobClientDownloadOptions, - BlobClientGetAccountInfoOptions, BlobClientGetPropertiesOptions, BlobClientGetTagsOptions, + AccessTier, BlobClientDeleteOptions, BlobClientDownloadOptions, BlobClientDownloadResult, + BlobClientGetAccountInfoOptions, BlobClientGetAccountInfoResult, + BlobClientGetPropertiesOptions, BlobClientGetPropertiesResult, BlobClientGetTagsOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, BlobClientSetTierOptions, BlobTags, BlockBlobClientCommitBlockListOptions, - BlockBlobClientUploadOptions, BlockList, BlockListType, BlockLookupList, + BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult, + BlockBlobClientUploadOptions, BlockBlobClientUploadResult, BlockList, BlockListType, + BlockLookupList, }, pipeline::StorageHeadersPolicy, BlobClientOptions, BlockBlobClient, 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 823d7f8a0c..b9c44eeda0 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 @@ -3,12 +3,10 @@ use crate::{ generated::clients::BlobContainerClient as GeneratedBlobContainerClient, - generated::models::{ - BlobContainerClientGetAccountInfoResult, BlobContainerClientGetPropertiesResult, - }, models::{ BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, - BlobContainerClientGetAccountInfoOptions, BlobContainerClientGetPropertiesOptions, + BlobContainerClientGetAccountInfoOptions, BlobContainerClientGetAccountInfoResult, + BlobContainerClientGetPropertiesOptions, BlobContainerClientGetPropertiesResult, BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientSetMetadataOptions, ListBlobsFlatSegmentResponse, }, diff --git a/sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs b/sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs index 1c111c8c81..f6810ebd16 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs @@ -3,10 +3,9 @@ use crate::{ generated::clients::BlobServiceClient as GeneratedBlobServiceClient, - generated::models::BlobServiceClientGetAccountInfoResult, models::{ - BlobServiceClientGetAccountInfoOptions, BlobServiceClientGetPropertiesOptions, - StorageServiceProperties, + BlobServiceClientGetAccountInfoOptions, BlobServiceClientGetAccountInfoResult, + BlobServiceClientGetPropertiesOptions, StorageServiceProperties, }, pipeline::StorageHeadersPolicy, BlobContainerClient, BlobServiceClientOptions, diff --git a/sdk/storage/azure_storage_blob/src/lib.rs b/sdk/storage/azure_storage_blob/src/lib.rs index 78a5556255..5071351cd5 100644 --- a/sdk/storage/azure_storage_blob/src/lib.rs +++ b/sdk/storage/azure_storage_blob/src/lib.rs @@ -16,21 +16,25 @@ pub use clients::*; pub mod models { pub use crate::generated::models::{ AccessTier, AccountKind, ArchiveStatus, BlobClientDeleteOptions, BlobClientDownloadOptions, - BlobClientDownloadResultHeaders, BlobClientGetAccountInfoOptions, - BlobClientGetAccountInfoResultHeaders, BlobClientGetPropertiesOptions, + BlobClientDownloadResult, BlobClientDownloadResultHeaders, BlobClientGetAccountInfoOptions, + BlobClientGetAccountInfoResult, BlobClientGetAccountInfoResultHeaders, + BlobClientGetPropertiesOptions, BlobClientGetPropertiesResult, BlobClientGetPropertiesResultHeaders, BlobClientGetTagsOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, BlobClientSetTierOptions, BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, BlobContainerClientGetAccountInfoOptions, - BlobContainerClientGetAccountInfoResultHeaders, BlobContainerClientGetPropertiesOptions, + BlobContainerClientGetAccountInfoResult, BlobContainerClientGetAccountInfoResultHeaders, + BlobContainerClientGetPropertiesOptions, BlobContainerClientGetPropertiesResult, BlobContainerClientGetPropertiesResultHeaders, BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientSetMetadataOptions, BlobImmutabilityPolicyMode, BlobServiceClientGetAccountInfoOptions, - BlobServiceClientGetAccountInfoResultHeaders, BlobServiceClientGetPropertiesOptions, - BlobTag, BlobTags, BlobType, BlockBlobClientCommitBlockListOptions, + BlobServiceClientGetAccountInfoResult, BlobServiceClientGetAccountInfoResultHeaders, + BlobServiceClientGetPropertiesOptions, BlobTag, BlobTags, BlobType, + BlockBlobClientCommitBlockListOptions, BlockBlobClientCommitBlockListResult, BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions, - BlockBlobClientUploadOptions, BlockBlobClientUploadResult, BlockList, BlockListType, - BlockLookupList, CopyStatus, LeaseState, LeaseStatus, ListBlobsFlatSegmentResponse, - PublicAccessType, RehydratePriority, StorageServiceProperties, + BlockBlobClientStageBlockResult, BlockBlobClientUploadOptions, BlockBlobClientUploadResult, + BlockList, BlockListType, BlockLookupList, CopyStatus, LeaseState, LeaseStatus, + ListBlobsFlatSegmentResponse, PublicAccessType, RehydratePriority, + StorageServiceProperties, }; } From 46c2a63c76c5db59d0bfcee44dd5dee3ecc74f7a Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 23 Jun 2025 16:49:27 -0700 Subject: [PATCH 6/6] Added helper for tags --- .../src/clients/blob_client.rs | 14 +++- sdk/storage/azure_storage_blob/src/lib.rs | 2 + .../azure_storage_blob/src/serialize.rs | 26 +++++++ .../azure_storage_blob/tests/blob_client.rs | 72 ++++++++++++------- 4 files changed, 86 insertions(+), 28 deletions(-) create mode 100644 sdk/storage/azure_storage_blob/src/serialize.rs 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 7e7e318ca4..b3358d5b02 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +use crate::serialize::serialize_blob_tags; use crate::{ generated::clients::BlobClient as GeneratedBlobClient, models::{ @@ -24,6 +25,7 @@ use azure_core::{ }, Bytes, Result, }; +use std::collections::HashMap; use std::sync::Arc; /// A client to interact with a specific Azure storage blob, although that blob may not yet exist. @@ -215,13 +217,21 @@ impl BlobClient { /// /// # Arguments /// + /// * `tags` - Name-value pairs associated with the blob as tag. Tags are case-sensitive. + /// The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters, + /// and tag values must be between 0 and 256 characters. + /// Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9), + /// space (' '), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_) /// * `options` - Optional configuration for the request. pub async fn set_tags( &self, - tags: RequestContent, + tags: HashMap, options: Option>, ) -> Result> { - self.client.set_tags(tags, options).await + let blob_tags = serialize_blob_tags(tags); + self.client + .set_tags(RequestContent::try_from(blob_tags)?, options) + .await } /// Gets the tags on a blob. diff --git a/sdk/storage/azure_storage_blob/src/lib.rs b/sdk/storage/azure_storage_blob/src/lib.rs index 5071351cd5..4f192dccd5 100644 --- a/sdk/storage/azure_storage_blob/src/lib.rs +++ b/sdk/storage/azure_storage_blob/src/lib.rs @@ -10,6 +10,7 @@ pub mod clients; mod generated; mod pipeline; +pub mod serialize; pub use clients::*; @@ -37,4 +38,5 @@ pub mod models { ListBlobsFlatSegmentResponse, PublicAccessType, RehydratePriority, StorageServiceProperties, }; + pub use crate::serialize::*; } diff --git a/sdk/storage/azure_storage_blob/src/serialize.rs b/sdk/storage/azure_storage_blob/src/serialize.rs new file mode 100644 index 0000000000..dea9311cf2 --- /dev/null +++ b/sdk/storage/azure_storage_blob/src/serialize.rs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +use crate::generated::models::{BlobTag, BlobTags}; +use azure_core::http::RequestContent; +use std::collections::HashMap; + +/// Takes in an offset and a length and returns the HTTP range in string format. +/// +/// # Arguments +/// +/// * `tags` - A hash map containing the name-value pairs associated with the blob as tags. +pub fn serialize_blob_tags(tags: HashMap) -> BlobTags { + let mut blob_tags: Vec = vec![]; + + for (k, v) in tags.into_iter() { + let blob_tag = BlobTag { + key: Some(k), + value: Some(v), + }; + blob_tags.push(blob_tag); + } + BlobTags { + blob_tag_set: Some(blob_tags), + } +} diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index 75ba28fd1f..014e5d6aa0 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -9,14 +9,14 @@ use azure_core_test::{recorded, TestContext}; use azure_storage_blob::models::{ AccessTier, AccountKind, BlobClientDownloadResultHeaders, BlobClientGetAccountInfoResultHeaders, BlobClientGetPropertiesResultHeaders, - BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobTag, BlobTags, - BlockBlobClientUploadOptions, LeaseState, + BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlockBlobClientUploadOptions, + LeaseState, }; +use azure_storage_blob::serialize::serialize_blob_tags; use azure_storage_blob_test::{ create_test_blob, get_blob_name, get_container_client, test_blob_tag_equality, }; use std::{collections::HashMap, error::Error}; - #[recorded::test] async fn test_get_blob_properties(ctx: TestContext) -> Result<(), Box> { // Recording Setup @@ -289,34 +289,54 @@ async fn test_blob_tags(ctx: TestContext) -> Result<(), Box> { create_test_blob(&blob_client).await?; // Set Tags with Tags Specified - let blob_tag_1 = BlobTag { - key: Some("hello".to_string()), - value: Some("world".to_string()), - }; - let blob_tag_2 = BlobTag { - key: Some("ferris".to_string()), - value: Some("crab".to_string()), - }; - let blob_tags = BlobTags { - blob_tag_set: Some(vec![blob_tag_1, blob_tag_2]), - }; - blob_client - .set_tags(RequestContent::try_from(blob_tags.clone())?, None) - .await?; + let blob_tags = HashMap::from([ + ("hello".to_string(), "world".to_string()), + ("ferris".to_string(), "crab".to_string()), + ]); + blob_client.set_tags(blob_tags.clone(), None).await?; // Assert let response_tags = blob_client.get_tags(None).await?.into_body().await?; - assert!(test_blob_tag_equality(blob_tags, response_tags)); + assert!(test_blob_tag_equality( + serialize_blob_tags(blob_tags), + response_tags + )); // Set Tags with No Tags (Clear Tags) - blob_client - .set_tags( - RequestContent::try_from(BlobTags { - blob_tag_set: Some(vec![]), - })?, - None, - ) - .await?; + blob_client.set_tags(HashMap::new(), None).await?; + + // Assert + let response_tags = blob_client.get_tags(None).await?.into_body().await?; + assert!(response_tags.blob_tag_set.is_none()); + + container_client.delete_container(None).await?; + Ok(()) +} + +#[recorded::test] +async fn test_blob_tags2(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 Tags with Tags Specified + let blob_tags = HashMap::from([ + ("hello".to_string(), "world".to_string()), + ("ferris".to_string(), "crab".to_string()), + ]); + blob_client.set_tags(blob_tags.clone(), None).await?; + + // Assert + let response_tags = blob_client.get_tags(None).await?.into_body().await?; + assert!(test_blob_tag_equality( + serialize_blob_tags(blob_tags), + response_tags + )); + + // Set Tags with No Tags (Clear Tags) + blob_client.set_tags(HashMap::new(), None).await?; // Assert let response_tags = blob_client.get_tags(None).await?.into_body().await?;