mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: cloud storage test (#2663)
* chore: show default user name * chore: update * feat: change collab storage type after auth type changed * chore: reload folder * chore: initial the group controller if need * chore: update patch * chore: update patch ref
This commit is contained in:
@ -6,7 +6,7 @@ use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use appflowy_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab_database::database::DatabaseData;
|
||||
use collab_database::user::{UserDatabase as InnerUserDatabase, UserDatabaseCollabBuilder};
|
||||
use collab_database::user::{DatabaseCollabBuilder, UserDatabase as InnerUserDatabase};
|
||||
use collab_database::views::{CreateDatabaseParams, CreateViewParams};
|
||||
use parking_lot::Mutex;
|
||||
use tokio::sync::RwLock;
|
||||
@ -183,7 +183,7 @@ impl DatabaseManager2 {
|
||||
match duplicated_view_id {
|
||||
None => {
|
||||
let params = CreateViewParams::new(database_id, target_view_id, name, layout.into());
|
||||
database.create_linked_view(params);
|
||||
database.create_linked_view(params)?;
|
||||
},
|
||||
Some(duplicated_view_id) => {
|
||||
database.duplicate_linked_view(&duplicated_view_id);
|
||||
@ -256,18 +256,27 @@ unsafe impl Send for UserDatabase {}
|
||||
|
||||
struct UserDatabaseCollabBuilderImpl(Arc<AppFlowyCollabBuilder>);
|
||||
|
||||
impl UserDatabaseCollabBuilder for UserDatabaseCollabBuilderImpl {
|
||||
fn build(&self, uid: i64, object_id: &str, db: Arc<RocksCollabDB>) -> Arc<MutexCollab> {
|
||||
self.0.build(uid, object_id, db)
|
||||
impl DatabaseCollabBuilder for UserDatabaseCollabBuilderImpl {
|
||||
fn build(
|
||||
&self,
|
||||
uid: i64,
|
||||
object_id: &str,
|
||||
object_name: &str,
|
||||
db: Arc<RocksCollabDB>,
|
||||
) -> Arc<MutexCollab> {
|
||||
self.0.build(uid, object_id, object_name, db)
|
||||
}
|
||||
|
||||
fn build_with_config(
|
||||
&self,
|
||||
uid: i64,
|
||||
object_id: &str,
|
||||
object_name: &str,
|
||||
db: Arc<RocksCollabDB>,
|
||||
config: &CollabPersistenceConfig,
|
||||
) -> Arc<MutexCollab> {
|
||||
self.0.build_with_config(uid, object_id, db, config)
|
||||
self
|
||||
.0
|
||||
.build_with_config(uid, object_id, object_name, db, config)
|
||||
}
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ impl<'a> CellBuilder<'a> {
|
||||
/// Build list of Cells from HashMap of cell string by field id.
|
||||
pub fn with_cells(cell_by_field_id: HashMap<String, String>, fields: &'a [Field]) -> Self {
|
||||
let field_maps = fields
|
||||
.into_iter()
|
||||
.iter()
|
||||
.map(|field| (field.id.clone(), field))
|
||||
.collect::<HashMap<String, &Field>>();
|
||||
|
||||
|
@ -574,9 +574,14 @@ impl DatabaseEditor {
|
||||
row_id: RowId,
|
||||
options: Vec<SelectOptionPB>,
|
||||
) -> FlowyResult<()> {
|
||||
let field = self.database.lock().fields.get_field(field_id).ok_or(
|
||||
FlowyError::record_not_found().context(format!("Field with id:{} not found", &field_id)),
|
||||
)?;
|
||||
let field = self
|
||||
.database
|
||||
.lock()
|
||||
.fields
|
||||
.get_field(field_id)
|
||||
.ok_or_else(|| {
|
||||
FlowyError::record_not_found().context(format!("Field with id:{} not found", &field_id))
|
||||
})?;
|
||||
debug_assert!(FieldType::from(field.field_type).is_select_option());
|
||||
|
||||
let mut type_option = select_type_option_from_field(&field)?;
|
||||
@ -670,9 +675,14 @@ impl DatabaseEditor {
|
||||
field_id: &str,
|
||||
changeset: ChecklistCellChangeset,
|
||||
) -> FlowyResult<()> {
|
||||
let field = self.database.lock().fields.get_field(field_id).ok_or(
|
||||
FlowyError::record_not_found().context(format!("Field with id:{} not found", &field_id)),
|
||||
)?;
|
||||
let field = self
|
||||
.database
|
||||
.lock()
|
||||
.fields
|
||||
.get_field(field_id)
|
||||
.ok_or_else(|| {
|
||||
FlowyError::record_not_found().context(format!("Field with id:{} not found", &field_id))
|
||||
})?;
|
||||
debug_assert!(FieldType::from(field.field_type).is_checklist());
|
||||
|
||||
self
|
||||
@ -684,7 +694,7 @@ impl DatabaseEditor {
|
||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||
pub async fn load_groups(&self, view_id: &str) -> FlowyResult<RepeatedGroupPB> {
|
||||
let view = self.database_views.get_view_editor(view_id).await?;
|
||||
let groups = view.v_load_groups().await?;
|
||||
let groups = view.v_load_groups().await.unwrap_or_default();
|
||||
Ok(RepeatedGroupPB { items: groups })
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ pub trait DatabaseViewData: Send + Sync + 'static {
|
||||
pub struct DatabaseViewEditor {
|
||||
pub view_id: String,
|
||||
delegate: Arc<dyn DatabaseViewData>,
|
||||
group_controller: Arc<RwLock<Box<dyn GroupController>>>,
|
||||
group_controller: Arc<RwLock<Option<Box<dyn GroupController>>>>,
|
||||
filter_controller: Arc<FilterController>,
|
||||
sort_controller: Arc<RwLock<SortController>>,
|
||||
pub notifier: DatabaseViewChangedNotifier,
|
||||
@ -192,10 +192,12 @@ impl DatabaseViewEditor {
|
||||
},
|
||||
Some(group_id) => {
|
||||
self
|
||||
.group_controller
|
||||
.write()
|
||||
.await
|
||||
.did_create_row(row, group_id);
|
||||
.mut_group_controller(|group_controller, _| {
|
||||
group_controller.did_create_row(row, group_id);
|
||||
Ok(())
|
||||
})
|
||||
.await;
|
||||
|
||||
let inserted_row = InsertedRowPB {
|
||||
row: RowPB::from(row),
|
||||
index: Some(index as i32),
|
||||
@ -347,22 +349,29 @@ impl DatabaseViewEditor {
|
||||
}
|
||||
/// Only call once after database view editor initialized
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub async fn v_load_groups(&self) -> FlowyResult<Vec<GroupPB>> {
|
||||
pub async fn v_load_groups(&self) -> Option<Vec<GroupPB>> {
|
||||
let groups = self
|
||||
.group_controller
|
||||
.read()
|
||||
.await
|
||||
.as_ref()?
|
||||
.groups()
|
||||
.into_iter()
|
||||
.map(|group_data| GroupPB::from(group_data.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
tracing::trace!("Number of groups: {}", groups.len());
|
||||
Ok(groups)
|
||||
Some(groups)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(self))]
|
||||
pub async fn v_get_group(&self, group_id: &str) -> FlowyResult<GroupPB> {
|
||||
match self.group_controller.read().await.get_group(group_id) {
|
||||
match self
|
||||
.group_controller
|
||||
.read()
|
||||
.await
|
||||
.as_ref()
|
||||
.and_then(|group| group.get_group(group_id))
|
||||
{
|
||||
None => Err(FlowyError::record_not_found().context("Can't find the group")),
|
||||
Some((_, group)) => Ok(GroupPB::from(group)),
|
||||
}
|
||||
@ -371,19 +380,22 @@ impl DatabaseViewEditor {
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub async fn v_move_group(&self, from_group: &str, to_group: &str) -> FlowyResult<()> {
|
||||
self
|
||||
.group_controller
|
||||
.write()
|
||||
.await
|
||||
.move_group(from_group, to_group)?;
|
||||
.mut_group_controller(|group_controller, _| group_controller.move_group(from_group, to_group))
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn group_id(&self) -> String {
|
||||
self.group_controller.read().await.field_id().to_string()
|
||||
pub async fn is_grouping_field(&self, field_id: &str) -> bool {
|
||||
match self.group_controller.read().await.as_ref() {
|
||||
Some(group_controller) => group_controller.field_id() == field_id,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when the user changes the grouping field
|
||||
pub async fn v_initialize_new_group(&self, field_id: &str) -> FlowyResult<()> {
|
||||
if self.group_controller.read().await.field_id() != field_id {
|
||||
let is_grouping_field = self.is_grouping_field(field_id).await;
|
||||
if !is_grouping_field {
|
||||
self.v_update_grouping_field(field_id).await?;
|
||||
|
||||
if let Some(view) = self.delegate.get_view_setting(&self.view_id).await {
|
||||
@ -400,10 +412,11 @@ impl DatabaseViewEditor {
|
||||
|
||||
pub async fn update_group_setting(&self, changeset: GroupSettingChangeset) -> FlowyResult<()> {
|
||||
self
|
||||
.group_controller
|
||||
.write()
|
||||
.await
|
||||
.apply_group_setting_changeset(changeset)
|
||||
.mut_group_controller(|group_controller, _| {
|
||||
group_controller.apply_group_setting_changeset(changeset)
|
||||
})
|
||||
.await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn v_get_all_sorts(&self) -> Vec<Sort> {
|
||||
@ -631,10 +644,11 @@ impl DatabaseViewEditor {
|
||||
.await;
|
||||
|
||||
self
|
||||
.group_controller
|
||||
.write()
|
||||
.await
|
||||
.did_update_field_type_option(&field);
|
||||
.mut_group_controller(|group_controller, _| {
|
||||
group_controller.did_update_field_type_option(&field);
|
||||
Ok(())
|
||||
})
|
||||
.await;
|
||||
|
||||
if let Some(filter) = self
|
||||
.delegate
|
||||
@ -672,7 +686,7 @@ impl DatabaseViewEditor {
|
||||
.map(|group| GroupPB::from(group.clone()))
|
||||
.collect();
|
||||
|
||||
*self.group_controller.write().await = new_group_controller;
|
||||
*self.group_controller.write().await = Some(new_group_controller);
|
||||
let changeset = GroupChangesPB {
|
||||
view_id: self.view_id.clone(),
|
||||
initial_groups: new_groups,
|
||||
@ -804,13 +818,19 @@ impl DatabaseViewEditor {
|
||||
where
|
||||
F: FnOnce(&mut Box<dyn GroupController>, Arc<Field>) -> FlowyResult<T>,
|
||||
{
|
||||
let group_field_id = self.group_controller.read().await.field_id().to_owned();
|
||||
match self.delegate.get_field(&group_field_id).await {
|
||||
None => None,
|
||||
Some(field) => {
|
||||
let mut write_guard = self.group_controller.write().await;
|
||||
f(&mut write_guard, field).ok()
|
||||
},
|
||||
let group_field_id = self
|
||||
.group_controller
|
||||
.read()
|
||||
.await
|
||||
.as_ref()
|
||||
.map(|group| group.field_id().to_owned())?;
|
||||
let field = self.delegate.get_field(&group_field_id).await?;
|
||||
|
||||
let mut write_guard = self.group_controller.write().await;
|
||||
if let Some(group_controller) = &mut *write_guard {
|
||||
f(group_controller, field).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::rows::RowId;
|
||||
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_error::FlowyResult;
|
||||
use lib_infra::future::{to_fut, Fut};
|
||||
|
||||
use crate::entities::FieldType;
|
||||
@ -35,13 +35,9 @@ pub async fn new_group_controller_with_field(
|
||||
pub async fn new_group_controller(
|
||||
view_id: String,
|
||||
delegate: Arc<dyn DatabaseViewData>,
|
||||
) -> FlowyResult<Box<dyn GroupController>> {
|
||||
let setting_reader = GroupSettingReaderImpl(delegate.clone());
|
||||
let setting_writer = GroupSettingWriterImpl(delegate.clone());
|
||||
|
||||
) -> FlowyResult<Option<Box<dyn GroupController>>> {
|
||||
let fields = delegate.get_fields(&view_id, None).await;
|
||||
let rows = delegate.get_rows(&view_id).await;
|
||||
let layout = delegate.get_layout_for_view(&view_id);
|
||||
let setting_reader = GroupSettingReaderImpl(delegate.clone());
|
||||
|
||||
// Read the grouping field or find a new grouping field
|
||||
let mut grouping_field = setting_reader
|
||||
@ -54,22 +50,29 @@ pub async fn new_group_controller(
|
||||
.cloned()
|
||||
});
|
||||
|
||||
if grouping_field.is_none() {
|
||||
grouping_field = find_new_grouping_field(&fields, &layout);
|
||||
let layout = delegate.get_layout_for_view(&view_id);
|
||||
// If the view is a board and the grouping field is empty, we need to find a new grouping field
|
||||
if layout.is_board() {
|
||||
if grouping_field.is_none() {
|
||||
grouping_field = find_new_grouping_field(&fields, &layout);
|
||||
}
|
||||
}
|
||||
|
||||
match grouping_field {
|
||||
None => Err(FlowyError::internal().context("No grouping field found".to_owned())),
|
||||
Some(_) => {
|
||||
if let Some(grouping_field) = grouping_field {
|
||||
let rows = delegate.get_rows(&view_id).await;
|
||||
let setting_writer = GroupSettingWriterImpl(delegate.clone());
|
||||
Ok(Some(
|
||||
make_group_controller(
|
||||
view_id,
|
||||
grouping_field.unwrap(),
|
||||
grouping_field,
|
||||
rows,
|
||||
setting_reader,
|
||||
setting_writer,
|
||||
)
|
||||
.await
|
||||
},
|
||||
.await?,
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ impl DatabaseViews {
|
||||
let view_editor = self.get_view_editor(view_id).await?;
|
||||
// If the id of the grouping field is equal to the updated field's id, then we need to
|
||||
// update the group setting
|
||||
if view_editor.group_id().await == field_id {
|
||||
if view_editor.is_grouping_field(field_id).await {
|
||||
view_editor.v_update_grouping_field(field_id).await?;
|
||||
}
|
||||
view_editor
|
||||
|
@ -108,7 +108,7 @@ fn update_cell_data_with_changeset(
|
||||
.into_iter()
|
||||
.for_each(|option_name| {
|
||||
let option = SelectOption::new(&option_name);
|
||||
cell_data.options.push(option.clone());
|
||||
cell_data.options.push(option);
|
||||
});
|
||||
|
||||
// Update options
|
||||
|
@ -134,7 +134,7 @@ async fn update_updated_at_field_on_other_cell_update() {
|
||||
.editor
|
||||
.get_cells_for_field(&test.view_id, &updated_at_field.id)
|
||||
.await;
|
||||
assert!(cells.len() > 0);
|
||||
assert!(!cells.is_empty());
|
||||
for (i, cell) in cells.into_iter().enumerate() {
|
||||
let timestamp = DateCellData::from(cell.as_ref()).timestamp.unwrap();
|
||||
println!(
|
||||
|
@ -11,7 +11,7 @@ async fn export_meta_csv_test() {
|
||||
let database = test.editor.clone();
|
||||
let s = database.export_csv(CSVFormat::META).await.unwrap();
|
||||
let mut reader = csv::Reader::from_reader(s.as_bytes());
|
||||
for header in reader.headers() {
|
||||
for header in reader.headers().unwrap() {
|
||||
dbg!(header);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user