Skip to content

[Storage] get_account_info all clients, set/get_tags for BlobClient, set_properties for BlobServiceClient #2795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: main
Choose a base branch
from

Conversation

vincenttran-msft
Copy link
Member

@vincenttran-msft vincenttran-msft commented Jul 15, 2025

As title states. .tsp: Azure/azure-rest-api-specs#35921

BlobClientSetTagsResult
• ̶ ̶D̶a̶t̶e̶

BlobClientGetAccountInfoResult
• Date
• Account_kind
• Is_hns_enabled
• Sku_name

BlobContainerClientGetAccountInfoResult
• ̶ ̶D̶a̶t̶e̶
• Account_kind
• Is_hns_enabled
• Sku_name

BlobServiceClientGetAccountInfoResult
• ̶ ̶D̶a̶t̶e̶
• Account_kind
• Is_hns_enabled
• Sku_name

This PR also moves the assets file.

@github-actions github-actions bot added the Storage Storage Service (Queues, Blobs, Files) label Jul 15, 2025
Copy link

github-actions bot commented Jul 15, 2025

API Change Check

APIView identified API level changes in this PR and created the following API reviews

azure_storage_blob

@vincenttran-msft vincenttran-msft marked this pull request as ready for review July 17, 2025 23:10
@Copilot Copilot AI review requested due to automatic review settings July 17, 2025 23:10
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds new functionality to the Azure Storage Blob client library, implementing get_account_info for all clients, set/get_tags and start_copy_from_url for BlobClient, and set_properties for BlobServiceClient. These additions align with the Azure REST API specifications and provide essential blob management capabilities.

  • Adds account information retrieval across all client types (BlobClient, BlobContainerClient, BlobServiceClient)
  • Implements blob tagging functionality with set/get operations for BlobClient
  • Adds blob copy functionality and service properties management

Reviewed Changes

Copilot reviewed 11 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
sdk/storage/azure_storage_blob_test/src/lib.rs Adds utility function for comparing blob tags in tests
sdk/storage/azure_storage_blob/src/parsers.rs Implements blob tags serialization helper function
sdk/storage/azure_storage_blob/src/models/mod.rs Exports new model types for account info, tags, and copy operations
sdk/storage/azure_storage_blob/src/clients/blob_service_client.rs Adds set_properties and get_account_info methods
sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs Adds get_account_info method
sdk/storage/azure_storage_blob/src/clients/blob_client.rs Adds start_copy_from_url, set_tags, get_tags, and get_account_info methods
sdk/storage/azure_storage_blob/tests/ Comprehensive test coverage for all new functionality
Comments suppressed due to low confidence (3)

sdk/storage/azure_storage_blob_test/src/lib.rs:124

  • [nitpick] The function name test_blob_tag_equality doesn't follow the naming convention for utility functions. Consider renaming to compare_blob_tags or blob_tags_equal since it's not a test function itself but a helper for tests.
pub fn test_blob_tag_equality(tags1: BlobTags, tags2: BlobTags) -> bool {

sdk/storage/azure_storage_blob_test/src/lib.rs:125

  • The variable name count_map is misleading since it stores key-value pairs from tags, not counts. Consider renaming to tag_map or first_tags_map to better reflect its purpose.
    let mut count_map = HashMap::new();

sdk/storage/azure_storage_blob/src/parsers.rs:50

  • Variable names k and v are too short and unclear. Consider using more descriptive names like key and value to improve readability.
    for (k, v) in tags.into_iter() {

@vincenttran-msft vincenttran-msft changed the title [Storage] get_account_info all clients, set/get_tags and start_copy_from_url for BlobClient, set_properties for BlobServiceClient [Storage] get_account_info all clients, set/get_tags for BlobClient, set_properties for BlobServiceClient Jul 21, 2025
@vincenttran-msft
Copy link
Member Author

Also, I believe I'm stumbling upon a pretty fun issue 😄
Since we refactored set_tags to accept a HashMap<String, String> and then call our helper to serialize it into the proper form, BlobTags, due to the unordered nature of HashMaps, the resultant BlobTags object can have two different forms, and this will trigger a test recording failure:
Scenario 1:

<Tags>
	<TagSet>
		<Tag>
			<Key>hello</Key>
			<Value>world</Value>
		</Tag>
		<Tag>
			<Key>ferris</Key>
			<Value>crab</Value>
		</Tag>
	</TagSet>
</Tags>",

Scenario 2:

<Tags>
	<TagSet>
		<Tag>
			<Key>ferris</Key>
			<Value>crab</Value>
		</Tag>
		<Tag>
			<Key>hello</Key>
			<Value>world</Value>
		</Tag>
	</TagSet>
</Tags>",

I am wondering if this is something we can resolve at the test proxy level (i.e. sort alphabetically before comparing) at least in areas where order shouldn't matter.

Copy link
Member

@heaths heaths left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement Serialize and/or Deserialize (probably both, it seems) for your custom tags model and much of this code goes away.

@vincenttran-msft
Copy link
Member Author

vincenttran-msft commented Jul 30, 2025

Also, I believe I'm stumbling upon a pretty fun issue 😄 Since we refactored set_tags to accept a HashMap<String, String> and then call our helper to serialize it into the proper form, BlobTags, due to the unordered nature of HashMaps, the resultant BlobTags object can have two different forms, and this will trigger a test recording failure: Scenario 1:

<Tags>
	<TagSet>
		<Tag>
			<Key>hello</Key>
			<Value>world</Value>
		</Tag>
		<Tag>
			<Key>ferris</Key>
			<Value>crab</Value>
		</Tag>
	</TagSet>
</Tags>",

Scenario 2:

<Tags>
	<TagSet>
		<Tag>
			<Key>ferris</Key>
			<Value>crab</Value>
		</Tag>
		<Tag>
			<Key>hello</Key>
			<Value>world</Value>
		</Tag>
	</TagSet>
</Tags>",

I am wondering if this is something we can resolve at the test proxy level (i.e. sort alphabetically before comparing) at least in areas where order shouldn't matter.

For this issue, @heaths Heath do you have any examples of using a CustomDefaultMatcher with compareBodies = false in Rust test proxy? I think that should sufficiently resolve this issue, just unsure how to enable that knob in Rust. Thanks!

I tried the following:

let recording = ctx.recording();
    let no_body_match: Matcher = CustomDefaultMatcher {
        compare_bodies: Some(false),
        ..Default::default()
    }
    .into();
    recording.set_matcher(no_body_match).await?;

But this yields an error (issue goes away if I don't set a custom matcher):

2025-07-30T20:12:27.757439Z ERROR test-proxy: 6170ecdb-ed3b-4ecb-ac1f-16e0c3ce7fa8 is not an active playback session. Check the value being passed and try again.
2025-07-30T20:12:27.758751Z ERROR azure_core_test::proxy::client: error=server returned error status which will not be retried: 400
Error: Error { context: Full(Custom { kind: HttpResponse { status: BadRequest, error_code: None }, error: HttpError { status: BadRequest, details: ErrorDetails { code: None, message: None }, body: {"Message":"6170ecdb-ed3b-4ecb-ac1f-16e0c3ce7fa8 is not an active playback session. Check the value being passed and try again.","Status":"BadRequest"}, .. } }, "server returned error status which will not be retried: 400") }

@heaths
Copy link
Member

heaths commented Aug 4, 2025

For the matcher, I don't recommend not comparing bodies. What's the point of a client test then? We don't need to test the service since you're presumably already done that.

A solution is much simpler:

#[cfg(test)]
let mut tags = std::collections::BTreeMap::new();
#[cfg(not(test))]
let mut tags = std::collections::HashMap::new();

As long as you stick to methods that exist on both - which were designed to be mostly compatible - it shouldn't matter. Use fully-qualified names and you don't have to worry about conditioning the imports as well.

@heaths heaths marked this pull request as draft August 4, 2025 22:57
@vincenttran-msft vincenttran-msft marked this pull request as ready for review August 11, 2025 18:05
@vincenttran-msft vincenttran-msft marked this pull request as draft August 11, 2025 19:25
@vincenttran-msft vincenttran-msft marked this pull request as ready for review August 11, 2025 19:25
@vincenttran-msft vincenttran-msft marked this pull request as draft August 14, 2025 00:46
@vincenttran-msft vincenttran-msft marked this pull request as ready for review August 14, 2025 01:33
Comment on lines +45 to +82
/// Converts a `BlobTags` struct into `HashMap<String, String>`.
impl TryFrom<BlobTags> for HashMap<String, String> {
type Error = &'static str;

fn try_from(blob_tags: BlobTags) -> Result<Self, Self::Error> {
let mut map = HashMap::new();

if let Some(tags) = blob_tags.blob_tag_set {
for tag in tags {
match (tag.key, tag.value) {
(Some(k), Some(v)) => {
map.insert(k, v);
}
_ => return Err("BlobTag missing key or value"),
}
}
}

Ok(map)
}
}

/// Converts a `HashMap<String, String>` into a `BlobTags` struct.
impl From<HashMap<String, String>> for BlobTags {
fn from(tags: HashMap<String, String>) -> Self {
let sorted_tags: BTreeMap<_, _> = tags.into_iter().collect();
let blob_tags = sorted_tags
.into_iter()
.map(|(k, v)| BlobTag {
key: Some(k),
value: Some(v),
})
.collect();
BlobTags {
blob_tag_set: Some(blob_tags),
}
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a TryFrom and From to be used for get_tags and set_tags respectively.

Comment on lines 443 to 446
// Assert
let response_tags = blob_client.get_tags(None).await?.into_body().await?;
assert_eq!(blob_tags, response_tags);
let map: HashMap<String, String> = response_tags.try_into()?;
assert_eq!(blob_tags, map);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usage example- so 1 extra step try_into() to get the user-friendly HashMap<String, String> format.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Storage Storage Service (Queues, Blobs, Files)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants