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

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
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,18 +11,19 @@ rust-version.workspace = true

[dependencies]
async-trait.workspace = true
azure_storage_common.workspace = true
azure_core = { workspace = true, features = ["xml"] }
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.workspace = true
azure_identity.workspace = true
azure_storage_blob_test.path = "../azure_storage_blob_test"
tokio = { workspace = true, features = ["macros"] }
36 changes: 33 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,33 @@ 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)
}

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,86 @@
// 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,
},
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>,
}

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-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 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