chore: fix test

This commit is contained in:
Nathan 2025-04-22 23:20:15 +08:00
parent 7e88a3897c
commit db5e4766af
21 changed files with 297 additions and 278 deletions

View file

@ -144,34 +144,34 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
SPEC CHECKSUMS:
app_links: 9028728e32c83a0831d9db8cf91c526d16cc5468
appflowy_backend: 464aeb3e5c6966a41641a2111e5ead72ce2695f7
auto_updater_macos: 3a42f1a06be6981f1a18be37e6e7bf86aa732118
bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9
connectivity_plus: e74b9f74717d2d99d45751750e266e55912baeb5
desktop_drop: e0b672a7d84c0a6cbc378595e82cdb15f2970a43
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31
flowy_infra_ui: 8760ff42a789de40bf5007a5f176b454722a341e
app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a
appflowy_backend: 865496343de667fc8c600e04b9fd05234e130cf9
auto_updater_macos: 3e3462c418fe4e731917eacd8d28eef7af84086d
bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
device_info_plus: 1b14eed9bf95428983aed283a8d51cce3d8c4215
file_selector_macos: cc3858c981fe6889f364731200d6232dac1d812d
flowy_infra_ui: 03301a39ad118771adbf051a664265c61c507f38
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
HotKey: 400beb7caa29054ea8d864c96f5ba7e5b4852277
hotkey_manager: b443f35f4d772162937aa73fd8995e579f8ac4e2
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e
package_info_plus: f0052d280d17aa382b932f399edf32507174e870
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
hotkey_manager: c32bf0bfe8f934b7bc17ab4ad5c4c142960b023c
irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f
screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161
Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1
sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90
share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737
share_plus: 1fa619de8392a4398bfaf176d441853922614e89
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
Sparkle: 5f8960a7a119aa7d45dacc0d5837017170bc5675
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
webview_flutter_wkwebview: 44d4dee7d7056d5ad185d25b38404436d56c547c
window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3
url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404
webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
PODFILE CHECKSUM: 0532f3f001ca3110b8be345d6491fff690e95823

View file

@ -11,7 +11,6 @@ async fn migrate_historical_empty_document_test() {
"historical_empty_document",
)
.unwrap();
let s = user_db_path.to_str().unwrap().to_string();
let test =
EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()).await;

View file

@ -104,7 +104,7 @@ impl AIManager {
}
}
async fn reload_with_workspace_id(&self, workspace_id: &str) {
async fn reload_with_workspace_id(&self, workspace_id: &Uuid) {
// Check if local AI is enabled for this workspace and if we're in local mode
let result = self.user_service.is_local_model().await;
if let Err(err) = &result {
@ -115,7 +115,9 @@ impl AIManager {
}
let is_local = result.unwrap_or(false);
let is_enabled = self.local_ai.is_enabled_on_workspace(workspace_id);
let is_enabled = self
.local_ai
.is_enabled_on_workspace(&workspace_id.to_string());
let is_running = self.local_ai.is_running();
info!(
"[AI Manager] Reloading workspace: {}, is_local: {}, is_enabled: {}, is_running: {}",
@ -161,17 +163,17 @@ impl AIManager {
}
#[instrument(skip_all, err)]
pub async fn on_launch_if_authenticated(&self, workspace_id: &str) -> Result<(), FlowyError> {
pub async fn on_launch_if_authenticated(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
self.reload_with_workspace_id(workspace_id).await;
Ok(())
}
pub async fn initialize_after_sign_in(&self, workspace_id: &str) -> Result<(), FlowyError> {
pub async fn initialize_after_sign_in(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
self.reload_with_workspace_id(workspace_id).await;
Ok(())
}
pub async fn initialize_after_sign_up(&self, workspace_id: &str) -> Result<(), FlowyError> {
pub async fn initialize_after_sign_up(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
self.reload_with_workspace_id(workspace_id).await;
Ok(())
}
@ -181,9 +183,7 @@ impl AIManager {
&self,
workspace_id: &Uuid,
) -> Result<(), FlowyError> {
self
.reload_with_workspace_id(&workspace_id.to_string())
.await;
self.reload_with_workspace_id(workspace_id).await;
Ok(())
}

View file

@ -81,11 +81,10 @@ impl UserStatusCallback for UserStatusCallbackImpl {
&self,
user_id: i64,
cloud_config: &Option<UserCloudConfig>,
user_workspace: &UserWorkspace,
workspace_id: &Uuid,
_device_id: &str,
auth_type: &AuthType,
) -> FlowyResult<()> {
let workspace_id = user_workspace.workspace_id()?;
if let Some(cloud_config) = cloud_config {
self
.server_provider
@ -101,7 +100,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
.folder_manager
.initialize(
user_id,
&workspace_id,
workspace_id,
FolderInitDataSource::LocalDisk {
create_if_not_exist: false,
},
@ -113,8 +112,8 @@ impl UserStatusCallback for UserStatusCallbackImpl {
.await?;
self.document_manager.initialize(user_id).await?;
let workspace_id = user_workspace.id.clone();
let cloned_ai_manager = self.ai_manager.clone();
let workspace_id = *workspace_id;
self.runtime.spawn(async move {
if let Err(err) = cloned_ai_manager
.on_launch_if_authenticated(&workspace_id)
@ -129,19 +128,18 @@ impl UserStatusCallback for UserStatusCallbackImpl {
async fn on_sign_in(
&self,
user_id: i64,
user_workspace: &UserWorkspace,
workspace_id: &Uuid,
device_id: &str,
auth_type: &AuthType,
) -> FlowyResult<()> {
event!(
tracing::Level::TRACE,
"Notify did sign in: latest_workspace: {:?}, device_id: {}",
user_workspace,
workspace_id,
device_id
);
let workspace_id = user_workspace.workspace_id()?;
let data_source = self
.folder_init_data_source(user_id, &workspace_id, auth_type)
.folder_init_data_source(user_id, workspace_id, auth_type)
.await?;
self
.folder_manager
@ -158,7 +156,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
self
.ai_manager
.initialize_after_sign_in(&user_workspace.id)
.initialize_after_sign_in(workspace_id)
.await?;
Ok(())
@ -168,7 +166,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
&self,
is_new_user: bool,
user_profile: &UserProfile,
user_workspace: &UserWorkspace,
workspace_id: &Uuid,
device_id: &str,
auth_type: &AuthType,
) -> FlowyResult<()> {
@ -176,12 +174,11 @@ impl UserStatusCallback for UserStatusCallbackImpl {
tracing::Level::TRACE,
"Notify did sign up: is new: {} user_workspace: {:?}, device_id: {}",
is_new_user,
user_workspace,
workspace_id,
device_id
);
let workspace_id = user_workspace.workspace_id()?;
let data_source = self
.folder_init_data_source(user_profile.uid, &workspace_id, auth_type)
.folder_init_data_source(user_profile.uid, workspace_id, auth_type)
.await?;
self
@ -191,7 +188,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
&user_profile.token,
is_new_user,
data_source,
&workspace_id,
workspace_id,
)
.await
.context("FolderManager error")?;
@ -210,7 +207,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
self
.ai_manager
.initialize_after_sign_up(&user_workspace.id)
.initialize_after_sign_up(workspace_id)
.await?;
Ok(())
}

View file

@ -1,106 +1,26 @@
use crate::entities::{AuthType, UserAuthResponse, UserWorkspace};
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use chrono::Utc;
use serde::de::{MapAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use crate::entities::UserAuthResponse;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Display;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
pub user_id: i64,
pub user_uuid: Uuid,
pub user_workspace: UserWorkspace,
pub workspace_id: String,
}
impl Display for Session {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"user_id: {}, user_workspace: {}:{}",
self.user_id, self.user_workspace.name, self.user_workspace.id,
"user_id: {}, user_workspace: {}",
self.user_id, self.workspace_id,
)
}
}
struct SessionVisitor;
impl<'de> Visitor<'de> for SessionVisitor {
type Value = Session;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Session")
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut user_id = None;
let mut user_uuid = None;
// For historical reasons, the session used to contain a workspace_id field.
// This field is no longer used, and is replaced by user_workspace.
let mut workspace_id = None;
let mut user_workspace = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"user_id" => {
user_id = Some(map.next_value()?);
},
"user_uuid" => {
user_uuid = Some(map.next_value()?);
},
"workspace_id" => {
workspace_id = Some(map.next_value()?);
},
"user_workspace" => {
user_workspace = Some(map.next_value()?);
},
_ => {
let _ = map.next_value::<Value>();
},
}
}
let user_id = user_id.ok_or(serde::de::Error::missing_field("user_id"))?;
let user_uuid = user_uuid.ok_or(serde::de::Error::missing_field("user_uuid"))?;
if user_workspace.is_none() {
if let Some(workspace_id) = workspace_id {
user_workspace = Some(UserWorkspace {
id: workspace_id,
name: "My Workspace".to_string(),
created_at: Utc::now(),
// For historical reasons, the database_storage_id is constructed by the user_id.
workspace_database_id: STANDARD.encode(format!("{}:user:database", user_id)),
icon: "".to_owned(),
member_count: 1,
role: None,
workspace_type: AuthType::Local,
})
}
}
let session = Session {
user_id,
user_uuid,
user_workspace: user_workspace.ok_or(serde::de::Error::missing_field("user_workspace"))?,
};
Ok(session)
}
}
impl<'de> Deserialize<'de> for Session {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(SessionVisitor)
}
}
impl<T> From<&T> for Session
where
T: UserAuthResponse,
@ -109,7 +29,7 @@ where
Self {
user_id: value.user_id(),
user_uuid: *value.user_uuid(),
user_workspace: value.latest_workspace().clone(),
workspace_id: value.latest_workspace().clone().id,
}
}
}

View file

@ -99,6 +99,17 @@ pub fn select_user_workspace(
Ok(row)
}
pub fn select_user_workspace_type(
workspace_id: &str,
conn: &mut SqliteConnection,
) -> FlowyResult<AuthType> {
let row = dsl::user_workspace_table
.filter(user_workspace_table::id.eq(workspace_id))
.select(user_workspace_table::workspace_type)
.first::<i32>(conn)?;
Ok(AuthType::from(row))
}
pub fn select_all_user_workspace(
uid: i64,
conn: &mut SqliteConnection,

View file

@ -94,12 +94,12 @@ pub async fn get_user_profile_handler(
let session = manager.get_session()?;
let mut user_profile = manager
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
.get_user_profile_from_disk(session.user_id, &session.workspace_id)
.await?;
let weak_manager = Arc::downgrade(&manager);
let cloned_user_profile = user_profile.clone();
let workspace_id = session.user_workspace.id.clone();
let workspace_id = session.workspace_id.clone();
// Refresh the user profile in the background
tokio::spawn(async move {
@ -433,7 +433,7 @@ pub async fn get_all_workspace_handler(
let manager = upgrade_manager(manager)?;
let session = manager.get_session()?;
let profile = manager
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
.get_user_profile_from_disk(session.user_id, &session.workspace_id)
.await?;
let user_workspaces = manager
.get_all_user_workspaces(profile.uid, profile.auth_type)
@ -822,7 +822,7 @@ pub async fn get_workspace_member_info(
manager: AFPluginState<Weak<UserManager>>,
) -> DataResult<WorkspaceMemberPB, FlowyError> {
let manager = upgrade_manager(manager)?;
let workspace_id = manager.get_session()?.user_workspace.workspace_id()?;
let workspace_id = Uuid::parse_str(&manager.get_session()?.workspace_id)?;
let member = manager
.get_workspace_member_info(param.uid, &workspace_id)
.await?;

View file

@ -285,7 +285,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
&self,
_user_id: i64,
_cloud_config: &Option<UserCloudConfig>,
_user_workspace: &UserWorkspace,
_workspace_id: &Uuid,
_device_id: &str,
_auth_type: &AuthType,
) -> FlowyResult<()> {
@ -300,7 +300,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
async fn on_sign_in(
&self,
_user_id: i64,
_user_workspace: &UserWorkspace,
_workspace_id: &Uuid,
_device_id: &str,
_auth_type: &AuthType,
) -> FlowyResult<()> {
@ -312,7 +312,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
&self,
_is_new_user: bool,
_user_profile: &UserProfile,
_user_workspace: &UserWorkspace,
_workspace_id: &Uuid,
_device_id: &str,
_auth_type: &AuthType,
) -> FlowyResult<()> {

View file

@ -5,11 +5,13 @@ use tracing::instrument;
use collab_integrate::CollabKVDB;
use flowy_error::FlowyResult;
use flowy_sqlite::kv::KVStorePreferences;
use flowy_user_pub::entities::AuthType;
use crate::migrations::migration::UserDataMigration;
use crate::migrations::session_migration::get_session_workspace;
use flowy_user_pub::session::Session;
use flowy_user_pub::sql::upsert_user_workspace;
use flowy_user_pub::sql::{select_user_workspace, upsert_user_workspace};
pub struct AnonUserWorkspaceTableMigration;
@ -32,17 +34,21 @@ impl UserDataMigration for AnonUserWorkspaceTableMigration {
#[instrument(name = "AnonUserWorkspaceTableMigration", skip_all, err)]
fn run(
&self,
session: &Session,
user: &Session,
_collab_db: &Arc<CollabKVDB>,
user_auth_type: &AuthType,
db: &mut SqliteConnection,
store_preferences: &Arc<KVStorePreferences>,
) -> FlowyResult<()> {
// For historical reason, anon user doesn't have a workspace in user_workspace_table.
// So we need to create a new entry for the anon user in the user_workspace_table.
if matches!(user_auth_type, AuthType::Local) {
let mut user_workspace = session.user_workspace.clone();
user_workspace.workspace_type = AuthType::Local;
upsert_user_workspace(session.user_id, *user_auth_type, user_workspace, db)?;
if let Some(mut user_workspace) = get_session_workspace(store_preferences) {
if select_user_workspace(&user_workspace.id, db).ok().is_none() {
user_workspace.workspace_type = AuthType::Local;
upsert_user_workspace(user.user_id, *user_auth_type, user_workspace, db)?;
}
}
}
Ok(())

View file

@ -8,6 +8,7 @@ use tracing::{instrument, trace};
use collab_integrate::CollabKVDB;
use flowy_error::FlowyResult;
use flowy_sqlite::kv::KVStorePreferences;
use flowy_user_pub::entities::AuthType;
use crate::migrations::migration::UserDataMigration;
@ -38,17 +39,15 @@ impl UserDataMigration for CollabDocKeyWithWorkspaceIdMigration {
#[instrument(name = "CollabDocKeyWithWorkspaceIdMigration", skip_all, err)]
fn run(
&self,
session: &Session,
user: &Session,
collab_db: &Arc<CollabKVDB>,
_user_auth_type: &AuthType,
_db: &mut SqliteConnection,
_store_preferences: &Arc<KVStorePreferences>,
) -> FlowyResult<()> {
trace!(
"migrate key with workspace id:{}",
session.user_workspace.id
);
trace!("migrate key with workspace id:{}", user.workspace_id);
collab_db.with_write_txn(|txn| {
migrate_old_keys(txn, &session.user_workspace.id)?;
migrate_old_keys(txn, &user.workspace_id)?;
Ok(())
})?;
Ok(())

View file

@ -12,6 +12,7 @@ use tracing::{event, instrument};
use collab_integrate::{CollabKVAction, CollabKVDB, PersistenceError};
use flowy_error::{FlowyError, FlowyResult};
use flowy_sqlite::kv::KVStorePreferences;
use flowy_user_pub::entities::AuthType;
use crate::migrations::migration::UserDataMigration;
@ -40,10 +41,11 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
#[instrument(name = "HistoricalEmptyDocumentMigration", skip_all, err)]
fn run(
&self,
session: &Session,
user: &Session,
collab_db: &Arc<CollabKVDB>,
user_auth_type: &AuthType,
_db: &mut SqliteConnection,
_store_preferences: &Arc<KVStorePreferences>,
) -> FlowyResult<()> {
// - The `empty document` struct has already undergone refactoring prior to the launch of the AppFlowy cloud version.
// - Consequently, if a user is utilizing the AppFlowy cloud version, there is no need to perform any migration for the `empty document` struct.
@ -52,32 +54,26 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
return Ok(());
}
collab_db.with_write_txn(|write_txn| {
let origin = CollabOrigin::Client(CollabClient::new(session.user_id, "phantom"));
let origin = CollabOrigin::Client(CollabClient::new(user.user_id, "phantom"));
let folder_collab = match load_collab(
session.user_id,
user.user_id,
write_txn,
&session.user_workspace.id,
&session.user_workspace.id,
&user.workspace_id,
&user.workspace_id,
) {
Ok(fc) => fc,
Err(_) => return Ok(()),
};
let folder = Folder::open(session.user_id, folder_collab, None)
let folder = Folder::open(user.user_id, folder_collab, None)
.map_err(|err| PersistenceError::Internal(err.into()))?;
if let Some(workspace_id) = folder.get_workspace_id() {
let migration_views = folder.get_views_belong_to(&workspace_id);
// For historical reasons, the first level documents are empty. So migrate them by inserting
// the default document data.
for view in migration_views {
if migrate_empty_document(
write_txn,
&origin,
&view,
session.user_id,
&session.user_workspace.id,
)
.is_err()
if migrate_empty_document(write_txn, &origin, &view, user.user_id, &user.workspace_id)
.is_err()
{
event!(
tracing::Level::ERROR,

View file

@ -75,7 +75,13 @@ impl UserLocalDataMigration {
let migration_name = migration.name().to_string();
if !duplicated_names.contains(&migration_name) {
migration.run(&self.session, &self.collab_db, user_auth_type, &mut conn)?;
migration.run(
&self.session,
&self.collab_db,
user_auth_type,
&mut conn,
&self.kv,
)?;
applied_migrations.push(migration.name().to_string());
save_migration_record(&mut conn, &migration_name);
duplicated_names.push(migration_name);
@ -100,6 +106,7 @@ pub trait UserDataMigration {
collab_db: &Arc<CollabKVDB>,
user_auth_type: &AuthType,
db: &mut SqliteConnection,
store_preferences: &Arc<KVStorePreferences>,
) -> FlowyResult<()>;
}

View file

@ -1,45 +1,120 @@
use crate::user_manager::manager_history_user::ANON_USER;
use chrono::Utc;
use flowy_sqlite::kv::KVStorePreferences;
use flowy_user_pub::entities::AuthType;
use flowy_user_pub::entities::{AuthType, Role, UserWorkspace};
use flowy_user_pub::session::Session;
use serde_json::{json, Value};
use serde::de::{MapAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use std::fmt;
use std::sync::Arc;
use uuid::Uuid;
const MIGRATION_USER_NO_USER_UUID: &str = "migration_user_no_user_uuid";
const MIGRATION_SESSION: &str = "migration_session_key";
pub const SESSION_CACHE_KEY_BACKUP: &str = "session_cache_key_backup";
pub fn migrate_session(
session_cache_key: &str,
store_preferences: &Arc<KVStorePreferences>,
) -> Option<Session> {
if !store_preferences.get_bool_or_default(MIGRATION_USER_NO_USER_UUID)
&& store_preferences
.set_bool(MIGRATION_USER_NO_USER_UUID, true)
.is_ok()
if !store_preferences.get_bool_or_default(MIGRATION_SESSION)
&& store_preferences.set_bool(MIGRATION_SESSION, true).is_ok()
{
if let Some(mut value) = store_preferences.get_object::<Value>(session_cache_key) {
if value.get("user_uuid").is_none() {
if let Some(map) = value.as_object_mut() {
map.insert("user_uuid".to_string(), json!(Uuid::new_v4()));
}
}
if let Ok(new_session) = serde_json::from_value::<Session>(value) {
let _ = store_preferences.set_object(session_cache_key, &new_session);
}
}
}
if let Some(mut session) = store_preferences.get_object::<Session>(session_cache_key) {
if let Some(anon_session) = store_preferences.get_object::<Session>(ANON_USER) {
if session.user_id == anon_session.user_id
&& session.user_workspace.workspace_type != AuthType::Local
{
session.user_workspace.workspace_type = AuthType::Local;
let _ = store_preferences.set_object(session_cache_key, &session);
}
if let Some(session) = store_preferences.get_object::<SessionBackup>(session_cache_key) {
let _ = store_preferences.set_object(SESSION_CACHE_KEY_BACKUP, &session);
let new_session = Session {
user_id: session.user_id,
user_uuid: session.user_uuid,
workspace_id: session.user_workspace.id,
};
let _ = store_preferences.set_object(session_cache_key, &new_session);
}
}
store_preferences.get_object::<Session>(session_cache_key)
}
#[derive(Debug, Clone, Serialize)]
struct SessionBackup {
user_id: i64,
user_uuid: Uuid,
user_workspace: UserWorkspace,
}
pub fn get_session_workspace(store_preferences: &Arc<KVStorePreferences>) -> Option<UserWorkspace> {
store_preferences
.get_object::<SessionBackup>(SESSION_CACHE_KEY_BACKUP)
.map(|v| v.user_workspace)
}
struct SessionVisitor;
impl<'de> Visitor<'de> for SessionVisitor {
type Value = SessionBackup;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("SessionBackup")
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut user_id = None;
let mut user_uuid = None;
// For historical reasons, the session used to contain a workspace_id field.
// This field is no longer used, and is replaced by user_workspace.
let mut workspace_id = None;
let mut user_workspace = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"user_id" => {
user_id = Some(map.next_value()?);
},
"user_uuid" => {
user_uuid = Some(map.next_value()?);
},
"workspace_id" => {
workspace_id = Some(map.next_value()?);
},
"user_workspace" => {
user_workspace = Some(map.next_value()?);
},
_ => {
let _ = map.next_value::<Value>();
},
}
}
let user_id = user_id.ok_or(serde::de::Error::missing_field("user_id"))?;
let user_uuid = user_uuid.unwrap_or_else(Uuid::new_v4);
if user_workspace.is_none() {
if let Some(workspace_id) = workspace_id {
user_workspace = Some(UserWorkspace {
id: workspace_id,
name: "My Workspace".to_string(),
created_at: Utc::now(),
workspace_database_id: Uuid::new_v4().to_string(),
icon: "".to_owned(),
member_count: 1,
role: Some(Role::Owner),
workspace_type: AuthType::Local,
})
}
}
let session = SessionBackup {
user_id,
user_uuid,
user_workspace: user_workspace.ok_or(serde::de::Error::missing_field("user_workspace"))?,
};
Ok(session)
}
}
impl<'de> Deserialize<'de> for SessionBackup {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(SessionVisitor)
}
}

View file

@ -8,6 +8,7 @@ use tracing::instrument;
use collab_integrate::{CollabKVAction, CollabKVDB};
use flowy_error::FlowyResult;
use flowy_sqlite::kv::KVStorePreferences;
use flowy_user_pub::entities::AuthType;
use crate::migrations::migration::UserDataMigration;
@ -38,19 +39,20 @@ impl UserDataMigration for FavoriteV1AndWorkspaceArrayMigration {
#[instrument(name = "FavoriteV1AndWorkspaceArrayMigration", skip_all, err)]
fn run(
&self,
session: &Session,
user: &Session,
collab_db: &Arc<CollabKVDB>,
_user_auth_type: &AuthType,
_db: &mut SqliteConnection,
_store_preferences: &Arc<KVStorePreferences>,
) -> FlowyResult<()> {
collab_db.with_write_txn(|write_txn| {
if let Ok(collab) = load_collab(
session.user_id,
user.user_id,
write_txn,
&session.user_workspace.id,
&session.user_workspace.id,
&user.workspace_id,
&user.workspace_id,
) {
let mut folder = Folder::open(session.user_id, collab, None)
let mut folder = Folder::open(user.user_id, collab, None)
.map_err(|err| PersistenceError::Internal(err.into()))?;
folder
.body
@ -70,9 +72,9 @@ impl UserDataMigration for FavoriteV1AndWorkspaceArrayMigration {
.encode_collab()
.map_err(|err| PersistenceError::Internal(err.into()))?;
write_txn.flush_doc(
session.user_id,
&session.user_workspace.id,
&session.user_workspace.id,
user.user_id,
&user.workspace_id,
&user.workspace_id,
encode.state_vector.to_vec(),
encode.doc_state.to_vec(),
)?;

View file

@ -8,6 +8,7 @@ use tracing::instrument;
use collab_integrate::{CollabKVAction, CollabKVDB};
use flowy_error::FlowyResult;
use flowy_sqlite::kv::KVStorePreferences;
use flowy_user_pub::entities::AuthType;
use crate::migrations::migration::UserDataMigration;
@ -36,19 +37,20 @@ impl UserDataMigration for WorkspaceTrashMapToSectionMigration {
#[instrument(name = "WorkspaceTrashMapToSectionMigration", skip_all, err)]
fn run(
&self,
session: &Session,
user: &Session,
collab_db: &Arc<CollabKVDB>,
_user_auth_type: &AuthType,
_db: &mut SqliteConnection,
_store_preferences: &Arc<KVStorePreferences>,
) -> FlowyResult<()> {
collab_db.with_write_txn(|write_txn| {
if let Ok(collab) = load_collab(
session.user_id,
user.user_id,
write_txn,
&session.user_workspace.id,
&session.user_workspace.id,
&user.workspace_id,
&user.workspace_id,
) {
let mut folder = Folder::open(session.user_id, collab, None)
let mut folder = Folder::open(user.user_id, collab, None)
.map_err(|err| PersistenceError::Internal(err.into()))?;
let trash_ids = folder
.get_trash_v1()
@ -64,9 +66,9 @@ impl UserDataMigration for WorkspaceTrashMapToSectionMigration {
.encode_collab()
.map_err(|err| PersistenceError::Internal(err.into()))?;
write_txn.flush_doc(
session.user_id,
&session.user_workspace.id,
&session.user_workspace.id,
user.user_id,
&user.workspace_id,
&user.workspace_id,
encode.state_vector.to_vec(),
encode.doc_state.to_vec(),
)?;

View file

@ -11,6 +11,7 @@ use flowy_sqlite::kv::KVStorePreferences;
use flowy_sqlite::DBConnection;
use flowy_user_pub::entities::{AuthType, UserWorkspace};
use flowy_user_pub::session::Session;
use flowy_user_pub::sql::{select_user_workspace, select_user_workspace_type};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Weak};
@ -46,10 +47,9 @@ impl AuthenticateUser {
pub async fn is_local_mode(&self) -> FlowyResult<bool> {
let session = self.get_session()?;
Ok(matches!(
session.user_workspace.workspace_type,
AuthType::Local
))
let mut conn = self.get_sqlite_connection(session.user_id)?;
let workspace_type = select_user_workspace_type(&session.workspace_id, &mut conn)?;
Ok(matches!(workspace_type, AuthType::Local))
}
pub fn device_id(&self) -> FlowyResult<String> {
@ -58,13 +58,15 @@ impl AuthenticateUser {
pub fn workspace_id(&self) -> FlowyResult<Uuid> {
let session = self.get_session()?;
let workspace_uuid = Uuid::from_str(&session.user_workspace.id)?;
let workspace_uuid = Uuid::from_str(&session.workspace_id)?;
Ok(workspace_uuid)
}
pub fn workspace_database_object_id(&self) -> FlowyResult<Uuid> {
let session = self.get_session()?;
let id = Uuid::from_str(&session.user_workspace.workspace_database_id)?;
let mut conn = self.get_sqlite_connection(session.user_id)?;
let workspace = select_user_workspace(&session.workspace_id, &mut conn)?;
let id = Uuid::from_str(&workspace.database_storage_id)?;
Ok(id)
}
@ -104,7 +106,7 @@ impl AuthenticateUser {
let session = self.get_session()?;
let collab_db = self.database.get_collab_db(uid)?;
let read_txn = collab_db.read_txn();
Ok(read_txn.is_exist(uid, session.user_workspace.id.as_str(), object_id))
Ok(read_txn.is_exist(uid, session.workspace_id.as_str(), object_id))
}
pub fn set_session(&self, session: Option<Arc<Session>>) -> Result<(), FlowyError> {
@ -133,7 +135,7 @@ impl AuthenticateUser {
self.set_session(Some(Arc::new(Session {
user_id: session.user_id,
user_uuid: session.user_uuid,
user_workspace,
workspace_id: user_workspace.id,
})))
}

View file

@ -1,4 +1,4 @@
use crate::migrations::session_migration::migrate_session;
use crate::migrations::session_migration::{get_session_workspace, migrate_session};
use crate::services::data_import::importer::load_collab_by_object_ids;
use crate::services::db::UserDBPath;
@ -51,6 +51,7 @@ pub(crate) struct ImportedFolder {
pub container_name: Option<String>,
pub parent_view_id: Option<String>,
pub source: ImportedSource,
pub workspace_database_id: String,
}
#[derive(Clone)]
@ -81,6 +82,11 @@ pub(crate) fn prepare_import(
let user_paths = UserPaths::new(path.to_string());
let other_store_preferences = Arc::new(KVStorePreferences::new(path)?);
migrate_session("appflowy_session_cache", &other_store_preferences);
let session_workspace = get_session_workspace(&other_store_preferences)
.ok_or(anyhow!("Can't find the session workspace"))?;
let workspace_database_id = session_workspace.workspace_database_id;
let imported_session = other_store_preferences
.get_object::<Session>("appflowy_session_cache")
.ok_or(anyhow!(
@ -105,7 +111,7 @@ pub(crate) fn prepare_import(
let mut conn = imported_sqlite_db.get_connection()?;
let imported_user_auth_type = select_user_profile(
imported_session.user_id,
&imported_session.user_workspace.id,
&imported_session.workspace_id,
&mut conn,
)
.map(|v| v.auth_type)
@ -126,6 +132,7 @@ pub(crate) fn prepare_import(
container_name: None,
parent_view_id,
source: ImportedSource::ExternalFolder,
workspace_database_id,
})
}
@ -154,13 +161,13 @@ pub(crate) fn generate_import_data(
imported_folder: ImportedFolder,
) -> anyhow::Result<ImportedAppFlowyData> {
info!(
"[AppflowyData]:importing workspace: {}:{}",
imported_folder.imported_session.user_workspace.name,
imported_folder.imported_session.user_workspace.id,
"[AppflowyData]:importing workspace: {}",
imported_folder.imported_session.workspace_id,
);
let workspace_id = current_session.user_workspace.id.clone();
let imported_workspace_id = imported_folder.imported_session.user_workspace.id.clone();
let workspace_id = current_session.workspace_id.clone();
let imported_workspace_id = imported_folder.imported_session.workspace_id.clone();
let imported_session = imported_folder.imported_session.clone();
let imported_workspace_database_id = imported_folder.workspace_database_id.clone();
let imported_collab_db = imported_folder.imported_collab_db.clone();
let imported_container_view_name = imported_folder.container_name.clone();
@ -202,18 +209,18 @@ pub(crate) fn generate_import_data(
// 2. workspace database views
// 3. user awareness
// So we remove these object ids from the list
let user_workspace_id = &imported_session.user_workspace.id;
let workspace_database_id = &imported_session.user_workspace.workspace_database_id;
let user_workspace_id = &imported_session.workspace_id;
let user_awareness_id =
user_awareness_object_id(&imported_session.user_uuid, user_workspace_id).to_string();
all_imported_object_ids.retain(|id| {
id != user_workspace_id && id != workspace_database_id && id != &user_awareness_id
id != user_workspace_id && id != &imported_workspace_database_id && id != &user_awareness_id
});
// 2. mapping the workspace database ids
if let Err(err) = mapping_workspace_database_ids(
&mut old_to_new_id_map,
&imported_session,
&imported_workspace_database_id,
&imported_collab_db_read_txn,
&mut database_view_ids_by_database_id,
&mut database_object_ids,
@ -293,7 +300,7 @@ pub(crate) fn generate_import_data(
for view_id in not_exist_parent_view_ids {
if let Err(err) = create_empty_document_for_view(
current_session.user_id,
&current_session.user_workspace.id,
&current_session.workspace_id,
&view_id,
current_collab_db_write_txn,
) {
@ -489,7 +496,7 @@ where
write_collab_object(
&collab,
current_session.user_id,
current_session.user_workspace.id.as_str(),
current_session.workspace_id.as_str(),
import_container_view_id,
collab_write_txn,
CollabType::Document,
@ -499,7 +506,7 @@ where
let import_container_views = NestedChildViewBuilder::new(
current_session.user_id,
current_session.user_workspace.id.clone(),
current_session.workspace_id.clone(),
)
.with_view_id(import_container_view_id)
.with_layout(ViewLayout::Document)
@ -514,6 +521,7 @@ where
fn mapping_workspace_database_ids<'a, W>(
old_to_new_id_map: &mut OldToNewIdMap,
imported_session: &Session,
imported_session_workspace_database_id: &str,
imported_collab_db_read_txn: &W,
database_view_ids_by_database_id: &mut HashMap<String, Vec<String>>,
database_object_ids: &mut HashSet<String>,
@ -524,20 +532,20 @@ where
{
let mut workspace_database_collab = Collab::new(
imported_session.user_id,
&imported_session.user_workspace.workspace_database_id,
imported_session_workspace_database_id,
"import_device",
vec![],
false,
);
imported_collab_db_read_txn.load_doc_with_txn(
imported_session.user_id,
&imported_session.user_workspace.id,
&imported_session.user_workspace.workspace_database_id,
&imported_session.workspace_id,
imported_session_workspace_database_id,
&mut workspace_database_collab.transact_mut(),
)?;
let workspace_database = init_workspace_database(
&imported_session.user_workspace.workspace_database_id,
imported_session_workspace_database_id,
workspace_database_collab,
);
for database_meta_list in workspace_database.get_all_database_meta() {
@ -661,7 +669,7 @@ where
write_collab_object(
database_collab,
session.user_id,
session.user_workspace.id.as_str(),
&session.workspace_id,
&new_database_object_id,
collab_write_txn,
CollabType::Database,
@ -684,7 +692,7 @@ where
})
.collect::<Vec<_>>();
for gen_collab in gen_database_row_document_collabs {
write_gen_collab(&session.user_workspace.id, gen_collab, collab_write_txn);
write_gen_collab(&session.workspace_id, gen_collab, collab_write_txn);
}
// remove the database object ids from the object ids
@ -771,7 +779,7 @@ where
.collect::<Vec<_>>();
for gen_collab in gen_database_row_collabs {
write_gen_collab(&session.user_workspace.id, gen_collab, collab_write_txn);
write_gen_collab(&session.workspace_id, gen_collab, collab_write_txn);
}
}
@ -936,7 +944,7 @@ where
{
let mut imported_folder_collab = Collab::new(
imported_session.user_id,
&imported_session.user_workspace.id,
&imported_session.workspace_id,
"migrate_device",
vec![],
false,
@ -945,15 +953,15 @@ where
imported_collab_db_read_txn
.load_doc_with_txn(
imported_session.user_id,
&imported_session.user_workspace.id,
&imported_session.user_workspace.id,
&imported_session.workspace_id,
&imported_session.workspace_id,
&mut imported_folder_collab.transact_mut(),
)
.map_err(|err| {
PersistenceError::Internal(anyhow!(
"[AppflowyData]: Can't load the user:{} folder:{}. {}",
imported_session.user_id,
imported_session.user_workspace.id,
imported_session.workspace_id,
err
))
})?;
@ -964,7 +972,7 @@ where
})?;
let mut imported_folder_data = imported_folder
.get_folder_data(&imported_session.user_workspace.id)
.get_folder_data(&imported_session.workspace_id)
.ok_or(PersistenceError::Internal(anyhow!(
"[AppflowyData]: Can't read the folder data"
)))?;
@ -1005,7 +1013,7 @@ where
// replace the old parent view id of the workspace
old_to_new_id_map.0.insert(
imported_session.user_workspace.id.clone(),
imported_session.workspace_id.clone(),
root_view_id.to_string(),
);

View file

@ -128,11 +128,13 @@ impl UserManager {
if let Ok(session) = self.get_session() {
info!(
"Init user session: {}:{}, workspace: {}",
session.user_id, session.user_workspace.name, session.user_workspace.id
"Init user session: {}, workspace: {}",
session.user_id, session.workspace_id
);
let workspace_uuid = Uuid::parse_str(&session.workspace_id)?;
let mut conn = self.db_connection(session.user_id)?;
let auth_type = select_user_workspace_type(&session.workspace_id, &mut conn)?;
let auth_type = session.user_workspace.workspace_type;
let uid = session.user_id;
let token = self.token_from_auth_type(&auth_type)?;
self
@ -158,11 +160,11 @@ impl UserManager {
let weak_cloud_services = Arc::downgrade(&self.cloud_service);
let weak_authenticate_user = Arc::downgrade(&self.authenticate_user);
let weak_pool = Arc::downgrade(&self.db_pool(uid)?);
let workspace_id = session.workspace_id.clone();
let cloned_session = session.clone();
if let Some(mut token_state_rx) = self.cloud_service.subscribe_token_state() {
event!(tracing::Level::DEBUG, "Listen token state change");
let user_uid = uid;
let workspace_id = session.user_workspace.id.clone();
tokio::spawn(async move {
while let Some(token_state) = token_state_rx.next().await {
debug!("Token state changed: {:?}", token_state);
@ -260,7 +262,7 @@ impl UserManager {
.on_launch_if_authenticated(
uid,
&cloud_config,
&session.user_workspace,
&workspace_uuid,
&self.authenticate_user.user_config.device_id,
&auth_type,
)
@ -340,6 +342,7 @@ impl UserManager {
self.prepare_user(&session).await;
let latest_workspace = response.latest_workspace.clone();
let workspace_id = Uuid::parse_str(&latest_workspace.id)?;
let user_profile = UserProfile::from((&response, &auth_type));
self.save_auth_data(&response, auth_type, &session).await?;
@ -352,7 +355,7 @@ impl UserManager {
.await
.on_sign_in(
user_profile.uid,
&latest_workspace,
&workspace_id,
&self.authenticate_user.user_config.device_id,
&auth_type,
)
@ -404,6 +407,7 @@ impl UserManager {
.save_auth_data(&response, *auth_type, &new_session)
.await?;
let _ = self.initial_user_awareness(&new_session, auth_type).await;
let workspace_id = Uuid::parse_str(&new_session.workspace_id)?;
self
.user_status_callback
.read()
@ -411,7 +415,7 @@ impl UserManager {
.on_sign_up(
response.is_new_user,
new_user_profile,
&new_session.user_workspace,
&workspace_id,
&self.authenticate_user.user_config.device_id,
auth_type,
)
@ -493,7 +497,7 @@ impl UserManager {
let session = self.get_session()?;
upsert_user_profile_change(
session.user_id,
&session.user_workspace.id,
&session.workspace_id,
self.db_connection(session.user_id)?,
changeset,
)?;
@ -523,7 +527,7 @@ impl UserManager {
self
.authenticate_user
.database
.backup(session.user_id, &session.user_workspace.id);
.backup(session.user_id, &session.workspace_id);
}
/// Fetches the user profile for the given user ID.
@ -627,7 +631,7 @@ impl UserManager {
pub fn workspace_id(&self) -> Result<String, FlowyError> {
let session = self.get_session()?;
Ok(session.user_workspace.id.clone())
Ok(session.workspace_id.clone())
}
pub fn token(&self) -> Result<Option<String>, FlowyError> {
@ -758,7 +762,7 @@ impl UserManager {
// Save the user profile change
upsert_user_profile_change(
user_update.uid,
&session.user_workspace.id,
&session.workspace_id,
self.db_connection(user_update.uid)?,
UserTableChangeset::from(user_update),
)?;
@ -784,17 +788,6 @@ impl UserManager {
.await?;
}
// Save the old user workspace setting.
let mut conn = self
.authenticate_user
.database
.get_connection(old_user.session.user_id)?;
upsert_user_workspace(
old_user.session.user_id,
*auth_type,
old_user.session.user_workspace.clone(),
&mut conn,
)?;
Ok(())
}
}

View file

@ -20,7 +20,7 @@ impl UserManager {
let session = self.get_session().ok()?;
let user_profile = self
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
.get_user_profile_from_disk(session.user_id, &session.workspace_id)
.await
.ok()?;
@ -48,7 +48,7 @@ impl UserManager {
"Anon user not found",
))?;
let profile = self
.get_user_profile_from_disk(anon_session.user_id, &anon_session.user_workspace.id)
.get_user_profile_from_disk(anon_session.user_id, &anon_session.workspace_id)
.await?;
Ok(UserProfilePB::from(profile))
}

View file

@ -121,7 +121,7 @@ impl UserManager {
session: &Session,
auth_type: &AuthType,
) -> FlowyResult<()> {
let object_id = user_awareness_object_id(&session.user_uuid, &session.user_workspace.id);
let object_id = user_awareness_object_id(&session.user_uuid, &session.workspace_id);
// Try to acquire mutable access to `is_loading_awareness`.
// Thread-safety is ensured by DashMap
@ -162,7 +162,7 @@ impl UserManager {
auth_type
);
let collab_db = self.get_collab_db(session.user_id)?;
let workspace_id = session.user_workspace.workspace_id()?;
let workspace_id = Uuid::parse_str(&session.workspace_id)?;
let doc_state =
CollabPersistenceImpl::new(collab_db.clone(), session.user_id, workspace_id)
.into_data_source();
@ -227,7 +227,7 @@ impl UserManager {
}
};
let workspace_id = session.user_workspace.workspace_id()?;
let workspace_id = Uuid::parse_str(&session.workspace_id)?;
let create_awareness = if authenticator.is_local() {
let doc_state =
CollabPersistenceImpl::new(collab_db.clone(), session.user_id, workspace_id)
@ -289,7 +289,7 @@ impl UserManager {
Ok(new_user_awareness) => {
// Validate session before storing the awareness
if let Ok(current_session) = authenticate_user.get_session() {
if current_session.user_workspace.id == session.user_workspace.id {
if current_session.workspace_id == session.workspace_id {
if let Some(user_awareness) = user_awareness.upgrade() {
info!("User awareness initialized successfully");
user_awareness.store(Some(new_user_awareness));
@ -366,7 +366,7 @@ impl UserManager {
info!("User awareness is not loaded when trying to access it");
let session = self.get_session()?;
let object_id = user_awareness_object_id(&session.user_uuid, &session.user_workspace.id);
let object_id = user_awareness_object_id(&session.user_uuid, &session.workspace_id);
let is_loading = self
.is_loading_awareness
.get(&object_id)
@ -375,7 +375,7 @@ impl UserManager {
if !is_loading {
let user_profile = self
.get_user_profile_from_disk(session.user_id, &session.user_workspace.id)
.get_user_profile_from_disk(session.user_id, &session.workspace_id)
.await?;
self
.initial_user_awareness(&session, &user_profile.workspace_auth_type)

View file

@ -101,7 +101,7 @@ impl UserManager {
collab_data: ImportedCollabData,
) -> Result<(), FlowyError> {
let user = self
.get_user_profile_from_disk(current_session.user_id, &current_session.user_workspace.id)
.get_user_profile_from_disk(current_session.user_id, &current_session.workspace_id)
.await?;
let user_collab_db = self
.get_collab_db(current_session.user_id)?
@ -109,12 +109,13 @@ impl UserManager {
.ok_or_else(|| FlowyError::internal().with_context("Collab db not found"))?;
let user_id = current_session.user_id;
let workspace_id = Uuid::parse_str(&current_session.workspace_id)?;
let weak_user_collab_db = Arc::downgrade(&user_collab_db);
let weak_user_cloud_service = self.cloud_service.get_user_service()?;
match upload_collab_objects_data(
user_id,
weak_user_collab_db,
&current_session.user_workspace.workspace_id()?,
&workspace_id,
&user.workspace_auth_type,
collab_data,
weak_user_cloud_service,
@ -146,6 +147,7 @@ impl UserManager {
container_name: None,
parent_view_id: None,
source: ImportedSource::AnonUser,
workspace_database_id: "".to_string(),
};
self.perform_import(import_context).await?;
Ok(())