Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 60 additions & 2 deletions sdk/storage/azure_storage_blob/src/models/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

use crate::models::{
AppendBlobClientCreateOptions, BlobTag, BlobTags, BlockBlobClientUploadBlobFromUrlOptions,
BlockBlobClientUploadOptions, PageBlobClientCreateOptions,
BlockBlobClientUploadOptions, PageBlobClientCreateOptions, StorageError, StorageErrorCode,
};
use azure_core::error::ErrorKind;
use azure_core::{error::ErrorKind, http::headers::Headers};
use std::collections::HashMap;

/// Augments the current options bag to only create if the Page blob does not already exist.
Expand Down Expand Up @@ -109,3 +109,61 @@ impl From<HashMap<String, String>> for BlobTags {
}
}
}

impl TryFrom<azure_core::Error> for StorageError {
type Error = azure_core::Error;

fn try_from(error: azure_core::Error) -> Result<Self, Self::Error> {
let message = error.to_string();

match error.kind() {
ErrorKind::HttpResponse {
status,
error_code,
raw_response,
} => {
let error_code = error_code.as_ref().ok_or_else(|| {
azure_core::Error::with_message(
azure_core::error::ErrorKind::DataConversion,
"error_code field missing from HttpResponse.",
)
})?;

let headers = raw_response
.as_ref()
.map(|raw_resp| raw_resp.headers().clone())
.unwrap_or_default();

let error_code_enum = error_code
.parse()
.unwrap_or(StorageErrorCode::UnknownValue(error_code.clone()));

Ok(StorageError {
status_code: *status,
error_code: error_code_enum,
message,
headers,
})
}
_ => Err(azure_core::Error::with_message(
azure_core::error::ErrorKind::DataConversion,
"ErrorKind was not HttpResponse and could not be parsed.",
)),
}
}
}

impl std::fmt::Display for StorageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Standard Error Message: {}\n", self.message)?;
writeln!(f, "Http Status Code: {}", self.status_code)?;
writeln!(f, "Storage Error Code: {}", self.error_code)?;
writeln!(f, "Response Headers:")?;

for (name, value) in self.headers.iter() {
writeln!(f, " \"{}\": \"{}\"", name.as_str(), value.as_str())?;
}

Ok(())
}
}
36 changes: 36 additions & 0 deletions sdk/storage/azure_storage_blob/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,39 @@ pub use crate::generated::models::{
VecSignedIdentifierHeaders,
};
pub use extensions::*;

use azure_core::error::ErrorKind;
use azure_core::http::{headers::Headers, StatusCode};
use azure_core::Error;

/// A Storage-specific error that provides access to HTTP response details.
///
#[derive(Debug, Clone)]
pub struct StorageError {
/// The HTTP status code.
pub status_code: StatusCode,
/// The Storage error code.
pub error_code: StorageErrorCode,
/// The error message.
pub message: String,
/// The headers from the response.
pub headers: Headers,
}

impl StorageError {
pub fn status_code(&self) -> StatusCode {
self.status_code
}

pub fn error_code(&self) -> StorageErrorCode {
self.error_code.clone()
}

pub fn message(&self) -> &str {
&self.message
}

pub fn headers(&self) -> &Headers {
&self.headers
}
}
22 changes: 21 additions & 1 deletion sdk/storage/azure_storage_blob/tests/blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

use azure_core::{
error::ErrorKind,
http::{RequestContent, StatusCode},
Bytes,
};
Expand All @@ -12,7 +13,7 @@ use azure_storage_blob::models::{
BlobClientGetAccountInfoResultHeaders, BlobClientGetPropertiesOptions,
BlobClientGetPropertiesResultHeaders, BlobClientSetMetadataOptions,
BlobClientSetPropertiesOptions, BlobClientSetTierOptions, BlockBlobClientUploadOptions,
LeaseState,
LeaseState, StorageError,
};

use azure_storage_blob_test::{create_test_blob, get_blob_name, get_container_client};
Expand Down Expand Up @@ -480,3 +481,22 @@ async fn test_get_account_info(ctx: TestContext) -> Result<(), Box<dyn Error>> {

Ok(())
}

#[recorded::test]
async fn test_storage_error_model(ctx: TestContext) -> Result<(), Box<dyn Error>> {
// 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));

// Act
let response = blob_client.download(None).await;
let error_response = response.unwrap_err();
let error_kind = error_response.kind();
assert!(matches!(error_kind, ErrorKind::HttpResponse { .. }));

let storage_error: StorageError = error_response.try_into()?;
println!("{}", storage_error);

Ok(())
}
Loading