diff --git a/.sqlx/query-f1c55a5255b1c7e5c96e21cb8557b6d46f713630d819f94cc7a966dbdaf00a2a.json b/.sqlx/query-f1c55a5255b1c7e5c96e21cb8557b6d46f713630d819f94cc7a966dbdaf00a2a.json new file mode 100644 index 00000000..25e35150 --- /dev/null +++ b/.sqlx/query-f1c55a5255b1c7e5c96e21cb8557b6d46f713630d819f94cc7a966dbdaf00a2a.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT workspace_id, COUNT(*) AS member_count\n FROM af_workspace_member\n WHERE workspace_id = ANY($1)\n GROUP BY workspace_id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "member_count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "UuidArray" + ] + }, + "nullable": [ + false, + null + ] + }, + "hash": "f1c55a5255b1c7e5c96e21cb8557b6d46f713630d819f94cc7a966dbdaf00a2a" +} diff --git a/Cargo.lock b/Cargo.lock index 49551561..90b26061 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1664,9 +1664,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" dependencies = [ "serde", ] @@ -4199,6 +4199,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.45" @@ -4300,9 +4306,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.63" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.4.2", "cfg-if 1.0.0", @@ -4341,9 +4347,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.99" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -4466,9 +4472,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.9" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", "thiserror", @@ -4477,9 +4483,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.9" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" dependencies = [ "pest", "pest_generator", @@ -4487,9 +4493,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.9" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" dependencies = [ "pest", "pest_meta", @@ -4500,9 +4506,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.9" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" dependencies = [ "once_cell", "pest", @@ -6575,12 +6581,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -6595,10 +6602,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] diff --git a/libs/client-api-test/src/client.rs b/libs/client-api-test/src/client.rs index e9961e68..7a4ae073 100644 --- a/libs/client-api-test/src/client.rs +++ b/libs/client-api-test/src/client.rs @@ -72,7 +72,6 @@ pub async fn workspace_id_from_client(c: &Client) -> String { c.get_workspaces() .await .unwrap() - .0 .first() .unwrap() .workspace_id diff --git a/libs/client-api-test/src/test_client.rs b/libs/client-api-test/src/test_client.rs index 12318960..4afbbac9 100644 --- a/libs/client-api-test/src/test_client.rs +++ b/libs/client-api-test/src/test_client.rs @@ -391,7 +391,6 @@ impl TestClient { .get_workspaces() .await .unwrap() - .0 .first() .unwrap() .workspace_id diff --git a/libs/client-api/src/http.rs b/libs/client-api/src/http.rs index 9db30dfc..4f8c8316 100644 --- a/libs/client-api/src/http.rs +++ b/libs/client-api/src/http.rs @@ -1,5 +1,6 @@ use crate::notify::{ClientToken, TokenStateReceiver}; use app_error::AppError; +use client_api_entity::workspace_dto::QueryWorkspaceParam; use client_api_entity::AuthProvider; use client_api_entity::CollabType; use gotrue::grant::PasswordGrant; @@ -16,7 +17,7 @@ use reqwest::Method; use reqwest::RequestBuilder; use client_api_entity::{ - AFSnapshotMeta, AFSnapshotMetas, AFUserProfile, AFUserWorkspaceInfo, AFWorkspace, AFWorkspaces, + AFSnapshotMeta, AFSnapshotMetas, AFUserProfile, AFUserWorkspaceInfo, AFWorkspace, QuerySnapshotParams, SnapshotData, }; use semver::Version; @@ -627,16 +628,26 @@ impl Client { AppResponse::<()>::from_response(resp).await?.into_error() } + pub async fn get_workspaces(&self) -> Result, AppResponseError> { + self + .get_workspaces_opt(QueryWorkspaceParam::default()) + .await + } + #[instrument(level = "info", skip_all, err)] - pub async fn get_workspaces(&self) -> Result { + pub async fn get_workspaces_opt( + &self, + param: QueryWorkspaceParam, + ) -> Result, AppResponseError> { let url = format!("{}/api/workspace", self.base_url); let resp = self .http_client_with_auth(Method::GET, &url) .await? + .query(¶m) .send() .await?; log_request_id(&resp); - AppResponse::::from_response(resp) + AppResponse::>::from_response(resp) .await? .into_data() } diff --git a/libs/database-entity/src/dto.rs b/libs/database-entity/src/dto.rs index f3363d13..8896657d 100644 --- a/libs/database-entity/src/dto.rs +++ b/libs/database-entity/src/dto.rs @@ -530,11 +530,9 @@ pub struct AFWorkspace { pub workspace_name: String, pub created_at: DateTime, pub icon: String, + pub member_count: Option, } -#[derive(Serialize, Deserialize)] -pub struct AFWorkspaces(pub Vec); - #[derive(Serialize, Deserialize)] pub struct AFWorkspaceSettings { #[serde(default)] diff --git a/libs/database/src/pg_row.rs b/libs/database/src/pg_row.rs index 0ce9b9a7..8ad6ff9d 100644 --- a/libs/database/src/pg_row.rs +++ b/libs/database/src/pg_row.rs @@ -47,6 +47,7 @@ impl TryFrom for AFWorkspace { workspace_name, created_at, icon, + member_count: None, }) } } diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index 5c5be795..2a5d8fff 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -674,6 +674,39 @@ pub async fn select_all_user_workspaces<'a, E: Executor<'a, Database = Postgres> Ok(workspaces) } +pub async fn select_member_count_for_workspaces<'a, E: Executor<'a, Database = Postgres>>( + executor: E, + workspace_ids: &[Uuid], +) -> Result, AppError> { + let query_res = sqlx::query!( + r#" + SELECT workspace_id, COUNT(*) AS member_count + FROM af_workspace_member + WHERE workspace_id = ANY($1) + GROUP BY workspace_id + "#, + workspace_ids + ) + .fetch_all(executor) + .await?; + + let mut ret = HashMap::with_capacity(workspace_ids.len()); + for row in query_res { + let count = match row.member_count { + Some(c) => c, + None => continue, + }; + ret.insert(row.workspace_id, count); + } + for workspace_id in workspace_ids.iter() { + if !ret.contains_key(workspace_id) { + ret.insert(*workspace_id, 0); + } + } + + Ok(ret) +} + pub async fn select_permission( pool: &PgPool, permission_id: &i64, diff --git a/libs/shared-entity/src/dto/workspace_dto.rs b/libs/shared-entity/src/dto/workspace_dto.rs index 6d0ce3f7..d8d21445 100644 --- a/libs/shared-entity/src/dto/workspace_dto.rs +++ b/libs/shared-entity/src/dto/workspace_dto.rs @@ -120,3 +120,8 @@ pub struct CollabResponse { #[serde(default)] pub object_id: String, } + +#[derive(Default, Debug, Deserialize, Serialize)] +pub struct QueryWorkspaceParam { + pub include_member_count: Option, +} diff --git a/src/api/workspace.rs b/src/api/workspace.rs index 91a3fcab..1513fff4 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -241,28 +241,20 @@ async fn delete_workspace_handler( Ok(AppResponse::Ok().into()) } -// TODO: also get shared workspaces +/// Get all user owned and shared workspaces #[instrument(skip_all, err)] async fn list_workspace_handler( uuid: UserUuid, state: Data, -) -> Result> { - let rows = workspace::ops::get_all_user_workspaces(&state.pg_pool, &uuid).await?; - let workspaces = rows - .into_iter() - .flat_map(|row| { - let result = AFWorkspace::try_from(row); - if let Err(err) = &result { - event!( - tracing::Level::ERROR, - "Failed to convert workspace row to AFWorkspace: {:?}", - err - ); - } - result - }) - .collect::>(); - Ok(AppResponse::Ok().with_data(AFWorkspaces(workspaces)).into()) + query: web::Query, +) -> Result>> { + let workspaces = workspace::ops::get_all_user_workspaces( + &state.pg_pool, + &uuid, + query.into_inner().include_member_count.unwrap_or(false), + ) + .await?; + Ok(AppResponse::Ok().with_data(workspaces).into()) } #[instrument(skip(payload, state), err)] diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index 54ec397c..8b163809 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -15,19 +15,20 @@ use app_error::{AppError, ErrorCode}; use appflowy_collaborate::collab::storage::CollabAccessControlStorage; use database::collab::upsert_collab_member_with_txn; use database::file::s3_client_impl::S3BucketStorage; -use database::pg_row::{AFWorkspaceMemberRow, AFWorkspaceRow}; +use database::pg_row::AFWorkspaceMemberRow; use database::user::select_uid_from_email; use database::workspace::{ change_workspace_icon, delete_from_workspace, delete_published_collabs, delete_workspace_members, get_invitation_by_id, insert_comment_to_published_view, insert_or_replace_publish_collab_metas, insert_user_workspace, insert_workspace_invitation, rename_workspace, select_all_user_workspaces, - select_comments_for_published_view, select_publish_collab_meta, select_published_collab_blob, - select_published_collab_info, select_user_is_allowed_to_delete_comment, - select_user_is_collab_publisher_for_all_views, select_user_is_workspace_owner, select_workspace, - select_workspace_invitations_for_user, select_workspace_member, select_workspace_member_list, - select_workspace_publish_namespace, select_workspace_publish_namespace_exists, - select_workspace_settings, select_workspace_total_collab_bytes, update_comment_deletion_status, + select_comments_for_published_view, select_member_count_for_workspaces, + select_publish_collab_meta, select_published_collab_blob, select_published_collab_info, + select_user_is_allowed_to_delete_comment, select_user_is_collab_publisher_for_all_views, + select_user_is_workspace_owner, select_workspace, select_workspace_invitations_for_user, + select_workspace_member, select_workspace_member_list, select_workspace_publish_namespace, + select_workspace_publish_namespace_exists, select_workspace_settings, + select_workspace_total_collab_bytes, update_comment_deletion_status, update_updated_at_of_workspace, update_workspace_invitation_set_status_accepted, update_workspace_publish_namespace, upsert_workspace_member, upsert_workspace_member_with_txn, upsert_workspace_settings, @@ -219,8 +220,32 @@ pub async fn delete_published_workspace_collab( pub async fn get_all_user_workspaces( pg_pool: &PgPool, user_uuid: &Uuid, -) -> Result, AppResponseError> { + include_member_count: bool, +) -> Result, AppResponseError> { let workspaces = select_all_user_workspaces(pg_pool, user_uuid).await?; + let mut workspaces = workspaces + .into_iter() + .flat_map(|row| { + let result = AFWorkspace::try_from(row); + if let Err(err) = &result { + tracing::error!("Failed to convert workspace row to AFWorkspace: {:?}", err); + } + result + }) + .collect::>(); + if include_member_count { + let ids = workspaces + .iter() + .map(|row| row.workspace_id) + .collect::>(); + let member_count_by_workspace_id = select_member_count_for_workspaces(pg_pool, &ids).await?; + for workspace in workspaces.iter_mut() { + if let Some(member_count) = member_count_by_workspace_id.get(&workspace.workspace_id) { + workspace.member_count = Some(*member_count); + } + } + } + Ok(workspaces) } diff --git a/tests/file_test/put_and_get.rs b/tests/file_test/put_and_get.rs index 04588913..dc231d37 100644 --- a/tests/file_test/put_and_get.rs +++ b/tests/file_test/put_and_get.rs @@ -16,7 +16,6 @@ async fn get_but_not_exists() { .get_workspaces() .await .unwrap() - .0 .first() .unwrap() .workspace_id diff --git a/tests/gotrue/admin.rs b/tests/gotrue/admin.rs index 8c87740f..a66f45bd 100644 --- a/tests/gotrue/admin.rs +++ b/tests/gotrue/admin.rs @@ -152,7 +152,7 @@ async fn admin_generate_link_and_user_sign_in_and_invite() { assert!(is_new); let workspaces = client.get_workspaces().await.unwrap(); - assert_eq!(workspaces.0.len(), 1); + assert_eq!(workspaces.len(), 1); let friend_email = generate_unique_email(); client.invite(&friend_email).await.unwrap(); diff --git a/tests/user/delete.rs b/tests/user/delete.rs index 725c85ff..c8324ed7 100644 --- a/tests/user/delete.rs +++ b/tests/user/delete.rs @@ -4,7 +4,7 @@ use gotrue::params::{AdminDeleteUserParams, AdminUserParams}; #[tokio::test] async fn admin_delete_create_same_user_hard() { let (client, user) = generate_unique_registered_user_client().await; - let workspaces = client.get_workspaces().await.unwrap().0; + let workspaces = client.get_workspaces().await.unwrap(); assert_eq!(workspaces.len(), 1); let workspace_id = workspaces[0].workspace_id; let user_uuid = client.get_profile().await.unwrap().uuid; @@ -48,7 +48,7 @@ async fn admin_delete_create_same_user_hard() { .await .unwrap(); let recreated_user_uuid = client.get_profile().await.unwrap().uuid; - let recreated_workspace_uuid = client.get_workspaces().await.unwrap().0[0].workspace_id; + let recreated_workspace_uuid = client.get_workspaces().await.unwrap()[0].workspace_id; assert_ne!(user_uuid, recreated_user_uuid); assert_ne!(workspace_id, recreated_workspace_uuid); } diff --git a/tests/user/sign_in.rs b/tests/user/sign_in.rs index c2d91c26..ffb749c1 100644 --- a/tests/user/sign_in.rs +++ b/tests/user/sign_in.rs @@ -63,7 +63,7 @@ async fn sign_in_success() { .is_some()); let workspaces = c.get_workspaces().await.unwrap(); - assert_eq!(workspaces.0.len(), 1); + assert_eq!(workspaces.len(), 1); let _ = c.get_profile().await.unwrap(); } @@ -78,7 +78,7 @@ async fn sign_in_success() { // workspaces should be the same let workspaces = c.get_workspaces().await.unwrap(); - assert_eq!(workspaces.0.len(), 1); + assert_eq!(workspaces.len(), 1); } } diff --git a/tests/workspace/invitation_crud.rs b/tests/workspace/invitation_crud.rs index 04bc6e86..73a832a9 100644 --- a/tests/workspace/invitation_crud.rs +++ b/tests/workspace/invitation_crud.rs @@ -1,6 +1,6 @@ use client_api_test::generate_unique_registered_user_client; use database_entity::dto::{AFRole, AFWorkspaceInvitationStatus}; -use shared_entity::dto::workspace_dto::WorkspaceMemberInvitation; +use shared_entity::dto::workspace_dto::{QueryWorkspaceParam, WorkspaceMemberInvitation}; #[tokio::test] async fn invite_workspace_crud() { @@ -9,7 +9,6 @@ async fn invite_workspace_crud() { .get_workspaces() .await .unwrap() - .0 .first() .unwrap() .workspace_id; @@ -67,4 +66,17 @@ async fn invite_workspace_crud() { .await .unwrap(); assert_eq!(accepted_invs.len(), 1); + + // workspace now have 2 members + let member_count = alice_client + .get_workspaces_opt(QueryWorkspaceParam { + include_member_count: Some(true), + }) + .await + .unwrap() + .first() + .unwrap() + .member_count + .unwrap(); + assert_eq!(member_count, 2); } diff --git a/tests/workspace/member_crud.rs b/tests/workspace/member_crud.rs index adec5df6..300f18b3 100644 --- a/tests/workspace/member_crud.rs +++ b/tests/workspace/member_crud.rs @@ -148,7 +148,7 @@ async fn add_not_exist_workspace_members() { .unwrap(); let workspaces = invited_client.get_workspaces().await.unwrap(); - assert_eq!(workspaces.0.len(), 2); + assert_eq!(workspaces.len(), 2); } #[tokio::test] diff --git a/tests/workspace/publish.rs b/tests/workspace/publish.rs index 96617181..d91ba26f 100644 --- a/tests/workspace/publish.rs +++ b/tests/workspace/publish.rs @@ -445,7 +445,6 @@ async fn get_first_workspace_string(c: &client_api::Client) -> String { c.get_workspaces() .await .unwrap() - .0 .first() .unwrap() .workspace_id diff --git a/tests/workspace/workspace_crud.rs b/tests/workspace/workspace_crud.rs index 37030977..5f484ae7 100644 --- a/tests/workspace/workspace_crud.rs +++ b/tests/workspace/workspace_crud.rs @@ -8,7 +8,7 @@ use shared_entity::dto::workspace_dto::PatchWorkspaceParam; async fn add_and_delete_workspace_for_user() { let (c, _user) = generate_unique_registered_user_client().await; let workspaces = c.get_workspaces().await.unwrap(); - assert_eq!(workspaces.0.len(), 1); + assert_eq!(workspaces.len(), 1); let newly_added_workspace = c .create_workspace(CreateWorkspaceParam { workspace_name: Some("my_workspace".to_string()), @@ -16,10 +16,9 @@ async fn add_and_delete_workspace_for_user() { .await .unwrap(); let workspaces = c.get_workspaces().await.unwrap(); - assert_eq!(workspaces.0.len(), 2); + assert_eq!(workspaces.len(), 2); let _ = workspaces - .0 .iter() .find(|w| { w.workspace_name == "my_workspace" && w.workspace_id == newly_added_workspace.workspace_id @@ -39,7 +38,7 @@ async fn add_and_delete_workspace_for_user() { c.delete_workspace(&workspace_id).await.unwrap(); let workspaces = c.get_workspaces().await.unwrap(); - assert_eq!(workspaces.0.len(), 1); + assert_eq!(workspaces.len(), 1); } #[tokio::test] @@ -49,7 +48,6 @@ async fn test_workspace_rename_and_icon_change() { .get_workspaces() .await .unwrap() - .0 .first() .unwrap() .workspace_id; @@ -66,7 +64,6 @@ async fn test_workspace_rename_and_icon_change() { let workspaces = c.get_workspaces().await.expect("Failed to get workspaces"); let actual_new_name = &workspaces - .0 .first() .expect("No workspace found") .workspace_name; @@ -83,7 +80,6 @@ async fn test_workspace_rename_and_icon_change() { .expect("Failed to rename workspace"); let workspaces = c.get_workspaces().await.expect("Failed to get workspaces"); let actual_new_name = &workspaces - .0 .first() .expect("No workspace found") .workspace_name; @@ -98,7 +94,7 @@ async fn test_workspace_rename_and_icon_change() { .await .expect("Failed to change icon"); let workspaces = c.get_workspaces().await.expect("Failed to get workspaces"); - let icon = &workspaces.0.first().expect("No workspace found").icon; + let icon = &workspaces.first().expect("No workspace found").icon; assert_eq!(icon, "icon123"); } { @@ -110,7 +106,7 @@ async fn test_workspace_rename_and_icon_change() { .await .expect("Failed to change icon"); let workspaces = c.get_workspaces().await.expect("Failed to get workspaces"); - let workspace = workspaces.0.first().expect("No workspace found"); + let workspace = workspaces.first().expect("No workspace found"); let icon = workspace.icon.as_str(); let name = workspace.workspace_name.as_str(); diff --git a/tests/workspace/workspace_settings.rs b/tests/workspace/workspace_settings.rs index 4166cdee..47e6ef19 100644 --- a/tests/workspace/workspace_settings.rs +++ b/tests/workspace/workspace_settings.rs @@ -8,7 +8,7 @@ use uuid::Uuid; #[tokio::test] async fn get_and_set_workspace_by_owner() { let (c, _user) = generate_unique_registered_user_client().await; - let workspaces = c.get_workspaces().await.unwrap().0; + let workspaces = c.get_workspaces().await.unwrap(); let workspace_id = workspaces.first().unwrap().workspace_id.to_string(); let mut settings = c.get_workspace_settings(&workspace_id).await.unwrap(); @@ -32,7 +32,7 @@ async fn get_and_set_workspace_by_owner() { #[tokio::test] async fn get_and_set_workspace_by_non_owner() { let (alice_client, _alice) = generate_unique_registered_user_client().await; - let workspaces = alice_client.get_workspaces().await.unwrap().0; + let workspaces = alice_client.get_workspaces().await.unwrap(); let alice_workspace_id = workspaces.first().unwrap().workspace_id; let (bob_client, bob) = generate_unique_registered_user_client().await;