From c9ca5537ffa80b05cf5e695541412f9896ac04ff Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Tue, 9 Sep 2025 13:29:24 -0700 Subject: [PATCH 1/5] model troubles --- .../src/clients/blob_container_client.rs | 54 +++++++++++++++---- .../azure_storage_blob/src/models/mod.rs | 21 ++++---- .../tests/blob_container_client.rs | 51 ++++++++++++++++-- 3 files changed, 100 insertions(+), 26 deletions(-) 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 f7b4604ee5..f78c4f1eeb 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 @@ -2,20 +2,24 @@ // Licensed under the MIT License. use crate::{ - generated::clients::BlobContainerClient as GeneratedBlobContainerClient, - generated::models::{ - BlobContainerClientAcquireLeaseResult, BlobContainerClientBreakLeaseResult, - BlobContainerClientChangeLeaseResult, BlobContainerClientGetAccountInfoResult, - BlobContainerClientGetPropertiesResult, BlobContainerClientReleaseLeaseResult, - BlobContainerClientRenewLeaseResult, + generated::{ + clients::BlobContainerClient as GeneratedBlobContainerClient, + models::{ + BlobContainerClientAcquireLeaseResult, BlobContainerClientBreakLeaseResult, + BlobContainerClientChangeLeaseResult, BlobContainerClientGetAccountInfoResult, + BlobContainerClientGetPropertiesResult, BlobContainerClientReleaseLeaseResult, + BlobContainerClientRenewLeaseResult, SignedIdentifier, + }, }, models::{ BlobContainerClientAcquireLeaseOptions, BlobContainerClientBreakLeaseOptions, BlobContainerClientChangeLeaseOptions, BlobContainerClientCreateOptions, - BlobContainerClientDeleteOptions, BlobContainerClientGetAccountInfoOptions, - BlobContainerClientGetPropertiesOptions, BlobContainerClientListBlobFlatSegmentOptions, - BlobContainerClientReleaseLeaseOptions, BlobContainerClientRenewLeaseOptions, - BlobContainerClientSetMetadataOptions, ListBlobsFlatSegmentResponse, + BlobContainerClientDeleteOptions, BlobContainerClientGetAccessPolicyOptions, + BlobContainerClientGetAccountInfoOptions, BlobContainerClientGetPropertiesOptions, + BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientReleaseLeaseOptions, + BlobContainerClientRenewLeaseOptions, BlobContainerClientSetAccessPolicyOptions, + BlobContainerClientSetAccessPolicyResult, BlobContainerClientSetMetadataOptions, + ListBlobsFlatSegmentResponse, }, pipeline::StorageHeadersPolicy, BlobClient, BlobContainerClientOptions, @@ -24,7 +28,7 @@ use azure_core::{ credentials::TokenCredential, http::{ policies::{BearerTokenCredentialPolicy, Policy}, - NoFormat, PageIterator, Pager, Response, Url, XmlFormat, + NoFormat, PageIterator, Pager, RequestContent, Response, Url, XmlFormat, }, Result, }; @@ -249,4 +253,32 @@ impl BlobContainerClient { ) -> Result> { self.client.get_account_info(options).await } + + /// Sets the permissions for the specified container. The permissions indicate whether blobs in a + /// container may be accessed publicly. + /// + /// # Arguments + /// + /// * `container_acl` - The access control list for the container. + /// * `options` - Optional configuration for the request. + pub async fn set_access_policy( + &self, + container_acl: RequestContent, XmlFormat>, + options: Option>, + ) -> Result> { + self.client.set_access_policy(container_acl, options).await + } + + /// Gets the permissions for the specified container. The permissions indicate whether container data + /// may be accessed publicly. + /// + /// # Arguments + /// + /// * `options` - Optional configuration for the request. + pub async fn get_access_policy( + &self, + options: Option>, + ) -> Result, XmlFormat>> { + self.client.get_access_policy(options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/models/mod.rs b/sdk/storage/azure_storage_blob/src/models/mod.rs index 23cbd1ba91..4a55f90167 100644 --- a/sdk/storage/azure_storage_blob/src/models/mod.rs +++ b/sdk/storage/azure_storage_blob/src/models/mod.rs @@ -4,7 +4,7 @@ mod extensions; pub use crate::generated::models::{ - AccessTier, AccountKind, AppendBlobClientAppendBlockFromUrlOptions, + AccessPolicy, AccessTier, AccountKind, AppendBlobClientAppendBlockFromUrlOptions, AppendBlobClientAppendBlockFromUrlResult, AppendBlobClientAppendBlockFromUrlResultHeaders, AppendBlobClientAppendBlockOptions, AppendBlobClientAppendBlockResult, AppendBlobClientAppendBlockResultHeaders, AppendBlobClientCreateOptions, @@ -36,15 +36,16 @@ pub use crate::generated::models::{ BlobContainerClientBreakLeaseResultHeaders, BlobContainerClientChangeLeaseOptions, BlobContainerClientChangeLeaseResult, BlobContainerClientChangeLeaseResultHeaders, BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, - BlobContainerClientGetAccountInfoOptions, BlobContainerClientGetAccountInfoResult, - BlobContainerClientGetAccountInfoResultHeaders, BlobContainerClientGetPropertiesOptions, - BlobContainerClientGetPropertiesResult, BlobContainerClientGetPropertiesResultHeaders, - BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientReleaseLeaseOptions, - BlobContainerClientReleaseLeaseResult, BlobContainerClientReleaseLeaseResultHeaders, - BlobContainerClientRenameResult, BlobContainerClientRenameResultHeaders, - BlobContainerClientRenewLeaseOptions, BlobContainerClientRenewLeaseResult, - BlobContainerClientRenewLeaseResultHeaders, BlobContainerClientRestoreResult, - BlobContainerClientRestoreResultHeaders, BlobContainerClientSetAccessPolicyResult, + BlobContainerClientGetAccessPolicyOptions, BlobContainerClientGetAccountInfoOptions, + BlobContainerClientGetAccountInfoResult, BlobContainerClientGetAccountInfoResultHeaders, + BlobContainerClientGetPropertiesOptions, BlobContainerClientGetPropertiesResult, + BlobContainerClientGetPropertiesResultHeaders, BlobContainerClientListBlobFlatSegmentOptions, + BlobContainerClientReleaseLeaseOptions, BlobContainerClientReleaseLeaseResult, + BlobContainerClientReleaseLeaseResultHeaders, BlobContainerClientRenameResult, + BlobContainerClientRenameResultHeaders, BlobContainerClientRenewLeaseOptions, + BlobContainerClientRenewLeaseResult, BlobContainerClientRenewLeaseResultHeaders, + BlobContainerClientRestoreResult, BlobContainerClientRestoreResultHeaders, + BlobContainerClientSetAccessPolicyOptions, BlobContainerClientSetAccessPolicyResult, BlobContainerClientSetAccessPolicyResultHeaders, BlobContainerClientSetMetadataOptions, BlobCopySourceTags, BlobDeleteType, BlobFlatListSegment, BlobImmutabilityPolicyMode, BlobItemInternal, BlobMetadata, BlobName, BlobPropertiesInternal, 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 32646ac431..a98b76e3d6 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_container_client.rs @@ -1,19 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -use azure_core::http::StatusCode; +use azure_core::{ + http::StatusCode, + time::{Duration, OffsetDateTime}, +}; use azure_core_test::{recorded, TestContext}; use azure_storage_blob::models::{ - AccountKind, BlobContainerClientAcquireLeaseResultHeaders, + AccessPolicy, AccountKind, BlobContainerClientAcquireLeaseResultHeaders, BlobContainerClientChangeLeaseResultHeaders, BlobContainerClientGetAccountInfoResultHeaders, BlobContainerClientGetPropertiesResultHeaders, BlobContainerClientListBlobFlatSegmentOptions, - BlobContainerClientSetMetadataOptions, BlobType, LeaseState, + BlobContainerClientSetMetadataOptions, BlobType, LeaseState, SignedIdentifier, }; use azure_storage_blob_test::{ create_test_blob, get_blob_service_client, get_container_client, get_container_name, }; use futures::{StreamExt, TryStreamExt}; -use std::{collections::HashMap, error::Error, time::Duration}; +use std::{collections::HashMap, error::Error}; use tokio::time; #[recorded::test] @@ -243,7 +246,7 @@ async fn test_container_lease_operations(ctx: TestContext) -> Result<(), Box Result<(), Box> { Ok(()) } + +#[recorded::test] +async fn test_container_access_policy(ctx: TestContext) -> Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, false).await?; + container_client.create_container(None).await?; + + // Set Access Policy w/ Policy Defined + let access_policy = AccessPolicy { + expiry: Some(OffsetDateTime::now_utc() + Duration::seconds(10)), + permission: Some("rw".to_string()), + start: Some(OffsetDateTime::now_utc()), + }; + let signed_identifier = SignedIdentifier { + access_policy: Some(access_policy), + id: None, + }; + + container_client + .set_access_policy(signed_identifier.into()?, None) + .await?; + + // Assert + let access_policy_response = container_client.get_access_policy(None).await?; + let signed_identifiers = access_policy_response.into_body().await?; + for signed_identifier in &signed_identifiers { + if let Some(access_policy) = &signed_identifier.access_policy { + assert!(signed_identifier.id.is_some()); + assert!(access_policy.start.is_some()); + assert!(access_policy.expiry.is_some()); + assert_eq!("rw", access_policy.permission.as_ref().unwrap()); + } + } + + container_client.delete_container(None).await?; + Ok(()) +} From 99f71ea2cc75bebf2260eef250f96789dc3ffda9 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Tue, 9 Sep 2025 18:31:03 -0700 Subject: [PATCH 2/5] Still failing --- .../src/clients/blob_container_client.rs | 22 ++++++++++-------- .../src/generated/models/pub_models.rs | 8 +++---- .../src/models/extensions.rs | 23 ++++++++++++++++++- sdk/storage/azure_storage_blob/src/parsers.rs | 2 -- .../tests/blob_container_client.rs | 4 ++-- 5 files changed, 40 insertions(+), 19 deletions(-) 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 f78c4f1eeb..3a0c186262 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 @@ -12,14 +12,14 @@ use crate::{ }, }, models::{ - BlobContainerClientAcquireLeaseOptions, BlobContainerClientBreakLeaseOptions, - BlobContainerClientChangeLeaseOptions, BlobContainerClientCreateOptions, - BlobContainerClientDeleteOptions, BlobContainerClientGetAccessPolicyOptions, - BlobContainerClientGetAccountInfoOptions, BlobContainerClientGetPropertiesOptions, - BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientReleaseLeaseOptions, - BlobContainerClientRenewLeaseOptions, BlobContainerClientSetAccessPolicyOptions, - BlobContainerClientSetAccessPolicyResult, BlobContainerClientSetMetadataOptions, - ListBlobsFlatSegmentResponse, + format_signed_identifiers, BlobContainerClientAcquireLeaseOptions, + BlobContainerClientBreakLeaseOptions, BlobContainerClientChangeLeaseOptions, + BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, + BlobContainerClientGetAccessPolicyOptions, BlobContainerClientGetAccountInfoOptions, + BlobContainerClientGetPropertiesOptions, BlobContainerClientListBlobFlatSegmentOptions, + BlobContainerClientReleaseLeaseOptions, BlobContainerClientRenewLeaseOptions, + BlobContainerClientSetAccessPolicyOptions, BlobContainerClientSetAccessPolicyResult, + BlobContainerClientSetMetadataOptions, ListBlobsFlatSegmentResponse, }, pipeline::StorageHeadersPolicy, BlobClient, BlobContainerClientOptions, @@ -263,10 +263,12 @@ impl BlobContainerClient { /// * `options` - Optional configuration for the request. pub async fn set_access_policy( &self, - container_acl: RequestContent, XmlFormat>, + container_acl: Vec, options: Option>, ) -> Result> { - self.client.set_access_policy(container_acl, options).await + self.client + .set_access_policy(format_signed_identifiers(container_acl)?, options) + .await } /// Gets the permissions for the specified container. The permissions indicate whether container data 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 feba35cbf9..3c925a1e4a 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 @@ -1293,13 +1293,13 @@ pub struct RetentionPolicy { #[derive(Clone, Default, Deserialize, SafeDebug, Serialize)] #[serde(rename = "SignedIdentifier")] pub struct SignedIdentifier { - /// The access policy for the signed identifier. - #[serde(rename = "AccessPolicy", skip_serializing_if = "Option::is_none")] - pub access_policy: Option, - + // TODO: REORDER USING GENERATED CODE CHANGES, THIS IS TEMPORARY /// The unique ID for the signed identifier. #[serde(rename = "Id", skip_serializing_if = "Option::is_none")] pub id: Option, + /// The access policy for the signed identifier. + #[serde(rename = "AccessPolicy", skip_serializing_if = "Option::is_none")] + pub access_policy: Option, } /// The properties that enable an account to host a static website diff --git a/sdk/storage/azure_storage_blob/src/models/extensions.rs b/sdk/storage/azure_storage_blob/src/models/extensions.rs index 0402583a34..5d5a6cce7d 100644 --- a/sdk/storage/azure_storage_blob/src/models/extensions.rs +++ b/sdk/storage/azure_storage_blob/src/models/extensions.rs @@ -1,9 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +use azure_core::{ + http::{RequestContent, XmlFormat}, + xml::to_xml_with_root, +}; +use serde::Serialize; + use crate::models::{ AppendBlobClientCreateOptions, BlobTag, BlobTags, BlockBlobClientUploadBlobFromUrlOptions, - PageBlobClientCreateOptions, + PageBlobClientCreateOptions, SignedIdentifier, }; use azure_core::error::ErrorKind; use std::collections::HashMap; @@ -89,3 +95,18 @@ impl From> for BlobTags { } } } + +// SignedIdentifiers wrapper for correct XML serialization. +#[derive(Serialize)] +struct SignedIdentifiersWrapper { + #[serde(rename = "SignedIdentifier")] + items: Vec, +} + +// Converts a `Vec` into `RequestContent, XmlFormat>`. +pub(crate) fn format_signed_identifiers( + value: Vec, +) -> Result, XmlFormat>, azure_core::Error> { + let wrapper = SignedIdentifiersWrapper { items: value }; + Ok(to_xml_with_root("SignedIdentifiers", &wrapper)?.into()) +} diff --git a/sdk/storage/azure_storage_blob/src/parsers.rs b/sdk/storage/azure_storage_blob/src/parsers.rs index 4cc65a18af..e3c36c42d5 100644 --- a/sdk/storage/azure_storage_blob/src/parsers.rs +++ b/sdk/storage/azure_storage_blob/src/parsers.rs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -use crate::models::{BlobTag, BlobTags}; -use std::collections::HashMap; use std::io::{Error, ErrorKind}; /// Takes in an offset and a length, verifies alignment to a 512-byte boundary, and 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 a98b76e3d6..38064dd7de 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_container_client.rs @@ -312,11 +312,11 @@ async fn test_container_access_policy(ctx: TestContext) -> Result<(), Box Date: Wed, 10 Sep 2025 11:57:36 -0700 Subject: [PATCH 3/5] Resolved, working --- .../src/generated/models/pub_models.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 3c925a1e4a..27cdca7bf8 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 @@ -25,27 +25,29 @@ use std::collections::HashMap; /// Represents an access policy. #[derive(Clone, Default, Deserialize, SafeDebug, Serialize)] pub struct AccessPolicy { + //TODO: Change from rfc7231 -> rfc3339 in generated code /// The date-time the policy expires. #[serde( default, rename = "Expiry", skip_serializing_if = "Option::is_none", - with = "azure_core::time::rfc7231::option" + with = "azure_core::time::rfc3339::option" )] pub expiry: Option, - /// The permissions for acl the policy. - #[serde(rename = "Permission", skip_serializing_if = "Option::is_none")] - pub permission: Option, - /// The date-time the policy is active. #[serde( default, rename = "Start", skip_serializing_if = "Option::is_none", - with = "azure_core::time::rfc7231::option" + with = "azure_core::time::rfc3339::option" )] pub start: Option, + + // TODO: REORDER IN ACTUAL GENERATED CODE + /// The permissions for acl the policy. + #[serde(rename = "Permission", skip_serializing_if = "Option::is_none")] + pub permission: Option, } /// Contains results for `AppendBlobClient::append_block_from_url()` From e80745cb1fbad565749951ee756e5ff3062a6177 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Fri, 12 Sep 2025 16:13:43 -0700 Subject: [PATCH 4/5] Regen against corrected RFC in typespec, merge main after resolving conflicts elsewhere --- .../src/generated/models/pub_models.rs | 18 ++++++++---------- .../azure_storage_blob/tsp-location.yaml | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) 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 27cdca7bf8..fa925bce96 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 @@ -25,7 +25,6 @@ use std::collections::HashMap; /// Represents an access policy. #[derive(Clone, Default, Deserialize, SafeDebug, Serialize)] pub struct AccessPolicy { - //TODO: Change from rfc7231 -> rfc3339 in generated code /// The date-time the policy expires. #[serde( default, @@ -35,6 +34,10 @@ pub struct AccessPolicy { )] pub expiry: Option, + /// The permissions for acl the policy. + #[serde(rename = "Permission", skip_serializing_if = "Option::is_none")] + pub permission: Option, + /// The date-time the policy is active. #[serde( default, @@ -43,11 +46,6 @@ pub struct AccessPolicy { with = "azure_core::time::rfc3339::option" )] pub start: Option, - - // TODO: REORDER IN ACTUAL GENERATED CODE - /// The permissions for acl the policy. - #[serde(rename = "Permission", skip_serializing_if = "Option::is_none")] - pub permission: Option, } /// Contains results for `AppendBlobClient::append_block_from_url()` @@ -1295,13 +1293,13 @@ pub struct RetentionPolicy { #[derive(Clone, Default, Deserialize, SafeDebug, Serialize)] #[serde(rename = "SignedIdentifier")] pub struct SignedIdentifier { - // TODO: REORDER USING GENERATED CODE CHANGES, THIS IS TEMPORARY - /// The unique ID for the signed identifier. - #[serde(rename = "Id", skip_serializing_if = "Option::is_none")] - pub id: Option, /// The access policy for the signed identifier. #[serde(rename = "AccessPolicy", skip_serializing_if = "Option::is_none")] pub access_policy: Option, + + /// The unique ID for the signed identifier. + #[serde(rename = "Id", skip_serializing_if = "Option::is_none")] + pub id: Option, } /// The properties that enable an account to host a static website diff --git a/sdk/storage/azure_storage_blob/tsp-location.yaml b/sdk/storage/azure_storage_blob/tsp-location.yaml index 5fa03759d8..022228d89b 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: c05b1ba8ec1b4472da48c7000784519f18a40f66 +commit: c3d773693455458c91efba4c2e72c3084458d8fe repo: Azure/azure-rest-api-specs additionalDirectories: From 6f4aa777fb4aa679873357e9b9adc6beac4081d1 Mon Sep 17 00:00:00 2001 From: Vincent Tran Date: Mon, 15 Sep 2025 13:39:17 -0700 Subject: [PATCH 5/5] Test recordings and Analyze fix --- sdk/storage/.dict.txt | 1 + sdk/storage/azure_storage_blob/assets.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/storage/.dict.txt b/sdk/storage/.dict.txt index 0bcd38e1b8..186df8af9a 100644 --- a/sdk/storage/.dict.txt +++ b/sdk/storage/.dict.txt @@ -24,6 +24,7 @@ testblob2 testblob3 testblob4 testcontainer +testid uncommittedblobs westus yourtagname diff --git a/sdk/storage/azure_storage_blob/assets.json b/sdk/storage/azure_storage_blob/assets.json index bad8863f67..07da66676b 100644 --- a/sdk/storage/azure_storage_blob/assets.json +++ b/sdk/storage/azure_storage_blob/assets.json @@ -1,6 +1,6 @@ { "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "rust", - "Tag": "rust/azure_storage_blob_094782fa40", + "Tag": "rust/azure_storage_blob_c8c2bbe44d", "TagPrefix": "rust/azure_storage_blob" } \ No newline at end of file