test: add database event tests (#2744)

* chore: add tests

* test: add tests

* test: add tests
This commit is contained in:
Nathan.fooo 2023-06-09 18:57:29 +08:00 committed by GitHub
parent 0f9f5251f2
commit e9be37a961
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 834 additions and 104 deletions

View File

@ -47,7 +47,7 @@ class DateCellDataPersistence implements CellDataPersistence<DateCellData> {
@override @override
Future<Option<FlowyError>> save(DateCellData data) { Future<Option<FlowyError>> save(DateCellData data) {
final payload = DateChangesetPB.create() final payload = DateChangesetPB.create()
..cellPath = _makeCellPath(cellContext); ..cellId = _makeCellPath(cellContext);
if (data.dateTime != null) { if (data.dateTime != null) {
final date = (data.dateTime!.millisecondsSinceEpoch ~/ 1000).toString(); final date = (data.dateTime!.millisecondsSinceEpoch ~/ 1000).toString();
payload.date = date; payload.date = date;

View File

@ -1,5 +1,4 @@
import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
@ -10,12 +9,10 @@ class GroupBackendService {
Future<Either<Unit, FlowyError>> groupByField({ Future<Either<Unit, FlowyError>> groupByField({
required String fieldId, required String fieldId,
required FieldType fieldType,
}) { }) {
final payload = GroupByFieldPayloadPB.create() final payload = GroupByFieldPayloadPB.create()
..viewId = viewId ..viewId = viewId
..fieldId = fieldId ..fieldId = fieldId;
..fieldType = fieldType;
return DatabaseEventSetGroupByField(payload).send(); return DatabaseEventSetGroupByField(payload).send();
} }

View File

@ -32,7 +32,6 @@ class DatabaseGroupBloc extends Bloc<DatabaseGroupEvent, DatabaseGroupState> {
setGroupByField: (String fieldId, FieldType fieldType) async { setGroupByField: (String fieldId, FieldType fieldType) async {
final result = await _groupBackendSvc.groupByField( final result = await _groupBackendSvc.groupByField(
fieldId: fieldId, fieldId: fieldId,
fieldType: fieldType,
); );
result.fold((l) => null, (err) => Log.error(err)); result.fold((l) => null, (err) => Log.error(err));
}, },

View File

@ -230,7 +230,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
for (final group in updatedGroups) { for (final group in updatedGroups) {
final columnController = final columnController =
boardController.getGroupController(group.groupId); boardController.getGroupController(group.groupId);
columnController?.updateGroupName(group.desc); columnController?.updateGroupName(group.groupName);
} }
}, },
); );
@ -285,7 +285,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
AppFlowyGroupData initializeGroupData(GroupPB group) { AppFlowyGroupData initializeGroupData(GroupPB group) {
return AppFlowyGroupData( return AppFlowyGroupData(
id: group.groupId, id: group.groupId,
name: group.desc, name: group.groupName,
items: _buildGroupItems(group), items: _buildGroupItems(group),
customData: GroupData( customData: GroupData(
group: group, group: group,

View File

@ -285,10 +285,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: csslib name: csslib
sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.17.3" version: "1.0.0"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
@ -648,10 +648,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: html name: html
sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8" sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.15.3" version: "0.15.4"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -48,8 +48,9 @@ void main() {
); );
final expectedGroupName = "No ${multiSelectField.name}"; final expectedGroupName = "No ${multiSelectField.name}";
assert( assert(
boardBloc.groupControllers.values.first.group.desc == expectedGroupName, boardBloc.groupControllers.values.first.group.groupName ==
"Expected $expectedGroupName, but receive ${boardBloc.groupControllers.values.first.group.desc}", expectedGroupName,
"Expected $expectedGroupName, but receive ${boardBloc.groupControllers.values.first.group.groupName}",
); );
}); });
@ -101,8 +102,8 @@ void main() {
final groups = final groups =
boardBloc.groupControllers.values.map((e) => e.group).toList(); boardBloc.groupControllers.values.map((e) => e.group).toList();
assert(groups[0].desc == "No ${multiSelectField.name}"); assert(groups[0].groupName == "No ${multiSelectField.name}");
assert(groups[1].desc == "B"); assert(groups[1].groupName == "B");
assert(groups[2].desc == "A"); assert(groups[2].groupName == "A");
}); });
} }

View File

@ -99,7 +99,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]] [[package]]
name = "appflowy-integrate" name = "appflowy-integrate"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1024,7 +1024,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -1042,7 +1042,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-client-ws" name = "collab-client-ws"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"bytes", "bytes",
"collab-sync", "collab-sync",
@ -1060,7 +1060,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1086,7 +1086,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-derive" name = "collab-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1098,7 +1098,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1115,7 +1115,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -1135,7 +1135,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-persistence" name = "collab-persistence"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"bincode", "bincode",
"chrono", "chrono",
@ -1155,7 +1155,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1186,7 +1186,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-sync" name = "collab-sync"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"bytes", "bytes",
"collab", "collab",

View File

@ -34,12 +34,12 @@ default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"] custom-protocol = ["tauri/custom-protocol"]
[patch.crates-io] [patch.crates-io]
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
#collab = { path = "../../AppFlowy-Collab/collab" } #collab = { path = "../../AppFlowy-Collab/collab" }
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" } #collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }

View File

@ -24,7 +24,7 @@ export class DateCellDataPersistence extends CellDataPersistence<CalendarData> {
} }
save(data: CalendarData): Promise<Result<void, FlowyError>> { save(data: CalendarData): Promise<Result<void, FlowyError>> {
const payload = DateChangesetPB.fromObject({ cell_path: _makeCellPath(this.cellIdentifier) }); const payload = DateChangesetPB.fromObject({ cell_id: _makeCellId(this.cellIdentifier) });
payload.date = ((data.date.getTime() / 1000) | 0).toString(); payload.date = ((data.date.getTime() / 1000) | 0).toString();
if (data.time !== undefined) { if (data.time !== undefined) {
payload.time = data.time; payload.time = data.time;
@ -34,7 +34,7 @@ export class DateCellDataPersistence extends CellDataPersistence<CalendarData> {
} }
} }
function _makeCellPath(cellIdentifier: CellIdentifier): CellIdPB { function _makeCellId(cellIdentifier: CellIdentifier): CellIdPB {
return CellIdPB.fromObject({ return CellIdPB.fromObject({
view_id: cellIdentifier.viewId, view_id: cellIdentifier.viewId,
field_id: cellIdentifier.fieldId, field_id: cellIdentifier.fieldId,

View File

@ -1,9 +1,9 @@
import { DatabaseNotification, FlowyError, GroupPB, GroupRowsNotificationPB, RowPB } from "@/services/backend"; import { DatabaseNotification, FlowyError, GroupPB, GroupRowsNotificationPB, RowPB } from '@/services/backend';
import { ChangeNotifier } from "$app/utils/change_notifier"; import { ChangeNotifier } from '$app/utils/change_notifier';
import { None, Ok, Option, Result, Some } from "ts-results"; import { None, Ok, Option, Result, Some } from 'ts-results';
import { DatabaseNotificationObserver } from "../notifications/observer"; import { DatabaseNotificationObserver } from '../notifications/observer';
import { Log } from "$app/utils/log"; import { Log } from '$app/utils/log';
import { DatabaseBackendService } from "../database_bd_svc"; import { DatabaseBackendService } from '../database_bd_svc';
export type GroupDataCallbacks = { export type GroupDataCallbacks = {
onRemoveRow: (groupId: string, rowId: string) => void; onRemoveRow: (groupId: string, rowId: string) => void;
@ -30,7 +30,7 @@ export class DatabaseGroupController {
} }
get name() { get name() {
return this.group.desc; return this.group.group_name;
} }
updateGroup = (group: GroupPB) => { updateGroup = (group: GroupPB) => {
@ -83,7 +83,7 @@ export class DatabaseGroupController {
} else { } else {
Log.error(result.val); Log.error(result.val);
} }
} },
}); });
}; };
@ -111,8 +111,7 @@ class GroupDataObserver {
private notifier?: ChangeNotifier<Result<GroupRowsNotificationPB, FlowyError>>; private notifier?: ChangeNotifier<Result<GroupRowsNotificationPB, FlowyError>>;
private listener?: DatabaseNotificationObserver; private listener?: DatabaseNotificationObserver;
constructor(public readonly groupId: string) { constructor(public readonly groupId: string) {}
}
subscribe = async (callbacks: { onRowsChanged: GroupRowsSubscribeCallback }) => { subscribe = async (callbacks: { onRowsChanged: GroupRowsSubscribeCallback }) => {
this.notifier = new ChangeNotifier(); this.notifier = new ChangeNotifier();
@ -132,7 +131,7 @@ class GroupDataObserver {
default: default:
break; break;
} }
} },
}); });
await this.listener.start(); await this.listener.start();
}; };

View File

@ -85,7 +85,7 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]] [[package]]
name = "appflowy-integrate" name = "appflowy-integrate"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -887,7 +887,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -905,7 +905,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-client-ws" name = "collab-client-ws"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"bytes", "bytes",
"collab-sync", "collab-sync",
@ -923,7 +923,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -949,7 +949,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-derive" name = "collab-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -961,7 +961,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -978,7 +978,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -998,7 +998,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-persistence" name = "collab-persistence"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"bincode", "bincode",
"chrono", "chrono",
@ -1018,7 +1018,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1049,7 +1049,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-sync" name = "collab-sync"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=87f534#87f53452241a65275f5b2878ba57dff2a0e2b838" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=c382b1#c382b1ceb44e5615175e3b01f2bf0d49f6c669e3"
dependencies = [ dependencies = [
"bytes", "bytes",
"collab", "collab",

View File

@ -33,11 +33,11 @@ opt-level = 3
incremental = false incremental = false
[patch.crates-io] [patch.crates-io]
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "87f534" } appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c382b1" }
#collab = { path = "../AppFlowy-Collab/collab" } #collab = { path = "../AppFlowy-Collab/collab" }
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" } #collab-folder = { path = "../AppFlowy-Collab/collab-folder" }

View File

@ -80,17 +80,11 @@ impl std::convert::From<CalendarLayout> for CalendarLayoutPB {
pub struct CalendarEventRequestPB { pub struct CalendarEventRequestPB {
#[pb(index = 1)] #[pb(index = 1)]
pub view_id: String, pub view_id: String,
// Currently, requesting the events within the specified month
// is not supported
#[pb(index = 2)]
pub month: String,
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct CalendarEventRequestParams { pub struct CalendarEventRequestParams {
pub view_id: String, pub view_id: String,
pub month: String,
} }
impl TryInto<CalendarEventRequestParams> for CalendarEventRequestPB { impl TryInto<CalendarEventRequestParams> for CalendarEventRequestPB {
@ -98,10 +92,7 @@ impl TryInto<CalendarEventRequestParams> for CalendarEventRequestPB {
fn try_into(self) -> Result<CalendarEventRequestParams, Self::Error> { fn try_into(self) -> Result<CalendarEventRequestParams, Self::Error> {
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::ViewIdIsInvalid)?; let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::ViewIdIsInvalid)?;
Ok(CalendarEventRequestParams { Ok(CalendarEventRequestParams { view_id: view_id.0 })
view_id: view_id.0,
month: self.month,
})
} }
} }

View File

@ -135,11 +135,13 @@ impl TryInto<MoveRowParams> for MoveRowPayloadPB {
fn try_into(self) -> Result<MoveRowParams, Self::Error> { fn try_into(self) -> Result<MoveRowParams, Self::Error> {
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?; let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseViewIdIsEmpty)?;
let from_row_id = NotEmptyStr::parse(self.from_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
let to_row_id = NotEmptyStr::parse(self.to_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
Ok(MoveRowParams { Ok(MoveRowParams {
view_id: view_id.0, view_id: view_id.0,
from_row_id: RowId::from(self.from_row_id), from_row_id: RowId::from(from_row_id.0),
to_row_id: RowId::from(self.to_row_id), to_row_id: RowId::from(to_row_id.0),
}) })
} }
} }

View File

@ -76,7 +76,7 @@ pub struct GroupPB {
pub group_id: String, pub group_id: String,
#[pb(index = 3)] #[pb(index = 3)]
pub desc: String, pub group_name: String,
#[pb(index = 4)] #[pb(index = 4)]
pub rows: Vec<RowPB>, pub rows: Vec<RowPB>,
@ -93,7 +93,7 @@ impl std::convert::From<GroupData> for GroupPB {
Self { Self {
field_id: group_data.field_id, field_id: group_data.field_id,
group_id: group_data.id, group_id: group_data.id,
desc: group_data.name, group_name: group_data.name,
rows: group_data.rows.into_iter().map(RowPB::from).collect(), rows: group_data.rows.into_iter().map(RowPB::from).collect(),
is_default: group_data.is_default, is_default: group_data.is_default,
is_visible: group_data.is_visible, is_visible: group_data.is_visible,
@ -108,9 +108,6 @@ pub struct GroupByFieldPayloadPB {
#[pb(index = 2)] #[pb(index = 2)]
pub view_id: String, pub view_id: String,
#[pb(index = 3)]
pub field_type: FieldType,
} }
impl TryInto<GroupByFieldParams> for GroupByFieldPayloadPB { impl TryInto<GroupByFieldParams> for GroupByFieldPayloadPB {
@ -124,18 +121,13 @@ impl TryInto<GroupByFieldParams> for GroupByFieldPayloadPB {
.map_err(|_| ErrorCode::ViewIdIsInvalid)? .map_err(|_| ErrorCode::ViewIdIsInvalid)?
.0; .0;
Ok(GroupByFieldParams { Ok(GroupByFieldParams { field_id, view_id })
field_id,
view_id,
field_type: self.field_type,
})
} }
} }
pub struct GroupByFieldParams { pub struct GroupByFieldParams {
pub field_id: String, pub field_id: String,
pub view_id: String, pub view_id: String,
pub field_type: FieldType,
} }
pub struct DeleteGroupParams { pub struct DeleteGroupParams {

View File

@ -158,6 +158,7 @@ impl TryInto<RowIdParams> for RowIdPB {
fn try_into(self) -> Result<RowIdParams, Self::Error> { fn try_into(self) -> Result<RowIdParams, Self::Error> {
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?; let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?;
let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
let group_id = match self.group_id { let group_id = match self.group_id {
Some(group_id) => Some( Some(group_id) => Some(
NotEmptyStr::parse(group_id) NotEmptyStr::parse(group_id)
@ -169,7 +170,7 @@ impl TryInto<RowIdParams> for RowIdPB {
Ok(RowIdParams { Ok(RowIdParams {
view_id: view_id.0, view_id: view_id.0,
row_id: RowId::from(self.row_id), row_id: RowId::from(row_id.0),
group_id, group_id,
}) })
} }

View File

@ -1,11 +1,12 @@
use collab_database::rows::RowId;
use flowy_derive::ProtoBuf;
use flowy_error::{ErrorCode, FlowyError};
use crate::entities::parser::NotEmptyStr; use crate::entities::parser::NotEmptyStr;
use crate::entities::SelectOptionPB; use crate::entities::SelectOptionPB;
use crate::services::field::checklist_type_option::ChecklistCellData; use crate::services::field::checklist_type_option::ChecklistCellData;
use crate::services::field::SelectOption; use crate::services::field::SelectOption;
use collab_database::rows::RowId;
use flowy_derive::ProtoBuf;
use flowy_error::{ErrorCode, FlowyError};
#[derive(Debug, Clone, Default, ProtoBuf)] #[derive(Debug, Clone, Default, ProtoBuf)]
pub struct ChecklistCellDataPB { pub struct ChecklistCellDataPB {
@ -16,7 +17,7 @@ pub struct ChecklistCellDataPB {
pub selected_options: Vec<SelectOptionPB>, pub selected_options: Vec<SelectOptionPB>,
#[pb(index = 3)] #[pb(index = 3)]
pub(crate) percentage: f64, pub percentage: f64,
} }
impl From<ChecklistCellData> for ChecklistCellDataPB { impl From<ChecklistCellData> for ChecklistCellDataPB {

View File

@ -25,7 +25,7 @@ pub struct DateCellDataPB {
#[derive(Clone, Debug, Default, ProtoBuf)] #[derive(Clone, Debug, Default, ProtoBuf)]
pub struct DateChangesetPB { pub struct DateChangesetPB {
#[pb(index = 1)] #[pb(index = 1)]
pub cell_path: CellIdPB, pub cell_id: CellIdPB,
#[pb(index = 2, one_of)] #[pb(index = 2, one_of)]
pub date: Option<String>, pub date: Option<String>,

View File

@ -294,7 +294,9 @@ pub(crate) async fn get_row_handler(
) -> DataResult<OptionalRowPB, FlowyError> { ) -> DataResult<OptionalRowPB, FlowyError> {
let params: RowIdParams = data.into_inner().try_into()?; let params: RowIdParams = data.into_inner().try_into()?;
let database_editor = manager.get_database_with_view_id(&params.view_id).await?; let database_editor = manager.get_database_with_view_id(&params.view_id).await?;
let row = database_editor.get_row(&params.row_id).map(RowPB::from); let row = database_editor
.get_row(&params.view_id, &params.row_id)
.map(RowPB::from);
data_result_ok(OptionalRowPB { row }) data_result_ok(OptionalRowPB { row })
} }
@ -525,7 +527,7 @@ pub(crate) async fn update_date_cell_handler(
manager: AFPluginState<Arc<DatabaseManager2>>, manager: AFPluginState<Arc<DatabaseManager2>>,
) -> Result<(), FlowyError> { ) -> Result<(), FlowyError> {
let data = data.into_inner(); let data = data.into_inner();
let cell_id: CellIdParams = data.cell_path.try_into()?; let cell_id: CellIdParams = data.cell_id.try_into()?;
let cell_changeset = DateCellChangeset { let cell_changeset = DateCellChangeset {
date: data.date, date: data.date,
time: data.time, time: data.time,

View File

@ -496,8 +496,12 @@ impl DatabaseEditor {
Ok(view_editor.v_get_rows().await) Ok(view_editor.v_get_rows().await)
} }
pub fn get_row(&self, row_id: &RowId) -> Option<Row> { pub fn get_row(&self, view_id: &str, row_id: &RowId) -> Option<Row> {
self.database.lock().get_row(row_id) if self.database.lock().views.is_row_exist(view_id, row_id) {
return None;
} else {
self.database.lock().get_row(row_id)
}
} }
pub async fn delete_row(&self, row_id: &RowId) { pub async fn delete_row(&self, row_id: &RowId) {
@ -832,6 +836,11 @@ impl DatabaseEditor {
from_group: &str, from_group: &str,
to_group: &str, to_group: &str,
) -> FlowyResult<()> { ) -> FlowyResult<()> {
// Do nothing if the group is the same
if from_group == to_group {
return Ok(());
}
let view = self.database_views.get_view_editor(view_id).await?; let view = self.database_views.get_view_editor(view_id).await?;
view.v_move_group(from_group, to_group).await?; view.v_move_group(from_group, to_group).await?;
Ok(()) Ok(())

View File

@ -375,6 +375,7 @@ impl DatabaseViewEditor {
.as_ref()? .as_ref()?
.groups() .groups()
.into_iter() .into_iter()
.filter(|group| group.is_visible)
.map(|group_data| GroupPB::from(group_data.clone())) .map(|group_data| GroupPB::from(group_data.clone()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
tracing::trace!("Number of groups: {}", groups.len()); tracing::trace!("Number of groups: {}", groups.len());

View File

@ -351,15 +351,21 @@ where
} }
pub(crate) fn update_group(&mut self, group_changeset: GroupChangeset) -> FlowyResult<()> { pub(crate) fn update_group(&mut self, group_changeset: GroupChangeset) -> FlowyResult<()> {
self.mut_group(&group_changeset.group_id, |group| { let update_group = self.mut_group(&group_changeset.group_id, |group| {
if let Some(visible) = group_changeset.visible { if let Some(visible) = group_changeset.visible {
group.visible = visible; group.visible = visible;
} }
if let Some(name) = &group_changeset.name { if let Some(name) = &group_changeset.name {
group.name = name.clone(); group.name = name.clone();
} }
})?; })?;
if let Some(group) = update_group {
self.group_by_id.get_mut(&group.id).map(|group_data| {
group_data.name = group.name.clone();
group_data.is_visible = group.visible;
});
}
Ok(()) Ok(())
} }
@ -370,6 +376,11 @@ where
.await .await
} }
/// # Arguments
///
/// * `mut_configuration_fn`: mutate the [GroupSetting] and return whether the [GroupSetting] is
/// changed. If the [GroupSetting] is changed, the [GroupSetting] will be saved to the storage.
///
fn mut_configuration( fn mut_configuration(
&mut self, &mut self,
mut_configuration_fn: impl FnOnce(&mut GroupSetting) -> bool, mut_configuration_fn: impl FnOnce(&mut GroupSetting) -> bool,
@ -392,7 +403,12 @@ where
Ok(()) Ok(())
} }
fn mut_group(&mut self, group_id: &str, mut_groups_fn: impl Fn(&mut Group)) -> FlowyResult<()> { fn mut_group(
&mut self,
group_id: &str,
mut_groups_fn: impl Fn(&mut Group),
) -> FlowyResult<Option<Group>> {
let mut updated_group = None;
self.mut_configuration(|configuration| { self.mut_configuration(|configuration| {
match configuration match configuration
.groups .groups
@ -402,10 +418,12 @@ where
None => false, None => false,
Some(group) => { Some(group) => {
mut_groups_fn(group); mut_groups_fn(group);
updated_group = Some(group.clone());
true true
}, },
} }
}) })?;
Ok(updated_group)
} }
} }

View File

@ -1,6 +1,7 @@
use collab_database::database::gen_row_id; use collab_database::database::gen_row_id;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::{CreateRowParams, RowId}; use collab_database::rows::{CreateRowParams, RowId};
use flowy_database2::entities::{FieldType, GroupPB, RowPB}; use flowy_database2::entities::{FieldType, GroupPB, RowPB};
use flowy_database2::services::cell::{ use flowy_database2::services::cell::{
delete_select_option_cell, insert_select_option_cell, insert_url_cell, delete_select_option_cell, insert_select_option_cell, insert_url_cell,
@ -233,7 +234,7 @@ impl DatabaseGroupTest {
} => { } => {
let group = self.group_at_index(group_index).await; let group = self.group_at_index(group_index).await;
assert_eq!(group.group_id, group_pb.group_id); assert_eq!(group.group_id, group_pb.group_id);
assert_eq!(group.desc, group_pb.desc); assert_eq!(group.group_name, group_pb.group_name);
}, },
GroupScript::UpdateSingleSelectSelectOption { inserted_options } => { GroupScript::UpdateSingleSelectSelectOption { inserted_options } => {
self self

View File

@ -463,7 +463,7 @@ async fn group_insert_single_select_option_test() {
]; ];
test.run_scripts(scripts).await; test.run_scripts(scripts).await;
let new_group = test.group_at_index(1).await; let new_group = test.group_at_index(1).await;
assert_eq!(new_group.desc, new_option_name); assert_eq!(new_group.group_name, new_option_name);
} }
#[tokio::test] #[tokio::test]

View File

@ -1,3 +1,5 @@
use bytes::Bytes;
use std::convert::TryFrom;
use std::env::temp_dir; use std::env::temp_dir;
use std::sync::Arc; use std::sync::Arc;
@ -132,6 +134,49 @@ impl FlowyCoreTest {
.parse::<flowy_folder2::entities::ViewPB>() .parse::<flowy_folder2::entities::ViewPB>()
} }
pub async fn create_board(&self, parent_id: &str, name: String, initial_data: Vec<u8>) -> ViewPB {
let payload = CreateViewPayloadPB {
parent_view_id: parent_id.to_string(),
name,
desc: "".to_string(),
thumbnail: None,
layout: ViewLayoutPB::Board,
initial_data,
meta: Default::default(),
set_as_current: true,
};
EventBuilder::new(self.clone())
.event(flowy_folder2::event_map::FolderEvent::CreateView)
.payload(payload)
.async_send()
.await
.parse::<flowy_folder2::entities::ViewPB>()
}
pub async fn create_calendar(
&self,
parent_id: &str,
name: String,
initial_data: Vec<u8>,
) -> ViewPB {
let payload = CreateViewPayloadPB {
parent_view_id: parent_id.to_string(),
name,
desc: "".to_string(),
thumbnail: None,
layout: ViewLayoutPB::Calendar,
initial_data,
meta: Default::default(),
set_as_current: true,
};
EventBuilder::new(self.clone())
.event(flowy_folder2::event_map::FolderEvent::CreateView)
.payload(payload)
.async_send()
.await
.parse::<flowy_folder2::entities::ViewPB>()
}
pub async fn get_database(&self, view_id: &str) -> DatabasePB { pub async fn get_database(&self, view_id: &str) -> DatabasePB {
EventBuilder::new(self.clone()) EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::GetDatabase) .event(flowy_database2::event_map::DatabaseEvent::GetDatabase)
@ -238,7 +283,20 @@ impl FlowyCoreTest {
.parse::<RowPB>() .parse::<RowPB>()
} }
pub async fn get_row(&self, view_id: &str, row_id: &str) -> RowPB { pub async fn delete_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::DeleteRow)
.payload(RowIdPB {
view_id: view_id.to_string(),
row_id: row_id.to_string(),
group_id: None,
})
.async_send()
.await
.error()
}
pub async fn get_row(&self, view_id: &str, row_id: &str) -> OptionalRowPB {
EventBuilder::new(self.clone()) EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::GetRow) .event(flowy_database2::event_map::DatabaseEvent::GetRow)
.payload(RowIdPB { .payload(RowIdPB {
@ -248,7 +306,7 @@ impl FlowyCoreTest {
}) })
.async_send() .async_send()
.await .await
.parse::<RowPB>() .parse::<OptionalRowPB>()
} }
pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> { pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
@ -264,6 +322,19 @@ impl FlowyCoreTest {
.error() .error()
} }
pub async fn move_row(&self, view_id: &str, row_id: &str, to_row_id: &str) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::MoveRow)
.payload(MoveRowPayloadPB {
view_id: view_id.to_string(),
from_row_id: row_id.to_string(),
to_row_id: to_row_id.to_string(),
})
.async_send()
.await
.error()
}
pub async fn update_cell(&self, changeset: CellChangesetPB) -> Option<FlowyError> { pub async fn update_cell(&self, changeset: CellChangesetPB) -> Option<FlowyError> {
EventBuilder::new(self.clone()) EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::UpdateCell) .event(flowy_database2::event_map::DatabaseEvent::UpdateCell)
@ -273,6 +344,15 @@ impl FlowyCoreTest {
.error() .error()
} }
pub async fn update_date_cell(&self, changeset: DateChangesetPB) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::UpdateDateCell)
.payload(changeset)
.async_send()
.await
.error()
}
pub async fn get_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> CellPB { pub async fn get_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> CellPB {
EventBuilder::new(self.clone()) EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::GetCell) .event(flowy_database2::event_map::DatabaseEvent::GetCell)
@ -286,6 +366,41 @@ impl FlowyCoreTest {
.parse::<CellPB>() .parse::<CellPB>()
} }
pub async fn get_date_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> DateCellDataPB {
let cell = self.get_cell(view_id, row_id, field_id).await;
DateCellDataPB::try_from(Bytes::from(cell.data)).unwrap()
}
pub async fn get_checklist_cell(
&self,
view_id: &str,
field_id: &str,
row_id: &str,
) -> ChecklistCellDataPB {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::GetChecklistCellData)
.payload(CellIdPB {
view_id: view_id.to_string(),
row_id: row_id.to_string(),
field_id: field_id.to_string(),
})
.async_send()
.await
.parse::<ChecklistCellDataPB>()
}
pub async fn update_checklist_cell(
&self,
changeset: ChecklistCellDataChangesetPB,
) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::UpdateChecklistCell)
.payload(changeset)
.async_send()
.await
.error()
}
pub async fn insert_option( pub async fn insert_option(
&self, &self,
view_id: &str, view_id: &str,
@ -317,6 +432,84 @@ impl FlowyCoreTest {
.error() .error()
} }
pub async fn get_groups(&self, view_id: &str) -> Vec<GroupPB> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::GetGroups)
.payload(DatabaseViewIdPB {
value: view_id.to_string(),
})
.async_send()
.await
.parse::<RepeatedGroupPB>()
.items
}
pub async fn move_group(&self, view_id: &str, from_id: &str, to_id: &str) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::MoveGroup)
.payload(MoveGroupPayloadPB {
view_id: view_id.to_string(),
from_group_id: from_id.to_string(),
to_group_id: to_id.to_string(),
})
.async_send()
.await
.error()
}
pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::SetGroupByField)
.payload(GroupByFieldPayloadPB {
field_id: field_id.to_string(),
view_id: view_id.to_string(),
})
.async_send()
.await
.error()
}
pub async fn update_group(
&self,
view_id: &str,
group_id: &str,
name: Option<String>,
visible: Option<bool>,
) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::UpdateGroup)
.payload(UpdateGroupPB {
view_id: view_id.to_string(),
group_id: group_id.to_string(),
name,
visible,
})
.async_send()
.await
.error()
}
pub async fn update_setting(&self, changeset: DatabaseSettingChangesetPB) -> Option<FlowyError> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::UpdateDatabaseSetting)
.payload(changeset)
.async_send()
.await
.error()
}
pub async fn get_all_calendar_events(&self, view_id: &str) -> Vec<CalendarEventPB> {
EventBuilder::new(self.clone())
.event(flowy_database2::event_map::DatabaseEvent::GetAllCalendarEvents)
.payload(CalendarEventRequestPB {
view_id: view_id.to_string(),
})
.async_send()
.await
.parse::<RepeatedCalendarEventPB>()
.items
}
pub async fn get_view(&self, view_id: &str) -> ViewPB { pub async fn get_view(&self, view_id: &str) -> ViewPB {
EventBuilder::new(self.clone()) EventBuilder::new(self.clone())
.event(flowy_folder2::event_map::FolderEvent::ReadView) .event(flowy_folder2::event_map::FolderEvent::ReadView)

View File

@ -1,9 +1,11 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use bytes::Bytes; use bytes::Bytes;
use lib_infra::util::timestamp;
use flowy_database2::entities::{ use flowy_database2::entities::{
CellChangesetPB, DatabaseLayoutPB, DatabaseViewIdPB, FieldType, SelectOptionCellDataPB, CellChangesetPB, CellIdPB, ChecklistCellDataChangesetPB, DatabaseLayoutPB,
DatabaseSettingChangesetPB, DatabaseViewIdPB, DateChangesetPB, FieldType, SelectOptionCellDataPB,
}; };
use flowy_test::event_builder::EventBuilder; use flowy_test::event_builder::EventBuilder;
use flowy_test::FlowyCoreTest; use flowy_test::FlowyCoreTest;
@ -93,6 +95,7 @@ async fn delete_field_event_test() {
assert_eq!(fields.len(), 2); assert_eq!(fields.len(), 2);
} }
// The primary field is not allowed to be deleted.
#[tokio::test] #[tokio::test]
async fn delete_primary_field_event_test() { async fn delete_primary_field_event_test() {
let test = FlowyCoreTest::new_with_user().await; let test = FlowyCoreTest::new_with_user().await;
@ -162,6 +165,7 @@ async fn duplicate_field_event_test() {
assert_eq!(fields.len(), 4); assert_eq!(fields.len(), 4);
} }
// The primary field is not allowed to be duplicated. So this test should return an error.
#[tokio::test] #[tokio::test]
async fn duplicate_primary_field_test() { async fn duplicate_primary_field_test() {
let test = FlowyCoreTest::new_with_user().await; let test = FlowyCoreTest::new_with_user().await;
@ -189,6 +193,40 @@ async fn create_row_event_test() {
assert_eq!(database.rows.len(), 4); assert_eq!(database.rows.len(), 4);
} }
#[tokio::test]
async fn delete_row_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
// delete the row
let database = test.get_database(&grid_view.id).await;
let error = test.delete_row(&grid_view.id, &database.rows[0].id).await;
assert!(error.is_none());
let database = test.get_database(&grid_view.id).await;
assert_eq!(database.rows.len(), 2);
// get the row again and check if it is deleted.
let optional_row = test.get_row(&grid_view.id, &database.rows[0].id).await;
assert!(optional_row.row.is_none());
}
#[tokio::test]
async fn delete_row_event_with_invalid_row_id_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
// delete the row with empty row_id. It should return an error.
let error = test.delete_row(&grid_view.id, "").await;
assert!(error.is_some());
}
#[tokio::test] #[tokio::test]
async fn duplicate_row_event_test() { async fn duplicate_row_event_test() {
let test = FlowyCoreTest::new_with_user().await; let test = FlowyCoreTest::new_with_user().await;
@ -206,6 +244,90 @@ async fn duplicate_row_event_test() {
assert_eq!(database.rows.len(), 4); assert_eq!(database.rows.len(), 4);
} }
#[tokio::test]
async fn duplicate_row_event_with_invalid_row_id_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
let database = test.get_database(&grid_view.id).await;
assert_eq!(database.rows.len(), 3);
let error = test.duplicate_row(&grid_view.id, "").await;
assert!(error.is_some());
let database = test.get_database(&grid_view.id).await;
assert_eq!(database.rows.len(), 3);
}
#[tokio::test]
async fn move_row_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
let database = test.get_database(&grid_view.id).await;
let row_1 = database.rows[0].id.clone();
let row_2 = database.rows[1].id.clone();
let row_3 = database.rows[2].id.clone();
let error = test.move_row(&grid_view.id, &row_1, &row_3).await;
assert!(error.is_none());
let database = test.get_database(&grid_view.id).await;
assert_eq!(database.rows[0].id, row_2);
assert_eq!(database.rows[1].id, row_3);
assert_eq!(database.rows[2].id, row_1);
}
#[tokio::test]
async fn move_row_event_test2() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
let database = test.get_database(&grid_view.id).await;
let row_1 = database.rows[0].id.clone();
let row_2 = database.rows[1].id.clone();
let row_3 = database.rows[2].id.clone();
let error = test.move_row(&grid_view.id, &row_2, &row_1).await;
assert!(error.is_none());
let database = test.get_database(&grid_view.id).await;
assert_eq!(database.rows[0].id, row_2);
assert_eq!(database.rows[1].id, row_1);
assert_eq!(database.rows[2].id, row_3);
}
#[tokio::test]
async fn move_row_event_with_invalid_row_id_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
let database = test.get_database(&grid_view.id).await;
let row_1 = database.rows[0].id.clone();
let row_2 = database.rows[1].id.clone();
let row_3 = database.rows[2].id.clone();
for i in 0..2 {
if i == 0 {
let error = test.move_row(&grid_view.id, &row_1, "").await;
assert!(error.is_some());
} else {
let error = test.move_row(&grid_view.id, "", &row_1).await;
assert!(error.is_some());
}
let database = test.get_database(&grid_view.id).await;
assert_eq!(database.rows[0].id, row_1);
assert_eq!(database.rows[1].id, row_2);
assert_eq!(database.rows[2].id, row_3);
}
}
#[tokio::test] #[tokio::test]
async fn update_text_cell_event_test() { async fn update_text_cell_event_test() {
let test = FlowyCoreTest::new_with_user().await; let test = FlowyCoreTest::new_with_user().await;
@ -280,14 +402,415 @@ async fn update_single_select_cell_event_test() {
let field_id = fields[1].id.clone(); let field_id = fields[1].id.clone();
assert_eq!(fields[1].field_type, FieldType::SingleSelect); assert_eq!(fields[1].field_type, FieldType::SingleSelect);
// Insert a new option. This should update the cell with the new option.
let error = test let error = test
.insert_option(&grid_view.id, &field_id, &row_id, "task 1") .insert_option(&grid_view.id, &field_id, &row_id, "task 1")
.await; .await;
assert!(error.is_none()); assert!(error.is_none());
// Check that the cell data is updated.
let cell = test.get_cell(&grid_view.id, &row_id, &field_id).await; let cell = test.get_cell(&grid_view.id, &row_id, &field_id).await;
let select_option_cell = SelectOptionCellDataPB::try_from(Bytes::from(cell.data)).unwrap(); let select_option_cell = SelectOptionCellDataPB::try_from(Bytes::from(cell.data)).unwrap();
assert_eq!(select_option_cell.options.len(), 1); assert_eq!(select_option_cell.options.len(), 1);
assert_eq!(select_option_cell.select_options.len(), 1); assert_eq!(select_option_cell.select_options.len(), 1);
} }
#[tokio::test]
async fn update_date_cell_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
let database = test.get_database(&grid_view.id).await;
// Create a date field
let date_field = test.create_field(&grid_view.id, FieldType::DateTime).await;
let cell_path = CellIdPB {
view_id: grid_view.id.clone(),
field_id: date_field.id.clone(),
row_id: database.rows[0].id.clone(),
};
// Insert data into the date cell of the first row.
let timestamp = 1686300557;
let timestamp_str = 1686300557.to_string();
let error = test
.update_date_cell(DateChangesetPB {
cell_id: cell_path,
date: Some(timestamp_str.clone()),
time: None,
include_time: None,
})
.await;
assert!(error.is_none());
// Check that the cell data is updated.
let cell = test
.get_date_cell(&grid_view.id, &database.rows[0].id, &date_field.id)
.await;
assert_eq!(cell.date, "Jun 09, 2023");
assert_eq!(cell.timestamp, timestamp);
}
#[tokio::test]
async fn update_date_cell_event_with_empty_time_str_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
let database = test.get_database(&grid_view.id).await;
let row_id = database.rows[0].id.clone();
// Create a date field
let date_field = test.create_field(&grid_view.id, FieldType::DateTime).await;
let cell_path = CellIdPB {
view_id: grid_view.id.clone(),
field_id: date_field.id.clone(),
row_id: row_id.clone(),
};
// Insert empty timestamp string
let error = test
.update_date_cell(DateChangesetPB {
cell_id: cell_path,
date: Some("".to_string()),
..Default::default()
})
.await;
assert!(error.is_none());
// Check that the cell data is updated.
let cell = test
.get_date_cell(&grid_view.id, &row_id, &date_field.id)
.await;
assert_eq!(cell.date, "");
assert_eq!(cell.timestamp, 0);
}
#[tokio::test]
async fn create_checklist_field_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
// create checklist field
let checklist_field = test.create_field(&grid_view.id, FieldType::Checklist).await;
let database = test.get_database(&grid_view.id).await;
// Get the checklist cell
let cell = test
.get_checklist_cell(&grid_view.id, &checklist_field.id, &database.rows[0].id)
.await;
assert_eq!(cell.options.len(), 0);
assert_eq!(cell.selected_options.len(), 0);
assert_eq!(cell.percentage, 0.0);
}
#[tokio::test]
async fn update_checklist_cell_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
// create checklist field
let checklist_field = test.create_field(&grid_view.id, FieldType::Checklist).await;
let database = test.get_database(&grid_view.id).await;
// update the checklist cell
let changeset = ChecklistCellDataChangesetPB {
view_id: grid_view.id.clone(),
row_id: database.rows[0].id.clone(),
field_id: checklist_field.id.clone(),
insert_options: vec![
"task 1".to_string(),
"task 2".to_string(),
"task 3".to_string(),
],
selected_option_ids: vec![],
delete_option_ids: vec![],
update_options: vec![],
};
test.update_checklist_cell(changeset).await;
// get the cell
let cell = test
.get_checklist_cell(&grid_view.id, &checklist_field.id, &database.rows[0].id)
.await;
assert_eq!(cell.options.len(), 3);
assert_eq!(cell.selected_options.len(), 0);
// select some options
let changeset = ChecklistCellDataChangesetPB {
view_id: grid_view.id.clone(),
row_id: database.rows[0].id.clone(),
field_id: checklist_field.id.clone(),
selected_option_ids: vec![cell.options[0].id.clone(), cell.options[1].id.clone()],
..Default::default()
};
test.update_checklist_cell(changeset).await;
// get the cell
let cell = test
.get_checklist_cell(&grid_view.id, &checklist_field.id, &database.rows[0].id)
.await;
assert_eq!(cell.options.len(), 3);
assert_eq!(cell.selected_options.len(), 2);
assert_eq!(cell.percentage, 0.6666666666666666);
}
// The number of groups should be 0 if there is no group by field in grid
#[tokio::test]
async fn get_groups_event_with_grid_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my board view".to_owned(), vec![])
.await;
let groups = test.get_groups(&grid_view.id).await;
assert_eq!(groups.len(), 0);
}
#[tokio::test]
async fn get_groups_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let board_view = test
.create_board(&current_workspace.id, "my board view".to_owned(), vec![])
.await;
let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups.len(), 4);
}
#[tokio::test]
async fn move_group_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let board_view = test
.create_board(&current_workspace.id, "my board view".to_owned(), vec![])
.await;
let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups.len(), 4);
let group_1 = groups[0].group_id.clone();
let group_2 = groups[1].group_id.clone();
let group_3 = groups[2].group_id.clone();
let group_4 = groups[3].group_id.clone();
let error = test.move_group(&board_view.id, &group_2, &group_3).await;
assert!(error.is_none());
let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups[0].group_id, group_1);
assert_eq!(groups[1].group_id, group_3);
assert_eq!(groups[2].group_id, group_2);
assert_eq!(groups[3].group_id, group_4);
let error = test.move_group(&board_view.id, &group_1, &group_4).await;
assert!(error.is_none());
let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups[0].group_id, group_3);
assert_eq!(groups[1].group_id, group_2);
assert_eq!(groups[2].group_id, group_4);
assert_eq!(groups[3].group_id, group_1);
}
#[tokio::test]
async fn move_group_event_with_invalid_id_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let board_view = test
.create_board(&current_workspace.id, "my board view".to_owned(), vec![])
.await;
// Empty to group id
let groups = test.get_groups(&board_view.id).await;
let error = test
.move_group(&board_view.id, &groups[0].group_id, "")
.await;
assert!(error.is_some());
// empty from group id
let error = test
.move_group(&board_view.id, "", &groups[1].group_id)
.await;
assert!(error.is_some());
}
#[tokio::test]
async fn rename_group_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let board_view = test
.create_board(&current_workspace.id, "my board view".to_owned(), vec![])
.await;
// Empty to group id
let groups = test.get_groups(&board_view.id).await;
let error = test
.update_group(
&board_view.id,
&groups[0].group_id,
Some("new name".to_owned()),
None,
)
.await;
assert!(error.is_none());
let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups[0].group_name, "new name".to_owned());
}
#[tokio::test]
async fn hide_group_event_test2() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let board_view = test
.create_board(&current_workspace.id, "my board view".to_owned(), vec![])
.await;
// Empty to group id
let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups.len(), 4);
let error = test
.update_group(&board_view.id, &groups[0].group_id, None, Some(false))
.await;
assert!(error.is_none());
let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups.len(), 3);
}
// Update the database layout type from grid to board
#[tokio::test]
async fn update_database_layout_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
let error = test
.update_setting(DatabaseSettingChangesetPB {
view_id: grid_view.id.clone(),
layout_type: Some(DatabaseLayoutPB::Board),
..Default::default()
})
.await;
assert!(error.is_none());
let database = test.get_database(&grid_view.id).await;
assert_eq!(database.layout_type, DatabaseLayoutPB::Board);
}
// Update the database layout type from grid to board. Set the checkbox field as the grouping field
#[tokio::test]
async fn update_database_layout_event_test2() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let grid_view = test
.create_grid(&current_workspace.id, "my grid view".to_owned(), vec![])
.await;
let fields = test.get_all_database_fields(&grid_view.id).await.items;
let checkbox_field = fields
.iter()
.find(|field| field.field_type == FieldType::Checkbox)
.unwrap();
test
.set_group_by_field(&grid_view.id, &checkbox_field.id)
.await;
let error = test
.update_setting(DatabaseSettingChangesetPB {
view_id: grid_view.id.clone(),
layout_type: Some(DatabaseLayoutPB::Board),
..Default::default()
})
.await;
assert!(error.is_none());
// Empty to group id
let groups = test.get_groups(&grid_view.id).await;
assert_eq!(groups.len(), 2);
}
// Create a checkbox field in the default board and then set it as the grouping field.
#[tokio::test]
async fn set_group_by_checkbox_field_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let board_view = test
.create_board(&current_workspace.id, "my board view".to_owned(), vec![])
.await;
let checkbox_field = test.create_field(&board_view.id, FieldType::Checkbox).await;
test
.set_group_by_field(&board_view.id, &checkbox_field.id)
.await;
let groups = test.get_groups(&board_view.id).await;
assert_eq!(groups.len(), 2);
}
#[tokio::test]
async fn get_all_calendar_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let calendar_view = test
.create_calendar(&current_workspace.id, "my calendar view".to_owned(), vec![])
.await;
// By default, there should be no events
let events = test.get_all_calendar_events(&calendar_view.id).await;
assert!(events.is_empty());
}
#[tokio::test]
async fn create_calendar_event_test() {
let test = FlowyCoreTest::new_with_user().await;
let current_workspace = test.get_current_workspace().await.workspace;
let calendar_view = test
.create_calendar(&current_workspace.id, "my calendar view".to_owned(), vec![])
.await;
let fields = test.get_all_database_fields(&calendar_view.id).await.items;
let date_field = fields
.iter()
.find(|field| field.field_type == FieldType::DateTime)
.unwrap();
// create a new row
let row = test.create_row(&calendar_view.id, None, None).await;
// Insert data into the date cell of the first row.
let timestamp_str = timestamp().to_string();
let error = test
.update_date_cell(DateChangesetPB {
cell_id: CellIdPB {
view_id: calendar_view.id.clone(),
field_id: date_field.id.clone(),
row_id: row.id,
},
date: Some(timestamp_str.clone()),
time: None,
include_time: None,
})
.await;
assert!(error.is_none());
let events = test.get_all_calendar_events(&calendar_view.id).await;
assert_eq!(events.len(), 1);
}