diff --git a/Cargo.lock b/Cargo.lock index e251495..f7ff589 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -277,6 +277,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "err" +version = "0.1.1" +source = "git+https://git.franklinblanco.dev/franklinblanco/err.git#18cc77b6266d0fc90237a7ccb297d3eeb574f78a" +dependencies = [ + "serde", + "sqlx", + "stdext", + "thiserror", +] + [[package]] name = "errno" version = "0.3.1" @@ -1374,6 +1385,12 @@ dependencies = [ "url", ] +[[package]] +name = "stdext" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3b6b32ae82412fb897ef134867d53a294f57ba5b758f06d71e865352c3e207" + [[package]] name = "stringprep" version = "0.1.3" @@ -1608,12 +1625,12 @@ version = "0.1.0" dependencies = [ "chrono", "data-encoding", + "err", "futures-util", "log", "ring", "serde", "sqlx", - "thiserror", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 00deee6..d157bd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,5 @@ ring = "0.16.20" data-encoding = "2.3.2" futures-util = "0.3" log = "0.4.19" -thiserror = "1.0.48" \ No newline at end of file + +err = { git = "https://git.franklinblanco.dev/franklinblanco/err.git" } \ No newline at end of file diff --git a/src/domain/error.rs b/src/domain/error.rs deleted file mode 100644 index 982945d..0000000 --- a/src/domain/error.rs +++ /dev/null @@ -1,39 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum Error { - /// Used to return a simple error from FromStr implementations. - #[error("Error parsing string into value")] - FromStrError, - /// Every error that is returned from a DAO operation. - #[error("Error from the Database: {0}")] - DatabaseError(#[from] sqlx::Error), - /// A vec of ValidationErrors - #[error("Validation Errors: {0:?}")] - ValidationErrors(Vec), - /// Something already exists. That something should be {0} - /// Example: "User" "Credential" - #[error("Error {0} Already exists.")] - AlreadyExistsError(String), - /// Example: "User with id X" - #[error("{0} Not found.")] - NotFoundError(String), - /// Used to specify authentication error. - /// Example: Password incorrect for user - #[error("Credential supplied is incorrect. {0}")] - IncorrectCredentialError(String), - #[error("Too many credentials supplied, maximum is 3.")] - TooManyCredentialsError, - /// Used for anything else. - #[error("Unexpected Error: {0}")] - UnexpectedError(String), -} - -/// Any string validation error such as Phone number validation or email, etc... -/// Reason should be a Key for internationalization -#[derive(Error, Debug)] -#[error("Error validating `{what}`. Reason: {reason}")] -pub struct ValidationError { - pub what: String, - pub reason: String, -} diff --git a/src/domain/impls/credential.rs b/src/domain/impls/credential.rs index a515bf4..2015395 100644 --- a/src/domain/impls/credential.rs +++ b/src/domain/impls/credential.rs @@ -1,5 +1,6 @@ use std::{fmt::Display, str::FromStr}; +use err::{Error, trace}; use sqlx::{ encode::IsNull, error::BoxDynError, @@ -8,8 +9,6 @@ use sqlx::{ }; use crate::domain::credential::CredentialType; -use crate::domain::error::Error; -use crate::domain::error::Error::FromStrError; impl FromStr for CredentialType { type Err = Error; @@ -19,7 +18,7 @@ impl FromStr for CredentialType { "PhoneNumber" => Ok(Self::PhoneNumber), "Email" => Ok(Self::Email), "Username" => Ok(Self::Username), - _ => Err(FromStrError), + _ => Err(Error::new(trace!()).error_type(err::ErrorType::Parser).message(s)), } } } diff --git a/src/domain/mod.rs b/src/domain/mod.rs index 308aaa6..0c56eb1 100644 --- a/src/domain/mod.rs +++ b/src/domain/mod.rs @@ -1,5 +1,4 @@ pub mod credential; -pub mod error; pub mod impls; pub mod token; pub mod user; diff --git a/src/service/user.rs b/src/service/user.rs index eb28431..c6fdc7c 100644 --- a/src/service/user.rs +++ b/src/service/user.rs @@ -7,17 +7,13 @@ use crate::domain::user::User; use crate::dto::token::{AuthenticateUserDto, RefreshAuthTokenForUserDto}; use crate::dto::users::{UserLoginPayload, UserRegisterPayload, UserResetPasswordPayload}; -use crate::domain::error::Error::{ - AlreadyExistsError, IncorrectCredentialError, NotFoundError, TooManyCredentialsError, - UnexpectedError, -}; -use crate::domain::error::{Error, ValidationError}; use crate::resources::expirations::AUTH_TOKEN_EXPIRATION_TIME_MILLIS; use crate::utils::hasher::{ generate_multiple_random_token_with_rng, hash_password, hash_password_with_existing_salt, }; use crate::validation::user_validator::validate_user_for_creation; use chrono::Utc; +use err::{trace, Error, ErrorType, ServiceError, ValidationError, x_u_res_db_or_res, u_res_or_res}; use log::error; use sqlx::{PgConnection, Postgres, Transaction}; @@ -30,13 +26,15 @@ pub async fn register_user<'a>( validate_user_for_creation(&user, &mut validation_errors); // Find if user exists if user.credentials.len() > 3 { - return Err(TooManyCredentialsError); + return Err(Error::new(trace!())); } for credential_dto in user.credentials.iter() { - match get_credential(transaction, credential_dto.credential.clone()).await? { + match x_u_res_db_or_res!(get_credential(transaction, credential_dto.credential.clone()).await) { None => {} Some(_) => { - return Err(AlreadyExistsError(String::from("Credential"))); + return Err(Error::new(trace!()).error_type(ErrorType::Service( + ServiceError::AlreadyExistsError(String::from("Credential")), + ))); } }; } @@ -52,11 +50,11 @@ pub async fn register_user<'a>( last_updated: now, }; - let persisted_user = insert_user(transaction, user_to_insert).await?; + let persisted_user = x_u_res_db_or_res!(insert_user(transaction, user_to_insert).await); // Insert Credentials for credential in user.credentials { - insert_credential(transaction, credential, &persisted_user.id).await?; + x_u_res_db_or_res!(insert_credential(transaction, credential, &persisted_user.id).await); } create_token_for_user(transaction, persisted_user.id).await @@ -65,23 +63,23 @@ pub async fn authenticate_user<'a>( conn: &mut PgConnection, user: AuthenticateUserDto, ) -> Result { - let persisted_user = match get_user_with_id(conn, &user.id).await? { + let persisted_user = match x_u_res_db_or_res!(get_user_with_id(conn, &user.id).await) { None => { - return Err(NotFoundError(format!("User with id: {}", user.id))); + return Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::NotFoundError(format!("User with id: {}", user.id))))); } Some(persisted_user) => persisted_user, }; - match validate_user_token(conn, &user.id, user.auth_token.clone()).await? { - None => Err(NotFoundError(format!("Auth Token {}", user.auth_token))), + match x_u_res_db_or_res!(validate_user_token(conn, &user.id, user.auth_token.clone()).await) { + None => return Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::NotFoundError(format!("Auth Token {}", user.auth_token))))), Some(persisted_token) => { // Check if persisted_token expired if Utc::now().timestamp_millis() - persisted_token.last_updated.timestamp_millis() > AUTH_TOKEN_EXPIRATION_TIME_MILLIS { - Err(IncorrectCredentialError(String::from( + Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::IncorrectCredentialError(String::from( "Auth Token Expired. Use refresh token to get a new one.", - ))) + ))))) } else { // Not expired Ok(persisted_user) @@ -95,9 +93,9 @@ pub async fn refresh_auth_token<'a>( conn: &mut PgConnection, user: RefreshAuthTokenForUserDto, ) -> Result { - let _persisted_user = match get_user_with_id(conn, &user.id).await? { + let _persisted_user = match x_u_res_db_or_res!(get_user_with_id(conn, &user.id).await) { None => { - return Err(NotFoundError(format!("User with id: {}", user.id))); + return Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::NotFoundError(format!("User with id: {}", user.id))))); } Some(persisted_user) => persisted_user, }; @@ -106,9 +104,9 @@ pub async fn refresh_auth_token<'a>( if tokens.len() > 0 { let new_auth_token = tokens.remove(0); - Ok(update_token(conn, user.refresh_token, new_auth_token).await?) + Ok(x_u_res_db_or_res!(update_token(conn, user.refresh_token, new_auth_token).await)) } else { - Err(UnexpectedError(String::from("No tokens were created."))) + Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::UnexpectedError(String::from("No tokens were created."))))) } } @@ -117,15 +115,15 @@ pub async fn reset_password( conn: &mut PgConnection, user: UserResetPasswordPayload, ) -> Result { - let password_matches = validate_user_password(conn, &user.id, user.password).await?; + let password_matches = u_res_or_res!(validate_user_password(conn, &user.id, user.password).await); if let Some(persisted_user) = password_matches { // Change pass - Ok(change_password(conn, persisted_user, &user.new_password).await?) + Ok(u_res_or_res!(change_password(conn, persisted_user, &user.new_password).await)) } else { - Err(IncorrectCredentialError(String::from( + Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::IncorrectCredentialError(String::from( "Password incorrect.", - ))) + ))))) } } @@ -136,14 +134,14 @@ pub async fn force_reset_password<'a>( user_id: &i32, new_password: String, ) -> Result { - let persisted_user = match get_user_with_id(conn, user_id).await? { + let persisted_user = match x_u_res_db_or_res!(get_user_with_id(conn, user_id).await) { None => { error!("Serious error. User doesn't exist but credentials pointing to the user do."); - return Err(Error::NotFoundError(format!("User with id: {user_id}"))); + return Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::NotFoundError(format!("User with id: {user_id}"))))); } Some(persisted_user) => persisted_user, }; - Ok(change_password(conn, persisted_user, &new_password).await?) + Ok(u_res_or_res!(change_password(conn, persisted_user, &new_password).await)) } /// @@ -151,19 +149,19 @@ pub async fn password_login<'a>( conn: &mut Transaction<'a, Postgres>, user: UserLoginPayload, ) -> Result { - let persisted_user_credential = match get_credential(conn, user.credential.clone()).await? { - None => return Err(NotFoundError(format!("Credential {}", user.credential))), + let persisted_user_credential = match x_u_res_db_or_res!(get_credential(conn, user.credential.clone()).await) { + None => return Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::NotFoundError(format!("Credential {}", user.credential))))), Some(persisted_credential) => persisted_credential, }; let persisted_user_opt = - validate_user_password(conn, &persisted_user_credential.user_id, user.password).await?; + u_res_or_res!(validate_user_password(conn, &persisted_user_credential.user_id, user.password).await); if let Some(_) = persisted_user_opt { - Ok(create_token_for_user(conn, persisted_user_credential.user_id).await?) + Ok(u_res_or_res!(create_token_for_user(conn, persisted_user_credential.user_id).await)) } else { - Err(NotFoundError(format!( + Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::NotFoundError(format!( "User with id: {}", persisted_user_credential.user_id - ))) + ))))) } } @@ -172,8 +170,8 @@ pub async fn get_user_credentials<'a>( transaction: &mut Transaction<'a, Postgres>, user: AuthenticateUserDto, ) -> Result, Error> { - let persisted_user = authenticate_user(transaction, user).await?; - Ok(fetch_user_credentials(transaction, &persisted_user.id).await?) + let persisted_user = u_res_or_res!(authenticate_user(transaction, user).await); + Ok(x_u_res_db_or_res!(fetch_user_credentials(transaction, &persisted_user.id).await)) } async fn create_token_for_user<'a>( @@ -188,18 +186,18 @@ async fn create_token_for_user<'a>( auth_token: match tokens.get(0) { None => { error!("Tokens were not created."); - return Err(Error::UnexpectedError(String::from( + return Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::UnexpectedError(String::from( "Tokens were not created.", - ))); + ))))); } Some(token) => token.clone(), }, refresh_token: match tokens.get(1) { None => { error!("Tokens were not created."); - return Err(Error::UnexpectedError(String::from( + return Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::UnexpectedError(String::from( "Tokens were not created.", - ))); + ))))); } Some(token) => token.clone(), }, @@ -208,7 +206,7 @@ async fn create_token_for_user<'a>( }; // Insert token in DB - Ok(insert_token(transaction, token_to_insert).await?) + Ok(x_u_res_db_or_res!(insert_token(transaction, token_to_insert).await)) } async fn validate_user_password<'a>( @@ -216,10 +214,10 @@ async fn validate_user_password<'a>( user_id: &i32, password: String, ) -> Result, Error> { - let persisted_user = match get_user_with_id(conn, user_id).await? { + let persisted_user = match x_u_res_db_or_res!(get_user_with_id(conn, user_id).await) { None => { error!("Serious error. User doesn't exist but credentials pointing to the user do."); - return Err(Error::NotFoundError(format!("User with id: {user_id}"))); + return Err(Error::new(trace!()).error_type(ErrorType::Service(ServiceError::NotFoundError(format!("User with id: {user_id}"))))); } Some(persisted_user) => persisted_user, }; @@ -240,5 +238,5 @@ async fn change_password<'a>( let hash_result = hash_password(&new_password); persisted_user.password = hash_result.hash; persisted_user.salt = hash_result.salt; - Ok(update_user(conn, persisted_user).await?) + Ok(x_u_res_db_or_res!(update_user(conn, persisted_user).await)) } diff --git a/src/validation/user_validator.rs b/src/validation/user_validator.rs index 781a1d4..ae934ea 100644 --- a/src/validation/user_validator.rs +++ b/src/validation/user_validator.rs @@ -1,5 +1,6 @@ +use err::ValidationError; + use crate::domain::credential::CredentialType; -use crate::domain::error::ValidationError; use crate::dto::users::{UserLoginPayload, UserRegisterPayload}; use crate::resources::error_messages::ERROR_INVALID_USERNAME; use crate::resources::{