mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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
This commit is contained in:
parent
d09574951b
commit
069519589e
2
.github/workflows/rust_ci.yaml
vendored
2
.github/workflows/rust_ci.yaml
vendored
@ -59,7 +59,7 @@ jobs:
|
|||||||
- name: Build FlowySDK
|
- name: Build FlowySDK
|
||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
run: |
|
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
|
- name: rustfmt rust-lib
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
|
2
frontend/.vscode/launch.json
vendored
2
frontend/.vscode/launch.json
vendored
@ -23,7 +23,7 @@
|
|||||||
"type": "dart",
|
"type": "dart",
|
||||||
"preLaunchTask": "AF: build_flowy_sdk",
|
"preLaunchTask": "AF: build_flowy_sdk",
|
||||||
"env": {
|
"env": {
|
||||||
"RUST_LOG": "info"
|
"RUST_LOG": "debug"
|
||||||
},
|
},
|
||||||
"cwd": "${workspaceRoot}/app_flowy"
|
"cwd": "${workspaceRoot}/app_flowy"
|
||||||
},
|
},
|
||||||
|
2
frontend/.vscode/tasks.json
vendored
2
frontend/.vscode/tasks.json
vendored
@ -48,7 +48,7 @@
|
|||||||
{
|
{
|
||||||
"label": "AF: build_flowy_sdk_for_android",
|
"label": "AF: build_flowy_sdk_for_android",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "cargo make --profile development-android appflowy-sdk-dev-android",
|
"command": "cargo make --profile development-android appflowy-core-dev-android",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
|
@ -18,7 +18,7 @@ on_error_task = "catch"
|
|||||||
run_task = { name = ["restore-crate-type"] }
|
run_task = { name = ["restore-crate-type"] }
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
RUST_LOG = "info"
|
RUST_LOG = "debug"
|
||||||
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||||
|
@ -186,43 +186,20 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void initializeGroups(List<GroupPB> groupsData) {
|
void initializeGroups(List<GroupPB> groups) {
|
||||||
for (var controller in groupControllers.values) {
|
for (var controller in groupControllers.values) {
|
||||||
controller.dispose();
|
controller.dispose();
|
||||||
}
|
}
|
||||||
groupControllers.clear();
|
groupControllers.clear();
|
||||||
boardController.clear();
|
boardController.clear();
|
||||||
|
|
||||||
//
|
boardController.addGroups(groups
|
||||||
List<AppFlowyGroupData> groups = groupsData
|
|
||||||
.where((group) => fieldController.getField(group.fieldId) != null)
|
.where((group) => fieldController.getField(group.fieldId) != null)
|
||||||
.map((group) {
|
.map((group) => initializeGroupData(group))
|
||||||
return AppFlowyGroupData(
|
.toList());
|
||||||
id: group.groupId,
|
|
||||||
name: group.desc,
|
|
||||||
items: _buildGroupItems(group),
|
|
||||||
customData: GroupData(
|
|
||||||
group: group,
|
|
||||||
fieldInfo: fieldController.getField(group.fieldId)!,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
boardController.addGroups(groups);
|
|
||||||
|
|
||||||
for (final group in groupsData) {
|
for (final group in groups) {
|
||||||
final delegate = GroupControllerDelegateImpl(
|
final controller = initializeGroupController(group);
|
||||||
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();
|
|
||||||
groupControllers[controller.group.groupId] = (controller);
|
groupControllers[controller.group.groupId] = (controller);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,11 +222,15 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
},
|
},
|
||||||
onDeletedGroup: (groupIds) {
|
onDeletedGroup: (groupIds) {
|
||||||
if (isClosed) return;
|
if (isClosed) return;
|
||||||
//
|
boardController.removeGroups(groupIds);
|
||||||
},
|
},
|
||||||
onInsertedGroup: (insertedGroups) {
|
onInsertedGroup: (insertedGroup) {
|
||||||
if (isClosed) return;
|
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) {
|
onUpdatedGroup: (updatedGroups) {
|
||||||
if (isClosed) return;
|
if (isClosed) return;
|
||||||
@ -294,6 +275,35 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@freezed
|
||||||
|
@ -17,7 +17,7 @@ typedef OnGridChanged = void Function(DatabasePB);
|
|||||||
typedef DidLoadGroups = void Function(List<GroupPB>);
|
typedef DidLoadGroups = void Function(List<GroupPB>);
|
||||||
typedef OnUpdatedGroup = void Function(List<GroupPB>);
|
typedef OnUpdatedGroup = void Function(List<GroupPB>);
|
||||||
typedef OnDeletedGroup = void Function(List<String>);
|
typedef OnDeletedGroup = void Function(List<String>);
|
||||||
typedef OnInsertedGroup = void Function(List<InsertedGroupPB>);
|
typedef OnInsertedGroup = void Function(InsertedGroupPB);
|
||||||
typedef OnResetGroups = void Function(List<GroupPB>);
|
typedef OnResetGroups = void Function(List<GroupPB>);
|
||||||
|
|
||||||
typedef OnRowsChanged = void Function(
|
typedef OnRowsChanged = void Function(
|
||||||
@ -90,8 +90,8 @@ class BoardDataController {
|
|||||||
onDeletedGroup.call(changeset.deletedGroups);
|
onDeletedGroup.call(changeset.deletedGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changeset.insertedGroups.isNotEmpty) {
|
for (final insertedGroup in changeset.insertedGroups) {
|
||||||
onInsertedGroup.call(changeset.insertedGroups);
|
onInsertedGroup.call(insertedGroup);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(e) => _onError?.call(e),
|
(e) => _onError?.call(e),
|
||||||
|
@ -46,7 +46,7 @@ class BoardListener {
|
|||||||
case DatabaseNotification.DidGroupByNewField:
|
case DatabaseNotification.DidGroupByNewField:
|
||||||
result.fold(
|
result.fold(
|
||||||
(payload) => _groupByNewFieldNotifier?.value =
|
(payload) => _groupByNewFieldNotifier?.value =
|
||||||
left(GroupViewChangesetPB.fromBuffer(payload).newGroups),
|
left(GroupViewChangesetPB.fromBuffer(payload).initialGroups),
|
||||||
(error) => _groupByNewFieldNotifier?.value = right(error),
|
(error) => _groupByNewFieldNotifier?.value = right(error),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -270,7 +270,7 @@ class GridCellController<T, D> extends Equatable {
|
|||||||
/// You can set [deduplicate] to true (default is false) to reduce the save operation.
|
/// 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].
|
/// It's useful when you call this method when user editing the [TextField].
|
||||||
/// The default debounce interval is 300 milliseconds.
|
/// The default debounce interval is 300 milliseconds.
|
||||||
void saveCellData(
|
Future<void> saveCellData(
|
||||||
D data, {
|
D data, {
|
||||||
bool deduplicate = false,
|
bool deduplicate = false,
|
||||||
void Function(Option<FlowyError>)? onFinish,
|
void Function(Option<FlowyError>)? onFinish,
|
||||||
|
@ -14,13 +14,16 @@ class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
|
|||||||
}) : super(URLCellEditorState.initial(cellController)) {
|
}) : super(URLCellEditorState.initial(cellController)) {
|
||||||
on<URLCellEditorEvent>(
|
on<URLCellEditorEvent>(
|
||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
event.when(
|
await event.when(
|
||||||
initial: () {
|
initial: () {
|
||||||
_startListening();
|
_startListening();
|
||||||
},
|
},
|
||||||
updateText: (text) {
|
updateText: (text) async {
|
||||||
cellController.saveCellData(text, deduplicate: true);
|
await cellController.saveCellData(text);
|
||||||
emit(state.copyWith(content: text));
|
emit(state.copyWith(
|
||||||
|
content: text,
|
||||||
|
isFinishEditing: true,
|
||||||
|
));
|
||||||
},
|
},
|
||||||
didReceiveCellUpdate: (cellData) {
|
didReceiveCellUpdate: (cellData) {
|
||||||
emit(state.copyWith(content: cellData?.content ?? ""));
|
emit(state.copyWith(content: cellData?.content ?? ""));
|
||||||
@ -63,12 +66,14 @@ class URLCellEditorEvent with _$URLCellEditorEvent {
|
|||||||
class URLCellEditorState with _$URLCellEditorState {
|
class URLCellEditorState with _$URLCellEditorState {
|
||||||
const factory URLCellEditorState({
|
const factory URLCellEditorState({
|
||||||
required String content,
|
required String content,
|
||||||
|
required bool isFinishEditing,
|
||||||
}) = _URLCellEditorState;
|
}) = _URLCellEditorState;
|
||||||
|
|
||||||
factory URLCellEditorState.initial(GridURLCellController context) {
|
factory URLCellEditorState.initial(GridURLCellController context) {
|
||||||
final cellData = context.getCellData();
|
final cellData = context.getCellData();
|
||||||
return URLCellEditorState(
|
return URLCellEditorState(
|
||||||
content: cellData?.content ?? "",
|
content: cellData?.content ?? "",
|
||||||
|
isFinishEditing: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,13 @@ import 'dart:async';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class URLCellEditor extends StatefulWidget {
|
class URLCellEditor extends StatefulWidget {
|
||||||
|
final VoidCallback onExit;
|
||||||
final GridURLCellController cellController;
|
final GridURLCellController cellController;
|
||||||
const URLCellEditor({required this.cellController, Key? key})
|
const URLCellEditor({
|
||||||
: super(key: key);
|
required this.cellController,
|
||||||
|
required this.onExit,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<URLCellEditor> createState() => _URLCellEditorState();
|
State<URLCellEditor> createState() => _URLCellEditorState();
|
||||||
@ -36,12 +40,17 @@ class _URLCellEditorState extends State<URLCellEditor> {
|
|||||||
if (_controller.text != state.content) {
|
if (_controller.text != state.content) {
|
||||||
_controller.text = state.content;
|
_controller.text = state.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.isFinishEditing) {
|
||||||
|
widget.onExit();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: TextField(
|
child: TextField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
onChanged: (value) => focusChanged(),
|
onSubmitted: (value) => focusChanged(),
|
||||||
maxLines: null,
|
onEditingComplete: () => focusChanged(),
|
||||||
|
maxLines: 1,
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
@ -57,11 +66,10 @@ class _URLCellEditorState extends State<URLCellEditor> {
|
|||||||
@override
|
@override
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
_cellBloc.close();
|
_cellBloc.close();
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> focusChanged() async {
|
void focusChanged() {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
if (_cellBloc.isClosed == false &&
|
if (_cellBloc.isClosed == false &&
|
||||||
_controller.text != _cellBloc.state.content) {
|
_controller.text != _cellBloc.state.content) {
|
||||||
@ -72,9 +80,13 @@ class _URLCellEditorState extends State<URLCellEditor> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class URLEditorPopover extends StatelessWidget {
|
class URLEditorPopover extends StatelessWidget {
|
||||||
|
final VoidCallback onExit;
|
||||||
final GridURLCellController cellController;
|
final GridURLCellController cellController;
|
||||||
const URLEditorPopover({required this.cellController, Key? key})
|
const URLEditorPopover({
|
||||||
: super(key: key);
|
required this.cellController,
|
||||||
|
required this.onExit,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -84,6 +96,7 @@ class URLEditorPopover extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(6),
|
padding: const EdgeInsets.all(6),
|
||||||
child: URLCellEditor(
|
child: URLCellEditor(
|
||||||
cellController: cellController,
|
cellController: cellController,
|
||||||
|
onExit: onExit,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -142,6 +142,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
|
|||||||
return URLEditorPopover(
|
return URLEditorPopover(
|
||||||
cellController: widget.cellControllerBuilder.build()
|
cellController: widget.cellControllerBuilder.build()
|
||||||
as GridURLCellController,
|
as GridURLCellController,
|
||||||
|
onExit: () => _popoverController.close(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onClose: () {
|
onClose: () {
|
||||||
@ -219,6 +220,7 @@ class _EditURLAccessoryState extends State<_EditURLAccessory>
|
|||||||
return URLEditorPopover(
|
return URLEditorPopover(
|
||||||
cellController:
|
cellController:
|
||||||
widget.cellControllerBuilder.build() as GridURLCellController,
|
widget.cellControllerBuilder.build() as GridURLCellController,
|
||||||
|
onExit: () => _popoverController.close(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -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 data_path = tauri::api::path::data_dir().unwrap();
|
||||||
let path = format!("{}/AppFlowy", data_path.to_str().unwrap());
|
let path = format!("{}/AppFlowy", data_path.to_str().unwrap());
|
||||||
let server_config = get_client_server_configuration().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()]);
|
.log_filter("trace", vec!["appflowy_tauri".to_string()]);
|
||||||
FlowySDK::new(config)
|
AppFlowyCore::new(config)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use flowy_core::FlowySDK;
|
use flowy_core::AppFlowyCore;
|
||||||
use lib_dispatch::prelude::{
|
use lib_dispatch::prelude::{
|
||||||
AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode,
|
AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode,
|
||||||
};
|
};
|
||||||
@ -39,7 +39,7 @@ pub async fn invoke_request(
|
|||||||
app_handler: AppHandle<Wry>,
|
app_handler: AppHandle<Wry>,
|
||||||
) -> AFTauriResponse {
|
) -> AFTauriResponse {
|
||||||
let request: AFPluginRequest = request.into();
|
let request: AFPluginRequest = request.into();
|
||||||
let state: State<FlowySDK> = app_handler.state();
|
let state: State<AppFlowyCore> = app_handler.state();
|
||||||
let dispatcher = state.inner().dispatcher();
|
let dispatcher = state.inner().dispatcher();
|
||||||
let response = AFPluginDispatcher::async_send(dispatcher, request).await;
|
let response = AFPluginDispatcher::async_send(dispatcher, request).await;
|
||||||
response.into()
|
response.into()
|
||||||
|
@ -20,7 +20,7 @@ use parking_lot::RwLock;
|
|||||||
use std::{ffi::CStr, os::raw::c_char};
|
use std::{ffi::CStr, os::raw::c_char};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref FLOWY_SDK: RwLock<Option<FlowySDK>> = RwLock::new(None);
|
static ref APPFLOWY_CORE: RwLock<Option<AppFlowyCore>> = RwLock::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[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 server_config = get_client_server_configuration().unwrap();
|
||||||
let log_crates = vec!["flowy-ffi".to_string()];
|
let log_crates = vec!["flowy-ffi".to_string()];
|
||||||
let config = FlowySDKConfig::new(path, "appflowy".to_string(), server_config).log_filter("info", log_crates);
|
let config = AppFlowyCoreConfig::new(path, "appflowy".to_string(), server_config).log_filter("info", log_crates);
|
||||||
*FLOWY_SDK.write() = Some(FlowySDK::new(config));
|
*APPFLOWY_CORE.write() = Some(AppFlowyCore::new(config));
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@ -46,7 +46,7 @@ pub extern "C" fn async_event(port: i64, input: *const u8, len: usize) {
|
|||||||
port
|
port
|
||||||
);
|
);
|
||||||
|
|
||||||
let dispatcher = match FLOWY_SDK.read().as_ref() {
|
let dispatcher = match APPFLOWY_CORE.read().as_ref() {
|
||||||
None => {
|
None => {
|
||||||
log::error!("sdk not init yet.");
|
log::error!("sdk not init yet.");
|
||||||
return;
|
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();
|
let request: AFPluginRequest = FFIRequest::from_u8_pointer(input, len).into();
|
||||||
log::trace!("[FFI]: {} Sync Event: {:?}", &request.id, &request.event,);
|
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 => {
|
None => {
|
||||||
log::error!("sdk not init yet.");
|
log::error!("sdk not init yet.");
|
||||||
return forget_rust(Vec::default());
|
return forget_rust(Vec::default());
|
||||||
|
@ -34,31 +34,31 @@ use user_model::UserProfile;
|
|||||||
static INIT_LOG: AtomicBool = AtomicBool::new(false);
|
static INIT_LOG: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FlowySDKConfig {
|
pub struct AppFlowyCoreConfig {
|
||||||
/// Different `FlowySDK` instance should have different name
|
/// Different `AppFlowyCoreConfig` instance should have different name
|
||||||
name: String,
|
name: String,
|
||||||
/// Panics if the `root` path is not existing
|
/// Panics if the `root` path is not existing
|
||||||
root: String,
|
storage_path: String,
|
||||||
log_filter: String,
|
log_filter: String,
|
||||||
server_config: ClientServerConfiguration,
|
server_config: ClientServerConfiguration,
|
||||||
pub document: DocumentConfig,
|
pub document: DocumentConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for FlowySDKConfig {
|
impl fmt::Debug for AppFlowyCoreConfig {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("FlowySDKConfig")
|
f.debug_struct("AppFlowyCoreConfig")
|
||||||
.field("root", &self.root)
|
.field("storage_path", &self.storage_path)
|
||||||
.field("server-config", &self.server_config)
|
.field("server-config", &self.server_config)
|
||||||
.field("document-config", &self.document)
|
.field("document-config", &self.document)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlowySDKConfig {
|
impl AppFlowyCoreConfig {
|
||||||
pub fn new(root: &str, name: String, server_config: ClientServerConfiguration) -> Self {
|
pub fn new(root: &str, name: String, server_config: ClientServerConfiguration) -> Self {
|
||||||
FlowySDKConfig {
|
AppFlowyCoreConfig {
|
||||||
name,
|
name,
|
||||||
root: root.to_owned(),
|
storage_path: root.to_owned(),
|
||||||
log_filter: create_log_filter("info".to_owned(), vec![]),
|
log_filter: create_log_filter("info".to_owned(), vec![]),
|
||||||
server_config,
|
server_config,
|
||||||
document: DocumentConfig::default(),
|
document: DocumentConfig::default(),
|
||||||
@ -106,9 +106,9 @@ fn create_log_filter(level: String, with_crates: Vec<String>) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FlowySDK {
|
pub struct AppFlowyCore {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub config: FlowySDKConfig,
|
pub config: AppFlowyCoreConfig,
|
||||||
pub user_session: Arc<UserSession>,
|
pub user_session: Arc<UserSession>,
|
||||||
pub document_manager: Arc<DocumentManager>,
|
pub document_manager: Arc<DocumentManager>,
|
||||||
pub folder_manager: Arc<FolderManager>,
|
pub folder_manager: Arc<FolderManager>,
|
||||||
@ -119,10 +119,10 @@ pub struct FlowySDK {
|
|||||||
pub task_dispatcher: Arc<RwLock<TaskDispatcher>>,
|
pub task_dispatcher: Arc<RwLock<TaskDispatcher>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlowySDK {
|
impl AppFlowyCore {
|
||||||
pub fn new(config: FlowySDKConfig) -> Self {
|
pub fn new(config: AppFlowyCoreConfig) -> Self {
|
||||||
init_log(&config);
|
init_log(&config);
|
||||||
init_kv(&config.root);
|
init_kv(&config.storage_path);
|
||||||
tracing::debug!("🔥 {:?}", config);
|
tracing::debug!("🔥 {:?}", config);
|
||||||
let runtime = tokio_default_runtime().unwrap();
|
let runtime = tokio_default_runtime().unwrap();
|
||||||
let task_scheduler = TaskDispatcher::new(Duration::from_secs(2));
|
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) {
|
if !INIT_LOG.load(Ordering::SeqCst) {
|
||||||
INIT_LOG.store(true, 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)
|
.env_filter(&config.log_filter)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_user_session(
|
fn mk_user_session(
|
||||||
config: &FlowySDKConfig,
|
config: &AppFlowyCoreConfig,
|
||||||
local_server: &Option<Arc<LocalServer>>,
|
local_server: &Option<Arc<LocalServer>>,
|
||||||
server_config: &ClientServerConfiguration,
|
server_config: &ClientServerConfiguration,
|
||||||
) -> Arc<UserSession> {
|
) -> Arc<UserSession> {
|
||||||
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);
|
let cloud_service = UserDepsResolver::resolve(local_server, server_config);
|
||||||
Arc::new(UserSession::new(user_config, cloud_service))
|
Arc::new(UserSession::new(user_config, cloud_service))
|
||||||
}
|
}
|
||||||
@ -282,7 +282,7 @@ struct UserStatusListener {
|
|||||||
folder_manager: Arc<FolderManager>,
|
folder_manager: Arc<FolderManager>,
|
||||||
grid_manager: Arc<DatabaseManager>,
|
grid_manager: Arc<DatabaseManager>,
|
||||||
ws_conn: Arc<FlowyWebSocketConnect>,
|
ws_conn: Arc<FlowyWebSocketConnect>,
|
||||||
config: FlowySDKConfig,
|
config: AppFlowyCoreConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserStatusListener {
|
impl UserStatusListener {
|
||||||
|
@ -135,7 +135,7 @@ pub struct GroupViewChangesetPB {
|
|||||||
pub inserted_groups: Vec<InsertedGroupPB>,
|
pub inserted_groups: Vec<InsertedGroupPB>,
|
||||||
|
|
||||||
#[pb(index = 3)]
|
#[pb(index = 3)]
|
||||||
pub new_groups: Vec<GroupPB>,
|
pub initial_groups: Vec<GroupPB>,
|
||||||
|
|
||||||
#[pb(index = 4)]
|
#[pb(index = 4)]
|
||||||
pub deleted_groups: Vec<String>,
|
pub deleted_groups: Vec<String>,
|
||||||
@ -146,7 +146,7 @@ pub struct GroupViewChangesetPB {
|
|||||||
|
|
||||||
impl GroupViewChangesetPB {
|
impl GroupViewChangesetPB {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.new_groups.is_empty()
|
self.initial_groups.is_empty()
|
||||||
&& self.inserted_groups.is_empty()
|
&& self.inserted_groups.is_empty()
|
||||||
&& self.deleted_groups.is_empty()
|
&& self.deleted_groups.is_empty()
|
||||||
&& self.update_groups.is_empty()
|
&& self.update_groups.is_empty()
|
||||||
|
@ -76,7 +76,7 @@ pub fn apply_cell_data_changeset<C: ToCellChangesetString, T: AsRef<FieldRevisio
|
|||||||
Ok(TypeCellData::new(cell_str, field_type).to_json())
|
Ok(TypeCellData::new(cell_str, field_type).to_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(
|
pub fn get_type_cell_protobuf<T: TryInto<TypeCellData, Error = FlowyError> + Debug>(
|
||||||
data: T,
|
data: T,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
cell_data_cache: Option<AtomicCellDataCache>,
|
cell_data_cache: Option<AtomicCellDataCache>,
|
||||||
@ -85,7 +85,13 @@ pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debu
|
|||||||
match data.try_into() {
|
match data.try_into() {
|
||||||
Ok(type_cell_data) => {
|
Ok(type_cell_data) => {
|
||||||
let TypeCellData { cell_str, field_type } = 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),
|
Ok(cell_bytes) => (field_type, cell_bytes),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Decode cell data failed, {:?}", e);
|
tracing::error!("Decode cell data failed, {:?}", e);
|
||||||
@ -97,12 +103,30 @@ pub fn decode_type_cell_data<T: TryInto<TypeCellData, Error = FlowyError> + Debu
|
|||||||
// It's okay to ignore this error, because it's okay that the current cell can't
|
// 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
|
// 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.
|
// the type of the data of cell is Number.
|
||||||
|
|
||||||
(to_field_type, CellProtobufBlob::default())
|
(to_field_type, CellProtobufBlob::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_type_cell_data<CellData, Output>(
|
||||||
|
data: CellData,
|
||||||
|
field_rev: &FieldRevision,
|
||||||
|
cell_data_cache: Option<AtomicCellDataCache>,
|
||||||
|
) -> Option<Output>
|
||||||
|
where
|
||||||
|
CellData: TryInto<TypeCellData, Error = FlowyError> + 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`
|
/// 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`
|
/// 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<T: TryInto<TypeCellData, Error = FlowyError> + Debu
|
|||||||
///
|
///
|
||||||
/// returns: CellBytes
|
/// returns: CellBytes
|
||||||
///
|
///
|
||||||
pub fn try_decode_cell_str(
|
pub fn try_decode_cell_str_to_cell_protobuf(
|
||||||
cell_str: String,
|
cell_str: String,
|
||||||
from_field_type: &FieldType,
|
from_field_type: &FieldType,
|
||||||
to_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<T: Default + 'static>(
|
||||||
|
cell_str: String,
|
||||||
|
from_field_type: &FieldType,
|
||||||
|
to_field_type: &FieldType,
|
||||||
|
field_rev: &FieldRevision,
|
||||||
|
cell_data_cache: Option<AtomicCellDataCache>,
|
||||||
|
) -> Option<T> {
|
||||||
|
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::<T>()
|
||||||
|
}
|
||||||
/// Returns a string that represents the current field_type's cell data.
|
/// 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
|
/// For example, The string of the Multi-Select cell will be a list of the option's name
|
||||||
/// separated by a comma.
|
/// separated by a comma.
|
||||||
|
@ -497,7 +497,7 @@ impl BoxCellData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unbox_or_none<T>(self) -> Option<T>
|
pub(crate) fn unbox_or_none<T>(self) -> Option<T>
|
||||||
where
|
where
|
||||||
T: Default + 'static,
|
T: Default + 'static,
|
||||||
{
|
{
|
||||||
|
@ -49,6 +49,15 @@ impl URLCellData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<URLCellDataPB> for URLCellData {
|
||||||
|
fn from(data: URLCellDataPB) -> Self {
|
||||||
|
Self {
|
||||||
|
url: data.url,
|
||||||
|
content: data.content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<str> for URLCellData {
|
impl AsRef<str> for URLCellData {
|
||||||
fn as_ref(&self) -> &str {
|
fn as_ref(&self) -> &str {
|
||||||
&self.url
|
&self.url
|
||||||
|
@ -4,7 +4,7 @@ use crate::manager::DatabaseUser;
|
|||||||
use crate::notification::{send_notification, DatabaseNotification};
|
use crate::notification::{send_notification, DatabaseNotification};
|
||||||
use crate::services::block_manager::DatabaseBlockManager;
|
use crate::services::block_manager::DatabaseBlockManager;
|
||||||
use crate::services::cell::{
|
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,
|
CellProtobufBlob, ToCellChangesetString, TypeCellData,
|
||||||
};
|
};
|
||||||
use crate::services::field::{
|
use crate::services::field::{
|
||||||
@ -392,8 +392,9 @@ impl DatabaseRevisionEditor {
|
|||||||
|
|
||||||
pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> {
|
pub async fn update_row(&self, changeset: RowChangeset) -> FlowyResult<()> {
|
||||||
let row_id = changeset.row_id.clone();
|
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.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,7 +441,7 @@ impl DatabaseRevisionEditor {
|
|||||||
|
|
||||||
/// Returns the cell data that encoded in protobuf.
|
/// Returns the cell data that encoded in protobuf.
|
||||||
pub async fn get_cell(&self, params: &CellPathParams) -> Option<CellPB> {
|
pub async fn get_cell(&self, params: &CellPathParams) -> Option<CellPB> {
|
||||||
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(
|
Some(CellPB::new(
|
||||||
¶ms.field_id,
|
¶ms.field_id,
|
||||||
¶ms.row_id,
|
¶ms.row_id,
|
||||||
@ -473,15 +474,15 @@ impl DatabaseRevisionEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_cell_protobuf(&self, params: &CellPathParams) -> Option<CellProtobufBlob> {
|
pub async fn get_cell_protobuf(&self, params: &CellPathParams) -> Option<CellProtobufBlob> {
|
||||||
let (_, cell_data) = self.decode_cell_data_from(params).await?;
|
let (_, cell_data) = self.get_type_cell_protobuf(params).await?;
|
||||||
Some(cell_data)
|
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 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 (_, row_rev) = self.block_manager.get_row_rev(¶ms.row_id).await.ok()??;
|
||||||
let cell_rev = row_rev.cells.get(¶ms.field_id)?.clone();
|
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,
|
cell_rev.type_cell_data,
|
||||||
&field_rev,
|
&field_rev,
|
||||||
Some(self.cell_data_cache.clone()),
|
Some(self.cell_data_cache.clone()),
|
||||||
@ -513,11 +514,12 @@ impl DatabaseRevisionEditor {
|
|||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
match self.database_pad.read().await.get_field_rev(field_id) {
|
match self.database_pad.read().await.get_field_rev(field_id) {
|
||||||
None => {
|
None => {
|
||||||
let msg = format!("Field:{} not found", &field_id);
|
let msg = format!("Field with id:{} not found", &field_id);
|
||||||
Err(FlowyError::internal().context(msg))
|
Err(FlowyError::internal().context(msg))
|
||||||
}
|
}
|
||||||
Some((_, field_rev)) => {
|
Some((_, field_rev)) => {
|
||||||
tracing::trace!("Cell changeset: id:{} / value:{:?}", &field_id, cell_changeset);
|
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?;
|
let cell_rev = self.get_cell_rev(row_id, field_id).await?;
|
||||||
// Update the changeset.data property with the return value.
|
// Update the changeset.data property with the return value.
|
||||||
let type_cell_data =
|
let type_cell_data =
|
||||||
@ -529,7 +531,7 @@ impl DatabaseRevisionEditor {
|
|||||||
type_cell_data,
|
type_cell_data,
|
||||||
};
|
};
|
||||||
self.block_manager.update_cell(cell_changeset).await?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB};
|
use crate::entities::{GroupPB, GroupRowsNotificationPB, GroupViewChangesetPB, InsertedGroupPB};
|
||||||
use crate::services::cell::DecodedCellData;
|
use crate::services::cell::DecodedCellData;
|
||||||
use crate::services::group::controller::MoveGroupRowContext;
|
use crate::services::group::controller::MoveGroupRowContext;
|
||||||
use crate::services::group::Group;
|
use crate::services::group::Group;
|
||||||
@ -10,42 +10,53 @@ use std::sync::Arc;
|
|||||||
///
|
///
|
||||||
/// For example, the `CheckboxGroupController` implements this trait to provide custom behavior.
|
/// For example, the `CheckboxGroupController` implements this trait to provide custom behavior.
|
||||||
///
|
///
|
||||||
pub trait GroupControllerCustomActions: Send + Sync {
|
pub trait GroupCustomize: Send + Sync {
|
||||||
type CellDataType: DecodedCellData;
|
type CellData: DecodedCellData;
|
||||||
/// Returns the a value of the cell, default value is None
|
/// 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
|
/// 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
|
/// is None. The row will be put in to the `No status` group
|
||||||
///
|
///
|
||||||
fn default_cell_rev(&self) -> Option<CellRevision> {
|
fn placeholder_cell(&self) -> Option<CellRevision> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a bool value to determine whether the group should contain this cell or not.
|
/// 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<InsertedGroupPB>, Option<GroupPB>)> {
|
||||||
|
Ok((None, None))
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds or removes a row if the cell data match the group filter.
|
/// Adds or removes a row if the cell data match the group filter.
|
||||||
/// It gets called after editing the cell or row
|
/// 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,
|
&mut self,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
cell_data: &Self::CellDataType,
|
cell_data: &Self::CellData,
|
||||||
) -> Vec<GroupRowsNotificationPB>;
|
) -> Vec<GroupRowsNotificationPB>;
|
||||||
|
|
||||||
/// Deletes the row from the group
|
/// Deletes the row from the group
|
||||||
fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsNotificationPB>;
|
fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB>;
|
||||||
|
|
||||||
/// Move row from one group to another
|
/// Move row from one group to another
|
||||||
fn move_row(
|
fn move_row(&mut self, cell_data: &Self::CellData, context: MoveGroupRowContext) -> Vec<GroupRowsNotificationPB>;
|
||||||
&mut self,
|
|
||||||
cell_data: &Self::CellDataType,
|
/// Returns None if there is no need to delete the group when corresponding row get removed
|
||||||
context: MoveGroupRowContext,
|
fn delete_group_when_move_row(&mut self, _row_rev: &RowRevision, _cell_data: &Self::CellData) -> Option<GroupPB> {
|
||||||
) -> Vec<GroupRowsNotificationPB>;
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines the shared actions any group controller can perform.
|
/// 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
|
/// The field that is used for grouping the rows
|
||||||
fn field_id(&self) -> &str;
|
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
|
/// Insert/Remove the row to the group if the corresponding cell data is changed
|
||||||
fn did_update_group_row(
|
fn did_update_group_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
old_row_rev: &Option<Arc<RowRevision>>,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<Vec<GroupRowsNotificationPB>>;
|
) -> FlowyResult<DidUpdateGroupRowResult>;
|
||||||
|
|
||||||
/// Remove the row from the group if the row gets deleted
|
/// Remove the row from the group if the row gets deleted
|
||||||
fn did_delete_delete_row(
|
fn did_delete_delete_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<Vec<GroupRowsNotificationPB>>;
|
) -> FlowyResult<DidMoveGroupRowResult>;
|
||||||
|
|
||||||
/// Move the row from one group to another group
|
/// Move the row from one group to another group
|
||||||
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupRowsNotificationPB>>;
|
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<DidMoveGroupRowResult>;
|
||||||
|
|
||||||
/// Update the group if the corresponding field is changed
|
/// Update the group if the corresponding field is changed
|
||||||
fn did_update_group_field(&mut self, field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>>;
|
fn did_update_group_field(&mut self, field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DidUpdateGroupRowResult {
|
||||||
|
pub(crate) inserted_group: Option<InsertedGroupPB>,
|
||||||
|
pub(crate) deleted_group: Option<GroupPB>,
|
||||||
|
pub(crate) row_changesets: Vec<GroupRowsNotificationPB>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DidMoveGroupRowResult {
|
||||||
|
pub(crate) deleted_group: Option<GroupPB>,
|
||||||
|
pub(crate) row_changesets: Vec<GroupRowsNotificationPB>,
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::entities::{GroupPB, GroupViewChangesetPB};
|
use crate::entities::{GroupPB, GroupViewChangesetPB, InsertedGroupPB};
|
||||||
use crate::services::field::RowSingleCellData;
|
use crate::services::field::RowSingleCellData;
|
||||||
use crate::services::group::{default_group_configuration, GeneratedGroupContext, Group};
|
use crate::services::group::{default_group_configuration, GeneratedGroupContext, Group};
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
@ -101,7 +101,7 @@ where
|
|||||||
|
|
||||||
/// Returns the no `status` group
|
/// 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> {
|
pub(crate) fn get_no_status_group(&self) -> Option<&Group> {
|
||||||
self.groups_map.get(&self.field_rev.id)
|
self.groups_map.get(&self.field_rev.id)
|
||||||
}
|
}
|
||||||
@ -140,6 +140,37 @@ where
|
|||||||
each(group);
|
each(group);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||||
|
pub(crate) fn add_new_group(&mut self, group_rev: GroupRevision) -> FlowyResult<InsertedGroupPB> {
|
||||||
|
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<()> {
|
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);
|
let from_index = self.groups_map.get_index_of(from_id);
|
||||||
@ -211,18 +242,12 @@ where
|
|||||||
old_groups.clear();
|
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
|
// The `all_group_revs` is the combination of the new groups and old groups
|
||||||
let MergeGroupResult {
|
let MergeGroupResult {
|
||||||
mut all_group_revs,
|
mut all_group_revs,
|
||||||
new_group_revs,
|
new_group_revs,
|
||||||
updated_group_revs: _,
|
|
||||||
deleted_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
|
let deleted_group_ids = deleted_group_revs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -231,13 +256,10 @@ where
|
|||||||
|
|
||||||
self.mut_configuration(|configuration| {
|
self.mut_configuration(|configuration| {
|
||||||
let mut is_changed = !deleted_group_ids.is_empty();
|
let mut is_changed = !deleted_group_ids.is_empty();
|
||||||
|
|
||||||
// Remove the groups
|
// Remove the groups
|
||||||
if !deleted_group_ids.is_empty() {
|
|
||||||
configuration
|
configuration
|
||||||
.groups
|
.groups
|
||||||
.retain(|group| !deleted_group_ids.contains(&group.id));
|
.retain(|group| !deleted_group_ids.contains(&group.id));
|
||||||
}
|
|
||||||
|
|
||||||
// Update/Insert new groups
|
// Update/Insert new groups
|
||||||
for group_rev in &mut all_group_revs {
|
for group_rev in &mut all_group_revs {
|
||||||
@ -276,7 +298,7 @@ where
|
|||||||
self.groups_map.insert(group.id.clone(), group);
|
self.groups_map.insert(group.id.clone(), group);
|
||||||
});
|
});
|
||||||
|
|
||||||
let new_groups = new_group_revs
|
let initial_groups = new_group_revs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|group_rev| {
|
.flat_map(|group_rev| {
|
||||||
let filter_content = filter_content_map.get(&group_rev.id)?;
|
let filter_content = filter_content_map.get(&group_rev.id)?;
|
||||||
@ -292,7 +314,7 @@ where
|
|||||||
|
|
||||||
let changeset = GroupViewChangesetPB {
|
let changeset = GroupViewChangesetPB {
|
||||||
view_id: self.view_id.clone(),
|
view_id: self.view_id.clone(),
|
||||||
new_groups,
|
initial_groups,
|
||||||
deleted_groups: deleted_group_ids,
|
deleted_groups: deleted_group_ids,
|
||||||
update_groups: vec![],
|
update_groups: vec![],
|
||||||
inserted_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
|
/// Merge the new groups into old groups while keeping the order in the old groups
|
||||||
///
|
///
|
||||||
fn merge_groups(old_groups: Vec<GroupRevision>, new_groups: Vec<GroupRevision>) -> MergeGroupResult {
|
fn merge_groups(
|
||||||
|
no_status_group: Option<GroupRevision>,
|
||||||
|
old_groups: Vec<GroupRevision>,
|
||||||
|
new_groups: Vec<GroupRevision>,
|
||||||
|
) -> MergeGroupResult {
|
||||||
let mut merge_result = MergeGroupResult::new();
|
let mut merge_result = MergeGroupResult::new();
|
||||||
// group_map is a helper map is used to filter out the new groups.
|
// group_map is a helper map is used to filter out the new groups.
|
||||||
let mut new_group_map: IndexMap<String, GroupRevision> = IndexMap::new();
|
let mut new_group_map: IndexMap<String, GroupRevision> = IndexMap::new();
|
||||||
@ -378,11 +404,8 @@ fn merge_groups(old_groups: Vec<GroupRevision>, new_groups: Vec<GroupRevision>)
|
|||||||
for old in old_groups {
|
for old in old_groups {
|
||||||
if let Some(new) = new_group_map.remove(&old.id) {
|
if let Some(new) = new_group_map.remove(&old.id) {
|
||||||
merge_result.all_group_revs.push(new.clone());
|
merge_result.all_group_revs.push(new.clone());
|
||||||
if is_group_changed(&new, &old) {
|
|
||||||
merge_result.updated_group_revs.push(new);
|
|
||||||
}
|
|
||||||
} else {
|
} 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<GroupRevision>, new_groups: Vec<GroupRevision>)
|
|||||||
merge_result.all_group_revs.push(group.clone());
|
merge_result.all_group_revs.push(group.clone());
|
||||||
merge_result.new_group_revs.push(group);
|
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
|
merge_result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +427,6 @@ fn is_group_changed(new: &GroupRevision, old: &GroupRevision) -> bool {
|
|||||||
if new.name != old.name {
|
if new.name != old.name {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +434,6 @@ struct MergeGroupResult {
|
|||||||
// Contains the new groups and the updated groups
|
// Contains the new groups and the updated groups
|
||||||
all_group_revs: Vec<GroupRevision>,
|
all_group_revs: Vec<GroupRevision>,
|
||||||
new_group_revs: Vec<GroupRevision>,
|
new_group_revs: Vec<GroupRevision>,
|
||||||
updated_group_revs: Vec<GroupRevision>,
|
|
||||||
deleted_group_revs: Vec<GroupRevision>,
|
deleted_group_revs: Vec<GroupRevision>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +442,6 @@ impl MergeGroupResult {
|
|||||||
Self {
|
Self {
|
||||||
all_group_revs: vec![],
|
all_group_revs: vec![],
|
||||||
new_group_revs: vec![],
|
new_group_revs: vec![],
|
||||||
updated_group_revs: vec![],
|
|
||||||
deleted_group_revs: vec![],
|
deleted_group_revs: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
|
use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB, InsertedRowPB, RowPB};
|
||||||
use crate::services::cell::{decode_type_cell_data, CellProtobufBlobParser, DecodedCellData};
|
use crate::services::cell::{get_type_cell_protobuf, CellProtobufBlobParser, DecodedCellData};
|
||||||
use crate::services::group::action::{GroupControllerCustomActions, GroupControllerSharedActions};
|
|
||||||
|
use crate::services::group::action::{
|
||||||
|
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerActions, GroupCustomize,
|
||||||
|
};
|
||||||
use crate::services::group::configuration::GroupContext;
|
use crate::services::group::configuration::GroupContext;
|
||||||
use crate::services::group::entities::Group;
|
use crate::services::group::entities::Group;
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
use grid_model::{
|
use grid_model::{
|
||||||
FieldRevision, GroupConfigurationContentSerde, GroupRevision, RowChangeset, RowRevision, TypeOptionDataDeserializer,
|
CellRevision, FieldRevision, GroupConfigurationContentSerde, GroupRevision, RowChangeset, RowRevision,
|
||||||
|
TypeOptionDataDeserializer,
|
||||||
};
|
};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
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
|
/// If the [FieldType] doesn't implement its group controller, then the [DefaultGroupController] will
|
||||||
/// be used.
|
/// 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 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);
|
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
|
// https://stackoverflow.com/questions/69413164/how-to-fix-this-clippy-warning-needless-collect
|
||||||
#[allow(clippy::needless_collect)]
|
#[allow(clippy::needless_collect)]
|
||||||
fn update_default_group(
|
fn update_no_status_group(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
other_group_changesets: &[GroupRowsNotificationPB],
|
other_group_changesets: &[GroupRowsNotificationPB],
|
||||||
) -> Option<GroupRowsNotificationPB> {
|
) -> Option<GroupRowsNotificationPB> {
|
||||||
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.
|
// [other_group_inserted_row] contains all the inserted rows except the default group.
|
||||||
let other_group_inserted_row = other_group_changesets
|
let other_group_inserted_row = other_group_changesets
|
||||||
@ -100,7 +104,7 @@ where
|
|||||||
.collect::<Vec<&InsertedRowPB>>();
|
.collect::<Vec<&InsertedRowPB>>();
|
||||||
|
|
||||||
// Calculate the inserted_rows of the default_group
|
// 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()
|
.iter()
|
||||||
.flat_map(|changeset| &changeset.deleted_rows)
|
.flat_map(|changeset| &changeset.deleted_rows)
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -113,10 +117,10 @@ where
|
|||||||
})
|
})
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
let mut changeset = GroupRowsNotificationPB::new(default_group.id.clone());
|
let mut changeset = GroupRowsNotificationPB::new(no_status_group.id.clone());
|
||||||
if !default_group_inserted_row.is_empty() {
|
if !no_status_group_rows.is_empty() {
|
||||||
changeset.inserted_rows.push(InsertedRowPB::new(row_rev.into()));
|
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.
|
// [other_group_delete_rows] contains all the deleted rows except the default group.
|
||||||
@ -138,7 +142,7 @@ where
|
|||||||
.collect::<Vec<&InsertedRowPB>>();
|
.collect::<Vec<&InsertedRowPB>>();
|
||||||
|
|
||||||
let mut deleted_row_ids = vec![];
|
let mut deleted_row_ids = vec![];
|
||||||
for row in &default_group.rows {
|
for row in &no_status_group.rows {
|
||||||
if default_group_deleted_rows
|
if default_group_deleted_rows
|
||||||
.iter()
|
.iter()
|
||||||
.any(|deleted_row| deleted_row.row.id == row.id)
|
.any(|deleted_row| deleted_row.row.id == row.id)
|
||||||
@ -146,20 +150,20 @@ where
|
|||||||
deleted_row_ids.push(row.id.clone());
|
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);
|
changeset.deleted_rows.extend(deleted_row_ids);
|
||||||
Some(changeset)
|
Some(changeset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, T, G, P> GroupControllerSharedActions for GenericGroupController<C, T, G, P>
|
impl<C, T, G, P> GroupControllerActions for GenericGroupController<C, T, G, P>
|
||||||
where
|
where
|
||||||
P: CellProtobufBlobParser,
|
P: CellProtobufBlobParser,
|
||||||
C: GroupConfigurationContentSerde,
|
C: GroupConfigurationContentSerde,
|
||||||
T: TypeOptionDataDeserializer,
|
T: TypeOptionDataDeserializer,
|
||||||
G: GroupGenerator<Context = GroupContext<C>, TypeOptionType = T>,
|
G: GroupGenerator<Context = GroupContext<C>, TypeOptionType = T>,
|
||||||
|
|
||||||
Self: GroupControllerCustomActions<CellDataType = P::Object>,
|
Self: GroupCustomize<CellData = P::Object>,
|
||||||
{
|
{
|
||||||
fn field_id(&self) -> &str {
|
fn field_id(&self) -> &str {
|
||||||
&self.field_id
|
&self.field_id
|
||||||
@ -178,13 +182,13 @@ where
|
|||||||
fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
|
fn fill_groups(&mut self, row_revs: &[Arc<RowRevision>], field_rev: &FieldRevision) -> FlowyResult<()> {
|
||||||
for row_rev in row_revs {
|
for row_rev in row_revs {
|
||||||
let cell_rev = match row_rev.cells.get(&self.field_id) {
|
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()),
|
Some(cell_rev) => Some(cell_rev.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(cell_rev) = cell_rev {
|
if let Some(cell_rev) = cell_rev {
|
||||||
let mut grouped_rows: Vec<GroupedRow> = vec![];
|
let mut grouped_rows: Vec<GroupedRow> = 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::<P>()?;
|
let cell_data = cell_bytes.parser::<P>()?;
|
||||||
for group in self.group_ctx.groups() {
|
for group in self.group_ctx.groups() {
|
||||||
if self.can_group(&group.filter_content, &cell_data) {
|
if self.can_group(&group.filter_content, &cell_data) {
|
||||||
@ -206,7 +210,7 @@ where
|
|||||||
}
|
}
|
||||||
match self.group_ctx.get_mut_no_status_group() {
|
match self.group_ctx.get_mut_no_status_group() {
|
||||||
None => {}
|
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(
|
fn did_update_group_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
old_row_rev: &Option<Arc<RowRevision>>,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
|
) -> FlowyResult<DidUpdateGroupRowResult> {
|
||||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
// let cell_data = row_rev.cells.get(&self.field_id).and_then(|cell_rev| {
|
||||||
let cell_bytes = decode_type_cell_data(cell_rev.type_cell_data.clone(), field_rev, None).1;
|
// let cell_data: Option<P> = get_type_cell_data(cell_rev, field_rev, None);
|
||||||
let cell_data = cell_bytes.parser::<P>()?;
|
// cell_data
|
||||||
let mut changesets = self.add_or_remove_row_in_groups_if_match(row_rev, &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) {
|
if let Some(cell_data) = get_cell_data_from_row_rev::<P>(Some(row_rev), field_rev) {
|
||||||
tracing::trace!("default_group_changeset: {}", default_group_changeset);
|
let old_row_rev = old_row_rev.as_ref().map(|old| old.as_ref());
|
||||||
if !default_group_changeset.is_empty() {
|
let old_cell_data = get_cell_data_from_row_rev::<P>(old_row_rev, field_rev);
|
||||||
changesets.push(default_group_changeset);
|
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)
|
result.row_changesets = changesets;
|
||||||
} else {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_delete_delete_row(
|
fn did_delete_delete_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
|
) -> FlowyResult<DidMoveGroupRowResult> {
|
||||||
// if the cell_rev is none, then the row must in the default group.
|
// 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) {
|
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::<P>()?;
|
let cell_data = cell_bytes.parser::<P>()?;
|
||||||
if !cell_data.is_empty() {
|
if !cell_data.is_empty() {
|
||||||
tracing::error!("did_delete_delete_row {:?}", cell_rev.type_cell_data);
|
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() {
|
match self.group_ctx.get_no_status_group() {
|
||||||
None => {
|
None => {
|
||||||
tracing::error!("Unexpected None value. It should have the no status group");
|
tracing::error!("Unexpected None value. It should have the no status group");
|
||||||
Ok(vec![])
|
|
||||||
}
|
}
|
||||||
Some(no_status_group) => {
|
Some(no_status_group) => {
|
||||||
if !no_status_group.contains_row(&row_rev.id) {
|
if !no_status_group.contains_row(&row_rev.id) {
|
||||||
tracing::error!("The row: {} should be in the no status group", 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(),
|
no_status_group.id.clone(),
|
||||||
vec![row_rev.id.clone()],
|
vec![row_rev.id.clone()],
|
||||||
)])
|
)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
#[tracing::instrument(level = "trace", skip_all, err)]
|
||||||
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
|
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<DidMoveGroupRowResult> {
|
||||||
|
let mut result = DidMoveGroupRowResult {
|
||||||
|
deleted_group: None,
|
||||||
|
row_changesets: vec![],
|
||||||
|
};
|
||||||
let cell_rev = match context.row_rev.cells.get(&self.field_id) {
|
let cell_rev = match context.row_rev.cells.get(&self.field_id) {
|
||||||
Some(cell_rev) => Some(cell_rev.clone()),
|
Some(cell_rev) => Some(cell_rev.clone()),
|
||||||
None => self.default_cell_rev(),
|
None => self.placeholder_cell(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(cell_rev) = cell_rev {
|
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::<P>()?;
|
let cell_data = cell_bytes.parser::<P>()?;
|
||||||
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 {
|
} else {
|
||||||
tracing::warn!("Unexpected moving group row, changes should not be empty");
|
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<Option<GroupViewChangesetPB>> {
|
fn did_update_group_field(&mut self, _field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>> {
|
||||||
@ -298,3 +328,12 @@ struct GroupedRow {
|
|||||||
row: RowPB,
|
row: RowPB,
|
||||||
group_id: String,
|
group_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_cell_data_from_row_rev<P: CellProtobufBlobParser>(
|
||||||
|
row_rev: Option<&RowRevision>,
|
||||||
|
field_rev: &FieldRevision,
|
||||||
|
) -> Option<P::Object> {
|
||||||
|
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::<P>().ok()
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::entities::{GroupRowsNotificationPB, InsertedRowPB, RowPB};
|
use crate::entities::{GroupRowsNotificationPB, InsertedRowPB, RowPB};
|
||||||
use crate::services::field::{CheckboxCellData, CheckboxCellDataParser, CheckboxTypeOptionPB, CHECK, UNCHECK};
|
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::configuration::GroupContext;
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{
|
||||||
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
||||||
@ -19,13 +19,13 @@ pub type CheckboxGroupController = GenericGroupController<
|
|||||||
|
|
||||||
pub type CheckboxGroupContext = GroupContext<CheckboxGroupConfigurationRevision>;
|
pub type CheckboxGroupContext = GroupContext<CheckboxGroupConfigurationRevision>;
|
||||||
|
|
||||||
impl GroupControllerCustomActions for CheckboxGroupController {
|
impl GroupCustomize for CheckboxGroupController {
|
||||||
type CellDataType = CheckboxCellData;
|
type CellData = CheckboxCellData;
|
||||||
fn default_cell_rev(&self) -> Option<CellRevision> {
|
fn placeholder_cell(&self) -> Option<CellRevision> {
|
||||||
Some(CellRevision::new(UNCHECK.to_string()))
|
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() {
|
if cell_data.is_check() {
|
||||||
content == CHECK
|
content == CHECK
|
||||||
} else {
|
} 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,
|
&mut self,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
cell_data: &Self::CellDataType,
|
cell_data: &Self::CellData,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_status_groups(|group| {
|
self.group_ctx.iter_mut_status_groups(|group| {
|
||||||
@ -79,7 +79,7 @@ impl GroupControllerCustomActions for CheckboxGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_groups(|group| {
|
||||||
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
||||||
@ -97,7 +97,7 @@ impl GroupControllerCustomActions for CheckboxGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellDataType,
|
_cell_data: &Self::CellData,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::entities::{GroupRowsNotificationPB, GroupViewChangesetPB, RowPB};
|
use crate::entities::{GroupViewChangesetPB, RowPB};
|
||||||
use crate::services::group::action::GroupControllerSharedActions;
|
use crate::services::group::action::{DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupControllerActions};
|
||||||
use crate::services::group::{Group, GroupController, MoveGroupRowContext};
|
use crate::services::group::{Group, GroupController, MoveGroupRowContext};
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
use grid_model::{FieldRevision, RowRevision};
|
use grid_model::{FieldRevision, RowRevision};
|
||||||
@ -31,7 +31,7 @@ impl DefaultGroupController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GroupControllerSharedActions for DefaultGroupController {
|
impl GroupControllerActions for DefaultGroupController {
|
||||||
fn field_id(&self) -> &str {
|
fn field_id(&self) -> &str {
|
||||||
&self.field_id
|
&self.field_id
|
||||||
}
|
}
|
||||||
@ -57,22 +57,33 @@ impl GroupControllerSharedActions for DefaultGroupController {
|
|||||||
|
|
||||||
fn did_update_group_row(
|
fn did_update_group_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_old_row_rev: &Option<Arc<RowRevision>>,
|
||||||
_row_rev: &RowRevision,
|
_row_rev: &RowRevision,
|
||||||
_field_rev: &FieldRevision,
|
_field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
|
) -> FlowyResult<DidUpdateGroupRowResult> {
|
||||||
Ok(vec![])
|
Ok(DidUpdateGroupRowResult {
|
||||||
|
inserted_group: None,
|
||||||
|
deleted_group: None,
|
||||||
|
row_changesets: vec![],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_delete_delete_row(
|
fn did_delete_delete_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_row_rev: &RowRevision,
|
_row_rev: &RowRevision,
|
||||||
_field_rev: &FieldRevision,
|
_field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
|
) -> FlowyResult<DidMoveGroupRowResult> {
|
||||||
Ok(vec![])
|
Ok(DidMoveGroupRowResult {
|
||||||
|
deleted_group: None,
|
||||||
|
row_changesets: vec![],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_group_row(&mut self, _context: MoveGroupRowContext) -> FlowyResult<Vec<GroupRowsNotificationPB>> {
|
fn move_group_row(&mut self, _context: MoveGroupRowContext) -> FlowyResult<DidMoveGroupRowResult> {
|
||||||
todo!()
|
Ok(DidMoveGroupRowResult {
|
||||||
|
deleted_group: None,
|
||||||
|
row_changesets: vec![],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn did_update_group_field(&mut self, _field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>> {
|
fn did_update_group_field(&mut self, _field_rev: &FieldRevision) -> FlowyResult<Option<GroupViewChangesetPB>> {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::entities::{GroupRowsNotificationPB, RowPB};
|
use crate::entities::{GroupRowsNotificationPB, RowPB};
|
||||||
use crate::services::cell::insert_select_option_cell;
|
use crate::services::cell::insert_select_option_cell;
|
||||||
use crate::services::field::{MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser};
|
use crate::services::field::{MultiSelectTypeOptionPB, SelectOptionCellDataPB, SelectOptionCellDataParser};
|
||||||
use crate::services::group::action::GroupControllerCustomActions;
|
use crate::services::group::action::GroupCustomize;
|
||||||
|
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{
|
||||||
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
||||||
@ -19,17 +19,17 @@ pub type MultiSelectGroupController = GenericGroupController<
|
|||||||
SelectOptionCellDataParser,
|
SelectOptionCellDataParser,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
impl GroupControllerCustomActions for MultiSelectGroupController {
|
impl GroupCustomize for MultiSelectGroupController {
|
||||||
type CellDataType = SelectOptionCellDataPB;
|
type CellData = SelectOptionCellDataPB;
|
||||||
|
|
||||||
fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
||||||
cell_data.select_options.iter().any(|option| option.id == content)
|
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,
|
&mut self,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
cell_data: &Self::CellDataType,
|
cell_data: &Self::CellData,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_status_groups(|group| {
|
self.group_ctx.iter_mut_status_groups(|group| {
|
||||||
@ -40,7 +40,7 @@ impl GroupControllerCustomActions for MultiSelectGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_status_groups(|group| {
|
self.group_ctx.iter_mut_status_groups(|group| {
|
||||||
if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
|
if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
|
||||||
@ -52,7 +52,7 @@ impl GroupControllerCustomActions for MultiSelectGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellDataType,
|
_cell_data: &Self::CellData,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::entities::{GroupRowsNotificationPB, RowPB};
|
use crate::entities::{GroupRowsNotificationPB, RowPB};
|
||||||
use crate::services::cell::insert_select_option_cell;
|
use crate::services::cell::insert_select_option_cell;
|
||||||
use crate::services::field::{SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB};
|
use crate::services::field::{SelectOptionCellDataPB, SelectOptionCellDataParser, SingleSelectTypeOptionPB};
|
||||||
use crate::services::group::action::GroupControllerCustomActions;
|
use crate::services::group::action::GroupCustomize;
|
||||||
|
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{
|
||||||
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
||||||
@ -20,16 +20,16 @@ pub type SingleSelectGroupController = GenericGroupController<
|
|||||||
SelectOptionCellDataParser,
|
SelectOptionCellDataParser,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
impl GroupControllerCustomActions for SingleSelectGroupController {
|
impl GroupCustomize for SingleSelectGroupController {
|
||||||
type CellDataType = SelectOptionCellDataPB;
|
type CellData = SelectOptionCellDataPB;
|
||||||
fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
fn can_group(&self, content: &str, cell_data: &SelectOptionCellDataPB) -> bool {
|
||||||
cell_data.select_options.iter().any(|option| option.id == content)
|
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,
|
&mut self,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
cell_data: &Self::CellDataType,
|
cell_data: &Self::CellData,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_status_groups(|group| {
|
self.group_ctx.iter_mut_status_groups(|group| {
|
||||||
@ -40,7 +40,7 @@ impl GroupControllerCustomActions for SingleSelectGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(&mut self, row_rev: &RowRevision, cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_status_groups(|group| {
|
self.group_ctx.iter_mut_status_groups(|group| {
|
||||||
if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
|
if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
|
||||||
@ -52,7 +52,7 @@ impl GroupControllerCustomActions for SingleSelectGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellDataType,
|
_cell_data: &Self::CellData,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
|
@ -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::cell::insert_url_cell;
|
||||||
use crate::services::field::{URLCellDataPB, URLCellDataParser, URLTypeOptionPB};
|
use crate::services::field::{URLCellData, URLCellDataPB, URLCellDataParser, URLTypeOptionPB};
|
||||||
use crate::services::group::action::GroupControllerCustomActions;
|
use crate::services::group::action::GroupCustomize;
|
||||||
use crate::services::group::configuration::GroupContext;
|
use crate::services::group::configuration::GroupContext;
|
||||||
use crate::services::group::controller::{
|
use crate::services::group::controller::{
|
||||||
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
GenericGroupController, GroupController, GroupGenerator, MoveGroupRowContext,
|
||||||
};
|
};
|
||||||
use crate::services::group::{make_no_status_group, move_group_row, GeneratedGroupConfig, GeneratedGroupContext};
|
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};
|
use grid_model::{CellRevision, FieldRevision, GroupRevision, RowRevision, URLGroupConfigurationRevision};
|
||||||
|
|
||||||
pub type URLGroupController =
|
pub type URLGroupController =
|
||||||
@ -14,21 +15,61 @@ pub type URLGroupController =
|
|||||||
|
|
||||||
pub type URLGroupContext = GroupContext<URLGroupConfigurationRevision>;
|
pub type URLGroupContext = GroupContext<URLGroupConfigurationRevision>;
|
||||||
|
|
||||||
impl GroupControllerCustomActions for URLGroupController {
|
impl GroupCustomize for URLGroupController {
|
||||||
type CellDataType = URLCellDataPB;
|
type CellData = URLCellDataPB;
|
||||||
|
|
||||||
fn default_cell_rev(&self) -> Option<CellRevision> {
|
fn placeholder_cell(&self) -> Option<CellRevision> {
|
||||||
Some(CellRevision::new("".to_string()))
|
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
|
cell_data.content == content
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_or_remove_row_in_groups_if_match(
|
fn create_or_delete_group_when_cell_changed(
|
||||||
&mut self,
|
&mut self,
|
||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
cell_data: &Self::CellDataType,
|
old_cell_data: Option<&Self::CellData>,
|
||||||
|
cell_data: &Self::CellData,
|
||||||
|
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
||||||
|
// 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<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_status_groups(|group| {
|
self.group_ctx.iter_mut_status_groups(|group| {
|
||||||
@ -51,7 +92,7 @@ impl GroupControllerCustomActions for URLGroupController {
|
|||||||
changesets
|
changesets
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupRowsNotificationPB> {
|
fn delete_row(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellData) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_groups(|group| {
|
||||||
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
let mut changeset = GroupRowsNotificationPB::new(group.id.clone());
|
||||||
@ -69,7 +110,7 @@ impl GroupControllerCustomActions for URLGroupController {
|
|||||||
|
|
||||||
fn move_row(
|
fn move_row(
|
||||||
&mut self,
|
&mut self,
|
||||||
_cell_data: &Self::CellDataType,
|
_cell_data: &Self::CellData,
|
||||||
mut context: MoveGroupRowContext,
|
mut context: MoveGroupRowContext,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) -> Vec<GroupRowsNotificationPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
@ -80,6 +121,19 @@ impl GroupControllerCustomActions for URLGroupController {
|
|||||||
});
|
});
|
||||||
group_changeset
|
group_changeset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn delete_group_when_move_row(&mut self, _row_rev: &RowRevision, cell_data: &Self::CellData) -> Option<GroupPB> {
|
||||||
|
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 {
|
impl GroupController for URLGroupController {
|
||||||
@ -117,13 +171,9 @@ impl GroupGenerator for URLGroupGenerator {
|
|||||||
let group_configs = cells
|
let group_configs = cells
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|value| value.into_url_field_cell_data())
|
.flat_map(|value| value.into_url_field_cell_data())
|
||||||
.map(|cell| {
|
.map(|cell| GeneratedGroupConfig {
|
||||||
let group_id = cell.content.clone();
|
group_rev: make_group_from_url_cell(&cell),
|
||||||
let group_name = cell.content.clone();
|
|
||||||
GeneratedGroupConfig {
|
|
||||||
group_rev: GroupRevision::new(group_id, group_name),
|
|
||||||
filter_content: cell.content,
|
filter_content: cell.content,
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect();
|
.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)
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::entities::RowPB;
|
use crate::entities::RowPB;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Debug, Eq)]
|
||||||
pub struct Group {
|
pub struct Group {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub field_id: String,
|
pub field_id: String,
|
||||||
|
@ -240,29 +240,44 @@ impl DatabaseViewRevisionEditor {
|
|||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
pub async fn did_delete_view_row(&self, row_rev: &RowRevision) {
|
pub async fn did_delete_view_row(&self, row_rev: &RowRevision) {
|
||||||
// Send the group notification if the current view has groups;
|
// Send the group notification if the current view has groups;
|
||||||
let changesets = self
|
let result = self
|
||||||
.mut_group_controller(|group_controller, field_rev| {
|
.mut_group_controller(|group_controller, field_rev| {
|
||||||
group_controller.did_delete_delete_row(row_rev, &field_rev)
|
group_controller.did_delete_delete_row(row_rev, &field_rev)
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
tracing::trace!("Delete row in view changeset: {:?}", changesets);
|
if let Some(result) = result {
|
||||||
if let Some(changesets) = changesets {
|
tracing::trace!("Delete row in view changeset: {:?}", result.row_changesets);
|
||||||
for changeset in changesets {
|
for changeset in result.row_changesets {
|
||||||
self.notify_did_update_group_rows(changeset).await;
|
self.notify_did_update_group_rows(changeset).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn did_update_view_cell(&self, row_rev: &RowRevision) {
|
pub async fn did_update_view_row(&self, old_row_rev: Option<Arc<RowRevision>>, row_rev: &RowRevision) {
|
||||||
let changesets = self
|
let result = self
|
||||||
.mut_group_controller(|group_controller, field_rev| {
|
.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;
|
.await;
|
||||||
|
|
||||||
if let Some(changesets) = changesets {
|
if let Some(Ok(result)) = result {
|
||||||
for changeset in changesets {
|
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;
|
self.notify_did_update_group_rows(changeset).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,8 +297,8 @@ impl DatabaseViewRevisionEditor {
|
|||||||
row_changeset: &mut RowChangeset,
|
row_changeset: &mut RowChangeset,
|
||||||
to_group_id: &str,
|
to_group_id: &str,
|
||||||
to_row_id: Option<String>,
|
to_row_id: Option<String>,
|
||||||
) -> Vec<GroupRowsNotificationPB> {
|
) {
|
||||||
let changesets = self
|
let result = self
|
||||||
.mut_group_controller(|group_controller, field_rev| {
|
.mut_group_controller(|group_controller, field_rev| {
|
||||||
let move_row_context = MoveGroupRowContext {
|
let move_row_context = MoveGroupRowContext {
|
||||||
row_rev,
|
row_rev,
|
||||||
@ -292,13 +307,25 @@ impl DatabaseViewRevisionEditor {
|
|||||||
to_group_id,
|
to_group_id,
|
||||||
to_row_id,
|
to_row_id,
|
||||||
};
|
};
|
||||||
|
group_controller.move_group_row(move_row_context)
|
||||||
let changesets = group_controller.move_group_row(move_row_context)?;
|
|
||||||
Ok(changesets)
|
|
||||||
})
|
})
|
||||||
.await;
|
.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
|
/// Only call once after grid view editor initialized
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
@ -334,7 +361,7 @@ impl DatabaseViewRevisionEditor {
|
|||||||
inserted_groups: vec![inserted_group],
|
inserted_groups: vec![inserted_group],
|
||||||
deleted_groups: vec![params.from_group_id.clone()],
|
deleted_groups: vec![params.from_group_id.clone()],
|
||||||
update_groups: vec![],
|
update_groups: vec![],
|
||||||
new_groups: vec![],
|
initial_groups: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
self.notify_did_update_view(changeset).await;
|
self.notify_did_update_view(changeset).await;
|
||||||
@ -610,7 +637,7 @@ impl DatabaseViewRevisionEditor {
|
|||||||
*self.group_controller.write().await = new_group_controller;
|
*self.group_controller.write().await = new_group_controller;
|
||||||
let changeset = GroupViewChangesetPB {
|
let changeset = GroupViewChangesetPB {
|
||||||
view_id: self.view_id.clone(),
|
view_id: self.view_id.clone(),
|
||||||
new_groups,
|
initial_groups: new_groups,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,14 +88,14 @@ impl DatabaseViewManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert/Delete the group's row if the corresponding cell data was changed.
|
/// 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<Arc<RowRevision>>, row_id: &str) {
|
||||||
match self.delegate.get_row_rev(row_id).await {
|
match self.delegate.get_row_rev(row_id).await {
|
||||||
None => {
|
None => {
|
||||||
tracing::warn!("Can not find the row in grid view");
|
tracing::warn!("Can not find the row in grid view");
|
||||||
}
|
}
|
||||||
Some((_, row_rev)) => {
|
Some((_, row_rev)) => {
|
||||||
for view_editor in self.view_editors.read().await.values() {
|
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<()> {
|
) -> FlowyResult<()> {
|
||||||
let mut row_changeset = RowChangeset::new(row_rev.id.clone());
|
let mut row_changeset = RowChangeset::new(row_rev.id.clone());
|
||||||
let view_editor = self.get_default_view_editor().await?;
|
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())
|
.move_view_group_row(&row_rev, &mut row_changeset, &to_group_id, to_row_id.clone())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -200,10 +200,6 @@ impl DatabaseViewManager {
|
|||||||
recv_row_changeset(row_changeset).await;
|
recv_row_changeset(row_changeset).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
for group_changeset in group_changesets {
|
|
||||||
view_editor.notify_did_update_group_rows(group_changeset).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 crate::grid::block_test::script::RowScript::*;
|
||||||
|
|
||||||
use grid_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset};
|
use grid_model::{GridBlockMetaRevision, GridBlockMetaRevisionChangeset};
|
||||||
@ -11,7 +11,7 @@ async fn grid_create_block() {
|
|||||||
CreateBlock { block: block_meta_rev },
|
CreateBlock { block: block_meta_rev },
|
||||||
AssertBlockCount(2),
|
AssertBlockCount(2),
|
||||||
];
|
];
|
||||||
GridRowTest::new().await.run_scripts(scripts).await;
|
DatabaseRowTest::new().await.run_scripts(scripts).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -37,5 +37,5 @@ async fn grid_update_block() {
|
|||||||
block: cloned_grid_block,
|
block: cloned_grid_block,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
GridRowTest::new().await.run_scripts(scripts).await;
|
DatabaseRowTest::new().await.run_scripts(scripts).await;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use crate::grid::block_test::script::RowScript::*;
|
use crate::grid::block_test::script::RowScript::*;
|
||||||
use crate::grid::block_test::script::{CreateRowScriptBuilder, GridRowTest};
|
use crate::grid::block_test::script::{CreateRowScriptBuilder, DatabaseRowTest};
|
||||||
use crate::grid::grid_editor::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, TWITTER};
|
use crate::grid::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, TWITTER};
|
||||||
use flowy_database::entities::FieldType;
|
use flowy_database::entities::FieldType;
|
||||||
use flowy_database::services::field::{SELECTION_IDS_SEPARATOR, UNCHECK};
|
use flowy_database::services::field::{SELECTION_IDS_SEPARATOR, UNCHECK};
|
||||||
use grid_model::RowChangeset;
|
use grid_model::RowChangeset;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_create_row_count_test() {
|
async fn grid_create_row_count_test() {
|
||||||
let mut test = GridRowTest::new().await;
|
let mut test = DatabaseRowTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertRowCount(6),
|
AssertRowCount(6),
|
||||||
CreateEmptyRow,
|
CreateEmptyRow,
|
||||||
@ -22,7 +22,7 @@ async fn grid_create_row_count_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_update_row() {
|
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 row_rev = test.row_builder().build();
|
||||||
let changeset = RowChangeset {
|
let changeset = RowChangeset {
|
||||||
row_id: row_rev.id.clone(),
|
row_id: row_rev.id.clone(),
|
||||||
@ -41,7 +41,7 @@ async fn grid_update_row() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_delete_row() {
|
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_1 = test.row_builder().build();
|
||||||
let row_2 = test.row_builder().build();
|
let row_2 = test.row_builder().build();
|
||||||
let row_ids = vec![row_1.id.clone(), row_2.id.clone()];
|
let row_ids = vec![row_1.id.clone(), row_2.id.clone()];
|
||||||
@ -67,7 +67,7 @@ async fn grid_delete_row() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_row_add_cells_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);
|
let mut builder = CreateRowScriptBuilder::new(&test);
|
||||||
builder.insert(FieldType::RichText, "hello world", "hello world");
|
builder.insert(FieldType::RichText, "hello world", "hello world");
|
||||||
builder.insert(FieldType::DateTime, "1647251762", "2022/03/14");
|
builder.insert(FieldType::DateTime, "1647251762", "2022/03/14");
|
||||||
@ -85,7 +85,7 @@ async fn grid_row_add_cells_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_row_insert_number_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", ""), ("", "")] {
|
for (val, expected) in &[("1647251762", "2022/03/14"), ("2022/03/14", ""), ("", "")] {
|
||||||
let mut builder = CreateRowScriptBuilder::new(&test);
|
let mut builder = CreateRowScriptBuilder::new(&test);
|
||||||
builder.insert(FieldType::DateTime, val, expected);
|
builder.insert(FieldType::DateTime, val, expected);
|
||||||
@ -96,7 +96,7 @@ async fn grid_row_insert_number_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_row_insert_date_test() {
|
async fn grid_row_insert_date_test() {
|
||||||
let mut test = GridRowTest::new().await;
|
let mut test = DatabaseRowTest::new().await;
|
||||||
for (val, expected) in &[
|
for (val, expected) in &[
|
||||||
("18,443", "$18,443.00"),
|
("18,443", "$18,443.00"),
|
||||||
("0", "$0.00"),
|
("0", "$0.00"),
|
||||||
@ -112,7 +112,7 @@ async fn grid_row_insert_date_test() {
|
|||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_row_insert_single_select_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);
|
let mut builder = CreateRowScriptBuilder::new(&test);
|
||||||
builder.insert_single_select_cell(|mut options| options.pop().unwrap(), PAUSED);
|
builder.insert_single_select_cell(|mut options| options.pop().unwrap(), PAUSED);
|
||||||
let scripts = builder.build();
|
let scripts = builder.build();
|
||||||
@ -121,7 +121,7 @@ async fn grid_row_insert_single_select_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_row_insert_multi_select_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);
|
let mut builder = CreateRowScriptBuilder::new(&test);
|
||||||
builder.insert_multi_select_cell(
|
builder.insert_multi_select_cell(
|
||||||
|mut options| {
|
|mut options| {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
|
use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
|
||||||
use crate::grid::block_test::util::GridRowTestBuilder;
|
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::entities::{CellPathParams, CreateRowParams, DatabaseViewLayout, FieldType, RowPB};
|
||||||
use flowy_database::services::field::*;
|
use flowy_database::services::field::*;
|
||||||
use flowy_database::services::row::DatabaseBlockRow;
|
use flowy_database::services::row::DatabaseBlockRow;
|
||||||
@ -48,13 +48,13 @@ pub enum RowScript {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridRowTest {
|
pub struct DatabaseRowTest {
|
||||||
inner: GridEditorTest,
|
inner: DatabaseEditorTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridRowTest {
|
impl DatabaseRowTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let editor_test = GridEditorTest::new_table().await;
|
let editor_test = DatabaseEditorTest::new_table().await;
|
||||||
Self { inner: editor_test }
|
Self { inner: editor_test }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,15 +282,15 @@ fn block_from_row_pbs(row_orders: Vec<RowPB>) -> Vec<DatabaseBlockRow> {
|
|||||||
map.into_values().collect::<Vec<_>>()
|
map.into_values().collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for GridRowTest {
|
impl std::ops::Deref for DatabaseRowTest {
|
||||||
type Target = GridEditorTest;
|
type Target = DatabaseEditorTest;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for GridRowTest {
|
impl std::ops::DerefMut for DatabaseRowTest {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
@ -303,7 +303,7 @@ pub struct CreateRowScriptBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CreateRowScriptBuilder<'a> {
|
impl<'a> CreateRowScriptBuilder<'a> {
|
||||||
pub fn new(test: &'a GridRowTest) -> Self {
|
pub fn new(test: &'a DatabaseRowTest) -> Self {
|
||||||
Self {
|
Self {
|
||||||
builder: test.row_builder(),
|
builder: test.row_builder(),
|
||||||
data_by_field_type: HashMap::new(),
|
data_by_field_type: HashMap::new(),
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use crate::grid::grid_editor::GridEditorTest;
|
use crate::grid::database_editor::DatabaseEditorTest;
|
||||||
use flowy_database::entities::CellChangesetPB;
|
use flowy_database::entities::CellChangesetPB;
|
||||||
|
|
||||||
pub enum CellScript {
|
pub enum CellScript {
|
||||||
UpdateCell { changeset: CellChangesetPB, is_err: bool },
|
UpdateCell { changeset: CellChangesetPB, is_err: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridCellTest {
|
pub struct DatabaseCellTest {
|
||||||
inner: GridEditorTest,
|
inner: DatabaseEditorTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridCellTest {
|
impl DatabaseCellTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let inner = GridEditorTest::new_table().await;
|
let inner = DatabaseEditorTest::new_table().await;
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,15 +48,15 @@ impl GridCellTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for GridCellTest {
|
impl std::ops::Deref for DatabaseCellTest {
|
||||||
type Target = GridEditorTest;
|
type Target = DatabaseEditorTest;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for GridCellTest {
|
impl std::ops::DerefMut for DatabaseCellTest {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::grid::cell_test::script::CellScript::*;
|
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 crate::grid::field_test::util::make_date_cell_string;
|
||||||
use flowy_database::entities::{CellChangesetPB, FieldType};
|
use flowy_database::entities::{CellChangesetPB, FieldType};
|
||||||
use flowy_database::services::cell::ToCellChangesetString;
|
use flowy_database::services::cell::ToCellChangesetString;
|
||||||
@ -8,7 +8,7 @@ use flowy_database::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOpti
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_cell_update() {
|
async fn grid_cell_update() {
|
||||||
let mut test = GridCellTest::new().await;
|
let mut test = DatabaseCellTest::new().await;
|
||||||
let field_revs = &test.field_revs;
|
let field_revs = &test.field_revs;
|
||||||
let row_revs = &test.row_revs;
|
let row_revs = &test.row_revs;
|
||||||
let grid_blocks = &test.block_meta_revs;
|
let grid_blocks = &test.block_meta_revs;
|
||||||
@ -60,7 +60,7 @@ async fn grid_cell_update() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn text_cell_date_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 text_field = test.get_first_field_rev(FieldType::RichText);
|
||||||
let cells = test
|
let cells = test
|
||||||
.editor
|
.editor
|
||||||
@ -84,7 +84,7 @@ async fn text_cell_date_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn url_cell_date_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 url_field = test.get_first_field_rev(FieldType::URL);
|
||||||
let cells = test
|
let cells = test
|
||||||
.editor
|
.editor
|
||||||
|
193
frontend/rust-lib/flowy-database/tests/grid/database_editor.rs
Normal file
193
frontend/rust-lib/flowy-database/tests/grid/database_editor.rs
Normal file
@ -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<DatabaseRevisionEditor>,
|
||||||
|
pub field_revs: Vec<Arc<FieldRevision>>,
|
||||||
|
pub block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
|
||||||
|
pub row_revs: Vec<Arc<RowRevision>>,
|
||||||
|
pub field_count: usize,
|
||||||
|
pub row_by_row_id: HashMap<String, RowPB>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Arc<RowRevision>> {
|
||||||
|
self.editor.get_all_row_revs(&self.view_id).await.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn grid_filters(&self) -> Vec<FilterPB> {
|
||||||
|
self.editor.get_all_filters().await.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_field_rev(&self, field_id: &str, field_type: FieldType) -> &Arc<FieldRevision> {
|
||||||
|
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::<Vec<_>>()
|
||||||
|
.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<FieldRevision> {
|
||||||
|
self.field_revs
|
||||||
|
.iter()
|
||||||
|
.filter(|field_rev| {
|
||||||
|
let t_field_type: FieldType = field_rev.ty.into();
|
||||||
|
t_field_type == field_type
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.pop()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_multi_select_type_option(&self, field_id: &str) -> Vec<SelectOptionPB> {
|
||||||
|
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::<MultiSelectTypeOptionPB>(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::<SingleSelectTypeOptionPB>(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::<ChecklistTypeOptionPB>(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::<CheckboxTypeOptionPB>(field_type.into())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_id(&self) -> &str {
|
||||||
|
&self.block_meta_revs.last().unwrap().block_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_cell<T: ToCellChangesetString>(&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;
|
||||||
|
}
|
||||||
|
}
|
@ -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::entities::{CreateFieldParams, FieldChangesetParams, FieldType};
|
||||||
use flowy_database::services::cell::{stringify_cell_data, TypeCellData};
|
use flowy_database::services::cell::{stringify_cell_data, TypeCellData};
|
||||||
use grid_model::FieldRevision;
|
use grid_model::FieldRevision;
|
||||||
@ -38,13 +38,13 @@ pub enum FieldScript {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridFieldTest {
|
pub struct DatabaseFieldTest {
|
||||||
inner: GridEditorTest,
|
inner: DatabaseEditorTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridFieldTest {
|
impl DatabaseFieldTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let editor_test = GridEditorTest::new_table().await;
|
let editor_test = DatabaseEditorTest::new_table().await;
|
||||||
Self { inner: editor_test }
|
Self { inner: editor_test }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,15 +144,15 @@ impl GridFieldTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for GridFieldTest {
|
impl std::ops::Deref for DatabaseFieldTest {
|
||||||
type Target = GridEditorTest;
|
type Target = DatabaseEditorTest;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for GridFieldTest {
|
impl std::ops::DerefMut for DatabaseFieldTest {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
use crate::grid::field_test::script::DatabaseFieldTest;
|
||||||
use crate::grid::field_test::script::FieldScript::*;
|
use crate::grid::field_test::script::FieldScript::*;
|
||||||
use crate::grid::field_test::script::GridFieldTest;
|
|
||||||
use crate::grid::field_test::util::*;
|
use crate::grid::field_test::util::*;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use flowy_database::entities::{FieldChangesetParams, FieldType};
|
use flowy_database::entities::{FieldChangesetParams, FieldType};
|
||||||
@ -8,7 +8,7 @@ use flowy_database::services::field::{gen_option_id, SingleSelectTypeOptionPB, C
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_create_field() {
|
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 (params, field_rev) = create_text_field(&test.view_id());
|
||||||
|
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -33,7 +33,7 @@ async fn grid_create_field() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_create_duplicate_field() {
|
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 (params, _) = create_text_field(&test.view_id());
|
||||||
let field_count = test.field_count();
|
let field_count = test.field_count();
|
||||||
let expected_field_count = field_count + 1;
|
let expected_field_count = field_count + 1;
|
||||||
@ -46,7 +46,7 @@ async fn grid_create_duplicate_field() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_update_field_with_empty_change() {
|
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 (params, _) = create_single_select_field(&test.view_id());
|
||||||
let create_field_index = test.field_count();
|
let create_field_index = test.field_count();
|
||||||
let scripts = vec![CreateField { params }];
|
let scripts = vec![CreateField { params }];
|
||||||
@ -71,7 +71,7 @@ async fn grid_update_field_with_empty_change() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_update_field() {
|
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 (params, _) = create_single_select_field(&test.view_id());
|
||||||
let scripts = vec![CreateField { params }];
|
let scripts = vec![CreateField { params }];
|
||||||
let create_field_index = test.field_count();
|
let create_field_index = test.field_count();
|
||||||
@ -107,7 +107,7 @@ async fn grid_update_field() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_delete_field() {
|
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 original_field_count = test.field_count();
|
||||||
let (params, _) = create_text_field(&test.view_id());
|
let (params, _) = create_text_field(&test.view_id());
|
||||||
let scripts = vec![CreateField { params }];
|
let scripts = vec![CreateField { params }];
|
||||||
@ -125,7 +125,7 @@ async fn grid_delete_field() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_switch_from_select_option_to_checkbox_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);
|
let field_rev = test.get_first_field_rev(FieldType::SingleSelect);
|
||||||
|
|
||||||
// Update the type option data of single select option
|
// 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]
|
#[tokio::test]
|
||||||
async fn grid_switch_from_checkbox_to_select_option_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 field_rev = test.get_first_field_rev(FieldType::Checkbox).clone();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
// switch to single-select field type
|
// 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"
|
// option1, option2 -> "option1.name, option2.name"
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_switch_from_multi_select_to_text_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 field_rev = test.get_first_field_rev(FieldType::MultiSelect).clone();
|
||||||
|
|
||||||
let multi_select_type_option = test.get_multi_select_type_option(&field_rev.id);
|
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 -> ""
|
// unchecked -> ""
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_switch_from_checkbox_to_text_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 field_rev = test.get_first_field_rev(FieldType::Checkbox);
|
||||||
|
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -265,7 +265,7 @@ async fn grid_switch_from_checkbox_to_text_test() {
|
|||||||
// "" -> unchecked
|
// "" -> unchecked
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_switch_from_text_to_checkbox_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 field_rev = test.get_first_field_rev(FieldType::RichText).clone();
|
||||||
|
|
||||||
let scripts = vec![
|
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)
|
// 1647251762 -> Mar 14,2022 (This string will be different base on current data setting)
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_switch_from_date_to_text_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 field_rev = test.get_first_field_rev(FieldType::DateTime).clone();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
SwitchToField {
|
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)
|
// $1 -> "$1"(This string will be different base on current data setting)
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_switch_from_number_to_text_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 field_rev = test.get_first_field_rev(FieldType::Number).clone();
|
||||||
|
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::grid::filter_test::script::FilterScript::*;
|
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;
|
use flowy_database::entities::CheckboxFilterConditionPB;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_checkbox_is_check_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();
|
let row_count = test.row_revs.len();
|
||||||
// The initial number of unchecked is 3
|
// The initial number of unchecked is 3
|
||||||
// The initial number of checked is 2
|
// The initial number of checked is 2
|
||||||
@ -20,7 +20,7 @@ async fn grid_filter_checkbox_is_check_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_checkbox_is_uncheck_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 expected = 3;
|
||||||
let row_count = test.row_revs.len();
|
let row_count = test.row_revs.len();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::grid::filter_test::script::FilterScript::*;
|
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;
|
use flowy_database::entities::ChecklistFilterConditionPB;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_checklist_is_incomplete_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 expected = 5;
|
||||||
let row_count = test.row_revs.len();
|
let row_count = test.row_revs.len();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -22,7 +22,7 @@ async fn grid_filter_checklist_is_incomplete_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_checklist_is_complete_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 expected = 1;
|
||||||
let row_count = test.row_revs.len();
|
let row_count = test.row_revs.len();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::grid::filter_test::script::FilterScript::*;
|
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;
|
use flowy_database::entities::DateFilterConditionPB;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_date_is_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 row_count = test.row_revs.len();
|
||||||
let expected = 3;
|
let expected = 3;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -25,7 +25,7 @@ async fn grid_filter_date_is_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_date_after_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 row_count = test.row_revs.len();
|
||||||
let expected = 3;
|
let expected = 3;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -46,7 +46,7 @@ async fn grid_filter_date_after_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_date_on_or_after_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 row_count = test.row_revs.len();
|
||||||
let expected = 3;
|
let expected = 3;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -67,7 +67,7 @@ async fn grid_filter_date_on_or_after_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_date_on_or_before_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 row_count = test.row_revs.len();
|
||||||
let expected = 4;
|
let expected = 4;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -88,7 +88,7 @@ async fn grid_filter_date_on_or_before_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_date_within_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 row_count = test.row_revs.len();
|
||||||
let expected = 5;
|
let expected = 5;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::grid::filter_test::script::FilterScript::*;
|
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;
|
use flowy_database::entities::NumberFilterConditionPB;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_number_is_equal_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 row_count = test.row_revs.len();
|
||||||
let expected = 1;
|
let expected = 1;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -23,7 +23,7 @@ async fn grid_filter_number_is_equal_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_number_is_less_than_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 row_count = test.row_revs.len();
|
||||||
let expected = 2;
|
let expected = 2;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -43,7 +43,7 @@ async fn grid_filter_number_is_less_than_test() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
async fn grid_filter_number_is_less_than_test2() {
|
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 row_count = test.row_revs.len();
|
||||||
let expected = 2;
|
let expected = 2;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -62,7 +62,7 @@ async fn grid_filter_number_is_less_than_test2() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_number_is_less_than_or_equal_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 row_count = test.row_revs.len();
|
||||||
let expected = 3;
|
let expected = 3;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -81,7 +81,7 @@ async fn grid_filter_number_is_less_than_or_equal_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_number_is_empty_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 row_count = test.row_revs.len();
|
||||||
let expected = 1;
|
let expected = 1;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -100,7 +100,7 @@ async fn grid_filter_number_is_empty_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_number_is_not_empty_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 row_count = test.row_revs.len();
|
||||||
let expected = 5;
|
let expected = 5;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
|
@ -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::cell::insert_select_option_cell;
|
||||||
use flowy_database::services::filter::FilterType;
|
use flowy_database::services::filter::FilterType;
|
||||||
use flowy_database::services::view_editor::GridViewChanged;
|
use flowy_database::services::view_editor::GridViewChanged;
|
||||||
use crate::grid::grid_editor::GridEditorTest;
|
use crate::grid::database_editor::DatabaseEditorTest;
|
||||||
|
|
||||||
pub struct FilterRowChanged {
|
pub struct FilterRowChanged {
|
||||||
pub(crate) showing_num_of_rows: usize,
|
pub(crate) showing_num_of_rows: usize,
|
||||||
@ -99,14 +99,14 @@ pub enum FilterScript {
|
|||||||
Wait { millisecond: u64 }
|
Wait { millisecond: u64 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridFilterTest {
|
pub struct DatabaseFilterTest {
|
||||||
inner: GridEditorTest,
|
inner: DatabaseEditorTest,
|
||||||
recv: Option<Receiver<GridViewChanged>>,
|
recv: Option<Receiver<GridViewChanged>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridFilterTest {
|
impl DatabaseFilterTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let editor_test = GridEditorTest::new_table().await;
|
let editor_test = DatabaseEditorTest::new_table().await;
|
||||||
Self {
|
Self {
|
||||||
inner: editor_test,
|
inner: editor_test,
|
||||||
recv: None,
|
recv: None,
|
||||||
@ -298,15 +298,15 @@ impl GridFilterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl std::ops::Deref for GridFilterTest {
|
impl std::ops::Deref for DatabaseFilterTest {
|
||||||
type Target = GridEditorTest;
|
type Target = DatabaseEditorTest;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for GridFilterTest {
|
impl std::ops::DerefMut for DatabaseFilterTest {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::grid::filter_test::script::FilterScript::*;
|
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};
|
use flowy_database::entities::{FieldType, SelectOptionConditionPB};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_multi_select_is_empty_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![
|
let scripts = vec![
|
||||||
CreateMultiSelectFilter {
|
CreateMultiSelectFilter {
|
||||||
condition: SelectOptionConditionPB::OptionIsEmpty,
|
condition: SelectOptionConditionPB::OptionIsEmpty,
|
||||||
@ -17,7 +17,7 @@ async fn grid_filter_multi_select_is_empty_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_multi_select_is_not_empty_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![
|
let scripts = vec![
|
||||||
CreateMultiSelectFilter {
|
CreateMultiSelectFilter {
|
||||||
condition: SelectOptionConditionPB::OptionIsNotEmpty,
|
condition: SelectOptionConditionPB::OptionIsNotEmpty,
|
||||||
@ -30,7 +30,7 @@ async fn grid_filter_multi_select_is_not_empty_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_multi_select_is_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 field_rev = test.get_first_field_rev(FieldType::MultiSelect);
|
||||||
let mut options = test.get_multi_select_type_option(&field_rev.id);
|
let mut options = test.get_multi_select_type_option(&field_rev.id);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -45,7 +45,7 @@ async fn grid_filter_multi_select_is_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_multi_select_is_test2() {
|
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 field_rev = test.get_first_field_rev(FieldType::MultiSelect);
|
||||||
let mut options = test.get_multi_select_type_option(&field_rev.id);
|
let mut options = test.get_multi_select_type_option(&field_rev.id);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -60,7 +60,7 @@ async fn grid_filter_multi_select_is_test2() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_single_select_is_empty_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 expected = 2;
|
||||||
let row_count = test.row_revs.len();
|
let row_count = test.row_revs.len();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -79,7 +79,7 @@ async fn grid_filter_single_select_is_empty_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_single_select_is_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 field_rev = test.get_first_field_rev(FieldType::SingleSelect);
|
||||||
let mut options = test.get_single_select_type_option(&field_rev.id).options;
|
let mut options = test.get_single_select_type_option(&field_rev.id).options;
|
||||||
let expected = 2;
|
let expected = 2;
|
||||||
@ -100,7 +100,7 @@ async fn grid_filter_single_select_is_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_single_select_is_test2() {
|
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 field_rev = test.get_first_field_rev(FieldType::SingleSelect);
|
||||||
let row_revs = test.get_row_revs().await;
|
let row_revs = test.get_row_revs().await;
|
||||||
let mut options = test.get_single_select_type_option(&field_rev.id).options;
|
let mut options = test.get_single_select_type_option(&field_rev.id).options;
|
||||||
|
@ -5,7 +5,7 @@ use flowy_database::services::filter::FilterType;
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_text_is_empty_test() {
|
async fn grid_filter_text_is_empty_test() {
|
||||||
let mut test = GridFilterTest::new().await;
|
let mut test = DatabaseFilterTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
CreateTextFilter {
|
CreateTextFilter {
|
||||||
condition: TextFilterConditionPB::TextIsEmpty,
|
condition: TextFilterConditionPB::TextIsEmpty,
|
||||||
@ -22,7 +22,7 @@ async fn grid_filter_text_is_empty_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_text_is_not_empty_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 ""
|
// Only one row's text of the initial rows is ""
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
CreateTextFilter {
|
CreateTextFilter {
|
||||||
@ -55,7 +55,7 @@ async fn grid_filter_text_is_not_empty_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_is_text_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"
|
// Only one row's text of the initial rows is "A"
|
||||||
let scripts = vec![CreateTextFilter {
|
let scripts = vec![CreateTextFilter {
|
||||||
condition: TextFilterConditionPB::Is,
|
condition: TextFilterConditionPB::Is,
|
||||||
@ -70,7 +70,7 @@ async fn grid_filter_is_text_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_contain_text_test() {
|
async fn grid_filter_contain_text_test() {
|
||||||
let mut test = GridFilterTest::new().await;
|
let mut test = DatabaseFilterTest::new().await;
|
||||||
let scripts = vec![CreateTextFilter {
|
let scripts = vec![CreateTextFilter {
|
||||||
condition: TextFilterConditionPB::Contains,
|
condition: TextFilterConditionPB::Contains,
|
||||||
content: "A".to_string(),
|
content: "A".to_string(),
|
||||||
@ -84,7 +84,7 @@ async fn grid_filter_contain_text_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_contain_text_test2() {
|
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 row_revs = test.row_revs.clone();
|
||||||
|
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -110,7 +110,7 @@ async fn grid_filter_contain_text_test2() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_does_not_contain_text_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"
|
// None of the initial rows contains the text "AB"
|
||||||
let scripts = vec![CreateTextFilter {
|
let scripts = vec![CreateTextFilter {
|
||||||
condition: TextFilterConditionPB::DoesNotContain,
|
condition: TextFilterConditionPB::DoesNotContain,
|
||||||
@ -125,7 +125,7 @@ async fn grid_filter_does_not_contain_text_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_start_with_text_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 {
|
let scripts = vec![CreateTextFilter {
|
||||||
condition: TextFilterConditionPB::StartsWith,
|
condition: TextFilterConditionPB::StartsWith,
|
||||||
content: "A".to_string(),
|
content: "A".to_string(),
|
||||||
@ -139,7 +139,7 @@ async fn grid_filter_start_with_text_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_ends_with_text_test() {
|
async fn grid_filter_ends_with_text_test() {
|
||||||
let mut test = GridFilterTest::new().await;
|
let mut test = DatabaseFilterTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
CreateTextFilter {
|
CreateTextFilter {
|
||||||
condition: TextFilterConditionPB::EndsWith,
|
condition: TextFilterConditionPB::EndsWith,
|
||||||
@ -153,7 +153,7 @@ async fn grid_filter_ends_with_text_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_update_text_filter_test() {
|
async fn grid_update_text_filter_test() {
|
||||||
let mut test = GridFilterTest::new().await;
|
let mut test = DatabaseFilterTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
CreateTextFilter {
|
CreateTextFilter {
|
||||||
condition: TextFilterConditionPB::EndsWith,
|
condition: TextFilterConditionPB::EndsWith,
|
||||||
@ -187,7 +187,7 @@ async fn grid_update_text_filter_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_delete_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 field_rev = test.get_first_field_rev(FieldType::RichText).clone();
|
||||||
let text_filter = TextFilterPB {
|
let text_filter = TextFilterPB {
|
||||||
condition: TextFilterConditionPB::TextIsEmpty,
|
condition: TextFilterConditionPB::TextIsEmpty,
|
||||||
@ -216,7 +216,7 @@ async fn grid_filter_delete_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn grid_filter_update_empty_text_cell_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 row_revs = test.row_revs.clone();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
CreateTextFilter {
|
CreateTextFilter {
|
||||||
|
@ -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<DatabaseRevisionEditor>,
|
|
||||||
pub field_revs: Vec<Arc<FieldRevision>>,
|
|
||||||
pub block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
|
|
||||||
pub row_revs: Vec<Arc<RowRevision>>,
|
|
||||||
pub field_count: usize,
|
|
||||||
pub row_by_row_id: HashMap<String, RowPB>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Arc<RowRevision>> {
|
|
||||||
self.editor.get_all_row_revs(&self.view_id).await.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn grid_filters(&self) -> Vec<FilterPB> {
|
|
||||||
self.editor.get_all_filters().await.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_field_rev(&self, field_id: &str, field_type: FieldType) -> &Arc<FieldRevision> {
|
|
||||||
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::<Vec<_>>()
|
|
||||||
.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<FieldRevision> {
|
|
||||||
self.field_revs
|
|
||||||
.iter()
|
|
||||||
.filter(|field_rev| {
|
|
||||||
let t_field_type: FieldType = field_rev.ty.into();
|
|
||||||
t_field_type == field_type
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.pop()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_multi_select_type_option(&self, field_id: &str) -> Vec<SelectOptionPB> {
|
|
||||||
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::<MultiSelectTypeOptionPB>(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::<SingleSelectTypeOptionPB>(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::<ChecklistTypeOptionPB>(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::<CheckboxTypeOptionPB>(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<T: ToCellChangesetString>(&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!()
|
|
||||||
}
|
|
@ -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::<Vec<_>>()
|
|
||||||
.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::<DateCellData>()
|
|
||||||
.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()
|
|
||||||
}
|
|
@ -1,2 +1,3 @@
|
|||||||
mod script;
|
mod script;
|
||||||
mod test;
|
mod test;
|
||||||
|
mod url_group_test;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::grid::grid_editor::GridEditorTest;
|
use crate::grid::database_editor::DatabaseEditorTest;
|
||||||
use flowy_database::entities::{
|
use flowy_database::entities::{
|
||||||
CreateRowParams, DatabaseViewLayout, FieldType, GroupPB, MoveGroupParams, MoveGroupRowParams, RowPB,
|
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::{
|
use flowy_database::services::field::{
|
||||||
edit_single_select_type_option, SelectOptionPB, SelectTypeOptionSharedAction, SingleSelectTypeOptionPB,
|
edit_single_select_type_option, SelectOptionPB, SelectTypeOptionSharedAction, SingleSelectTypeOptionPB,
|
||||||
};
|
};
|
||||||
@ -37,11 +37,16 @@ pub enum GroupScript {
|
|||||||
group_index: usize,
|
group_index: usize,
|
||||||
row_index: usize,
|
row_index: usize,
|
||||||
},
|
},
|
||||||
UpdateRow {
|
UpdateGroupedCell {
|
||||||
from_group_index: usize,
|
from_group_index: usize,
|
||||||
row_index: usize,
|
row_index: usize,
|
||||||
to_group_index: usize,
|
to_group_index: usize,
|
||||||
},
|
},
|
||||||
|
UpdateGroupedCellWithData {
|
||||||
|
from_group_index: usize,
|
||||||
|
row_index: usize,
|
||||||
|
cell_data: String,
|
||||||
|
},
|
||||||
MoveGroup {
|
MoveGroup {
|
||||||
from_group_index: usize,
|
from_group_index: usize,
|
||||||
to_group_index: usize,
|
to_group_index: usize,
|
||||||
@ -54,13 +59,13 @@ pub enum GroupScript {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridGroupTest {
|
pub struct DatabaseGroupTest {
|
||||||
inner: GridEditorTest,
|
inner: DatabaseEditorTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridGroupTest {
|
impl DatabaseGroupTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let editor_test = GridEditorTest::new_board().await;
|
let editor_test = DatabaseEditorTest::new_board().await;
|
||||||
Self { inner: editor_test }
|
Self { inner: editor_test }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +114,6 @@ impl GridGroupTest {
|
|||||||
assert_eq!(row.id, compare_row.id);
|
assert_eq!(row.id, compare_row.id);
|
||||||
}
|
}
|
||||||
GroupScript::CreateRow { group_index } => {
|
GroupScript::CreateRow { group_index } => {
|
||||||
//
|
|
||||||
let group = self.group_at_index(group_index).await;
|
let group = self.group_at_index(group_index).await;
|
||||||
let params = CreateRowParams {
|
let params = CreateRowParams {
|
||||||
database_id: self.editor.database_id.clone(),
|
database_id: self.editor.database_id.clone(),
|
||||||
@ -123,7 +127,7 @@ impl GridGroupTest {
|
|||||||
let row = self.row_at_index(group_index, row_index).await;
|
let row = self.row_at_index(group_index, row_index).await;
|
||||||
self.editor.delete_row(&row.id).await.unwrap();
|
self.editor.delete_row(&row.id).await.unwrap();
|
||||||
}
|
}
|
||||||
GroupScript::UpdateRow {
|
GroupScript::UpdateGroupedCell {
|
||||||
from_group_index,
|
from_group_index,
|
||||||
row_index,
|
row_index,
|
||||||
to_group_index,
|
to_group_index,
|
||||||
@ -154,6 +158,7 @@ impl GridGroupTest {
|
|||||||
FieldType::MultiSelect => {
|
FieldType::MultiSelect => {
|
||||||
insert_select_option_cell(vec![to_group.group_id.clone()], &field_rev)
|
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");
|
panic!("Unsupported group field type");
|
||||||
}
|
}
|
||||||
@ -165,6 +170,27 @@ impl GridGroupTest {
|
|||||||
row_changeset.cell_by_field_id.insert(field_id, cell_rev);
|
row_changeset.cell_by_field_id.insert(field_id, cell_rev);
|
||||||
self.editor.update_row(row_changeset).await.unwrap();
|
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 {
|
GroupScript::MoveGroup {
|
||||||
from_group_index,
|
from_group_index,
|
||||||
to_group_index,
|
to_group_index,
|
||||||
@ -258,15 +284,15 @@ impl GridGroupTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for GridGroupTest {
|
impl std::ops::Deref for DatabaseGroupTest {
|
||||||
type Target = GridEditorTest;
|
type Target = DatabaseEditorTest;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for GridGroupTest {
|
impl std::ops::DerefMut for DatabaseGroupTest {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
@ -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 crate::grid::group_test::script::GroupScript::*;
|
||||||
|
|
||||||
use flowy_database::services::field::SelectOptionPB;
|
use flowy_database::services::field::SelectOptionPB;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_init_test() {
|
async fn group_init_test() {
|
||||||
let mut test = GridGroupTest::new().await;
|
let mut test = DatabaseGroupTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertGroupCount(4),
|
AssertGroupCount(4),
|
||||||
AssertGroupRowCount {
|
AssertGroupRowCount {
|
||||||
@ -30,7 +30,7 @@ async fn group_init_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_row_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 group = test.group_at_index(1).await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
// Move the row at 0 in group0 to group1 at 1
|
// Move the row at 0 in group0 to group1 at 1
|
||||||
@ -55,7 +55,7 @@ async fn group_move_row_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_row_to_other_group_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 group = test.group_at_index(1).await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
MoveRow {
|
MoveRow {
|
||||||
@ -83,7 +83,7 @@ async fn group_move_row_to_other_group_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_two_row_to_other_group_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 group_1 = test.group_at_index(1).await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
// Move row at index 0 from group 1 to group 2 at index 1
|
// 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]
|
#[tokio::test]
|
||||||
async fn group_move_row_to_other_group_and_reorder_from_up_to_down_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_1 = test.group_at_index(1).await;
|
||||||
let group_2 = test.group_at_index(2).await;
|
let group_2 = test.group_at_index(2).await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -173,7 +173,7 @@ async fn group_move_row_to_other_group_and_reorder_from_up_to_down_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_row_to_other_group_and_reorder_from_bottom_to_up_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 {
|
let scripts = vec![MoveRow {
|
||||||
from_group_index: 1,
|
from_group_index: 1,
|
||||||
from_row_index: 0,
|
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]
|
#[tokio::test]
|
||||||
async fn group_create_row_test() {
|
async fn group_create_row_test() {
|
||||||
let mut test = GridGroupTest::new().await;
|
let mut test = DatabaseGroupTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
CreateRow { group_index: 1 },
|
CreateRow { group_index: 1 },
|
||||||
AssertGroupRowCount {
|
AssertGroupRowCount {
|
||||||
@ -223,7 +223,7 @@ async fn group_create_row_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_delete_row_test() {
|
async fn group_delete_row_test() {
|
||||||
let mut test = GridGroupTest::new().await;
|
let mut test = DatabaseGroupTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
DeleteRow {
|
DeleteRow {
|
||||||
group_index: 1,
|
group_index: 1,
|
||||||
@ -239,7 +239,7 @@ async fn group_delete_row_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_delete_all_row_test() {
|
async fn group_delete_all_row_test() {
|
||||||
let mut test = GridGroupTest::new().await;
|
let mut test = DatabaseGroupTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
DeleteRow {
|
DeleteRow {
|
||||||
group_index: 1,
|
group_index: 1,
|
||||||
@ -259,10 +259,10 @@ async fn group_delete_all_row_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_update_row_test() {
|
async fn group_update_row_test() {
|
||||||
let mut test = GridGroupTest::new().await;
|
let mut test = DatabaseGroupTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
// Update the row at 0 in group0 by setting the row's group field data
|
// Update the row at 0 in group0 by setting the row's group field data
|
||||||
UpdateRow {
|
UpdateGroupedCell {
|
||||||
from_group_index: 1,
|
from_group_index: 1,
|
||||||
row_index: 0,
|
row_index: 0,
|
||||||
to_group_index: 2,
|
to_group_index: 2,
|
||||||
@ -281,10 +281,10 @@ async fn group_update_row_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_reorder_group_test() {
|
async fn group_reorder_group_test() {
|
||||||
let mut test = GridGroupTest::new().await;
|
let mut test = DatabaseGroupTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
// Update the row at 0 in group0 by setting the row's group field data
|
// Update the row at 0 in group0 by setting the row's group field data
|
||||||
UpdateRow {
|
UpdateGroupedCell {
|
||||||
from_group_index: 1,
|
from_group_index: 1,
|
||||||
row_index: 0,
|
row_index: 0,
|
||||||
to_group_index: 2,
|
to_group_index: 2,
|
||||||
@ -303,9 +303,9 @@ async fn group_reorder_group_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_to_default_group_test() {
|
async fn group_move_to_default_group_test() {
|
||||||
let mut test = GridGroupTest::new().await;
|
let mut test = DatabaseGroupTest::new().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
UpdateRow {
|
UpdateGroupedCell {
|
||||||
from_group_index: 1,
|
from_group_index: 1,
|
||||||
row_index: 0,
|
row_index: 0,
|
||||||
to_group_index: 0,
|
to_group_index: 0,
|
||||||
@ -324,10 +324,10 @@ async fn group_move_to_default_group_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_from_default_group_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
|
// Move one row from group 1 to group 0
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
UpdateRow {
|
UpdateGroupedCell {
|
||||||
from_group_index: 1,
|
from_group_index: 1,
|
||||||
row_index: 0,
|
row_index: 0,
|
||||||
to_group_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
|
// Move one row from group 0 to group 1
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
UpdateRow {
|
UpdateGroupedCell {
|
||||||
from_group_index: 0,
|
from_group_index: 0,
|
||||||
row_index: 0,
|
row_index: 0,
|
||||||
to_group_index: 1,
|
to_group_index: 1,
|
||||||
@ -364,7 +364,7 @@ async fn group_move_from_default_group_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_group_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_0 = test.group_at_index(0).await;
|
||||||
let group_1 = test.group_at_index(1).await;
|
let group_1 = test.group_at_index(1).await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -394,7 +394,7 @@ async fn group_move_group_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_group_row_after_move_group_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_1 = test.group_at_index(1).await;
|
||||||
let group_2 = test.group_at_index(2).await;
|
let group_2 = test.group_at_index(2).await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -430,7 +430,7 @@ async fn group_move_group_row_after_move_group_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_move_group_to_default_group_pos_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_0 = test.group_at_index(0).await;
|
||||||
let group_3 = test.group_at_index(3).await;
|
let group_3 = test.group_at_index(3).await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
@ -452,7 +452,7 @@ async fn group_move_group_to_default_group_pos_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_insert_single_select_option_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 new_option_name = "New option";
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertGroupCount(4),
|
AssertGroupCount(4),
|
||||||
@ -468,7 +468,7 @@ async fn group_insert_single_select_option_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn group_group_by_other_field() {
|
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 multi_select_field = test.get_multi_select_field().await;
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
GroupByField {
|
GroupByField {
|
||||||
@ -486,52 +486,3 @@ async fn group_group_by_other_field() {
|
|||||||
];
|
];
|
||||||
test.run_scripts(scripts).await;
|
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;
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
use grid_model::BuildDatabaseContext;
|
||||||
|
|
||||||
|
// Calendar unit test mock data
|
||||||
|
pub fn make_test_calendar() -> BuildDatabaseContext {
|
||||||
|
todo!()
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
19
frontend/rust-lib/flowy-database/tests/grid/mock_data/mod.rs
Normal file
19
frontend/rust-lib/flowy-database/tests/grid/mock_data/mod.rs
Normal file
@ -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";
|
@ -1,8 +1,10 @@
|
|||||||
mod block_test;
|
mod block_test;
|
||||||
mod cell_test;
|
mod cell_test;
|
||||||
|
mod database_editor;
|
||||||
mod field_test;
|
mod field_test;
|
||||||
mod filter_test;
|
mod filter_test;
|
||||||
mod grid_editor;
|
|
||||||
mod group_test;
|
mod group_test;
|
||||||
mod snapshot_test;
|
mod snapshot_test;
|
||||||
mod sort_test;
|
mod sort_test;
|
||||||
|
|
||||||
|
mod mock_data;
|
||||||
|
@ -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<String>,
|
|
||||||
},
|
|
||||||
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<GridMetaEditor>,
|
|
||||||
pub field_metas: Vec<FieldMeta>,
|
|
||||||
pub grid_blocks: Vec<GridBlockMetaSnapshot>,
|
|
||||||
pub row_metas: Vec<Arc<RowMeta>>,
|
|
||||||
pub field_count: usize,
|
|
||||||
|
|
||||||
pub row_order_by_row_id: HashMap<String, RowOrder>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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::<FieldOrder>(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<EditorScript>) {
|
|
||||||
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::<FieldOrder>(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::<FieldOrder>(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::<FieldOrder>(None).await.unwrap();
|
|
||||||
assert_eq!(self.field_count, self.field_metas.len());
|
|
||||||
}
|
|
||||||
EditorScript::AssertFieldCount(count) => {
|
|
||||||
assert_eq!(
|
|
||||||
self.editor.get_field_metas::<FieldOrder>(None).await.unwrap().len(),
|
|
||||||
count
|
|
||||||
);
|
|
||||||
}
|
|
||||||
EditorScript::AssertFieldEqual {
|
|
||||||
field_index,
|
|
||||||
field_meta,
|
|
||||||
} => {
|
|
||||||
let field_metas = self.editor.get_field_metas::<FieldOrder>(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::<Vec<RowOrder>>();
|
|
||||||
|
|
||||||
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::<GridPadBuilder>(None).await.unwrap();
|
|
||||||
println!("{}", grid_pad.delta_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_row_metas(&self) -> Vec<Arc<RowMeta>> {
|
|
||||||
get_row_metas(&self.editor).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_row_metas(editor: &Arc<GridMetaEditor>) -> Vec<Arc<RowMeta>> {
|
|
||||||
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::<RichTextTypeOptionPB>(&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::<SingleSelectTypeOption>(&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()
|
|
||||||
}
|
|
@ -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_client_sync::client_database::{DatabaseOperations, DatabaseRevisionPad};
|
||||||
use flowy_revision::{RevisionSnapshot, REVISION_WRITE_INTERVAL_IN_MILLIS};
|
use flowy_revision::{RevisionSnapshot, REVISION_WRITE_INTERVAL_IN_MILLIS};
|
||||||
@ -26,15 +26,15 @@ pub enum SnapshotScript {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridSnapshotTest {
|
pub struct DatabaseSnapshotTest {
|
||||||
inner: GridEditorTest,
|
inner: DatabaseEditorTest,
|
||||||
pub current_snapshot: Option<RevisionSnapshot>,
|
pub current_snapshot: Option<RevisionSnapshot>,
|
||||||
pub current_revision: Option<Revision>,
|
pub current_revision: Option<Revision>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridSnapshotTest {
|
impl DatabaseSnapshotTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let editor_test = GridEditorTest::new_table().await;
|
let editor_test = DatabaseEditorTest::new_table().await;
|
||||||
Self {
|
Self {
|
||||||
inner: editor_test,
|
inner: editor_test,
|
||||||
current_snapshot: None,
|
current_snapshot: None,
|
||||||
@ -88,15 +88,15 @@ impl GridSnapshotTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl std::ops::Deref for GridSnapshotTest {
|
impl std::ops::Deref for DatabaseSnapshotTest {
|
||||||
type Target = GridEditorTest;
|
type Target = DatabaseEditorTest;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for GridSnapshotTest {
|
impl std::ops::DerefMut for DatabaseSnapshotTest {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::grid::field_test::util::create_text_field;
|
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]
|
#[tokio::test]
|
||||||
async fn snapshot_create_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 (_, field_rev) = create_text_field(&test.grid_id());
|
||||||
let scripts = vec![CreateField { field_rev }, WriteSnapshot];
|
let scripts = vec![CreateField { field_rev }, WriteSnapshot];
|
||||||
test.run_scripts(scripts).await;
|
test.run_scripts(scripts).await;
|
||||||
@ -19,7 +19,7 @@ async fn snapshot_create_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn snapshot_multi_version_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();
|
let original_content = test.grid_pad().await.json_str().unwrap();
|
||||||
|
|
||||||
// Create a field
|
// Create a field
|
||||||
|
@ -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 flowy_database::entities::FieldType;
|
||||||
use grid_model::SortCondition;
|
use grid_model::SortCondition;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_checkbox_and_then_text_by_descending_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 checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
|
||||||
let text_field = test.get_first_field_rev(FieldType::RichText);
|
let text_field = test.get_first_field_rev(FieldType::RichText);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
|
@ -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 crate::grid::sort_test::script::SortScript::*;
|
||||||
use flowy_database::entities::FieldType;
|
use flowy_database::entities::FieldType;
|
||||||
use grid_model::SortCondition;
|
use grid_model::SortCondition;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_text_with_checkbox_by_ascending_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 text_field = test.get_first_field_rev(FieldType::RichText).clone();
|
||||||
let checkbox_field = test.get_first_field_rev(FieldType::Checkbox).clone();
|
let checkbox_field = test.get_first_field_rev(FieldType::Checkbox).clone();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::grid::grid_editor::GridEditorTest;
|
use crate::grid::database_editor::DatabaseEditorTest;
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use flowy_database::entities::{AlterSortParams, CellPathParams, DeleteSortParams};
|
use flowy_database::entities::{AlterSortParams, CellPathParams, DeleteSortParams};
|
||||||
use flowy_database::services::sort::SortType;
|
use flowy_database::services::sort::SortType;
|
||||||
@ -36,15 +36,15 @@ pub enum SortScript {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GridSortTest {
|
pub struct DatabaseSortTest {
|
||||||
inner: GridEditorTest,
|
inner: DatabaseEditorTest,
|
||||||
pub current_sort_rev: Option<SortRevision>,
|
pub current_sort_rev: Option<SortRevision>,
|
||||||
recv: Option<Receiver<GridViewChanged>>,
|
recv: Option<Receiver<GridViewChanged>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridSortTest {
|
impl DatabaseSortTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let editor_test = GridEditorTest::new_table().await;
|
let editor_test = DatabaseEditorTest::new_table().await;
|
||||||
Self {
|
Self {
|
||||||
inner: editor_test,
|
inner: editor_test,
|
||||||
current_sort_rev: None,
|
current_sort_rev: None,
|
||||||
@ -154,15 +154,15 @@ async fn assert_sort_changed(
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for GridSortTest {
|
impl std::ops::Deref for DatabaseSortTest {
|
||||||
type Target = GridEditorTest;
|
type Target = DatabaseEditorTest;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for GridSortTest {
|
impl std::ops::DerefMut for DatabaseSortTest {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.inner
|
&mut self.inner
|
||||||
}
|
}
|
||||||
|
@ -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 flowy_database::entities::FieldType;
|
||||||
use grid_model::SortCondition;
|
use grid_model::SortCondition;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_text_by_ascending_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 text_field = test.get_first_field_rev(FieldType::RichText);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
@ -25,7 +25,7 @@ async fn sort_text_by_ascending_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_change_notification_by_update_text_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 text_field = test.get_first_field_rev(FieldType::RichText).clone();
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
InsertSort {
|
InsertSort {
|
||||||
@ -57,7 +57,7 @@ async fn sort_change_notification_by_update_text_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_text_by_ascending_and_delete_sort_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 text_field = test.get_first_field_rev(FieldType::RichText).clone();
|
||||||
let scripts = vec![InsertSort {
|
let scripts = vec![InsertSort {
|
||||||
field_rev: text_field.clone(),
|
field_rev: text_field.clone(),
|
||||||
@ -80,7 +80,7 @@ async fn sort_text_by_ascending_and_delete_sort_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_text_by_descending_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 text_field = test.get_first_field_rev(FieldType::RichText);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
@ -101,7 +101,7 @@ async fn sort_text_by_descending_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_checkbox_by_ascending_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 checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
@ -118,7 +118,7 @@ async fn sort_checkbox_by_ascending_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_checkbox_by_descending_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 checkbox_field = test.get_first_field_rev(FieldType::Checkbox);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
@ -139,7 +139,7 @@ async fn sort_checkbox_by_descending_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_date_by_ascending_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 date_field = test.get_first_field_rev(FieldType::DateTime);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
@ -160,7 +160,7 @@ async fn sort_date_by_ascending_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_date_by_descending_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 date_field = test.get_first_field_rev(FieldType::DateTime);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
@ -195,7 +195,7 @@ async fn sort_date_by_descending_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_number_by_descending_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 number_field = test.get_first_field_rev(FieldType::Number);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
@ -216,7 +216,7 @@ async fn sort_number_by_descending_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_single_select_by_descending_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 single_select = test.get_first_field_rev(FieldType::SingleSelect);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
@ -237,7 +237,7 @@ async fn sort_single_select_by_descending_test() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sort_multi_select_by_ascending_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 multi_select = test.get_first_field_rev(FieldType::MultiSelect);
|
||||||
let scripts = vec![
|
let scripts = vec![
|
||||||
AssertCellContentOrder {
|
AssertCellContentOrder {
|
||||||
|
@ -5,8 +5,6 @@ use std::convert::TryInto;
|
|||||||
impl lib_dispatch::Error for FlowyError {
|
impl lib_dispatch::Error for FlowyError {
|
||||||
fn as_response(&self) -> AFPluginEventResponse {
|
fn as_response(&self) -> AFPluginEventResponse {
|
||||||
let bytes: Bytes = self.clone().try_into().unwrap();
|
let bytes: Bytes = self.clone().try_into().unwrap();
|
||||||
|
|
||||||
println!("Serialize FlowyError: {:?} to event response", self);
|
|
||||||
ResponseBuilder::Err().data(bytes).build()
|
ResponseBuilder::Err().data(bytes).build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ pub mod helper;
|
|||||||
|
|
||||||
use crate::helper::*;
|
use crate::helper::*;
|
||||||
|
|
||||||
use flowy_core::{FlowySDK, FlowySDKConfig};
|
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
|
||||||
use flowy_document::entities::DocumentVersionPB;
|
use flowy_document::entities::DocumentVersionPB;
|
||||||
use flowy_net::get_client_server_configuration;
|
use flowy_net::get_client_server_configuration;
|
||||||
use flowy_user::entities::UserProfilePB;
|
use flowy_user::entities::UserProfilePB;
|
||||||
@ -16,11 +16,11 @@ pub mod prelude {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FlowySDKTest {
|
pub struct FlowySDKTest {
|
||||||
pub inner: FlowySDK,
|
pub inner: AppFlowyCore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for FlowySDKTest {
|
impl std::ops::Deref for FlowySDKTest {
|
||||||
type Target = FlowySDK;
|
type Target = AppFlowyCore;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
@ -36,10 +36,10 @@ impl std::default::Default for FlowySDKTest {
|
|||||||
impl FlowySDKTest {
|
impl FlowySDKTest {
|
||||||
pub fn new(document_version: DocumentVersionPB) -> Self {
|
pub fn new(document_version: DocumentVersionPB) -> Self {
|
||||||
let server_config = get_client_server_configuration().unwrap();
|
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)
|
.with_document_version(document_version)
|
||||||
.log_filter("info", vec![]);
|
.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());
|
std::mem::forget(sdk.dispatcher());
|
||||||
Self { inner: sdk }
|
Self { inner: sdk }
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
echo "Start building rust sdk"
|
echo "Start building rust sdk"
|
||||||
rustup show
|
rustup show
|
||||||
cargo make --profile development-windows-x86 appflowy-sdk-dev
|
cargo make --profile development-windows-x86 appflowy-core-dev
|
@ -17,15 +17,15 @@ rustup show
|
|||||||
|
|
||||||
case "$FLOWY_DEV_ENV" in
|
case "$FLOWY_DEV_ENV" in
|
||||||
Linux)
|
Linux)
|
||||||
cargo make --profile "development-linux-$(uname -m)" appflowy-sdk-dev
|
cargo make --profile "development-linux-$(uname -m)" appflowy-core-dev
|
||||||
;;
|
;;
|
||||||
|
|
||||||
macOS)
|
macOS)
|
||||||
cargo make --profile "development-mac-$(uname -m)" appflowy-sdk-dev
|
cargo make --profile "development-mac-$(uname -m)" appflowy-core-dev
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Windows)
|
Windows)
|
||||||
cargo make --profile development-windows appflowy-sdk-dev
|
cargo make --profile development-windows appflowy-core-dev
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
|
@ -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]
|
[tasks.env_check]
|
||||||
dependencies = ["echo_env", "install_flutter_protobuf"]
|
dependencies = ["echo_env", "install_flutter_protobuf"]
|
||||||
@ -15,12 +10,12 @@ condition = { env_set = [
|
|||||||
"stable",
|
"stable",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[tasks.appflowy-sdk-dev]
|
[tasks.appflowy-core-dev]
|
||||||
mac_alias = "appflowy-sdk-dev-macos"
|
mac_alias = "appflowy-core-dev-macos"
|
||||||
windows_alias = "appflowy-sdk-dev-windows"
|
windows_alias = "appflowy-core-dev-windows"
|
||||||
linux_alias = "appflowy-sdk-dev-linux"
|
linux_alias = "appflowy-core-dev-linux"
|
||||||
|
|
||||||
[tasks.appflowy-sdk-dev-android]
|
[tasks.appflowy-core-dev-android]
|
||||||
category = "Build"
|
category = "Build"
|
||||||
dependencies = ["env_check"]
|
dependencies = ["env_check"]
|
||||||
run_task = { name = [
|
run_task = { name = [
|
||||||
@ -29,7 +24,7 @@ run_task = { name = [
|
|||||||
"restore-crate-type",
|
"restore-crate-type",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[tasks.appflowy-sdk-dev-macos]
|
[tasks.appflowy-core-dev-macos]
|
||||||
category = "Build"
|
category = "Build"
|
||||||
dependencies = ["env_check"]
|
dependencies = ["env_check"]
|
||||||
run_task = { name = [
|
run_task = { name = [
|
||||||
@ -39,7 +34,7 @@ run_task = { name = [
|
|||||||
"restore-crate-type",
|
"restore-crate-type",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[tasks.appflowy-sdk-dev-windows]
|
[tasks.appflowy-core-dev-windows]
|
||||||
category = "Build"
|
category = "Build"
|
||||||
dependencies = ["env_check"]
|
dependencies = ["env_check"]
|
||||||
run_task = { name = [
|
run_task = { name = [
|
||||||
@ -49,7 +44,7 @@ run_task = { name = [
|
|||||||
"restore-crate-type",
|
"restore-crate-type",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[tasks.appflowy-sdk-dev-linux]
|
[tasks.appflowy-core-dev-linux]
|
||||||
category = "Build"
|
category = "Build"
|
||||||
dependencies = ["env_check"]
|
dependencies = ["env_check"]
|
||||||
run_task = { name = [
|
run_task = { name = [
|
||||||
@ -59,7 +54,6 @@ run_task = { name = [
|
|||||||
"restore-crate-type",
|
"restore-crate-type",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
[tasks.sdk-build]
|
[tasks.sdk-build]
|
||||||
private = true
|
private = true
|
||||||
@ -112,7 +106,7 @@ script = [
|
|||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
|
|
||||||
#
|
#
|
||||||
[tasks.appflowy-sdk-release]
|
[tasks.appflowy-core-release]
|
||||||
description = "Build flowy sdk in release mode"
|
description = "Build flowy sdk in release mode"
|
||||||
category = "Build"
|
category = "Build"
|
||||||
dependencies = ["env_check"]
|
dependencies = ["env_check"]
|
||||||
@ -144,7 +138,7 @@ linux_alias = "post-desktop-linux"
|
|||||||
private = true
|
private = true
|
||||||
script = [
|
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}
|
dart_ffi_dir= set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/packages/appflowy_backend/${TARGET_OS}
|
||||||
lib = set lib${LIB_NAME}.${LIB_EXT}
|
lib = set lib${LIB_NAME}.${LIB_EXT}
|
||||||
|
|
||||||
@ -161,7 +155,7 @@ script_runner = "@duckscript"
|
|||||||
private = true
|
private = true
|
||||||
script = [
|
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
|
dart_ffi_dir= set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/windows/flutter/dart_ffi
|
||||||
lib = set ${LIB_NAME}.${LIB_EXT}
|
lib = set ${LIB_NAME}.${LIB_EXT}
|
||||||
|
|
||||||
@ -180,7 +174,7 @@ script_runner = "@duckscript"
|
|||||||
private = true
|
private = true
|
||||||
script = [
|
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
|
dart_ffi_dir= set ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/linux/flutter/dart_ffi
|
||||||
lib = set lib${LIB_NAME}.${LIB_EXT}
|
lib = set lib${LIB_NAME}.${LIB_EXT}
|
||||||
|
|
||||||
|
@ -4,17 +4,33 @@ windows_alias = "appflowy-windows"
|
|||||||
linux_alias = "appflowy-linux"
|
linux_alias = "appflowy-linux"
|
||||||
|
|
||||||
[tasks.appflowy-macos]
|
[tasks.appflowy-macos]
|
||||||
dependencies = ["appflowy-sdk-release"]
|
dependencies = ["appflowy-core-release"]
|
||||||
run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] }
|
run_task = { name = [
|
||||||
|
"code_generation",
|
||||||
|
"set-app-version",
|
||||||
|
"flutter-build",
|
||||||
|
"copy-to-product",
|
||||||
|
] }
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
|
||||||
[tasks.appflowy-windows]
|
[tasks.appflowy-windows]
|
||||||
dependencies = ["appflowy-sdk-release"]
|
dependencies = ["appflowy-core-release"]
|
||||||
run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] }
|
run_task = { name = [
|
||||||
|
"code_generation",
|
||||||
|
"set-app-version",
|
||||||
|
"flutter-build",
|
||||||
|
"copy-to-product",
|
||||||
|
] }
|
||||||
|
|
||||||
[tasks.appflowy-linux]
|
[tasks.appflowy-linux]
|
||||||
dependencies = ["appflowy-sdk-release"]
|
dependencies = ["appflowy-core-release"]
|
||||||
run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product", "create-release-archive"] }
|
run_task = { name = [
|
||||||
|
"code_generation",
|
||||||
|
"set-app-version",
|
||||||
|
"flutter-build",
|
||||||
|
"copy-to-product",
|
||||||
|
"create-release-archive",
|
||||||
|
] }
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
|
||||||
[tasks.appflowy-dev]
|
[tasks.appflowy-dev]
|
||||||
@ -23,17 +39,32 @@ windows_alias = "appflowy-windows-dev"
|
|||||||
linux_alias = "appflowy-linux-dev"
|
linux_alias = "appflowy-linux-dev"
|
||||||
|
|
||||||
[tasks.appflowy-macos-dev]
|
[tasks.appflowy-macos-dev]
|
||||||
dependencies = ["appflowy-sdk-dev"]
|
dependencies = ["appflowy-core-dev"]
|
||||||
run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] }
|
run_task = { name = [
|
||||||
|
"code_generation",
|
||||||
|
"set-app-version",
|
||||||
|
"flutter-build",
|
||||||
|
"copy-to-product",
|
||||||
|
] }
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
|
||||||
[tasks.appflowy-windows-dev]
|
[tasks.appflowy-windows-dev]
|
||||||
dependencies = ["appflowy-sdk-dev"]
|
dependencies = ["appflowy-core-dev"]
|
||||||
run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] }
|
run_task = { name = [
|
||||||
|
"code_generation",
|
||||||
|
"set-app-version",
|
||||||
|
"flutter-build",
|
||||||
|
"copy-to-product",
|
||||||
|
] }
|
||||||
|
|
||||||
[tasks.appflowy-linux-dev]
|
[tasks.appflowy-linux-dev]
|
||||||
dependencies = ["appflowy-sdk-dev"]
|
dependencies = ["appflowy-core-dev"]
|
||||||
run_task = { name = ["code_generation", "set-app-version", "flutter-build", "copy-to-product"] }
|
run_task = { name = [
|
||||||
|
"code_generation",
|
||||||
|
"set-app-version",
|
||||||
|
"flutter-build",
|
||||||
|
"copy-to-product",
|
||||||
|
] }
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
|
||||||
[tasks.copy-to-product]
|
[tasks.copy-to-product]
|
||||||
@ -96,15 +127,13 @@ script = [
|
|||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
|
|
||||||
[tasks.set-app-version]
|
[tasks.set-app-version]
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
if is_empty ${APP_VERSION}
|
if is_empty ${APP_VERSION}
|
||||||
APP_VERSION = set ${CURRENT_APP_VERSION}
|
APP_VERSION = set ${CURRENT_APP_VERSION}
|
||||||
set_env APP_VERSION ${CURRENT_APP_VERSION}
|
set_env APP_VERSION ${CURRENT_APP_VERSION}
|
||||||
end
|
end
|
||||||
echo APP_VERSION: ${APP_VERSION}
|
echo APP_VERSION: ${APP_VERSION}
|
||||||
""",
|
"""]
|
||||||
]
|
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
|
|
||||||
# The following tasks will create an archive that will be used on the GitHub Releases section
|
# 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]
|
[tasks.create-release-archive-linux]
|
||||||
script = [
|
script = [
|
||||||
"cd ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/product/${APP_VERSION}/${TARGET_OS}/Release",
|
"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]
|
[tasks.create-release-archive-windows]
|
||||||
@ -136,36 +165,30 @@ script = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[tasks.flutter-build]
|
[tasks.flutter-build]
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
cd app_flowy/
|
cd app_flowy/
|
||||||
flutter clean
|
flutter clean
|
||||||
flutter pub get
|
flutter pub get
|
||||||
flutter build ${TARGET_OS} --${BUILD_FLAG} --build-name=${APP_VERSION}
|
flutter build ${TARGET_OS} --${BUILD_FLAG} --build-name=${APP_VERSION}
|
||||||
""",
|
"""]
|
||||||
]
|
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
|
||||||
[tasks.flutter-build.linux]
|
[tasks.flutter-build.linux]
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
cd app_flowy/
|
cd app_flowy/
|
||||||
flutter clean
|
flutter clean
|
||||||
flutter pub get
|
flutter pub get
|
||||||
flutter build ${TARGET_OS} --${BUILD_FLAG}
|
flutter build ${TARGET_OS} --${BUILD_FLAG}
|
||||||
""",
|
"""]
|
||||||
]
|
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
|
||||||
[tasks.flutter-build.windows]
|
[tasks.flutter-build.windows]
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
cd app_flowy
|
cd app_flowy
|
||||||
exec cmd.exe /c flutter clean
|
exec cmd.exe /c flutter clean
|
||||||
exec cmd.exe /c flutter pub get
|
exec cmd.exe /c flutter pub get
|
||||||
exec cmd.exe /c flutter build ${TARGET_OS} --${BUILD_FLAG}
|
exec cmd.exe /c flutter build ${TARGET_OS} --${BUILD_FLAG}
|
||||||
""",
|
"""]
|
||||||
]
|
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
|
|
||||||
[tasks.code_generation]
|
[tasks.code_generation]
|
||||||
@ -177,7 +200,7 @@ script = [
|
|||||||
flutter packages pub get
|
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 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
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
"""
|
""",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tasks.code_generation.windows]
|
[tasks.code_generation.windows]
|
||||||
@ -189,7 +212,7 @@ script = [
|
|||||||
exec cmd.exe /c flutter packages pub get
|
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 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
|
exec cmd.exe /c flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
"""
|
""",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tasks.dry_code_generation]
|
[tasks.dry_code_generation]
|
||||||
@ -199,7 +222,7 @@ script = [
|
|||||||
cd app_flowy
|
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 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
|
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
"""
|
""",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tasks.dry_code_generation.windows]
|
[tasks.dry_code_generation.windows]
|
||||||
@ -209,5 +232,5 @@ script = [
|
|||||||
cd ./app_flowy/
|
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 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
|
exec cmd.exe /c flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||||
"""
|
""",
|
||||||
]
|
]
|
||||||
|
@ -7,6 +7,7 @@ script = ["""
|
|||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
|
||||||
[tasks.tauri_dev]
|
[tasks.tauri_dev]
|
||||||
|
env = { RUST_LOG = "debug" }
|
||||||
script = ["""
|
script = ["""
|
||||||
cd appflowy_tauri
|
cd appflowy_tauri
|
||||||
npm run tauri dev
|
npm run tauri dev
|
||||||
|
@ -18,6 +18,7 @@ cargo make --profile test-linux dart_unit_test_inner
|
|||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
|
|
||||||
[tasks.dart_unit_test_inner]
|
[tasks.dart_unit_test_inner]
|
||||||
|
env = { RUST_LOG = "info" }
|
||||||
dependencies = ["build-test-lib"]
|
dependencies = ["build-test-lib"]
|
||||||
description = "Run flutter unit tests"
|
description = "Run flutter unit tests"
|
||||||
script = '''
|
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"] }
|
run_task = { name = ["rust_lib_unit_test", "shared_lib_unit_test"] }
|
||||||
|
|
||||||
[tasks.rust_lib_unit_test]
|
[tasks.rust_lib_unit_test]
|
||||||
|
env = { RUST_LOG = "info" }
|
||||||
description = "Run rust-lib unit tests"
|
description = "Run rust-lib unit tests"
|
||||||
script = '''
|
script = '''
|
||||||
cd rust-lib
|
cd rust-lib
|
||||||
@ -36,6 +38,7 @@ cargo test --no-default-features --features="sync, rev-sqlite"
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
[tasks.shared_lib_unit_test]
|
[tasks.shared_lib_unit_test]
|
||||||
|
env = { RUST_LOG = "info" }
|
||||||
description = "Run shared-lib unit test"
|
description = "Run shared-lib unit test"
|
||||||
script = '''
|
script = '''
|
||||||
cd ../shared-lib
|
cd ../shared-lib
|
||||||
@ -62,8 +65,7 @@ fi
|
|||||||
[tasks.clean_profraw_files]
|
[tasks.clean_profraw_files]
|
||||||
description = "Cleans profraw files that are created by `cargo test`"
|
description = "Cleans profraw files that are created by `cargo test`"
|
||||||
script_runner = "@duckscript"
|
script_runner = "@duckscript"
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
rust_lib_profs = glob_array ./rust-lib/**/*.profraw
|
rust_lib_profs = glob_array ./rust-lib/**/*.profraw
|
||||||
for prof in ${rust_lib_profs}
|
for prof in ${rust_lib_profs}
|
||||||
full_path = canonicalize ${prof}
|
full_path = canonicalize ${prof}
|
||||||
@ -76,14 +78,12 @@ script = [
|
|||||||
rm ${full_path}
|
rm ${full_path}
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""]
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.run_rustlib_coverage_tests]
|
[tasks.run_rustlib_coverage_tests]
|
||||||
description = "Run tests with coverage instrumentation"
|
description = "Run tests with coverage instrumentation"
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
echo --- Running coverage tests ---
|
echo --- Running coverage tests ---
|
||||||
cd rust-lib/
|
cd rust-lib/
|
||||||
|
|
||||||
@ -91,14 +91,12 @@ script = [
|
|||||||
RUSTFLAGS='-C instrument-coverage' \
|
RUSTFLAGS='-C instrument-coverage' \
|
||||||
LLVM_PROFILE_FILE='prof-%p-%m.profraw' \
|
LLVM_PROFILE_FILE='prof-%p-%m.profraw' \
|
||||||
cargo test --no-default-features --features="sync,rev-sqlite"
|
cargo test --no-default-features --features="sync,rev-sqlite"
|
||||||
"""
|
"""]
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.run_sharedlib_coverage_tests]
|
[tasks.run_sharedlib_coverage_tests]
|
||||||
description = "Run tests with coverage instrumentation"
|
description = "Run tests with coverage instrumentation"
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
echo --- Running coverage tests ---
|
echo --- Running coverage tests ---
|
||||||
cd ../shared-lib
|
cd ../shared-lib
|
||||||
|
|
||||||
@ -107,8 +105,7 @@ script = [
|
|||||||
LLVM_PROFILE_FILE='prof-%p-%m.profraw' \
|
LLVM_PROFILE_FILE='prof-%p-%m.profraw' \
|
||||||
cargo test --no-default-features
|
cargo test --no-default-features
|
||||||
|
|
||||||
"""
|
"""]
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.get_rustlib_grcov_report]
|
[tasks.get_rustlib_grcov_report]
|
||||||
description = "Get `grcov` HTML report for test coverage for rust-lib"
|
description = "Get `grcov` HTML report for test coverage for rust-lib"
|
||||||
@ -128,7 +125,7 @@ script = [
|
|||||||
--output-path target/coverage-html
|
--output-path target/coverage-html
|
||||||
|
|
||||||
echo "--- Done! Generated HTML report under 'target/coverage-html' for rustlib."
|
echo "--- Done! Generated HTML report under 'target/coverage-html' for rustlib."
|
||||||
"""
|
""",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tasks.get_sharedlib_grcov_report]
|
[tasks.get_sharedlib_grcov_report]
|
||||||
@ -149,21 +146,20 @@ script = [
|
|||||||
--output-path target/coverage-html
|
--output-path target/coverage-html
|
||||||
|
|
||||||
echo "--- Done! Generated HTML report under 'target/coverage-html' for sharedlib."
|
echo "--- Done! Generated HTML report under 'target/coverage-html' for sharedlib."
|
||||||
"""
|
""",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tasks.get_grcov_report]
|
[tasks.get_grcov_report]
|
||||||
description = "Get `grcov` HTML report for test coverage"
|
description = "Get `grcov` HTML report for test coverage"
|
||||||
run_task = { name = [
|
run_task = { name = [
|
||||||
"get_rustlib_grcov_report",
|
"get_rustlib_grcov_report",
|
||||||
"get_sharedlib_grcov_report"
|
"get_sharedlib_grcov_report",
|
||||||
], parallel = true }
|
], parallel = true }
|
||||||
|
|
||||||
[tasks.get_sharedlib_lcov_report]
|
[tasks.get_sharedlib_lcov_report]
|
||||||
description = "Generates `lcov` report for `shared-lib`"
|
description = "Generates `lcov` report for `shared-lib`"
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
echo Getting 'lcov' results for 'shared-lib'
|
echo Getting 'lcov' results for 'shared-lib'
|
||||||
|
|
||||||
cd ../shared-lib
|
cd ../shared-lib
|
||||||
@ -178,14 +174,12 @@ script = [
|
|||||||
--output-path target/coverage.lcov
|
--output-path target/coverage.lcov
|
||||||
|
|
||||||
echo "--- Done! Generated 'target/coverage.lcov' sharedlib."
|
echo "--- Done! Generated 'target/coverage.lcov' sharedlib."
|
||||||
"""
|
"""]
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.get_rustlib_lcov_report]
|
[tasks.get_rustlib_lcov_report]
|
||||||
description = "Generates `lcov` report for `rust-lib`"
|
description = "Generates `lcov` report for `rust-lib`"
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script = [
|
script = ["""
|
||||||
"""
|
|
||||||
echo Getting 'lcov' results for 'rust-lib'
|
echo Getting 'lcov' results for 'rust-lib'
|
||||||
|
|
||||||
cd rust-lib/
|
cd rust-lib/
|
||||||
@ -200,14 +194,13 @@ script = [
|
|||||||
--output-path target/coverage.lcov
|
--output-path target/coverage.lcov
|
||||||
|
|
||||||
echo "--- Done! Generated 'target/coverage.lcov' for rustlib."
|
echo "--- Done! Generated 'target/coverage.lcov' for rustlib."
|
||||||
"""
|
"""]
|
||||||
]
|
|
||||||
|
|
||||||
[tasks.get_lcov_report]
|
[tasks.get_lcov_report]
|
||||||
description = "Get `lcov` reports for test coverage"
|
description = "Get `lcov` reports for test coverage"
|
||||||
run_task = { name = [
|
run_task = { name = [
|
||||||
"get_sharedlib_lcov_report",
|
"get_sharedlib_lcov_report",
|
||||||
"get_rustlib_lcov_report"
|
"get_rustlib_lcov_report",
|
||||||
], parallel = true }
|
], parallel = true }
|
||||||
|
|
||||||
[tasks.rust_unit_test_with_coverage]
|
[tasks.rust_unit_test_with_coverage]
|
||||||
@ -218,5 +211,5 @@ run_task = { name = [
|
|||||||
"run_rustlib_coverage_tests",
|
"run_rustlib_coverage_tests",
|
||||||
"run_sharedlib_coverage_tests",
|
"run_sharedlib_coverage_tests",
|
||||||
"get_lcov_report",
|
"get_lcov_report",
|
||||||
"clean_profraw_files"
|
"clean_profraw_files",
|
||||||
] }
|
] }
|
||||||
|
@ -10,7 +10,7 @@ chrono = "0.4.19"
|
|||||||
bytes = { version = "1.0" }
|
bytes = { version = "1.0" }
|
||||||
pin-project = "1.0.12"
|
pin-project = "1.0.12"
|
||||||
futures-core = { version = "0.3" }
|
futures-core = { version = "0.3" }
|
||||||
tokio = { version = "1.0", features = ["time", "rt"] }
|
tokio = { version = "1", features = ["time", "rt"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
async-trait = "0.1.59"
|
async-trait = "0.1.59"
|
||||||
md5 = "0.7.0"
|
md5 = "0.7.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user