Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f7626db
Clean building
vincenttran-msft Oct 2, 2025
5442ea3
Use macro with caveat- now pub fn on underlying generated client
vincenttran-msft Oct 3, 2025
7338e3a
Refactor that any URLs are Url type
vincenttran-msft Oct 10, 2025
e48e295
Remaining Qs on decoding in helper, '/' '\' consistency
vincenttran-msft Oct 13, 2025
12d463d
building, tests not building
vincenttran-msft Oct 14, 2025
de1223b
Flat refactor applied to all clients, all tests passing, cargo clippy…
vincenttran-msft Oct 14, 2025
aabed60
Regens, but broken on some http concepts that likely got moved
vincenttran-msft Oct 17, 2025
784cb7c
Merge branch 'main' into vincenttran/flattened_clients
vincenttran-msft Oct 17, 2025
aaaeedd
Main crate no compiler errors
vincenttran-msft Oct 17, 2025
cde3fc5
Fix eventhubs and azure_storage_blob_test code after refactor
vincenttran-msft Oct 17, 2025
f6328f3
Refactor all test code to reflect refactor, all test cases passing
vincenttran-msft Oct 17, 2025
538347b
fix lint
vincenttran-msft Oct 17, 2025
6cb2a9f
Checkpoint store README
vincenttran-msft Oct 17, 2025
40d5149
one more nit
vincenttran-msft Oct 18, 2025
c2f7f5f
EventHubs test working in record, pushing working asset + working in …
vincenttran-msft Oct 20, 2025
507d3c1
Add test recordings for encoding_edge_cases, remove public access tes…
vincenttran-msft Oct 21, 2025
f4944e7
refactor
vincenttran-msft Oct 21, 2025
363147f
Add new changelog entries, PR feedback, and add list_blobs name asser…
vincenttran-msft Oct 22, 2025
8373688
nit on test name
vincenttran-msft Oct 22, 2025
f591773
Merge branch 'main' into vincenttran/flattened_clients
vincenttran-msft Oct 22, 2025
dec3d88
Fix list blobs test after merging main in
vincenttran-msft Oct 22, 2025
ec30aff
Recording to match nit
vincenttran-msft Oct 22, 2025
27d6e5c
PR feedback, improve encoding test case
vincenttran-msft Oct 22, 2025
ca19acd
Address U+FFFF and U+FFFE concerns with try_from helper for easy to u…
vincenttran-msft Oct 24, 2025
6ad4794
Analyze nits, add changelog entry for BlobName TryFrom impl
vincenttran-msft Oct 24, 2025
b951650
Remove unicode edge cases, will address in seperate PR to fix-up list…
vincenttran-msft Oct 24, 2025
c1bb718
nit
vincenttran-msft Oct 24, 2025
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
4 changes: 2 additions & 2 deletions Cargo.lock

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

2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ futures = "0.3"
getrandom = { version = "0.3" }
gloo-timers = { version = "0.3" }
hmac = { version = "0.12" }
litemap = "0.7.4"
openssl = { version = "0.10.72" }
opentelemetry = { version = "0.30", features = ["trace"] }
opentelemetry_sdk = "0.30"
Expand Down Expand Up @@ -149,7 +148,6 @@ url = "2.2"
uuid = { version = "1.18", features = ["v4"] }
wasm-bindgen-futures = "0.4"
wasm-bindgen-test = "0.3"
zerofrom = "0.1.5"
zip = { version = "4.0.0", default-features = false, features = ["deflate"] }

[workspace.lints.clippy]
Expand Down
374 changes: 183 additions & 191 deletions eng/emitter-package-lock.json

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions eng/emitter-package.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"main": "dist/src/index.js",
"dependencies": {
"@azure-tools/typespec-rust": "0.24.0"
"@azure-tools/typespec-rust": "0.24.1"
},
"devDependencies": {
"@azure-tools/typespec-azure-core": "0.60",
"@azure-tools/typespec-azure-rulesets": "0.60",
"@azure-tools/typespec-client-generator-core": "0.60",
"@typespec/compiler": "1.4",
"@typespec/http": "1.4",
"@typespec/openapi": "1.4",
"@typespec/rest": "0.74",
"@typespec/versioning": "0.74",
"@typespec/xml": "0.74"
"@azure-tools/typespec-azure-core": "0.61",
"@azure-tools/typespec-azure-rulesets": "0.61",
"@azure-tools/typespec-client-generator-core": ">=0.61.1",
"@typespec/compiler": "1.5.0",
"@typespec/http": "1.5.0",
"@typespec/openapi": "1.5.0",
"@typespec/rest": "0.75.0",
"@typespec/versioning": "0.75.0",
"@typespec/xml": "0.75.0"
}
}
}
2 changes: 1 addition & 1 deletion sdk/eventhubs/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "rust",
"TagPrefix": "rust/eventhubs",
"Tag": "rust/eventhubs_12266aa668"
"Tag": "rust/eventhubs_ad9b84912d"
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let blob_client = BlobContainerClient::new(
"https://yourstorageaccount.blob.core.windows.net",
"yourcontainername".to_string(),
credential.clone(),
"yourcontainername",
Some(credential.clone()),
None,
)?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Instantiate a blob client with OpenTelemetry instrumentation enabled
let blob_container_client = BlobContainerClient::new(
&storage_account_url,
container,
credential,
&container,
Some(credential),
Some(BlobContainerClientOptions {
client_options: ClientOptions {
instrumentation: InstrumentationOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = DeveloperToolsCredential::new(None)?;
let blob_container_client = BlobContainerClient::new(
&storage_account_url,
container_name,
credential.clone(),
&container_name,
Some(credential.clone()),
None,
)?;
let consumer = ConsumerClient::builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@ impl BlobCheckpointStore {
blob_name: &str,
metadata: HashMap<String, String>,
) -> Result<(Option<OffsetDateTime>, Option<Etag>)> {
let blob_client = self
.blob_container_client
.blob_client(blob_name.to_string());
let blob_client = self.blob_container_client.blob_client(blob_name);

let result = blob_client.set_metadata(metadata.clone(), None).await;
match result {
Expand Down Expand Up @@ -106,9 +104,7 @@ impl BlobCheckpointStore {
metadata: Option<HashMap<String, String>>,
etag: Option<Etag>,
) -> Result<(Option<OffsetDateTime>, Option<Etag>)> {
let blob_client = self
.blob_container_client
.blob_client(blob_name.to_string());
let blob_client = self.blob_container_client.blob_client(blob_name);

if etag.is_some() {
debug!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ pub fn create_test_checkpoint_store(recording: &Recording) -> Result<Arc<BlobChe
recording.instrument(&mut options.client_options);
let blob_container_client = BlobContainerClient::new(
&recording.var("AZURE_STORAGE_BLOB_ENDPOINT", None),
recording.var("AZURE_STORAGE_BLOB_CONTAINER", None),
credential.clone(),
&recording.var("AZURE_STORAGE_BLOB_CONTAINER", None),
Some(credential.clone()),
Some(options),
)?;

Expand Down
3 changes: 3 additions & 0 deletions sdk/storage/azure_storage_blob/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

### Features Added

- Added support for SAS (Shared Access Signature) authentication scheme.
- Added support for unauthenticated client access (public access clients).

### Breaking Changes

### Bugs Fixed
Expand Down
24 changes: 12 additions & 12 deletions sdk/storage/azure_storage_blob/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ 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".to_string(), // container name
"blob_name".to_string(), // blob name
credential, // credential
"container_name", // container name
"blob_name", // blob name
Some(credential), // credential
Some(BlobClientOptions::default()), // BlobClient options
)?;
Ok(())
Expand All @@ -75,9 +75,9 @@ 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".to_string(), // container name
"blob_name".to_string(), // blob name
credential, // credential
"container_name", // container name
"blob_name", // blob name
Some(credential), // credential
Some(BlobClientOptions::default()), // BlobClient options
)?;
Ok(())
Expand All @@ -96,9 +96,9 @@ 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".to_string(),
"blob_name".to_string(),
credential,
"container_name",
"blob_name",
Some(credential),
Some(BlobClientOptions::default()),
)?;

Expand Down Expand Up @@ -127,9 +127,9 @@ 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".to_string(),
"blob_name".to_string(),
credential,
"container_name",
"blob_name",
Some(credential),
Some(BlobClientOptions::default()),
)?;
let blob_properties = blob_client.get_properties(
Expand Down
2 changes: 1 addition & 1 deletion sdk/storage/azure_storage_blob/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_4dd8ebabce",
"Tag": "rust/azure_storage_blob_f947fbbe2f",
"TagPrefix": "rust/azure_storage_blob"
}
5 changes: 3 additions & 2 deletions sdk/storage/azure_storage_blob/perf/list_blob_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ impl PerfTest for ListBlobTest {
let recording = context.recording();
let credential = recording.credential();
let container_name = format!("perf-container-{}", uuid::Uuid::new_v4());
let client = BlobContainerClient::new(&self.endpoint, container_name, credential, None)?;
let client =
BlobContainerClient::new(&self.endpoint, &container_name, Some(credential), None)?;
self.client.set(client).map_err(|_| {
azure_core::Error::with_message(ErrorKind::Other, "Failed to set client")
})?;
Expand All @@ -95,7 +96,7 @@ impl PerfTest for ListBlobTest {
// Create the blobs for the test.
for i in 0..self.count {
let blob_name = format!("blob-{}", i);
let blob_client = container_client.blob_client(blob_name);
let blob_client = container_client.blob_client(&blob_name);
let body = vec![0u8; 1024 * 1024]; // 1 MB blob
let body_bytes = Bytes::from(body);

Expand Down
118 changes: 84 additions & 34 deletions sdk/storage/azure_storage_blob/src/clients/append_blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,35 @@ use crate::{
AppendBlobClientSealResult,
},
pipeline::StorageHeadersPolicy,
AppendBlobClientOptions, BlobClientOptions,
AppendBlobClientOptions,
};
use azure_core::{
credentials::TokenCredential,
http::{
policies::{BearerTokenCredentialPolicy, Policy},
NoFormat, RequestContent, Response, Url,
NoFormat, Pipeline, RequestContent, Response, Url,
},
Bytes, Result,
tracing, Bytes, Result,
};
use std::sync::Arc;

/// A client to interact with a specific Azure storage Append blob, although that blob may not yet exist.
pub struct AppendBlobClient {
pub(crate) endpoint: Url,
pub(crate) client: GeneratedAppendBlobClient,
pub(super) client: GeneratedAppendBlobClient,
}

impl AppendBlobClient {
/// Creates a new AppendBlobClient, using Entra ID authentication.
impl GeneratedAppendBlobClient {
/// Creates a new GeneratedAppendBlobClient from a blob URL.
///
/// # Arguments
///
/// * `endpoint` - The full URL of the Azure storage account, for example `https://myaccount.blob.core.windows.net/`
/// * `container_name` - The name of the container containing this Append blob.
/// * `blob_name` - The name of the Append blob to interact with.
/// * `credential` - An implementation of [`TokenCredential`] that can provide an Entra ID token to use when authenticating.
/// * `blob_url` - The full URL of the Append blob, for example `https://myaccount.blob.core.windows.net/mycontainer/myblob`.
/// * `credential` - An optional implementation of [`TokenCredential`] that can provide an Entra ID token to use when authenticating.
/// * `options` - Optional configuration for the client.
pub fn new(
endpoint: &str,
container_name: String,
blob_name: String,
credential: Arc<dyn TokenCredential>,
#[tracing::new("Storage.Blob.AppendBlob")]
pub fn from_url(
blob_url: Url,
credential: Option<Arc<dyn TokenCredential>>,
options: Option<AppendBlobClientOptions>,
) -> Result<Self> {
let mut options = options.unwrap_or_default();
Expand All @@ -53,32 +49,86 @@ impl AppendBlobClient {
.per_call_policies
.push(storage_headers_policy);

let client = GeneratedAppendBlobClient::new(
endpoint,
credential,
container_name,
blob_name,
Some(options),
)?;
let per_retry_policies = if let Some(token_credential) = credential {
if !blob_url.scheme().starts_with("https") {
return Err(azure_core::Error::with_message(
azure_core::error::ErrorKind::Other,
format!("{blob_url} must use http(s)"),
));
}
let auth_policy: Arc<dyn Policy> = Arc::new(BearerTokenCredentialPolicy::new(
token_credential,
vec!["https://storage.azure.com/.default"],
));
vec![auth_policy]
} else {
Vec::default()
};

let pipeline = Pipeline::new(
option_env!("CARGO_PKG_NAME"),
option_env!("CARGO_PKG_VERSION"),
options.client_options.clone(),
Vec::default(),
per_retry_policies,
None,
);

Ok(Self {
endpoint: endpoint.parse()?,
client,
endpoint: blob_url,
version: options.version,
pipeline,
})
}
}

impl AppendBlobClient {
/// Creates a new AppendBlobClient, using Entra ID authentication.
///
/// # Arguments
///
/// * `endpoint` - The full URL of the Azure storage account, for example `https://myaccount.blob.core.windows.net/`
/// * `container_name` - The name of the container containing this Append blob.
/// * `blob_name` - The name of the Append blob to interact with.
/// * `credential` - An implementation of [`TokenCredential`] that can provide an Entra ID token to use when authenticating.
/// * `options` - Optional configuration for the client.
pub fn new(
endpoint: &str,
container_name: &str,
blob_name: &str,
credential: Option<Arc<dyn TokenCredential>>,
options: Option<AppendBlobClientOptions>,
) -> Result<Self> {
let mut url = Url::parse(endpoint)?;

/// Gets the endpoint of the Storage account this client is connected to.
pub fn endpoint(&self) -> &Url {
&self.endpoint
url.path_segments_mut()
.expect("Invalid endpoint URL: Cannot append container_name and blob_name to the blob endpoint.")
.extend([container_name, blob_name]);

let client = GeneratedAppendBlobClient::from_url(url, credential, options)?;
Ok(Self { client })
}

/// Gets the container name of the Storage account this client is connected to.
pub fn container_name(&self) -> &str {
&self.client.container_name
/// Creates a new AppendBlobClient from a blob URL.
///
/// # Arguments
///
/// * `blob_url` - The full URL of the Append blob, for example `https://myaccount.blob.core.windows.net/mycontainer/myblob`.
/// * `credential` - An optional implementation of [`TokenCredential`] that can provide an Entra ID token to use when authenticating.
/// * `options` - Optional configuration for the client.
pub fn from_blob_url(
blob_url: Url,
credential: Option<Arc<dyn TokenCredential>>,
options: Option<AppendBlobClientOptions>,
) -> Result<Self> {
let client = GeneratedAppendBlobClient::from_url(blob_url, credential, options)?;

Ok(Self { client })
}

/// Gets the blob name of the Storage account this client is connected to.
pub fn blob_name(&self) -> &str {
&self.client.blob_name
/// Gets the URL of the Storage account this client is connected to.
pub fn url(&self) -> &Url {
&self.client.endpoint
}

/// Creates a new Append blob.
Expand Down
Loading