diff --git a/libs/client-api/src/http_publish.rs b/libs/client-api/src/http_publish.rs index f5a20c1e..db6d02cf 100644 --- a/libs/client-api/src/http_publish.rs +++ b/libs/client-api/src/http_publish.rs @@ -1,7 +1,10 @@ use bytes::Bytes; use database_entity::dto::UpdatePublishNamespace; use reqwest::{Body, Method}; -use shared_entity::response::{AppResponse, AppResponseError}; +use shared_entity::{ + dto::workspace_dto::PublishInfo, + response::{AppResponse, AppResponseError}, +}; use crate::Client; @@ -112,23 +115,13 @@ impl Client { } } -#[derive(Debug, serde::Deserialize)] -pub struct PublishInfo { - pub namespace: String, - pub doc_name: String, - pub view_id: uuid::Uuid, -} - // Guest API (no login required) impl Client { pub async fn get_published_info( &self, view_id: &uuid::Uuid, ) -> Result { - let url = format!( - "{}/api/workspace/published_info/{}", - self.base_url, view_id, - ); + let url = format!("{}/api/workspace/published_info/{}", self.base_url, view_id,); let resp = self.cloud_client.get(&url).send().await?; AppResponse::::from_response(resp) diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index 7691e77c..fea37239 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -147,7 +147,7 @@ pub async fn select_user_is_collab_publisher( pg_pool: &PgPool, user_uuid: &Uuid, workspace_uuid: &Uuid, - doc_name: &str, + view_id: &Uuid, ) -> Result { let exists = sqlx::query_scalar!( r#" @@ -155,12 +155,12 @@ pub async fn select_user_is_collab_publisher( SELECT 1 FROM af_published_collab WHERE workspace_id = $1 - AND doc_name = $2 + AND view_id = $2 AND published_by = (SELECT uid FROM af_user WHERE uuid = $3) ); "#, workspace_uuid, - doc_name, + view_id, user_uuid, ) .fetch_one(pg_pool) @@ -890,20 +890,22 @@ pub async fn select_workspace_publish_namespace<'a, E: Executor<'a, Database = P pub async fn insert_or_replace_publish_collab_meta<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, + view_id: &Uuid, doc_name: &str, publisher_uuid: &Uuid, metadata: &serde_json::Value, ) -> Result<(), AppError> { let res = sqlx::query!( r#" - INSERT INTO af_published_collab (doc_name, published_by, workspace_id, metadata) - VALUES ($1, (SELECT uid FROM af_user WHERE uuid = $2), $3, $4) - ON CONFLICT (workspace_id, doc_name) DO UPDATE - SET metadata = $4 + INSERT INTO af_published_collab (workspace_id, view_id, doc_name, published_by, metadata) + VALUES ($1, $2, $3, (SELECT uid FROM af_user WHERE uuid = $4), $5) + ON CONFLICT (workspace_id, view_id) DO UPDATE + SET metadata = $5 "#, + workspace_id, + view_id, doc_name, publisher_uuid, - workspace_id, metadata, ) .execute(executor) @@ -945,24 +947,24 @@ pub async fn select_publish_collab_meta<'a, E: Executor<'a, Database = Postgres> pub async fn delete_published_collab<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, - doc_name: &str, + view_id: &Uuid, ) -> Result<(), AppError> { let res = sqlx::query!( r#" DELETE FROM af_published_collab - WHERE workspace_id = $1 AND doc_name = $2 + WHERE workspace_id = $1 AND view_id = $2 "#, workspace_id, - doc_name, + view_id, ) .execute(executor) .await?; if res.rows_affected() != 1 { tracing::error!( - "Failed to delete published collab, workspace_id: {}, doc_name: {}, rows_affected: {}", + "Failed to delete published collab, workspace_id: {}, view_id: {}, rows_affected: {}", workspace_id, - doc_name, + view_id, res.rows_affected() ); } @@ -974,18 +976,19 @@ pub async fn delete_published_collab<'a, E: Executor<'a, Database = Postgres>>( pub async fn insert_or_replace_published_collab_blob<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, - doc_name: &str, + view_id: &Uuid, blob: &[u8], ) -> Result<(), AppError> { let res = sqlx::query!( r#" UPDATE af_published_collab SET blob = $1 - WHERE workspace_id = $2 AND doc_name = $3 + WHERE workspace_id = $2 + AND view_id = $3 "#, blob, workspace_id, - doc_name, + view_id, ) .execute(executor) .await?; @@ -993,7 +996,7 @@ pub async fn insert_or_replace_published_collab_blob<'a, E: Executor<'a, Databas if res.rows_affected() != 1 { tracing::error!( "Failed to insert or replace published collab blob, workspace_id: {}, doc_name: {}, rows_affected: {}", - workspace_id, doc_name, res.rows_affected() + workspace_id, view_id, res.rows_affected() ); } diff --git a/libs/shared-entity/src/dto/workspace_dto.rs b/libs/shared-entity/src/dto/workspace_dto.rs index 6d0ce3f7..1d4e97f2 100644 --- a/libs/shared-entity/src/dto/workspace_dto.rs +++ b/libs/shared-entity/src/dto/workspace_dto.rs @@ -120,3 +120,10 @@ pub struct CollabResponse { #[serde(default)] pub object_id: String, } + +#[derive(Debug, serde::Deserialize)] +pub struct PublishInfo { + pub namespace: String, + pub doc_name: String, + pub view_id: uuid::Uuid, +} diff --git a/migrations/20240618173348_publish_collab_2.sql b/migrations/20240618173348_publish_collab_2.sql new file mode 100644 index 00000000..ad1564fe --- /dev/null +++ b/migrations/20240618173348_publish_collab_2.sql @@ -0,0 +1,6 @@ +ALTER TABLE af_published_collab ADD COLUMN view_id UUID NOT NULL DEFAULT gen_random_uuid(); +ALTER TABLE af_published_collab DROP CONSTRAINT af_published_collab_pkey; +ALTER TABLE af_published_collab ADD PRIMARY KEY (workspace_id, view_id); + +CREATE INDEX IF NOT EXISTS idx_workspace_id_on_af_published_collab ON af_published_collab (workspace_id); +CREATE INDEX IF NOT EXISTS idx_published_by_on_af_published_collab ON af_published_collab (published_by); diff --git a/src/api/workspace.rs b/src/api/workspace.rs index 734f4c2a..bad586c6 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -138,12 +138,15 @@ pub fn workspace_scope() -> Scope { .route(web::get().to(get_publish_namespace_handler)) ) .service( - web::resource("/{workspace_id}/publish/{doc_name}/blob") + web::resource("/{workspace_id}/publish/{view_id}/blob") .route(web::put().to(put_publish_collab_blob_handler)) ) .service( - web::resource("/{workspace_id}/publish/{doc_name}") + web::resource("/{workspace_id}/publish/{view_id}/{doc_name}") .route(web::put().to(put_publish_collab_handler)) + ) + .service( + web::resource("/{workspace_id}/publish/{view_id}") .route(web::delete().to(delete_publish_collab_handler)) ) .service( @@ -986,15 +989,16 @@ async fn get_published_collab_blob_handler( } async fn put_publish_collab_handler( - path_param: web::Path<(Uuid, String)>, + path_param: web::Path<(Uuid, Uuid, String)>, user_uuid: UserUuid, metadata: Json, state: Data, ) -> Result>> { - let (workspace_id, doc_name) = path_param.into_inner(); + let (workspace_id, view_id, doc_name) = path_param.into_inner(); biz::workspace::ops::publish_collab( &state.pg_pool, &workspace_id, + &view_id, &doc_name, &user_uuid, &metadata, @@ -1004,16 +1008,16 @@ async fn put_publish_collab_handler( } async fn put_publish_collab_blob_handler( - path_param: web::Path<(Uuid, String)>, + path_param: web::Path<(Uuid, Uuid)>, user_uuid: UserUuid, collab_data: Bytes, state: Data, ) -> Result>> { - let (workspace_id, doc_name) = path_param.into_inner(); + let (workspace_id, view_id) = path_param.into_inner(); biz::workspace::ops::put_published_collab_blob( &state.pg_pool, &workspace_id, - &doc_name, + &view_id, &user_uuid, &collab_data, ) @@ -1022,15 +1026,15 @@ async fn put_publish_collab_blob_handler( } async fn delete_publish_collab_handler( - path_param: web::Path<(Uuid, String)>, + path_param: web::Path<(Uuid, Uuid)>, user_uuid: UserUuid, state: Data, ) -> Result>> { - let (workspace_id, doc_name) = path_param.into_inner(); + let (workspace_id, view_id) = path_param.into_inner(); biz::workspace::ops::delete_published_workspace_collab( &state.pg_pool, &workspace_id, - &doc_name, + &view_id, &user_uuid, ) .await?; diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index 05c2530a..997801e0 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -153,26 +153,34 @@ pub async fn get_workspace_publish_namespace( pub async fn publish_collab( pg_pool: &PgPool, workspace_id: &Uuid, + view_id: &Uuid, doc_name: &str, publisher_uuid: &Uuid, metadata: &serde_json::Value, ) -> Result<(), AppError> { - check_workspace_owner_or_publisher(pg_pool, publisher_uuid, workspace_id, doc_name).await?; + check_workspace_owner_or_publisher(pg_pool, publisher_uuid, workspace_id, view_id).await?; check_collab_doc_name(doc_name).await?; - insert_or_replace_publish_collab_meta(pg_pool, workspace_id, doc_name, publisher_uuid, metadata) - .await?; + insert_or_replace_publish_collab_meta( + pg_pool, + workspace_id, + view_id, + doc_name, + publisher_uuid, + metadata, + ) + .await?; Ok(()) } pub async fn put_published_collab_blob( pg_pool: &PgPool, workspace_id: &Uuid, - doc_name: &str, + view_id: &Uuid, publisher_uuid: &Uuid, collab_data: &[u8], ) -> Result<(), AppError> { - check_workspace_owner_or_publisher(pg_pool, publisher_uuid, workspace_id, doc_name).await?; - insert_or_replace_published_collab_blob(pg_pool, workspace_id, doc_name, collab_data).await?; + check_workspace_owner_or_publisher(pg_pool, publisher_uuid, workspace_id, view_id).await?; + insert_or_replace_published_collab_blob(pg_pool, workspace_id, view_id, collab_data).await?; Ok(()) } @@ -196,11 +204,11 @@ pub async fn get_published_collab_blob( pub async fn delete_published_workspace_collab( pg_pool: &PgPool, workspace_id: &Uuid, - doc_name: &str, + view_id: &Uuid, user_uuid: &Uuid, ) -> Result<(), AppError> { - check_workspace_owner_or_publisher(pg_pool, user_uuid, workspace_id, doc_name).await?; - delete_published_collab(pg_pool, workspace_id, doc_name).await?; + check_workspace_owner_or_publisher(pg_pool, user_uuid, workspace_id, view_id).await?; + delete_published_collab(pg_pool, workspace_id, view_id).await?; Ok(()) } @@ -585,12 +593,12 @@ async fn check_workspace_owner_or_publisher( pg_pool: &PgPool, user_uuid: &Uuid, workspace_id: &Uuid, - doc_name: &str, + view_id: &Uuid, ) -> Result<(), AppError> { let is_owner = select_user_is_workspace_owner(pg_pool, user_uuid, workspace_id).await?; if !is_owner { let is_publisher = - select_user_is_collab_publisher(pg_pool, user_uuid, workspace_id, doc_name).await?; + select_user_is_collab_publisher(pg_pool, user_uuid, workspace_id, view_id).await?; if !is_publisher { return Err(AppError::UserUnAuthorized( "User is not the owner of the workspace or the publisher of the document".to_string(), diff --git a/tests/workspace/publish.rs b/tests/workspace/publish.rs index 4c5bb7a8..9bdda974 100644 --- a/tests/workspace/publish.rs +++ b/tests/workspace/publish.rs @@ -73,8 +73,10 @@ async fn test_publish_doc() { .unwrap(); let my_doc_name = "my-doc"; + let view_id = uuid::Uuid::new_v4(); c.publish_collab( &workspace_id, + &view_id, my_doc_name, Metadata { title: "my_title".to_string(), @@ -99,7 +101,7 @@ async fn test_publish_doc() { assert!(collab_data.is_empty()); // empty data because publisher need to set it } - c.put_published_collab_blob(&workspace_id, my_doc_name, "some_collab_data") + c.put_published_collab_blob(&workspace_id, &view_id, "some_collab_data") .await .unwrap(); @@ -113,7 +115,7 @@ async fn test_publish_doc() { assert!(collab_data == "some_collab_data"); } - c.delete_published_collab(&workspace_id, my_doc_name) + c.delete_published_collab(&workspace_id, &view_id) .await .unwrap();