refactor: remove singleton db (#4208)

* refactor: remove singleton db

* chore: fix warning

* chore: fix warning

* chore: update test

* chore: only resotre or backup when init call

* test: fix

* test: fix

* test: fix

* fix: timeout notification

* chore: rename

* chore: rename

* chore: disable test

* chore: remove log

* chore: remove log

* chore: add log

* chore: rename test functions

* chore: add test asset

* chore: bump client api

* chore: disable some tests
This commit is contained in:
Nathan.fooo 2023-12-27 11:42:39 +08:00 committed by GitHub
parent 121ed5a06e
commit df8409178b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 947 additions and 829 deletions

View File

@ -25,7 +25,7 @@ env:
jobs: jobs:
test-on-ubuntu: test-on-ubuntu:
environment: SUPABASE_CI # environment: SUPABASE_CI
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout source code - name: Checkout source code
@ -52,21 +52,21 @@ jobs:
workspaces: | workspaces: |
frontend/rust-lib frontend/rust-lib
- name: Create .env file in flowy-server # - name: Create .env file in flowy-server
working-directory: frontend/rust-lib/flowy-server # working-directory: frontend/rust-lib/flowy-server
run: | # run: |
touch .env.ci # touch .env.ci
echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci # echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci # echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci # echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
#
- name: Create .env file in event-integration # - name: Create .env file in event-integration
working-directory: frontend/rust-lib/event-integration # working-directory: frontend/rust-lib/event-integration
run: | # run: |
touch .env.ci # touch .env.ci
echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci # echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci # echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci # echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
- name: Checkout appflowy cloud code - name: Checkout appflowy cloud code
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -86,11 +86,11 @@ jobs:
sed -i 's/GOTRUE_SMTP_ADMIN_EMAIL=.*/GOTRUE_SMTP_ADMIN_EMAIL=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_ADMIN_EMAIL }}/' .env sed -i 's/GOTRUE_SMTP_ADMIN_EMAIL=.*/GOTRUE_SMTP_ADMIN_EMAIL=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_ADMIN_EMAIL }}/' .env
sed -i 's/GOTRUE_EXTERNAL_GOOGLE_ENABLED=.*/GOTRUE_EXTERNAL_GOOGLE_ENABLED=true/' .env sed -i 's/GOTRUE_EXTERNAL_GOOGLE_ENABLED=.*/GOTRUE_EXTERNAL_GOOGLE_ENABLED=true/' .env
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
cat .env
- name: Run Docker-Compose - name: Run Docker-Compose
working-directory: AppFlowy-Cloud working-directory: AppFlowy-Cloud
run: | run: |
docker compose down -v --remove-orphans
docker compose up -d docker compose up -d
- name: Run rust-lib tests - name: Run rust-lib tests

View File

@ -16,7 +16,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.appearance); await tester.openSettingsPage(SettingsPage.appearance);
@ -48,7 +48,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.appearance); await tester.openSettingsPage(SettingsPage.appearance);

View File

@ -21,7 +21,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final findFirstCard = find.descendant( final findFirstCard = find.descendant(
of: find.byType(AppFlowyGroupCard), of: find.byType(AppFlowyGroupCard),
@ -64,7 +64,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final findLastCard = find.descendant( final findLastCard = find.descendant(
of: find.byType(AppFlowyGroupCard), of: find.byType(AppFlowyGroupCard),

View File

@ -17,7 +17,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final card1 = find.ancestor( final card1 = find.ancestor(
of: find.findTextInFlowyText(card1Name), of: find.findTextInFlowyText(card1Name),
matching: find.byType(AppFlowyGroupCard), matching: find.byType(AppFlowyGroupCard),

View File

@ -17,7 +17,7 @@ void main() {
testWidgets('expand/collapse hidden groups', (tester) async { testWidgets('expand/collapse hidden groups', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final collapseFinder = find.byFlowySvg(FlowySvgs.pull_left_outlined_s); final collapseFinder = find.byFlowySvg(FlowySvgs.pull_left_outlined_s);
final expandFinder = find.byFlowySvg(FlowySvgs.hamburger_s_s); final expandFinder = find.byFlowySvg(FlowySvgs.hamburger_s_s);
@ -46,7 +46,7 @@ void main() {
testWidgets('hide first group, and show it again', (tester) async { testWidgets('hide first group, and show it again', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
// Tap the options of the first group // Tap the options of the first group
final optionsFinder = find final optionsFinder = find
@ -82,7 +82,7 @@ void main() {
testWidgets('delete a group', (tester) async { testWidgets('delete a group', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 4); expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 4);

View File

@ -18,7 +18,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
const name = 'Card 1'; const name = 'Card 1';
final card1 = find.findTextInFlowyText(name); final card1 = find.findTextInFlowyText(name);
await tester.hoverOnWidget( await tester.hoverOnWidget(
@ -36,7 +36,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
const name = 'Card 1'; const name = 'Card 1';
final card1 = find.findTextInFlowyText(name); final card1 = find.findTextInFlowyText(name);
await tester.hoverOnWidget( await tester.hoverOnWidget(
@ -53,7 +53,7 @@ void main() {
testWidgets('add new group', (tester) async { testWidgets('add new group', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Board); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
// assert number of groups // assert number of groups
tester.assertNumberOfGroups(4); tester.assertNumberOfGroups(4);

View File

@ -31,7 +31,7 @@ void main() {
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr()); tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
await tester.tapGoButton(); await tester.tapGoButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
// reanme the name of the anon user // reanme the name of the anon user
await tester.openSettings(); await tester.openSettings();
@ -41,13 +41,14 @@ void main() {
matching: find.byType(UserNameInput), matching: find.byType(UserNameInput),
); );
await tester.enterText(userNameFinder, 'local_user'); await tester.enterText(userNameFinder, 'local_user');
await tester.openSettingsPage(SettingsPage.user);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// sign up with Google // sign up with Google
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
await tester.pumpAndSettle();
// sign out // sign out
await tester.expectToSeeHomePage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user); await tester.openSettingsPage(SettingsPage.user);
await tester.logout(); await tester.logout();

View File

@ -1,74 +0,0 @@
// ignore_for_file: unused_import
import 'dart:io';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/sign_in_anonymous_button.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path/path.dart' as p;
import 'package:integration_test/integration_test.dart';
import '../util/database_test_op.dart';
import '../util/dir.dart';
import '../util/mock/mock_file_picker.dart';
import '../util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('appflowy cloud', () {
testWidgets('anon user -> af cloud -> continue anon', (tester) async {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloud,
);
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
await tester.tapGoButton();
await tester.expectToSeeHomePage();
// reanme the name of the anon user
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
final userNameFinder = find.descendant(
of: find.byType(SettingsUserView),
matching: find.byType(UserNameInput),
);
await tester.enterText(userNameFinder, 'local_user');
await tester.openSettingsPage(SettingsPage.user);
await tester.tapEscButton();
await tester.expectToSeeHomePage();
// sign up with Google
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePage();
// sign out
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.logout();
await tester.pumpAndSettle();
tester.expectToSeeText(LocaleKeys.signIn_continueAnonymousUser.tr());
// tap the continue as anonymous button
await tester.tapButton(find.byType(SignInAnonymousButton));
await tester.expectToSeeHomePage();
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
final userNameInput = tester.widget(userNameFinder) as UserNameInput;
expect(userNameInput.name, 'local_user');
});
});
}

View File

@ -25,7 +25,7 @@ void main() {
cloudType: AuthenticatorType.appflowyCloud, cloudType: AuthenticatorType.appflowyCloud,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
}); });
testWidgets('sign out', (tester) async { testWidgets('sign out', (tester) async {

View File

@ -1,17 +1,17 @@
import 'empty_test.dart' as preset_af_cloud_env_test; import 'empty_test.dart' as preset_af_cloud_env_test;
import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test; import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test;
import 'document_sync_test.dart' as document_sync_test; // import 'document_sync_test.dart' as document_sync_test;
import 'user_setting_sync_test.dart' as user_sync_test; import 'user_setting_sync_test.dart' as user_sync_test;
// import 'anon_user_continue_test.dart' as anon_user_continue_test; import 'anon_user_continue_test.dart' as anon_user_continue_test;
Future<void> main() async { Future<void> main() async {
preset_af_cloud_env_test.main(); preset_af_cloud_env_test.main();
appflowy_cloud_auth_test.main(); appflowy_cloud_auth_test.main();
document_sync_test.main(); // document_sync_test.main();
user_sync_test.main(); user_sync_test.main();
// anon_user_continue_test.main(); anon_user_continue_test.main();
} }

View File

@ -22,8 +22,8 @@ import '../util/util.dart';
void main() { void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); IntegrationTestWidgetsFlutterBinding.ensureInitialized();
const pageName = 'Sample';
final email = '${uuid()}@appflowy.io'; final email = '${uuid()}@appflowy.io';
const inputContent = 'Hello world, this is a test document';
// The test will create a new document called Sample, and sync it to the server. // The test will create a new document called Sample, and sync it to the server.
// Then the test will logout the user, and login with the same user. The data will // Then the test will logout the user, and login with the same user. The data will
@ -35,20 +35,21 @@ void main() {
email: email, email: email,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
// create a new document called Sample // create a new document called Sample
await tester.createNewPageWithName( await tester.createNewPage(
name: pageName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
// focus on the editor // focus on the editor
await tester.editor.tapLineOfEditorAt(0); await tester.editor.tapLineOfEditorAt(0);
await tester.ime.insertText('hello world'); await tester.ime.insertText(inputContent);
expect(find.text(inputContent, findRichText: true), findsOneWidget);
await tester.pumpAndSettle(); // TODO(nathan): remove the await
expect(find.text('hello world', findRichText: true), findsOneWidget); // 6 seconds for data sync
await tester.waitForSeconds(6);
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user); await tester.openSettingsPage(SettingsPage.user);
@ -62,15 +63,10 @@ void main() {
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.pumpAndSettle(const Duration(seconds: 2));
// The document will be synced from the server // the latest document will be opened, so the content must be the inputContent
await tester.openPage( await tester.pumpAndSettle();
pageName, expect(find.text(inputContent, findRichText: true), findsOneWidget);
);
await tester.pumpAndSettle(const Duration(seconds: 2));
expect(find.text('hello world', findRichText: true), findsOneWidget);
}); });
}); });
} }

View File

@ -15,7 +15,7 @@ void main() {
testWidgets('sign in with supabase', (tester) async { testWidgets('sign in with supabase', (tester) async {
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase); await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
}); });
testWidgets('sign out with supabase', (tester) async { testWidgets('sign out with supabase', (tester) async {

View File

@ -36,7 +36,7 @@ void main() {
email: email, email: email,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user); await tester.openSettingsPage(SettingsPage.user);
@ -74,7 +74,7 @@ void main() {
email: email, email: email,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.openSettings(); await tester.openSettings();

View File

@ -14,7 +14,9 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar); await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Calendar,
);
// open setting // open setting
await tester.tapDatabaseSettingButton(); await tester.tapDatabaseSettingButton();
@ -36,7 +38,7 @@ void main() {
// Create calendar view // Create calendar view
const name = 'calendar'; const name = 'calendar';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: name, name: name,
layout: ViewLayoutPB.Calendar, layout: ViewLayoutPB.Calendar,
); );
@ -68,7 +70,9 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create the calendar view // Create the calendar view
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar); await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Calendar,
);
// Scroll until today's date cell is visible // Scroll until today's date cell is visible
await tester.scrollToToday(); await tester.scrollToToday();
@ -148,7 +152,9 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create the calendar view // Create the calendar view
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar); await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Calendar,
);
// Create a new event on the first of this month // Create a new event on the first of this month
final today = DateTime.now(); final today = DateTime.now();

View File

@ -15,7 +15,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.editCell( await tester.editCell(
rowIndex: 0, rowIndex: 0,
@ -37,7 +37,7 @@ void main() {
testWidgets('multiple text cells', (tester) async { testWidgets('multiple text cells', (tester) async {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: 'my grid', name: 'my grid',
layout: ViewLayoutPB.Grid, layout: ViewLayoutPB.Grid,
); );
@ -77,7 +77,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.Number; const fieldType = FieldType.Number;
@ -135,7 +135,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.assertCheckboxCell(rowIndex: 0, isSelected: false); await tester.assertCheckboxCell(rowIndex: 0, isSelected: false);
await tester.tapCheckboxCellInGrid(rowIndex: 0); await tester.tapCheckboxCellInGrid(rowIndex: 0);
@ -153,7 +153,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.CreatedTime; const fieldType = FieldType.CreatedTime;
// Create a create time field // Create a create time field
@ -171,7 +171,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.LastEditedTime; const fieldType = FieldType.LastEditedTime;
// Create a last time field // Create a last time field
@ -189,7 +189,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.DateTime; const fieldType = FieldType.DateTime;
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType, fieldType.name);
@ -282,7 +282,7 @@ void main() {
const fieldType = FieldType.SingleSelect; const fieldType = FieldType.SingleSelect;
// When create a grid, it will create a single select field by default // When create a grid, it will create a single select field by default
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Tap the cell to invoke the selection option editor // Tap the cell to invoke the selection option editor
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType); await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
@ -358,7 +358,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.MultiSelect; const fieldType = FieldType.MultiSelect;
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType, fieldType.name);
@ -441,7 +441,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.Checklist; const fieldType = FieldType.Checklist;
await tester.createField(fieldType, fieldType.name); await tester.createField(fieldType, fieldType.name);

View File

@ -15,7 +15,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid); await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);
// create a field // create a field

View File

@ -17,7 +17,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Invoke the field editor // Invoke the field editor
await tester.tapGridFieldWithName('Name'); await tester.tapGridFieldWithName('Name');
@ -34,7 +34,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Invoke the field editor // Invoke the field editor
await tester.tapGridFieldWithName('Type'); await tester.tapGridFieldWithName('Type');
@ -56,7 +56,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new grid // create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field // create a field
await tester.createField(FieldType.Checklist, 'checklist'); await tester.createField(FieldType.Checklist, 'checklist');
@ -70,7 +70,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field // create a field
await tester.createField(FieldType.Checkbox, 'New field 1'); await tester.createField(FieldType.Checkbox, 'New field 1');
@ -90,7 +90,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field // create a field
await tester.scrollToRight(find.byType(GridPage)); await tester.scrollToRight(find.byType(GridPage));
@ -110,7 +110,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.scrollToRight(find.byType(GridPage)); await tester.scrollToRight(find.byType(GridPage));
@ -133,7 +133,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.scrollToRight(find.byType(GridPage)); await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton(); await tester.tapNewPropertyButton();
@ -153,7 +153,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
for (final fieldType in [ for (final fieldType in [
FieldType.Checklist, FieldType.Checklist,
@ -185,7 +185,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Grid, layout: ViewLayoutPB.Grid,
); );

View File

@ -20,7 +20,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -34,7 +34,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -53,7 +53,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -80,7 +80,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -101,7 +101,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -135,7 +135,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -168,7 +168,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -216,7 +216,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -231,7 +231,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -271,7 +271,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -310,7 +310,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();
@ -327,7 +327,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// Create a new grid // Create a new grid
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Hover first row and then open the row page // Hover first row and then open the row page
await tester.openFirstRowDetailPage(); await tester.openFirstRowDetailPage();

View File

@ -13,7 +13,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.tapCreateRowButtonInGrid(); await tester.tapCreateRowButtonInGrid();
// The initial number of rows is 3 // The initial number of rows is 3
@ -25,7 +25,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.hoverOnFirstRowOfGrid(); await tester.hoverOnFirstRowOfGrid();
@ -41,7 +41,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.hoverOnFirstRowOfGrid(); await tester.hoverOnFirstRowOfGrid();
// Open the row menu and then click the delete // Open the row menu and then click the delete
@ -59,7 +59,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.assertRowCountInGridPage(3); await tester.assertRowCountInGridPage(3);
await tester.pumpAndSettle(); await tester.pumpAndSettle();

View File

@ -14,7 +14,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// open setting // open setting
await tester.tapDatabaseSettingButton(); await tester.tapDatabaseSettingButton();
@ -31,7 +31,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// open setting // open setting
await tester.tapDatabaseSettingButton(); await tester.tapDatabaseSettingButton();

View File

@ -14,7 +14,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Create board view // Create board view
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board); await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
@ -35,7 +35,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Create board view // Create board view
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board); await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
@ -60,7 +60,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid); await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// Create board view // Create board view
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board); await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);

View File

@ -18,7 +18,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName(); await tester.createNewPageWithNameUnderParent();
// mock the clipboard // mock the clipboard
const lines = 3; const lines = 3;

View File

@ -307,7 +307,7 @@ extension on WidgetTester {
await tapGoButton(); await tapGoButton();
// create a new document // create a new document
await createNewPageWithName(); await createNewPageWithNameUnderParent();
await beforeTest?.call(editor.getCurrentEditorState()); await beforeTest?.call(editor.getCurrentEditorState());

View File

@ -17,7 +17,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName(); await tester.createNewPageWithNameUnderParent();
// expect to see a new document // expect to see a new document
tester.expectToSeePageName( tester.expectToSeePageName(

View File

@ -86,7 +86,7 @@ void main() {
Future<String> createDocumentToReference(WidgetTester tester) async { Future<String> createDocumentToReference(WidgetTester tester) async {
final name = 'document_${uuid()}'; final name = 'document_${uuid()}';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: name, name: name,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
openAfterCreated: false, openAfterCreated: false,

View File

@ -32,7 +32,7 @@ void main() {
await tester.tapAt(Offset.zero); await tester.tapAt(Offset.zero);
await tester.tapAt(Offset.zero); await tester.tapAt(Offset.zero);
await tester.createNewPageWithName(name: 'test'); await tester.createNewPageWithNameUnderParent(name: 'test');
await tester.openPage(gettingStarted); await tester.openPage(gettingStarted);
// check the status again // check the status again

View File

@ -136,13 +136,13 @@ Future<void> insertReferenceDatabase(
// create a new grid // create a new grid
final id = uuid(); final id = uuid();
final name = '${layout.name}_$id'; final name = '${layout.name}_$id';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: name, name: name,
layout: layout, layout: layout,
openAfterCreated: false, openAfterCreated: false,
); );
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: 'insert_a_reference_${layout.name}', name: 'insert_a_reference_${layout.name}',
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
openAfterCreated: true, openAfterCreated: true,
@ -171,7 +171,7 @@ Future<void> createInlineDatabase(
) async { ) async {
// create a new document // create a new document
final documentName = 'insert_a_inline_${layout.name}'; final documentName = 'insert_a_inline_${layout.name}';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: documentName, name: documentName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
openAfterCreated: true, openAfterCreated: true,

View File

@ -31,7 +31,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: LocaleKeys.document_plugins_image_addAnImage.tr(), name: LocaleKeys.document_plugins_image_addAnImage.tr(),
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -79,7 +79,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: LocaleKeys.document_plugins_image_addAnImage.tr(), name: LocaleKeys.document_plugins_image_addAnImage.tr(),
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -133,7 +133,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: LocaleKeys.document_plugins_image_addAnImage.tr(), name: LocaleKeys.document_plugins_image_addAnImage.tr(),
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );

View File

@ -20,7 +20,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: 'math equation', name: 'math equation',
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -66,7 +66,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: 'math equation', name: 'math equation',
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );

View File

@ -105,14 +105,14 @@ Future<String> insertInlinePage(
// create a new grid // create a new grid
final id = uuid(); final id = uuid();
final name = '${layout.name}_$id'; final name = '${layout.name}_$id';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: name, name: name,
layout: layout, layout: layout,
openAfterCreated: false, openAfterCreated: false,
); );
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: 'insert_a_inline_page_${layout.name}', name: 'insert_a_inline_page_${layout.name}',
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );

View File

@ -24,7 +24,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );

View File

@ -15,7 +15,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: 'outline_test', name: 'outline_test',
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -32,7 +32,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: 'outline_test', name: 'outline_test',
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );

View File

@ -40,7 +40,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -86,7 +86,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -125,7 +125,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -162,7 +162,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -196,7 +196,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// create a new document // create a new document
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );

View File

@ -18,7 +18,7 @@ void main() {
// create a new document called Sample // create a new document called Sample
const pageName = 'Sample'; const pageName = 'Sample';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: pageName, name: pageName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
@ -75,7 +75,7 @@ void main() {
// create a new document called Sample // create a new document called Sample
const pageName = 'Sample'; const pageName = 'Sample';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: pageName, name: pageName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );

View File

@ -19,7 +19,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.appearance); await tester.openSettingsPage(SettingsPage.appearance);
@ -71,7 +71,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
await tester.pumpAndSettle(); await tester.pumpAndSettle();

View File

@ -14,7 +14,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.language); await tester.openSettingsPage(SettingsPage.language);

View File

@ -31,7 +31,7 @@ void main() {
].map((e) => 'document_$e').toList(); ].map((e) => 'document_$e').toList();
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
final parentName = i == 0 ? gettingStarted : names[i - 1]; final parentName = i == 0 ? gettingStarted : names[i - 1];
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: names[i], name: names[i],
parentName: parentName, parentName: parentName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
@ -113,7 +113,7 @@ void main() {
final names = [1, 2].map((e) => 'document_$e').toList(); final names = [1, 2].map((e) => 'document_$e').toList();
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
final parentName = i == 0 ? gettingStarted : names[i - 1]; final parentName = i == 0 ? gettingStarted : names[i - 1];
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: names[i], name: names[i],
parentName: parentName, parentName: parentName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
@ -170,7 +170,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(); await tester.createNewPageWithNameUnderParent();
await tester.favoriteViewByName(gettingStarted); await tester.favoriteViewByName(gettingStarted);
expect( expect(
find.byWidgetPredicate( find.byWidgetPredicate(
@ -190,7 +190,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.createNewPageWithName(); await tester.createNewPageWithNameUnderParent();
await tester.favoriteViewByName(gettingStarted); await tester.favoriteViewByName(gettingStarted);
await tester.hoverOnPageName( await tester.hoverOnPageName(
gettingStarted, gettingStarted,

View File

@ -18,7 +18,7 @@ void main() {
// create document, board, grid and calendar views // create document, board, grid and calendar views
for (final value in ViewLayoutPB.values) { for (final value in ViewLayoutPB.values) {
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: value.name, name: value.name,
parentName: gettingStarted, parentName: gettingStarted,
layout: value, layout: value,
@ -46,7 +46,7 @@ void main() {
// create document, board, grid and calendar views // create document, board, grid and calendar views
for (final value in ViewLayoutPB.values) { for (final value in ViewLayoutPB.values) {
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: value.name, name: value.name,
parentName: gettingStarted, parentName: gettingStarted,
layout: value, layout: value,

View File

@ -41,7 +41,7 @@ void main() {
for (final layout in ViewLayoutPB.values) { for (final layout in ViewLayoutPB.values) {
// create a new page // create a new page
final name = 'AppFlowy_$layout'; final name = 'AppFlowy_$layout';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: name, name: name,
layout: layout, layout: layout,
); );
@ -79,7 +79,7 @@ void main() {
final names = [1, 2, 3, 4].map((e) => 'document_$e').toList(); final names = [1, 2, 3, 4].map((e) => 'document_$e').toList();
for (var i = 0; i < names.length; i++) { for (var i = 0; i < names.length; i++) {
final parentName = i == 0 ? gettingStarted : names[i - 1]; final parentName = i == 0 ? gettingStarted : names[i - 1];
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: names[i], name: names[i],
parentName: parentName, parentName: parentName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
@ -144,14 +144,14 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
const document = 'document'; const document = 'document';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: document, name: document,
openAfterCreated: false, openAfterCreated: false,
); );
tester.expectToSeePageName(document, layout: ViewLayoutPB.Document); tester.expectToSeePageName(document, layout: ViewLayoutPB.Document);
const grid = 'grid'; const grid = 'grid';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: grid, name: grid,
layout: ViewLayoutPB.Grid, layout: ViewLayoutPB.Grid,
openAfterCreated: false, openAfterCreated: false,
@ -187,7 +187,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
const grid = 'grid'; const grid = 'grid';
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: grid, name: grid,
layout: ViewLayoutPB.Grid, layout: ViewLayoutPB.Grid,
openAfterCreated: true, openAfterCreated: true,

View File

@ -33,7 +33,7 @@ void main() {
); );
await tester.tapGoButton(); await tester.tapGoButton();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
// switch to user B // switch to user B
{ {
@ -51,7 +51,7 @@ void main() {
); );
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
// set user name for userB // set user name for userB
await tester.openSettings(); await tester.openSettings();
@ -71,7 +71,7 @@ void main() {
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
tester.expectToSeeUserName(userA); tester.expectToSeeUserName(userA);
} }
@ -88,7 +88,7 @@ void main() {
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
tester.expectToSeeUserName(userB); tester.expectToSeeUserName(userB);
} }
}); });
@ -99,7 +99,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// home and readme document // home and readme document
await tester.expectToSeeHomePage(); await tester.expectToSeeHomePageWithGetStartedPage();
// open settings and restore the location // open settings and restore the location
await tester.openSettings(); await tester.openSettings();

View File

@ -32,12 +32,12 @@ void main() {
findsNothing, findsNothing,
); );
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: _documentName, name: _documentName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );
await tester.createNewPageWithName( await tester.createNewPageWithNameUnderParent(
name: _documentTwoName, name: _documentTwoName,
layout: ViewLayoutPB.Document, layout: ViewLayoutPB.Document,
); );

View File

@ -290,7 +290,7 @@ extension CommonOperations on WidgetTester {
await tapButton(markdownButton); await tapButton(markdownButton);
} }
Future<void> createNewPageWithName({ Future<void> createNewPageWithNameUnderParent({
String? name, String? name,
ViewLayoutPB layout = ViewLayoutPB.Document, ViewLayoutPB layout = ViewLayoutPB.Document,
String? parentName, String? parentName,
@ -333,6 +333,13 @@ extension CommonOperations on WidgetTester {
} }
} }
Future<void> createNewPage({
ViewLayoutPB layout = ViewLayoutPB.Document,
bool openAfterCreated = true,
}) async {
await tapButton(find.byType(SidebarNewPageButton));
}
Future<void> simulateKeyEvent( Future<void> simulateKeyEvent(
LogicalKeyboardKey key, { LogicalKeyboardKey key, {
bool isControlPressed = false, bool isControlPressed = false,

View File

@ -18,7 +18,7 @@ const String gettingStarted = 'Getting started';
extension Expectation on WidgetTester { extension Expectation on WidgetTester {
/// Expect to see the home page and with a default read me page. /// Expect to see the home page and with a default read me page.
Future<void> expectToSeeHomePage() async { Future<void> expectToSeeHomePageWithGetStartedPage() async {
final finder = find.byType(HomeStack); final finder = find.byType(HomeStack);
await pumpUntilFound(finder); await pumpUntilFound(finder);
expect(finder, findsOneWidget); expect(finder, findsOneWidget);
@ -27,6 +27,12 @@ extension Expectation on WidgetTester {
await pumpUntilFound(docFinder); await pumpUntilFound(docFinder);
} }
Future<void> expectToSeeHomePage() async {
final finder = find.byType(HomeStack);
await pumpUntilFound(finder);
expect(finder, findsOneWidget);
}
/// Expect to see the page name on the home page. /// Expect to see the page name on the home page.
void expectToSeePageName( void expectToSeePageName(
String name, { String name, {

View File

@ -49,7 +49,6 @@ class NetworkListener {
return NetworkTypePB.NetworkUnknown; return NetworkTypePB.NetworkUnknown;
} }
}(); }();
Log.info("Network type: $networkType");
final state = NetworkStatePB.create()..ty = networkType; final state = NetworkStatePB.create()..ty = networkType;
UserEventUpdateNetworkState(state).send().then((result) { UserEventUpdateNetworkState(state).send().then((result) {
result.fold( result.fold(

View File

@ -105,7 +105,7 @@ enum AuthenticatorType {
supabase, supabase,
appflowyCloud; appflowyCloud;
bool get isEnabled => this != AuthenticatorType.local; bool get isLocal => this == AuthenticatorType.local;
int get value { int get value {
switch (this) { switch (this) {
case AuthenticatorType.local: case AuthenticatorType.local:
@ -161,8 +161,12 @@ class AppFlowyCloudSharedEnv {
if (Env.enableCustomCloud) { if (Env.enableCustomCloud) {
// Use the custom cloud configuration. // Use the custom cloud configuration.
final cloudType = await getAuthenticatorType(); final cloudType = await getAuthenticatorType();
final appflowyCloudConfig = await getAppFlowyCloudConfig(); final appflowyCloudConfig = cloudType.isLocal
final supabaseCloudConfig = await getSupabaseCloudConfig(); ? AppFlowyCloudConfiguration.defaultConfig()
: await getAppFlowyCloudConfig();
final supabaseCloudConfig = cloudType.isLocal
? SupabaseConfiguration.defaultConfig()
: await getSupabaseCloudConfig();
return AppFlowyCloudSharedEnv( return AppFlowyCloudSharedEnv(
authenticatorType: cloudType, authenticatorType: cloudType,
@ -184,6 +188,13 @@ class AppFlowyCloudSharedEnv {
); );
} }
} }
@override
String toString() {
return 'authenticator: $_authenticatorType\n'
'appflowy: ${appflowyCloudConfig.toJson()}\n'
'supabase: ${supabaseConfig.toJson()})\n';
}
} }
Future<AppFlowyCloudConfiguration> configurationFromUri( Future<AppFlowyCloudConfiguration> configurationFromUri(

View File

@ -28,9 +28,10 @@ import 'package:appflowy/workspace/application/user/prelude.dart';
import 'package:appflowy/workspace/application/view/prelude.dart'; import 'package:appflowy/workspace/application/view/prelude.dart';
import 'package:appflowy/workspace/application/workspace/prelude.dart'; import 'package:appflowy/workspace/application/workspace/prelude.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart'; import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:flowy_infra/file_picker/file_picker_impl.dart'; import 'package:flowy_infra/file_picker/file_picker_impl.dart';
import 'package:flowy_infra/file_picker/file_picker_service.dart'; import 'package:flowy_infra/file_picker/file_picker_service.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
@ -55,6 +56,7 @@ class DependencyResolver {
Future<void> _resolveCloudDeps(GetIt getIt) async { Future<void> _resolveCloudDeps(GetIt getIt) async {
final env = await AppFlowyCloudSharedEnv.fromEnv(); final env = await AppFlowyCloudSharedEnv.fromEnv();
Log.info("cloud setting: \n$env");
getIt.registerFactory<AppFlowyCloudSharedEnv>(() => env); getIt.registerFactory<AppFlowyCloudSharedEnv>(() => env);
if (isAppFlowyCloudEnabled) { if (isAppFlowyCloudEnabled) {

View File

@ -4,7 +4,6 @@ import 'dart:io';
import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart'; import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy_backend/appflowy_backend.dart'; import 'package:appflowy_backend/appflowy_backend.dart';
import 'package:appflowy_backend/log.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
@ -139,9 +138,14 @@ Future<void> initGetIt(
LaunchConfiguration config, LaunchConfiguration config,
) async { ) async {
getIt.registerFactory<EntryPoint>(() => f); getIt.registerFactory<EntryPoint>(() => f);
getIt.registerLazySingleton<FlowySDK>(() { getIt.registerLazySingleton<FlowySDK>(
() {
return FlowySDK(); return FlowySDK();
}); },
dispose: (sdk) async {
await sdk.dispose();
},
);
getIt.registerLazySingleton<AppLauncher>( getIt.registerLazySingleton<AppLauncher>(
() => AppLauncher( () => AppLauncher(
context: LaunchContext( context: LaunchContext(
@ -205,7 +209,6 @@ class AppLauncher {
} }
Future<void> dispose() async { Future<void> dispose() async {
Log.info('AppLauncher dispose');
for (final task in tasks) { for (final task in tasks) {
await task.dispose(); await task.dispose();
} }

View File

@ -87,7 +87,7 @@ class AppFlowyCloudDeepLink {
(_) async { (_) async {
final deviceId = await getDeviceId(); final deviceId = await getDeviceId();
final payload = OauthSignInPB( final payload = OauthSignInPB(
authType: AuthenticatorPB.AppFlowyCloud, authenticator: AuthenticatorPB.AppFlowyCloud,
map: { map: {
AuthServiceMapKeys.signInURL: uri.toString(), AuthServiceMapKeys.signInURL: uri.toString(),
AuthServiceMapKeys.deviceId: deviceId, AuthServiceMapKeys.deviceId: deviceId,

View File

@ -5,6 +5,7 @@ import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/application/auth/device_id.dart'; import 'package:appflowy/user/application/auth/device_id.dart';
import 'package:appflowy/user/application/user_service.dart'; import 'package:appflowy/user/application/user_service.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
@ -45,7 +46,7 @@ class AppFlowyCloudMockAuthService implements AuthService {
Map<String, String> params = const {}, Map<String, String> params = const {},
}) async { }) async {
final payload = SignInUrlPayloadPB.create() final payload = SignInUrlPayloadPB.create()
..authType = AuthenticatorPB.AppFlowyCloud ..authenticator = AuthenticatorPB.AppFlowyCloud
// don't use nanoid here, the gotrue server will transform the email // don't use nanoid here, the gotrue server will transform the email
..email = userEmail; ..email = userEmail;
@ -55,17 +56,22 @@ class AppFlowyCloudMockAuthService implements AuthService {
return getSignInURLResult.fold( return getSignInURLResult.fold(
(urlPB) async { (urlPB) async {
final payload = OauthSignInPB( final payload = OauthSignInPB(
authType: AuthenticatorPB.AppFlowyCloud, authenticator: AuthenticatorPB.AppFlowyCloud,
map: { map: {
AuthServiceMapKeys.signInURL: urlPB.signInUrl, AuthServiceMapKeys.signInURL: urlPB.signInUrl,
AuthServiceMapKeys.deviceId: deviceId, AuthServiceMapKeys.deviceId: deviceId,
}, },
); );
return await UserEventOauthSignIn(payload) Log.info("UserEventOauthSignIn with payload: $payload");
.send() return await UserEventOauthSignIn(payload).send().then((value) {
.then((value) => value.swap()); value.fold((l) => null, (err) => Log.error(err));
return value.swap();
});
},
(r) {
Log.error(r);
return left(r);
}, },
(r) => left(r),
); );
} }

View File

@ -171,7 +171,7 @@ class SupabaseAuthService implements AuthService {
required Map<String, String> map, required Map<String, String> map,
}) async { }) async {
final payload = OauthSignInPB( final payload = OauthSignInPB(
authType: AuthenticatorPB.Supabase, authenticator: AuthenticatorPB.Supabase,
map: map, map: map,
); );

View File

@ -67,7 +67,7 @@ class SupabaseMockAuthService implements AuthService {
// Create the OAuth sign-in payload. // Create the OAuth sign-in payload.
final payload = OauthSignInPB( final payload = OauthSignInPB(
authType: AuthenticatorPB.Supabase, authenticator: AuthenticatorPB.Supabase,
map: { map: {
AuthServiceMapKeys.uuid: uuid, AuthServiceMapKeys.uuid: uuid,
AuthServiceMapKeys.email: email, AuthServiceMapKeys.email: email,

View File

@ -1,5 +1,4 @@
import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/env/env.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:appflowy/user/application/auth/auth_service.dart';
@ -95,9 +94,6 @@ class SplashScreen extends StatelessWidget {
} }
void _handleUnauthenticated(BuildContext context, Unauthenticated result) { void _handleUnauthenticated(BuildContext context, Unauthenticated result) {
Log.trace(
"_handleUnauthenticated -> enable custom cloud: ${Env.enableCustomCloud}, appflowy cloud is enabled: $isAppFlowyCloudEnabled, appflowy cloud config: ${getIt<AppFlowyCloudSharedEnv>().appflowyCloudConfig.toJson()}",
);
// replace Splash screen as root page // replace Splash screen as root page
if (isAuthEnabled || PlatformExtension.isMobile) { if (isAuthEnabled || PlatformExtension.isMobile) {
context.go(SignInScreen.routeName); context.go(SignInScreen.routeName);

View File

@ -7,7 +7,6 @@ import 'package:appflowy/workspace/application/favorite/favorite_listener.dart';
import 'package:appflowy/workspace/application/recent/recent_service.dart'; import 'package:appflowy/workspace/application/recent/recent_service.dart';
import 'package:appflowy/workspace/application/view/view_listener.dart'; import 'package:appflowy/workspace/application/view/view_listener.dart';
import 'package:appflowy/workspace/application/view/view_service.dart'; import 'package:appflowy/workspace/application/view/view_service.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
@ -256,9 +255,6 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
Future<ViewPB?> _updateChildViews( Future<ViewPB?> _updateChildViews(
ChildViewUpdatePB update, ChildViewUpdatePB update,
) async { ) async {
Log.debug(
'received child views of ${this.view.name}(${this.view.id}) update, $update',
);
if (update.createChildViews.isNotEmpty) { if (update.createChildViews.isNotEmpty) {
// refresh the child views if the update isn't empty // refresh the child views if the update isn't empty
// because there's no info to get the inserted index. // because there's no info to get the inserted index.

View File

@ -24,7 +24,7 @@ class FlowySDK {
FlowySDK(); FlowySDK();
void dispose() {} Future<void> dispose() async {}
Future<void> init(String configuration) async { Future<void> init(String configuration) async {
final port = RustStreamReceiver.shared.port; final port = RustStreamReceiver.shared.port;

View File

@ -139,7 +139,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]] [[package]]
name = "app-error" name = "app-error"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -786,7 +786,7 @@ dependencies = [
[[package]] [[package]]
name = "client-api" name = "client-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -1325,7 +1325,7 @@ dependencies = [
"cssparser-macros", "cssparser-macros",
"dtoa-short", "dtoa-short",
"itoa 1.0.6", "itoa 1.0.6",
"phf 0.11.2", "phf 0.8.0",
"smallvec", "smallvec",
] ]
@ -1471,7 +1471,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]] [[package]]
name = "database-entity" name = "database-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2842,7 +2842,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue" name = "gotrue"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2858,7 +2858,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue-entity" name = "gotrue-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -3280,7 +3280,7 @@ dependencies = [
[[package]] [[package]]
name = "infra" name = "infra"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -4378,7 +4378,6 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [ dependencies = [
"phf_macros 0.11.2",
"phf_shared 0.11.2", "phf_shared 0.11.2",
] ]
@ -4470,19 +4469,6 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator 0.11.2",
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 2.0.32",
]
[[package]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.8.0" version = "0.8.0"
@ -4724,7 +4710,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
dependencies = [ dependencies = [
"bytes", "bytes",
"heck 0.4.1", "heck 0.4.1",
"itertools 0.11.0", "itertools 0.10.5",
"log", "log",
"multimap", "multimap",
"once_cell", "once_cell",
@ -4745,7 +4731,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"itertools 0.11.0", "itertools 0.10.5",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.32", "syn 2.0.32",
@ -5040,7 +5026,7 @@ dependencies = [
[[package]] [[package]]
name = "realtime-entity" name = "realtime-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -5062,7 +5048,7 @@ dependencies = [
[[package]] [[package]]
name = "realtime-protocol" name = "realtime-protocol"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -5809,7 +5795,7 @@ dependencies = [
[[package]] [[package]]
name = "shared_entity" name = "shared_entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -7699,7 +7685,7 @@ dependencies = [
[[package]] [[package]]
name = "workspace-template" name = "workspace-template"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View File

@ -57,7 +57,7 @@ custom-protocol = ["tauri/custom-protocol"]
# Run the script: # Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id # scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "80d4048c69a22c0af77b2f2e9b13b1004b2156e2" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "a455c9de8e6448cec956da5a00ba5cc136ed7274" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #

View File

@ -125,7 +125,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]] [[package]]
name = "app-error" name = "app-error"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -667,7 +667,7 @@ dependencies = [
[[package]] [[package]]
name = "client-api" name = "client-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -1149,7 +1149,7 @@ dependencies = [
"cssparser-macros", "cssparser-macros",
"dtoa-short", "dtoa-short",
"itoa", "itoa",
"phf 0.11.2", "phf 0.8.0",
"smallvec", "smallvec",
] ]
@ -1277,7 +1277,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]] [[package]]
name = "database-entity" name = "database-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2483,7 +2483,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue" name = "gotrue"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2499,7 +2499,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue-entity" name = "gotrue-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2860,7 +2860,7 @@ dependencies = [
[[package]] [[package]]
name = "infra" name = "infra"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -3665,7 +3665,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [ dependencies = [
"phf_macros 0.8.0", "phf_macros",
"phf_shared 0.8.0", "phf_shared 0.8.0",
"proc-macro-hack", "proc-macro-hack",
] ]
@ -3685,7 +3685,6 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [ dependencies = [
"phf_macros 0.11.2",
"phf_shared 0.11.2", "phf_shared 0.11.2",
] ]
@ -3753,19 +3752,6 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator 0.11.2",
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 2.0.31",
]
[[package]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.8.0" version = "0.8.0"
@ -3969,7 +3955,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
dependencies = [ dependencies = [
"bytes", "bytes",
"heck 0.4.1", "heck 0.4.1",
"itertools 0.11.0", "itertools 0.10.5",
"log", "log",
"multimap", "multimap",
"once_cell", "once_cell",
@ -3990,7 +3976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"itertools 0.11.0", "itertools 0.10.5",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.31", "syn 2.0.31",
@ -4329,7 +4315,7 @@ dependencies = [
[[package]] [[package]]
name = "realtime-entity" name = "realtime-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -4351,7 +4337,7 @@ dependencies = [
[[package]] [[package]]
name = "realtime-protocol" name = "realtime-protocol"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -5020,7 +5006,7 @@ dependencies = [
[[package]] [[package]]
name = "shared_entity" name = "shared_entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -6401,7 +6387,7 @@ dependencies = [
[[package]] [[package]]
name = "workspace-template" name = "workspace-template"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a455c9de8e6448cec956da5a00ba5cc136ed7274#a455c9de8e6448cec956da5a00ba5cc136ed7274"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View File

@ -99,7 +99,7 @@ incremental = false
# Run the script: # Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id # scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "80d4048c69a22c0af77b2f2e9b13b1004b2156e2" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "a455c9de8e6448cec956da5a00ba5cc136ed7274" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #

View File

@ -71,6 +71,23 @@ pub struct AppFlowyCollabBuilder {
device_id: String, device_id: String,
} }
pub struct CollabBuilderConfig {
pub sync_enable: bool,
}
impl Default for CollabBuilderConfig {
fn default() -> Self {
Self { sync_enable: true }
}
}
impl CollabBuilderConfig {
pub fn sync_enable(mut self, sync_enable: bool) -> Self {
self.sync_enable = sync_enable;
self
}
}
impl AppFlowyCollabBuilder { impl AppFlowyCollabBuilder {
pub fn new<T: CollabStorageProvider>(storage_provider: T, device_id: String) -> Self { pub fn new<T: CollabStorageProvider>(storage_provider: T, device_id: String) -> Self {
Self { Self {
@ -144,12 +161,21 @@ impl AppFlowyCollabBuilder {
uid: i64, uid: i64,
object_id: &str, object_id: &str,
object_type: CollabType, object_type: CollabType,
raw_data: CollabRawData, doc_state: CollabRawData,
collab_db: Weak<RocksCollabDB>, collab_db: Weak<RocksCollabDB>,
build_config: CollabBuilderConfig,
) -> Result<Arc<MutexCollab>, Error> { ) -> Result<Arc<MutexCollab>, Error> {
let config = CollabPersistenceConfig::default(); let persistence_config = CollabPersistenceConfig::default();
self self
.build_with_config(uid, object_id, object_type, collab_db, raw_data, &config) .build_with_config(
uid,
object_id,
object_type,
collab_db,
doc_state,
&persistence_config,
build_config,
)
.await .await
} }
@ -167,30 +193,33 @@ impl AppFlowyCollabBuilder {
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type. /// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type.
/// - `collab_db`: A weak reference to the [RocksCollabDB]. /// - `collab_db`: A weak reference to the [RocksCollabDB].
/// ///
#[allow(clippy::too_many_arguments)]
pub async fn build_with_config( pub async fn build_with_config(
&self, &self,
uid: i64, uid: i64,
object_id: &str, object_id: &str,
object_type: CollabType, object_type: CollabType,
collab_db: Weak<RocksCollabDB>, collab_db: Weak<RocksCollabDB>,
collab_raw_data: CollabRawData, doc_state: CollabRawData,
config: &CollabPersistenceConfig, persistence_config: &CollabPersistenceConfig,
build_config: CollabBuilderConfig,
) -> Result<Arc<MutexCollab>, Error> { ) -> Result<Arc<MutexCollab>, Error> {
let collab = Arc::new( let collab = Arc::new(
CollabBuilder::new(uid, object_id) CollabBuilder::new(uid, object_id)
.with_raw_data(collab_raw_data) .with_raw_data(doc_state)
.with_plugin(RocksdbDiskPlugin::new_with_config( .with_plugin(RocksdbDiskPlugin::new_with_config(
uid, uid,
collab_db.clone(), collab_db.clone(),
config.clone(), persistence_config.clone(),
self.rocksdb_backup.lock().clone(), self.rocksdb_backup.lock().clone(),
)) ))
.with_device_id(self.device_id.clone()) .with_device_id(self.device_id.clone())
.build()?, .build()?,
); );
{ {
let cloud_storage_type = self.cloud_storage.read().await.storage_source();
let collab_object = self.collab_object(uid, object_id, object_type)?; let collab_object = self.collab_object(uid, object_id, object_type)?;
if build_config.sync_enable {
let cloud_storage_type = self.cloud_storage.read().await.storage_source();
let span = tracing::span!(tracing::Level::TRACE, "collab_builder", object_id = %object_id); let span = tracing::span!(tracing::Level::TRACE, "collab_builder", object_id = %object_id);
let _enter = span.enter(); let _enter = span.enter();
match cloud_storage_type { match cloud_storage_type {
@ -240,15 +269,16 @@ impl AppFlowyCollabBuilder {
}, },
CollabDataSource::Local => {}, CollabDataSource::Local => {},
} }
}
if let Some(snapshot_persistence) = self.snapshot_persistence.lock().as_ref() { if let Some(snapshot_persistence) = self.snapshot_persistence.lock().as_ref() {
if config.enable_snapshot { if persistence_config.enable_snapshot {
let snapshot_plugin = CollabSnapshotPlugin::new( let snapshot_plugin = CollabSnapshotPlugin::new(
uid, uid,
collab_object, collab_object,
snapshot_persistence.clone(), snapshot_persistence.clone(),
collab_db, collab_db,
config.snapshot_per_update, persistence_config.snapshot_per_update,
); );
// tracing::trace!("add snapshot plugin: {}", object_id); // tracing::trace!("add snapshot plugin: {}", object_id);
collab.lock().add_plugin(Arc::new(snapshot_plugin)); collab.lock().add_plugin(Arc::new(snapshot_plugin));

View File

@ -70,6 +70,13 @@ pub extern "C" fn init_sdk(data: *mut c_char) -> i64 {
DEFAULT_NAME.to_string(), DEFAULT_NAME.to_string(),
) )
.log_filter("info", log_crates); .log_filter("info", log_crates);
// Ensure that the database is closed before initialization. Also, verify that the init_sdk function can be called
// multiple times (is reentrant). Currently, only the database resource is exclusive.
if let Some(core) = &*APPFLOWY_CORE.0.lock() {
core.close_db();
}
*APPFLOWY_CORE.0.lock() = Some(AppFlowyCore::new(config)); *APPFLOWY_CORE.0.lock() = Some(AppFlowyCore::new(config));
0 0
} }

View File

@ -18,7 +18,7 @@ use crate::EventIntegrationTest;
const TEXT_BLOCK_TY: &str = "paragraph"; const TEXT_BLOCK_TY: &str = "paragraph";
pub struct DocumentEventTest { pub struct DocumentEventTest {
inner: EventIntegrationTest, event_test: EventIntegrationTest,
} }
pub struct OpenDocumentData { pub struct OpenDocumentData {
@ -29,15 +29,15 @@ pub struct OpenDocumentData {
impl DocumentEventTest { impl DocumentEventTest {
pub async fn new() -> Self { pub async fn new() -> Self {
let sdk = EventIntegrationTest::new_with_guest_user().await; let sdk = EventIntegrationTest::new_with_guest_user().await;
Self { inner: sdk } Self { event_test: sdk }
} }
pub fn new_with_core(core: EventIntegrationTest) -> Self { pub fn new_with_core(core: EventIntegrationTest) -> Self {
Self { inner: core } Self { event_test: core }
} }
pub async fn create_document(&self) -> ViewPB { pub async fn create_document(&self) -> ViewPB {
let core = &self.inner; let core = &self.event_test;
let current_workspace = core.get_current_workspace().await; let current_workspace = core.get_current_workspace().await;
let parent_id = current_workspace.id.clone(); let parent_id = current_workspace.id.clone();
@ -61,22 +61,12 @@ impl DocumentEventTest {
} }
pub async fn open_document(&self, doc_id: String) -> OpenDocumentData { pub async fn open_document(&self, doc_id: String) -> OpenDocumentData {
let core = &self.inner; self.event_test.open_document(doc_id).await
let payload = OpenDocumentPayloadPB {
document_id: doc_id.clone(),
};
let data = EventBuilder::new(core.clone())
.event(DocumentEvent::OpenDocument)
.payload(payload)
.async_send()
.await
.parse::<DocumentDataPB>();
OpenDocumentData { id: doc_id, data }
} }
pub async fn get_block(&self, doc_id: &str, block_id: &str) -> Option<BlockPB> { pub async fn get_block(&self, doc_id: &str, block_id: &str) -> Option<BlockPB> {
let document = self.open_document(doc_id.to_string()).await; let document_data = self.event_test.open_document(doc_id.to_string()).await;
document.data.blocks.get(block_id).cloned() document_data.data.blocks.get(block_id).cloned()
} }
pub async fn get_page_id(&self, doc_id: &str) -> String { pub async fn get_page_id(&self, doc_id: &str) -> String {
@ -85,8 +75,8 @@ impl DocumentEventTest {
} }
pub async fn get_document_data(&self, doc_id: &str) -> DocumentDataPB { pub async fn get_document_data(&self, doc_id: &str) -> DocumentDataPB {
let document = self.open_document(doc_id.to_string()).await; let document_data = self.event_test.open_document(doc_id.to_string()).await;
document.data document_data.data
} }
pub async fn get_block_children(&self, doc_id: &str, block_id: &str) -> Option<Vec<String>> { pub async fn get_block_children(&self, doc_id: &str, block_id: &str) -> Option<Vec<String>> {
@ -104,7 +94,7 @@ impl DocumentEventTest {
} }
pub async fn apply_actions(&self, payload: ApplyActionPayloadPB) { pub async fn apply_actions(&self, payload: ApplyActionPayloadPB) {
let core = &self.inner; let core = &self.event_test;
EventBuilder::new(core.clone()) EventBuilder::new(core.clone())
.event(DocumentEvent::ApplyAction) .event(DocumentEvent::ApplyAction)
.payload(payload) .payload(payload)
@ -116,7 +106,7 @@ impl DocumentEventTest {
&self, &self,
payload: ConvertDocumentPayloadPB, payload: ConvertDocumentPayloadPB,
) -> ConvertDocumentResponsePB { ) -> ConvertDocumentResponsePB {
let core = &self.inner; let core = &self.event_test;
EventBuilder::new(core.clone()) EventBuilder::new(core.clone())
.event(DocumentEvent::ConvertDocument) .event(DocumentEvent::ConvertDocument)
.payload(payload) .payload(payload)
@ -130,7 +120,7 @@ impl DocumentEventTest {
&self, &self,
payload: ConvertDataToJsonPayloadPB, payload: ConvertDataToJsonPayloadPB,
) -> ConvertDataToJsonResponsePB { ) -> ConvertDataToJsonResponsePB {
let core = &self.inner; let core = &self.event_test;
EventBuilder::new(core.clone()) EventBuilder::new(core.clone())
.event(DocumentEvent::ConvertDataToJSON) .event(DocumentEvent::ConvertDataToJSON)
.payload(payload) .payload(payload)
@ -140,7 +130,7 @@ impl DocumentEventTest {
} }
pub async fn create_text(&self, payload: TextDeltaPayloadPB) { pub async fn create_text(&self, payload: TextDeltaPayloadPB) {
let core = &self.inner; let core = &self.event_test;
EventBuilder::new(core.clone()) EventBuilder::new(core.clone())
.event(DocumentEvent::CreateText) .event(DocumentEvent::CreateText)
.payload(payload) .payload(payload)
@ -149,7 +139,7 @@ impl DocumentEventTest {
} }
pub async fn apply_text_delta(&self, payload: TextDeltaPayloadPB) { pub async fn apply_text_delta(&self, payload: TextDeltaPayloadPB) {
let core = &self.inner; let core = &self.event_test;
EventBuilder::new(core.clone()) EventBuilder::new(core.clone())
.event(DocumentEvent::ApplyTextDeltaEvent) .event(DocumentEvent::ApplyTextDeltaEvent)
.payload(payload) .payload(payload)
@ -158,7 +148,7 @@ impl DocumentEventTest {
} }
pub async fn undo(&self, doc_id: String) -> DocumentRedoUndoResponsePB { pub async fn undo(&self, doc_id: String) -> DocumentRedoUndoResponsePB {
let core = &self.inner; let core = &self.event_test;
let payload = DocumentRedoUndoPayloadPB { let payload = DocumentRedoUndoPayloadPB {
document_id: doc_id.clone(), document_id: doc_id.clone(),
}; };
@ -171,7 +161,7 @@ impl DocumentEventTest {
} }
pub async fn redo(&self, doc_id: String) -> DocumentRedoUndoResponsePB { pub async fn redo(&self, doc_id: String) -> DocumentRedoUndoResponsePB {
let core = &self.inner; let core = &self.event_test;
let payload = DocumentRedoUndoPayloadPB { let payload = DocumentRedoUndoPayloadPB {
document_id: doc_id.clone(), document_id: doc_id.clone(),
}; };
@ -184,7 +174,7 @@ impl DocumentEventTest {
} }
pub async fn can_undo_redo(&self, doc_id: String) -> DocumentRedoUndoResponsePB { pub async fn can_undo_redo(&self, doc_id: String) -> DocumentRedoUndoResponsePB {
let core = &self.inner; let core = &self.event_test;
let payload = DocumentRedoUndoPayloadPB { let payload = DocumentRedoUndoPayloadPB {
document_id: doc_id.clone(), document_id: doc_id.clone(),
}; };

View File

@ -17,7 +17,7 @@ use crate::event_builder::EventBuilder;
use crate::EventIntegrationTest; use crate::EventIntegrationTest;
impl EventIntegrationTest { impl EventIntegrationTest {
pub async fn create_document( pub async fn create_and_open_document(
&self, &self,
parent_id: &str, parent_id: &str,
name: String, name: String,
@ -86,11 +86,11 @@ impl EventIntegrationTest {
DocumentData::from(pb) DocumentData::from(pb)
} }
pub async fn get_document_update(&self, document_id: &str) -> Vec<u8> { pub async fn get_document_doc_state(&self, document_id: &str) -> Vec<u8> {
let workspace_id = self.user_manager.workspace_id().unwrap(); let workspace_id = self.user_manager.workspace_id().unwrap();
let cloud_service = self.document_manager.get_cloud_service().clone(); let cloud_service = self.document_manager.get_cloud_service().clone();
let remote_updates = cloud_service let remote_updates = cloud_service
.get_document_updates(document_id, &workspace_id) .get_document_doc_state(document_id, &workspace_id)
.await .await
.unwrap(); .unwrap();
@ -107,10 +107,10 @@ impl EventIntegrationTest {
} }
} }
pub fn assert_document_data_equal(collab_update: &[u8], doc_id: &str, expected: DocumentData) { pub fn assert_document_data_equal(doc_state: &[u8], doc_id: &str, expected: DocumentData) {
let collab = MutexCollab::new(CollabOrigin::Server, doc_id, vec![]); let collab = MutexCollab::new(CollabOrigin::Server, doc_id, vec![]);
collab.lock().with_origin_transact_mut(|txn| { collab.lock().with_origin_transact_mut(|txn| {
let update = Update::decode_v1(collab_update).unwrap(); let update = Update::decode_v1(doc_state).unwrap();
txn.apply_update(update); txn.apply_update(update);
}); });
let document = Document::open(Arc::new(collab)).unwrap(); let document = Document::open(Arc::new(collab)).unwrap();

View File

@ -75,6 +75,14 @@ impl EventIntegrationTest {
.parse::<ViewPB>() .parse::<ViewPB>()
} }
pub async fn get_trash(&self) -> RepeatedTrashPB {
EventBuilder::new(self.clone())
.event(FolderEvent::ListTrashItems)
.async_send()
.await
.parse::<RepeatedTrashPB>()
}
pub async fn delete_view(&self, view_id: &str) { pub async fn delete_view(&self, view_id: &str) {
let payload = RepeatedViewIdPB { let payload = RepeatedViewIdPB {
items: vec![view_id.to_string()], items: vec![view_id.to_string()],

View File

@ -1,13 +1,17 @@
use std::env::temp_dir; use std::env::temp_dir;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use nanoid::nanoid; use nanoid::nanoid;
use parking_lot::RwLock; use parking_lot::RwLock;
use tokio::select;
use tokio::time::sleep;
use flowy_core::config::AppFlowyCoreConfig; use flowy_core::config::AppFlowyCoreConfig;
use flowy_core::AppFlowyCore; use flowy_core::AppFlowyCore;
use flowy_notification::register_notification_sender; use flowy_notification::register_notification_sender;
use flowy_server::AppFlowyServer;
use flowy_user::entities::AuthenticatorPB; use flowy_user::entities::AuthenticatorPB;
use crate::user_event::TestNotificationSender; use crate::user_event::TestNotificationSender;
@ -21,8 +25,8 @@ pub mod user_event;
#[derive(Clone)] #[derive(Clone)]
pub struct EventIntegrationTest { pub struct EventIntegrationTest {
pub auth_type: Arc<RwLock<AuthenticatorPB>>, pub authenticator: Arc<RwLock<AuthenticatorPB>>,
pub inner: AppFlowyCore, pub appflowy_core: AppFlowyCore,
#[allow(dead_code)] #[allow(dead_code)]
cleaner: Arc<Cleaner>, cleaner: Arc<Cleaner>,
pub notification_sender: TestNotificationSender, pub notification_sender: TestNotificationSender,
@ -34,6 +38,7 @@ impl EventIntegrationTest {
std::fs::create_dir_all(&temp_dir).unwrap(); std::fs::create_dir_all(&temp_dir).unwrap();
Self::new_with_user_data_path(temp_dir, nanoid!(6)).await Self::new_with_user_data_path(temp_dir, nanoid!(6)).await
} }
pub async fn new_with_user_data_path(path_buf: PathBuf, name: String) -> Self { pub async fn new_with_user_data_path(path_buf: PathBuf, name: String) -> Self {
let path = path_buf.to_str().unwrap().to_string(); let path = path_buf.to_str().unwrap().to_string();
let device_id = uuid::Uuid::new_v4().to_string(); let device_id = uuid::Uuid::new_v4().to_string();
@ -42,22 +47,61 @@ impl EventIntegrationTest {
vec![ vec![
"flowy_test".to_string(), "flowy_test".to_string(),
"tokio".to_string(), "tokio".to_string(),
"lib_dispatch".to_string(), // "lib_dispatch".to_string(),
], ],
); );
let inner = init_core(config).await; let inner = init_core(config).await;
let notification_sender = TestNotificationSender::new(); let notification_sender = TestNotificationSender::new();
let auth_type = Arc::new(RwLock::new(AuthenticatorPB::Local)); let authenticator = Arc::new(RwLock::new(AuthenticatorPB::Local));
register_notification_sender(notification_sender.clone()); register_notification_sender(notification_sender.clone());
// In case of dropping the runtime that runs the core, we need to forget the dispatcher
std::mem::forget(inner.dispatcher()); std::mem::forget(inner.dispatcher());
Self { Self {
inner, appflowy_core: inner,
auth_type, authenticator,
notification_sender, notification_sender,
cleaner: Arc::new(Cleaner(path_buf)), cleaner: Arc::new(Cleaner(path_buf)),
} }
} }
pub fn get_appflowy_cloud_server(&self) -> Arc<dyn AppFlowyServer> {
self
.appflowy_core
.server_provider
.get_appflowy_cloud_server()
.unwrap()
}
pub async fn wait_ws_connected(&self) {
if self
.get_appflowy_cloud_server()
.get_ws_state()
.is_connected()
{
return;
}
let mut ws_state = self
.get_appflowy_cloud_server()
.subscribe_ws_state()
.unwrap();
loop {
select! {
_ = sleep(Duration::from_secs(20)) => {
panic!("wait_ws_connected timeout");
}
state = ws_state.recv() => {
if let Ok(state) = &state {
if state.is_connected() {
break;
}
}
}
}
}
}
} }
#[cfg(feature = "single_thread")] #[cfg(feature = "single_thread")]
@ -79,7 +123,7 @@ impl std::ops::Deref for EventIntegrationTest {
type Target = AppFlowyCore; type Target = AppFlowyCore;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.inner &self.appflowy_core
} }
} }

View File

@ -66,7 +66,7 @@ impl EventIntegrationTest {
.unwrap(); .unwrap();
let request = AFPluginRequest::new(SignUp).payload(payload); let request = AFPluginRequest::new(SignUp).payload(payload);
let user_profile = AFPluginDispatcher::async_send(self.inner.dispatcher(), request) let user_profile = AFPluginDispatcher::async_send(self.appflowy_core.dispatcher(), request)
.await .await
.parse::<UserProfilePB, FlowyError>() .parse::<UserProfilePB, FlowyError>()
.unwrap() .unwrap()
@ -88,7 +88,7 @@ impl EventIntegrationTest {
let map = third_party_sign_up_param(Uuid::new_v4().to_string()); let map = third_party_sign_up_param(Uuid::new_v4().to_string());
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthenticatorPB::Supabase, authenticator: AuthenticatorPB::Supabase,
}; };
EventBuilder::new(self.clone()) EventBuilder::new(self.clone())
@ -107,7 +107,7 @@ impl EventIntegrationTest {
} }
pub fn set_auth_type(&self, auth_type: AuthenticatorPB) { pub fn set_auth_type(&self, auth_type: AuthenticatorPB) {
*self.auth_type.write() = auth_type; *self.authenticator.write() = auth_type;
} }
pub async fn init_anon_user(&self) -> UserProfilePB { pub async fn init_anon_user(&self) -> UserProfilePB {
@ -133,7 +133,7 @@ impl EventIntegrationTest {
pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> { pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
let payload = SignInUrlPayloadPB { let payload = SignInUrlPayloadPB {
email: email.to_string(), email: email.to_string(),
auth_type: AuthenticatorPB::AppFlowyCloud, authenticator: AuthenticatorPB::AppFlowyCloud,
}; };
let sign_in_url = EventBuilder::new(self.clone()) let sign_in_url = EventBuilder::new(self.clone())
.event(GenerateSignInURL) .event(GenerateSignInURL)
@ -148,7 +148,7 @@ impl EventIntegrationTest {
map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string()); map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string());
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthenticatorPB::AppFlowyCloud, authenticator: AuthenticatorPB::AppFlowyCloud,
}; };
let user_profile = EventBuilder::new(self.clone()) let user_profile = EventBuilder::new(self.clone())
@ -175,7 +175,7 @@ impl EventIntegrationTest {
); );
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthenticatorPB::Supabase, authenticator: AuthenticatorPB::Supabase,
}; };
let user_profile = EventBuilder::new(self.clone()) let user_profile = EventBuilder::new(self.clone())
@ -246,7 +246,7 @@ impl TestNotificationSender {
F: Fn(&T) -> bool + Send + 'static, F: Fn(&T) -> bool + Send + 'static,
{ {
let id = id.to_string(); let id = id.to_string();
let (tx, rx) = tokio::sync::mpsc::channel::<T>(10); let (tx, rx) = tokio::sync::mpsc::channel::<T>(1);
let mut receiver = self.sender.subscribe(); let mut receiver = self.sender.subscribe();
af_spawn(async move { af_spawn(async move {
while let Ok(value) = receiver.recv().await { while let Ok(value) = receiver.recv().await {

View File

@ -1,34 +1,37 @@
use std::time::Duration; use std::time::Duration;
use event_integration::document_event::assert_document_data_equal; use event_integration::document_event::assert_document_data_equal;
use event_integration::user_event::user_localhost_af_cloud;
use event_integration::EventIntegrationTest;
use flowy_document2::entities::DocumentSyncStatePB; use flowy_document2::entities::DocumentSyncStatePB;
use crate::document::af_cloud_test::util::AFCloudDocumentTest;
use crate::util::receive_with_timeout; use crate::util::receive_with_timeout;
#[tokio::test] #[tokio::test]
async fn af_cloud_edit_document_test() { async fn af_cloud_edit_document_test() {
if let Some(test) = AFCloudDocumentTest::new().await { user_localhost_af_cloud().await;
let document_id = test.create_document().await; let test = EventIntegrationTest::new().await;
let cloned_test = test.clone(); test.af_cloud_sign_up().await;
let cloned_document_id = document_id.clone(); test.wait_ws_connected().await;
test.inner.dispatcher().spawn(async move {
cloned_test // create document and then insert content
.insert_document_text(&cloned_document_id, "hello world", 0) let current_workspace = test.get_current_workspace().await;
let view = test
.create_and_open_document(&current_workspace.id, "my document".to_string(), vec![])
.await; .await;
}); test.insert_document_text(&view.id, "hello world", 0).await;
let document_id = view.id;
println!("document_id: {}", document_id);
// wait all update are send to the remote // wait all update are send to the remote
let rx = test let rx = test
.notification_sender .notification_sender
.subscribe_with_condition::<DocumentSyncStatePB, _>(&document_id, |pb| !pb.is_syncing); .subscribe_with_condition::<DocumentSyncStatePB, _>(&document_id, |pb| !pb.is_syncing);
receive_with_timeout(rx, Duration::from_secs(25)) let _ = receive_with_timeout(rx, Duration::from_secs(30)).await;
.await
.unwrap();
let document_data = test.get_document_data(&document_id).await; let document_data = test.get_document_data(&document_id).await;
let update = test.get_document_update(&document_id).await; let doc_state = test.get_document_doc_state(&document_id).await;
assert!(!update.is_empty()); assert!(!doc_state.is_empty());
assert_document_data_equal(&update, &document_id, document_data); assert_document_data_equal(&doc_state, &document_id, document_data);
}
} }

View File

@ -1,2 +1 @@
mod edit_test; mod edit_test;
mod util;

View File

@ -1,33 +0,0 @@
use std::ops::Deref;
use crate::util::{generate_test_email, AFCloudTest};
pub struct AFCloudDocumentTest {
inner: AFCloudTest,
}
impl AFCloudDocumentTest {
pub async fn new() -> Option<Self> {
let inner = AFCloudTest::new().await?;
let email = generate_test_email();
let _ = inner.af_cloud_sign_in_with_email(&email).await.unwrap();
Some(Self { inner })
}
pub async fn create_document(&self) -> String {
let current_workspace = self.inner.get_current_workspace().await;
let view = self
.inner
.create_document(&current_workspace.id, "my document".to_string(), vec![])
.await;
view.id
}
}
impl Deref for AFCloudDocumentTest {
type Target = AFCloudTest;
fn deref(&self) -> &Self::Target {
&self.inner
}
}

View File

@ -14,7 +14,7 @@ async fn supabase_document_edit_sync_test() {
let cloned_test = test.clone(); let cloned_test = test.clone();
let cloned_document_id = document_id.clone(); let cloned_document_id = document_id.clone();
test.inner.dispatcher().spawn(async move { test.appflowy_core.dispatcher().spawn(async move {
cloned_test cloned_test
.insert_document_text(&cloned_document_id, "hello world", 0) .insert_document_text(&cloned_document_id, "hello world", 0)
.await; .await;
@ -29,7 +29,7 @@ async fn supabase_document_edit_sync_test() {
.unwrap(); .unwrap();
let document_data = test.get_document_data(&document_id).await; let document_data = test.get_document_data(&document_id).await;
let update = test.get_document_update(&document_id).await; let update = test.get_document_doc_state(&document_id).await;
assert_document_data_equal(&update, &document_id, document_data); assert_document_data_equal(&update, &document_id, document_data);
} }
} }
@ -55,7 +55,7 @@ async fn supabase_document_edit_sync_test2() {
.unwrap(); .unwrap();
let document_data = test.get_document_data(&document_id).await; let document_data = test.get_document_data(&document_id).await;
let update = test.get_document_update(&document_id).await; let update = test.get_document_doc_state(&document_id).await;
assert_document_data_equal(&update, &document_id, document_data); assert_document_data_equal(&update, &document_id, document_data);
} }
} }

View File

@ -23,7 +23,7 @@ impl FlowySupabaseDocumentTest {
let current_workspace = self.inner.get_current_workspace().await; let current_workspace = self.inner.get_current_workspace().await;
self self
.inner .inner
.create_document(&current_workspace.id, "my document".to_string(), vec![]) .create_and_open_document(&current_workspace.id, "my document".to_string(), vec![])
.await .await
} }

View File

@ -24,7 +24,7 @@ async fn create_child_view_in_workspace_subscription_test() {
let cloned_test = test.clone(); let cloned_test = test.clone();
let cloned_workspace_id = workspace.id.clone(); let cloned_workspace_id = workspace.id.clone();
test.inner.dispatcher().spawn(async move { test.appflowy_core.dispatcher().spawn(async move {
cloned_test cloned_test
.create_view(&cloned_workspace_id, "workspace child view".to_string()) .create_view(&cloned_workspace_id, "workspace child view".to_string())
.await; .await;
@ -50,7 +50,7 @@ async fn create_child_view_in_view_subscription_test() {
let cloned_test = test.clone(); let cloned_test = test.clone();
let child_view_id = workspace_child_view.id.clone(); let child_view_id = workspace_child_view.id.clone();
test.inner.dispatcher().spawn(async move { test.appflowy_core.dispatcher().spawn(async move {
cloned_test cloned_test
.create_view( .create_view(
&child_view_id, &child_view_id,
@ -82,7 +82,7 @@ async fn delete_view_subscription_test() {
let delete_view_id = workspace.views.first().unwrap().id.clone(); let delete_view_id = workspace.views.first().unwrap().id.clone();
let cloned_delete_view_id = delete_view_id.clone(); let cloned_delete_view_id = delete_view_id.clone();
test test
.inner .appflowy_core
.dispatcher() .dispatcher()
.spawn(async move { .spawn(async move {
cloned_test.delete_view(&cloned_delete_view_id).await; cloned_test.delete_view(&cloned_delete_view_id).await;
@ -91,7 +91,7 @@ async fn delete_view_subscription_test() {
.unwrap(); .unwrap();
let update = test let update = test
.inner .appflowy_core
.dispatcher() .dispatcher()
.run_until(receive_with_timeout(rx, Duration::from_secs(30))) .run_until(receive_with_timeout(rx, Duration::from_secs(30)))
.await .await
@ -114,7 +114,7 @@ async fn update_view_subscription_test() {
assert!(!view.is_favorite); assert!(!view.is_favorite);
let update_view_id = view.id.clone(); let update_view_id = view.id.clone();
test.inner.dispatcher().spawn(async move { test.appflowy_core.dispatcher().spawn(async move {
cloned_test cloned_test
.update_view(UpdateViewPayloadPB { .update_view(UpdateViewPayloadPB {
view_id: update_view_id, view_id: update_view_id,

View File

@ -1,5 +1,7 @@
use event_integration::user_event::user_localhost_af_cloud;
use event_integration::EventIntegrationTest; use event_integration::EventIntegrationTest;
use flowy_core::DEFAULT_NAME; use flowy_core::DEFAULT_NAME;
use flowy_user::entities::AuthenticatorPB;
use crate::util::unzip_history_user_db; use crate::util::unzip_history_user_db;
@ -32,45 +34,86 @@ async fn reading_039_anon_user_data_test() {
assert_eq!(third_level_views[0].name, "Grid1".to_string()); assert_eq!(third_level_views[0].name, "Grid1".to_string());
assert_eq!(third_level_views[1].name, "Grid2".to_string()); assert_eq!(third_level_views[1].name, "Grid2".to_string());
let trash_items = test.get_trash().await.items;
assert_eq!(trash_items.len(), 1);
drop(cleaner); drop(cleaner);
} }
// #[tokio::test]
// async fn migrate_anon_user_data_to_af_cloud_test() { #[tokio::test]
// let (cleaner, user_db_path) = unzip_history_user_db("./tests/asset", "039_local").unwrap(); async fn migrate_anon_user_data_to_af_cloud_test() {
// user_localhost_af_cloud().await; let (cleaner, user_db_path) = unzip_history_user_db("./tests/asset", "040_local").unwrap();
// let test = // In the 040_local, the structure is:
// EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()).await; // workspace:
// let anon_first_level_views = test.get_all_workspace_views().await; // view: Document1
// let anon_second_level_views = test // view: Document2
// .get_views(&anon_first_level_views[0].id) // view: Grid1
// .await // view: Grid2
// .child_views; user_localhost_af_cloud().await;
// let test =
// // The anon user data will be migrated to the AppFlowy cloud after sign up EventIntegrationTest::new_with_user_data_path(user_db_path.clone(), DEFAULT_NAME.to_string())
// let user = test.af_cloud_sign_up().await; .await;
// assert_eq!(user.authenticator, AuthenticatorPB::AppFlowyCloud); let anon_trash = test.get_trash().await;
// assert_eq!(anon_trash.items.len(), 1);
// let user_first_level_views = test.get_all_workspace_views().await; assert_eq!(
// let user_second_level_views = test anon_trash.items[0].name,
// .get_views(&user_first_level_views[0].id) "Local Getting started".to_string()
// .await );
// .child_views;
// let anon_first_level_views = test.get_all_workspace_views().await;
// // first let anon_second_level_views = test
// assert_eq!(anon_first_level_views.len(), 1); .get_views(&anon_first_level_views[0].id)
// assert_eq!(user_first_level_views.len(), 1); .await
// assert_eq!( .child_views;
// anon_first_level_views[0].name, let anon_third_level_views = test
// user_first_level_views[0].name .get_views(&anon_second_level_views[0].id)
// ); .await
// assert_ne!(anon_first_level_views[0].id, user_first_level_views[0].id); .child_views;
//
// // second // The anon user data will be migrated to the AppFlowy cloud after sign up
// assert_eq!(anon_second_level_views.len(), user_second_level_views.len()); let user = test.af_cloud_sign_up().await;
// assert_eq!( let user_trash = test.get_trash().await;
// anon_second_level_views[0].name, let workspace = test.get_current_workspace().await;
// user_second_level_views[0].name println!("user workspace: {:?}", workspace.id);
// ); assert_eq!(user.authenticator, AuthenticatorPB::AppFlowyCloud);
// assert_ne!(anon_second_level_views[0].id, user_second_level_views[0].id);
// drop(cleaner); let user_first_level_views = test.get_all_workspace_views().await;
// } println!("user first level views: {:?}", user_first_level_views);
let user_second_level_views = test
.get_views(&user_first_level_views[0].id)
.await
.child_views;
println!("user second level views: {:?}", user_second_level_views);
let user_third_level_views = test
.get_views(&user_second_level_views[0].id)
.await
.child_views;
println!("user third level views: {:?}", user_third_level_views);
// check first level
assert_eq!(anon_first_level_views.len(), 1);
assert_eq!(user_first_level_views.len(), 1);
assert_ne!(anon_first_level_views[0].id, user_first_level_views[0].id);
assert_eq!(
anon_first_level_views[0].name,
user_first_level_views[0].name
);
// check second level
assert_eq!(anon_second_level_views.len(), user_second_level_views.len());
assert_ne!(anon_second_level_views[0].id, user_second_level_views[0].id);
assert_eq!(
anon_second_level_views[0].name,
user_second_level_views[0].name
);
// check third level
assert_eq!(anon_third_level_views.len(), 2);
assert_eq!(user_third_level_views[0].name, "Grid1".to_string());
assert_eq!(user_third_level_views[1].name, "Grid2".to_string());
drop(cleaner);
// check the trash
assert_eq!(user_trash.items.len(), 1);
assert_eq!(user_trash.items[0].name, anon_trash.items[0].name);
}

View File

@ -1,21 +1,21 @@
use event_integration::user_event::user_localhost_af_cloud;
use event_integration::EventIntegrationTest; use event_integration::EventIntegrationTest;
use flowy_user::entities::UpdateUserProfilePayloadPB; use flowy_user::entities::UpdateUserProfilePayloadPB;
use crate::util::{generate_test_email, get_af_cloud_config}; use crate::util::generate_test_email;
#[tokio::test] #[tokio::test]
async fn af_cloud_sign_up_test() { async fn af_cloud_sign_up_test() {
if get_af_cloud_config().is_some() { user_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await; let test = EventIntegrationTest::new().await;
let email = generate_test_email(); let email = generate_test_email();
let user = test.af_cloud_sign_in_with_email(&email).await.unwrap(); let user = test.af_cloud_sign_in_with_email(&email).await.unwrap();
assert_eq!(user.email, email); assert_eq!(user.email, email);
}
} }
#[tokio::test] #[tokio::test]
async fn af_cloud_update_user_metadata() { async fn af_cloud_update_user_metadata() {
if get_af_cloud_config().is_some() { user_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await; let test = EventIntegrationTest::new().await;
let user = test.af_cloud_sign_up().await; let user = test.af_cloud_sign_up().await;
@ -37,5 +37,4 @@ async fn af_cloud_update_user_metadata() {
new_profile.stability_ai_key, new_profile.stability_ai_key,
"new stability ai key".to_string() "new stability ai key".to_string()
); );
}
} }

View File

@ -1,10 +1,9 @@
use event_integration::user_event::user_localhost_af_cloud;
use event_integration::EventIntegrationTest; use event_integration::EventIntegrationTest;
use crate::util::get_af_cloud_config;
#[tokio::test] #[tokio::test]
async fn af_cloud_add_workspace_member_test() { async fn af_cloud_add_workspace_member_test() {
if get_af_cloud_config().is_some() { user_localhost_af_cloud().await;
let test_1 = EventIntegrationTest::new().await; let test_1 = EventIntegrationTest::new().await;
let user_1 = test_1.af_cloud_sign_up().await; let user_1 = test_1.af_cloud_sign_up().await;
@ -23,12 +22,11 @@ async fn af_cloud_add_workspace_member_test() {
assert_eq!(members.len(), 2); assert_eq!(members.len(), 2);
assert_eq!(members[0].email, user_1.email); assert_eq!(members[0].email, user_1.email);
assert_eq!(members[1].email, user_2.email); assert_eq!(members[1].email, user_2.email);
}
} }
#[tokio::test] #[tokio::test]
async fn af_cloud_delete_workspace_member_test() { async fn af_cloud_delete_workspace_member_test() {
if get_af_cloud_config().is_some() { user_localhost_af_cloud().await;
let test_1 = EventIntegrationTest::new().await; let test_1 = EventIntegrationTest::new().await;
let user_1 = test_1.af_cloud_sign_up().await; let user_1 = test_1.af_cloud_sign_up().await;
@ -46,5 +44,4 @@ async fn af_cloud_delete_workspace_member_test() {
let members = test_1.get_workspace_members(&user_1.workspace_id).await; let members = test_1.get_workspace_members(&user_1.workspace_id).await;
assert_eq!(members.len(), 1); assert_eq!(members.len(), 1);
assert_eq!(members[0].email, user_1.email); assert_eq!(members[0].email, user_1.email);
}
} }

View File

@ -35,7 +35,7 @@ async fn third_party_sign_up_test() {
map.insert(USER_DEVICE_ID.to_string(), uuid::Uuid::new_v4().to_string()); map.insert(USER_DEVICE_ID.to_string(), uuid::Uuid::new_v4().to_string());
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthenticatorPB::Supabase, authenticator: AuthenticatorPB::Supabase,
}; };
let response = EventBuilder::new(test.clone()) let response = EventBuilder::new(test.clone())
@ -79,7 +79,7 @@ async fn third_party_sign_up_with_duplicated_uuid() {
.event(OauthSignIn) .event(OauthSignIn)
.payload(OauthSignInPB { .payload(OauthSignInPB {
map: map.clone(), map: map.clone(),
auth_type: AuthenticatorPB::Supabase, authenticator: AuthenticatorPB::Supabase,
}) })
.async_send() .async_send()
.await .await
@ -90,7 +90,7 @@ async fn third_party_sign_up_with_duplicated_uuid() {
.event(OauthSignIn) .event(OauthSignIn)
.payload(OauthSignInPB { .payload(OauthSignInPB {
map: map.clone(), map: map.clone(),
auth_type: AuthenticatorPB::Supabase, authenticator: AuthenticatorPB::Supabase,
}) })
.async_send() .async_send()
.await .await

View File

@ -21,7 +21,7 @@ async fn initial_workspace_test() {
); );
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthenticatorPB::Supabase, authenticator: AuthenticatorPB::Supabase,
}; };
let _ = EventBuilder::new(test.clone()) let _ = EventBuilder::new(test.clone())

View File

@ -8,8 +8,8 @@ use std::time::Duration;
use anyhow::Error; use anyhow::Error;
use collab_folder::FolderData; use collab_folder::FolderData;
use collab_plugins::cloud_storage::RemoteCollabStorage; use collab_plugins::cloud_storage::RemoteCollabStorage;
use nanoid::nanoid;
use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Receiver;
use tokio::time::timeout; use tokio::time::timeout;
use uuid::Uuid; use uuid::Uuid;
use zip::ZipArchive; use zip::ZipArchive;
@ -21,11 +21,11 @@ use flowy_database_deps::cloud::DatabaseCloudService;
use flowy_folder_deps::cloud::{FolderCloudService, FolderSnapshot}; use flowy_folder_deps::cloud::{FolderCloudService, FolderSnapshot};
use flowy_server::supabase::api::*; use flowy_server::supabase::api::*;
use flowy_server::{AppFlowyEncryption, EncryptionImpl}; use flowy_server::{AppFlowyEncryption, EncryptionImpl};
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
use flowy_server_config::supabase_config::SupabaseConfiguration; use flowy_server_config::supabase_config::SupabaseConfiguration;
use flowy_user::entities::{AuthenticatorPB, UpdateUserProfilePayloadPB}; use flowy_user::entities::{AuthenticatorPB, UpdateUserProfilePayloadPB};
use flowy_user::errors::FlowyError; use flowy_user::errors::FlowyError;
use flowy_user::event_map::UserCloudServiceProvider; use flowy_user::event_map::UserCloudServiceProvider;
use flowy_user::event_map::UserEvent::*; use flowy_user::event_map::UserEvent::*;
use flowy_user_deps::cloud::UserCloudService; use flowy_user_deps::cloud::UserCloudService;
use flowy_user_deps::entities::Authenticator; use flowy_user_deps::entities::Authenticator;
@ -36,26 +36,26 @@ pub fn get_supabase_config() -> Option<SupabaseConfiguration> {
} }
pub struct FlowySupabaseTest { pub struct FlowySupabaseTest {
inner: EventIntegrationTest, event_test: EventIntegrationTest,
} }
impl FlowySupabaseTest { impl FlowySupabaseTest {
pub async fn new() -> Option<Self> { pub async fn new() -> Option<Self> {
let _ = get_supabase_config()?; let _ = get_supabase_config()?;
let test = EventIntegrationTest::new().await; let event_test = EventIntegrationTest::new().await;
test.set_auth_type(AuthenticatorPB::Supabase); event_test.set_auth_type(AuthenticatorPB::Supabase);
test event_test
.server_provider .server_provider
.set_authenticator(Authenticator::Supabase); .set_authenticator(Authenticator::Supabase);
Some(Self { inner: test }) Some(Self { event_test })
} }
pub async fn update_user_profile( pub async fn update_user_profile(
&self, &self,
payload: UpdateUserProfilePayloadPB, payload: UpdateUserProfilePayloadPB,
) -> Option<FlowyError> { ) -> Option<FlowyError> {
EventBuilder::new(self.inner.clone()) EventBuilder::new(self.event_test.clone())
.event(UpdateUserProfile) .event(UpdateUserProfile)
.payload(payload) .payload(payload)
.async_send() .async_send()
@ -68,16 +68,12 @@ impl Deref for FlowySupabaseTest {
type Target = EventIntegrationTest; type Target = EventIntegrationTest;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.inner &self.event_test
} }
} }
pub async fn receive_with_timeout<T>( pub async fn receive_with_timeout<T>(mut receiver: Receiver<T>, duration: Duration) -> Option<T> {
mut receiver: Receiver<T>, timeout(duration, receiver.recv()).await.ok()?
duration: Duration,
) -> Result<T, Box<dyn std::error::Error + Send>> {
let res = timeout(duration, receiver.recv()).await.unwrap().unwrap();
Ok(res)
} }
pub fn get_supabase_ci_config() -> Option<SupabaseConfiguration> { pub fn get_supabase_ci_config() -> Option<SupabaseConfiguration> {
@ -171,7 +167,7 @@ pub fn unzip_history_user_db(root: &str, folder_name: &str) -> std::io::Result<(
// Open the zip file // Open the zip file
let zip_file_path = format!("{}/{}.zip", root, folder_name); let zip_file_path = format!("{}/{}.zip", root, folder_name);
let reader = File::open(zip_file_path)?; let reader = File::open(zip_file_path)?;
let output_folder_path = format!("{}/unit_test_{}", root, nanoid!(6)); let output_folder_path = format!("{}/unit_test_{}", root, Uuid::new_v4());
// Create a ZipArchive from the file // Create a ZipArchive from the file
let mut archive = ZipArchive::new(reader)?; let mut archive = ZipArchive::new(reader)?;
@ -202,44 +198,6 @@ pub fn unzip_history_user_db(root: &str, folder_name: &str) -> std::io::Result<(
)) ))
} }
pub struct AFCloudTest {
inner: EventIntegrationTest,
}
impl AFCloudTest {
pub async fn new() -> Option<Self> {
let _ = get_af_cloud_config()?;
let test = EventIntegrationTest::new().await;
test.set_auth_type(AuthenticatorPB::AppFlowyCloud);
test
.server_provider
.set_authenticator(Authenticator::AppFlowyCloud);
Some(Self { inner: test })
}
}
impl Deref for AFCloudTest {
type Target = EventIntegrationTest;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
pub fn generate_test_email() -> String { pub fn generate_test_email() -> String {
format!("{}@test.com", Uuid::new_v4()) format!("{}@test.com", Uuid::new_v4())
} }
/// To run the test, create a .env.ci file in the 'event-integration' directory and set the following environment variables:
///
/// - `APPFLOWY_CLOUD_BASE_URL=http://localhost:8000`
/// - `APPFLOWY_CLOUD_WS_BASE_URL=ws://localhost:8000/ws`
/// - `APPFLOWY_CLOUD_GOTRUE_URL=http://localhost:9998`
///
/// - `GOTRUE_ADMIN_EMAIL=admin@example.com`
/// - `GOTRUE_ADMIN_PASSWORD=password`
pub fn get_af_cloud_config() -> Option<AFCloudConfiguration> {
dotenv::from_filename("./.env.ci").ok()?;
AFCloudConfiguration::from_env().ok()
}

View File

@ -13,7 +13,6 @@ use flowy_server::{AppFlowyEncryption, AppFlowyServer, EncryptionImpl};
use flowy_server_config::af_cloud_config::AFCloudConfiguration; use flowy_server_config::af_cloud_config::AFCloudConfiguration;
use flowy_server_config::supabase_config::SupabaseConfiguration; use flowy_server_config::supabase_config::SupabaseConfiguration;
use flowy_sqlite::kv::StorePreferences; use flowy_sqlite::kv::StorePreferences;
use flowy_user::services::db::{get_user_profile, get_user_workspace, open_user_db};
use flowy_user_deps::entities::*; use flowy_user_deps::entities::*;
use crate::AppFlowyCoreConfig; use crate::AppFlowyCoreConfig;
@ -90,8 +89,13 @@ impl ServerProvider {
*self.server.write() = server_type; *self.server.write() = server_type;
} }
pub fn get_appflowy_cloud_server(&self) -> FlowyResult<Arc<dyn AppFlowyServer>> {
let server = self.get_server(&Server::AppFlowyCloud)?;
Ok(server)
}
/// Returns a [AppFlowyServer] trait implementation base on the provider_type. /// Returns a [AppFlowyServer] trait implementation base on the provider_type.
pub(crate) fn get_server(&self, server_type: &Server) -> FlowyResult<Arc<dyn AppFlowyServer>> { pub fn get_server(&self, server_type: &Server) -> FlowyResult<Arc<dyn AppFlowyServer>> {
if let Some(provider) = self.providers.read().get(server_type) { if let Some(provider) = self.providers.read().get(server_type) {
return Ok(provider.clone()); return Ok(provider.clone());
} }
@ -169,19 +173,22 @@ pub fn current_server_type(store_preferences: &Arc<StorePreferences>) -> Server
} }
struct LocalServerDBImpl { struct LocalServerDBImpl {
#[allow(dead_code)]
storage_path: String, storage_path: String,
} }
impl LocalServerDB for LocalServerDBImpl { impl LocalServerDB for LocalServerDBImpl {
fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> { fn get_user_profile(&self, _uid: i64) -> Result<UserProfile, FlowyError> {
let sqlite_db = open_user_db(&self.storage_path, uid)?; Err(
let user_profile = get_user_profile(&sqlite_db, uid)?; FlowyError::local_version_not_support()
Ok(user_profile) .with_context("LocalServer doesn't support get_user_profile"),
)
} }
fn get_user_workspace(&self, uid: i64) -> Result<Option<UserWorkspace>, FlowyError> { fn get_user_workspace(&self, _uid: i64) -> Result<Option<UserWorkspace>, FlowyError> {
let sqlite_db = open_user_db(&self.storage_path, uid)?; Err(
let user_workspace = get_user_workspace(&sqlite_db, uid)?; FlowyError::local_version_not_support()
Ok(user_workspace) .with_context("LocalServer doesn't support get_user_workspace"),
)
} }
} }

View File

@ -261,7 +261,7 @@ impl DatabaseCloudService for ServerProvider {
} }
impl DocumentCloudService for ServerProvider { impl DocumentCloudService for ServerProvider {
fn get_document_updates( fn get_document_doc_state(
&self, &self,
document_id: &str, document_id: &str,
workspace_id: &str, workspace_id: &str,
@ -272,7 +272,7 @@ impl DocumentCloudService for ServerProvider {
FutureResult::new(async move { FutureResult::new(async move {
server? server?
.document_service() .document_service()
.get_document_updates(&document_id, &workspace_id) .get_document_doc_state(&document_id, &workspace_id)
.await .await
}) })
} }

View File

@ -66,6 +66,10 @@ impl AppFlowyCore {
runtime.block_on(Self::init(config, cloned_runtime)) runtime.block_on(Self::init(config, cloned_runtime))
} }
pub fn close_db(&self) {
self.user_manager.close_db();
}
#[instrument(skip(config, runtime))] #[instrument(skip(config, runtime))]
async fn init(config: AppFlowyCoreConfig, runtime: Arc<AFPluginRuntime>) -> Self { async fn init(config: AppFlowyCoreConfig, runtime: Arc<AFPluginRuntime>) -> Self {
#[allow(clippy::if_same_then_else)] #[allow(clippy::if_same_then_else)]

View File

@ -16,7 +16,7 @@ use lru::LruCache;
use tokio::sync::{Mutex, RwLock}; use tokio::sync::{Mutex, RwLock};
use tracing::{event, instrument, trace}; use tracing::{event, instrument, trace};
use collab_integrate::collab_builder::AppFlowyCollabBuilder; use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB}; use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
use flowy_database_deps::cloud::DatabaseCloudService; use flowy_database_deps::cloud::DatabaseCloudService;
use flowy_error::{internal_error, FlowyError, FlowyResult}; use flowy_error::{internal_error, FlowyError, FlowyResult};
@ -457,6 +457,7 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
collab_db, collab_db,
collab_raw_data, collab_raw_data,
config, config,
CollabBuilderConfig::default().sync_enable(true),
)) ))
.unwrap() .unwrap()
} }

View File

@ -496,8 +496,10 @@ fn merge_groups(
merge_result.new_groups.extend(new_group_map.into_values()); merge_result.new_groups.extend(new_group_map.into_values());
// The `No status` group index is initialized to 0 // The `No status` group index is initialized to 0
if !no_status_group_inserted && no_status_group.is_some() { if !no_status_group_inserted {
merge_result.all_groups.insert(0, no_status_group.unwrap()); if let Some(group) = no_status_group {
merge_result.all_groups.insert(0, group);
}
} }
merge_result merge_result
} }

View File

@ -8,7 +8,7 @@ use lib_infra::future::FutureResult;
/// Each kind of server should implement this trait. Check out the [AppFlowyServerProvider] of /// Each kind of server should implement this trait. Check out the [AppFlowyServerProvider] of
/// [flowy-server] crate for more information. /// [flowy-server] crate for more information.
pub trait DocumentCloudService: Send + Sync + 'static { pub trait DocumentCloudService: Send + Sync + 'static {
fn get_document_updates( fn get_document_doc_state(
&self, &self,
document_id: &str, document_id: &str,
workspace_id: &str, workspace_id: &str,

View File

@ -3,6 +3,9 @@ use std::sync::Arc;
use std::sync::Weak; use std::sync::Weak;
use collab::core::collab::{CollabRawData, MutexCollab}; use collab::core::collab::{CollabRawData, MutexCollab};
use collab::core::collab_plugin::EncodedCollabV1;
use collab::core::origin::CollabOrigin;
use collab::preclude::Collab;
use collab_document::blocks::DocumentData; use collab_document::blocks::DocumentData;
use collab_document::document::Document; use collab_document::document::Document;
use collab_document::document_data::{default_document_collab_data, default_document_data}; use collab_document::document_data::{default_document_collab_data, default_document_data};
@ -12,10 +15,10 @@ use lru::LruCache;
use parking_lot::Mutex; use parking_lot::Mutex;
use tracing::{event, instrument}; use tracing::{event, instrument};
use collab_integrate::collab_builder::AppFlowyCollabBuilder; use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::RocksCollabDB; use collab_integrate::RocksCollabDB;
use flowy_document_deps::cloud::DocumentCloudService; use flowy_document_deps::cloud::DocumentCloudService;
use flowy_error::{internal_error, FlowyError, FlowyResult}; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_storage::FileStorageService; use flowy_storage::FileStorageService;
use crate::document::MutexDocument; use crate::document::MutexDocument;
@ -88,16 +91,26 @@ impl DocumentManager {
uid: i64, uid: i64,
doc_id: &str, doc_id: &str,
data: Option<DocumentData>, data: Option<DocumentData>,
) -> FlowyResult<Arc<MutexDocument>> { ) -> FlowyResult<()> {
tracing::trace!("create a document: {:?}", doc_id); tracing::trace!("create a document: {:?}", doc_id);
if self.is_doc_exist(doc_id).unwrap_or(false) { if self.is_doc_exist(doc_id).unwrap_or(false) {
self.get_document(doc_id).await Err(FlowyError::new(
ErrorCode::RecordAlreadyExists,
format!("document {} already exists", doc_id),
))
} else { } else {
let collab = self.collab_for_document(uid, doc_id, vec![]).await?; let encoded_collab_v1 =
let data = data.unwrap_or_else(default_document_data); doc_state_from_document_data(doc_id, data.unwrap_or_else(default_document_data))?;
let document = Arc::new(MutexDocument::create_with_data(collab, data)?); let collab = self
Ok(document) .collab_for_document(
uid,
doc_id,
vec![encoded_collab_v1.doc_state.to_vec()],
false,
)
.await?;
collab.lock().flush();
Ok(())
} }
} }
@ -113,7 +126,7 @@ impl DocumentManager {
// Try to get the document from the cloud service // Try to get the document from the cloud service
let result: Result<CollabRawData, FlowyError> = self let result: Result<CollabRawData, FlowyError> = self
.cloud_service .cloud_service
.get_document_updates(doc_id, &self.user.workspace_id()?) .get_document_doc_state(doc_id, &self.user.workspace_id()?)
.await; .await;
updates = match result { updates = match result {
@ -138,7 +151,7 @@ impl DocumentManager {
let uid = self.user.user_id()?; let uid = self.user.user_id()?;
event!(tracing::Level::DEBUG, "Initialize document: {}", doc_id); event!(tracing::Level::DEBUG, "Initialize document: {}", doc_id);
let collab = self.collab_for_document(uid, doc_id, updates).await?; let collab = self.collab_for_document(uid, doc_id, updates, true).await?;
let document = Arc::new(MutexDocument::open(doc_id, collab)?); let document = Arc::new(MutexDocument::open(doc_id, collab)?);
// save the document to the memory and read it from the memory if we open the same document again. // save the document to the memory and read it from the memory if we open the same document again.
@ -155,11 +168,13 @@ impl DocumentManager {
if !self.is_doc_exist(doc_id)? { if !self.is_doc_exist(doc_id)? {
updates = self updates = self
.cloud_service .cloud_service
.get_document_updates(doc_id, &self.user.workspace_id()?) .get_document_doc_state(doc_id, &self.user.workspace_id()?)
.await?; .await?;
} }
let uid = self.user.user_id()?; let uid = self.user.user_id()?;
let collab = self.collab_for_document(uid, doc_id, updates).await?; let collab = self
.collab_for_document(uid, doc_id, updates, false)
.await?;
Document::open(collab)? Document::open(collab)?
.get_document_data() .get_document_data()
.map_err(internal_error) .map_err(internal_error)
@ -218,12 +233,20 @@ impl DocumentManager {
&self, &self,
uid: i64, uid: i64,
doc_id: &str, doc_id: &str,
updates: Vec<Vec<u8>>, doc_state: Vec<Vec<u8>>,
sync_enable: bool,
) -> FlowyResult<Arc<MutexCollab>> { ) -> FlowyResult<Arc<MutexCollab>> {
let db = self.user.collab_db(uid)?; let db = self.user.collab_db(uid)?;
let collab = self let collab = self
.collab_builder .collab_builder
.build(uid, doc_id, CollabType::Document, updates, db) .build(
uid,
doc_id,
CollabType::Document,
doc_state,
db,
CollabBuilderConfig::default().sync_enable(sync_enable),
)
.await?; .await?;
Ok(collab) Ok(collab)
} }
@ -249,3 +272,16 @@ impl DocumentManager {
&self.storage_service &self.storage_service
} }
} }
fn doc_state_from_document_data(
doc_id: &str,
data: DocumentData,
) -> Result<EncodedCollabV1, FlowyError> {
let collab = Arc::new(MutexCollab::from_collab(Collab::new_with_origin(
CollabOrigin::Empty,
doc_id,
vec![],
)));
let _ = Document::create_with_data(collab.clone(), data).map_err(internal_error)?;
Ok(collab.encode_collab_v1())
}

View File

@ -14,11 +14,11 @@ async fn restore_document() {
let doc_id: String = gen_document_id(); let doc_id: String = gen_document_id();
let data = default_document_data(); let data = default_document_data();
let uid = test.user.user_id().unwrap(); let uid = test.user.user_id().unwrap();
let document_a = test test
.create_document(uid, &doc_id, Some(data.clone())) .create_document(uid, &doc_id, Some(data.clone()))
.await .await
.unwrap(); .unwrap();
let data_a = document_a.lock().get_document_data().unwrap(); let data_a = test.get_document_data(&doc_id).await.unwrap();
assert_eq!(data_a, data); assert_eq!(data_a, data);
let data_b = test let data_b = test

View File

@ -57,7 +57,13 @@ pub struct FakeUser {
impl FakeUser { impl FakeUser {
pub fn new() -> Self { pub fn new() -> Self {
Self { collab_db: db() } setup_log();
let tempdir = TempDir::new().unwrap();
let path = tempdir.into_path();
let collab_db = Arc::new(RocksCollabDB::open(path).unwrap());
Self { collab_db }
} }
} }
@ -79,7 +85,7 @@ impl DocumentUser for FakeUser {
} }
} }
pub fn db() -> Arc<RocksCollabDB> { pub fn setup_log() {
static START: Once = Once::new(); static START: Once = Once::new();
START.call_once(|| { START.call_once(|| {
std::env::set_var("RUST_LOG", "collab_persistence=trace"); std::env::set_var("RUST_LOG", "collab_persistence=trace");
@ -89,10 +95,6 @@ pub fn db() -> Arc<RocksCollabDB> {
.finish(); .finish();
subscriber.try_init().unwrap(); subscriber.try_init().unwrap();
}); });
let tempdir = TempDir::new().unwrap();
let path = tempdir.into_path();
Arc::new(RocksCollabDB::open(path).unwrap())
} }
pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> { pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> {
@ -129,7 +131,7 @@ pub fn gen_id() -> String {
pub struct LocalTestDocumentCloudServiceImpl(); pub struct LocalTestDocumentCloudServiceImpl();
impl DocumentCloudService for LocalTestDocumentCloudServiceImpl { impl DocumentCloudService for LocalTestDocumentCloudServiceImpl {
fn get_document_updates( fn get_document_doc_state(
&self, &self,
_document_id: &str, _document_id: &str,
_workspace_id: &str, _workspace_id: &str,

View File

@ -262,6 +262,9 @@ pub enum ErrorCode {
#[error("rocksdb internal error")] #[error("rocksdb internal error")]
RocksdbInternal = 87, RocksdbInternal = 87,
#[error("Local version not support")]
LocalVersionNotSupport = 88,
} }
impl ErrorCode { impl ErrorCode {

View File

@ -59,6 +59,10 @@ impl FlowyError {
self.code == ErrorCode::UserUnauthorized || self.code == ErrorCode::RecordNotFound self.code == ErrorCode::UserUnauthorized || self.code == ErrorCode::RecordNotFound
} }
pub fn is_local_version_not_support(&self) -> bool {
self.code == ErrorCode::LocalVersionNotSupport
}
static_flowy_error!(internal, ErrorCode::Internal); static_flowy_error!(internal, ErrorCode::Internal);
static_flowy_error!(record_not_found, ErrorCode::RecordNotFound); static_flowy_error!(record_not_found, ErrorCode::RecordNotFound);
static_flowy_error!(workspace_name, ErrorCode::WorkspaceNameInvalid); static_flowy_error!(workspace_name, ErrorCode::WorkspaceNameInvalid);
@ -105,6 +109,7 @@ impl FlowyError {
static_flowy_error!(collab_not_sync, ErrorCode::CollabDataNotSync); static_flowy_error!(collab_not_sync, ErrorCode::CollabDataNotSync);
static_flowy_error!(server_error, ErrorCode::InternalServerError); static_flowy_error!(server_error, ErrorCode::InternalServerError);
static_flowy_error!(not_support, ErrorCode::NotSupportYet); static_flowy_error!(not_support, ErrorCode::NotSupportYet);
static_flowy_error!(local_version_not_support, ErrorCode::LocalVersionNotSupport);
} }
impl std::convert::From<ErrorCode> for FlowyError { impl std::convert::From<ErrorCode> for FlowyError {

View File

@ -10,7 +10,7 @@ use collab_folder::{
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use tracing::{error, event, info, instrument, Level}; use tracing::{error, event, info, instrument, Level};
use collab_integrate::collab_builder::AppFlowyCollabBuilder; use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB}; use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_folder_deps::cloud::{gen_view_id, FolderCloudService}; use flowy_folder_deps::cloud::{gen_view_id, FolderCloudService};
@ -134,6 +134,7 @@ impl FolderManager {
collab_db, collab_db,
raw_data, raw_data,
&CollabPersistenceConfig::new().enable_snapshot(true), &CollabPersistenceConfig::new().enable_snapshot(true),
CollabBuilderConfig::default().sync_enable(true),
) )
.await?; .await?;
Ok(collab) Ok(collab)

View File

@ -67,7 +67,7 @@ impl FolderManager {
}, },
FolderInitDataSource::Cloud(raw_data) => { FolderInitDataSource::Cloud(raw_data) => {
if raw_data.is_empty() { if raw_data.is_empty() {
event!(Level::INFO, "remote folder data is empty, open from local"); event!(Level::ERROR, "remote folder data is empty, open from local");
self self
.open_local_folder(uid, &workspace_id, collab_db, folder_notifier) .open_local_folder(uid, &workspace_id, collab_db, folder_notifier)
.await? .await?
@ -111,7 +111,11 @@ impl FolderManager {
collab_db: Weak<RocksCollabDB>, collab_db: Weak<RocksCollabDB>,
folder_notifier: FolderNotify, folder_notifier: FolderNotify,
) -> Result<Folder, FlowyError> { ) -> Result<Folder, FlowyError> {
event!(Level::INFO, "Create folder with default folder builder"); event!(
Level::INFO,
"Create folder:{} with default folder builder",
workspace_id
);
let folder_data = let folder_data =
DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers).await; DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers).await;
let collab = self let collab = self

View File

@ -16,7 +16,7 @@ impl<T> DocumentCloudService for AFCloudDocumentCloudServiceImpl<T>
where where
T: AFServer, T: AFServer,
{ {
fn get_document_updates( fn get_document_doc_state(
&self, &self,
document_id: &str, document_id: &str,
workspace_id: &str, workspace_id: &str,

View File

@ -232,7 +232,7 @@ where
collab_object: &CollabObject, collab_object: &CollabObject,
data: Vec<u8>, data: Vec<u8>,
override_if_exist: bool, override_if_exist: bool,
) -> FutureResult<(), Error> { ) -> FutureResult<(), FlowyError> {
let try_get_client = self.server.try_get_client(); let try_get_client = self.server.try_get_client();
let collab_object = collab_object.clone(); let collab_object = collab_object.clone();
FutureResult::new(async move { FutureResult::new(async move {

View File

@ -168,6 +168,14 @@ impl AppFlowyServer for AppFlowyCloudServer {
Arc::new(AFCloudDocumentCloudServiceImpl(server)) Arc::new(AFCloudDocumentCloudServiceImpl(server))
} }
fn subscribe_ws_state(&self) -> Option<WSConnectStateReceiver> {
Some(self.ws_client.subscribe_connect_state())
}
fn get_ws_state(&self) -> ConnectState {
self.ws_client.get_state()
}
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn collab_ws_channel( fn collab_ws_channel(
&self, &self,

View File

@ -7,7 +7,7 @@ use lib_infra::future::FutureResult;
pub(crate) struct LocalServerDocumentCloudServiceImpl(); pub(crate) struct LocalServerDocumentCloudServiceImpl();
impl DocumentCloudService for LocalServerDocumentCloudServiceImpl { impl DocumentCloudService for LocalServerDocumentCloudServiceImpl {
fn get_document_updates( fn get_document_doc_state(
&self, &self,
_document_id: &str, _document_id: &str,
_workspace_id: &str, _workspace_id: &str,

View File

@ -121,7 +121,10 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
fn open_workspace(&self, _workspace_id: &str) -> FutureResult<UserWorkspace, FlowyError> { fn open_workspace(&self, _workspace_id: &str) -> FutureResult<UserWorkspace, FlowyError> {
FutureResult::new(async { FutureResult::new(async {
Err(FlowyError::not_support().with_context("local server doesn't support open workspace")) Err(
FlowyError::local_version_not_support()
.with_context("local server doesn't support open workspace"),
)
}) })
} }
@ -142,7 +145,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
_collab_object: &CollabObject, _collab_object: &CollabObject,
_data: Vec<u8>, _data: Vec<u8>,
_override_if_exist: bool, _override_if_exist: bool,
) -> FutureResult<(), Error> { ) -> FutureResult<(), FlowyError> {
FutureResult::new(async { Ok(()) }) FutureResult::new(async { Ok(()) })
} }
} }

View File

@ -2,7 +2,7 @@ use std::sync::Arc;
use anyhow::Error; use anyhow::Error;
use client_api::collab_sync::collab_msg::CollabMessage; use client_api::collab_sync::collab_msg::CollabMessage;
use client_api::ws::{WSConnectStateReceiver, WebSocketChannel}; use client_api::ws::{ConnectState, WSConnectStateReceiver, WebSocketChannel};
use collab_entity::CollabObject; use collab_entity::CollabObject;
use collab_plugins::cloud_storage::RemoteCollabStorage; use collab_plugins::cloud_storage::RemoteCollabStorage;
use parking_lot::RwLock; use parking_lot::RwLock;
@ -108,6 +108,14 @@ pub trait AppFlowyServer: Send + Sync + 'static {
None None
} }
fn subscribe_ws_state(&self) -> Option<WSConnectStateReceiver> {
None
}
fn get_ws_state(&self) -> ConnectState {
ConnectState::Closed
}
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn collab_ws_channel( fn collab_ws_channel(
&self, &self,

View File

@ -28,7 +28,7 @@ where
T: SupabaseServerService, T: SupabaseServerService,
{ {
#[tracing::instrument(level = "debug", skip(self))] #[tracing::instrument(level = "debug", skip(self))]
fn get_document_updates( fn get_document_doc_state(
&self, &self,
document_id: &str, document_id: &str,
workspace_id: &str, workspace_id: &str,

View File

@ -311,7 +311,7 @@ where
collab_object: &CollabObject, collab_object: &CollabObject,
data: Vec<u8>, data: Vec<u8>,
_override_if_exist: bool, _override_if_exist: bool,
) -> FutureResult<(), Error> { ) -> FutureResult<(), FlowyError> {
let try_get_postgrest = self.server.try_get_weak_postgrest(); let try_get_postgrest = self.server.try_get_weak_postgrest();
let cloned_collab_object = collab_object.clone(); let cloned_collab_object = collab_object.clone();
let (tx, rx) = channel(); let (tx, rx) = channel();

View File

@ -142,7 +142,7 @@ pub trait UserCloudService: Send + Sync + 'static {
collab_object: &CollabObject, collab_object: &CollabObject,
data: Vec<u8>, data: Vec<u8>,
override_if_exist: bool, override_if_exist: bool,
) -> FutureResult<(), Error>; ) -> FutureResult<(), FlowyError>;
} }
pub type UserUpdateReceiver = tokio::sync::mpsc::Receiver<UserUpdate>; pub type UserUpdateReceiver = tokio::sync::mpsc::Receiver<UserUpdate>;

Some files were not shown because too many files have changed in this diff Show More