1
0
Fork 0
forked from 2sys/shoutdotdev

remove common suffix from AppError enum members

This commit is contained in:
Brent Schroeter 2025-04-11 23:06:27 -07:00
parent 1f08b5a590
commit 2c15cdfd11
7 changed files with 31 additions and 36 deletions

View file

@ -8,18 +8,16 @@ use validator::ValidationErrors;
#[derive(Debug)]
pub enum AppError {
InternalServerError(anyhow::Error),
ForbiddenError(String),
NotFoundError(String),
BadRequestError(String),
TooManyRequestsError(String),
Forbidden(String),
NotFound(String),
BadRequest(String),
TooManyRequests(String),
}
impl AppError {
pub fn from_validation_errors(errs: ValidationErrors) -> Self {
// TODO: customize validation errors formatting
Self::BadRequestError(
serde_json::to_string(&errs).unwrap_or("validation error".to_string()),
)
Self::BadRequest(serde_json::to_string(&errs).unwrap_or("validation error".to_string()))
}
}
@ -30,21 +28,21 @@ impl IntoResponse for AppError {
tracing::error!("Application error: {:?}", err);
(StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong").into_response()
}
Self::ForbiddenError(client_message) => {
Self::Forbidden(client_message) => {
tracing::info!("Forbidden: {}", client_message);
(StatusCode::FORBIDDEN, client_message).into_response()
}
Self::NotFoundError(client_message) => {
Self::NotFound(client_message) => {
tracing::info!("Not found: {}", client_message);
(StatusCode::NOT_FOUND, client_message).into_response()
}
Self::TooManyRequestsError(client_message) => {
Self::TooManyRequests(client_message) => {
// Debug level so that if this is from a runaway loop, it won't
// overwhelm server logs
tracing::debug!("Too many requests: {}", client_message);
(StatusCode::TOO_MANY_REQUESTS, client_message).into_response()
}
Self::BadRequestError(client_message) => {
Self::BadRequest(client_message) => {
tracing::info!("Bad user input: {}", client_message);
(StatusCode::BAD_REQUEST, client_message).into_response()
}
@ -66,16 +64,16 @@ impl Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::InternalServerError(inner) => inner.fmt(f),
AppError::ForbiddenError(client_message) => {
AppError::Forbidden(client_message) => {
write!(f, "ForbiddenError: {}", client_message)
}
AppError::NotFoundError(client_message) => {
AppError::NotFound(client_message) => {
write!(f, "NotFoundError: {}", client_message)
}
AppError::BadRequestError(client_message) => {
AppError::BadRequest(client_message) => {
write!(f, "BadRequestError: {}", client_message)
}
AppError::TooManyRequestsError(client_message) => {
AppError::TooManyRequests(client_message) => {
write!(f, "TooManyRequestsError: {}", client_message)
}
}

View file

@ -167,7 +167,7 @@ async fn callback(
})?;
if session_csrf_token != query.state {
tracing::debug!("oauth csrf tokens did not match");
return Err(AppError::ForbiddenError(
return Err(AppError::Forbidden(
"OAuth CSRF tokens do not match.".to_string(),
));
}

View file

@ -40,7 +40,7 @@ fn get_channel_by_params<'a>(
.filter(Channel::with_team(team_id))
.first(conn)
{
diesel::QueryResult::Err(diesel::result::Error::NotFound) => Err(AppError::NotFoundError(
diesel::QueryResult::Err(diesel::result::Error::NotFound) => Err(AppError::NotFound(
"Channel with that team and ID not found.".to_string(),
)),
diesel::QueryResult::Err(err) => Err(err.into()),
@ -153,7 +153,7 @@ async fn post_new_channel(
.await
.unwrap()?,
_ => {
return Err(AppError::BadRequestError(
return Err(AppError::BadRequest(
"Channel type not recognized.".to_string(),
));
}
@ -189,7 +189,7 @@ async fn channel_page(
.unwrap()?
{
None => {
return Err(AppError::NotFoundError(
return Err(AppError::NotFound(
"Channel with that team and ID not found".to_string(),
));
}
@ -272,7 +272,7 @@ async fn update_channel(
.context("Failed to load Channel while updating.")?
};
if updated_rows != 1 {
return Err(AppError::NotFoundError(
return Err(AppError::NotFound(
"Channel with that team and ID not found".to_string(),
));
}
@ -308,7 +308,7 @@ async fn update_channel_email_recipient(
guards::require_team_membership(&current_user, &team_id, &db_conn).await?;
if !is_permissible_email(&form_body.recipient) {
return Err(AppError::BadRequestError(
return Err(AppError::BadRequest(
"Unable to validate email address format.".to_string(),
));
}
@ -400,7 +400,7 @@ async fn verify_email(
guards::require_team_membership(&current_user, &team_id, &db_conn).await?;
if form_body.code.len() != VERIFICATION_CODE_LEN {
return Err(AppError::BadRequestError(format!(
return Err(AppError::BadRequest(format!(
"Verification code must be {} characters long.",
VERIFICATION_CODE_LEN
)));
@ -414,15 +414,13 @@ async fn verify_email(
let channel = get_channel_by_params(conn, &team_id, &channel_id)?;
let config: EmailBackendConfig = channel.backend_config.try_into()?;
if config.verified {
return Err(AppError::BadRequestError(
return Err(AppError::BadRequest(
"Channel's email address is already verified.".to_string(),
));
}
const MAX_VERIFICATION_GUESSES: u32 = 100;
if config.verification_code_guesses > MAX_VERIFICATION_GUESSES {
return Err(AppError::BadRequestError(
"Verification expired.".to_string(),
));
return Err(AppError::BadRequest("Verification expired.".to_string()));
}
let new_config = if config.verification_code == verification_code {
EmailBackendConfig {

View file

@ -34,7 +34,7 @@ pub async fn require_team_membership(
};
match maybe_team {
Some((team, _)) => Ok(team),
None => Err(AppError::ForbiddenError(
None => Err(AppError::Forbidden(
"not a member of requested team".to_string(),
)),
}
@ -50,6 +50,6 @@ pub async fn require_valid_csrf_token(
if validate_csrf_token(db_conn, csrf_token, Some(current_user.id)).await? {
Ok(())
} else {
Err(AppError::ForbiddenError("invalid CSRF token".to_string()))
Err(AppError::Forbidden("invalid CSRF token".to_string()))
}
}

View file

@ -124,7 +124,7 @@ async fn project_page(
.filter(Project::with_team(&team_id))
.first(conn)
{
diesel::QueryResult::Err(diesel::NotFound) => Err(AppError::NotFoundError(
diesel::QueryResult::Err(diesel::NotFound) => Err(AppError::NotFound(
"Project with that team and ID not found.".to_string(),
)),
other => other
@ -213,7 +213,7 @@ async fn update_enabled_channels(
.filter(Project::with_team(&team_id))
.first(conn)
{
diesel::QueryResult::Err(diesel::NotFound) => Err(AppError::NotFoundError(
diesel::QueryResult::Err(diesel::NotFound) => Err(AppError::NotFound(
"Project with that team and ID not found.".to_string(),
)),
other => other

View file

@ -139,7 +139,7 @@ async fn remove_api_key(
"there should never be more than 1 API key with the same ID"
);
if n_deleted == 0 {
Err(AppError::NotFoundError(
Err(AppError::NotFound(
"no API key with that ID and team found".to_owned(),
))
} else {

View file

@ -67,9 +67,8 @@ async fn say_get(
query.validate().map_err(AppError::from_validation_errors)?;
let api_key = {
let query_key = try_parse_as_uuid(&query.key).or(Err(AppError::ForbiddenError(
"key not accepted".to_string(),
)))?;
let query_key = try_parse_as_uuid(&query.key)
.or(Err(AppError::Forbidden("key not accepted".to_string())))?;
db_conn
.interact::<_, Result<ApiKey, AppError>>(move |conn| {
update(api_keys::table.filter(ApiKey::with_id(&query_key)))
@ -78,7 +77,7 @@ async fn say_get(
.get_result(conn)
.optional()
.context("failed to get API key")?
.ok_or(AppError::ForbiddenError("key not accepted.".to_string()))
.ok_or(AppError::Forbidden("key not accepted.".to_string()))
})
.await
.unwrap()?
@ -146,7 +145,7 @@ async fn say_get(
.unwrap()?
.is_none()
{
return Err(AppError::TooManyRequestsError(
return Err(AppError::TooManyRequests(
"team rate limit exceeded".to_string(),
));
}