From dec5f37e4e4f13abb33cc5717f58390496bcf32c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 2 Dec 2022 13:58:23 +0100 Subject: [PATCH] Finish porting remaining db methods to sea-orm --- .../20221109000000_test_schema.sql | 2 + .../20221111092550_reconnection_support.sql | 6 +- crates/collab/src/db.rs | 687 ++++++++---------- crates/collab/src/db/language_server.rs | 30 + crates/collab/src/db/project.rs | 20 +- crates/collab/src/db/project_collaborator.rs | 15 +- crates/collab/src/db/worktree.rs | 6 +- .../src/db/worktree_diagnostic_summary.rs | 21 + crates/collab/src/db/worktree_entry.rs | 24 +- crates/collab/src/rpc.rs | 6 +- 10 files changed, 399 insertions(+), 418 deletions(-) create mode 100644 crates/collab/src/db/language_server.rs create mode 100644 crates/collab/src/db/worktree_diagnostic_summary.rs diff --git a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql index aeb6b7f720..e62f834fbf 100644 --- a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql +++ b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql @@ -72,6 +72,7 @@ CREATE TABLE "worktree_entries" ( PRIMARY KEY(project_id, worktree_id, id), FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE ); +CREATE INDEX "index_worktree_entries_on_project_id" ON "worktree_entries" ("project_id"); CREATE INDEX "index_worktree_entries_on_project_id_and_worktree_id" ON "worktree_entries" ("project_id", "worktree_id"); CREATE TABLE "worktree_diagnostic_summaries" ( @@ -84,6 +85,7 @@ CREATE TABLE "worktree_diagnostic_summaries" ( PRIMARY KEY(project_id, worktree_id, path), FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE ); +CREATE INDEX "index_worktree_diagnostic_summaries_on_project_id" ON "worktree_diagnostic_summaries" ("project_id"); CREATE INDEX "index_worktree_diagnostic_summaries_on_project_id_and_worktree_id" ON "worktree_diagnostic_summaries" ("project_id", "worktree_id"); CREATE TABLE "language_servers" ( diff --git a/crates/collab/migrations/20221111092550_reconnection_support.sql b/crates/collab/migrations/20221111092550_reconnection_support.sql index b742f8e0cd..a7d45a9759 100644 --- a/crates/collab/migrations/20221111092550_reconnection_support.sql +++ b/crates/collab/migrations/20221111092550_reconnection_support.sql @@ -22,18 +22,19 @@ CREATE INDEX "index_worktrees_on_project_id" ON "worktrees" ("project_id"); CREATE TABLE "worktree_entries" ( "project_id" INTEGER NOT NULL, - "worktree_id" INTEGER NOT NULL, + "worktree_id" INT8 NOT NULL, "id" INTEGER NOT NULL, "is_dir" BOOL NOT NULL, "path" VARCHAR NOT NULL, "inode" INT8 NOT NULL, - "mtime_seconds" INTEGER NOT NULL, + "mtime_seconds" INT8 NOT NULL, "mtime_nanos" INTEGER NOT NULL, "is_symlink" BOOL NOT NULL, "is_ignored" BOOL NOT NULL, PRIMARY KEY(project_id, worktree_id, id), FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE ); +CREATE INDEX "index_worktree_entries_on_project_id" ON "worktree_entries" ("project_id"); CREATE INDEX "index_worktree_entries_on_project_id_and_worktree_id" ON "worktree_entries" ("project_id", "worktree_id"); CREATE TABLE "worktree_diagnostic_summaries" ( @@ -46,6 +47,7 @@ CREATE TABLE "worktree_diagnostic_summaries" ( PRIMARY KEY(project_id, worktree_id, path), FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE ); +CREATE INDEX "index_worktree_diagnostic_summaries_on_project_id" ON "worktree_diagnostic_summaries" ("project_id"); CREATE INDEX "index_worktree_diagnostic_summaries_on_project_id_and_worktree_id" ON "worktree_diagnostic_summaries" ("project_id", "worktree_id"); CREATE TABLE "language_servers" ( diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 3d828b2e79..b01c6e7504 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1,5 +1,6 @@ mod access_token; mod contact; +mod language_server; mod project; mod project_collaborator; mod room; @@ -9,6 +10,7 @@ mod signup; mod tests; mod user; mod worktree; +mod worktree_diagnostic_summary; mod worktree_entry; use crate::{Error, Result}; @@ -1493,7 +1495,7 @@ impl Database { .await?; worktree::Entity::insert_many(worktrees.iter().map(|worktree| worktree::ActiveModel { - id: ActiveValue::set(WorktreeId(worktree.id as u32)), + id: ActiveValue::set(worktree.id as i64), project_id: ActiveValue::set(project.id), abs_path: ActiveValue::set(worktree.abs_path.clone()), root_name: ActiveValue::set(worktree.root_name.clone()), @@ -1563,7 +1565,7 @@ impl Database { .ok_or_else(|| anyhow!("no such project"))?; worktree::Entity::insert_many(worktrees.iter().map(|worktree| worktree::ActiveModel { - id: ActiveValue::set(WorktreeId(worktree.id as u32)), + id: ActiveValue::set(worktree.id as i64), project_id: ActiveValue::set(project.id), abs_path: ActiveValue::set(worktree.abs_path.clone()), root_name: ActiveValue::set(worktree.root_name.clone()), @@ -1576,11 +1578,8 @@ impl Database { worktree::Entity::delete_many() .filter( worktree::Column::ProjectId.eq(project.id).and( - worktree::Column::Id.is_not_in( - worktrees - .iter() - .map(|worktree| WorktreeId(worktree.id as u32)), - ), + worktree::Column::Id + .is_not_in(worktrees.iter().map(|worktree| worktree.id as i64)), ), ) .exec(&tx) @@ -1601,7 +1600,7 @@ impl Database { ) -> Result>> { self.transact(|tx| async move { let project_id = ProjectId::from_proto(update.project_id); - let worktree_id = WorktreeId::from_proto(update.worktree_id); + let worktree_id = update.worktree_id as i64; // Ensure the update comes from the host. let project = project::Entity::find_by_id(project_id) @@ -1609,13 +1608,14 @@ impl Database { .one(&tx) .await? .ok_or_else(|| anyhow!("no such project"))?; + let room_id = project.room_id; // Update metadata. worktree::Entity::update(worktree::ActiveModel { id: ActiveValue::set(worktree_id), project_id: ActiveValue::set(project_id), root_name: ActiveValue::set(update.root_name.clone()), - scan_id: ActiveValue::set(update.scan_id as u32), + scan_id: ActiveValue::set(update.scan_id as i64), is_complete: ActiveValue::set(update.is_last_update), abs_path: ActiveValue::set(update.abs_path.clone()), ..Default::default() @@ -1623,76 +1623,57 @@ impl Database { .exec(&tx) .await?; - // if !update.updated_entries.is_empty() { - // let mut params = - // "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?),".repeat(update.updated_entries.len()); - // params.pop(); + worktree_entry::Entity::insert_many(update.updated_entries.iter().map(|entry| { + let mtime = entry.mtime.clone().unwrap_or_default(); + worktree_entry::ActiveModel { + project_id: ActiveValue::set(project_id), + worktree_id: ActiveValue::set(worktree_id), + id: ActiveValue::set(entry.id as i64), + is_dir: ActiveValue::set(entry.is_dir), + path: ActiveValue::set(entry.path.clone()), + inode: ActiveValue::set(entry.inode as i64), + mtime_seconds: ActiveValue::set(mtime.seconds as i64), + mtime_nanos: ActiveValue::set(mtime.nanos), + is_symlink: ActiveValue::set(entry.is_symlink), + is_ignored: ActiveValue::set(entry.is_ignored), + } + })) + .on_conflict( + OnConflict::columns([ + worktree_entry::Column::ProjectId, + worktree_entry::Column::WorktreeId, + worktree_entry::Column::Id, + ]) + .update_columns([ + worktree_entry::Column::IsDir, + worktree_entry::Column::Path, + worktree_entry::Column::Inode, + worktree_entry::Column::MtimeSeconds, + worktree_entry::Column::MtimeNanos, + worktree_entry::Column::IsSymlink, + worktree_entry::Column::IsIgnored, + ]) + .to_owned(), + ) + .exec(&tx) + .await?; - // let query = format!( - // " - // INSERT INTO worktree_entries ( - // project_id, - // worktree_id, - // id, - // is_dir, - // path, - // inode, - // mtime_seconds, - // mtime_nanos, - // is_symlink, - // is_ignored - // ) - // VALUES {params} - // ON CONFLICT (project_id, worktree_id, id) DO UPDATE SET - // is_dir = excluded.is_dir, - // path = excluded.path, - // inode = excluded.inode, - // mtime_seconds = excluded.mtime_seconds, - // mtime_nanos = excluded.mtime_nanos, - // is_symlink = excluded.is_symlink, - // is_ignored = excluded.is_ignored - // " - // ); - // let mut query = sqlx::query(&query); - // for entry in &update.updated_entries { - // let mtime = entry.mtime.clone().unwrap_or_default(); - // query = query - // .bind(project_id) - // .bind(worktree_id) - // .bind(entry.id as i64) - // .bind(entry.is_dir) - // .bind(&entry.path) - // .bind(entry.inode as i64) - // .bind(mtime.seconds as i64) - // .bind(mtime.nanos as i32) - // .bind(entry.is_symlink) - // .bind(entry.is_ignored); - // } - // query.execute(&mut tx).await?; - // } + worktree_entry::Entity::delete_many() + .filter( + worktree_entry::Column::ProjectId + .eq(project_id) + .and(worktree_entry::Column::WorktreeId.eq(worktree_id)) + .and( + worktree_entry::Column::Id + .is_in(update.removed_entries.iter().map(|id| *id as i64)), + ), + ) + .exec(&tx) + .await?; - // if !update.removed_entries.is_empty() { - // let mut params = "?,".repeat(update.removed_entries.len()); - // params.pop(); - // let query = format!( - // " - // DELETE FROM worktree_entries - // WHERE project_id = ? AND worktree_id = ? AND id IN ({params}) - // " - // ); - - // let mut query = sqlx::query(&query).bind(project_id).bind(worktree_id); - // for entry_id in &update.removed_entries { - // query = query.bind(*entry_id as i64); - // } - // query.execute(&mut tx).await?; - // } - - // let connection_ids = self.get_guest_connection_ids(project_id, &mut tx).await?; - // self.commit_room_transaction(room_id, tx, connection_ids) - // .await - - todo!() + let connection_ids = self.project_guest_connection_ids(project_id, &tx).await?; + self.commit_room_transaction(room_id, tx, connection_ids) + .await }) .await } @@ -1703,57 +1684,51 @@ impl Database { connection_id: ConnectionId, ) -> Result>> { self.transact(|tx| async { - todo!() - // let project_id = ProjectId::from_proto(update.project_id); - // let worktree_id = WorktreeId::from_proto(update.worktree_id); - // let summary = update - // .summary - // .as_ref() - // .ok_or_else(|| anyhow!("invalid summary"))?; + let project_id = ProjectId::from_proto(update.project_id); + let worktree_id = update.worktree_id as i64; + let summary = update + .summary + .as_ref() + .ok_or_else(|| anyhow!("invalid summary"))?; - // // Ensure the update comes from the host. - // let room_id: RoomId = sqlx::query_scalar( - // " - // SELECT room_id - // FROM projects - // WHERE id = $1 AND host_connection_id = $2 - // ", - // ) - // .bind(project_id) - // .bind(connection_id.0 as i32) - // .fetch_one(&mut tx) - // .await?; + // Ensure the update comes from the host. + let project = project::Entity::find_by_id(project_id) + .one(&tx) + .await? + .ok_or_else(|| anyhow!("no such project"))?; + if project.host_connection_id != connection_id.0 { + return Err(anyhow!("can't update a project hosted by someone else"))?; + } - // // Update summary. - // sqlx::query( - // " - // INSERT INTO worktree_diagnostic_summaries ( - // project_id, - // worktree_id, - // path, - // language_server_id, - // error_count, - // warning_count - // ) - // VALUES ($1, $2, $3, $4, $5, $6) - // ON CONFLICT (project_id, worktree_id, path) DO UPDATE SET - // language_server_id = excluded.language_server_id, - // error_count = excluded.error_count, - // warning_count = excluded.warning_count - // ", - // ) - // .bind(project_id) - // .bind(worktree_id) - // .bind(&summary.path) - // .bind(summary.language_server_id as i64) - // .bind(summary.error_count as i32) - // .bind(summary.warning_count as i32) - // .execute(&mut tx) - // .await?; + // Update summary. + worktree_diagnostic_summary::Entity::insert(worktree_diagnostic_summary::ActiveModel { + project_id: ActiveValue::set(project_id), + worktree_id: ActiveValue::set(worktree_id), + path: ActiveValue::set(summary.path.clone()), + language_server_id: ActiveValue::set(summary.language_server_id as i64), + error_count: ActiveValue::set(summary.error_count), + warning_count: ActiveValue::set(summary.warning_count), + ..Default::default() + }) + .on_conflict( + OnConflict::columns([ + worktree_diagnostic_summary::Column::ProjectId, + worktree_diagnostic_summary::Column::WorktreeId, + worktree_diagnostic_summary::Column::Path, + ]) + .update_columns([ + worktree_diagnostic_summary::Column::LanguageServerId, + worktree_diagnostic_summary::Column::ErrorCount, + worktree_diagnostic_summary::Column::WarningCount, + ]) + .to_owned(), + ) + .exec(&tx) + .await?; - // let connection_ids = self.get_guest_connection_ids(project_id, &mut tx).await?; - // self.commit_room_transaction(room_id, tx, connection_ids) - // .await + let connection_ids = self.project_guest_connection_ids(project_id, &tx).await?; + self.commit_room_transaction(project.room_id, tx, connection_ids) + .await }) .await } @@ -1764,44 +1739,42 @@ impl Database { connection_id: ConnectionId, ) -> Result>> { self.transact(|tx| async { - todo!() - // let project_id = ProjectId::from_proto(update.project_id); - // let server = update - // .server - // .as_ref() - // .ok_or_else(|| anyhow!("invalid language server"))?; + let project_id = ProjectId::from_proto(update.project_id); + let server = update + .server + .as_ref() + .ok_or_else(|| anyhow!("invalid language server"))?; - // // Ensure the update comes from the host. - // let room_id: RoomId = sqlx::query_scalar( - // " - // SELECT room_id - // FROM projects - // WHERE id = $1 AND host_connection_id = $2 - // ", - // ) - // .bind(project_id) - // .bind(connection_id.0 as i32) - // .fetch_one(&mut tx) - // .await?; + // Ensure the update comes from the host. + let project = project::Entity::find_by_id(project_id) + .one(&tx) + .await? + .ok_or_else(|| anyhow!("no such project"))?; + if project.host_connection_id != connection_id.0 { + return Err(anyhow!("can't update a project hosted by someone else"))?; + } - // // Add the newly-started language server. - // sqlx::query( - // " - // INSERT INTO language_servers (project_id, id, name) - // VALUES ($1, $2, $3) - // ON CONFLICT (project_id, id) DO UPDATE SET - // name = excluded.name - // ", - // ) - // .bind(project_id) - // .bind(server.id as i64) - // .bind(&server.name) - // .execute(&mut tx) - // .await?; + // Add the newly-started language server. + language_server::Entity::insert(language_server::ActiveModel { + project_id: ActiveValue::set(project_id), + id: ActiveValue::set(server.id as i64), + name: ActiveValue::set(server.name.clone()), + ..Default::default() + }) + .on_conflict( + OnConflict::columns([ + language_server::Column::ProjectId, + language_server::Column::Id, + ]) + .update_column(language_server::Column::Name) + .to_owned(), + ) + .exec(&tx) + .await?; - // let connection_ids = self.get_guest_connection_ids(project_id, &mut tx).await?; - // self.commit_room_transaction(room_id, tx, connection_ids) - // .await + let connection_ids = self.project_guest_connection_ids(project_id, &tx).await?; + self.commit_room_transaction(project.room_id, tx, connection_ids) + .await }) .await } @@ -1812,194 +1785,135 @@ impl Database { connection_id: ConnectionId, ) -> Result> { self.transact(|tx| async move { - todo!() - // let (room_id, user_id) = sqlx::query_as::<_, (RoomId, UserId)>( - // " - // SELECT room_id, user_id - // FROM room_participants - // WHERE answering_connection_id = $1 - // ", - // ) - // .bind(connection_id.0 as i32) - // .fetch_one(&mut tx) - // .await?; + let participant = room_participant::Entity::find() + .filter(room_participant::Column::AnsweringConnectionId.eq(connection_id.0)) + .one(&tx) + .await? + .ok_or_else(|| anyhow!("must join a room first"))?; - // // Ensure project id was shared on this room. - // sqlx::query( - // " - // SELECT 1 - // FROM projects - // WHERE id = $1 AND room_id = $2 - // ", - // ) - // .bind(project_id) - // .bind(room_id) - // .fetch_one(&mut tx) - // .await?; + let project = project::Entity::find_by_id(project_id) + .one(&tx) + .await? + .ok_or_else(|| anyhow!("no such project"))?; + if project.room_id != participant.room_id { + return Err(anyhow!("no such project"))?; + } - // let mut collaborators = sqlx::query_as::<_, ProjectCollaborator>( - // " - // SELECT * - // FROM project_collaborators - // WHERE project_id = $1 - // ", - // ) - // .bind(project_id) - // .fetch_all(&mut tx) - // .await?; - // let replica_ids = collaborators - // .iter() - // .map(|c| c.replica_id) - // .collect::>(); - // let mut replica_id = ReplicaId(1); - // while replica_ids.contains(&replica_id) { - // replica_id.0 += 1; - // } - // let new_collaborator = ProjectCollaborator { - // project_id, - // connection_id: connection_id.0 as i32, - // user_id, - // replica_id, - // is_host: false, - // }; + let mut collaborators = project + .find_related(project_collaborator::Entity) + .all(&tx) + .await?; + let replica_ids = collaborators + .iter() + .map(|c| c.replica_id) + .collect::>(); + let mut replica_id = ReplicaId(1); + while replica_ids.contains(&replica_id) { + replica_id.0 += 1; + } + let new_collaborator = project_collaborator::ActiveModel { + project_id: ActiveValue::set(project_id), + connection_id: ActiveValue::set(connection_id.0), + user_id: ActiveValue::set(participant.user_id), + replica_id: ActiveValue::set(replica_id), + is_host: ActiveValue::set(false), + ..Default::default() + } + .insert(&tx) + .await?; + collaborators.push(new_collaborator); - // sqlx::query( - // " - // INSERT INTO project_collaborators ( - // project_id, - // connection_id, - // user_id, - // replica_id, - // is_host - // ) - // VALUES ($1, $2, $3, $4, $5) - // ", - // ) - // .bind(new_collaborator.project_id) - // .bind(new_collaborator.connection_id) - // .bind(new_collaborator.user_id) - // .bind(new_collaborator.replica_id) - // .bind(new_collaborator.is_host) - // .execute(&mut tx) - // .await?; - // collaborators.push(new_collaborator); + let db_worktrees = project.find_related(worktree::Entity).all(&tx).await?; + let mut worktrees = db_worktrees + .into_iter() + .map(|db_worktree| { + ( + db_worktree.id as u64, + Worktree { + id: db_worktree.id as u64, + abs_path: db_worktree.abs_path, + root_name: db_worktree.root_name, + visible: db_worktree.visible, + entries: Default::default(), + diagnostic_summaries: Default::default(), + scan_id: db_worktree.scan_id as u64, + is_complete: db_worktree.is_complete, + }, + ) + }) + .collect::>(); - // let worktree_rows = sqlx::query_as::<_, WorktreeRow>( - // " - // SELECT * - // FROM worktrees - // WHERE project_id = $1 - // ", - // ) - // .bind(project_id) - // .fetch_all(&mut tx) - // .await?; - // let mut worktrees = worktree_rows - // .into_iter() - // .map(|worktree_row| { - // ( - // worktree_row.id, - // Worktree { - // id: worktree_row.id, - // abs_path: worktree_row.abs_path, - // root_name: worktree_row.root_name, - // visible: worktree_row.visible, - // entries: Default::default(), - // diagnostic_summaries: Default::default(), - // scan_id: worktree_row.scan_id as u64, - // is_complete: worktree_row.is_complete, - // }, - // ) - // }) - // .collect::>(); + // Populate worktree entries. + { + let mut db_entries = worktree_entry::Entity::find() + .filter(worktree_entry::Column::ProjectId.eq(project_id)) + .stream(&tx) + .await?; + while let Some(db_entry) = db_entries.next().await { + let db_entry = db_entry?; + if let Some(worktree) = worktrees.get_mut(&(db_entry.worktree_id as u64)) { + worktree.entries.push(proto::Entry { + id: db_entry.id as u64, + is_dir: db_entry.is_dir, + path: db_entry.path, + inode: db_entry.inode as u64, + mtime: Some(proto::Timestamp { + seconds: db_entry.mtime_seconds as u64, + nanos: db_entry.mtime_nanos, + }), + is_symlink: db_entry.is_symlink, + is_ignored: db_entry.is_ignored, + }); + } + } + } - // // Populate worktree entries. - // { - // let mut entries = sqlx::query_as::<_, WorktreeEntry>( - // " - // SELECT * - // FROM worktree_entries - // WHERE project_id = $1 - // ", - // ) - // .bind(project_id) - // .fetch(&mut tx); - // while let Some(entry) = entries.next().await { - // let entry = entry?; - // if let Some(worktree) = worktrees.get_mut(&entry.worktree_id) { - // worktree.entries.push(proto::Entry { - // id: entry.id as u64, - // is_dir: entry.is_dir, - // path: entry.path, - // inode: entry.inode as u64, - // mtime: Some(proto::Timestamp { - // seconds: entry.mtime_seconds as u64, - // nanos: entry.mtime_nanos as u32, - // }), - // is_symlink: entry.is_symlink, - // is_ignored: entry.is_ignored, - // }); - // } - // } - // } + // Populate worktree diagnostic summaries. + { + let mut db_summaries = worktree_diagnostic_summary::Entity::find() + .filter(worktree_diagnostic_summary::Column::ProjectId.eq(project_id)) + .stream(&tx) + .await?; + while let Some(db_summary) = db_summaries.next().await { + let db_summary = db_summary?; + if let Some(worktree) = worktrees.get_mut(&(db_summary.worktree_id as u64)) { + worktree + .diagnostic_summaries + .push(proto::DiagnosticSummary { + path: db_summary.path, + language_server_id: db_summary.language_server_id as u64, + error_count: db_summary.error_count as u32, + warning_count: db_summary.warning_count as u32, + }); + } + } + } - // // Populate worktree diagnostic summaries. - // { - // let mut summaries = sqlx::query_as::<_, WorktreeDiagnosticSummary>( - // " - // SELECT * - // FROM worktree_diagnostic_summaries - // WHERE project_id = $1 - // ", - // ) - // .bind(project_id) - // .fetch(&mut tx); - // while let Some(summary) = summaries.next().await { - // let summary = summary?; - // if let Some(worktree) = worktrees.get_mut(&summary.worktree_id) { - // worktree - // .diagnostic_summaries - // .push(proto::DiagnosticSummary { - // path: summary.path, - // language_server_id: summary.language_server_id as u64, - // error_count: summary.error_count as u32, - // warning_count: summary.warning_count as u32, - // }); - // } - // } - // } + // Populate language servers. + let language_servers = project + .find_related(language_server::Entity) + .all(&tx) + .await?; - // // Populate language servers. - // let language_servers = sqlx::query_as::<_, LanguageServer>( - // " - // SELECT * - // FROM language_servers - // WHERE project_id = $1 - // ", - // ) - // .bind(project_id) - // .fetch_all(&mut tx) - // .await?; - - // self.commit_room_transaction( - // room_id, - // tx, - // ( - // Project { - // collaborators, - // worktrees, - // language_servers: language_servers - // .into_iter() - // .map(|language_server| proto::LanguageServer { - // id: language_server.id.to_proto(), - // name: language_server.name, - // }) - // .collect(), - // }, - // replica_id as ReplicaId, - // ), - // ) - // .await + self.commit_room_transaction( + project.room_id, + tx, + ( + Project { + collaborators, + worktrees, + language_servers: language_servers + .into_iter() + .map(|language_server| proto::LanguageServer { + id: language_server.id as u64, + name: language_server.name, + }) + .collect(), + }, + replica_id as ReplicaId, + ), + ) + .await }) .await } @@ -2010,59 +1924,42 @@ impl Database { connection_id: ConnectionId, ) -> Result> { self.transact(|tx| async move { - todo!() - // let result = sqlx::query( - // " - // DELETE FROM project_collaborators - // WHERE project_id = $1 AND connection_id = $2 - // ", - // ) - // .bind(project_id) - // .bind(connection_id.0 as i32) - // .execute(&mut tx) - // .await?; + let result = project_collaborator::Entity::delete_many() + .filter( + project_collaborator::Column::ProjectId + .eq(project_id) + .and(project_collaborator::Column::ConnectionId.eq(connection_id.0)), + ) + .exec(&tx) + .await?; + if result.rows_affected == 0 { + Err(anyhow!("not a collaborator on this project"))?; + } - // if result.rows_affected() == 0 { - // Err(anyhow!("not a collaborator on this project"))?; - // } + let project = project::Entity::find_by_id(project_id) + .one(&tx) + .await? + .ok_or_else(|| anyhow!("no such project"))?; + let collaborators = project + .find_related(project_collaborator::Entity) + .all(&tx) + .await?; + let connection_ids = collaborators + .into_iter() + .map(|collaborator| ConnectionId(collaborator.connection_id)) + .collect(); - // let connection_ids = sqlx::query_scalar::<_, i32>( - // " - // SELECT connection_id - // FROM project_collaborators - // WHERE project_id = $1 - // ", - // ) - // .bind(project_id) - // .fetch_all(&mut tx) - // .await? - // .into_iter() - // .map(|id| ConnectionId(id as u32)) - // .collect(); - - // let (room_id, host_user_id, host_connection_id) = - // sqlx::query_as::<_, (RoomId, i32, i32)>( - // " - // SELECT room_id, host_user_id, host_connection_id - // FROM projects - // WHERE id = $1 - // ", - // ) - // .bind(project_id) - // .fetch_one(&mut tx) - // .await?; - - // self.commit_room_transaction( - // room_id, - // tx, - // LeftProject { - // id: project_id, - // host_user_id: UserId(host_user_id), - // host_connection_id: ConnectionId(host_connection_id as u32), - // connection_ids, - // }, - // ) - // .await + self.commit_room_transaction( + project.room_id, + tx, + LeftProject { + id: project_id, + host_user_id: project.host_user_id, + host_connection_id: ConnectionId(project.host_connection_id), + connection_ids, + }, + ) + .await }) .await } @@ -2442,8 +2339,6 @@ id_type!(ProjectCollaboratorId); id_type!(ReplicaId); id_type!(SignupId); id_type!(UserId); -id_type!(WorktreeId); -id_type!(WorktreeEntryId); pub struct LeftRoom { pub room: proto::Room, @@ -2453,7 +2348,7 @@ pub struct LeftRoom { pub struct Project { pub collaborators: Vec, - pub worktrees: BTreeMap, + pub worktrees: BTreeMap, pub language_servers: Vec, } @@ -2465,7 +2360,7 @@ pub struct LeftProject { } pub struct Worktree { - pub id: WorktreeId, + pub id: u64, pub abs_path: String, pub root_name: String, pub visible: bool, diff --git a/crates/collab/src/db/language_server.rs b/crates/collab/src/db/language_server.rs new file mode 100644 index 0000000000..d2c045c121 --- /dev/null +++ b/crates/collab/src/db/language_server.rs @@ -0,0 +1,30 @@ +use super::ProjectId; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "language_servers")] +pub struct Model { + #[sea_orm(primary_key)] + pub project_id: ProjectId, + #[sea_orm(primary_key)] + pub id: i64, + pub name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::project::Entity", + from = "Column::ProjectId", + to = "super::project::Column::Id" + )] + Project, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Project.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/db/project.rs b/crates/collab/src/db/project.rs index c8083402a3..5bf8addec8 100644 --- a/crates/collab/src/db/project.rs +++ b/crates/collab/src/db/project.rs @@ -26,7 +26,11 @@ pub enum Relation { )] Room, #[sea_orm(has_many = "super::worktree::Entity")] - Worktree, + Worktrees, + #[sea_orm(has_many = "super::project_collaborator::Entity")] + Collaborators, + #[sea_orm(has_many = "super::language_server::Entity")] + LanguageServers, } impl Related for Entity { @@ -43,7 +47,19 @@ impl Related for Entity { impl Related for Entity { fn to() -> RelationDef { - Relation::Worktree.def() + Relation::Worktrees.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Collaborators.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::LanguageServers.def() } } diff --git a/crates/collab/src/db/project_collaborator.rs b/crates/collab/src/db/project_collaborator.rs index bccf451a63..56048c3181 100644 --- a/crates/collab/src/db/project_collaborator.rs +++ b/crates/collab/src/db/project_collaborator.rs @@ -14,6 +14,19 @@ pub struct Model { } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} +pub enum Relation { + #[sea_orm( + belongs_to = "super::project::Entity", + from = "Column::ProjectId", + to = "super::project::Column::Id" + )] + Project, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Project.def() + } +} impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/db/worktree.rs b/crates/collab/src/db/worktree.rs index 8cad41e8a9..b9f0f97dee 100644 --- a/crates/collab/src/db/worktree.rs +++ b/crates/collab/src/db/worktree.rs @@ -1,17 +1,17 @@ -use super::{ProjectId, WorktreeId}; +use super::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "worktrees")] pub struct Model { #[sea_orm(primary_key)] - pub id: WorktreeId, + pub id: i64, #[sea_orm(primary_key)] pub project_id: ProjectId, pub abs_path: String, pub root_name: String, pub visible: bool, - pub scan_id: u32, + pub scan_id: i64, pub is_complete: bool, } diff --git a/crates/collab/src/db/worktree_diagnostic_summary.rs b/crates/collab/src/db/worktree_diagnostic_summary.rs new file mode 100644 index 0000000000..49bf4f6e03 --- /dev/null +++ b/crates/collab/src/db/worktree_diagnostic_summary.rs @@ -0,0 +1,21 @@ +use super::ProjectId; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "worktree_diagnostic_summaries")] +pub struct Model { + #[sea_orm(primary_key)] + pub project_id: ProjectId, + #[sea_orm(primary_key)] + pub worktree_id: i64, + #[sea_orm(primary_key)] + pub path: String, + pub language_server_id: i64, + pub error_count: u32, + pub warning_count: u32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/db/worktree_entry.rs b/crates/collab/src/db/worktree_entry.rs index 8698d844c1..f38ef7b3f7 100644 --- a/crates/collab/src/db/worktree_entry.rs +++ b/crates/collab/src/db/worktree_entry.rs @@ -1,23 +1,25 @@ -use super::{ProjectId, WorktreeEntryId, WorktreeId}; +use super::ProjectId; use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "worktree_entries")] pub struct Model { #[sea_orm(primary_key)] - project_id: ProjectId, + pub project_id: ProjectId, #[sea_orm(primary_key)] - worktree_id: WorktreeId, + pub worktree_id: i64, #[sea_orm(primary_key)] - id: WorktreeEntryId, - is_dir: bool, - path: String, - inode: u64, - mtime_seconds: u64, - mtime_nanos: u32, - is_symlink: bool, - is_ignored: bool, + pub id: i64, + pub is_dir: bool, + pub path: String, + pub inode: i64, + pub mtime_seconds: i64, + pub mtime_nanos: u32, + pub is_symlink: bool, + pub is_ignored: bool, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 01866b074d..d3b95a82e6 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1019,7 +1019,7 @@ async fn join_project( .worktrees .iter() .map(|(id, worktree)| proto::WorktreeMetadata { - id: id.to_proto(), + id: *id, root_name: worktree.root_name.clone(), visible: worktree.visible, abs_path: worktree.abs_path.clone(), @@ -1060,7 +1060,7 @@ async fn join_project( // Stream this worktree's entries. let message = proto::UpdateWorktree { project_id: project_id.to_proto(), - worktree_id: worktree_id.to_proto(), + worktree_id, abs_path: worktree.abs_path.clone(), root_name: worktree.root_name, updated_entries: worktree.entries, @@ -1078,7 +1078,7 @@ async fn join_project( session.connection_id, proto::UpdateDiagnosticSummary { project_id: project_id.to_proto(), - worktree_id: worktree.id.to_proto(), + worktree_id: worktree.id, summary: Some(summary), }, )?;