diff --git a/sdk/storage/azure_storage_blob/assets.json b/sdk/storage/azure_storage_blob/assets.json index e326bc025c..83d6370c42 100644 --- a/sdk/storage/azure_storage_blob/assets.json +++ b/sdk/storage/azure_storage_blob/assets.json @@ -1,6 +1,6 @@ { "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "rust", - "Tag": "rust/azure_storage_blob_cb9b90a35f", + "Tag": "rust/azure_storage_blob_39899fca75", "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 1625ea677f..47244cb279 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 @@ -86,7 +86,7 @@ impl AppendBlobClient { /// # Arguments /// /// * `content_length` - Total length of the blob data to be uploaded. - /// * `options` - Optional configuration for the request. See [`AppendBlobClientCreateOptionsExt`](crate::models::AppendBlobClientCreateOptionsExt) for additional usage helpers. + /// * `options` - Optional configuration for the request. pub async fn create( &self, options: Option>, 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 525b38e4fc..877d7bf871 100644 --- a/sdk/storage/azure_storage_blob/src/clients/blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/blob_client.rs @@ -182,9 +182,8 @@ impl BlobClient { options.if_none_match = Some(String::from("*")); } - let block_blob_client = self.client.get_block_blob_client(); - - block_blob_client + self.client + .get_block_blob_client() .upload(data, content_length, Some(options)) .await } diff --git a/sdk/storage/azure_storage_blob/src/clients/block_blob_client.rs b/sdk/storage/azure_storage_blob/src/clients/block_blob_client.rs index 9bb2808f1b..5e51224bd6 100644 --- a/sdk/storage/azure_storage_blob/src/clients/block_blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/block_blob_client.rs @@ -6,14 +6,14 @@ use crate::{ generated::models::{ BlobClientDownloadResult, BlobClientGetPropertiesResult, BlockBlobClientCommitBlockListResult, BlockBlobClientStageBlockResult, - BlockBlobClientUploadResult, + BlockBlobClientUploadBlobFromUrlResult, BlockBlobClientUploadResult, }, models::{ BlobClientDeleteOptions, BlobClientDownloadOptions, BlobClientGetPropertiesOptions, BlobClientSetMetadataOptions, BlobClientSetPropertiesOptions, BlobClientSetTierOptions, BlockBlobClientCommitBlockListOptions, BlockBlobClientGetBlockListOptions, - BlockBlobClientStageBlockOptions, BlockBlobClientUploadOptions, BlockList, BlockListType, - BlockLookupList, + BlockBlobClientStageBlockOptions, BlockBlobClientUploadBlobFromUrlOptions, + BlockBlobClientUploadOptions, BlockList, BlockListType, BlockLookupList, }, pipeline::StorageHeadersPolicy, BlobClientOptions, BlockBlobClientOptions, @@ -135,4 +135,23 @@ impl BlockBlobClient { ) -> Result> { self.client.get_block_list(list_type, options).await } + + /// Creates a new Block Blob where the content of the blob is read from a given URL. The default behavior is content of an existing blob is overwritten with the new blob. + /// + /// # Arguments + /// + /// * `copy_source` - A URL of up to 2 KB in length that specifies a file or blob. The value should be URL-encoded as it would appear in a request URI. + /// The source must either be public or must be authenticated via a shared access signature as part of the url or using the source_authorization keyword. + /// If the source is public, no authentication is required. Examples: + /// - `https://myaccount.blob.core.windows.net/mycontainer/myblob` + /// - `https://myaccount.blob.core.windows.net/mycontainer/myblob?snapshot=` + /// - `https://otheraccount.blob.core.windows.net/mycontainer/myblob?sastoken` + /// * `options` - Optional configuration for the request. + pub async fn upload_blob_from_url( + &self, + copy_source: String, + options: Option>, + ) -> Result> { + self.client.upload_blob_from_url(copy_source, options).await + } } diff --git a/sdk/storage/azure_storage_blob/src/clients/page_blob_client.rs b/sdk/storage/azure_storage_blob/src/clients/page_blob_client.rs index 5b53e2d52e..3784bfe4ca 100644 --- a/sdk/storage/azure_storage_blob/src/clients/page_blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/clients/page_blob_client.rs @@ -90,7 +90,7 @@ impl PageBlobClient { /// /// * `content_length` - The maximum size for the Page blob, up to 1TB. The page blob size must /// be aligned to a 512-byte boundary. - /// * `options` - Optional configuration for the request. See [`PageBlobClientCreateOptionsExt`](crate::models::PageBlobClientCreateOptionsExt) for additional usage helpers. + /// * `options` - Optional configuration for the request. pub async fn create( &self, content_length: u64, diff --git a/sdk/storage/azure_storage_blob/src/generated/clients/block_blob_client.rs b/sdk/storage/azure_storage_blob/src/generated/clients/block_blob_client.rs index f5ab180144..c2c023b805 100644 --- a/sdk/storage/azure_storage_blob/src/generated/clients/block_blob_client.rs +++ b/sdk/storage/azure_storage_blob/src/generated/clients/block_blob_client.rs @@ -5,10 +5,10 @@ use crate::generated::models::{ BlockBlobClientCommitBlockListOptions, BlockBlobClientCommitBlockListResult, - BlockBlobClientGetBlockListOptions, BlockBlobClientPutBlobFromUrlOptions, - BlockBlobClientPutBlobFromUrlResult, BlockBlobClientQueryOptions, BlockBlobClientQueryResult, + BlockBlobClientGetBlockListOptions, BlockBlobClientQueryOptions, BlockBlobClientQueryResult, BlockBlobClientStageBlockFromUrlOptions, BlockBlobClientStageBlockFromUrlResult, BlockBlobClientStageBlockOptions, BlockBlobClientStageBlockResult, + BlockBlobClientUploadBlobFromUrlOptions, BlockBlobClientUploadBlobFromUrlResult, BlockBlobClientUploadOptions, BlockBlobClientUploadResult, BlockList, BlockListType, BlockLookupList, QueryRequest, }; @@ -286,165 +286,6 @@ impl BlockBlobClient { Ok(rsp.into()) } - /// The Put Blob from URL operation creates a new Block Blob where the contents of the blob are read from a given URL. This - /// API is supported beginning with the 2020-04-08 version. Partial updates are not supported with Put Blob from URL; the - /// content of an existing blob is overwritten with the content of the new blob. To perform partial updates to a block blob’s - /// contents using a source URL, use the Put Block from URL API in conjunction with Put Block List. - /// - /// # Arguments - /// - /// * `content_length` - The length of the request. - /// * `copy_source` - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that - /// specifies a page blob snapshot. The value should be URL-encoded as it would appear in a request URI. The source blob must - /// either be public or must be authenticated via a shared access signature. - /// * `options` - Optional parameters for the request. - #[tracing::function("Storage.Blob.Container.Blob.BlockBlob.putBlobFromUrl")] - pub async fn put_blob_from_url( - &self, - content_length: u64, - copy_source: String, - options: Option>, - ) -> Result> { - let options = options.unwrap_or_default(); - let ctx = Context::with_context(&options.method_options.context); - let mut url = self.endpoint.clone(); - let mut path = String::from("{containerName}/{blobName}"); - path = path.replace("{blobName}", &self.blob_name); - path = path.replace("{containerName}", &self.container_name); - url = url.join(&path)?; - url.query_pairs_mut() - .append_key_only("BlockBlob") - .append_key_only("fromUrl"); - if let Some(timeout) = options.timeout { - url.query_pairs_mut() - .append_pair("timeout", &timeout.to_string()); - } - let mut request = Request::new(url, Method::Put); - request.insert_header("accept", "application/xml"); - request.insert_header("content-length", content_length.to_string()); - if let Some(transactional_content_md5) = options.transactional_content_md5 { - request.insert_header("content-md5", encode(transactional_content_md5)); - } - if let Some(if_match) = options.if_match { - request.insert_header("if-match", if_match); - } - if let Some(if_modified_since) = options.if_modified_since { - request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); - } - if let Some(if_none_match) = options.if_none_match { - request.insert_header("if-none-match", if_none_match); - } - if let Some(if_unmodified_since) = options.if_unmodified_since { - request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); - } - if let Some(tier) = options.tier { - request.insert_header("x-ms-access-tier", tier.to_string()); - } - if let Some(blob_cache_control) = options.blob_cache_control { - request.insert_header("x-ms-blob-cache-control", blob_cache_control); - } - if let Some(blob_content_disposition) = options.blob_content_disposition { - request.insert_header("x-ms-blob-content-disposition", blob_content_disposition); - } - if let Some(blob_content_encoding) = options.blob_content_encoding { - request.insert_header("x-ms-blob-content-encoding", blob_content_encoding); - } - if let Some(blob_content_language) = options.blob_content_language { - request.insert_header("x-ms-blob-content-language", blob_content_language); - } - if let Some(blob_content_md5) = options.blob_content_md5 { - request.insert_header("x-ms-blob-content-md5", encode(blob_content_md5)); - } - if let Some(blob_content_type) = options.blob_content_type { - request.insert_header("x-ms-blob-content-type", blob_content_type); - } - request.insert_header("x-ms-blob-type", "BlockBlob"); - if let Some(client_request_id) = options.client_request_id { - request.insert_header("x-ms-client-request-id", client_request_id); - } - request.insert_header("x-ms-copy-source", copy_source); - if let Some(copy_source_authorization) = options.copy_source_authorization { - request.insert_header("x-ms-copy-source-authorization", copy_source_authorization); - } - if let Some(copy_source_blob_properties) = options.copy_source_blob_properties { - request.insert_header( - "x-ms-copy-source-blob-properties", - copy_source_blob_properties.to_string(), - ); - } - if let Some(copy_source_tags) = options.copy_source_tags { - request.insert_header("x-ms-copy-source-tag-option", copy_source_tags.to_string()); - } - if let Some(encryption_algorithm) = options.encryption_algorithm { - request.insert_header( - "x-ms-encryption-algorithm", - encryption_algorithm.to_string(), - ); - } - if let Some(encryption_key) = options.encryption_key { - request.insert_header("x-ms-encryption-key", encryption_key); - } - if let Some(encryption_key_sha256) = options.encryption_key_sha256 { - request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); - } - if let Some(encryption_scope) = options.encryption_scope { - request.insert_header("x-ms-encryption-scope", encryption_scope); - } - if let Some(file_request_intent) = options.file_request_intent { - request.insert_header("x-ms-file-request-intent", file_request_intent.to_string()); - } - if let Some(if_tags) = options.if_tags { - request.insert_header("x-ms-if-tags", if_tags); - } - if let Some(lease_id) = options.lease_id { - request.insert_header("x-ms-lease-id", lease_id); - } - if let Some(metadata) = options.metadata { - for (k, v) in &metadata { - request.insert_header(format!("x-ms-meta-{k}"), v); - } - } - if let Some(source_content_md5) = options.source_content_md5 { - request.insert_header("x-ms-source-content-md5", encode(source_content_md5)); - } - if let Some(source_if_match) = options.source_if_match { - request.insert_header("x-ms-source-if-match", source_if_match); - } - if let Some(source_if_modified_since) = options.source_if_modified_since { - request.insert_header( - "x-ms-source-if-modified-since", - to_rfc7231(&source_if_modified_since), - ); - } - if let Some(source_if_none_match) = options.source_if_none_match { - request.insert_header("x-ms-source-if-none-match", source_if_none_match); - } - if let Some(source_if_tags) = options.source_if_tags { - request.insert_header("x-ms-source-if-tags", source_if_tags); - } - if let Some(source_if_unmodified_since) = options.source_if_unmodified_since { - request.insert_header( - "x-ms-source-if-unmodified-since", - to_rfc7231(&source_if_unmodified_since), - ); - } - if let Some(blob_tags_string) = options.blob_tags_string { - request.insert_header("x-ms-tags", blob_tags_string); - } - request.insert_header("x-ms-version", &self.version); - let rsp = self.pipeline.send(&ctx, &mut request).await?; - if !rsp.status().is_success() { - let status = rsp.status(); - let http_error = HttpError::new(rsp).await; - let error_kind = ErrorKind::http_response( - status, - http_error.error_code().map(std::borrow::ToOwned::to_owned), - ); - return Err(Error::new(error_kind, http_error)); - } - Ok(rsp.into()) - } - /// The Query operation enables users to select/project on blob data by providing simple query expressions. /// /// # Arguments @@ -860,6 +701,163 @@ impl BlockBlobClient { } Ok(rsp.into()) } + + /// The Put Blob from URL operation creates a new Block Blob where the contents of the blob are read from a given URL. This + /// API is supported beginning with the 2020-04-08 version. Partial updates are not supported with Put Blob from URL; the + /// content of an existing blob is overwritten with the content of the new blob. To perform partial updates to a block blob’s + /// contents using a source URL, use the Put Block from URL API in conjunction with Put Block List. + /// + /// # Arguments + /// + /// * `copy_source` - Specifies the name of the source page blob snapshot. This value is a URL of up to 2 KB in length that + /// specifies a page blob snapshot. The value should be URL-encoded as it would appear in a request URI. The source blob must + /// either be public or must be authenticated via a shared access signature. + /// * `options` - Optional parameters for the request. + #[tracing::function("Storage.Blob.Container.Blob.BlockBlob.uploadBlobFromUrl")] + pub async fn upload_blob_from_url( + &self, + copy_source: String, + options: Option>, + ) -> Result> { + let options = options.unwrap_or_default(); + let ctx = Context::with_context(&options.method_options.context); + let mut url = self.endpoint.clone(); + let mut path = String::from("{containerName}/{blobName}"); + path = path.replace("{blobName}", &self.blob_name); + path = path.replace("{containerName}", &self.container_name); + url = url.join(&path)?; + url.query_pairs_mut() + .append_key_only("BlockBlob") + .append_key_only("fromUrl"); + if let Some(timeout) = options.timeout { + url.query_pairs_mut() + .append_pair("timeout", &timeout.to_string()); + } + let mut request = Request::new(url, Method::Put); + request.insert_header("accept", "application/xml"); + request.insert_header("content-length", "0"); + if let Some(transactional_content_md5) = options.transactional_content_md5 { + request.insert_header("content-md5", encode(transactional_content_md5)); + } + if let Some(if_match) = options.if_match { + request.insert_header("if-match", if_match); + } + if let Some(if_modified_since) = options.if_modified_since { + request.insert_header("if-modified-since", to_rfc7231(&if_modified_since)); + } + if let Some(if_none_match) = options.if_none_match { + request.insert_header("if-none-match", if_none_match); + } + if let Some(if_unmodified_since) = options.if_unmodified_since { + request.insert_header("if-unmodified-since", to_rfc7231(&if_unmodified_since)); + } + if let Some(tier) = options.tier { + request.insert_header("x-ms-access-tier", tier.to_string()); + } + if let Some(blob_cache_control) = options.blob_cache_control { + request.insert_header("x-ms-blob-cache-control", blob_cache_control); + } + if let Some(blob_content_disposition) = options.blob_content_disposition { + request.insert_header("x-ms-blob-content-disposition", blob_content_disposition); + } + if let Some(blob_content_encoding) = options.blob_content_encoding { + request.insert_header("x-ms-blob-content-encoding", blob_content_encoding); + } + if let Some(blob_content_language) = options.blob_content_language { + request.insert_header("x-ms-blob-content-language", blob_content_language); + } + if let Some(blob_content_md5) = options.blob_content_md5 { + request.insert_header("x-ms-blob-content-md5", encode(blob_content_md5)); + } + if let Some(blob_content_type) = options.blob_content_type { + request.insert_header("x-ms-blob-content-type", blob_content_type); + } + request.insert_header("x-ms-blob-type", "BlockBlob"); + if let Some(client_request_id) = options.client_request_id { + request.insert_header("x-ms-client-request-id", client_request_id); + } + request.insert_header("x-ms-copy-source", copy_source); + if let Some(copy_source_authorization) = options.copy_source_authorization { + request.insert_header("x-ms-copy-source-authorization", copy_source_authorization); + } + if let Some(copy_source_blob_properties) = options.copy_source_blob_properties { + request.insert_header( + "x-ms-copy-source-blob-properties", + copy_source_blob_properties.to_string(), + ); + } + if let Some(copy_source_tags) = options.copy_source_tags { + request.insert_header("x-ms-copy-source-tag-option", copy_source_tags.to_string()); + } + if let Some(encryption_algorithm) = options.encryption_algorithm { + request.insert_header( + "x-ms-encryption-algorithm", + encryption_algorithm.to_string(), + ); + } + if let Some(encryption_key) = options.encryption_key { + request.insert_header("x-ms-encryption-key", encryption_key); + } + if let Some(encryption_key_sha256) = options.encryption_key_sha256 { + request.insert_header("x-ms-encryption-key-sha256", encryption_key_sha256); + } + if let Some(encryption_scope) = options.encryption_scope { + request.insert_header("x-ms-encryption-scope", encryption_scope); + } + if let Some(file_request_intent) = options.file_request_intent { + request.insert_header("x-ms-file-request-intent", file_request_intent.to_string()); + } + if let Some(if_tags) = options.if_tags { + request.insert_header("x-ms-if-tags", if_tags); + } + if let Some(lease_id) = options.lease_id { + request.insert_header("x-ms-lease-id", lease_id); + } + if let Some(metadata) = options.metadata { + for (k, v) in &metadata { + request.insert_header(format!("x-ms-meta-{k}"), v); + } + } + if let Some(source_content_md5) = options.source_content_md5 { + request.insert_header("x-ms-source-content-md5", encode(source_content_md5)); + } + if let Some(source_if_match) = options.source_if_match { + request.insert_header("x-ms-source-if-match", source_if_match); + } + if let Some(source_if_modified_since) = options.source_if_modified_since { + request.insert_header( + "x-ms-source-if-modified-since", + to_rfc7231(&source_if_modified_since), + ); + } + if let Some(source_if_none_match) = options.source_if_none_match { + request.insert_header("x-ms-source-if-none-match", source_if_none_match); + } + if let Some(source_if_tags) = options.source_if_tags { + request.insert_header("x-ms-source-if-tags", source_if_tags); + } + if let Some(source_if_unmodified_since) = options.source_if_unmodified_since { + request.insert_header( + "x-ms-source-if-unmodified-since", + to_rfc7231(&source_if_unmodified_since), + ); + } + if let Some(blob_tags_string) = options.blob_tags_string { + request.insert_header("x-ms-tags", blob_tags_string); + } + request.insert_header("x-ms-version", &self.version); + let rsp = self.pipeline.send(&ctx, &mut request).await?; + if !rsp.status().is_success() { + let status = rsp.status(); + let http_error = HttpError::new(rsp).await; + let error_kind = ErrorKind::http_response( + status, + http_error.error_code().map(std::borrow::ToOwned::to_owned), + ); + return Err(Error::new(error_kind, http_error)); + } + Ok(rsp.into()) + } } impl Default for BlockBlobClientOptions { 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 b8f2cce833..9cf24df535 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 @@ -18,9 +18,9 @@ use super::{ BlobContainerClientRenameResult, BlobContainerClientRenewLeaseResult, BlobContainerClientRestoreResult, BlobContainerClientSetAccessPolicyResult, BlobImmutabilityPolicyMode, BlobServiceClientGetAccountInfoResult, BlobTags, BlobType, - BlockBlobClientCommitBlockListResult, BlockBlobClientPutBlobFromUrlResult, - BlockBlobClientQueryResult, BlockBlobClientStageBlockFromUrlResult, - BlockBlobClientStageBlockResult, BlockBlobClientUploadResult, BlockList, CopyStatus, + BlockBlobClientCommitBlockListResult, BlockBlobClientQueryResult, + BlockBlobClientStageBlockFromUrlResult, BlockBlobClientStageBlockResult, + BlockBlobClientUploadBlobFromUrlResult, BlockBlobClientUploadResult, BlockList, CopyStatus, FilterBlobSegment, LeaseDuration, LeaseState, LeaseStatus, ListBlobsFlatSegmentResponse, ListBlobsHierarchySegmentResponse, PageBlobClientClearPagesResult, PageBlobClientCopyIncrementalResult, PageBlobClientCreateResult, PageBlobClientResizeResult, @@ -1764,70 +1764,6 @@ impl BlockBlobClientCommitBlockListResultHeaders } } -/// Provides access to typed response headers for `BlockBlobClient::put_blob_from_url()` -pub trait BlockBlobClientPutBlobFromUrlResultHeaders: private::Sealed { - fn content_md5(&self) -> Result>>; - fn date(&self) -> Result>; - fn last_modified(&self) -> Result>; - fn etag(&self) -> Result>; - fn encryption_key_sha256(&self) -> Result>; - fn encryption_scope(&self) -> Result>; - fn is_server_encrypted(&self) -> Result>; - fn version_id(&self) -> Result>; -} - -impl BlockBlobClientPutBlobFromUrlResultHeaders - for Response -{ - /// If the blob has an MD5 hash and this operation is to read the full blob, this response header is returned so that the - /// client can check for message content integrity. - fn content_md5(&self) -> Result>> { - 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| { - parse_rfc7231(h.as_str()) - }) - } - - /// The ETag contains a value that you can use to perform operations conditionally. - fn etag(&self) -> Result> { - Headers::get_optional_as(self.headers(), &ETAG) - } - - /// The SHA-256 hash of the encryption key used to encrypt the blob. This header is only returned when the blob was encrypted - /// with a customer-provided key. - fn encryption_key_sha256(&self) -> Result> { - Headers::get_optional_as(self.headers(), &ENCRYPTION_KEY_SHA256) - } - - /// If the blob has a MD5 hash, and if request contains range header (Range or x-ms-range), this response header is returned - /// with the value of the whole blob's MD5 value. This value may or may not be equal to the value returned in Content-MD5 - /// header, with the latter calculated from the requested range - fn encryption_scope(&self) -> Result> { - Headers::get_optional_as(self.headers(), &ENCRYPTION_SCOPE) - } - - /// The value of this header is set to true if the contents of the request are successfully encrypted using the specified - /// algorithm, and false otherwise. - fn is_server_encrypted(&self) -> Result> { - Headers::get_optional_as(self.headers(), &REQUEST_SERVER_ENCRYPTED) - } - - /// A DateTime value returned by the service that uniquely identifies the blob. The value of this header indicates the blob - /// version, and may be used in subsequent requests to access this version of the blob. - fn version_id(&self) -> Result> { - Headers::get_optional_as(self.headers(), &VERSION_ID) - } -} - /// Provides access to typed response headers for `BlockBlobClient::query()` pub trait BlockBlobClientQueryResultHeaders: private::Sealed { fn accept_ranges(&self) -> Result>; @@ -2135,6 +2071,64 @@ impl BlockBlobClientStageBlockResultHeaders } } +/// Provides access to typed response headers for `BlockBlobClient::upload_blob_from_url()` +pub trait BlockBlobClientUploadBlobFromUrlResultHeaders: private::Sealed { + fn content_md5(&self) -> Result>>; + fn last_modified(&self) -> Result>; + fn etag(&self) -> Result>; + fn encryption_key_sha256(&self) -> Result>; + fn encryption_scope(&self) -> Result>; + fn is_server_encrypted(&self) -> Result>; + fn version_id(&self) -> Result>; +} + +impl BlockBlobClientUploadBlobFromUrlResultHeaders + for Response +{ + /// If the blob has an MD5 hash and this operation is to read the full blob, this response header is returned so that the + /// client can check for message content integrity. + fn content_md5(&self) -> Result>> { + Headers::get_optional_with(self.headers(), &CONTENT_MD5, |h| decode(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| { + parse_rfc7231(h.as_str()) + }) + } + + /// The ETag contains a value that you can use to perform operations conditionally. + fn etag(&self) -> Result> { + Headers::get_optional_as(self.headers(), &ETAG) + } + + /// The SHA-256 hash of the encryption key used to encrypt the blob. This header is only returned when the blob was encrypted + /// with a customer-provided key. + fn encryption_key_sha256(&self) -> Result> { + Headers::get_optional_as(self.headers(), &ENCRYPTION_KEY_SHA256) + } + + /// If the blob has a MD5 hash, and if request contains range header (Range or x-ms-range), this response header is returned + /// with the value of the whole blob's MD5 value. This value may or may not be equal to the value returned in Content-MD5 + /// header, with the latter calculated from the requested range + fn encryption_scope(&self) -> Result> { + Headers::get_optional_as(self.headers(), &ENCRYPTION_SCOPE) + } + + /// The value of this header is set to true if the contents of the request are successfully encrypted using the specified + /// algorithm, and false otherwise. + fn is_server_encrypted(&self) -> Result> { + Headers::get_optional_as(self.headers(), &REQUEST_SERVER_ENCRYPTED) + } + + /// A DateTime value returned by the service that uniquely identifies the blob. The value of this header indicates the blob + /// version, and may be used in subsequent requests to access this version of the blob. + fn version_id(&self) -> Result> { + Headers::get_optional_as(self.headers(), &VERSION_ID) + } +} + /// Provides access to typed response headers for `BlockBlobClient::upload()` pub trait BlockBlobClientUploadResultHeaders: private::Sealed { fn content_md5(&self) -> Result>>; @@ -2680,8 +2674,8 @@ mod private { BlobContainerClientRenameResult, BlobContainerClientRenewLeaseResult, BlobContainerClientRestoreResult, BlobContainerClientSetAccessPolicyResult, BlobServiceClientGetAccountInfoResult, BlobTags, BlockBlobClientCommitBlockListResult, - BlockBlobClientPutBlobFromUrlResult, BlockBlobClientQueryResult, - BlockBlobClientStageBlockFromUrlResult, BlockBlobClientStageBlockResult, + BlockBlobClientQueryResult, BlockBlobClientStageBlockFromUrlResult, + BlockBlobClientStageBlockResult, BlockBlobClientUploadBlobFromUrlResult, BlockBlobClientUploadResult, BlockList, FilterBlobSegment, ListBlobsFlatSegmentResponse, ListBlobsHierarchySegmentResponse, PageBlobClientClearPagesResult, PageBlobClientCopyIncrementalResult, PageBlobClientCreateResult, @@ -2727,10 +2721,10 @@ mod private { impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} - impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} + impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} impl Sealed for Response {} 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 8ece63fced..aa0b2ca494 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 @@ -1784,120 +1784,6 @@ pub struct BlockBlobClientGetBlockListOptions<'a> { pub timeout: Option, } -/// Options to be passed to `BlockBlobClient::put_blob_from_url()` -#[derive(Clone, Default, SafeDebug)] -pub struct BlockBlobClientPutBlobFromUrlOptions<'a> { - /// Optional. Sets the blob's cache control. If specified, this property is stored with the blob and returned with a read - /// request. - pub blob_cache_control: Option, - - /// Optional. Sets the blob's content disposition. If specified, this property is stored with the blob and returned with a - /// read request. - pub blob_content_disposition: Option, - - /// Optional. Sets the blob's content encoding. If specified, this property is stored with the blob and returned with a read - /// request. - pub blob_content_encoding: Option, - - /// Optional. Set the blob's content language. If specified, this property is stored with the blob and returned with a read - /// request. - pub blob_content_language: Option, - - /// Optional. An MD5 hash of the blob content. Note that this hash is not validated, as the hashes for the individual blocks - /// were validated when each was uploaded. - pub blob_content_md5: Option>, - - /// Optional. Sets the blob's content type. If specified, this property is stored with the blob and returned with a read request. - pub blob_content_type: Option, - - /// Optional. Used to set blob tags in various blob operations. - pub blob_tags_string: Option, - - /// An opaque, globally-unique, client-generated string identifier for the request. - pub client_request_id: Option, - - /// Only Bearer type is supported. Credentials should be a valid OAuth access token to copy source. - pub copy_source_authorization: Option, - - /// Optional, default is true. Indicates if properties from the source blob should be copied. - pub copy_source_blob_properties: Option, - - /// Optional, default 'replace'. Indicates if source tags should be copied or replaced with the tags specified by x-ms-tags. - pub copy_source_tags: Option, - - /// Optional. Version 2019-07-07 and later. Specifies the algorithm to use for encryption. If not specified, the default is - /// AES256. - pub encryption_algorithm: Option, - - /// Optional. Version 2019-07-07 and later. Specifies the encryption key to use to encrypt the data provided in the request. - /// If not specified, the request will be encrypted with the root account key. - pub encryption_key: Option, - - /// Optional. Version 2019-07-07 and later. Specifies the SHA256 hash of the encryption key used to encrypt the data provided - /// in the request. This header is only used for encryption with a customer-provided key. If the request is authenticated - /// with a client token, this header should be specified using the SHA256 hash of the encryption key. - pub encryption_key_sha256: Option, - - /// Optional. Version 2019-07-07 and later. Specifies the encryption scope to use to encrypt the data provided in the request. - /// If not specified, the request will be encrypted with the root account key. - pub encryption_scope: Option, - - /// Valid value is backup - pub file_request_intent: Option, - - /// A condition that must be met in order for the request to be processed. - pub if_match: 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, - - /// A condition that must be met in order for the request to be processed. - pub if_none_match: Option, - - /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. - pub if_tags: Option, - - /// A date-time value. A request is made under the condition that the resource has not been modified since the specified date-time. - pub if_unmodified_since: Option, - - /// If specified, the operation only succeeds if the resource's lease is active and matches this ID. - pub lease_id: Option, - - /// The metadata headers. - pub metadata: Option>, - - /// Allows customization of the method call. - pub method_options: ClientMethodOptions<'a>, - - /// Specify the md5 calculated for the range of bytes that must be read from the copy source. - pub source_content_md5: Option>, - - /// Specify an ETag value to operate only on blobs with a matching value. - pub source_if_match: Option, - - /// Specify this header value to operate only on a blob if it has been modified since the specified date/time. - pub source_if_modified_since: Option, - - /// Specify this header value to operate only on a blob if it has been modified since the specified date/time. - pub source_if_none_match: Option, - - /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. - pub source_if_tags: Option, - - /// Specify this header value to operate only on a blob if it has not been modified since the specified date/time. - pub source_if_unmodified_since: Option, - - /// The tier to be set on the blob. - pub tier: Option, - - /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts for Blob Service Operations.](https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations) - pub timeout: Option, - - /// Optional. An MD5 hash of the blob content. Note that this hash is not validated, as the hashes for the individual blocks - /// were validated when each was uploaded. - pub transactional_content_md5: Option>, -} - /// Options to be passed to `BlockBlobClient::query()` #[derive(Clone, Default, SafeDebug)] pub struct BlockBlobClientQueryOptions<'a> { @@ -2156,6 +2042,120 @@ pub struct BlockBlobClientUploadOptions<'a> { pub transactional_content_md5: Option>, } +/// Options to be passed to `BlockBlobClient::upload_blob_from_url()` +#[derive(Clone, Default, SafeDebug)] +pub struct BlockBlobClientUploadBlobFromUrlOptions<'a> { + /// Optional. Sets the blob's cache control. If specified, this property is stored with the blob and returned with a read + /// request. + pub blob_cache_control: Option, + + /// Optional. Sets the blob's content disposition. If specified, this property is stored with the blob and returned with a + /// read request. + pub blob_content_disposition: Option, + + /// Optional. Sets the blob's content encoding. If specified, this property is stored with the blob and returned with a read + /// request. + pub blob_content_encoding: Option, + + /// Optional. Set the blob's content language. If specified, this property is stored with the blob and returned with a read + /// request. + pub blob_content_language: Option, + + /// Optional. An MD5 hash of the blob content. Note that this hash is not validated, as the hashes for the individual blocks + /// were validated when each was uploaded. + pub blob_content_md5: Option>, + + /// Optional. Sets the blob's content type. If specified, this property is stored with the blob and returned with a read request. + pub blob_content_type: Option, + + /// Optional. Used to set blob tags in various blob operations. + pub blob_tags_string: Option, + + /// An opaque, globally-unique, client-generated string identifier for the request. + pub client_request_id: Option, + + /// Only Bearer type is supported. Credentials should be a valid OAuth access token to copy source. + pub copy_source_authorization: Option, + + /// Optional, default is true. Indicates if properties from the source blob should be copied. + pub copy_source_blob_properties: Option, + + /// Optional, default 'replace'. Indicates if source tags should be copied or replaced with the tags specified by x-ms-tags. + pub copy_source_tags: Option, + + /// Optional. Version 2019-07-07 and later. Specifies the algorithm to use for encryption. If not specified, the default is + /// AES256. + pub encryption_algorithm: Option, + + /// Optional. Version 2019-07-07 and later. Specifies the encryption key to use to encrypt the data provided in the request. + /// If not specified, the request will be encrypted with the root account key. + pub encryption_key: Option, + + /// Optional. Version 2019-07-07 and later. Specifies the SHA256 hash of the encryption key used to encrypt the data provided + /// in the request. This header is only used for encryption with a customer-provided key. If the request is authenticated + /// with a client token, this header should be specified using the SHA256 hash of the encryption key. + pub encryption_key_sha256: Option, + + /// Optional. Version 2019-07-07 and later. Specifies the encryption scope to use to encrypt the data provided in the request. + /// If not specified, the request will be encrypted with the root account key. + pub encryption_scope: Option, + + /// Valid value is backup + pub file_request_intent: Option, + + /// A condition that must be met in order for the request to be processed. + pub if_match: 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, + + /// A condition that must be met in order for the request to be processed. + pub if_none_match: Option, + + /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. + pub if_tags: Option, + + /// A date-time value. A request is made under the condition that the resource has not been modified since the specified date-time. + pub if_unmodified_since: Option, + + /// If specified, the operation only succeeds if the resource's lease is active and matches this ID. + pub lease_id: Option, + + /// The metadata headers. + pub metadata: Option>, + + /// Allows customization of the method call. + pub method_options: ClientMethodOptions<'a>, + + /// Specify the md5 calculated for the range of bytes that must be read from the copy source. + pub source_content_md5: Option>, + + /// Specify an ETag value to operate only on blobs with a matching value. + pub source_if_match: Option, + + /// Specify this header value to operate only on a blob if it has been modified since the specified date/time. + pub source_if_modified_since: Option, + + /// Specify this header value to operate only on a blob if it has been modified since the specified date/time. + pub source_if_none_match: Option, + + /// Specify a SQL where clause on blob tags to operate only on blobs with a matching value. + pub source_if_tags: Option, + + /// Specify this header value to operate only on a blob if it has not been modified since the specified date/time. + pub source_if_unmodified_since: Option, + + /// The tier to be set on the blob. + pub tier: Option, + + /// The timeout parameter is expressed in seconds. For more information, see [Setting Timeouts for Blob Service Operations.](https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/setting-timeouts-for-blob-service-operations) + pub timeout: Option, + + /// Optional. An MD5 hash of the blob content. Note that this hash is not validated, as the hashes for the individual blocks + /// were validated when each was uploaded. + pub transactional_content_md5: Option>, +} + /// Options to be passed to `PageBlobClient::clear_pages()` #[derive(Clone, Default, SafeDebug)] pub struct PageBlobClientClearPagesOptions<'a> { diff --git a/sdk/storage/azure_storage_blob/src/generated/models/pub_models.rs b/sdk/storage/azure_storage_blob/src/generated/models/pub_models.rs index 7dfce93f70..10709f51f4 100644 --- a/sdk/storage/azure_storage_blob/src/generated/models/pub_models.rs +++ b/sdk/storage/azure_storage_blob/src/generated/models/pub_models.rs @@ -258,10 +258,7 @@ pub struct BlobItemInternal { pub name: Option, /// The object replication metadata of the blob. - #[serde( - rename = "ObjectReplicationMetadata", - skip_serializing_if = "Option::is_none" - )] + #[serde(rename = "OrMetadata", skip_serializing_if = "Option::is_none")] pub object_replication_metadata: Option, /// The properties of the blob. @@ -641,10 +638,6 @@ pub struct Block { #[derive(SafeDebug)] pub struct BlockBlobClientCommitBlockListResult; -/// Contains results for `BlockBlobClient::put_blob_from_url()` -#[derive(SafeDebug)] -pub struct BlockBlobClientPutBlobFromUrlResult; - /// Contains results for `BlockBlobClient::query()` #[derive(SafeDebug)] pub struct BlockBlobClientQueryResult; @@ -657,6 +650,10 @@ pub struct BlockBlobClientStageBlockFromUrlResult; #[derive(SafeDebug)] pub struct BlockBlobClientStageBlockResult; +/// Contains results for `BlockBlobClient::upload_blob_from_url()` +#[derive(SafeDebug)] +pub struct BlockBlobClientUploadBlobFromUrlResult; + /// Contains results for `BlockBlobClient::upload()` #[derive(SafeDebug)] pub struct BlockBlobClientUploadResult; diff --git a/sdk/storage/azure_storage_blob/src/models/extensions.rs b/sdk/storage/azure_storage_blob/src/models/extensions.rs index 4a358fc70a..0402583a34 100644 --- a/sdk/storage/azure_storage_blob/src/models/extensions.rs +++ b/sdk/storage/azure_storage_blob/src/models/extensions.rs @@ -2,22 +2,18 @@ // Licensed under the MIT License. use crate::models::{ - AppendBlobClientCreateOptions, BlobTag, BlobTags, PageBlobClientCreateOptions, + AppendBlobClientCreateOptions, BlobTag, BlobTags, BlockBlobClientUploadBlobFromUrlOptions, + PageBlobClientCreateOptions, }; use azure_core::error::ErrorKind; use std::collections::HashMap; -/// Provides usage helpers for setting the `PageBlobClientCreateOptions` optional configurations. -pub trait PageBlobClientCreateOptionsExt { - /// Augments the current options bag to only create if the Page blob does not already exist. - /// # Arguments - /// - /// * `self` - The options bag to be modified. - fn with_if_not_exists(self) -> Self; -} - -impl PageBlobClientCreateOptionsExt for PageBlobClientCreateOptions<'_> { - fn with_if_not_exists(self) -> Self { +/// Augments the current options bag to only create if the Page blob does not already exist. +/// # Arguments +/// +/// * `self` - The options bag to be modified. +impl<'a> PageBlobClientCreateOptions<'a> { + pub fn with_if_not_exists(self) -> Self { Self { if_none_match: Some("*".into()), ..self @@ -25,17 +21,25 @@ impl PageBlobClientCreateOptionsExt for PageBlobClientCreateOptions<'_> { } } -/// Provides usage helpers for setting the `AppendBlobClientCreateOptions` optional configurations. -pub trait AppendBlobClientCreateOptionsExt { - /// Augments the current options bag to only create if the Append blob does not already exist. - /// # Arguments - /// - /// * `self` - The options bag to be modified. - fn with_if_not_exists(self) -> Self; +/// Augments the current options bag to only create if the Append blob does not already exist. +/// # Arguments +/// +/// * `self` - The options bag to be modified. +impl<'a> AppendBlobClientCreateOptions<'a> { + pub fn with_if_not_exists(self) -> Self { + Self { + if_none_match: Some("*".into()), + ..self + } + } } -impl AppendBlobClientCreateOptionsExt for AppendBlobClientCreateOptions<'_> { - fn with_if_not_exists(self) -> Self { +/// Augments the current options bag to only create if the Block blob does not already exist. +/// # Arguments +/// +/// * `self` - The options bag to be modified. +impl<'a> BlockBlobClientUploadBlobFromUrlOptions<'a> { + pub fn with_if_not_exists(self) -> Self { Self { if_none_match: Some("*".into()), ..self diff --git a/sdk/storage/azure_storage_blob/src/models/mod.rs b/sdk/storage/azure_storage_blob/src/models/mod.rs index 08dcd719e7..526cf35a30 100644 --- a/sdk/storage/azure_storage_blob/src/models/mod.rs +++ b/sdk/storage/azure_storage_blob/src/models/mod.rs @@ -32,8 +32,8 @@ pub use crate::generated::models::{ BlobContainerClientListBlobFlatSegmentOptions, BlobContainerClientReleaseLeaseOptions, BlobContainerClientReleaseLeaseResult, BlobContainerClientReleaseLeaseResultHeaders, BlobContainerClientRenewLeaseOptions, BlobContainerClientRenewLeaseResult, - BlobContainerClientSetMetadataOptions, BlobFlatListSegment, BlobImmutabilityPolicyMode, - BlobItemInternal, BlobMetadata, BlobName, BlobPropertiesInternal, + BlobContainerClientSetMetadataOptions, BlobCopySourceTags, BlobDeleteType, BlobFlatListSegment, + BlobImmutabilityPolicyMode, BlobItemInternal, BlobMetadata, BlobName, BlobPropertiesInternal, BlobServiceClientGetAccountInfoOptions, BlobServiceClientGetAccountInfoResult, BlobServiceClientGetAccountInfoResultHeaders, BlobServiceClientGetPropertiesOptions, BlobServiceClientListContainersSegmentOptions, BlobServiceClientSetPropertiesOptions, @@ -41,20 +41,22 @@ pub use crate::generated::models::{ BlockBlobClientCommitBlockListOptions, BlockBlobClientCommitBlockListResult, BlockBlobClientCommitBlockListResultHeaders, BlockBlobClientGetBlockListOptions, BlockBlobClientStageBlockOptions, BlockBlobClientStageBlockResult, - BlockBlobClientStageBlockResultHeaders, BlockBlobClientUploadOptions, - BlockBlobClientUploadResult, BlockBlobClientUploadResultHeaders, BlockList, BlockListType, - BlockLookupList, ContainerItem, CopyStatus, CorsRule, LeaseDuration, LeaseState, LeaseStatus, - ListBlobsFlatSegmentResponse, ListBlobsIncludeItem, ListContainersIncludeType, - ListContainersSegmentResponse, Logging, Metrics, ObjectReplicationMetadata, - PageBlobClientClearPagesOptions, PageBlobClientClearPagesResult, - PageBlobClientClearPagesResultHeaders, PageBlobClientCreateOptions, PageBlobClientCreateResult, - PageBlobClientCreateResultHeaders, PageBlobClientGetPageRangesOptions, - PageBlobClientResizeOptions, PageBlobClientResizeResult, PageBlobClientResizeResultHeaders, - PageBlobClientSetSequenceNumberOptions, PageBlobClientSetSequenceNumberResult, - PageBlobClientSetSequenceNumberResultHeaders, PageBlobClientUploadPagesFromUrlOptions, - PageBlobClientUploadPagesFromUrlResult, PageBlobClientUploadPagesOptions, - PageBlobClientUploadPagesResult, PageBlobClientUploadPagesResultHeaders, PageList, - PremiumPageBlobAccessTier, PublicAccessType, RehydratePriority, RetentionPolicy, - SequenceNumberActionType, StaticWebsite, + BlockBlobClientStageBlockResultHeaders, BlockBlobClientUploadBlobFromUrlOptions, + BlockBlobClientUploadBlobFromUrlResult, BlockBlobClientUploadBlobFromUrlResultHeaders, + BlockBlobClientUploadOptions, BlockBlobClientUploadResult, BlockBlobClientUploadResultHeaders, + BlockList, BlockListType, BlockLookupList, ContainerItem, CopyStatus, CorsRule, + DeleteSnapshotsOptionType, EncryptionAlgorithmType, FileShareTokenIntent, + ImmutabilityPolicyMode, LeaseDuration, LeaseState, LeaseStatus, ListBlobsFlatSegmentResponse, + ListBlobsIncludeItem, ListContainersIncludeType, ListContainersSegmentResponse, Logging, + Metrics, ObjectReplicationMetadata, PageBlobClientClearPagesOptions, + PageBlobClientClearPagesResult, PageBlobClientClearPagesResultHeaders, + PageBlobClientCreateOptions, PageBlobClientCreateResult, PageBlobClientCreateResultHeaders, + PageBlobClientGetPageRangesOptions, PageBlobClientResizeOptions, PageBlobClientResizeResult, + PageBlobClientResizeResultHeaders, PageBlobClientSetSequenceNumberOptions, + PageBlobClientSetSequenceNumberResult, PageBlobClientSetSequenceNumberResultHeaders, + PageBlobClientUploadPagesFromUrlOptions, PageBlobClientUploadPagesFromUrlResult, + PageBlobClientUploadPagesOptions, PageBlobClientUploadPagesResult, + PageBlobClientUploadPagesResultHeaders, PageList, PremiumPageBlobAccessTier, PublicAccessType, + RehydratePriority, RetentionPolicy, SequenceNumberActionType, StaticWebsite, }; 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 70116192d8..63b3df03cc 100644 --- a/sdk/storage/azure_storage_blob/tests/append_blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/append_blob_client.rs @@ -78,7 +78,7 @@ async fn test_append_block_from_url(ctx: TestContext) -> Result<(), Box Result<(), Box assert_eq!(StatusCode::NotFound, error.unwrap()); container_client.create_container(None).await?; - create_test_blob(&blob_client).await?; + create_test_blob(&blob_client, None).await?; // No Option Scenario let response = blob_client.get_properties(None).await?; @@ -59,7 +60,7 @@ async fn test_set_blob_properties(ctx: TestContext) -> Result<(), Box let recording = ctx.recording(); let container_client = get_container_client(recording, true).await?; let blob_client = container_client.blob_client(get_blob_name(recording)); - create_test_blob(&blob_client).await?; + create_test_blob(&blob_client, None).await?; // Set Content Settings let set_properties_options = BlobClientSetPropertiesOptions { @@ -157,7 +158,7 @@ async fn test_delete_blob(ctx: TestContext) -> Result<(), Box> { let recording = ctx.recording(); let container_client = get_container_client(recording, true).await?; let blob_client = container_client.blob_client(get_blob_name(recording)); - create_test_blob(&blob_client).await?; + create_test_blob(&blob_client, None).await?; // Existence Check blob_client.get_properties(None).await?; @@ -263,7 +264,7 @@ async fn test_set_access_tier(ctx: TestContext) -> Result<(), Box> { let recording = ctx.recording(); let container_client = get_container_client(recording, true).await?; let blob_client = container_client.blob_client(get_blob_name(recording)); - create_test_blob(&blob_client).await?; + create_test_blob(&blob_client, None).await?; let original_response = blob_client.get_properties(None).await?; let og_access_tier = original_response.access_tier()?; @@ -289,7 +290,7 @@ async fn test_blob_lease_operations(ctx: TestContext) -> Result<(), Box Result<(), Box Result<(), Box> { recording.set_matcher(Matcher::BodilessMatcher).await?; let container_client = get_container_client(recording, true).await?; let blob_client = container_client.blob_client(get_blob_name(recording)); - create_test_blob(&blob_client).await?; + create_test_blob(&blob_client, None).await?; // Set Tags with Tags Specified let blob_tags = HashMap::from([ 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 63391461c6..32646ac431 100644 --- a/sdk/storage/azure_storage_blob/tests/blob_container_client.rs +++ b/sdk/storage/azure_storage_blob/tests/blob_container_client.rs @@ -93,8 +93,8 @@ async fn test_list_blobs(ctx: TestContext) -> Result<(), Box> { let blob_names = ["testblob1".to_string(), "testblob2".to_string()]; container_client.create_container(None).await?; - create_test_blob(&container_client.blob_client(blob_names[0].clone())).await?; - create_test_blob(&container_client.blob_client(blob_names[1].clone())).await?; + create_test_blob(&container_client.blob_client(blob_names[0].clone()), None).await?; + create_test_blob(&container_client.blob_client(blob_names[1].clone()), None).await?; let mut list_blobs_response = container_client.list_blobs(None)?; @@ -128,10 +128,10 @@ async fn test_list_blobs_with_continuation(ctx: TestContext) -> Result<(), Box Result<(), Box> { container_client.delete_container(None).await?; Ok(()) } + +#[recorded::test] +async fn test_upload_blob_from_url(ctx: TestContext) -> Result<(), Box> { + // Recording Setup + let recording = ctx.recording(); + let container_client = get_container_client(recording, true).await?; + let source_blob_client = container_client.blob_client(get_blob_name(recording)); + create_test_blob( + &source_blob_client, + Some(RequestContent::from(b"initialD ata".to_vec())), + ) + .await?; + let source_url = format!( + "{}{}/{}", + source_blob_client.endpoint(), + source_blob_client.container_name(), + source_blob_client.blob_name() + ); + + let blob_client = container_client.blob_client(get_blob_name(recording)); + + let overwrite_blob_client = container_client.blob_client(get_blob_name(recording)); + create_test_blob( + &overwrite_blob_client, + Some(RequestContent::from(b"overruled!".to_vec())), + ) + .await?; + let overwrite_url = format!( + "{}{}/{}", + overwrite_blob_client.endpoint(), + overwrite_blob_client.container_name(), + overwrite_blob_client.blob_name() + ); + + // Regular Scenario + blob_client + .block_blob_client() + .upload_blob_from_url(source_url.clone(), None) + .await?; + + let create_options = BlockBlobClientUploadBlobFromUrlOptions::default().with_if_not_exists(); + + // No Overwrite Existing Blob Scenario + let response = blob_client + .block_blob_client() + .upload_blob_from_url(overwrite_url.clone(), Some(create_options)) + .await; + // Assert + let error = response.unwrap_err().http_status(); + assert_eq!(StatusCode::Conflict, error.unwrap()); + + // Overwrite Existing Blob Scenario + blob_client + .block_blob_client() + .upload_blob_from_url(overwrite_url.clone(), None) + .await?; + + // Public Resource Scenario + blob_client + .block_blob_client() + .upload_blob_from_url( + "https://www.gutenberg.org/cache/epub/1533/pg1533.txt".into(), + None, + ) + .await?; + + // Source Authorization Scenario + let access_token = format!( + "Bearer {}", + recording + .credential() + .get_token(&["https://storage.azure.com/.default"], None) + .await? + .token + .secret() + ); + + let source_auth_options = BlockBlobClientUploadBlobFromUrlOptions { + copy_source_authorization: Some(access_token), + ..Default::default() + }; + + blob_client + .block_blob_client() + .upload_blob_from_url(overwrite_url.clone(), Some(source_auth_options)) + .await?; + + container_client.delete_container(None).await?; + Ok(()) +} diff --git a/sdk/storage/azure_storage_blob/tests/page_blob_client.rs b/sdk/storage/azure_storage_blob/tests/page_blob_client.rs index e0d230ad7b..bbc98e944d 100644 --- a/sdk/storage/azure_storage_blob/tests/page_blob_client.rs +++ b/sdk/storage/azure_storage_blob/tests/page_blob_client.rs @@ -7,9 +7,8 @@ use azure_storage_blob::{ format_page_range, models::{ BlobClientDownloadResultHeaders, BlobClientGetPropertiesResultHeaders, BlobType, - PageBlobClientCreateOptions, PageBlobClientCreateOptionsExt, - PageBlobClientSetSequenceNumberOptions, PageBlobClientSetSequenceNumberResultHeaders, - SequenceNumberActionType, + PageBlobClientCreateOptions, PageBlobClientSetSequenceNumberOptions, + PageBlobClientSetSequenceNumberResultHeaders, SequenceNumberActionType, }, }; use azure_storage_blob_test::{get_blob_name, get_container_client}; diff --git a/sdk/storage/azure_storage_blob/tsp-location.yaml b/sdk/storage/azure_storage_blob/tsp-location.yaml index 55366e1d1b..5fa03759d8 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: fcc4cdefee43823ab15e05be6b42228bed96c1b1 +commit: c05b1ba8ec1b4472da48c7000784519f18a40f66 repo: Azure/azure-rest-api-specs additionalDirectories: diff --git a/sdk/storage/azure_storage_blob_test/src/lib.rs b/sdk/storage/azure_storage_blob_test/src/lib.rs index cb6b164896..be74aee8e4 100644 --- a/sdk/storage/azure_storage_blob_test/src/lib.rs +++ b/sdk/storage/azure_storage_blob_test/src/lib.rs @@ -3,7 +3,7 @@ use azure_core::{ http::{ClientOptions, NoFormat, RequestContent, Response}, - Result, + Bytes, Result, }; use azure_core_test::Recording; use azure_storage_blob::{ @@ -95,20 +95,31 @@ pub async fn get_container_client( Ok(container_client) } -/// Creates a test blob with no options, containing the data "b'hello rusty world'" with content length 17. +/// Creates a test blob with no options, containing the data "b'hello rusty world'" with content length 17 if no data specified. /// /// # Arguments /// /// * `blob_client` - A reference to a BlobClient instance. +/// * `data` - Blob content to be uploaded. pub async fn create_test_blob( blob_client: &BlobClient, + data: Option>, ) -> Result> { - blob_client - .upload( - RequestContent::from(b"hello rusty world".to_vec()), - true, - 17, - None, - ) - .await + match data { + Some(content) => { + blob_client + .upload(content.clone(), true, content.body().len() as u64, None) + .await + } + None => { + blob_client + .upload( + RequestContent::from(b"hello rusty world".to_vec()), + true, + 17, + None, + ) + .await + } + } }