Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/models.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub use self::action::{NewVersionOwnerAction, VersionAction, VersionOwnerAction};
pub use self::category::{Category, CrateCategory, NewCategory};
pub use self::crate_owner_invitation::{CrateOwnerInvitation, NewCrateOwnerInvitationOutcome};
pub use self::crate_owner_invitation::{
CrateOwnerInvitation, NewCrateOwnerInvitation, NewCrateOwnerInvitationOutcome,
};
pub use self::default_versions::{update_default_version, verify_default_version};
pub use self::deleted_crate::NewDeletedCrate;
pub use self::dependency::{Dependency, DependencyKind, ReverseDependency};
Expand Down
46 changes: 21 additions & 25 deletions src/models/crate_owner_invitation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,20 @@ pub enum NewCrateOwnerInvitationOutcome {
InviteCreated { plaintext_token: SecretString },
}

/// The model representing a row in the `crate_owner_invitations` database table.
#[derive(Clone, Debug, Identifiable, Queryable)]
#[diesel(primary_key(invited_user_id, crate_id))]
pub struct CrateOwnerInvitation {
#[derive(Clone, Debug, Insertable)]
#[diesel(table_name = crate_owner_invitations, check_for_backend(diesel::pg::Pg))]
pub struct NewCrateOwnerInvitation {
pub invited_user_id: i32,
pub invited_by_user_id: i32,
pub crate_id: i32,
pub created_at: NaiveDateTime,
#[diesel(deserialize_as = String)]
pub token: SecretString,
}

impl CrateOwnerInvitation {
impl NewCrateOwnerInvitation {
pub async fn create(
invited_user_id: i32,
invited_by_user_id: i32,
crate_id: i32,
&self,
conn: &mut AsyncPgConnection,
config: &config::Server,
) -> QueryResult<NewCrateOwnerInvitationOutcome> {
#[derive(Insertable, Clone, Copy, Debug)]
#[diesel(table_name = crate_owner_invitations, check_for_backend(diesel::pg::Pg))]
struct NewRecord {
invited_user_id: i32,
invited_by_user_id: i32,
crate_id: i32,
}

// Before actually creating the invite, check if an expired invitation already exists
// and delete it from the database. This allows obtaining a new invite if the old one
// expired, instead of returning "already exists".
Expand All @@ -52,7 +38,7 @@ impl CrateOwnerInvitation {
// This does a SELECT FOR UPDATE + DELETE instead of a DELETE with a WHERE clause to
// use the model's `is_expired` method, centralizing our expiration checking logic.
let existing: Option<CrateOwnerInvitation> = crate_owner_invitations::table
.find((invited_user_id, crate_id))
.find((self.invited_user_id, self.crate_id))
.for_update()
.first(conn)
.await
Expand All @@ -70,11 +56,7 @@ impl CrateOwnerInvitation {
.await?;

let res: Option<CrateOwnerInvitation> = diesel::insert_into(crate_owner_invitations::table)
.values(&NewRecord {
invited_user_id,
invited_by_user_id,
crate_id,
})
.values(self)
// The ON CONFLICT DO NOTHING clause results in not creating the invite if another one
// already exists. This does not cause problems with expired invitation as those are
// deleted before doing this INSERT.
Expand All @@ -90,7 +72,21 @@ impl CrateOwnerInvitation {
None => NewCrateOwnerInvitationOutcome::AlreadyExists,
})
}
}

/// The model representing a row in the `crate_owner_invitations` database table.
#[derive(Clone, Debug, Identifiable, Queryable)]
#[diesel(primary_key(invited_user_id, crate_id))]
pub struct CrateOwnerInvitation {
pub invited_user_id: i32,
pub invited_by_user_id: i32,
pub crate_id: i32,
pub created_at: NaiveDateTime,
#[diesel(deserialize_as = String)]
pub token: SecretString,
}

impl CrateOwnerInvitation {
pub async fn find_by_id(
user_id: i32,
crate_id: i32,
Expand Down
16 changes: 11 additions & 5 deletions src/models/krate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::controllers::helpers::pagination::*;
use crate::models::helpers::with_count::*;
use crate::models::version::TopVersions;
use crate::models::{
CrateOwner, CrateOwnerInvitation, NewCrateOwnerInvitationOutcome, Owner, OwnerKind,
CrateOwner, NewCrateOwnerInvitation, NewCrateOwnerInvitationOutcome, Owner, OwnerKind,
ReverseDependency, User, Version,
};
use crate::schema::*;
Expand Down Expand Up @@ -401,10 +401,16 @@ impl Crate {
match owner {
// Users are invited and must accept before being added
Owner::User(user) => {
let creation_ret =
CrateOwnerInvitation::create(user.id, req_user.id, self.id, conn, &app.config)
.await
.map_err(BoxedAppError::from)?;
let invite = NewCrateOwnerInvitation {
invited_user_id: user.id,
invited_by_user_id: req_user.id,
crate_id: self.id,
};

let creation_ret = invite
.create(conn, &app.config)
.await
.map_err(BoxedAppError::from)?;

match creation_ret {
NewCrateOwnerInvitationOutcome::InviteCreated { plaintext_token } => {
Expand Down