From 306faad11eb2b3fc8fd1001af412001633f85013 Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Thu, 12 Sep 2024 01:13:07 +0800 Subject: [PATCH] feat: use database row body --- Cargo.lock | 12 +- Cargo.toml | 12 +- libs/client-api-test/src/test_client.rs | 7 +- libs/client-api/src/collab_sync/plugin.rs | 11 + .../src/group/plugin/history_plugin.rs | 31 +++ services/appflowy-history/src/biz/history.rs | 37 ++++ src/biz/workspace/publish_dup.rs | 197 +++++++----------- tests/workspace/publish.rs | 14 +- 8 files changed, 179 insertions(+), 142 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30c2d801..ebc1da28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2090,7 +2090,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7ca89752d1a249490916113f854847e46aac42d8#7ca89752d1a249490916113f854847e46aac42d8" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0340ca880ed108582e7751839c9e9496f00aa5a5#0340ca880ed108582e7751839c9e9496f00aa5a5" dependencies = [ "anyhow", "arc-swap", @@ -2115,7 +2115,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7ca89752d1a249490916113f854847e46aac42d8#7ca89752d1a249490916113f854847e46aac42d8" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0340ca880ed108582e7751839c9e9496f00aa5a5#0340ca880ed108582e7751839c9e9496f00aa5a5" dependencies = [ "anyhow", "async-trait", @@ -2146,7 +2146,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7ca89752d1a249490916113f854847e46aac42d8#7ca89752d1a249490916113f854847e46aac42d8" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0340ca880ed108582e7751839c9e9496f00aa5a5#0340ca880ed108582e7751839c9e9496f00aa5a5" dependencies = [ "anyhow", "arc-swap", @@ -2166,7 +2166,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7ca89752d1a249490916113f854847e46aac42d8#7ca89752d1a249490916113f854847e46aac42d8" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0340ca880ed108582e7751839c9e9496f00aa5a5#0340ca880ed108582e7751839c9e9496f00aa5a5" dependencies = [ "anyhow", "bytes", @@ -2185,7 +2185,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7ca89752d1a249490916113f854847e46aac42d8#7ca89752d1a249490916113f854847e46aac42d8" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0340ca880ed108582e7751839c9e9496f00aa5a5#0340ca880ed108582e7751839c9e9496f00aa5a5" dependencies = [ "anyhow", "arc-swap", @@ -2270,7 +2270,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7ca89752d1a249490916113f854847e46aac42d8#7ca89752d1a249490916113f854847e46aac42d8" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0340ca880ed108582e7751839c9e9496f00aa5a5#0340ca880ed108582e7751839c9e9496f00aa5a5" dependencies = [ "anyhow", "collab", diff --git a/Cargo.toml b/Cargo.toml index 608a4f5c..1d75ebbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -287,12 +287,12 @@ debug = true [patch.crates-io] # It's diffcult to resovle different version with the same crate used in AppFlowy Frontend and the Client-API crate. # So using patch to workaround this issue. -collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7ca89752d1a249490916113f854847e46aac42d8" } -collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7ca89752d1a249490916113f854847e46aac42d8" } -collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7ca89752d1a249490916113f854847e46aac42d8" } -collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7ca89752d1a249490916113f854847e46aac42d8" } -collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7ca89752d1a249490916113f854847e46aac42d8" } -collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7ca89752d1a249490916113f854847e46aac42d8" } +collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0340ca880ed108582e7751839c9e9496f00aa5a5" } +collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0340ca880ed108582e7751839c9e9496f00aa5a5" } +collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0340ca880ed108582e7751839c9e9496f00aa5a5" } +collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0340ca880ed108582e7751839c9e9496f00aa5a5" } +collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0340ca880ed108582e7751839c9e9496f00aa5a5" } +collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0340ca880ed108582e7751839c9e9496f00aa5a5" } [features] history = [] diff --git a/libs/client-api-test/src/test_client.rs b/libs/client-api-test/src/test_client.rs index 730ab38c..23b4436c 100644 --- a/libs/client-api-test/src/test_client.rs +++ b/libs/client-api-test/src/test_client.rs @@ -585,11 +585,10 @@ impl TestClient { workspace_id: String, object_id: String, collab_type: CollabType, - ) -> Collab { + ) -> Result { let resp = self .get_collab(workspace_id, object_id.clone(), collab_type) - .await - .unwrap(); + .await?; let collab = Collab::new_with_source( CollabOrigin::Server, &object_id, @@ -598,7 +597,7 @@ impl TestClient { false, ) .unwrap(); - collab + Ok(collab) } pub async fn batch_get_collab( diff --git a/libs/client-api/src/collab_sync/plugin.rs b/libs/client-api/src/collab_sync/plugin.rs index e7beea32..13f6a4a2 100644 --- a/libs/client-api/src/collab_sync/plugin.rs +++ b/libs/client-api/src/collab_sync/plugin.rs @@ -6,6 +6,7 @@ use std::time::Duration; use anyhow::anyhow; use collab::core::awareness::{AwarenessUpdate, Event}; +use collab::core::collab_plugin::CollabPluginType; use collab::core::collab_state::SyncState; use collab::core::origin::CollabOrigin; use collab::preclude::{Collab, CollabPlugin}; @@ -211,6 +212,16 @@ where .is_destroyed .store(true, std::sync::atomic::Ordering::SeqCst); } + + fn init(&self, _object_id: &str, _origin: &CollabOrigin, _doc: &yrs::Doc) {} + + fn receive_update(&self, _object_id: &str, _txn: &yrs::TransactionMut, _update: &[u8]) {} + + fn after_transaction(&self, _object_id: &str, _txn: &mut yrs::TransactionMut) {} + + fn plugin_type(&self) -> CollabPluginType { + CollabPluginType::CloudStorage + } } #[derive(Clone, Debug)] diff --git a/services/appflowy-collaborate/src/group/plugin/history_plugin.rs b/services/appflowy-collaborate/src/group/plugin/history_plugin.rs index 0ac505ed..c67799cb 100644 --- a/services/appflowy-collaborate/src/group/plugin/history_plugin.rs +++ b/services/appflowy-collaborate/src/group/plugin/history_plugin.rs @@ -117,4 +117,35 @@ where } }); } + + fn init(&self, _object_id: &str, _origin: &collab_folder::CollabOrigin, _doc: &yrs::Doc) {} + + fn did_init(&self, _collab: &Collab, _object_id: &str) {} + + fn receive_local_update( + &self, + _origin: &collab_folder::CollabOrigin, + _object_id: &str, + _update: &[u8], + ) { + } + + fn receive_local_state( + &self, + _origin: &collab_folder::CollabOrigin, + _object_id: &str, + _event: &yrs::sync::awareness::Event, + _update: &yrs::sync::AwarenessUpdate, + ) { + } + + fn after_transaction(&self, _object_id: &str, _txn: &mut TransactionMut) {} + + fn start_init_sync(&self) {} + + fn destroy(&self) {} + + fn plugin_type(&self) -> collab::core::collab_plugin::CollabPluginType { + todo!() + } } diff --git a/services/appflowy-history/src/biz/history.rs b/services/appflowy-history/src/biz/history.rs index b566e07b..47157cbd 100644 --- a/services/appflowy-history/src/biz/history.rs +++ b/services/appflowy-history/src/biz/history.rs @@ -129,6 +129,43 @@ impl CollabPlugin for CountUpdatePlugin { fn receive_update(&self, _object_id: &str, txn: &TransactionMut, _update: &[u8]) { self.snapshot_generator.did_apply_update(txn); } + + fn init( + &self, + _object_id: &str, + _origin: &collab::core::origin::CollabOrigin, + _doc: &collab::preclude::Doc, + ) { + } + + fn did_init(&self, _collab: &Collab, _object_id: &str) {} + + fn receive_local_update( + &self, + _origin: &collab::core::origin::CollabOrigin, + _object_id: &str, + _update: &[u8], + ) { + } + + fn receive_local_state( + &self, + _origin: &collab::core::origin::CollabOrigin, + _object_id: &str, + _event: &collab::core::awareness::Event, + _update: &collab::preclude::sync::AwarenessUpdate, + ) { + } + + fn after_transaction(&self, _object_id: &str, _txn: &mut TransactionMut) {} + + fn start_init_sync(&self) {} + + fn destroy(&self) {} + + fn plugin_type(&self) -> collab::core::collab_plugin::CollabPluginType { + todo!() + } } pub async fn get_snapshots( diff --git a/src/biz/workspace/publish_dup.rs b/src/biz/workspace/publish_dup.rs index 72abce52..f224d242 100644 --- a/src/biz/workspace/publish_dup.rs +++ b/src/biz/workspace/publish_dup.rs @@ -3,11 +3,16 @@ use appflowy_collaborate::collab::storage::CollabAccessControlStorage; use collab::core::collab::DataSource; use collab::preclude::Collab; -use collab::preclude::MapExt; use collab_database::database::DatabaseBody; use collab_database::entity::FieldType; use collab_database::rows::meta_id_from_row_id; +use collab_database::rows::DatabaseRowBody; +use collab_database::rows::RowId; use collab_database::rows::RowMetaKey; +use collab_database::rows::CELL_FIELD_TYPE; +use collab_database::rows::ROW_CELLS; +use collab_database::rows::ROW_DATABASE_ID; +use collab_database::template::entity::CELL_DATA; use collab_database::workspace_database::WorkspaceDatabaseBody; use collab_document::blocks::DocumentData; use collab_document::document::Document; @@ -730,52 +735,88 @@ impl PublishCollabDuplicator { // assign a new id for the row let dup_row_id = gen_view_id(); let mut db_row_collab = collab_from_doc_state(row_bin_data.clone(), &dup_row_id)?; + let mut db_row_body = DatabaseRowBody::open(&mut db_row_collab) + .map_err(|e| AppError::Unhandled(e.to_string()))?; { - // update database_id and row_id in data let mut txn = db_row_collab.context.transact_mut(); - let data = db_row_collab + // update database_id + db_row_body .data - .get(&txn, "data") - .ok_or_else(|| { - AppError::RecordNotFound("no data found in database row collab".to_string()) - })? - .cast::() - .map_err(|err| AppError::Unhandled(format!("data not map: {:?}", err)))?; - data.insert(&mut txn, "id", dup_row_id.clone()); - data.insert(&mut txn, "database_id", new_db_id.clone()); + .insert(&mut txn, ROW_DATABASE_ID, new_db_id.clone()); + { + // handle document in database row + let pub_is_doc_empty_key = + meta_id_from_row_id(&pub_row_id.parse()?, RowMetaKey::IsDocumentEmpty); + let pub_is_doc_empty = db_row_body.meta.get(&txn, &pub_is_doc_empty_key); + if let Some(Out::Any(Any::Bool(is_doc_empty))) = pub_is_doc_empty { + if !is_doc_empty { + let pub_row_doc_id = + meta_id_from_row_id(&pub_row_id.parse()?, RowMetaKey::DocumentId); + let row_doc_doc_state = match published_db + .database_row_document_collabs + .get(&pub_row_doc_id) + { + Some(doc_state) => doc_state, + None => { + tracing::error!("no document found for row: {}", pub_row_doc_id); + continue; + }, + }; + let pub_doc_collab = + collab_from_doc_state(row_doc_doc_state.to_vec(), &pub_row_doc_id)?; + let pub_doc = + Document::open(pub_doc_collab).map_err(|e| AppError::Unhandled(e.to_string()))?; + let dup_row_doc_id = + meta_id_from_row_id(&dup_row_id.parse()?, RowMetaKey::DocumentId); + let mut new_doc_view = Box::pin(self.deep_copy_doc( + &pub_row_doc_id, + dup_row_doc_id.clone(), + pub_doc, + PublishViewMetaData::default(), + )) + .await?; + new_doc_view.parent_view_id.clone_from(&dup_row_doc_id); // orphan folder view + self + .views_to_add + .insert(dup_row_doc_id.clone(), new_doc_view); + } + } + } + + db_row_body + .update_id(&mut txn, RowId::from(dup_row_id.clone())) + .map_err(|e| AppError::Unhandled(format!("failed to update row id: {:?}", e)))?; { - // update relation cells - // db_row_collb: Object { - // "data": Object { - // "cells": Object { - // "MBaTsr": Object { - // "data": Array [ - // String("eefb5700-8cf7-411e-9596-f60b9a51916e"), - // String("23d5e054-42c8-4754-ad69-527e4ffc1e46"), - // // above are published row ids of related database - // // we need to replace them with respective duplicated row ids - // ], - // "field_type": Number(10), - // // use this condition to filter out relation cells - // }, - // }, + // "cells": Object { + // "MBaTsr": Object { + // "data": Array [ + // String("eefb5700-8cf7-411e-9596-f60b9a51916e"), + // String("23d5e054-42c8-4754-ad69-527e4ffc1e46"), + // // above are published row ids of related database + // // we need to replace them with respective duplicated row ids + // ], + // "field_type": Number(10), + // // use this condition to filter out relation cells // }, - // } - let cells: MapRef = db_row_collab + // }, + let cells: MapRef = db_row_body .data - .get_with_path(&txn, ["data", "cells"]) + .get(&txn, ROW_CELLS) .ok_or_else(|| { AppError::RecordNotFound("no cells found in database row collab".to_string()) - })?; + })? + .cast() + .map_err(|e| AppError::Unhandled(format!("not a map: {:?}", e)))?; + // collect all cell with field type as relation let mut rel_row_idss = vec![]; for (_, out) in cells.iter(&txn) { if let Ok(m) = out.cast::() { - if let Some(Out::Any(Any::BigInt(n))) = m.get(&txn, "field_type") { + if let Some(Out::Any(Any::BigInt(n))) = m.get(&txn, CELL_FIELD_TYPE) { if n == FieldType::Relation as i64 { - let relation_data = m.get(&txn, "data").ok_or_else(|| { + let relation_data = m.get(&txn, CELL_DATA).ok_or_else(|| { AppError::RecordNotFound("no data found in relation cell".to_string()) })?; if let Ok(arr) = relation_data.cast::() { @@ -785,9 +826,9 @@ impl PublishCollabDuplicator { } } } + // replace all relation cells with duplicated row ids for rel_row_ids in rel_row_idss { let num_refs = rel_row_ids.len(&txn); - let mut pub_row_ids = Vec::with_capacity(num_refs as usize); for rel_row_id in rel_row_ids.iter(&txn) { if let Out::Any(Any::String(s)) = rel_row_id { @@ -807,96 +848,10 @@ impl PublishCollabDuplicator { } } } - - { - // handle row meta - let row_meta: MapRef = db_row_collab - .data - .get(&txn, "meta") - .ok_or_else(|| { - AppError::RecordNotFound(format!( - "no meta found in database row collab: {}", - pub_row_id - )) - })? - .cast() - .map_err(|err| AppError::Unhandled(format!("not a map: {:?}", err)))?; - - { - // handle document in database row - let pub_is_doc_empty_key = - meta_id_from_row_id(&pub_row_id.parse()?, RowMetaKey::IsDocumentEmpty); - let pub_is_doc_empty = row_meta.get(&txn, &pub_is_doc_empty_key); - if let Some(Out::Any(Any::Bool(is_doc_empty))) = pub_is_doc_empty { - row_meta.remove(&mut txn, &pub_is_doc_empty_key); - let dup_row_doc_id = - meta_id_from_row_id(&dup_row_id.parse()?, RowMetaKey::DocumentId); - row_meta.insert(&mut txn, dup_row_doc_id.clone(), Any::Bool(is_doc_empty)); - - if !is_doc_empty { - let pub_row_doc_id = - meta_id_from_row_id(&pub_row_id.parse()?, RowMetaKey::DocumentId); - - let row_doc_doc_state = published_db - .database_row_document_collabs - .get(&pub_row_doc_id) - .ok_or_else(|| { - AppError::RecordNotFound(format!("doc not found: {}", pub_row_doc_id)) - })?; - - let doc_collab = - collab_from_doc_state(row_doc_doc_state.to_vec(), &pub_row_doc_id)?; - let doc = - Document::open(doc_collab).map_err(|e| AppError::Unhandled(e.to_string()))?; - let mut new_doc_view = Box::pin(self.deep_copy_doc( - &pub_row_doc_id, - dup_row_doc_id.clone(), - doc, - PublishViewMetaData::default(), - )) - .await?; - new_doc_view.parent_view_id.clone_from(&dup_row_doc_id); // orphan folder view - self - .views_to_add - .insert(dup_row_doc_id.clone(), new_doc_view); - } - } - } - { - // handle icon id - let pub_icon_id_key = meta_id_from_row_id(&pub_row_id.parse()?, RowMetaKey::IconId); - let pub_icon_id = row_meta.get(&txn, &pub_icon_id_key); - if let Some(Out::Any(Any::String(icon_id))) = pub_icon_id { - row_meta.remove(&mut txn, &pub_icon_id_key); - let dup_icon_id_key = meta_id_from_row_id(&dup_row_id.parse()?, RowMetaKey::IconId); - row_meta.insert(&mut txn, dup_icon_id_key, icon_id); - } - } - { - // handle cover id - let pub_cover_id_key = meta_id_from_row_id(&pub_row_id.parse()?, RowMetaKey::CoverId); - let pub_cover_id = row_meta.get(&txn, &pub_cover_id_key); - if let Some(Out::Any(Any::String(cover_id))) = pub_cover_id { - row_meta.remove(&mut txn, &pub_cover_id_key); - let dup_cover_id_key = meta_id_from_row_id(&dup_row_id.parse()?, RowMetaKey::CoverId); - row_meta.insert(&mut txn, dup_cover_id_key, cover_id); - } - } - { - // handle attachment_count - let pub_attach_count_key = - meta_id_from_row_id(&pub_row_id.parse()?, RowMetaKey::CoverId); - let pub_attach_count = row_meta.get(&txn, &pub_attach_count_key); - if let Some(Out::Any(Any::BigInt(attachment_count))) = pub_attach_count { - row_meta.remove(&mut txn, &pub_attach_count_key); - let dup_attach_count_key = - meta_id_from_row_id(&dup_row_id.parse()?, RowMetaKey::AttachmentCount); - row_meta.insert(&mut txn, dup_attach_count_key, attachment_count); - } - } - } } + println!("{:#?}", db_row_collab.to_json_value()); + // write new row collab to storage let db_row_ec_bytes = tokio::task::spawn_blocking(move || collab_to_bin(&db_row_collab, CollabType::DatabaseRow)) diff --git a/tests/workspace/publish.rs b/tests/workspace/publish.rs index a2d13851..c69dd54f 100644 --- a/tests/workspace/publish.rs +++ b/tests/workspace/publish.rs @@ -947,7 +947,8 @@ async fn duplicate_to_workspace_doc_inline_database() { .database_id; let db_collab = client_2 .get_collab_to_collab(workspace_id_2, dup_grid1_db_id, CollabType::Database) - .await; + .await + .unwrap(); let dup_db_id = DatabaseBody::database_id_from_collab(&db_collab).unwrap(); assert_ne!(dup_db_id, pub_db_id); @@ -1066,7 +1067,8 @@ async fn duplicate_to_workspace_db_embedded_in_doc() { doc_with_embedded_db.view_id.clone(), CollabType::Folder, ) - .await; + .await + .unwrap(); let doc = Document::open(doc_collab).unwrap(); let doc_data = doc.get_document_data().unwrap(); let grid = doc_data @@ -1282,16 +1284,18 @@ async fn duplicate_to_workspace_db_row_with_doc() { first_row_id.to_string(), CollabType::DatabaseRow, ) - .await; - + .await + .unwrap(); let row_detail = RowDetail::from_collab(&mut row_collab).unwrap(); + assert_eq!(row_detail.meta.is_document_empty, false); let doc_id = row_detail.document_id; let _doc_collab = client_2 .get_collab_to_collab(workspace_id_2.clone(), doc_id.clone(), CollabType::Document) .await; let folder_collab = client_2 .get_collab_to_collab(workspace_id_2.clone(), workspace_id_2, CollabType::Folder) - .await; + .await + .unwrap(); let folder = Folder::open(UserId::from(client_2.uid().await), folder_collab, None).unwrap(); let doc_view = folder.get_view(&doc_id).unwrap(); assert_eq!(doc_view.id, doc_view.parent_view_id);