support multi user db initialize

This commit is contained in:
appflowy 2021-07-18 09:03:21 +08:00
parent 848975e7f4
commit a63d5f5eaf
7 changed files with 509 additions and 276 deletions

View File

@ -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"

View File

@ -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<DBConnection, UserError> {
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<Option<String>> = RefCell::new(None);
lazy_static! {
static ref DB_MAP: RwLock<HashMap<String, Database>> = RwLock::new(HashMap::new());
}
fn set_user_id(user_id: Option<String>) {
USER_ID.with(|id| {
*id.borrow_mut() = user_id;
});
}
fn get_user_id() -> Option<String> { USER_ID.with(|id| id.borrow().clone()) }
static INIT_FLAG: AtomicBool = AtomicBool::new(false);
static INIT_FLAG_MAP: Lazy<Mutex<HashMap<String, bool>>> = 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 {

View File

@ -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(())

View File

@ -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::<UserDetail>();
}
#[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::<UserDetail>();
dbg!(&response);
let _ = UserTestBuilder::new()
.event(GetStatus)
.sync_send()
.parse::<UserDetail>();
}
#[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::<UserDetail>();
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::<UserDetail>();
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
);
}

View File

@ -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::<SignUpResponse>();
// 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::<SignUpResponse>();
// // 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
// );
// }
// }

View File

@ -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::<UserDetail>();
}
#[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::<UserDetail>();
dbg!(&response);
let _ = UserTestBuilder::new()
.event(GetStatus)
.sync_send()
.parse::<UserDetail>();
}
// 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::<UserDetail>();
// }
//
// #[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::<UserDetail>();
// dbg!(&response);
//
// let _ = UserTestBuilder::new()
// .event(GetStatus)
// .sync_send()
// .parse::<UserDetail>();
// }

View File

@ -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::<UserDetail>();
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::<UserDetail>();
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::<UserDetail>();
//
// 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::<UserDetail>();
//
// 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
// );
// }