@@ -2736,6 +2736,16 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "lipsum"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ee7271c76a89032dcc7e595c0a739a9c5514eab483deb0e82981fe2098c56a"
+dependencies = [
+ "rand 0.8.3",
+ "rand_chacha 0.3.0",
+]
+
[[package]]
name = "lock_api"
version = "0.4.2"
@@ -5872,6 +5882,7 @@ dependencies = [
"http-auth-basic",
"jwt-simple",
"lazy_static",
+ "lipsum",
"oauth2",
"oauth2-surf",
"parking_lot",
@@ -5,6 +5,10 @@ edition = "2018"
name = "zed-server"
version = "0.1.0"
+[[bin]]
+name = "seed"
+required-features = ["seed-support"]
+
[dependencies]
anyhow = "1.0.40"
async-std = { version = "1.8.0", features = ["attributes"] }
@@ -19,6 +23,7 @@ futures = "0.3"
handlebars = "3.5"
http-auth-basic = "0.1.3"
jwt-simple = "0.10.0"
+lipsum = { version = "0.8", optional = true }
oauth2 = { version = "4.0.0", default_features = false }
oauth2-surf = "0.1.1"
parking_lot = "0.11.1"
@@ -46,6 +51,9 @@ features = ["runtime-async-std-rustls", "postgres", "time"]
[dev-dependencies]
gpui = { path = "../gpui" }
-zed = { path = "../zed", features = ["test-support"] }
lazy_static = "1.4"
serde_json = { version = "1.0.64", features = ["preserve_order"] }
+zed = { path = "../zed", features = ["test-support"] }
+
+[features]
+seed-support = ["lipsum"]
@@ -1,6 +1,11 @@
-use sqlx::postgres::PgPoolOptions;
+use db::{Db, UserId};
+use rand::prelude::*;
use tide::log;
+use time::{Duration, OffsetDateTime};
+#[allow(unused)]
+#[path = "../db.rs"]
+mod db;
#[path = "../env.rs"]
mod env;
@@ -13,95 +18,74 @@ async fn main() {
);
}
+ let mut rng = StdRng::from_entropy();
let database_url = std::env::var("DATABASE_URL").expect("missing DATABASE_URL env var");
- let db = PgPoolOptions::new()
- .max_connections(5)
- .connect(&database_url)
+ let db = Db::new(&database_url, 5)
.await
.expect("failed to connect to postgres database");
let zed_users = ["nathansobo", "maxbrunsfeld", "as-cii", "iamnbutler"];
- let mut zed_user_ids = Vec::<i32>::new();
+ let mut zed_user_ids = Vec::<UserId>::new();
for zed_user in zed_users {
- zed_user_ids.push(
- sqlx::query_scalar(
- r#"
- INSERT INTO users
- (github_login, admin)
- VALUES
- ($1, true)
- ON CONFLICT (github_login) DO UPDATE SET
- github_login=EXCLUDED.github_login
- RETURNING id
- "#,
- )
- .bind(zed_user)
- .fetch_one(&db)
- .await
- .expect("failed to insert user"),
- )
+ if let Some(user_id) = db.get_user(zed_user).await.expect("failed to fetch user") {
+ zed_user_ids.push(user_id);
+ } else {
+ zed_user_ids.push(
+ db.create_user(zed_user, true)
+ .await
+ .expect("failed to insert user"),
+ );
+ }
}
- let zed_org_id: i32 = sqlx::query_scalar(
- r#"
- INSERT INTO orgs
- (name, slug)
- VALUES
- ('Zed', 'zed')
- ON CONFLICT (slug) DO UPDATE SET
- slug=EXCLUDED.slug
- RETURNING id
- "#,
- )
- .fetch_one(&db)
- .await
- .expect("failed to insert org");
-
- let general_channel_id: i32 = sqlx::query_scalar(
- r#"
- INSERT INTO channels
- (owner_is_user, owner_id, name)
- VALUES
- (false, $1, 'General')
- ON CONFLICT (owner_is_user, owner_id, name) DO UPDATE SET
- name=EXCLUDED.name
- RETURNING id
- "#,
- )
- .bind(zed_org_id)
- .fetch_one(&db)
- .await
- .expect("failed to insert channel");
-
- for user_id in zed_user_ids {
- sqlx::query(
- r#"
- INSERT INTO org_memberships
- (org_id, user_id, admin)
- VALUES
- ($1, $2, true)
- ON CONFLICT DO NOTHING
- "#,
- )
- .bind(zed_org_id)
- .bind(user_id)
- .execute(&db)
+ let zed_org_id = if let Some(org) = db
+ .find_org_by_slug("zed")
.await
- .expect("failed to insert org membership");
+ .expect("failed to fetch org")
+ {
+ org.id
+ } else {
+ db.create_org("Zed", "zed")
+ .await
+ .expect("failed to insert org")
+ };
- sqlx::query(
- r#"
- INSERT INTO channel_memberships
- (channel_id, user_id, admin)
- VALUES
- ($1, $2, true)
- ON CONFLICT DO NOTHING
- "#,
- )
- .bind(general_channel_id)
- .bind(user_id)
- .execute(&db)
+ let general_channel_id = if let Some(channel) = db
+ .get_org_channels(zed_org_id)
.await
- .expect("failed to insert channel membership");
+ .expect("failed to fetch channels")
+ .iter()
+ .find(|c| c.name == "General")
+ {
+ channel.id
+ } else {
+ let channel_id = db
+ .create_org_channel(zed_org_id, "General")
+ .await
+ .expect("failed to insert channel");
+
+ let now = OffsetDateTime::now_utc();
+ let max_seconds = Duration::days(100).as_seconds_f64();
+ let mut timestamps = (0..1000)
+ .map(|_| now - Duration::seconds_f64(rng.gen_range(0_f64..=max_seconds)))
+ .collect::<Vec<_>>();
+ timestamps.sort();
+ for timestamp in timestamps {
+ let sender_id = *zed_user_ids.choose(&mut rng).unwrap();
+ let body = lipsum::lipsum_words(rng.gen_range(1..=50));
+ db.create_channel_message(channel_id, sender_id, &body, timestamp)
+ .await
+ .expect("failed to insert message");
+ }
+ channel_id
+ };
+
+ for user_id in zed_user_ids {
+ db.add_org_member(zed_org_id, user_id, true)
+ .await
+ .expect("failed to insert org membership");
+ db.add_channel_member(general_channel_id, user_id, true)
+ .await
+ .expect("failed to insert channel membership");
}
}
@@ -27,35 +27,6 @@ pub struct Db {
test_mode: bool,
}
-#[derive(Debug, FromRow, Serialize)]
-pub struct User {
- pub id: UserId,
- pub github_login: String,
- pub admin: bool,
-}
-
-#[derive(Debug, FromRow, Serialize)]
-pub struct Signup {
- pub id: SignupId,
- pub github_login: String,
- pub email_address: String,
- pub about: String,
-}
-
-#[derive(Debug, FromRow, Serialize)]
-pub struct Channel {
- pub id: ChannelId,
- pub name: String,
-}
-
-#[derive(Debug, FromRow)]
-pub struct ChannelMessage {
- pub id: MessageId,
- pub sender_id: UserId,
- pub body: String,
- pub sent_at: OffsetDateTime,
-}
-
impl Db {
pub async fn new(url: &str, max_connections: u32) -> tide::Result<Self> {
let pool = DbOptions::new()
@@ -113,6 +84,22 @@ impl Db {
// users
+ #[allow(unused)] // Help rust-analyzer
+ #[cfg(any(test, feature = "seed-support"))]
+ pub async fn get_user(&self, github_login: &str) -> Result<Option<UserId>> {
+ test_support!(self, {
+ let query = "
+ SELECT id
+ FROM users
+ WHERE github_login = $1
+ ";
+ sqlx::query_scalar(query)
+ .bind(github_login)
+ .fetch_optional(&self.pool)
+ .await
+ })
+ }
+
pub async fn create_user(&self, github_login: &str, admin: bool) -> Result<UserId> {
test_support!(self, {
let query = "
@@ -231,7 +218,23 @@ impl Db {
// orgs
- #[cfg(test)]
+ #[allow(unused)] // Help rust-analyzer
+ #[cfg(any(test, feature = "seed-support"))]
+ pub async fn find_org_by_slug(&self, slug: &str) -> Result<Option<Org>> {
+ test_support!(self, {
+ let query = "
+ SELECT *
+ FROM orgs
+ WHERE slug = $1
+ ";
+ sqlx::query_as(query)
+ .bind(slug)
+ .fetch_optional(&self.pool)
+ .await
+ })
+ }
+
+ #[cfg(any(test, feature = "seed-support"))]
pub async fn create_org(&self, name: &str, slug: &str) -> Result<OrgId> {
test_support!(self, {
let query = "
@@ -248,7 +251,7 @@ impl Db {
})
}
- #[cfg(test)]
+ #[cfg(any(test, feature = "seed-support"))]
pub async fn add_org_member(
&self,
org_id: OrgId,
@@ -259,6 +262,7 @@ impl Db {
let query = "
INSERT INTO org_memberships (org_id, user_id, admin)
VALUES ($1, $2, $3)
+ ON CONFLICT DO NOTHING
";
sqlx::query(query)
.bind(org_id.0)
@@ -272,7 +276,7 @@ impl Db {
// channels
- #[cfg(test)]
+ #[cfg(any(test, feature = "seed-support"))]
pub async fn create_org_channel(&self, org_id: OrgId, name: &str) -> Result<ChannelId> {
test_support!(self, {
let query = "
@@ -289,7 +293,25 @@ impl Db {
})
}
- pub async fn get_channels_for_user(&self, user_id: UserId) -> Result<Vec<Channel>> {
+ #[allow(unused)] // Help rust-analyzer
+ #[cfg(any(test, feature = "seed-support"))]
+ pub async fn get_org_channels(&self, org_id: OrgId) -> Result<Vec<Channel>> {
+ test_support!(self, {
+ let query = "
+ SELECT *
+ FROM channels
+ WHERE
+ channels.owner_is_user = false AND
+ channels.owner_id = $1
+ ";
+ sqlx::query_as(query)
+ .bind(org_id.0)
+ .fetch_all(&self.pool)
+ .await
+ })
+ }
+
+ pub async fn get_accessible_channels(&self, user_id: UserId) -> Result<Vec<Channel>> {
test_support!(self, {
let query = "
SELECT
@@ -328,7 +350,7 @@ impl Db {
})
}
- #[cfg(test)]
+ #[cfg(any(test, feature = "seed-support"))]
pub async fn add_channel_member(
&self,
channel_id: ChannelId,
@@ -339,6 +361,7 @@ impl Db {
let query = "
INSERT INTO channel_memberships (channel_id, user_id, admin)
VALUES ($1, $2, $3)
+ ON CONFLICT DO NOTHING
";
sqlx::query(query)
.bind(channel_id.0)
@@ -432,10 +455,45 @@ macro_rules! id_type {
}
id_type!(UserId);
+#[derive(Debug, FromRow, Serialize)]
+pub struct User {
+ pub id: UserId,
+ pub github_login: String,
+ pub admin: bool,
+}
+
id_type!(OrgId);
-id_type!(ChannelId);
+#[derive(FromRow)]
+pub struct Org {
+ pub id: OrgId,
+ pub name: String,
+ pub slug: String,
+}
+
id_type!(SignupId);
+#[derive(Debug, FromRow, Serialize)]
+pub struct Signup {
+ pub id: SignupId,
+ pub github_login: String,
+ pub email_address: String,
+ pub about: String,
+}
+
+id_type!(ChannelId);
+#[derive(Debug, FromRow, Serialize)]
+pub struct Channel {
+ pub id: ChannelId,
+ pub name: String,
+}
+
id_type!(MessageId);
+#[derive(Debug, FromRow)]
+pub struct ChannelMessage {
+ pub id: MessageId,
+ pub sender_id: UserId,
+ pub body: String,
+ pub sent_at: OffsetDateTime,
+}
#[cfg(test)]
pub mod tests {