Remove dead code for old admin pages

Max Brunsfeld created

Change summary

crates/collab/src/api.rs                     | 217 --------
crates/collab/src/db/queries.rs              |   1 
crates/collab/src/db/queries/signups.rs      | 349 --------------
crates/collab/src/db/queries/users.rs        |  36 -
crates/collab/src/db/tests/db_tests.rs       | 541 ----------------------
crates/collab/src/rpc.rs                     |  10 
crates/collab/src/tests/integration_tests.rs |   1 
7 files changed, 5 insertions(+), 1,150 deletions(-)

Detailed changes

crates/collab/src/api.rs 🔗

@@ -1,8 +1,7 @@
 use crate::{
     auth,
-    db::{Invite, NewSignup, NewUserParams, User, UserId, WaitlistSummary},
-    rpc::{self, ResultExt},
-    AppState, Error, Result,
+    db::{User, UserId},
+    rpc, AppState, Error, Result,
 };
 use anyhow::anyhow;
 use axum::{
@@ -11,7 +10,7 @@ use axum::{
     http::{self, Request, StatusCode},
     middleware::{self, Next},
     response::IntoResponse,
-    routing::{get, post, put},
+    routing::{get, post},
     Extension, Json, Router,
 };
 use axum_extra::response::ErasedJson;
@@ -23,18 +22,9 @@ use tracing::instrument;
 pub fn routes(rpc_server: Arc<rpc::Server>, state: Arc<AppState>) -> Router<Body> {
     Router::new()
         .route("/user", get(get_authenticated_user))
-        .route("/users", get(get_users).post(create_user))
-        .route("/users/:id", put(update_user).delete(destroy_user))
         .route("/users/:id/access_tokens", post(create_access_token))
-        .route("/users_with_no_invites", get(get_users_with_no_invites))
-        .route("/invite_codes/:code", get(get_user_for_invite_code))
         .route("/panic", post(trace_panic))
         .route("/rpc_server_snapshot", get(get_rpc_server_snapshot))
-        .route("/signups", post(create_signup))
-        .route("/signups_summary", get(get_waitlist_summary))
-        .route("/user_invites", post(create_invite_from_code))
-        .route("/unsent_invites", get(get_unsent_invites))
-        .route("/sent_invites", post(record_sent_invites))
         .layer(
             ServiceBuilder::new()
                 .layer(Extension(state))
@@ -104,28 +94,6 @@ async fn get_authenticated_user(
     return Ok(Json(AuthenticatedUserResponse { user, metrics_id }));
 }
 
-#[derive(Debug, Deserialize)]
-struct GetUsersQueryParams {
-    query: Option<String>,
-    page: Option<u32>,
-    limit: Option<u32>,
-}
-
-async fn get_users(
-    Query(params): Query<GetUsersQueryParams>,
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<Json<Vec<User>>> {
-    let limit = params.limit.unwrap_or(100);
-    let users = if let Some(query) = params.query {
-        app.db.fuzzy_search_users(&query, limit).await?
-    } else {
-        app.db
-            .get_all_users(params.page.unwrap_or(0), limit)
-            .await?
-    };
-    Ok(Json(users))
-}
-
 #[derive(Deserialize, Debug)]
 struct CreateUserParams {
     github_user_id: i32,
@@ -145,119 +113,6 @@ struct CreateUserResponse {
     metrics_id: String,
 }
 
-async fn create_user(
-    Json(params): Json<CreateUserParams>,
-    Extension(app): Extension<Arc<AppState>>,
-    Extension(rpc_server): Extension<Arc<rpc::Server>>,
-) -> Result<Json<Option<CreateUserResponse>>> {
-    let user = NewUserParams {
-        github_login: params.github_login,
-        github_user_id: params.github_user_id,
-        invite_count: params.invite_count,
-    };
-
-    // Creating a user via the normal signup process
-    let result = if let Some(email_confirmation_code) = params.email_confirmation_code {
-        if let Some(result) = app
-            .db
-            .create_user_from_invite(
-                &Invite {
-                    email_address: params.email_address,
-                    email_confirmation_code,
-                },
-                user,
-            )
-            .await?
-        {
-            result
-        } else {
-            return Ok(Json(None));
-        }
-    }
-    // Creating a user as an admin
-    else if params.admin {
-        app.db
-            .create_user(&params.email_address, false, user)
-            .await?
-    } else {
-        Err(Error::Http(
-            StatusCode::UNPROCESSABLE_ENTITY,
-            "email confirmation code is required".into(),
-        ))?
-    };
-
-    if let Some(inviter_id) = result.inviting_user_id {
-        rpc_server
-            .invite_code_redeemed(inviter_id, result.user_id)
-            .await
-            .trace_err();
-    }
-
-    let user = app
-        .db
-        .get_user_by_id(result.user_id)
-        .await?
-        .ok_or_else(|| anyhow!("couldn't find the user we just created"))?;
-
-    Ok(Json(Some(CreateUserResponse {
-        user,
-        metrics_id: result.metrics_id,
-        signup_device_id: result.signup_device_id,
-    })))
-}
-
-#[derive(Deserialize)]
-struct UpdateUserParams {
-    admin: Option<bool>,
-    invite_count: Option<i32>,
-}
-
-async fn update_user(
-    Path(user_id): Path<i32>,
-    Json(params): Json<UpdateUserParams>,
-    Extension(app): Extension<Arc<AppState>>,
-    Extension(rpc_server): Extension<Arc<rpc::Server>>,
-) -> Result<()> {
-    let user_id = UserId(user_id);
-
-    if let Some(admin) = params.admin {
-        app.db.set_user_is_admin(user_id, admin).await?;
-    }
-
-    if let Some(invite_count) = params.invite_count {
-        app.db
-            .set_invite_count_for_user(user_id, invite_count)
-            .await?;
-        rpc_server.invite_count_updated(user_id).await.trace_err();
-    }
-
-    Ok(())
-}
-
-async fn destroy_user(
-    Path(user_id): Path<i32>,
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<()> {
-    app.db.destroy_user(UserId(user_id)).await?;
-    Ok(())
-}
-
-#[derive(Debug, Deserialize)]
-struct GetUsersWithNoInvites {
-    invited_by_another_user: bool,
-}
-
-async fn get_users_with_no_invites(
-    Query(params): Query<GetUsersWithNoInvites>,
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<Json<Vec<User>>> {
-    Ok(Json(
-        app.db
-            .get_users_with_no_invites(params.invited_by_another_user)
-            .await?,
-    ))
-}
-
 #[derive(Debug, Deserialize)]
 struct Panic {
     version: String,
@@ -327,69 +182,3 @@ async fn create_access_token(
         encrypted_access_token,
     }))
 }
-
-async fn get_user_for_invite_code(
-    Path(code): Path<String>,
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<Json<User>> {
-    Ok(Json(app.db.get_user_for_invite_code(&code).await?))
-}
-
-async fn create_signup(
-    Json(params): Json<NewSignup>,
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<()> {
-    app.db.create_signup(&params).await?;
-    Ok(())
-}
-
-async fn get_waitlist_summary(
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<Json<WaitlistSummary>> {
-    Ok(Json(app.db.get_waitlist_summary().await?))
-}
-
-#[derive(Deserialize)]
-pub struct CreateInviteFromCodeParams {
-    invite_code: String,
-    email_address: String,
-    device_id: Option<String>,
-    #[serde(default)]
-    added_to_mailing_list: bool,
-}
-
-async fn create_invite_from_code(
-    Json(params): Json<CreateInviteFromCodeParams>,
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<Json<Invite>> {
-    Ok(Json(
-        app.db
-            .create_invite_from_code(
-                &params.invite_code,
-                &params.email_address,
-                params.device_id.as_deref(),
-                params.added_to_mailing_list,
-            )
-            .await?,
-    ))
-}
-
-#[derive(Deserialize)]
-pub struct GetUnsentInvitesParams {
-    pub count: usize,
-}
-
-async fn get_unsent_invites(
-    Query(params): Query<GetUnsentInvitesParams>,
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<Json<Vec<Invite>>> {
-    Ok(Json(app.db.get_unsent_invites(params.count).await?))
-}
-
-async fn record_sent_invites(
-    Json(params): Json<Vec<Invite>>,
-    Extension(app): Extension<Arc<AppState>>,
-) -> Result<()> {
-    app.db.record_sent_invites(&params).await?;
-    Ok(())
-}

crates/collab/src/db/queries/signups.rs 🔗

@@ -1,349 +0,0 @@
-use super::*;
-use hyper::StatusCode;
-
-impl Database {
-    pub async fn create_invite_from_code(
-        &self,
-        code: &str,
-        email_address: &str,
-        device_id: Option<&str>,
-        added_to_mailing_list: bool,
-    ) -> Result<Invite> {
-        self.transaction(|tx| async move {
-            let existing_user = user::Entity::find()
-                .filter(user::Column::EmailAddress.eq(email_address))
-                .one(&*tx)
-                .await?;
-
-            if existing_user.is_some() {
-                Err(anyhow!("email address is already in use"))?;
-            }
-
-            let inviting_user_with_invites = match user::Entity::find()
-                .filter(
-                    user::Column::InviteCode
-                        .eq(code)
-                        .and(user::Column::InviteCount.gt(0)),
-                )
-                .one(&*tx)
-                .await?
-            {
-                Some(inviting_user) => inviting_user,
-                None => {
-                    return Err(Error::Http(
-                        StatusCode::UNAUTHORIZED,
-                        "unable to find an invite code with invites remaining".to_string(),
-                    ))?
-                }
-            };
-            user::Entity::update_many()
-                .filter(
-                    user::Column::Id
-                        .eq(inviting_user_with_invites.id)
-                        .and(user::Column::InviteCount.gt(0)),
-                )
-                .col_expr(
-                    user::Column::InviteCount,
-                    Expr::col(user::Column::InviteCount).sub(1),
-                )
-                .exec(&*tx)
-                .await?;
-
-            let signup = signup::Entity::insert(signup::ActiveModel {
-                email_address: ActiveValue::set(email_address.into()),
-                email_confirmation_code: ActiveValue::set(random_email_confirmation_code()),
-                email_confirmation_sent: ActiveValue::set(false),
-                inviting_user_id: ActiveValue::set(Some(inviting_user_with_invites.id)),
-                platform_linux: ActiveValue::set(false),
-                platform_mac: ActiveValue::set(false),
-                platform_windows: ActiveValue::set(false),
-                platform_unknown: ActiveValue::set(true),
-                device_id: ActiveValue::set(device_id.map(|device_id| device_id.into())),
-                added_to_mailing_list: ActiveValue::set(added_to_mailing_list),
-                ..Default::default()
-            })
-            .on_conflict(
-                OnConflict::column(signup::Column::EmailAddress)
-                    .update_column(signup::Column::InvitingUserId)
-                    .to_owned(),
-            )
-            .exec_with_returning(&*tx)
-            .await?;
-
-            Ok(Invite {
-                email_address: signup.email_address,
-                email_confirmation_code: signup.email_confirmation_code,
-            })
-        })
-        .await
-    }
-
-    pub async fn create_user_from_invite(
-        &self,
-        invite: &Invite,
-        user: NewUserParams,
-    ) -> Result<Option<NewUserResult>> {
-        self.transaction(|tx| async {
-            let tx = tx;
-            let signup = signup::Entity::find()
-                .filter(
-                    signup::Column::EmailAddress
-                        .eq(invite.email_address.as_str())
-                        .and(
-                            signup::Column::EmailConfirmationCode
-                                .eq(invite.email_confirmation_code.as_str()),
-                        ),
-                )
-                .one(&*tx)
-                .await?
-                .ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "no such invite".to_string()))?;
-
-            if signup.user_id.is_some() {
-                return Ok(None);
-            }
-
-            let user = user::Entity::insert(user::ActiveModel {
-                email_address: ActiveValue::set(Some(invite.email_address.clone())),
-                github_login: ActiveValue::set(user.github_login.clone()),
-                github_user_id: ActiveValue::set(Some(user.github_user_id)),
-                admin: ActiveValue::set(false),
-                invite_count: ActiveValue::set(user.invite_count),
-                invite_code: ActiveValue::set(Some(random_invite_code())),
-                metrics_id: ActiveValue::set(Uuid::new_v4()),
-                ..Default::default()
-            })
-            .on_conflict(
-                OnConflict::column(user::Column::GithubLogin)
-                    .update_columns([
-                        user::Column::EmailAddress,
-                        user::Column::GithubUserId,
-                        user::Column::Admin,
-                    ])
-                    .to_owned(),
-            )
-            .exec_with_returning(&*tx)
-            .await?;
-
-            let mut signup = signup.into_active_model();
-            signup.user_id = ActiveValue::set(Some(user.id));
-            let signup = signup.update(&*tx).await?;
-
-            if let Some(inviting_user_id) = signup.inviting_user_id {
-                let (user_id_a, user_id_b, a_to_b) = if inviting_user_id < user.id {
-                    (inviting_user_id, user.id, true)
-                } else {
-                    (user.id, inviting_user_id, false)
-                };
-
-                contact::Entity::insert(contact::ActiveModel {
-                    user_id_a: ActiveValue::set(user_id_a),
-                    user_id_b: ActiveValue::set(user_id_b),
-                    a_to_b: ActiveValue::set(a_to_b),
-                    should_notify: ActiveValue::set(true),
-                    accepted: ActiveValue::set(true),
-                    ..Default::default()
-                })
-                .on_conflict(OnConflict::new().do_nothing().to_owned())
-                .exec_without_returning(&*tx)
-                .await?;
-            }
-
-            Ok(Some(NewUserResult {
-                user_id: user.id,
-                metrics_id: user.metrics_id.to_string(),
-                inviting_user_id: signup.inviting_user_id,
-                signup_device_id: signup.device_id,
-            }))
-        })
-        .await
-    }
-
-    pub async fn set_invite_count_for_user(&self, id: UserId, count: i32) -> Result<()> {
-        self.transaction(|tx| async move {
-            if count > 0 {
-                user::Entity::update_many()
-                    .filter(
-                        user::Column::Id
-                            .eq(id)
-                            .and(user::Column::InviteCode.is_null()),
-                    )
-                    .set(user::ActiveModel {
-                        invite_code: ActiveValue::set(Some(random_invite_code())),
-                        ..Default::default()
-                    })
-                    .exec(&*tx)
-                    .await?;
-            }
-
-            user::Entity::update_many()
-                .filter(user::Column::Id.eq(id))
-                .set(user::ActiveModel {
-                    invite_count: ActiveValue::set(count),
-                    ..Default::default()
-                })
-                .exec(&*tx)
-                .await?;
-            Ok(())
-        })
-        .await
-    }
-
-    pub async fn get_invite_code_for_user(&self, id: UserId) -> Result<Option<(String, i32)>> {
-        self.transaction(|tx| async move {
-            match user::Entity::find_by_id(id).one(&*tx).await? {
-                Some(user) if user.invite_code.is_some() => {
-                    Ok(Some((user.invite_code.unwrap(), user.invite_count)))
-                }
-                _ => Ok(None),
-            }
-        })
-        .await
-    }
-
-    pub async fn get_user_for_invite_code(&self, code: &str) -> Result<User> {
-        self.transaction(|tx| async move {
-            user::Entity::find()
-                .filter(user::Column::InviteCode.eq(code))
-                .one(&*tx)
-                .await?
-                .ok_or_else(|| {
-                    Error::Http(
-                        StatusCode::NOT_FOUND,
-                        "that invite code does not exist".to_string(),
-                    )
-                })
-        })
-        .await
-    }
-
-    pub async fn create_signup(&self, signup: &NewSignup) -> Result<()> {
-        self.transaction(|tx| async move {
-            signup::Entity::insert(signup::ActiveModel {
-                email_address: ActiveValue::set(signup.email_address.clone()),
-                email_confirmation_code: ActiveValue::set(random_email_confirmation_code()),
-                email_confirmation_sent: ActiveValue::set(false),
-                platform_mac: ActiveValue::set(signup.platform_mac),
-                platform_windows: ActiveValue::set(signup.platform_windows),
-                platform_linux: ActiveValue::set(signup.platform_linux),
-                platform_unknown: ActiveValue::set(false),
-                editor_features: ActiveValue::set(Some(signup.editor_features.clone())),
-                programming_languages: ActiveValue::set(Some(signup.programming_languages.clone())),
-                device_id: ActiveValue::set(signup.device_id.clone()),
-                added_to_mailing_list: ActiveValue::set(signup.added_to_mailing_list),
-                ..Default::default()
-            })
-            .on_conflict(
-                OnConflict::column(signup::Column::EmailAddress)
-                    .update_columns([
-                        signup::Column::PlatformMac,
-                        signup::Column::PlatformWindows,
-                        signup::Column::PlatformLinux,
-                        signup::Column::EditorFeatures,
-                        signup::Column::ProgrammingLanguages,
-                        signup::Column::DeviceId,
-                        signup::Column::AddedToMailingList,
-                    ])
-                    .to_owned(),
-            )
-            .exec(&*tx)
-            .await?;
-            Ok(())
-        })
-        .await
-    }
-
-    pub async fn get_signup(&self, email_address: &str) -> Result<signup::Model> {
-        self.transaction(|tx| async move {
-            let signup = signup::Entity::find()
-                .filter(signup::Column::EmailAddress.eq(email_address))
-                .one(&*tx)
-                .await?
-                .ok_or_else(|| {
-                    anyhow!("signup with email address {} doesn't exist", email_address)
-                })?;
-
-            Ok(signup)
-        })
-        .await
-    }
-
-    pub async fn get_waitlist_summary(&self) -> Result<WaitlistSummary> {
-        self.transaction(|tx| async move {
-            let query = "
-                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,
-                    COALESCE(SUM(CASE WHEN platform_unknown THEN 1 ELSE 0 END), 0) as unknown_count
-                FROM (
-                    SELECT *
-                    FROM signups
-                    WHERE
-                        NOT email_confirmation_sent
-                ) AS unsent
-            ";
-            Ok(
-                WaitlistSummary::find_by_statement(Statement::from_sql_and_values(
-                    self.pool.get_database_backend(),
-                    query.into(),
-                    vec![],
-                ))
-                .one(&*tx)
-                .await?
-                .ok_or_else(|| anyhow!("invalid result"))?,
-            )
-        })
-        .await
-    }
-
-    pub async fn record_sent_invites(&self, invites: &[Invite]) -> Result<()> {
-        let emails = invites
-            .iter()
-            .map(|s| s.email_address.as_str())
-            .collect::<Vec<_>>();
-        self.transaction(|tx| async {
-            let tx = tx;
-            signup::Entity::update_many()
-                .filter(signup::Column::EmailAddress.is_in(emails.iter().copied()))
-                .set(signup::ActiveModel {
-                    email_confirmation_sent: ActiveValue::set(true),
-                    ..Default::default()
-                })
-                .exec(&*tx)
-                .await?;
-            Ok(())
-        })
-        .await
-    }
-
-    pub async fn get_unsent_invites(&self, count: usize) -> Result<Vec<Invite>> {
-        self.transaction(|tx| async move {
-            Ok(signup::Entity::find()
-                .select_only()
-                .column(signup::Column::EmailAddress)
-                .column(signup::Column::EmailConfirmationCode)
-                .filter(
-                    signup::Column::EmailConfirmationSent.eq(false).and(
-                        signup::Column::PlatformMac
-                            .eq(true)
-                            .or(signup::Column::PlatformUnknown.eq(true)),
-                    ),
-                )
-                .order_by_asc(signup::Column::CreatedAt)
-                .limit(count as u64)
-                .into_model()
-                .all(&*tx)
-                .await?)
-        })
-        .await
-    }
-}
-
-fn random_invite_code() -> String {
-    nanoid::nanoid!(16)
-}
-
-fn random_email_confirmation_code() -> String {
-    nanoid::nanoid!(64)
-}

crates/collab/src/db/queries/users.rs 🔗

@@ -123,27 +123,6 @@ impl Database {
         .await
     }
 
-    pub async fn get_users_with_no_invites(
-        &self,
-        invited_by_another_user: bool,
-    ) -> Result<Vec<User>> {
-        self.transaction(|tx| async move {
-            Ok(user::Entity::find()
-                .filter(
-                    user::Column::InviteCount
-                        .eq(0)
-                        .and(if invited_by_another_user {
-                            user::Column::InviterId.is_not_null()
-                        } else {
-                            user::Column::InviterId.is_null()
-                        }),
-                )
-                .all(&*tx)
-                .await?)
-        })
-        .await
-    }
-
     pub async fn get_user_metrics_id(&self, id: UserId) -> Result<String> {
         #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
         enum QueryAs {
@@ -163,21 +142,6 @@ impl Database {
         .await
     }
 
-    pub async fn set_user_is_admin(&self, id: UserId, is_admin: bool) -> Result<()> {
-        self.transaction(|tx| async move {
-            user::Entity::update_many()
-                .filter(user::Column::Id.eq(id))
-                .set(user::ActiveModel {
-                    admin: ActiveValue::set(is_admin),
-                    ..Default::default()
-                })
-                .exec(&*tx)
-                .await?;
-            Ok(())
-        })
-        .await
-    }
-
     pub async fn set_user_connected_once(&self, id: UserId, connected_once: bool) -> Result<()> {
         self.transaction(|tx| async move {
             user::Entity::update_many()

crates/collab/src/db/tests/db_tests.rs 🔗

@@ -575,308 +575,6 @@ async fn test_fuzzy_search_users() {
     }
 }
 
-#[gpui::test]
-async fn test_invite_codes() {
-    let test_db = TestDb::postgres(build_background_executor());
-    let db = test_db.db();
-
-    let NewUserResult { user_id: user1, .. } = db
-        .create_user(
-            "user1@example.com",
-            false,
-            NewUserParams {
-                github_login: "user1".into(),
-                github_user_id: 0,
-                invite_count: 0,
-            },
-        )
-        .await
-        .unwrap();
-
-    // Initially, user 1 has no invite code
-    assert_eq!(db.get_invite_code_for_user(user1).await.unwrap(), None);
-
-    // Setting invite count to 0 when no code is assigned does not assign a new code
-    db.set_invite_count_for_user(user1, 0).await.unwrap();
-    assert!(db.get_invite_code_for_user(user1).await.unwrap().is_none());
-
-    // User 1 creates an invite code that can be used twice.
-    db.set_invite_count_for_user(user1, 2).await.unwrap();
-    let (invite_code, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
-    assert_eq!(invite_count, 2);
-
-    // User 2 redeems the invite code and becomes a contact of user 1.
-    let user2_invite = db
-        .create_invite_from_code(
-            &invite_code,
-            "user2@example.com",
-            Some("user-2-device-id"),
-            true,
-        )
-        .await
-        .unwrap();
-    let NewUserResult {
-        user_id: user2,
-        inviting_user_id,
-        signup_device_id,
-        metrics_id,
-    } = db
-        .create_user_from_invite(
-            &user2_invite,
-            NewUserParams {
-                github_login: "user2".into(),
-                github_user_id: 2,
-                invite_count: 7,
-            },
-        )
-        .await
-        .unwrap()
-        .unwrap();
-    let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
-    assert_eq!(invite_count, 1);
-    assert_eq!(inviting_user_id, Some(user1));
-    assert_eq!(signup_device_id.unwrap(), "user-2-device-id");
-    assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id);
-    assert_eq!(
-        db.get_contacts(user1).await.unwrap(),
-        [Contact::Accepted {
-            user_id: user2,
-            should_notify: true,
-            busy: false,
-        }]
-    );
-    assert_eq!(
-        db.get_contacts(user2).await.unwrap(),
-        [Contact::Accepted {
-            user_id: user1,
-            should_notify: false,
-            busy: false,
-        }]
-    );
-    assert!(db.has_contact(user1, user2).await.unwrap());
-    assert!(db.has_contact(user2, user1).await.unwrap());
-    assert_eq!(
-        db.get_invite_code_for_user(user2).await.unwrap().unwrap().1,
-        7
-    );
-
-    // User 3 redeems the invite code and becomes a contact of user 1.
-    let user3_invite = db
-        .create_invite_from_code(&invite_code, "user3@example.com", None, true)
-        .await
-        .unwrap();
-    let NewUserResult {
-        user_id: user3,
-        inviting_user_id,
-        signup_device_id,
-        ..
-    } = db
-        .create_user_from_invite(
-            &user3_invite,
-            NewUserParams {
-                github_login: "user-3".into(),
-                github_user_id: 3,
-                invite_count: 3,
-            },
-        )
-        .await
-        .unwrap()
-        .unwrap();
-    let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
-    assert_eq!(invite_count, 0);
-    assert_eq!(inviting_user_id, Some(user1));
-    assert!(signup_device_id.is_none());
-    assert_eq!(
-        db.get_contacts(user1).await.unwrap(),
-        [
-            Contact::Accepted {
-                user_id: user2,
-                should_notify: true,
-                busy: false,
-            },
-            Contact::Accepted {
-                user_id: user3,
-                should_notify: true,
-                busy: false,
-            }
-        ]
-    );
-    assert_eq!(
-        db.get_contacts(user3).await.unwrap(),
-        [Contact::Accepted {
-            user_id: user1,
-            should_notify: false,
-            busy: false,
-        }]
-    );
-    assert!(db.has_contact(user1, user3).await.unwrap());
-    assert!(db.has_contact(user3, user1).await.unwrap());
-    assert_eq!(
-        db.get_invite_code_for_user(user3).await.unwrap().unwrap().1,
-        3
-    );
-
-    // Trying to reedem the code for the third time results in an error.
-    db.create_invite_from_code(
-        &invite_code,
-        "user4@example.com",
-        Some("user-4-device-id"),
-        true,
-    )
-    .await
-    .unwrap_err();
-
-    // Invite count can be updated after the code has been created.
-    db.set_invite_count_for_user(user1, 2).await.unwrap();
-    let (latest_code, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
-    assert_eq!(latest_code, invite_code); // Invite code doesn't change when we increment above 0
-    assert_eq!(invite_count, 2);
-
-    // User 4 can now redeem the invite code and becomes a contact of user 1.
-    let user4_invite = db
-        .create_invite_from_code(
-            &invite_code,
-            "user4@example.com",
-            Some("user-4-device-id"),
-            true,
-        )
-        .await
-        .unwrap();
-    let user4 = db
-        .create_user_from_invite(
-            &user4_invite,
-            NewUserParams {
-                github_login: "user-4".into(),
-                github_user_id: 4,
-                invite_count: 5,
-            },
-        )
-        .await
-        .unwrap()
-        .unwrap()
-        .user_id;
-
-    let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
-    assert_eq!(invite_count, 1);
-    assert_eq!(
-        db.get_contacts(user1).await.unwrap(),
-        [
-            Contact::Accepted {
-                user_id: user2,
-                should_notify: true,
-                busy: false,
-            },
-            Contact::Accepted {
-                user_id: user3,
-                should_notify: true,
-                busy: false,
-            },
-            Contact::Accepted {
-                user_id: user4,
-                should_notify: true,
-                busy: false,
-            }
-        ]
-    );
-    assert_eq!(
-        db.get_contacts(user4).await.unwrap(),
-        [Contact::Accepted {
-            user_id: user1,
-            should_notify: false,
-            busy: false,
-        }]
-    );
-    assert!(db.has_contact(user1, user4).await.unwrap());
-    assert!(db.has_contact(user4, user1).await.unwrap());
-    assert_eq!(
-        db.get_invite_code_for_user(user4).await.unwrap().unwrap().1,
-        5
-    );
-
-    // An existing user cannot redeem invite codes.
-    db.create_invite_from_code(
-        &invite_code,
-        "user2@example.com",
-        Some("user-2-device-id"),
-        true,
-    )
-    .await
-    .unwrap_err();
-    let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
-    assert_eq!(invite_count, 1);
-
-    // A newer user can invite an existing one via a different email address
-    // than the one they used to sign up.
-    let user5 = db
-        .create_user(
-            "user5@example.com",
-            false,
-            NewUserParams {
-                github_login: "user5".into(),
-                github_user_id: 5,
-                invite_count: 0,
-            },
-        )
-        .await
-        .unwrap()
-        .user_id;
-    db.set_invite_count_for_user(user5, 5).await.unwrap();
-    let (user5_invite_code, _) = db.get_invite_code_for_user(user5).await.unwrap().unwrap();
-    let user5_invite_to_user1 = db
-        .create_invite_from_code(&user5_invite_code, "user1@different.com", None, true)
-        .await
-        .unwrap();
-    let user1_2 = db
-        .create_user_from_invite(
-            &user5_invite_to_user1,
-            NewUserParams {
-                github_login: "user1".into(),
-                github_user_id: 1,
-                invite_count: 5,
-            },
-        )
-        .await
-        .unwrap()
-        .unwrap()
-        .user_id;
-    assert_eq!(user1_2, user1);
-    assert_eq!(
-        db.get_contacts(user1).await.unwrap(),
-        [
-            Contact::Accepted {
-                user_id: user2,
-                should_notify: true,
-                busy: false,
-            },
-            Contact::Accepted {
-                user_id: user3,
-                should_notify: true,
-                busy: false,
-            },
-            Contact::Accepted {
-                user_id: user4,
-                should_notify: true,
-                busy: false,
-            },
-            Contact::Accepted {
-                user_id: user5,
-                should_notify: false,
-                busy: false,
-            }
-        ]
-    );
-    assert_eq!(
-        db.get_contacts(user5).await.unwrap(),
-        [Contact::Accepted {
-            user_id: user1,
-            should_notify: true,
-            busy: false,
-        }]
-    );
-    assert!(db.has_contact(user1, user5).await.unwrap());
-    assert!(db.has_contact(user5, user1).await.unwrap());
-}
-
 test_both_dbs!(test_channels, test_channels_postgres, test_channels_sqlite);
 
 async fn test_channels(db: &Arc<Database>) {
@@ -1329,245 +1027,6 @@ async fn test_channel_renames(db: &Arc<Database>) {
     assert!(bad_name_rename.is_err())
 }
 
-#[gpui::test]
-async fn test_multiple_signup_overwrite() {
-    let test_db = TestDb::postgres(build_background_executor());
-    let db = test_db.db();
-
-    let email_address = "user_1@example.com".to_string();
-
-    let initial_signup_created_at_milliseconds = 0;
-
-    let initial_signup = NewSignup {
-        email_address: email_address.clone(),
-        platform_mac: false,
-        platform_linux: true,
-        platform_windows: false,
-        editor_features: vec!["speed".into()],
-        programming_languages: vec!["rust".into(), "c".into()],
-        device_id: Some(format!("device_id")),
-        added_to_mailing_list: false,
-        created_at: Some(
-            DateTime::from_timestamp_millis(initial_signup_created_at_milliseconds).unwrap(),
-        ),
-    };
-
-    db.create_signup(&initial_signup).await.unwrap();
-
-    let initial_signup_from_db = db.get_signup(&email_address).await.unwrap();
-
-    assert_eq!(
-        initial_signup_from_db.clone(),
-        signup::Model {
-            email_address: initial_signup.email_address,
-            platform_mac: initial_signup.platform_mac,
-            platform_linux: initial_signup.platform_linux,
-            platform_windows: initial_signup.platform_windows,
-            editor_features: Some(initial_signup.editor_features),
-            programming_languages: Some(initial_signup.programming_languages),
-            added_to_mailing_list: initial_signup.added_to_mailing_list,
-            ..initial_signup_from_db
-        }
-    );
-
-    let subsequent_signup = NewSignup {
-        email_address: email_address.clone(),
-        platform_mac: true,
-        platform_linux: false,
-        platform_windows: true,
-        editor_features: vec!["git integration".into(), "clean design".into()],
-        programming_languages: vec!["d".into(), "elm".into()],
-        device_id: Some(format!("different_device_id")),
-        added_to_mailing_list: true,
-        // subsequent signup happens next day
-        created_at: Some(
-            DateTime::from_timestamp_millis(
-                initial_signup_created_at_milliseconds + (1000 * 60 * 60 * 24),
-            )
-            .unwrap(),
-        ),
-    };
-
-    db.create_signup(&subsequent_signup).await.unwrap();
-
-    let subsequent_signup_from_db = db.get_signup(&email_address).await.unwrap();
-
-    assert_eq!(
-        subsequent_signup_from_db.clone(),
-        signup::Model {
-            platform_mac: subsequent_signup.platform_mac,
-            platform_linux: subsequent_signup.platform_linux,
-            platform_windows: subsequent_signup.platform_windows,
-            editor_features: Some(subsequent_signup.editor_features),
-            programming_languages: Some(subsequent_signup.programming_languages),
-            device_id: subsequent_signup.device_id,
-            added_to_mailing_list: subsequent_signup.added_to_mailing_list,
-            // shouldn't overwrite their creation Datetime - user shouldn't lose their spot in line
-            created_at: initial_signup_from_db.created_at,
-            ..subsequent_signup_from_db
-        }
-    );
-}
-
-#[gpui::test]
-async fn test_signups() {
-    let test_db = TestDb::postgres(build_background_executor());
-    let db = test_db.db();
-
-    let usernames = (0..8).map(|i| format!("person-{i}")).collect::<Vec<_>>();
-
-    let all_signups = usernames
-        .iter()
-        .enumerate()
-        .map(|(i, username)| NewSignup {
-            email_address: format!("{username}@example.com"),
-            platform_mac: true,
-            platform_linux: i % 2 == 0,
-            platform_windows: i % 4 == 0,
-            editor_features: vec!["speed".into()],
-            programming_languages: vec!["rust".into(), "c".into()],
-            device_id: Some(format!("device_id_{i}")),
-            added_to_mailing_list: i != 0, // One user failed to subscribe
-            created_at: Some(DateTime::from_timestamp_millis(i as i64).unwrap()), // Signups are consecutive
-        })
-        .collect::<Vec<NewSignup>>();
-
-    // people sign up on the waitlist
-    for signup in &all_signups {
-        // users can sign up multiple times without issues
-        for _ in 0..2 {
-            db.create_signup(&signup).await.unwrap();
-        }
-    }
-
-    assert_eq!(
-        db.get_waitlist_summary().await.unwrap(),
-        WaitlistSummary {
-            count: 8,
-            mac_count: 8,
-            linux_count: 4,
-            windows_count: 2,
-            unknown_count: 0,
-        }
-    );
-
-    // retrieve the next batch of signup emails to send
-    let signups_batch1 = db.get_unsent_invites(3).await.unwrap();
-    let addresses = signups_batch1
-        .iter()
-        .map(|s| &s.email_address)
-        .collect::<Vec<_>>();
-    assert_eq!(
-        addresses,
-        &[
-            all_signups[0].email_address.as_str(),
-            all_signups[1].email_address.as_str(),
-            all_signups[2].email_address.as_str()
-        ]
-    );
-    assert_ne!(
-        signups_batch1[0].email_confirmation_code,
-        signups_batch1[1].email_confirmation_code
-    );
-
-    // the waitlist isn't updated until we record that the emails
-    // were successfully sent.
-    let signups_batch = db.get_unsent_invites(3).await.unwrap();
-    assert_eq!(signups_batch, signups_batch1);
-
-    // once the emails go out, we can retrieve the next batch
-    // of signups.
-    db.record_sent_invites(&signups_batch1).await.unwrap();
-    let signups_batch2 = db.get_unsent_invites(3).await.unwrap();
-    let addresses = signups_batch2
-        .iter()
-        .map(|s| &s.email_address)
-        .collect::<Vec<_>>();
-    assert_eq!(
-        addresses,
-        &[
-            all_signups[3].email_address.as_str(),
-            all_signups[4].email_address.as_str(),
-            all_signups[5].email_address.as_str()
-        ]
-    );
-
-    // 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,
-            unknown_count: 0,
-        }
-    );
-
-    // user completes the signup process by providing their
-    // github account.
-    let NewUserResult {
-        user_id,
-        inviting_user_id,
-        signup_device_id,
-        ..
-    } = db
-        .create_user_from_invite(
-            &Invite {
-                ..signups_batch1[0].clone()
-            },
-            NewUserParams {
-                github_login: usernames[0].clone(),
-                github_user_id: 0,
-                invite_count: 5,
-            },
-        )
-        .await
-        .unwrap()
-        .unwrap();
-    let user = db.get_user_by_id(user_id).await.unwrap().unwrap();
-    assert!(inviting_user_id.is_none());
-    assert_eq!(user.github_login, usernames[0]);
-    assert_eq!(
-        user.email_address,
-        Some(all_signups[0].email_address.clone())
-    );
-    assert_eq!(user.invite_count, 5);
-    assert_eq!(signup_device_id.unwrap(), "device_id_0");
-
-    // cannot redeem the same signup again.
-    assert!(db
-        .create_user_from_invite(
-            &Invite {
-                email_address: signups_batch1[0].email_address.clone(),
-                email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(),
-            },
-            NewUserParams {
-                github_login: "some-other-github_account".into(),
-                github_user_id: 1,
-                invite_count: 5,
-            },
-        )
-        .await
-        .unwrap()
-        .is_none());
-
-    // cannot redeem a signup with the wrong confirmation code.
-    db.create_user_from_invite(
-        &Invite {
-            email_address: signups_batch1[1].email_address.clone(),
-            email_confirmation_code: "the-wrong-code".to_string(),
-        },
-        NewUserParams {
-            github_login: usernames[1].clone(),
-            github_user_id: 2,
-            invite_count: 5,
-        },
-    )
-    .await
-    .unwrap_err();
-}
-
 fn build_background_executor() -> Arc<Background> {
     Deterministic::new(0).build_background()
 }

crates/collab/src/rpc.rs 🔗

@@ -553,9 +553,8 @@ impl Server {
                 this.app_state.db.set_user_connected_once(user_id, true).await?;
             }
 
-            let (contacts, invite_code, channels_for_user, channel_invites) = future::try_join4(
+            let (contacts, channels_for_user, channel_invites) = future::try_join3(
                 this.app_state.db.get_contacts(user_id),
-                this.app_state.db.get_invite_code_for_user(user_id),
                 this.app_state.db.get_channels_for_user(user_id),
                 this.app_state.db.get_channel_invites_for_user(user_id)
             ).await?;
@@ -568,13 +567,6 @@ impl Server {
                     channels_for_user,
                     channel_invites
                 ))?;
-
-                if let Some((code, count)) = invite_code {
-                    this.peer.send(connection_id, proto::UpdateInviteInfo {
-                        url: format!("{}{}", this.app_state.config.invite_link_prefix, code),
-                        count: count as u32,
-                    })?;
-                }
             }
 
             if let Some(incoming_call) = this.app_state.db.incoming_call_for_user(user_id).await? {

crates/collab/src/tests/integration_tests.rs 🔗

@@ -3146,6 +3146,7 @@ async fn test_local_settings(
         )
         .await;
     let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
+    deterministic.run_until_parked();
     let project_id = active_call_a
         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
         .await