fix: migrate cloud document when the document content is not sync to … (#4108)

* fix: migrate cloud document when the document content is not sync to local

* chore: clippy
This commit is contained in:
Nathan.fooo 2023-12-06 09:14:02 -08:00 committed by GitHub
parent 4837d7f7fe
commit d86431dfbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 118 additions and 83 deletions

View File

@ -131,7 +131,7 @@ async fn hide_group_event_test() {
let groups = test.get_groups(&board_view.id).await; let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups.len(), 4); assert_eq!(groups.len(), 4);
assert_eq!(groups[0].is_visible, false); assert!(!groups[0].is_visible);
} }
#[tokio::test] #[tokio::test]

View File

@ -4,7 +4,7 @@ use flowy_core::DEFAULT_NAME;
use crate::util::unzip_history_user_db; use crate::util::unzip_history_user_db;
#[tokio::test] #[tokio::test]
async fn migrate_historical_empty_document_test() { async fn collab_db_restore_test() {
let (cleaner, user_db_path) = unzip_history_user_db( let (cleaner, user_db_path) = unzip_history_user_db(
"./tests/user/migration_test/history_user_db", "./tests/user/migration_test/history_user_db",
"038_collab_db_corrupt_restore", "038_collab_db_corrupt_restore",

View File

@ -220,7 +220,7 @@ impl TryInto<CreateFieldParams> for CreateFieldPayloadPB {
CreateFieldPosition::Before => { CreateFieldPosition::Before => {
let field_id = self let field_id = self
.target_field_id .target_field_id
.ok_or_else(|| ErrorCode::InvalidParams)?; .ok_or(ErrorCode::InvalidParams)?;
let field_id = NotEmptyStr::parse(field_id) let field_id = NotEmptyStr::parse(field_id)
.map_err(|_| ErrorCode::InvalidParams)? .map_err(|_| ErrorCode::InvalidParams)?
.0; .0;
@ -229,7 +229,7 @@ impl TryInto<CreateFieldParams> for CreateFieldPayloadPB {
CreateFieldPosition::After => { CreateFieldPosition::After => {
let field_id = self let field_id = self
.target_field_id .target_field_id
.ok_or_else(|| ErrorCode::InvalidParams)?; .ok_or(ErrorCode::InvalidParams)?;
let field_id = NotEmptyStr::parse(field_id) let field_id = NotEmptyStr::parse(field_id)
.map_err(|_| ErrorCode::InvalidParams)? .map_err(|_| ErrorCode::InvalidParams)?
.0; .0;
@ -564,7 +564,7 @@ pub enum FieldType {
impl Display for FieldType { impl Display for FieldType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let value: i64 = self.clone().into(); let value: i64 = (*self).into();
f.write_fmt(format_args!("{}", value)) f.write_fmt(format_args!("{}", value))
} }
} }
@ -577,13 +577,13 @@ impl AsRef<FieldType> for FieldType {
impl From<&FieldType> for FieldType { impl From<&FieldType> for FieldType {
fn from(field_type: &FieldType) -> Self { fn from(field_type: &FieldType) -> Self {
field_type.clone() *field_type
} }
} }
impl FieldType { impl FieldType {
pub fn value(&self) -> i64 { pub fn value(&self) -> i64 {
self.clone().into() (*self).into()
} }
pub fn default_name(&self) -> String { pub fn default_name(&self) -> String {
@ -666,7 +666,7 @@ impl From<FieldType> for i64 {
impl From<&FieldType> for i64 { impl From<&FieldType> for i64 {
fn from(ty: &FieldType) -> Self { fn from(ty: &FieldType) -> Self {
i64::from(ty.clone()) i64::from(*ty)
} }
} }

View File

@ -47,7 +47,7 @@ impl std::convert::From<&Filter> for FilterPB {
Self { Self {
id: filter.id.clone(), id: filter.id.clone(),
field_id: filter.field_id.clone(), field_id: filter.field_id.clone(),
field_type: filter.field_type.clone(), field_type: filter.field_type,
data: bytes.to_vec(), data: bytes.to_vec(),
} }
} }

View File

@ -25,7 +25,7 @@ impl std::convert::From<&Sort> for SortPB {
Self { Self {
id: sort.id.clone(), id: sort.id.clone(),
field_id: sort.field_id.clone(), field_id: sort.field_id.clone(),
field_type: sort.field_type.clone(), field_type: sort.field_type,
condition: sort.condition.into(), condition: sort.condition.into(),
} }
} }

View File

@ -25,7 +25,7 @@ impl TypeCellData {
pub fn from_field_type(field_type: &FieldType) -> TypeCellData { pub fn from_field_type(field_type: &FieldType) -> TypeCellData {
Self { Self {
cell_str: "".to_string(), cell_str: "".to_string(),
field_type: field_type.clone(), field_type: *field_type,
} }
} }

View File

@ -346,7 +346,7 @@ impl DatabaseEditor {
} }
let old_field_type = FieldType::from(field.field_type); let old_field_type = FieldType::from(field.field_type);
let old_type_option = field.get_any_type_option(old_field_type.clone()); let old_type_option = field.get_any_type_option(old_field_type);
let new_type_option = field let new_type_option = field
.get_any_type_option(new_field_type) .get_any_type_option(new_field_type)
.unwrap_or_else(|| default_type_option_data_from_type(new_field_type)); .unwrap_or_else(|| default_type_option_data_from_type(new_field_type));
@ -464,7 +464,7 @@ impl DatabaseEditor {
field.map(|field| { field.map(|field| {
let field_type = FieldType::from(field.field_type); let field_type = FieldType::from(field.field_type);
let type_option = field let type_option = field
.get_any_type_option(field_type.clone()) .get_any_type_option(field_type)
.unwrap_or_else(|| default_type_option_data_from_type(&field_type)); .unwrap_or_else(|| default_type_option_data_from_type(&field_type));
(field, type_option_to_pb(type_option, &field_type)) (field, type_option_to_pb(type_option, &field_type))
}) })
@ -1265,7 +1265,7 @@ impl DatabaseViewOperation for DatabaseViewOperationImpl {
let (_, field) = self.database.lock().create_field_with_mut( let (_, field) = self.database.lock().create_field_with_mut(
view_id, view_id,
name.to_string(), name.to_string(),
field_type.clone().into(), field_type.into(),
&OrderObjectPosition::default(), &OrderObjectPosition::default(),
|field| { |field| {
field field

View File

@ -139,7 +139,7 @@ impl DatabaseLayoutDepsResolver {
Field::new( Field::new(
field_id, field_id,
"Date".to_string(), "Date".to_string(),
field_type.clone().into(), field_type.into(),
false, false,
) )
.with_type_option_data(field_type, default_date_type_option.into()) .with_type_option_data(field_type, default_date_type_option.into())
@ -152,7 +152,7 @@ impl DatabaseLayoutDepsResolver {
Field::new( Field::new(
field_id, field_id,
"Status".to_string(), "Status".to_string(),
field_type.clone().into(), field_type.into(),
false, false,
) )
.with_type_option_data(field_type, default_select_type_option.into()) .with_type_option_data(field_type, default_select_type_option.into())

View File

@ -118,7 +118,7 @@ pub(crate) async fn get_cell_for_row(
Some(RowSingleCellData { Some(RowSingleCellData {
row_id: row_cell.row_id.clone(), row_id: row_cell.row_id.clone(),
field_id: field.id.clone(), field_id: field.id.clone(),
field_type: field_type.clone(), field_type,
cell_data, cell_data,
}) })
} }
@ -143,7 +143,7 @@ pub(crate) async fn get_cells_for_field(
RowSingleCellData { RowSingleCellData {
row_id: row_cell.row_id.clone(), row_id: row_cell.row_id.clone(),
field_id: field.id.clone(), field_id: field.id.clone(),
field_type: field_type.clone(), field_type,
cell_data, cell_data,
} }
}) })

View File

@ -12,7 +12,7 @@ impl FieldBuilder {
let mut field = Field::new( let mut field = Field::new(
gen_field_id(), gen_field_id(),
"".to_string(), "".to_string(),
field_type.clone().into(), field_type.into(),
false, false,
); );
field.width = 150; field.width = 150;

View File

@ -12,7 +12,7 @@ mod tests {
fn checkout_box_description_test() { fn checkout_box_description_test() {
let type_option = CheckboxTypeOption::default(); let type_option = CheckboxTypeOption::default();
let field_type = FieldType::Checkbox; let field_type = FieldType::Checkbox;
let field_rev = FieldBuilder::from_field_type(field_type.clone()).build(); let field_rev = FieldBuilder::from_field_type(field_type).build();
// the checkout value will be checked if the value is "1", "true" or "yes" // the checkout value will be checked if the value is "1", "true" or "yes"
assert_checkbox(&type_option, "1", CHECK, &field_type, &field_rev); assert_checkbox(&type_option, "1", CHECK, &field_type, &field_rev);

View File

@ -12,7 +12,7 @@ mod tests {
fn number_type_option_input_test() { fn number_type_option_input_test() {
let type_option = NumberTypeOption::default(); let type_option = NumberTypeOption::default();
let field_type = FieldType::Number; let field_type = FieldType::Number;
let field = FieldBuilder::from_field_type(field_type.clone()).build(); let field = FieldBuilder::from_field_type(field_type).build();
// Input is empty String // Input is empty String
assert_number(&type_option, "", "", &field_type, &field); assert_number(&type_option, "", "", &field_type, &field);
@ -31,7 +31,7 @@ mod tests {
let field_type = FieldType::Number; let field_type = FieldType::Number;
let mut type_option = NumberTypeOption::new(); let mut type_option = NumberTypeOption::new();
type_option.format = NumberFormat::USD; type_option.format = NumberFormat::USD;
let field = FieldBuilder::new(field_type.clone(), type_option.clone()).build(); let field = FieldBuilder::new(field_type, type_option.clone()).build();
assert_number(&type_option, "", "", &field_type, &field); assert_number(&type_option, "", "", &field_type, &field);
assert_number(&type_option, "abc", "", &field_type, &field); assert_number(&type_option, "abc", "", &field_type, &field);
@ -49,7 +49,7 @@ mod tests {
let field_type = FieldType::Number; let field_type = FieldType::Number;
let mut type_option = NumberTypeOption::new(); let mut type_option = NumberTypeOption::new();
type_option.format = NumberFormat::USD; type_option.format = NumberFormat::USD;
let field = FieldBuilder::new(field_type.clone(), type_option.clone()).build(); let field = FieldBuilder::new(field_type, type_option.clone()).build();
assert_number( assert_number(
&type_option, &type_option,
@ -71,7 +71,7 @@ mod tests {
let field_type = FieldType::Number; let field_type = FieldType::Number;
let mut type_option = NumberTypeOption::new(); let mut type_option = NumberTypeOption::new();
type_option.format = NumberFormat::USD; type_option.format = NumberFormat::USD;
let field = FieldBuilder::new(field_type.clone(), type_option.clone()).build(); let field = FieldBuilder::new(field_type, type_option.clone()).build();
assert_number(&type_option, "€0.2", "$0.2", &field_type, &field); assert_number(&type_option, "€0.2", "$0.2", &field_type, &field);
assert_number(&type_option, "-€0.2", "-$0.2", &field_type, &field); assert_number(&type_option, "-€0.2", "-$0.2", &field_type, &field);
@ -85,7 +85,7 @@ mod tests {
let field_type = FieldType::Number; let field_type = FieldType::Number;
let mut type_option = NumberTypeOption::new(); let mut type_option = NumberTypeOption::new();
type_option.format = NumberFormat::EUR; type_option.format = NumberFormat::EUR;
let field = FieldBuilder::new(field_type.clone(), type_option.clone()).build(); let field = FieldBuilder::new(field_type, type_option.clone()).build();
assert_number(&type_option, "0.2", "€0,2", &field_type, &field); assert_number(&type_option, "0.2", "€0,2", &field_type, &field);
assert_number(&type_option, "1000", "€1.000", &field_type, &field); assert_number(&type_option, "1000", "€1.000", &field_type, &field);

View File

@ -12,7 +12,7 @@ mod tests {
#[test] #[test]
fn date_type_to_text_type() { fn date_type_to_text_type() {
let field_type = FieldType::DateTime; let field_type = FieldType::DateTime;
let field = FieldBuilder::new(field_type.clone(), DateTypeOption::test()).build(); let field = FieldBuilder::new(field_type, DateTypeOption::test()).build();
assert_eq!( assert_eq!(
stringify_cell_data( stringify_cell_data(
@ -77,7 +77,7 @@ mod tests {
options: vec![done_option.clone()], options: vec![done_option.clone()],
disable_color: false, disable_color: false,
}; };
let field = FieldBuilder::new(field_type.clone(), single_select).build(); let field = FieldBuilder::new(field_type, single_select).build();
assert_eq!( assert_eq!(
stringify_cell_data( stringify_cell_data(
@ -107,7 +107,7 @@ mod tests {
let france_option_id = france.id; let france_option_id = france.id;
let argentina_option_id = argentina.id; let argentina_option_id = argentina.id;
let field_rev = FieldBuilder::new(field_type.clone(), multi_select).build(); let field_rev = FieldBuilder::new(field_type, multi_select).build();
assert_eq!( assert_eq!(
stringify_cell_data( stringify_cell_data(

View File

@ -272,7 +272,7 @@ pub fn default_type_option_data_from_type(field_type: &FieldType) -> TypeOptionD
FieldType::Number => NumberTypeOption::default().into(), FieldType::Number => NumberTypeOption::default().into(),
FieldType::DateTime => DateTypeOption::default().into(), FieldType::DateTime => DateTypeOption::default().into(),
FieldType::LastEditedTime | FieldType::CreatedTime => TimestampTypeOption { FieldType::LastEditedTime | FieldType::CreatedTime => TimestampTypeOption {
field_type: field_type.clone(), field_type: *field_type,
date_format: DateFormat::Friendly, date_format: DateFormat::Friendly,
time_format: TimeFormat::TwelveHour, time_format: TimeFormat::TwelveHour,
include_time: true, include_time: true,

View File

@ -78,7 +78,7 @@ struct CellDataCacheKey(u64);
impl CellDataCacheKey { impl CellDataCacheKey {
pub fn new(field_rev: &Field, decoded_field_type: FieldType, cell: &Cell) -> Self { pub fn new(field_rev: &Field, decoded_field_type: FieldType, cell: &Cell) -> Self {
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
if let Some(type_option_data) = field_rev.get_any_type_option(&decoded_field_type) { if let Some(type_option_data) = field_rev.get_any_type_option(decoded_field_type) {
type_option_data.hash(&mut hasher); type_option_data.hash(&mut hasher);
} }
hasher.write(field_rev.id.as_bytes()); hasher.write(field_rev.id.as_bytes());
@ -141,7 +141,7 @@ where
decoded_field_type: &FieldType, decoded_field_type: &FieldType,
field: &Field, field: &Field,
) -> FlowyResult<<Self as TypeOption>::CellData> { ) -> FlowyResult<<Self as TypeOption>::CellData> {
let key = CellDataCacheKey::new(field, decoded_field_type.clone(), cell); let key = CellDataCacheKey::new(field, *decoded_field_type, cell);
if let Some(cell_data_cache) = self.cell_data_cache.as_ref() { if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
let read_guard = cell_data_cache.read(); let read_guard = cell_data_cache.read();
if let Some(cell_data) = read_guard.get(key.as_ref()).cloned() { if let Some(cell_data) = read_guard.get(key.as_ref()).cloned() {

View File

@ -117,7 +117,7 @@ impl std::convert::From<&Filter> for FilterType {
Self { Self {
filter_id: filter.id.clone(), filter_id: filter.id.clone(),
field_id: filter.field_id.clone(), field_id: filter.field_id.clone(),
field_type: filter.field_type.clone(), field_type: filter.field_type,
} }
} }
} }
@ -127,7 +127,7 @@ impl std::convert::From<&FilterPB> for FilterType {
Self { Self {
filter_id: filter.id.clone(), filter_id: filter.id.clone(),
field_id: filter.field_id.clone(), field_id: filter.field_id.clone(),
field_type: filter.field_type.clone(), field_type: filter.field_type,
} }
} }
} }

View File

@ -147,7 +147,7 @@ fn default_field(field_str: String, is_primary: bool) -> Field {
Field::new( Field::new(
gen_field_id(), gen_field_id(),
field_str, field_str,
field_type.clone().into(), field_type.into(),
is_primary, is_primary,
) )
.with_type_option_data(field_type, type_option_data) .with_type_option_data(field_type, type_option_data)

View File

@ -240,7 +240,7 @@ fn cmp_row(
fields: &[Arc<Field>], fields: &[Arc<Field>],
cell_data_cache: &CellCache, cell_data_cache: &CellCache,
) -> Ordering { ) -> Ordering {
let field_type = sort.field_type.clone(); let field_type = sort.field_type;
match fields match fields
.iter() .iter()
.find(|field_rev| field_rev.id == sort.field_id) .find(|field_rev| field_rev.id == sort.field_id)

View File

@ -109,7 +109,7 @@ impl From<&Sort> for SortType {
Self { Self {
sort_id: data.id.clone(), sort_id: data.id.clone(),
field_id: data.field_id.clone(), field_id: data.field_id.clone(),
field_type: data.field_type.clone(), field_type: data.field_type,
} }
} }
} }

View File

@ -147,7 +147,7 @@ impl DatabaseEditorTest {
pub fn get_multi_select_type_option(&self, field_id: &str) -> Vec<SelectOption> { pub fn get_multi_select_type_option(&self, field_id: &str) -> Vec<SelectOption> {
let field_type = FieldType::MultiSelect; let field_type = FieldType::MultiSelect;
let field = self.get_field(field_id, field_type.clone()); let field = self.get_field(field_id, field_type);
let type_option = field let type_option = field
.get_type_option::<MultiSelectTypeOption>(field_type) .get_type_option::<MultiSelectTypeOption>(field_type)
.unwrap(); .unwrap();
@ -156,7 +156,7 @@ impl DatabaseEditorTest {
pub fn get_single_select_type_option(&self, field_id: &str) -> SingleSelectTypeOption { pub fn get_single_select_type_option(&self, field_id: &str) -> SingleSelectTypeOption {
let field_type = FieldType::SingleSelect; let field_type = FieldType::SingleSelect;
let field = self.get_field(field_id, field_type.clone()); let field = self.get_field(field_id, field_type);
field field
.get_type_option::<SingleSelectTypeOption>(field_type) .get_type_option::<SingleSelectTypeOption>(field_type)
.unwrap() .unwrap()
@ -165,7 +165,7 @@ impl DatabaseEditorTest {
#[allow(dead_code)] #[allow(dead_code)]
pub fn get_checklist_type_option(&self, field_id: &str) -> ChecklistTypeOption { pub fn get_checklist_type_option(&self, field_id: &str) -> ChecklistTypeOption {
let field_type = FieldType::Checklist; let field_type = FieldType::Checklist;
let field = self.get_field(field_id, field_type.clone()); let field = self.get_field(field_id, field_type);
field field
.get_type_option::<ChecklistTypeOption>(field_type) .get_type_option::<ChecklistTypeOption>(field_type)
.unwrap() .unwrap()
@ -174,7 +174,7 @@ impl DatabaseEditorTest {
#[allow(dead_code)] #[allow(dead_code)]
pub fn get_checkbox_type_option(&self, field_id: &str) -> CheckboxTypeOption { pub fn get_checkbox_type_option(&self, field_id: &str) -> CheckboxTypeOption {
let field_type = FieldType::Checkbox; let field_type = FieldType::Checkbox;
let field = self.get_field(field_id, field_type.clone()); let field = self.get_field(field_id, field_type);
field field
.get_type_option::<CheckboxTypeOption>(field_type) .get_type_option::<CheckboxTypeOption>(field_type)
.unwrap() .unwrap()

View File

@ -1,5 +1,6 @@
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::views::OrderObjectPosition; use collab_database::views::OrderObjectPosition;
use flowy_database2::entities::{CreateFieldParams, FieldType}; use flowy_database2::entities::{CreateFieldParams, FieldType};
use flowy_database2::services::field::{ use flowy_database2::services::field::{
type_option_to_pb, DateCellChangeset, DateFormat, DateTypeOption, FieldBuilder, type_option_to_pb, DateCellChangeset, DateFormat, DateTypeOption, FieldBuilder,
@ -9,7 +10,7 @@ use flowy_database2::services::field::{
pub fn create_text_field(grid_id: &str) -> (CreateFieldParams, Field) { pub fn create_text_field(grid_id: &str) -> (CreateFieldParams, Field) {
let field_type = FieldType::RichText; let field_type = FieldType::RichText;
let type_option = RichTextTypeOption::default(); let type_option = RichTextTypeOption::default();
let text_field = FieldBuilder::new(field_type.clone(), type_option.clone()) let text_field = FieldBuilder::new(field_type, type_option.clone())
.name("Name") .name("Name")
.visibility(true) .visibility(true)
.primary(true) .primary(true)
@ -31,7 +32,7 @@ pub fn create_single_select_field(grid_id: &str) -> (CreateFieldParams, Field) {
let mut type_option = SingleSelectTypeOption::default(); let mut type_option = SingleSelectTypeOption::default();
type_option.options.push(SelectOption::new("Done")); type_option.options.push(SelectOption::new("Done"));
type_option.options.push(SelectOption::new("Progress")); type_option.options.push(SelectOption::new("Progress"));
let single_select_field = FieldBuilder::new(field_type.clone(), type_option.clone()) let single_select_field = FieldBuilder::new(field_type, type_option.clone())
.name("Name") .name("Name")
.visibility(true) .visibility(true)
.build(); .build();
@ -76,17 +77,15 @@ pub fn create_timestamp_field(grid_id: &str, field_type: FieldType) -> (CreateFi
date_format: DateFormat::US, date_format: DateFormat::US,
time_format: TimeFormat::TwentyFourHour, time_format: TimeFormat::TwentyFourHour,
include_time: true, include_time: true,
field_type: field_type.clone(), field_type,
}; };
let field: Field = match field_type { let field: Field = match field_type {
FieldType::LastEditedTime => { FieldType::LastEditedTime => FieldBuilder::new(field_type, timestamp_type_option.clone())
FieldBuilder::new(field_type.clone(), timestamp_type_option.clone()) .name("Updated At")
.name("Updated At") .visibility(true)
.visibility(true) .build(),
.build() FieldType::CreatedTime => FieldBuilder::new(field_type, timestamp_type_option.clone())
},
FieldType::CreatedTime => FieldBuilder::new(field_type.clone(), timestamp_type_option.clone())
.name("Created At") .name("Created At")
.visibility(true) .visibility(true)
.build(), .build(),

View File

@ -1,7 +1,5 @@
use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData}; use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData};
use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, LayoutSettings}; use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting, LayoutSettings};
use flowy_database2::services::field_settings::default_field_settings_for_fields;
use flowy_database2::services::setting::BoardLayoutSetting;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use flowy_database2::entities::FieldType; use flowy_database2::entities::FieldType;
@ -10,6 +8,8 @@ use flowy_database2::services::field::{
DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption, SelectOption, SelectOptionColor, DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption, SelectOption, SelectOptionColor,
SingleSelectTypeOption, TimeFormat, TimestampTypeOption, SingleSelectTypeOption, TimeFormat, TimestampTypeOption,
}; };
use flowy_database2::services::field_settings::default_field_settings_for_fields;
use flowy_database2::services::setting::BoardLayoutSetting;
use crate::database::database_editor::TestRowBuilder; use crate::database::database_editor::TestRowBuilder;
use crate::database::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, PLANNED, TWITTER}; use crate::database::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, PLANNED, TWITTER};
@ -22,7 +22,7 @@ pub fn make_test_board() -> DatabaseData {
for field_type in FieldType::iter() { for field_type in FieldType::iter() {
match field_type { match field_type {
FieldType::RichText => { FieldType::RichText => {
let text_field = FieldBuilder::from_field_type(field_type.clone()) let text_field = FieldBuilder::from_field_type(field_type)
.name("Name") .name("Name")
.visibility(true) .visibility(true)
.primary(true) .primary(true)
@ -31,7 +31,7 @@ pub fn make_test_board() -> DatabaseData {
}, },
FieldType::Number => { FieldType::Number => {
// Number // Number
let number_field = FieldBuilder::from_field_type(field_type.clone()) let number_field = FieldBuilder::from_field_type(field_type)
.name("Price") .name("Price")
.visibility(true) .visibility(true)
.build(); .build();
@ -45,7 +45,7 @@ pub fn make_test_board() -> DatabaseData {
timezone_id: "Etc/UTC".to_owned(), timezone_id: "Etc/UTC".to_owned(),
}; };
let name = "Time"; let name = "Time";
let date_field = FieldBuilder::new(field_type.clone(), date_type_option) let date_field = FieldBuilder::new(field_type, date_type_option)
.name(name) .name(name)
.visibility(true) .visibility(true)
.build(); .build();
@ -57,14 +57,14 @@ pub fn make_test_board() -> DatabaseData {
date_format: DateFormat::US, date_format: DateFormat::US,
time_format: TimeFormat::TwentyFourHour, time_format: TimeFormat::TwentyFourHour,
include_time: true, include_time: true,
field_type: field_type.clone(), field_type,
}; };
let name = match field_type { let name = match field_type {
FieldType::LastEditedTime => "Last Modified", FieldType::LastEditedTime => "Last Modified",
FieldType::CreatedTime => "Created At", FieldType::CreatedTime => "Created At",
_ => "", _ => "",
}; };
let date_field = FieldBuilder::new(field_type.clone(), date_type_option) let date_field = FieldBuilder::new(field_type, date_type_option)
.name(name) .name(name)
.visibility(true) .visibility(true)
.build(); .build();
@ -79,7 +79,7 @@ pub fn make_test_board() -> DatabaseData {
single_select_type_option single_select_type_option
.options .options
.extend(vec![option1, option2, option3]); .extend(vec![option1, option2, option3]);
let single_select_field = FieldBuilder::new(field_type.clone(), single_select_type_option) let single_select_field = FieldBuilder::new(field_type, single_select_type_option)
.name("Status") .name("Status")
.visibility(true) .visibility(true)
.build(); .build();
@ -92,7 +92,7 @@ pub fn make_test_board() -> DatabaseData {
let option3 = SelectOption::with_color(TWITTER, SelectOptionColor::Yellow); let option3 = SelectOption::with_color(TWITTER, SelectOptionColor::Yellow);
let mut type_option = MultiSelectTypeOption::default(); let mut type_option = MultiSelectTypeOption::default();
type_option.options.extend(vec![option1, option2, option3]); type_option.options.extend(vec![option1, option2, option3]);
let multi_select_field = FieldBuilder::new(field_type.clone(), type_option) let multi_select_field = FieldBuilder::new(field_type, type_option)
.name("Platform") .name("Platform")
.visibility(true) .visibility(true)
.build(); .build();
@ -100,7 +100,7 @@ pub fn make_test_board() -> DatabaseData {
}, },
FieldType::Checkbox => { FieldType::Checkbox => {
// Checkbox // Checkbox
let checkbox_field = FieldBuilder::from_field_type(field_type.clone()) let checkbox_field = FieldBuilder::from_field_type(field_type)
.name("is urgent") .name("is urgent")
.visibility(true) .visibility(true)
.build(); .build();
@ -108,7 +108,7 @@ pub fn make_test_board() -> DatabaseData {
}, },
FieldType::URL => { FieldType::URL => {
// URL // URL
let url = FieldBuilder::from_field_type(field_type.clone()) let url = FieldBuilder::from_field_type(field_type)
.name("link") .name("link")
.visibility(true) .visibility(true)
.build(); .build();
@ -120,7 +120,7 @@ pub fn make_test_board() -> DatabaseData {
// let option3 = SelectOption::with_color(THIRD_THING, SelectOptionColor::Yellow); // let option3 = SelectOption::with_color(THIRD_THING, SelectOptionColor::Yellow);
let type_option = ChecklistTypeOption::default(); let type_option = ChecklistTypeOption::default();
// type_option.options.extend(vec![option1, option2, option3]); // type_option.options.extend(vec![option1, option2, option3]);
let checklist_field = FieldBuilder::new(field_type.clone(), type_option) let checklist_field = FieldBuilder::new(field_type, type_option)
.name("TODO") .name("TODO")
.visibility(true) .visibility(true)
.build(); .build();

View File

@ -1,6 +1,5 @@
use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData}; use collab_database::database::{gen_database_id, gen_database_view_id, gen_row_id, DatabaseData};
use collab_database::views::{DatabaseLayout, DatabaseView}; use collab_database::views::{DatabaseLayout, DatabaseView};
use flowy_database2::services::field_settings::default_field_settings_for_fields;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use flowy_database2::entities::FieldType; use flowy_database2::entities::FieldType;
@ -9,6 +8,7 @@ use flowy_database2::services::field::{
DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption, NumberFormat, NumberTypeOption, DateFormat, DateTypeOption, FieldBuilder, MultiSelectTypeOption, NumberFormat, NumberTypeOption,
SelectOption, SelectOptionColor, SingleSelectTypeOption, TimeFormat, TimestampTypeOption, SelectOption, SelectOptionColor, SingleSelectTypeOption, TimeFormat, TimestampTypeOption,
}; };
use flowy_database2::services::field_settings::default_field_settings_for_fields;
use crate::database::database_editor::TestRowBuilder; use crate::database::database_editor::TestRowBuilder;
use crate::database::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, PLANNED, TWITTER}; use crate::database::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, PLANNED, TWITTER};
@ -21,7 +21,7 @@ pub fn make_test_grid() -> DatabaseData {
for field_type in FieldType::iter() { for field_type in FieldType::iter() {
match field_type { match field_type {
FieldType::RichText => { FieldType::RichText => {
let text_field = FieldBuilder::from_field_type(field_type.clone()) let text_field = FieldBuilder::from_field_type(field_type)
.name("Name") .name("Name")
.visibility(true) .visibility(true)
.primary(true) .primary(true)
@ -33,7 +33,7 @@ pub fn make_test_grid() -> DatabaseData {
let mut type_option = NumberTypeOption::default(); let mut type_option = NumberTypeOption::default();
type_option.set_format(NumberFormat::USD); type_option.set_format(NumberFormat::USD);
let number_field = FieldBuilder::new(field_type.clone(), type_option) let number_field = FieldBuilder::new(field_type, type_option)
.name("Price") .name("Price")
.visibility(true) .visibility(true)
.build(); .build();
@ -47,7 +47,7 @@ pub fn make_test_grid() -> DatabaseData {
timezone_id: "Etc/UTC".to_owned(), timezone_id: "Etc/UTC".to_owned(),
}; };
let name = "Time"; let name = "Time";
let date_field = FieldBuilder::new(field_type.clone(), date_type_option) let date_field = FieldBuilder::new(field_type, date_type_option)
.name(name) .name(name)
.visibility(true) .visibility(true)
.build(); .build();
@ -59,14 +59,14 @@ pub fn make_test_grid() -> DatabaseData {
date_format: DateFormat::US, date_format: DateFormat::US,
time_format: TimeFormat::TwentyFourHour, time_format: TimeFormat::TwentyFourHour,
include_time: true, include_time: true,
field_type: field_type.clone(), field_type,
}; };
let name = match field_type { let name = match field_type {
FieldType::LastEditedTime => "Last Modified", FieldType::LastEditedTime => "Last Modified",
FieldType::CreatedTime => "Created At", FieldType::CreatedTime => "Created At",
_ => "", _ => "",
}; };
let timestamp_field = FieldBuilder::new(field_type.clone(), timestamp_type_option) let timestamp_field = FieldBuilder::new(field_type, timestamp_type_option)
.name(name) .name(name)
.visibility(true) .visibility(true)
.build(); .build();
@ -81,7 +81,7 @@ pub fn make_test_grid() -> DatabaseData {
single_select_type_option single_select_type_option
.options .options
.extend(vec![option1, option2, option3]); .extend(vec![option1, option2, option3]);
let single_select_field = FieldBuilder::new(field_type.clone(), single_select_type_option) let single_select_field = FieldBuilder::new(field_type, single_select_type_option)
.name("Status") .name("Status")
.visibility(true) .visibility(true)
.build(); .build();
@ -94,7 +94,7 @@ pub fn make_test_grid() -> DatabaseData {
let option3 = SelectOption::with_color(TWITTER, SelectOptionColor::Yellow); let option3 = SelectOption::with_color(TWITTER, SelectOptionColor::Yellow);
let mut type_option = MultiSelectTypeOption::default(); let mut type_option = MultiSelectTypeOption::default();
type_option.options.extend(vec![option1, option2, option3]); type_option.options.extend(vec![option1, option2, option3]);
let multi_select_field = FieldBuilder::new(field_type.clone(), type_option) let multi_select_field = FieldBuilder::new(field_type, type_option)
.name("Platform") .name("Platform")
.visibility(true) .visibility(true)
.build(); .build();
@ -102,7 +102,7 @@ pub fn make_test_grid() -> DatabaseData {
}, },
FieldType::Checkbox => { FieldType::Checkbox => {
// Checkbox // Checkbox
let checkbox_field = FieldBuilder::from_field_type(field_type.clone()) let checkbox_field = FieldBuilder::from_field_type(field_type)
.name("is urgent") .name("is urgent")
.visibility(true) .visibility(true)
.build(); .build();
@ -110,7 +110,7 @@ pub fn make_test_grid() -> DatabaseData {
}, },
FieldType::URL => { FieldType::URL => {
// URL // URL
let url = FieldBuilder::from_field_type(field_type.clone()) let url = FieldBuilder::from_field_type(field_type)
.name("link") .name("link")
.visibility(true) .visibility(true)
.build(); .build();
@ -122,7 +122,7 @@ pub fn make_test_grid() -> DatabaseData {
// let option3 = SelectOption::with_color(THIRD_THING, SelectOptionColor::Yellow); // let option3 = SelectOption::with_color(THIRD_THING, SelectOptionColor::Yellow);
let type_option = ChecklistTypeOption::default(); let type_option = ChecklistTypeOption::default();
// type_option.options.extend(vec![option1, option2, option3]); // type_option.options.extend(vec![option1, option2, option3]);
let checklist_field = FieldBuilder::new(field_type.clone(), type_option) let checklist_field = FieldBuilder::new(field_type, type_option)
.name("TODO") .name("TODO")
.visibility(true) .visibility(true)
.build(); .build();
@ -274,7 +274,7 @@ pub fn make_no_date_test_grid() -> DatabaseData {
for field_type in FieldType::iter() { for field_type in FieldType::iter() {
match field_type { match field_type {
FieldType::RichText => { FieldType::RichText => {
let text_field = FieldBuilder::from_field_type(field_type.clone()) let text_field = FieldBuilder::from_field_type(field_type)
.name("Name") .name("Name")
.visibility(true) .visibility(true)
.primary(true) .primary(true)
@ -286,7 +286,7 @@ pub fn make_no_date_test_grid() -> DatabaseData {
let mut type_option = NumberTypeOption::default(); let mut type_option = NumberTypeOption::default();
type_option.set_format(NumberFormat::USD); type_option.set_format(NumberFormat::USD);
let number_field = FieldBuilder::new(field_type.clone(), type_option) let number_field = FieldBuilder::new(field_type, type_option)
.name("Price") .name("Price")
.visibility(true) .visibility(true)
.build(); .build();

View File

@ -234,7 +234,8 @@ impl UserManager {
Box::new(HistoricalEmptyDocumentMigration), Box::new(HistoricalEmptyDocumentMigration),
Box::new(FavoriteV1AndWorkspaceArrayMigration), Box::new(FavoriteV1AndWorkspaceArrayMigration),
]; ];
match UserLocalDataMigration::new(session.clone(), collab_db, sqlite_pool).run(migrations) match UserLocalDataMigration::new(session.clone(), collab_db, sqlite_pool)
.run(migrations, &current_authenticator)
{ {
Ok(applied_migrations) => { Ok(applied_migrations) => {
if !applied_migrations.is_empty() { if !applied_migrations.is_empty() {

View File

@ -9,6 +9,7 @@ use tracing::{event, instrument};
use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction}; use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
use flowy_error::{internal_error, FlowyError, FlowyResult}; use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_user_deps::entities::Authenticator;
use crate::migrations::migration::UserDataMigration; use crate::migrations::migration::UserDataMigration;
use crate::migrations::util::load_collab; use crate::migrations::util::load_collab;
@ -23,7 +24,18 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
} }
#[instrument(name = "HistoricalEmptyDocumentMigration", skip_all, err)] #[instrument(name = "HistoricalEmptyDocumentMigration", skip_all, err)]
fn run(&self, session: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()> { fn run(
&self,
session: &Session,
collab_db: &Arc<RocksCollabDB>,
authenticator: &Authenticator,
) -> FlowyResult<()> {
// - The `empty document` struct has already undergone refactoring prior to the launch of the AppFlowy cloud version.
// - Consequently, if a user is utilizing the AppFlowy cloud version, there is no need to perform any migration for the `empty document` struct.
// - This migration step is only necessary for users who are transitioning from a local version of AppFlowy to the cloud version.
if !matches!(authenticator, Authenticator::Local) {
return Ok(());
}
let write_txn = collab_db.write_txn(); let write_txn = collab_db.write_txn();
let origin = CollabOrigin::Client(CollabClient::new(session.user_id, "phantom")); let origin = CollabOrigin::Client(CollabClient::new(session.user_id, "phantom"));
let folder_collab = match load_collab(session.user_id, &write_txn, &session.user_workspace.id) { let folder_collab = match load_collab(session.user_id, &write_txn, &session.user_workspace.id) {
@ -37,7 +49,7 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
// For historical reasons, the first level documents are empty. So migrate them by inserting // For historical reasons, the first level documents are empty. So migrate them by inserting
// the default document data. // the default document data.
for view in migration_views { for view in migration_views {
if let Err(_) = migrate_empty_document(&write_txn, &origin, &view, session.user_id) { if migrate_empty_document(&write_txn, &origin, &view, session.user_id).is_err() {
event!( event!(
tracing::Level::ERROR, tracing::Level::ERROR,
"Failed to migrate document {}", "Failed to migrate document {}",
@ -46,7 +58,6 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
} }
} }
event!(tracing::Level::INFO, "Save all migrated documents");
write_txn.commit_transaction().map_err(internal_error)?; write_txn.commit_transaction().map_err(internal_error)?;
Ok(()) Ok(())
} }
@ -62,6 +73,7 @@ where
W: YrsDocAction<'a>, W: YrsDocAction<'a>,
PersistenceError: From<W::Error>, PersistenceError: From<W::Error>,
{ {
// If the document is not exist, we don't need to migrate it.
if load_collab(user_id, write_txn, &view.id).is_err() { if load_collab(user_id, write_txn, &view.id).is_err() {
let collab = Arc::new(MutexCollab::new(origin.clone(), &view.id, vec![])); let collab = Arc::new(MutexCollab::new(origin.clone(), &view.id, vec![]));
let document = Document::create_with_data(collab, default_document_data())?; let document = Document::create_with_data(collab, default_document_data())?;

View File

@ -7,6 +7,7 @@ use collab_integrate::RocksCollabDB;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use flowy_sqlite::schema::user_data_migration_records; use flowy_sqlite::schema::user_data_migration_records;
use flowy_sqlite::ConnectionPool; use flowy_sqlite::ConnectionPool;
use flowy_user_deps::entities::Authenticator;
use crate::services::entities::Session; use crate::services::entities::Session;
@ -42,7 +43,11 @@ impl UserLocalDataMigration {
/// ///
/// * `migrations` - A vector of boxed dynamic `UserDataMigration` objects representing the migrations to be applied. /// * `migrations` - A vector of boxed dynamic `UserDataMigration` objects representing the migrations to be applied.
/// ///
pub fn run(self, migrations: Vec<Box<dyn UserDataMigration>>) -> FlowyResult<Vec<String>> { pub fn run(
self,
migrations: Vec<Box<dyn UserDataMigration>>,
authenticator: &Authenticator,
) -> FlowyResult<Vec<String>> {
let mut applied_migrations = vec![]; let mut applied_migrations = vec![];
let conn = self.sqlite_pool.get()?; let conn = self.sqlite_pool.get()?;
let record = get_all_records(&conn)?; let record = get_all_records(&conn)?;
@ -54,7 +59,7 @@ impl UserLocalDataMigration {
{ {
let migration_name = migration.name().to_string(); let migration_name = migration.name().to_string();
if !duplicated_names.contains(&migration_name) { if !duplicated_names.contains(&migration_name) {
migration.run(&self.session, &self.collab_db)?; migration.run(&self.session, &self.collab_db, authenticator)?;
applied_migrations.push(migration.name().to_string()); applied_migrations.push(migration.name().to_string());
save_record(&conn, &migration_name); save_record(&conn, &migration_name);
duplicated_names.push(migration_name); duplicated_names.push(migration_name);
@ -70,7 +75,12 @@ impl UserLocalDataMigration {
pub trait UserDataMigration { pub trait UserDataMigration {
/// Migration with the same name will be skipped /// Migration with the same name will be skipped
fn name(&self) -> &str; fn name(&self) -> &str;
fn run(&self, user: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()>; fn run(
&self,
user: &Session,
collab_db: &Arc<RocksCollabDB>,
authenticator: &Authenticator,
) -> FlowyResult<()>;
} }
fn save_record(conn: &SqliteConnection, migration_name: &str) { fn save_record(conn: &SqliteConnection, migration_name: &str) {

View File

@ -5,6 +5,7 @@ use tracing::instrument;
use collab_integrate::{RocksCollabDB, YrsDocAction}; use collab_integrate::{RocksCollabDB, YrsDocAction};
use flowy_error::{internal_error, FlowyResult}; use flowy_error::{internal_error, FlowyResult};
use flowy_user_deps::entities::Authenticator;
use crate::migrations::migration::UserDataMigration; use crate::migrations::migration::UserDataMigration;
use crate::migrations::util::load_collab; use crate::migrations::util::load_collab;
@ -21,7 +22,19 @@ impl UserDataMigration for FavoriteV1AndWorkspaceArrayMigration {
} }
#[instrument(name = "FavoriteV1AndWorkspaceArrayMigration", skip_all, err)] #[instrument(name = "FavoriteV1AndWorkspaceArrayMigration", skip_all, err)]
fn run(&self, session: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()> { fn run(
&self,
session: &Session,
collab_db: &Arc<RocksCollabDB>,
authenticator: &Authenticator,
) -> FlowyResult<()> {
// Note on `favorite` Struct Refactoring and Migration:
// - The `favorite` struct has already undergone refactoring prior to the launch of the AppFlowy cloud version.
// - Consequently, if a user is utilizing the AppFlowy cloud version, there is no need to perform any migration for the `favorite` struct.
// - This migration step is only necessary for users who are transitioning from a local version of AppFlowy to the cloud version.
if !matches!(authenticator, Authenticator::Local) {
return Ok(());
}
let write_txn = collab_db.write_txn(); let write_txn = collab_db.write_txn();
if let Ok(collab) = load_collab(session.user_id, &write_txn, &session.user_workspace.id) { if let Ok(collab) = load_collab(session.user_id, &write_txn, &session.user_workspace.id) {
let folder = Folder::open(session.user_id, collab, None)?; let folder = Folder::open(session.user_id, collab, None)?;