diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index b457c4c116..f676e01339 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1,56 +1,47 @@ -mod access_token; -mod channel; -mod channel_member; -mod channel_path; -mod contact; -mod follower; -mod language_server; -mod project; -mod project_collaborator; -mod room; -mod room_participant; -mod server; -mod signup; #[cfg(test)] -mod tests; -mod user; -mod worktree; -mod worktree_diagnostic_summary; -mod worktree_entry; -mod worktree_repository; -mod worktree_repository_statuses; -mod worktree_settings_file; +mod db_tests; +mod ids; +mod tables; +#[cfg(test)] +pub mod test_db; use crate::executor::Executor; use crate::{Error, Result}; use anyhow::anyhow; use collections::{BTreeMap, HashMap, HashSet}; -pub use contact::Contact; use dashmap::DashMap; use futures::StreamExt; use hyper::StatusCode; use rand::prelude::StdRng; use rand::{Rng, SeedableRng}; use rpc::{proto, ConnectionId}; -use sea_orm::Condition; -pub use sea_orm::ConnectOptions; use sea_orm::{ - entity::prelude::*, ActiveValue, ConnectionTrait, DatabaseConnection, DatabaseTransaction, - DbErr, FromQueryResult, IntoActiveModel, IsolationLevel, JoinType, QueryOrder, QuerySelect, - Statement, TransactionTrait, + entity::prelude::*, ActiveValue, Condition, ConnectionTrait, DatabaseConnection, + DatabaseTransaction, DbErr, FromQueryResult, IntoActiveModel, IsolationLevel, JoinType, + QueryOrder, QuerySelect, Statement, TransactionTrait, }; use sea_query::{Alias, Expr, OnConflict, Query}; use serde::{Deserialize, Serialize}; -pub use signup::{Invite, NewSignup, WaitlistSummary}; -use sqlx::migrate::{Migrate, Migration, MigrationSource}; -use sqlx::Connection; -use std::fmt::Write as _; -use std::ops::{Deref, DerefMut}; -use std::path::Path; -use std::time::Duration; -use std::{future::Future, marker::PhantomData, rc::Rc, sync::Arc}; +use sqlx::{ + migrate::{Migrate, Migration, MigrationSource}, + Connection, +}; +use std::{ + fmt::Write as _, + future::Future, + marker::PhantomData, + ops::{Deref, DerefMut}, + path::Path, + rc::Rc, + sync::Arc, + time::Duration, +}; +use tables::*; use tokio::sync::{Mutex, OwnedMutexGuard}; -pub use user::Model as User; + +pub use ids::*; +pub use sea_orm::ConnectOptions; +pub use tables::user::Model as User; pub struct Database { options: ConnectOptions, @@ -4083,6 +4074,60 @@ impl RoomGuard { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Contact { + Accepted { + user_id: UserId, + should_notify: bool, + busy: bool, + }, + Outgoing { + user_id: UserId, + }, + Incoming { + user_id: UserId, + should_notify: bool, + }, +} + +impl Contact { + pub fn user_id(&self) -> UserId { + match self { + Contact::Accepted { user_id, .. } => *user_id, + Contact::Outgoing { user_id } => *user_id, + Contact::Incoming { user_id, .. } => *user_id, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)] +pub struct Invite { + pub email_address: String, + pub email_confirmation_code: String, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct NewSignup { + pub email_address: String, + pub platform_mac: bool, + pub platform_windows: bool, + pub platform_linux: bool, + pub editor_features: Vec, + pub programming_languages: Vec, + pub device_id: Option, + pub added_to_mailing_list: bool, + pub created_at: Option, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, FromQueryResult)] +pub struct WaitlistSummary { + pub count: i64, + pub linux_count: i64, + pub mac_count: i64, + pub windows_count: i64, + pub unknown_count: i64, +} + #[derive(Debug, Serialize, Deserialize)] pub struct NewUserParams { pub github_login: String, @@ -4120,139 +4165,6 @@ fn random_email_confirmation_code() -> String { nanoid::nanoid!(64) } -macro_rules! id_type { - ($name:ident) => { - #[derive( - Clone, - Copy, - Debug, - Default, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Serialize, - Deserialize, - )] - #[serde(transparent)] - pub struct $name(pub i32); - - impl $name { - #[allow(unused)] - pub const MAX: Self = Self(i32::MAX); - - #[allow(unused)] - pub fn from_proto(value: u64) -> Self { - Self(value as i32) - } - - #[allow(unused)] - pub fn to_proto(self) -> u64 { - self.0 as u64 - } - } - - impl std::fmt::Display for $name { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - self.0.fmt(f) - } - } - - impl From<$name> for sea_query::Value { - fn from(value: $name) -> Self { - sea_query::Value::Int(Some(value.0)) - } - } - - impl sea_orm::TryGetable for $name { - fn try_get( - res: &sea_orm::QueryResult, - pre: &str, - col: &str, - ) -> Result { - Ok(Self(i32::try_get(res, pre, col)?)) - } - } - - impl sea_query::ValueType for $name { - fn try_from(v: Value) -> Result { - match v { - Value::TinyInt(Some(int)) => { - Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?)) - } - Value::SmallInt(Some(int)) => { - Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?)) - } - Value::Int(Some(int)) => { - Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?)) - } - Value::BigInt(Some(int)) => { - Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?)) - } - Value::TinyUnsigned(Some(int)) => { - Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?)) - } - Value::SmallUnsigned(Some(int)) => { - Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?)) - } - Value::Unsigned(Some(int)) => { - Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?)) - } - Value::BigUnsigned(Some(int)) => { - Ok(Self(int.try_into().map_err(|_| sea_query::ValueTypeErr)?)) - } - _ => Err(sea_query::ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!($name).into() - } - - fn array_type() -> sea_query::ArrayType { - sea_query::ArrayType::Int - } - - fn column_type() -> sea_query::ColumnType { - sea_query::ColumnType::Integer(None) - } - } - - impl sea_orm::TryFromU64 for $name { - fn try_from_u64(n: u64) -> Result { - Ok(Self(n.try_into().map_err(|_| { - DbErr::ConvertFromU64(concat!( - "error converting ", - stringify!($name), - " to u64" - )) - })?)) - } - } - - impl sea_query::Nullable for $name { - fn null() -> Value { - Value::Int(None) - } - } - }; -} - -id_type!(AccessTokenId); -id_type!(ChannelId); -id_type!(ChannelMemberId); -id_type!(ContactId); -id_type!(FollowerId); -id_type!(RoomId); -id_type!(RoomParticipantId); -id_type!(ProjectId); -id_type!(ProjectCollaboratorId); -id_type!(ReplicaId); -id_type!(ServerId); -id_type!(SignupId); -id_type!(UserId); - #[derive(Clone)] pub struct JoinRoom { pub room: proto::Room, @@ -4370,130 +4282,3 @@ pub struct WorktreeSettingsFile { enum QueryUserIds { UserId, } - -#[cfg(test)] -pub use test::*; - -#[cfg(test)] -mod test { - use super::*; - use gpui::executor::Background; - use parking_lot::Mutex; - use sea_orm::ConnectionTrait; - use sqlx::migrate::MigrateDatabase; - use std::sync::Arc; - - pub struct TestDb { - pub db: Option>, - pub connection: Option, - } - - impl TestDb { - pub fn sqlite(background: Arc) -> Self { - let url = format!("sqlite::memory:"); - let runtime = tokio::runtime::Builder::new_current_thread() - .enable_io() - .enable_time() - .build() - .unwrap(); - - let mut db = runtime.block_on(async { - let mut options = ConnectOptions::new(url); - options.max_connections(5); - let db = Database::new(options, Executor::Deterministic(background)) - .await - .unwrap(); - let sql = include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/migrations.sqlite/20221109000000_test_schema.sql" - )); - db.pool - .execute(sea_orm::Statement::from_string( - db.pool.get_database_backend(), - sql.into(), - )) - .await - .unwrap(); - db - }); - - db.runtime = Some(runtime); - - Self { - db: Some(Arc::new(db)), - connection: None, - } - } - - pub fn postgres(background: Arc) -> Self { - static LOCK: Mutex<()> = Mutex::new(()); - - let _guard = LOCK.lock(); - let mut rng = StdRng::from_entropy(); - let url = format!( - "postgres://postgres@localhost/zed-test-{}", - rng.gen::() - ); - let runtime = tokio::runtime::Builder::new_current_thread() - .enable_io() - .enable_time() - .build() - .unwrap(); - - let mut db = runtime.block_on(async { - sqlx::Postgres::create_database(&url) - .await - .expect("failed to create test db"); - let mut options = ConnectOptions::new(url); - options - .max_connections(5) - .idle_timeout(Duration::from_secs(0)); - let db = Database::new(options, Executor::Deterministic(background)) - .await - .unwrap(); - let migrations_path = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations"); - db.migrate(Path::new(migrations_path), false).await.unwrap(); - db - }); - - db.runtime = Some(runtime); - - Self { - db: Some(Arc::new(db)), - connection: None, - } - } - - pub fn db(&self) -> &Arc { - self.db.as_ref().unwrap() - } - } - - impl Drop for TestDb { - fn drop(&mut self) { - let db = self.db.take().unwrap(); - if let sea_orm::DatabaseBackend::Postgres = db.pool.get_database_backend() { - db.runtime.as_ref().unwrap().block_on(async { - use util::ResultExt; - let query = " - SELECT pg_terminate_backend(pg_stat_activity.pid) - FROM pg_stat_activity - WHERE - pg_stat_activity.datname = current_database() AND - pid <> pg_backend_pid(); - "; - db.pool - .execute(sea_orm::Statement::from_string( - db.pool.get_database_backend(), - query.into(), - )) - .await - .log_err(); - sqlx::Postgres::drop_database(db.options.get_url()) - .await - .log_err(); - }) - } - } - } -} diff --git a/crates/collab/src/db/tests.rs b/crates/collab/src/db/db_tests.rs similarity index 99% rename from crates/collab/src/db/tests.rs rename to crates/collab/src/db/db_tests.rs index dbbf162d12..8e9a80dbab 100644 --- a/crates/collab/src/db/tests.rs +++ b/crates/collab/src/db/db_tests.rs @@ -1,9 +1,8 @@ use super::*; use gpui::executor::{Background, Deterministic}; -use std::sync::Arc; - -#[cfg(test)] use pretty_assertions::{assert_eq, assert_ne}; +use std::sync::Arc; +use test_db::TestDb; macro_rules! test_both_dbs { ($postgres_test_name:ident, $sqlite_test_name:ident, $db:ident, $body:block) => { diff --git a/crates/collab/src/db/ids.rs b/crates/collab/src/db/ids.rs new file mode 100644 index 0000000000..514c973dad --- /dev/null +++ b/crates/collab/src/db/ids.rs @@ -0,0 +1,125 @@ +use crate::Result; +use sea_orm::DbErr; +use sea_query::{Value, ValueTypeErr}; +use serde::{Deserialize, Serialize}; + +macro_rules! id_type { + ($name:ident) => { + #[derive( + Clone, + Copy, + Debug, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + )] + #[serde(transparent)] + pub struct $name(pub i32); + + impl $name { + #[allow(unused)] + pub const MAX: Self = Self(i32::MAX); + + #[allow(unused)] + pub fn from_proto(value: u64) -> Self { + Self(value as i32) + } + + #[allow(unused)] + pub fn to_proto(self) -> u64 { + self.0 as u64 + } + } + + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } + } + + impl From<$name> for sea_query::Value { + fn from(value: $name) -> Self { + sea_query::Value::Int(Some(value.0)) + } + } + + impl sea_orm::TryGetable for $name { + fn try_get( + res: &sea_orm::QueryResult, + pre: &str, + col: &str, + ) -> Result { + Ok(Self(i32::try_get(res, pre, col)?)) + } + } + + impl sea_query::ValueType for $name { + fn try_from(v: Value) -> Result { + Ok(Self(value_to_integer(v)?)) + } + + fn type_name() -> String { + stringify!($name).into() + } + + fn array_type() -> sea_query::ArrayType { + sea_query::ArrayType::Int + } + + fn column_type() -> sea_query::ColumnType { + sea_query::ColumnType::Integer(None) + } + } + + impl sea_orm::TryFromU64 for $name { + fn try_from_u64(n: u64) -> Result { + Ok(Self(n.try_into().map_err(|_| { + DbErr::ConvertFromU64(concat!( + "error converting ", + stringify!($name), + " to u64" + )) + })?)) + } + } + + impl sea_query::Nullable for $name { + fn null() -> Value { + Value::Int(None) + } + } + }; +} + +fn value_to_integer(v: Value) -> Result { + match v { + Value::TinyInt(Some(int)) => int.try_into().map_err(|_| ValueTypeErr), + Value::SmallInt(Some(int)) => int.try_into().map_err(|_| ValueTypeErr), + Value::Int(Some(int)) => int.try_into().map_err(|_| ValueTypeErr), + Value::BigInt(Some(int)) => int.try_into().map_err(|_| ValueTypeErr), + Value::TinyUnsigned(Some(int)) => int.try_into().map_err(|_| ValueTypeErr), + Value::SmallUnsigned(Some(int)) => int.try_into().map_err(|_| ValueTypeErr), + Value::Unsigned(Some(int)) => int.try_into().map_err(|_| ValueTypeErr), + Value::BigUnsigned(Some(int)) => int.try_into().map_err(|_| ValueTypeErr), + _ => Err(ValueTypeErr), + } +} + +id_type!(AccessTokenId); +id_type!(ChannelId); +id_type!(ChannelMemberId); +id_type!(ContactId); +id_type!(FollowerId); +id_type!(RoomId); +id_type!(RoomParticipantId); +id_type!(ProjectId); +id_type!(ProjectCollaboratorId); +id_type!(ReplicaId); +id_type!(ServerId); +id_type!(SignupId); +id_type!(UserId); diff --git a/crates/collab/src/db/signup.rs b/crates/collab/src/db/signup.rs deleted file mode 100644 index 6368482de9..0000000000 --- a/crates/collab/src/db/signup.rs +++ /dev/null @@ -1,57 +0,0 @@ -use super::{SignupId, UserId}; -use sea_orm::{entity::prelude::*, FromQueryResult}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] -#[sea_orm(table_name = "signups")] -pub struct Model { - #[sea_orm(primary_key)] - pub id: SignupId, - pub email_address: String, - pub email_confirmation_code: String, - pub email_confirmation_sent: bool, - pub created_at: DateTime, - pub device_id: Option, - pub user_id: Option, - pub inviting_user_id: Option, - pub platform_mac: bool, - pub platform_linux: bool, - pub platform_windows: bool, - pub platform_unknown: bool, - pub editor_features: Option>, - pub programming_languages: Option>, - pub added_to_mailing_list: bool, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} - -impl ActiveModelBehavior for ActiveModel {} - -#[derive(Clone, Debug, PartialEq, Eq, FromQueryResult, Serialize, Deserialize)] -pub struct Invite { - pub email_address: String, - pub email_confirmation_code: String, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct NewSignup { - pub email_address: String, - pub platform_mac: bool, - pub platform_windows: bool, - pub platform_linux: bool, - pub editor_features: Vec, - pub programming_languages: Vec, - pub device_id: Option, - pub added_to_mailing_list: bool, - pub created_at: Option, -} - -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, FromQueryResult)] -pub struct WaitlistSummary { - pub count: i64, - pub linux_count: i64, - pub mac_count: i64, - pub windows_count: i64, - pub unknown_count: i64, -} diff --git a/crates/collab/src/db/tables.rs b/crates/collab/src/db/tables.rs new file mode 100644 index 0000000000..c4c7e4f312 --- /dev/null +++ b/crates/collab/src/db/tables.rs @@ -0,0 +1,20 @@ +pub mod access_token; +pub mod channel; +pub mod channel_member; +pub mod channel_path; +pub mod contact; +pub mod follower; +pub mod language_server; +pub mod project; +pub mod project_collaborator; +pub mod room; +pub mod room_participant; +pub mod server; +pub mod signup; +pub mod user; +pub mod worktree; +pub mod worktree_diagnostic_summary; +pub mod worktree_entry; +pub mod worktree_repository; +pub mod worktree_repository_statuses; +pub mod worktree_settings_file; diff --git a/crates/collab/src/db/access_token.rs b/crates/collab/src/db/tables/access_token.rs similarity index 94% rename from crates/collab/src/db/access_token.rs rename to crates/collab/src/db/tables/access_token.rs index f5caa4843d..da7392b98c 100644 --- a/crates/collab/src/db/access_token.rs +++ b/crates/collab/src/db/tables/access_token.rs @@ -1,4 +1,4 @@ -use super::{AccessTokenId, UserId}; +use crate::db::{AccessTokenId, UserId}; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/channel.rs b/crates/collab/src/db/tables/channel.rs similarity index 81% rename from crates/collab/src/db/channel.rs rename to crates/collab/src/db/tables/channel.rs index 8834190645..f00b4ced62 100644 --- a/crates/collab/src/db/channel.rs +++ b/crates/collab/src/db/tables/channel.rs @@ -1,4 +1,4 @@ -use super::ChannelId; +use crate::db::ChannelId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)] @@ -30,9 +30,3 @@ impl Related for Entity { Relation::Room.def() } } - -// impl Related for Entity { -// fn to() -> RelationDef { -// Relation::Follower.def() -// } -// } diff --git a/crates/collab/src/db/channel_member.rs b/crates/collab/src/db/tables/channel_member.rs similarity index 94% rename from crates/collab/src/db/channel_member.rs rename to crates/collab/src/db/tables/channel_member.rs index f0f1a852cb..ba3db5a155 100644 --- a/crates/collab/src/db/channel_member.rs +++ b/crates/collab/src/db/tables/channel_member.rs @@ -1,6 +1,4 @@ -use crate::db::channel_member; - -use super::{ChannelId, ChannelMemberId, UserId}; +use crate::db::{channel_member, ChannelId, ChannelMemberId, UserId}; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/channel_path.rs b/crates/collab/src/db/tables/channel_path.rs similarity index 93% rename from crates/collab/src/db/channel_path.rs rename to crates/collab/src/db/tables/channel_path.rs index 08ecbddb56..323f116dae 100644 --- a/crates/collab/src/db/channel_path.rs +++ b/crates/collab/src/db/tables/channel_path.rs @@ -1,4 +1,4 @@ -use super::ChannelId; +use crate::db::ChannelId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/contact.rs b/crates/collab/src/db/tables/contact.rs similarity index 59% rename from crates/collab/src/db/contact.rs rename to crates/collab/src/db/tables/contact.rs index c39d6643b3..38af8b782b 100644 --- a/crates/collab/src/db/contact.rs +++ b/crates/collab/src/db/tables/contact.rs @@ -1,4 +1,4 @@ -use super::{ContactId, UserId}; +use crate::db::{ContactId, UserId}; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)] @@ -30,29 +30,3 @@ pub enum Relation { } impl ActiveModelBehavior for ActiveModel {} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Contact { - Accepted { - user_id: UserId, - should_notify: bool, - busy: bool, - }, - Outgoing { - user_id: UserId, - }, - Incoming { - user_id: UserId, - should_notify: bool, - }, -} - -impl Contact { - pub fn user_id(&self) -> UserId { - match self { - Contact::Accepted { user_id, .. } => *user_id, - Contact::Outgoing { user_id } => *user_id, - Contact::Incoming { user_id, .. } => *user_id, - } - } -} diff --git a/crates/collab/src/db/follower.rs b/crates/collab/src/db/tables/follower.rs similarity index 93% rename from crates/collab/src/db/follower.rs rename to crates/collab/src/db/tables/follower.rs index f1243dc99e..ffd45434e9 100644 --- a/crates/collab/src/db/follower.rs +++ b/crates/collab/src/db/tables/follower.rs @@ -1,9 +1,8 @@ -use super::{FollowerId, ProjectId, RoomId, ServerId}; +use crate::db::{FollowerId, ProjectId, RoomId, ServerId}; use rpc::ConnectionId; use sea_orm::entity::prelude::*; -use serde::Serialize; -#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel, Serialize)] +#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "followers")] pub struct Model { #[sea_orm(primary_key)] diff --git a/crates/collab/src/db/language_server.rs b/crates/collab/src/db/tables/language_server.rs similarity index 96% rename from crates/collab/src/db/language_server.rs rename to crates/collab/src/db/tables/language_server.rs index d2c045c121..9ff8c75fc6 100644 --- a/crates/collab/src/db/language_server.rs +++ b/crates/collab/src/db/tables/language_server.rs @@ -1,4 +1,4 @@ -use super::ProjectId; +use crate::db::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/project.rs b/crates/collab/src/db/tables/project.rs similarity index 97% rename from crates/collab/src/db/project.rs rename to crates/collab/src/db/tables/project.rs index 5b1f9f8467..8c26836046 100644 --- a/crates/collab/src/db/project.rs +++ b/crates/collab/src/db/tables/project.rs @@ -1,4 +1,4 @@ -use super::{ProjectId, Result, RoomId, ServerId, UserId}; +use crate::db::{ProjectId, Result, RoomId, ServerId, UserId}; use anyhow::anyhow; use rpc::ConnectionId; use sea_orm::entity::prelude::*; diff --git a/crates/collab/src/db/project_collaborator.rs b/crates/collab/src/db/tables/project_collaborator.rs similarity index 92% rename from crates/collab/src/db/project_collaborator.rs rename to crates/collab/src/db/tables/project_collaborator.rs index 60b5f284e9..ac57befa63 100644 --- a/crates/collab/src/db/project_collaborator.rs +++ b/crates/collab/src/db/tables/project_collaborator.rs @@ -1,4 +1,4 @@ -use super::{ProjectCollaboratorId, ProjectId, ReplicaId, ServerId, UserId}; +use crate::db::{ProjectCollaboratorId, ProjectId, ReplicaId, ServerId, UserId}; use rpc::ConnectionId; use sea_orm::entity::prelude::*; diff --git a/crates/collab/src/db/room.rs b/crates/collab/src/db/tables/room.rs similarity index 97% rename from crates/collab/src/db/room.rs rename to crates/collab/src/db/tables/room.rs index c1624f0f2a..f72f7000a7 100644 --- a/crates/collab/src/db/room.rs +++ b/crates/collab/src/db/tables/room.rs @@ -1,4 +1,4 @@ -use super::{ChannelId, RoomId}; +use crate::db::{ChannelId, RoomId}; use sea_orm::entity::prelude::*; #[derive(Clone, Default, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/room_participant.rs b/crates/collab/src/db/tables/room_participant.rs similarity index 94% rename from crates/collab/src/db/room_participant.rs rename to crates/collab/src/db/tables/room_participant.rs index f939a3bfb8..537cac9f14 100644 --- a/crates/collab/src/db/room_participant.rs +++ b/crates/collab/src/db/tables/room_participant.rs @@ -1,4 +1,4 @@ -use super::{ProjectId, RoomId, RoomParticipantId, ServerId, UserId}; +use crate::db::{ProjectId, RoomId, RoomParticipantId, ServerId, UserId}; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/server.rs b/crates/collab/src/db/tables/server.rs similarity index 93% rename from crates/collab/src/db/server.rs rename to crates/collab/src/db/tables/server.rs index e3905f2448..ea847bdf74 100644 --- a/crates/collab/src/db/server.rs +++ b/crates/collab/src/db/tables/server.rs @@ -1,4 +1,4 @@ -use super::ServerId; +use crate::db::ServerId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/tables/signup.rs b/crates/collab/src/db/tables/signup.rs new file mode 100644 index 0000000000..79d9f0580c --- /dev/null +++ b/crates/collab/src/db/tables/signup.rs @@ -0,0 +1,28 @@ +use crate::db::{SignupId, UserId}; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "signups")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: SignupId, + pub email_address: String, + pub email_confirmation_code: String, + pub email_confirmation_sent: bool, + pub created_at: DateTime, + pub device_id: Option, + pub user_id: Option, + pub inviting_user_id: Option, + pub platform_mac: bool, + pub platform_linux: bool, + pub platform_windows: bool, + pub platform_unknown: bool, + pub editor_features: Option>, + pub programming_languages: Option>, + pub added_to_mailing_list: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/db/user.rs b/crates/collab/src/db/tables/user.rs similarity index 98% rename from crates/collab/src/db/user.rs rename to crates/collab/src/db/tables/user.rs index 2d0e2fdf0b..402b06c2a7 100644 --- a/crates/collab/src/db/user.rs +++ b/crates/collab/src/db/tables/user.rs @@ -1,4 +1,4 @@ -use super::UserId; +use crate::db::UserId; use sea_orm::entity::prelude::*; use serde::Serialize; diff --git a/crates/collab/src/db/worktree.rs b/crates/collab/src/db/tables/worktree.rs similarity index 97% rename from crates/collab/src/db/worktree.rs rename to crates/collab/src/db/tables/worktree.rs index fce72722db..46d9877dff 100644 --- a/crates/collab/src/db/worktree.rs +++ b/crates/collab/src/db/tables/worktree.rs @@ -1,4 +1,4 @@ -use super::ProjectId; +use crate::db::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/worktree_diagnostic_summary.rs b/crates/collab/src/db/tables/worktree_diagnostic_summary.rs similarity index 95% rename from crates/collab/src/db/worktree_diagnostic_summary.rs rename to crates/collab/src/db/tables/worktree_diagnostic_summary.rs index f3dd8083fb..5620ed255f 100644 --- a/crates/collab/src/db/worktree_diagnostic_summary.rs +++ b/crates/collab/src/db/tables/worktree_diagnostic_summary.rs @@ -1,4 +1,4 @@ -use super::ProjectId; +use crate::db::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/worktree_entry.rs b/crates/collab/src/db/tables/worktree_entry.rs similarity index 96% rename from crates/collab/src/db/worktree_entry.rs rename to crates/collab/src/db/tables/worktree_entry.rs index cf5090ab6d..81bf6e2d53 100644 --- a/crates/collab/src/db/worktree_entry.rs +++ b/crates/collab/src/db/tables/worktree_entry.rs @@ -1,4 +1,4 @@ -use super::ProjectId; +use crate::db::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/worktree_repository.rs b/crates/collab/src/db/tables/worktree_repository.rs similarity index 95% rename from crates/collab/src/db/worktree_repository.rs rename to crates/collab/src/db/tables/worktree_repository.rs index 116d7b3ed9..6f86ff0c2d 100644 --- a/crates/collab/src/db/worktree_repository.rs +++ b/crates/collab/src/db/tables/worktree_repository.rs @@ -1,4 +1,4 @@ -use super::ProjectId; +use crate::db::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/worktree_repository_statuses.rs b/crates/collab/src/db/tables/worktree_repository_statuses.rs similarity index 95% rename from crates/collab/src/db/worktree_repository_statuses.rs rename to crates/collab/src/db/tables/worktree_repository_statuses.rs index fc15efc816..cab016749d 100644 --- a/crates/collab/src/db/worktree_repository_statuses.rs +++ b/crates/collab/src/db/tables/worktree_repository_statuses.rs @@ -1,4 +1,4 @@ -use super::ProjectId; +use crate::db::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/worktree_settings_file.rs b/crates/collab/src/db/tables/worktree_settings_file.rs similarity index 94% rename from crates/collab/src/db/worktree_settings_file.rs rename to crates/collab/src/db/tables/worktree_settings_file.rs index f8e87f6e59..92348c1ec9 100644 --- a/crates/collab/src/db/worktree_settings_file.rs +++ b/crates/collab/src/db/tables/worktree_settings_file.rs @@ -1,4 +1,4 @@ -use super::ProjectId; +use crate::db::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] diff --git a/crates/collab/src/db/test_db.rs b/crates/collab/src/db/test_db.rs new file mode 100644 index 0000000000..064f85c700 --- /dev/null +++ b/crates/collab/src/db/test_db.rs @@ -0,0 +1,120 @@ +use super::*; +use gpui::executor::Background; +use parking_lot::Mutex; +use sea_orm::ConnectionTrait; +use sqlx::migrate::MigrateDatabase; +use std::sync::Arc; + +pub struct TestDb { + pub db: Option>, + pub connection: Option, +} + +impl TestDb { + pub fn sqlite(background: Arc) -> Self { + let url = format!("sqlite::memory:"); + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build() + .unwrap(); + + let mut db = runtime.block_on(async { + let mut options = ConnectOptions::new(url); + options.max_connections(5); + let db = Database::new(options, Executor::Deterministic(background)) + .await + .unwrap(); + let sql = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/migrations.sqlite/20221109000000_test_schema.sql" + )); + db.pool + .execute(sea_orm::Statement::from_string( + db.pool.get_database_backend(), + sql.into(), + )) + .await + .unwrap(); + db + }); + + db.runtime = Some(runtime); + + Self { + db: Some(Arc::new(db)), + connection: None, + } + } + + pub fn postgres(background: Arc) -> Self { + static LOCK: Mutex<()> = Mutex::new(()); + + let _guard = LOCK.lock(); + let mut rng = StdRng::from_entropy(); + let url = format!( + "postgres://postgres@localhost/zed-test-{}", + rng.gen::() + ); + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build() + .unwrap(); + + let mut db = runtime.block_on(async { + sqlx::Postgres::create_database(&url) + .await + .expect("failed to create test db"); + let mut options = ConnectOptions::new(url); + options + .max_connections(5) + .idle_timeout(Duration::from_secs(0)); + let db = Database::new(options, Executor::Deterministic(background)) + .await + .unwrap(); + let migrations_path = concat!(env!("CARGO_MANIFEST_DIR"), "/migrations"); + db.migrate(Path::new(migrations_path), false).await.unwrap(); + db + }); + + db.runtime = Some(runtime); + + Self { + db: Some(Arc::new(db)), + connection: None, + } + } + + pub fn db(&self) -> &Arc { + self.db.as_ref().unwrap() + } +} + +impl Drop for TestDb { + fn drop(&mut self) { + let db = self.db.take().unwrap(); + if let sea_orm::DatabaseBackend::Postgres = db.pool.get_database_backend() { + db.runtime.as_ref().unwrap().block_on(async { + use util::ResultExt; + let query = " + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE + pg_stat_activity.datname = current_database() AND + pid <> pg_backend_pid(); + "; + db.pool + .execute(sea_orm::Statement::from_string( + db.pool.get_database_backend(), + query.into(), + )) + .await + .log_err(); + sqlx::Postgres::drop_database(db.options.get_url()) + .await + .log_err(); + }) + } + } +} diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 46cbcb0213..c9f358ca5b 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -1,5 +1,5 @@ use crate::{ - db::{NewUserParams, TestDb, UserId}, + db::{test_db::TestDb, NewUserParams, UserId}, executor::Executor, rpc::{Server, CLEANUP_TIMEOUT}, AppState,