From 069519589e45bea8704801bf1155993d6a50ac0c Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Thu, 2 Feb 2023 23:02:49 +0800 Subject: [PATCH] Fix/grid group (#1787) * ci: config rust log * chore: rename flowy-sdk to appflowy-core * fix: create group after editing the url * fix: start listen on new group * chore: add tests * refactor: mock data * ci: update command --- .github/workflows/rust_ci.yaml | 2 +- frontend/.vscode/launch.json | 2 +- frontend/.vscode/tasks.json | 2 +- frontend/Makefile.toml | 2 +- .../plugins/board/application/board_bloc.dart | 74 ++- .../application/board_data_controller.dart | 6 +- .../board/application/board_listener.dart | 2 +- .../cell/cell_service/cell_controller.dart | 2 +- .../cell/url_cell_editor_bloc.dart | 13 +- .../widgets/cell/url_cell/cell_editor.dart | 29 +- .../widgets/cell/url_cell/url_cell.dart | 2 + frontend/appflowy_tauri/src-tauri/src/init.rs | 8 +- .../appflowy_tauri/src-tauri/src/request.rs | 4 +- frontend/rust-lib/dart-ffi/src/lib.rs | 10 +- frontend/rust-lib/flowy-core/src/lib.rs | 38 +- .../group_entities/group_changeset.rs | 4 +- .../src/services/cell/cell_operation.rs | 46 +- .../field/type_options/type_option_cell.rs | 2 +- .../url_type_option_entities.rs | 9 + .../src/services/grid_editor.rs | 18 +- .../src/services/group/action.rs | 61 +- .../src/services/group/configuration.rs | 75 ++- .../src/services/group/controller.rs | 119 ++-- .../controller_impls/checkbox_controller.rs | 18 +- .../controller_impls/default_controller.rs | 29 +- .../multi_select_controller.rs | 14 +- .../single_select_controller.rs | 14 +- .../group/controller_impls/url_controller.rs | 92 ++- .../src/services/group/entities.rs | 2 +- .../src/services/view_editor/editor.rs | 61 +- .../services/view_editor/editor_manager.rs | 10 +- .../tests/grid/block_test/block_test.rs | 6 +- .../tests/grid/block_test/row_test.rs | 20 +- .../tests/grid/block_test/script.rs | 18 +- .../tests/grid/cell_test/script.rs | 16 +- .../tests/grid/cell_test/test.rs | 8 +- .../tests/grid/database_editor.rs | 193 ++++++ .../tests/grid/field_test/script.rs | 16 +- .../tests/grid/field_test/test.rs | 26 +- .../grid/filter_test/checkbox_filter_test.rs | 6 +- .../grid/filter_test/checklist_filter_test.rs | 6 +- .../grid/filter_test/date_filter_test.rs | 12 +- .../grid/filter_test/number_filter_test.rs | 14 +- .../tests/grid/filter_test/script.rs | 16 +- .../filter_test/select_option_filter_test.rs | 16 +- .../grid/filter_test/text_filter_test.rs | 22 +- .../flowy-database/tests/grid/grid_editor.rs | 573 ------------------ .../flowy-database/tests/grid/grid_test.rs | 381 ------------ .../tests/grid/group_test/mod.rs | 1 + .../tests/grid/group_test/script.rs | 50 +- .../tests/grid/group_test/test.rs | 97 +-- .../tests/grid/group_test/url_group_test.rs | 148 +++++ .../tests/grid/mock_data/board_mock_data.rs | 191 ++++++ .../grid/mock_data/calendar_mock_data.rs | 6 + .../tests/grid/mock_data/grid_mock_data.rs | 193 ++++++ .../tests/grid/mock_data/mod.rs | 19 + .../rust-lib/flowy-database/tests/grid/mod.rs | 4 +- .../flowy-database/tests/grid/script.rs | 374 ------------ .../tests/grid/snapshot_test/script.rs | 16 +- .../tests/grid/snapshot_test/test.rs | 6 +- .../grid/sort_test/checkbox_and_text_test.rs | 4 +- .../tests/grid/sort_test/multi_sort_test.rs | 4 +- .../tests/grid/sort_test/script.rs | 16 +- .../tests/grid/sort_test/single_sort_test.rs | 24 +- .../rust-lib/flowy-error/src/ext/dispatch.rs | 2 - frontend/rust-lib/flowy-test/src/lib.rs | 10 +- frontend/scripts/build_sdk.cmd | 2 +- frontend/scripts/build_sdk.sh | 6 +- frontend/scripts/makefile/desktop.toml | 30 +- frontend/scripts/makefile/flutter.toml | 89 ++- frontend/scripts/makefile/tauri.toml | 1 + frontend/scripts/makefile/tests.toml | 55 +- shared-lib/lib-infra/Cargo.toml | 2 +- 73 files changed, 1569 insertions(+), 1900 deletions(-) create mode 100644 frontend/rust-lib/flowy-database/tests/grid/database_editor.rs delete mode 100644 frontend/rust-lib/flowy-database/tests/grid/grid_editor.rs delete mode 100644 frontend/rust-lib/flowy-database/tests/grid/grid_test.rs create mode 100644 frontend/rust-lib/flowy-database/tests/grid/group_test/url_group_test.rs create mode 100644 frontend/rust-lib/flowy-database/tests/grid/mock_data/board_mock_data.rs create mode 100644 frontend/rust-lib/flowy-database/tests/grid/mock_data/calendar_mock_data.rs create mode 100644 frontend/rust-lib/flowy-database/tests/grid/mock_data/grid_mock_data.rs create mode 100644 frontend/rust-lib/flowy-database/tests/grid/mock_data/mod.rs delete mode 100644 frontend/rust-lib/flowy-database/tests/grid/script.rs diff --git a/.github/workflows/rust_ci.yaml b/.github/workflows/rust_ci.yaml index dc230301a4..0991dd0dcb 100644 --- a/.github/workflows/rust_ci.yaml +++ b/.github/workflows/rust_ci.yaml @@ -59,7 +59,7 @@ jobs: - name: Build FlowySDK working-directory: frontend run: | - cargo make --profile development-linux-x86_64 appflowy-sdk-dev + cargo make --profile development-linux-x86_64 appflowy-core-dev - name: rustfmt rust-lib run: cargo fmt --all -- --check diff --git a/frontend/.vscode/launch.json b/frontend/.vscode/launch.json index 1c90f14245..3e85432aa1 100644 --- a/frontend/.vscode/launch.json +++ b/frontend/.vscode/launch.json @@ -23,7 +23,7 @@ "type": "dart", "preLaunchTask": "AF: build_flowy_sdk", "env": { - "RUST_LOG": "info" + "RUST_LOG": "debug" }, "cwd": "${workspaceRoot}/app_flowy" }, diff --git a/frontend/.vscode/tasks.json b/frontend/.vscode/tasks.json index b4bc5d7e73..0aeb819865 100644 --- a/frontend/.vscode/tasks.json +++ b/frontend/.vscode/tasks.json @@ -48,7 +48,7 @@ { "label": "AF: build_flowy_sdk_for_android", "type": "shell", - "command": "cargo make --profile development-android appflowy-sdk-dev-android", + "command": "cargo make --profile development-android appflowy-core-dev-android", "group": "build", "options": { "cwd": "${workspaceFolder}" diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index 16c7e80b96..2c9788d4b8 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -18,7 +18,7 @@ on_error_task = "catch" run_task = { name = ["restore-crate-type"] } [env] -RUST_LOG = "info" +RUST_LOG = "debug" CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true CARGO_MAKE_CRATE_FS_NAME = "dart_ffi" CARGO_MAKE_CRATE_NAME = "dart-ffi" diff --git a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart index f62363c153..2b74d62416 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart @@ -186,43 +186,20 @@ class BoardBloc extends Bloc { return super.close(); } - void initializeGroups(List groupsData) { + void initializeGroups(List groups) { for (var controller in groupControllers.values) { controller.dispose(); } groupControllers.clear(); boardController.clear(); - // - List groups = groupsData + boardController.addGroups(groups .where((group) => fieldController.getField(group.fieldId) != null) - .map((group) { - return AppFlowyGroupData( - id: group.groupId, - name: group.desc, - items: _buildGroupItems(group), - customData: GroupData( - group: group, - fieldInfo: fieldController.getField(group.fieldId)!, - ), - ); - }).toList(); - boardController.addGroups(groups); + .map((group) => initializeGroupData(group)) + .toList()); - for (final group in groupsData) { - final delegate = GroupControllerDelegateImpl( - controller: boardController, - fieldController: fieldController, - onNewColumnItem: (groupId, row, index) { - add(BoardEvent.didCreateRow(group, row, index)); - }, - ); - final controller = GroupController( - databaseId: state.databaseId, - group: group, - delegate: delegate, - ); - controller.startListening(); + for (final group in groups) { + final controller = initializeGroupController(group); groupControllers[controller.group.groupId] = (controller); } } @@ -245,11 +222,15 @@ class BoardBloc extends Bloc { }, onDeletedGroup: (groupIds) { if (isClosed) return; - // + boardController.removeGroups(groupIds); }, - onInsertedGroup: (insertedGroups) { + onInsertedGroup: (insertedGroup) { if (isClosed) return; - // + final group = insertedGroup.group; + final newGroup = initializeGroupData(group); + final controller = initializeGroupController(group); + groupControllers[controller.group.groupId] = (controller); + boardController.addGroup(newGroup); }, onUpdatedGroup: (updatedGroups) { if (isClosed) return; @@ -294,6 +275,35 @@ class BoardBloc extends Bloc { ), ); } + + GroupController initializeGroupController(GroupPB group) { + final delegate = GroupControllerDelegateImpl( + controller: boardController, + fieldController: fieldController, + onNewColumnItem: (groupId, row, index) { + add(BoardEvent.didCreateRow(group, row, index)); + }, + ); + final controller = GroupController( + databaseId: state.databaseId, + group: group, + delegate: delegate, + ); + controller.startListening(); + return controller; + } + + AppFlowyGroupData initializeGroupData(GroupPB group) { + return AppFlowyGroupData( + id: group.groupId, + name: group.desc, + items: _buildGroupItems(group), + customData: GroupData( + group: group, + fieldInfo: fieldController.getField(group.fieldId)!, + ), + ); + } } @freezed diff --git a/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart b/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart index 522e7bc723..f68983e053 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart @@ -17,7 +17,7 @@ typedef OnGridChanged = void Function(DatabasePB); typedef DidLoadGroups = void Function(List); typedef OnUpdatedGroup = void Function(List); typedef OnDeletedGroup = void Function(List); -typedef OnInsertedGroup = void Function(List); +typedef OnInsertedGroup = void Function(InsertedGroupPB); typedef OnResetGroups = void Function(List); typedef OnRowsChanged = void Function( @@ -90,8 +90,8 @@ class BoardDataController { onDeletedGroup.call(changeset.deletedGroups); } - if (changeset.insertedGroups.isNotEmpty) { - onInsertedGroup.call(changeset.insertedGroups); + for (final insertedGroup in changeset.insertedGroups) { + onInsertedGroup.call(insertedGroup); } }, (e) => _onError?.call(e), diff --git a/frontend/app_flowy/lib/plugins/board/application/board_listener.dart b/frontend/app_flowy/lib/plugins/board/application/board_listener.dart index 4adeb8b4b1..1747daaf5c 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_listener.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_listener.dart @@ -46,7 +46,7 @@ class BoardListener { case DatabaseNotification.DidGroupByNewField: result.fold( (payload) => _groupByNewFieldNotifier?.value = - left(GroupViewChangesetPB.fromBuffer(payload).newGroups), + left(GroupViewChangesetPB.fromBuffer(payload).initialGroups), (error) => _groupByNewFieldNotifier?.value = right(error), ); break; diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart index 817456df03..b32fd124c7 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart @@ -270,7 +270,7 @@ class GridCellController extends Equatable { /// You can set [deduplicate] to true (default is false) to reduce the save operation. /// It's useful when you call this method when user editing the [TextField]. /// The default debounce interval is 300 milliseconds. - void saveCellData( + Future saveCellData( D data, { bool deduplicate = false, void Function(Option)? onFinish, diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/url_cell_editor_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/url_cell_editor_bloc.dart index 7218eb37ad..a21c5e52bc 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/url_cell_editor_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/url_cell_editor_bloc.dart @@ -14,13 +14,16 @@ class URLCellEditorBloc extends Bloc { }) : super(URLCellEditorState.initial(cellController)) { on( (event, emit) async { - event.when( + await event.when( initial: () { _startListening(); }, - updateText: (text) { - cellController.saveCellData(text, deduplicate: true); - emit(state.copyWith(content: text)); + updateText: (text) async { + await cellController.saveCellData(text); + emit(state.copyWith( + content: text, + isFinishEditing: true, + )); }, didReceiveCellUpdate: (cellData) { emit(state.copyWith(content: cellData?.content ?? "")); @@ -63,12 +66,14 @@ class URLCellEditorEvent with _$URLCellEditorEvent { class URLCellEditorState with _$URLCellEditorState { const factory URLCellEditorState({ required String content, + required bool isFinishEditing, }) = _URLCellEditorState; factory URLCellEditorState.initial(GridURLCellController context) { final cellData = context.getCellData(); return URLCellEditorState( content: cellData?.content ?? "", + isFinishEditing: true, ); } } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart index 3a0d3ad6ed..f7cf3ba38f 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart @@ -6,9 +6,13 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; class URLCellEditor extends StatefulWidget { + final VoidCallback onExit; final GridURLCellController cellController; - const URLCellEditor({required this.cellController, Key? key}) - : super(key: key); + const URLCellEditor({ + required this.cellController, + required this.onExit, + Key? key, + }) : super(key: key); @override State createState() => _URLCellEditorState(); @@ -36,12 +40,17 @@ class _URLCellEditorState extends State { if (_controller.text != state.content) { _controller.text = state.content; } + + if (state.isFinishEditing) { + widget.onExit(); + } }, child: TextField( autofocus: true, controller: _controller, - onChanged: (value) => focusChanged(), - maxLines: null, + onSubmitted: (value) => focusChanged(), + onEditingComplete: () => focusChanged(), + maxLines: 1, style: Theme.of(context).textTheme.bodyMedium, decoration: const InputDecoration( contentPadding: EdgeInsets.zero, @@ -57,11 +66,10 @@ class _URLCellEditorState extends State { @override Future dispose() async { _cellBloc.close(); - super.dispose(); } - Future focusChanged() async { + void focusChanged() { if (mounted) { if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) { @@ -72,9 +80,13 @@ class _URLCellEditorState extends State { } class URLEditorPopover extends StatelessWidget { + final VoidCallback onExit; final GridURLCellController cellController; - const URLEditorPopover({required this.cellController, Key? key}) - : super(key: key); + const URLEditorPopover({ + required this.cellController, + required this.onExit, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -84,6 +96,7 @@ class URLEditorPopover extends StatelessWidget { padding: const EdgeInsets.all(6), child: URLCellEditor( cellController: cellController, + onExit: onExit, ), ), ); diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart index c7847c17a3..c7a68826d8 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart @@ -142,6 +142,7 @@ class _GridURLCellState extends GridCellState { return URLEditorPopover( cellController: widget.cellControllerBuilder.build() as GridURLCellController, + onExit: () => _popoverController.close(), ); }, onClose: () { @@ -219,6 +220,7 @@ class _EditURLAccessoryState extends State<_EditURLAccessory> return URLEditorPopover( cellController: widget.cellControllerBuilder.build() as GridURLCellController, + onExit: () => _popoverController.close(), ); }, ); diff --git a/frontend/appflowy_tauri/src-tauri/src/init.rs b/frontend/appflowy_tauri/src-tauri/src/init.rs index af1b34918a..911ea88c1d 100644 --- a/frontend/appflowy_tauri/src-tauri/src/init.rs +++ b/frontend/appflowy_tauri/src-tauri/src/init.rs @@ -1,10 +1,10 @@ -use flowy_core::{get_client_server_configuration, FlowySDK, FlowySDKConfig}; +use flowy_core::{get_client_server_configuration, AppFlowyCore, AppFlowyCoreConfig}; -pub fn init_flowy_core() -> FlowySDK { +pub fn init_flowy_core() -> AppFlowyCore { let data_path = tauri::api::path::data_dir().unwrap(); let path = format!("{}/AppFlowy", data_path.to_str().unwrap()); let server_config = get_client_server_configuration().unwrap(); - let config = FlowySDKConfig::new(&path, "AppFlowy".to_string(), server_config) + let config = AppFlowyCoreConfig::new(&path, "AppFlowy".to_string(), server_config) .log_filter("trace", vec!["appflowy_tauri".to_string()]); - FlowySDK::new(config) + AppFlowyCore::new(config) } diff --git a/frontend/appflowy_tauri/src-tauri/src/request.rs b/frontend/appflowy_tauri/src-tauri/src/request.rs index b4c27f5e08..40386cac11 100644 --- a/frontend/appflowy_tauri/src-tauri/src/request.rs +++ b/frontend/appflowy_tauri/src-tauri/src/request.rs @@ -1,4 +1,4 @@ -use flowy_core::FlowySDK; +use flowy_core::AppFlowyCore; use lib_dispatch::prelude::{ AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode, }; @@ -39,7 +39,7 @@ pub async fn invoke_request( app_handler: AppHandle, ) -> AFTauriResponse { let request: AFPluginRequest = request.into(); - let state: State = app_handler.state(); + let state: State = app_handler.state(); let dispatcher = state.inner().dispatcher(); let response = AFPluginDispatcher::async_send(dispatcher, request).await; response.into() diff --git a/frontend/rust-lib/dart-ffi/src/lib.rs b/frontend/rust-lib/dart-ffi/src/lib.rs index 6bb45797a4..4afbd3b88e 100644 --- a/frontend/rust-lib/dart-ffi/src/lib.rs +++ b/frontend/rust-lib/dart-ffi/src/lib.rs @@ -20,7 +20,7 @@ use parking_lot::RwLock; use std::{ffi::CStr, os::raw::c_char}; lazy_static! { - static ref FLOWY_SDK: RwLock> = RwLock::new(None); + static ref APPFLOWY_CORE: RwLock> = RwLock::new(None); } #[no_mangle] @@ -30,8 +30,8 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 { let server_config = get_client_server_configuration().unwrap(); let log_crates = vec!["flowy-ffi".to_string()]; - let config = FlowySDKConfig::new(path, "appflowy".to_string(), server_config).log_filter("info", log_crates); - *FLOWY_SDK.write() = Some(FlowySDK::new(config)); + let config = AppFlowyCoreConfig::new(path, "appflowy".to_string(), server_config).log_filter("info", log_crates); + *APPFLOWY_CORE.write() = Some(AppFlowyCore::new(config)); 0 } @@ -46,7 +46,7 @@ pub extern "C" fn async_event(port: i64, input: *const u8, len: usize) { port ); - let dispatcher = match FLOWY_SDK.read().as_ref() { + let dispatcher = match APPFLOWY_CORE.read().as_ref() { None => { log::error!("sdk not init yet."); return; @@ -64,7 +64,7 @@ pub extern "C" fn sync_event(input: *const u8, len: usize) -> *const u8 { let request: AFPluginRequest = FFIRequest::from_u8_pointer(input, len).into(); log::trace!("[FFI]: {} Sync Event: {:?}", &request.id, &request.event,); - let dispatcher = match FLOWY_SDK.read().as_ref() { + let dispatcher = match APPFLOWY_CORE.read().as_ref() { None => { log::error!("sdk not init yet."); return forget_rust(Vec::default()); diff --git a/frontend/rust-lib/flowy-core/src/lib.rs b/frontend/rust-lib/flowy-core/src/lib.rs index eab047a5fa..d72f636457 100644 --- a/frontend/rust-lib/flowy-core/src/lib.rs +++ b/frontend/rust-lib/flowy-core/src/lib.rs @@ -34,31 +34,31 @@ use user_model::UserProfile; static INIT_LOG: AtomicBool = AtomicBool::new(false); #[derive(Clone)] -pub struct FlowySDKConfig { - /// Different `FlowySDK` instance should have different name +pub struct AppFlowyCoreConfig { + /// Different `AppFlowyCoreConfig` instance should have different name name: String, /// Panics if the `root` path is not existing - root: String, + storage_path: String, log_filter: String, server_config: ClientServerConfiguration, pub document: DocumentConfig, } -impl fmt::Debug for FlowySDKConfig { +impl fmt::Debug for AppFlowyCoreConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlowySDKConfig") - .field("root", &self.root) + f.debug_struct("AppFlowyCoreConfig") + .field("storage_path", &self.storage_path) .field("server-config", &self.server_config) .field("document-config", &self.document) .finish() } } -impl FlowySDKConfig { +impl AppFlowyCoreConfig { pub fn new(root: &str, name: String, server_config: ClientServerConfiguration) -> Self { - FlowySDKConfig { + AppFlowyCoreConfig { name, - root: root.to_owned(), + storage_path: root.to_owned(), log_filter: create_log_filter("info".to_owned(), vec![]), server_config, document: DocumentConfig::default(), @@ -106,9 +106,9 @@ fn create_log_filter(level: String, with_crates: Vec) -> String { } #[derive(Clone)] -pub struct FlowySDK { +pub struct AppFlowyCore { #[allow(dead_code)] - pub config: FlowySDKConfig, + pub config: AppFlowyCoreConfig, pub user_session: Arc, pub document_manager: Arc, pub folder_manager: Arc, @@ -119,10 +119,10 @@ pub struct FlowySDK { pub task_dispatcher: Arc>, } -impl FlowySDK { - pub fn new(config: FlowySDKConfig) -> Self { +impl AppFlowyCore { + pub fn new(config: AppFlowyCoreConfig) -> Self { init_log(&config); - init_kv(&config.root); + init_kv(&config.storage_path); tracing::debug!("🔥 {:?}", config); let runtime = tokio_default_runtime().unwrap(); let task_scheduler = TaskDispatcher::new(Duration::from_secs(2)); @@ -257,22 +257,22 @@ fn init_kv(root: &str) { } } -fn init_log(config: &FlowySDKConfig) { +fn init_log(config: &AppFlowyCoreConfig) { if !INIT_LOG.load(Ordering::SeqCst) { INIT_LOG.store(true, Ordering::SeqCst); - let _ = lib_log::Builder::new("AppFlowy-Client", &config.root) + let _ = lib_log::Builder::new("AppFlowy-Client", &config.storage_path) .env_filter(&config.log_filter) .build(); } } fn mk_user_session( - config: &FlowySDKConfig, + config: &AppFlowyCoreConfig, local_server: &Option>, server_config: &ClientServerConfiguration, ) -> Arc { - let user_config = UserSessionConfig::new(&config.name, &config.root); + let user_config = UserSessionConfig::new(&config.name, &config.storage_path); let cloud_service = UserDepsResolver::resolve(local_server, server_config); Arc::new(UserSession::new(user_config, cloud_service)) } @@ -282,7 +282,7 @@ struct UserStatusListener { folder_manager: Arc, grid_manager: Arc, ws_conn: Arc, - config: FlowySDKConfig, + config: AppFlowyCoreConfig, } impl UserStatusListener { diff --git a/frontend/rust-lib/flowy-database/src/entities/group_entities/group_changeset.rs b/frontend/rust-lib/flowy-database/src/entities/group_entities/group_changeset.rs index df165a0c46..c894558050 100644 --- a/frontend/rust-lib/flowy-database/src/entities/group_entities/group_changeset.rs +++ b/frontend/rust-lib/flowy-database/src/entities/group_entities/group_changeset.rs @@ -135,7 +135,7 @@ pub struct GroupViewChangesetPB { pub inserted_groups: Vec, #[pb(index = 3)] - pub new_groups: Vec, + pub initial_groups: Vec, #[pb(index = 4)] pub deleted_groups: Vec, @@ -146,7 +146,7 @@ pub struct GroupViewChangesetPB { impl GroupViewChangesetPB { pub fn is_empty(&self) -> bool { - self.new_groups.is_empty() + self.initial_groups.is_empty() && self.inserted_groups.is_empty() && self.deleted_groups.is_empty() && self.update_groups.is_empty() diff --git a/frontend/rust-lib/flowy-database/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-database/src/services/cell/cell_operation.rs index 7f00b2abca..1dad9ed3c8 100644 --- a/frontend/rust-lib/flowy-database/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-database/src/services/cell/cell_operation.rs @@ -76,7 +76,7 @@ pub fn apply_cell_data_changeset + Debug>( +pub fn get_type_cell_protobuf + Debug>( data: T, field_rev: &FieldRevision, cell_data_cache: Option, @@ -85,7 +85,13 @@ pub fn decode_type_cell_data + Debu match data.try_into() { Ok(type_cell_data) => { let TypeCellData { cell_str, field_type } = type_cell_data; - match try_decode_cell_str(cell_str, &field_type, &to_field_type, field_rev, cell_data_cache) { + match try_decode_cell_str_to_cell_protobuf( + cell_str, + &field_type, + &to_field_type, + field_rev, + cell_data_cache, + ) { Ok(cell_bytes) => (field_type, cell_bytes), Err(e) => { tracing::error!("Decode cell data failed, {:?}", e); @@ -97,12 +103,30 @@ pub fn decode_type_cell_data + Debu // It's okay to ignore this error, because it's okay that the current cell can't // display the existing cell data. For example, the UI of the text cell will be blank if // the type of the data of cell is Number. - (to_field_type, CellProtobufBlob::default()) } } } +pub fn get_type_cell_data( + data: CellData, + field_rev: &FieldRevision, + cell_data_cache: Option, +) -> Option +where + CellData: TryInto + Debug, + Output: Default + 'static, +{ + let to_field_type = field_rev.ty.into(); + match data.try_into() { + Ok(type_cell_data) => { + let TypeCellData { cell_str, field_type } = type_cell_data; + try_decode_cell_str_to_cell_data(cell_str, &field_type, &to_field_type, field_rev, cell_data_cache) + } + Err(_err) => None, + } +} + /// Decode the opaque cell data from one field type to another using the corresponding `TypeOption` /// /// The cell data might become an empty string depends on the to_field_type's `TypeOption` @@ -120,7 +144,7 @@ pub fn decode_type_cell_data + Debu /// /// returns: CellBytes /// -pub fn try_decode_cell_str( +pub fn try_decode_cell_str_to_cell_protobuf( cell_str: String, from_field_type: &FieldType, to_field_type: &FieldType, @@ -135,6 +159,20 @@ pub fn try_decode_cell_str( } } +pub fn try_decode_cell_str_to_cell_data( + cell_str: String, + from_field_type: &FieldType, + to_field_type: &FieldType, + field_rev: &FieldRevision, + cell_data_cache: Option, +) -> Option { + let handler = TypeOptionCellExt::new_with_cell_data_cache(field_rev, cell_data_cache) + .get_type_option_cell_data_handler(to_field_type)?; + handler + .get_cell_data(cell_str, from_field_type, field_rev) + .ok()? + .unbox_or_none::() +} /// Returns a string that represents the current field_type's cell data. /// For example, The string of the Multi-Select cell will be a list of the option's name /// separated by a comma. diff --git a/frontend/rust-lib/flowy-database/src/services/field/type_options/type_option_cell.rs b/frontend/rust-lib/flowy-database/src/services/field/type_options/type_option_cell.rs index 619ef7f01b..fd0d9a1d94 100644 --- a/frontend/rust-lib/flowy-database/src/services/field/type_options/type_option_cell.rs +++ b/frontend/rust-lib/flowy-database/src/services/field/type_options/type_option_cell.rs @@ -497,7 +497,7 @@ impl BoxCellData { } } - fn unbox_or_none(self) -> Option + pub(crate) fn unbox_or_none(self) -> Option where T: Default + 'static, { diff --git a/frontend/rust-lib/flowy-database/src/services/field/type_options/url_type_option/url_type_option_entities.rs b/frontend/rust-lib/flowy-database/src/services/field/type_options/url_type_option/url_type_option_entities.rs index c49af048d3..f9e98bbcca 100644 --- a/frontend/rust-lib/flowy-database/src/services/field/type_options/url_type_option/url_type_option_entities.rs +++ b/frontend/rust-lib/flowy-database/src/services/field/type_options/url_type_option/url_type_option_entities.rs @@ -49,6 +49,15 @@ impl URLCellData { } } +impl From for URLCellData { + fn from(data: URLCellDataPB) -> Self { + Self { + url: data.url, + content: data.content, + } + } +} + impl AsRef for URLCellData { fn as_ref(&self) -> &str { &self.url diff --git a/frontend/rust-lib/flowy-database/src/services/grid_editor.rs b/frontend/rust-lib/flowy-database/src/services/grid_editor.rs index 7070976553..b18bdeb2a2 100644 --- a/frontend/rust-lib/flowy-database/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-database/src/services/grid_editor.rs @@ -4,7 +4,7 @@ use crate::manager::DatabaseUser; use crate::notification::{send_notification, DatabaseNotification}; use crate::services::block_manager::DatabaseBlockManager; use crate::services::cell::{ - apply_cell_data_changeset, decode_type_cell_data, stringify_cell_data, AnyTypeCache, AtomicCellDataCache, + apply_cell_data_changeset, get_type_cell_protobuf, stringify_cell_data, AnyTypeCache, AtomicCellDataCache, CellProtobufBlob, ToCellChangesetString, TypeCellData, }; use crate::services::field::{ @@ -392,8 +392,9 @@ impl DatabaseRevisionEditor { pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> { let row_id = changeset.row_id.clone(); + let old_row = self.get_row_rev(&row_id).await?; self.block_manager.update_row(changeset).await?; - self.view_manager.did_update_cell(&row_id).await; + self.view_manager.did_update_row(old_row, &row_id).await; Ok(()) } @@ -440,7 +441,7 @@ impl DatabaseRevisionEditor { /// Returns the cell data that encoded in protobuf. pub async fn get_cell(&self, params: &CellPathParams) -> Option { - let (field_type, cell_bytes) = self.decode_cell_data_from(params).await?; + let (field_type, cell_bytes) = self.get_type_cell_protobuf(params).await?; Some(CellPB::new( ¶ms.field_id, ¶ms.row_id, @@ -473,15 +474,15 @@ impl DatabaseRevisionEditor { } pub async fn get_cell_protobuf(&self, params: &CellPathParams) -> Option { - let (_, cell_data) = self.decode_cell_data_from(params).await?; + let (_, cell_data) = self.get_type_cell_protobuf(params).await?; Some(cell_data) } - async fn decode_cell_data_from(&self, params: &CellPathParams) -> Option<(FieldType, CellProtobufBlob)> { + async fn get_type_cell_protobuf(&self, params: &CellPathParams) -> Option<(FieldType, CellProtobufBlob)> { let field_rev = self.get_field_rev(¶ms.field_id).await?; let (_, row_rev) = self.block_manager.get_row_rev(¶ms.row_id).await.ok()??; let cell_rev = row_rev.cells.get(¶ms.field_id)?.clone(); - Some(decode_type_cell_data( + Some(get_type_cell_protobuf( cell_rev.type_cell_data, &field_rev, Some(self.cell_data_cache.clone()), @@ -513,11 +514,12 @@ impl DatabaseRevisionEditor { ) -> FlowyResult<()> { match self.database_pad.read().await.get_field_rev(field_id) { None => { - let msg = format!("Field:{} not found", &field_id); + let msg = format!("Field with id:{} not found", &field_id); Err(FlowyError::internal().context(msg)) } Some((_, field_rev)) => { tracing::trace!("Cell changeset: id:{} / value:{:?}", &field_id, cell_changeset); + let old_row_rev = self.get_row_rev(row_id).await?.clone(); let cell_rev = self.get_cell_rev(row_id, field_id).await?; // Update the changeset.data property with the return value. let type_cell_data = @@ -529,7 +531,7 @@ impl DatabaseRevisionEditor { type_cell_data, }; self.block_manager.update_cell(cell_changeset).await?; - self.view_manager.did_update_cell(row_id).await; + self.view_manager.did_update_row(old_row_rev, row_id).await; Ok(()) } } diff --git a/frontend/rust-lib/flowy-database/src/services/group/action.rs b/frontend/rust-lib/flowy-database/src/services/group/action.rs index fe739f123c..bbdc71ed1b 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/action.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/action.rs @@ -1,4 +1,4 @@ -use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB}; +use crate::entities::{GroupPB, GroupRowsNotificationPB, GroupViewChangesetPB, InsertedGroupPB}; use crate::services::cell::DecodedCellData; use crate::services::group::controller::MoveGroupRowContext; use crate::services::group::Group; @@ -10,42 +10,53 @@ use std::sync::Arc; /// /// For example, the `CheckboxGroupController` implements this trait to provide custom behavior. /// -pub trait GroupControllerCustomActions: Send + Sync { - type CellDataType: DecodedCellData; - /// Returns the a value of the cell, default value is None +pub trait GroupCustomize: Send + Sync { + type CellData: DecodedCellData; + /// Returns the a value of the cell if the cell data is not exist. + /// The default value is `None` /// /// Determine which group the row is placed in based on the data of the cell. If the cell data /// is None. The row will be put in to the `No status` group /// - fn default_cell_rev(&self) -> Option { + fn placeholder_cell(&self) -> Option { None } /// Returns a bool value to determine whether the group should contain this cell or not. - fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool; + fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool; + + fn create_or_delete_group_when_cell_changed( + &mut self, + _row_rev: &RowRevision, + _old_cell_data: Option<&Self::CellData>, + _cell_data: &Self::CellData, + ) -> FlowyResult<(Option, Option)> { + Ok((None, None)) + } /// Adds or removes a row if the cell data match the group filter. /// It gets called after editing the cell or row /// - fn add_or_remove_row_in_groups_if_match( + fn add_or_remove_row_when_cell_changed( &mut self, row_rev: &RowRevision, - cell_data: &Self::CellDataType, + cell_data: &Self::CellData, ) -> Vec; /// Deletes the row from the group - fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec; + fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellData) -> Vec; /// Move row from one group to another - fn move_row( - &mut self, - cell_data: &Self::CellDataType, - context: MoveGroupRowContext, - ) -> Vec; + fn move_row(&mut self, cell_data: &Self::CellData, context: MoveGroupRowContext) -> Vec; + + /// Returns None if there is no need to delete the group when corresponding row get removed + fn delete_group_when_move_row(&mut self, _row_rev: &RowRevision, _cell_data: &Self::CellData) -> Option { + None + } } /// Defines the shared actions any group controller can perform. -pub trait GroupControllerSharedActions: Send + Sync { +pub trait GroupControllerActions: Send + Sync { /// The field that is used for grouping the rows fn field_id(&self) -> &str; @@ -64,20 +75,34 @@ pub trait GroupControllerSharedActions: Send + Sync { /// Insert/Remove the row to the group if the corresponding cell data is changed fn did_update_group_row( &mut self, + old_row_rev: &Option>, row_rev: &RowRevision, field_rev: &FieldRevision, - ) -> FlowyResult>; + ) -> FlowyResult; /// Remove the row from the group if the row gets deleted fn did_delete_delete_row( &mut self, row_rev: &RowRevision, field_rev: &FieldRevision, - ) -> FlowyResult>; + ) -> FlowyResult; /// Move the row from one group to another group - fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult>; + fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult; /// Update the group if the corresponding field is changed fn did_update_group_field(&mut self, field_rev: &FieldRevision) -> FlowyResult>; } + +#[derive(Debug)] +pub struct DidUpdateGroupRowResult { + pub(crate) inserted_group: Option, + pub(crate) deleted_group: Option, + pub(crate) row_changesets: Vec, +} + +#[derive(Debug)] +pub struct DidMoveGroupRowResult { + pub(crate) deleted_group: Option, + pub(crate) row_changesets: Vec, +} diff --git a/frontend/rust-lib/flowy-database/src/services/group/configuration.rs b/frontend/rust-lib/flowy-database/src/services/group/configuration.rs index 86085ad613..f7fc1a7c03 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/configuration.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/configuration.rs @@ -1,4 +1,4 @@ -use crate::entities::{GroupPB, GroupViewChangesetPB}; +use crate::entities::{GroupPB, GroupViewChangesetPB, InsertedGroupPB}; use crate::services::field::RowSingleCellData; use crate::services::group::{default_group_configuration, GeneratedGroupContext, Group}; use flowy_error::{FlowyError, FlowyResult}; @@ -101,7 +101,7 @@ where /// Returns the no `status` group /// - /// We take the `id` of the `field` as the default group id + /// We take the `id` of the `field` as the no status group id pub(crate) fn get_no_status_group(&self) -> Option<&Group> { self.groups_map.get(&self.field_rev.id) } @@ -140,6 +140,37 @@ where each(group); }); } + #[tracing::instrument(level = "trace", skip(self), err)] + pub(crate) fn add_new_group(&mut self, group_rev: GroupRevision) -> FlowyResult { + let group = Group::new( + group_rev.id.clone(), + self.field_rev.id.clone(), + group_rev.name.clone(), + group_rev.id.clone(), + ); + self.groups_map.insert(group_rev.id.clone(), group); + let (index, group) = self.get_group(&group_rev.id).unwrap(); + let insert_group = InsertedGroupPB { + group: GroupPB::from(group.clone()), + index: index as i32, + }; + self.mut_configuration(|configuration| { + configuration.groups.push(group_rev); + true + })?; + + Ok(insert_group) + } + + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn delete_group(&mut self, deleted_group_id: &str) -> FlowyResult<()> { + self.groups_map.remove(deleted_group_id); + self.mut_configuration(|configuration| { + configuration.groups.retain(|group| group.id != deleted_group_id); + true + })?; + Ok(()) + } pub(crate) fn move_group(&mut self, from_id: &str, to_id: &str) -> FlowyResult<()> { let from_index = self.groups_map.get_index_of(from_id); @@ -211,18 +242,12 @@ where old_groups.clear(); } - // The `No status` group index is initialized to 0 - if let Some(no_status_group) = no_status_group { - old_groups.insert(0, no_status_group); - } - // The `all_group_revs` is the combination of the new groups and old groups let MergeGroupResult { mut all_group_revs, new_group_revs, - updated_group_revs: _, deleted_group_revs, - } = merge_groups(old_groups, new_groups); + } = merge_groups(no_status_group, old_groups, new_groups); let deleted_group_ids = deleted_group_revs .into_iter() @@ -231,13 +256,10 @@ where self.mut_configuration(|configuration| { let mut is_changed = !deleted_group_ids.is_empty(); - // Remove the groups - if !deleted_group_ids.is_empty() { - configuration - .groups - .retain(|group| !deleted_group_ids.contains(&group.id)); - } + configuration + .groups + .retain(|group| !deleted_group_ids.contains(&group.id)); // Update/Insert new groups for group_rev in &mut all_group_revs { @@ -276,7 +298,7 @@ where self.groups_map.insert(group.id.clone(), group); }); - let new_groups = new_group_revs + let initial_groups = new_group_revs .into_iter() .flat_map(|group_rev| { let filter_content = filter_content_map.get(&group_rev.id)?; @@ -292,7 +314,7 @@ where let changeset = GroupViewChangesetPB { view_id: self.view_id.clone(), - new_groups, + initial_groups, deleted_groups: deleted_group_ids, update_groups: vec![], inserted_groups: vec![], @@ -366,7 +388,11 @@ where /// Merge the new groups into old groups while keeping the order in the old groups /// -fn merge_groups(old_groups: Vec, new_groups: Vec) -> MergeGroupResult { +fn merge_groups( + no_status_group: Option, + old_groups: Vec, + new_groups: Vec, +) -> MergeGroupResult { let mut merge_result = MergeGroupResult::new(); // group_map is a helper map is used to filter out the new groups. let mut new_group_map: IndexMap = IndexMap::new(); @@ -378,11 +404,8 @@ fn merge_groups(old_groups: Vec, new_groups: Vec) for old in old_groups { if let Some(new) = new_group_map.remove(&old.id) { merge_result.all_group_revs.push(new.clone()); - if is_group_changed(&new, &old) { - merge_result.updated_group_revs.push(new); - } } else { - merge_result.all_group_revs.push(old); + merge_result.deleted_group_revs.push(old); } } @@ -392,6 +415,11 @@ fn merge_groups(old_groups: Vec, new_groups: Vec) merge_result.all_group_revs.push(group.clone()); merge_result.new_group_revs.push(group); } + + // The `No status` group index is initialized to 0 + if let Some(no_status_group) = no_status_group { + merge_result.all_group_revs.insert(0, no_status_group); + } merge_result } @@ -399,7 +427,6 @@ fn is_group_changed(new: &GroupRevision, old: &GroupRevision) -> bool { if new.name != old.name { return true; } - false } @@ -407,7 +434,6 @@ struct MergeGroupResult { // Contains the new groups and the updated groups all_group_revs: Vec, new_group_revs: Vec, - updated_group_revs: Vec, deleted_group_revs: Vec, } @@ -416,7 +442,6 @@ impl MergeGroupResult { Self { all_group_revs: vec![], new_group_revs: vec![], - updated_group_revs: vec![], deleted_group_revs: vec![], } } diff --git a/frontend/rust-lib/flowy-database/src/services/group/controller.rs b/frontend/rust-lib/flowy-database/src/services/group/controller.rs index 34398812ea..2a3119beec 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/controller.rs @@ -1,11 +1,15 @@ use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB, InsertedRowPB, RowPB}; -use crate::services::cell::{decode_type_cell_data, CellProtobufBlobParser, DecodedCellData}; -use crate::services::group::action::{GroupControllerCustomActions, GroupControllerSharedActions}; +use crate::services::cell::{get_type_cell_protobuf, CellProtobufBlobParser, DecodedCellData}; + +use crate::services::group::action::{ + DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerActions, GroupCustomize, +}; use crate::services::group::configuration::GroupContext; use crate::services::group::entities::Group; use flowy_error::FlowyResult; use grid_model::{ - FieldRevision, GroupConfigurationContentSerde, GroupRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer, + CellRevision, FieldRevision, GroupConfigurationContentSerde, GroupRevision, RowChangeset, RowRevision, + TypeOptionDataDeserializer, }; use std::marker::PhantomData; use std::sync::Arc; @@ -18,7 +22,7 @@ use std::sync::Arc; /// If the [FieldType] doesn't implement its group controller, then the [DefaultGroupController] will /// be used. /// -pub trait GroupController: GroupControllerSharedActions + Send + Sync { +pub trait GroupController: GroupControllerActions + Send + Sync { fn will_create_row(&mut self, row_rev: &mut RowRevision, field_rev: &FieldRevision, group_id: &str); fn did_create_row(&mut self, row_pb: &RowPB, group_id: &str); } @@ -86,12 +90,12 @@ where // https://stackoverflow.com/questions/69413164/how-to-fix-this-clippy-warning-needless-collect #[allow(clippy::needless_collect)] - fn update_default_group( + fn update_no_status_group( &mut self, row_rev: &RowRevision, other_group_changesets: &[GroupRowsNotificationPB], ) -> Option { - let default_group = self.group_ctx.get_mut_no_status_group()?; + let no_status_group = self.group_ctx.get_mut_no_status_group()?; // [other_group_inserted_row] contains all the inserted rows except the default group. let other_group_inserted_row = other_group_changesets @@ -100,7 +104,7 @@ where .collect::>(); // Calculate the inserted_rows of the default_group - let default_group_inserted_row = other_group_changesets + let no_status_group_rows = other_group_changesets .iter() .flat_map(|changeset| &changeset.deleted_rows) .cloned() @@ -113,10 +117,10 @@ where }) .collect::>(); - let mut changeset = GroupRowsNotificationPB::new(default_group.id.clone()); - if !default_group_inserted_row.is_empty() { + let mut changeset = GroupRowsNotificationPB::new(no_status_group.id.clone()); + if !no_status_group_rows.is_empty() { changeset.inserted_rows.push(InsertedRowPB::new(row_rev.into())); - default_group.add_row(row_rev.into()); + no_status_group.add_row(row_rev.into()); } // [other_group_delete_rows] contains all the deleted rows except the default group. @@ -138,7 +142,7 @@ where .collect::>(); let mut deleted_row_ids = vec![]; - for row in &default_group.rows { + for row in &no_status_group.rows { if default_group_deleted_rows .iter() .any(|deleted_row| deleted_row.row.id == row.id) @@ -146,20 +150,20 @@ where deleted_row_ids.push(row.id.clone()); } } - default_group.rows.retain(|row| !deleted_row_ids.contains(&row.id)); + no_status_group.rows.retain(|row| !deleted_row_ids.contains(&row.id)); changeset.deleted_rows.extend(deleted_row_ids); Some(changeset) } } -impl GroupControllerSharedActions for GenericGroupController +impl GroupControllerActions for GenericGroupController where P: CellProtobufBlobParser, C: GroupConfigurationContentSerde, T: TypeOptionDataDeserializer, G: GroupGenerator, TypeOptionType = T>, - Self: GroupControllerCustomActions, + Self: GroupCustomize, { fn field_id(&self) -> &str { &self.field_id @@ -178,13 +182,13 @@ where fn fill_groups(&mut self, row_revs: &[Arc], field_rev: &FieldRevision) -> FlowyResult<()> { for row_rev in row_revs { let cell_rev = match row_rev.cells.get(&self.field_id) { - None => self.default_cell_rev(), + None => self.placeholder_cell(), Some(cell_rev) => Some(cell_rev.clone()), }; if let Some(cell_rev) = cell_rev { let mut grouped_rows: Vec = vec![]; - let cell_bytes = decode_type_cell_data(cell_rev.type_cell_data, field_rev, None).1; + let cell_bytes = get_type_cell_protobuf(cell_rev.type_cell_data, field_rev, None).1; let cell_data = cell_bytes.parser::

()?; for group in self.group_ctx.groups() { if self.can_group(&group.filter_content, &cell_data) { @@ -206,7 +210,7 @@ where } match self.group_ctx.get_mut_no_status_group() { None => {} - Some(default_group) => default_group.add_row(row_rev.into()), + Some(no_status_group) => no_status_group.add_row(row_rev.into()), } } @@ -220,73 +224,99 @@ where fn did_update_group_row( &mut self, + old_row_rev: &Option>, row_rev: &RowRevision, field_rev: &FieldRevision, - ) -> FlowyResult> { - if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { - let cell_bytes = decode_type_cell_data(cell_rev.type_cell_data.clone(), field_rev, None).1; - let cell_data = cell_bytes.parser::

()?; - let mut changesets = self.add_or_remove_row_in_groups_if_match(row_rev, &cell_data); + ) -> FlowyResult { + // let cell_data = row_rev.cells.get(&self.field_id).and_then(|cell_rev| { + // let cell_data: Option

= get_type_cell_data(cell_rev, field_rev, None); + // cell_data + // }); + let mut result = DidUpdateGroupRowResult { + inserted_group: None, + deleted_group: None, + row_changesets: vec![], + }; - if let Some(default_group_changeset) = self.update_default_group(row_rev, &changesets) { - tracing::trace!("default_group_changeset: {}", default_group_changeset); - if !default_group_changeset.is_empty() { - changesets.push(default_group_changeset); + if let Some(cell_data) = get_cell_data_from_row_rev::

(Some(row_rev), field_rev) { + let old_row_rev = old_row_rev.as_ref().map(|old| old.as_ref()); + let old_cell_data = get_cell_data_from_row_rev::

(old_row_rev, field_rev); + if let Ok((insert, delete)) = + self.create_or_delete_group_when_cell_changed(row_rev, old_cell_data.as_ref(), &cell_data) + { + result.inserted_group = insert; + result.deleted_group = delete; + } + + let mut changesets = self.add_or_remove_row_when_cell_changed(row_rev, &cell_data); + if let Some(changeset) = self.update_no_status_group(row_rev, &changesets) { + if !changeset.is_empty() { + changesets.push(changeset); } } - Ok(changesets) - } else { - Ok(vec![]) + result.row_changesets = changesets; } + + Ok(result) } fn did_delete_delete_row( &mut self, row_rev: &RowRevision, field_rev: &FieldRevision, - ) -> FlowyResult> { + ) -> FlowyResult { // if the cell_rev is none, then the row must in the default group. + let mut result = DidMoveGroupRowResult { + deleted_group: None, + row_changesets: vec![], + }; if let Some(cell_rev) = row_rev.cells.get(&self.field_id) { - let cell_bytes = decode_type_cell_data(cell_rev.type_cell_data.clone(), field_rev, None).1; + let cell_bytes = get_type_cell_protobuf(cell_rev.type_cell_data.clone(), field_rev, None).1; let cell_data = cell_bytes.parser::

()?; if !cell_data.is_empty() { tracing::error!("did_delete_delete_row {:?}", cell_rev.type_cell_data); - return Ok(self.delete_row(row_rev, &cell_data)); + result.row_changesets = self.delete_row(row_rev, &cell_data); + return Ok(result); } } match self.group_ctx.get_no_status_group() { None => { tracing::error!("Unexpected None value. It should have the no status group"); - Ok(vec![]) } Some(no_status_group) => { if !no_status_group.contains_row(&row_rev.id) { tracing::error!("The row: {} should be in the no status group", row_rev.id); } - Ok(vec![GroupRowsNotificationPB::delete( + result.row_changesets = vec![GroupRowsNotificationPB::delete( no_status_group.id.clone(), vec![row_rev.id.clone()], - )]) + )]; } } + Ok(result) } #[tracing::instrument(level = "trace", skip_all, err)] - fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult> { + fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult { + let mut result = DidMoveGroupRowResult { + deleted_group: None, + row_changesets: vec![], + }; let cell_rev = match context.row_rev.cells.get(&self.field_id) { Some(cell_rev) => Some(cell_rev.clone()), - None => self.default_cell_rev(), + None => self.placeholder_cell(), }; if let Some(cell_rev) = cell_rev { - let cell_bytes = decode_type_cell_data(cell_rev.type_cell_data, context.field_rev, None).1; + let cell_bytes = get_type_cell_protobuf(cell_rev.type_cell_data, context.field_rev, None).1; let cell_data = cell_bytes.parser::

()?; - Ok(self.move_row(&cell_data, context)) + result.deleted_group = self.delete_group_when_move_row(context.row_rev, &cell_data); + result.row_changesets = self.move_row(&cell_data, context); } else { tracing::warn!("Unexpected moving group row, changes should not be empty"); - Ok(vec![]) } + Ok(result) } fn did_update_group_field(&mut self, _field_rev: &FieldRevision) -> FlowyResult> { @@ -298,3 +328,12 @@ struct GroupedRow { row: RowPB, group_id: String, } + +fn get_cell_data_from_row_rev( + row_rev: Option<&RowRevision>, + field_rev: &FieldRevision, +) -> Option { + let cell_rev: &CellRevision = row_rev.and_then(|row_rev| row_rev.cells.get(&field_rev.id))?; + let cell_bytes = get_type_cell_protobuf(cell_rev.type_cell_data.clone(), field_rev, None).1; + cell_bytes.parser::

().ok() +} diff --git a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/checkbox_controller.rs b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/checkbox_controller.rs index e8466e9141..e707b8048f 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/checkbox_controller.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/checkbox_controller.rs @@ -1,6 +1,6 @@ use crate::entities::{GroupRowsNotificationPB, InsertedRowPB, RowPB}; use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK}; -use crate::services::group::action::GroupControllerCustomActions; +use crate::services::group::action::GroupCustomize; use crate::services::group::configuration::GroupContext; use crate::services::group::controller::{ GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext, @@ -19,13 +19,13 @@ pub type CheckboxGroupController = GenericGroupController< pub type CheckboxGroupContext = GroupContext; -impl GroupControllerCustomActions for CheckboxGroupController { - type CellDataType = CheckboxCellData; - fn default_cell_rev(&self) -> Option { +impl GroupCustomize for CheckboxGroupController { + type CellData = CheckboxCellData; + fn placeholder_cell(&self) -> Option { Some(CellRevision::new(UNCHECK.to_string())) } - fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool { + fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool { if cell_data.is_check() { content == CHECK } else { @@ -33,10 +33,10 @@ impl GroupControllerCustomActions for CheckboxGroupController { } } - fn add_or_remove_row_in_groups_if_match( + fn add_or_remove_row_when_cell_changed( &mut self, row_rev: &RowRevision, - cell_data: &Self::CellDataType, + cell_data: &Self::CellData, ) -> Vec { let mut changesets = vec![]; self.group_ctx.iter_mut_status_groups(|group| { @@ -79,7 +79,7 @@ impl GroupControllerCustomActions for CheckboxGroupController { changesets } - fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec { + fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellData) -> Vec { let mut changesets = vec![]; self.group_ctx.iter_mut_groups(|group| { let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); @@ -97,7 +97,7 @@ impl GroupControllerCustomActions for CheckboxGroupController { fn move_row( &mut self, - _cell_data: &Self::CellDataType, + _cell_data: &Self::CellData, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; diff --git a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/default_controller.rs b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/default_controller.rs index 3d74a47c3c..0f969b23e3 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/default_controller.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/default_controller.rs @@ -1,5 +1,5 @@ -use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB, RowPB}; -use crate::services::group::action::GroupControllerSharedActions; +use crate::entities::{GroupViewChangesetPB, RowPB}; +use crate::services::group::action::{DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerActions}; use crate::services::group::{Group, GroupController, MoveGroupRowContext}; use flowy_error::FlowyResult; use grid_model::{FieldRevision, RowRevision}; @@ -31,7 +31,7 @@ impl DefaultGroupController { } } -impl GroupControllerSharedActions for DefaultGroupController { +impl GroupControllerActions for DefaultGroupController { fn field_id(&self) -> &str { &self.field_id } @@ -57,22 +57,33 @@ impl GroupControllerSharedActions for DefaultGroupController { fn did_update_group_row( &mut self, + _old_row_rev: &Option>, _row_rev: &RowRevision, _field_rev: &FieldRevision, - ) -> FlowyResult> { - Ok(vec![]) + ) -> FlowyResult { + Ok(DidUpdateGroupRowResult { + inserted_group: None, + deleted_group: None, + row_changesets: vec![], + }) } fn did_delete_delete_row( &mut self, _row_rev: &RowRevision, _field_rev: &FieldRevision, - ) -> FlowyResult> { - Ok(vec![]) + ) -> FlowyResult { + Ok(DidMoveGroupRowResult { + deleted_group: None, + row_changesets: vec![], + }) } - fn move_group_row(&mut self, _context: MoveGroupRowContext) -> FlowyResult> { - todo!() + fn move_group_row(&mut self, _context: MoveGroupRowContext) -> FlowyResult { + Ok(DidMoveGroupRowResult { + deleted_group: None, + row_changesets: vec![], + }) } fn did_update_group_field(&mut self, _field_rev: &FieldRevision) -> FlowyResult> { diff --git a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs index 0fe22fc0d6..ee18924f68 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/select_option_controller/multi_select_controller.rs @@ -1,7 +1,7 @@ use crate::entities::{GroupRowsNotificationPB, RowPB}; use crate::services::cell::insert_select_option_cell; use crate::services::field::{MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser}; -use crate::services::group::action::GroupControllerCustomActions; +use crate::services::group::action::GroupCustomize; use crate::services::group::controller::{ GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext, @@ -19,17 +19,17 @@ pub type MultiSelectGroupController = GenericGroupController< SelectOptionCellDataParser, >; -impl GroupControllerCustomActions for MultiSelectGroupController { - type CellDataType = SelectOptionCellDataPB; +impl GroupCustomize for MultiSelectGroupController { + type CellData = SelectOptionCellDataPB; fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool { cell_data.select_options.iter().any(|option| option.id == content) } - fn add_or_remove_row_in_groups_if_match( + fn add_or_remove_row_when_cell_changed( &mut self, row_rev: &RowRevision, - cell_data: &Self::CellDataType, + cell_data: &Self::CellData, ) -> Vec { let mut changesets = vec![]; self.group_ctx.iter_mut_status_groups(|group| { @@ -40,7 +40,7 @@ impl GroupControllerCustomActions for MultiSelectGroupController { changesets } - fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { + fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellData) -> Vec { let mut changesets = vec![]; self.group_ctx.iter_mut_status_groups(|group| { if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) { @@ -52,7 +52,7 @@ impl GroupControllerCustomActions for MultiSelectGroupController { fn move_row( &mut self, - _cell_data: &Self::CellDataType, + _cell_data: &Self::CellData, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; diff --git a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/select_option_controller/single_select_controller.rs b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/select_option_controller/single_select_controller.rs index d9c16d8e2d..ce469b1221 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/select_option_controller/single_select_controller.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/select_option_controller/single_select_controller.rs @@ -1,7 +1,7 @@ use crate::entities::{GroupRowsNotificationPB, RowPB}; use crate::services::cell::insert_select_option_cell; use crate::services::field::{SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB}; -use crate::services::group::action::GroupControllerCustomActions; +use crate::services::group::action::GroupCustomize; use crate::services::group::controller::{ GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext, @@ -20,16 +20,16 @@ pub type SingleSelectGroupController = GenericGroupController< SelectOptionCellDataParser, >; -impl GroupControllerCustomActions for SingleSelectGroupController { - type CellDataType = SelectOptionCellDataPB; +impl GroupCustomize for SingleSelectGroupController { + type CellData = SelectOptionCellDataPB; fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool { cell_data.select_options.iter().any(|option| option.id == content) } - fn add_or_remove_row_in_groups_if_match( + fn add_or_remove_row_when_cell_changed( &mut self, row_rev: &RowRevision, - cell_data: &Self::CellDataType, + cell_data: &Self::CellData, ) -> Vec { let mut changesets = vec![]; self.group_ctx.iter_mut_status_groups(|group| { @@ -40,7 +40,7 @@ impl GroupControllerCustomActions for SingleSelectGroupController { changesets } - fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec { + fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellData) -> Vec { let mut changesets = vec![]; self.group_ctx.iter_mut_status_groups(|group| { if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) { @@ -52,7 +52,7 @@ impl GroupControllerCustomActions for SingleSelectGroupController { fn move_row( &mut self, - _cell_data: &Self::CellDataType, + _cell_data: &Self::CellData, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; diff --git a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/url_controller.rs b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/url_controller.rs index 4df0de982c..fd17a0c81c 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/controller_impls/url_controller.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/controller_impls/url_controller.rs @@ -1,12 +1,13 @@ -use crate::entities::{GroupRowsNotificationPB, InsertedRowPB, RowPB}; +use crate::entities::{GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowPB}; use crate::services::cell::insert_url_cell; -use crate::services::field::{URLCellDataPB, URLCellDataParser, URLTypeOptionPB}; -use crate::services::group::action::GroupControllerCustomActions; +use crate::services::field::{URLCellData, URLCellDataPB, URLCellDataParser, URLTypeOptionPB}; +use crate::services::group::action::GroupCustomize; use crate::services::group::configuration::GroupContext; use crate::services::group::controller::{ GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext, }; use crate::services::group::{make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroupContext}; +use flowy_error::FlowyResult; use grid_model::{CellRevision, FieldRevision, GroupRevision, RowRevision, URLGroupConfigurationRevision}; pub type URLGroupController = @@ -14,21 +15,61 @@ pub type URLGroupController = pub type URLGroupContext = GroupContext; -impl GroupControllerCustomActions for URLGroupController { - type CellDataType = URLCellDataPB; +impl GroupCustomize for URLGroupController { + type CellData = URLCellDataPB; - fn default_cell_rev(&self) -> Option { + fn placeholder_cell(&self) -> Option { Some(CellRevision::new("".to_string())) } - fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool { + fn can_group(&self, content: &str, cell_data: &Self::CellData) -> bool { cell_data.content == content } - fn add_or_remove_row_in_groups_if_match( + fn create_or_delete_group_when_cell_changed( &mut self, row_rev: &RowRevision, - cell_data: &Self::CellDataType, + old_cell_data: Option<&Self::CellData>, + cell_data: &Self::CellData, + ) -> FlowyResult<(Option, Option)> { + // Just return if the group with this url already exists + let mut inserted_group = None; + if self.group_ctx.get_group(&cell_data.url).is_none() { + let cell_data: URLCellData = cell_data.clone().into(); + let group_revision = make_group_from_url_cell(&cell_data); + let mut new_group = self.group_ctx.add_new_group(group_revision)?; + new_group.group.rows.push(RowPB::from(row_rev)); + inserted_group = Some(new_group); + } + + // Delete the old url group if there are no rows in that group + let deleted_group = + match old_cell_data.and_then(|old_cell_data| self.group_ctx.get_group(&old_cell_data.content)) { + None => None, + Some((_, group)) => { + if group.rows.len() == 1 { + Some(group.clone()) + } else { + None + } + } + }; + + let deleted_group = match deleted_group { + None => None, + Some(group) => { + self.group_ctx.delete_group(&group.id)?; + Some(GroupPB::from(group.clone())) + } + }; + + Ok((inserted_group, deleted_group)) + } + + fn add_or_remove_row_when_cell_changed( + &mut self, + row_rev: &RowRevision, + cell_data: &Self::CellData, ) -> Vec { let mut changesets = vec![]; self.group_ctx.iter_mut_status_groups(|group| { @@ -51,7 +92,7 @@ impl GroupControllerCustomActions for URLGroupController { changesets } - fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec { + fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellData) -> Vec { let mut changesets = vec![]; self.group_ctx.iter_mut_groups(|group| { let mut changeset = GroupRowsNotificationPB::new(group.id.clone()); @@ -69,7 +110,7 @@ impl GroupControllerCustomActions for URLGroupController { fn move_row( &mut self, - _cell_data: &Self::CellDataType, + _cell_data: &Self::CellData, mut context: MoveGroupRowContext, ) -> Vec { let mut group_changeset = vec![]; @@ -80,6 +121,19 @@ impl GroupControllerCustomActions for URLGroupController { }); group_changeset } + + fn delete_group_when_move_row(&mut self, _row_rev: &RowRevision, cell_data: &Self::CellData) -> Option { + let mut deleted_group = None; + if let Some((_, group)) = self.group_ctx.get_group(&cell_data.content) { + if group.rows.len() == 1 { + deleted_group = Some(GroupPB::from(group.clone())); + } + } + if deleted_group.is_some() { + let _ = self.group_ctx.delete_group(&deleted_group.as_ref().unwrap().group_id); + } + deleted_group + } } impl GroupController for URLGroupController { @@ -117,13 +171,9 @@ impl GroupGenerator for URLGroupGenerator { let group_configs = cells .into_iter() .flat_map(|value| value.into_url_field_cell_data()) - .map(|cell| { - let group_id = cell.content.clone(); - let group_name = cell.content.clone(); - GeneratedGroupConfig { - group_rev: GroupRevision::new(group_id, group_name), - filter_content: cell.content, - } + .map(|cell| GeneratedGroupConfig { + group_rev: make_group_from_url_cell(&cell), + filter_content: cell.content, }) .collect(); @@ -134,3 +184,9 @@ impl GroupGenerator for URLGroupGenerator { } } } + +fn make_group_from_url_cell(cell: &URLCellData) -> GroupRevision { + let group_id = cell.content.clone(); + let group_name = cell.content.clone(); + GroupRevision::new(group_id, group_name) +} diff --git a/frontend/rust-lib/flowy-database/src/services/group/entities.rs b/frontend/rust-lib/flowy-database/src/services/group/entities.rs index baa4842402..d556dcd3c9 100644 --- a/frontend/rust-lib/flowy-database/src/services/group/entities.rs +++ b/frontend/rust-lib/flowy-database/src/services/group/entities.rs @@ -1,6 +1,6 @@ use crate::entities::RowPB; -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Debug, Eq)] pub struct Group { pub id: String, pub field_id: String, diff --git a/frontend/rust-lib/flowy-database/src/services/view_editor/editor.rs b/frontend/rust-lib/flowy-database/src/services/view_editor/editor.rs index ec6f1ed89e..8870342ec6 100644 --- a/frontend/rust-lib/flowy-database/src/services/view_editor/editor.rs +++ b/frontend/rust-lib/flowy-database/src/services/view_editor/editor.rs @@ -240,29 +240,44 @@ impl DatabaseViewRevisionEditor { #[tracing::instrument(level = "trace", skip_all)] pub async fn did_delete_view_row(&self, row_rev: &RowRevision) { // Send the group notification if the current view has groups; - let changesets = self + let result = self .mut_group_controller(|group_controller, field_rev| { group_controller.did_delete_delete_row(row_rev, &field_rev) }) .await; - tracing::trace!("Delete row in view changeset: {:?}", changesets); - if let Some(changesets) = changesets { - for changeset in changesets { + if let Some(result) = result { + tracing::trace!("Delete row in view changeset: {:?}", result.row_changesets); + for changeset in result.row_changesets { self.notify_did_update_group_rows(changeset).await; } } } - pub async fn did_update_view_cell(&self, row_rev: &RowRevision) { - let changesets = self + pub async fn did_update_view_row(&self, old_row_rev: Option>, row_rev: &RowRevision) { + let result = self .mut_group_controller(|group_controller, field_rev| { - group_controller.did_update_group_row(row_rev, &field_rev) + Ok(group_controller.did_update_group_row(&old_row_rev, row_rev, &field_rev)) }) .await; - if let Some(changesets) = changesets { - for changeset in changesets { + if let Some(Ok(result)) = result { + let mut changeset = GroupViewChangesetPB { + view_id: self.view_id.clone(), + ..Default::default() + }; + if let Some(inserted_group) = result.inserted_group { + tracing::trace!("Create group after editing the row: {:?}", inserted_group); + changeset.inserted_groups.push(inserted_group); + } + if let Some(delete_group) = result.deleted_group { + tracing::trace!("Delete group after editing the row: {:?}", delete_group); + changeset.deleted_groups.push(delete_group.group_id); + } + self.notify_did_update_view(changeset).await; + + tracing::trace!("Group changesets after editing the row: {:?}", result.row_changesets); + for changeset in result.row_changesets { self.notify_did_update_group_rows(changeset).await; } } @@ -282,8 +297,8 @@ impl DatabaseViewRevisionEditor { row_changeset: &mut RowChangeset, to_group_id: &str, to_row_id: Option, - ) -> Vec { - let changesets = self + ) { + let result = self .mut_group_controller(|group_controller, field_rev| { let move_row_context = MoveGroupRowContext { row_rev, @@ -292,13 +307,25 @@ impl DatabaseViewRevisionEditor { to_group_id, to_row_id, }; - - let changesets = group_controller.move_group_row(move_row_context)?; - Ok(changesets) + group_controller.move_group_row(move_row_context) }) .await; - changesets.unwrap_or_default() + if let Some(result) = result { + let mut changeset = GroupViewChangesetPB { + view_id: self.view_id.clone(), + ..Default::default() + }; + if let Some(delete_group) = result.deleted_group { + tracing::info!("Delete group after moving the row: {:?}", delete_group); + changeset.deleted_groups.push(delete_group.group_id); + } + self.notify_did_update_view(changeset).await; + + for changeset in result.row_changesets { + self.notify_did_update_group_rows(changeset).await; + } + } } /// Only call once after grid view editor initialized #[tracing::instrument(level = "trace", skip(self))] @@ -334,7 +361,7 @@ impl DatabaseViewRevisionEditor { inserted_groups: vec![inserted_group], deleted_groups: vec![params.from_group_id.clone()], update_groups: vec![], - new_groups: vec![], + initial_groups: vec![], }; self.notify_did_update_view(changeset).await; @@ -610,7 +637,7 @@ impl DatabaseViewRevisionEditor { *self.group_controller.write().await = new_group_controller; let changeset = GroupViewChangesetPB { view_id: self.view_id.clone(), - new_groups, + initial_groups: new_groups, ..Default::default() }; diff --git a/frontend/rust-lib/flowy-database/src/services/view_editor/editor_manager.rs b/frontend/rust-lib/flowy-database/src/services/view_editor/editor_manager.rs index c3a25fc895..f070117c0a 100644 --- a/frontend/rust-lib/flowy-database/src/services/view_editor/editor_manager.rs +++ b/frontend/rust-lib/flowy-database/src/services/view_editor/editor_manager.rs @@ -88,14 +88,14 @@ impl DatabaseViewManager { } /// Insert/Delete the group's row if the corresponding cell data was changed. - pub async fn did_update_cell(&self, row_id: &str) { + pub async fn did_update_row(&self, old_row_rev: Option>, row_id: &str) { match self.delegate.get_row_rev(row_id).await { None => { tracing::warn!("Can not find the row in grid view"); } Some((_, row_rev)) => { for view_editor in self.view_editors.read().await.values() { - view_editor.did_update_view_cell(&row_rev).await; + view_editor.did_update_view_row(old_row_rev.clone(), &row_rev).await; } } } @@ -192,7 +192,7 @@ impl DatabaseViewManager { ) -> FlowyResult<()> { let mut row_changeset = RowChangeset::new(row_rev.id.clone()); let view_editor = self.get_default_view_editor().await?; - let group_changesets = view_editor + view_editor .move_view_group_row(&row_rev, &mut row_changeset, &to_group_id, to_row_id.clone()) .await; @@ -200,10 +200,6 @@ impl DatabaseViewManager { recv_row_changeset(row_changeset).await; } - for group_changeset in group_changesets { - view_editor.notify_did_update_group_rows(group_changeset).await; - } - Ok(()) } diff --git a/frontend/rust-lib/flowy-database/tests/grid/block_test/block_test.rs b/frontend/rust-lib/flowy-database/tests/grid/block_test/block_test.rs index 5b70df7220..bfccd5ba2d 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/block_test/block_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/block_test/block_test.rs @@ -1,4 +1,4 @@ -use crate::grid::block_test::script::GridRowTest; +use crate::grid::block_test::script::DatabaseRowTest; use crate::grid::block_test::script::RowScript::*; use grid_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset}; @@ -11,7 +11,7 @@ async fn grid_create_block() { CreateBlock { block: block_meta_rev }, AssertBlockCount(2), ]; - GridRowTest::new().await.run_scripts(scripts).await; + DatabaseRowTest::new().await.run_scripts(scripts).await; } #[tokio::test] @@ -37,5 +37,5 @@ async fn grid_update_block() { block: cloned_grid_block, }, ]; - GridRowTest::new().await.run_scripts(scripts).await; + DatabaseRowTest::new().await.run_scripts(scripts).await; } diff --git a/frontend/rust-lib/flowy-database/tests/grid/block_test/row_test.rs b/frontend/rust-lib/flowy-database/tests/grid/block_test/row_test.rs index e1d32b9f5e..ecf55e13af 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/block_test/row_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/block_test/row_test.rs @@ -1,13 +1,13 @@ use crate::grid::block_test::script::RowScript::*; -use crate::grid::block_test::script::{CreateRowScriptBuilder, GridRowTest}; -use crate::grid::grid_editor::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, TWITTER}; +use crate::grid::block_test::script::{CreateRowScriptBuilder, DatabaseRowTest}; +use crate::grid::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, TWITTER}; use flowy_database::entities::FieldType; use flowy_database::services::field::{SELECTION_IDS_SEPARATOR, UNCHECK}; use grid_model::RowChangeset; #[tokio::test] async fn grid_create_row_count_test() { - let mut test = GridRowTest::new().await; + let mut test = DatabaseRowTest::new().await; let scripts = vec![ AssertRowCount(6), CreateEmptyRow, @@ -22,7 +22,7 @@ async fn grid_create_row_count_test() { #[tokio::test] async fn grid_update_row() { - let mut test = GridRowTest::new().await; + let mut test = DatabaseRowTest::new().await; let row_rev = test.row_builder().build(); let changeset = RowChangeset { row_id: row_rev.id.clone(), @@ -41,7 +41,7 @@ async fn grid_update_row() { #[tokio::test] async fn grid_delete_row() { - let mut test = GridRowTest::new().await; + let mut test = DatabaseRowTest::new().await; let row_1 = test.row_builder().build(); let row_2 = test.row_builder().build(); let row_ids = vec![row_1.id.clone(), row_2.id.clone()]; @@ -67,7 +67,7 @@ async fn grid_delete_row() { #[tokio::test] async fn grid_row_add_cells_test() { - let mut test = GridRowTest::new().await; + let mut test = DatabaseRowTest::new().await; let mut builder = CreateRowScriptBuilder::new(&test); builder.insert(FieldType::RichText, "hello world", "hello world"); builder.insert(FieldType::DateTime, "1647251762", "2022/03/14"); @@ -85,7 +85,7 @@ async fn grid_row_add_cells_test() { #[tokio::test] async fn grid_row_insert_number_test() { - let mut test = GridRowTest::new().await; + let mut test = DatabaseRowTest::new().await; for (val, expected) in &[("1647251762", "2022/03/14"), ("2022/03/14", ""), ("", "")] { let mut builder = CreateRowScriptBuilder::new(&test); builder.insert(FieldType::DateTime, val, expected); @@ -96,7 +96,7 @@ async fn grid_row_insert_number_test() { #[tokio::test] async fn grid_row_insert_date_test() { - let mut test = GridRowTest::new().await; + let mut test = DatabaseRowTest::new().await; for (val, expected) in &[ ("18,443", "$18,443.00"), ("0", "$0.00"), @@ -112,7 +112,7 @@ async fn grid_row_insert_date_test() { } #[tokio::test] async fn grid_row_insert_single_select_test() { - let mut test = GridRowTest::new().await; + let mut test = DatabaseRowTest::new().await; let mut builder = CreateRowScriptBuilder::new(&test); builder.insert_single_select_cell(|mut options| options.pop().unwrap(), PAUSED); let scripts = builder.build(); @@ -121,7 +121,7 @@ async fn grid_row_insert_single_select_test() { #[tokio::test] async fn grid_row_insert_multi_select_test() { - let mut test = GridRowTest::new().await; + let mut test = DatabaseRowTest::new().await; let mut builder = CreateRowScriptBuilder::new(&test); builder.insert_multi_select_cell( |mut options| { diff --git a/frontend/rust-lib/flowy-database/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-database/tests/grid/block_test/script.rs index a76309574e..bd0606aa3a 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/block_test/script.rs @@ -1,6 +1,6 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow}; use crate::grid::block_test::util::GridRowTestBuilder; -use crate::grid::grid_editor::GridEditorTest; +use crate::grid::database_editor::DatabaseEditorTest; use flowy_database::entities::{CellPathParams, CreateRowParams, DatabaseViewLayout, FieldType, RowPB}; use flowy_database::services::field::*; use flowy_database::services::row::DatabaseBlockRow; @@ -48,13 +48,13 @@ pub enum RowScript { }, } -pub struct GridRowTest { - inner: GridEditorTest, +pub struct DatabaseRowTest { + inner: DatabaseEditorTest, } -impl GridRowTest { +impl DatabaseRowTest { pub async fn new() -> Self { - let editor_test = GridEditorTest::new_table().await; + let editor_test = DatabaseEditorTest::new_table().await; Self { inner: editor_test } } @@ -282,15 +282,15 @@ fn block_from_row_pbs(row_orders: Vec) -> Vec { map.into_values().collect::>() } -impl std::ops::Deref for GridRowTest { - type Target = GridEditorTest; +impl std::ops::Deref for DatabaseRowTest { + type Target = DatabaseEditorTest; fn deref(&self) -> &Self::Target { &self.inner } } -impl std::ops::DerefMut for GridRowTest { +impl std::ops::DerefMut for DatabaseRowTest { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } @@ -303,7 +303,7 @@ pub struct CreateRowScriptBuilder<'a> { } impl<'a> CreateRowScriptBuilder<'a> { - pub fn new(test: &'a GridRowTest) -> Self { + pub fn new(test: &'a DatabaseRowTest) -> Self { Self { builder: test.row_builder(), data_by_field_type: HashMap::new(), diff --git a/frontend/rust-lib/flowy-database/tests/grid/cell_test/script.rs b/frontend/rust-lib/flowy-database/tests/grid/cell_test/script.rs index 5fbee68c85..debcf1b1b2 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/cell_test/script.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/cell_test/script.rs @@ -1,17 +1,17 @@ -use crate::grid::grid_editor::GridEditorTest; +use crate::grid::database_editor::DatabaseEditorTest; use flowy_database::entities::CellChangesetPB; pub enum CellScript { UpdateCell { changeset: CellChangesetPB, is_err: bool }, } -pub struct GridCellTest { - inner: GridEditorTest, +pub struct DatabaseCellTest { + inner: DatabaseEditorTest, } -impl GridCellTest { +impl DatabaseCellTest { pub async fn new() -> Self { - let inner = GridEditorTest::new_table().await; + let inner = DatabaseEditorTest::new_table().await; Self { inner } } @@ -48,15 +48,15 @@ impl GridCellTest { } } -impl std::ops::Deref for GridCellTest { - type Target = GridEditorTest; +impl std::ops::Deref for DatabaseCellTest { + type Target = DatabaseEditorTest; fn deref(&self) -> &Self::Target { &self.inner } } -impl std::ops::DerefMut for GridCellTest { +impl std::ops::DerefMut for DatabaseCellTest { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } diff --git a/frontend/rust-lib/flowy-database/tests/grid/cell_test/test.rs b/frontend/rust-lib/flowy-database/tests/grid/cell_test/test.rs index bbca00f0f7..c00fb9e1f6 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/cell_test/test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/cell_test/test.rs @@ -1,5 +1,5 @@ use crate::grid::cell_test::script::CellScript::*; -use crate::grid::cell_test::script::GridCellTest; +use crate::grid::cell_test::script::DatabaseCellTest; use crate::grid::field_test::util::make_date_cell_string; use flowy_database::entities::{CellChangesetPB, FieldType}; use flowy_database::services::cell::ToCellChangesetString; @@ -8,7 +8,7 @@ use flowy_database::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOpti #[tokio::test] async fn grid_cell_update() { - let mut test = GridCellTest::new().await; + let mut test = DatabaseCellTest::new().await; let field_revs = &test.field_revs; let row_revs = &test.row_revs; let grid_blocks = &test.block_meta_revs; @@ -60,7 +60,7 @@ async fn grid_cell_update() { #[tokio::test] async fn text_cell_date_test() { - let test = GridCellTest::new().await; + let test = DatabaseCellTest::new().await; let text_field = test.get_first_field_rev(FieldType::RichText); let cells = test .editor @@ -84,7 +84,7 @@ async fn text_cell_date_test() { #[tokio::test] async fn url_cell_date_test() { - let test = GridCellTest::new().await; + let test = DatabaseCellTest::new().await; let url_field = test.get_first_field_rev(FieldType::URL); let cells = test .editor diff --git a/frontend/rust-lib/flowy-database/tests/grid/database_editor.rs b/frontend/rust-lib/flowy-database/tests/grid/database_editor.rs new file mode 100644 index 0000000000..f26dcc1e70 --- /dev/null +++ b/frontend/rust-lib/flowy-database/tests/grid/database_editor.rs @@ -0,0 +1,193 @@ +use crate::grid::mock_data::*; +use bytes::Bytes; +use flowy_database::entities::*; +use flowy_database::services::cell::ToCellChangesetString; +use flowy_database::services::field::SelectOptionPB; +use flowy_database::services::field::*; +use flowy_database::services::grid_editor::DatabaseRevisionEditor; +use flowy_test::helper::ViewTest; +use flowy_test::FlowySDKTest; +use grid_model::*; +use std::collections::HashMap; +use std::sync::Arc; +use strum::EnumCount; + +pub struct DatabaseEditorTest { + pub sdk: FlowySDKTest, + pub view_id: String, + pub editor: Arc, + pub field_revs: Vec>, + pub block_meta_revs: Vec>, + pub row_revs: Vec>, + pub field_count: usize, + pub row_by_row_id: HashMap, +} + +impl DatabaseEditorTest { + pub async fn new_table() -> Self { + Self::new(DatabaseViewLayout::Grid).await + } + + pub async fn new_board() -> Self { + Self::new(DatabaseViewLayout::Board).await + } + + pub async fn new(layout: DatabaseViewLayout) -> Self { + let sdk = FlowySDKTest::default(); + let _ = sdk.init_user().await; + let test = match layout { + DatabaseViewLayout::Grid => { + let build_context = make_test_grid(); + let view_data: Bytes = build_context.into(); + ViewTest::new_grid_view(&sdk, view_data.to_vec()).await + } + DatabaseViewLayout::Board => { + let build_context = make_test_board(); + let view_data: Bytes = build_context.into(); + ViewTest::new_board_view(&sdk, view_data.to_vec()).await + } + DatabaseViewLayout::Calendar => { + let build_context = make_test_calendar(); + let view_data: Bytes = build_context.into(); + ViewTest::new_calendar_view(&sdk, view_data.to_vec()).await + } + }; + + let editor = sdk.grid_manager.open_database(&test.view.id).await.unwrap(); + let field_revs = editor.get_field_revs(None).await.unwrap(); + let block_meta_revs = editor.get_block_meta_revs().await.unwrap(); + let row_pbs = editor.get_all_row_revs(&test.view.id).await.unwrap(); + assert_eq!(block_meta_revs.len(), 1); + + // It seems like you should add the field in the make_test_grid() function. + // Because we assert the initialize count of the fields is equal to FieldType::COUNT. + assert_eq!(field_revs.len(), FieldType::COUNT); + + let grid_id = test.view.id; + Self { + sdk, + view_id: grid_id, + editor, + field_revs, + block_meta_revs, + row_revs: row_pbs, + field_count: FieldType::COUNT, + row_by_row_id: HashMap::default(), + } + } + + pub async fn get_row_revs(&self) -> Vec> { + self.editor.get_all_row_revs(&self.view_id).await.unwrap() + } + + pub async fn grid_filters(&self) -> Vec { + self.editor.get_all_filters().await.unwrap() + } + + pub fn get_field_rev(&self, field_id: &str, field_type: FieldType) -> &Arc { + self.field_revs + .iter() + .filter(|field_rev| { + let t_field_type: FieldType = field_rev.ty.into(); + field_rev.id == field_id && t_field_type == field_type + }) + .collect::>() + .pop() + .unwrap() + } + + /// returns the first `FieldRevision` in the build-in test grid. + /// Not support duplicate `FieldType` in test grid yet. + pub fn get_first_field_rev(&self, field_type: FieldType) -> &Arc { + self.field_revs + .iter() + .filter(|field_rev| { + let t_field_type: FieldType = field_rev.ty.into(); + t_field_type == field_type + }) + .collect::>() + .pop() + .unwrap() + } + + pub fn get_multi_select_type_option(&self, field_id: &str) -> Vec { + let field_type = FieldType::MultiSelect; + let field_rev = self.get_field_rev(field_id, field_type.clone()); + let type_option = field_rev + .get_type_option::(field_type.into()) + .unwrap(); + type_option.options + } + + pub fn get_single_select_type_option(&self, field_id: &str) -> SingleSelectTypeOptionPB { + let field_type = FieldType::SingleSelect; + let field_rev = self.get_field_rev(field_id, field_type.clone()); + field_rev + .get_type_option::(field_type.into()) + .unwrap() + } + + #[allow(dead_code)] + pub fn get_checklist_type_option(&self, field_id: &str) -> ChecklistTypeOptionPB { + let field_type = FieldType::Checklist; + let field_rev = self.get_field_rev(field_id, field_type.clone()); + field_rev + .get_type_option::(field_type.into()) + .unwrap() + } + + #[allow(dead_code)] + pub fn get_checkbox_type_option(&self, field_id: &str) -> CheckboxTypeOptionPB { + let field_type = FieldType::Checkbox; + let field_rev = self.get_field_rev(field_id, field_type.clone()); + field_rev + .get_type_option::(field_type.into()) + .unwrap() + } + + pub fn block_id(&self) -> &str { + &self.block_meta_revs.last().unwrap().block_id + } + + pub async fn update_cell(&mut self, field_id: &str, row_id: String, cell_changeset: T) { + let field_rev = self + .field_revs + .iter() + .find(|field_rev| field_rev.id == field_id) + .unwrap(); + + self.editor + .update_cell_with_changeset(&row_id, &field_rev.id, cell_changeset) + .await + .unwrap(); + } + + pub(crate) async fn update_text_cell(&mut self, row_id: String, content: &str) { + let field_rev = self + .field_revs + .iter() + .find(|field_rev| { + let field_type: FieldType = field_rev.ty.into(); + field_type == FieldType::RichText + }) + .unwrap() + .clone(); + + self.update_cell(&field_rev.id, row_id, content.to_string()).await; + } + + pub(crate) async fn update_single_select_cell(&mut self, row_id: String, option_id: &str) { + let field_rev = self + .field_revs + .iter() + .find(|field_rev| { + let field_type: FieldType = field_rev.ty.into(); + field_type == FieldType::SingleSelect + }) + .unwrap() + .clone(); + + let cell_changeset = SelectOptionCellChangeset::from_insert_option_id(option_id); + self.update_cell(&field_rev.id, row_id, cell_changeset).await; + } +} diff --git a/frontend/rust-lib/flowy-database/tests/grid/field_test/script.rs b/frontend/rust-lib/flowy-database/tests/grid/field_test/script.rs index 690d6895de..5115ab50f1 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/field_test/script.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/field_test/script.rs @@ -1,4 +1,4 @@ -use crate::grid::grid_editor::GridEditorTest; +use crate::grid::database_editor::DatabaseEditorTest; use flowy_database::entities::{CreateFieldParams, FieldChangesetParams, FieldType}; use flowy_database::services::cell::{stringify_cell_data, TypeCellData}; use grid_model::FieldRevision; @@ -38,13 +38,13 @@ pub enum FieldScript { }, } -pub struct GridFieldTest { - inner: GridEditorTest, +pub struct DatabaseFieldTest { + inner: DatabaseEditorTest, } -impl GridFieldTest { +impl DatabaseFieldTest { pub async fn new() -> Self { - let editor_test = GridEditorTest::new_table().await; + let editor_test = DatabaseEditorTest::new_table().await; Self { inner: editor_test } } @@ -144,15 +144,15 @@ impl GridFieldTest { } } -impl std::ops::Deref for GridFieldTest { - type Target = GridEditorTest; +impl std::ops::Deref for DatabaseFieldTest { + type Target = DatabaseEditorTest; fn deref(&self) -> &Self::Target { &self.inner } } -impl std::ops::DerefMut for GridFieldTest { +impl std::ops::DerefMut for DatabaseFieldTest { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } diff --git a/frontend/rust-lib/flowy-database/tests/grid/field_test/test.rs b/frontend/rust-lib/flowy-database/tests/grid/field_test/test.rs index 825c9f92ed..d3d1109135 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/field_test/test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/field_test/test.rs @@ -1,5 +1,5 @@ +use crate::grid::field_test::script::DatabaseFieldTest; use crate::grid::field_test::script::FieldScript::*; -use crate::grid::field_test::script::GridFieldTest; use crate::grid::field_test::util::*; use bytes::Bytes; use flowy_database::entities::{FieldChangesetParams, FieldType}; @@ -8,7 +8,7 @@ use flowy_database::services::field::{gen_option_id, SingleSelectTypeOptionPB, C #[tokio::test] async fn grid_create_field() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let (params, field_rev) = create_text_field(&test.view_id()); let scripts = vec![ @@ -33,7 +33,7 @@ async fn grid_create_field() { #[tokio::test] async fn grid_create_duplicate_field() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let (params, _) = create_text_field(&test.view_id()); let field_count = test.field_count(); let expected_field_count = field_count + 1; @@ -46,7 +46,7 @@ async fn grid_create_duplicate_field() { #[tokio::test] async fn grid_update_field_with_empty_change() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let (params, _) = create_single_select_field(&test.view_id()); let create_field_index = test.field_count(); let scripts = vec![CreateField { params }]; @@ -71,7 +71,7 @@ async fn grid_update_field_with_empty_change() { #[tokio::test] async fn grid_update_field() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let (params, _) = create_single_select_field(&test.view_id()); let scripts = vec![CreateField { params }]; let create_field_index = test.field_count(); @@ -107,7 +107,7 @@ async fn grid_update_field() { #[tokio::test] async fn grid_delete_field() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let original_field_count = test.field_count(); let (params, _) = create_text_field(&test.view_id()); let scripts = vec![CreateField { params }]; @@ -125,7 +125,7 @@ async fn grid_delete_field() { #[tokio::test] async fn grid_switch_from_select_option_to_checkbox_test() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let field_rev = test.get_first_field_rev(FieldType::SingleSelect); // Update the type option data of single select option @@ -160,7 +160,7 @@ async fn grid_switch_from_select_option_to_checkbox_test() { #[tokio::test] async fn grid_switch_from_checkbox_to_select_option_test() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let field_rev = test.get_first_field_rev(FieldType::Checkbox).clone(); let scripts = vec![ // switch to single-select field type @@ -203,7 +203,7 @@ async fn grid_switch_from_checkbox_to_select_option_test() { // option1, option2 -> "option1.name, option2.name" #[tokio::test] async fn grid_switch_from_multi_select_to_text_test() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let field_rev = test.get_first_field_rev(FieldType::MultiSelect).clone(); let multi_select_type_option = test.get_multi_select_type_option(&field_rev.id); @@ -235,7 +235,7 @@ async fn grid_switch_from_multi_select_to_text_test() { // unchecked -> "" #[tokio::test] async fn grid_switch_from_checkbox_to_text_test() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let field_rev = test.get_first_field_rev(FieldType::Checkbox); let scripts = vec![ @@ -265,7 +265,7 @@ async fn grid_switch_from_checkbox_to_text_test() { // "" -> unchecked #[tokio::test] async fn grid_switch_from_text_to_checkbox_test() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let field_rev = test.get_first_field_rev(FieldType::RichText).clone(); let scripts = vec![ @@ -288,7 +288,7 @@ async fn grid_switch_from_text_to_checkbox_test() { // 1647251762 -> Mar 14,2022 (This string will be different base on current data setting) #[tokio::test] async fn grid_switch_from_date_to_text_test() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let field_rev = test.get_first_field_rev(FieldType::DateTime).clone(); let scripts = vec![ SwitchToField { @@ -316,7 +316,7 @@ async fn grid_switch_from_date_to_text_test() { // $1 -> "$1"(This string will be different base on current data setting) #[tokio::test] async fn grid_switch_from_number_to_text_test() { - let mut test = GridFieldTest::new().await; + let mut test = DatabaseFieldTest::new().await; let field_rev = test.get_first_field_rev(FieldType::Number).clone(); let scripts = vec![ diff --git a/frontend/rust-lib/flowy-database/tests/grid/filter_test/checkbox_filter_test.rs b/frontend/rust-lib/flowy-database/tests/grid/filter_test/checkbox_filter_test.rs index 7a13af169c..646b26ba8d 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/filter_test/checkbox_filter_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/filter_test/checkbox_filter_test.rs @@ -1,10 +1,10 @@ use crate::grid::filter_test::script::FilterScript::*; -use crate::grid::filter_test::script::{FilterRowChanged, GridFilterTest}; +use crate::grid::filter_test::script::{DatabaseFilterTest, FilterRowChanged}; use flowy_database::entities::CheckboxFilterConditionPB; #[tokio::test] async fn grid_filter_checkbox_is_check_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); // The initial number of unchecked is 3 // The initial number of checked is 2 @@ -20,7 +20,7 @@ async fn grid_filter_checkbox_is_check_test() { #[tokio::test] async fn grid_filter_checkbox_is_uncheck_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let expected = 3; let row_count = test.row_revs.len(); let scripts = vec![ diff --git a/frontend/rust-lib/flowy-database/tests/grid/filter_test/checklist_filter_test.rs b/frontend/rust-lib/flowy-database/tests/grid/filter_test/checklist_filter_test.rs index a94adcf804..5340a7c27c 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/filter_test/checklist_filter_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/filter_test/checklist_filter_test.rs @@ -1,10 +1,10 @@ use crate::grid::filter_test::script::FilterScript::*; -use crate::grid::filter_test::script::{FilterRowChanged, GridFilterTest}; +use crate::grid::filter_test::script::{DatabaseFilterTest, FilterRowChanged}; use flowy_database::entities::ChecklistFilterConditionPB; #[tokio::test] async fn grid_filter_checklist_is_incomplete_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let expected = 5; let row_count = test.row_revs.len(); let scripts = vec![ @@ -22,7 +22,7 @@ async fn grid_filter_checklist_is_incomplete_test() { #[tokio::test] async fn grid_filter_checklist_is_complete_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let expected = 1; let row_count = test.row_revs.len(); let scripts = vec![ diff --git a/frontend/rust-lib/flowy-database/tests/grid/filter_test/date_filter_test.rs b/frontend/rust-lib/flowy-database/tests/grid/filter_test/date_filter_test.rs index abe2a8c5ec..47a459967b 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/filter_test/date_filter_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/filter_test/date_filter_test.rs @@ -1,10 +1,10 @@ use crate::grid::filter_test::script::FilterScript::*; -use crate::grid::filter_test::script::{FilterRowChanged, GridFilterTest}; +use crate::grid::filter_test::script::{DatabaseFilterTest, FilterRowChanged}; use flowy_database::entities::DateFilterConditionPB; #[tokio::test] async fn grid_filter_date_is_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 3; let scripts = vec![ @@ -25,7 +25,7 @@ async fn grid_filter_date_is_test() { #[tokio::test] async fn grid_filter_date_after_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 3; let scripts = vec![ @@ -46,7 +46,7 @@ async fn grid_filter_date_after_test() { #[tokio::test] async fn grid_filter_date_on_or_after_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 3; let scripts = vec![ @@ -67,7 +67,7 @@ async fn grid_filter_date_on_or_after_test() { #[tokio::test] async fn grid_filter_date_on_or_before_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 4; let scripts = vec![ @@ -88,7 +88,7 @@ async fn grid_filter_date_on_or_before_test() { #[tokio::test] async fn grid_filter_date_within_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 5; let scripts = vec![ diff --git a/frontend/rust-lib/flowy-database/tests/grid/filter_test/number_filter_test.rs b/frontend/rust-lib/flowy-database/tests/grid/filter_test/number_filter_test.rs index 577630566e..ee1b2b7b53 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/filter_test/number_filter_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/filter_test/number_filter_test.rs @@ -1,10 +1,10 @@ use crate::grid::filter_test::script::FilterScript::*; -use crate::grid::filter_test::script::{FilterRowChanged, GridFilterTest}; +use crate::grid::filter_test::script::{DatabaseFilterTest, FilterRowChanged}; use flowy_database::entities::NumberFilterConditionPB; #[tokio::test] async fn grid_filter_number_is_equal_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 1; let scripts = vec![ @@ -23,7 +23,7 @@ async fn grid_filter_number_is_equal_test() { #[tokio::test] async fn grid_filter_number_is_less_than_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 2; let scripts = vec![ @@ -43,7 +43,7 @@ async fn grid_filter_number_is_less_than_test() { #[tokio::test] #[should_panic] async fn grid_filter_number_is_less_than_test2() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 2; let scripts = vec![ @@ -62,7 +62,7 @@ async fn grid_filter_number_is_less_than_test2() { #[tokio::test] async fn grid_filter_number_is_less_than_or_equal_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 3; let scripts = vec![ @@ -81,7 +81,7 @@ async fn grid_filter_number_is_less_than_or_equal_test() { #[tokio::test] async fn grid_filter_number_is_empty_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 1; let scripts = vec![ @@ -100,7 +100,7 @@ async fn grid_filter_number_is_empty_test() { #[tokio::test] async fn grid_filter_number_is_not_empty_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_count = test.row_revs.len(); let expected = 5; let scripts = vec![ diff --git a/frontend/rust-lib/flowy-database/tests/grid/filter_test/script.rs b/frontend/rust-lib/flowy-database/tests/grid/filter_test/script.rs index a27f389e8b..623cb03ea0 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/filter_test/script.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/filter_test/script.rs @@ -15,7 +15,7 @@ use flowy_sqlite::schema::view_table::dsl::view_table; use flowy_database::services::cell::insert_select_option_cell; use flowy_database::services::filter::FilterType; use flowy_database::services::view_editor::GridViewChanged; -use crate::grid::grid_editor::GridEditorTest; +use crate::grid::database_editor::DatabaseEditorTest; pub struct FilterRowChanged { pub(crate) showing_num_of_rows: usize, @@ -99,14 +99,14 @@ pub enum FilterScript { Wait { millisecond: u64 } } -pub struct GridFilterTest { - inner: GridEditorTest, +pub struct DatabaseFilterTest { + inner: DatabaseEditorTest, recv: Option>, } -impl GridFilterTest { +impl DatabaseFilterTest { pub async fn new() -> Self { - let editor_test = GridEditorTest::new_table().await; + let editor_test = DatabaseEditorTest::new_table().await; Self { inner: editor_test, recv: None, @@ -298,15 +298,15 @@ impl GridFilterTest { } -impl std::ops::Deref for GridFilterTest { - type Target = GridEditorTest; +impl std::ops::Deref for DatabaseFilterTest { + type Target = DatabaseEditorTest; fn deref(&self) -> &Self::Target { &self.inner } } -impl std::ops::DerefMut for GridFilterTest { +impl std::ops::DerefMut for DatabaseFilterTest { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } diff --git a/frontend/rust-lib/flowy-database/tests/grid/filter_test/select_option_filter_test.rs b/frontend/rust-lib/flowy-database/tests/grid/filter_test/select_option_filter_test.rs index 05d149575b..da1f6e4c16 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/filter_test/select_option_filter_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/filter_test/select_option_filter_test.rs @@ -1,10 +1,10 @@ use crate::grid::filter_test::script::FilterScript::*; -use crate::grid::filter_test::script::{FilterRowChanged, GridFilterTest}; +use crate::grid::filter_test::script::{DatabaseFilterTest, FilterRowChanged}; use flowy_database::entities::{FieldType, SelectOptionConditionPB}; #[tokio::test] async fn grid_filter_multi_select_is_empty_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let scripts = vec![ CreateMultiSelectFilter { condition: SelectOptionConditionPB::OptionIsEmpty, @@ -17,7 +17,7 @@ async fn grid_filter_multi_select_is_empty_test() { #[tokio::test] async fn grid_filter_multi_select_is_not_empty_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let scripts = vec![ CreateMultiSelectFilter { condition: SelectOptionConditionPB::OptionIsNotEmpty, @@ -30,7 +30,7 @@ async fn grid_filter_multi_select_is_not_empty_test() { #[tokio::test] async fn grid_filter_multi_select_is_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let field_rev = test.get_first_field_rev(FieldType::MultiSelect); let mut options = test.get_multi_select_type_option(&field_rev.id); let scripts = vec![ @@ -45,7 +45,7 @@ async fn grid_filter_multi_select_is_test() { #[tokio::test] async fn grid_filter_multi_select_is_test2() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let field_rev = test.get_first_field_rev(FieldType::MultiSelect); let mut options = test.get_multi_select_type_option(&field_rev.id); let scripts = vec![ @@ -60,7 +60,7 @@ async fn grid_filter_multi_select_is_test2() { #[tokio::test] async fn grid_filter_single_select_is_empty_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let expected = 2; let row_count = test.row_revs.len(); let scripts = vec![ @@ -79,7 +79,7 @@ async fn grid_filter_single_select_is_empty_test() { #[tokio::test] async fn grid_filter_single_select_is_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let field_rev = test.get_first_field_rev(FieldType::SingleSelect); let mut options = test.get_single_select_type_option(&field_rev.id).options; let expected = 2; @@ -100,7 +100,7 @@ async fn grid_filter_single_select_is_test() { #[tokio::test] async fn grid_filter_single_select_is_test2() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let field_rev = test.get_first_field_rev(FieldType::SingleSelect); let row_revs = test.get_row_revs().await; let mut options = test.get_single_select_type_option(&field_rev.id).options; diff --git a/frontend/rust-lib/flowy-database/tests/grid/filter_test/text_filter_test.rs b/frontend/rust-lib/flowy-database/tests/grid/filter_test/text_filter_test.rs index c6f917a012..2c34eeeef2 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/filter_test/text_filter_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/filter_test/text_filter_test.rs @@ -5,7 +5,7 @@ use flowy_database::services::filter::FilterType; #[tokio::test] async fn grid_filter_text_is_empty_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let scripts = vec![ CreateTextFilter { condition: TextFilterConditionPB::TextIsEmpty, @@ -22,7 +22,7 @@ async fn grid_filter_text_is_empty_test() { #[tokio::test] async fn grid_filter_text_is_not_empty_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; // Only one row's text of the initial rows is "" let scripts = vec![ CreateTextFilter { @@ -55,7 +55,7 @@ async fn grid_filter_text_is_not_empty_test() { #[tokio::test] async fn grid_filter_is_text_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; // Only one row's text of the initial rows is "A" let scripts = vec![CreateTextFilter { condition: TextFilterConditionPB::Is, @@ -70,7 +70,7 @@ async fn grid_filter_is_text_test() { #[tokio::test] async fn grid_filter_contain_text_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let scripts = vec![CreateTextFilter { condition: TextFilterConditionPB::Contains, content: "A".to_string(), @@ -84,7 +84,7 @@ async fn grid_filter_contain_text_test() { #[tokio::test] async fn grid_filter_contain_text_test2() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_revs = test.row_revs.clone(); let scripts = vec![ @@ -110,7 +110,7 @@ async fn grid_filter_contain_text_test2() { #[tokio::test] async fn grid_filter_does_not_contain_text_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; // None of the initial rows contains the text "AB" let scripts = vec![CreateTextFilter { condition: TextFilterConditionPB::DoesNotContain, @@ -125,7 +125,7 @@ async fn grid_filter_does_not_contain_text_test() { #[tokio::test] async fn grid_filter_start_with_text_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let scripts = vec![CreateTextFilter { condition: TextFilterConditionPB::StartsWith, content: "A".to_string(), @@ -139,7 +139,7 @@ async fn grid_filter_start_with_text_test() { #[tokio::test] async fn grid_filter_ends_with_text_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let scripts = vec![ CreateTextFilter { condition: TextFilterConditionPB::EndsWith, @@ -153,7 +153,7 @@ async fn grid_filter_ends_with_text_test() { #[tokio::test] async fn grid_update_text_filter_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let scripts = vec![ CreateTextFilter { condition: TextFilterConditionPB::EndsWith, @@ -187,7 +187,7 @@ async fn grid_update_text_filter_test() { #[tokio::test] async fn grid_filter_delete_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let field_rev = test.get_first_field_rev(FieldType::RichText).clone(); let text_filter = TextFilterPB { condition: TextFilterConditionPB::TextIsEmpty, @@ -216,7 +216,7 @@ async fn grid_filter_delete_test() { #[tokio::test] async fn grid_filter_update_empty_text_cell_test() { - let mut test = GridFilterTest::new().await; + let mut test = DatabaseFilterTest::new().await; let row_revs = test.row_revs.clone(); let scripts = vec![ CreateTextFilter { diff --git a/frontend/rust-lib/flowy-database/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-database/tests/grid/grid_editor.rs deleted file mode 100644 index cf833bcbb2..0000000000 --- a/frontend/rust-lib/flowy-database/tests/grid/grid_editor.rs +++ /dev/null @@ -1,573 +0,0 @@ -#![allow(clippy::all)] -#![allow(dead_code)] -#![allow(unused_imports)] -use crate::grid::block_test::util::GridRowTestBuilder; -use bytes::Bytes; -use flowy_client_sync::client_database::DatabaseBuilder; -use flowy_database::entities::*; -use flowy_database::services::cell::ToCellChangesetString; -use flowy_database::services::field::SelectOptionPB; -use flowy_database::services::field::*; -use flowy_database::services::grid_editor::{DatabaseRevisionEditor, GridRevisionSerde}; -use flowy_database::services::row::{CreateRowRevisionPayload, RowRevisionBuilder}; -use flowy_database::services::setting::GridSettingChangesetBuilder; -use flowy_error::FlowyResult; -use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS; -use flowy_test::helper::ViewTest; -use flowy_test::FlowySDKTest; -use grid_model::*; -use std::collections::HashMap; -use std::sync::Arc; -use std::time::Duration; -use strum::EnumCount; -use strum::IntoEnumIterator; -use tokio::time::sleep; - -pub struct GridEditorTest { - pub sdk: FlowySDKTest, - pub view_id: String, - pub editor: Arc, - pub field_revs: Vec>, - pub block_meta_revs: Vec>, - pub row_revs: Vec>, - pub field_count: usize, - pub row_by_row_id: HashMap, -} - -impl GridEditorTest { - pub async fn new_table() -> Self { - Self::new(DatabaseViewLayout::Grid).await - } - - pub async fn new_board() -> Self { - Self::new(DatabaseViewLayout::Board).await - } - - pub async fn new(layout: DatabaseViewLayout) -> Self { - let sdk = FlowySDKTest::default(); - let _ = sdk.init_user().await; - let test = match layout { - DatabaseViewLayout::Grid => { - let build_context = make_test_grid(); - let view_data: Bytes = build_context.into(); - ViewTest::new_grid_view(&sdk, view_data.to_vec()).await - } - DatabaseViewLayout::Board => { - let build_context = make_test_board(); - let view_data: Bytes = build_context.into(); - ViewTest::new_board_view(&sdk, view_data.to_vec()).await - } - DatabaseViewLayout::Calendar => { - let build_context = make_test_calendar(); - let view_data: Bytes = build_context.into(); - ViewTest::new_calendar_view(&sdk, view_data.to_vec()).await - } - }; - - let editor = sdk.grid_manager.open_database(&test.view.id).await.unwrap(); - let field_revs = editor.get_field_revs(None).await.unwrap(); - let block_meta_revs = editor.get_block_meta_revs().await.unwrap(); - let row_pbs = editor.get_all_row_revs(&test.view.id).await.unwrap(); - assert_eq!(block_meta_revs.len(), 1); - - // It seems like you should add the field in the make_test_grid() function. - // Because we assert the initialize count of the fields is equal to FieldType::COUNT. - assert_eq!(field_revs.len(), FieldType::COUNT); - - let grid_id = test.view.id; - Self { - sdk, - view_id: grid_id, - editor, - field_revs, - block_meta_revs, - row_revs: row_pbs, - field_count: FieldType::COUNT, - row_by_row_id: HashMap::default(), - } - } - - pub async fn get_row_revs(&self) -> Vec> { - self.editor.get_all_row_revs(&self.view_id).await.unwrap() - } - - pub async fn grid_filters(&self) -> Vec { - self.editor.get_all_filters().await.unwrap() - } - - pub fn get_field_rev(&self, field_id: &str, field_type: FieldType) -> &Arc { - self.field_revs - .iter() - .filter(|field_rev| { - let t_field_type: FieldType = field_rev.ty.into(); - field_rev.id == field_id && t_field_type == field_type - }) - .collect::>() - .pop() - .unwrap() - } - - /// returns the first `FieldRevision` in the build-in test grid. - /// Not support duplicate `FieldType` in test grid yet. - pub fn get_first_field_rev(&self, field_type: FieldType) -> &Arc { - self.field_revs - .iter() - .filter(|field_rev| { - let t_field_type: FieldType = field_rev.ty.into(); - t_field_type == field_type - }) - .collect::>() - .pop() - .unwrap() - } - - pub fn get_multi_select_type_option(&self, field_id: &str) -> Vec { - let field_type = FieldType::MultiSelect; - let field_rev = self.get_field_rev(field_id, field_type.clone()); - let type_option = field_rev - .get_type_option::(field_type.into()) - .unwrap(); - type_option.options - } - - pub fn get_single_select_type_option(&self, field_id: &str) -> SingleSelectTypeOptionPB { - let field_type = FieldType::SingleSelect; - let field_rev = self.get_field_rev(field_id, field_type.clone()); - let type_option = field_rev - .get_type_option::(field_type.into()) - .unwrap(); - type_option - } - - pub fn get_checklist_type_option(&self, field_id: &str) -> ChecklistTypeOptionPB { - let field_type = FieldType::Checklist; - let field_rev = self.get_field_rev(field_id, field_type.clone()); - let type_option = field_rev - .get_type_option::(field_type.into()) - .unwrap(); - type_option - } - pub fn get_checkbox_type_option(&self, field_id: &str) -> CheckboxTypeOptionPB { - let field_type = FieldType::Checkbox; - let field_rev = self.get_field_rev(field_id, field_type.clone()); - let type_option = field_rev - .get_type_option::(field_type.into()) - .unwrap(); - type_option - } - - pub fn block_id(&self) -> &str { - &self.block_meta_revs.last().unwrap().block_id - } - - pub async fn update_cell(&mut self, field_id: &str, row_id: String, cell_changeset: T) { - let field_rev = self - .field_revs - .iter() - .find(|field_rev| field_rev.id == field_id) - .unwrap(); - - self.editor - .update_cell_with_changeset(&row_id, &field_rev.id, cell_changeset) - .await - .unwrap(); - } - - pub(crate) async fn update_text_cell(&mut self, row_id: String, content: &str) { - let field_rev = self - .field_revs - .iter() - .find(|field_rev| { - let field_type: FieldType = field_rev.ty.into(); - field_type == FieldType::RichText - }) - .unwrap() - .clone(); - - self.update_cell(&field_rev.id, row_id, content.to_string()).await; - } - - pub(crate) async fn update_single_select_cell(&mut self, row_id: String, option_id: &str) { - let field_rev = self - .field_revs - .iter() - .find(|field_rev| { - let field_type: FieldType = field_rev.ty.into(); - field_type == FieldType::SingleSelect - }) - .unwrap() - .clone(); - - let cell_changeset = SelectOptionCellChangeset::from_insert_option_id(&option_id); - self.update_cell(&field_rev.id, row_id, cell_changeset).await; - } -} - -pub const GOOGLE: &str = "Google"; -pub const FACEBOOK: &str = "Facebook"; -pub const TWITTER: &str = "Twitter"; - -pub const COMPLETED: &str = "Completed"; -pub const PLANNED: &str = "Planned"; -pub const PAUSED: &str = "Paused"; - -pub const FIRST_THING: &str = "Wake up at 6:00 am"; -pub const SECOND_THING: &str = "Get some coffee"; -pub const THIRD_THING: &str = "Start working"; - -/// The build-in test data for grid. Currently, there are five rows in this grid, if you want to add -/// more rows or alter the data in this grid. Some tests may fail. So you need to fix the failed tests. -fn make_test_grid() -> BuildDatabaseContext { - let mut grid_builder = DatabaseBuilder::new(); - // Iterate through the FieldType to create the corresponding Field. - for field_type in FieldType::iter() { - let field_type: FieldType = field_type; - - // The - match field_type { - FieldType::RichText => { - let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) - .name("Name") - .visibility(true) - .primary(true) - .build(); - grid_builder.add_field(text_field); - } - FieldType::Number => { - // Number - let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); - let number_field = FieldBuilder::new(number).name("Price").visibility(true).build(); - grid_builder.add_field(number_field); - } - FieldType::DateTime => { - // Date - let date = DateTypeOptionBuilder::default() - .date_format(DateFormat::US) - .time_format(TimeFormat::TwentyFourHour); - let date_field = FieldBuilder::new(date).name("Time").visibility(true).build(); - grid_builder.add_field(date_field); - } - FieldType::SingleSelect => { - // Single Select - let single_select = SingleSelectTypeOptionBuilder::default() - .add_option(SelectOptionPB::new(COMPLETED)) - .add_option(SelectOptionPB::new(PLANNED)) - .add_option(SelectOptionPB::new(PAUSED)); - let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build(); - grid_builder.add_field(single_select_field); - } - FieldType::MultiSelect => { - // MultiSelect - let multi_select = MultiSelectTypeOptionBuilder::default() - .add_option(SelectOptionPB::new(GOOGLE)) - .add_option(SelectOptionPB::new(FACEBOOK)) - .add_option(SelectOptionPB::new(TWITTER)); - let multi_select_field = FieldBuilder::new(multi_select) - .name("Platform") - .visibility(true) - .build(); - grid_builder.add_field(multi_select_field); - } - FieldType::Checkbox => { - // Checkbox - let checkbox = CheckboxTypeOptionBuilder::default(); - let checkbox_field = FieldBuilder::new(checkbox).name("is urgent").visibility(true).build(); - grid_builder.add_field(checkbox_field); - } - FieldType::URL => { - // URL - let url = URLTypeOptionBuilder::default(); - let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); - grid_builder.add_field(url_field); - } - FieldType::Checklist => { - let checklist = ChecklistTypeOptionBuilder::default() - .add_option(SelectOptionPB::new(FIRST_THING)) - .add_option(SelectOptionPB::new(SECOND_THING)) - .add_option(SelectOptionPB::new(THIRD_THING)); - let checklist_field = FieldBuilder::new(checklist).name("TODO").visibility(true).build(); - grid_builder.add_field(checklist_field); - } - } - } - - for i in 0..6 { - let block_id = grid_builder.block_id().to_owned(); - let field_revs = grid_builder.field_revs(); - let mut row_builder = GridRowTestBuilder::new(&block_id, field_revs); - match i { - 0 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("A"), - FieldType::Number => row_builder.insert_number_cell("1"), - FieldType::DateTime => row_builder.insert_date_cell("1647251762"), - FieldType::MultiSelect => row_builder - .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]), - FieldType::Checklist => row_builder.insert_checklist_cell(|options| options), - FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), - FieldType::URL => row_builder.insert_url_cell("AppFlowy website - https://www.appflowy.io"), - _ => "".to_owned(), - }; - } - } - 1 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell(""), - FieldType::Number => row_builder.insert_number_cell("2"), - FieldType::DateTime => row_builder.insert_date_cell("1647251762"), - FieldType::MultiSelect => row_builder - .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(1)]), - FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), - _ => "".to_owned(), - }; - } - } - 2 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("C"), - FieldType::Number => row_builder.insert_number_cell("3"), - FieldType::DateTime => row_builder.insert_date_cell("1647251762"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(0)) - } - FieldType::MultiSelect => { - row_builder.insert_multi_select_cell(|mut options| vec![options.remove(1)]) - } - FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), - _ => "".to_owned(), - }; - } - } - 3 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("DA"), - FieldType::Number => row_builder.insert_number_cell("4"), - FieldType::DateTime => row_builder.insert_date_cell("1668704685"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(0)) - } - FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), - _ => "".to_owned(), - }; - } - } - 4 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("AE"), - FieldType::Number => row_builder.insert_number_cell(""), - FieldType::DateTime => row_builder.insert_date_cell("1668359085"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(1)) - } - - FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), - _ => "".to_owned(), - }; - } - } - 5 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("AE"), - FieldType::Number => row_builder.insert_number_cell("5"), - FieldType::DateTime => row_builder.insert_date_cell("1671938394"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(1)) - } - FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), - _ => "".to_owned(), - }; - } - } - _ => {} - } - - let row_rev = row_builder.build(); - grid_builder.add_row(row_rev); - } - grid_builder.build() -} - -// Kanban board unit test mock data -fn make_test_board() -> BuildDatabaseContext { - let mut grid_builder = DatabaseBuilder::new(); - // Iterate through the FieldType to create the corresponding Field. - for field_type in FieldType::iter() { - let field_type: FieldType = field_type; - - // The - match field_type { - FieldType::RichText => { - let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) - .name("Name") - .visibility(true) - .primary(true) - .build(); - grid_builder.add_field(text_field); - } - FieldType::Number => { - // Number - let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); - let number_field = FieldBuilder::new(number).name("Price").visibility(true).build(); - grid_builder.add_field(number_field); - } - FieldType::DateTime => { - // Date - let date = DateTypeOptionBuilder::default() - .date_format(DateFormat::US) - .time_format(TimeFormat::TwentyFourHour); - let date_field = FieldBuilder::new(date).name("Time").visibility(true).build(); - grid_builder.add_field(date_field); - } - FieldType::SingleSelect => { - // Single Select - let single_select = SingleSelectTypeOptionBuilder::default() - .add_option(SelectOptionPB::new(COMPLETED)) - .add_option(SelectOptionPB::new(PLANNED)) - .add_option(SelectOptionPB::new(PAUSED)); - let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build(); - grid_builder.add_field(single_select_field); - } - FieldType::MultiSelect => { - // MultiSelect - let multi_select = MultiSelectTypeOptionBuilder::default() - .add_option(SelectOptionPB::new(GOOGLE)) - .add_option(SelectOptionPB::new(FACEBOOK)) - .add_option(SelectOptionPB::new(TWITTER)); - let multi_select_field = FieldBuilder::new(multi_select) - .name("Platform") - .visibility(true) - .build(); - grid_builder.add_field(multi_select_field); - } - FieldType::Checkbox => { - // Checkbox - let checkbox = CheckboxTypeOptionBuilder::default(); - let checkbox_field = FieldBuilder::new(checkbox).name("is urgent").visibility(true).build(); - grid_builder.add_field(checkbox_field); - } - FieldType::URL => { - // URL - let url = URLTypeOptionBuilder::default(); - let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); - grid_builder.add_field(url_field); - } - FieldType::Checklist => { - let checklist = ChecklistTypeOptionBuilder::default() - .add_option(SelectOptionPB::new(FIRST_THING)) - .add_option(SelectOptionPB::new(SECOND_THING)) - .add_option(SelectOptionPB::new(THIRD_THING)); - let checklist_field = FieldBuilder::new(checklist).name("TODO").visibility(true).build(); - grid_builder.add_field(checklist_field); - } - } - } - - // We have many assumptions base on the number of the rows, so do not change the number of the loop. - for i in 0..5 { - let block_id = grid_builder.block_id().to_owned(); - let field_revs = grid_builder.field_revs(); - let mut row_builder = GridRowTestBuilder::new(&block_id, field_revs); - match i { - 0 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("A"), - FieldType::Number => row_builder.insert_number_cell("1"), - // 1647251762 => Mar 14,2022 - FieldType::DateTime => row_builder.insert_date_cell("1647251762"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(0)) - } - FieldType::MultiSelect => row_builder - .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]), - FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), - FieldType::URL => row_builder.insert_url_cell("https://appflowy.io"), - _ => "".to_owned(), - }; - } - } - 1 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("B"), - FieldType::Number => row_builder.insert_number_cell("2"), - // 1647251762 => Mar 14,2022 - FieldType::DateTime => row_builder.insert_date_cell("1647251762"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(0)) - } - FieldType::MultiSelect => row_builder - .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]), - FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), - _ => "".to_owned(), - }; - } - } - 2 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("C"), - FieldType::Number => row_builder.insert_number_cell("3"), - // 1647251762 => Mar 14,2022 - FieldType::DateTime => row_builder.insert_date_cell("1647251762"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(1)) - } - FieldType::MultiSelect => { - row_builder.insert_multi_select_cell(|mut options| vec![options.remove(0)]) - } - FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), - FieldType::URL => row_builder.insert_url_cell("https://github.com/AppFlowy-IO/AppFlowy"), - _ => "".to_owned(), - }; - } - } - 3 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("DA"), - FieldType::Number => row_builder.insert_number_cell("4"), - FieldType::DateTime => row_builder.insert_date_cell("1668704685"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(1)) - } - FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), - FieldType::URL => row_builder.insert_url_cell("https://appflowy.io"), - _ => "".to_owned(), - }; - } - } - 4 => { - for field_type in FieldType::iter() { - match field_type { - FieldType::RichText => row_builder.insert_text_cell("AE"), - FieldType::Number => row_builder.insert_number_cell(""), - FieldType::DateTime => row_builder.insert_date_cell("1668359085"), - FieldType::SingleSelect => { - row_builder.insert_single_select_cell(|mut options| options.remove(2)) - } - - FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), - _ => "".to_owned(), - }; - } - } - _ => {} - } - - let row_rev = row_builder.build(); - grid_builder.add_row(row_rev); - } - grid_builder.build() -} - -// Calendar unit test mock data -fn make_test_calendar() -> BuildDatabaseContext { - todo!() -} diff --git a/frontend/rust-lib/flowy-database/tests/grid/grid_test.rs b/frontend/rust-lib/flowy-database/tests/grid/grid_test.rs deleted file mode 100644 index 0624c03b39..0000000000 --- a/frontend/rust-lib/flowy-database/tests/grid/grid_test.rs +++ /dev/null @@ -1,381 +0,0 @@ -use crate::grid::script::EditorScript::*; -use crate::grid::script::*; -use chrono::NaiveDateTime; -use flowy_database::services::field::{ - DateCellContentChangeset, DateCellData, MultiSelectTypeOptionPB, SelectOption, SelectOptionCellContentChangeset, - SingleSelectTypeOption, SELECTION_IDS_SEPARATOR, -}; -use flowy_database::services::row::{decode_cell_data_from_type_option_cell_data, CreateRowMetaBuilder}; -use grid_model::entities::{ - CellChangeset, FieldChangesetParams, FieldType, GridBlockInfoChangeset, GridBlockMetaSnapshot, RowMetaChangeset, - TypeOptionDataFormat, -}; - -#[tokio::test] -async fn grid_create_field() { - let mut test = GridEditorTest::new().await; - let (text_field_params, text_field_meta) = create_text_field(&test.grid_id); - let (single_select_params, single_select_field) = create_single_select_field(&test.grid_id); - let scripts = vec![ - CreateField { - params: text_field_params, - }, - AssertFieldEqual { - field_index: test.field_count, - field_meta: text_field_meta, - }, - ]; - test.run_scripts(scripts).await; - - let scripts = vec![ - CreateField { - params: single_select_params, - }, - AssertFieldEqual { - field_index: test.field_count, - field_meta: single_select_field, - }, - ]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_create_duplicate_field() { - let mut test = GridEditorTest::new().await; - let (params, _) = create_text_field(&test.grid_id); - let field_count = test.field_count; - let expected_field_count = field_count + 1; - let scripts = vec![ - CreateField { params: params.clone() }, - CreateField { params }, - AssertFieldCount(expected_field_count), - ]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_update_field_with_empty_change() { - let mut test = GridEditorTest::new().await; - let (params, field_meta) = create_single_select_field(&test.grid_id); - let changeset = FieldChangesetParams { - field_id: field_meta.id.clone(), - grid_id: test.grid_id.clone(), - ..Default::default() - }; - - let scripts = vec![ - CreateField { params }, - UpdateField { changeset }, - AssertFieldEqual { - field_index: test.field_count, - field_meta, - }, - ]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_update_field() { - let mut test = GridEditorTest::new().await; - let (single_select_params, single_select_field) = create_single_select_field(&test.grid_id); - let mut cloned_field = single_select_field.clone(); - - let mut single_select_type_option = SingleSelectTypeOption::from(&single_select_field); - single_select_type_option.options.push(SelectOption::new("Unknown")); - let changeset = FieldChangesetParams { - field_id: single_select_field.id.clone(), - grid_id: test.grid_id.clone(), - frozen: Some(true), - width: Some(1000), - type_option_data: Some(single_select_type_option.protobuf_bytes().to_vec()), - ..Default::default() - }; - - cloned_field.frozen = true; - cloned_field.width = 1000; - cloned_field.insert_type_option_entry(&single_select_type_option); - - let scripts = vec![ - CreateField { - params: single_select_params, - }, - UpdateField { changeset }, - AssertFieldEqual { - field_index: test.field_count, - field_meta: cloned_field, - }, - ]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_delete_field() { - let mut test = GridEditorTest::new().await; - let expected_field_count = test.field_count; - let (text_params, text_field) = create_text_field(&test.grid_id); - let scripts = vec![ - CreateField { params: text_params }, - DeleteField { field_meta: text_field }, - AssertFieldCount(expected_field_count), - ]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_create_block() { - let grid_block = GridBlockMetaSnapshot::new(); - let scripts = vec![ - AssertBlockCount(1), - CreateBlock { block: grid_block }, - AssertBlockCount(2), - ]; - GridEditorTest::new().await.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_update_block() { - let grid_block = GridBlockMetaSnapshot::new(); - let mut cloned_grid_block = grid_block.clone(); - let changeset = GridBlockInfoChangeset { - block_id: grid_block.block_id.clone(), - start_row_index: Some(2), - row_count: Some(10), - }; - - cloned_grid_block.start_row_index = 2; - cloned_grid_block.row_count = 10; - - let scripts = vec![ - AssertBlockCount(1), - CreateBlock { block: grid_block }, - UpdateBlock { changeset }, - AssertBlockCount(2), - AssertBlockEqual { - block_index: 1, - block: cloned_grid_block, - }, - ]; - GridEditorTest::new().await.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_create_row() { - let scripts = vec![AssertRowCount(3), CreateEmptyRow, CreateEmptyRow, AssertRowCount(5)]; - GridEditorTest::new().await.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_create_row2() { - let mut test = GridEditorTest::new().await; - let create_row_context = CreateRowMetaBuilder::new(&test.field_metas).build(); - let scripts = vec![ - AssertRowCount(3), - CreateRow { - context: create_row_context, - }, - AssertRowCount(4), - ]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_update_row() { - let mut test = GridEditorTest::new().await; - let context = CreateRowMetaBuilder::new(&test.field_metas).build(); - let changeset = RowMetaChangeset { - row_id: context.row_id.clone(), - height: None, - visibility: None, - cell_by_field_id: Default::default(), - }; - - let scripts = vec![ - AssertRowCount(3), - CreateRow { context }, - UpdateRow { - changeset: changeset.clone(), - }, - AssertRow { changeset }, - AssertRowCount(4), - ]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_delete_row() { - let mut test = GridEditorTest::new().await; - let context_1 = CreateRowMetaBuilder::new(&test.field_metas).build(); - let context_2 = CreateRowMetaBuilder::new(&test.field_metas).build(); - let row_ids = vec![context_1.row_id.clone(), context_2.row_id.clone()]; - let scripts = vec![ - AssertRowCount(3), - CreateRow { context: context_1 }, - CreateRow { context: context_2 }, - AssertBlockCount(1), - AssertBlock { - block_index: 0, - row_count: 5, - start_row_index: 0, - }, - DeleteRow { row_ids }, - AssertBlock { - block_index: 0, - row_count: 3, - start_row_index: 0, - }, - ]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_row_add_cells_test() { - let mut test = GridEditorTest::new().await; - let mut builder = CreateRowMetaBuilder::new(&test.field_metas); - for field in &test.field_metas { - match field.field_type { - FieldType::RichText => { - builder.add_cell(&field.id, "hello world".to_owned()).unwrap(); - } - FieldType::Number => { - builder.add_cell(&field.id, "18,443".to_owned()).unwrap(); - } - FieldType::DateTime => { - builder - .add_cell(&field.id, make_date_cell_string("1647251762")) - .unwrap(); - } - FieldType::SingleSelect => { - let type_option = SingleSelectTypeOption::from(field); - let option = type_option.options.first().unwrap(); - builder.add_select_option_cell(&field.id, option.id.clone()).unwrap(); - } - FieldType::MultiSelect => { - let type_option = MultiSelectTypeOptionPB::from(field); - let ops_ids = type_option - .options - .iter() - .map(|option| option.id.clone()) - .collect::>() - .join(SELECTION_IDS_SEPARATOR); - builder.add_select_option_cell(&field.id, ops_ids).unwrap(); - } - FieldType::Checkbox => { - builder.add_cell(&field.id, "false".to_string()).unwrap(); - } - FieldType::URL => { - builder.add_cell(&field.id, "1".to_string()).unwrap(); - } - } - } - let context = builder.build(); - let scripts = vec![CreateRow { context }, AssertGridMetaPad]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_row_add_date_cell_test() { - let mut test = GridEditorTest::new().await; - let mut builder = CreateRowMetaBuilder::new(&test.field_metas); - let mut date_field = None; - let timestamp = 1647390674; - for field in &test.field_metas { - if field.field_type == FieldType::DateTime { - date_field = Some(field.clone()); - NaiveDateTime::from_timestamp(123, 0); - // The data should not be empty - assert!(builder.add_cell(&field.id, "".to_string()).is_err()); - assert!(builder.add_cell(&field.id, make_date_cell_string("123")).is_ok()); - assert!(builder - .add_cell(&field.id, make_date_cell_string(×tamp.to_string())) - .is_ok()); - } - } - let context = builder.build(); - let date_field = date_field.unwrap(); - let cell_data = context.cell_by_field_id.get(&date_field.id).unwrap().clone(); - assert_eq!( - decode_cell_data_from_type_option_cell_data(cell_data.data.clone(), &date_field, &date_field.field_type) - .parse::() - .unwrap() - .date, - "2022/03/16", - ); - let scripts = vec![CreateRow { context }]; - test.run_scripts(scripts).await; -} - -#[tokio::test] -async fn grid_cell_update() { - let mut test = GridEditorTest::new().await; - let field_metas = &test.field_metas; - let row_metas = &test.row_metas; - let grid_blocks = &test.grid_blocks; - assert_eq!(row_metas.len(), 3); - assert_eq!(grid_blocks.len(), 1); - - let block_id = &grid_blocks.first().unwrap().block_id; - let mut scripts = vec![]; - for (index, row_meta) in row_metas.iter().enumerate() { - for field_meta in field_metas { - if index == 0 { - let data = match field_meta.field_type { - FieldType::RichText => "".to_string(), - FieldType::Number => "123".to_string(), - FieldType::DateTime => make_date_cell_string("123"), - FieldType::SingleSelect => { - let type_option = SingleSelectTypeOption::from(field_meta); - SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str() - } - FieldType::MultiSelect => { - let type_option = MultiSelectTypeOptionPB::from(field_meta); - SelectOptionCellContentChangeset::from_insert(&type_option.options.first().unwrap().id).to_str() - } - FieldType::Checkbox => "1".to_string(), - FieldType::URL => "1".to_string(), - }; - - scripts.push(UpdateCell { - changeset: CellChangeset { - database_id: block_id.to_string(), - row_id: row_meta.id.clone(), - field_id: field_meta.id.clone(), - cell_content_changeset: Some(data), - }, - is_err: false, - }); - } - - if index == 1 { - let (data, is_err) = match field_meta.field_type { - FieldType::RichText => ("1".to_string().repeat(10001), true), - FieldType::Number => ("abc".to_string(), true), - FieldType::DateTime => ("abc".to_string(), true), - FieldType::SingleSelect => (SelectOptionCellContentChangeset::from_insert("abc").to_str(), false), - FieldType::MultiSelect => (SelectOptionCellContentChangeset::from_insert("abc").to_str(), false), - FieldType::Checkbox => ("2".to_string(), false), - FieldType::URL => ("2".to_string(), false), - }; - - scripts.push(UpdateCell { - changeset: CellChangeset { - database_id: block_id.to_string(), - row_id: row_meta.id.clone(), - field_id: field_meta.id.clone(), - cell_content_changeset: Some(data), - }, - is_err, - }); - } - } - } - - test.run_scripts(scripts).await; -} - -fn make_date_cell_string(s: &str) -> String { - serde_json::to_string(&DateCellContentChangeset { - date: Some(s.to_string()), - time: None, - }) - .unwrap() -} diff --git a/frontend/rust-lib/flowy-database/tests/grid/group_test/mod.rs b/frontend/rust-lib/flowy-database/tests/grid/group_test/mod.rs index 63d424afaf..67671ae7f5 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/group_test/mod.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/group_test/mod.rs @@ -1,2 +1,3 @@ mod script; mod test; +mod url_group_test; diff --git a/frontend/rust-lib/flowy-database/tests/grid/group_test/script.rs b/frontend/rust-lib/flowy-database/tests/grid/group_test/script.rs index c529ae10c4..7c20ab7815 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/group_test/script.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/group_test/script.rs @@ -1,8 +1,8 @@ -use crate::grid::grid_editor::GridEditorTest; +use crate::grid::database_editor::DatabaseEditorTest; use flowy_database::entities::{ CreateRowParams, DatabaseViewLayout, FieldType, GroupPB, MoveGroupParams, MoveGroupRowParams, RowPB, }; -use flowy_database::services::cell::{delete_select_option_cell, insert_select_option_cell}; +use flowy_database::services::cell::{delete_select_option_cell, insert_select_option_cell, insert_url_cell}; use flowy_database::services::field::{ edit_single_select_type_option, SelectOptionPB, SelectTypeOptionSharedAction, SingleSelectTypeOptionPB, }; @@ -37,11 +37,16 @@ pub enum GroupScript { group_index: usize, row_index: usize, }, - UpdateRow { + UpdateGroupedCell { from_group_index: usize, row_index: usize, to_group_index: usize, }, + UpdateGroupedCellWithData { + from_group_index: usize, + row_index: usize, + cell_data: String, + }, MoveGroup { from_group_index: usize, to_group_index: usize, @@ -54,13 +59,13 @@ pub enum GroupScript { }, } -pub struct GridGroupTest { - inner: GridEditorTest, +pub struct DatabaseGroupTest { + inner: DatabaseEditorTest, } -impl GridGroupTest { +impl DatabaseGroupTest { pub async fn new() -> Self { - let editor_test = GridEditorTest::new_board().await; + let editor_test = DatabaseEditorTest::new_board().await; Self { inner: editor_test } } @@ -109,7 +114,6 @@ impl GridGroupTest { assert_eq!(row.id, compare_row.id); } GroupScript::CreateRow { group_index } => { - // let group = self.group_at_index(group_index).await; let params = CreateRowParams { database_id: self.editor.database_id.clone(), @@ -123,7 +127,7 @@ impl GridGroupTest { let row = self.row_at_index(group_index, row_index).await; self.editor.delete_row(&row.id).await.unwrap(); } - GroupScript::UpdateRow { + GroupScript::UpdateGroupedCell { from_group_index, row_index, to_group_index, @@ -154,6 +158,7 @@ impl GridGroupTest { FieldType::MultiSelect => { insert_select_option_cell(vec![to_group.group_id.clone()], &field_rev) } + FieldType::URL => insert_url_cell(to_group.group_id.clone(), &field_rev), _ => { panic!("Unsupported group field type"); } @@ -165,6 +170,27 @@ impl GridGroupTest { row_changeset.cell_by_field_id.insert(field_id, cell_rev); self.editor.update_row(row_changeset).await.unwrap(); } + GroupScript::UpdateGroupedCellWithData { + from_group_index, + row_index, + cell_data, + } => { + let from_group = self.group_at_index(from_group_index).await; + let field_id = from_group.field_id; + let field_rev = self.editor.get_field_rev(&field_id).await.unwrap(); + let field_type: FieldType = field_rev.ty.into(); + let cell_rev = match field_type { + FieldType::URL => insert_url_cell(cell_data, &field_rev), + _ => { + panic!("Unsupported group field type"); + } + }; + + let row_id = self.row_at_index(from_group_index, row_index).await.id; + let mut row_changeset = RowChangeset::new(row_id); + row_changeset.cell_by_field_id.insert(field_id, cell_rev); + self.editor.update_row(row_changeset).await.unwrap(); + } GroupScript::MoveGroup { from_group_index, to_group_index, @@ -258,15 +284,15 @@ impl GridGroupTest { } } -impl std::ops::Deref for GridGroupTest { - type Target = GridEditorTest; +impl std::ops::Deref for DatabaseGroupTest { + type Target = DatabaseEditorTest; fn deref(&self) -> &Self::Target { &self.inner } } -impl std::ops::DerefMut for GridGroupTest { +impl std::ops::DerefMut for DatabaseGroupTest { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } diff --git a/frontend/rust-lib/flowy-database/tests/grid/group_test/test.rs b/frontend/rust-lib/flowy-database/tests/grid/group_test/test.rs index d89d22ea0c..7c55b82e8a 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/group_test/test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/group_test/test.rs @@ -1,11 +1,11 @@ -use crate::grid::group_test::script::GridGroupTest; +use crate::grid::group_test::script::DatabaseGroupTest; use crate::grid::group_test::script::GroupScript::*; use flowy_database::services::field::SelectOptionPB; #[tokio::test] async fn group_init_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let scripts = vec![ AssertGroupCount(4), AssertGroupRowCount { @@ -30,7 +30,7 @@ async fn group_init_test() { #[tokio::test] async fn group_move_row_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let group = test.group_at_index(1).await; let scripts = vec![ // Move the row at 0 in group0 to group1 at 1 @@ -55,7 +55,7 @@ async fn group_move_row_test() { #[tokio::test] async fn group_move_row_to_other_group_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let group = test.group_at_index(1).await; let scripts = vec![ MoveRow { @@ -83,7 +83,7 @@ async fn group_move_row_to_other_group_test() { #[tokio::test] async fn group_move_two_row_to_other_group_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let group_1 = test.group_at_index(1).await; let scripts = vec![ // Move row at index 0 from group 1 to group 2 at index 1 @@ -137,7 +137,7 @@ async fn group_move_two_row_to_other_group_test() { #[tokio::test] async fn group_move_row_to_other_group_and_reorder_from_up_to_down_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let group_1 = test.group_at_index(1).await; let group_2 = test.group_at_index(2).await; let scripts = vec![ @@ -173,7 +173,7 @@ async fn group_move_row_to_other_group_and_reorder_from_up_to_down_test() { #[tokio::test] async fn group_move_row_to_other_group_and_reorder_from_bottom_to_up_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let scripts = vec![MoveRow { from_group_index: 1, from_row_index: 0, @@ -204,7 +204,7 @@ async fn group_move_row_to_other_group_and_reorder_from_bottom_to_up_test() { } #[tokio::test] async fn group_create_row_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let scripts = vec![ CreateRow { group_index: 1 }, AssertGroupRowCount { @@ -223,7 +223,7 @@ async fn group_create_row_test() { #[tokio::test] async fn group_delete_row_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let scripts = vec![ DeleteRow { group_index: 1, @@ -239,7 +239,7 @@ async fn group_delete_row_test() { #[tokio::test] async fn group_delete_all_row_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let scripts = vec![ DeleteRow { group_index: 1, @@ -259,10 +259,10 @@ async fn group_delete_all_row_test() { #[tokio::test] async fn group_update_row_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let scripts = vec![ // Update the row at 0 in group0 by setting the row's group field data - UpdateRow { + UpdateGroupedCell { from_group_index: 1, row_index: 0, to_group_index: 2, @@ -281,10 +281,10 @@ async fn group_update_row_test() { #[tokio::test] async fn group_reorder_group_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let scripts = vec![ // Update the row at 0 in group0 by setting the row's group field data - UpdateRow { + UpdateGroupedCell { from_group_index: 1, row_index: 0, to_group_index: 2, @@ -303,9 +303,9 @@ async fn group_reorder_group_test() { #[tokio::test] async fn group_move_to_default_group_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let scripts = vec![ - UpdateRow { + UpdateGroupedCell { from_group_index: 1, row_index: 0, to_group_index: 0, @@ -324,10 +324,10 @@ async fn group_move_to_default_group_test() { #[tokio::test] async fn group_move_from_default_group_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; // Move one row from group 1 to group 0 let scripts = vec![ - UpdateRow { + UpdateGroupedCell { from_group_index: 1, row_index: 0, to_group_index: 0, @@ -345,7 +345,7 @@ async fn group_move_from_default_group_test() { // Move one row from group 0 to group 1 let scripts = vec![ - UpdateRow { + UpdateGroupedCell { from_group_index: 0, row_index: 0, to_group_index: 1, @@ -364,7 +364,7 @@ async fn group_move_from_default_group_test() { #[tokio::test] async fn group_move_group_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let group_0 = test.group_at_index(0).await; let group_1 = test.group_at_index(1).await; let scripts = vec![ @@ -394,7 +394,7 @@ async fn group_move_group_test() { #[tokio::test] async fn group_move_group_row_after_move_group_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let group_1 = test.group_at_index(1).await; let group_2 = test.group_at_index(2).await; let scripts = vec![ @@ -430,7 +430,7 @@ async fn group_move_group_row_after_move_group_test() { #[tokio::test] async fn group_move_group_to_default_group_pos_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let group_0 = test.group_at_index(0).await; let group_3 = test.group_at_index(3).await; let scripts = vec![ @@ -452,7 +452,7 @@ async fn group_move_group_to_default_group_pos_test() { #[tokio::test] async fn group_insert_single_select_option_test() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let new_option_name = "New option"; let scripts = vec![ AssertGroupCount(4), @@ -468,7 +468,7 @@ async fn group_insert_single_select_option_test() { #[tokio::test] async fn group_group_by_other_field() { - let mut test = GridGroupTest::new().await; + let mut test = DatabaseGroupTest::new().await; let multi_select_field = test.get_multi_select_field().await; let scripts = vec![ GroupByField { @@ -486,52 +486,3 @@ async fn group_group_by_other_field() { ]; test.run_scripts(scripts).await; } - -#[tokio::test] -async fn group_group_by_url() { - let mut test = GridGroupTest::new().await; - let url_field = test.get_url_field().await; - let scripts = vec![ - GroupByField { - field_id: url_field.id.clone(), - }, - AssertGroupRowCount { - group_index: 0, - row_count: 2, - }, - AssertGroupRowCount { - group_index: 1, - row_count: 2, - }, - AssertGroupRowCount { - group_index: 2, - row_count: 1, - }, - AssertGroupCount(3), - MoveRow { - from_group_index: 0, - from_row_index: 0, - to_group_index: 1, - to_row_index: 0, - }, - MoveRow { - from_group_index: 1, - from_row_index: 0, - to_group_index: 2, - to_row_index: 0, - }, - AssertGroupRowCount { - group_index: 0, - row_count: 1, - }, - AssertGroupRowCount { - group_index: 1, - row_count: 2, - }, - AssertGroupRowCount { - group_index: 2, - row_count: 2, - }, - ]; - test.run_scripts(scripts).await; -} diff --git a/frontend/rust-lib/flowy-database/tests/grid/group_test/url_group_test.rs b/frontend/rust-lib/flowy-database/tests/grid/group_test/url_group_test.rs new file mode 100644 index 0000000000..5865c1f999 --- /dev/null +++ b/frontend/rust-lib/flowy-database/tests/grid/group_test/url_group_test.rs @@ -0,0 +1,148 @@ +use crate::grid::group_test::script::DatabaseGroupTest; +use crate::grid::group_test::script::GroupScript::*; + +#[tokio::test] +async fn group_group_by_url() { + let mut test = DatabaseGroupTest::new().await; + let url_field = test.get_url_field().await; + let scripts = vec![ + GroupByField { + field_id: url_field.id.clone(), + }, + // no status group + AssertGroupRowCount { + group_index: 0, + row_count: 2, + }, + // https://appflowy.io + AssertGroupRowCount { + group_index: 1, + row_count: 2, + }, + // https://github.com/AppFlowy-IO/AppFlowy + AssertGroupRowCount { + group_index: 2, + row_count: 1, + }, + AssertGroupCount(3), + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn group_alter_url_to_another_group_url_test() { + let mut test = DatabaseGroupTest::new().await; + let url_field = test.get_url_field().await; + let scripts = vec![ + GroupByField { + field_id: url_field.id.clone(), + }, + // no status group + AssertGroupRowCount { + group_index: 0, + row_count: 2, + }, + // https://appflowy.io + AssertGroupRowCount { + group_index: 1, + row_count: 2, + }, + // https://github.com/AppFlowy-IO/AppFlowy + AssertGroupRowCount { + group_index: 2, + row_count: 1, + }, + // When moving the last row from 2nd group to 1nd group, the 2nd group will be removed + UpdateGroupedCell { + from_group_index: 2, + row_index: 0, + to_group_index: 1, + }, + AssertGroupCount(2), + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn group_alter_url_to_new_url_test() { + let mut test = DatabaseGroupTest::new().await; + let url_field = test.get_url_field().await; + let scripts = vec![ + GroupByField { + field_id: url_field.id.clone(), + }, + // When moving the last row from 2nd group to 1nd group, the 2nd group will be removed + UpdateGroupedCellWithData { + from_group_index: 0, + row_index: 0, + cell_data: "https://github.com/AppFlowy-IO".to_string(), + }, + // no status group + AssertGroupRowCount { + group_index: 0, + row_count: 1, + }, + // https://appflowy.io + AssertGroupRowCount { + group_index: 1, + row_count: 2, + }, + // https://github.com/AppFlowy-IO/AppFlowy + AssertGroupRowCount { + group_index: 2, + row_count: 1, + }, + AssertGroupRowCount { + group_index: 3, + row_count: 1, + }, + AssertGroupCount(4), + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn group_move_url_group_row_test() { + let mut test = DatabaseGroupTest::new().await; + let url_field = test.get_url_field().await; + let scripts = vec![ + GroupByField { + field_id: url_field.id.clone(), + }, + // no status group + AssertGroupRowCount { + group_index: 0, + row_count: 2, + }, + // https://appflowy.io + AssertGroupRowCount { + group_index: 1, + row_count: 2, + }, + // https://github.com/AppFlowy-IO/AppFlowy + AssertGroupRowCount { + group_index: 2, + row_count: 1, + }, + AssertGroupCount(3), + MoveRow { + from_group_index: 0, + from_row_index: 0, + to_group_index: 1, + to_row_index: 0, + }, + AssertGroupRowCount { + group_index: 0, + row_count: 1, + }, + AssertGroupRowCount { + group_index: 1, + row_count: 3, + }, + AssertGroupRowCount { + group_index: 2, + row_count: 1, + }, + ]; + test.run_scripts(scripts).await; +} diff --git a/frontend/rust-lib/flowy-database/tests/grid/mock_data/board_mock_data.rs b/frontend/rust-lib/flowy-database/tests/grid/mock_data/board_mock_data.rs new file mode 100644 index 0000000000..015cc01e86 --- /dev/null +++ b/frontend/rust-lib/flowy-database/tests/grid/mock_data/board_mock_data.rs @@ -0,0 +1,191 @@ +// #![allow(clippy::all)] +// #![allow(dead_code)] +// #![allow(unused_imports)] +use crate::grid::block_test::util::GridRowTestBuilder; +use crate::grid::mock_data::{ + COMPLETED, FACEBOOK, FIRST_THING, GOOGLE, PAUSED, PLANNED, SECOND_THING, THIRD_THING, TWITTER, +}; + +use flowy_client_sync::client_database::DatabaseBuilder; +use flowy_database::entities::*; + +use flowy_database::services::field::SelectOptionPB; +use flowy_database::services::field::*; + +use grid_model::*; + +use strum::IntoEnumIterator; + +// Kanban board unit test mock data +pub fn make_test_board() -> BuildDatabaseContext { + let mut grid_builder = DatabaseBuilder::new(); + // Iterate through the FieldType to create the corresponding Field. + for field_type in FieldType::iter() { + let field_type: FieldType = field_type; + + // The + match field_type { + FieldType::RichText => { + let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) + .name("Name") + .visibility(true) + .primary(true) + .build(); + grid_builder.add_field(text_field); + } + FieldType::Number => { + // Number + let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); + let number_field = FieldBuilder::new(number).name("Price").visibility(true).build(); + grid_builder.add_field(number_field); + } + FieldType::DateTime => { + // Date + let date = DateTypeOptionBuilder::default() + .date_format(DateFormat::US) + .time_format(TimeFormat::TwentyFourHour); + let date_field = FieldBuilder::new(date).name("Time").visibility(true).build(); + grid_builder.add_field(date_field); + } + FieldType::SingleSelect => { + // Single Select + let single_select = SingleSelectTypeOptionBuilder::default() + .add_option(SelectOptionPB::new(COMPLETED)) + .add_option(SelectOptionPB::new(PLANNED)) + .add_option(SelectOptionPB::new(PAUSED)); + let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build(); + grid_builder.add_field(single_select_field); + } + FieldType::MultiSelect => { + // MultiSelect + let multi_select = MultiSelectTypeOptionBuilder::default() + .add_option(SelectOptionPB::new(GOOGLE)) + .add_option(SelectOptionPB::new(FACEBOOK)) + .add_option(SelectOptionPB::new(TWITTER)); + let multi_select_field = FieldBuilder::new(multi_select) + .name("Platform") + .visibility(true) + .build(); + grid_builder.add_field(multi_select_field); + } + FieldType::Checkbox => { + // Checkbox + let checkbox = CheckboxTypeOptionBuilder::default(); + let checkbox_field = FieldBuilder::new(checkbox).name("is urgent").visibility(true).build(); + grid_builder.add_field(checkbox_field); + } + FieldType::URL => { + // URL + let url = URLTypeOptionBuilder::default(); + let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); + grid_builder.add_field(url_field); + } + FieldType::Checklist => { + let checklist = ChecklistTypeOptionBuilder::default() + .add_option(SelectOptionPB::new(FIRST_THING)) + .add_option(SelectOptionPB::new(SECOND_THING)) + .add_option(SelectOptionPB::new(THIRD_THING)); + let checklist_field = FieldBuilder::new(checklist).name("TODO").visibility(true).build(); + grid_builder.add_field(checklist_field); + } + } + } + + // We have many assumptions base on the number of the rows, so do not change the number of the loop. + for i in 0..5 { + let block_id = grid_builder.block_id().to_owned(); + let field_revs = grid_builder.field_revs(); + let mut row_builder = GridRowTestBuilder::new(&block_id, field_revs); + match i { + 0 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("A"), + FieldType::Number => row_builder.insert_number_cell("1"), + // 1647251762 => Mar 14,2022 + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(0)) + } + FieldType::MultiSelect => row_builder + .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]), + FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), + FieldType::URL => row_builder.insert_url_cell("https://appflowy.io"), + _ => "".to_owned(), + }; + } + } + 1 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("B"), + FieldType::Number => row_builder.insert_number_cell("2"), + // 1647251762 => Mar 14,2022 + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(0)) + } + FieldType::MultiSelect => row_builder + .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]), + FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), + _ => "".to_owned(), + }; + } + } + 2 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("C"), + FieldType::Number => row_builder.insert_number_cell("3"), + // 1647251762 => Mar 14,2022 + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(1)) + } + FieldType::MultiSelect => { + row_builder.insert_multi_select_cell(|mut options| vec![options.remove(0)]) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + FieldType::URL => row_builder.insert_url_cell("https://github.com/AppFlowy-IO/AppFlowy"), + _ => "".to_owned(), + }; + } + } + 3 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("DA"), + FieldType::Number => row_builder.insert_number_cell("4"), + FieldType::DateTime => row_builder.insert_date_cell("1668704685"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(1)) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + FieldType::URL => row_builder.insert_url_cell("https://appflowy.io"), + _ => "".to_owned(), + }; + } + } + 4 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("AE"), + FieldType::Number => row_builder.insert_number_cell(""), + FieldType::DateTime => row_builder.insert_date_cell("1668359085"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(2)) + } + + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + _ => "".to_owned(), + }; + } + } + _ => {} + } + + let row_rev = row_builder.build(); + grid_builder.add_row(row_rev); + } + grid_builder.build() +} diff --git a/frontend/rust-lib/flowy-database/tests/grid/mock_data/calendar_mock_data.rs b/frontend/rust-lib/flowy-database/tests/grid/mock_data/calendar_mock_data.rs new file mode 100644 index 0000000000..9f28ec4f20 --- /dev/null +++ b/frontend/rust-lib/flowy-database/tests/grid/mock_data/calendar_mock_data.rs @@ -0,0 +1,6 @@ +use grid_model::BuildDatabaseContext; + +// Calendar unit test mock data +pub fn make_test_calendar() -> BuildDatabaseContext { + todo!() +} diff --git a/frontend/rust-lib/flowy-database/tests/grid/mock_data/grid_mock_data.rs b/frontend/rust-lib/flowy-database/tests/grid/mock_data/grid_mock_data.rs new file mode 100644 index 0000000000..882c3109c5 --- /dev/null +++ b/frontend/rust-lib/flowy-database/tests/grid/mock_data/grid_mock_data.rs @@ -0,0 +1,193 @@ +// #![allow(clippy::all)] +// #![allow(dead_code)] +// #![allow(unused_imports)] +use crate::grid::block_test::util::GridRowTestBuilder; +use crate::grid::mock_data::{ + COMPLETED, FACEBOOK, FIRST_THING, GOOGLE, PAUSED, PLANNED, SECOND_THING, THIRD_THING, TWITTER, +}; + +use flowy_client_sync::client_database::DatabaseBuilder; +use flowy_database::entities::*; + +use flowy_database::services::field::SelectOptionPB; +use flowy_database::services::field::*; + +use grid_model::*; + +use strum::IntoEnumIterator; + +pub fn make_test_grid() -> BuildDatabaseContext { + let mut grid_builder = DatabaseBuilder::new(); + // Iterate through the FieldType to create the corresponding Field. + for field_type in FieldType::iter() { + let field_type: FieldType = field_type; + + // The + match field_type { + FieldType::RichText => { + let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) + .name("Name") + .visibility(true) + .primary(true) + .build(); + grid_builder.add_field(text_field); + } + FieldType::Number => { + // Number + let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); + let number_field = FieldBuilder::new(number).name("Price").visibility(true).build(); + grid_builder.add_field(number_field); + } + FieldType::DateTime => { + // Date + let date = DateTypeOptionBuilder::default() + .date_format(DateFormat::US) + .time_format(TimeFormat::TwentyFourHour); + let date_field = FieldBuilder::new(date).name("Time").visibility(true).build(); + grid_builder.add_field(date_field); + } + FieldType::SingleSelect => { + // Single Select + let single_select = SingleSelectTypeOptionBuilder::default() + .add_option(SelectOptionPB::new(COMPLETED)) + .add_option(SelectOptionPB::new(PLANNED)) + .add_option(SelectOptionPB::new(PAUSED)); + let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build(); + grid_builder.add_field(single_select_field); + } + FieldType::MultiSelect => { + // MultiSelect + let multi_select = MultiSelectTypeOptionBuilder::default() + .add_option(SelectOptionPB::new(GOOGLE)) + .add_option(SelectOptionPB::new(FACEBOOK)) + .add_option(SelectOptionPB::new(TWITTER)); + let multi_select_field = FieldBuilder::new(multi_select) + .name("Platform") + .visibility(true) + .build(); + grid_builder.add_field(multi_select_field); + } + FieldType::Checkbox => { + // Checkbox + let checkbox = CheckboxTypeOptionBuilder::default(); + let checkbox_field = FieldBuilder::new(checkbox).name("is urgent").visibility(true).build(); + grid_builder.add_field(checkbox_field); + } + FieldType::URL => { + // URL + let url = URLTypeOptionBuilder::default(); + let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); + grid_builder.add_field(url_field); + } + FieldType::Checklist => { + let checklist = ChecklistTypeOptionBuilder::default() + .add_option(SelectOptionPB::new(FIRST_THING)) + .add_option(SelectOptionPB::new(SECOND_THING)) + .add_option(SelectOptionPB::new(THIRD_THING)); + let checklist_field = FieldBuilder::new(checklist).name("TODO").visibility(true).build(); + grid_builder.add_field(checklist_field); + } + } + } + + for i in 0..6 { + let block_id = grid_builder.block_id().to_owned(); + let field_revs = grid_builder.field_revs(); + let mut row_builder = GridRowTestBuilder::new(&block_id, field_revs); + match i { + 0 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("A"), + FieldType::Number => row_builder.insert_number_cell("1"), + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::MultiSelect => row_builder + .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]), + FieldType::Checklist => row_builder.insert_checklist_cell(|options| options), + FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), + FieldType::URL => row_builder.insert_url_cell("AppFlowy website - https://www.appflowy.io"), + _ => "".to_owned(), + }; + } + } + 1 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell(""), + FieldType::Number => row_builder.insert_number_cell("2"), + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::MultiSelect => row_builder + .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(1)]), + FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), + _ => "".to_owned(), + }; + } + } + 2 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("C"), + FieldType::Number => row_builder.insert_number_cell("3"), + FieldType::DateTime => row_builder.insert_date_cell("1647251762"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(0)) + } + FieldType::MultiSelect => { + row_builder.insert_multi_select_cell(|mut options| vec![options.remove(1)]) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + _ => "".to_owned(), + }; + } + } + 3 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("DA"), + FieldType::Number => row_builder.insert_number_cell("4"), + FieldType::DateTime => row_builder.insert_date_cell("1668704685"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(0)) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + _ => "".to_owned(), + }; + } + } + 4 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("AE"), + FieldType::Number => row_builder.insert_number_cell(""), + FieldType::DateTime => row_builder.insert_date_cell("1668359085"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(1)) + } + + FieldType::Checkbox => row_builder.insert_checkbox_cell("false"), + _ => "".to_owned(), + }; + } + } + 5 => { + for field_type in FieldType::iter() { + match field_type { + FieldType::RichText => row_builder.insert_text_cell("AE"), + FieldType::Number => row_builder.insert_number_cell("5"), + FieldType::DateTime => row_builder.insert_date_cell("1671938394"), + FieldType::SingleSelect => { + row_builder.insert_single_select_cell(|mut options| options.remove(1)) + } + FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), + _ => "".to_owned(), + }; + } + } + _ => {} + } + + let row_rev = row_builder.build(); + grid_builder.add_row(row_rev); + } + grid_builder.build() +} diff --git a/frontend/rust-lib/flowy-database/tests/grid/mock_data/mod.rs b/frontend/rust-lib/flowy-database/tests/grid/mock_data/mod.rs new file mode 100644 index 0000000000..6b47039952 --- /dev/null +++ b/frontend/rust-lib/flowy-database/tests/grid/mock_data/mod.rs @@ -0,0 +1,19 @@ +mod board_mock_data; +mod calendar_mock_data; +mod grid_mock_data; + +pub use board_mock_data::*; +pub use calendar_mock_data::*; +pub use grid_mock_data::*; + +pub const GOOGLE: &str = "Google"; +pub const FACEBOOK: &str = "Facebook"; +pub const TWITTER: &str = "Twitter"; + +pub const COMPLETED: &str = "Completed"; +pub const PLANNED: &str = "Planned"; +pub const PAUSED: &str = "Paused"; + +pub const FIRST_THING: &str = "Wake up at 6:00 am"; +pub const SECOND_THING: &str = "Get some coffee"; +pub const THIRD_THING: &str = "Start working"; diff --git a/frontend/rust-lib/flowy-database/tests/grid/mod.rs b/frontend/rust-lib/flowy-database/tests/grid/mod.rs index 4539f674a1..c07432f423 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/mod.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/mod.rs @@ -1,8 +1,10 @@ mod block_test; mod cell_test; +mod database_editor; mod field_test; mod filter_test; -mod grid_editor; mod group_test; mod snapshot_test; mod sort_test; + +mod mock_data; diff --git a/frontend/rust-lib/flowy-database/tests/grid/script.rs b/frontend/rust-lib/flowy-database/tests/grid/script.rs deleted file mode 100644 index 87107881ce..0000000000 --- a/frontend/rust-lib/flowy-database/tests/grid/script.rs +++ /dev/null @@ -1,374 +0,0 @@ -use bytes::Bytes; -use flowy_client_sync::client_grid::GridBuilder; -use flowy_database::services::field::*; -use flowy_database::services::grid_meta_editor::{GridMetaEditor, GridPadBuilder}; -use flowy_database::services::row::CreateRowMetaPayload; -use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS; -use flowy_test::helper::ViewTest; -use flowy_test::FlowySDKTest; -use grid_model::entities::{ - BuildGridContext, CellChangeset, Field, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, - GridBlockInfoChangeset, GridBlockMetaSnapshot, InsertFieldParams, RowMeta, RowMetaChangeset, RowOrder, - TypeOptionDataFormat, -}; -use std::collections::HashMap; -use std::sync::Arc; -use std::time::Duration; -use strum::EnumCount; -use tokio::time::sleep; - -pub enum EditorScript { - CreateField { - params: InsertFieldParams, - }, - UpdateField { - changeset: FieldChangesetParams, - }, - DeleteField { - field_meta: FieldMeta, - }, - AssertFieldCount(usize), - AssertFieldEqual { - field_index: usize, - field_meta: FieldMeta, - }, - CreateBlock { - block: GridBlockMetaSnapshot, - }, - UpdateBlock { - changeset: GridBlockInfoChangeset, - }, - AssertBlockCount(usize), - AssertBlock { - block_index: usize, - row_count: i32, - start_row_index: i32, - }, - AssertBlockEqual { - block_index: usize, - block: GridBlockMetaSnapshot, - }, - CreateEmptyRow, - CreateRow { - context: CreateRowMetaPayload, - }, - UpdateRow { - changeset: RowMetaChangeset, - }, - AssertRow { - changeset: RowMetaChangeset, - }, - DeleteRow { - row_ids: Vec, - }, - UpdateCell { - changeset: CellChangeset, - is_err: bool, - }, - AssertRowCount(usize), - // AssertRowEqual{ row_index: usize, row: RowMeta}, - AssertGridMetaPad, -} - -pub struct GridEditorTest { - pub sdk: FlowySDKTest, - pub grid_id: String, - pub editor: Arc, - pub field_metas: Vec, - pub grid_blocks: Vec, - pub row_metas: Vec>, - pub field_count: usize, - - pub row_order_by_row_id: HashMap, -} - -impl GridEditorTest { - pub async fn new() -> Self { - let sdk = FlowySDKTest::default(); - let _ = sdk.init_user().await; - let build_context = make_template_1_grid(); - let view_data: Bytes = build_context.into(); - let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await; - let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap(); - let field_metas = editor.get_field_metas::(None).await.unwrap(); - let grid_blocks = editor.get_block_metas().await.unwrap(); - let row_metas = get_row_metas(&editor).await; - - let grid_id = test.view.id; - Self { - sdk, - grid_id, - editor, - field_metas, - grid_blocks, - row_metas, - field_count: FieldType::COUNT, - row_order_by_row_id: HashMap::default(), - } - } - - pub async fn run_scripts(&mut self, scripts: Vec) { - for script in scripts { - self.run_script(script).await; - } - } - - pub async fn run_script(&mut self, script: EditorScript) { - let grid_manager = self.sdk.grid_manager.clone(); - let pool = self.sdk.user_session.db_pool().unwrap(); - let rev_manager = self.editor.rev_manager(); - let _cache = rev_manager.revision_cache().await; - - match script { - EditorScript::CreateField { params } => { - if !self.editor.contain_field(¶ms.field.id).await { - self.field_count += 1; - } - - self.editor.insert_field(params).await.unwrap(); - self.field_metas = self.editor.get_field_metas::(None).await.unwrap(); - assert_eq!(self.field_count, self.field_metas.len()); - } - EditorScript::UpdateField { changeset: change } => { - self.editor.update_field(change).await.unwrap(); - self.field_metas = self.editor.get_field_metas::(None).await.unwrap(); - } - EditorScript::DeleteField { field_meta } => { - if self.editor.contain_field(&field_meta.id).await { - self.field_count -= 1; - } - - self.editor.delete_field(&field_meta.id).await.unwrap(); - self.field_metas = self.editor.get_field_metas::(None).await.unwrap(); - assert_eq!(self.field_count, self.field_metas.len()); - } - EditorScript::AssertFieldCount(count) => { - assert_eq!( - self.editor.get_field_metas::(None).await.unwrap().len(), - count - ); - } - EditorScript::AssertFieldEqual { - field_index, - field_meta, - } => { - let field_metas = self.editor.get_field_metas::(None).await.unwrap(); - assert_eq!(field_metas[field_index].clone(), field_meta); - } - EditorScript::CreateBlock { block } => { - self.editor.create_block(block).await.unwrap(); - self.grid_blocks = self.editor.get_block_metas().await.unwrap(); - } - EditorScript::UpdateBlock { changeset: change } => { - self.editor.update_block(change).await.unwrap(); - } - EditorScript::AssertBlockCount(count) => { - assert_eq!(self.editor.get_block_metas().await.unwrap().len(), count); - } - EditorScript::AssertBlock { - block_index, - row_count, - start_row_index, - } => { - assert_eq!(self.grid_blocks[block_index].row_count, row_count); - assert_eq!(self.grid_blocks[block_index].start_row_index, start_row_index); - } - EditorScript::AssertBlockEqual { block_index, block } => { - let blocks = self.editor.get_block_metas().await.unwrap(); - let compared_block = blocks[block_index].clone(); - assert_eq!(compared_block, block); - } - EditorScript::CreateEmptyRow => { - let row_order = self.editor.create_row(None).await.unwrap(); - self.row_order_by_row_id.insert(row_order.row_id.clone(), row_order); - self.row_metas = self.get_row_metas().await; - self.grid_blocks = self.editor.get_block_metas().await.unwrap(); - } - EditorScript::CreateRow { context } => { - let row_orders = self.editor.insert_rows(vec![context]).await.unwrap(); - for row_order in row_orders { - self.row_order_by_row_id.insert(row_order.row_id.clone(), row_order); - } - self.row_metas = self.get_row_metas().await; - self.grid_blocks = self.editor.get_block_metas().await.unwrap(); - } - EditorScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(), - EditorScript::DeleteRow { row_ids } => { - let row_orders = row_ids - .into_iter() - .map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone()) - .collect::>(); - - self.editor.delete_rows(row_orders).await.unwrap(); - self.row_metas = self.get_row_metas().await; - self.grid_blocks = self.editor.get_block_metas().await.unwrap(); - } - EditorScript::AssertRow { changeset } => { - let row = self.row_metas.iter().find(|row| row.id == changeset.row_id).unwrap(); - - if let Some(visibility) = changeset.visibility { - assert_eq!(row.visibility, visibility); - } - - if let Some(height) = changeset.height { - assert_eq!(row.height, height); - } - } - EditorScript::UpdateCell { changeset, is_err } => { - let result = self.editor.update_cell(changeset).await; - if is_err { - assert!(result.is_err()) - } else { - let _ = result.unwrap(); - self.row_metas = self.get_row_metas().await; - } - } - EditorScript::AssertRowCount(count) => { - assert_eq!(self.row_metas.len(), count); - } - EditorScript::AssertGridMetaPad => { - sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await; - let mut grid_rev_manager = grid_manager.make_grid_rev_manager(&self.grid_id, pool.clone()).unwrap(); - let grid_pad = grid_rev_manager.load::(None).await.unwrap(); - println!("{}", grid_pad.delta_str()); - } - } - } - - async fn get_row_metas(&self) -> Vec> { - get_row_metas(&self.editor).await - } -} - -async fn get_row_metas(editor: &Arc) -> Vec> { - editor - .grid_block_snapshots(None) - .await - .unwrap() - .pop() - .unwrap() - .row_metas -} - -pub fn create_text_field(grid_id: &str) -> (InsertFieldParams, FieldMeta) { - let field_meta = FieldBuilder::new(RichTextTypeOptionBuilder::default()) - .name("Name") - .visibility(true) - .build(); - - let cloned_field_meta = field_meta.clone(); - - let type_option_data = field_meta - .get_type_option_entry::(&field_meta.field_type) - .unwrap() - .protobuf_bytes() - .to_vec(); - - let field = Field { - id: field_meta.id, - name: field_meta.name, - desc: field_meta.desc, - field_type: field_meta.field_type, - frozen: field_meta.frozen, - visibility: field_meta.visibility, - width: field_meta.width, - is_primary: false, - }; - - let params = InsertFieldParams { - grid_id: grid_id.to_owned(), - field, - type_option_data, - start_field_id: None, - }; - (params, cloned_field_meta) -} - -pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldMeta) { - let single_select = SingleSelectTypeOptionBuilder::default() - .option(SelectOption::new("Done")) - .option(SelectOption::new("Progress")); - - let field_meta = FieldBuilder::new(single_select).name("Name").visibility(true).build(); - let cloned_field_meta = field_meta.clone(); - let type_option_data = field_meta - .get_type_option_entry::(&field_meta.field_type) - .unwrap() - .protobuf_bytes() - .to_vec(); - - let field = Field { - id: field_meta.id, - name: field_meta.name, - desc: field_meta.desc, - field_type: field_meta.field_type, - frozen: field_meta.frozen, - visibility: field_meta.visibility, - width: field_meta.width, - is_primary: false, - }; - - let params = InsertFieldParams { - grid_id: grid_id.to_owned(), - field, - type_option_data, - start_field_id: None, - }; - (params, cloned_field_meta) -} - -fn make_template_1_grid() -> BuildGridContext { - let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) - .name("Name") - .visibility(true) - .build(); - - // Single Select - let single_select = SingleSelectTypeOptionBuilder::default() - .option(SelectOption::new("Live")) - .option(SelectOption::new("Completed")) - .option(SelectOption::new("Planned")) - .option(SelectOption::new("Paused")); - let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build(); - - // MultiSelect - let multi_select = MultiSelectTypeOptionBuilder::default() - .option(SelectOption::new("Google")) - .option(SelectOption::new("Facebook")) - .option(SelectOption::new("Twitter")); - let multi_select_field = FieldBuilder::new(multi_select) - .name("Platform") - .visibility(true) - .build(); - - // Number - let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); - let number_field = FieldBuilder::new(number).name("Price").visibility(true).build(); - - // Date - let date = DateTypeOptionBuilder::default() - .date_format(DateFormat::US) - .time_format(TimeFormat::TwentyFourHour); - let date_field = FieldBuilder::new(date).name("Time").visibility(true).build(); - - // Checkbox - let checkbox = CheckboxTypeOptionBuilder::default(); - let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build(); - - // URL - let url = URLTypeOptionBuilder::default(); - let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); - - GridBuilder::default() - .add_field(text_field) - .add_field(single_select_field) - .add_field(multi_select_field) - .add_field(number_field) - .add_field(date_field) - .add_field(checkbox_field) - .add_field(url_field) - .add_empty_row() - .add_empty_row() - .add_empty_row() - .build() -} diff --git a/frontend/rust-lib/flowy-database/tests/grid/snapshot_test/script.rs b/frontend/rust-lib/flowy-database/tests/grid/snapshot_test/script.rs index c6e379241e..ecfb7fa0b0 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/snapshot_test/script.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/snapshot_test/script.rs @@ -1,4 +1,4 @@ -use crate::grid::grid_editor::GridEditorTest; +use crate::grid::database_editor::DatabaseEditorTest; use flowy_client_sync::client_database::{DatabaseOperations, DatabaseRevisionPad}; use flowy_revision::{RevisionSnapshot, REVISION_WRITE_INTERVAL_IN_MILLIS}; @@ -26,15 +26,15 @@ pub enum SnapshotScript { }, } -pub struct GridSnapshotTest { - inner: GridEditorTest, +pub struct DatabaseSnapshotTest { + inner: DatabaseEditorTest, pub current_snapshot: Option, pub current_revision: Option, } -impl GridSnapshotTest { +impl DatabaseSnapshotTest { pub async fn new() -> Self { - let editor_test = GridEditorTest::new_table().await; + let editor_test = DatabaseEditorTest::new_table().await; Self { inner: editor_test, current_snapshot: None, @@ -88,15 +88,15 @@ impl GridSnapshotTest { } } } -impl std::ops::Deref for GridSnapshotTest { - type Target = GridEditorTest; +impl std::ops::Deref for DatabaseSnapshotTest { + type Target = DatabaseEditorTest; fn deref(&self) -> &Self::Target { &self.inner } } -impl std::ops::DerefMut for GridSnapshotTest { +impl std::ops::DerefMut for DatabaseSnapshotTest { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } diff --git a/frontend/rust-lib/flowy-database/tests/grid/snapshot_test/test.rs b/frontend/rust-lib/flowy-database/tests/grid/snapshot_test/test.rs index 237b1b2a49..b2896de3d3 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/snapshot_test/test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/snapshot_test/test.rs @@ -1,9 +1,9 @@ use crate::grid::field_test::util::create_text_field; -use crate::grid::snapshot_test::script::{GridSnapshotTest, SnapshotScript::*}; +use crate::grid::snapshot_test::script::{DatabaseSnapshotTest, SnapshotScript::*}; #[tokio::test] async fn snapshot_create_test() { - let mut test = GridSnapshotTest::new().await; + let mut test = DatabaseSnapshotTest::new().await; let (_, field_rev) = create_text_field(&test.grid_id()); let scripts = vec![CreateField { field_rev }, WriteSnapshot]; test.run_scripts(scripts).await; @@ -19,7 +19,7 @@ async fn snapshot_create_test() { #[tokio::test] async fn snapshot_multi_version_test() { - let mut test = GridSnapshotTest::new().await; + let mut test = DatabaseSnapshotTest::new().await; let original_content = test.grid_pad().await.json_str().unwrap(); // Create a field diff --git a/frontend/rust-lib/flowy-database/tests/grid/sort_test/checkbox_and_text_test.rs b/frontend/rust-lib/flowy-database/tests/grid/sort_test/checkbox_and_text_test.rs index c24fdc150f..a7c303e5a7 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/sort_test/checkbox_and_text_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/sort_test/checkbox_and_text_test.rs @@ -1,10 +1,10 @@ -use crate::grid::sort_test::script::{GridSortTest, SortScript::*}; +use crate::grid::sort_test::script::{DatabaseSortTest, SortScript::*}; use flowy_database::entities::FieldType; use grid_model::SortCondition; #[tokio::test] async fn sort_checkbox_and_then_text_by_descending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let checkbox_field = test.get_first_field_rev(FieldType::Checkbox); let text_field = test.get_first_field_rev(FieldType::RichText); let scripts = vec![ diff --git a/frontend/rust-lib/flowy-database/tests/grid/sort_test/multi_sort_test.rs b/frontend/rust-lib/flowy-database/tests/grid/sort_test/multi_sort_test.rs index dd1a287358..cd52c7b2dd 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/sort_test/multi_sort_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/sort_test/multi_sort_test.rs @@ -1,11 +1,11 @@ -use crate::grid::sort_test::script::GridSortTest; +use crate::grid::sort_test::script::DatabaseSortTest; use crate::grid::sort_test::script::SortScript::*; use flowy_database::entities::FieldType; use grid_model::SortCondition; #[tokio::test] async fn sort_text_with_checkbox_by_ascending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let text_field = test.get_first_field_rev(FieldType::RichText).clone(); let checkbox_field = test.get_first_field_rev(FieldType::Checkbox).clone(); let scripts = vec![ diff --git a/frontend/rust-lib/flowy-database/tests/grid/sort_test/script.rs b/frontend/rust-lib/flowy-database/tests/grid/sort_test/script.rs index ccc06ab599..b419c2daab 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/sort_test/script.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/sort_test/script.rs @@ -1,4 +1,4 @@ -use crate::grid::grid_editor::GridEditorTest; +use crate::grid::database_editor::DatabaseEditorTest; use async_stream::stream; use flowy_database::entities::{AlterSortParams, CellPathParams, DeleteSortParams}; use flowy_database::services::sort::SortType; @@ -36,15 +36,15 @@ pub enum SortScript { }, } -pub struct GridSortTest { - inner: GridEditorTest, +pub struct DatabaseSortTest { + inner: DatabaseEditorTest, pub current_sort_rev: Option, recv: Option>, } -impl GridSortTest { +impl DatabaseSortTest { pub async fn new() -> Self { - let editor_test = GridEditorTest::new_table().await; + let editor_test = DatabaseEditorTest::new_table().await; Self { inner: editor_test, current_sort_rev: None, @@ -154,15 +154,15 @@ async fn assert_sort_changed( .await; } -impl std::ops::Deref for GridSortTest { - type Target = GridEditorTest; +impl std::ops::Deref for DatabaseSortTest { + type Target = DatabaseEditorTest; fn deref(&self) -> &Self::Target { &self.inner } } -impl std::ops::DerefMut for GridSortTest { +impl std::ops::DerefMut for DatabaseSortTest { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } diff --git a/frontend/rust-lib/flowy-database/tests/grid/sort_test/single_sort_test.rs b/frontend/rust-lib/flowy-database/tests/grid/sort_test/single_sort_test.rs index ddd52d709e..4327e353c3 100644 --- a/frontend/rust-lib/flowy-database/tests/grid/sort_test/single_sort_test.rs +++ b/frontend/rust-lib/flowy-database/tests/grid/sort_test/single_sort_test.rs @@ -1,10 +1,10 @@ -use crate::grid::sort_test::script::{GridSortTest, SortScript::*}; +use crate::grid::sort_test::script::{DatabaseSortTest, SortScript::*}; use flowy_database::entities::FieldType; use grid_model::SortCondition; #[tokio::test] async fn sort_text_by_ascending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let text_field = test.get_first_field_rev(FieldType::RichText); let scripts = vec![ AssertCellContentOrder { @@ -25,7 +25,7 @@ async fn sort_text_by_ascending_test() { #[tokio::test] async fn sort_change_notification_by_update_text_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let text_field = test.get_first_field_rev(FieldType::RichText).clone(); let scripts = vec![ InsertSort { @@ -57,7 +57,7 @@ async fn sort_change_notification_by_update_text_test() { #[tokio::test] async fn sort_text_by_ascending_and_delete_sort_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let text_field = test.get_first_field_rev(FieldType::RichText).clone(); let scripts = vec![InsertSort { field_rev: text_field.clone(), @@ -80,7 +80,7 @@ async fn sort_text_by_ascending_and_delete_sort_test() { #[tokio::test] async fn sort_text_by_descending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let text_field = test.get_first_field_rev(FieldType::RichText); let scripts = vec![ AssertCellContentOrder { @@ -101,7 +101,7 @@ async fn sort_text_by_descending_test() { #[tokio::test] async fn sort_checkbox_by_ascending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let checkbox_field = test.get_first_field_rev(FieldType::Checkbox); let scripts = vec![ AssertCellContentOrder { @@ -118,7 +118,7 @@ async fn sort_checkbox_by_ascending_test() { #[tokio::test] async fn sort_checkbox_by_descending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let checkbox_field = test.get_first_field_rev(FieldType::Checkbox); let scripts = vec![ AssertCellContentOrder { @@ -139,7 +139,7 @@ async fn sort_checkbox_by_descending_test() { #[tokio::test] async fn sort_date_by_ascending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let date_field = test.get_first_field_rev(FieldType::DateTime); let scripts = vec![ AssertCellContentOrder { @@ -160,7 +160,7 @@ async fn sort_date_by_ascending_test() { #[tokio::test] async fn sort_date_by_descending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let date_field = test.get_first_field_rev(FieldType::DateTime); let scripts = vec![ AssertCellContentOrder { @@ -195,7 +195,7 @@ async fn sort_date_by_descending_test() { #[tokio::test] async fn sort_number_by_descending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let number_field = test.get_first_field_rev(FieldType::Number); let scripts = vec![ AssertCellContentOrder { @@ -216,7 +216,7 @@ async fn sort_number_by_descending_test() { #[tokio::test] async fn sort_single_select_by_descending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let single_select = test.get_first_field_rev(FieldType::SingleSelect); let scripts = vec![ AssertCellContentOrder { @@ -237,7 +237,7 @@ async fn sort_single_select_by_descending_test() { #[tokio::test] async fn sort_multi_select_by_ascending_test() { - let mut test = GridSortTest::new().await; + let mut test = DatabaseSortTest::new().await; let multi_select = test.get_first_field_rev(FieldType::MultiSelect); let scripts = vec![ AssertCellContentOrder { diff --git a/frontend/rust-lib/flowy-error/src/ext/dispatch.rs b/frontend/rust-lib/flowy-error/src/ext/dispatch.rs index 0f9c61dfed..c072931af6 100644 --- a/frontend/rust-lib/flowy-error/src/ext/dispatch.rs +++ b/frontend/rust-lib/flowy-error/src/ext/dispatch.rs @@ -5,8 +5,6 @@ use std::convert::TryInto; impl lib_dispatch::Error for FlowyError { fn as_response(&self) -> AFPluginEventResponse { let bytes: Bytes = self.clone().try_into().unwrap(); - - println!("Serialize FlowyError: {:?} to event response", self); ResponseBuilder::Err().data(bytes).build() } } diff --git a/frontend/rust-lib/flowy-test/src/lib.rs b/frontend/rust-lib/flowy-test/src/lib.rs index 38a81dc584..c85544dfd2 100644 --- a/frontend/rust-lib/flowy-test/src/lib.rs +++ b/frontend/rust-lib/flowy-test/src/lib.rs @@ -3,7 +3,7 @@ pub mod helper; use crate::helper::*; -use flowy_core::{FlowySDK, FlowySDKConfig}; +use flowy_core::{AppFlowyCore, AppFlowyCoreConfig}; use flowy_document::entities::DocumentVersionPB; use flowy_net::get_client_server_configuration; use flowy_user::entities::UserProfilePB; @@ -16,11 +16,11 @@ pub mod prelude { #[derive(Clone)] pub struct FlowySDKTest { - pub inner: FlowySDK, + pub inner: AppFlowyCore, } impl std::ops::Deref for FlowySDKTest { - type Target = FlowySDK; + type Target = AppFlowyCore; fn deref(&self) -> &Self::Target { &self.inner @@ -36,10 +36,10 @@ impl std::default::Default for FlowySDKTest { impl FlowySDKTest { pub fn new(document_version: DocumentVersionPB) -> Self { let server_config = get_client_server_configuration().unwrap(); - let config = FlowySDKConfig::new(&root_dir(), nanoid!(6), server_config) + let config = AppFlowyCoreConfig::new(&root_dir(), nanoid!(6), server_config) .with_document_version(document_version) .log_filter("info", vec![]); - let sdk = std::thread::spawn(|| FlowySDK::new(config)).join().unwrap(); + let sdk = std::thread::spawn(|| AppFlowyCore::new(config)).join().unwrap(); std::mem::forget(sdk.dispatcher()); Self { inner: sdk } } diff --git a/frontend/scripts/build_sdk.cmd b/frontend/scripts/build_sdk.cmd index b13e2aa258..05a95b29c4 100644 --- a/frontend/scripts/build_sdk.cmd +++ b/frontend/scripts/build_sdk.cmd @@ -1,3 +1,3 @@ echo "Start building rust sdk" rustup show -cargo make --profile development-windows-x86 appflowy-sdk-dev \ No newline at end of file +cargo make --profile development-windows-x86 appflowy-core-dev \ No newline at end of file diff --git a/frontend/scripts/build_sdk.sh b/frontend/scripts/build_sdk.sh index f9ea3e1a58..b94776ccd0 100755 --- a/frontend/scripts/build_sdk.sh +++ b/frontend/scripts/build_sdk.sh @@ -17,15 +17,15 @@ rustup show case "$FLOWY_DEV_ENV" in Linux) - cargo make --profile "development-linux-$(uname -m)" appflowy-sdk-dev + cargo make --profile "development-linux-$(uname -m)" appflowy-core-dev ;; macOS) - cargo make --profile "development-mac-$(uname -m)" appflowy-sdk-dev + cargo make --profile "development-mac-$(uname -m)" appflowy-core-dev ;; Windows) - cargo make --profile development-windows appflowy-sdk-dev + cargo make --profile development-windows appflowy-core-dev ;; *) diff --git a/frontend/scripts/makefile/desktop.toml b/frontend/scripts/makefile/desktop.toml index 2c0d0f5cd2..fbcff0334f 100644 --- a/frontend/scripts/makefile/desktop.toml +++ b/frontend/scripts/makefile/desktop.toml @@ -1,8 +1,3 @@ -# cargo make --profile production task - -# Run the task with profile, e.g. -# cargo make --profile development-mac appflowy-sdk-dev -# cargo make --profile production-windows-x86 appflowy-sdk-dev [tasks.env_check] dependencies = ["echo_env", "install_flutter_protobuf"] @@ -15,12 +10,12 @@ condition = { env_set = [ "stable", ] } -[tasks.appflowy-sdk-dev] -mac_alias = "appflowy-sdk-dev-macos" -windows_alias = "appflowy-sdk-dev-windows" -linux_alias = "appflowy-sdk-dev-linux" +[tasks.appflowy-core-dev] +mac_alias = "appflowy-core-dev-macos" +windows_alias = "appflowy-core-dev-windows" +linux_alias = "appflowy-core-dev-linux" -[tasks.appflowy-sdk-dev-android] +[tasks.appflowy-core-dev-android] category = "Build" dependencies = ["env_check"] run_task = { name = [ @@ -29,7 +24,7 @@ run_task = { name = [ "restore-crate-type", ] } -[tasks.appflowy-sdk-dev-macos] +[tasks.appflowy-core-dev-macos] category = "Build" dependencies = ["env_check"] run_task = { name = [ @@ -39,7 +34,7 @@ run_task = { name = [ "restore-crate-type", ] } -[tasks.appflowy-sdk-dev-windows] +[tasks.appflowy-core-dev-windows] category = "Build" dependencies = ["env_check"] run_task = { name = [ @@ -49,7 +44,7 @@ run_task = { name = [ "restore-crate-type", ] } -[tasks.appflowy-sdk-dev-linux] +[tasks.appflowy-core-dev-linux] category = "Build" dependencies = ["env_check"] run_task = { name = [ @@ -59,7 +54,6 @@ run_task = { name = [ "restore-crate-type", ] } - # [tasks.sdk-build] private = true @@ -112,7 +106,7 @@ script = [ script_runner = "@duckscript" # -[tasks.appflowy-sdk-release] +[tasks.appflowy-core-release] description = "Build flowy sdk in release mode" category = "Build" dependencies = ["env_check"] @@ -144,7 +138,7 @@ linux_alias = "post-desktop-linux" private = true script = [ """ - echo "🚀 🚀 🚀 Flowy-SDK(macOS) build success" + echo "🚀 🚀 🚀 AppFlowy-Core build success" dart_ffi_dir= set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/packages/appflowy_backend/${TARGET_OS} lib = set lib${LIB_NAME}.${LIB_EXT} @@ -161,7 +155,7 @@ script_runner = "@duckscript" private = true script = [ """ - echo "🚀 🚀 🚀 Flowy-SDK(windows) build success" + echo "🚀 🚀 🚀 AppFlowy-Core build success" dart_ffi_dir= set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/windows/flutter/dart_ffi lib = set ${LIB_NAME}.${LIB_EXT} @@ -180,7 +174,7 @@ script_runner = "@duckscript" private = true script = [ """ - echo "🚀 🚀 🚀 Flowy-SDK(linux) build success" + echo "🚀 🚀 🚀 AppFlowy-Core build success" dart_ffi_dir= set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/linux/flutter/dart_ffi lib = set lib${LIB_NAME}.${LIB_EXT} diff --git a/frontend/scripts/makefile/flutter.toml b/frontend/scripts/makefile/flutter.toml index 8458378734..046c3ef7e6 100644 --- a/frontend/scripts/makefile/flutter.toml +++ b/frontend/scripts/makefile/flutter.toml @@ -4,17 +4,33 @@ windows_alias = "appflowy-windows" linux_alias = "appflowy-linux" [tasks.appflowy-macos] -dependencies = ["appflowy-sdk-release"] -run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] } +dependencies = ["appflowy-core-release"] +run_task = { name = [ + "code_generation", + "set-app-version", + "flutter-build", + "copy-to-product", +] } script_runner = "@shell" [tasks.appflowy-windows] -dependencies = ["appflowy-sdk-release"] -run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] } +dependencies = ["appflowy-core-release"] +run_task = { name = [ + "code_generation", + "set-app-version", + "flutter-build", + "copy-to-product", +] } [tasks.appflowy-linux] -dependencies = ["appflowy-sdk-release"] -run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product", "create-release-archive"] } +dependencies = ["appflowy-core-release"] +run_task = { name = [ + "code_generation", + "set-app-version", + "flutter-build", + "copy-to-product", + "create-release-archive", +] } script_runner = "@shell" [tasks.appflowy-dev] @@ -23,17 +39,32 @@ windows_alias = "appflowy-windows-dev" linux_alias = "appflowy-linux-dev" [tasks.appflowy-macos-dev] -dependencies = ["appflowy-sdk-dev"] -run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] } +dependencies = ["appflowy-core-dev"] +run_task = { name = [ + "code_generation", + "set-app-version", + "flutter-build", + "copy-to-product", +] } script_runner = "@shell" [tasks.appflowy-windows-dev] -dependencies = ["appflowy-sdk-dev"] -run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] } +dependencies = ["appflowy-core-dev"] +run_task = { name = [ + "code_generation", + "set-app-version", + "flutter-build", + "copy-to-product", +] } [tasks.appflowy-linux-dev] -dependencies = ["appflowy-sdk-dev"] -run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] } +dependencies = ["appflowy-core-dev"] +run_task = { name = [ + "code_generation", + "set-app-version", + "flutter-build", + "copy-to-product", +] } script_runner = "@shell" [tasks.copy-to-product] @@ -96,15 +127,13 @@ script = [ script_runner = "@duckscript" [tasks.set-app-version] -script = [ - """ +script = [""" if is_empty ${APP_VERSION} APP_VERSION = set ${CURRENT_APP_VERSION} set_env APP_VERSION ${CURRENT_APP_VERSION} end echo APP_VERSION: ${APP_VERSION} - """, -] + """] script_runner = "@duckscript" # The following tasks will create an archive that will be used on the GitHub Releases section @@ -118,7 +147,7 @@ linux_alias = "create-release-archive-linux" [tasks.create-release-archive-linux] script = [ "cd ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/product/${APP_VERSION}/${TARGET_OS}/Release", - "tar -czf ${PRODUCT_NAME}-${TARGET_OS}-x86.tar.gz *" + "tar -czf ${PRODUCT_NAME}-${TARGET_OS}-x86.tar.gz *", ] [tasks.create-release-archive-windows] @@ -136,36 +165,30 @@ script = [ ] [tasks.flutter-build] -script = [ - """ +script = [""" cd app_flowy/ flutter clean flutter pub get flutter build ${TARGET_OS} --${BUILD_FLAG} --build-name=${APP_VERSION} - """, -] + """] script_runner = "@shell" [tasks.flutter-build.linux] -script = [ - """ +script = [""" cd app_flowy/ flutter clean flutter pub get flutter build ${TARGET_OS} --${BUILD_FLAG} - """, -] + """] script_runner = "@shell" [tasks.flutter-build.windows] -script = [ - """ +script = [""" cd app_flowy exec cmd.exe /c flutter clean exec cmd.exe /c flutter pub get exec cmd.exe /c flutter build ${TARGET_OS} --${BUILD_FLAG} - """, -] + """] script_runner = "@duckscript" [tasks.code_generation] @@ -177,7 +200,7 @@ script = [ flutter packages pub get flutter packages pub run easy_localization:generate -S assets/translations/ -f keys -o locale_keys.g.dart -S assets/translations -s en.json flutter packages pub run build_runner build --delete-conflicting-outputs - """ + """, ] [tasks.code_generation.windows] @@ -189,7 +212,7 @@ script = [ exec cmd.exe /c flutter packages pub get exec cmd.exe /c flutter packages pub run easy_localization:generate -S assets/translations/ -f keys -o locale_keys.g.dart -S assets/translations -s en.json exec cmd.exe /c flutter packages pub run build_runner build --delete-conflicting-outputs - """ + """, ] [tasks.dry_code_generation] @@ -199,7 +222,7 @@ script = [ cd app_flowy flutter packages pub run easy_localization:generate -S assets/translations/ -f keys -o locale_keys.g.dart -S assets/translations -s en.json flutter packages pub run build_runner build --delete-conflicting-outputs - """ + """, ] [tasks.dry_code_generation.windows] @@ -209,5 +232,5 @@ script = [ cd ./app_flowy/ exec cmd.exe /c flutter packages pub run easy_localization:generate -S assets/translations/ -f keys -o locale_keys.g.dart -S assets/translations -s en.json exec cmd.exe /c flutter packages pub run build_runner build --delete-conflicting-outputs - """ + """, ] diff --git a/frontend/scripts/makefile/tauri.toml b/frontend/scripts/makefile/tauri.toml index d753ac16fa..988ed18d71 100644 --- a/frontend/scripts/makefile/tauri.toml +++ b/frontend/scripts/makefile/tauri.toml @@ -7,6 +7,7 @@ script = [""" script_runner = "@shell" [tasks.tauri_dev] +env = { RUST_LOG = "debug" } script = [""" cd appflowy_tauri npm run tauri dev diff --git a/frontend/scripts/makefile/tests.toml b/frontend/scripts/makefile/tests.toml index 66a1383277..3d2d3c9708 100644 --- a/frontend/scripts/makefile/tests.toml +++ b/frontend/scripts/makefile/tests.toml @@ -18,6 +18,7 @@ cargo make --profile test-linux dart_unit_test_inner script_runner = "@shell" [tasks.dart_unit_test_inner] +env = { RUST_LOG = "info" } dependencies = ["build-test-lib"] description = "Run flutter unit tests" script = ''' @@ -29,6 +30,7 @@ flutter test --dart-define=RUST_LOG=${TEST_RUST_LOG} --concurrency=1 run_task = { name = ["rust_lib_unit_test", "shared_lib_unit_test"] } [tasks.rust_lib_unit_test] +env = { RUST_LOG = "info" } description = "Run rust-lib unit tests" script = ''' cd rust-lib @@ -36,6 +38,7 @@ cargo test --no-default-features --features="sync, rev-sqlite" ''' [tasks.shared_lib_unit_test] +env = { RUST_LOG = "info" } description = "Run shared-lib unit test" script = ''' cd ../shared-lib @@ -62,8 +65,7 @@ fi [tasks.clean_profraw_files] description = "Cleans profraw files that are created by `cargo test`" script_runner = "@duckscript" -script = [ - """ +script = [""" rust_lib_profs = glob_array ./rust-lib/**/*.profraw for prof in ${rust_lib_profs} full_path = canonicalize ${prof} @@ -76,14 +78,12 @@ script = [ rm ${full_path} end - """ -] + """] [tasks.run_rustlib_coverage_tests] description = "Run tests with coverage instrumentation" script_runner = "@shell" -script = [ - """ +script = [""" echo --- Running coverage tests --- cd rust-lib/ @@ -91,14 +91,12 @@ script = [ RUSTFLAGS='-C instrument-coverage' \ LLVM_PROFILE_FILE='prof-%p-%m.profraw' \ cargo test --no-default-features --features="sync,rev-sqlite" - """ -] + """] [tasks.run_sharedlib_coverage_tests] description = "Run tests with coverage instrumentation" script_runner = "@shell" -script = [ - """ +script = [""" echo --- Running coverage tests --- cd ../shared-lib @@ -107,8 +105,7 @@ script = [ LLVM_PROFILE_FILE='prof-%p-%m.profraw' \ cargo test --no-default-features - """ -] + """] [tasks.get_rustlib_grcov_report] description = "Get `grcov` HTML report for test coverage for rust-lib" @@ -128,7 +125,7 @@ script = [ --output-path target/coverage-html echo "--- Done! Generated HTML report under 'target/coverage-html' for rustlib." - """ + """, ] [tasks.get_sharedlib_grcov_report] @@ -149,21 +146,20 @@ script = [ --output-path target/coverage-html echo "--- Done! Generated HTML report under 'target/coverage-html' for sharedlib." - """ + """, ] [tasks.get_grcov_report] description = "Get `grcov` HTML report for test coverage" run_task = { name = [ "get_rustlib_grcov_report", - "get_sharedlib_grcov_report" + "get_sharedlib_grcov_report", ], parallel = true } [tasks.get_sharedlib_lcov_report] description = "Generates `lcov` report for `shared-lib`" script_runner = "@shell" -script = [ - """ +script = [""" echo Getting 'lcov' results for 'shared-lib' cd ../shared-lib @@ -178,14 +174,12 @@ script = [ --output-path target/coverage.lcov echo "--- Done! Generated 'target/coverage.lcov' sharedlib." - """ -] + """] [tasks.get_rustlib_lcov_report] description = "Generates `lcov` report for `rust-lib`" script_runner = "@shell" -script = [ - """ +script = [""" echo Getting 'lcov' results for 'rust-lib' cd rust-lib/ @@ -200,23 +194,22 @@ script = [ --output-path target/coverage.lcov echo "--- Done! Generated 'target/coverage.lcov' for rustlib." - """ -] + """] [tasks.get_lcov_report] description = "Get `lcov` reports for test coverage" run_task = { name = [ "get_sharedlib_lcov_report", - "get_rustlib_lcov_report" + "get_rustlib_lcov_report", ], parallel = true } [tasks.rust_unit_test_with_coverage] description = "Run rust unit test with code coverage" run_task = { name = [ - "check_grcov", - 'appflowy-flutter-deps-tools', - "run_rustlib_coverage_tests", - "run_sharedlib_coverage_tests", - "get_lcov_report", - "clean_profraw_files" - ]} + "check_grcov", + 'appflowy-flutter-deps-tools', + "run_rustlib_coverage_tests", + "run_sharedlib_coverage_tests", + "get_lcov_report", + "clean_profraw_files", +] } diff --git a/shared-lib/lib-infra/Cargo.toml b/shared-lib/lib-infra/Cargo.toml index f62edfb615..2070ac9c41 100644 --- a/shared-lib/lib-infra/Cargo.toml +++ b/shared-lib/lib-infra/Cargo.toml @@ -10,7 +10,7 @@ chrono = "0.4.19" bytes = { version = "1.0" } pin-project = "1.0.12" futures-core = { version = "0.3" } -tokio = { version = "1.0", features = ["time", "rt"] } +tokio = { version = "1", features = ["time", "rt"] } rand = "0.8.5" async-trait = "0.1.59" md5 = "0.7.0"