AppFlowy-Cloud/tests/collab/database_crud.rs
Zack c0f7b1d3a9
feat: upsert database row (#1067)
* feat: upsert database row

* feat: improve api to accept database row document payload

* feat: test case for upsert

* chore: refactor

* feat: database row doc content impl

* refactor: simplify insert row field names

* feat: folder updates for database row document

* refactor: simplify row detail return

* feat: add doc contents for database row detail

* feat: refactor database row creation

* feat: upsert database row doc modification

* feat: ignore empty string

* feat: add document similarity check

* chore: refactor to use broadcast with timeout

* feat: wrap broadcast timeout in a tokio spawn
2024-12-18 13:47:20 +08:00

332 lines
9.5 KiB
Rust

use std::collections::HashMap;
use client_api_test::{generate_unique_registered_user_client, workspace_id_from_client};
use collab_database::entity::FieldType;
use serde_json::json;
use shared_entity::dto::workspace_dto::AFInsertDatabaseField;
#[tokio::test]
async fn database_row_upsert_with_doc() {
let (c, _user) = generate_unique_registered_user_client().await;
let workspace_id = workspace_id_from_client(&c).await;
let databases = c.list_databases(&workspace_id).await.unwrap();
assert_eq!(databases.len(), 1);
let todo_db = &databases[0];
// Upsert row
let row_id = c
.upsert_database_item(
&workspace_id,
&todo_db.id,
"my_pre_hash_123".to_string(),
HashMap::from([]),
Some("This is a document of a database row".to_string()),
)
.await
.unwrap();
{
// Get row and check data
let row_detail = &c
.list_database_row_details(&workspace_id, &todo_db.id, &[&row_id], true)
.await
.unwrap()[0];
assert!(row_detail.has_doc);
assert_eq!(
row_detail.doc,
Some(String::from("\nThis is a document of a database row"))
);
}
// Upsert row with another doc
let _ = c
.upsert_database_item(
&workspace_id,
&todo_db.id,
"my_pre_hash_123".to_string(),
HashMap::from([]),
Some("This is a another document".to_string()),
)
.await
.unwrap();
{
// Get row and check that doc has been modified
let row_detail = &c
.list_database_row_details(&workspace_id, &todo_db.id, &[&row_id], true)
.await
.unwrap()[0];
assert_eq!(
row_detail.doc,
Some(String::from("\nThis is a another document"))
);
}
}
#[tokio::test]
async fn database_row_upsert() {
let (c, _user) = generate_unique_registered_user_client().await;
let workspace_id = workspace_id_from_client(&c).await;
let databases = c.list_databases(&workspace_id).await.unwrap();
assert_eq!(databases.len(), 1);
let todo_db = &databases[0];
// predefined string to be used to identify the row
let pre_hash = String::from("my_id_123");
// Upsert row
let row_id = c
.upsert_database_item(
&workspace_id,
&todo_db.id,
pre_hash.clone(),
HashMap::from([
(String::from("Description"), json!("description_1")),
(String::from("Status"), json!("To Do")),
(String::from("Multiselect"), json!(["social", "news"])),
]),
Some("".to_string()),
)
.await
.unwrap();
{
// Get row and check data
let row_detail = &c
.list_database_row_details(&workspace_id, &todo_db.id, &[&row_id], false)
.await
.unwrap()[0];
assert_eq!(row_detail.cells["Description"], "description_1");
assert_eq!(row_detail.cells["Status"], "To Do");
assert_eq!(row_detail.cells["Multiselect"][0], "social");
assert_eq!(row_detail.cells["Multiselect"][1], "news");
assert!(!row_detail.has_doc);
}
{
// Upsert row again with different data, using same pre_hash
// row_id return should be the same as previous
let row_id_2 = c
.upsert_database_item(
&workspace_id,
&todo_db.id,
pre_hash,
HashMap::from([
(String::from("Description"), json!("description_2")),
(String::from("Status"), json!("Doing")),
(String::from("Multiselect"), json!(["fast", "self-host"])),
]),
Some("This is a document of a database row".to_string()),
)
.await
.unwrap();
assert_eq!(row_id, row_id_2);
}
{
// Get row and check data, it should be modified
let row_detail = &c
.list_database_row_details(&workspace_id, &todo_db.id, &[&row_id], true)
.await
.unwrap()[0];
assert_eq!(row_detail.cells["Description"], "description_2");
assert_eq!(row_detail.cells["Status"], "Doing");
assert_eq!(row_detail.cells["Multiselect"][0], "fast");
assert_eq!(row_detail.cells["Multiselect"][1], "self-host");
assert!(row_detail.has_doc);
assert_eq!(
row_detail.doc,
Some("\nThis is a document of a database row".to_string())
);
}
}
#[tokio::test]
async fn database_fields_crud() {
let (c, _user) = generate_unique_registered_user_client().await;
let workspace_id = workspace_id_from_client(&c).await;
let databases = c.list_databases(&workspace_id).await.unwrap();
assert_eq!(databases.len(), 1);
let todo_db = &databases[0];
let my_num_field_id = {
c.add_database_field(
&workspace_id,
&todo_db.id,
&AFInsertDatabaseField {
name: "MyNumberColumn".to_string(),
field_type: FieldType::Number.into(),
..Default::default()
},
)
.await
.unwrap()
};
let my_datetime_field_id = {
c.add_database_field(
&workspace_id,
&todo_db.id,
&AFInsertDatabaseField {
name: "MyDateTimeColumn".to_string(),
field_type: FieldType::DateTime.into(),
..Default::default()
},
)
.await
.unwrap()
};
let my_url_field_id = {
c.add_database_field(
&workspace_id,
&todo_db.id,
&AFInsertDatabaseField {
name: "MyUrlField".to_string(),
field_type: FieldType::URL.into(),
..Default::default()
},
)
.await
.unwrap()
};
let my_checkbox_field_id = {
c.add_database_field(
&workspace_id,
&todo_db.id,
&AFInsertDatabaseField {
name: "MyCheckboxColumn".to_string(),
field_type: FieldType::Checkbox.into(),
..Default::default()
},
)
.await
.unwrap()
};
{
let my_description = "my task 123";
let my_status = "To Do";
let new_row_id = c
.add_database_item(
&workspace_id,
&todo_db.id,
HashMap::from([
(String::from("Description"), json!(my_description)),
(String::from("Status"), json!(my_status)),
(String::from("Multiselect"), json!(["social", "news"])),
(my_num_field_id, json!(123)),
(my_datetime_field_id, json!(1733210221)),
(my_url_field_id, json!("https://appflowy.io")),
(my_checkbox_field_id, json!(true)),
]),
None,
)
.await
.unwrap();
let row_details = c
.list_database_row_details(&workspace_id, &todo_db.id, &[&new_row_id], false)
.await
.unwrap();
assert_eq!(row_details.len(), 1);
let new_row_detail = &row_details[0];
assert_eq!(new_row_detail.cells["Description"], my_description);
assert_eq!(new_row_detail.cells["Status"], my_status);
assert_eq!(new_row_detail.cells["Multiselect"][0], "social");
assert_eq!(new_row_detail.cells["Multiselect"][1], "news");
assert_eq!(new_row_detail.cells["MyNumberColumn"], "123");
assert_eq!(
new_row_detail.cells["MyDateTimeColumn"],
json!({
"end": serde_json::Value::Null,
"pretty_end_date": serde_json::Value::Null,
"pretty_end_datetime": serde_json::Value::Null,
"pretty_end_time": serde_json::Value::Null,
"pretty_start_date": "2024-12-03",
"pretty_start_datetime": "2024-12-03 07:17:01 UTC",
"pretty_start_time": "07:17:01",
"start": "2024-12-03T07:17:01+00:00",
"timezone": "UTC",
}),
);
assert_eq!(new_row_detail.cells["MyUrlField"], "https://appflowy.io");
assert_eq!(new_row_detail.cells["MyCheckboxColumn"], true);
}
}
#[tokio::test]
async fn database_fields_unsupported_field_type() {
let (c, _user) = generate_unique_registered_user_client().await;
let workspace_id = workspace_id_from_client(&c).await;
let databases = c.list_databases(&workspace_id).await.unwrap();
assert_eq!(databases.len(), 1);
let todo_db = &databases[0];
let my_rel_field_id = {
c.add_database_field(
&workspace_id,
&todo_db.id,
&AFInsertDatabaseField {
name: "MyRelationCol".to_string(),
field_type: FieldType::Relation.into(),
..Default::default()
},
)
.await
.unwrap()
};
{
let my_description = "my task 123";
let my_status = "To Do";
let new_row_id = c
.add_database_item(
&workspace_id,
&todo_db.id,
HashMap::from([
(String::from("Description"), json!(my_description)),
(String::from("Status"), json!(my_status)),
(String::from("Multiselect"), json!(["social", "news"])),
(my_rel_field_id, json!("relation_data")),
]),
None,
)
.await
.unwrap();
let row_details = c
.list_database_row_details(&workspace_id, &todo_db.id, &[&new_row_id], false)
.await
.unwrap();
assert_eq!(row_details.len(), 1);
let new_row_detail = &row_details[0];
assert!(!new_row_detail.cells.contains_key("MyRelationCol"));
}
}
#[tokio::test]
async fn database_insert_row_with_doc() {
let (c, _user) = generate_unique_registered_user_client().await;
let workspace_id = workspace_id_from_client(&c).await;
let databases = c.list_databases(&workspace_id).await.unwrap();
assert_eq!(databases.len(), 1);
let todo_db = &databases[0];
let row_doc = "This is a document of a database row";
let new_row_id = c
.add_database_item(
&workspace_id,
&todo_db.id,
HashMap::from([]),
row_doc.to_string().into(),
)
.await
.unwrap();
let row_details = c
.list_database_row_details(&workspace_id, &todo_db.id, &[&new_row_id], true)
.await
.unwrap();
let row_detail = &row_details[0];
assert!(row_detail.has_doc);
assert_eq!(
row_detail.doc,
Some("\nThis is a document of a database row".to_string())
);
}