objectstore_server/endpoints/
common.rs1use std::error::Error;
4
5use axum::Json;
6use axum::http::StatusCode;
7use axum::response::{IntoResponse, Response};
8use http::HeaderValue;
9use objectstore_service::error::Error as ServiceError;
10use serde::{Deserialize, Serialize};
11use thiserror::Error;
12
13use crate::auth::AuthError;
14use crate::extractors::batch::BatchError;
15
16#[derive(Debug, Error)]
18pub enum ApiError {
19 #[error("client error: {0}")]
21 Client(String),
22
23 #[error("auth error: {0}")]
25 Auth(#[from] AuthError),
26
27 #[error("service error: {0}")]
29 Service(#[from] ServiceError),
30
31 #[error("batch error: {0}")]
33 Batch(#[from] BatchError),
34
35 #[error("internal error: {0}")]
37 Internal(String),
38}
39
40pub type ApiResult<T> = Result<T, ApiError>;
42
43#[derive(Serialize, Deserialize, Debug)]
45pub struct ApiErrorResponse {
46 #[serde(default)]
48 detail: Option<String>,
49 #[serde(default, skip_serializing_if = "Vec::is_empty")]
51 causes: Vec<String>,
52}
53
54impl ApiErrorResponse {
55 pub fn from_error<E: Error + ?Sized>(error: &E) -> Self {
57 let detail = Some(error.to_string());
58
59 let mut causes = Vec::new();
60 let mut source = error.source();
61 while let Some(s) = source {
62 causes.push(s.to_string());
63 source = s.source();
64 }
65
66 Self { detail, causes }
67 }
68}
69
70impl ApiError {
71 pub fn status(&self) -> StatusCode {
73 match &self {
74 ApiError::Client(_) => StatusCode::BAD_REQUEST,
75
76 ApiError::Batch(BatchError::BadRequest(_))
77 | ApiError::Batch(BatchError::Metadata(_))
78 | ApiError::Batch(BatchError::Multipart(_)) => StatusCode::BAD_REQUEST,
79 ApiError::Batch(BatchError::LimitExceeded(_)) => StatusCode::PAYLOAD_TOO_LARGE,
80 ApiError::Batch(BatchError::RateLimited) => StatusCode::TOO_MANY_REQUESTS,
81 ApiError::Batch(BatchError::ResponseSerialization { .. }) => {
82 objectstore_log::error!(!!self, "error serializing batch response");
83 StatusCode::INTERNAL_SERVER_ERROR
84 }
85
86 ApiError::Auth(AuthError::BadRequest(_)) => StatusCode::BAD_REQUEST,
87 ApiError::Auth(AuthError::ValidationFailure(_))
88 | ApiError::Auth(AuthError::VerificationFailure) => StatusCode::UNAUTHORIZED,
89 ApiError::Auth(AuthError::NotPermitted) => StatusCode::FORBIDDEN,
90 ApiError::Auth(AuthError::InternalError(_)) => {
91 objectstore_log::error!(!!self, "auth system error");
92 StatusCode::INTERNAL_SERVER_ERROR
93 }
94
95 ApiError::Service(ServiceError::Client(_)) => StatusCode::BAD_REQUEST,
96 ApiError::Service(ServiceError::Metadata(_)) => StatusCode::BAD_REQUEST,
97 ApiError::Service(ServiceError::RangeNotSatisfiable { .. }) => {
98 StatusCode::RANGE_NOT_SATISFIABLE
99 }
100 ApiError::Service(ServiceError::InvalidUploadId(_)) => StatusCode::BAD_REQUEST,
101 ApiError::Service(ServiceError::AtCapacity) => StatusCode::TOO_MANY_REQUESTS,
102 ApiError::Service(ServiceError::NotImplemented) => StatusCode::NOT_IMPLEMENTED,
103 ApiError::Service(_) => {
104 objectstore_log::error!(!!self, "error handling request");
105 StatusCode::INTERNAL_SERVER_ERROR
106 }
107
108 ApiError::Internal(_) => {
109 objectstore_log::error!(!!self, "internal error");
110 StatusCode::INTERNAL_SERVER_ERROR
111 }
112 }
113 }
114}
115
116impl IntoResponse for ApiError {
117 fn into_response(self) -> Response {
118 let body = ApiErrorResponse::from_error(&self);
119 (self.status(), Json(body)).into_response()
120 }
121}
122
123pub fn insert_accept_ranges(response: &mut Response) {
125 response.headers_mut().insert(
126 http::header::ACCEPT_RANGES,
127 HeaderValue::from_static("bytes"),
128 );
129}