feat: add support for create database pages via API
This commit is contained in:
parent
9e067e618b
commit
484cd595e0
|
|
@ -2171,7 +2171,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=be6bb90faac22ca8443a950ea3deafc7ec99b3a8#be6bb90faac22ca8443a950ea3deafc7ec99b3a8"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|
@ -2196,7 +2196,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=be6bb90faac22ca8443a950ea3deafc7ec99b3a8#be6bb90faac22ca8443a950ea3deafc7ec99b3a8"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
|
@ -2235,7 +2235,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=be6bb90faac22ca8443a950ea3deafc7ec99b3a8#be6bb90faac22ca8443a950ea3deafc7ec99b3a8"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|
@ -2256,7 +2256,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=be6bb90faac22ca8443a950ea3deafc7ec99b3a8#be6bb90faac22ca8443a950ea3deafc7ec99b3a8"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -2276,7 +2276,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=be6bb90faac22ca8443a950ea3deafc7ec99b3a8#be6bb90faac22ca8443a950ea3deafc7ec99b3a8"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
|
@ -2298,7 +2298,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-importer"
|
name = "collab-importer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=be6bb90faac22ca8443a950ea3deafc7ec99b3a8#be6bb90faac22ca8443a950ea3deafc7ec99b3a8"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
|
|
@ -2401,7 +2401,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=be6bb90faac22ca8443a950ea3deafc7ec99b3a8#be6bb90faac22ca8443a950ea3deafc7ec99b3a8"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
|
|
|
||||||
14
Cargo.toml
14
Cargo.toml
|
|
@ -309,13 +309,13 @@ lto = false # Disable Link-Time Optimization
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# It's diffcult to resovle different version with the same crate used in AppFlowy Frontend and the Client-API crate.
|
# 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.
|
# So using patch to workaround this issue.
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "be6bb90faac22ca8443a950ea3deafc7ec99b3a8" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "be6bb90faac22ca8443a950ea3deafc7ec99b3a8" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "be6bb90faac22ca8443a950ea3deafc7ec99b3a8" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "be6bb90faac22ca8443a950ea3deafc7ec99b3a8" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "be6bb90faac22ca8443a950ea3deafc7ec99b3a8" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "be6bb90faac22ca8443a950ea3deafc7ec99b3a8" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" }
|
||||||
collab-importer = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "be6bb90faac22ca8443a950ea3deafc7ec99b3a8" }
|
collab-importer = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
history = []
|
history = []
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ pub trait CollabStorageAccessControl: Send + Sync + 'static {
|
||||||
async fn enforce_delete(&self, workspace_id: &str, uid: &i64, oid: &str) -> Result<(), AppError>;
|
async fn enforce_delete(&self, workspace_id: &str, uid: &i64, oid: &str) -> Result<(), AppError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum GetCollabOrigin {
|
pub enum GetCollabOrigin {
|
||||||
User { uid: i64 },
|
User { uid: i64 },
|
||||||
Server,
|
Server,
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,19 @@ pub enum ViewLayout {
|
||||||
Chat = 4,
|
Chat = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ViewLayout {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let s = match self {
|
||||||
|
ViewLayout::Document => "Document",
|
||||||
|
ViewLayout::Grid => "Grid",
|
||||||
|
ViewLayout::Board => "Board",
|
||||||
|
ViewLayout::Calendar => "Calendar",
|
||||||
|
ViewLayout::Chat => "Chat",
|
||||||
|
};
|
||||||
|
write!(f, "{}", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for ViewLayout {
|
impl Default for ViewLayout {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Document
|
Self::Document
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use app_error::AppError;
|
||||||
use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
|
use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use collab::core::collab::DataSource;
|
||||||
use collab::preclude::Collab;
|
use collab::preclude::Collab;
|
||||||
use collab_database::database::DatabaseBody;
|
use collab_database::database::DatabaseBody;
|
||||||
use collab_database::entity::FieldType;
|
use collab_database::entity::FieldType;
|
||||||
|
|
@ -12,6 +13,7 @@ use collab_database::fields::Field;
|
||||||
use collab_database::fields::TypeOptions;
|
use collab_database::fields::TypeOptions;
|
||||||
use collab_database::rows::RowDetail;
|
use collab_database::rows::RowDetail;
|
||||||
use collab_database::workspace_database::NoPersistenceDatabaseCollabService;
|
use collab_database::workspace_database::NoPersistenceDatabaseCollabService;
|
||||||
|
use collab_database::workspace_database::WorkspaceDatabase;
|
||||||
use collab_database::workspace_database::WorkspaceDatabaseBody;
|
use collab_database::workspace_database::WorkspaceDatabaseBody;
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use collab_entity::EncodedCollab;
|
use collab_entity::EncodedCollab;
|
||||||
|
|
@ -295,6 +297,30 @@ pub async fn get_user_workspace_structure(
|
||||||
collab_folder_to_folder_view(root_view_id, &folder, depth, &publish_view_ids)
|
collab_folder_to_folder_view(root_view_id, &folder, depth, &publish_view_ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_latest_workspace_database(
|
||||||
|
collab_storage: &CollabAccessControlStorage,
|
||||||
|
pg_pool: &PgPool,
|
||||||
|
collab_origin: GetCollabOrigin,
|
||||||
|
workspace_id: Uuid,
|
||||||
|
) -> Result<(String, WorkspaceDatabase), AppError> {
|
||||||
|
let workspace_database_oid = select_workspace_database_oid(pg_pool, &workspace_id).await?;
|
||||||
|
let workspace_database_collab = {
|
||||||
|
let encoded_collab = get_latest_collab_encoded(
|
||||||
|
collab_storage,
|
||||||
|
collab_origin,
|
||||||
|
&workspace_id.to_string(),
|
||||||
|
&workspace_database_oid,
|
||||||
|
CollabType::WorkspaceDatabase,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
collab_from_doc_state(encoded_collab.doc_state.to_vec(), &workspace_database_oid)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let workspace_database = WorkspaceDatabase::open(workspace_database_collab)
|
||||||
|
.map_err(|err| AppError::Unhandled(format!("failed to open workspace database: {}", err)))?;
|
||||||
|
Ok((workspace_database_oid, workspace_database))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_latest_collab_folder(
|
pub async fn get_latest_collab_folder(
|
||||||
collab_storage: &CollabAccessControlStorage,
|
collab_storage: &CollabAccessControlStorage,
|
||||||
collab_origin: GetCollabOrigin,
|
collab_origin: GetCollabOrigin,
|
||||||
|
|
@ -736,3 +762,15 @@ fn add_to_selection_from_type_options(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn collab_from_doc_state(doc_state: Vec<u8>, object_id: &str) -> Result<Collab, AppError> {
|
||||||
|
let collab = Collab::new_with_source(
|
||||||
|
CollabOrigin::Server,
|
||||||
|
object_id,
|
||||||
|
DataSource::DocStateV1(doc_state),
|
||||||
|
vec![],
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map_err(|e| AppError::Unhandled(e.to_string()))?;
|
||||||
|
Ok(collab)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
use authentication::jwt::OptionalUserUuid;
|
use authentication::jwt::OptionalUserUuid;
|
||||||
use collab::core::collab::DataSource;
|
|
||||||
use collab::preclude::Collab;
|
|
||||||
use collab_folder::CollabOrigin;
|
use collab_folder::CollabOrigin;
|
||||||
use collab_rt_entity::{ClientCollabMessage, UpdateSync};
|
use collab_rt_entity::{ClientCollabMessage, UpdateSync};
|
||||||
use collab_rt_protocol::{Message, SyncMessage};
|
use collab_rt_protocol::{Message, SyncMessage};
|
||||||
|
|
@ -750,15 +748,3 @@ pub async fn broadcast_update(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collab_from_doc_state(doc_state: Vec<u8>, object_id: &str) -> Result<Collab, AppError> {
|
|
||||||
let collab = Collab::new_with_source(
|
|
||||||
CollabOrigin::Server,
|
|
||||||
object_id,
|
|
||||||
DataSource::DocStateV1(doc_state),
|
|
||||||
vec![],
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.map_err(|e| AppError::Unhandled(e.to_string()))?;
|
|
||||||
Ok(collab)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,20 @@ use app_error::AppError;
|
||||||
use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
|
use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use collab::core::collab::Collab;
|
use collab::core::collab::Collab;
|
||||||
|
use collab_database::database::{
|
||||||
|
gen_database_group_id, gen_database_id, gen_field_id, gen_row_id, Database, DatabaseContext,
|
||||||
|
};
|
||||||
|
use collab_database::entity::{CreateDatabaseParams, CreateViewParams, EncodedDatabase, FieldType};
|
||||||
|
use collab_database::fields::select_type_option::{
|
||||||
|
SelectOption, SelectOptionColor, SelectOptionIds, SingleSelectTypeOption,
|
||||||
|
};
|
||||||
|
use collab_database::fields::{default_field_settings_for_fields, Field};
|
||||||
|
use collab_database::rows::{new_cell_builder, CreateRowParams};
|
||||||
|
use collab_database::template::entity::CELL_DATA;
|
||||||
|
use collab_database::views::{
|
||||||
|
BoardLayoutSetting, CalendarLayoutSetting, DatabaseLayout, Group, GroupSetting, GroupSettingMap,
|
||||||
|
LayoutSetting, LayoutSettings,
|
||||||
|
};
|
||||||
use collab_database::workspace_database::{NoPersistenceDatabaseCollabService, WorkspaceDatabase};
|
use collab_database::workspace_database::{NoPersistenceDatabaseCollabService, WorkspaceDatabase};
|
||||||
use collab_database::{database::DatabaseBody, rows::RowId};
|
use collab_database::{database::DatabaseBody, rows::RowId};
|
||||||
use collab_document::document::Document;
|
use collab_document::document::Document;
|
||||||
|
|
@ -32,12 +46,18 @@ use crate::biz::collab::folder_view::{
|
||||||
parse_extra_field_as_json, to_dto_view_icon, to_dto_view_layout, to_folder_view_icon,
|
parse_extra_field_as_json, to_dto_view_icon, to_dto_view_layout, to_folder_view_icon,
|
||||||
to_space_permission,
|
to_space_permission,
|
||||||
};
|
};
|
||||||
|
use crate::biz::collab::ops::{collab_from_doc_state, get_latest_workspace_database};
|
||||||
use crate::biz::collab::{
|
use crate::biz::collab::{
|
||||||
folder_view::view_is_space,
|
folder_view::view_is_space,
|
||||||
ops::{get_latest_collab_encoded, get_latest_collab_folder},
|
ops::{get_latest_collab_encoded, get_latest_collab_folder},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ops::{broadcast_update, collab_from_doc_state};
|
use super::ops::broadcast_update;
|
||||||
|
|
||||||
|
struct WorkspaceDatabaseUpdate {
|
||||||
|
pub updated_encoded_collab: Vec<u8>,
|
||||||
|
pub encoded_updates: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
struct FolderUpdate {
|
struct FolderUpdate {
|
||||||
pub updated_encoded_collab: Vec<u8>,
|
pub updated_encoded_collab: Vec<u8>,
|
||||||
|
|
@ -140,11 +160,8 @@ pub async fn create_page(
|
||||||
view_layout: &ViewLayout,
|
view_layout: &ViewLayout,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
) -> Result<Page, AppError> {
|
) -> Result<Page, AppError> {
|
||||||
if *view_layout != ViewLayout::Document {
|
match view_layout {
|
||||||
return Err(AppError::InvalidRequest(
|
ViewLayout::Document => {
|
||||||
"Only document layout is supported for page creation".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
create_document_page(
|
create_document_page(
|
||||||
pg_pool,
|
pg_pool,
|
||||||
collab_storage,
|
collab_storage,
|
||||||
|
|
@ -154,6 +171,45 @@ pub async fn create_page(
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
},
|
||||||
|
ViewLayout::Grid => {
|
||||||
|
create_grid_page(
|
||||||
|
pg_pool,
|
||||||
|
collab_storage,
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
parent_view_id,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
},
|
||||||
|
ViewLayout::Calendar => {
|
||||||
|
create_calendar_page(
|
||||||
|
pg_pool,
|
||||||
|
collab_storage,
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
parent_view_id,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
},
|
||||||
|
ViewLayout::Board => {
|
||||||
|
create_board_page(
|
||||||
|
pg_pool,
|
||||||
|
collab_storage,
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
parent_view_id,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
},
|
||||||
|
layout => Err(AppError::InvalidRequest(format!(
|
||||||
|
"The layout type {} is not supported for page creation",
|
||||||
|
layout
|
||||||
|
))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_default_document_collab_param() -> Result<CollabParams, AppError> {
|
fn prepare_default_document_collab_param() -> Result<CollabParams, AppError> {
|
||||||
|
|
@ -173,6 +229,175 @@ fn prepare_default_document_collab_param() -> Result<CollabParams, AppError> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
async fn prepare_new_encoded_database(
|
||||||
|
view_id: &str,
|
||||||
|
database_id: &str,
|
||||||
|
name: &str,
|
||||||
|
fields: Vec<Field>,
|
||||||
|
rows: Vec<CreateRowParams>,
|
||||||
|
database_layout: DatabaseLayout,
|
||||||
|
layout_setting: Option<LayoutSetting>,
|
||||||
|
group_settings: Vec<GroupSettingMap>,
|
||||||
|
) -> Result<EncodedDatabase, AppError> {
|
||||||
|
let timestamp = collab_database::database::timestamp();
|
||||||
|
let context = DatabaseContext::new(Arc::new(NoPersistenceDatabaseCollabService));
|
||||||
|
let field_settings = default_field_settings_for_fields(&fields, database_layout);
|
||||||
|
let mut layout_settings = LayoutSettings::default();
|
||||||
|
if let Some(layout_setting) = layout_setting {
|
||||||
|
layout_settings.insert(database_layout, layout_setting);
|
||||||
|
}
|
||||||
|
let params = CreateDatabaseParams {
|
||||||
|
database_id: database_id.to_string(),
|
||||||
|
fields,
|
||||||
|
rows,
|
||||||
|
views: vec![CreateViewParams {
|
||||||
|
database_id: database_id.to_string(),
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
name: name.to_string(),
|
||||||
|
layout: database_layout,
|
||||||
|
layout_settings,
|
||||||
|
filters: vec![],
|
||||||
|
group_settings,
|
||||||
|
sorts: vec![],
|
||||||
|
field_settings,
|
||||||
|
created_at: timestamp,
|
||||||
|
modified_at: timestamp,
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
let database = Database::create_with_view(params, context)
|
||||||
|
.await
|
||||||
|
.map_err(|err| AppError::Internal(anyhow!("Failed to create database with view: {}", err)))?;
|
||||||
|
database
|
||||||
|
.encode_database_collabs()
|
||||||
|
.await
|
||||||
|
.map_err(|err| AppError::Internal(anyhow!("Failed to encode database: {}", err)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prepare_default_calendar_encoded_database(
|
||||||
|
view_id: &str,
|
||||||
|
database_id: &str,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<EncodedDatabase, AppError> {
|
||||||
|
let text_field = Field::from_field_type("Title", FieldType::RichText, true);
|
||||||
|
let date_field = Field::from_field_type("Date", FieldType::DateTime, true);
|
||||||
|
let date_field_id = date_field.id.clone();
|
||||||
|
let multi_select_field = Field::from_field_type("Tags", FieldType::MultiSelect, true);
|
||||||
|
let fields = vec![text_field, date_field, multi_select_field];
|
||||||
|
let layout_setting = CalendarLayoutSetting::new(date_field_id);
|
||||||
|
|
||||||
|
prepare_new_encoded_database(
|
||||||
|
view_id,
|
||||||
|
database_id,
|
||||||
|
name,
|
||||||
|
fields,
|
||||||
|
vec![],
|
||||||
|
DatabaseLayout::Calendar,
|
||||||
|
Some(layout_setting.into()),
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prepare_default_grid_encoded_database(
|
||||||
|
view_id: &str,
|
||||||
|
database_id: &str,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<EncodedDatabase, AppError> {
|
||||||
|
let text_field = Field::from_field_type("Name", FieldType::RichText, true);
|
||||||
|
let single_select_field = Field::from_field_type("Type", FieldType::SingleSelect, true);
|
||||||
|
let checkbox_field = Field::from_field_type("Done", FieldType::Checkbox, true);
|
||||||
|
let fields = vec![text_field, single_select_field, checkbox_field];
|
||||||
|
let rows = (0..3)
|
||||||
|
.map(|_| CreateRowParams::new(gen_row_id(), database_id.to_string()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
prepare_new_encoded_database(
|
||||||
|
view_id,
|
||||||
|
database_id,
|
||||||
|
name,
|
||||||
|
fields,
|
||||||
|
rows,
|
||||||
|
DatabaseLayout::Grid,
|
||||||
|
None,
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prepare_default_board_encoded_database(
|
||||||
|
view_id: &str,
|
||||||
|
database_id: &str,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<EncodedDatabase, AppError> {
|
||||||
|
let card_title_field = Field::from_field_type("Description", FieldType::RichText, true);
|
||||||
|
let text_field_id = card_title_field.id.clone();
|
||||||
|
|
||||||
|
let to_do_option = SelectOption::with_color("To Do", SelectOptionColor::Purple);
|
||||||
|
let doing_option = SelectOption::with_color("Doing", SelectOptionColor::Orange);
|
||||||
|
let done_option = SelectOption::with_color("Done", SelectOptionColor::Yellow);
|
||||||
|
let default_option_id = to_do_option.id.clone();
|
||||||
|
let options = vec![to_do_option, doing_option, done_option];
|
||||||
|
let card_status_option_ids: Vec<String> =
|
||||||
|
options.iter().map(|option| option.id.clone()).collect();
|
||||||
|
let mut card_status_options = SingleSelectTypeOption::default();
|
||||||
|
card_status_options.options.extend(options);
|
||||||
|
let mut card_status_field = Field::new(
|
||||||
|
gen_field_id(),
|
||||||
|
"Status".to_string(),
|
||||||
|
FieldType::SingleSelect.into(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
card_status_field.type_options.insert(
|
||||||
|
FieldType::SingleSelect.to_string(),
|
||||||
|
card_status_options.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let card_status_field_id = card_status_field.id.clone();
|
||||||
|
let card_status_field_type = card_status_field.field_type;
|
||||||
|
let mut group_ids = vec![card_status_field_id.clone()];
|
||||||
|
group_ids.extend(card_status_option_ids);
|
||||||
|
let groups = group_ids.iter().map(|id| Group::new(id.clone())).collect();
|
||||||
|
let group_settings: Vec<GroupSettingMap> = vec![GroupSetting {
|
||||||
|
id: gen_database_group_id(),
|
||||||
|
field_id: card_status_field_id.clone(),
|
||||||
|
field_type: card_status_field_type,
|
||||||
|
groups,
|
||||||
|
content: Default::default(),
|
||||||
|
}
|
||||||
|
.into()];
|
||||||
|
|
||||||
|
let mut rows = vec![];
|
||||||
|
let card_status_select_option_ids = SelectOptionIds::from(vec![default_option_id.clone()]);
|
||||||
|
for i in 0..3 {
|
||||||
|
let card_status_cell_data = card_status_select_option_ids.to_cell_data(FieldType::SingleSelect);
|
||||||
|
let mut description_cell = new_cell_builder(FieldType::RichText);
|
||||||
|
let description_text = format!("Card {}", i + 1);
|
||||||
|
description_cell.insert(CELL_DATA.into(), description_text.into());
|
||||||
|
let mut row = CreateRowParams::new(gen_row_id(), database_id.to_string());
|
||||||
|
row
|
||||||
|
.cells
|
||||||
|
.insert(card_status_field_id.clone(), card_status_cell_data);
|
||||||
|
row.cells.insert(text_field_id.clone(), description_cell);
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
let fields = vec![card_title_field, card_status_field];
|
||||||
|
let layout_setting = BoardLayoutSetting::new();
|
||||||
|
|
||||||
|
prepare_new_encoded_database(
|
||||||
|
view_id,
|
||||||
|
database_id,
|
||||||
|
name,
|
||||||
|
fields,
|
||||||
|
rows,
|
||||||
|
DatabaseLayout::Board,
|
||||||
|
Some(layout_setting.into()),
|
||||||
|
group_settings,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn add_new_space_to_folder(
|
async fn add_new_space_to_folder(
|
||||||
uid: i64,
|
uid: i64,
|
||||||
|
|
@ -248,17 +473,36 @@ async fn update_space_properties(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn add_new_database_to_workspace(
|
||||||
|
workspace_database: &mut WorkspaceDatabase,
|
||||||
|
database_id: &str,
|
||||||
|
view_id: &str,
|
||||||
|
) -> Result<WorkspaceDatabaseUpdate, AppError> {
|
||||||
|
let view_ids_by_database_id =
|
||||||
|
HashMap::from([(database_id.to_string(), vec![view_id.to_string()])]);
|
||||||
|
let encoded_updates = workspace_database
|
||||||
|
.batch_add_database(view_ids_by_database_id)
|
||||||
|
.encode_update_v1();
|
||||||
|
let updated_encoded_collab = workspace_database_to_encoded_collab(workspace_database)?;
|
||||||
|
Ok(WorkspaceDatabaseUpdate {
|
||||||
|
updated_encoded_collab,
|
||||||
|
encoded_updates,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn add_new_view_to_folder(
|
async fn add_new_view_to_folder(
|
||||||
uid: i64,
|
uid: i64,
|
||||||
parent_view_id: &str,
|
parent_view_id: &str,
|
||||||
view_id: &str,
|
view_id: &str,
|
||||||
folder: &mut Folder,
|
folder: &mut Folder,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
|
layout: collab_folder::ViewLayout,
|
||||||
) -> Result<FolderUpdate, AppError> {
|
) -> Result<FolderUpdate, AppError> {
|
||||||
let encoded_update = {
|
let encoded_update = {
|
||||||
let view = NestedChildViewBuilder::new(uid, parent_view_id.to_string())
|
let view = NestedChildViewBuilder::new(uid, parent_view_id.to_string())
|
||||||
.with_view_id(view_id)
|
.with_view_id(view_id)
|
||||||
.with_name(name.unwrap_or_default())
|
.with_name(name.unwrap_or_default())
|
||||||
|
.with_layout(layout)
|
||||||
.build()
|
.build()
|
||||||
.view;
|
.view;
|
||||||
let mut txn = folder.collab.transact_mut();
|
let mut txn = folder.collab.transact_mut();
|
||||||
|
|
@ -372,6 +616,55 @@ fn folder_to_encoded_collab(folder: &Folder) -> Result<Vec<u8>, AppError> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn workspace_database_to_encoded_collab(
|
||||||
|
workspace_db: &WorkspaceDatabase,
|
||||||
|
) -> Result<Vec<u8>, AppError> {
|
||||||
|
let encoded_workspace_db_collab = workspace_db
|
||||||
|
.encode_collab_v1()
|
||||||
|
.map_err(|err| AppError::Internal(anyhow!("Failed to encode workspace folder: {}", err)))?;
|
||||||
|
encoded_workspace_db_collab
|
||||||
|
.encode_to_bytes()
|
||||||
|
.map_err(|err| {
|
||||||
|
AppError::Internal(anyhow!(
|
||||||
|
"Failed to encode workspace folder to bytes: {}",
|
||||||
|
err
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn insert_and_broadcast_workspace_database_update(
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: Uuid,
|
||||||
|
workspace_database_id: &str,
|
||||||
|
workspace_database_update: WorkspaceDatabaseUpdate,
|
||||||
|
collab_storage: &CollabAccessControlStorage,
|
||||||
|
transaction: &mut Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<(), AppError> {
|
||||||
|
let params = CollabParams {
|
||||||
|
object_id: workspace_database_id.to_string(),
|
||||||
|
encoded_collab_v1: workspace_database_update.updated_encoded_collab.into(),
|
||||||
|
collab_type: CollabType::WorkspaceDatabase,
|
||||||
|
embeddings: None,
|
||||||
|
};
|
||||||
|
let action_description = format!("Update workspace database: {}", workspace_id);
|
||||||
|
collab_storage
|
||||||
|
.insert_new_collab_with_transaction(
|
||||||
|
&workspace_id.to_string(),
|
||||||
|
&uid,
|
||||||
|
params,
|
||||||
|
transaction,
|
||||||
|
&action_description,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
broadcast_update(
|
||||||
|
collab_storage,
|
||||||
|
workspace_database_id,
|
||||||
|
workspace_database_update.encoded_updates.clone(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn insert_and_broadcast_workspace_folder_update(
|
async fn insert_and_broadcast_workspace_folder_update(
|
||||||
uid: i64,
|
uid: i64,
|
||||||
workspace_id: Uuid,
|
workspace_id: Uuid,
|
||||||
|
|
@ -417,8 +710,15 @@ async fn create_document_page(
|
||||||
let collab_origin = GetCollabOrigin::User { uid };
|
let collab_origin = GetCollabOrigin::User { uid };
|
||||||
let mut folder =
|
let mut folder =
|
||||||
get_latest_collab_folder(collab_storage, collab_origin, &workspace_id.to_string()).await?;
|
get_latest_collab_folder(collab_storage, collab_origin, &workspace_id.to_string()).await?;
|
||||||
let folder_update =
|
let folder_update = add_new_view_to_folder(
|
||||||
add_new_view_to_folder(uid, parent_view_id, &view_id, &mut folder, name).await?;
|
uid,
|
||||||
|
parent_view_id,
|
||||||
|
&view_id,
|
||||||
|
&mut folder,
|
||||||
|
name,
|
||||||
|
collab_folder::ViewLayout::Document,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
let mut transaction = pg_pool.begin().await?;
|
let mut transaction = pg_pool.begin().await?;
|
||||||
let action = format!("Create new collab: {}", view_id);
|
let action = format!("Create new collab: {}", view_id);
|
||||||
collab_storage
|
collab_storage
|
||||||
|
|
@ -442,6 +742,170 @@ async fn create_document_page(
|
||||||
Ok(Page { view_id })
|
Ok(Page { view_id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_grid_page(
|
||||||
|
pg_pool: &PgPool,
|
||||||
|
collab_storage: &CollabAccessControlStorage,
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: Uuid,
|
||||||
|
parent_view_id: &str,
|
||||||
|
name: Option<&str>,
|
||||||
|
) -> Result<Page, AppError> {
|
||||||
|
let view_id = Uuid::new_v4().to_string();
|
||||||
|
let database_id = gen_database_id();
|
||||||
|
let default_grid_encoded_database =
|
||||||
|
prepare_default_grid_encoded_database(&view_id, &database_id, name.unwrap_or_default()).await?;
|
||||||
|
create_database_page(
|
||||||
|
pg_pool,
|
||||||
|
collab_storage,
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
parent_view_id,
|
||||||
|
&view_id,
|
||||||
|
collab_folder::ViewLayout::Grid,
|
||||||
|
name,
|
||||||
|
&default_grid_encoded_database,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_board_page(
|
||||||
|
pg_pool: &PgPool,
|
||||||
|
collab_storage: &CollabAccessControlStorage,
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: Uuid,
|
||||||
|
parent_view_id: &str,
|
||||||
|
name: Option<&str>,
|
||||||
|
) -> Result<Page, AppError> {
|
||||||
|
let view_id = Uuid::new_v4().to_string();
|
||||||
|
let database_id = gen_database_id();
|
||||||
|
let default_board_encoded_database =
|
||||||
|
prepare_default_board_encoded_database(&view_id, &database_id, name.unwrap_or_default())
|
||||||
|
.await?;
|
||||||
|
create_database_page(
|
||||||
|
pg_pool,
|
||||||
|
collab_storage,
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
parent_view_id,
|
||||||
|
&view_id,
|
||||||
|
collab_folder::ViewLayout::Board,
|
||||||
|
name,
|
||||||
|
&default_board_encoded_database,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_calendar_page(
|
||||||
|
pg_pool: &PgPool,
|
||||||
|
collab_storage: &CollabAccessControlStorage,
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: Uuid,
|
||||||
|
parent_view_id: &str,
|
||||||
|
name: Option<&str>,
|
||||||
|
) -> Result<Page, AppError> {
|
||||||
|
let view_id = Uuid::new_v4().to_string();
|
||||||
|
let database_id = gen_database_id();
|
||||||
|
let default_calendar_encoded_database =
|
||||||
|
prepare_default_calendar_encoded_database(&view_id, &database_id, name.unwrap_or_default())
|
||||||
|
.await?;
|
||||||
|
create_database_page(
|
||||||
|
pg_pool,
|
||||||
|
collab_storage,
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
parent_view_id,
|
||||||
|
&view_id,
|
||||||
|
collab_folder::ViewLayout::Calendar,
|
||||||
|
name,
|
||||||
|
&default_calendar_encoded_database,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
async fn create_database_page(
|
||||||
|
pg_pool: &PgPool,
|
||||||
|
collab_storage: &CollabAccessControlStorage,
|
||||||
|
uid: i64,
|
||||||
|
workspace_id: Uuid,
|
||||||
|
parent_view_id: &str,
|
||||||
|
view_id: &str,
|
||||||
|
view_layout: collab_folder::ViewLayout,
|
||||||
|
name: Option<&str>,
|
||||||
|
encoded_database: &EncodedDatabase,
|
||||||
|
) -> Result<Page, AppError> {
|
||||||
|
let collab_origin = GetCollabOrigin::User { uid };
|
||||||
|
let mut folder = get_latest_collab_folder(
|
||||||
|
collab_storage,
|
||||||
|
collab_origin.clone(),
|
||||||
|
&workspace_id.to_string(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let folder_update =
|
||||||
|
add_new_view_to_folder(uid, parent_view_id, view_id, &mut folder, name, view_layout).await?;
|
||||||
|
let (workspace_database_id, mut workspace_database) =
|
||||||
|
get_latest_workspace_database(collab_storage, pg_pool, collab_origin, workspace_id).await?;
|
||||||
|
let database_id = encoded_database.encoded_database_collab.object_id.clone();
|
||||||
|
let workspace_database_update =
|
||||||
|
add_new_database_to_workspace(&mut workspace_database, &database_id, view_id).await?;
|
||||||
|
let database_collab_params = CollabParams {
|
||||||
|
object_id: database_id.clone(),
|
||||||
|
encoded_collab_v1: encoded_database
|
||||||
|
.encoded_database_collab
|
||||||
|
.encoded_collab
|
||||||
|
.encode_to_bytes()?
|
||||||
|
.into(),
|
||||||
|
collab_type: CollabType::Database,
|
||||||
|
embeddings: None,
|
||||||
|
};
|
||||||
|
let row_collab_params_list = encoded_database
|
||||||
|
.encoded_row_collabs
|
||||||
|
.iter()
|
||||||
|
.map(|row_collab| CollabParams {
|
||||||
|
object_id: row_collab.object_id.clone(),
|
||||||
|
encoded_collab_v1: row_collab.encoded_collab.encode_to_bytes().unwrap().into(),
|
||||||
|
collab_type: CollabType::DatabaseRow,
|
||||||
|
embeddings: None,
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let mut transaction = pg_pool.begin().await?;
|
||||||
|
let action = format!("Create new database collab: {}", database_id);
|
||||||
|
collab_storage
|
||||||
|
.insert_new_collab_with_transaction(
|
||||||
|
&workspace_id.to_string(),
|
||||||
|
&uid,
|
||||||
|
database_collab_params,
|
||||||
|
&mut transaction,
|
||||||
|
&action,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
collab_storage
|
||||||
|
.batch_insert_new_collab(&workspace_id.to_string(), &uid, row_collab_params_list)
|
||||||
|
.await?;
|
||||||
|
insert_and_broadcast_workspace_folder_update(
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
folder_update,
|
||||||
|
collab_storage,
|
||||||
|
&mut transaction,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
insert_and_broadcast_workspace_database_update(
|
||||||
|
uid,
|
||||||
|
workspace_id,
|
||||||
|
&workspace_database_id,
|
||||||
|
workspace_database_update,
|
||||||
|
collab_storage,
|
||||||
|
&mut transaction,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
transaction.commit().await?;
|
||||||
|
Ok(Page {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn move_page_to_trash(
|
pub async fn move_page_to_trash(
|
||||||
pg_pool: &PgPool,
|
pg_pool: &PgPool,
|
||||||
collab_storage: &CollabAccessControlStorage,
|
collab_storage: &CollabAccessControlStorage,
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,10 @@ use yrs::{Map, MapRef};
|
||||||
|
|
||||||
use crate::biz::collab::folder_view::to_folder_view_icon;
|
use crate::biz::collab::folder_view::to_folder_view_icon;
|
||||||
use crate::biz::collab::folder_view::to_folder_view_layout;
|
use crate::biz::collab::folder_view::to_folder_view_layout;
|
||||||
|
use crate::biz::collab::ops::collab_from_doc_state;
|
||||||
use crate::biz::collab::ops::get_latest_collab_encoded;
|
use crate::biz::collab::ops::get_latest_collab_encoded;
|
||||||
|
|
||||||
use super::ops::broadcast_update;
|
use super::ops::broadcast_update;
|
||||||
use super::ops::collab_from_doc_state;
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn duplicate_published_collab_to_workspace(
|
pub async fn duplicate_published_collab_to_workspace(
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,81 @@ async fn get_page_view() {
|
||||||
assert_eq!(resp.data.row_data.len(), 0);
|
assert_eq!(resp.data.row_data.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_new_page_with_database() {
|
||||||
|
let (c, _user) = generate_unique_registered_user_client().await;
|
||||||
|
let workspaces = c.get_workspaces().await.unwrap();
|
||||||
|
assert_eq!(workspaces.len(), 1);
|
||||||
|
let workspace_id = workspaces[0].workspace_id;
|
||||||
|
let folder_view = c
|
||||||
|
.get_workspace_folder(&workspace_id.to_string(), Some(2), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let general_space = &folder_view
|
||||||
|
.children
|
||||||
|
.into_iter()
|
||||||
|
.find(|v| v.name == "General")
|
||||||
|
.unwrap();
|
||||||
|
let calendar_page = c
|
||||||
|
.create_workspace_page_view(
|
||||||
|
workspace_id,
|
||||||
|
&CreatePageParams {
|
||||||
|
parent_view_id: general_space.view_id.clone(),
|
||||||
|
layout: ViewLayout::Calendar,
|
||||||
|
name: Some("New calendar".to_string()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let grid_page = c
|
||||||
|
.create_workspace_page_view(
|
||||||
|
workspace_id,
|
||||||
|
&CreatePageParams {
|
||||||
|
parent_view_id: general_space.view_id.clone(),
|
||||||
|
layout: ViewLayout::Grid,
|
||||||
|
name: Some("New grid".to_string()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let board_page = c
|
||||||
|
.create_workspace_page_view(
|
||||||
|
workspace_id,
|
||||||
|
&CreatePageParams {
|
||||||
|
parent_view_id: general_space.view_id.clone(),
|
||||||
|
layout: ViewLayout::Grid,
|
||||||
|
name: Some("New board".to_string()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
sleep(Duration::from_secs(1)).await;
|
||||||
|
let folder_view = c
|
||||||
|
.get_workspace_folder(&workspace_id.to_string(), Some(2), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let general_space = &folder_view
|
||||||
|
.children
|
||||||
|
.into_iter()
|
||||||
|
.find(|v| v.name == "General")
|
||||||
|
.unwrap();
|
||||||
|
let views_under_general_space: HashSet<String> = general_space
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.view_id.clone())
|
||||||
|
.collect();
|
||||||
|
for view_id in &[
|
||||||
|
calendar_page.view_id.clone(),
|
||||||
|
grid_page.view_id.clone(),
|
||||||
|
board_page.view_id.clone(),
|
||||||
|
] {
|
||||||
|
assert!(views_under_general_space.contains(view_id));
|
||||||
|
c.get_workspace_page_view(workspace_id, view_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn create_new_document_page() {
|
async fn create_new_document_page() {
|
||||||
let (c, _user) = generate_unique_registered_user_client().await;
|
let (c, _user) = generate_unique_registered_user_client().await;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use app_error::ErrorCode;
|
use app_error::ErrorCode;
|
||||||
use appflowy_cloud::biz::collab::folder_view::collab_folder_to_folder_view;
|
use appflowy_cloud::biz::collab::folder_view::collab_folder_to_folder_view;
|
||||||
use appflowy_cloud::biz::workspace::ops::collab_from_doc_state;
|
use appflowy_cloud::biz::collab::ops::collab_from_doc_state;
|
||||||
use client_api::entity::{
|
use client_api::entity::{
|
||||||
AFRole, GlobalComment, PatchPublishedCollab, PublishCollabItem, PublishCollabMetadata,
|
AFRole, GlobalComment, PatchPublishedCollab, PublishCollabItem, PublishCollabMetadata,
|
||||||
PublishInfoMeta,
|
PublishInfoMeta,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue