mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
test: add databaase event test (#2728)
* test: add tests and fix modify primary field bug * test: add more test * fix: tauri buiuld * chore: disable share link button
This commit is contained in:
parent
1b56538a2f
commit
ce8cee5637
@ -16,8 +16,10 @@ class TextCellDataPersistence implements CellDataPersistence<String> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Option<FlowyError>> save(String data) async {
|
Future<Option<FlowyError>> save(String data) async {
|
||||||
final fut =
|
final fut = _cellBackendSvc.updateCell(
|
||||||
_cellBackendSvc.updateCell(cellContext: cellContext, data: data);
|
cellContext: cellContext,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
return fut.then((result) {
|
return fut.then((result) {
|
||||||
return result.fold(
|
return result.fold(
|
||||||
(l) => none(),
|
(l) => none(),
|
||||||
|
@ -29,7 +29,6 @@ class FieldBackendService {
|
|||||||
|
|
||||||
Future<Either<Unit, FlowyError>> updateField({
|
Future<Either<Unit, FlowyError>> updateField({
|
||||||
String? name,
|
String? name,
|
||||||
FieldType? fieldType,
|
|
||||||
bool? frozen,
|
bool? frozen,
|
||||||
bool? visibility,
|
bool? visibility,
|
||||||
double? width,
|
double? width,
|
||||||
@ -42,10 +41,6 @@ class FieldBackendService {
|
|||||||
payload.name = name;
|
payload.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldType != null) {
|
|
||||||
payload.fieldType = fieldType;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frozen != null) {
|
if (frozen != null) {
|
||||||
payload.frozen = frozen;
|
payload.frozen = frozen;
|
||||||
}
|
}
|
||||||
|
@ -124,13 +124,29 @@ class _FieldOperationList extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _actionCell(FieldAction action) {
|
Widget _actionCell(FieldAction action) {
|
||||||
|
bool enable = true;
|
||||||
|
|
||||||
|
// If the field is primary, delete and duplicate are disabled.
|
||||||
|
if (fieldInfo.field.isPrimary) {
|
||||||
|
switch (action) {
|
||||||
|
case FieldAction.hide:
|
||||||
|
break;
|
||||||
|
case FieldAction.duplicate:
|
||||||
|
enable = false;
|
||||||
|
break;
|
||||||
|
case FieldAction.delete:
|
||||||
|
enable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Flexible(
|
return Flexible(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: GridSize.popoverItemHeight,
|
height: GridSize.popoverItemHeight,
|
||||||
child: FieldActionCell(
|
child: FieldActionCell(
|
||||||
fieldInfo: fieldInfo,
|
fieldInfo: fieldInfo,
|
||||||
action: action,
|
action: action,
|
||||||
enable: action != FieldAction.delete || !fieldInfo.field.isPrimary,
|
enable: enable,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -72,5 +72,6 @@ class CheckboxCardCellState with _$CheckboxCardCellState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _isSelected(String? cellData) {
|
bool _isSelected(String? cellData) {
|
||||||
|
// The backend use "Yes" and "No" to represent the checkbox cell data.
|
||||||
return cellData == "Yes";
|
return cellData == "Yes";
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
|||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/plugins/document/application/share_bloc.dart';
|
import 'package:appflowy/plugins/document/application/share_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
|
||||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-document2/entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-document2/entities.pb.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
@ -104,11 +103,11 @@ class ShareActionList extends StatelessWidget {
|
|||||||
showMessageToast('Exported to: $exportPath');
|
showMessageToast('Exported to: $exportPath');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ShareAction.copyLink:
|
// case ShareAction.copyLink:
|
||||||
NavigatorAlertDialog(
|
// NavigatorAlertDialog(
|
||||||
title: LocaleKeys.shareAction_workInProgress.tr(),
|
// title: LocaleKeys.shareAction_workInProgress.tr(),
|
||||||
).show(context);
|
// ).show(context);
|
||||||
break;
|
// break;
|
||||||
}
|
}
|
||||||
controller.close();
|
controller.close();
|
||||||
},
|
},
|
||||||
@ -118,7 +117,7 @@ class ShareActionList extends StatelessWidget {
|
|||||||
|
|
||||||
enum ShareAction {
|
enum ShareAction {
|
||||||
markdown,
|
markdown,
|
||||||
copyLink,
|
// copyLink,
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShareActionWrapper extends ActionCell {
|
class ShareActionWrapper extends ActionCell {
|
||||||
@ -133,8 +132,8 @@ class ShareActionWrapper extends ActionCell {
|
|||||||
switch (inner) {
|
switch (inner) {
|
||||||
case ShareAction.markdown:
|
case ShareAction.markdown:
|
||||||
return LocaleKeys.shareAction_markdown.tr();
|
return LocaleKeys.shareAction_markdown.tr();
|
||||||
case ShareAction.copyLink:
|
// case ShareAction.copyLink:
|
||||||
return LocaleKeys.shareAction_copyLink.tr();
|
// return LocaleKeys.shareAction_copyLink.tr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart';
|
|
||||||
import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart';
|
|
||||||
import 'package:appflowy/plugins/database_view/application/filter/filter_service.dart';
|
|
||||||
import 'package:appflowy/plugins/database_view/grid/application/filter/filter_menu_bloc.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/text_filter.pb.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
import '../util.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
late AppFlowyGridTest gridTest;
|
|
||||||
setUpAll(() async {
|
|
||||||
gridTest = await AppFlowyGridTest.ensureInitialized();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("create a text filter and then alter the filter's field)", () async {
|
|
||||||
final context = await gridTest.createTestGrid();
|
|
||||||
final service = FilterBackendService(viewId: context.gridView.id);
|
|
||||||
final textField = context.textFieldContext();
|
|
||||||
|
|
||||||
// Create the filter menu bloc
|
|
||||||
final menuBloc = GridFilterMenuBloc(
|
|
||||||
fieldController: context.fieldController,
|
|
||||||
viewId: context.gridView.id,
|
|
||||||
)..add(const GridFilterMenuEvent.initial());
|
|
||||||
|
|
||||||
// Insert filter for the text field
|
|
||||||
await service.insertTextFilter(
|
|
||||||
fieldId: textField.id,
|
|
||||||
condition: TextFilterConditionPB.TextIsEmpty,
|
|
||||||
content: "",
|
|
||||||
);
|
|
||||||
await gridResponseFuture();
|
|
||||||
assert(menuBloc.state.filters.length == 1);
|
|
||||||
|
|
||||||
// Edit the text field
|
|
||||||
final loader = FieldTypeOptionLoader(
|
|
||||||
viewId: context.gridView.id,
|
|
||||||
field: textField.field,
|
|
||||||
);
|
|
||||||
|
|
||||||
final editorBloc = FieldEditorBloc(
|
|
||||||
isGroupField: false,
|
|
||||||
loader: loader,
|
|
||||||
field: textField.field,
|
|
||||||
)..add(const FieldEditorEvent.initial());
|
|
||||||
await gridResponseFuture();
|
|
||||||
|
|
||||||
// Alter the field type to Number
|
|
||||||
editorBloc.add(const FieldEditorEvent.switchToField(FieldType.Number));
|
|
||||||
await gridResponseFuture();
|
|
||||||
|
|
||||||
// Check the number of filters
|
|
||||||
assert(menuBloc.state.filters.isEmpty);
|
|
||||||
});
|
|
||||||
}
|
|
31
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
31
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1134,7 +1134,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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1154,7 +1154,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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1173,6 +1173,7 @@ dependencies = [
|
|||||||
"rusoto_credential",
|
"rusoto_credential",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"similar 2.2.1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-retry",
|
"tokio-retry",
|
||||||
@ -1184,7 +1185,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=cbc2e0#cbc2e0acb8420dc997921bb3f56b99f9975c2aab"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=a647d9#a647d922ef432510d6be0abb5f968d9a75dc7011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1789,7 +1790,7 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"similar",
|
"similar 1.3.0",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
"tera",
|
"tera",
|
||||||
"toml 0.5.11",
|
"toml 0.5.11",
|
||||||
@ -1818,6 +1819,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"appflowy-integrate",
|
"appflowy-integrate",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"diesel",
|
||||||
"flowy-config",
|
"flowy-config",
|
||||||
"flowy-database2",
|
"flowy-database2",
|
||||||
"flowy-document2",
|
"flowy-document2",
|
||||||
@ -1839,6 +1841,7 @@ dependencies = [
|
|||||||
"serde_repr",
|
"serde_repr",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5055,6 +5058,12 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec"
|
checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "similar"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
|
@ -21,23 +21,13 @@ export abstract class TypeOptionParser<T> {
|
|||||||
export class FieldBackendService {
|
export class FieldBackendService {
|
||||||
constructor(public readonly viewId: string, public readonly fieldId: string) {}
|
constructor(public readonly viewId: string, public readonly fieldId: string) {}
|
||||||
|
|
||||||
updateField = (data: {
|
updateField = (data: { name?: string; frozen?: boolean; visibility?: boolean; width?: number }) => {
|
||||||
name?: string;
|
|
||||||
fieldType?: FieldType;
|
|
||||||
frozen?: boolean;
|
|
||||||
visibility?: boolean;
|
|
||||||
width?: number;
|
|
||||||
}) => {
|
|
||||||
const payload = FieldChangesetPB.fromObject({ view_id: this.viewId, field_id: this.fieldId });
|
const payload = FieldChangesetPB.fromObject({ view_id: this.viewId, field_id: this.fieldId });
|
||||||
|
|
||||||
if (data.name !== undefined) {
|
if (data.name !== undefined) {
|
||||||
payload.name = data.name;
|
payload.name = data.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.fieldType !== undefined) {
|
|
||||||
payload.field_type = data.fieldType;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.frozen !== undefined) {
|
if (data.frozen !== undefined) {
|
||||||
payload.frozen = data.frozen;
|
payload.frozen = data.frozen;
|
||||||
}
|
}
|
||||||
|
1
frontend/rust-lib/Cargo.lock
generated
1
frontend/rust-lib/Cargo.lock
generated
@ -1864,6 +1864,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"flowy-core",
|
"flowy-core",
|
||||||
|
"flowy-database2",
|
||||||
"flowy-folder2",
|
"flowy-folder2",
|
||||||
"flowy-net",
|
"flowy-net",
|
||||||
"flowy-notification",
|
"flowy-notification",
|
||||||
|
@ -1,182 +0,0 @@
|
|||||||
use crate::entities::FieldType;
|
|
||||||
|
|
||||||
use crate::services::cell::TypeCellData;
|
|
||||||
use crate::services::database::DatabaseEditor;
|
|
||||||
use crate::services::field::{
|
|
||||||
CheckboxTypeOptionPB, ChecklistTypeOptionPB, DateCellData, DateTypeOptionPB,
|
|
||||||
MultiSelectTypeOptionPB, NumberTypeOptionPB, RichTextTypeOptionPB, SingleSelectTypeOptionPB,
|
|
||||||
URLCellData,
|
|
||||||
};
|
|
||||||
use database_model::{FieldRevision, TypeOptionDataDeserializer};
|
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::{json, Map, Value};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
pub struct ExportField {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub field_type: i64,
|
|
||||||
pub visibility: bool,
|
|
||||||
pub width: i64,
|
|
||||||
pub type_options: HashMap<String, Value>,
|
|
||||||
pub is_primary: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
struct ExportCell {
|
|
||||||
data: String,
|
|
||||||
field_type: FieldType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Arc<FieldRevision>> for ExportField {
|
|
||||||
fn from(field_rev: &Arc<FieldRevision>) -> Self {
|
|
||||||
let field_type = FieldType::from(field_rev.ty);
|
|
||||||
let mut type_options: HashMap<String, Value> = HashMap::new();
|
|
||||||
|
|
||||||
field_rev
|
|
||||||
.type_options
|
|
||||||
.iter()
|
|
||||||
.filter(|(k, _)| k == &&field_rev.ty.to_string())
|
|
||||||
.for_each(|(k, s)| {
|
|
||||||
let value = match field_type {
|
|
||||||
FieldType::RichText => {
|
|
||||||
let pb = RichTextTypeOptionPB::from_json_str(s);
|
|
||||||
serde_json::to_value(pb).unwrap()
|
|
||||||
},
|
|
||||||
FieldType::Number => {
|
|
||||||
let pb = NumberTypeOptionPB::from_json_str(s);
|
|
||||||
let mut map = Map::new();
|
|
||||||
map.insert("format".to_string(), json!(pb.format as u8));
|
|
||||||
map.insert("scale".to_string(), json!(pb.scale));
|
|
||||||
map.insert("symbol".to_string(), json!(pb.symbol));
|
|
||||||
map.insert("name".to_string(), json!(pb.name));
|
|
||||||
Value::Object(map)
|
|
||||||
},
|
|
||||||
FieldType::DateTime => {
|
|
||||||
let pb = DateTypeOptionPB::from_json_str(s);
|
|
||||||
let mut map = Map::new();
|
|
||||||
map.insert("date_format".to_string(), json!(pb.date_format as u8));
|
|
||||||
map.insert("time_format".to_string(), json!(pb.time_format as u8));
|
|
||||||
map.insert("field_type".to_string(), json!(FieldType::DateTime as u8));
|
|
||||||
Value::Object(map)
|
|
||||||
},
|
|
||||||
FieldType::SingleSelect => {
|
|
||||||
let pb = SingleSelectTypeOptionPB::from_json_str(s);
|
|
||||||
let value = serde_json::to_string(&pb).unwrap();
|
|
||||||
let mut map = Map::new();
|
|
||||||
map.insert("content".to_string(), Value::String(value));
|
|
||||||
Value::Object(map)
|
|
||||||
},
|
|
||||||
FieldType::MultiSelect => {
|
|
||||||
let pb = MultiSelectTypeOptionPB::from_json_str(s);
|
|
||||||
let value = serde_json::to_string(&pb).unwrap();
|
|
||||||
let mut map = Map::new();
|
|
||||||
map.insert("content".to_string(), Value::String(value));
|
|
||||||
Value::Object(map)
|
|
||||||
},
|
|
||||||
FieldType::Checkbox => {
|
|
||||||
let pb = CheckboxTypeOptionPB::from_json_str(s);
|
|
||||||
serde_json::to_value(pb).unwrap()
|
|
||||||
},
|
|
||||||
FieldType::URL => {
|
|
||||||
let pb = RichTextTypeOptionPB::from_json_str(s);
|
|
||||||
serde_json::to_value(pb).unwrap()
|
|
||||||
},
|
|
||||||
FieldType::Checklist => {
|
|
||||||
let pb = ChecklistTypeOptionPB::from_json_str(s);
|
|
||||||
let value = serde_json::to_string(&pb).unwrap();
|
|
||||||
let mut map = Map::new();
|
|
||||||
map.insert("content".to_string(), Value::String(value));
|
|
||||||
Value::Object(map)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
type_options.insert(k.clone(), value);
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
id: field_rev.id.clone(),
|
|
||||||
name: field_rev.name.clone(),
|
|
||||||
field_type: field_rev.ty as i64,
|
|
||||||
visibility: true,
|
|
||||||
width: 100,
|
|
||||||
type_options,
|
|
||||||
is_primary: field_rev.is_primary,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CSVExport;
|
|
||||||
impl CSVExport {
|
|
||||||
pub async fn export_database(
|
|
||||||
&self,
|
|
||||||
view_id: &str,
|
|
||||||
database_editor: &Arc<DatabaseEditor>,
|
|
||||||
) -> FlowyResult<String> {
|
|
||||||
let mut wtr = csv::Writer::from_writer(vec![]);
|
|
||||||
let row_revs = database_editor.get_all_row_revs(view_id).await?;
|
|
||||||
let field_revs = database_editor.get_field_revs(None).await?;
|
|
||||||
|
|
||||||
// Write fields
|
|
||||||
let field_records = field_revs
|
|
||||||
.iter()
|
|
||||||
.map(|field| ExportField::from(field))
|
|
||||||
.map(|field| serde_json::to_string(&field).unwrap())
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
wtr
|
|
||||||
.write_record(&field_records)
|
|
||||||
.map_err(|e| FlowyError::internal().context(e))?;
|
|
||||||
|
|
||||||
// Write rows
|
|
||||||
let mut field_by_field_id = IndexMap::new();
|
|
||||||
field_revs.into_iter().for_each(|field| {
|
|
||||||
field_by_field_id.insert(field.id.clone(), field);
|
|
||||||
});
|
|
||||||
for row_rev in row_revs {
|
|
||||||
let cells = field_by_field_id
|
|
||||||
.iter()
|
|
||||||
.map(|(field_id, field)| {
|
|
||||||
let field_type = FieldType::from(field.ty);
|
|
||||||
let data = row_rev
|
|
||||||
.cells
|
|
||||||
.get(field_id)
|
|
||||||
.map(|cell| TypeCellData::try_from(cell))
|
|
||||||
.map(|data| {
|
|
||||||
data
|
|
||||||
.map(|data| match field_type {
|
|
||||||
FieldType::DateTime => {
|
|
||||||
match serde_json::from_str::<DateCellData>(&data.cell_str) {
|
|
||||||
Ok(cell_data) => cell_data.timestamp.unwrap_or_default().to_string(),
|
|
||||||
Err(_) => "".to_string(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FieldType::URL => match serde_json::from_str::<URLCellData>(&data.cell_str) {
|
|
||||||
Ok(cell_data) => cell_data.content,
|
|
||||||
Err(_) => "".to_string(),
|
|
||||||
},
|
|
||||||
_ => data.cell_str,
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| "".to_string());
|
|
||||||
let cell = ExportCell { data, field_type };
|
|
||||||
serde_json::to_string(&cell).unwrap()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if let Err(e) = wtr.write_record(&cells) {
|
|
||||||
tracing::warn!("CSV failed to write record: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = wtr
|
|
||||||
.into_inner()
|
|
||||||
.map_err(|e| FlowyError::internal().context(e))?;
|
|
||||||
let csv = String::from_utf8(data).map_err(|e| FlowyError::internal().context(e))?;
|
|
||||||
Ok(csv)
|
|
||||||
}
|
|
||||||
}
|
|
@ -155,6 +155,8 @@ pub struct CreateFieldPayloadPB {
|
|||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub field_type: FieldType,
|
pub field_type: FieldType,
|
||||||
|
|
||||||
|
/// If the type_option_data is not empty, it will be used to create the field.
|
||||||
|
/// Otherwise, the default value will be used.
|
||||||
#[pb(index = 3, one_of)]
|
#[pb(index = 3, one_of)]
|
||||||
pub type_option_data: Option<Vec<u8>>,
|
pub type_option_data: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
@ -163,6 +165,8 @@ pub struct CreateFieldPayloadPB {
|
|||||||
pub struct CreateFieldParams {
|
pub struct CreateFieldParams {
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
pub field_type: FieldType,
|
pub field_type: FieldType,
|
||||||
|
/// If the type_option_data is not empty, it will be used to create the field.
|
||||||
|
/// Otherwise, the default value will be used.
|
||||||
pub type_option_data: Option<Vec<u8>>,
|
pub type_option_data: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,9 +193,6 @@ pub struct UpdateFieldTypePayloadPB {
|
|||||||
|
|
||||||
#[pb(index = 3)]
|
#[pb(index = 3)]
|
||||||
pub field_type: FieldType,
|
pub field_type: FieldType,
|
||||||
|
|
||||||
#[pb(index = 4)]
|
|
||||||
pub create_if_not_exist: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EditFieldParams {
|
pub struct EditFieldParams {
|
||||||
@ -401,18 +402,13 @@ pub struct FieldChangesetPB {
|
|||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
|
|
||||||
#[pb(index = 5, one_of)]
|
#[pb(index = 5, one_of)]
|
||||||
pub field_type: Option<FieldType>,
|
|
||||||
|
|
||||||
#[pb(index = 6, one_of)]
|
|
||||||
pub frozen: Option<bool>,
|
pub frozen: Option<bool>,
|
||||||
|
|
||||||
#[pb(index = 7, one_of)]
|
#[pb(index = 6, one_of)]
|
||||||
pub visibility: Option<bool>,
|
pub visibility: Option<bool>,
|
||||||
|
|
||||||
#[pb(index = 8, one_of)]
|
#[pb(index = 7, one_of)]
|
||||||
pub width: Option<i32>,
|
pub width: Option<i32>,
|
||||||
// #[pb(index = 9, one_of)]
|
|
||||||
// pub type_option_data: Option<Vec<u8>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryInto<FieldChangesetParams> for FieldChangesetPB {
|
impl TryInto<FieldChangesetParams> for FieldChangesetPB {
|
||||||
@ -421,7 +417,6 @@ impl TryInto<FieldChangesetParams> for FieldChangesetPB {
|
|||||||
fn try_into(self) -> Result<FieldChangesetParams, Self::Error> {
|
fn try_into(self) -> Result<FieldChangesetParams, 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 field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
||||||
let field_type = self.field_type.map(FieldType::from);
|
|
||||||
// if let Some(type_option_data) = self.type_option_data.as_ref() {
|
// if let Some(type_option_data) = self.type_option_data.as_ref() {
|
||||||
// if type_option_data.is_empty() {
|
// if type_option_data.is_empty() {
|
||||||
// return Err(ErrorCode::TypeOptionDataIsEmpty);
|
// return Err(ErrorCode::TypeOptionDataIsEmpty);
|
||||||
@ -433,7 +428,6 @@ impl TryInto<FieldChangesetParams> for FieldChangesetPB {
|
|||||||
view_id: view_id.0,
|
view_id: view_id.0,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
desc: self.desc,
|
desc: self.desc,
|
||||||
field_type,
|
|
||||||
frozen: self.frozen,
|
frozen: self.frozen,
|
||||||
visibility: self.visibility,
|
visibility: self.visibility,
|
||||||
width: self.width,
|
width: self.width,
|
||||||
@ -452,8 +446,6 @@ pub struct FieldChangesetParams {
|
|||||||
|
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
|
|
||||||
pub field_type: Option<FieldType>,
|
|
||||||
|
|
||||||
pub frozen: Option<bool>,
|
pub frozen: Option<bool>,
|
||||||
|
|
||||||
pub visibility: Option<bool>,
|
pub visibility: Option<bool>,
|
||||||
|
@ -249,7 +249,7 @@ pub(crate) async fn get_field_type_option_data_handler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create FieldMeta and save it. Return the FieldTypeOptionData.
|
/// Create TypeOptionPB and save it. Return the FieldTypeOptionData.
|
||||||
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||||
pub(crate) async fn create_field_type_option_data_handler(
|
pub(crate) async fn create_field_type_option_data_handler(
|
||||||
data: AFPluginData<CreateFieldPayloadPB>,
|
data: AFPluginData<CreateFieldPayloadPB>,
|
||||||
|
@ -10,7 +10,7 @@ use collab_database::views::{DatabaseLayout, DatabaseView, LayoutSetting};
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use tokio::sync::{broadcast, RwLock};
|
use tokio::sync::{broadcast, RwLock};
|
||||||
|
|
||||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
||||||
use flowy_task::TaskDispatcher;
|
use flowy_task::TaskDispatcher;
|
||||||
use lib_infra::future::{to_fut, Fut};
|
use lib_infra::future::{to_fut, Fut};
|
||||||
|
|
||||||
@ -199,32 +199,32 @@ impl DatabaseEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of fields of the view.
|
||||||
|
/// If `field_ids` is not provided, all the fields will be returned in the order of the field that
|
||||||
|
/// defined in the view. Otherwise, the fields will be returned in the order of the `field_ids`.
|
||||||
pub fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Vec<Field> {
|
pub fn get_fields(&self, view_id: &str, field_ids: Option<Vec<String>>) -> Vec<Field> {
|
||||||
self.database.lock().get_fields(view_id, field_ids)
|
let database = self.database.lock();
|
||||||
|
let field_ids = field_ids.unwrap_or_else(|| {
|
||||||
|
database
|
||||||
|
.fields
|
||||||
|
.get_all_field_orders()
|
||||||
|
.into_iter()
|
||||||
|
.map(|field| field.id)
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
database.get_fields(view_id, Some(field_ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> {
|
pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> {
|
||||||
let is_primary = self
|
|
||||||
.database
|
|
||||||
.lock()
|
|
||||||
.fields
|
|
||||||
.get_field(¶ms.field_id)
|
|
||||||
.map(|field| field.is_primary)
|
|
||||||
.unwrap_or(false);
|
|
||||||
self
|
self
|
||||||
.database
|
.database
|
||||||
.lock()
|
.lock()
|
||||||
.fields
|
.fields
|
||||||
.update_field(¶ms.field_id, |mut update| {
|
.update_field(¶ms.field_id, |update| {
|
||||||
update = update
|
update
|
||||||
.set_name_if_not_none(params.name)
|
.set_name_if_not_none(params.name)
|
||||||
.set_width_at_if_not_none(params.width.map(|value| value as i64))
|
.set_width_at_if_not_none(params.width.map(|value| value as i64))
|
||||||
.set_visibility_if_not_none(params.visibility);
|
.set_visibility_if_not_none(params.visibility);
|
||||||
if is_primary {
|
|
||||||
tracing::warn!("Cannot update primary field type");
|
|
||||||
} else {
|
|
||||||
update.set_field_type_if_not_none(params.field_type.map(|field_type| field_type.into()));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
self
|
self
|
||||||
.notify_did_update_database_field(¶ms.field_id)
|
.notify_did_update_database_field(¶ms.field_id)
|
||||||
@ -233,6 +233,21 @@ impl DatabaseEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_field(&self, field_id: &str) -> FlowyResult<()> {
|
pub async fn delete_field(&self, field_id: &str) -> FlowyResult<()> {
|
||||||
|
let is_primary = self
|
||||||
|
.database
|
||||||
|
.lock()
|
||||||
|
.fields
|
||||||
|
.get_field(field_id)
|
||||||
|
.map(|field| field.is_primary)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if is_primary {
|
||||||
|
return Err(FlowyError::new(
|
||||||
|
ErrorCode::Internal,
|
||||||
|
"Can not delete primary field",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let database_id = {
|
let database_id = {
|
||||||
let database = self.database.lock();
|
let database = self.database.lock();
|
||||||
database.delete_field(field_id);
|
database.delete_field(field_id);
|
||||||
@ -283,6 +298,13 @@ impl DatabaseEditor {
|
|||||||
match field {
|
match field {
|
||||||
None => {},
|
None => {},
|
||||||
Some(field) => {
|
Some(field) => {
|
||||||
|
if field.is_primary {
|
||||||
|
return Err(FlowyError::new(
|
||||||
|
ErrorCode::Internal,
|
||||||
|
"Can not update primary field's field type",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let old_field_type = FieldType::from(field.field_type);
|
let old_field_type = FieldType::from(field.field_type);
|
||||||
let old_type_option = field.get_any_type_option(old_field_type.clone());
|
let old_type_option = field.get_any_type_option(old_field_type.clone());
|
||||||
let new_type_option = field
|
let new_type_option = field
|
||||||
@ -312,6 +334,21 @@ impl DatabaseEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn duplicate_field(&self, view_id: &str, field_id: &str) -> FlowyResult<()> {
|
pub async fn duplicate_field(&self, view_id: &str, field_id: &str) -> FlowyResult<()> {
|
||||||
|
let is_primary = self
|
||||||
|
.database
|
||||||
|
.lock()
|
||||||
|
.fields
|
||||||
|
.get_field(field_id)
|
||||||
|
.map(|field| field.is_primary)
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if is_primary {
|
||||||
|
return Err(FlowyError::new(
|
||||||
|
ErrorCode::Internal,
|
||||||
|
"Can not duplicate primary field",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let value = self
|
let value = self
|
||||||
.database
|
.database
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -90,7 +90,9 @@ impl CellDataDecoder for CheckboxTypeOption {
|
|||||||
return Ok(Default::default());
|
return Ok(Default::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.parse_cell(cell)
|
let cell = self.parse_cell(cell);
|
||||||
|
println!("cell: {:?}", cell);
|
||||||
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||||
|
@ -233,30 +233,6 @@ async fn grid_switch_from_checkbox_to_text_test() {
|
|||||||
test.run_scripts(scripts).await;
|
test.run_scripts(scripts).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test when switching the current field from Checkbox to Text test
|
|
||||||
// input:
|
|
||||||
// "Yes" -> check
|
|
||||||
// "" -> unchecked
|
|
||||||
#[tokio::test]
|
|
||||||
async fn grid_switch_from_text_to_checkbox_test() {
|
|
||||||
let mut test = DatabaseFieldTest::new().await;
|
|
||||||
let field = test.get_first_field(FieldType::RichText).clone();
|
|
||||||
|
|
||||||
let scripts = vec![
|
|
||||||
SwitchToField {
|
|
||||||
field_id: field.id.clone(),
|
|
||||||
new_field_type: FieldType::Checkbox,
|
|
||||||
},
|
|
||||||
AssertCellContent {
|
|
||||||
field_id: field.id.clone(),
|
|
||||||
row_index: 0,
|
|
||||||
from_field_type: FieldType::RichText,
|
|
||||||
expected_content: "".to_string(),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
test.run_scripts(scripts).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test when switching the current field from Date to Text test
|
// Test when switching the current field from Date to Text test
|
||||||
// input:
|
// input:
|
||||||
// 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)
|
||||||
|
@ -41,10 +41,14 @@ pub(crate) async fn open_workspace_handler(
|
|||||||
match params.value {
|
match params.value {
|
||||||
None => Err(FlowyError::workspace_id().context("workspace id should not be empty")),
|
None => Err(FlowyError::workspace_id().context("workspace id should not be empty")),
|
||||||
Some(workspace_id) => {
|
Some(workspace_id) => {
|
||||||
let workspace = folder.open_workspace(&workspace_id).await?;
|
if workspace_id.is_empty() {
|
||||||
let views = folder.get_workspace_views(&workspace_id).await?;
|
return Err(FlowyError::workspace_id().context("workspace id should not be empty"));
|
||||||
let workspace_pb: WorkspacePB = (workspace, views).into();
|
} else {
|
||||||
data_result_ok(workspace_pb)
|
let workspace = folder.open_workspace(&workspace_id).await?;
|
||||||
|
let views = folder.get_workspace_views(&workspace_id).await?;
|
||||||
|
let workspace_pb: WorkspacePB = (workspace, views).into();
|
||||||
|
data_result_ok(workspace_pb)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,7 +72,7 @@ pub(crate) async fn read_workspaces_handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(folder), err)]
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
pub async fn read_cur_workspace_setting_handler(
|
pub async fn read_current_workspace_setting_handler(
|
||||||
folder: AFPluginState<Arc<Folder2Manager>>,
|
folder: AFPluginState<Arc<Folder2Manager>>,
|
||||||
) -> DataResult<WorkspaceSettingPB, FlowyError> {
|
) -> DataResult<WorkspaceSettingPB, FlowyError> {
|
||||||
let workspace = folder.get_current_workspace().await?;
|
let workspace = folder.get_current_workspace().await?;
|
||||||
|
@ -13,7 +13,7 @@ pub fn init(folder: Arc<Folder2Manager>) -> AFPlugin {
|
|||||||
.event(FolderEvent::CreateWorkspace, create_workspace_handler)
|
.event(FolderEvent::CreateWorkspace, create_workspace_handler)
|
||||||
.event(
|
.event(
|
||||||
FolderEvent::GetCurrentWorkspace,
|
FolderEvent::GetCurrentWorkspace,
|
||||||
read_cur_workspace_setting_handler,
|
read_current_workspace_setting_handler,
|
||||||
)
|
)
|
||||||
.event(FolderEvent::ReadAllWorkspaces, read_workspaces_handler)
|
.event(FolderEvent::ReadAllWorkspaces, read_workspaces_handler)
|
||||||
.event(FolderEvent::OpenWorkspace, open_workspace_handler)
|
.event(FolderEvent::OpenWorkspace, open_workspace_handler)
|
||||||
|
@ -10,6 +10,7 @@ flowy-core = { path = "../flowy-core" }
|
|||||||
flowy-user = { path = "../flowy-user"}
|
flowy-user = { path = "../flowy-user"}
|
||||||
flowy-net = { path = "../flowy-net"}
|
flowy-net = { path = "../flowy-net"}
|
||||||
flowy-folder2 = { path = "../flowy-folder2", features = ["test_helper"] }
|
flowy-folder2 = { path = "../flowy-folder2", features = ["test_helper"] }
|
||||||
|
flowy-database2 = { path = "../flowy-database2" }
|
||||||
lib-dispatch = { path = "../lib-dispatch" }
|
lib-dispatch = { path = "../lib-dispatch" }
|
||||||
lib-ot = { path = "../../../shared-lib/lib-ot" }
|
lib-ot = { path = "../../../shared-lib/lib-ot" }
|
||||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||||
|
@ -5,10 +5,10 @@ use nanoid::nanoid;
|
|||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
|
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
|
||||||
use flowy_folder2::entities::{
|
use flowy_database2::entities::*;
|
||||||
CreateViewPayloadPB, RepeatedViewIdPB, ViewIdPB, ViewPB, WorkspaceSettingPB,
|
use flowy_folder2::entities::*;
|
||||||
};
|
|
||||||
use flowy_user::entities::{AuthTypePB, UserProfilePB};
|
use flowy_user::entities::{AuthTypePB, UserProfilePB};
|
||||||
|
use flowy_user::errors::FlowyError;
|
||||||
|
|
||||||
use crate::event_builder::EventBuilder;
|
use crate::event_builder::EventBuilder;
|
||||||
use crate::user_event::{async_sign_up, init_user_setting, SignUpContext};
|
use crate::user_event::{async_sign_up, init_user_setting, SignUpContext};
|
||||||
@ -113,6 +113,210 @@ impl FlowyCoreTest {
|
|||||||
.parse::<flowy_folder2::entities::ViewPB>()
|
.parse::<flowy_folder2::entities::ViewPB>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_grid(&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::Grid,
|
||||||
|
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 {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::GetDatabase)
|
||||||
|
.payload(DatabaseViewIdPB {
|
||||||
|
value: view_id.to_string(),
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<flowy_database2::entities::DatabasePB>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_database_fields(&self, view_id: &str) -> RepeatedFieldPB {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::GetFields)
|
||||||
|
.payload(GetFieldPayloadPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
field_ids: None,
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<RepeatedFieldPB>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_field(&self, view_id: &str, field_type: FieldType) -> FieldPB {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::CreateTypeOption)
|
||||||
|
.payload(CreateFieldPayloadPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
field_type,
|
||||||
|
type_option_data: None,
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<TypeOptionPB>()
|
||||||
|
.field
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_field(&self, changeset: FieldChangesetPB) {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::UpdateField)
|
||||||
|
.payload(changeset)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::DeleteField)
|
||||||
|
.payload(DeleteFieldPayloadPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
field_id: field_id.to_string(),
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_field_type(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
field_id: &str,
|
||||||
|
field_type: FieldType,
|
||||||
|
) -> Option<FlowyError> {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::UpdateFieldType)
|
||||||
|
.payload(UpdateFieldTypePayloadPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
field_id: field_id.to_string(),
|
||||||
|
field_type,
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn duplicate_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::DuplicateField)
|
||||||
|
.payload(DuplicateFieldPayloadPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
field_id: field_id.to_string(),
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_row(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
start_row_id: Option<String>,
|
||||||
|
data: Option<RowDataPB>,
|
||||||
|
) -> RowPB {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::CreateRow)
|
||||||
|
.payload(CreateRowPayloadPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
start_row_id,
|
||||||
|
group_id: None,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<RowPB>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_row(&self, view_id: &str, row_id: &str) -> RowPB {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::GetRow)
|
||||||
|
.payload(RowIdPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
row_id: row_id.to_string(),
|
||||||
|
group_id: None,
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<RowPB>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::DuplicateRow)
|
||||||
|
.payload(RowIdPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
row_id: row_id.to_string(),
|
||||||
|
group_id: None,
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_cell(&self, changeset: CellChangesetPB) -> Option<FlowyError> {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::UpdateCell)
|
||||||
|
.payload(changeset)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> CellPB {
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::GetCell)
|
||||||
|
.payload(CellIdPB {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
row_id: row_id.to_string(),
|
||||||
|
field_id: field_id.to_string(),
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<CellPB>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn insert_option(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
field_id: &str,
|
||||||
|
row_id: &str,
|
||||||
|
name: &str,
|
||||||
|
) -> Option<FlowyError> {
|
||||||
|
let option = EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::CreateSelectOption)
|
||||||
|
.payload(CreateSelectOptionPayloadPB {
|
||||||
|
field_id: field_id.to_string(),
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
option_name: name.to_string(),
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<SelectOptionPB>();
|
||||||
|
|
||||||
|
EventBuilder::new(self.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::InsertOrUpdateSelectOption)
|
||||||
|
.payload(RepeatedSelectOptionPayload {
|
||||||
|
view_id: view_id.to_string(),
|
||||||
|
field_id: field_id.to_string(),
|
||||||
|
row_id: row_id.to_string(),
|
||||||
|
items: vec![option],
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.error()
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -1 +1,291 @@
|
|||||||
|
use bytes::Bytes;
|
||||||
|
use flowy_database2::entities::{
|
||||||
|
CellChangesetPB, DatabaseLayoutPB, DatabaseViewIdPB, FieldType, SelectOptionCellDataPB,
|
||||||
|
};
|
||||||
|
use flowy_test::event_builder::EventBuilder;
|
||||||
|
use flowy_test::FlowyCoreTest;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_database_id_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// The view id can be used to get the database id.
|
||||||
|
let database_id = EventBuilder::new(test.clone())
|
||||||
|
.event(flowy_database2::event_map::DatabaseEvent::GetDatabaseId)
|
||||||
|
.payload(DatabaseViewIdPB {
|
||||||
|
value: grid_view.id.clone(),
|
||||||
|
})
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<flowy_database2::entities::DatabaseIdPB>()
|
||||||
|
.value;
|
||||||
|
|
||||||
|
assert_ne!(database_id, grid_view.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_database_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let database = test.get_database(&grid_view.id).await;
|
||||||
|
assert_eq!(database.fields.len(), 3);
|
||||||
|
assert_eq!(database.rows.len(), 3);
|
||||||
|
assert_eq!(database.layout_type, DatabaseLayoutPB::Grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_field_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
assert_eq!(fields[0].field_type, FieldType::RichText);
|
||||||
|
assert_eq!(fields[1].field_type, FieldType::SingleSelect);
|
||||||
|
assert_eq!(fields[2].field_type, FieldType::Checkbox);
|
||||||
|
assert_eq!(fields.len(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_field_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
test.create_field(&grid_view.id, FieldType::Checkbox).await;
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
assert_eq!(fields.len(), 4);
|
||||||
|
assert_eq!(fields[3].field_type, FieldType::Checkbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn delete_field_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
assert_eq!(fields[0].field_type, FieldType::RichText);
|
||||||
|
assert_eq!(fields[1].field_type, FieldType::SingleSelect);
|
||||||
|
assert_eq!(fields[2].field_type, FieldType::Checkbox);
|
||||||
|
|
||||||
|
let error = test.delete_field(&grid_view.id, &fields[1].id).await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
assert_eq!(fields.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn delete_primary_field_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
// the primary field is not allowed to be deleted.
|
||||||
|
assert!(fields[0].is_primary);
|
||||||
|
let error = test.delete_field(&grid_view.id, &fields[0].id).await;
|
||||||
|
assert!(error.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_field_type_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
let error = test
|
||||||
|
.update_field_type(&grid_view.id, &fields[1].id, FieldType::Checklist)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
assert_eq!(fields[1].field_type, FieldType::Checklist);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_primary_field_type_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
// the primary field is not allowed to be deleted.
|
||||||
|
assert!(fields[0].is_primary);
|
||||||
|
|
||||||
|
// the primary field is not allowed to be updated.
|
||||||
|
let error = test
|
||||||
|
.update_field_type(&grid_view.id, &fields[0].id, FieldType::Checklist)
|
||||||
|
.await;
|
||||||
|
assert!(error.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn duplicate_field_event_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
// the primary field is not allowed to be updated.
|
||||||
|
let error = test.duplicate_field(&grid_view.id, &fields[1].id).await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
assert_eq!(fields.len(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn duplicate_primary_field_test() {
|
||||||
|
let test = FlowyCoreTest::new_with_user().await;
|
||||||
|
let current_workspace = test.get_current_workspace().await.workspace;
|
||||||
|
let grid_view = test
|
||||||
|
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
// the primary field is not allowed to be duplicated.
|
||||||
|
let error = test.duplicate_field(&grid_view.id, &fields[0].id).await;
|
||||||
|
assert!(error.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_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(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let _ = test.create_row(&grid_view.id, None, None).await;
|
||||||
|
let database = test.get_database(&grid_view.id).await;
|
||||||
|
assert_eq!(database.rows.len(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn duplicate_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(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
let database = test.get_database(&grid_view.id).await;
|
||||||
|
let error = test
|
||||||
|
.duplicate_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(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_text_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(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
let database = test.get_database(&grid_view.id).await;
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
|
||||||
|
let row_id = database.rows[0].id.clone();
|
||||||
|
let field_id = fields[0].id.clone();
|
||||||
|
assert_eq!(fields[0].field_type, FieldType::RichText);
|
||||||
|
|
||||||
|
// Update the first cell of the first row.
|
||||||
|
let error = test
|
||||||
|
.update_cell(CellChangesetPB {
|
||||||
|
view_id: grid_view.id.clone(),
|
||||||
|
row_id: row_id.clone(),
|
||||||
|
field_id: field_id.clone(),
|
||||||
|
cell_changeset: "hello world".to_string(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let cell = test.get_cell(&grid_view.id, &row_id, &field_id).await;
|
||||||
|
let s = String::from_utf8(cell.data).unwrap();
|
||||||
|
assert_eq!(s, "hello world");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_checkbox_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(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
let database = test.get_database(&grid_view.id).await;
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
|
||||||
|
let row_id = database.rows[0].id.clone();
|
||||||
|
let field_id = fields[2].id.clone();
|
||||||
|
assert_eq!(fields[2].field_type, FieldType::Checkbox);
|
||||||
|
|
||||||
|
for input in vec!["yes", "true", "1"] {
|
||||||
|
let error = test
|
||||||
|
.update_cell(CellChangesetPB {
|
||||||
|
view_id: grid_view.id.clone(),
|
||||||
|
row_id: row_id.clone(),
|
||||||
|
field_id: field_id.clone(),
|
||||||
|
cell_changeset: input.to_string(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
let cell = test.get_cell(&grid_view.id, &row_id, &field_id).await;
|
||||||
|
let output = String::from_utf8(cell.data).unwrap();
|
||||||
|
assert_eq!(output, "Yes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_single_select_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(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||||
|
.await;
|
||||||
|
let database = test.get_database(&grid_view.id).await;
|
||||||
|
let fields = test.get_all_database_fields(&grid_view.id).await.items;
|
||||||
|
let row_id = database.rows[0].id.clone();
|
||||||
|
let field_id = fields[1].id.clone();
|
||||||
|
assert_eq!(fields[1].field_type, FieldType::SingleSelect);
|
||||||
|
|
||||||
|
let error = test
|
||||||
|
.insert_option(&grid_view.id, &field_id, &row_id, "task 1")
|
||||||
|
.await;
|
||||||
|
assert!(error.is_none());
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
assert_eq!(select_option_cell.options.len(), 1);
|
||||||
|
assert_eq!(select_option_cell.select_options.len(), 1);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user