Skip to content

Commit

Permalink
Merge pull request #787 from AppFlowy-IO/fix/dup-doc-db
Browse files Browse the repository at this point in the history
fix: parent database id for inline doc
  • Loading branch information
speed2exe authored Sep 4, 2024
2 parents 5b5e561 + e1fc5f2 commit 40017ee
Show file tree
Hide file tree
Showing 3 changed files with 342 additions and 18 deletions.
91 changes: 74 additions & 17 deletions src/biz/workspace/publish_dup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ impl PublishCollabDuplicator {
ViewLayout::Document => {
let doc_collab = collab_from_doc_state(published_blob, "")?;
let doc = Document::open(doc_collab).map_err(|e| AppError::Unhandled(e.to_string()))?;
let new_doc_view = self.deep_copy_doc(new_view_id, doc, metadata).await?;
let new_doc_view = self
.deep_copy_doc(publish_view_id, new_view_id, doc, metadata)
.await?;
Ok(Some(new_doc_view))
},
ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => {
Expand All @@ -360,6 +362,7 @@ impl PublishCollabDuplicator {

async fn deep_copy_doc<'a>(
&mut self,
pub_view_id: &str,
new_view_id: String,
doc: Document,
metadata: PublishViewMetaData,
Expand All @@ -376,7 +379,7 @@ impl PublishCollabDuplicator {
}

if let Err(err) = self
.deep_copy_doc_databases(&mut doc_data, &mut ret_view)
.deep_copy_doc_databases(pub_view_id, &mut doc_data, &mut ret_view)
.await
{
tracing::error!("failed to deep copy doc databases: {}", err);
Expand Down Expand Up @@ -493,6 +496,7 @@ impl PublishCollabDuplicator {

async fn deep_copy_doc_databases(
&mut self,
pub_view_id: &str,
doc_data: &mut DocumentData,
ret_view: &mut View,
) -> Result<(), AppError> {
Expand All @@ -517,29 +521,82 @@ impl PublishCollabDuplicator {
.as_str()
.ok_or_else(|| AppError::RecordNotFound("view_id not a string".to_string()))?;

if let Some((new_view_id, new_parent_id)) = self
.deep_copy_database_inline_doc(block_view_id, block_parent_id, &ret_view.id)
.await?
{
block.data.insert(
"view_id".to_string(),
serde_json::Value::String(new_view_id),
);
block.data.insert(
"parent_id".to_string(),
serde_json::Value::String(new_parent_id),
);
if pub_view_id == block_parent_id {
// inline database in doc
if let Some(new_view_id) = self
.deep_copy_inline_database_in_doc(block_view_id, &ret_view.id)
.await?
{
block.data.insert(
"view_id".to_string(),
serde_json::Value::String(new_view_id),
);
block.data.insert(
"parent_id".to_string(),
serde_json::Value::String(ret_view.id.clone()),
);
} else {
tracing::warn!("deep_copy_doc_databases: view not found: {}", block_view_id);
}
} else {
tracing::warn!("deep_copy_doc_databases: view not found: {}", block_view_id);
// reference to database
if let Some((new_view_id, new_parent_id)) = self
.deep_copy_ref_database_in_doc(block_view_id, block_parent_id, &ret_view.id)
.await?
{
block.data.insert(
"view_id".to_string(),
serde_json::Value::String(new_view_id),
);
block.data.insert(
"parent_id".to_string(),
serde_json::Value::String(new_parent_id),
);
} else {
tracing::warn!("deep_copy_doc_databases: view not found: {}", block_view_id);
}
}
}

Ok(())
}

/// deep copy database for doc
/// deep copy inline database for doc
/// returns new view_id
/// parent_view_id is assumed to be doc itself
async fn deep_copy_inline_database_in_doc<'a>(
&mut self,
view_id: &str,
doc_view_id: &String,
) -> Result<Option<String>, AppError> {
let (metadata, published_blob) = match self
.get_published_data_for_view_id(&view_id.parse()?)
.await?
{
Some(published_data) => published_data,
None => {
tracing::warn!("No published collab data found for view_id: {}", view_id);
return Ok(None);
},
};

let published_db = serde_json::from_slice::<PublishDatabaseData>(&published_blob)?;
let mut parent_view = self
.deep_copy_database_view(gen_view_id(), published_db, &metadata, view_id)
.await?;
let parent_view_id = parent_view.id.clone();
if parent_view.parent_view_id.is_empty() {
parent_view.parent_view_id.clone_from(doc_view_id);
self
.views_to_add
.insert(parent_view.id.clone(), parent_view);
}
Ok(Some(parent_view_id))
}

/// deep copy referenced database for doc
/// returns new (view_id, parent_id)
async fn deep_copy_database_inline_doc<'a>(
async fn deep_copy_ref_database_in_doc<'a>(
&mut self,
view_id: &str,
parent_id: &str,
Expand Down
120 changes: 119 additions & 1 deletion tests/workspace/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use appflowy_cloud::biz::workspace::publish_dup::{
use collab::util::MapExt;
use collab_database::views::ViewMap;
use collab_database::workspace_database::WorkspaceDatabaseBody;
use collab_document::document::Document;
use collab_entity::CollabType;
use collab_folder::{CollabOrigin, Folder};
use shared_entity::dto::publish_dto::PublishDatabaseData;
Expand Down Expand Up @@ -933,7 +934,6 @@ async fn duplicate_to_workspace_doc_inline_database() {
.into_iter()
.find(|v| v.name == "View of grid1")
.unwrap();
println!("{:#?}", view_of_grid1_fv);
assert_ne!(view_of_grid1_fv.view_id, view_of_grid_1_view_id.to_string());

{
Expand Down Expand Up @@ -985,6 +985,124 @@ async fn duplicate_to_workspace_doc_inline_database() {
}
}

#[tokio::test]
async fn duplicate_to_workspace_db_embedded_in_doc() {
let client_1 = TestClient::new_user().await;
let workspace_id = client_1.workspace_id().await;

// embedded doc with db
// database is created in the doc, not linked from a separate view
let doc_with_embedded_db_view_id: uuid::Uuid = uuid::Uuid::new_v4();
let doc_with_embedded_db_metadata: PublishViewMetaData =
serde_json::from_str(published_data::DOC_WITH_EMBEDDED_DB_META).unwrap();
let doc_with_embedded_db_blob = hex::decode(published_data::DOC_WITH_EMBEDDED_DB_HEX).unwrap();

// user will also need to publish the database (even though it is embedded)
// uuid must be fixed because it is referenced in the doc
let embedded_db_view_id: uuid::Uuid = "bb221175-14da-4a05-a09d-595e42d2350f".parse().unwrap();
let embedded_db_metadata: PublishViewMetaData =
serde_json::from_str(published_data::EMBEDDED_DB_META).unwrap();
let embedded_db_blob = hex::decode(published_data::EMBEDDED_DB_HEX).unwrap();

client_1
.api_client
.publish_collabs(
&workspace_id,
vec![
PublishCollabItem {
meta: PublishCollabMetadata {
view_id: doc_with_embedded_db_view_id,
publish_name: "doc-with-embedded-db".to_string(),
metadata: doc_with_embedded_db_metadata.clone(),
},
data: doc_with_embedded_db_blob,
},
PublishCollabItem {
meta: PublishCollabMetadata {
view_id: embedded_db_view_id,
publish_name: "embedded-db".to_string(),
metadata: embedded_db_metadata.clone(),
},
data: embedded_db_blob,
},
],
)
.await
.unwrap();

{
let mut client_2 = TestClient::new_user().await;
let workspace_id_2 = client_2.workspace_id().await;

// Open workspace to trigger group creation
client_2
.open_collab(&workspace_id_2, &workspace_id_2, CollabType::Folder)
.await;

let fv = client_2
.api_client
.get_workspace_folder(&workspace_id_2, Some(5))
.await
.unwrap();

// duplicate doc_with_embedded_db to workspace2
// Result fv should be:
// .
// ├── Getting Started (existing)
// └── db_with_embedded_db (inside should contain the database)
client_2
.api_client
.duplicate_published_to_workspace(
&workspace_id_2,
&PublishedDuplicate {
published_view_id: doc_with_embedded_db_view_id.to_string(),
dest_view_id: fv.view_id, // use the root view
},
)
.await
.unwrap();

{
let fv = client_2
.api_client
.get_workspace_folder(&workspace_id_2, Some(5))
.await
.unwrap();
println!("{:#?}", fv);
let doc_with_embedded_db = fv
.children
.into_iter()
.find(|v| v.name == "docwithembeddeddb")
.unwrap();
let collab_resp = client_2
.get_collab(QueryCollabParams {
workspace_id: workspace_id_2.clone(),
inner: QueryCollab {
object_id: doc_with_embedded_db.view_id.clone(),
collab_type: CollabType::Folder,
},
})
.await
.unwrap();

let doc_collab =
collab_from_doc_state(collab_resp.encode_collab.doc_state.to_vec(), "").unwrap();
let doc = Document::open(doc_collab).unwrap();
let doc_data = doc.get_document_data().unwrap();
let grid = doc_data
.blocks
.iter()
.find(|(_k, b)| b.ty == "grid")
.unwrap()
.1;

// because it is embedded, the database parent's id is the view id of the doc
let parent_id = grid.data.get("parent_id").unwrap().as_str().unwrap();
assert_ne!(parent_id, doc_with_embedded_db.view_id.clone());
}
}
}

fn get_database_id_and_row_ids(published_db_blob: &[u8]) -> (String, HashSet<String>) {
let pub_db_data = serde_json::from_slice::<PublishDatabaseData>(published_db_blob).unwrap();
let db_collab = collab_from_doc_state(pub_db_data.database_collab, "").unwrap();
Expand Down
Loading

0 comments on commit 40017ee

Please sign in to comment.