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}