diff --git a/sdk/storage/assets.json b/sdk/storage/assets.json index 38706fd454..20cba4eaa9 100644 --- a/sdk/storage/assets.json +++ b/sdk/storage/assets.json @@ -1,6 +1,6 @@ { "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "rust", - "Tag": "rust/azure_storage_blob_83b2698a78", + "Tag": "rust/azure_storage_blob_f12afa9550", "TagPrefix": "rust/azure_storage_blob" } \ No newline at end of file diff --git a/sdk/storage/azure_storage_blob/src/clients/append_blob_client.rs b/sdk/storage/azure_storage_blob/src/clients/append_blob_client.rs index 9a4df6c5e0..24ebc6753d 100644 --- a/sdk/storage/azure_storage_blob/src/clients/append_blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/append_blob_client.rs @@ -3,7 +3,12 @@ use crate::{ generated::clients::AppendBlobClient as GeneratedAppendBlobClient, - models::{AppendBlobClientCreateOptions, AppendBlobClientCreateResult}, + models::{ + AppendBlobClientAppendBlockFromUrlOptions, AppendBlobClientAppendBlockFromUrlResult, + AppendBlobClientAppendBlockOptions, AppendBlobClientAppendBlockResult, + AppendBlobClientCreateOptions, AppendBlobClientCreateResult, AppendBlobClientSealOptions, + AppendBlobClientSealResult, + }, pipeline::StorageHeadersPolicy, AppendBlobClientOptions, BlobClientOptions, }; @@ -97,4 +102,53 @@ impl AppendBlobClient { ) -> Result> { self.client.create(options).await } + + /// Commits a new block of data to the end of an Append blob. + /// + /// # Arguments + /// + /// * `data` - The blob data to append. + /// * `content_length` - Total length of the blob data to be appended. + /// * `options` - Optional configuration for the request. + pub async fn append_block( + &self, + data: RequestContent, + content_length: u64, + options: Option>, + ) -> Result> { + self.client + .append_block(data, content_length, options) + .await + } + + /// Creates a new block to be committed as part of an Append blob where the contents are + /// read from a URL. + /// + /// # Arguments + /// + /// * `source_url` - The URL of the copy source. + /// * `content_length` - Total length of the blob data to be appended. + /// * `options` - Optional configuration for the request. + pub async fn append_block_from_url( + &self, + source_url: String, + content_length: u64, + options: Option>, + ) -> Result> { + self.client + .append_block_from_url(source_url, content_length, options) + .await + } + + /// Seals the Append blob to make it read-only. Seal is supported only on version 2019-12-12 or later. + /// + /// # Arguments + /// + /// * `options` - Optional parameters for the request. + pub async fn seal( + &self, + options: Option>, + ) -> Result> { + self.client.seal(options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs index fb61c5af3e..b19d94b006 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -4,14 +4,16 @@ use crate::{ generated::clients::BlobClient as GeneratedBlobClient, generated::models::{ - BlobClientDownloadResult, BlobClientGetPropertiesResult, - BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult, - BlockBlobClientUploadResult, + BlobClientAcquireLeaseResult, BlobClientBreakLeaseResult, BlobClientChangeLeaseResult, + BlobClientDownloadResult, BlobClientGetPropertiesResult, BlobClientReleaseLeaseResult, + BlobClientRenewLeaseResult, BlockBlobClientCommitBlockListResult, + BlockBlobClientStageBlockResult, BlockBlobClientUploadResult, }, models::{ - AccessTierOptional, BlobClientDeleteOptions, BlobClientDownloadOptions, - BlobClientGetPropertiesOptions, BlobClientSetMetadataOptions, - BlobClientSetPropertiesOptions, BlobClientSetTierOptions, + AccessTierOptional, BlobClientAcquireLeaseOptions, BlobClientBreakLeaseOptions, + BlobClientChangeLeaseOptions, BlobClientDeleteOptions, BlobClientDownloadOptions, + BlobClientGetPropertiesOptions, BlobClientReleaseLeaseOptions, BlobClientRenewLeaseOptions, + BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTierOptions, BlockBlobClientCommitBlockListOptions, BlockBlobClientUploadOptions, BlockList, BlockListType, BlockLookupList, }, @@ -233,4 +235,82 @@ impl BlobClient { ) -> Result> { self.client.set_tier(tier, options).await } + + /// Requests a new lease on a blob. The lease lock duration can be 15 to 60 seconds, or can be infinite. + /// + /// # Arguments + /// + /// * `duration` - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + /// non-infinite lease can be between 15 and 60 seconds. + /// * `options` - Optional configuration for the request. + pub async fn acquire_lease( + &self, + duration: i32, + options: Option>, + ) -> Result> { + self.client.acquire_lease(duration, options).await + } + + /// Ends a lease and ensures that another client can't acquire a new lease until the current lease + /// period has expired. + /// + /// # Arguments + /// + /// * `options` - Optional configuration for the request. + pub async fn break_lease( + &self, + options: Option>, + ) -> Result> { + self.client.break_lease(options).await + } + + /// Changes the ID of an existing lease to the proposed lease ID. + /// + /// # Arguments + /// + /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + /// lease ID must match. + /// * `proposed_lease_id` - The proposed lease ID for the blob. + /// * `options` - Optional configuration for the request. + pub async fn change_lease( + &self, + lease_id: String, + proposed_lease_id: String, + options: Option>, + ) -> Result> { + self.client + .change_lease(lease_id, proposed_lease_id, options) + .await + } + + /// Frees the lease so that another client can immediately acquire a lease + /// against the blob as soon as the release is complete. + /// + /// # Arguments + /// + /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + /// lease ID must match. + /// * `options` - Optional configuration for the request. + pub async fn release_lease( + &self, + lease_id: String, + options: Option>, + ) -> Result> { + self.client.release_lease(lease_id, options).await + } + + /// Renews the lease on a blob. + /// + /// # Arguments + /// + /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + /// lease ID must match. + /// * `options` - Optional configuration for the request. + pub async fn renew_lease( + &self, + lease_id: String, + options: Option>, + ) -> Result> { + self.client.renew_lease(lease_id, options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs b/sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs index 83a13f632c..01a785cb26 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_container_client.rs @@ -3,11 +3,18 @@ use crate::{ generated::clients::BlobContainerClient as GeneratedBlobContainerClient, - generated::models::BlobContainerClientGetPropertiesResult, + generated::models::{ + BlobContainerClientAcquireLeaseResult, BlobContainerClientBreakLeaseResult, + BlobContainerClientChangeLeaseResult, BlobContainerClientGetPropertiesResult, + BlobContainerClientReleaseLeaseResult, BlobContainerClientRenewLeaseResult, + }, models::{ - BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, - BlobContainerClientGetPropertiesOptions, BlobContainerClientListBlobFlatSegmentOptions, - BlobContainerClientSetMetadataOptions, ListBlobsFlatSegmentResponse, + BlobContainerClientAcquireLeaseOptions, BlobContainerClientBreakLeaseOptions, + BlobContainerClientChangeLeaseOptions, BlobContainerClientCreateOptions, + BlobContainerClientDeleteOptions, BlobContainerClientGetPropertiesOptions, + BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientReleaseLeaseOptions, + BlobContainerClientRenewLeaseOptions, BlobContainerClientSetMetadataOptions, + ListBlobsFlatSegmentResponse, }, pipeline::StorageHeadersPolicy, BlobClient, BlobContainerClientOptions, @@ -157,4 +164,82 @@ impl BlobContainerClient { ) -> Result>> { self.client.list_blob_flat_segment(options) } + + /// Requests a new lease on a container. The lease lock duration can be 15 to 60 seconds, or can be infinite. + /// + /// # Arguments + /// + /// * `duration` - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + /// non-infinite lease can be between 15 and 60 seconds. + /// * `options` - Optional configuration for the request. + pub async fn acquire_lease( + &self, + duration: i32, + options: Option>, + ) -> Result> { + self.client.acquire_lease(duration, options).await + } + + /// Ends a lease and ensures that another client can't acquire a new lease until the current lease + /// period has expired. + /// + /// # Arguments + /// + /// * `options` - Optional configuration for the request. + pub async fn break_lease( + &self, + options: Option>, + ) -> Result> { + self.client.break_lease(options).await + } + + /// Changes the ID of an existing lease to the proposed lease ID. + /// + /// # Arguments + /// + /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + /// lease ID must match. + /// * `proposed_lease_id` - The proposed lease ID for the container. + /// * `options` - Optional configuration for the request. + pub async fn change_lease( + &self, + lease_id: String, + proposed_lease_id: String, + options: Option>, + ) -> Result> { + self.client + .change_lease(lease_id, proposed_lease_id, options) + .await + } + + /// Frees the lease so that another client can immediately acquire a lease + /// against the container as soon as the release is complete. + /// + /// # Arguments + /// + /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + /// lease ID must match. + /// * `options` - Optional configuration for the request. + pub async fn release_lease( + &self, + lease_id: String, + options: Option>, + ) -> Result> { + self.client.release_lease(lease_id, options).await + } + + /// Renews the lease on a container. + /// + /// # Arguments + /// + /// * `lease_id` - A lease ID for the source path. The source path must have an active lease and the + /// lease ID must match. + /// * `options` - Optional configuration for the request. + pub async fn renew_lease( + &self, + lease_id: String, + options: Option>, + ) -> Result> { + self.client.renew_lease(lease_id, options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs index a9450b45f5..9f5a5d8cbe 100644 --- a/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/generated/clients/blob_client.rs @@ -153,9 +153,12 @@ impl BlobClient { /// /// # Arguments /// + /// * `duration` - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + /// non-infinite lease can be between 15 and 60 seconds. A lease duration cannot be changed using renew or change. /// * `options` - Optional parameters for the request. pub async fn acquire_lease( &self, + duration: i32, options: Option>, ) -> Result> { let options = options.unwrap_or_default(); @@ -193,9 +196,7 @@ impl BlobClient { request.insert_header("x-ms-if-tags", if_tags); } request.insert_header("x-ms-lease-action", "acquire"); - if let Some(duration) = options.duration { - request.insert_header("x-ms-lease-duration", duration.to_string()); - } + request.insert_header("x-ms-lease-duration", duration.to_string()); if let Some(proposed_lease_id) = options.proposed_lease_id { request.insert_header("x-ms-proposed-lease-id", proposed_lease_id); } diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/blob_container_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/blob_container_client.rs index c1871f0902..7bb8d3c5f4 100644 --- a/sdk/storage/azure_storage_blob/src/generated/clients/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/src/generated/clients/blob_container_client.rs @@ -106,9 +106,12 @@ impl BlobContainerClient { /// /// # Arguments /// + /// * `duration` - Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A + /// non-infinite lease can be between 15 and 60 seconds. A lease duration cannot be changed using renew or change. /// * `options` - Optional parameters for the request. pub async fn acquire_lease( &self, + duration: i32, options: Option>, ) -> Result> { let options = options.unwrap_or_default(); @@ -135,9 +138,7 @@ impl BlobContainerClient { request.insert_header("x-ms-client-request-id", client_request_id); } request.insert_header("x-ms-lease-action", "acquire"); - if let Some(duration) = options.duration { - request.insert_header("x-ms-lease-duration", duration.to_string()); - } + request.insert_header("x-ms-lease-duration", duration.to_string()); if let Some(proposed_lease_id) = options.proposed_lease_id { request.insert_header("x-ms-proposed-lease-id", proposed_lease_id); } diff --git a/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs b/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs index ba8ace8590..7a09d50746 100644 --- a/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs +++ b/sdk/storage/azure_storage_blob/src/generated/models/header_traits.rs @@ -115,7 +115,6 @@ const VERSION_ID: HeaderName = HeaderName::from_static("x-ms-version-id"); /// Provides access to typed response headers for `AppendBlobClient::append_block_from_url()` pub trait AppendBlobClientAppendBlockFromUrlResultHeaders: private::Sealed { fn content_md5(&self) -> Result>>; - fn date(&self) -> Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn blob_append_offset(&self) -> Result>; @@ -135,11 +134,6 @@ impl AppendBlobClientAppendBlockFromUrlResultHeaders Headers::get_optional_with(self.headers(), &CONTENT_MD5, |h| decode(h.as_str())) } - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -191,7 +185,6 @@ impl AppendBlobClientAppendBlockFromUrlResultHeaders /// Provides access to typed response headers for `AppendBlobClient::append_block()` pub trait AppendBlobClientAppendBlockResultHeaders: private::Sealed { fn content_md5(&self) -> Result>>; - fn date(&self) -> Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn blob_append_offset(&self) -> Result>; @@ -211,11 +204,6 @@ impl AppendBlobClientAppendBlockResultHeaders Headers::get_optional_with(self.headers(), &CONTENT_MD5, |h| decode(h.as_str())) } - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -322,18 +310,12 @@ impl AppendBlobClientCreateResultHeaders for Response Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn is_sealed(&self) -> Result>; } impl AppendBlobClientSealResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -368,18 +350,12 @@ impl BlobClientAbortCopyFromUrlResultHeaders /// Provides access to typed response headers for `BlobClient::acquire_lease()` pub trait BlobClientAcquireLeaseResultHeaders: private::Sealed { - fn date(&self) -> Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn lease_id(&self) -> Result>; } impl BlobClientAcquireLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -400,18 +376,12 @@ impl BlobClientAcquireLeaseResultHeaders for Response Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn lease_time(&self) -> Result>; } impl BlobClientBreakLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -432,18 +402,12 @@ impl BlobClientBreakLeaseResultHeaders for Response Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn lease_id(&self) -> Result>; } impl BlobClientChangeLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -1222,17 +1186,11 @@ impl BlobClientGetPropertiesResultHeaders for Response Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; } impl BlobClientReleaseLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -1248,18 +1206,12 @@ impl BlobClientReleaseLeaseResultHeaders for Response Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn lease_id(&self) -> Result>; } impl BlobClientRenewLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -1424,7 +1376,6 @@ impl BlobClientUndeleteResultHeaders for Response Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn lease_id(&self) -> Result>; @@ -1433,11 +1384,6 @@ pub trait BlobContainerClientAcquireLeaseResultHeaders: private::Sealed { impl BlobContainerClientAcquireLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -1458,21 +1404,14 @@ impl BlobContainerClientAcquireLeaseResultHeaders /// Provides access to typed response headers for `BlobContainerClient::break_lease()` pub trait BlobContainerClientBreakLeaseResultHeaders: private::Sealed { - fn date(&self) -> Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; - fn lease_id(&self) -> Result>; fn lease_time(&self) -> Result>; } impl BlobContainerClientBreakLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -1485,11 +1424,6 @@ impl BlobContainerClientBreakLeaseResultHeaders Headers::get_optional_as(self.headers(), &ETAG) } - /// Uniquely identifies a blobs' lease - fn lease_id(&self) -> Result> { - Headers::get_optional_as(self.headers(), &LEASE_ID) - } - /// Approximate time remaining in the lease period, in seconds. fn lease_time(&self) -> Result> { Headers::get_optional_as(self.headers(), &LEASE_TIME) @@ -1498,7 +1432,6 @@ impl BlobContainerClientBreakLeaseResultHeaders /// Provides access to typed response headers for `BlobContainerClient::change_lease()` pub trait BlobContainerClientChangeLeaseResultHeaders: private::Sealed { - fn date(&self) -> Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn lease_id(&self) -> Result>; @@ -1507,11 +1440,6 @@ pub trait BlobContainerClientChangeLeaseResultHeaders: private::Sealed { impl BlobContainerClientChangeLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -1655,7 +1583,6 @@ impl BlobContainerClientGetPropertiesResultHeaders /// Provides access to typed response headers for `BlobContainerClient::release_lease()` pub trait BlobContainerClientReleaseLeaseResultHeaders: private::Sealed { - fn date(&self) -> Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; } @@ -1663,11 +1590,6 @@ pub trait BlobContainerClientReleaseLeaseResultHeaders: private::Sealed { impl BlobContainerClientReleaseLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { @@ -1697,7 +1619,6 @@ impl BlobContainerClientRenameResultHeaders /// Provides access to typed response headers for `BlobContainerClient::renew_lease()` pub trait BlobContainerClientRenewLeaseResultHeaders: private::Sealed { - fn date(&self) -> Result>; fn last_modified(&self) -> Result>; fn etag(&self) -> Result>; fn lease_id(&self) -> Result>; @@ -1706,11 +1627,6 @@ pub trait BlobContainerClientRenewLeaseResultHeaders: private::Sealed { impl BlobContainerClientRenewLeaseResultHeaders for Response { - /// UTC date/time value generated by the service that indicates the time at which the response was initiated - fn date(&self) -> Result> { - Headers::get_optional_with(self.headers(), &DATE, |h| parse_rfc7231(h.as_str())) - } - /// The date/time that the container was last modified. fn last_modified(&self) -> Result> { Headers::get_optional_with(self.headers(), &LAST_MODIFIED, |h| { diff --git a/sdk/storage/azure_storage_blob/src/generated/models/method_options.rs b/sdk/storage/azure_storage_blob/src/generated/models/method_options.rs index faac0279ab..ef52b65727 100644 --- a/sdk/storage/azure_storage_blob/src/generated/models/method_options.rs +++ b/sdk/storage/azure_storage_blob/src/generated/models/method_options.rs @@ -314,10 +314,6 @@ pub struct BlobClientAcquireLeaseOptions<'a> { /// An opaque, globally-unique, client-generated string identifier for the request. pub client_request_id: Option, - /// Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A non-infinite lease - /// can be between 15 and 60 seconds. A lease duration cannot be changed using renew or change. - pub duration: Option, - /// A condition that must be met in order for the request to be processed. pub if_match: Option, @@ -1139,10 +1135,6 @@ pub struct BlobContainerClientAcquireLeaseOptions<'a> { /// An opaque, globally-unique, client-generated string identifier for the request. pub client_request_id: Option, - /// Specifies the duration of the lease, in seconds, or negative one (-1) for a lease that never expires. A non-infinite lease - /// can be between 15 and 60 seconds. A lease duration cannot be changed using renew or change. - pub duration: Option, - /// A date-time value. A request is made under the condition that the resource has been modified since the specified date-time. pub if_modified_since: Option, diff --git a/sdk/storage/azure_storage_blob/src/models/mod.rs b/sdk/storage/azure_storage_blob/src/models/mod.rs index 7689d1235d..acd9fccbd1 100644 --- a/sdk/storage/azure_storage_blob/src/models/mod.rs +++ b/sdk/storage/azure_storage_blob/src/models/mod.rs @@ -4,23 +4,32 @@ mod extensions; pub use crate::generated::models::{ - AccessTierOptional, AppendBlobClientCreateOptions, AppendBlobClientCreateResult, ArchiveStatus, + AccessTierOptional, AppendBlobClientAppendBlockFromUrlOptions, + AppendBlobClientAppendBlockFromUrlResult, AppendBlobClientAppendBlockOptions, + AppendBlobClientAppendBlockResult, AppendBlobClientCreateOptions, AppendBlobClientCreateResult, + AppendBlobClientSealOptions, AppendBlobClientSealResult, ArchiveStatus, + BlobClientAcquireLeaseOptions, BlobClientAcquireLeaseResultHeaders, + BlobClientBreakLeaseOptions, BlobClientChangeLeaseOptions, BlobClientChangeLeaseResultHeaders, BlobClientDeleteOptions, BlobClientDownloadOptions, BlobClientDownloadResult, BlobClientDownloadResultHeaders, BlobClientGetPropertiesOptions, BlobClientGetPropertiesResult, - BlobClientGetPropertiesResultHeaders, BlobClientSetMetadataOptions, - BlobClientSetPropertiesOptions, BlobClientSetTierOptions, BlobContainerClientCreateOptions, - BlobContainerClientDeleteOptions, BlobContainerClientGetPropertiesOptions, - BlobContainerClientGetPropertiesResult, BlobContainerClientGetPropertiesResultHeaders, - BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientSetMetadataOptions, - BlobImmutabilityPolicyMode, BlobServiceClientGetPropertiesOptions, - BlobServiceClientListContainersSegmentOptions, BlobType, BlockBlobClientCommitBlockListOptions, - BlockBlobClientCommitBlockListResult, BlockBlobClientGetBlockListOptions, - BlockBlobClientStageBlockOptions, BlockBlobClientStageBlockResult, - BlockBlobClientUploadOptions, BlockBlobClientUploadResult, BlockList, BlockListType, - BlockLookupList, CopyStatus, LeaseState, LeaseStatus, ListBlobsFlatSegmentResponse, - ListContainersSegmentResponse, PageBlobClientClearPagesOptions, PageBlobClientClearPagesResult, - PageBlobClientCreateOptions, PageBlobClientCreateResult, PageBlobClientResizeOptions, - PageBlobClientResizeResult, PageBlobClientUploadPagesOptions, PageBlobClientUploadPagesResult, - PublicAccessType, RehydratePriority, StorageServiceProperties, + BlobClientGetPropertiesResultHeaders, BlobClientReleaseLeaseOptions, + BlobClientRenewLeaseOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, + BlobClientSetTierOptions, BlobContainerClientAcquireLeaseOptions, + BlobContainerClientAcquireLeaseResultHeaders, BlobContainerClientBreakLeaseOptions, + BlobContainerClientChangeLeaseOptions, BlobContainerClientChangeLeaseResultHeaders, + BlobContainerClientCreateOptions, BlobContainerClientDeleteOptions, + BlobContainerClientGetPropertiesOptions, BlobContainerClientGetPropertiesResult, + BlobContainerClientGetPropertiesResultHeaders, BlobContainerClientListBlobFlatSegmentOptions, + BlobContainerClientReleaseLeaseOptions, BlobContainerClientRenewLeaseOptions, + BlobContainerClientSetMetadataOptions, BlobImmutabilityPolicyMode, + BlobServiceClientGetPropertiesOptions, BlobServiceClientListContainersSegmentOptions, BlobType, + BlockBlobClientCommitBlockListOptions, BlockBlobClientCommitBlockListResult, + BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions, + BlockBlobClientStageBlockResult, BlockBlobClientUploadOptions, BlockBlobClientUploadResult, + BlockList, BlockListType, BlockLookupList, CopyStatus, LeaseState, LeaseStatus, + ListBlobsFlatSegmentResponse, ListContainersSegmentResponse, PageBlobClientClearPagesOptions, + PageBlobClientClearPagesResult, PageBlobClientCreateOptions, PageBlobClientCreateResult, + PageBlobClientResizeOptions, PageBlobClientResizeResult, PageBlobClientUploadPagesOptions, + PageBlobClientUploadPagesResult, PublicAccessType, RehydratePriority, StorageServiceProperties, }; pub use extensions::*; diff --git a/sdk/storage/azure_storage_blob/tests/append_blob_client.rs b/sdk/storage/azure_storage_blob/tests/append_blob_client.rs index f98dd5d247..70116192d8 100644 --- a/sdk/storage/azure_storage_blob/tests/append_blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/append_blob_client.rs @@ -1,9 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +use azure_core::http::{RequestContent, StatusCode}; use azure_core_test::{recorded, TestContext}; -use azure_storage_blob::models::{BlobClientGetPropertiesResultHeaders, BlobType}; -use azure_storage_blob_test::{get_blob_name, get_container_client}; +use azure_storage_blob::models::{ + BlobClientDownloadResultHeaders, BlobClientGetPropertiesResultHeaders, BlobType, +}; +use azure_storage_blob_test::{create_test_blob, get_blob_name, get_container_client}; use std::error::Error; #[recorded::test] @@ -27,3 +30,118 @@ async fn test_create_append_blob(ctx: TestContext) -> Result<(), Box> container_client.delete_container(None).await?; Ok(()) } + +#[recorded::test] +async fn test_append_block(ctx: TestContext) -> Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, true).await?; + let blob_client = container_client.blob_client(get_blob_name(recording)); + let append_blob_client = blob_client.append_blob_client(); + append_blob_client.create(None).await?; + let mut block_1 = b"hello".to_vec(); + let block_2 = b" rusty world".to_vec(); + + // Act + append_blob_client + .append_block( + RequestContent::from(block_1.clone()), + u64::try_from(block_1.len())?, + None, + ) + .await?; + append_blob_client + .append_block( + RequestContent::from(block_2.clone()), + u64::try_from(block_2.len())?, + None, + ) + .await?; + + // Assert + let response = blob_client.download(None).await?; + let content_length = response.content_length()?; + let (status_code, _, response_body) = response.deconstruct(); + assert!(status_code.is_success()); + assert_eq!(17, content_length.unwrap()); + block_1.extend(&block_2); + assert_eq!(block_1, response_body.collect().await?); + + container_client.delete_container(None).await?; + Ok(()) +} + +#[recorded::test] +async fn test_append_block_from_url(ctx: TestContext) -> Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, true).await?; + let blob_client = container_client.blob_client(get_blob_name(recording)); + let blob_client_2 = container_client.blob_client(get_blob_name(recording)); + create_test_blob(&blob_client_2).await?; + let source_url = format!( + "{}{}/{}", + blob_client_2.endpoint(), + blob_client_2.container_name(), + blob_client_2.blob_name() + ); + let append_blob_client = blob_client.append_blob_client(); + append_blob_client.create(None).await?; + + // Act + append_blob_client + .append_block_from_url(source_url, 17, None) + .await?; + + // Assert + let response = blob_client.download(None).await?; + let content_length = response.content_length()?; + let (status_code, _, response_body) = response.deconstruct(); + assert!(status_code.is_success()); + assert_eq!(17, content_length.unwrap()); + assert_eq!( + b"hello rusty world".to_vec(), + response_body.collect().await? + ); + + container_client.delete_container(None).await?; + Ok(()) +} + +#[recorded::test] +async fn test_seal_append_blob(ctx: TestContext) -> Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, true).await?; + let blob_client = container_client.blob_client(get_blob_name(recording)); + let append_blob_client = blob_client.append_blob_client(); + append_blob_client.create(None).await?; + let test_block = b"test".to_vec(); + + // Act + append_blob_client.seal(None).await?; + let response = append_blob_client + .append_block( + RequestContent::from(test_block.clone()), + u64::try_from(test_block.len())?, + None, + ) + .await; + + // Assert + let error = response.unwrap_err().http_status(); + assert_eq!(StatusCode::Conflict, error.unwrap()); + + // Check Read-Only + let response = blob_client.download(None).await?; + let content_length = response.content_length()?; + let (status_code, _, response_body) = response.deconstruct(); + + // Assert + assert!(status_code.is_success()); + assert_eq!(0, content_length.unwrap()); + assert_eq!(b"".to_vec(), response_body.collect().await?); + + container_client.delete_container(None).await?; + Ok(()) +} diff --git a/sdk/storage/azure_storage_blob/tests/blob_client.rs b/sdk/storage/azure_storage_blob/tests/blob_client.rs index ef95fbbb0b..3155564720 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_client.rs @@ -7,12 +7,15 @@ use azure_core::{ }; use azure_core_test::{recorded, TestContext}; use azure_storage_blob::models::{ - AccessTierOptional, BlobClientDownloadResultHeaders, BlobClientGetPropertiesResultHeaders, - BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlockBlobClientUploadOptions, + AccessTierOptional, BlobClientAcquireLeaseResultHeaders, BlobClientChangeLeaseResultHeaders, + BlobClientDownloadOptions, BlobClientDownloadResultHeaders, BlobClientGetPropertiesOptions, + BlobClientGetPropertiesResultHeaders, BlobClientSetMetadataOptions, + BlobClientSetPropertiesOptions, BlobClientSetTierOptions, BlockBlobClientUploadOptions, LeaseState, }; use azure_storage_blob_test::{create_test_blob, get_blob_name, get_container_client}; -use std::{collections::HashMap, error::Error}; +use std::{collections::HashMap, error::Error, time::Duration}; +use tokio::time; #[recorded::test] async fn test_get_blob_properties(ctx: TestContext) -> Result<(), Box> { @@ -276,3 +279,147 @@ async fn test_set_access_tier(ctx: TestContext) -> Result<(), Box> { container_client.delete_container(None).await?; Ok(()) } + +#[recorded::test] +async fn test_blob_lease_operations(ctx: TestContext) -> Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, true).await?; + let blob_name = get_blob_name(recording); + let blob_client = container_client.blob_client(blob_name.clone()); + let other_blob_client = container_client.blob_client(blob_name); + create_test_blob(&blob_client).await?; + + // Acquire Lease + let acquire_response = blob_client.acquire_lease(15, None).await?; + let lease_id = acquire_response.lease_id()?.unwrap(); + let other_acquire_response = other_blob_client.acquire_lease(15, None).await; + // Assert + let error = other_acquire_response.unwrap_err().http_status(); + assert_eq!(StatusCode::Conflict, error.unwrap()); + + // Change Lease + let proposed_lease_id = "00000000-1111-2222-3333-444444444444".to_string(); + let change_lease_response = blob_client + .change_lease(lease_id, proposed_lease_id.clone(), None) + .await?; + // Assert + let lease_id = change_lease_response.lease_id()?.unwrap(); + assert_eq!(proposed_lease_id.clone().to_string(), lease_id); + + // Sleep until lease expires + time::sleep(Duration::from_secs(15)).await; + + // Renew Lease + blob_client + .renew_lease(proposed_lease_id.clone(), None) + .await?; + let other_acquire_response = other_blob_client.acquire_lease(15, None).await; + // Assert + let error = other_acquire_response.unwrap_err().http_status(); + assert_eq!(StatusCode::Conflict, error.unwrap()); + + // Break Lease + blob_client.break_lease(None).await?; + let other_acquire_response = other_blob_client.acquire_lease(15, None).await; + // Assert + let error = other_acquire_response.unwrap_err().http_status(); + assert_eq!(StatusCode::Conflict, error.unwrap()); + + // Release Lease + blob_client + .release_lease(proposed_lease_id.clone(), None) + .await?; + other_blob_client.acquire_lease(15, None).await?; + + container_client.delete_container(None).await?; + Ok(()) +} + +#[recorded::test] +async fn test_leased_blob_operations(ctx: TestContext) -> Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, true).await?; + let blob_name = get_blob_name(recording); + let blob_client = container_client.blob_client(blob_name.clone()); + create_test_blob(&blob_client).await?; + let acquire_response = blob_client.acquire_lease(-1, None).await?; + let lease_id = acquire_response.lease_id()?.unwrap(); + + // Set Properties, Set Metadata, Set Access Tier + let set_properties_options = BlobClientSetPropertiesOptions { + blob_content_language: Some("spanish".to_string()), + blob_content_disposition: Some("inline".to_string()), + lease_id: Some(lease_id.clone()), + ..Default::default() + }; + blob_client + .set_properties(Some(set_properties_options)) + .await?; + + let update_metadata = HashMap::from([("updated".to_string(), "values".to_string())]); + let set_metadata_options = BlobClientSetMetadataOptions { + metadata: Some(update_metadata.clone()), + lease_id: Some(lease_id.clone()), + ..Default::default() + }; + blob_client.set_metadata(Some(set_metadata_options)).await?; + + let set_tier_options = BlobClientSetTierOptions { + lease_id: Some(lease_id.clone()), + ..Default::default() + }; + blob_client + .set_tier(AccessTierOptional::Cold, Some(set_tier_options)) + .await?; + + // Assert + let get_properties_options = BlobClientGetPropertiesOptions { + lease_id: Some(lease_id.clone()), + ..Default::default() + }; + let response = blob_client + .get_properties(Some(get_properties_options)) + .await?; + let content_language = response.content_language()?; + let content_disposition = response.content_disposition()?; + let response_metadata = response.metadata()?; + let access_tier = response.access_tier()?; + + assert_eq!("spanish".to_string(), content_language.unwrap()); + assert_eq!("inline".to_string(), content_disposition.unwrap()); + assert_eq!(update_metadata, response_metadata); + assert_eq!(AccessTierOptional::Cold.to_string(), access_tier.unwrap()); + + // Overwrite Upload + let data = b"overruled!"; + let upload_options = BlockBlobClientUploadOptions { + lease_id: Some(lease_id.clone()), + ..Default::default() + }; + blob_client + .upload( + RequestContent::from(data.to_vec()), + true, + u64::try_from(data.len())?, + Some(upload_options), + ) + .await?; + + // Assert + let download_options = BlobClientDownloadOptions { + lease_id: Some(lease_id.clone()), + ..Default::default() + }; + let response = blob_client.download(Some(download_options)).await?; + let content_length = response.content_length()?; + let (status_code, _, response_body) = response.deconstruct(); + assert!(status_code.is_success()); + assert_eq!(10, content_length.unwrap()); + assert_eq!(data.to_vec(), response_body.collect().await?); + + blob_client.break_lease(None).await?; + container_client.delete_container(None).await?; + Ok(()) +} diff --git a/sdk/storage/azure_storage_blob/tests/blob_container_client.rs b/sdk/storage/azure_storage_blob/tests/blob_container_client.rs index 822ba98363..65420e18fb 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_container_client.rs @@ -4,12 +4,16 @@ use azure_core::http::StatusCode; use azure_core_test::{recorded, TestContext}; use azure_storage_blob::models::{ + BlobContainerClientAcquireLeaseResultHeaders, BlobContainerClientChangeLeaseResultHeaders, BlobContainerClientGetPropertiesResultHeaders, BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientSetMetadataOptions, BlobType, LeaseState, }; -use azure_storage_blob_test::{create_test_blob, get_container_client}; +use azure_storage_blob_test::{ + create_test_blob, get_blob_service_client, get_container_client, get_container_name, +}; use futures::{StreamExt, TryStreamExt}; -use std::{collections::HashMap, error::Error}; +use std::{collections::HashMap, error::Error, time::Duration}; +use tokio::time; #[recorded::test] async fn test_create_container(ctx: TestContext) -> Result<(), Box> { @@ -201,3 +205,73 @@ async fn test_list_blobs_with_continuation(ctx: TestContext) -> Result<(), Box Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let blob_service_client = get_blob_service_client(recording)?; + let container_name = get_container_name(recording); + let container_client = blob_service_client.blob_container_client(container_name.clone()); + let other_container_client = blob_service_client.blob_container_client(container_name); + container_client.create_container(None).await?; + + // Acquire Lease + let acquire_response = container_client.acquire_lease(15, None).await?; + let lease_id = acquire_response.lease_id()?.unwrap(); + let other_acquire_response = other_container_client.acquire_lease(15, None).await; + // Assert + let error = other_acquire_response.unwrap_err().http_status(); + assert_eq!(StatusCode::Conflict, error.unwrap()); + + let update_metadata = HashMap::from([("hello".to_string(), "world".to_string())]); + let set_metadata_options = BlobContainerClientSetMetadataOptions { + lease_id: Some(lease_id.clone()), + metadata: Some(update_metadata.clone()), + ..Default::default() + }; + container_client + .set_metadata(Some(set_metadata_options)) + .await?; + + // Change Lease + let proposed_lease_id = "00000000-1111-2222-3333-444444444444".to_string(); + let change_lease_response = container_client + .change_lease(lease_id, proposed_lease_id.clone(), None) + .await?; + // Assert + let lease_id = change_lease_response.lease_id()?.unwrap(); + assert_eq!(proposed_lease_id.clone().to_string(), lease_id); + + // Sleep until lease expires + time::sleep(Duration::from_secs(15)).await; + + // Renew Lease + container_client + .renew_lease(proposed_lease_id.clone(), None) + .await?; + let other_acquire_response = other_container_client.acquire_lease(15, None).await; + // Assert + let error = other_acquire_response.unwrap_err().http_status(); + assert_eq!(StatusCode::Conflict, error.unwrap()); + + // Break Lease + container_client.break_lease(None).await?; + let other_acquire_response = other_container_client.acquire_lease(15, None).await; + // Assert + let error = other_acquire_response.unwrap_err().http_status(); + assert_eq!(StatusCode::Conflict, error.unwrap()); + + // Release Lease + container_client + .release_lease(proposed_lease_id.clone(), None) + .await?; + let other_acquire_response = other_container_client.acquire_lease(15, None).await; + let lease_id = other_acquire_response?.lease_id().unwrap(); + other_container_client + .release_lease(lease_id.unwrap(), None) + .await?; + + container_client.delete_container(None).await?; + Ok(()) +} diff --git a/sdk/storage/azure_storage_blob/tsp-location.yaml b/sdk/storage/azure_storage_blob/tsp-location.yaml index 558ee24519..545a4d0bf1 100644 --- a/sdk/storage/azure_storage_blob/tsp-location.yaml +++ b/sdk/storage/azure_storage_blob/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/storage/Microsoft.BlobStorage -commit: ee1ab3f8db24317d6903867f016b88f13190d877 +commit: 41532d82bfae3ea0f933e10279a5fa3d27ae4b03 repo: Azure/azure-rest-api-specs additionalDirectories: