From 71f80be8f7d3fbd90928aa89bc4ac136a699d3a2 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Tue, 24 Oct 2023 20:11:06 +0800 Subject: [PATCH] feat: save user metadata (#3754) * feat: save user metadata * chore: update client api * refactor: separate test methods * chore: save updated at * chore: clippy * chore: fix test --- .run/ProtoBuf_Gen.run.xml | 23 - .run/Run backend.run.xml | 18 - .run/dart-event.run.xml | 16 - .../lib/startup/plugin/src/sandbox.dart | 5 +- .../startup/tasks/appflowy_cloud_task.dart | 2 + .../user/application/user_auth_listener.dart | 2 +- .../presentation/screens/splash_screen.dart | 12 +- frontend/appflowy_tauri/src-tauri/Cargo.lock | 15 +- frontend/appflowy_tauri/src-tauri/Cargo.toml | 2 +- frontend/rust-lib/Cargo.lock | 33 +- frontend/rust-lib/Cargo.toml | 2 +- .../src/document/document_event.rs | 14 +- .../event-integration/src/event_builder.rs | 8 +- .../event-integration/src/folder_event.rs | 115 --- .../rust-lib/event-integration/src/lib.rs | 900 +----------------- .../event-integration/src/test_database.rs | 447 +++++++++ .../event-integration/src/test_document.rs | 119 +++ .../event-integration/src/test_folder.rs | 173 ++++ .../event-integration/src/test_user.rs | 296 ++++++ .../event-integration/src/user_event.rs | 84 -- .../tests/database/local_test/test.rs | 82 +- .../tests/document/af_cloud_test/edit_test.rs | 2 +- .../tests/document/supabase_test/edit_test.rs | 2 +- .../tests/folder/local_test/script.rs | 44 +- .../folder/local_test/subscription_test.rs | 10 +- .../tests/folder/local_test/test.rs | 34 +- .../tests/user/af_cloud_test/test.rs | 27 +- .../tests/user/local_test/auth_test.rs | 16 +- .../user/local_test/user_awareness_test.rs | 4 +- .../user/local_test/user_profile_test.rs | 24 +- .../user/migration_test/document_test.rs | 4 +- .../tests/user/migration_test/version_test.rs | 4 +- .../tests/user/supabase_test/auth_test.rs | 19 +- .../user/supabase_test/workspace_test.rs | 4 +- .../rust-lib/event-integration/tests/util.rs | 14 +- .../rust-lib/flowy-database2/src/manager.rs | 7 +- .../tests/database/database_editor.rs | 19 +- .../rust-lib/flowy-document2/src/manager.rs | 7 +- .../flowy-document2/src/notification.rs | 1 + .../rust-lib/flowy-folder2/src/manager.rs | 7 +- frontend/rust-lib/flowy-server/Cargo.toml | 2 +- .../{user.rs => user/cloud_service_impl.rs} | 101 +- .../src/af_cloud/impls/user/dto.rs | 65 ++ .../src/af_cloud/impls/user/mod.rs | 5 + .../src/af_cloud/impls/user/util.rs | 10 + .../src/local_server/impls/user.rs | 7 +- .../flowy-server/src/supabase/api/user.rs | 9 +- .../flowy-server/src/supabase/entities.rs | 3 + .../down.sql | 3 + .../2023-10-24-074032_user_updated_at/up.sql | 3 + frontend/rust-lib/flowy-sqlite/src/schema.rs | 1 + .../rust-lib/flowy-user-deps/src/cloud.rs | 2 +- .../rust-lib/flowy-user-deps/src/entities.rs | 91 +- .../rust-lib/flowy-user/src/entities/auth.rs | 3 + .../flowy-user/src/entities/user_setting.rs | 4 +- .../rust-lib/flowy-user/src/event_handler.rs | 15 +- frontend/rust-lib/flowy-user/src/event_map.rs | 2 +- frontend/rust-lib/flowy-user/src/manager.rs | 50 +- .../rust-lib/flowy-user/src/notification.rs | 1 + .../flowy-user/src/services/user_sql.rs | 3 + .../flowy-user/src/services/user_workspace.rs | 2 +- frontend/scripts/makefile/tests.toml | 2 +- 62 files changed, 1525 insertions(+), 1476 deletions(-) delete mode 100644 .run/ProtoBuf_Gen.run.xml delete mode 100644 .run/Run backend.run.xml delete mode 100644 .run/dart-event.run.xml delete mode 100644 frontend/rust-lib/event-integration/src/folder_event.rs create mode 100644 frontend/rust-lib/event-integration/src/test_database.rs create mode 100644 frontend/rust-lib/event-integration/src/test_document.rs create mode 100644 frontend/rust-lib/event-integration/src/test_folder.rs create mode 100644 frontend/rust-lib/event-integration/src/test_user.rs delete mode 100644 frontend/rust-lib/event-integration/src/user_event.rs rename frontend/rust-lib/flowy-server/src/af_cloud/impls/{user.rs => user/cloud_service_impl.rs} (71%) create mode 100644 frontend/rust-lib/flowy-server/src/af_cloud/impls/user/dto.rs create mode 100644 frontend/rust-lib/flowy-server/src/af_cloud/impls/user/mod.rs create mode 100644 frontend/rust-lib/flowy-server/src/af_cloud/impls/user/util.rs create mode 100644 frontend/rust-lib/flowy-sqlite/migrations/2023-10-24-074032_user_updated_at/down.sql create mode 100644 frontend/rust-lib/flowy-sqlite/migrations/2023-10-24-074032_user_updated_at/up.sql diff --git a/.run/ProtoBuf_Gen.run.xml b/.run/ProtoBuf_Gen.run.xml deleted file mode 100644 index 13d9202183f9..000000000000 --- a/.run/ProtoBuf_Gen.run.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - \ No newline at end of file diff --git a/.run/Run backend.run.xml b/.run/Run backend.run.xml deleted file mode 100644 index 840a367809c6..000000000000 --- a/.run/Run backend.run.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - \ No newline at end of file diff --git a/.run/dart-event.run.xml b/.run/dart-event.run.xml deleted file mode 100644 index c64ee9bf57cf..000000000000 --- a/.run/dart-event.run.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/appflowy_flutter/lib/startup/plugin/src/sandbox.dart b/frontend/appflowy_flutter/lib/startup/plugin/src/sandbox.dart index a10ff9631adc..740fb3f4d2e2 100644 --- a/frontend/appflowy_flutter/lib/startup/plugin/src/sandbox.dart +++ b/frontend/appflowy_flutter/lib/startup/plugin/src/sandbox.dart @@ -42,10 +42,7 @@ class PluginSandbox { PluginConfig? config, }) { if (_pluginBuilders.containsKey(pluginType)) { - throw PlatformException( - code: '-1', - message: "$pluginType was registered before", - ); + return; } _pluginBuilders[pluginType] = builder; diff --git a/frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart b/frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart index ca21b40c63a1..420c9d93fc20 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/appflowy_cloud_task.dart @@ -2,6 +2,7 @@ import 'package:appflowy/env/env.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:appflowy/user/application/user_auth_listener.dart'; +import 'package:appflowy_backend/log.dart'; class InitAppFlowyCloudTask extends LaunchTask { final _authStateListener = UserAuthStateListener(); @@ -18,6 +19,7 @@ class InitAppFlowyCloudTask extends LaunchTask { isLoggingOut = false; }, onInvalidAuth: (message) async { + Log.error(message); await getIt().signOut(); // TODO(nathan): Show a dialog to notify the user that the session is expired. if (!isLoggingOut) { diff --git a/frontend/appflowy_flutter/lib/user/application/user_auth_listener.dart b/frontend/appflowy_flutter/lib/user/application/user_auth_listener.dart index c0d1231e4b2b..776d537f36af 100644 --- a/frontend/appflowy_flutter/lib/user/application/user_auth_listener.dart +++ b/frontend/appflowy_flutter/lib/user/application/user_auth_listener.dart @@ -53,7 +53,7 @@ class UserAuthStateListener { _didSignIn?.call(); break; case AuthStatePB.InvalidAuth: - _onInvalidAuth?.call(""); + _onInvalidAuth?.call(pb.message); break; default: break; diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart index 1b53d80bdf7f..3ea6b371da81 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/splash_screen.dart @@ -14,16 +14,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -// [[diagram: splash screen]] -// ┌────────────────┐1.get user ┌──────────┐ ┌────────────┐ 2.send UserEventCheckUser -// │ SplashScreen │──────────▶│SplashBloc│────▶│ISplashUser │─────┐ -// └────────────────┘ └──────────┘ └────────────┘ │ -// │ -// ▼ -// ┌───────────┐ ┌─────────────┐ ┌────────┐ -// │HomeScreen │◀───────────│BlocListener │◀────────────────│RustSDK │ -// └───────────┘ └─────────────┘ └────────┘ -// 4. Show HomeScreen or SignIn 3.return AuthState class SplashScreen extends StatelessWidget { /// Root Page of the app. const SplashScreen({ @@ -82,7 +72,7 @@ class SplashScreen extends StatelessWidget { (check) async { /// If encryption is needed, the user is navigated to the encryption screen. /// Otherwise, it fetches the current workspace for the user and navigates them - if (check.isNeedSecret) { + if (check.requireSecret) { getIt().pushEncryptionScreen(context, userProfile); } else { final result = await FolderEventGetCurrentWorkspace().send(); diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 37005a67164d..417a4d21a4d7 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -762,7 +762,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "bytes", @@ -1438,7 +1438,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "chrono", @@ -2781,7 +2781,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "futures-util", @@ -2797,7 +2797,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "jsonwebtoken", @@ -3232,7 +3232,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "reqwest", @@ -4915,7 +4915,7 @@ dependencies = [ [[package]] name = "realtime-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "bytes", "collab", @@ -5637,9 +5637,10 @@ dependencies = [ [[package]] name = "shared_entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", + "collab-entity", "database-entity", "gotrue-entity", "opener", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index ea85a88118e0..71833a1417fd 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -38,7 +38,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 = "7a309c6f69d8b34709292052e9ef0561e16c82a1" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "141354bfa9d08c387cf9beb9697b89b052790e89" } # Please use the following script to update collab. # Working directory: frontend # diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 1f7b9ad4368d..591e3240e62e 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -660,7 +660,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "bytes", @@ -1138,7 +1138,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.8.0", + "phf 0.11.2", "smallvec", ] @@ -1265,7 +1265,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "chrono", @@ -2440,7 +2440,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "futures-util", @@ -2456,7 +2456,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "jsonwebtoken", @@ -2816,7 +2816,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", "reqwest", @@ -3623,7 +3623,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros", + "phf_macros 0.8.0", "phf_shared 0.8.0", "proc-macro-hack", ] @@ -3643,6 +3643,7 @@ 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", ] @@ -3710,6 +3711,19 @@ 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" @@ -4251,7 +4265,7 @@ dependencies = [ [[package]] name = "realtime-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "bytes", "collab", @@ -4872,9 +4886,10 @@ dependencies = [ [[package]] name = "shared_entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89" dependencies = [ "anyhow", + "collab-entity", "database-entity", "gotrue-entity", "opener", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 561fdfeb7ca9..9f3142145e9d 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -82,7 +82,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 = "7a309c6f69d8b34709292052e9ef0561e16c82a1" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "141354bfa9d08c387cf9beb9697b89b052790e89" } # Please use the following script to update collab. # Working directory: frontend # diff --git a/frontend/rust-lib/event-integration/src/document/document_event.rs b/frontend/rust-lib/event-integration/src/document/document_event.rs index 8439d3ffb36d..ab20d240bd39 100644 --- a/frontend/rust-lib/event-integration/src/document/document_event.rs +++ b/frontend/rust-lib/event-integration/src/document/document_event.rs @@ -1,18 +1,20 @@ +use std::collections::HashMap; + +use serde_json::Value; + use flowy_document2::entities::*; use flowy_document2::event_map::DocumentEvent; use flowy_folder2::entities::{CreateViewPayloadPB, ViewLayoutPB, ViewPB}; use flowy_folder2::event_map::FolderEvent; -use serde_json::Value; -use std::collections::HashMap; use crate::document::utils::{gen_delta_str, gen_id, gen_text_block_data}; use crate::event_builder::EventBuilder; -use crate::FlowyCoreTest; +use crate::EventIntegrationTest; const TEXT_BLOCK_TY: &str = "paragraph"; pub struct DocumentEventTest { - inner: FlowyCoreTest, + inner: EventIntegrationTest, } pub struct OpenDocumentData { @@ -22,11 +24,11 @@ pub struct OpenDocumentData { impl DocumentEventTest { pub async fn new() -> Self { - let sdk = FlowyCoreTest::new_with_guest_user().await; + let sdk = EventIntegrationTest::new_with_guest_user().await; Self { inner: sdk } } - pub fn new_with_core(core: FlowyCoreTest) -> Self { + pub fn new_with_core(core: EventIntegrationTest) -> Self { Self { inner: core } } diff --git a/frontend/rust-lib/event-integration/src/event_builder.rs b/frontend/rust-lib/event-integration/src/event_builder.rs index 943507add39d..a6c0209622f2 100644 --- a/frontend/rust-lib/event-integration/src/event_builder.rs +++ b/frontend/rust-lib/event-integration/src/event_builder.rs @@ -10,7 +10,7 @@ use lib_dispatch::prelude::{ AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, ToBytes, *, }; -use crate::FlowyCoreTest; +use crate::EventIntegrationTest; #[derive(Clone)] pub struct EventBuilder { @@ -18,7 +18,7 @@ pub struct EventBuilder { } impl EventBuilder { - pub fn new(sdk: FlowyCoreTest) -> Self { + pub fn new(sdk: EventIntegrationTest) -> Self { Self { context: TestContext::new(sdk), } @@ -121,13 +121,13 @@ impl EventBuilder { #[derive(Clone)] pub struct TestContext { - pub sdk: FlowyCoreTest, + pub sdk: EventIntegrationTest, request: Option, response: Option, } impl TestContext { - pub fn new(sdk: FlowyCoreTest) -> Self { + pub fn new(sdk: EventIntegrationTest) -> Self { Self { sdk, request: None, diff --git a/frontend/rust-lib/event-integration/src/folder_event.rs b/frontend/rust-lib/event-integration/src/folder_event.rs deleted file mode 100644 index a6cbebf09de2..000000000000 --- a/frontend/rust-lib/event-integration/src/folder_event.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::event_builder::EventBuilder; -use crate::FlowyCoreTest; -use flowy_folder2::entities::*; -use flowy_folder2::event_map::FolderEvent::*; - -pub struct ViewTest { - pub sdk: FlowyCoreTest, - pub workspace: WorkspacePB, - pub parent_view: ViewPB, - pub child_view: ViewPB, -} - -impl ViewTest { - #[allow(dead_code)] - pub async fn new(sdk: &FlowyCoreTest, layout: ViewLayoutPB, data: Vec) -> Self { - let workspace = create_workspace(sdk, "Workspace", "").await; - open_workspace(sdk, &workspace.id).await; - let app = create_app(sdk, "App", "AppFlowy GitHub Project", &workspace.id).await; - let view = create_view(sdk, &app.id, layout, data).await; - Self { - sdk: sdk.clone(), - workspace, - parent_view: app, - child_view: view, - } - } - - pub async fn new_grid_view(sdk: &FlowyCoreTest, data: Vec) -> Self { - Self::new(sdk, ViewLayoutPB::Grid, data).await - } - - pub async fn new_board_view(sdk: &FlowyCoreTest, data: Vec) -> Self { - Self::new(sdk, ViewLayoutPB::Board, data).await - } - - pub async fn new_calendar_view(sdk: &FlowyCoreTest, data: Vec) -> Self { - Self::new(sdk, ViewLayoutPB::Calendar, data).await - } - - pub async fn new_document_view(sdk: &FlowyCoreTest) -> Self { - Self::new(sdk, ViewLayoutPB::Document, vec![]).await - } -} - -async fn create_workspace(sdk: &FlowyCoreTest, name: &str, desc: &str) -> WorkspacePB { - let request = CreateWorkspacePayloadPB { - name: name.to_owned(), - desc: desc.to_owned(), - }; - - EventBuilder::new(sdk.clone()) - .event(CreateWorkspace) - .payload(request) - .async_send() - .await - .parse::() -} - -async fn open_workspace(sdk: &FlowyCoreTest, workspace_id: &str) { - let payload = WorkspaceIdPB { - value: Some(workspace_id.to_owned()), - }; - let _ = EventBuilder::new(sdk.clone()) - .event(OpenWorkspace) - .payload(payload) - .async_send() - .await; -} - -async fn create_app(sdk: &FlowyCoreTest, name: &str, desc: &str, workspace_id: &str) -> ViewPB { - let create_app_request = CreateViewPayloadPB { - parent_view_id: workspace_id.to_owned(), - name: name.to_string(), - desc: desc.to_string(), - thumbnail: None, - layout: ViewLayoutPB::Document, - initial_data: vec![], - meta: Default::default(), - set_as_current: true, - index: None, - }; - - EventBuilder::new(sdk.clone()) - .event(CreateView) - .payload(create_app_request) - .async_send() - .await - .parse::() -} - -async fn create_view( - sdk: &FlowyCoreTest, - app_id: &str, - layout: ViewLayoutPB, - data: Vec, -) -> ViewPB { - let payload = CreateViewPayloadPB { - parent_view_id: app_id.to_string(), - name: "View A".to_string(), - desc: "".to_string(), - thumbnail: Some("http://1.png".to_string()), - layout, - initial_data: data, - meta: Default::default(), - set_as_current: true, - index: None, - }; - - EventBuilder::new(sdk.clone()) - .event(CreateView) - .payload(payload) - .async_send() - .await - .parse::() -} diff --git a/frontend/rust-lib/event-integration/src/lib.rs b/frontend/rust-lib/event-integration/src/lib.rs index ea2b28c33dee..c7fe6c00d9fc 100644 --- a/frontend/rust-lib/event-integration/src/lib.rs +++ b/frontend/rust-lib/event-integration/src/lib.rs @@ -1,59 +1,33 @@ -use std::collections::HashMap; -use std::convert::TryFrom; use std::env::temp_dir; use std::path::PathBuf; use std::sync::Arc; -use bytes::Bytes; -use collab::core::collab::MutexCollab; -use collab::core::origin::CollabOrigin; -use collab::preclude::updates::decoder::Decode; -use collab::preclude::{merge_updates_v1, Update}; -use collab_document::blocks::DocumentData; -use collab_document::document::Document; use nanoid::nanoid; use parking_lot::RwLock; -use protobuf::ProtobufError; -use tokio::sync::broadcast::{channel, Sender}; -use uuid::Uuid; use flowy_core::{AppFlowyCore, AppFlowyCoreConfig}; -use flowy_database2::entities::*; -use flowy_database2::event_map::DatabaseEvent; -use flowy_document2::entities::{DocumentDataPB, OpenDocumentPayloadPB}; -use flowy_document2::event_map::DocumentEvent; -use flowy_folder2::entities::icon::UpdateViewIconPayloadPB; -use flowy_folder2::entities::*; -use flowy_folder2::event_map::FolderEvent; -use flowy_notification::entities::SubscribeObject; -use flowy_notification::{register_notification_sender, NotificationSender}; -use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID}; -use flowy_user::entities::{ - AuthTypePB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, UpdateCloudConfigPB, - UserCloudConfigPB, UserProfilePB, -}; -use flowy_user::errors::{FlowyError, FlowyResult}; -use flowy_user::event_map::UserEvent::*; +use flowy_notification::register_notification_sender; +use flowy_user::entities::AuthTypePB; -use crate::document::document_event::{DocumentEventTest, OpenDocumentData}; -use crate::event_builder::EventBuilder; -use crate::user_event::{async_sign_up, SignUpContext}; +use crate::test_user::TestNotificationSender; pub mod document; pub mod event_builder; -pub mod folder_event; -pub mod user_event; +pub mod test_database; +pub mod test_document; +pub mod test_folder; +pub mod test_user; #[derive(Clone)] -pub struct FlowyCoreTest { - auth_type: Arc>, - inner: AppFlowyCore, +pub struct EventIntegrationTest { + pub auth_type: Arc>, + pub inner: AppFlowyCore, #[allow(dead_code)] cleaner: Arc, pub notification_sender: TestNotificationSender, } -impl Default for FlowyCoreTest { +impl Default for EventIntegrationTest { fn default() -> Self { let temp_dir = temp_dir().join(nanoid!(6)); std::fs::create_dir_all(&temp_dir).unwrap(); @@ -61,51 +35,10 @@ impl Default for FlowyCoreTest { } } -impl FlowyCoreTest { +impl EventIntegrationTest { pub fn new() -> Self { Self::default() } - - pub async fn insert_document_text(&self, document_id: &str, text: &str, index: usize) { - let document_event = DocumentEventTest::new_with_core(self.clone()); - document_event - .insert_index(document_id, text, index, None) - .await; - } - - pub async fn get_document_data(&self, view_id: &str) -> DocumentData { - let pb = EventBuilder::new(self.clone()) - .event(DocumentEvent::GetDocumentData) - .payload(OpenDocumentPayloadPB { - document_id: view_id.to_string(), - }) - .async_send() - .await - .parse::(); - - DocumentData::from(pb) - } - - pub async fn get_document_update(&self, document_id: &str) -> Vec { - 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) - .await - .unwrap(); - - if remote_updates.is_empty() { - return vec![]; - } - - let updates = remote_updates - .iter() - .map(|update| update.as_ref()) - .collect::>(); - - merge_updates_v1(&updates).unwrap() - } - pub fn new_with_user_data_path(path: PathBuf, name: String) -> Self { let config = AppFlowyCoreConfig::new(path.to_str().unwrap(), name).log_filter( "trace", @@ -129,711 +62,9 @@ impl FlowyCoreTest { cleaner: Arc::new(Cleaner(path)), } } - - pub async fn enable_encryption(&self) -> String { - let config = EventBuilder::new(self.clone()) - .event(GetCloudConfig) - .async_send() - .await - .parse::(); - let update = UpdateCloudConfigPB { - enable_sync: None, - enable_encrypt: Some(true), - }; - let error = EventBuilder::new(self.clone()) - .event(SetCloudConfig) - .payload(update) - .async_send() - .await - .error(); - assert!(error.is_none()); - config.encrypt_secret - } - - pub async fn get_user_profile(&self) -> Result { - EventBuilder::new(self.clone()) - .event(GetUserProfile) - .async_send() - .await - .try_parse::() - } - - pub async fn new_with_guest_user() -> Self { - let test = Self::default(); - test.sign_up_as_guest().await; - test - } - - pub async fn sign_up_as_guest(&self) -> SignUpContext { - async_sign_up(self.inner.dispatcher(), AuthTypePB::Local).await - } - - pub async fn supabase_party_sign_up(&self) -> UserProfilePB { - let map = third_party_sign_up_param(Uuid::new_v4().to_string()); - let payload = OauthSignInPB { - map, - auth_type: AuthTypePB::Supabase, - }; - - EventBuilder::new(self.clone()) - .event(OauthSignIn) - .payload(payload) - .async_send() - .await - .parse::() - } - - pub async fn sign_out(&self) { - EventBuilder::new(self.clone()) - .event(SignOut) - .async_send() - .await; - } - - pub fn set_auth_type(&self, auth_type: AuthTypePB) { - *self.auth_type.write() = auth_type; - } - - pub async fn init_user(&self) -> UserProfilePB { - self.sign_up_as_guest().await.user_profile - } - - pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult { - let payload = SignInUrlPayloadPB { - email: email.to_string(), - auth_type: AuthTypePB::AFCloud, - }; - let sign_in_url = EventBuilder::new(self.clone()) - .event(GetSignInURL) - .payload(payload) - .async_send() - .await - .try_parse::()? - .sign_in_url; - - let mut map = HashMap::new(); - map.insert(USER_SIGN_IN_URL.to_string(), sign_in_url); - map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string()); - let payload = OauthSignInPB { - map, - auth_type: AuthTypePB::AFCloud, - }; - - let user_profile = EventBuilder::new(self.clone()) - .event(OauthSignIn) - .payload(payload) - .async_send() - .await - .try_parse::()?; - - Ok(user_profile) - } - - pub async fn supabase_sign_up_with_uuid( - &self, - uuid: &str, - email: Option, - ) -> FlowyResult { - let mut map = HashMap::new(); - map.insert(USER_UUID.to_string(), uuid.to_string()); - map.insert(USER_DEVICE_ID.to_string(), uuid.to_string()); - map.insert( - USER_EMAIL.to_string(), - email.unwrap_or_else(|| format!("{}@appflowy.io", nanoid!(10))), - ); - let payload = OauthSignInPB { - map, - auth_type: AuthTypePB::Supabase, - }; - - let user_profile = EventBuilder::new(self.clone()) - .event(OauthSignIn) - .payload(payload) - .async_send() - .await - .try_parse::()?; - - Ok(user_profile) - } - - // Must sign up/ sign in first - pub async fn get_current_workspace(&self) -> WorkspaceSettingPB { - EventBuilder::new(self.clone()) - .event(FolderEvent::GetCurrentWorkspace) - .async_send() - .await - .parse::() - } - - pub async fn get_all_workspace_views(&self) -> Vec { - EventBuilder::new(self.clone()) - .event(FolderEvent::ReadWorkspaceViews) - .async_send() - .await - .parse::() - .items - } - - pub async fn get_views(&self, parent_view_id: &str) -> ViewPB { - EventBuilder::new(self.clone()) - .event(FolderEvent::ReadView) - .payload(ViewIdPB { - value: parent_view_id.to_string(), - }) - .async_send() - .await - .parse::() - } - - pub async fn delete_view(&self, view_id: &str) { - let payload = RepeatedViewIdPB { - items: vec![view_id.to_string()], - }; - - // delete the view. the view will be moved to trash - EventBuilder::new(self.clone()) - .event(FolderEvent::DeleteView) - .payload(payload) - .async_send() - .await; - } - - pub async fn update_view(&self, changeset: UpdateViewPayloadPB) -> Option { - // delete the view. the view will be moved to trash - EventBuilder::new(self.clone()) - .event(FolderEvent::UpdateView) - .payload(changeset) - .async_send() - .await - .error() - } - - pub async fn update_view_icon(&self, payload: UpdateViewIconPayloadPB) -> Option { - EventBuilder::new(self.clone()) - .event(FolderEvent::UpdateViewIcon) - .payload(payload) - .async_send() - .await - .error() - } - - pub async fn create_view(&self, parent_id: &str, name: String) -> ViewPB { - let payload = CreateViewPayloadPB { - parent_view_id: parent_id.to_string(), - name, - desc: "".to_string(), - thumbnail: None, - layout: Default::default(), - initial_data: vec![], - meta: Default::default(), - set_as_current: false, - index: None, - }; - EventBuilder::new(self.clone()) - .event(FolderEvent::CreateView) - .payload(payload) - .async_send() - .await - .parse::() - } - - pub async fn create_document( - &self, - parent_id: &str, - name: String, - initial_data: Vec, - ) -> ViewPB { - let payload = CreateViewPayloadPB { - parent_view_id: parent_id.to_string(), - name, - desc: "".to_string(), - thumbnail: None, - layout: ViewLayoutPB::Document, - initial_data, - meta: Default::default(), - set_as_current: true, - index: None, - }; - let view = EventBuilder::new(self.clone()) - .event(FolderEvent::CreateView) - .payload(payload) - .async_send() - .await - .parse::(); - - let payload = OpenDocumentPayloadPB { - document_id: view.id.clone(), - }; - - let _ = EventBuilder::new(self.clone()) - .event(DocumentEvent::OpenDocument) - .payload(payload) - .async_send() - .await - .parse::(); - - view - } - - pub async fn create_grid(&self, parent_id: &str, name: String, initial_data: Vec) -> ViewPB { - let payload = CreateViewPayloadPB { - parent_view_id: parent_id.to_string(), - name, - desc: "".to_string(), - thumbnail: None, - layout: ViewLayoutPB::Grid, - initial_data, - meta: Default::default(), - set_as_current: true, - index: None, - }; - EventBuilder::new(self.clone()) - .event(FolderEvent::CreateView) - .payload(payload) - .async_send() - .await - .parse::() - } - - pub async fn open_database(&self, view_id: &str) { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetDatabase) - .payload(DatabaseViewIdPB { - value: view_id.to_string(), - }) - .async_send() - .await; - } - - pub async fn open_document(&self, doc_id: String) -> OpenDocumentData { - let payload = OpenDocumentPayloadPB { - document_id: doc_id.clone(), - }; - let data = EventBuilder::new(self.clone()) - .event(DocumentEvent::OpenDocument) - .payload(payload) - .async_send() - .await - .parse::(); - OpenDocumentData { id: doc_id, data } - } - - pub async fn create_board(&self, parent_id: &str, name: String, initial_data: Vec) -> ViewPB { - let payload = CreateViewPayloadPB { - parent_view_id: parent_id.to_string(), - name, - desc: "".to_string(), - thumbnail: None, - layout: ViewLayoutPB::Board, - initial_data, - meta: Default::default(), - set_as_current: true, - index: None, - }; - EventBuilder::new(self.clone()) - .event(FolderEvent::CreateView) - .payload(payload) - .async_send() - .await - .parse::() - } - - pub async fn create_calendar( - &self, - parent_id: &str, - name: String, - initial_data: Vec, - ) -> ViewPB { - let payload = CreateViewPayloadPB { - parent_view_id: parent_id.to_string(), - name, - desc: "".to_string(), - thumbnail: None, - layout: ViewLayoutPB::Calendar, - initial_data, - meta: Default::default(), - set_as_current: true, - index: None, - }; - EventBuilder::new(self.clone()) - .event(FolderEvent::CreateView) - .payload(payload) - .async_send() - .await - .parse::() - } - - pub async fn get_database(&self, view_id: &str) -> DatabasePB { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetDatabase) - .payload(DatabaseViewIdPB { - value: view_id.to_string(), - }) - .async_send() - .await - .parse::() - } - - pub async fn get_all_database_fields(&self, view_id: &str) -> RepeatedFieldPB { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetFields) - .payload(GetFieldPayloadPB { - view_id: view_id.to_string(), - field_ids: None, - }) - .async_send() - .await - .parse::() - } - - pub async fn create_field(&self, view_id: &str, field_type: FieldType) -> FieldPB { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::CreateTypeOption) - .payload(CreateFieldPayloadPB { - view_id: view_id.to_string(), - field_type, - type_option_data: None, - }) - .async_send() - .await - .parse::() - .field - } - - pub async fn update_field(&self, changeset: FieldChangesetPB) { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::UpdateField) - .payload(changeset) - .async_send() - .await; - } - - pub async fn delete_field(&self, view_id: &str, field_id: &str) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::DeleteField) - .payload(DeleteFieldPayloadPB { - view_id: view_id.to_string(), - field_id: field_id.to_string(), - }) - .async_send() - .await - .error() - } - - pub async fn update_field_type( - &self, - view_id: &str, - field_id: &str, - field_type: FieldType, - ) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::UpdateFieldType) - .payload(UpdateFieldTypePayloadPB { - view_id: view_id.to_string(), - field_id: field_id.to_string(), - field_type, - }) - .async_send() - .await - .error() - } - - pub async fn duplicate_field(&self, view_id: &str, field_id: &str) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::DuplicateField) - .payload(DuplicateFieldPayloadPB { - view_id: view_id.to_string(), - field_id: field_id.to_string(), - }) - .async_send() - .await - .error() - } - - pub async fn get_primary_field(&self, database_view_id: &str) -> FieldPB { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetPrimaryField) - .payload(DatabaseViewIdPB { - value: database_view_id.to_string(), - }) - .async_send() - .await - .parse::() - } - - pub async fn create_row( - &self, - view_id: &str, - start_row_id: Option, - data: Option, - ) -> RowMetaPB { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::CreateRow) - .payload(CreateRowPayloadPB { - view_id: view_id.to_string(), - start_row_id, - group_id: None, - data, - }) - .async_send() - .await - .parse::() - } - - pub async fn delete_row(&self, view_id: &str, row_id: &str) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::DeleteRow) - .payload(RowIdPB { - view_id: view_id.to_string(), - row_id: row_id.to_string(), - group_id: None, - }) - .async_send() - .await - .error() - } - - pub async fn get_row(&self, view_id: &str, row_id: &str) -> OptionalRowPB { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetRow) - .payload(RowIdPB { - view_id: view_id.to_string(), - row_id: row_id.to_string(), - group_id: None, - }) - .async_send() - .await - .parse::() - } - - pub async fn get_row_meta(&self, view_id: &str, row_id: &str) -> RowMetaPB { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetRowMeta) - .payload(RowIdPB { - view_id: view_id.to_string(), - row_id: row_id.to_string(), - group_id: None, - }) - .async_send() - .await - .parse::() - } - - pub async fn update_row_meta(&self, changeset: UpdateRowMetaChangesetPB) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::UpdateRowMeta) - .payload(changeset) - .async_send() - .await - .error() - } - - pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::DuplicateRow) - .payload(RowIdPB { - view_id: view_id.to_string(), - row_id: row_id.to_string(), - group_id: None, - }) - .async_send() - .await - .error() - } - - pub async fn move_row(&self, view_id: &str, row_id: &str, to_row_id: &str) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::MoveRow) - .payload(MoveRowPayloadPB { - view_id: view_id.to_string(), - from_row_id: row_id.to_string(), - to_row_id: to_row_id.to_string(), - }) - .async_send() - .await - .error() - } - - pub async fn update_cell(&self, changeset: CellChangesetPB) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::UpdateCell) - .payload(changeset) - .async_send() - .await - .error() - } - - pub async fn update_date_cell(&self, changeset: DateChangesetPB) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::UpdateDateCell) - .payload(changeset) - .async_send() - .await - .error() - } - - pub async fn get_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> CellPB { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetCell) - .payload(CellIdPB { - view_id: view_id.to_string(), - row_id: row_id.to_string(), - field_id: field_id.to_string(), - }) - .async_send() - .await - .parse::() - } - - pub async fn get_date_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> DateCellDataPB { - let cell = self.get_cell(view_id, row_id, field_id).await; - DateCellDataPB::try_from(Bytes::from(cell.data)).unwrap() - } - - pub async fn get_checklist_cell( - &self, - view_id: &str, - field_id: &str, - row_id: &str, - ) -> ChecklistCellDataPB { - let cell = self.get_cell(view_id, row_id, field_id).await; - ChecklistCellDataPB::try_from(Bytes::from(cell.data)).unwrap() - } - - pub async fn update_checklist_cell( - &self, - changeset: ChecklistCellDataChangesetPB, - ) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::UpdateChecklistCell) - .payload(changeset) - .async_send() - .await - .error() - } - - pub async fn insert_option( - &self, - view_id: &str, - field_id: &str, - row_id: &str, - name: &str, - ) -> Option { - let option = EventBuilder::new(self.clone()) - .event(DatabaseEvent::CreateSelectOption) - .payload(CreateSelectOptionPayloadPB { - field_id: field_id.to_string(), - view_id: view_id.to_string(), - option_name: name.to_string(), - }) - .async_send() - .await - .parse::(); - - EventBuilder::new(self.clone()) - .event(DatabaseEvent::InsertOrUpdateSelectOption) - .payload(RepeatedSelectOptionPayload { - view_id: view_id.to_string(), - field_id: field_id.to_string(), - row_id: row_id.to_string(), - items: vec![option], - }) - .async_send() - .await - .error() - } - - pub async fn get_groups(&self, view_id: &str) -> Vec { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetGroups) - .payload(DatabaseViewIdPB { - value: view_id.to_string(), - }) - .async_send() - .await - .parse::() - .items - } - - pub async fn move_group(&self, view_id: &str, from_id: &str, to_id: &str) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::MoveGroup) - .payload(MoveGroupPayloadPB { - view_id: view_id.to_string(), - from_group_id: from_id.to_string(), - to_group_id: to_id.to_string(), - }) - .async_send() - .await - .error() - } - - pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::SetGroupByField) - .payload(GroupByFieldPayloadPB { - field_id: field_id.to_string(), - view_id: view_id.to_string(), - }) - .async_send() - .await - .error() - } - - pub async fn update_group( - &self, - view_id: &str, - group_id: &str, - name: Option, - visible: Option, - ) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::UpdateGroup) - .payload(UpdateGroupPB { - view_id: view_id.to_string(), - group_id: group_id.to_string(), - name, - visible, - }) - .async_send() - .await - .error() - } - - pub async fn update_setting(&self, changeset: DatabaseSettingChangesetPB) -> Option { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::UpdateDatabaseSetting) - .payload(changeset) - .async_send() - .await - .error() - } - - pub async fn get_all_calendar_events(&self, view_id: &str) -> Vec { - EventBuilder::new(self.clone()) - .event(DatabaseEvent::GetAllCalendarEvents) - .payload(CalendarEventRequestPB { - view_id: view_id.to_string(), - }) - .async_send() - .await - .parse::() - .items - } - - pub async fn get_view(&self, view_id: &str) -> ViewPB { - EventBuilder::new(self.clone()) - .event(FolderEvent::ReadView) - .payload(ViewIdPB { - value: view_id.to_string(), - }) - .async_send() - .await - .parse::() - } } -impl std::ops::Deref for FlowyCoreTest { +impl std::ops::Deref for EventIntegrationTest { type Target = AppFlowyCore; fn deref(&self) -> &Self::Target { @@ -841,89 +72,6 @@ impl std::ops::Deref for FlowyCoreTest { } } -#[derive(Clone)] -pub struct TestNotificationSender { - sender: Arc>, -} - -impl Default for TestNotificationSender { - fn default() -> Self { - let (sender, _) = channel(1000); - Self { - sender: Arc::new(sender), - } - } -} - -impl TestNotificationSender { - pub fn new() -> Self { - Self::default() - } - - pub fn subscribe(&self, id: &str, ty: impl Into + Send) -> tokio::sync::mpsc::Receiver - where - T: TryFrom + Send + 'static, - { - let id = id.to_string(); - let (tx, rx) = tokio::sync::mpsc::channel::(10); - let mut receiver = self.sender.subscribe(); - let ty = ty.into(); - tokio::spawn(async move { - // DatabaseNotification::DidUpdateDatabaseSnapshotState - while let Ok(value) = receiver.recv().await { - if value.id == id && value.ty == ty { - if let Some(payload) = value.payload { - match T::try_from(Bytes::from(payload)) { - Ok(object) => { - let _ = tx.send(object).await; - }, - Err(e) => { - panic!( - "Failed to parse notification payload to type: {:?} with error: {}", - std::any::type_name::(), - e - ); - }, - } - } - } - } - }); - rx - } - - pub fn subscribe_with_condition(&self, id: &str, when: F) -> tokio::sync::mpsc::Receiver - where - T: TryFrom + Send + 'static, - F: Fn(&T) -> bool + Send + 'static, - { - let id = id.to_string(); - let (tx, rx) = tokio::sync::mpsc::channel::(10); - let mut receiver = self.sender.subscribe(); - tokio::spawn(async move { - while let Ok(value) = receiver.recv().await { - if value.id == id { - if let Some(payload) = value.payload { - if let Ok(object) = T::try_from(Bytes::from(payload)) { - if when(&object) { - let _ = tx.send(object).await; - } - } - } - } - } - }); - rx - } -} - -impl NotificationSender for TestNotificationSender { - fn send_subject(&self, subject: SubscribeObject) -> Result<(), String> { - let _ = self.sender.send(subject); - Ok(()) - } -} - pub struct Cleaner(PathBuf); impl Cleaner { @@ -941,25 +89,3 @@ impl Drop for Cleaner { Self::cleanup(&self.0) } } - -pub fn third_party_sign_up_param(uuid: String) -> HashMap { - let mut params = HashMap::new(); - params.insert(USER_UUID.to_string(), uuid); - params.insert( - USER_EMAIL.to_string(), - format!("{}@test.com", Uuid::new_v4()), - ); - params.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string()); - params -} - -pub fn assert_document_data_equal(collab_update: &[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(); - txn.apply_update(update); - }); - let document = Document::open(Arc::new(collab)).unwrap(); - let actual = document.get_document_data().unwrap(); - assert_eq!(actual, expected); -} diff --git a/frontend/rust-lib/event-integration/src/test_database.rs b/frontend/rust-lib/event-integration/src/test_database.rs new file mode 100644 index 000000000000..ebd258e4b131 --- /dev/null +++ b/frontend/rust-lib/event-integration/src/test_database.rs @@ -0,0 +1,447 @@ +use std::convert::TryFrom; + +use bytes::Bytes; + +use flowy_database2::entities::*; +use flowy_database2::event_map::DatabaseEvent; +use flowy_folder2::entities::*; +use flowy_folder2::event_map::FolderEvent; +use flowy_user::errors::FlowyError; + +use crate::event_builder::EventBuilder; +use crate::EventIntegrationTest; + +impl EventIntegrationTest { + pub async fn create_grid(&self, parent_id: &str, name: String, initial_data: Vec) -> ViewPB { + let payload = CreateViewPayloadPB { + parent_view_id: parent_id.to_string(), + name, + desc: "".to_string(), + thumbnail: None, + layout: ViewLayoutPB::Grid, + initial_data, + meta: Default::default(), + set_as_current: true, + index: None, + }; + EventBuilder::new(self.clone()) + .event(FolderEvent::CreateView) + .payload(payload) + .async_send() + .await + .parse::() + } + + pub async fn open_database(&self, view_id: &str) { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetDatabase) + .payload(DatabaseViewIdPB { + value: view_id.to_string(), + }) + .async_send() + .await; + } + + pub async fn create_board(&self, parent_id: &str, name: String, initial_data: Vec) -> ViewPB { + let payload = CreateViewPayloadPB { + parent_view_id: parent_id.to_string(), + name, + desc: "".to_string(), + thumbnail: None, + layout: ViewLayoutPB::Board, + initial_data, + meta: Default::default(), + set_as_current: true, + index: None, + }; + EventBuilder::new(self.clone()) + .event(FolderEvent::CreateView) + .payload(payload) + .async_send() + .await + .parse::() + } + + pub async fn create_calendar( + &self, + parent_id: &str, + name: String, + initial_data: Vec, + ) -> ViewPB { + let payload = CreateViewPayloadPB { + parent_view_id: parent_id.to_string(), + name, + desc: "".to_string(), + thumbnail: None, + layout: ViewLayoutPB::Calendar, + initial_data, + meta: Default::default(), + set_as_current: true, + index: None, + }; + EventBuilder::new(self.clone()) + .event(FolderEvent::CreateView) + .payload(payload) + .async_send() + .await + .parse::() + } + + pub async fn get_database(&self, view_id: &str) -> DatabasePB { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetDatabase) + .payload(DatabaseViewIdPB { + value: view_id.to_string(), + }) + .async_send() + .await + .parse::() + } + + pub async fn get_all_database_fields(&self, view_id: &str) -> RepeatedFieldPB { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetFields) + .payload(GetFieldPayloadPB { + view_id: view_id.to_string(), + field_ids: None, + }) + .async_send() + .await + .parse::() + } + + pub async fn create_field(&self, view_id: &str, field_type: FieldType) -> FieldPB { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::CreateTypeOption) + .payload(CreateFieldPayloadPB { + view_id: view_id.to_string(), + field_type, + type_option_data: None, + }) + .async_send() + .await + .parse::() + .field + } + + pub async fn update_field(&self, changeset: FieldChangesetPB) { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::UpdateField) + .payload(changeset) + .async_send() + .await; + } + + pub async fn delete_field(&self, view_id: &str, field_id: &str) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::DeleteField) + .payload(DeleteFieldPayloadPB { + view_id: view_id.to_string(), + field_id: field_id.to_string(), + }) + .async_send() + .await + .error() + } + + pub async fn update_field_type( + &self, + view_id: &str, + field_id: &str, + field_type: FieldType, + ) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::UpdateFieldType) + .payload(UpdateFieldTypePayloadPB { + view_id: view_id.to_string(), + field_id: field_id.to_string(), + field_type, + }) + .async_send() + .await + .error() + } + + pub async fn duplicate_field(&self, view_id: &str, field_id: &str) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::DuplicateField) + .payload(DuplicateFieldPayloadPB { + view_id: view_id.to_string(), + field_id: field_id.to_string(), + }) + .async_send() + .await + .error() + } + + pub async fn get_primary_field(&self, database_view_id: &str) -> FieldPB { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetPrimaryField) + .payload(DatabaseViewIdPB { + value: database_view_id.to_string(), + }) + .async_send() + .await + .parse::() + } + + pub async fn create_row( + &self, + view_id: &str, + start_row_id: Option, + data: Option, + ) -> RowMetaPB { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::CreateRow) + .payload(CreateRowPayloadPB { + view_id: view_id.to_string(), + start_row_id, + group_id: None, + data, + }) + .async_send() + .await + .parse::() + } + + pub async fn delete_row(&self, view_id: &str, row_id: &str) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::DeleteRow) + .payload(RowIdPB { + view_id: view_id.to_string(), + row_id: row_id.to_string(), + group_id: None, + }) + .async_send() + .await + .error() + } + + pub async fn get_row(&self, view_id: &str, row_id: &str) -> OptionalRowPB { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetRow) + .payload(RowIdPB { + view_id: view_id.to_string(), + row_id: row_id.to_string(), + group_id: None, + }) + .async_send() + .await + .parse::() + } + + pub async fn get_row_meta(&self, view_id: &str, row_id: &str) -> RowMetaPB { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetRowMeta) + .payload(RowIdPB { + view_id: view_id.to_string(), + row_id: row_id.to_string(), + group_id: None, + }) + .async_send() + .await + .parse::() + } + + pub async fn update_row_meta(&self, changeset: UpdateRowMetaChangesetPB) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::UpdateRowMeta) + .payload(changeset) + .async_send() + .await + .error() + } + + pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::DuplicateRow) + .payload(RowIdPB { + view_id: view_id.to_string(), + row_id: row_id.to_string(), + group_id: None, + }) + .async_send() + .await + .error() + } + + pub async fn move_row(&self, view_id: &str, row_id: &str, to_row_id: &str) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::MoveRow) + .payload(MoveRowPayloadPB { + view_id: view_id.to_string(), + from_row_id: row_id.to_string(), + to_row_id: to_row_id.to_string(), + }) + .async_send() + .await + .error() + } + + pub async fn update_cell(&self, changeset: CellChangesetPB) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::UpdateCell) + .payload(changeset) + .async_send() + .await + .error() + } + + pub async fn update_date_cell(&self, changeset: DateChangesetPB) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::UpdateDateCell) + .payload(changeset) + .async_send() + .await + .error() + } + + pub async fn get_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> CellPB { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetCell) + .payload(CellIdPB { + view_id: view_id.to_string(), + row_id: row_id.to_string(), + field_id: field_id.to_string(), + }) + .async_send() + .await + .parse::() + } + + pub async fn get_date_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> DateCellDataPB { + let cell = self.get_cell(view_id, row_id, field_id).await; + DateCellDataPB::try_from(Bytes::from(cell.data)).unwrap() + } + + pub async fn get_checklist_cell( + &self, + view_id: &str, + field_id: &str, + row_id: &str, + ) -> ChecklistCellDataPB { + let cell = self.get_cell(view_id, row_id, field_id).await; + ChecklistCellDataPB::try_from(Bytes::from(cell.data)).unwrap() + } + + pub async fn update_checklist_cell( + &self, + changeset: ChecklistCellDataChangesetPB, + ) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::UpdateChecklistCell) + .payload(changeset) + .async_send() + .await + .error() + } + + pub async fn insert_option( + &self, + view_id: &str, + field_id: &str, + row_id: &str, + name: &str, + ) -> Option { + let option = EventBuilder::new(self.clone()) + .event(DatabaseEvent::CreateSelectOption) + .payload(CreateSelectOptionPayloadPB { + field_id: field_id.to_string(), + view_id: view_id.to_string(), + option_name: name.to_string(), + }) + .async_send() + .await + .parse::(); + + EventBuilder::new(self.clone()) + .event(DatabaseEvent::InsertOrUpdateSelectOption) + .payload(RepeatedSelectOptionPayload { + view_id: view_id.to_string(), + field_id: field_id.to_string(), + row_id: row_id.to_string(), + items: vec![option], + }) + .async_send() + .await + .error() + } + + pub async fn get_groups(&self, view_id: &str) -> Vec { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetGroups) + .payload(DatabaseViewIdPB { + value: view_id.to_string(), + }) + .async_send() + .await + .parse::() + .items + } + + pub async fn move_group(&self, view_id: &str, from_id: &str, to_id: &str) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::MoveGroup) + .payload(MoveGroupPayloadPB { + view_id: view_id.to_string(), + from_group_id: from_id.to_string(), + to_group_id: to_id.to_string(), + }) + .async_send() + .await + .error() + } + + pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::SetGroupByField) + .payload(GroupByFieldPayloadPB { + field_id: field_id.to_string(), + view_id: view_id.to_string(), + }) + .async_send() + .await + .error() + } + + pub async fn update_group( + &self, + view_id: &str, + group_id: &str, + name: Option, + visible: Option, + ) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::UpdateGroup) + .payload(UpdateGroupPB { + view_id: view_id.to_string(), + group_id: group_id.to_string(), + name, + visible, + }) + .async_send() + .await + .error() + } + + pub async fn update_setting(&self, changeset: DatabaseSettingChangesetPB) -> Option { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::UpdateDatabaseSetting) + .payload(changeset) + .async_send() + .await + .error() + } + + pub async fn get_all_calendar_events(&self, view_id: &str) -> Vec { + EventBuilder::new(self.clone()) + .event(DatabaseEvent::GetAllCalendarEvents) + .payload(CalendarEventRequestPB { + view_id: view_id.to_string(), + }) + .async_send() + .await + .parse::() + .items + } +} diff --git a/frontend/rust-lib/event-integration/src/test_document.rs b/frontend/rust-lib/event-integration/src/test_document.rs new file mode 100644 index 000000000000..45029f2d3660 --- /dev/null +++ b/frontend/rust-lib/event-integration/src/test_document.rs @@ -0,0 +1,119 @@ +use std::sync::Arc; + +use collab::core::collab::MutexCollab; +use collab::core::origin::CollabOrigin; +use collab::preclude::updates::decoder::Decode; +use collab::preclude::{merge_updates_v1, Update}; +use collab_document::blocks::DocumentData; +use collab_document::document::Document; + +use flowy_document2::entities::{DocumentDataPB, OpenDocumentPayloadPB}; +use flowy_document2::event_map::DocumentEvent; +use flowy_folder2::entities::{CreateViewPayloadPB, ViewLayoutPB, ViewPB}; +use flowy_folder2::event_map::FolderEvent; + +use crate::document::document_event::{DocumentEventTest, OpenDocumentData}; +use crate::event_builder::EventBuilder; +use crate::EventIntegrationTest; + +impl EventIntegrationTest { + pub async fn create_document( + &self, + parent_id: &str, + name: String, + initial_data: Vec, + ) -> ViewPB { + let payload = CreateViewPayloadPB { + parent_view_id: parent_id.to_string(), + name, + desc: "".to_string(), + thumbnail: None, + layout: ViewLayoutPB::Document, + initial_data, + meta: Default::default(), + set_as_current: true, + index: None, + }; + let view = EventBuilder::new(self.clone()) + .event(FolderEvent::CreateView) + .payload(payload) + .async_send() + .await + .parse::(); + + let payload = OpenDocumentPayloadPB { + document_id: view.id.clone(), + }; + + let _ = EventBuilder::new(self.clone()) + .event(DocumentEvent::OpenDocument) + .payload(payload) + .async_send() + .await + .parse::(); + + view + } + pub async fn open_document(&self, doc_id: String) -> OpenDocumentData { + let payload = OpenDocumentPayloadPB { + document_id: doc_id.clone(), + }; + let data = EventBuilder::new(self.clone()) + .event(DocumentEvent::OpenDocument) + .payload(payload) + .async_send() + .await + .parse::(); + OpenDocumentData { id: doc_id, data } + } + pub async fn insert_document_text(&self, document_id: &str, text: &str, index: usize) { + let document_event = DocumentEventTest::new_with_core(self.clone()); + document_event + .insert_index(document_id, text, index, None) + .await; + } + + pub async fn get_document_data(&self, view_id: &str) -> DocumentData { + let pb = EventBuilder::new(self.clone()) + .event(DocumentEvent::GetDocumentData) + .payload(OpenDocumentPayloadPB { + document_id: view_id.to_string(), + }) + .async_send() + .await + .parse::(); + + DocumentData::from(pb) + } + + pub async fn get_document_update(&self, document_id: &str) -> Vec { + 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) + .await + .unwrap(); + + if remote_updates.is_empty() { + return vec![]; + } + + let updates = remote_updates + .iter() + .map(|update| update.as_ref()) + .collect::>(); + + merge_updates_v1(&updates).unwrap() + } +} + +pub fn assert_document_data_equal(collab_update: &[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(); + txn.apply_update(update); + }); + let document = Document::open(Arc::new(collab)).unwrap(); + let actual = document.get_document_data().unwrap(); + assert_eq!(actual, expected); +} diff --git a/frontend/rust-lib/event-integration/src/test_folder.rs b/frontend/rust-lib/event-integration/src/test_folder.rs new file mode 100644 index 000000000000..d5f0e7a141c2 --- /dev/null +++ b/frontend/rust-lib/event-integration/src/test_folder.rs @@ -0,0 +1,173 @@ +use flowy_folder2::entities::icon::UpdateViewIconPayloadPB; +use flowy_folder2::entities::*; +use flowy_folder2::event_map::FolderEvent; +use flowy_folder2::event_map::FolderEvent::*; +use flowy_user::errors::FlowyError; + +use crate::event_builder::EventBuilder; +use crate::EventIntegrationTest; + +impl EventIntegrationTest { + // Must sign up/ sign in first + pub async fn get_current_workspace(&self) -> WorkspaceSettingPB { + EventBuilder::new(self.clone()) + .event(FolderEvent::GetCurrentWorkspace) + .async_send() + .await + .parse::() + } + + pub async fn get_all_workspace_views(&self) -> Vec { + EventBuilder::new(self.clone()) + .event(FolderEvent::ReadWorkspaceViews) + .async_send() + .await + .parse::() + .items + } + + pub async fn get_views(&self, parent_view_id: &str) -> ViewPB { + EventBuilder::new(self.clone()) + .event(FolderEvent::ReadView) + .payload(ViewIdPB { + value: parent_view_id.to_string(), + }) + .async_send() + .await + .parse::() + } + + pub async fn delete_view(&self, view_id: &str) { + let payload = RepeatedViewIdPB { + items: vec![view_id.to_string()], + }; + + // delete the view. the view will be moved to trash + EventBuilder::new(self.clone()) + .event(FolderEvent::DeleteView) + .payload(payload) + .async_send() + .await; + } + + pub async fn update_view(&self, changeset: UpdateViewPayloadPB) -> Option { + // delete the view. the view will be moved to trash + EventBuilder::new(self.clone()) + .event(FolderEvent::UpdateView) + .payload(changeset) + .async_send() + .await + .error() + } + + pub async fn update_view_icon(&self, payload: UpdateViewIconPayloadPB) -> Option { + EventBuilder::new(self.clone()) + .event(FolderEvent::UpdateViewIcon) + .payload(payload) + .async_send() + .await + .error() + } + + pub async fn create_view(&self, parent_id: &str, name: String) -> ViewPB { + let payload = CreateViewPayloadPB { + parent_view_id: parent_id.to_string(), + name, + desc: "".to_string(), + thumbnail: None, + layout: Default::default(), + initial_data: vec![], + meta: Default::default(), + set_as_current: false, + index: None, + }; + EventBuilder::new(self.clone()) + .event(FolderEvent::CreateView) + .payload(payload) + .async_send() + .await + .parse::() + } + + pub async fn get_view(&self, view_id: &str) -> ViewPB { + EventBuilder::new(self.clone()) + .event(FolderEvent::ReadView) + .payload(ViewIdPB { + value: view_id.to_string(), + }) + .async_send() + .await + .parse::() + } +} + +pub struct ViewTest { + pub sdk: EventIntegrationTest, + pub workspace: WorkspacePB, + pub child_view: ViewPB, +} + +impl ViewTest { + #[allow(dead_code)] + pub async fn new(sdk: &EventIntegrationTest, layout: ViewLayoutPB, data: Vec) -> Self { + let workspace = create_workspace(sdk, "Workspace", "").await; + let payload = WorkspaceIdPB { + value: Some(workspace.id.clone()), + }; + let _ = EventBuilder::new(sdk.clone()) + .event(OpenWorkspace) + .payload(payload) + .async_send() + .await; + + let payload = CreateViewPayloadPB { + parent_view_id: workspace.id.clone(), + name: "View A".to_string(), + desc: "".to_string(), + thumbnail: Some("http://1.png".to_string()), + layout, + initial_data: data, + meta: Default::default(), + set_as_current: true, + index: None, + }; + + let view = EventBuilder::new(sdk.clone()) + .event(CreateView) + .payload(payload) + .async_send() + .await + .parse::(); + Self { + sdk: sdk.clone(), + workspace, + child_view: view, + } + } + + pub async fn new_grid_view(sdk: &EventIntegrationTest, data: Vec) -> Self { + Self::new(sdk, ViewLayoutPB::Grid, data).await + } + + pub async fn new_board_view(sdk: &EventIntegrationTest, data: Vec) -> Self { + Self::new(sdk, ViewLayoutPB::Board, data).await + } + + pub async fn new_calendar_view(sdk: &EventIntegrationTest, data: Vec) -> Self { + Self::new(sdk, ViewLayoutPB::Calendar, data).await + } +} + +async fn create_workspace(sdk: &EventIntegrationTest, name: &str, desc: &str) -> WorkspacePB { + let request = CreateWorkspacePayloadPB { + name: name.to_owned(), + desc: desc.to_owned(), + }; + + EventBuilder::new(sdk.clone()) + .event(CreateWorkspace) + .payload(request) + .async_send() + .await + .parse::() +} diff --git a/frontend/rust-lib/event-integration/src/test_user.rs b/frontend/rust-lib/event-integration/src/test_user.rs new file mode 100644 index 000000000000..b09c4b9acc3b --- /dev/null +++ b/frontend/rust-lib/event-integration/src/test_user.rs @@ -0,0 +1,296 @@ +use std::collections::HashMap; +use std::convert::TryFrom; +use std::sync::Arc; + +use bytes::Bytes; +use nanoid::nanoid; +use protobuf::ProtobufError; +use tokio::sync::broadcast::{channel, Sender}; +use uuid::Uuid; + +use flowy_notification::entities::SubscribeObject; +use flowy_notification::NotificationSender; +use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID}; +use flowy_user::entities::{ + AuthTypePB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, SignUpPayloadPB, UpdateCloudConfigPB, + UpdateUserProfilePayloadPB, UserCloudConfigPB, UserProfilePB, +}; +use flowy_user::errors::{FlowyError, FlowyResult}; +use flowy_user::event_map::UserEvent::*; +use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes}; + +use crate::event_builder::EventBuilder; +use crate::EventIntegrationTest; + +impl EventIntegrationTest { + pub async fn enable_encryption(&self) -> String { + let config = EventBuilder::new(self.clone()) + .event(GetCloudConfig) + .async_send() + .await + .parse::(); + let update = UpdateCloudConfigPB { + enable_sync: None, + enable_encrypt: Some(true), + }; + let error = EventBuilder::new(self.clone()) + .event(SetCloudConfig) + .payload(update) + .async_send() + .await + .error(); + assert!(error.is_none()); + config.encrypt_secret + } + + pub async fn new_with_guest_user() -> Self { + let test = Self::default(); + test.sign_up_as_guest().await; + test + } + + pub async fn sign_up_as_guest(&self) -> SignUpContext { + let password = login_password(); + let email = unique_email(); + let payload = SignUpPayloadPB { + email, + name: "appflowy".to_string(), + password: password.clone(), + auth_type: AuthTypePB::Local, + device_id: uuid::Uuid::new_v4().to_string(), + } + .into_bytes() + .unwrap(); + + let request = AFPluginRequest::new(SignUp).payload(payload); + let user_profile = AFPluginDispatcher::async_send(self.inner.dispatcher(), request) + .await + .parse::() + .unwrap() + .unwrap(); + + // let _ = create_default_workspace_if_need(dispatch.clone(), &user_profile.id); + SignUpContext { + user_profile, + password, + } + } + + pub async fn af_cloud_sign_up(&self) -> UserProfilePB { + let email = unique_email(); + self.af_cloud_sign_in_with_email(&email).await.unwrap() + } + + pub async fn supabase_party_sign_up(&self) -> UserProfilePB { + let map = third_party_sign_up_param(Uuid::new_v4().to_string()); + let payload = OauthSignInPB { + map, + auth_type: AuthTypePB::Supabase, + }; + + EventBuilder::new(self.clone()) + .event(OauthSignIn) + .payload(payload) + .async_send() + .await + .parse::() + } + + pub async fn sign_out(&self) { + EventBuilder::new(self.clone()) + .event(SignOut) + .async_send() + .await; + } + + pub fn set_auth_type(&self, auth_type: AuthTypePB) { + *self.auth_type.write() = auth_type; + } + + pub async fn init_user(&self) -> UserProfilePB { + self.sign_up_as_guest().await.user_profile + } + + pub async fn get_user_profile(&self) -> Result { + EventBuilder::new(self.clone()) + .event(GetUserProfile) + .async_send() + .await + .try_parse::() + } + + pub async fn update_user_profile(&self, params: UpdateUserProfilePayloadPB) { + EventBuilder::new(self.clone()) + .event(UpdateUserProfile) + .payload(params) + .async_send() + .await; + } + + pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult { + let payload = SignInUrlPayloadPB { + email: email.to_string(), + auth_type: AuthTypePB::AFCloud, + }; + let sign_in_url = EventBuilder::new(self.clone()) + .event(GetSignInURL) + .payload(payload) + .async_send() + .await + .try_parse::()? + .sign_in_url; + + let mut map = HashMap::new(); + map.insert(USER_SIGN_IN_URL.to_string(), sign_in_url); + map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string()); + let payload = OauthSignInPB { + map, + auth_type: AuthTypePB::AFCloud, + }; + + let user_profile = EventBuilder::new(self.clone()) + .event(OauthSignIn) + .payload(payload) + .async_send() + .await + .try_parse::()?; + + Ok(user_profile) + } + + pub async fn supabase_sign_up_with_uuid( + &self, + uuid: &str, + email: Option, + ) -> FlowyResult { + let mut map = HashMap::new(); + map.insert(USER_UUID.to_string(), uuid.to_string()); + map.insert(USER_DEVICE_ID.to_string(), uuid.to_string()); + map.insert( + USER_EMAIL.to_string(), + email.unwrap_or_else(|| format!("{}@appflowy.io", nanoid!(10))), + ); + let payload = OauthSignInPB { + map, + auth_type: AuthTypePB::Supabase, + }; + + let user_profile = EventBuilder::new(self.clone()) + .event(OauthSignIn) + .payload(payload) + .async_send() + .await + .try_parse::()?; + + Ok(user_profile) + } +} + +#[derive(Clone)] +pub struct TestNotificationSender { + sender: Arc>, +} + +impl Default for TestNotificationSender { + fn default() -> Self { + let (sender, _) = channel(1000); + Self { + sender: Arc::new(sender), + } + } +} + +impl TestNotificationSender { + pub fn new() -> Self { + Self::default() + } + + pub fn subscribe(&self, id: &str, ty: impl Into + Send) -> tokio::sync::mpsc::Receiver + where + T: TryFrom + Send + 'static, + { + let id = id.to_string(); + let (tx, rx) = tokio::sync::mpsc::channel::(10); + let mut receiver = self.sender.subscribe(); + let ty = ty.into(); + tokio::spawn(async move { + // DatabaseNotification::DidUpdateDatabaseSnapshotState + while let Ok(value) = receiver.recv().await { + if value.id == id && value.ty == ty { + if let Some(payload) = value.payload { + match T::try_from(Bytes::from(payload)) { + Ok(object) => { + let _ = tx.send(object).await; + }, + Err(e) => { + panic!( + "Failed to parse notification payload to type: {:?} with error: {}", + std::any::type_name::(), + e + ); + }, + } + } + } + } + }); + rx + } + + pub fn subscribe_with_condition(&self, id: &str, when: F) -> tokio::sync::mpsc::Receiver + where + T: TryFrom + Send + 'static, + F: Fn(&T) -> bool + Send + 'static, + { + let id = id.to_string(); + let (tx, rx) = tokio::sync::mpsc::channel::(10); + let mut receiver = self.sender.subscribe(); + tokio::spawn(async move { + while let Ok(value) = receiver.recv().await { + if value.id == id { + if let Some(payload) = value.payload { + if let Ok(object) = T::try_from(Bytes::from(payload)) { + if when(&object) { + let _ = tx.send(object).await; + } + } + } + } + } + }); + rx + } +} +impl NotificationSender for TestNotificationSender { + fn send_subject(&self, subject: SubscribeObject) -> Result<(), String> { + let _ = self.sender.send(subject); + Ok(()) + } +} + +pub fn third_party_sign_up_param(uuid: String) -> HashMap { + let mut params = HashMap::new(); + params.insert(USER_UUID.to_string(), uuid); + params.insert( + USER_EMAIL.to_string(), + format!("{}@test.com", Uuid::new_v4()), + ); + params.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string()); + params +} + +pub fn unique_email() -> String { + format!("{}@appflowy.io", Uuid::new_v4()) +} + +pub fn login_email() -> String { + "annie2@appflowy.io".to_string() +} + +pub fn login_password() -> String { + "HelloWorld!123".to_string() +} +pub struct SignUpContext { + pub user_profile: UserProfilePB, + pub password: String, +} diff --git a/frontend/rust-lib/event-integration/src/user_event.rs b/frontend/rust-lib/event-integration/src/user_event.rs deleted file mode 100644 index 67e44e732e6f..000000000000 --- a/frontend/rust-lib/event-integration/src/user_event.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::sync::Arc; - -use nanoid::nanoid; - -use flowy_user::entities::{AuthTypePB, SignUpPayloadPB, UserProfilePB}; -use flowy_user::errors::FlowyError; -use flowy_user::event_map::UserEvent::*; -use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes}; - -pub fn random_email() -> String { - format!("{}@appflowy.io", nanoid!(20)) -} - -pub fn login_email() -> String { - "annie2@appflowy.io".to_string() -} - -pub fn login_password() -> String { - "HelloWorld!123".to_string() -} - -pub struct SignUpContext { - pub user_profile: UserProfilePB, - pub password: String, -} - -pub fn sign_up(dispatch: Arc) -> SignUpContext { - let password = login_password(); - let payload = SignUpPayloadPB { - email: random_email(), - name: "app flowy".to_string(), - password: password.clone(), - auth_type: AuthTypePB::Local, - device_id: uuid::Uuid::new_v4().to_string(), - } - .into_bytes() - .unwrap(); - - let request = AFPluginRequest::new(SignUp).payload(payload); - let user_profile = AFPluginDispatcher::sync_send(dispatch, request) - .parse::() - .unwrap() - .unwrap(); - - SignUpContext { - user_profile, - password, - } -} - -pub async fn async_sign_up( - dispatch: Arc, - auth_type: AuthTypePB, -) -> SignUpContext { - let password = login_password(); - let email = random_email(); - let payload = SignUpPayloadPB { - email, - name: "appflowy".to_string(), - password: password.clone(), - auth_type, - device_id: uuid::Uuid::new_v4().to_string(), - } - .into_bytes() - .unwrap(); - - let request = AFPluginRequest::new(SignUp).payload(payload); - let user_profile = AFPluginDispatcher::async_send(dispatch.clone(), request) - .await - .parse::() - .unwrap() - .unwrap(); - - // let _ = create_default_workspace_if_need(dispatch.clone(), &user_profile.id); - SignUpContext { - user_profile, - password, - } -} - -pub async fn init_user_setting(dispatch: Arc) { - let request = AFPluginRequest::new(InitUser); - let _ = AFPluginDispatcher::async_send(dispatch.clone(), request).await; -} diff --git a/frontend/rust-lib/event-integration/tests/database/local_test/test.rs b/frontend/rust-lib/event-integration/tests/database/local_test/test.rs index d21a1596ceb5..03c7d5cbd36e 100644 --- a/frontend/rust-lib/event-integration/tests/database/local_test/test.rs +++ b/frontend/rust-lib/event-integration/tests/database/local_test/test.rs @@ -3,7 +3,7 @@ use std::convert::TryFrom; use bytes::Bytes; use event_integration::event_builder::EventBuilder; -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_database2::entities::{ CellChangesetPB, CellIdPB, ChecklistCellDataChangesetPB, DatabaseLayoutPB, DatabaseSettingChangesetPB, DatabaseViewIdPB, DateChangesetPB, FieldType, SelectOptionCellDataPB, @@ -13,7 +13,7 @@ use lib_infra::util::timestamp; #[tokio::test] async fn get_database_id_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -35,7 +35,7 @@ async fn get_database_id_event_test() { #[tokio::test] async fn get_database_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -49,7 +49,7 @@ async fn get_database_event_test() { #[tokio::test] async fn get_field_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -64,7 +64,7 @@ async fn get_field_event_test() { #[tokio::test] async fn create_field_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -78,7 +78,7 @@ async fn create_field_event_test() { #[tokio::test] async fn delete_field_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -99,7 +99,7 @@ async fn delete_field_event_test() { // The primary field is not allowed to be deleted. #[tokio::test] async fn delete_primary_field_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -114,7 +114,7 @@ async fn delete_primary_field_event_test() { #[tokio::test] async fn update_field_type_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -132,7 +132,7 @@ async fn update_field_type_event_test() { #[tokio::test] async fn update_primary_field_type_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -151,7 +151,7 @@ async fn update_primary_field_type_event_test() { #[tokio::test] async fn duplicate_field_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -169,7 +169,7 @@ async fn duplicate_field_event_test() { // The primary field is not allowed to be duplicated. So this test should return an error. #[tokio::test] async fn duplicate_primary_field_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -183,7 +183,7 @@ async fn duplicate_primary_field_test() { #[tokio::test] async fn get_primary_field_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -196,7 +196,7 @@ async fn get_primary_field_event_test() { #[tokio::test] async fn create_row_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -209,7 +209,7 @@ async fn create_row_event_test() { #[tokio::test] async fn delete_row_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -232,7 +232,7 @@ async fn delete_row_event_test() { #[tokio::test] async fn get_row_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -248,7 +248,7 @@ async fn get_row_event_test() { #[tokio::test] async fn update_row_meta_event_with_url_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -276,7 +276,7 @@ async fn update_row_meta_event_with_url_test() { #[tokio::test] async fn update_row_meta_event_with_cover_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -304,7 +304,7 @@ async fn update_row_meta_event_with_cover_test() { #[tokio::test] async fn delete_row_event_with_invalid_row_id_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -317,7 +317,7 @@ async fn delete_row_event_with_invalid_row_id_test() { #[tokio::test] async fn duplicate_row_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -334,7 +334,7 @@ async fn duplicate_row_event_test() { #[tokio::test] async fn duplicate_row_event_with_invalid_row_id_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -351,7 +351,7 @@ async fn duplicate_row_event_with_invalid_row_id_test() { #[tokio::test] async fn move_row_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -371,7 +371,7 @@ async fn move_row_event_test() { #[tokio::test] async fn move_row_event_test2() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -391,7 +391,7 @@ async fn move_row_event_test2() { #[tokio::test] async fn move_row_event_with_invalid_row_id_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -418,7 +418,7 @@ async fn move_row_event_with_invalid_row_id_test() { #[tokio::test] async fn update_text_cell_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -448,7 +448,7 @@ async fn update_text_cell_event_test() { #[tokio::test] async fn update_checkbox_cell_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -479,7 +479,7 @@ async fn update_checkbox_cell_event_test() { #[tokio::test] async fn update_single_select_cell_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -506,7 +506,7 @@ async fn update_single_select_cell_event_test() { #[tokio::test] async fn update_date_cell_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -543,7 +543,7 @@ async fn update_date_cell_event_test() { #[tokio::test] async fn update_date_cell_event_with_empty_time_str_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -579,7 +579,7 @@ async fn update_date_cell_event_with_empty_time_str_test() { #[tokio::test] async fn create_checklist_field_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -600,7 +600,7 @@ async fn create_checklist_field_test() { #[tokio::test] async fn update_checklist_cell_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -657,7 +657,7 @@ async fn update_checklist_cell_test() { // The number of groups should be 0 if there is no group by field in grid #[tokio::test] async fn get_groups_event_with_grid_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my board view".to_owned(), vec![]) @@ -669,7 +669,7 @@ async fn get_groups_event_with_grid_test() { #[tokio::test] async fn get_groups_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let board_view = test .create_board(¤t_workspace.id, "my board view".to_owned(), vec![]) @@ -681,7 +681,7 @@ async fn get_groups_event_test() { #[tokio::test] async fn move_group_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let board_view = test .create_board(¤t_workspace.id, "my board view".to_owned(), vec![]) @@ -715,7 +715,7 @@ async fn move_group_event_test() { #[tokio::test] async fn move_group_event_with_invalid_id_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let board_view = test .create_board(¤t_workspace.id, "my board view".to_owned(), vec![]) @@ -737,7 +737,7 @@ async fn move_group_event_with_invalid_id_test() { #[tokio::test] async fn rename_group_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let board_view = test .create_board(¤t_workspace.id, "my board view".to_owned(), vec![]) @@ -761,7 +761,7 @@ async fn rename_group_event_test() { #[tokio::test] async fn hide_group_event_test2() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let board_view = test .create_board(¤t_workspace.id, "my board view".to_owned(), vec![]) @@ -783,7 +783,7 @@ async fn hide_group_event_test2() { // Update the database layout type from grid to board #[tokio::test] async fn update_database_layout_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -805,7 +805,7 @@ async fn update_database_layout_event_test() { // Update the database layout type from grid to board. Set the checkbox field as the grouping field #[tokio::test] async fn update_database_layout_event_test2() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let grid_view = test .create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![]) @@ -837,7 +837,7 @@ async fn update_database_layout_event_test2() { // Create a checkbox field in the default board and then set it as the grouping field. #[tokio::test] async fn set_group_by_checkbox_field_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let board_view = test .create_board(¤t_workspace.id, "my board view".to_owned(), vec![]) @@ -854,7 +854,7 @@ async fn set_group_by_checkbox_field_test() { #[tokio::test] async fn get_all_calendar_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let calendar_view = test .create_calendar(¤t_workspace.id, "my calendar view".to_owned(), vec![]) @@ -867,7 +867,7 @@ async fn get_all_calendar_event_test() { #[tokio::test] async fn create_calendar_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let calendar_view = test .create_calendar(¤t_workspace.id, "my calendar view".to_owned(), vec![]) diff --git a/frontend/rust-lib/event-integration/tests/document/af_cloud_test/edit_test.rs b/frontend/rust-lib/event-integration/tests/document/af_cloud_test/edit_test.rs index 255864a0eead..772b345efa47 100644 --- a/frontend/rust-lib/event-integration/tests/document/af_cloud_test/edit_test.rs +++ b/frontend/rust-lib/event-integration/tests/document/af_cloud_test/edit_test.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use event_integration::assert_document_data_equal; +use event_integration::test_document::assert_document_data_equal; use flowy_document2::entities::DocumentSyncStatePB; use crate::document::af_cloud_test::util::AFCloudDocumentTest; diff --git a/frontend/rust-lib/event-integration/tests/document/supabase_test/edit_test.rs b/frontend/rust-lib/event-integration/tests/document/supabase_test/edit_test.rs index e0596e62997a..7300c3773bd8 100644 --- a/frontend/rust-lib/event-integration/tests/document/supabase_test/edit_test.rs +++ b/frontend/rust-lib/event-integration/tests/document/supabase_test/edit_test.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use event_integration::assert_document_data_equal; +use event_integration::test_document::assert_document_data_equal; use flowy_document2::entities::DocumentSyncStatePB; use crate::document::supabase_test::helper::FlowySupabaseDocumentTest; diff --git a/frontend/rust-lib/event-integration/tests/folder/local_test/script.rs b/frontend/rust-lib/event-integration/tests/folder/local_test/script.rs index 5e375e6c6ed7..24c56d8f4231 100644 --- a/frontend/rust-lib/event-integration/tests/folder/local_test/script.rs +++ b/frontend/rust-lib/event-integration/tests/folder/local_test/script.rs @@ -1,7 +1,7 @@ use collab_folder::core::ViewLayout; use event_integration::event_builder::EventBuilder; -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_folder2::entities::icon::{UpdateViewIconPayloadPB, ViewIconPB}; use flowy_folder2::entities::*; use flowy_folder2::event_map::FolderEvent::*; @@ -64,7 +64,7 @@ pub enum FolderScript { } pub struct FolderTest { - pub sdk: FlowyCoreTest, + pub sdk: EventIntegrationTest, pub all_workspace: Vec, pub workspace: WorkspacePB, pub parent_view: ViewPB, @@ -75,7 +75,7 @@ pub struct FolderTest { impl FolderTest { pub async fn new() -> Self { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let _ = sdk.init_user().await; let workspace = create_workspace(&sdk, "FolderWorkspace", "Folder test workspace").await; let parent_view = create_app(&sdk, &workspace.id, "Folder App", "Folder test app").await; @@ -201,7 +201,7 @@ impl FolderTest { } } } -pub async fn create_workspace(sdk: &FlowyCoreTest, name: &str, desc: &str) -> WorkspacePB { +pub async fn create_workspace(sdk: &EventIntegrationTest, name: &str, desc: &str) -> WorkspacePB { let request = CreateWorkspacePayloadPB { name: name.to_owned(), desc: desc.to_owned(), @@ -215,7 +215,10 @@ pub async fn create_workspace(sdk: &FlowyCoreTest, name: &str, desc: &str) -> Wo .parse::() } -pub async fn read_workspace(sdk: &FlowyCoreTest, workspace_id: Option) -> Vec { +pub async fn read_workspace( + sdk: &EventIntegrationTest, + workspace_id: Option, +) -> Vec { let request = WorkspaceIdPB { value: workspace_id, }; @@ -241,7 +244,12 @@ pub async fn read_workspace(sdk: &FlowyCoreTest, workspace_id: Option) - workspaces } -pub async fn create_app(sdk: &FlowyCoreTest, workspace_id: &str, name: &str, desc: &str) -> ViewPB { +pub async fn create_app( + sdk: &EventIntegrationTest, + workspace_id: &str, + name: &str, + desc: &str, +) -> ViewPB { let create_view_request = CreateViewPayloadPB { parent_view_id: workspace_id.to_owned(), name: name.to_string(), @@ -263,7 +271,7 @@ pub async fn create_app(sdk: &FlowyCoreTest, workspace_id: &str, name: &str, des } pub async fn create_view( - sdk: &FlowyCoreTest, + sdk: &EventIntegrationTest, app_id: &str, name: &str, desc: &str, @@ -288,7 +296,7 @@ pub async fn create_view( .parse::() } -pub async fn read_view(sdk: &FlowyCoreTest, view_id: &str) -> ViewPB { +pub async fn read_view(sdk: &EventIntegrationTest, view_id: &str) -> ViewPB { let view_id = ViewIdPB::from(view_id); EventBuilder::new(sdk.clone()) .event(ReadView) @@ -299,7 +307,7 @@ pub async fn read_view(sdk: &FlowyCoreTest, view_id: &str) -> ViewPB { } pub async fn move_view( - sdk: &FlowyCoreTest, + sdk: &EventIntegrationTest, view_id: String, parent_id: String, prev_view_id: Option, @@ -319,7 +327,7 @@ pub async fn move_view( assert!(error.is_none()); } pub async fn update_view( - sdk: &FlowyCoreTest, + sdk: &EventIntegrationTest, view_id: &str, name: Option, desc: Option, @@ -340,7 +348,7 @@ pub async fn update_view( .await; } -pub async fn update_view_icon(sdk: &FlowyCoreTest, view_id: &str, icon: Option) { +pub async fn update_view_icon(sdk: &EventIntegrationTest, view_id: &str, icon: Option) { let request = UpdateViewIconPayloadPB { view_id: view_id.to_string(), icon, @@ -352,7 +360,7 @@ pub async fn update_view_icon(sdk: &FlowyCoreTest, view_id: &str, icon: Option) { +pub async fn delete_view(sdk: &EventIntegrationTest, view_ids: Vec) { let request = RepeatedViewIdPB { items: view_ids }; EventBuilder::new(sdk.clone()) .event(DeleteView) @@ -361,7 +369,7 @@ pub async fn delete_view(sdk: &FlowyCoreTest, view_ids: Vec) { .await; } -pub async fn read_trash(sdk: &FlowyCoreTest) -> RepeatedTrashPB { +pub async fn read_trash(sdk: &EventIntegrationTest) -> RepeatedTrashPB { EventBuilder::new(sdk.clone()) .event(ReadTrash) .async_send() @@ -369,7 +377,7 @@ pub async fn read_trash(sdk: &FlowyCoreTest) -> RepeatedTrashPB { .parse::() } -pub async fn restore_app_from_trash(sdk: &FlowyCoreTest, app_id: &str) { +pub async fn restore_app_from_trash(sdk: &EventIntegrationTest, app_id: &str) { let id = TrashIdPB { id: app_id.to_owned(), }; @@ -380,7 +388,7 @@ pub async fn restore_app_from_trash(sdk: &FlowyCoreTest, app_id: &str) { .await; } -pub async fn restore_view_from_trash(sdk: &FlowyCoreTest, view_id: &str) { +pub async fn restore_view_from_trash(sdk: &EventIntegrationTest, view_id: &str) { let id = TrashIdPB { id: view_id.to_owned(), }; @@ -391,14 +399,14 @@ pub async fn restore_view_from_trash(sdk: &FlowyCoreTest, view_id: &str) { .await; } -pub async fn delete_all_trash(sdk: &FlowyCoreTest) { +pub async fn delete_all_trash(sdk: &EventIntegrationTest) { EventBuilder::new(sdk.clone()) .event(DeleteAllTrash) .async_send() .await; } -pub async fn toggle_favorites(sdk: &FlowyCoreTest, view_id: Vec) { +pub async fn toggle_favorites(sdk: &EventIntegrationTest, view_id: Vec) { let request = RepeatedViewIdPB { items: view_id }; EventBuilder::new(sdk.clone()) .event(ToggleFavorite) @@ -407,7 +415,7 @@ pub async fn toggle_favorites(sdk: &FlowyCoreTest, view_id: Vec) { .await; } -pub async fn read_favorites(sdk: &FlowyCoreTest) -> RepeatedViewPB { +pub async fn read_favorites(sdk: &EventIntegrationTest) -> RepeatedViewPB { EventBuilder::new(sdk.clone()) .event(ReadFavorites) .async_send() diff --git a/frontend/rust-lib/event-integration/tests/folder/local_test/subscription_test.rs b/frontend/rust-lib/event-integration/tests/folder/local_test/subscription_test.rs index f3fb6208a36f..2df246b212ac 100644 --- a/frontend/rust-lib/event-integration/tests/folder/local_test/subscription_test.rs +++ b/frontend/rust-lib/event-integration/tests/folder/local_test/subscription_test.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_folder2::entities::{ChildViewUpdatePB, RepeatedViewPB, UpdateViewPayloadPB}; use flowy_folder2::notification::FolderNotification; @@ -16,7 +16,7 @@ use crate::util::receive_with_timeout; /// 5. Await the notification for workspace view updates with a timeout of 30 seconds. /// 6. Ensure that the received views contain the newly created "test_view". async fn create_child_view_in_workspace_subscription_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let workspace = test.get_current_workspace().await.workspace; let mut rx = test .notification_sender @@ -40,7 +40,7 @@ async fn create_child_view_in_workspace_subscription_test() { #[tokio::test] async fn create_child_view_in_view_subscription_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let mut workspace = test.get_current_workspace().await.workspace; let workspace_child_view = workspace.views.pop().unwrap(); let mut rx = test.notification_sender.subscribe::( @@ -72,7 +72,7 @@ async fn create_child_view_in_view_subscription_test() { #[tokio::test] async fn delete_view_subscription_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let workspace = test.get_current_workspace().await.workspace; let mut rx = test .notification_sender @@ -94,7 +94,7 @@ async fn delete_view_subscription_test() { #[tokio::test] async fn update_view_subscription_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let mut workspace = test.get_current_workspace().await.workspace; let mut rx = test .notification_sender diff --git a/frontend/rust-lib/event-integration/tests/folder/local_test/test.rs b/frontend/rust-lib/event-integration/tests/folder/local_test/test.rs index 25994e2a6096..87cc6b82d9e6 100644 --- a/frontend/rust-lib/event-integration/tests/folder/local_test/test.rs +++ b/frontend/rust-lib/event-integration/tests/folder/local_test/test.rs @@ -1,12 +1,12 @@ use event_integration::event_builder::EventBuilder; -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_folder2::entities::icon::{UpdateViewIconPayloadPB, ViewIconPB, ViewIconTypePB}; use flowy_folder2::entities::*; use flowy_user::errors::ErrorCode; #[tokio::test] async fn create_workspace_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let request = CreateWorkspacePayloadPB { name: "my second workspace".to_owned(), desc: "".to_owned(), @@ -22,7 +22,7 @@ async fn create_workspace_event_test() { #[tokio::test] async fn open_workspace_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let payload = CreateWorkspacePayloadPB { name: "my second workspace".to_owned(), desc: "".to_owned(), @@ -52,7 +52,7 @@ async fn open_workspace_event_test() { #[tokio::test] async fn create_view_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let view = test .create_view(¤t_workspace.id, "My first view".to_string()) @@ -64,7 +64,7 @@ async fn create_view_event_test() { #[tokio::test] async fn update_view_event_with_name_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let view = test .create_view(¤t_workspace.id, "My first view".to_string()) @@ -85,7 +85,7 @@ async fn update_view_event_with_name_test() { #[tokio::test] async fn update_view_icon_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let view = test .create_view(¤t_workspace.id, "My first view".to_string()) @@ -109,7 +109,7 @@ async fn update_view_icon_event_test() { #[tokio::test] async fn delete_view_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let view = test .create_view(¤t_workspace.id, "My first view".to_string()) @@ -132,7 +132,7 @@ async fn delete_view_event_test() { #[tokio::test] async fn put_back_trash_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let view = test .create_view(¤t_workspace.id, "My first view".to_string()) @@ -175,7 +175,7 @@ async fn put_back_trash_event_test() { #[tokio::test] async fn delete_view_permanently_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let view = test .create_view(¤t_workspace.id, "My first view".to_string()) @@ -224,7 +224,7 @@ async fn delete_view_permanently_event_test() { #[tokio::test] async fn delete_all_trash_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; for i in 0..3 { @@ -268,7 +268,7 @@ async fn delete_all_trash_test() { #[tokio::test] async fn multiple_hierarchy_view_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; for i in 1..4 { let parent = test @@ -344,7 +344,7 @@ async fn multiple_hierarchy_view_test() { #[tokio::test] async fn move_view_event_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; for i in 1..4 { let parent = test @@ -382,7 +382,7 @@ async fn move_view_event_test() { #[tokio::test] async fn move_view_event_after_delete_view_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; for i in 1..6 { let _ = test @@ -424,7 +424,7 @@ async fn move_view_event_after_delete_view_test() { #[tokio::test] async fn move_view_event_after_delete_view_test2() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let parent = test .create_view(¤t_workspace.id, "My view".to_string()) @@ -466,7 +466,7 @@ async fn move_view_event_after_delete_view_test2() { #[tokio::test] async fn create_parent_view_with_invalid_name() { for (name, code) in invalid_workspace_name_test_case() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let request = CreateWorkspacePayloadPB { name, desc: "".to_owned(), @@ -494,7 +494,7 @@ fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> { #[tokio::test] async fn move_view_across_parent_test() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let current_workspace = test.get_current_workspace().await.workspace; let parent_1 = test .create_view(¤t_workspace.id, "My view 1".to_string()) @@ -539,7 +539,7 @@ async fn move_view_across_parent_test() { } async fn move_folder_nested_view( - sdk: FlowyCoreTest, + sdk: EventIntegrationTest, view_id: String, new_parent_id: String, prev_view_id: Option, diff --git a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/test.rs b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/test.rs index 79540f573df8..e0a138e0fac4 100644 --- a/frontend/rust-lib/event-integration/tests/user/af_cloud_test/test.rs +++ b/frontend/rust-lib/event-integration/tests/user/af_cloud_test/test.rs @@ -1,13 +1,36 @@ -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; +use flowy_user::entities::UpdateUserProfilePayloadPB; use crate::util::{generate_test_email, get_af_cloud_config}; #[tokio::test] async fn af_cloud_sign_up_test() { if get_af_cloud_config().is_some() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); 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_of_open_ai_key() { + if get_af_cloud_config().is_some() { + let test = EventIntegrationTest::new(); + 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()); + + test + .update_user_profile(UpdateUserProfilePayloadPB { + id: user.id, + openai_key: Some("new openai_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()); + } +} diff --git a/frontend/rust-lib/event-integration/tests/user/local_test/auth_test.rs b/frontend/rust-lib/event-integration/tests/user/local_test/auth_test.rs index 2edb5d7d4ed4..4866d3672049 100644 --- a/frontend/rust-lib/event-integration/tests/user/local_test/auth_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/local_test/auth_test.rs @@ -1,5 +1,5 @@ -use event_integration::user_event::*; -use event_integration::{event_builder::EventBuilder, FlowyCoreTest}; +use event_integration::test_user::{login_password, unique_email}; +use event_integration::{event_builder::EventBuilder, EventIntegrationTest}; use flowy_user::entities::{AuthTypePB, SignInPayloadPB, SignUpPayloadPB}; use flowy_user::errors::ErrorCode; use flowy_user::event_map::UserEvent::*; @@ -9,7 +9,7 @@ use crate::user::local_test::helper::*; #[tokio::test] async fn sign_up_with_invalid_email() { for email in invalid_email_test_case() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let request = SignUpPayloadPB { email: email.to_string(), name: valid_name(), @@ -33,9 +33,9 @@ async fn sign_up_with_invalid_email() { } #[tokio::test] async fn sign_up_with_long_password() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let request = SignUpPayloadPB { - email: random_email(), + email: unique_email(), name: valid_name(), password: "1234".repeat(100).as_str().to_string(), auth_type: AuthTypePB::Local, @@ -58,7 +58,7 @@ async fn sign_up_with_long_password() { #[tokio::test] async fn sign_in_with_invalid_email() { for email in invalid_email_test_case() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let request = SignInPayloadPB { email: email.to_string(), password: login_password(), @@ -84,10 +84,10 @@ async fn sign_in_with_invalid_email() { #[tokio::test] async fn sign_in_with_invalid_password() { for password in invalid_password_test_case() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let request = SignInPayloadPB { - email: random_email(), + email: unique_email(), password, name: "".to_string(), auth_type: AuthTypePB::Local, diff --git a/frontend/rust-lib/event-integration/tests/user/local_test/user_awareness_test.rs b/frontend/rust-lib/event-integration/tests/user/local_test/user_awareness_test.rs index 75348b7687a5..68bae5c7a4b7 100644 --- a/frontend/rust-lib/event-integration/tests/user/local_test/user_awareness_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/local_test/user_awareness_test.rs @@ -1,13 +1,13 @@ use std::collections::HashMap; use event_integration::event_builder::EventBuilder; -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_user::entities::{ReminderPB, RepeatedReminderPB}; use flowy_user::event_map::UserEvent::*; #[tokio::test] async fn user_update_with_name() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let _ = sdk.sign_up_as_guest().await; let mut meta = HashMap::new(); meta.insert("object_id".to_string(), "".to_string()); diff --git a/frontend/rust-lib/event-integration/tests/user/local_test/user_profile_test.rs b/frontend/rust-lib/event-integration/tests/user/local_test/user_profile_test.rs index 6969be29acde..a488de02a11f 100644 --- a/frontend/rust-lib/event-integration/tests/user/local_test/user_profile_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/local_test/user_profile_test.rs @@ -1,14 +1,16 @@ -use crate::user::local_test::helper::*; -use event_integration::{event_builder::EventBuilder, FlowyCoreTest}; +use nanoid::nanoid; + +use event_integration::{event_builder::EventBuilder, EventIntegrationTest}; use flowy_user::entities::{UpdateUserProfilePayloadPB, UserProfilePB}; use flowy_user::{errors::ErrorCode, event_map::UserEvent::*}; -use nanoid::nanoid; + +use crate::user::local_test::helper::*; // use serial_test::*; #[tokio::test] async fn user_profile_get_failed() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let result = EventBuilder::new(sdk) .event(GetUserProfile) .async_send() @@ -19,7 +21,7 @@ async fn user_profile_get_failed() { #[tokio::test] async fn user_profile_get() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let user_profile = test.init_user().await; let user = EventBuilder::new(test.clone()) .event(GetUserProfile) @@ -30,7 +32,7 @@ async fn user_profile_get() { #[tokio::test] async fn user_update_with_name() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let user = sdk.init_user().await; let new_name = "hello_world".to_owned(); let request = UpdateUserProfilePayloadPB::new(user.id).name(&new_name); @@ -49,7 +51,7 @@ async fn user_update_with_name() { #[tokio::test] async fn user_update_with_ai_key() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let user = sdk.init_user().await; let openai_key = "openai_key".to_owned(); let stability_ai_key = "stability_ai_key".to_owned(); @@ -72,7 +74,7 @@ async fn user_update_with_ai_key() { #[tokio::test] async fn user_update_with_email() { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let user = sdk.init_user().await; let new_email = format!("{}@gmail.com", nanoid!(6)); let request = UpdateUserProfilePayloadPB::new(user.id).email(&new_email); @@ -90,7 +92,7 @@ async fn user_update_with_email() { #[tokio::test] async fn user_update_with_invalid_email() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let user = test.init_user().await; for email in invalid_email_test_case() { let request = UpdateUserProfilePayloadPB::new(user.id).email(&email); @@ -109,7 +111,7 @@ async fn user_update_with_invalid_email() { #[tokio::test] async fn user_update_with_invalid_password() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let user = test.init_user().await; for password in invalid_password_test_case() { let request = UpdateUserProfilePayloadPB::new(user.id).password(&password); @@ -126,7 +128,7 @@ async fn user_update_with_invalid_password() { #[tokio::test] async fn user_update_with_invalid_name() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let user = test.init_user().await; let request = UpdateUserProfilePayloadPB::new(user.id).name(""); assert!(EventBuilder::new(test.clone()) diff --git a/frontend/rust-lib/event-integration/tests/user/migration_test/document_test.rs b/frontend/rust-lib/event-integration/tests/user/migration_test/document_test.rs index 167bbecc9cd9..9f3100b7ca4b 100644 --- a/frontend/rust-lib/event-integration/tests/user/migration_test/document_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/migration_test/document_test.rs @@ -1,4 +1,4 @@ -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_core::DEFAULT_NAME; use flowy_folder2::entities::ViewLayoutPB; @@ -11,7 +11,7 @@ async fn migrate_historical_empty_document_test() { "historical_empty_document", ) .unwrap(); - let test = FlowyCoreTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()); + let test = EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()); let views = test.get_all_workspace_views().await; assert_eq!(views.len(), 3); diff --git a/frontend/rust-lib/event-integration/tests/user/migration_test/version_test.rs b/frontend/rust-lib/event-integration/tests/user/migration_test/version_test.rs index 05caa93510c3..ccd1b25e8c4a 100644 --- a/frontend/rust-lib/event-integration/tests/user/migration_test/version_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/migration_test/version_test.rs @@ -1,4 +1,4 @@ -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_core::DEFAULT_NAME; use flowy_folder2::entities::ViewLayoutPB; @@ -11,7 +11,7 @@ async fn migrate_020_historical_empty_document_test() { "020_historical_user_data", ) .unwrap(); - let test = FlowyCoreTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()); + let test = EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()); let mut views = test.get_all_workspace_views().await; assert_eq!(views.len(), 1); diff --git a/frontend/rust-lib/event-integration/tests/user/supabase_test/auth_test.rs b/frontend/rust-lib/event-integration/tests/user/supabase_test/auth_test.rs index 2027a0919288..999df506ef86 100644 --- a/frontend/rust-lib/event-integration/tests/user/supabase_test/auth_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/supabase_test/auth_test.rs @@ -10,7 +10,7 @@ use serde_json::json; use event_integration::document::document_event::DocumentEventTest; use event_integration::event_builder::EventBuilder; -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_core::DEFAULT_NAME; use flowy_encrypt::decrypt_text; use flowy_server::supabase::define::{USER_EMAIL, USER_UUID}; @@ -23,7 +23,7 @@ use crate::util::*; #[tokio::test] async fn third_party_sign_up_test() { if get_supabase_config().is_some() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let mut map = HashMap::new(); map.insert(USER_UUID.to_string(), uuid::Uuid::new_v4().to_string()); map.insert( @@ -48,7 +48,7 @@ async fn third_party_sign_up_test() { #[tokio::test] async fn third_party_sign_up_with_encrypt_test() { if get_supabase_config().is_some() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); test.supabase_party_sign_up().await; let user_profile = test.get_user_profile().await.unwrap(); assert!(user_profile.encryption_sign.is_empty()); @@ -65,7 +65,7 @@ async fn third_party_sign_up_with_encrypt_test() { #[tokio::test] async fn third_party_sign_up_with_duplicated_uuid() { if get_supabase_config().is_some() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let email = format!("{}@appflowy.io", nanoid!(6)); let mut map = HashMap::new(); map.insert(USER_UUID.to_string(), uuid::Uuid::new_v4().to_string()); @@ -98,7 +98,7 @@ async fn third_party_sign_up_with_duplicated_uuid() { #[tokio::test] async fn third_party_sign_up_with_duplicated_email() { if get_supabase_config().is_some() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let email = format!("{}@appflowy.io", nanoid!(6)); test .supabase_sign_up_with_uuid(&uuid::Uuid::new_v4().to_string(), Some(email.clone())) @@ -116,7 +116,7 @@ async fn third_party_sign_up_with_duplicated_email() { #[tokio::test] async fn sign_up_as_guest_and_then_update_to_new_cloud_user_test() { if get_supabase_config().is_some() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let old_views = test .folder_manager .get_current_workspace_views() @@ -148,7 +148,7 @@ async fn sign_up_as_guest_and_then_update_to_new_cloud_user_test() { #[tokio::test] async fn sign_up_as_guest_and_then_update_to_existing_cloud_user_test() { if get_supabase_config().is_some() { - let test = FlowyCoreTest::new_with_guest_user().await; + let test = EventIntegrationTest::new_with_guest_user().await; let uuid = uuid::Uuid::new_v4().to_string(); let email = format!("{}@appflowy.io", nanoid!(6)); @@ -260,7 +260,7 @@ async fn update_user_profile_with_existing_email_test() { #[tokio::test] async fn migrate_anon_document_on_cloud_signup() { if get_supabase_config().is_some() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let user_profile = test.sign_up_as_guest().await.user_profile; let view = test @@ -305,7 +305,8 @@ async fn migrate_anon_data_on_cloud_signup() { "workspace_sync", ) .unwrap(); - let test = FlowyCoreTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()); + let test = + EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()); let user_profile = test.supabase_party_sign_up().await; // Get the folder data from remote diff --git a/frontend/rust-lib/event-integration/tests/user/supabase_test/workspace_test.rs b/frontend/rust-lib/event-integration/tests/user/supabase_test/workspace_test.rs index 74236b458e7e..49e4e289c7c2 100644 --- a/frontend/rust-lib/event-integration/tests/user/supabase_test/workspace_test.rs +++ b/frontend/rust-lib/event-integration/tests/user/supabase_test/workspace_test.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use event_integration::{event_builder::EventBuilder, FlowyCoreTest}; +use event_integration::{event_builder::EventBuilder, EventIntegrationTest}; use flowy_folder2::entities::WorkspaceSettingPB; use flowy_folder2::event_map::FolderEvent::GetCurrentWorkspace; use flowy_server::supabase::define::{USER_EMAIL, USER_UUID}; @@ -12,7 +12,7 @@ use crate::util::*; #[tokio::test] async fn initial_workspace_test() { if get_supabase_config().is_some() { - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); let mut map = HashMap::new(); map.insert(USER_UUID.to_string(), uuid::Uuid::new_v4().to_string()); map.insert( diff --git a/frontend/rust-lib/event-integration/tests/util.rs b/frontend/rust-lib/event-integration/tests/util.rs index d0c104ffb8c4..9847095458bc 100644 --- a/frontend/rust-lib/event-integration/tests/util.rs +++ b/frontend/rust-lib/event-integration/tests/util.rs @@ -16,7 +16,7 @@ use zip::ZipArchive; use event_integration::event_builder::EventBuilder; use event_integration::Cleaner; -use event_integration::FlowyCoreTest; +use event_integration::EventIntegrationTest; use flowy_database_deps::cloud::DatabaseCloudService; use flowy_folder_deps::cloud::{FolderCloudService, FolderSnapshot}; use flowy_server::supabase::api::*; @@ -36,13 +36,13 @@ pub fn get_supabase_config() -> Option { } pub struct FlowySupabaseTest { - inner: FlowyCoreTest, + inner: EventIntegrationTest, } impl FlowySupabaseTest { pub fn new() -> Option { let _ = get_supabase_config()?; - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); test.set_auth_type(AuthTypePB::Supabase); test.server_provider.set_auth_type(AuthType::Supabase); @@ -76,7 +76,7 @@ impl FlowySupabaseTest { } impl Deref for FlowySupabaseTest { - type Target = FlowyCoreTest; + type Target = EventIntegrationTest; fn deref(&self) -> &Self::Target { &self.inner @@ -215,13 +215,13 @@ pub fn unzip_history_user_db(root: &str, folder_name: &str) -> std::io::Result<( } pub struct AFCloudTest { - inner: FlowyCoreTest, + inner: EventIntegrationTest, } impl AFCloudTest { pub fn new() -> Option { let _ = get_af_cloud_config()?; - let test = FlowyCoreTest::new(); + let test = EventIntegrationTest::new(); test.set_auth_type(AuthTypePB::AFCloud); test.server_provider.set_auth_type(AuthType::AFCloud); @@ -230,7 +230,7 @@ impl AFCloudTest { } impl Deref for AFCloudTest { - type Target = FlowyCoreTest; + type Target = EventIntegrationTest; fn deref(&self) -> &Self::Target { &self.inner diff --git a/frontend/rust-lib/flowy-database2/src/manager.rs b/frontend/rust-lib/flowy-database2/src/manager.rs index 96dd331e88ea..10734ba8a50a 100644 --- a/frontend/rust-lib/flowy-database2/src/manager.rs +++ b/frontend/rust-lib/flowy-database2/src/manager.rs @@ -132,7 +132,12 @@ impl DatabaseManager { Ok(()) } - #[instrument(level = "debug", skip_all, err)] + #[instrument( + name = "database_initialize_with_new_user", + level = "debug", + skip_all, + err + )] pub async fn initialize_with_new_user( &self, user_id: i64, diff --git a/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs b/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs index 4b102fe0e96d..9fe298be3d5a 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs @@ -6,8 +6,8 @@ use collab_database::fields::Field; use collab_database::rows::{CreateRowParams, RowDetail, RowId}; use strum::EnumCount; -use event_integration::folder_event::ViewTest; -use event_integration::FlowyCoreTest; +use event_integration::test_folder::ViewTest; +use event_integration::EventIntegrationTest; use flowy_database2::entities::{FieldType, FilterPB, RowMetaPB}; use flowy_database2::services::cell::{CellBuilder, ToCellChangeset}; use flowy_database2::services::database::DatabaseEditor; @@ -26,8 +26,7 @@ use crate::database::mock_data::{ }; pub struct DatabaseEditorTest { - pub sdk: FlowyCoreTest, - pub app_id: String, + pub sdk: EventIntegrationTest, pub view_id: String, pub editor: Arc, pub fields: Vec>, @@ -38,7 +37,7 @@ pub struct DatabaseEditorTest { impl DatabaseEditorTest { pub async fn new_grid() -> Self { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let _ = sdk.init_user().await; let params = make_test_grid(); @@ -47,7 +46,7 @@ impl DatabaseEditorTest { } pub async fn new_no_date_grid() -> Self { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let _ = sdk.init_user().await; let params = make_no_date_test_grid(); @@ -56,7 +55,7 @@ impl DatabaseEditorTest { } pub async fn new_board() -> Self { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let _ = sdk.init_user().await; let params = make_test_board(); @@ -65,7 +64,7 @@ impl DatabaseEditorTest { } pub async fn new_calendar() -> Self { - let sdk = FlowyCoreTest::new(); + let sdk = EventIntegrationTest::new(); let _ = sdk.init_user().await; let params = make_test_calendar(); @@ -73,7 +72,7 @@ impl DatabaseEditorTest { Self::new(sdk, view_test).await } - pub async fn new(sdk: FlowyCoreTest, test: ViewTest) -> Self { + pub async fn new(sdk: EventIntegrationTest, test: ViewTest) -> Self { let editor = sdk .database_manager .get_database_with_view_id(&test.child_view.id) @@ -92,10 +91,8 @@ impl DatabaseEditorTest { .collect(); let view_id = test.child_view.id; - let app_id = test.parent_view.id; Self { sdk, - app_id, view_id, editor, fields, diff --git a/frontend/rust-lib/flowy-document2/src/manager.rs b/frontend/rust-lib/flowy-document2/src/manager.rs index 2fe3dd829e79..ee8a9c9a9c49 100644 --- a/frontend/rust-lib/flowy-document2/src/manager.rs +++ b/frontend/rust-lib/flowy-document2/src/manager.rs @@ -59,7 +59,12 @@ impl DocumentManager { Ok(()) } - #[instrument(level = "debug", skip_all, err)] + #[instrument( + name = "document_initialize_with_new_user", + level = "debug", + skip_all, + err + )] pub async fn initialize_with_new_user(&self, uid: i64, workspace_id: String) -> FlowyResult<()> { self.initialize(uid, workspace_id).await?; Ok(()) diff --git a/frontend/rust-lib/flowy-document2/src/notification.rs b/frontend/rust-lib/flowy-document2/src/notification.rs index 94c5bed28f9f..b468ec20c7f3 100644 --- a/frontend/rust-lib/flowy-document2/src/notification.rs +++ b/frontend/rust-lib/flowy-document2/src/notification.rs @@ -29,6 +29,7 @@ impl std::convert::From for DocumentNotification { } } +#[tracing::instrument(level = "trace")] pub(crate) fn send_notification(id: &str, ty: DocumentNotification) -> NotificationBuilder { NotificationBuilder::new(id, ty, DOCUMENT_OBSERVABLE_SOURCE) } diff --git a/frontend/rust-lib/flowy-folder2/src/manager.rs b/frontend/rust-lib/flowy-folder2/src/manager.rs index fda3198502b1..b15ce1b2c56c 100644 --- a/frontend/rust-lib/flowy-folder2/src/manager.rs +++ b/frontend/rust-lib/flowy-folder2/src/manager.rs @@ -265,7 +265,12 @@ impl FolderManager { /// Initialize the folder for the new user. /// Using the [DefaultFolderBuilder] to create the default workspace for the new user. - #[instrument(level = "debug", skip_all, err)] + #[instrument( + name = "folder_initialize_with_new_user", + level = "debug", + skip_all, + err + )] pub async fn initialize_with_new_user( &self, user_id: i64, diff --git a/frontend/rust-lib/flowy-server/Cargo.toml b/frontend/rust-lib/flowy-server/Cargo.toml index 45b2b13d49ef..ccdc0a7b5681 100644 --- a/frontend/rust-lib/flowy-server/Cargo.toml +++ b/frontend/rust-lib/flowy-server/Cargo.toml @@ -23,7 +23,7 @@ bytes = { version = "1.5", features = ["serde"] } tokio-retry = "0.3" anyhow = "1.0" uuid = { version = "1.3.3", features = ["v4"] } -chrono = { version = "0.4.31", default-features = false, features = ["clock"] } +chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"] } collab = { version = "0.1.0" } collab-plugins = { version = "0.1.0"} collab-document = { version = "0.1.0" } diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs similarity index 71% rename from frontend/rust-lib/flowy-server/src/af_cloud/impls/user.rs rename to frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs index bd8419d93fe7..b5d090df87b0 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/cloud_service_impl.rs @@ -2,11 +2,8 @@ use std::collections::HashMap; use std::sync::Arc; use anyhow::{anyhow, Error}; -use client_api::entity::dto::auth_dto::UpdateUsernameParams; -use client_api::entity::dto::workspace_dto::CreateWorkspaceMember; -use client_api::entity::{ - AFRole, AFUserProfileView, AFWorkspace, AFWorkspaces, InsertCollabParams, OAuthProvider, -}; +use client_api::entity::workspace_dto::CreateWorkspaceMember; +use client_api::entity::{AFRole, AFWorkspace, InsertCollabParams, OAuthProvider}; use collab_entity::CollabObject; use flowy_error::{ErrorCode, FlowyError}; @@ -15,6 +12,10 @@ use flowy_user_deps::entities::*; use lib_infra::box_any::BoxAny; use lib_infra::future::FutureResult; +use crate::af_cloud::impls::user::dto::{ + af_update_from_update_params, user_profile_from_af_profile, +}; +use crate::af_cloud::impls::user::util::encryption_type_from_profile; use crate::af_cloud::{AFCloudClient, AFServer}; use crate::supabase::define::{USER_DEVICE_ID, USER_SIGN_IN_URL}; @@ -92,12 +93,7 @@ where FutureResult::new(async move { let client = try_get_client?; client - .update_user(UpdateUsernameParams { - name: params.name, - email: params.email, - password: params.password, - metadata: None, - }) + .update_user(af_update_from_update_params(params)) .await?; Ok(()) }) @@ -111,30 +107,18 @@ where FutureResult::new(async move { let client = try_get_client?; let profile = client.get_profile().await?; - let encryption_type = encryption_type_from_profile(&profile); - Ok(Some(UserProfile { - email: profile.email.unwrap_or("".to_string()), - name: profile.name.unwrap_or("".to_string()), - token: client.get_token()?, - icon_url: "".to_owned(), - openai_key: "".to_owned(), - stability_ai_key: "".to_owned(), - workspace_id: match profile.latest_workspace_id { - Some(w) => w.to_string(), - None => "".to_string(), - }, - auth_type: AuthType::AFCloud, - encryption_type, - uid: profile.uid.ok_or(anyhow!("no uid found"))?, - })) + let token = client.get_token()?; + let profile = user_profile_from_af_profile(token, profile)?; + + Ok(Some(profile)) }) } - fn get_user_workspaces(&self, _uid: i64) -> FutureResult, Error> { + fn get_all_user_workspaces(&self, _uid: i64) -> FutureResult, Error> { let try_get_client = self.server.try_get_client(); FutureResult::new(async move { let workspaces = try_get_client?.get_workspaces().await?; - Ok(to_user_workspaces(workspaces)?) + Ok(to_user_workspaces(workspaces.0)?) }) } @@ -151,7 +135,7 @@ where let client_token = client.access_token()?; // compare and check - if uid != profile.uid.ok_or(anyhow!("expecting uid"))? { + if uid != profile.uid { return Err(anyhow!("uid mismatch")); } if token != client_token { @@ -238,59 +222,42 @@ pub async fn user_sign_in_with_url( params: AFCloudOAuthParams, ) -> Result { let is_new_user = client.sign_in_with_url(¶ms.sign_in_url).await?; - let (profile, af_workspaces) = tokio::try_join!(client.get_profile(), client.get_workspaces())?; - let latest_workspace = to_user_workspace( - af_workspaces - .get_latest(&profile) - .or(af_workspaces.first().cloned()) - .ok_or(anyhow!("no workspace found"))?, - )?; + let workspace_profile = client.get_user_workspace_info().await?; + let user_profile = workspace_profile.user_profile; - let user_workspaces = to_user_workspaces(af_workspaces)?; - let encryption_type = encryption_type_from_profile(&profile); + let latest_workspace = to_user_workspace(workspace_profile.visiting_workspace); + let user_workspaces = to_user_workspaces(workspace_profile.workspaces)?; + let encryption_type = encryption_type_from_profile(&user_profile); Ok(AuthResponse { - user_id: profile.uid.ok_or(anyhow!("no uid found"))?, - name: profile.name.ok_or(anyhow!("no name found"))?, + user_id: user_profile.uid, + name: user_profile.name.unwrap_or_default(), latest_workspace, user_workspaces, - email: profile.email, + email: user_profile.email, token: Some(client.get_token()?), device_id: params.device_id, encryption_type, is_new_user, + updated_at: user_profile.updated_at, + metadata: user_profile.metadata, }) } -fn encryption_type_from_profile(profile: &AFUserProfileView) -> EncryptionType { - match &profile.encryption_sign { - Some(e) => EncryptionType::SelfEncryption(e.to_string()), - None => EncryptionType::NoEncryption, - } -} - -fn to_user_workspace(af_workspace: AFWorkspace) -> Result { - Ok(UserWorkspace { +fn to_user_workspace(af_workspace: AFWorkspace) -> UserWorkspace { + UserWorkspace { id: af_workspace.workspace_id.to_string(), - name: af_workspace - .workspace_name - .ok_or(anyhow!("no workspace_name found"))?, - created_at: af_workspace - .created_at - .ok_or(anyhow!("no created_at found"))?, - database_views_aggregate_id: af_workspace - .database_storage_id - .ok_or(anyhow!("no database_views_aggregate_id found"))? - .to_string(), - }) + name: af_workspace.workspace_name, + created_at: af_workspace.created_at, + database_views_aggregate_id: af_workspace.database_storage_id.to_string(), + } } -fn to_user_workspaces(af_workspaces: AFWorkspaces) -> Result, FlowyError> { - let mut result = Vec::with_capacity(af_workspaces.len()); - for item in af_workspaces.0.into_iter() { - let user_workspace = to_user_workspace(item)?; - result.push(user_workspace); +fn to_user_workspaces(workspaces: Vec) -> Result, FlowyError> { + let mut result = Vec::with_capacity(workspaces.len()); + for item in workspaces.into_iter() { + result.push(to_user_workspace(item)); } Ok(result) } diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/dto.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/dto.rs new file mode 100644 index 000000000000..e2c2922d0c89 --- /dev/null +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/dto.rs @@ -0,0 +1,65 @@ +use anyhow::Error; +use client_api::entity::auth_dto::{UpdateUserParams, UserMetaData}; +use client_api::entity::AFUserProfile; + +use flowy_user_deps::entities::{ + AuthType, UpdateUserProfileParams, UserProfile, USER_METADATA_ICON_URL, + USER_METADATA_OPEN_AI_KEY, USER_METADATA_STABILITY_AI_KEY, +}; + +use crate::af_cloud::impls::user::util::encryption_type_from_profile; + +pub fn af_update_from_update_params(update: UpdateUserProfileParams) -> UpdateUserParams { + let mut user_metadata = UserMetaData::new(); + if let Some(openai_key) = update.openai_key { + user_metadata.insert(USER_METADATA_OPEN_AI_KEY, openai_key); + } + + if let Some(stability_ai_key) = update.stability_ai_key { + user_metadata.insert(USER_METADATA_STABILITY_AI_KEY, stability_ai_key); + } + + if let Some(icon_url) = update.icon_url { + user_metadata.insert(USER_METADATA_ICON_URL, icon_url); + } + + UpdateUserParams { + name: update.name, + email: update.email, + password: update.password, + metadata: Some(user_metadata), + } +} + +pub fn user_profile_from_af_profile( + token: String, + profile: AFUserProfile, +) -> Result { + let encryption_type = encryption_type_from_profile(&profile); + let (icon_url, openai_key, stability_ai_key) = { + profile + .metadata + .map(|m| { + ( + m.get(USER_METADATA_ICON_URL).map(|v| v.to_string()), + m.get(USER_METADATA_OPEN_AI_KEY).map(|v| v.to_string()), + m.get(USER_METADATA_STABILITY_AI_KEY).map(|v| v.to_string()), + ) + }) + .unwrap_or_default() + }; + + Ok(UserProfile { + email: profile.email.unwrap_or("".to_string()), + name: profile.name.unwrap_or("".to_string()), + token, + icon_url: icon_url.unwrap_or_default(), + openai_key: openai_key.unwrap_or_default(), + stability_ai_key: stability_ai_key.unwrap_or_default(), + workspace_id: profile.latest_workspace_id.to_string(), + auth_type: AuthType::AFCloud, + encryption_type, + uid: profile.uid, + updated_at: profile.updated_at, + }) +} diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/mod.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/mod.rs new file mode 100644 index 000000000000..96e13cbc2d0f --- /dev/null +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/mod.rs @@ -0,0 +1,5 @@ +pub use cloud_service_impl::*; + +mod cloud_service_impl; +mod dto; +mod util; diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/util.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/util.rs new file mode 100644 index 000000000000..b357861aa3cc --- /dev/null +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/user/util.rs @@ -0,0 +1,10 @@ +use client_api::entity::AFUserProfile; + +use flowy_user_deps::entities::EncryptionType; + +pub fn encryption_type_from_profile(profile: &AFUserProfile) -> EncryptionType { + match &profile.encryption_sign { + Some(e) => EncryptionType::SelfEncryption(e.to_string()), + None => EncryptionType::NoEncryption, + } +} diff --git a/frontend/rust-lib/flowy-server/src/local_server/impls/user.rs b/frontend/rust-lib/flowy-server/src/local_server/impls/user.rs index 7828d9f2e80d..5cc0455ac150 100644 --- a/frontend/rust-lib/flowy-server/src/local_server/impls/user.rs +++ b/frontend/rust-lib/flowy-server/src/local_server/impls/user.rs @@ -11,6 +11,7 @@ use flowy_user_deps::entities::*; use flowy_user_deps::DEFAULT_USER_NAME; use lib_infra::box_any::BoxAny; use lib_infra::future::FutureResult; +use lib_infra::util::timestamp; use crate::local_server::uid::UserIDGenerator; use crate::local_server::LocalServerDB; @@ -46,6 +47,8 @@ impl UserCloudService for LocalServerUserAuthServiceImpl { token: None, device_id: params.device_id, encryption_type: EncryptionType::NoEncryption, + updated_at: timestamp(), + metadata: None, }) }) } @@ -69,6 +72,8 @@ impl UserCloudService for LocalServerUserAuthServiceImpl { token: None, device_id: params.device_id, encryption_type: EncryptionType::NoEncryption, + updated_at: timestamp(), + metadata: None, }) }) } @@ -104,7 +109,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl { FutureResult::new(async { Ok(None) }) } - fn get_user_workspaces(&self, _uid: i64) -> FutureResult, Error> { + fn get_all_user_workspaces(&self, _uid: i64) -> FutureResult, Error> { FutureResult::new(async { Ok(vec![]) }) } diff --git a/frontend/rust-lib/flowy-server/src/supabase/api/user.rs b/frontend/rust-lib/flowy-server/src/supabase/api/user.rs index 6a91aafb276a..69581d69f0dd 100644 --- a/frontend/rust-lib/flowy-server/src/supabase/api/user.rs +++ b/frontend/rust-lib/flowy-server/src/supabase/api/user.rs @@ -129,6 +129,8 @@ where token: None, device_id: params.device_id, encryption_type: EncryptionType::from_sign(&user_profile.encryption_sign), + updated_at: user_profile.updated_at.timestamp(), + metadata: None, }) }) } @@ -158,6 +160,8 @@ where token: None, device_id: params.device_id, encryption_type: EncryptionType::from_sign(&response.encryption_sign), + updated_at: response.updated_at.timestamp(), + metadata: None, }) }) } @@ -220,12 +224,13 @@ where workspace_id: response.latest_workspace_id, auth_type: AuthType::Supabase, encryption_type: EncryptionType::from_sign(&response.encryption_sign), + updated_at: response.updated_at.timestamp(), })), } }) } - fn get_user_workspaces(&self, uid: i64) -> FutureResult, Error> { + fn get_all_user_workspaces(&self, uid: i64) -> FutureResult, Error> { let try_get_postgrest = self.server.try_get_postgrest(); FutureResult::new(async move { let postgrest = try_get_postgrest?; @@ -419,7 +424,7 @@ async fn get_user_profile( ) -> Result, Error> { let mut builder = postgrest .from(USER_PROFILE_VIEW) - .select("uid, email, name, encryption_sign, latest_workspace_id"); + .select("uid, email, name, encryption_sign, latest_workspace_id, updated_at"); match params { GetUserProfileParams::Uid(uid) => builder = builder.eq("uid", uid.to_string()), diff --git a/frontend/rust-lib/flowy-server/src/supabase/entities.rs b/frontend/rust-lib/flowy-server/src/supabase/entities.rs index d50374caed49..41518dd995b7 100644 --- a/frontend/rust-lib/flowy-server/src/supabase/entities.rs +++ b/frontend/rust-lib/flowy-server/src/supabase/entities.rs @@ -1,6 +1,7 @@ use std::fmt; use std::fmt::Display; +use chrono::{DateTime, Utc}; use serde::Deserialize; use serde_json::Value; use uuid::Uuid; @@ -27,6 +28,8 @@ pub(crate) struct UserProfileResponse { #[serde(deserialize_with = "deserialize_null_or_default")] pub encryption_sign: String, + + pub updated_at: DateTime, } #[derive(Debug, Deserialize)] diff --git a/frontend/rust-lib/flowy-sqlite/migrations/2023-10-24-074032_user_updated_at/down.sql b/frontend/rust-lib/flowy-sqlite/migrations/2023-10-24-074032_user_updated_at/down.sql new file mode 100644 index 000000000000..74e73657f700 --- /dev/null +++ b/frontend/rust-lib/flowy-sqlite/migrations/2023-10-24-074032_user_updated_at/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE user_table +DROP COLUMN updated_at; diff --git a/frontend/rust-lib/flowy-sqlite/migrations/2023-10-24-074032_user_updated_at/up.sql b/frontend/rust-lib/flowy-sqlite/migrations/2023-10-24-074032_user_updated_at/up.sql new file mode 100644 index 000000000000..c13d17e4e5e8 --- /dev/null +++ b/frontend/rust-lib/flowy-sqlite/migrations/2023-10-24-074032_user_updated_at/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE user_table +ADD COLUMN updated_at BIGINT NOT NULL DEFAULT 0; diff --git a/frontend/rust-lib/flowy-sqlite/src/schema.rs b/frontend/rust-lib/flowy-sqlite/src/schema.rs index e10785f9cf98..a5ccad50f2d5 100644 --- a/frontend/rust-lib/flowy-sqlite/src/schema.rs +++ b/frontend/rust-lib/flowy-sqlite/src/schema.rs @@ -32,6 +32,7 @@ diesel::table! { auth_type -> Integer, encryption_type -> Text, stability_ai_key -> Text, + updated_at -> BigInt, } } diff --git a/frontend/rust-lib/flowy-user-deps/src/cloud.rs b/frontend/rust-lib/flowy-user-deps/src/cloud.rs index 65f289e90405..bb50d388af11 100644 --- a/frontend/rust-lib/flowy-user-deps/src/cloud.rs +++ b/frontend/rust-lib/flowy-user-deps/src/cloud.rs @@ -95,7 +95,7 @@ pub trait UserCloudService: Send + Sync + 'static { ) -> FutureResult, FlowyError>; /// Return the all the workspaces of the user - fn get_user_workspaces(&self, uid: i64) -> FutureResult, Error>; + fn get_all_user_workspaces(&self, uid: i64) -> FutureResult, Error>; fn check_user(&self, credential: UserCredentials) -> FutureResult<(), Error>; diff --git a/frontend/rust-lib/flowy-user-deps/src/entities.rs b/frontend/rust-lib/flowy-user-deps/src/entities.rs index 9edf3e17f741..e94cb6fa1fb2 100644 --- a/frontend/rust-lib/flowy-user-deps/src/entities.rs +++ b/frontend/rust-lib/flowy-user-deps/src/entities.rs @@ -2,9 +2,15 @@ use std::str::FromStr; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use serde_json::Value; use serde_repr::*; use uuid::Uuid; +pub const USER_METADATA_OPEN_AI_KEY: &str = "openai_key"; +pub const USER_METADATA_STABILITY_AI_KEY: &str = "stability_ai_key"; +pub const USER_METADATA_ICON_URL: &str = "icon_url"; +pub const USER_METADATA_UPDATE_AT: &str = "updated_at"; + pub trait UserAuthResponse { fn user_id(&self) -> i64; fn user_name(&self) -> &str; @@ -14,52 +20,8 @@ pub trait UserAuthResponse { fn user_token(&self) -> Option; fn user_email(&self) -> Option; fn encryption_type(&self) -> EncryptionType; -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct SignInResponse { - pub user_id: i64, - pub name: String, - pub latest_workspace: UserWorkspace, - pub user_workspaces: Vec, - pub email: Option, - pub token: Option, - pub device_id: String, - pub encryption_type: EncryptionType, -} - -impl UserAuthResponse for SignInResponse { - fn user_id(&self) -> i64 { - self.user_id - } - - fn user_name(&self) -> &str { - &self.name - } - - fn latest_workspace(&self) -> &UserWorkspace { - &self.latest_workspace - } - - fn user_workspaces(&self) -> &[UserWorkspace] { - &self.user_workspaces - } - - fn device_id(&self) -> &str { - &self.device_id - } - - fn user_token(&self) -> Option { - self.token.clone() - } - - fn user_email(&self) -> Option { - self.email.clone() - } - - fn encryption_type(&self) -> EncryptionType { - self.encryption_type.clone() - } + fn metadata(&self) -> &Option; + fn updated_at(&self) -> i64; } #[derive(Default, Serialize, Deserialize, Debug)] @@ -91,6 +53,8 @@ pub struct AuthResponse { pub token: Option, pub device_id: String, pub encryption_type: EncryptionType, + pub updated_at: i64, + pub metadata: Option, } impl UserAuthResponse for AuthResponse { @@ -125,6 +89,14 @@ impl UserAuthResponse for AuthResponse { fn encryption_type(&self) -> EncryptionType { self.encryption_type.clone() } + + fn metadata(&self) -> &Option { + &self.metadata + } + + fn updated_at(&self) -> i64 { + self.updated_at + } } #[derive(Clone, Debug)] @@ -196,6 +168,7 @@ pub struct UserProfile { pub auth_type: AuthType, // If the encryption_sign is not empty, which means the user has enabled the encryption. pub encryption_type: EncryptionType, + pub updated_at: i64, } #[derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)] @@ -243,17 +216,37 @@ where { fn from(params: (&T, &AuthType)) -> Self { let (value, auth_type) = params; + let (icon_url, openai_key, stability_ai_key) = { + value + .metadata() + .as_ref() + .map(|m| { + ( + m.get(USER_METADATA_ICON_URL) + .map(|v| v.to_string()) + .unwrap_or_default(), + m.get(USER_METADATA_OPEN_AI_KEY) + .map(|v| v.to_string()) + .unwrap_or_default(), + m.get(USER_METADATA_STABILITY_AI_KEY) + .map(|v| v.to_string()) + .unwrap_or_default(), + ) + }) + .unwrap_or_default() + }; Self { uid: value.user_id(), email: value.user_email().unwrap_or_default(), name: value.user_name().to_owned(), token: value.user_token().unwrap_or_default(), - icon_url: "".to_owned(), - openai_key: "".to_owned(), + icon_url, + openai_key, workspace_id: value.latest_workspace().id.to_owned(), auth_type: auth_type.clone(), encryption_type: value.encryption_type(), - stability_ai_key: "".to_owned(), + stability_ai_key, + updated_at: value.updated_at(), } } } diff --git a/frontend/rust-lib/flowy-user/src/entities/auth.rs b/frontend/rust-lib/flowy-user/src/entities/auth.rs index 8733e99ce1b8..311eb9747fb1 100644 --- a/frontend/rust-lib/flowy-user/src/entities/auth.rs +++ b/frontend/rust-lib/flowy-user/src/entities/auth.rs @@ -240,6 +240,9 @@ pub struct UserStatePB { pub struct AuthStateChangedPB { #[pb(index = 1)] pub state: AuthStatePB, + + #[pb(index = 2)] + pub message: String, } #[derive(ProtoBuf_Enum, Debug, Clone)] diff --git a/frontend/rust-lib/flowy-user/src/entities/user_setting.rs b/frontend/rust-lib/flowy-user/src/entities/user_setting.rs index a4e91f4a5c34..3e4b59cde3c2 100644 --- a/frontend/rust-lib/flowy-user/src/entities/user_setting.rs +++ b/frontend/rust-lib/flowy-user/src/entities/user_setting.rs @@ -173,9 +173,9 @@ pub struct UserSecretPB { } #[derive(Default, ProtoBuf)] -pub struct UserEncryptionSecretCheckPB { +pub struct UserEncryptionConfigurationPB { #[pb(index = 1)] - pub is_need_secret: bool, + pub require_secret: bool, } impl From for UserCloudConfigPB { diff --git a/frontend/rust-lib/flowy-user/src/event_handler.rs b/frontend/rust-lib/flowy-user/src/event_handler.rs index de92c4fb569f..4e077326033b 100644 --- a/frontend/rust-lib/flowy-user/src/event_handler.rs +++ b/frontend/rust-lib/flowy-user/src/event_handler.rs @@ -2,6 +2,7 @@ use std::sync::Weak; use std::{convert::TryInto, sync::Arc}; use serde_json::Value; +use tracing::event; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_sqlite::kv::StorePreferences; @@ -89,7 +90,7 @@ pub async fn check_user_handler( Ok(()) } -#[tracing::instrument(level = "debug", skip(manager))] +#[tracing::instrument(level = "debug", skip(manager), err)] pub async fn get_user_profile_handler( manager: AFPluginState>, ) -> DataResult { @@ -105,6 +106,12 @@ pub async fn get_user_profile_handler( } }); + event!( + tracing::Level::DEBUG, + "Get user profile: {:?}", + user_profile + ); + data_result_ok(user_profile.into()) } @@ -330,7 +337,7 @@ pub async fn set_encrypt_secret_handler( #[tracing::instrument(level = "debug", skip_all, err)] pub async fn check_encrypt_secret_handler( manager: AFPluginState>, -) -> DataResult { +) -> DataResult { let manager = upgrade_manager(manager)?; let uid = manager.get_session()?.user_id; let profile = manager.get_user_profile(uid).await?; @@ -346,7 +353,9 @@ pub async fn check_encrypt_secret_handler( }, }; - data_result_ok(UserEncryptionSecretCheckPB { is_need_secret }) + data_result_ok(UserEncryptionConfigurationPB { + require_secret: is_need_secret, + }) } #[tracing::instrument(level = "debug", skip_all, err)] diff --git a/frontend/rust-lib/flowy-user/src/event_map.rs b/frontend/rust-lib/flowy-user/src/event_map.rs index 22b700f30023..3fc698593aec 100644 --- a/frontend/rust-lib/flowy-user/src/event_map.rs +++ b/frontend/rust-lib/flowy-user/src/event_map.rs @@ -272,7 +272,7 @@ pub enum UserEvent { #[event(input = "UserSecretPB")] SetEncryptionSecret = 15, - #[event(output = "UserEncryptionSecretCheckPB")] + #[event(output = "UserEncryptionConfigurationPB")] CheckEncryptionSign = 16, /// Return the all the workspaces of the user diff --git a/frontend/rust-lib/flowy-user/src/manager.rs b/frontend/rust-lib/flowy-user/src/manager.rs index 6610583e7a39..7fe83d3ec163 100644 --- a/frontend/rust-lib/flowy-user/src/manager.rs +++ b/frontend/rust-lib/flowy-user/src/manager.rs @@ -149,6 +149,7 @@ impl UserManager { UserTokenState::Invalid => { send_auth_state_notification(AuthStateChangedPB { state: AuthStatePB::InvalidAuth, + message: "Token is invalid".to_string(), }) .send(); }, @@ -256,6 +257,7 @@ impl UserManager { } send_auth_state_notification(AuthStateChangedPB { state: AuthStatePB::AuthStateSignIn, + message: "Sign in success".to_string(), }) .send(); Ok(user_profile) @@ -387,6 +389,7 @@ impl UserManager { send_auth_state_notification(AuthStateChangedPB { state: AuthStatePB::AuthStateSignIn, + message: "Sign up success".to_string(), }) .send(); Ok(()) @@ -402,7 +405,7 @@ impl UserManager { tokio::spawn(async move { match server.sign_out(None).await { Ok(_) => {}, - Err(e) => tracing::error!("Sign out failed: {:?}", e), + Err(e) => event!(tracing::Level::ERROR, "{:?}", e), } }); Ok(()) @@ -421,8 +424,12 @@ impl UserManager { ) -> Result<(), FlowyError> { let changeset = UserTableChangeset::new(params.clone()); let session = self.get_session()?; - save_user_profile_change(session.user_id, self.db_pool(session.user_id)?, changeset)?; - self.update_user(session.user_id, None, params).await?; + upsert_user_profile_change(session.user_id, self.db_pool(session.user_id)?, changeset)?; + + let profile = self.get_user_profile(session.user_id).await?; + self + .update_user(session.user_id, profile.token, params) + .await?; Ok(()) } @@ -462,15 +469,14 @@ impl UserManager { .await? .ok_or_else(|| FlowyError::new(ErrorCode::RecordNotFound, "User not found"))?; - if !is_user_encryption_sign_valid(old_user_profile, &new_user_profile.encryption_type.sign()) { - return Err(FlowyError::new( - ErrorCode::InvalidEncryptSecret, - "Invalid encryption sign", - )); + if new_user_profile.updated_at > old_user_profile.updated_at { + check_encryption_sign(old_user_profile, &new_user_profile.encryption_type.sign()); + + // Save the new user profile + let changeset = UserTableChangeset::from_user_profile(new_user_profile.clone()); + let _ = upsert_user_profile_change(uid, self.database.get_pool(uid)?, changeset); } - let changeset = UserTableChangeset::from_user_profile(new_user_profile.clone()); - let _ = save_user_profile_change(uid, self.database.get_pool(uid)?, changeset); Ok(new_user_profile) } @@ -501,13 +507,12 @@ impl UserManager { async fn update_user( &self, uid: i64, - token: Option, + token: String, params: UpdateUserProfileParams, ) -> Result<(), FlowyError> { let server = self.cloud_services.get_user_service()?; - let token = token.to_owned(); tokio::spawn(async move { - let credentials = UserCredentials::new(token, Some(uid), None); + let credentials = UserCredentials::new(Some(token), Some(uid), None); server.update_user(credentials, params).await }) .await @@ -613,6 +618,7 @@ impl UserManager { ) -> Result<(), FlowyError> { let user_profile = UserProfile::from((response, auth_type)); let uid = user_profile.uid; + event!(tracing::Level::DEBUG, "Save new history user: {:?}", uid); self.add_historical_user( uid, response.device_id(), @@ -620,7 +626,9 @@ impl UserManager { auth_type, self.user_dir(uid), ); + event!(tracing::Level::DEBUG, "Save new history user workspace"); save_user_workspaces(uid, self.db_pool(uid)?, response.user_workspaces())?; + event!(tracing::Level::INFO, "Save new user profile to disk"); self .save_user(uid, (user_profile, auth_type.clone()).into()) .await?; @@ -640,12 +648,12 @@ impl UserManager { if session.user_id == user_update.uid { debug!("Receive user update: {:?}", user_update); let user_profile = self.get_user_profile(user_update.uid).await?; - if !is_user_encryption_sign_valid(&user_profile, &user_update.encryption_sign) { + if !check_encryption_sign(&user_profile, &user_update.encryption_sign) { return Ok(()); } // Save the user profile change - save_user_profile_change( + upsert_user_profile_change( user_update.uid, self.db_pool(user_update.uid)?, UserTableChangeset::from(user_update), @@ -685,24 +693,30 @@ impl UserManager { } } -fn is_user_encryption_sign_valid(user_profile: &UserProfile, encryption_sign: &str) -> bool { +fn check_encryption_sign(user_profile: &UserProfile, encryption_sign: &str) -> bool { // If the local user profile's encryption sign is not equal to the user update's encryption sign, // which means the user enable encryption in another device, we should logout the current user. let is_valid = user_profile.encryption_type.sign() == encryption_sign; if !is_valid { send_auth_state_notification(AuthStateChangedPB { state: AuthStatePB::InvalidAuth, + message: "Encryption configuration was changed".to_string(), }) .send(); } is_valid } -fn save_user_profile_change( +fn upsert_user_profile_change( uid: i64, pool: Arc, changeset: UserTableChangeset, ) -> FlowyResult<()> { + event!( + tracing::Level::DEBUG, + "Update user profile with changeset: {:?}", + changeset + ); let conn = pool.get()?; diesel_update_table!(user_table, changeset, &*conn); let user: UserProfile = user_table::dsl::user_table @@ -719,5 +733,5 @@ fn save_user_profile_change( fn save_user_token(uid: i64, pool: Arc, token: String) -> FlowyResult<()> { let params = UpdateUserProfileParams::new(uid).with_token(token); let changeset = UserTableChangeset::new(params); - save_user_profile_change(uid, pool, changeset) + upsert_user_profile_change(uid, pool, changeset) } diff --git a/frontend/rust-lib/flowy-user/src/notification.rs b/frontend/rust-lib/flowy-user/src/notification.rs index 38083ea1977d..b3d15ba19ca4 100644 --- a/frontend/rust-lib/flowy-user/src/notification.rs +++ b/frontend/rust-lib/flowy-user/src/notification.rs @@ -21,6 +21,7 @@ impl std::convert::From for i32 { } } +#[tracing::instrument(level = "trace")] pub(crate) fn send_notification(id: &str, ty: UserNotification) -> NotificationBuilder { NotificationBuilder::new(id, ty, USER_OBSERVABLE_SOURCE) } diff --git a/frontend/rust-lib/flowy-user/src/services/user_sql.rs b/frontend/rust-lib/flowy-user/src/services/user_sql.rs index 0ee53f225fb0..b60343443058 100644 --- a/frontend/rust-lib/flowy-user/src/services/user_sql.rs +++ b/frontend/rust-lib/flowy-user/src/services/user_sql.rs @@ -19,6 +19,7 @@ pub struct UserTable { pub(crate) auth_type: i32, pub(crate) encryption_type: String, pub(crate) stability_ai_key: String, + pub(crate) updated_at: i64, } impl UserTable { @@ -43,6 +44,7 @@ impl From<(UserProfile, AuthType)> for UserTable { auth_type: auth_type as i32, encryption_type, stability_ai_key: user_profile.stability_ai_key, + updated_at: user_profile.updated_at, } } } @@ -60,6 +62,7 @@ impl From for UserProfile { auth_type: AuthType::from(table.auth_type), encryption_type: EncryptionType::from_str(&table.encryption_type).unwrap_or_default(), stability_ai_key: table.stability_ai_key, + updated_at: table.updated_at, } } } diff --git a/frontend/rust-lib/flowy-user/src/services/user_workspace.rs b/frontend/rust-lib/flowy-user/src/services/user_workspace.rs index 76023704ad85..2684f31bf828 100644 --- a/frontend/rust-lib/flowy-user/src/services/user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/services/user_workspace.rs @@ -74,7 +74,7 @@ impl UserManager { if let Ok(service) = self.cloud_services.get_user_service() { if let Ok(pool) = self.db_pool(uid) { tokio::spawn(async move { - if let Ok(new_user_workspaces) = service.get_user_workspaces(uid).await { + if let Ok(new_user_workspaces) = service.get_all_user_workspaces(uid).await { let _ = save_user_workspaces(uid, pool, &new_user_workspaces); let repeated_workspace_pbs = RepeatedUserWorkspacePB::from(new_user_workspaces); send_notification(&uid.to_string(), UserNotification::DidUpdateUserWorkspaces) diff --git a/frontend/scripts/makefile/tests.toml b/frontend/scripts/makefile/tests.toml index ef06f98a9090..c5e946b1066e 100644 --- a/frontend/scripts/makefile/tests.toml +++ b/frontend/scripts/makefile/tests.toml @@ -62,7 +62,7 @@ flutter test --dart-define=RUST_LOG=${RUST_LOG} -j, --concurrency=1 --coverage script_runner = "@shell" [tasks.rust_unit_test] -run_task = { name = ["rust_lib_unit_test", "shared_lib_unit_test"] } +run_task = { name = ["rust_lib_unit_test"] } [tasks.supabase_unit_test] env = { RUST_LOG = "info" }