From e81a2ff5776612dd16fc5f6a4eb800a5f06687de Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Wed, 14 Feb 2024 09:38:05 +0800 Subject: [PATCH] fix: token refresh on local (#4650) * fix: refresh user token on local * chore: add test --- CHANGELOG.md | 2 +- frontend/Makefile.toml | 2 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- .../local_test/import_af_data_local_test.rs | 50 ++++++++ .../tests/user/local_test/mod.rs | 1 + .../flowy-user/src/user_manager/manager.rs | 112 +++++++++++------- 6 files changed, 126 insertions(+), 43 deletions(-) create mode 100644 frontend/rust-lib/event-integration/tests/user/local_test/import_af_data_local_test.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 889c811c4c..57ad262e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # Release Notes -## Version 0.4.7 - 02/08/2024 +## Version 0.4.8 - 02/13/2024 ### Bug Fixes - Fixed a possible error when loading workspaces diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index 1b338f95b9..7f05064bed 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true CARGO_MAKE_CRATE_FS_NAME = "dart_ffi" CARGO_MAKE_CRATE_NAME = "dart-ffi" LIB_NAME = "dart_ffi" -APPFLOWY_VERSION = "0.4.7" +APPFLOWY_VERSION = "0.4.8" FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite" PRODUCT_NAME = "AppFlowy" MACOSX_DEPLOYMENT_TARGET = "11.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 5a38b20f22..85edb513ff 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.4.7 +version: 0.4.8 environment: flutter: ">=3.18.0-0.2.pre" diff --git a/frontend/rust-lib/event-integration/tests/user/local_test/import_af_data_local_test.rs b/frontend/rust-lib/event-integration/tests/user/local_test/import_af_data_local_test.rs new file mode 100644 index 0000000000..0c801c77be --- /dev/null +++ b/frontend/rust-lib/event-integration/tests/user/local_test/import_af_data_local_test.rs @@ -0,0 +1,50 @@ +use crate::util::unzip_history_user_db; +use event_integration::user_event::user_localhost_af_cloud; +use event_integration::EventIntegrationTest; +use flowy_core::DEFAULT_NAME; +use std::time::Duration; + +#[tokio::test] +async fn import_appflowy_data_folder_into_new_view_test() { + let import_container_name = "040_local".to_string(); + let (cleaner, user_db_path) = + unzip_history_user_db("./tests/asset", &import_container_name).unwrap(); + let (imported_af_folder_cleaner, imported_af_data_path) = + unzip_history_user_db("./tests/asset", &import_container_name).unwrap(); + + user_localhost_af_cloud().await; + let test = + EventIntegrationTest::new_with_user_data_path(user_db_path.clone(), DEFAULT_NAME.to_string()) + .await; + // In the 040_local, the structure is: + // workspace: + // view: Document1 + // view: Document2 + // view: Grid1 + // view: Grid2 + // Sleep for 2 seconds to wait for the initial workspace to be created + tokio::time::sleep(Duration::from_secs(5)).await; + + test + .import_appflowy_data( + imported_af_data_path.to_str().unwrap().to_string(), + Some(import_container_name.clone()), + ) + .await + .unwrap(); + + // after import, the structure is: + // workspace: + // view: Getting Started + // view: 040_local + // view: Document1 + // view: Document2 + // view: Grid1 + // view: Grid2 + let views = test.get_all_workspace_views().await; + assert_eq!(views.len(), 2); + assert_eq!(views[1].name, import_container_name); + + drop(cleaner); + drop(imported_af_folder_cleaner); +} diff --git a/frontend/rust-lib/event-integration/tests/user/local_test/mod.rs b/frontend/rust-lib/event-integration/tests/user/local_test/mod.rs index 78d6ca1eca..0f1e2e47d5 100644 --- a/frontend/rust-lib/event-integration/tests/user/local_test/mod.rs +++ b/frontend/rust-lib/event-integration/tests/user/local_test/mod.rs @@ -1,4 +1,5 @@ mod auth_test; mod helper; +mod import_af_data_local_test; mod user_awareness_test; mod user_profile_test; diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs index a046237943..1e44a19b0b 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs @@ -17,7 +17,7 @@ use std::sync::atomic::{AtomicI64, Ordering}; use std::sync::{Arc, Weak}; use tokio::sync::{Mutex, RwLock}; use tokio_stream::StreamExt; -use tracing::{debug, error, event, info, instrument}; +use tracing::{debug, error, event, info, instrument, warn}; use lib_dispatch::prelude::af_spawn; use lib_infra::box_any::BoxAny; @@ -152,56 +152,88 @@ impl UserManager { user.email ); + self.prepare_user(&session).await; + self.prepare_backup(&session).await; + // Set the token if the current cloud service using token to authenticate // Currently, only the AppFlowy cloud using token to init the client api. - if let Err(err) = self.cloud_services.set_token(&user.token) { - error!("Set token failed: {}", err); - } + // TODO(nathan): using trait to separate the init process for different cloud service + if user.authenticator.is_appflowy_cloud() { + if let Err(err) = self.cloud_services.set_token(&user.token) { + error!("Set token failed: {}", err); + } - // Subscribe the token state - let weak_cloud_services = Arc::downgrade(&self.cloud_services); - let weak_authenticate_user = Arc::downgrade(&self.authenticate_user); - let weak_pool = Arc::downgrade(&self.db_pool(user.uid)?); - let cloned_session = session.clone(); - if let Some(mut token_state_rx) = self.cloud_services.subscribe_token_state() { - event!(tracing::Level::DEBUG, "Listen token state change"); - let user_uid = user.uid; - let local_token = user.token.clone(); - af_spawn(async move { - while let Some(token_state) = token_state_rx.next().await { - debug!("Token state changed: {:?}", token_state); - match token_state { - UserTokenState::Refresh { token: new_token } => { - // Only save the token if the token is different from the current token - if new_token != local_token { - if let Some(conn) = weak_pool.upgrade().and_then(|pool| pool.get().ok()) { - // Save the new token - if let Err(err) = save_user_token(user_uid, conn, new_token) { - error!("Save user token failed: {}", err); + // Subscribe the token state + let weak_cloud_services = Arc::downgrade(&self.cloud_services); + let weak_authenticate_user = Arc::downgrade(&self.authenticate_user); + let weak_pool = Arc::downgrade(&self.db_pool(user.uid)?); + let cloned_session = session.clone(); + if let Some(mut token_state_rx) = self.cloud_services.subscribe_token_state() { + event!(tracing::Level::DEBUG, "Listen token state change"); + let user_uid = user.uid; + let local_token = user.token.clone(); + af_spawn(async move { + while let Some(token_state) = token_state_rx.next().await { + debug!("Token state changed: {:?}", token_state); + match token_state { + UserTokenState::Refresh { token: new_token } => { + // Only save the token if the token is different from the current token + if new_token != local_token { + if let Some(conn) = weak_pool.upgrade().and_then(|pool| pool.get().ok()) { + // Save the new token + if let Err(err) = save_user_token(user_uid, conn, new_token) { + error!("Save user token failed: {}", err); + } } } - } - }, - UserTokenState::Invalid => { - // Force user to sign out when the token is invalid - if let (Some(cloud_services), Some(authenticate_user), Some(conn)) = ( - weak_cloud_services.upgrade(), - weak_authenticate_user.upgrade(), - weak_pool.upgrade().and_then(|pool| pool.get().ok()), - ) { + }, + UserTokenState::Invalid => { + // Attempt to upgrade the weak reference for cloud_services + let cloud_services = match weak_cloud_services.upgrade() { + Some(cloud_services) => cloud_services, + None => { + error!("Failed to upgrade weak reference for cloud_services"); + return; // Exit early if the upgrade fails + }, + }; + + // Attempt to upgrade the weak reference for authenticate_user + let authenticate_user = match weak_authenticate_user.upgrade() { + Some(authenticate_user) => authenticate_user, + None => { + warn!("Failed to upgrade weak reference for authenticate_user"); + return; // Exit early if the upgrade fails + }, + }; + + // Attempt to upgrade the weak reference for pool and then get a connection + let conn = match weak_pool.upgrade() { + Some(pool) => match pool.get() { + Ok(conn) => conn, + Err(_) => { + warn!("Failed to get connection from pool"); + return; // Exit early if getting connection fails + }, + }, + None => { + warn!("Failed to upgrade weak reference for pool"); + return; // Exit early if the upgrade fails + }, + }; + + // If all upgrades succeed, proceed with the sign_out operation if let Err(err) = sign_out(&cloud_services, &cloned_session, &authenticate_user, conn).await { error!("Sign out when token invalid failed: {:?}", err); } - } - }, + // Force user to sign out when the token is invalid + }, + } } - } - }); + }); + } } - self.prepare_user(&session).await; - self.prepare_backup(&session).await; // Do the user data migration if needed event!(tracing::Level::INFO, "Prepare user data migration"); @@ -270,7 +302,7 @@ impl UserManager { /// /// A sign-in notification is also sent after a successful sign-in. /// - #[tracing::instrument(level = "debug", skip(self, params))] + #[tracing::instrument(level = "info", skip(self, params))] pub async fn sign_in( &self, params: SignInParams,