diff --git a/frontend/rust-lib/event-integration/src/user_event.rs b/frontend/rust-lib/event-integration/src/user_event.rs index 07c8560a09..dca523253f 100644 --- a/frontend/rust-lib/event-integration/src/user_event.rs +++ b/frontend/rust-lib/event-integration/src/user_event.rs @@ -313,6 +313,17 @@ impl EventIntegrationTest { .async_send() .await; } + + pub async fn leave_workspace(&self, workspace_id: &str) { + let payload = UserWorkspaceIdPB { + workspace_id: workspace_id.to_string(), + }; + EventBuilder::new(self.clone()) + .event(UserEvent::LeaveWorkspace) + .payload(payload) + .async_send() + .await; + } } #[derive(Clone)] diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/member_test.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/member_test.rs index fff097275a..3f132234dd 100644 --- a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/member_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/member_test.rs @@ -1,3 +1,4 @@ +use crate::user::af_cloud_test::util::get_synced_workspaces; use event_integration::user_event::user_localhost_af_cloud; use event_integration::EventIntegrationTest; @@ -45,3 +46,28 @@ async fn af_cloud_delete_workspace_member_test() { assert_eq!(members.len(), 1); assert_eq!(members[0].email, user_1.email); } + +#[tokio::test] +async fn af_cloud_leave_workspace_test() { + user_localhost_af_cloud().await; + let test_1 = EventIntegrationTest::new().await; + let user_1 = test_1.af_cloud_sign_up().await; + + let test_2 = EventIntegrationTest::new().await; + let user_2 = test_2.af_cloud_sign_up().await; + + test_1 + .add_workspace_member(&user_1.workspace_id, &user_2.email) + .await; + + // test_2 should have 2 workspace + let workspaces = get_synced_workspaces(&test_2, user_2.id).await; + assert_eq!(workspaces.len(), 2); + + // user_2 leaves the workspace + test_2.leave_workspace(&user_1.workspace_id).await; + + // user_2 should have 1 workspace + let workspaces = get_synced_workspaces(&test_2, user_2.id).await; + assert_eq!(workspaces.len(), 1); +} diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/mod.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/mod.rs index d6b6b4b382..3fbfac5a1f 100644 --- a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/mod.rs +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/mod.rs @@ -2,4 +2,5 @@ mod anon_user_test; mod auth_test; mod import_af_data_folder_test; mod member_test; +mod util; mod workspace_test; diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/util.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/util.rs new file mode 100644 index 0000000000..3302f442de --- /dev/null +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/util.rs @@ -0,0 +1,27 @@ +use std::time::Duration; + +use event_integration::EventIntegrationTest; +use flowy_user::{ + entities::{RepeatedUserWorkspacePB, UserWorkspacePB}, + protobuf::UserNotification, +}; + +use crate::util::receive_with_timeout; + +pub async fn get_synced_workspaces( + test: &EventIntegrationTest, + user_id: i64, +) -> Vec { + let _workspaces = test.get_all_workspaces().await.items; + let sub_id = user_id.to_string(); + let rx = test + .notification_sender + .subscribe::( + &sub_id, + UserNotification::DidUpdateUserWorkspaces as i32, + ); + receive_with_timeout(rx, Duration::from_secs(30)) + .await + .unwrap() + .items +} diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs index 8f1968cec3..68adb22065 100644 --- a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/workspace_test.rs @@ -1,11 +1,7 @@ -use std::time::Duration; - use event_integration::user_event::user_localhost_af_cloud; use event_integration::EventIntegrationTest; -use flowy_user::entities::{RepeatedUserWorkspacePB, UserWorkspacePB}; -use flowy_user::protobuf::UserNotification; -use crate::util::receive_with_timeout; +use crate::user::af_cloud_test::util::get_synced_workspaces; #[tokio::test] async fn af_cloud_workspace_delete() { @@ -111,18 +107,3 @@ async fn af_cloud_open_workspace_test() { assert_eq!(views[1].name, "my first document".to_string()); assert_eq!(views[2].name, "my second document".to_string()); } - -async fn get_synced_workspaces(test: &EventIntegrationTest, user_id: i64) -> Vec { - let _workspaces = test.get_all_workspaces().await.items; - let sub_id = user_id.to_string(); - let rx = test - .notification_sender - .subscribe::( - &sub_id, - UserNotification::DidUpdateUserWorkspaces as i32, - ); - receive_with_timeout(rx, Duration::from_secs(30)) - .await - .unwrap() - .items -} diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs index 4e900385cb..835cc2680e 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs @@ -346,6 +346,16 @@ where Ok(()) }) } + + fn leave_workspace(&self, workspace_id: &str) -> FutureResult<(), FlowyError> { + let try_get_client = self.server.try_get_client(); + let workspace_id = workspace_id.to_string(); + FutureResult::new(async move { + let client = try_get_client?; + client.leave_workspace(&workspace_id).await?; + Ok(()) + }) + } } async fn get_admin_client(client: &Arc) -> FlowyResult { diff --git a/frontend/rust-lib/flowy-user-pub/src/cloud.rs b/frontend/rust-lib/flowy-user-pub/src/cloud.rs index 928e1ce7f0..2c13185658 100644 --- a/frontend/rust-lib/flowy-user-pub/src/cloud.rs +++ b/frontend/rust-lib/flowy-user-pub/src/cloud.rs @@ -234,6 +234,10 @@ pub trait UserCloudService: Send + Sync + 'static { workspace_id: &str, objects: Vec, ) -> FutureResult<(), FlowyError>; + + fn leave_workspace(&self, workspace_id: &str) -> FutureResult<(), FlowyError> { + FutureResult::new(async { Ok(()) }) + } } pub type UserUpdateReceiver = tokio::sync::mpsc::Receiver; diff --git a/frontend/rust-lib/flowy-user/src/event_handler.rs b/frontend/rust-lib/flowy-user/src/event_handler.rs index bff1ef891b..386b3a453b 100644 --- a/frontend/rust-lib/flowy-user/src/event_handler.rs +++ b/frontend/rust-lib/flowy-user/src/event_handler.rs @@ -709,3 +709,14 @@ pub async fn change_workspace_icon_handler( .await?; Ok(()) } + +#[tracing::instrument(level = "debug", skip_all, err)] +pub async fn leave_workspace_handler( + param: AFPluginData, + manager: AFPluginState>, +) -> Result<(), FlowyError> { + let workspace_id = param.into_inner().workspace_id; + let manager = upgrade_manager(manager)?; + manager.leave_workspace(&workspace_id).await?; + Ok(()) +} diff --git a/frontend/rust-lib/flowy-user/src/event_map.rs b/frontend/rust-lib/flowy-user/src/event_map.rs index 611fd9bad1..4bbf191f6c 100644 --- a/frontend/rust-lib/flowy-user/src/event_map.rs +++ b/frontend/rust-lib/flowy-user/src/event_map.rs @@ -64,6 +64,7 @@ pub fn init(user_manager: Weak) -> AFPlugin { .event(UserEvent::DeleteWorkspace, delete_workspace_handler) .event(UserEvent::RenameWorkspace, rename_workspace_handler) .event(UserEvent::ChangeWorkspaceIcon, change_workspace_icon_handler) + .event(UserEvent::LeaveWorkspace, leave_workspace_handler) } #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)] @@ -208,6 +209,9 @@ pub enum UserEvent { #[event(input = "ChangeWorkspaceIconPB")] ChangeWorkspaceIcon = 45, + + #[event(input = "UserWorkspaceIdPB")] + LeaveWorkspace = 46, } pub trait UserStatusCallback: Send + Sync + 'static { diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index 231f0299ba..64bec8ebd3 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -202,6 +202,19 @@ impl UserManager { save_user_workspaces(uid, conn, &[user_workspace]) } + pub async fn leave_workspace(&self, workspace_id: &str) -> FlowyResult<()> { + self + .cloud_services + .get_user_service()? + .leave_workspace(workspace_id) + .await?; + + // delete workspace from local sqlite db + let uid = self.user_id()?; + let conn = self.db_connection(uid)?; + delete_user_workspaces(conn, workspace_id) + } + pub async fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()> { self .cloud_services