From 395486adc3ae4dc15ad789a6c4a36101b170d782 Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Sat, 29 Jun 2024 11:23:23 +0800 Subject: [PATCH 1/4] fix: fix various publish issues --- libs/app-error/src/lib.rs | 4 -- libs/database/src/workspace.rs | 2 +- .../20240629035230_publish_collab_6.sql | 9 +++ src/api/workspace.rs | 1 + src/biz/workspace/access_control.rs | 6 +- src/biz/workspace/ops.rs | 10 +--- tests/workspace/publish.rs | 60 +++++++++++++++++-- 7 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 migrations/20240629035230_publish_collab_6.sql diff --git a/libs/app-error/src/lib.rs b/libs/app-error/src/lib.rs index 08e25949..54b88a00 100644 --- a/libs/app-error/src/lib.rs +++ b/libs/app-error/src/lib.rs @@ -117,9 +117,6 @@ pub enum AppError { #[error("{0}")] OverrideWithIncorrectData(String), - #[error("{0}")] - PublishNamespaceNotSet(String), - #[error("{0}")] PublishNamespaceAlreadyTaken(String), } @@ -180,7 +177,6 @@ impl AppError { AppError::NoRequiredData(_) => ErrorCode::NoRequiredData, AppError::OverrideWithIncorrectData(_) => ErrorCode::OverrideWithIncorrectData, AppError::Utf8Error(_) => ErrorCode::Internal, - AppError::PublishNamespaceNotSet(_) => ErrorCode::PublishNamespaceNotSet, AppError::PublishNamespaceAlreadyTaken(_) => ErrorCode::PublishNamespaceAlreadyTaken, } } diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index 926e3c02..8ce830cf 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -887,7 +887,7 @@ pub async fn update_workspace_publish_namespace<'a, E: Executor<'a, Database = P pub async fn select_workspace_publish_namespace<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, -) -> Result, AppError> { +) -> Result { let res = sqlx::query_scalar!( r#" SELECT publish_namespace diff --git a/migrations/20240629035230_publish_collab_6.sql b/migrations/20240629035230_publish_collab_6.sql new file mode 100644 index 00000000..7a6ae459 --- /dev/null +++ b/migrations/20240629035230_publish_collab_6.sql @@ -0,0 +1,9 @@ +-- Update existing null values to ensure no nulls are present before adding NOT NULL constraint +UPDATE public.af_workspace +SET publish_namespace = uuid_generate_v4()::text +WHERE publish_namespace IS NULL; + +-- Alter the column to set NOT NULL constraint and a default value +ALTER TABLE public.af_workspace +ALTER COLUMN publish_namespace SET NOT NULL, +ALTER COLUMN publish_namespace SET DEFAULT uuid_generate_v4()::text; diff --git a/src/api/workspace.rs b/src/api/workspace.rs index e0f48459..f612bde8 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -47,6 +47,7 @@ pub const WORKSPACE_MEMBER_PATTERN: &str = "/api/workspace/{workspace_id}/member pub const WORKSPACE_INVITE_PATTERN: &str = "/api/workspace/{workspace_id}/invite"; pub const COLLAB_PATTERN: &str = "/api/workspace/{workspace_id}/collab/{object_id}"; pub const V1_COLLAB_PATTERN: &str = "/api/workspace/v1/{workspace_id}/collab/{object_id}"; +pub const WORKSPACE_PUBLISH_PATTERN: &str = "/api/workspace/{workspace_id}/publish"; pub const WORKSPACE_PUBLISH_NAMESPACE_PATTERN: &str = "/api/workspace/{workspace_id}/publish-namespace"; diff --git a/src/biz/workspace/access_control.rs b/src/biz/workspace/access_control.rs index deecf616..652bc02e 100644 --- a/src/biz/workspace/access_control.rs +++ b/src/biz/workspace/access_control.rs @@ -21,7 +21,7 @@ use database_entity::dto::AFRole; use crate::api::workspace::{ WORKSPACE_INVITE_PATTERN, WORKSPACE_MEMBER_PATTERN, WORKSPACE_PATTERN, - WORKSPACE_PUBLISH_NAMESPACE_PATTERN, + WORKSPACE_PUBLISH_NAMESPACE_PATTERN, WORKSPACE_PUBLISH_PATTERN, }; use crate::middleware::access_control_mw::{AccessResource, MiddlewareAccessControl}; use crate::state::UserCache; @@ -48,6 +48,10 @@ where ], // Require role for given resources require_role_rules: vec![ + ( + ResourceDef::new(WORKSPACE_PUBLISH_PATTERN), + [(Method::DELETE, AFRole::Member)].into(), + ), // Only the Owner can manager the workspace members ( ResourceDef::new(WORKSPACE_MEMBER_PATTERN), diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index 16bbeae6..7fa8de16 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -132,15 +132,7 @@ pub async fn get_workspace_publish_namespace( pg_pool: &PgPool, workspace_id: &Uuid, ) -> Result { - let namespace = match select_workspace_publish_namespace(pg_pool, workspace_id).await? { - Some(namespace) => namespace, - None => { - return Err(AppError::PublishNamespaceNotSet( - "publish namespace is not set for the workspace".to_string(), - )) - }, - }; - Ok(namespace) + select_workspace_publish_namespace(pg_pool, workspace_id).await } pub async fn publish_collabs( diff --git a/tests/workspace/publish.rs b/tests/workspace/publish.rs index c0ca9986..bab581f0 100644 --- a/tests/workspace/publish.rs +++ b/tests/workspace/publish.rs @@ -1,4 +1,5 @@ -use client_api::entity::{PublishCollabItem, PublishCollabMetadata}; +use client_api::entity::{AFRole, PublishCollabItem, PublishCollabMetadata}; +use client_api_test::TestClient; use client_api_test::{generate_unique_registered_user_client, localhost_client}; #[tokio::test] @@ -7,13 +8,11 @@ async fn test_set_publish_namespace_set() { let workspace_id = get_first_workspace_string(&c).await; { - // cannot get namespace if not set - let err = c + // can get namespace before setting, which is random string + let _ = c .get_workspace_publish_namespace(&workspace_id.to_string()) .await - .err() .unwrap(); - assert_eq!(format!("{:?}", err.code), "PublishNamespaceNotSet"); } let namespace = uuid::Uuid::new_v4().to_string(); @@ -127,6 +126,20 @@ async fn test_publish_doc() { .await .unwrap(); assert_eq!(blob, "yrs_encoded_data_1"); + + let publish_info = guest_client + .get_published_collab_info(&view_id_2) + .await + .unwrap(); + assert_eq!(publish_info.namespace, Some(my_namespace.clone())); + assert_eq!(publish_info.publish_name, publish_name_2); + assert_eq!(publish_info.view_id, view_id_2); + + let blob = guest_client + .get_published_collab_blob(&my_namespace, publish_name_2) + .await + .unwrap(); + assert_eq!(blob, "yrs_encoded_data_2"); } c.unpublish_collabs(&workspace_id, &[view_id_1]) @@ -200,6 +213,43 @@ async fn get_first_workspace_string(c: &client_api::Client) -> String { .to_string() } +#[tokio::test] +async fn workspace_member_publish_unpublish() { + let client_1 = TestClient::new_user_without_ws_conn().await; + let workspace_id = client_1.workspace_id().await; + let client_2 = TestClient::new_user_without_ws_conn().await; + client_1 + .invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member) + .await + .unwrap(); + + let view_id = uuid::Uuid::new_v4(); + // member can publish without owner setting namespace + client_2 + .api_client + .publish_collabs::( + &workspace_id, + vec![PublishCollabItem { + meta: PublishCollabMetadata { + view_id, + publish_name: "publish-name-1".to_string(), + metadata: MyCustomMetadata { + title: "my_title_1".to_string(), + }, + }, + data: "yrs_encoded_data_1".as_bytes(), + }], + ) + .await + .unwrap(); + + client_2 + .api_client + .unpublish_collabs(&workspace_id, &[view_id]) + .await + .unwrap(); +} + #[derive(serde::Serialize, serde::Deserialize)] struct MyCustomMetadata { title: String, From 9aef3613b6623ca1c0d209cd351b0b8b05f01ea3 Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Sat, 29 Jun 2024 12:34:45 +0800 Subject: [PATCH 2/4] fix: sqlx --- ...a5a9835c82a2ea8f87499d6cdd570979d0c3e001fa40e140a64fedf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.sqlx/query-0d4b11e74a5a9835c82a2ea8f87499d6cdd570979d0c3e001fa40e140a64fedf.json b/.sqlx/query-0d4b11e74a5a9835c82a2ea8f87499d6cdd570979d0c3e001fa40e140a64fedf.json index 3b2c8141..ed3b47a8 100644 --- a/.sqlx/query-0d4b11e74a5a9835c82a2ea8f87499d6cdd570979d0c3e001fa40e140a64fedf.json +++ b/.sqlx/query-0d4b11e74a5a9835c82a2ea8f87499d6cdd570979d0c3e001fa40e140a64fedf.json @@ -15,7 +15,7 @@ ] }, "nullable": [ - true + false ] }, "hash": "0d4b11e74a5a9835c82a2ea8f87499d6cdd570979d0c3e001fa40e140a64fedf" From d06dbadc05ebf7547f1e399959643aa182ae8cd3 Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Sat, 29 Jun 2024 13:50:56 +0800 Subject: [PATCH 3/4] fix: update publish collab --- libs/database/src/workspace.rs | 5 +++- tests/workspace/publish.rs | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index 8ce830cf..f654aa05 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -932,7 +932,10 @@ pub async fn insert_or_replace_publish_collab_metas<'a, E: Executor<'a, Database $6::bytea[] ) ON CONFLICT (workspace_id, view_id) DO UPDATE - SET metadata = EXCLUDED.metadata + SET metadata = EXCLUDED.metadata, + blob = EXCLUDED.blob, + published_by = EXCLUDED.published_by, + publish_name = EXCLUDED.publish_name "#, workspace_id, &view_ids, diff --git a/tests/workspace/publish.rs b/tests/workspace/publish.rs index bab581f0..bb15b636 100644 --- a/tests/workspace/publish.rs +++ b/tests/workspace/publish.rs @@ -142,6 +142,51 @@ async fn test_publish_doc() { assert_eq!(blob, "yrs_encoded_data_2"); } + // updates data + c.publish_collabs::( + &workspace_id, + vec![ + PublishCollabItem { + meta: PublishCollabMetadata { + view_id: view_id_1, + publish_name: publish_name_1.to_string(), + metadata: MyCustomMetadata { + title: "my_title_1".to_string(), + }, + }, + data: "yrs_encoded_data_3".as_bytes(), + }, + PublishCollabItem { + meta: PublishCollabMetadata { + view_id: view_id_2, + publish_name: publish_name_2.to_string(), + metadata: MyCustomMetadata { + title: "my_title_2".to_string(), + }, + }, + data: "yrs_encoded_data_4".as_bytes(), + }, + ], + ) + .await + .unwrap(); + + { + // should see updated data + let guest_client = localhost_client(); + let blob = guest_client + .get_published_collab_blob(&my_namespace, publish_name_1) + .await + .unwrap(); + assert_eq!(blob, "yrs_encoded_data_3"); + + let blob = guest_client + .get_published_collab_blob(&my_namespace, publish_name_2) + .await + .unwrap(); + assert_eq!(blob, "yrs_encoded_data_4"); + } + c.unpublish_collabs(&workspace_id, &[view_id_1]) .await .unwrap(); From 315dd0c9234bd90f81000c3aa5715d740c49433f Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Sat, 29 Jun 2024 13:57:36 +0800 Subject: [PATCH 4/4] fix: sqlx --- ...0186aab8a6f881dfec948a99c96d6f02bcf11eae7aeeea62e5a0.json} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .sqlx/{query-3ad29cddcb72f375c98db91f697cbe957267575acd14ec01af2425617eea5628.json => query-0857da7f3d100186aab8a6f881dfec948a99c96d6f02bcf11eae7aeeea62e5a0.json} (77%) diff --git a/.sqlx/query-3ad29cddcb72f375c98db91f697cbe957267575acd14ec01af2425617eea5628.json b/.sqlx/query-0857da7f3d100186aab8a6f881dfec948a99c96d6f02bcf11eae7aeeea62e5a0.json similarity index 77% rename from .sqlx/query-3ad29cddcb72f375c98db91f697cbe957267575acd14ec01af2425617eea5628.json rename to .sqlx/query-0857da7f3d100186aab8a6f881dfec948a99c96d6f02bcf11eae7aeeea62e5a0.json index b7c5e885..3d2465a7 100644 --- a/.sqlx/query-3ad29cddcb72f375c98db91f697cbe957267575acd14ec01af2425617eea5628.json +++ b/.sqlx/query-0857da7f3d100186aab8a6f881dfec948a99c96d6f02bcf11eae7aeeea62e5a0.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO af_published_collab (workspace_id, view_id, publish_name, published_by, metadata, blob)\n SELECT * FROM UNNEST(\n (SELECT array_agg((SELECT $1::uuid)) FROM generate_series(1, $7))::uuid[],\n $2::uuid[],\n $3::text[],\n (SELECT array_agg((SELECT uid FROM af_user WHERE uuid = $4)) FROM generate_series(1, $7))::bigint[],\n $5::jsonb[],\n $6::bytea[]\n )\n ON CONFLICT (workspace_id, view_id) DO UPDATE\n SET metadata = EXCLUDED.metadata\n ", + "query": "\n INSERT INTO af_published_collab (workspace_id, view_id, publish_name, published_by, metadata, blob)\n SELECT * FROM UNNEST(\n (SELECT array_agg((SELECT $1::uuid)) FROM generate_series(1, $7))::uuid[],\n $2::uuid[],\n $3::text[],\n (SELECT array_agg((SELECT uid FROM af_user WHERE uuid = $4)) FROM generate_series(1, $7))::bigint[],\n $5::jsonb[],\n $6::bytea[]\n )\n ON CONFLICT (workspace_id, view_id) DO UPDATE\n SET metadata = EXCLUDED.metadata,\n blob = EXCLUDED.blob,\n published_by = EXCLUDED.published_by,\n publish_name = EXCLUDED.publish_name\n ", "describe": { "columns": [], "parameters": { @@ -16,5 +16,5 @@ }, "nullable": [] }, - "hash": "3ad29cddcb72f375c98db91f697cbe957267575acd14ec01af2425617eea5628" + "hash": "0857da7f3d100186aab8a6f881dfec948a99c96d6f02bcf11eae7aeeea62e5a0" }