diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 26521ceb27..504880f0a3 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -1,6 +1,6 @@ use crate::{ auth, - db::{Invite, NewUserParams, ProjectId, Signup, User, UserId}, + db::{Invite, NewUserParams, ProjectId, Signup, User, UserId, WaitlistSummary}, rpc::{self, ResultExt}, AppState, Error, Result, }; @@ -46,6 +46,7 @@ pub fn routes(rpc_server: &Arc, state: Arc) -> Router>, +) -> Result> { + Ok(Json(app.db.get_waitlist_summary().await?)) +} + #[derive(Deserialize)] pub struct CreateInviteFromCodeParams { invite_code: String, diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 9c1ab84570..1509b15cb2 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -35,6 +35,7 @@ pub trait Db: Send + Sync { async fn create_invite_from_code(&self, code: &str, email_address: &str) -> Result; async fn create_signup(&self, signup: Signup) -> Result<()>; + async fn get_waitlist_summary(&self) -> Result; async fn get_unsent_invites(&self, count: usize) -> Result>; async fn record_sent_invites(&self, invites: &[Invite]) -> Result<()>; async fn create_user_from_invite( @@ -384,6 +385,26 @@ impl Db for PostgresDb { Ok(()) } + async fn get_waitlist_summary(&self) -> Result { + Ok(sqlx::query_as( + " + SELECT + COUNT(*) as count, + COALESCE(SUM(CASE WHEN platform_linux THEN 1 ELSE 0 END), 0) as linux_count, + COALESCE(SUM(CASE WHEN platform_mac THEN 1 ELSE 0 END), 0) as mac_count, + COALESCE(SUM(CASE WHEN platform_windows THEN 1 ELSE 0 END), 0) as windows_count + FROM ( + SELECT * + FROM signups + WHERE + NOT email_confirmation_sent + ) AS unsent + ", + ) + .fetch_one(&self.pool) + .await?) + } + async fn get_unsent_invites(&self, count: usize) -> Result> { Ok(sqlx::query_as( " @@ -1630,6 +1651,18 @@ pub struct Signup { pub programming_languages: Vec, } +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, FromRow)] +pub struct WaitlistSummary { + #[sqlx(default)] + pub count: i64, + #[sqlx(default)] + pub linux_count: i64, + #[sqlx(default)] + pub mac_count: i64, + #[sqlx(default)] + pub windows_count: i64, +} + #[derive(FromRow, PartialEq, Debug, Serialize, Deserialize)] pub struct Invite { pub email_address: String, @@ -1812,6 +1845,10 @@ mod test { unimplemented!() } + async fn get_waitlist_summary(&self) -> Result { + unimplemented!() + } + async fn get_unsent_invites(&self, _count: usize) -> Result> { unimplemented!() } diff --git a/crates/collab/src/db_tests.rs b/crates/collab/src/db_tests.rs index aa9a0b6995..4ff8bbd0f6 100644 --- a/crates/collab/src/db_tests.rs +++ b/crates/collab/src/db_tests.rs @@ -966,8 +966,8 @@ async fn test_signups() { db.create_signup(Signup { email_address: format!("person-{i}@example.com"), platform_mac: true, - platform_linux: true, - platform_windows: false, + platform_linux: i % 2 == 0, + platform_windows: i % 4 == 0, editor_features: vec!["speed".into()], programming_languages: vec!["rust".into(), "c".into()], }) @@ -975,6 +975,16 @@ async fn test_signups() { .unwrap(); } + assert_eq!( + db.get_waitlist_summary().await.unwrap(), + WaitlistSummary { + count: 8, + mac_count: 8, + linux_count: 4, + windows_count: 2, + } + ); + // retrieve the next batch of signup emails to send let signups_batch1 = db.get_unsent_invites(3).await.unwrap(); let addresses = signups_batch1 @@ -1016,6 +1026,17 @@ async fn test_signups() { ] ); + // the sent invites are excluded from the summary. + assert_eq!( + db.get_waitlist_summary().await.unwrap(), + WaitlistSummary { + count: 5, + mac_count: 5, + linux_count: 2, + windows_count: 1, + } + ); + // user completes the signup process by providing their // github account. let (user_id, inviter_id) = db