Skip to content
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

[Storage] stage_block, commit_block_list for BlobClient, get_container_properties for ContainerClient, get_service_properties for ServiceClient #2273

Draft
wants to merge 3 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
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ path = "sdk/identity/azure_identity"
version = "0.1.0"
path = "sdk/storage"

[workspace.dependencies.azure_storage_blob]
version = "0.1.0"
path = "sdk/storage/azure_storage_blob"

[workspace.dependencies]
async-lock = "3.0"
async-process = "2.0"
Expand Down
1 change: 1 addition & 0 deletions sdk/storage/.dict.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
AAABBBCCC
appendblock
appendpos
blockid
Expand Down
2 changes: 1 addition & 1 deletion sdk/storage/assets.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "rust",
"Tag": "rust/azure_storage_blob_d76e2bb82c",
"Tag": "rust/azure_storage_blob_d1ca6ac4ca",
"TagPrefix": "rust/azure_storage_blob"
}
9 changes: 5 additions & 4 deletions sdk/storage/azure_storage_blob/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,20 @@ rust-version.workspace = true

[dependencies]
async-trait.workspace = true
azure_storage_common.workspace = true
azure_core = { workspace = true, features = ["xml"] }
azure_identity = { workspace = true }
azure_storage_common.workspace = true
serde.workspace = true
time.workspace = true
typespec_client_core = { workspace = true, features = ["derive"] }
uuid.workspace = true
url.workspace = true
uuid.workspace = true

[lints]
workspace = true

[dev-dependencies]
tokio = { workspace = true, features = ["macros"] }
azure_identity.workspace = true
azure_core_test.path = "../../core/azure_core_test"
azure_identity.workspace = true
azure_storage_blob_test.path = "../azure_storage_blob_test"
tokio = { workspace = true, features = ["macros"] }
50 changes: 47 additions & 3 deletions sdk/storage/azure_storage_blob/src/clients/blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ use crate::{
clients::GeneratedBlobClient,
models::{
BlobBlobClientDownloadOptions, BlobBlobClientGetPropertiesOptions,
BlobBlockBlobClientUploadOptions, BlobProperties,
BlobBlockBlobClientCommitBlockListOptions, BlobBlockBlobClientStageBlockOptions,
BlobBlockBlobClientUploadOptions, BlobProperties, BlockLookupList,
},
pipeline::StorageHeadersPolicy,
BlobClientOptions,
};
use azure_core::{
credentials::TokenCredential, BearerTokenCredentialPolicy, Bytes, Policy, RequestContent,
Response, Result, Url,
base64, credentials::TokenCredential, BearerTokenCredentialPolicy, Bytes, Policy,
RequestContent, Response, Result, Url,
};
use std::sync::Arc;

Expand Down Expand Up @@ -117,4 +118,47 @@ impl BlobClient {
.await?;
Ok(response)
}

pub async fn commit_block_list(
&self,
blocks: RequestContent<BlockLookupList>,
options: Option<BlobBlockBlobClientCommitBlockListOptions<'_>>,
) -> Result<Response<()>> {
let response = self
.client
.get_blob_block_blob_client(self.container_name.clone(), self.blob_name.clone())
.commit_block_list(blocks, options)
.await?;
Ok(response)
}

// Currently blocked by generated code, uncomment when we can consume newest definition
// pub async fn get_block_list(
// &self,
// list_type: BlockListType,
// options: Option<BlobBlockBlobClientGetBlockListOptions<'_>>,
// ) -> Result<Response<BlockList>> {
// let response = self
// .client
// .get_blob_block_blob_client(self.container_name.clone(), self.blob_name.clone())
// .get_block_list(list_type, options)
// .await?;
// Ok(response)
// }
Comment on lines +135 to +147
Copy link
Member Author

Choose a reason for hiding this comment

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

This was fully working E2E against a forked copy of the generated code that contained the necessary fixes. This will be fixed when we can regenerate and get the necessary generated code changes (emitter currently is broken by newest .tsp definition).

Same comment applies to get_service_properties for BlobServiceClient.


pub async fn stage_block(
&self,
block_id: &str,
content_length: i64,
body: RequestContent<Bytes>,
options: Option<BlobBlockBlobClientStageBlockOptions<'_>>,
) -> Result<Response<()>> {
let block_id = base64::encode(block_id);
let response = self
.client
.get_blob_block_blob_client(self.container_name.clone(), self.blob_name.clone())
.stage_block(&block_id, content_length, body, options)
.await?;
Ok(response)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// Licensed under the MIT License.

use crate::clients::GeneratedBlobClient;
use crate::models::{BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions};
use crate::models::{
BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions,
BlobContainerClientGetPropertiesOptions, ContainerProperties,
};
use crate::pipeline::StorageHeadersPolicy;
use crate::BlobClientOptions;
use azure_core::{
Expand Down Expand Up @@ -80,4 +83,18 @@ impl BlobContainerClient {
.await?;
Ok(response)
}

pub async fn get_container_properties(
&self,
options: Option<BlobContainerClientGetPropertiesOptions<'_>>,
) -> Result<ContainerProperties> {
let response = self
.client
.get_blob_container_client(self.container_name.clone())
.get_properties(options)
.await?;

let container_properties: ContainerProperties = response.headers().get()?;
Ok(container_properties)
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,66 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

use crate::{
clients::GeneratedBlobClient,
models::{BlobServiceClientGetPropertiesOptions, StorageServiceProperties},
pipeline::StorageHeadersPolicy,
BlobClientOptions,
};
use azure_core::{
credentials::TokenCredential, BearerTokenCredentialPolicy, Policy, Response, Result, Url,
};
use std::sync::Arc;

pub struct BlobServiceClient {
endpoint: Url,
client: GeneratedBlobClient,
}

impl BlobServiceClient {
pub fn new(
endpoint: &str,
credential: Arc<dyn TokenCredential>,
options: Option<BlobClientOptions>,
) -> Result<Self> {
let mut options = options.unwrap_or_default();

let storage_headers_policy = Arc::new(StorageHeadersPolicy);
options
.client_options
.per_call_policies
.push(storage_headers_policy);

let oauth_token_policy = BearerTokenCredentialPolicy::new(
credential.clone(),
["https://storage.azure.com/.default"],
);
options
.client_options
.per_try_policies
.push(Arc::new(oauth_token_policy) as Arc<dyn Policy>);

let client = GeneratedBlobClient::new(endpoint, credential, Some(options))?;

Ok(Self {
endpoint: endpoint.parse()?,
client,
})
}

pub fn endpoint(&self) -> &Url {
&self.endpoint
}

pub async fn get_service_properties(
&self,
options: Option<BlobServiceClientGetPropertiesOptions<'_>>,
) -> Result<Response<StorageServiceProperties>> {
let response = self
.client
.get_blob_service_client()
.get_properties(options)
.await?;
Ok(response)
}
}
5 changes: 5 additions & 0 deletions sdk/storage/azure_storage_blob/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ mod generated;
pub mod clients {
pub mod blob_client;
pub mod blob_container_client;
pub mod blob_service_client;

pub use blob_client::BlobClient;
pub use blob_container_client::BlobContainerClient as ContainerClient;
pub use blob_service_client::BlobServiceClient as ServiceClient;

pub use crate::generated::clients::{
BlobAppendBlobClient, BlobBlobClient, BlobBlockBlobClient,
Expand Down Expand Up @@ -60,6 +62,9 @@ pub mod models {

mod blob_properties;
pub use blob_properties::BlobProperties;

mod container_properties;
pub use container_properties::ContainerProperties;
}

pub use crate::generated::clients::{BlobClient, BlobClientOptions};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

use azure_core::{
headers::{
FromHeaders, HeaderName, Headers, ETAG, HAS_IMMUTABILITY_POLICY, HAS_LEGAL_HOLD,
LEASE_STATE, LEASE_STATUS, VERSION,
},
Error, Etag, LeaseStatus,
};
use typespec_client_core::fmt::SafeDebug;

use crate::models::LeaseState;

pub const LAST_MODIFIED: HeaderName = HeaderName::from_static("last-modified");
pub const IMMUTABLE_STORAGE_WITH_VERSIONING_ENABLED: HeaderName =
HeaderName::from_static("x-ms-immutable-storage-with-versioning-enabled");

/// Properties of an Azure Storage container.
///
#[derive(Clone, Default, SafeDebug)]
pub struct ContainerProperties {
pub last_modified: Option<String>,
pub lease_state: Option<LeaseState>,
pub lease_status: Option<LeaseStatus>,
pub has_immutability_policy: Option<bool>,
pub has_legal_hold: Option<bool>,
pub immutable_storage_with_versioning_enabled: Option<String>,
pub etag: Option<Etag>,
pub version: Option<String>,
}

impl FromHeaders for ContainerProperties {
type Error = Error;
fn header_names() -> &'static [&'static str] {
&[
"etag",
"last-modified",
"x-ms-lease-state",
"x-ms-lease-status",
"x-ms-immutable-storage-with-versioning-enabled",
"x-ms-has-immutability-policy",
"x-ms-version",
Copy link
Member

Choose a reason for hiding this comment

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

Version is not a container property, we should remove this and all its parsing logic.

"x-ms-has-legal-hold",
]
}

fn from_headers(headers: &Headers) -> Result<Option<Self>, Error> {
let mut properties = ContainerProperties {
..Default::default()
};

let last_modified = headers.get_optional_str(&LAST_MODIFIED);
properties.last_modified = last_modified.map(|s| s.to_string());

let lease_state: Option<LeaseState> = headers.get_optional_as(&LEASE_STATE)?;
properties.lease_state = lease_state;

let lease_status: Option<LeaseStatus> = headers.get_optional_as(&LEASE_STATUS)?;
properties.lease_status = lease_status;

let has_immutability_policy: Option<bool> =
headers.get_optional_as(&HAS_IMMUTABILITY_POLICY)?;
properties.has_immutability_policy = has_immutability_policy;

let has_legal_hold: Option<bool> = headers.get_optional_as(&HAS_LEGAL_HOLD)?;
properties.has_legal_hold = has_legal_hold;

let immutable_storage_with_versioning_enabled =
headers.get_optional_str(&IMMUTABLE_STORAGE_WITH_VERSIONING_ENABLED);
properties.immutable_storage_with_versioning_enabled =
immutable_storage_with_versioning_enabled.map(|s| s.to_string());

let version = headers.get_optional_str(&VERSION);
properties.version = version.map(|s| s.to_string());

let etag: Option<Etag> = headers.get_optional_as(&ETAG)?;
properties.etag = etag;

let last_modified = headers.get_optional_str(&LAST_MODIFIED);
properties.last_modified = last_modified.map(|s| s.to_string());

let lease_state: Option<LeaseState> = headers.get_optional_as(&LEASE_STATE)?;
properties.lease_state = lease_state;

let lease_status: Option<LeaseStatus> = headers.get_optional_as(&LEASE_STATUS)?;
properties.lease_status = lease_status;

Ok(Some(properties))
}
}
Loading