Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion sdk/storage/azure_storage_blob/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repository.workspace = true
rust-version.workspace = true
homepage = "https://github.com/azure/azure-sdk-for-rust"
documentation = "https://docs.rs/azure_storage_blob"
keywords = ["sdk", "cloud", "blobs"]
keywords = ["sdk", "azure", "cloud", "storage", "blobs"]
categories = ["api-bindings"]

[features]
Expand Down
50 changes: 27 additions & 23 deletions sdk/storage/azure_storage_blob/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Azure Blob storage is Microsoft's object storage solution for the cloud. Blob st

## Getting started

**⚠️ Note: The `azure_storage_blob` crate is currently under active development and not all features may be implemented or work as intended. This crate is in beta and not suitable for Production environments. For any general feedback or usage issues, please open a GitHub issue [here](https://github.com/Azure/azure-sdk-for-rust/issues).**
**⚠️ Note: The `azure_storage_blob` crate is currently under active development and not all features may be implemented or work as intended. This crate is in beta and not suitable for Production environments. For any general feedback or usage issues, please [open a GitHub issue](https://github.com/Azure/azure-sdk-for-rust/issues).**

### Install the package

Expand All @@ -18,7 +18,7 @@ cargo add azure_storage_blob

### Prerequisites

* You must have an [Azure subscription] and an [Azure storage account] to use this package.
- You must have an [Azure subscription] and an [Azure storage account] to use this package.

### Create a storage account

Expand Down Expand Up @@ -48,10 +48,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let blob_client = BlobClient::new(
"https://<storage_account_name>.blob.core.windows.net/", // Endpoint
"container_name", // Container Name
"blob_name", // Blob Name
Some(credential), // Credential
Some(BlobClientOptions::default()), // BlobClient Options
"<container_name>", // Container Name
"<blob_name>", // Blob Name
Some(credential), // Credential
Some(BlobClientOptions::default()), // BlobClient Options
)?;
Ok(())
}
Expand All @@ -65,14 +65,16 @@ You may need to specify RBAC roles to access Blob Storage via Microsoft Entra ID

You can find executable examples for all major SDK functions in:

* [blob_hello_world.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_hello_world.rs) - Getting started: create a container, upload and download a blob
* [blob_container_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_container_client.rs) - Container-level operations: metadata, list blobs with continuation, access policies
* [blob_service_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_service_client.rs) - Service-level operations: list containers, service properties, statistics
* [block_blob_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/block_blob_client.rs) - Block blob operations: staged block upload, copy from URL
* [append_blob_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/append_blob_client.rs) - Append blob operations: create, append blocks, seal
* [page_blob_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/page_blob_client.rs) - Page blob operations: create, upload/clear pages, list page ranges, resize
* [blob_storage_logging.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_storage_logging.rs) - Logging and OpenTelemetry distributed tracing
* [storage_error.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/storage_error.rs) - Structured error handling with `StorageError`
- [blob_hello_world.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_hello_world.rs) - Getting started: create a container, upload and download a blob
- [blob_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_client.rs) - Blob-level operations: exists, metadata, index tags, access tier
- [blob_container_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_container_client.rs) - Container-level operations: metadata, list blobs with continuation, access policies
- [blob_service_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_service_client.rs) - Service-level operations: list containers, service properties, statistics
- [block_blob_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/block_blob_client.rs) - Block blob operations: staged block upload, copy from URL
- [append_blob_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/append_blob_client.rs) - Append blob operations: create, append blocks, seal
- [page_blob_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/page_blob_client.rs) - Page blob operations: create, upload/clear pages, list page ranges, resize
- [blob_storage_upload_file.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_storage_upload_file.rs) - Upload a local file with streaming support for large files
- [blob_storage_logging.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/blob_storage_logging.rs) - Logging and OpenTelemetry distributed tracing
- [storage_error.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_blob/examples/storage_error.rs) - Structured error handling with `StorageError`

### Upload a blob

Expand All @@ -86,8 +88,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let blob_client = BlobClient::new(
"https://<storage_account_name>.blob.core.windows.net/",
"container_name",
"blob_name",
"<container_name>",
"<blob_name>",
Some(credential),
Some(BlobClientOptions::default()),
)?;
Expand All @@ -114,12 +116,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let blob_client = BlobClient::new(
"https://<storage_account_name>.blob.core.windows.net/", // Endpoint
"container_name", // Container Name
"blob_name", // Blob Name
Some(credential), // Credential
Some(BlobClientOptions::default()), // BlobClient Options
"<container_name>", // Container Name
"<blob_name>", // Blob Name
Some(credential), // Credential
Some(BlobClientOptions::default()), // BlobClient Options
)?;
let blob_properties = blob_client.get_properties(None).await?;
let response = blob_client.download(None).await?;
let data = String::from_utf8(response.body.collect().await?.into())?;
println!("Downloaded: {data}");
Ok(())
}
```
Expand All @@ -131,7 +135,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
By default, all storage clients create an HTTP transport with automatic decompression disabled,
which is required for partitioned (multi-part) downloads to work correctly. If you set a custom transport
in client options (e.g., a `reqwest::Client` with gzip enabled) without disabling automatic
decompression, partitioned downloads via [`BlobClient::download`](https://docs.rs/azure_storage_blob/latest/azure_storage_blob/clients/struct.BlobClient.html#method.download).
decompression, partitioned downloads via [`BlobClient::download`](https://docs.rs/azure_storage_blob/latest/azure_storage_blob/clients/struct.BlobClient.html#method.download) may not work correctly.
If you need to provide a custom transport, disable automatic decompression to be consistent with default SDK behavior.

## Next Steps
Expand All @@ -154,7 +158,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
[Azure Portal]: https://learn.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal
[Azure PowerShell]: https://learn.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-powershell
[Azure CLI]: https://learn.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-cli
[cargo]: https://dev-doc.rust-lang.org/stable/cargo/commands/cargo.html
[cargo]: https://doc.rust-lang.org/cargo/
[Azure Identity]: https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/identity/azure_identity
[API reference documentation]: https://docs.rs/crate/azure_storage_blob/latest
[Package (crates.io)]: https://crates.io/crates/azure_storage_blob
Expand Down
1 change: 1 addition & 0 deletions sdk/storage/azure_storage_blob/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This directory contains a set of examples for the use of the Blob Storage client
| `append_blob_client.rs` | Append blob operations: create, append blocks, seal |
| `page_blob_client.rs` | Page blob operations: create, upload/clear pages, list page ranges, resize |
| `blob_storage_logging.rs` | Logging and OpenTelemetry distributed tracing |
| `blob_storage_upload_file.rs` | Upload a local file with streaming support for large files |
| `storage_error.rs` | Structured error handling with `StorageError` |

## Setup
Expand Down
73 changes: 73 additions & 0 deletions sdk/storage/azure_storage_blob/tests/blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1456,3 +1456,76 @@ async fn test_gzip_blob_with_metadata_roundtrip(ctx: TestContext) -> Result<(),
container_client.delete(None).await?;
Ok(())
}

// TODO(#XXXX): `if_match`/`if_none_match` type inconsistency between download and upload options.
Comment thread
vincenttran-msft marked this conversation as resolved.
Outdated
//
// `BlobClientDownloadOptions.if_match` is `Option<String>`, but
// `BlockBlobClientUploadOptions.if_match` is `Option<Etag>`. Meanwhile,
// `BlobDownloadProperties.etag` returns `Option<Etag>`.
//
// This means optimistic-concurrency workflows require awkward conversions:
// download → read etag (Etag) → convert to String for conditional download,
// but upload accepts Etag directly. Both should use `Option<Etag>`.
//
// This test demonstrates the friction. Once resolved, the `.to_string()` calls
// on the download side should be replaced with direct Etag usage.
#[recorded::test]
async fn conditional_download_upload_roundtrip(ctx: TestContext) -> Result<(), Box<dyn Error>> {
let recording = ctx.recording();
let container_client =
get_container_client(recording, true, StorageAccount::Standard, None).await?;
let blob_name = get_blob_name(recording);
let blob_client = container_client.blob_client(&blob_name);

// Upload initial content.
let original = b"version 1";
blob_client
.upload(RequestContent::from(original.to_vec()), None)
.await?;

// Download and grab the etag from properties.
let result = blob_client.download(None).await?;
let etag = result
.properties
.etag
.expect("upload should return an etag"); // etag is Etag
Comment thread
vincenttran-msft marked this conversation as resolved.
Outdated

// ── Upload side: Etag flows directly ──
// This is clean — no conversion needed.
let _upload_opts = BlockBlobClientUploadOptions {
if_match: Some(etag.clone()), // Option<Etag> ✓
..Default::default()
};

// ── Download side: forced to convert Etag → String ──
// This is the friction — we have an Etag, but download wants a String.
let _download_opts = BlobClientDownloadOptions {
if_match: Some(etag.to_string()), // Option<String> — why not Option<Etag>?
..Default::default()
};
Comment thread
vincenttran-msft marked this conversation as resolved.
Outdated

// Conditional download with the etag should succeed (blob hasn't changed).
let result = blob_client
.download(Some(BlobClientDownloadOptions {
if_match: Some(etag.to_string()), // forced .to_string() conversion
..Default::default()
}))
.await?;
let body = result.body.collect().await?;
assert_eq!(original, body.as_ref());

// Conditional re-upload (overwrite only if etag still matches).
let updated = b"version 2";
blob_client
.upload(
RequestContent::from(updated.to_vec()),
Some(BlockBlobClientUploadOptions {
if_match: Some(etag), // Etag works directly — no conversion needed
..Default::default()
}),
)
.await?;

container_client.delete(None).await?;
Ok(())
}
2 changes: 1 addition & 1 deletion sdk/storage/azure_storage_queue/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repository.workspace = true
rust-version.workspace = true
homepage = "https://github.com/azure/azure-sdk-for-rust"
documentation = "https://docs.rs/azure_storage_queue"
keywords = ["sdk", "cloud", "queues"]
keywords = ["sdk", "azure", "cloud", "storage", "queues"]
categories = ["api-bindings"]

[features]
Expand Down
67 changes: 58 additions & 9 deletions sdk/storage/azure_storage_queue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ cargo add azure_storage_queue

### Prerequisites

* You must have an [Azure subscription] and an [Azure storage account] to use this package.
- You must have an [Azure subscription] and an [Azure storage account] to use this package.

### Create a storage account

Expand All @@ -36,7 +36,7 @@ az storage account create -n my-storage-account-name -g my-resource-group

#### Authenticate the client

In order to interact with the Azure Queue service, you'll need to create an instance of a client, `QueueClient`. The [Azure Identity] library makes it easy to add Microsoft Entra ID support for authenticating Azure SDK clients with their corresponding Azure services:
In order to interact with the Azure Queue service, you'll need to create an instance of a client, `QueueClient` or `QueueServiceClient`. The [Azure Identity] library makes it easy to add Microsoft Entra ID support for authenticating Azure SDK clients with their corresponding Azure services:

```rust no_run
use azure_storage_queue::{QueueClient, QueueClientOptions};
Expand All @@ -48,7 +48,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let queue_client = QueueClient::new(
"https://<storage_account_name>.queue.core.windows.net/", // Endpoint
"queue-name", // Queue Name
"<queue_name>", // Queue Name
Some(credential), // Credential
Some(QueueClientOptions::default()), // QueueClient Options
)?;
Expand All @@ -64,11 +64,60 @@ You may need to specify RBAC roles to access Queues via Microsoft Entra ID. Plea

You can find executable examples for all major SDK functions in:

* [queue_hello_world.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/queue_hello_world.rs) - Getting started: create a queue, send and receive messages
* [queue_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/queue_client.rs) - Queue-level operations: metadata, send/peek/receive/delete, TTL/visibility options
* [queue_service_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/queue_service_client.rs) - Service-level operations: list queues, service properties, statistics
* [access_policy.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/access_policy.rs) - Set and get queue access policies (stored access policies for SAS)
* [queue_storage_logging.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/queue_storage_logging.rs) - Logging and OpenTelemetry distributed tracing
- [queue_hello_world.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/queue_hello_world.rs) - Getting started: create a queue, send and receive messages
- [queue_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/queue_client.rs) - Queue-level operations: metadata, send/peek/receive/delete, TTL/visibility options
- [queue_service_client.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/queue_service_client.rs) - Service-level operations: list queues, service properties, statistics
- [access_policy.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/access_policy.rs) - Set and get queue access policies (stored access policies for SAS)
- [queue_storage_logging.rs](https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/storage/azure_storage_queue/examples/queue_storage_logging.rs) - Logging and OpenTelemetry distributed tracing

### Send a message

```rust no_run
use azure_storage_queue::{models::QueueMessage, QueueClient, QueueClientOptions};
use azure_identity::DeveloperToolsCredential;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let queue_client = QueueClient::new(
"https://<storage_account_name>.queue.core.windows.net/",
"<queue_name>",
Some(credential),
Some(QueueClientOptions::default()),
)?;

let message = QueueMessage {
message_text: Some("hello world".to_string()),
};
queue_client.send_message(message.try_into()?, None).await?;
Ok(())
}
```

### Receive messages

```rust no_run
use azure_storage_queue::{QueueClient, QueueClientOptions};
use azure_identity::DeveloperToolsCredential;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let queue_client = QueueClient::new(
"https://<storage_account_name>.queue.core.windows.net/",
"<queue_name>",
Some(credential),
Some(QueueClientOptions::default()),
)?;

let response = queue_client.receive_messages(None).await?;
let messages = response.into_model()?;
for msg in messages.items.unwrap_or_default() {
println!("{}", msg.message_text.as_deref().unwrap_or("<empty>"));
}
Ok(())
}
```

## Next steps

Expand All @@ -90,7 +139,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
[Azure Portal]: https://learn.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal
[Azure PowerShell]: https://learn.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-powershell
[Azure CLI]: https://learn.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-cli
[cargo]: https://dev-doc.rust-lang.org/stable/cargo/commands/cargo.html
[cargo]: https://doc.rust-lang.org/cargo/
[Azure Identity]: https://github.com/Azure/azure-sdk-for-rust/tree/main/sdk/identity/azure_identity
[API reference documentation]: https://docs.rs/crate/azure_storage_queue/latest
[Package (crates.io)]: https://crates.io/crates/azure_storage_queue
Expand Down
Loading