Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: self referencing database #830

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading