Fix joining hosted projects (#9038)

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2024-03-07 19:56:41 -07:00 committed by GitHub
parent f67abd2943
commit 866d791760
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 104 additions and 69 deletions

View File

@ -3,9 +3,7 @@ mod channel_index;
use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage};
use anyhow::{anyhow, Result};
use channel_index::ChannelIndex;
use client::{
ChannelId, Client, ClientSettings, HostedProjectId, Subscription, User, UserId, UserStore,
};
use client::{ChannelId, Client, ClientSettings, ProjectId, Subscription, User, UserId, UserStore};
use collections::{hash_map, HashMap, HashSet};
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{
@ -37,7 +35,7 @@ struct NotesVersion {
#[derive(Debug, Clone)]
pub struct HostedProject {
id: HostedProjectId,
project_id: ProjectId,
channel_id: ChannelId,
name: SharedString,
_visibility: proto::ChannelVisibility,
@ -46,7 +44,7 @@ pub struct HostedProject {
impl From<proto::HostedProject> for HostedProject {
fn from(project: proto::HostedProject) -> Self {
Self {
id: HostedProjectId(project.id),
project_id: ProjectId(project.project_id),
channel_id: ChannelId(project.channel_id),
_visibility: project.visibility(),
name: project.name.into(),
@ -59,7 +57,7 @@ pub struct ChannelStore {
channel_invitations: Vec<Arc<Channel>>,
channel_participants: HashMap<ChannelId, Vec<Arc<User>>>,
channel_states: HashMap<ChannelId, ChannelState>,
hosted_projects: HashMap<HostedProjectId, HostedProject>,
hosted_projects: HashMap<ProjectId, HostedProject>,
outgoing_invites: HashSet<(ChannelId, UserId)>,
update_channels_tx: mpsc::UnboundedSender<proto::UpdateChannels>,
@ -88,7 +86,7 @@ pub struct ChannelState {
observed_notes_version: NotesVersion,
observed_chat_message: Option<u64>,
role: Option<ChannelRole>,
projects: HashSet<HostedProjectId>,
projects: HashSet<ProjectId>,
}
impl Channel {
@ -305,8 +303,8 @@ impl ChannelStore {
self.channel_index.by_id().get(&channel_id)
}
pub fn projects_for_id(&self, channel_id: ChannelId) -> Vec<(SharedString, HostedProjectId)> {
let mut projects: Vec<(SharedString, HostedProjectId)> = self
pub fn projects_for_id(&self, channel_id: ChannelId) -> Vec<(SharedString, ProjectId)> {
let mut projects: Vec<(SharedString, ProjectId)> = self
.channel_states
.get(&channel_id)
.map(|state| state.projects.clone())
@ -1159,27 +1157,27 @@ impl ChannelStore {
let hosted_project: HostedProject = hosted_project.into();
if let Some(old_project) = self
.hosted_projects
.insert(hosted_project.id, hosted_project.clone())
.insert(hosted_project.project_id, hosted_project.clone())
{
self.channel_states
.entry(old_project.channel_id)
.or_default()
.remove_hosted_project(old_project.id);
.remove_hosted_project(old_project.project_id);
}
self.channel_states
.entry(hosted_project.channel_id)
.or_default()
.add_hosted_project(hosted_project.id);
.add_hosted_project(hosted_project.project_id);
}
for hosted_project_id in payload.deleted_hosted_projects {
let hosted_project_id = HostedProjectId(hosted_project_id);
let hosted_project_id = ProjectId(hosted_project_id);
if let Some(old_project) = self.hosted_projects.remove(&hosted_project_id) {
self.channel_states
.entry(old_project.channel_id)
.or_default()
.remove_hosted_project(old_project.id);
.remove_hosted_project(old_project.project_id);
}
}
}
@ -1289,11 +1287,11 @@ impl ChannelState {
}
}
fn add_hosted_project(&mut self, project_id: HostedProjectId) {
fn add_hosted_project(&mut self, project_id: ProjectId) {
self.projects.insert(project_id);
}
fn remove_hosted_project(&mut self, project_id: HostedProjectId) {
fn remove_hosted_project(&mut self, project_id: ProjectId) {
self.projects.remove(&project_id);
}
}

View File

@ -25,7 +25,7 @@ impl std::fmt::Display for ChannelId {
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct HostedProjectId(pub u64);
pub struct ProjectId(pub u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParticipantIndex(pub u32);

View File

@ -9,20 +9,21 @@ impl Database {
roles: &HashMap<ChannelId, ChannelRole>,
tx: &DatabaseTransaction,
) -> Result<Vec<proto::HostedProject>> {
Ok(hosted_project::Entity::find()
let projects = hosted_project::Entity::find()
.find_also_related(project::Entity)
.filter(hosted_project::Column::ChannelId.is_in(channel_ids.iter().map(|id| id.0)))
.all(tx)
.await?
.into_iter()
.flat_map(|project| {
if project.deleted_at.is_some() {
.flat_map(|(hosted_project, project)| {
if hosted_project.deleted_at.is_some() {
return None;
}
match project.visibility {
match hosted_project.visibility {
ChannelVisibility::Public => {}
ChannelVisibility::Members => {
let is_visible = roles
.get(&project.channel_id)
.get(&hosted_project.channel_id)
.map(|role| role.can_see_all_descendants())
.unwrap_or(false);
if !is_visible {
@ -31,13 +32,15 @@ impl Database {
}
};
Some(proto::HostedProject {
id: project.id.to_proto(),
channel_id: project.channel_id.to_proto(),
name: project.name.clone(),
visibility: project.visibility.into(),
project_id: project?.id.to_proto(),
channel_id: hosted_project.channel_id.to_proto(),
name: hosted_project.name.clone(),
visibility: hosted_project.visibility.into(),
})
})
.collect())
.collect();
Ok(projects)
}
pub async fn get_hosted_project(

View File

@ -512,18 +512,30 @@ impl Database {
/// Adds the given connection to the specified hosted project
pub async fn join_hosted_project(
&self,
id: HostedProjectId,
id: ProjectId,
user_id: UserId,
connection: ConnectionId,
) -> Result<(Project, ReplicaId)> {
self.transaction(|tx| async move {
let (hosted_project, role) = self.get_hosted_project(id, user_id, &tx).await?;
let project = project::Entity::find()
.filter(project::Column::HostedProjectId.eq(hosted_project.id))
let (project, hosted_project) = project::Entity::find_by_id(id)
.find_also_related(hosted_project::Entity)
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("hosted project is no longer shared"))?;
let Some(hosted_project) = hosted_project else {
return Err(anyhow!("project is not hosted"))?;
};
let channel = channel::Entity::find_by_id(hosted_project.channel_id)
.one(&*tx)
.await?
.ok_or_else(|| anyhow!("no such channel"))?;
let role = self
.check_user_is_channel_participant(&channel, user_id, &tx)
.await?;
self.join_project_internal(project, user_id, connection, role, &tx)
.await
})

View File

@ -15,4 +15,13 @@ pub struct Model {
impl ActiveModelBehavior for ActiveModel {}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
pub enum Relation {
#[sea_orm(has_one = "super::project::Entity")]
Project,
}
impl Related<super::project::Entity> for Entity {
fn to() -> RelationDef {
Relation::Project.def()
}
}

View File

@ -50,6 +50,12 @@ pub enum Relation {
Collaborators,
#[sea_orm(has_many = "super::language_server::Entity")]
LanguageServers,
#[sea_orm(
belongs_to = "super::hosted_project::Entity",
from = "Column::HostedProjectId",
to = "super::hosted_project::Column::Id"
)]
HostedProject,
}
impl Related<super::user::Entity> for Entity {
@ -82,4 +88,10 @@ impl Related<super::language_server::Entity> for Entity {
}
}
impl Related<super::hosted_project::Entity> for Entity {
fn to() -> RelationDef {
Relation::HostedProject.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -4,9 +4,9 @@ use crate::{
auth::{self, Impersonator},
db::{
self, BufferId, ChannelId, ChannelRole, ChannelsForUser, CreatedChannelMessage, Database,
HostedProjectId, InviteMemberResult, MembershipUpdated, MessageId, NotificationId, Project,
ProjectId, RemoveChannelMemberResult, ReplicaId, RespondToChannelInvite, RoomId, ServerId,
User, UserId,
InviteMemberResult, MembershipUpdated, MessageId, NotificationId, Project, ProjectId,
RemoveChannelMemberResult, ReplicaId, RespondToChannelInvite, RoomId, ServerId, User,
UserId,
},
executor::Executor,
AppState, Error, Result,
@ -1770,7 +1770,7 @@ async fn join_hosted_project(
.db()
.await
.join_hosted_project(
HostedProjectId(request.id as i32),
ProjectId(request.project_id as i32),
session.user_id,
session.connection_id,
)

View File

@ -8,7 +8,7 @@ use crate::{
};
use call::ActiveCall;
use channel::{Channel, ChannelEvent, ChannelStore};
use client::{ChannelId, Client, Contact, HostedProjectId, User, UserStore};
use client::{ChannelId, Client, Contact, ProjectId, User, UserStore};
use contact_finder::ContactFinder;
use db::kvp::KEY_VALUE_STORE;
use editor::{Editor, EditorElement, EditorStyle};
@ -185,7 +185,7 @@ enum ListEntry {
depth: usize,
},
HostedProject {
id: HostedProjectId,
id: ProjectId,
name: SharedString,
},
Contact {
@ -1035,7 +1035,7 @@ impl CollabPanel {
fn render_channel_project(
&self,
id: HostedProjectId,
id: ProjectId,
name: &SharedString,
is_selected: bool,
cx: &mut ViewContext<Self>,

View File

@ -12,8 +12,7 @@ mod project_tests;
use anyhow::{anyhow, bail, Context as _, Result};
use async_trait::async_trait;
use client::{
proto, Client, Collaborator, HostedProjectId, PendingEntitySubscription, TypedEnvelope,
UserStore,
proto, Client, Collaborator, PendingEntitySubscription, ProjectId, TypedEnvelope, UserStore,
};
use clock::ReplicaId;
use collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque};
@ -175,7 +174,7 @@ pub struct Project {
prettiers_per_worktree: HashMap<WorktreeId, HashSet<Option<PathBuf>>>,
prettier_instances: HashMap<PathBuf, PrettierInstance>,
tasks: Model<Inventory>,
hosted_project_id: Option<HostedProjectId>,
hosted_project_id: Option<ProjectId>,
}
pub enum LanguageServerToQuery {
@ -780,29 +779,31 @@ impl Project {
}
pub async fn hosted(
_hosted_project_id: HostedProjectId,
_user_store: Model<UserStore>,
_client: Arc<Client>,
_languages: Arc<LanguageRegistry>,
_fs: Arc<dyn Fs>,
_cx: AsyncAppContext,
remote_id: ProjectId,
user_store: Model<UserStore>,
client: Arc<Client>,
languages: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>,
cx: AsyncAppContext,
) -> Result<Model<Self>> {
// let response = client
// .request_envelope(proto::JoinHostedProject {
// id: hosted_project_id.0,
// })
// .await?;
// Self::from_join_project_response(
// response,
// Some(hosted_project_id),
// client,
// user_store,
// languages,
// fs,
// cx,
// )
// .await
Err(anyhow!("disabled"))
client.authenticate_and_connect(true, &cx).await?;
let subscription = client.subscribe_to_entity(remote_id.0)?;
let response = client
.request_envelope(proto::JoinHostedProject {
project_id: remote_id.0,
})
.await?;
Self::from_join_project_response(
response,
subscription,
client,
user_store,
languages,
fs,
cx,
)
.await
}
fn release(&mut self, cx: &mut AppContext) {
@ -1050,7 +1051,7 @@ impl Project {
}
}
pub fn hosted_project_id(&self) -> Option<HostedProjectId> {
pub fn hosted_project_id(&self) -> Option<ProjectId> {
self.hosted_project_id
}

View File

@ -408,7 +408,7 @@ message JoinProject {
}
message JoinHostedProject {
uint64 id = 1;
uint64 project_id = 1;
}
message JoinProjectResponse {
@ -1067,7 +1067,7 @@ message ChannelParticipants {
}
message HostedProject {
uint64 id = 1;
uint64 project_id = 1;
uint64 channel_id = 2;
string name = 3;
ChannelVisibility visibility = 4;

View File

@ -15,7 +15,7 @@ use anyhow::{anyhow, Context as _, Result};
use call::{call_settings::CallSettings, ActiveCall};
use client::{
proto::{self, ErrorCode, PeerId},
ChannelId, Client, ErrorExt, HostedProjectId, Status, TypedEnvelope, UserStore,
ChannelId, Client, ErrorExt, ProjectId, Status, TypedEnvelope, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use derive_more::{Deref, DerefMut};
@ -4462,7 +4462,7 @@ pub fn create_and_open_local_file(
}
pub fn join_hosted_project(
hosted_project_id: HostedProjectId,
hosted_project_id: ProjectId,
app_state: Arc<AppState>,
cx: &mut AppContext,
) -> Task<Result<()>> {