Skip to content

[Storage] [WIP] set/get_tags for BlobClient #2530

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

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions sdk/storage/azure_storage_blob/src/clients/blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
use crate::{
generated::clients::BlobClient as GeneratedBlobClient,
generated::models::{
BlobClientDownloadResult, BlobClientGetPropertiesResult,
BlobClientDownloadResult, BlobClientGetPropertiesResult, BlobClientSetTagsResult, BlobTags,
BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult,
BlockBlobClientUploadResult,
},
models::{AccessTier, BlockList, BlockListType, BlockLookupList},
pipeline::StorageHeadersPolicy,
BlobClientDeleteOptions, BlobClientDownloadOptions, BlobClientGetPropertiesOptions,
BlobClientOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions,
BlobClientSetTierOptions, BlockBlobClientCommitBlockListOptions,
BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions,
BlockBlobClientUploadOptions,
BlobClientGetTagsOptions, BlobClientOptions, BlobClientSetMetadataOptions,
BlobClientSetPropertiesOptions, BlobClientSetTagsOptions, BlobClientSetTierOptions,
BlockBlobClientCommitBlockListOptions, BlockBlobClientGetBlockListOptions,
BlockBlobClientStageBlockOptions, BlockBlobClientUploadOptions,
};
use azure_core::{
credentials::TokenCredential,
Expand Down Expand Up @@ -250,4 +250,30 @@ impl BlobClient {
) -> Result<Response<()>> {
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<BlobTags>,
options: Option<BlobClientSetTagsOptions<'_>>,
) -> Result<Response<BlobClientSetTagsResult>> {
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<BlobClientGetTagsOptions<'_>>,
) -> Result<Response<BlobTags>> {
self.client.get_tags(options).await
}
}
22 changes: 11 additions & 11 deletions sdk/storage/azure_storage_blob/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ pub use crate::generated::clients::{
};
pub use crate::generated::models::{
BlobClientDeleteOptions, BlobClientDownloadOptions, BlobClientGetPropertiesOptions,
BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTierOptions,
BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions,
BlobContainerClientGetPropertiesOptions, BlobContainerClientSetMetadataOptions,
BlobServiceClientGetPropertiesOptions, BlockBlobClientCommitBlockListOptions,
BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions,
BlockBlobClientUploadOptions,
BlobClientGetTagsOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions,
BlobClientSetTagsOptions, BlobClientSetTierOptions, BlobContainerClientCreateOptions,
BlobContainerClientDeleteOptions, BlobContainerClientGetPropertiesOptions,
BlobContainerClientSetMetadataOptions, BlobServiceClientGetPropertiesOptions,
BlockBlobClientCommitBlockListOptions, BlockBlobClientGetBlockListOptions,
BlockBlobClientStageBlockOptions, BlockBlobClientUploadOptions,
};

pub mod models {
pub use crate::generated::models::{
AccessTier, ArchiveStatus, BlobClientDownloadResult, BlobClientDownloadResultHeaders,
BlobClientGetPropertiesResult, BlobClientGetPropertiesResultHeaders,
BlobContainerClientGetPropertiesResult, BlobContainerClientGetPropertiesResultHeaders,
BlobImmutabilityPolicyMode, BlobType, BlockBlobClientCommitBlockListResult,
BlockBlobClientStageBlockResult, BlockBlobClientUploadResult, BlockList, BlockListType,
BlockLookupList, CopyStatus, LeaseState, LeaseStatus, PublicAccessType, RehydratePriority,
StorageServiceProperties,
BlobClientSetTagsResultHeaders, BlobContainerClientGetPropertiesResult,
BlobContainerClientGetPropertiesResultHeaders, BlobImmutabilityPolicyMode, BlobTag,
BlobTags, BlobType, BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult,
BlockBlobClientUploadResult, BlockList, BlockListType, BlockLookupList, CopyStatus,
LeaseState, LeaseStatus, PublicAccessType, RehydratePriority, StorageServiceProperties,
};
}
59 changes: 54 additions & 5 deletions sdk/storage/azure_storage_blob/tests/blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@
// Licensed under the MIT License.

use azure_core::{
http::{RequestContent, StatusCode},
http::{response, Request, RequestContent, StatusCode},
Bytes,
};
use azure_core_test::{recorded, TestContext};
use azure_storage_blob::{
models::{
AccessTier, BlobClientDownloadResultHeaders, BlobClientGetPropertiesResultHeaders,
BlockListType, BlockLookupList, LeaseState,
AccessTier, BlobClientDownloadResultHeaders, BlobClientGetPropertiesResultHeaders, BlobTag,
BlobTags, BlockListType, BlockLookupList, LeaseState,
},
BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlockBlobClientUploadOptions,
BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTagsOptions,
BlockBlobClientUploadOptions,
};
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]
Expand Down Expand Up @@ -426,3 +429,49 @@ async fn test_set_access_tier(ctx: TestContext) -> Result<(), Box<dyn Error>> {
container_client.delete_container(None).await?;
Ok(())
}

#[recorded::test]
async fn test_blob_tags(ctx: TestContext) -> Result<(), Box<dyn Error>> {
// 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: 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: vec![],
})?,
None,
)
.await?;

// Assert
let response_tags = blob_client.get_tags(None).await?.into_body().await?;
assert!(response_tags.blob_tag_set.is_empty());

container_client.delete_container(None).await?;
Ok(())
}
25 changes: 23 additions & 2 deletions sdk/storage/azure_storage_blob_test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ use azure_core::{
};
use azure_core_test::Recording;
use azure_storage_blob::{
models::BlockBlobClientUploadResult, BlobClient, BlobContainerClient,
BlobContainerClientOptions, BlobServiceClient, BlobServiceClientOptions,
models::{BlobTag, BlobTags, BlockBlobClientUploadResult},
BlobClient, BlobContainerClient, BlobContainerClientOptions, BlobServiceClient,
BlobServiceClientOptions,
};
use std::collections::HashMap;

/// Takes in a Recording instance and returns an instrumented options bag and endpoint.
///
Expand Down Expand Up @@ -102,3 +104,22 @@ pub async fn create_test_blob(
)
.await
}

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 {
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 {
// 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()
}
Loading