mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
121ed5a06e
commit
df8409178b
34
.github/workflows/rust_ci.yaml
vendored
34
.github/workflows/rust_ci.yaml
vendored
@ -25,7 +25,7 @@ env:
|
||||
|
||||
jobs:
|
||||
test-on-ubuntu:
|
||||
environment: SUPABASE_CI
|
||||
# environment: SUPABASE_CI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
@ -52,21 +52,21 @@ jobs:
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- name: Create .env file in flowy-server
|
||||
working-directory: frontend/rust-lib/flowy-server
|
||||
run: |
|
||||
touch .env.ci
|
||||
echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
|
||||
- name: Create .env file in event-integration
|
||||
working-directory: frontend/rust-lib/event-integration
|
||||
run: |
|
||||
touch .env.ci
|
||||
echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
# - name: Create .env file in flowy-server
|
||||
# working-directory: frontend/rust-lib/flowy-server
|
||||
# run: |
|
||||
# touch .env.ci
|
||||
# echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
# echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
# echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
#
|
||||
# - name: Create .env file in event-integration
|
||||
# working-directory: frontend/rust-lib/event-integration
|
||||
# run: |
|
||||
# touch .env.ci
|
||||
# echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
# echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
# echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
|
||||
- name: Checkout appflowy cloud code
|
||||
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_EXTERNAL_GOOGLE_ENABLED=.*/GOTRUE_EXTERNAL_GOOGLE_ENABLED=true/' .env
|
||||
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
cat .env
|
||||
|
||||
- name: Run Docker-Compose
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
docker compose down -v --remove-orphans
|
||||
docker compose up -d
|
||||
|
||||
- name: Run rust-lib tests
|
||||
|
@ -16,7 +16,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
@ -48,7 +48,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
|
@ -21,7 +21,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findFirstCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
@ -64,7 +64,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findLastCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
|
@ -17,7 +17,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
final card1 = find.ancestor(
|
||||
of: find.findTextInFlowyText(card1Name),
|
||||
matching: find.byType(AppFlowyGroupCard),
|
||||
|
@ -17,7 +17,7 @@ void main() {
|
||||
testWidgets('expand/collapse hidden groups', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
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 expandFinder = find.byFlowySvg(FlowySvgs.hamburger_s_s);
|
||||
@ -46,7 +46,7 @@ void main() {
|
||||
testWidgets('hide first group, and show it again', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
// Tap the options of the first group
|
||||
final optionsFinder = find
|
||||
@ -82,7 +82,7 @@ void main() {
|
||||
testWidgets('delete a group', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 4);
|
||||
|
||||
|
@ -18,7 +18,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
const name = 'Card 1';
|
||||
final card1 = find.findTextInFlowyText(name);
|
||||
await tester.hoverOnWidget(
|
||||
@ -36,7 +36,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
const name = 'Card 1';
|
||||
final card1 = find.findTextInFlowyText(name);
|
||||
await tester.hoverOnWidget(
|
||||
@ -53,7 +53,7 @@ void main() {
|
||||
testWidgets('add new group', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
// assert number of groups
|
||||
tester.assertNumberOfGroups(4);
|
||||
|
@ -31,7 +31,7 @@ void main() {
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// reanme the name of the anon user
|
||||
await tester.openSettings();
|
||||
@ -41,13 +41,14 @@ void main() {
|
||||
matching: find.byType(UserNameInput),
|
||||
);
|
||||
await tester.enterText(userNameFinder, 'local_user');
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// sign up with Google
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// sign out
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.logout();
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
}
|
@ -25,7 +25,7 @@ void main() {
|
||||
cloudType: AuthenticatorType.appflowyCloud,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
});
|
||||
|
||||
testWidgets('sign out', (tester) async {
|
||||
|
@ -1,17 +1,17 @@
|
||||
import 'empty_test.dart' as preset_af_cloud_env_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 '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 {
|
||||
preset_af_cloud_env_test.main();
|
||||
|
||||
appflowy_cloud_auth_test.main();
|
||||
|
||||
document_sync_test.main();
|
||||
// document_sync_test.main();
|
||||
|
||||
user_sync_test.main();
|
||||
|
||||
// anon_user_continue_test.main();
|
||||
anon_user_continue_test.main();
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
const pageName = 'Sample';
|
||||
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.
|
||||
// Then the test will logout the user, and login with the same user. The data will
|
||||
@ -35,20 +35,21 @@ void main() {
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// create a new document called Sample
|
||||
await tester.createNewPageWithName(
|
||||
name: pageName,
|
||||
await tester.createNewPage(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// focus on the editor
|
||||
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();
|
||||
expect(find.text('hello world', findRichText: true), findsOneWidget);
|
||||
// TODO(nathan): remove the await
|
||||
// 6 seconds for data sync
|
||||
await tester.waitForSeconds(6);
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
@ -62,15 +63,10 @@ void main() {
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
|
||||
// The document will be synced from the server
|
||||
await tester.openPage(
|
||||
pageName,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
expect(find.text('hello world', findRichText: true), findsOneWidget);
|
||||
// the latest document will be opened, so the content must be the inputContent
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(inputContent, findRichText: true), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ void main() {
|
||||
testWidgets('sign in with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
});
|
||||
|
||||
testWidgets('sign out with supabase', (tester) async {
|
||||
|
@ -36,7 +36,7 @@ void main() {
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
@ -74,7 +74,7 @@ void main() {
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.openSettings();
|
||||
|
@ -14,7 +14,9 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
|
||||
// open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
@ -36,7 +38,7 @@ void main() {
|
||||
|
||||
// Create calendar view
|
||||
const name = 'calendar';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
@ -68,7 +70,9 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create the calendar view
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
|
||||
// Scroll until today's date cell is visible
|
||||
await tester.scrollToToday();
|
||||
@ -148,7 +152,9 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
final today = DateTime.now();
|
||||
|
@ -15,7 +15,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
@ -37,7 +37,7 @@ void main() {
|
||||
testWidgets('multiple text cells', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'my grid',
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
@ -77,7 +77,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Number;
|
||||
|
||||
@ -135,7 +135,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.assertCheckboxCell(rowIndex: 0, isSelected: false);
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 0);
|
||||
@ -153,7 +153,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.CreatedTime;
|
||||
// Create a create time field
|
||||
@ -171,7 +171,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.LastEditedTime;
|
||||
// Create a last time field
|
||||
@ -189,7 +189,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.DateTime;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
@ -282,7 +282,7 @@ void main() {
|
||||
const fieldType = FieldType.SingleSelect;
|
||||
|
||||
// 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
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
@ -358,7 +358,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.MultiSelect;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
@ -441,7 +441,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Checklist;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
@ -15,7 +15,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
|
@ -17,7 +17,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Name');
|
||||
@ -34,7 +34,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
@ -56,7 +56,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checklist, 'checklist');
|
||||
@ -70,7 +70,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checkbox, 'New field 1');
|
||||
@ -90,7 +90,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
@ -110,7 +110,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
@ -133,7 +133,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
@ -153,7 +153,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
for (final fieldType in [
|
||||
FieldType.Checklist,
|
||||
@ -185,7 +185,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
|
||||
|
@ -20,7 +20,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -34,7 +34,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -53,7 +53,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -80,7 +80,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -101,7 +101,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -135,7 +135,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -168,7 +168,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -216,7 +216,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -231,7 +231,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -271,7 +271,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -310,7 +310,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
@ -327,7 +327,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// 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
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
@ -13,7 +13,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateRowButtonInGrid();
|
||||
|
||||
// The initial number of rows is 3
|
||||
@ -25,7 +25,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
|
||||
@ -41,7 +41,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
|
||||
// Open the row menu and then click the delete
|
||||
@ -59,7 +59,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.assertRowCountInGridPage(3);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
@ -14,7 +14,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
@ -31,7 +31,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
|
@ -14,7 +14,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
@ -35,7 +35,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
@ -60,7 +60,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
|
@ -18,7 +18,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// mock the clipboard
|
||||
const lines = 3;
|
||||
|
@ -307,7 +307,7 @@ extension on WidgetTester {
|
||||
await tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await createNewPageWithName();
|
||||
await createNewPageWithNameUnderParent();
|
||||
|
||||
await beforeTest?.call(editor.getCurrentEditorState());
|
||||
|
||||
|
@ -17,7 +17,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// expect to see a new document
|
||||
tester.expectToSeePageName(
|
||||
|
@ -86,7 +86,7 @@ void main() {
|
||||
Future<String> createDocumentToReference(WidgetTester tester) async {
|
||||
final name = 'document_${uuid()}';
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: ViewLayoutPB.Document,
|
||||
openAfterCreated: false,
|
||||
|
@ -32,7 +32,7 @@ void main() {
|
||||
await tester.tapAt(Offset.zero);
|
||||
await tester.tapAt(Offset.zero);
|
||||
|
||||
await tester.createNewPageWithName(name: 'test');
|
||||
await tester.createNewPageWithNameUnderParent(name: 'test');
|
||||
await tester.openPage(gettingStarted);
|
||||
|
||||
// check the status again
|
||||
|
@ -136,13 +136,13 @@ Future<void> insertReferenceDatabase(
|
||||
// create a new grid
|
||||
final id = uuid();
|
||||
final name = '${layout.name}_$id';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: layout,
|
||||
openAfterCreated: false,
|
||||
);
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'insert_a_reference_${layout.name}',
|
||||
layout: ViewLayoutPB.Document,
|
||||
openAfterCreated: true,
|
||||
@ -171,7 +171,7 @@ Future<void> createInlineDatabase(
|
||||
) async {
|
||||
// create a new document
|
||||
final documentName = 'insert_a_inline_${layout.name}';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: documentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
openAfterCreated: true,
|
||||
|
@ -31,7 +31,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
@ -79,7 +79,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
@ -133,7 +133,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
@ -20,7 +20,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'math equation',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
@ -66,7 +66,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'math equation',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
@ -105,14 +105,14 @@ Future<String> insertInlinePage(
|
||||
// create a new grid
|
||||
final id = uuid();
|
||||
final name = '${layout.name}_$id';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: layout,
|
||||
openAfterCreated: false,
|
||||
);
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'insert_a_inline_page_${layout.name}',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
@ -24,7 +24,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
|
@ -15,7 +15,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'outline_test',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
@ -32,7 +32,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'outline_test',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
@ -40,7 +40,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@ -86,7 +86,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@ -125,7 +125,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@ -162,7 +162,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@ -196,7 +196,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
|
@ -18,7 +18,7 @@ void main() {
|
||||
|
||||
// create a new document called Sample
|
||||
const pageName = 'Sample';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: pageName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
@ -75,7 +75,7 @@ void main() {
|
||||
|
||||
// create a new document called Sample
|
||||
const pageName = 'Sample';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: pageName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
@ -19,7 +19,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
@ -71,7 +71,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
@ -14,7 +14,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.language);
|
||||
|
@ -31,7 +31,7 @@ void main() {
|
||||
].map((e) => 'document_$e').toList();
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
final parentName = i == 0 ? gettingStarted : names[i - 1];
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: names[i],
|
||||
parentName: parentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
@ -113,7 +113,7 @@ void main() {
|
||||
final names = [1, 2].map((e) => 'document_$e').toList();
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
final parentName = i == 0 ? gettingStarted : names[i - 1];
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: names[i],
|
||||
parentName: parentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
@ -170,7 +170,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
await tester.favoriteViewByName(gettingStarted);
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
@ -190,7 +190,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
await tester.favoriteViewByName(gettingStarted);
|
||||
await tester.hoverOnPageName(
|
||||
gettingStarted,
|
||||
|
@ -18,7 +18,7 @@ void main() {
|
||||
|
||||
// create document, board, grid and calendar views
|
||||
for (final value in ViewLayoutPB.values) {
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
layout: value,
|
||||
@ -46,7 +46,7 @@ void main() {
|
||||
|
||||
// create document, board, grid and calendar views
|
||||
for (final value in ViewLayoutPB.values) {
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
layout: value,
|
||||
|
@ -41,7 +41,7 @@ void main() {
|
||||
for (final layout in ViewLayoutPB.values) {
|
||||
// create a new page
|
||||
final name = 'AppFlowy_$layout';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: layout,
|
||||
);
|
||||
@ -79,7 +79,7 @@ void main() {
|
||||
final names = [1, 2, 3, 4].map((e) => 'document_$e').toList();
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
final parentName = i == 0 ? gettingStarted : names[i - 1];
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: names[i],
|
||||
parentName: parentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
@ -144,14 +144,14 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
const document = 'document';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: document,
|
||||
openAfterCreated: false,
|
||||
);
|
||||
tester.expectToSeePageName(document, layout: ViewLayoutPB.Document);
|
||||
|
||||
const grid = 'grid';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: grid,
|
||||
layout: ViewLayoutPB.Grid,
|
||||
openAfterCreated: false,
|
||||
@ -187,7 +187,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
const grid = 'grid';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: grid,
|
||||
layout: ViewLayoutPB.Grid,
|
||||
openAfterCreated: true,
|
||||
|
@ -33,7 +33,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// switch to user B
|
||||
{
|
||||
@ -51,7 +51,7 @@ void main() {
|
||||
);
|
||||
await tester.tapCustomLocationButton();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// set user name for userB
|
||||
await tester.openSettings();
|
||||
@ -71,7 +71,7 @@ void main() {
|
||||
await tester.tapCustomLocationButton();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
tester.expectToSeeUserName(userA);
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ void main() {
|
||||
await tester.tapCustomLocationButton();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
tester.expectToSeeUserName(userB);
|
||||
}
|
||||
});
|
||||
@ -99,7 +99,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// home and readme document
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// open settings and restore the location
|
||||
await tester.openSettings();
|
||||
|
@ -32,12 +32,12 @@ void main() {
|
||||
findsNothing,
|
||||
);
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: _documentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: _documentTwoName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
@ -290,7 +290,7 @@ extension CommonOperations on WidgetTester {
|
||||
await tapButton(markdownButton);
|
||||
}
|
||||
|
||||
Future<void> createNewPageWithName({
|
||||
Future<void> createNewPageWithNameUnderParent({
|
||||
String? name,
|
||||
ViewLayoutPB layout = ViewLayoutPB.Document,
|
||||
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(
|
||||
LogicalKeyboardKey key, {
|
||||
bool isControlPressed = false,
|
||||
|
@ -18,7 +18,7 @@ const String gettingStarted = 'Getting started';
|
||||
|
||||
extension Expectation on WidgetTester {
|
||||
/// 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);
|
||||
await pumpUntilFound(finder);
|
||||
expect(finder, findsOneWidget);
|
||||
@ -27,6 +27,12 @@ extension Expectation on WidgetTester {
|
||||
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.
|
||||
void expectToSeePageName(
|
||||
String name, {
|
||||
|
@ -49,7 +49,6 @@ class NetworkListener {
|
||||
return NetworkTypePB.NetworkUnknown;
|
||||
}
|
||||
}();
|
||||
Log.info("Network type: $networkType");
|
||||
final state = NetworkStatePB.create()..ty = networkType;
|
||||
UserEventUpdateNetworkState(state).send().then((result) {
|
||||
result.fold(
|
||||
|
17
frontend/appflowy_flutter/lib/env/cloud_env.dart
vendored
17
frontend/appflowy_flutter/lib/env/cloud_env.dart
vendored
@ -105,7 +105,7 @@ enum AuthenticatorType {
|
||||
supabase,
|
||||
appflowyCloud;
|
||||
|
||||
bool get isEnabled => this != AuthenticatorType.local;
|
||||
bool get isLocal => this == AuthenticatorType.local;
|
||||
int get value {
|
||||
switch (this) {
|
||||
case AuthenticatorType.local:
|
||||
@ -161,8 +161,12 @@ class AppFlowyCloudSharedEnv {
|
||||
if (Env.enableCustomCloud) {
|
||||
// Use the custom cloud configuration.
|
||||
final cloudType = await getAuthenticatorType();
|
||||
final appflowyCloudConfig = await getAppFlowyCloudConfig();
|
||||
final supabaseCloudConfig = await getSupabaseCloudConfig();
|
||||
final appflowyCloudConfig = cloudType.isLocal
|
||||
? AppFlowyCloudConfiguration.defaultConfig()
|
||||
: await getAppFlowyCloudConfig();
|
||||
final supabaseCloudConfig = cloudType.isLocal
|
||||
? SupabaseConfiguration.defaultConfig()
|
||||
: await getSupabaseCloudConfig();
|
||||
|
||||
return AppFlowyCloudSharedEnv(
|
||||
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(
|
||||
|
@ -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/workspace/prelude.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-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_service.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
@ -55,6 +56,7 @@ class DependencyResolver {
|
||||
|
||||
Future<void> _resolveCloudDeps(GetIt getIt) async {
|
||||
final env = await AppFlowyCloudSharedEnv.fromEnv();
|
||||
Log.info("cloud setting: \n$env");
|
||||
getIt.registerFactory<AppFlowyCloudSharedEnv>(() => env);
|
||||
|
||||
if (isAppFlowyCloudEnabled) {
|
||||
|
@ -4,7 +4,6 @@ import 'dart:io';
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy_backend/appflowy_backend.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
@ -139,9 +138,14 @@ Future<void> initGetIt(
|
||||
LaunchConfiguration config,
|
||||
) async {
|
||||
getIt.registerFactory<EntryPoint>(() => f);
|
||||
getIt.registerLazySingleton<FlowySDK>(() {
|
||||
return FlowySDK();
|
||||
});
|
||||
getIt.registerLazySingleton<FlowySDK>(
|
||||
() {
|
||||
return FlowySDK();
|
||||
},
|
||||
dispose: (sdk) async {
|
||||
await sdk.dispose();
|
||||
},
|
||||
);
|
||||
getIt.registerLazySingleton<AppLauncher>(
|
||||
() => AppLauncher(
|
||||
context: LaunchContext(
|
||||
@ -205,7 +209,6 @@ class AppLauncher {
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
Log.info('AppLauncher dispose');
|
||||
for (final task in tasks) {
|
||||
await task.dispose();
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ class AppFlowyCloudDeepLink {
|
||||
(_) async {
|
||||
final deviceId = await getDeviceId();
|
||||
final payload = OauthSignInPB(
|
||||
authType: AuthenticatorPB.AppFlowyCloud,
|
||||
authenticator: AuthenticatorPB.AppFlowyCloud,
|
||||
map: {
|
||||
AuthServiceMapKeys.signInURL: uri.toString(),
|
||||
AuthServiceMapKeys.deviceId: deviceId,
|
||||
|
@ -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/user_service.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-user/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
@ -45,7 +46,7 @@ class AppFlowyCloudMockAuthService implements AuthService {
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
final payload = SignInUrlPayloadPB.create()
|
||||
..authType = AuthenticatorPB.AppFlowyCloud
|
||||
..authenticator = AuthenticatorPB.AppFlowyCloud
|
||||
// don't use nanoid here, the gotrue server will transform the email
|
||||
..email = userEmail;
|
||||
|
||||
@ -55,17 +56,22 @@ class AppFlowyCloudMockAuthService implements AuthService {
|
||||
return getSignInURLResult.fold(
|
||||
(urlPB) async {
|
||||
final payload = OauthSignInPB(
|
||||
authType: AuthenticatorPB.AppFlowyCloud,
|
||||
authenticator: AuthenticatorPB.AppFlowyCloud,
|
||||
map: {
|
||||
AuthServiceMapKeys.signInURL: urlPB.signInUrl,
|
||||
AuthServiceMapKeys.deviceId: deviceId,
|
||||
},
|
||||
);
|
||||
return await UserEventOauthSignIn(payload)
|
||||
.send()
|
||||
.then((value) => value.swap());
|
||||
Log.info("UserEventOauthSignIn with payload: $payload");
|
||||
return await UserEventOauthSignIn(payload).send().then((value) {
|
||||
value.fold((l) => null, (err) => Log.error(err));
|
||||
return value.swap();
|
||||
});
|
||||
},
|
||||
(r) {
|
||||
Log.error(r);
|
||||
return left(r);
|
||||
},
|
||||
(r) => left(r),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ class SupabaseAuthService implements AuthService {
|
||||
required Map<String, String> map,
|
||||
}) async {
|
||||
final payload = OauthSignInPB(
|
||||
authType: AuthenticatorPB.Supabase,
|
||||
authenticator: AuthenticatorPB.Supabase,
|
||||
map: map,
|
||||
);
|
||||
|
||||
|
@ -67,7 +67,7 @@ class SupabaseMockAuthService implements AuthService {
|
||||
|
||||
// Create the OAuth sign-in payload.
|
||||
final payload = OauthSignInPB(
|
||||
authType: AuthenticatorPB.Supabase,
|
||||
authenticator: AuthenticatorPB.Supabase,
|
||||
map: {
|
||||
AuthServiceMapKeys.uuid: uuid,
|
||||
AuthServiceMapKeys.email: email,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
@ -95,9 +94,6 @@ class SplashScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
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
|
||||
if (isAuthEnabled || PlatformExtension.isMobile) {
|
||||
context.go(SignInScreen.routeName);
|
||||
|
@ -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/view/view_listener.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-folder2/view.pb.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
@ -256,9 +255,6 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
||||
Future<ViewPB?> _updateChildViews(
|
||||
ChildViewUpdatePB update,
|
||||
) async {
|
||||
Log.debug(
|
||||
'received child views of ${this.view.name}(${this.view.id}) update, $update',
|
||||
);
|
||||
if (update.createChildViews.isNotEmpty) {
|
||||
// refresh the child views if the update isn't empty
|
||||
// because there's no info to get the inserted index.
|
||||
|
@ -24,7 +24,7 @@ class FlowySDK {
|
||||
|
||||
FlowySDK();
|
||||
|
||||
void dispose() {}
|
||||
Future<void> dispose() async {}
|
||||
|
||||
Future<void> init(String configuration) async {
|
||||
final port = RustStreamReceiver.shared.port;
|
||||
|
40
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
40
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -139,7 +139,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -786,7 +786,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -1325,7 +1325,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa 1.0.6",
|
||||
"phf 0.11.2",
|
||||
"phf 0.8.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -1471,7 +1471,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2842,7 +2842,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2858,7 +2858,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3280,7 +3280,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -4378,7 +4378,6 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -4470,19 +4469,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -4724,7 +4710,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck 0.4.1",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
@ -4745,7 +4731,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.32",
|
||||
@ -5040,7 +5026,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -5062,7 +5048,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-protocol"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -5809,7 +5795,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -7699,7 +7685,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "workspace-template"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -57,7 +57,7 @@ custom-protocol = ["tauri/custom-protocol"]
|
||||
# Run the script:
|
||||
# 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.
|
||||
# Working directory: frontend
|
||||
#
|
||||
|
42
frontend/rust-lib/Cargo.lock
generated
42
frontend/rust-lib/Cargo.lock
generated
@ -125,7 +125,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -667,7 +667,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -1149,7 +1149,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa",
|
||||
"phf 0.11.2",
|
||||
"phf 0.8.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -1277,7 +1277,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2483,7 +2483,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2499,7 +2499,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2860,7 +2860,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -3665,7 +3665,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros 0.8.0",
|
||||
"phf_macros",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
@ -3685,7 +3685,6 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -3753,19 +3752,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -3969,7 +3955,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck 0.4.1",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
@ -3990,7 +3976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.31",
|
||||
@ -4329,7 +4315,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -4351,7 +4337,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-protocol"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -5020,7 +5006,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -6401,7 +6387,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "workspace-template"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -99,7 +99,7 @@ incremental = false
|
||||
# Run the script:
|
||||
# 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.
|
||||
# Working directory: frontend
|
||||
#
|
||||
|
@ -71,6 +71,23 @@ pub struct AppFlowyCollabBuilder {
|
||||
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 {
|
||||
pub fn new<T: CollabStorageProvider>(storage_provider: T, device_id: String) -> Self {
|
||||
Self {
|
||||
@ -144,12 +161,21 @@ impl AppFlowyCollabBuilder {
|
||||
uid: i64,
|
||||
object_id: &str,
|
||||
object_type: CollabType,
|
||||
raw_data: CollabRawData,
|
||||
doc_state: CollabRawData,
|
||||
collab_db: Weak<RocksCollabDB>,
|
||||
build_config: CollabBuilderConfig,
|
||||
) -> Result<Arc<MutexCollab>, Error> {
|
||||
let config = CollabPersistenceConfig::default();
|
||||
let persistence_config = CollabPersistenceConfig::default();
|
||||
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
|
||||
}
|
||||
|
||||
@ -167,88 +193,92 @@ impl AppFlowyCollabBuilder {
|
||||
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type.
|
||||
/// - `collab_db`: A weak reference to the [RocksCollabDB].
|
||||
///
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn build_with_config(
|
||||
&self,
|
||||
uid: i64,
|
||||
object_id: &str,
|
||||
object_type: CollabType,
|
||||
collab_db: Weak<RocksCollabDB>,
|
||||
collab_raw_data: CollabRawData,
|
||||
config: &CollabPersistenceConfig,
|
||||
doc_state: CollabRawData,
|
||||
persistence_config: &CollabPersistenceConfig,
|
||||
build_config: CollabBuilderConfig,
|
||||
) -> Result<Arc<MutexCollab>, Error> {
|
||||
let collab = Arc::new(
|
||||
CollabBuilder::new(uid, object_id)
|
||||
.with_raw_data(collab_raw_data)
|
||||
.with_raw_data(doc_state)
|
||||
.with_plugin(RocksdbDiskPlugin::new_with_config(
|
||||
uid,
|
||||
collab_db.clone(),
|
||||
config.clone(),
|
||||
persistence_config.clone(),
|
||||
self.rocksdb_backup.lock().clone(),
|
||||
))
|
||||
.with_device_id(self.device_id.clone())
|
||||
.build()?,
|
||||
);
|
||||
{
|
||||
let cloud_storage_type = self.cloud_storage.read().await.storage_source();
|
||||
let collab_object = self.collab_object(uid, object_id, object_type)?;
|
||||
let span = tracing::span!(tracing::Level::TRACE, "collab_builder", object_id = %object_id);
|
||||
let _enter = span.enter();
|
||||
match cloud_storage_type {
|
||||
CollabDataSource::AppFlowyCloud => {
|
||||
#[cfg(feature = "appflowy_cloud_integrate")]
|
||||
{
|
||||
trace!("init appflowy cloud collab plugins");
|
||||
let local_collab = Arc::downgrade(&collab);
|
||||
let plugins = self
|
||||
.cloud_storage
|
||||
.read()
|
||||
.await
|
||||
.get_plugins(CollabStorageProviderContext::AppFlowyCloud {
|
||||
uid,
|
||||
collab_object: collab_object.clone(),
|
||||
local_collab,
|
||||
})
|
||||
.await;
|
||||
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 _enter = span.enter();
|
||||
match cloud_storage_type {
|
||||
CollabDataSource::AppFlowyCloud => {
|
||||
#[cfg(feature = "appflowy_cloud_integrate")]
|
||||
{
|
||||
trace!("init appflowy cloud collab plugins");
|
||||
let local_collab = Arc::downgrade(&collab);
|
||||
let plugins = self
|
||||
.cloud_storage
|
||||
.read()
|
||||
.await
|
||||
.get_plugins(CollabStorageProviderContext::AppFlowyCloud {
|
||||
uid,
|
||||
collab_object: collab_object.clone(),
|
||||
local_collab,
|
||||
})
|
||||
.await;
|
||||
|
||||
trace!("add appflowy cloud collab plugins: {}", plugins.len());
|
||||
for plugin in plugins {
|
||||
collab.lock().add_plugin(plugin);
|
||||
trace!("add appflowy cloud collab plugins: {}", plugins.len());
|
||||
for plugin in plugins {
|
||||
collab.lock().add_plugin(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
CollabDataSource::Supabase => {
|
||||
#[cfg(feature = "supabase_integrate")]
|
||||
{
|
||||
trace!("init supabase collab plugins");
|
||||
let local_collab = Arc::downgrade(&collab);
|
||||
let local_collab_db = collab_db.clone();
|
||||
let plugins = self
|
||||
.cloud_storage
|
||||
.read()
|
||||
.await
|
||||
.get_plugins(CollabStorageProviderContext::Supabase {
|
||||
uid,
|
||||
collab_object: collab_object.clone(),
|
||||
local_collab,
|
||||
local_collab_db,
|
||||
})
|
||||
.await;
|
||||
for plugin in plugins {
|
||||
collab.lock().add_plugin(plugin);
|
||||
},
|
||||
CollabDataSource::Supabase => {
|
||||
#[cfg(feature = "supabase_integrate")]
|
||||
{
|
||||
trace!("init supabase collab plugins");
|
||||
let local_collab = Arc::downgrade(&collab);
|
||||
let local_collab_db = collab_db.clone();
|
||||
let plugins = self
|
||||
.cloud_storage
|
||||
.read()
|
||||
.await
|
||||
.get_plugins(CollabStorageProviderContext::Supabase {
|
||||
uid,
|
||||
collab_object: collab_object.clone(),
|
||||
local_collab,
|
||||
local_collab_db,
|
||||
})
|
||||
.await;
|
||||
for plugin in plugins {
|
||||
collab.lock().add_plugin(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
CollabDataSource::Local => {},
|
||||
},
|
||||
CollabDataSource::Local => {},
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
uid,
|
||||
collab_object,
|
||||
snapshot_persistence.clone(),
|
||||
collab_db,
|
||||
config.snapshot_per_update,
|
||||
persistence_config.snapshot_per_update,
|
||||
);
|
||||
// tracing::trace!("add snapshot plugin: {}", object_id);
|
||||
collab.lock().add_plugin(Arc::new(snapshot_plugin));
|
||||
|
@ -70,6 +70,13 @@ pub extern "C" fn init_sdk(data: *mut c_char) -> i64 {
|
||||
DEFAULT_NAME.to_string(),
|
||||
)
|
||||
.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));
|
||||
0
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ use crate::EventIntegrationTest;
|
||||
const TEXT_BLOCK_TY: &str = "paragraph";
|
||||
|
||||
pub struct DocumentEventTest {
|
||||
inner: EventIntegrationTest,
|
||||
event_test: EventIntegrationTest,
|
||||
}
|
||||
|
||||
pub struct OpenDocumentData {
|
||||
@ -29,15 +29,15 @@ pub struct OpenDocumentData {
|
||||
impl DocumentEventTest {
|
||||
pub async fn new() -> Self {
|
||||
let sdk = EventIntegrationTest::new_with_guest_user().await;
|
||||
Self { inner: sdk }
|
||||
Self { event_test: sdk }
|
||||
}
|
||||
|
||||
pub fn new_with_core(core: EventIntegrationTest) -> Self {
|
||||
Self { inner: core }
|
||||
Self { event_test: core }
|
||||
}
|
||||
|
||||
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 parent_id = current_workspace.id.clone();
|
||||
|
||||
@ -61,22 +61,12 @@ impl DocumentEventTest {
|
||||
}
|
||||
|
||||
pub async fn open_document(&self, doc_id: String) -> OpenDocumentData {
|
||||
let core = &self.inner;
|
||||
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 }
|
||||
self.event_test.open_document(doc_id).await
|
||||
}
|
||||
|
||||
pub async fn get_block(&self, doc_id: &str, block_id: &str) -> Option<BlockPB> {
|
||||
let document = self.open_document(doc_id.to_string()).await;
|
||||
document.data.blocks.get(block_id).cloned()
|
||||
let document_data = self.event_test.open_document(doc_id.to_string()).await;
|
||||
document_data.data.blocks.get(block_id).cloned()
|
||||
}
|
||||
|
||||
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 {
|
||||
let document = self.open_document(doc_id.to_string()).await;
|
||||
document.data
|
||||
let document_data = self.event_test.open_document(doc_id.to_string()).await;
|
||||
document_data.data
|
||||
}
|
||||
|
||||
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) {
|
||||
let core = &self.inner;
|
||||
let core = &self.event_test;
|
||||
EventBuilder::new(core.clone())
|
||||
.event(DocumentEvent::ApplyAction)
|
||||
.payload(payload)
|
||||
@ -116,7 +106,7 @@ impl DocumentEventTest {
|
||||
&self,
|
||||
payload: ConvertDocumentPayloadPB,
|
||||
) -> ConvertDocumentResponsePB {
|
||||
let core = &self.inner;
|
||||
let core = &self.event_test;
|
||||
EventBuilder::new(core.clone())
|
||||
.event(DocumentEvent::ConvertDocument)
|
||||
.payload(payload)
|
||||
@ -130,7 +120,7 @@ impl DocumentEventTest {
|
||||
&self,
|
||||
payload: ConvertDataToJsonPayloadPB,
|
||||
) -> ConvertDataToJsonResponsePB {
|
||||
let core = &self.inner;
|
||||
let core = &self.event_test;
|
||||
EventBuilder::new(core.clone())
|
||||
.event(DocumentEvent::ConvertDataToJSON)
|
||||
.payload(payload)
|
||||
@ -140,7 +130,7 @@ impl DocumentEventTest {
|
||||
}
|
||||
|
||||
pub async fn create_text(&self, payload: TextDeltaPayloadPB) {
|
||||
let core = &self.inner;
|
||||
let core = &self.event_test;
|
||||
EventBuilder::new(core.clone())
|
||||
.event(DocumentEvent::CreateText)
|
||||
.payload(payload)
|
||||
@ -149,7 +139,7 @@ impl DocumentEventTest {
|
||||
}
|
||||
|
||||
pub async fn apply_text_delta(&self, payload: TextDeltaPayloadPB) {
|
||||
let core = &self.inner;
|
||||
let core = &self.event_test;
|
||||
EventBuilder::new(core.clone())
|
||||
.event(DocumentEvent::ApplyTextDeltaEvent)
|
||||
.payload(payload)
|
||||
@ -158,7 +148,7 @@ impl DocumentEventTest {
|
||||
}
|
||||
|
||||
pub async fn undo(&self, doc_id: String) -> DocumentRedoUndoResponsePB {
|
||||
let core = &self.inner;
|
||||
let core = &self.event_test;
|
||||
let payload = DocumentRedoUndoPayloadPB {
|
||||
document_id: doc_id.clone(),
|
||||
};
|
||||
@ -171,7 +161,7 @@ impl DocumentEventTest {
|
||||
}
|
||||
|
||||
pub async fn redo(&self, doc_id: String) -> DocumentRedoUndoResponsePB {
|
||||
let core = &self.inner;
|
||||
let core = &self.event_test;
|
||||
let payload = DocumentRedoUndoPayloadPB {
|
||||
document_id: doc_id.clone(),
|
||||
};
|
||||
@ -184,7 +174,7 @@ impl DocumentEventTest {
|
||||
}
|
||||
|
||||
pub async fn can_undo_redo(&self, doc_id: String) -> DocumentRedoUndoResponsePB {
|
||||
let core = &self.inner;
|
||||
let core = &self.event_test;
|
||||
let payload = DocumentRedoUndoPayloadPB {
|
||||
document_id: doc_id.clone(),
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ use crate::event_builder::EventBuilder;
|
||||
use crate::EventIntegrationTest;
|
||||
|
||||
impl EventIntegrationTest {
|
||||
pub async fn create_document(
|
||||
pub async fn create_and_open_document(
|
||||
&self,
|
||||
parent_id: &str,
|
||||
name: String,
|
||||
@ -86,11 +86,11 @@ impl EventIntegrationTest {
|
||||
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 cloud_service = self.document_manager.get_cloud_service().clone();
|
||||
let remote_updates = cloud_service
|
||||
.get_document_updates(document_id, &workspace_id)
|
||||
.get_document_doc_state(document_id, &workspace_id)
|
||||
.await
|
||||
.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![]);
|
||||
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);
|
||||
});
|
||||
let document = Document::open(Arc::new(collab)).unwrap();
|
||||
|
@ -75,6 +75,14 @@ impl EventIntegrationTest {
|
||||
.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) {
|
||||
let payload = RepeatedViewIdPB {
|
||||
items: vec![view_id.to_string()],
|
||||
|
@ -1,13 +1,17 @@
|
||||
use std::env::temp_dir;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use nanoid::nanoid;
|
||||
use parking_lot::RwLock;
|
||||
use tokio::select;
|
||||
use tokio::time::sleep;
|
||||
|
||||
use flowy_core::config::AppFlowyCoreConfig;
|
||||
use flowy_core::AppFlowyCore;
|
||||
use flowy_notification::register_notification_sender;
|
||||
use flowy_server::AppFlowyServer;
|
||||
use flowy_user::entities::AuthenticatorPB;
|
||||
|
||||
use crate::user_event::TestNotificationSender;
|
||||
@ -21,8 +25,8 @@ pub mod user_event;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventIntegrationTest {
|
||||
pub auth_type: Arc<RwLock<AuthenticatorPB>>,
|
||||
pub inner: AppFlowyCore,
|
||||
pub authenticator: Arc<RwLock<AuthenticatorPB>>,
|
||||
pub appflowy_core: AppFlowyCore,
|
||||
#[allow(dead_code)]
|
||||
cleaner: Arc<Cleaner>,
|
||||
pub notification_sender: TestNotificationSender,
|
||||
@ -34,6 +38,7 @@ impl EventIntegrationTest {
|
||||
std::fs::create_dir_all(&temp_dir).unwrap();
|
||||
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 {
|
||||
let path = path_buf.to_str().unwrap().to_string();
|
||||
let device_id = uuid::Uuid::new_v4().to_string();
|
||||
@ -42,22 +47,61 @@ impl EventIntegrationTest {
|
||||
vec![
|
||||
"flowy_test".to_string(),
|
||||
"tokio".to_string(),
|
||||
"lib_dispatch".to_string(),
|
||||
// "lib_dispatch".to_string(),
|
||||
],
|
||||
);
|
||||
|
||||
let inner = init_core(config).await;
|
||||
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());
|
||||
|
||||
// In case of dropping the runtime that runs the core, we need to forget the dispatcher
|
||||
std::mem::forget(inner.dispatcher());
|
||||
Self {
|
||||
inner,
|
||||
auth_type,
|
||||
appflowy_core: inner,
|
||||
authenticator,
|
||||
notification_sender,
|
||||
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")]
|
||||
@ -79,7 +123,7 @@ impl std::ops::Deref for EventIntegrationTest {
|
||||
type Target = AppFlowyCore;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
&self.appflowy_core
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ impl EventIntegrationTest {
|
||||
.unwrap();
|
||||
|
||||
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
|
||||
.parse::<UserProfilePB, FlowyError>()
|
||||
.unwrap()
|
||||
@ -88,7 +88,7 @@ impl EventIntegrationTest {
|
||||
let map = third_party_sign_up_param(Uuid::new_v4().to_string());
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthenticatorPB::Supabase,
|
||||
authenticator: AuthenticatorPB::Supabase,
|
||||
};
|
||||
|
||||
EventBuilder::new(self.clone())
|
||||
@ -107,7 +107,7 @@ impl EventIntegrationTest {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -133,7 +133,7 @@ impl EventIntegrationTest {
|
||||
pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
|
||||
let payload = SignInUrlPayloadPB {
|
||||
email: email.to_string(),
|
||||
auth_type: AuthenticatorPB::AppFlowyCloud,
|
||||
authenticator: AuthenticatorPB::AppFlowyCloud,
|
||||
};
|
||||
let sign_in_url = EventBuilder::new(self.clone())
|
||||
.event(GenerateSignInURL)
|
||||
@ -148,7 +148,7 @@ impl EventIntegrationTest {
|
||||
map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string());
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthenticatorPB::AppFlowyCloud,
|
||||
authenticator: AuthenticatorPB::AppFlowyCloud,
|
||||
};
|
||||
|
||||
let user_profile = EventBuilder::new(self.clone())
|
||||
@ -175,7 +175,7 @@ impl EventIntegrationTest {
|
||||
);
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthenticatorPB::Supabase,
|
||||
authenticator: AuthenticatorPB::Supabase,
|
||||
};
|
||||
|
||||
let user_profile = EventBuilder::new(self.clone())
|
||||
@ -246,7 +246,7 @@ impl TestNotificationSender {
|
||||
F: Fn(&T) -> bool + Send + 'static,
|
||||
{
|
||||
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();
|
||||
af_spawn(async move {
|
||||
while let Ok(value) = receiver.recv().await {
|
||||
|
BIN
frontend/rust-lib/event-integration/tests/asset/040_local.zip
Normal file
BIN
frontend/rust-lib/event-integration/tests/asset/040_local.zip
Normal file
Binary file not shown.
@ -1,34 +1,37 @@
|
||||
use std::time::Duration;
|
||||
|
||||
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 crate::document::af_cloud_test::util::AFCloudDocumentTest;
|
||||
use crate::util::receive_with_timeout;
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_edit_document_test() {
|
||||
if let Some(test) = AFCloudDocumentTest::new().await {
|
||||
let document_id = test.create_document().await;
|
||||
let cloned_test = test.clone();
|
||||
let cloned_document_id = document_id.clone();
|
||||
test.inner.dispatcher().spawn(async move {
|
||||
cloned_test
|
||||
.insert_document_text(&cloned_document_id, "hello world", 0)
|
||||
.await;
|
||||
});
|
||||
user_localhost_af_cloud().await;
|
||||
let test = EventIntegrationTest::new().await;
|
||||
test.af_cloud_sign_up().await;
|
||||
test.wait_ws_connected().await;
|
||||
|
||||
// wait all update are send to the remote
|
||||
let rx = test
|
||||
.notification_sender
|
||||
.subscribe_with_condition::<DocumentSyncStatePB, _>(&document_id, |pb| !pb.is_syncing);
|
||||
receive_with_timeout(rx, Duration::from_secs(25))
|
||||
.await
|
||||
.unwrap();
|
||||
// create document and then insert content
|
||||
let current_workspace = test.get_current_workspace().await;
|
||||
let view = test
|
||||
.create_and_open_document(¤t_workspace.id, "my document".to_string(), vec![])
|
||||
.await;
|
||||
test.insert_document_text(&view.id, "hello world", 0).await;
|
||||
|
||||
let document_data = test.get_document_data(&document_id).await;
|
||||
let update = test.get_document_update(&document_id).await;
|
||||
assert!(!update.is_empty());
|
||||
assert_document_data_equal(&update, &document_id, document_data);
|
||||
}
|
||||
let document_id = view.id;
|
||||
println!("document_id: {}", document_id);
|
||||
|
||||
// wait all update are send to the remote
|
||||
let rx = test
|
||||
.notification_sender
|
||||
.subscribe_with_condition::<DocumentSyncStatePB, _>(&document_id, |pb| !pb.is_syncing);
|
||||
let _ = receive_with_timeout(rx, Duration::from_secs(30)).await;
|
||||
|
||||
let document_data = test.get_document_data(&document_id).await;
|
||||
let doc_state = test.get_document_doc_state(&document_id).await;
|
||||
assert!(!doc_state.is_empty());
|
||||
assert_document_data_equal(&doc_state, &document_id, document_data);
|
||||
}
|
||||
|
@ -1,2 +1 @@
|
||||
mod edit_test;
|
||||
mod util;
|
||||
|
@ -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(¤t_workspace.id, "my document".to_string(), vec![])
|
||||
.await;
|
||||
view.id
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AFCloudDocumentTest {
|
||||
type Target = AFCloudTest;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ async fn supabase_document_edit_sync_test() {
|
||||
|
||||
let cloned_test = test.clone();
|
||||
let cloned_document_id = document_id.clone();
|
||||
test.inner.dispatcher().spawn(async move {
|
||||
test.appflowy_core.dispatcher().spawn(async move {
|
||||
cloned_test
|
||||
.insert_document_text(&cloned_document_id, "hello world", 0)
|
||||
.await;
|
||||
@ -29,7 +29,7 @@ async fn supabase_document_edit_sync_test() {
|
||||
.unwrap();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -55,7 +55,7 @@ async fn supabase_document_edit_sync_test2() {
|
||||
.unwrap();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ impl FlowySupabaseDocumentTest {
|
||||
let current_workspace = self.inner.get_current_workspace().await;
|
||||
self
|
||||
.inner
|
||||
.create_document(¤t_workspace.id, "my document".to_string(), vec![])
|
||||
.create_and_open_document(¤t_workspace.id, "my document".to_string(), vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ async fn create_child_view_in_workspace_subscription_test() {
|
||||
|
||||
let cloned_test = test.clone();
|
||||
let cloned_workspace_id = workspace.id.clone();
|
||||
test.inner.dispatcher().spawn(async move {
|
||||
test.appflowy_core.dispatcher().spawn(async move {
|
||||
cloned_test
|
||||
.create_view(&cloned_workspace_id, "workspace child view".to_string())
|
||||
.await;
|
||||
@ -50,7 +50,7 @@ async fn create_child_view_in_view_subscription_test() {
|
||||
|
||||
let cloned_test = test.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
|
||||
.create_view(
|
||||
&child_view_id,
|
||||
@ -82,7 +82,7 @@ async fn delete_view_subscription_test() {
|
||||
let delete_view_id = workspace.views.first().unwrap().id.clone();
|
||||
let cloned_delete_view_id = delete_view_id.clone();
|
||||
test
|
||||
.inner
|
||||
.appflowy_core
|
||||
.dispatcher()
|
||||
.spawn(async move {
|
||||
cloned_test.delete_view(&cloned_delete_view_id).await;
|
||||
@ -91,7 +91,7 @@ async fn delete_view_subscription_test() {
|
||||
.unwrap();
|
||||
|
||||
let update = test
|
||||
.inner
|
||||
.appflowy_core
|
||||
.dispatcher()
|
||||
.run_until(receive_with_timeout(rx, Duration::from_secs(30)))
|
||||
.await
|
||||
@ -114,7 +114,7 @@ async fn update_view_subscription_test() {
|
||||
assert!(!view.is_favorite);
|
||||
|
||||
let update_view_id = view.id.clone();
|
||||
test.inner.dispatcher().spawn(async move {
|
||||
test.appflowy_core.dispatcher().spawn(async move {
|
||||
cloned_test
|
||||
.update_view(UpdateViewPayloadPB {
|
||||
view_id: update_view_id,
|
||||
|
@ -1,5 +1,7 @@
|
||||
use event_integration::user_event::user_localhost_af_cloud;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_core::DEFAULT_NAME;
|
||||
use flowy_user::entities::AuthenticatorPB;
|
||||
|
||||
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[1].name, "Grid2".to_string());
|
||||
|
||||
let trash_items = test.get_trash().await.items;
|
||||
assert_eq!(trash_items.len(), 1);
|
||||
|
||||
drop(cleaner);
|
||||
}
|
||||
// #[tokio::test]
|
||||
// async fn migrate_anon_user_data_to_af_cloud_test() {
|
||||
// let (cleaner, user_db_path) = unzip_history_user_db("./tests/asset", "039_local").unwrap();
|
||||
// user_localhost_af_cloud().await;
|
||||
// let test =
|
||||
// EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()).await;
|
||||
// let anon_first_level_views = test.get_all_workspace_views().await;
|
||||
// let anon_second_level_views = test
|
||||
// .get_views(&anon_first_level_views[0].id)
|
||||
// .await
|
||||
// .child_views;
|
||||
//
|
||||
// // The anon user data will be migrated to the AppFlowy cloud after sign up
|
||||
// let user = test.af_cloud_sign_up().await;
|
||||
// assert_eq!(user.authenticator, AuthenticatorPB::AppFlowyCloud);
|
||||
//
|
||||
// let user_first_level_views = test.get_all_workspace_views().await;
|
||||
// let user_second_level_views = test
|
||||
// .get_views(&user_first_level_views[0].id)
|
||||
// .await
|
||||
// .child_views;
|
||||
//
|
||||
// // first
|
||||
// assert_eq!(anon_first_level_views.len(), 1);
|
||||
// assert_eq!(user_first_level_views.len(), 1);
|
||||
// assert_eq!(
|
||||
// anon_first_level_views[0].name,
|
||||
// user_first_level_views[0].name
|
||||
// );
|
||||
// assert_ne!(anon_first_level_views[0].id, user_first_level_views[0].id);
|
||||
//
|
||||
// // second
|
||||
// assert_eq!(anon_second_level_views.len(), user_second_level_views.len());
|
||||
// assert_eq!(
|
||||
// anon_second_level_views[0].name,
|
||||
// user_second_level_views[0].name
|
||||
// );
|
||||
// assert_ne!(anon_second_level_views[0].id, user_second_level_views[0].id);
|
||||
// drop(cleaner);
|
||||
// }
|
||||
|
||||
#[tokio::test]
|
||||
async fn migrate_anon_user_data_to_af_cloud_test() {
|
||||
let (cleaner, user_db_path) = unzip_history_user_db("./tests/asset", "040_local").unwrap();
|
||||
// In the 040_local, the structure is:
|
||||
// workspace:
|
||||
// view: Document1
|
||||
// view: Document2
|
||||
// view: Grid1
|
||||
// view: Grid2
|
||||
user_localhost_af_cloud().await;
|
||||
let test =
|
||||
EventIntegrationTest::new_with_user_data_path(user_db_path.clone(), DEFAULT_NAME.to_string())
|
||||
.await;
|
||||
let anon_trash = test.get_trash().await;
|
||||
assert_eq!(anon_trash.items.len(), 1);
|
||||
assert_eq!(
|
||||
anon_trash.items[0].name,
|
||||
"Local Getting started".to_string()
|
||||
);
|
||||
|
||||
let anon_first_level_views = test.get_all_workspace_views().await;
|
||||
let anon_second_level_views = test
|
||||
.get_views(&anon_first_level_views[0].id)
|
||||
.await
|
||||
.child_views;
|
||||
let anon_third_level_views = test
|
||||
.get_views(&anon_second_level_views[0].id)
|
||||
.await
|
||||
.child_views;
|
||||
|
||||
// The anon user data will be migrated to the AppFlowy cloud after sign up
|
||||
let user = test.af_cloud_sign_up().await;
|
||||
let user_trash = test.get_trash().await;
|
||||
let workspace = test.get_current_workspace().await;
|
||||
println!("user workspace: {:?}", workspace.id);
|
||||
assert_eq!(user.authenticator, AuthenticatorPB::AppFlowyCloud);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -1,41 +1,40 @@
|
||||
use event_integration::user_event::user_localhost_af_cloud;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_user::entities::UpdateUserProfilePayloadPB;
|
||||
|
||||
use crate::util::{generate_test_email, get_af_cloud_config};
|
||||
use crate::util::generate_test_email;
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_sign_up_test() {
|
||||
if get_af_cloud_config().is_some() {
|
||||
let test = EventIntegrationTest::new().await;
|
||||
let email = generate_test_email();
|
||||
let user = test.af_cloud_sign_in_with_email(&email).await.unwrap();
|
||||
assert_eq!(user.email, email);
|
||||
}
|
||||
user_localhost_af_cloud().await;
|
||||
let test = EventIntegrationTest::new().await;
|
||||
let email = generate_test_email();
|
||||
let user = test.af_cloud_sign_in_with_email(&email).await.unwrap();
|
||||
assert_eq!(user.email, email);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_update_user_metadata() {
|
||||
if get_af_cloud_config().is_some() {
|
||||
let test = EventIntegrationTest::new().await;
|
||||
let user = test.af_cloud_sign_up().await;
|
||||
user_localhost_af_cloud().await;
|
||||
let test = EventIntegrationTest::new().await;
|
||||
let user = test.af_cloud_sign_up().await;
|
||||
|
||||
let old_profile = test.get_user_profile().await.unwrap();
|
||||
assert_eq!(old_profile.openai_key, "".to_string());
|
||||
let old_profile = test.get_user_profile().await.unwrap();
|
||||
assert_eq!(old_profile.openai_key, "".to_string());
|
||||
|
||||
test
|
||||
.update_user_profile(UpdateUserProfilePayloadPB {
|
||||
id: user.id,
|
||||
openai_key: Some("new openai key".to_string()),
|
||||
stability_ai_key: Some("new stability ai key".to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
test
|
||||
.update_user_profile(UpdateUserProfilePayloadPB {
|
||||
id: user.id,
|
||||
openai_key: Some("new openai key".to_string()),
|
||||
stability_ai_key: Some("new stability ai key".to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
|
||||
let new_profile = test.get_user_profile().await.unwrap();
|
||||
assert_eq!(new_profile.openai_key, "new openai key".to_string());
|
||||
assert_eq!(
|
||||
new_profile.stability_ai_key,
|
||||
"new stability ai key".to_string()
|
||||
);
|
||||
}
|
||||
let new_profile = test.get_user_profile().await.unwrap();
|
||||
assert_eq!(new_profile.openai_key, "new openai key".to_string());
|
||||
assert_eq!(
|
||||
new_profile.stability_ai_key,
|
||||
"new stability ai key".to_string()
|
||||
);
|
||||
}
|
||||
|
@ -1,50 +1,47 @@
|
||||
use event_integration::user_event::user_localhost_af_cloud;
|
||||
use event_integration::EventIntegrationTest;
|
||||
|
||||
use crate::util::get_af_cloud_config;
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_add_workspace_member_test() {
|
||||
if get_af_cloud_config().is_some() {
|
||||
let test_1 = EventIntegrationTest::new().await;
|
||||
let user_1 = test_1.af_cloud_sign_up().await;
|
||||
user_localhost_af_cloud().await;
|
||||
let test_1 = EventIntegrationTest::new().await;
|
||||
let user_1 = test_1.af_cloud_sign_up().await;
|
||||
|
||||
let test_2 = EventIntegrationTest::new().await;
|
||||
let user_2 = test_2.af_cloud_sign_up().await;
|
||||
let test_2 = EventIntegrationTest::new().await;
|
||||
let user_2 = test_2.af_cloud_sign_up().await;
|
||||
|
||||
let members = test_1.get_workspace_members(&user_1.workspace_id).await;
|
||||
assert_eq!(members.len(), 1);
|
||||
assert_eq!(members[0].email, user_1.email);
|
||||
let members = test_1.get_workspace_members(&user_1.workspace_id).await;
|
||||
assert_eq!(members.len(), 1);
|
||||
assert_eq!(members[0].email, user_1.email);
|
||||
|
||||
test_1
|
||||
.add_workspace_member(&user_1.workspace_id, &user_2.email)
|
||||
.await;
|
||||
test_1
|
||||
.add_workspace_member(&user_1.workspace_id, &user_2.email)
|
||||
.await;
|
||||
|
||||
let members = test_1.get_workspace_members(&user_1.workspace_id).await;
|
||||
assert_eq!(members.len(), 2);
|
||||
assert_eq!(members[0].email, user_1.email);
|
||||
assert_eq!(members[1].email, user_2.email);
|
||||
}
|
||||
let members = test_1.get_workspace_members(&user_1.workspace_id).await;
|
||||
assert_eq!(members.len(), 2);
|
||||
assert_eq!(members[0].email, user_1.email);
|
||||
assert_eq!(members[1].email, user_2.email);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_delete_workspace_member_test() {
|
||||
if get_af_cloud_config().is_some() {
|
||||
let test_1 = EventIntegrationTest::new().await;
|
||||
let user_1 = test_1.af_cloud_sign_up().await;
|
||||
user_localhost_af_cloud().await;
|
||||
let test_1 = EventIntegrationTest::new().await;
|
||||
let user_1 = test_1.af_cloud_sign_up().await;
|
||||
|
||||
let test_2 = EventIntegrationTest::new().await;
|
||||
let user_2 = test_2.af_cloud_sign_up().await;
|
||||
let test_2 = EventIntegrationTest::new().await;
|
||||
let user_2 = test_2.af_cloud_sign_up().await;
|
||||
|
||||
test_1
|
||||
.add_workspace_member(&user_1.workspace_id, &user_2.email)
|
||||
.await;
|
||||
test_1
|
||||
.add_workspace_member(&user_1.workspace_id, &user_2.email)
|
||||
.await;
|
||||
|
||||
test_1
|
||||
.delete_workspace_member(&user_1.workspace_id, &user_2.email)
|
||||
.await;
|
||||
test_1
|
||||
.delete_workspace_member(&user_1.workspace_id, &user_2.email)
|
||||
.await;
|
||||
|
||||
let members = test_1.get_workspace_members(&user_1.workspace_id).await;
|
||||
assert_eq!(members.len(), 1);
|
||||
assert_eq!(members[0].email, user_1.email);
|
||||
}
|
||||
let members = test_1.get_workspace_members(&user_1.workspace_id).await;
|
||||
assert_eq!(members.len(), 1);
|
||||
assert_eq!(members[0].email, user_1.email);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ async fn third_party_sign_up_test() {
|
||||
map.insert(USER_DEVICE_ID.to_string(), uuid::Uuid::new_v4().to_string());
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthenticatorPB::Supabase,
|
||||
authenticator: AuthenticatorPB::Supabase,
|
||||
};
|
||||
|
||||
let response = EventBuilder::new(test.clone())
|
||||
@ -79,7 +79,7 @@ async fn third_party_sign_up_with_duplicated_uuid() {
|
||||
.event(OauthSignIn)
|
||||
.payload(OauthSignInPB {
|
||||
map: map.clone(),
|
||||
auth_type: AuthenticatorPB::Supabase,
|
||||
authenticator: AuthenticatorPB::Supabase,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
@ -90,7 +90,7 @@ async fn third_party_sign_up_with_duplicated_uuid() {
|
||||
.event(OauthSignIn)
|
||||
.payload(OauthSignInPB {
|
||||
map: map.clone(),
|
||||
auth_type: AuthenticatorPB::Supabase,
|
||||
authenticator: AuthenticatorPB::Supabase,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
|
@ -21,7 +21,7 @@ async fn initial_workspace_test() {
|
||||
);
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthenticatorPB::Supabase,
|
||||
authenticator: AuthenticatorPB::Supabase,
|
||||
};
|
||||
|
||||
let _ = EventBuilder::new(test.clone())
|
||||
|
@ -8,8 +8,8 @@ use std::time::Duration;
|
||||
use anyhow::Error;
|
||||
use collab_folder::FolderData;
|
||||
use collab_plugins::cloud_storage::RemoteCollabStorage;
|
||||
use nanoid::nanoid;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
|
||||
use tokio::time::timeout;
|
||||
use uuid::Uuid;
|
||||
use zip::ZipArchive;
|
||||
@ -21,11 +21,11 @@ use flowy_database_deps::cloud::DatabaseCloudService;
|
||||
use flowy_folder_deps::cloud::{FolderCloudService, FolderSnapshot};
|
||||
use flowy_server::supabase::api::*;
|
||||
use flowy_server::{AppFlowyEncryption, EncryptionImpl};
|
||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||
use flowy_user::entities::{AuthenticatorPB, UpdateUserProfilePayloadPB};
|
||||
use flowy_user::errors::FlowyError;
|
||||
use flowy_user::event_map::UserCloudServiceProvider;
|
||||
|
||||
use flowy_user::event_map::UserEvent::*;
|
||||
use flowy_user_deps::cloud::UserCloudService;
|
||||
use flowy_user_deps::entities::Authenticator;
|
||||
@ -36,26 +36,26 @@ pub fn get_supabase_config() -> Option<SupabaseConfiguration> {
|
||||
}
|
||||
|
||||
pub struct FlowySupabaseTest {
|
||||
inner: EventIntegrationTest,
|
||||
event_test: EventIntegrationTest,
|
||||
}
|
||||
|
||||
impl FlowySupabaseTest {
|
||||
pub async fn new() -> Option<Self> {
|
||||
let _ = get_supabase_config()?;
|
||||
let test = EventIntegrationTest::new().await;
|
||||
test.set_auth_type(AuthenticatorPB::Supabase);
|
||||
test
|
||||
let event_test = EventIntegrationTest::new().await;
|
||||
event_test.set_auth_type(AuthenticatorPB::Supabase);
|
||||
event_test
|
||||
.server_provider
|
||||
.set_authenticator(Authenticator::Supabase);
|
||||
|
||||
Some(Self { inner: test })
|
||||
Some(Self { event_test })
|
||||
}
|
||||
|
||||
pub async fn update_user_profile(
|
||||
&self,
|
||||
payload: UpdateUserProfilePayloadPB,
|
||||
) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.inner.clone())
|
||||
EventBuilder::new(self.event_test.clone())
|
||||
.event(UpdateUserProfile)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
@ -68,16 +68,12 @@ impl Deref for FlowySupabaseTest {
|
||||
type Target = EventIntegrationTest;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
&self.event_test
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn receive_with_timeout<T>(
|
||||
mut receiver: Receiver<T>,
|
||||
duration: Duration,
|
||||
) -> Result<T, Box<dyn std::error::Error + Send>> {
|
||||
let res = timeout(duration, receiver.recv()).await.unwrap().unwrap();
|
||||
Ok(res)
|
||||
pub async fn receive_with_timeout<T>(mut receiver: Receiver<T>, duration: Duration) -> Option<T> {
|
||||
timeout(duration, receiver.recv()).await.ok()?
|
||||
}
|
||||
|
||||
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
|
||||
let zip_file_path = format!("{}/{}.zip", root, folder_name);
|
||||
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
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ use flowy_server::{AppFlowyEncryption, AppFlowyServer, EncryptionImpl};
|
||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||
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 crate::AppFlowyCoreConfig;
|
||||
@ -90,8 +89,13 @@ impl ServerProvider {
|
||||
*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.
|
||||
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) {
|
||||
return Ok(provider.clone());
|
||||
}
|
||||
@ -169,19 +173,22 @@ pub fn current_server_type(store_preferences: &Arc<StorePreferences>) -> Server
|
||||
}
|
||||
|
||||
struct LocalServerDBImpl {
|
||||
#[allow(dead_code)]
|
||||
storage_path: String,
|
||||
}
|
||||
|
||||
impl LocalServerDB for LocalServerDBImpl {
|
||||
fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
|
||||
let sqlite_db = open_user_db(&self.storage_path, uid)?;
|
||||
let user_profile = get_user_profile(&sqlite_db, uid)?;
|
||||
Ok(user_profile)
|
||||
fn get_user_profile(&self, _uid: i64) -> Result<UserProfile, FlowyError> {
|
||||
Err(
|
||||
FlowyError::local_version_not_support()
|
||||
.with_context("LocalServer doesn't support get_user_profile"),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_user_workspace(&self, uid: i64) -> Result<Option<UserWorkspace>, FlowyError> {
|
||||
let sqlite_db = open_user_db(&self.storage_path, uid)?;
|
||||
let user_workspace = get_user_workspace(&sqlite_db, uid)?;
|
||||
Ok(user_workspace)
|
||||
fn get_user_workspace(&self, _uid: i64) -> Result<Option<UserWorkspace>, FlowyError> {
|
||||
Err(
|
||||
FlowyError::local_version_not_support()
|
||||
.with_context("LocalServer doesn't support get_user_workspace"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ impl DatabaseCloudService for ServerProvider {
|
||||
}
|
||||
|
||||
impl DocumentCloudService for ServerProvider {
|
||||
fn get_document_updates(
|
||||
fn get_document_doc_state(
|
||||
&self,
|
||||
document_id: &str,
|
||||
workspace_id: &str,
|
||||
@ -272,7 +272,7 @@ impl DocumentCloudService for ServerProvider {
|
||||
FutureResult::new(async move {
|
||||
server?
|
||||
.document_service()
|
||||
.get_document_updates(&document_id, &workspace_id)
|
||||
.get_document_doc_state(&document_id, &workspace_id)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
@ -66,6 +66,10 @@ impl AppFlowyCore {
|
||||
runtime.block_on(Self::init(config, cloned_runtime))
|
||||
}
|
||||
|
||||
pub fn close_db(&self) {
|
||||
self.user_manager.close_db();
|
||||
}
|
||||
|
||||
#[instrument(skip(config, runtime))]
|
||||
async fn init(config: AppFlowyCoreConfig, runtime: Arc<AFPluginRuntime>) -> Self {
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
|
@ -16,7 +16,7 @@ use lru::LruCache;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tracing::{event, instrument, trace};
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
|
||||
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||
use flowy_database_deps::cloud::DatabaseCloudService;
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
@ -457,6 +457,7 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
|
||||
collab_db,
|
||||
collab_raw_data,
|
||||
config,
|
||||
CollabBuilderConfig::default().sync_enable(true),
|
||||
))
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -496,8 +496,10 @@ fn merge_groups(
|
||||
merge_result.new_groups.extend(new_group_map.into_values());
|
||||
|
||||
// The `No status` group index is initialized to 0
|
||||
if !no_status_group_inserted && no_status_group.is_some() {
|
||||
merge_result.all_groups.insert(0, no_status_group.unwrap());
|
||||
if !no_status_group_inserted {
|
||||
if let Some(group) = no_status_group {
|
||||
merge_result.all_groups.insert(0, group);
|
||||
}
|
||||
}
|
||||
merge_result
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use lib_infra::future::FutureResult;
|
||||
/// Each kind of server should implement this trait. Check out the [AppFlowyServerProvider] of
|
||||
/// [flowy-server] crate for more information.
|
||||
pub trait DocumentCloudService: Send + Sync + 'static {
|
||||
fn get_document_updates(
|
||||
fn get_document_doc_state(
|
||||
&self,
|
||||
document_id: &str,
|
||||
workspace_id: &str,
|
||||
|
@ -3,6 +3,9 @@ use std::sync::Arc;
|
||||
use std::sync::Weak;
|
||||
|
||||
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::document::Document;
|
||||
use collab_document::document_data::{default_document_collab_data, default_document_data};
|
||||
@ -12,10 +15,10 @@ use lru::LruCache;
|
||||
use parking_lot::Mutex;
|
||||
use tracing::{event, instrument};
|
||||
|
||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
|
||||
use collab_integrate::RocksCollabDB;
|
||||
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 crate::document::MutexDocument;
|
||||
@ -88,16 +91,26 @@ impl DocumentManager {
|
||||
uid: i64,
|
||||
doc_id: &str,
|
||||
data: Option<DocumentData>,
|
||||
) -> FlowyResult<Arc<MutexDocument>> {
|
||||
) -> FlowyResult<()> {
|
||||
tracing::trace!("create a document: {:?}", doc_id);
|
||||
|
||||
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 {
|
||||
let collab = self.collab_for_document(uid, doc_id, vec![]).await?;
|
||||
let data = data.unwrap_or_else(default_document_data);
|
||||
let document = Arc::new(MutexDocument::create_with_data(collab, data)?);
|
||||
Ok(document)
|
||||
let encoded_collab_v1 =
|
||||
doc_state_from_document_data(doc_id, data.unwrap_or_else(default_document_data))?;
|
||||
let collab = self
|
||||
.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
|
||||
let result: Result<CollabRawData, FlowyError> = self
|
||||
.cloud_service
|
||||
.get_document_updates(doc_id, &self.user.workspace_id()?)
|
||||
.get_document_doc_state(doc_id, &self.user.workspace_id()?)
|
||||
.await;
|
||||
|
||||
updates = match result {
|
||||
@ -138,7 +151,7 @@ impl DocumentManager {
|
||||
|
||||
let uid = self.user.user_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)?);
|
||||
|
||||
// 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)? {
|
||||
updates = self
|
||||
.cloud_service
|
||||
.get_document_updates(doc_id, &self.user.workspace_id()?)
|
||||
.get_document_doc_state(doc_id, &self.user.workspace_id()?)
|
||||
.await?;
|
||||
}
|
||||
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)?
|
||||
.get_document_data()
|
||||
.map_err(internal_error)
|
||||
@ -218,12 +233,20 @@ impl DocumentManager {
|
||||
&self,
|
||||
uid: i64,
|
||||
doc_id: &str,
|
||||
updates: Vec<Vec<u8>>,
|
||||
doc_state: Vec<Vec<u8>>,
|
||||
sync_enable: bool,
|
||||
) -> FlowyResult<Arc<MutexCollab>> {
|
||||
let db = self.user.collab_db(uid)?;
|
||||
let collab = self
|
||||
.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?;
|
||||
Ok(collab)
|
||||
}
|
||||
@ -249,3 +272,16 @@ impl DocumentManager {
|
||||
&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())
|
||||
}
|
||||
|
@ -14,11 +14,11 @@ async fn restore_document() {
|
||||
let doc_id: String = gen_document_id();
|
||||
let data = default_document_data();
|
||||
let uid = test.user.user_id().unwrap();
|
||||
let document_a = test
|
||||
test
|
||||
.create_document(uid, &doc_id, Some(data.clone()))
|
||||
.await
|
||||
.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);
|
||||
|
||||
let data_b = test
|
||||
|
@ -57,7 +57,13 @@ pub struct FakeUser {
|
||||
|
||||
impl FakeUser {
|
||||
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();
|
||||
START.call_once(|| {
|
||||
std::env::set_var("RUST_LOG", "collab_persistence=trace");
|
||||
@ -89,10 +95,6 @@ pub fn db() -> Arc<RocksCollabDB> {
|
||||
.finish();
|
||||
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> {
|
||||
@ -129,7 +131,7 @@ pub fn gen_id() -> String {
|
||||
|
||||
pub struct LocalTestDocumentCloudServiceImpl();
|
||||
impl DocumentCloudService for LocalTestDocumentCloudServiceImpl {
|
||||
fn get_document_updates(
|
||||
fn get_document_doc_state(
|
||||
&self,
|
||||
_document_id: &str,
|
||||
_workspace_id: &str,
|
||||
|
@ -262,6 +262,9 @@ pub enum ErrorCode {
|
||||
|
||||
#[error("rocksdb internal error")]
|
||||
RocksdbInternal = 87,
|
||||
|
||||
#[error("Local version not support")]
|
||||
LocalVersionNotSupport = 88,
|
||||
}
|
||||
|
||||
impl ErrorCode {
|
||||
|
@ -59,6 +59,10 @@ impl FlowyError {
|
||||
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!(record_not_found, ErrorCode::RecordNotFound);
|
||||
static_flowy_error!(workspace_name, ErrorCode::WorkspaceNameInvalid);
|
||||
@ -105,6 +109,7 @@ impl FlowyError {
|
||||
static_flowy_error!(collab_not_sync, ErrorCode::CollabDataNotSync);
|
||||
static_flowy_error!(server_error, ErrorCode::InternalServerError);
|
||||
static_flowy_error!(not_support, ErrorCode::NotSupportYet);
|
||||
static_flowy_error!(local_version_not_support, ErrorCode::LocalVersionNotSupport);
|
||||
}
|
||||
|
||||
impl std::convert::From<ErrorCode> for FlowyError {
|
||||
|
@ -10,7 +10,7 @@ use collab_folder::{
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
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 flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_folder_deps::cloud::{gen_view_id, FolderCloudService};
|
||||
@ -134,6 +134,7 @@ impl FolderManager {
|
||||
collab_db,
|
||||
raw_data,
|
||||
&CollabPersistenceConfig::new().enable_snapshot(true),
|
||||
CollabBuilderConfig::default().sync_enable(true),
|
||||
)
|
||||
.await?;
|
||||
Ok(collab)
|
||||
|
@ -67,7 +67,7 @@ impl FolderManager {
|
||||
},
|
||||
FolderInitDataSource::Cloud(raw_data) => {
|
||||
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
|
||||
.open_local_folder(uid, &workspace_id, collab_db, folder_notifier)
|
||||
.await?
|
||||
@ -111,7 +111,11 @@ impl FolderManager {
|
||||
collab_db: Weak<RocksCollabDB>,
|
||||
folder_notifier: FolderNotify,
|
||||
) -> 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 =
|
||||
DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers).await;
|
||||
let collab = self
|
||||
|
@ -16,7 +16,7 @@ impl<T> DocumentCloudService for AFCloudDocumentCloudServiceImpl<T>
|
||||
where
|
||||
T: AFServer,
|
||||
{
|
||||
fn get_document_updates(
|
||||
fn get_document_doc_state(
|
||||
&self,
|
||||
document_id: &str,
|
||||
workspace_id: &str,
|
||||
|
@ -232,7 +232,7 @@ where
|
||||
collab_object: &CollabObject,
|
||||
data: Vec<u8>,
|
||||
override_if_exist: bool,
|
||||
) -> FutureResult<(), Error> {
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
let collab_object = collab_object.clone();
|
||||
FutureResult::new(async move {
|
||||
|
@ -168,6 +168,14 @@ impl AppFlowyServer for AppFlowyCloudServer {
|
||||
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)]
|
||||
fn collab_ws_channel(
|
||||
&self,
|
||||
|
@ -7,7 +7,7 @@ use lib_infra::future::FutureResult;
|
||||
pub(crate) struct LocalServerDocumentCloudServiceImpl();
|
||||
|
||||
impl DocumentCloudService for LocalServerDocumentCloudServiceImpl {
|
||||
fn get_document_updates(
|
||||
fn get_document_doc_state(
|
||||
&self,
|
||||
_document_id: &str,
|
||||
_workspace_id: &str,
|
||||
|
@ -121,7 +121,10 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
|
||||
fn open_workspace(&self, _workspace_id: &str) -> FutureResult<UserWorkspace, FlowyError> {
|
||||
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,
|
||||
_data: Vec<u8>,
|
||||
_override_if_exist: bool,
|
||||
) -> FutureResult<(), Error> {
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
FutureResult::new(async { Ok(()) })
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::Error;
|
||||
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_plugins::cloud_storage::RemoteCollabStorage;
|
||||
use parking_lot::RwLock;
|
||||
@ -108,6 +108,14 @@ pub trait AppFlowyServer: Send + Sync + 'static {
|
||||
None
|
||||
}
|
||||
|
||||
fn subscribe_ws_state(&self) -> Option<WSConnectStateReceiver> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_ws_state(&self) -> ConnectState {
|
||||
ConnectState::Closed
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn collab_ws_channel(
|
||||
&self,
|
||||
|
@ -28,7 +28,7 @@ where
|
||||
T: SupabaseServerService,
|
||||
{
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
fn get_document_updates(
|
||||
fn get_document_doc_state(
|
||||
&self,
|
||||
document_id: &str,
|
||||
workspace_id: &str,
|
||||
|
@ -311,7 +311,7 @@ where
|
||||
collab_object: &CollabObject,
|
||||
data: Vec<u8>,
|
||||
_override_if_exist: bool,
|
||||
) -> FutureResult<(), Error> {
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let cloned_collab_object = collab_object.clone();
|
||||
let (tx, rx) = channel();
|
||||
|
@ -142,7 +142,7 @@ pub trait UserCloudService: Send + Sync + 'static {
|
||||
collab_object: &CollabObject,
|
||||
data: Vec<u8>,
|
||||
override_if_exist: bool,
|
||||
) -> FutureResult<(), Error>;
|
||||
) -> FutureResult<(), FlowyError>;
|
||||
}
|
||||
|
||||
pub type UserUpdateReceiver = tokio::sync::mpsc::Receiver<UserUpdate>;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user