diff --git a/rust-lib/flowy-user/Cargo.toml b/rust-lib/flowy-user/Cargo.toml index 44dffac38f..db0be3480b 100644 --- a/rust-lib/flowy-user/Cargo.toml +++ b/rust-lib/flowy-user/Cargo.toml @@ -26,6 +26,10 @@ lazy_static = "1.4.0" fancy-regex = "0.5.0" diesel = {version = "1.4.7", features = ["sqlite"]} diesel_derives = {version = "1.4.1", features = ["sqlite"]} +thread_local = "1.1.3" +thread-id = "3.3.0" +once_cell = "1.7.2" +parking_lot = "0.11" [dev-dependencies] quickcheck = "0.9.2" diff --git a/rust-lib/flowy-user/src/services/user_session/database.rs b/rust-lib/flowy-user/src/services/user_session/database.rs index 135937459b..77b621c6d5 100644 --- a/rust-lib/flowy-user/src/services/user_session/database.rs +++ b/rust-lib/flowy-user/src/services/user_session/database.rs @@ -1,8 +1,11 @@ use crate::errors::{ErrorBuilder, UserError, UserErrorCode}; use flowy_database::{DBConnection, Database}; use lazy_static::lazy_static; +use once_cell::sync::Lazy; +use parking_lot::Mutex; use std::{ cell::RefCell, + collections::HashMap, sync::{ atomic::{AtomicBool, Ordering}, RwLock, @@ -25,7 +28,8 @@ impl UserDB { } fn open_user_db(&self, user_id: &str) -> Result<(), UserError> { - INIT_FLAG.store(true, Ordering::SeqCst); + set_user_db_init(true, user_id); + let dir = format!("{}/{}", self.db_dir, user_id); let db = flowy_database::init(&dir).map_err(|e| { ErrorBuilder::new(UserErrorCode::DatabaseInitFailed) @@ -33,58 +37,41 @@ impl UserDB { .build() })?; - let mut user_db = DB.write().map_err(|e| { + let mut db_map = DB_MAP.write().map_err(|e| { ErrorBuilder::new(UserErrorCode::DatabaseWriteLocked) .error(e) .build() })?; - *(user_db) = Some(db); - set_user_id(Some(user_id.to_owned())); + db_map.insert(user_id.to_owned(), db); Ok(()) } - pub(crate) fn close_user_db(&self) -> Result<(), UserError> { - INIT_FLAG.store(false, Ordering::SeqCst); + pub(crate) fn close_user_db(&self, user_id: &str) -> Result<(), UserError> { + set_user_db_init(false, user_id); - let mut write_guard = DB.write().map_err(|e| { + let mut db_map = DB_MAP.write().map_err(|e| { ErrorBuilder::new(UserErrorCode::DatabaseWriteLocked) .msg(format!("Close user db failed. {:?}", e)) .build() })?; - *write_guard = None; - set_user_id(None); - + db_map.remove(user_id); Ok(()) } pub(crate) fn get_connection(&self, user_id: &str) -> Result { - if !INIT_FLAG.load(Ordering::SeqCst) { + if !is_user_db_init(user_id) { let _ = self.open_user_db(user_id); } - let thread_user_id = get_user_id(); - if thread_user_id.is_some() { - if thread_user_id != Some(user_id.to_owned()) { - let msg = format!( - "Database owner does not match. origin: {:?}, current: {}", - thread_user_id, user_id - ); - log::error!("{}", msg); - - return Err(ErrorBuilder::new(UserErrorCode::DatabaseUserDidNotMatch) - .msg(msg) - .build()); - } - } - - let read_guard = DB.read().map_err(|e| { + let db_map = DB_MAP.read().map_err(|e| { ErrorBuilder::new(UserErrorCode::DatabaseReadLocked) .error(e) .build() })?; - match read_guard.as_ref() { + + match db_map.get(user_id) { None => Err(ErrorBuilder::new(UserErrorCode::DatabaseInitFailed) .msg("Database is not initialization") .build()), @@ -93,17 +80,24 @@ impl UserDB { } } -thread_local! { - static USER_ID: RefCell> = RefCell::new(None); +lazy_static! { + static ref DB_MAP: RwLock> = RwLock::new(HashMap::new()); } -fn set_user_id(user_id: Option) { - USER_ID.with(|id| { - *id.borrow_mut() = user_id; - }); -} -fn get_user_id() -> Option { USER_ID.with(|id| id.borrow().clone()) } -static INIT_FLAG: AtomicBool = AtomicBool::new(false); +static INIT_FLAG_MAP: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); +fn set_user_db_init(is_init: bool, user_id: &str) { + INIT_FLAG_MAP + .lock() + .entry(user_id.to_owned()) + .or_insert_with(|| is_init); +} + +fn is_user_db_init(user_id: &str) -> bool { + match INIT_FLAG_MAP.lock().get(user_id) { + None => false, + Some(flag) => flag.clone(), + } +} #[cfg(test)] mod tests { diff --git a/rust-lib/flowy-user/src/services/user_session/user_session.rs b/rust-lib/flowy-user/src/services/user_session/user_session.rs index 4437f804f2..07b4898f8a 100644 --- a/rust-lib/flowy-user/src/services/user_session/user_session.rs +++ b/rust-lib/flowy-user/src/services/user_session/user_session.rs @@ -75,7 +75,7 @@ impl UserSession { Ok(_) => {}, Err(_) => {}, } - let _ = self.database.close_user_db()?; + let _ = self.database.close_user_db(&user_id)?; let _ = set_current_user_id(None)?; Ok(()) diff --git a/rust-lib/flowy-user/tests/event/sign_in_test.rs b/rust-lib/flowy-user/tests/event/sign_in_test.rs index eaacf915de..d29ff80df4 100644 --- a/rust-lib/flowy-user/tests/event/sign_in_test.rs +++ b/rust-lib/flowy-user/tests/event/sign_in_test.rs @@ -20,6 +20,7 @@ fn sign_in_success() { } #[test] +#[serial] fn sign_in_with_invalid_email() { for email in invalid_email_test_case() { let request = SignInRequest { @@ -40,6 +41,7 @@ fn sign_in_with_invalid_email() { } #[test] +#[serial] fn sign_in_with_invalid_password() { for password in invalid_password_test_case() { let request = SignInRequest { @@ -58,3 +60,233 @@ fn sign_in_with_invalid_password() { ); } } + +#[test] +#[serial] +fn sign_up_success() { + let _ = UserTestBuilder::new().event(SignOut).sync_send(); + let request = SignUpRequest { + email: random_valid_email(), + name: valid_name(), + password: valid_password(), + }; + + let _response = UserTestBuilder::new() + .logout() + .event(SignUp) + .request(request) + .sync_send(); +} + +#[test] +#[serial] +fn sign_up_with_invalid_email() { + for email in invalid_email_test_case() { + let request = SignUpRequest { + email: email.to_string(), + name: valid_name(), + password: valid_password(), + }; + + assert_eq!( + UserTestBuilder::new() + .event(SignUp) + .request(request) + .sync_send() + .error() + .code, + UserErrorCode::EmailInvalid + ); + } +} +#[test] +#[serial] +fn sign_up_with_invalid_password() { + for password in invalid_password_test_case() { + let request = SignUpRequest { + email: random_valid_email(), + name: valid_name(), + password, + }; + + assert_eq!( + UserTestBuilder::new() + .event(SignUp) + .request(request) + .sync_send() + .error() + .code, + UserErrorCode::PasswordInvalid + ); + } +} + +#[test] +#[should_panic] +#[serial] +fn user_status_get_failed_before_login() { + let _ = UserTestBuilder::new() + .logout() + .event(GetStatus) + .sync_send() + .parse::(); +} + +#[test] +#[serial] +fn user_status_get_success_after_login() { + let request = SignInRequest { + email: random_valid_email(), + password: valid_password(), + }; + + let response = UserTestBuilder::new() + .logout() + .event(SignIn) + .request(request) + .sync_send() + .parse::(); + dbg!(&response); + + let _ = UserTestBuilder::new() + .event(GetStatus) + .sync_send() + .parse::(); +} + +#[test] +#[serial] +fn user_update_with_name() { + let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); + let new_name = "hello_world".to_owned(); + let request = UpdateUserRequest { + id: user_detail.id.clone(), + name: Some(new_name.clone()), + email: None, + workspace: None, + password: None, + }; + + let user_detail = UserTestBuilder::new() + .event(UpdateUser) + .request(request) + .sync_send() + .parse::(); + + assert_eq!(user_detail.name, new_name,); +} + +#[test] +#[serial] +fn user_update_with_email() { + let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); + let new_email = "123@gmai.com".to_owned(); + let request = UpdateUserRequest { + id: user_detail.id.clone(), + name: None, + email: Some(new_email.clone()), + workspace: None, + password: None, + }; + + let user_detail = UserTestBuilder::new() + .event(UpdateUser) + .request(request) + .sync_send() + .parse::(); + + assert_eq!(user_detail.email, new_email,); +} + +#[test] +#[serial] +fn user_update_with_password() { + let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); + let new_password = "H123world!".to_owned(); + let request = UpdateUserRequest { + id: user_detail.id.clone(), + name: None, + email: None, + workspace: None, + password: Some(new_password.clone()), + }; + + let _ = UserTestBuilder::new() + .event(UpdateUser) + .request(request) + .sync_send() + .assert_success(); +} + +#[test] +#[serial] +fn user_update_with_invalid_email() { + let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); + for email in invalid_email_test_case() { + let request = UpdateUserRequest { + id: user_detail.id.clone(), + name: None, + email: Some(email), + workspace: None, + password: None, + }; + + assert_eq!( + UserTestBuilder::new() + .event(UpdateUser) + .request(request) + .sync_send() + .error() + .code, + UserErrorCode::EmailInvalid + ); + } +} + +#[test] +#[serial] +fn user_update_with_invalid_password() { + let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); + for password in invalid_password_test_case() { + let request = UpdateUserRequest { + id: user_detail.id.clone(), + name: None, + email: None, + workspace: None, + password: Some(password), + }; + + assert_eq!( + UserTestBuilder::new() + .event(UpdateUser) + .request(request) + .sync_send() + .error() + .code, + UserErrorCode::PasswordInvalid + ); + } +} + +#[test] +#[serial] +fn user_update_with_invalid_name() { + let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); + let request = UpdateUserRequest { + id: user_detail.id.clone(), + name: Some("".to_string()), + email: None, + workspace: None, + password: None, + }; + + assert_eq!( + UserTestBuilder::new() + .event(UpdateUser) + .request(request) + .sync_send() + .error() + .code, + UserErrorCode::UserNameInvalid + ); +} diff --git a/rust-lib/flowy-user/tests/event/sign_up_test.rs b/rust-lib/flowy-user/tests/event/sign_up_test.rs index 7c9ed4d9e4..e023d80bf7 100644 --- a/rust-lib/flowy-user/tests/event/sign_up_test.rs +++ b/rust-lib/flowy-user/tests/event/sign_up_test.rs @@ -1,62 +1,65 @@ -use crate::helper::*; -use flowy_user::{errors::*, event::UserEvent::*, prelude::*}; -use serial_test::*; - -#[test] -#[serial] -fn sign_up_success() { - let _ = UserTestBuilder::new().event(SignOut).sync_send(); - let request = SignUpRequest { - email: random_valid_email(), - name: valid_name(), - password: valid_password(), - }; - - let _response = UserTestBuilder::new() - .event(SignUp) - .request(request) - .sync_send(); - // .parse::(); - // dbg!(&response); -} - -#[test] -fn sign_up_with_invalid_email() { - for email in invalid_email_test_case() { - let request = SignUpRequest { - email: email.to_string(), - name: valid_name(), - password: valid_password(), - }; - - assert_eq!( - UserTestBuilder::new() - .event(SignUp) - .request(request) - .sync_send() - .error() - .code, - UserErrorCode::EmailInvalid - ); - } -} -#[test] -fn sign_up_with_invalid_password() { - for password in invalid_password_test_case() { - let request = SignUpRequest { - email: random_valid_email(), - name: valid_name(), - password, - }; - - assert_eq!( - UserTestBuilder::new() - .event(SignUp) - .request(request) - .sync_send() - .error() - .code, - UserErrorCode::PasswordInvalid - ); - } -} +// use crate::helper::*; +// use flowy_user::{errors::*, event::UserEvent::*, prelude::*}; +// use serial_test::*; +// +// #[test] +// #[serial] +// fn sign_up_success() { +// let _ = UserTestBuilder::new().event(SignOut).sync_send(); +// let request = SignUpRequest { +// email: random_valid_email(), +// name: valid_name(), +// password: valid_password(), +// }; +// +// let _response = UserTestBuilder::new() +// .logout() +// .event(SignUp) +// .request(request) +// .sync_send(); +// // .parse::(); +// // dbg!(&response); +// } +// +// #[test] +// #[serial] +// fn sign_up_with_invalid_email() { +// for email in invalid_email_test_case() { +// let request = SignUpRequest { +// email: email.to_string(), +// name: valid_name(), +// password: valid_password(), +// }; +// +// assert_eq!( +// UserTestBuilder::new() +// .event(SignUp) +// .request(request) +// .sync_send() +// .error() +// .code, +// UserErrorCode::EmailInvalid +// ); +// } +// } +// #[test] +// #[serial] +// fn sign_up_with_invalid_password() { +// for password in invalid_password_test_case() { +// let request = SignUpRequest { +// email: random_valid_email(), +// name: valid_name(), +// password, +// }; +// +// assert_eq!( +// UserTestBuilder::new() +// .event(SignUp) +// .request(request) +// .sync_send() +// .error() +// .code, +// UserErrorCode::PasswordInvalid +// ); +// } +// } diff --git a/rust-lib/flowy-user/tests/event/user_status_test.rs b/rust-lib/flowy-user/tests/event/user_status_test.rs index ee5e56345d..a6350b1fc7 100644 --- a/rust-lib/flowy-user/tests/event/user_status_test.rs +++ b/rust-lib/flowy-user/tests/event/user_status_test.rs @@ -1,36 +1,36 @@ -use crate::helper::*; -use flowy_user::{event::UserEvent::*, prelude::*}; -use serial_test::*; - -#[test] -#[should_panic] -#[serial] -fn user_status_get_failed_before_login() { - let _ = UserTestBuilder::new() - .logout() - .event(GetStatus) - .sync_send() - .parse::(); -} - -#[test] -#[serial] -fn user_status_get_success_after_login() { - let request = SignInRequest { - email: random_valid_email(), - password: valid_password(), - }; - - let response = UserTestBuilder::new() - .logout() - .event(SignIn) - .request(request) - .sync_send() - .parse::(); - dbg!(&response); - - let _ = UserTestBuilder::new() - .event(GetStatus) - .sync_send() - .parse::(); -} +// use crate::helper::*; +// use flowy_user::{event::UserEvent::*, prelude::*}; +// use serial_test::*; +// +// #[test] +// #[should_panic] +// #[serial] +// fn user_status_get_failed_before_login() { +// let _ = UserTestBuilder::new() +// .logout() +// .event(GetStatus) +// .sync_send() +// .parse::(); +// } +// +// #[test] +// #[serial] +// fn user_status_get_success_after_login() { +// let request = SignInRequest { +// email: random_valid_email(), +// password: valid_password(), +// }; +// +// let response = UserTestBuilder::new() +// .logout() +// .event(SignIn) +// .request(request) +// .sync_send() +// .parse::(); +// dbg!(&response); +// +// let _ = UserTestBuilder::new() +// .event(GetStatus) +// .sync_send() +// .parse::(); +// } diff --git a/rust-lib/flowy-user/tests/event/user_update_test.rs b/rust-lib/flowy-user/tests/event/user_update_test.rs index cd113e4fbf..7dc83bfca4 100644 --- a/rust-lib/flowy-user/tests/event/user_update_test.rs +++ b/rust-lib/flowy-user/tests/event/user_update_test.rs @@ -1,140 +1,140 @@ -use crate::helper::*; -use flowy_user::{errors::UserErrorCode, event::UserEvent::*, prelude::*}; -use serial_test::*; - -#[test] -#[serial] -fn user_update_with_name() { - let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); - let new_name = "hello_world".to_owned(); - let request = UpdateUserRequest { - id: user_detail.id.clone(), - name: Some(new_name.clone()), - email: None, - workspace: None, - password: None, - }; - - let user_detail = UserTestBuilder::new() - .event(UpdateUser) - .request(request) - .sync_send() - .parse::(); - - assert_eq!(user_detail.name, new_name,); -} - -#[test] -#[serial] -fn user_update_with_email() { - let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); - let new_email = "123@gmai.com".to_owned(); - let request = UpdateUserRequest { - id: user_detail.id.clone(), - name: None, - email: Some(new_email.clone()), - workspace: None, - password: None, - }; - - let user_detail = UserTestBuilder::new() - .event(UpdateUser) - .request(request) - .sync_send() - .parse::(); - - assert_eq!(user_detail.email, new_email,); -} - -#[test] -#[serial] -fn user_update_with_password() { - let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); - let new_password = "H123world!".to_owned(); - let request = UpdateUserRequest { - id: user_detail.id.clone(), - name: None, - email: None, - workspace: None, - password: Some(new_password.clone()), - }; - - let _ = UserTestBuilder::new() - .event(UpdateUser) - .request(request) - .sync_send() - .assert_success(); -} - -#[test] -#[serial] -fn user_update_with_invalid_email() { - let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); - for email in invalid_email_test_case() { - let request = UpdateUserRequest { - id: user_detail.id.clone(), - name: None, - email: Some(email), - workspace: None, - password: None, - }; - - assert_eq!( - UserTestBuilder::new() - .event(UpdateUser) - .request(request) - .sync_send() - .error() - .code, - UserErrorCode::EmailInvalid - ); - } -} - -#[test] -#[serial] -fn user_update_with_invalid_password() { - let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); - for password in invalid_password_test_case() { - let request = UpdateUserRequest { - id: user_detail.id.clone(), - name: None, - email: None, - workspace: None, - password: Some(password), - }; - - assert_eq!( - UserTestBuilder::new() - .event(UpdateUser) - .request(request) - .sync_send() - .error() - .code, - UserErrorCode::PasswordInvalid - ); - } -} - -#[test] -#[serial] -fn user_update_with_invalid_name() { - let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); - let request = UpdateUserRequest { - id: user_detail.id.clone(), - name: Some("".to_string()), - email: None, - workspace: None, - password: None, - }; - - assert_eq!( - UserTestBuilder::new() - .event(UpdateUser) - .request(request) - .sync_send() - .error() - .code, - UserErrorCode::UserNameInvalid - ); -} +// use crate::helper::*; +// use flowy_user::{errors::UserErrorCode, event::UserEvent::*, prelude::*}; +// use serial_test::*; +// +// #[test] +// #[serial] +// fn user_update_with_name() { +// let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); +// let new_name = "hello_world".to_owned(); +// let request = UpdateUserRequest { +// id: user_detail.id.clone(), +// name: Some(new_name.clone()), +// email: None, +// workspace: None, +// password: None, +// }; +// +// let user_detail = UserTestBuilder::new() +// .event(UpdateUser) +// .request(request) +// .sync_send() +// .parse::(); +// +// assert_eq!(user_detail.name, new_name,); +// } +// +// #[test] +// #[serial] +// fn user_update_with_email() { +// let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); +// let new_email = "123@gmai.com".to_owned(); +// let request = UpdateUserRequest { +// id: user_detail.id.clone(), +// name: None, +// email: Some(new_email.clone()), +// workspace: None, +// password: None, +// }; +// +// let user_detail = UserTestBuilder::new() +// .event(UpdateUser) +// .request(request) +// .sync_send() +// .parse::(); +// +// assert_eq!(user_detail.email, new_email,); +// } +// +// #[test] +// #[serial] +// fn user_update_with_password() { +// let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); +// let new_password = "H123world!".to_owned(); +// let request = UpdateUserRequest { +// id: user_detail.id.clone(), +// name: None, +// email: None, +// workspace: None, +// password: Some(new_password.clone()), +// }; +// +// let _ = UserTestBuilder::new() +// .event(UpdateUser) +// .request(request) +// .sync_send() +// .assert_success(); +// } +// +// #[test] +// #[serial] +// fn user_update_with_invalid_email() { +// let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); +// for email in invalid_email_test_case() { +// let request = UpdateUserRequest { +// id: user_detail.id.clone(), +// name: None, +// email: Some(email), +// workspace: None, +// password: None, +// }; +// +// assert_eq!( +// UserTestBuilder::new() +// .event(UpdateUser) +// .request(request) +// .sync_send() +// .error() +// .code, +// UserErrorCode::EmailInvalid +// ); +// } +// } +// +// #[test] +// #[serial] +// fn user_update_with_invalid_password() { +// let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); +// for password in invalid_password_test_case() { +// let request = UpdateUserRequest { +// id: user_detail.id.clone(), +// name: None, +// email: None, +// workspace: None, +// password: Some(password), +// }; +// +// assert_eq!( +// UserTestBuilder::new() +// .event(UpdateUser) +// .request(request) +// .sync_send() +// .error() +// .code, +// UserErrorCode::PasswordInvalid +// ); +// } +// } +// +// #[test] +// #[serial] +// fn user_update_with_invalid_name() { +// let user_detail = UserTestBuilder::new().login().user_detail.unwrap(); +// let request = UpdateUserRequest { +// id: user_detail.id.clone(), +// name: Some("".to_string()), +// email: None, +// workspace: None, +// password: None, +// }; +// +// assert_eq!( +// UserTestBuilder::new() +// .event(UpdateUser) +// .request(request) +// .sync_send() +// .error() +// .code, +// UserErrorCode::UserNameInvalid +// ); +// }