feat: use database row body

This commit is contained in:
Zack Fu Zi Xiang 2024-09-12 01:13:07 +08:00
parent cff144a857
commit 306faad11e
No known key found for this signature in database
8 changed files with 179 additions and 142 deletions

12
Cargo.lock generated
View File

@ -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",

View File

@ -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 = []

View File

@ -585,11 +585,10 @@ impl TestClient {
workspace_id: String,
object_id: String,
collab_type: CollabType,
) -> Collab {
) -> Result<Collab, AppResponseError> {
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(

View File

@ -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)]

View File

@ -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!()
}
}

View File

@ -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(

View File

@ -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::<MapRef>()
.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::<MapRef>() {
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::<ArrayRef>() {
@ -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))

View File

@ -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);