Skip to content

Commit 806aa08

Browse files
committed
feat: Add SAS support and bump version to 0.4.0
This release introduces several significant changes, including: - Updated version to 0.4.0 in Cargo.toml and Cargo.lock. - Added support for constructing clients directly from SAS URLs. - Removed deprecated methods and changed parameter types for better flexibility. - Enhanced examples to demonstrate SAS token usage. - Updated tests to validate SAS token functionality for both QueueClient and QueueServiceClient.
1 parent f4841b1 commit 806aa08

File tree

15 files changed

+610
-130
lines changed

15 files changed

+610
-130
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/storage/azure_storage_queue/CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Release History
22

3+
## 0.4.0 (Unreleased)
4+
5+
### Features Added
6+
7+
- Added support for client construction directly from URLs:
8+
- `QueueClient::from_url()`
9+
- `QueueServiceClient::from_url()`
10+
- Added support for SAS (shared access signature) URLs via the new `from_url()` methods.
11+
12+
### Breaking Changes
13+
14+
- Removed the `queue_name()` accessor on `QueueClient`.
15+
- Removed the `endpoint` struct field on all clients, as this value is now returned directly from the underlying generated client.
16+
- Changed the `queue_name` parameter from owned `String` to `&str` reference on `QueueClient::new()`.
17+
- The `credential` parameter is now `Option<Arc<dyn TokenCredential>>` on `new()` and `from_url()` client constructors, allowing for construction of public access clients and clients using SAS tokens.
18+
- Changed `QueueServiceClient::queue_client()` to return `Result<QueueClient>` instead of `QueueClient`.
19+
20+
### Bugs Fixed
21+
22+
### Other Changes
23+
324
## 0.3.0 (Unreleased)
425

526
### Features Added

sdk/storage/azure_storage_queue/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "azure_storage_queue"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
description = "Microsoft Azure Queue client library for Rust"
55
readme = "README.md"
66
authors.workspace = true

sdk/storage/azure_storage_queue/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
4949
let queue_client = QueueClient::new(
5050
"https://<storage_account_name>.blob.core.windows.net/", // endpoint
5151
"queue-name", // queue name
52-
credential, // credential
52+
Some(credential), // credential
5353
Some(QueueClientOptions::default()), // QueueClient options
5454
)?;
5555
Ok(())
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "rust",
4-
"Tag": "rust/azure_storage_queue_b234d5c152",
4+
"Tag": "rust/azure_storage_queue_064e449006",
55
"TagPrefix": "rust/azure_storage_queue"
66
}

sdk/storage/azure_storage_queue/examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ This directory contains a set of example for the use of the Storage Queue client
77
The following environment variables need to be set:
88

99
- `AZURE_QUEUE_STORAGE_ACCOUNT_NAME=<storage_account_name>`
10+
- `AZURE_QUEUE_STORAGE_ACCOUNT_SAS_URL=<storage_account_sas_url>` (This is the queue service URL with a SAS token appended without the queue name)

sdk/storage/azure_storage_queue/examples/queue_client.rs

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use std::collections::HashMap;
55

66
use azure_core::{
7-
http::{Response, StatusCode, XmlFormat},
7+
http::{Response, StatusCode, Url, XmlFormat},
88
Error,
99
};
1010
use azure_identity::DeveloperToolsCredential;
@@ -172,16 +172,7 @@ async fn peek_and_receive_messages(
172172
Ok(())
173173
}
174174

175-
#[tokio::main]
176-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
177-
let credential = DeveloperToolsCredential::new(None)?;
178-
179-
// Retrieve the storage account endpoint from environment variable.
180-
let endpoint = get_endpoint();
181-
182-
let queue_name = get_random_queue_name();
183-
let queue_client = QueueClient::new(&endpoint, &queue_name, credential.clone(), None)?;
184-
175+
async fn run_queue_tests(queue_client: &QueueClient) -> Result<(), Box<dyn std::error::Error>> {
185176
// Create and manage queue
186177
let result = queue_client.create(None).await;
187178
log_operation_result(&result, "create");
@@ -219,11 +210,84 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
219210
let result = queue_client.delete(None).await;
220211
log_operation_result(&result, "delete");
221212

222-
let non_existing_queue_client =
223-
QueueClient::new(&endpoint, "non-existent-queue", credential.clone(), None)?;
213+
Ok(())
214+
}
215+
216+
async fn test_with_sas_token() -> Result<(), Box<dyn std::error::Error>> {
217+
// Try to get SAS URL from environment variable
218+
let sas_url = match std::env::var("AZURE_QUEUE_STORAGE_SAS_URL") {
219+
Ok(url) => url,
220+
Err(_) => {
221+
println!("Skipping SAS token test - AZURE_QUEUE_STORAGE_SAS_URL not set");
222+
return Ok(());
223+
}
224+
};
225+
226+
println!("\n=== Testing with SAS Token Authentication ===");
227+
228+
let parsed_url = Url::parse(&sas_url)?;
229+
let queue_name = get_random_queue_name();
230+
231+
// Construct the queue URL by appending the queue name to the service URL
232+
let mut queue_url = parsed_url.clone();
233+
queue_url
234+
.path_segments_mut()
235+
.map_err(|_| {
236+
azure_core::Error::with_message(azure_core::error::ErrorKind::Other, "Invalid SAS URL")
237+
})?
238+
.push(&queue_name);
239+
240+
let queue_client = QueueClient::from_url(queue_url, None, None)?;
241+
242+
// Run the same tests with SAS authentication
243+
run_queue_tests(&queue_client).await?;
244+
245+
println!("=== SAS Token Authentication Test Complete ===\n");
246+
247+
// Test using the `new` constructor with SAS token
248+
println!("=== Testing with SAS Token using new() constructor ===");
249+
250+
let queue_name = get_random_queue_name();
251+
let queue_client = QueueClient::new(&sas_url, &queue_name, None, None)?;
252+
253+
// Run the same tests with SAS authentication using new constructor
254+
run_queue_tests(&queue_client).await?;
255+
256+
println!("=== SAS Token new() Constructor Test Complete ===\n");
257+
258+
Ok(())
259+
}
260+
261+
#[tokio::main]
262+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
263+
let credential = DeveloperToolsCredential::new(None)?;
264+
265+
// Retrieve the storage account endpoint from environment variable.
266+
let endpoint = get_endpoint();
267+
268+
let queue_name = get_random_queue_name();
269+
let queue_client = QueueClient::new(&endpoint, &queue_name, Some(credential.clone()), None)?;
270+
271+
println!("=== Testing with Entra ID Authentication ===");
272+
273+
// Run tests with Entra ID authentication
274+
run_queue_tests(&queue_client).await?;
275+
276+
// Check non-existent queue
277+
let non_existing_queue_client = QueueClient::new(
278+
&endpoint,
279+
"non-existent-queue",
280+
Some(credential.clone()),
281+
None,
282+
)?;
224283
let result = non_existing_queue_client.exists().await;
225284
log_operation_result(&result, "check_non_existent");
226285

286+
println!("=== Entra ID Authentication Test Complete ===\n");
287+
288+
// Test with SAS token authentication if available
289+
test_with_sas_token().await?;
290+
227291
Ok(())
228292
}
229293

sdk/storage/azure_storage_queue/examples/queue_service_client.rs

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33

44
use std::sync::Arc;
55

6-
use azure_core::{http::StatusCode, Error};
6+
use azure_core::{
7+
http::{StatusCode, Url},
8+
Error,
9+
};
710
use azure_storage_queue::{
811
models::{
912
CorsRule, ListQueuesIncludeType, ListQueuesResponse, QueueServiceClientListQueuesOptions,
@@ -104,7 +107,7 @@ async fn get_statistics(
104107
) -> Result<(), Box<dyn std::error::Error>> {
105108
let secondary_endpoint = get_secondary_endpoint();
106109
let secondary_queue_client =
107-
QueueServiceClient::new(&secondary_endpoint, credential.clone(), None)?;
110+
QueueServiceClient::new(&secondary_endpoint, Some(credential.clone()), None)?;
108111
let result = secondary_queue_client.get_statistics(None).await;
109112
log_operation_result(&result, "get_statistics");
110113

@@ -123,6 +126,78 @@ async fn get_statistics(
123126
Ok(())
124127
}
125128

129+
async fn test_sas_token() -> Result<(), Box<dyn std::error::Error>> {
130+
// Try to get SAS URL from environment variable
131+
let sas_url = match std::env::var("AZURE_QUEUE_STORAGE_ACCOUNT_SAS_URL") {
132+
Ok(url) => url,
133+
Err(_) => {
134+
println!("Skipping SAS token test - AZURE_QUEUE_STORAGE_ACCOUNT_SAS_URL not set");
135+
return Ok(());
136+
}
137+
};
138+
139+
println!("\n=== Testing SAS Token Authentication ===");
140+
141+
let parsed_url = Url::parse(&sas_url)?;
142+
let queue_client = QueueServiceClient::from_url(parsed_url, None, None)?;
143+
144+
let queue_name = get_random_queue_name();
145+
146+
// Create queue using SAS token
147+
let result = queue_client.create_queue(&queue_name, None).await;
148+
log_operation_result(&result, "create_queue_with_sas");
149+
150+
// List queues and validate the created queue exists
151+
let list_result = queue_client.list_queues(None);
152+
match list_result {
153+
Ok(pager) => {
154+
let mut found = false;
155+
let mut pager = pager.into_pages();
156+
while let Some(page_result) = pager.next().await {
157+
match page_result {
158+
Ok(response) => {
159+
let queue_list: ListQueuesResponse = response.into_model()?;
160+
for queue in queue_list.queue_items {
161+
if queue.name.as_ref() == Some(&queue_name) {
162+
found = true;
163+
println!(
164+
"Successfully validated queue '{}' exists in list",
165+
queue_name
166+
);
167+
break;
168+
}
169+
}
170+
if found {
171+
break;
172+
}
173+
}
174+
Err(e) => {
175+
eprintln!("Error listing queues with SAS: {}", e);
176+
break;
177+
}
178+
}
179+
}
180+
if !found {
181+
eprintln!(
182+
"Warning: Queue '{}' was created but not found in list",
183+
queue_name
184+
);
185+
}
186+
}
187+
Err(e) => {
188+
eprintln!("Error listing queues with SAS: {}", e);
189+
}
190+
}
191+
192+
// Delete queue using SAS token
193+
let result = queue_client.delete_queue(&queue_name, None).await;
194+
log_operation_result(&result, "delete_queue_with_sas");
195+
196+
println!("=== SAS Token Authentication Test Complete ===\n");
197+
198+
Ok(())
199+
}
200+
126201
#[tokio::main]
127202
async fn main() -> Result<(), Box<dyn std::error::Error>> {
128203
let credential = DeveloperToolsCredential::new(None)?;
@@ -131,7 +206,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
131206
let endpoint = get_endpoint();
132207

133208
let queue_name = get_random_queue_name();
134-
let queue_client = QueueServiceClient::new(&endpoint, credential.clone(), None)?;
209+
let queue_client = QueueServiceClient::new(&endpoint, Some(credential.clone()), None)?;
135210

136211
// Create and manage queue
137212
let result = queue_client.create_queue(&queue_name, None).await;
@@ -149,6 +224,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
149224
let result = queue_client.delete_queue(&queue_name, None).await;
150225
log_operation_result(&result, "delete_queue");
151226

227+
// Test SAS token authentication if available
228+
test_sas_token().await?;
229+
152230
Ok(())
153231
}
154232

0 commit comments

Comments
 (0)