error_util/error/
mod.rs

1use actix_web::http::StatusCode;
2use actix_web::{error, Error, HttpResponse};
3use alcoholic_jwt::ValidationError;
4use std::env::VarError;
5use std::fmt::Formatter;
6
7use bson::ser::Error as BsonError;
8use mongodb::error::Error as MongoError;
9
10use reqwest::Error as ReqwestError;
11use tonic::transport::Error as GrpcConnectError;
12
13use serde::{Deserialize, Serialize};
14use tonic::{Code, Status as GrpcStatusError, Status};
15
16use async_graphql::Error as GraphQLError;
17
18#[derive(Debug)]
19pub enum AppError {
20    Unauthorized,
21    ReqwestAPIError(ReqwestError),
22    JwksError(ValidationError),
23    Mongo(MongoError),
24    ConfigError(VarError),
25    ClientError(HttpError),
26    ServerError(HttpError),
27    BsonError(BsonError),
28    AppError(HttpError),
29    StandardError(String),
30    GrpcConnectionError(GrpcConnectError),
31    GrpcStatusError(GrpcStatusError),
32    GraphQLError(GraphQLError),
33}
34
35impl AppError {
36    fn convert_grpc_error_to_status(&self, err: &GrpcStatusError) -> StatusCode {
37        match err.code() {
38            Code::Ok => StatusCode::OK,
39            Code::Cancelled => StatusCode::FAILED_DEPENDENCY,
40            Code::Unknown => StatusCode::UNPROCESSABLE_ENTITY,
41            Code::InvalidArgument => StatusCode::BAD_REQUEST,
42            Code::DeadlineExceeded => StatusCode::REQUEST_TIMEOUT,
43            Code::NotFound => StatusCode::NOT_FOUND,
44            Code::AlreadyExists => StatusCode::CONFLICT,
45            Code::PermissionDenied => StatusCode::FORBIDDEN,
46            Code::ResourceExhausted => StatusCode::EXPECTATION_FAILED,
47            Code::FailedPrecondition => StatusCode::EXPECTATION_FAILED,
48            Code::Aborted => StatusCode::UNPROCESSABLE_ENTITY,
49            Code::OutOfRange => StatusCode::RANGE_NOT_SATISFIABLE,
50            Code::Unimplemented => StatusCode::NOT_FOUND,
51            Code::Internal => StatusCode::INTERNAL_SERVER_ERROR,
52            Code::Unavailable => StatusCode::SERVICE_UNAVAILABLE,
53            Code::DataLoss => StatusCode::INSUFFICIENT_STORAGE,
54            Code::Unauthenticated => StatusCode::UNAUTHORIZED,
55        }
56    }
57
58    pub fn convert_app_error_to_graphql_error(&self) -> GraphQLError {
59        GraphQLError {
60            message: self.to_string(),
61            source: None,
62            extensions: None,
63        }
64    }
65
66    pub fn convert_status_error_to_graphql_error(status: Status) -> GraphQLError {
67        GraphQLError {
68            message: format!("{}", status.to_string()),
69            source: None,
70            extensions: None,
71        }
72    }
73
74    pub fn convert_app_error_to_grpc_status(&self) -> Status {
75        match self {
76            AppError::Unauthorized => Status::unauthenticated("not authorized".to_string()),
77            AppError::ReqwestAPIError(error) => Status::internal(error.to_string()),
78            AppError::JwksError(error) => Status::permission_denied(error.to_string()),
79            AppError::ConfigError(error) => Status::internal(error.to_string()),
80            AppError::ClientError(error) => Status::internal(error.to_string()),
81            AppError::ServerError(error) => Status::internal(error.to_string()),
82            AppError::BsonError(error) => Status::failed_precondition(error.to_string()),
83            AppError::AppError(error) => Status::failed_precondition(error.to_string()),
84            AppError::StandardError(error) => Status::invalid_argument(error.to_string()),
85            AppError::GrpcConnectionError(error) => Status::unavailable(error.to_string()),
86            AppError::GrpcStatusError(error) => Status::new(error.code(), error.message()),
87            AppError::GraphQLError(error) => Status::internal(error.clone().message),
88            AppError::Mongo(error) => {
89                let mut error_msg = error.to_string();
90                let ex: Option<&String> = error.get_custom();
91
92                if let Some(ex_str) = ex {
93                    error_msg = ex_str.clone()
94                }
95                Status::failed_precondition(error_msg)
96            },
97        }
98    }
99}
100
101impl std::fmt::Display for AppError {
102    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103        match self {
104            AppError::Unauthorized => write!(f, "unauthorized"),
105            AppError::ReqwestAPIError(err) => write!(f, "external api error: {}", err),
106            AppError::JwksError(err) => write!(f, "validation error: {}", err),
107            AppError::ConfigError(err) => write!(f, "configuration error: {}", err),
108            AppError::ClientError(err) => write!(f, "External Client error: {}", err),
109            AppError::ServerError(err) => write!(f, "External Server error: {}", err),
110            AppError::BsonError(err) => write!(f, "Bson error: {}", err),
111            AppError::AppError(err) => write!(f, "Document not found : {}", err),
112            AppError::StandardError(msg) => write!(f, "see message: {}", msg),
113            AppError::GrpcConnectionError(error) => {
114                write!(f, "grpc client connect error: {}", error)
115            }
116            AppError::GrpcStatusError(error) => {
117                write!(f, "grpc client connect error: {}", error)
118            }
119            AppError::GraphQLError(error) => {
120                write!(f, "graphQL error: {}", error.clone().message)
121            }
122
123            AppError::Mongo(error) => {
124                let mut error_msg = error.to_string();
125                let ex: Option<&String> = error.get_custom();
126
127                if let Some(ex_str) = ex {
128                    error_msg = ex_str.clone()
129                }
130                write!(f, "mongo error: {}", error_msg)
131            }
132        }
133    }
134}
135
136impl error::ResponseError for AppError {
137    fn status_code(&self) -> StatusCode {
138        match self {
139            AppError::Unauthorized => StatusCode::UNAUTHORIZED,
140            AppError::ReqwestAPIError(_) => StatusCode::INTERNAL_SERVER_ERROR,
141            AppError::JwksError(_) => StatusCode::UNAUTHORIZED,
142            AppError::ConfigError(_) => StatusCode::INTERNAL_SERVER_ERROR,
143            AppError::ClientError(_) => StatusCode::BAD_REQUEST,
144            AppError::ServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
145            AppError::BsonError(_) => StatusCode::UNPROCESSABLE_ENTITY,
146            AppError::AppError(_) => StatusCode::INTERNAL_SERVER_ERROR,
147            AppError::StandardError(_) => StatusCode::INTERNAL_SERVER_ERROR,
148            AppError::GrpcConnectionError(_) => StatusCode::SERVICE_UNAVAILABLE,
149            AppError::GrpcStatusError(status) => self.convert_grpc_error_to_status(status),
150            AppError::GraphQLError(_error) => StatusCode::INTERNAL_SERVER_ERROR,
151            AppError::Mongo(_error) => StatusCode::INTERNAL_SERVER_ERROR
152        }
153    }
154
155    fn error_response(&self) -> HttpResponse {
156        HttpResponse::build(self.status_code()).json(AppErrorResponse {
157            message: self.error_response(),
158        })
159    }
160}
161
162impl AppError {
163    fn error_response(&self) -> String {
164        match self {
165            AppError::Unauthorized => "unauthorized".into(),
166            AppError::ReqwestAPIError(err) => err.to_string(),
167            AppError::JwksError(err) => err.to_string(),
168            AppError::ConfigError(err) => err.to_string(),
169            AppError::ClientError(err) => err.clone().message,
170            AppError::ServerError(err) => err.clone().message,
171            AppError::BsonError(err) => err.to_string(),
172            AppError::AppError(err) => err.clone().message,
173            AppError::StandardError(msg) => msg.into(),
174            AppError::GrpcConnectionError(error) => error.to_string(),
175            AppError::GrpcStatusError(error) => error.to_string(),
176            AppError::GraphQLError(error) => error.clone().message,
177            AppError::Mongo(error) => {
178                let mut error_msg = error.to_string();
179                let ex: Option<&String> = error.get_custom();
180
181                if let Some(ex_str) = ex {
182                    error_msg = ex_str.clone()
183                }
184
185                error_msg.to_string()
186            }
187        }
188    }
189}
190
191impl From<Error> for AppError {
192    fn from(err: Error) -> Self {
193        AppError::StandardError(err.to_string())
194    }
195}
196
197impl From<ValidationError> for AppError {
198    fn from(err: ValidationError) -> Self {
199        AppError::JwksError(err)
200    }
201}
202
203#[derive(Debug, Serialize)]
204pub struct AppErrorResponse {
205    message: String,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct HttpError {
210    pub status: u16,
211    pub message: String,
212}
213
214impl std::fmt::Display for HttpError {
215    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
216        write!(f, "api error: {}", self.message)
217    }
218}