Skip to content

Commit

Permalink
Merge pull request #830 from AppFlowy-IO/fix/dup-self-ref
Browse files Browse the repository at this point in the history
fix: self referencing database
  • Loading branch information
speed2exe authored Sep 17, 2024
2 parents 0577e96 + 0c1cdbe commit 4908cea
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 25 deletions.
77 changes: 52 additions & 25 deletions src/biz/workspace/publish_dup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,44 @@ impl PublishCollabDuplicator {
.duplicated_refs
.insert(pub_db_id.clone(), Some(new_db_id.clone()));

{
// assign new id to all views of database.
// this will mark the database as duplicated
let txn = db_collab.context.transact();
let mut db_views = db_body.views.get_all_views(&txn);
let mut new_db_view_ids: Vec<String> = Vec::with_capacity(db_views.len());
for db_view in db_views.iter_mut() {
let new_db_view_id = if db_view.id == publish_view_id {
self
.duplicated_db_main_view
.insert(pub_db_id.clone(), new_view_id.clone());
new_view_id.clone()
} else {
gen_view_id()
};
self
.duplicated_db_view
.insert(db_view.id.clone(), new_db_view_id.clone());

new_db_view_ids.push(new_db_view_id);
}

// Add this database as linked view
self
.workspace_databases
.insert(new_db_id.clone(), new_db_view_ids);
}

// assign new id to all rows of database.
// this will mark the rows as duplicated
for pub_row_id in published_db.database_row_collabs.keys() {
// assign a new id for the row
let dup_row_id = gen_row_id();
self
.duplicated_db_row
.insert(pub_row_id.clone(), dup_row_id.to_string());
}

{
// handle row relations
let mut txn = db_collab.context.transact_mut();
Expand Down Expand Up @@ -734,8 +772,12 @@ impl PublishCollabDuplicator {

// duplicate db collab rows
for (pub_row_id, row_bin_data) in &published_db.database_row_collabs {
// assign a new id for the row
let dup_row_id = gen_row_id();
let dup_row_id = self
.duplicated_db_row
.get(pub_row_id)
.ok_or_else(|| AppError::RecordNotFound(format!("row not found: {}", pub_row_id)))?
.clone();

let mut db_row_collab = collab_from_doc_state(row_bin_data.clone(), &dup_row_id)?;
let mut db_row_body = DatabaseRowBody::open(pub_row_id.clone().into(), &mut db_row_collab)
.map_err(|e| AppError::Unhandled(e.to_string()))?;
Expand All @@ -754,7 +796,7 @@ impl PublishCollabDuplicator {

// updates row id along with meta keys
db_row_body
.update_id(&mut txn, dup_row_id.clone())
.update_id(&mut txn, dup_row_id.clone().into())
.map_err(|e| AppError::Unhandled(format!("failed to update row id: {:?}", e)))?;

// duplicate row document if exists
Expand Down Expand Up @@ -860,34 +902,24 @@ impl PublishCollabDuplicator {
dup_row_id.to_string(),
(CollabType::DatabaseRow, db_row_ec_bytes),
);
self
.duplicated_db_row
.insert(pub_row_id.clone(), dup_row_id.to_string());
}

// accumulate list of database views (Board, Cal, ...) to be linked to the database
let mut new_db_view_ids: Vec<String> = vec![];
{
let mut txn = db_collab.context.transact_mut();
db_body.root.insert(&mut txn, "id", new_db_id.clone());

let mut db_views = db_body.views.get_all_views(&txn);
for db_view in db_views.iter_mut() {
let new_db_view_id = if db_view.id == publish_view_id {
self
.duplicated_db_main_view
.insert(pub_db_id.clone(), new_view_id.clone());
new_view_id.clone()
} else {
gen_view_id()
};
self
.duplicated_db_view
.insert(db_view.id.clone(), new_db_view_id.clone());
let new_db_view_id = self.duplicated_db_view.get(&db_view.id).ok_or_else(|| {
AppError::Unhandled(format!(
"view not found in duplicated_db_view: {}",
db_view.id
))
})?;

db_view.id.clone_from(&new_db_view_id);
db_view.id.clone_from(new_db_view_id);
db_view.database_id.clone_from(&new_db_id);
new_db_view_ids.push(db_view.id.clone());

// update all views's row's id
for row_order in db_view.row_orders.iter_mut() {
Expand Down Expand Up @@ -917,11 +949,6 @@ impl PublishCollabDuplicator {
.collabs_to_insert
.insert(new_db_id.clone(), (CollabType::Database, db_encoded_collab));

// Add this database as linked view
self
.workspace_databases
.insert(new_db_id.clone(), new_db_view_ids);

Ok((pub_db_id, new_db_id, false))
}

Expand Down
82 changes: 82 additions & 0 deletions tests/workspace/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,88 @@ async fn duplicate_to_workspace_db_row_with_doc() {
}
}

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

// database with a row referencing itself
// uuid must be fixed because the collab contains row data which reference itself
let db_rel_self_view_id: uuid::Uuid = "18d72589-80d7-4041-9342-5d572facb7c9".parse().unwrap();
let db_rel_self_metadata: PublishViewMetaData =
serde_json::from_str(published_data::DB_REL_SELF_META).unwrap();
let db_rel_self_blob = hex::decode(published_data::DB_REL_SELF_HEX).unwrap();

client_1
.api_client
.publish_collabs(
&workspace_id,
vec![PublishCollabItem {
meta: PublishCollabMetadata {
view_id: db_rel_self_view_id,
publish_name: "db-rel-self".to_string(),
metadata: db_rel_self_metadata.clone(),
},
data: db_rel_self_blob,
}],
)
.await
.unwrap();

{
let mut client_2 = TestClient::new_user().await;
let workspace_id_2 = client_2.workspace_id().await;
let fv = client_2
.api_client
.get_workspace_folder(&workspace_id_2, Some(5), None)
.await
.unwrap();

client_2
.api_client
.duplicate_published_to_workspace(
&workspace_id_2,
&PublishedDuplicate {
published_view_id: db_rel_self_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), None)
.await
.unwrap();
println!("{:#?}", fv);

let db_rel_self = fv
.children
.iter()
.find(|v| v.name == "self_ref_db")
.unwrap();

let db_rel_self_collab = client_2
.get_db_collab_from_view(&workspace_id_2, &db_rel_self.view_id)
.await;
let txn = db_rel_self_collab.transact();
let db_rel_self_body = DatabaseBody::from_collab(&db_rel_self_collab).unwrap();
let database_id = db_rel_self_body.get_database_id(&txn);
let all_fields = db_rel_self_body.fields.get_all_fields(&txn);
let rel_fields = all_fields
.iter()
.map(|f| &f.type_options)
.flat_map(|t| t.iter())
.filter(|(k, _v)| **k == FieldType::Relation.type_id())
.map(|(_k, v)| v)
.flat_map(|v| v.iter())
.collect::<Vec<_>>();
assert_eq!(rel_fields.len(), 1);
assert_eq!(rel_fields[0].1.to_string(), database_id);
}
}

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 4908cea

Please sign in to comment.