Skip to content
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
25 changes: 19 additions & 6 deletions sdk/storage/azure_storage_blob/src/clients/blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
BlobClientReleaseLeaseResult, BlobClientRenewLeaseResult, BlockBlobClientUploadResult,
},
models::{
AccessTier, BlobClientAcquireLeaseOptions, BlobClientBreakLeaseOptions,
AccessConditions, AccessTier, BlobClientAcquireLeaseOptions, BlobClientBreakLeaseOptions,
BlobClientChangeLeaseOptions, BlobClientDeleteOptions, BlobClientDownloadOptions,
BlobClientGetAccountInfoOptions, BlobClientGetPropertiesOptions, BlobClientGetTagsOptions,
BlobClientReleaseLeaseOptions, BlobClientRenewLeaseOptions, BlobClientSetLegalHoldOptions,
Expand Down Expand Up @@ -224,21 +224,34 @@ impl BlobClient {
/// # Arguments
///
/// * `data` - The blob data to upload.
/// * `overwrite` - Whether the blob to be uploaded should overwrite the current data. If True, `upload()` will overwrite the existing data.
/// If False, the operation will fail with ResourceExistsError.
/// * `content_length` - Total length of the blob data to be uploaded.
/// * `access_conditions` - Access conditions to control when the upload should succeed.
/// * `options` - Optional configuration for the request.
///
pub async fn upload(
&self,
data: RequestContent<Bytes, NoFormat>,
overwrite: bool,
content_length: u64,
access_conditions: AccessConditions,
options: Option<BlockBlobClientUploadOptions<'_>>,
) -> Result<Response<BlockBlobClientUploadResult, NoFormat>> {
let mut options = options.unwrap_or_default();

if !overwrite {
options.if_none_match = Some(String::from("*"));
// Apply access conditions specified in AccessConditions(will squash, ie. take precedence over any provided in options bag as-is)
if access_conditions.if_match.is_some() {
options.if_match = access_conditions.if_match;
}
if access_conditions.if_modified_since.is_some() {
options.if_modified_since = access_conditions.if_modified_since;
}
if access_conditions.if_none_match.is_some() {
options.if_none_match = access_conditions.if_none_match;
}
if access_conditions.if_unmodified_since.is_some() {
options.if_unmodified_since = access_conditions.if_unmodified_since;
}
if access_conditions.if_tags.is_some() {
options.if_tags = access_conditions.if_tags;
}

self.block_blob_client()
Expand Down
61 changes: 61 additions & 0 deletions sdk/storage/azure_storage_blob/src/models/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,67 @@ use crate::models::{
PageBlobClientCreateOptions, SignedIdentifier, SignedIdentifiers,
};
use std::collections::HashMap;
use time::OffsetDateTime;

/// Access conditions for blob operations.
///
/// Specifies HTTP conditional headers to control when a blob operation should be performed.
#[derive(Clone, Default, Debug)]
pub struct AccessConditions {
/// A condition that must be met (ETag match) for the request to be processed.
pub if_match: Option<String>,

/// A date-time value. Request is made only if the resource has been modified since the specified date-time.
pub if_modified_since: Option<OffsetDateTime>,

/// A condition that must be met (ETag does not match) for the request to be processed.
pub if_none_match: Option<String>,

/// A date-time value. Request is made only if the resource has not been modified since the specified date-time.
pub if_unmodified_since: Option<OffsetDateTime>,

/// A SQL where clause on blob tags to operate only on blobs with matching tag values.
pub if_tags: Option<String>,
}

impl AccessConditions {
/// Creates access conditions that allow overwriting an existing blob.
///
/// This is equivalent to having no access conditions - the operation will succeed
/// whether the blob exists or not. This is the most common case for uploads.
pub fn allow_overwrite() -> Self {
Self::default()
}

/// Creates access conditions that only succeed if the resource does not exist.
///
/// This sets `if_none_match` to "*" which causes the operation to fail if the resource already exists.
/// Use this when you want to ensure you're creating a new blob, not overwriting an existing one.
pub fn if_not_exists() -> Self {
Self {
if_none_match: Some("*".to_string()),
..Default::default()
}
}

/// Creates access conditions that require the resource to exist with a specific ETag.
///
/// Use this for optimistic concurrency - the operation will only succeed if the blob
/// hasn't been modified since you last read it.
pub fn if_match(etag: impl Into<String>) -> Self {
Self {
if_match: Some(etag.into()),
..Default::default()
}
}

/// Creates empty access conditions (no restrictions).
///
/// Alias for `allow_overwrite()`. The operation will succeed whether the blob exists or not.
pub fn none() -> Self {
Copy link
Member Author

Choose a reason for hiding this comment

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

Could get rid of this if we want to keep it explicit explicit, you need to provide something, even if that something means send None and match service behavior (ie. allow_overwrite())

Self::default()
}
}

/// Augments the current options bag to only create if the Page blob does not already exist.
/// # Arguments
Expand Down
2 changes: 2 additions & 0 deletions sdk/storage/azure_storage_blob/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

mod extensions;

pub use extensions::AccessConditions;

pub use crate::generated::models::{
AccessPolicy, AccessTier, AccountKind, AppendBlobClientAppendBlockFromUrlOptions,
AppendBlobClientAppendBlockFromUrlResult, AppendBlobClientAppendBlockFromUrlResultHeaders,
Expand Down
Loading
Loading