diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index 6afae1e3437..079f320fe82 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -2,9 +2,10 @@ use crate::app::AppState; use crate::auth::AuthCheck; use crate::auth::Authentication; use crate::controllers::helpers::pagination::{Page, PaginationOptions, PaginationQueryParams}; +use crate::models::crate_owner_invitation::AcceptError; use crate::models::{Crate, CrateOwnerInvitation, Rights, User}; use crate::schema::{crate_owner_invitations, crates, users}; -use crate::util::errors::{bad_request, forbidden, internal, AppResult, BoxedAppError}; +use crate::util::errors::{bad_request, custom, forbidden, internal, AppResult, BoxedAppError}; use crate::util::RequestUtils; use crate::views::{ EncodableCrateOwnerInvitation, EncodableCrateOwnerInvitationV1, EncodablePublicUser, @@ -20,6 +21,7 @@ use diesel::prelude::*; use diesel::sql_types::Bool; use diesel_async::{AsyncPgConnection, RunQueryDsl}; use http::request::Parts; +use http::StatusCode; use indexmap::IndexMap; use std::collections::{HashMap, HashSet}; @@ -380,3 +382,19 @@ pub async fn accept_crate_owner_invitation_with_token( }, })) } + +impl From for BoxedAppError { + fn from(error: AcceptError) -> Self { + match error { + AcceptError::Diesel(error) => error.into(), + AcceptError::Expired { crate_name } => { + let detail = format!( + "The invitation to become an owner of the {crate_name} crate expired. \ + Please reach out to an owner of the crate to request a new invitation.", + ); + + custom(StatusCode::GONE, detail) + } + } + } +} diff --git a/src/models.rs b/src/models.rs index 03bfc833d0e..5e71967d866 100644 --- a/src/models.rs +++ b/src/models.rs @@ -22,7 +22,7 @@ pub mod helpers; mod action; pub mod category; -mod crate_owner_invitation; +pub mod crate_owner_invitation; pub mod default_versions; mod deleted_crate; pub mod dependency; diff --git a/src/models/crate_owner_invitation.rs b/src/models/crate_owner_invitation.rs index 94c5be77643..9830ec5defb 100644 --- a/src/models/crate_owner_invitation.rs +++ b/src/models/crate_owner_invitation.rs @@ -2,13 +2,11 @@ use chrono::{NaiveDateTime, Utc}; use diesel::prelude::*; use diesel_async::scoped_futures::ScopedFutureExt; use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl}; -use http::StatusCode; use secrecy::SecretString; use crate::config; use crate::models::{CrateOwner, OwnerKind}; use crate::schema::{crate_owner_invitations, crate_owners, crates}; -use crate::util::errors::{custom, AppResult}; #[derive(Debug)] pub enum NewCrateOwnerInvitationOutcome { @@ -109,7 +107,7 @@ impl CrateOwnerInvitation { self, conn: &mut AsyncPgConnection, config: &config::Server, - ) -> AppResult<()> { + ) -> Result<(), AcceptError> { use diesel_async::scoped_futures::ScopedFutureExt; use diesel_async::{AsyncConnection, RunQueryDsl}; @@ -120,12 +118,7 @@ impl CrateOwnerInvitation { .first(conn) .await?; - let detail = format!( - "The invitation to become an owner of the {crate_name} crate expired. \ - Please reach out to an owner of the crate to request a new invitation.", - ); - - return Err(custom(StatusCode::GONE, detail)); + return Err(AcceptError::Expired { crate_name }); } conn.transaction(|conn| { @@ -170,3 +163,11 @@ impl CrateOwnerInvitation { self.created_at + config.ownership_invitations_expiration } } + +#[derive(Debug, thiserror::Error)] +pub enum AcceptError { + #[error(transparent)] + Diesel(#[from] diesel::result::Error), + #[error("The invitation has expired")] + Expired { crate_name: String }, +}