Skip to content

Commit 9cbe90f

Browse files
committed
models/crate_owner_invitation: Use expires_at column to expiry checks
1 parent f98cee9 commit 9cbe90f

File tree

3 files changed

+13
-51
lines changed

3 files changed

+13
-51
lines changed

src/controllers/crate_owner_invitation.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,6 @@ async fn prepare_list(
265265
}
266266

267267
// Turn `CrateOwnerInvitation`s into `EncodablePrivateCrateOwnerInvitation`.
268-
let config = &state.config;
269268
let mut invitations = Vec::new();
270269
let mut users_in_response = HashSet::new();
271270
for invitation in raw_invitations.into_iter() {
@@ -278,7 +277,7 @@ async fn prepare_list(
278277
.ok_or_else(|| internal(format!("missing crate with id {}", invitation.crate_id)))?
279278
.clone(),
280279
created_at: invitation.created_at,
281-
expires_at: invitation.expires_at(config),
280+
expires_at: invitation.expires_at.naive_utc(),
282281
});
283282
users_in_response.insert(invitation.invited_user_id);
284283
users_in_response.insert(invitation.invited_by_user_id);
@@ -342,10 +341,8 @@ pub async fn handle_crate_owner_invitation(
342341
let invitation =
343342
CrateOwnerInvitation::find_by_id(user_id, crate_invite.crate_id, &mut conn).await?;
344343

345-
let config = &state.config;
346-
347344
if crate_invite.accepted {
348-
invitation.accept(&mut conn, config).await?;
345+
invitation.accept(&mut conn).await?;
349346
} else {
350347
invitation.decline(&mut conn).await?;
351348
}
@@ -370,10 +367,8 @@ pub async fn accept_crate_owner_invitation_with_token(
370367
let mut conn = state.db_write().await?;
371368
let invitation = CrateOwnerInvitation::find_by_token(&token, &mut conn).await?;
372369

373-
let config = &state.config;
374-
375370
let crate_id = invitation.crate_id;
376-
invitation.accept(&mut conn, config).await?;
371+
invitation.accept(&mut conn).await?;
377372

378373
Ok(json!({
379374
"crate_owner_invitation": {

src/models/crate_owner_invitation.rs

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use diesel_async::scoped_futures::ScopedFutureExt;
44
use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl};
55
use secrecy::SecretString;
66

7-
use crate::config;
87
use crate::models::CrateOwner;
98
use crate::schema::{crate_owner_invitations, crate_owners, crates};
109

@@ -27,32 +26,14 @@ impl NewCrateOwnerInvitation {
2726
pub async fn create(
2827
&self,
2928
conn: &mut AsyncPgConnection,
30-
config: &config::Server,
3129
) -> QueryResult<NewCrateOwnerInvitationOutcome> {
3230
// Before actually creating the invite, check if an expired invitation already exists
3331
// and delete it from the database. This allows obtaining a new invite if the old one
3432
// expired, instead of returning "already exists".
35-
conn.transaction(|conn| {
36-
async move {
37-
// This does a SELECT FOR UPDATE + DELETE instead of a DELETE with a WHERE clause to
38-
// use the model's `is_expired` method, centralizing our expiration checking logic.
39-
let existing: Option<CrateOwnerInvitation> = crate_owner_invitations::table
40-
.find((self.invited_user_id, self.crate_id))
41-
.for_update()
42-
.first(conn)
43-
.await
44-
.optional()?;
45-
46-
if let Some(existing) = existing {
47-
if existing.is_expired(config) {
48-
diesel::delete(&existing).execute(conn).await?;
49-
}
50-
}
51-
QueryResult::Ok(())
52-
}
53-
.scope_boxed()
54-
})
55-
.await?;
33+
diesel::delete(crate_owner_invitations::table)
34+
.filter(crate_owner_invitations::expires_at.le(Utc::now()))
35+
.execute(conn)
36+
.await?;
5637

5738
let res: Option<CrateOwnerInvitation> = diesel::insert_into(crate_owner_invitations::table)
5839
.values(self)
@@ -83,7 +64,7 @@ pub struct CrateOwnerInvitation {
8364
pub created_at: NaiveDateTime,
8465
#[diesel(deserialize_as = String)]
8566
pub token: SecretString,
86-
pub expires_at: Option<DateTime<Utc>>,
67+
pub expires_at: DateTime<Utc>,
8768
}
8869

8970
impl CrateOwnerInvitation {
@@ -105,15 +86,8 @@ impl CrateOwnerInvitation {
10586
.await
10687
}
10788

108-
pub async fn accept(
109-
self,
110-
conn: &mut AsyncPgConnection,
111-
config: &config::Server,
112-
) -> Result<(), AcceptError> {
113-
use diesel_async::scoped_futures::ScopedFutureExt;
114-
use diesel_async::{AsyncConnection, RunQueryDsl};
115-
116-
if self.is_expired(config) {
89+
pub async fn accept(self, conn: &mut AsyncPgConnection) -> Result<(), AcceptError> {
90+
if self.is_expired() {
11791
let crate_name: String = crates::table
11892
.find(self.crate_id)
11993
.select(crates::name)
@@ -151,12 +125,8 @@ impl CrateOwnerInvitation {
151125
Ok(())
152126
}
153127

154-
pub fn is_expired(&self, config: &config::Server) -> bool {
155-
self.expires_at(config) <= Utc::now().naive_utc()
156-
}
157-
158-
pub fn expires_at(&self, config: &config::Server) -> NaiveDateTime {
159-
self.created_at + config.ownership_invitations_expiration
128+
pub fn is_expired(&self) -> bool {
129+
self.expires_at <= Utc::now()
160130
}
161131
}
162132

src/models/krate.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,10 +409,7 @@ impl Crate {
409409
expires_at,
410410
};
411411

412-
let creation_ret = invite
413-
.create(conn, &app.config)
414-
.await
415-
.map_err(BoxedAppError::from)?;
412+
let creation_ret = invite.create(conn).await.map_err(BoxedAppError::from)?;
416413

417414
match creation_ret {
418415
NewCrateOwnerInvitationOutcome::InviteCreated { plaintext_token } => {

0 commit comments

Comments
 (0)