Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error handling with thiserror #494

Merged
merged 11 commits into from
Jun 28, 2024
1 change: 1 addition & 0 deletions backend/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "time"

# Important secondary crates
anyhow = "1.0"
thiserror = "1.0"
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.11", features = ["json"] }
serde_json = "1.0"
Expand Down
3 changes: 2 additions & 1 deletion backend/server/src/models/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ where

let claims =
decode_auth_token(token, decoding_key, jwt_validator).ok_or(ChaosError::NotLoggedIn)?;

let pool = &app_state.db;
let possible_user = is_super_user(claims.sub, pool).await;

Expand All @@ -102,6 +103,6 @@ where
}
}

Err(ChaosError::NotLoggedIn)
Err(ChaosError::Unauthorized)
}
}
35 changes: 23 additions & 12 deletions backend/server/src/models/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,42 @@ use axum::response::{IntoResponse, Redirect, Response};

/// Custom error enum for Chaos.
///
/// Handles all anyhow errors (when `?` is used) alongside
/// Handles all errors thrown by libraries (when `?` is used) alongside
/// specific errors for business logic.
#[derive(thiserror::Error, Debug)]
pub enum ChaosError {
#[error("Not logged in")]
NotLoggedIn,

#[error("Not authorized")]
Unauthorized,

#[error("Forbidden operation")]
ForbiddenOperation,
ServerError(anyhow::Error),

#[error("SQLx error")]
DatabaseError(#[from] sqlx::Error),

#[error("Reqwest error")]
ReqwestError(#[from] reqwest::Error),

#[error("OAuth2 error")]
OAuthError(#[from] oauth2::RequestTokenError<oauth2::reqwest::Error<reqwest::Error>, oauth2::StandardErrorResponse<oauth2::basic::BasicErrorResponseType>>)
}

/// Implementation for converting errors into responses. Manages error code and message returned.
impl IntoResponse for ChaosError {
fn into_response(self) -> Response {
match self {
ChaosError::NotLoggedIn => Redirect::temporary("/auth/google").into_response(),
ChaosError::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized").into_response(),
ChaosError::ForbiddenOperation => (StatusCode::FORBIDDEN, "Forbidden operation").into_response(),
ChaosError::ServerError(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response()
ChaosError::DatabaseError(db_error) => match db_error {
// We only care about the RowNotFound error, as others are miscellaneous DB errors.
sqlx::Error::RowNotFound => (StatusCode::NOT_FOUND, "Not found").into_response(),
_ => (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response(),
},
_ => (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
}
}
}

impl<E> From<E> for ChaosError
where
E: Into<anyhow::Error>,
{
fn from(err: E) -> Self {
ChaosError::ServerError(err.into())
}
}
Loading