AppFlowy/frontend/rust-lib/flowy-user/src/services/database.rs

153 lines
4.4 KiB
Rust
Raw Normal View History

2022-07-19 06:40:56 +00:00
use crate::entities::{SignInResponse, SignUpResponse, UpdateUserProfileParams, UserProfilePB};
2022-03-19 08:23:34 +00:00
use flowy_database::ConnectionPool;
2022-01-11 05:34:45 +00:00
use flowy_database::{schema::user_table, DBConnection, Database};
2022-03-19 08:23:34 +00:00
use flowy_error::{ErrorCode, FlowyError};
2021-07-09 15:31:44 +00:00
use lazy_static::lazy_static;
use parking_lot::RwLock;
use std::path::PathBuf;
use std::{collections::HashMap, sync::Arc, time::Duration};
2022-01-11 05:34:45 +00:00
pub struct UserDB {
2021-07-09 15:31:44 +00:00
db_dir: String,
}
impl UserDB {
pub fn new(db_dir: &str) -> Self {
Self {
db_dir: db_dir.to_owned(),
}
}
2021-07-09 15:31:44 +00:00
feat: Customize the storage folder path (#1538) * feat: support customize folder path * feat: add l10n and optimize the logic * chore: code refactor * feat: add file read/write permission for macOS * fix: add toast for restoring path * feat: fetch apps and show them * feat: fetch apps and show them * feat: implement select document logic * feat: l10n and add select item callback * feat: add space between tile * chore: move file exporter to settings * chore: update UI * feat: support customizing folder when launching the app * feat: auto register after customizing folder * feat: l10n * feat: l10n * chore: reinitialize flowy sdk when calling init_sdk * chore: remove flowysdk const keyword to make sure it can be rebuild * chore: clear kv values when user logout * chore: replace current workspace id key in kv.db * feat: add config.name as a part of seesion_cache_key * feat: support open folder when launching * chore: fix some bugs * chore: dart fix & flutter analyze * chore: wrap 'sign up with ramdom user' as interface * feat: dismiss settings view after changing the folder * fix: read kv value after initializaing with new path * chore: remove user_id prefix from current workspace key * fix: move open latest view action to bloc * test: add test utils for integration tests * chore: move integration_test to its parent directory * test: add integration_test ci * test: switch to B from A, then switch to A again * chore: fix warings and format code and fix tests * chore: remove comment out codes * chore: rename some properties name and optimize the logic * chore: abstract logic of settings file exporter widget to cubit * chore: abstract location customizer view from file system view * chore: abstract settings page index to enum type * chore: remove the redundant underscore * test: fix integration test error * chore: enable integration test for windows and ubuntu * feat: abstract file picker as service and mock it under integration test * chore: fix bloc test Co-authored-by: nathan <nathan@appflowy.io>
2022-12-20 03:14:42 +00:00
#[tracing::instrument(level = "trace", skip(self))]
fn open_user_db_if_need(&self, user_id: &str) -> Result<Arc<ConnectionPool>, FlowyError> {
if user_id.is_empty() {
2022-03-19 08:23:34 +00:00
return Err(ErrorCode::UserIdIsEmpty.into());
}
2021-07-18 01:03:21 +00:00
if let Some(database) = DB_MAP.read().get(user_id) {
return Ok(database.get_pool());
}
let mut write_guard = DB_MAP.write();
// The Write guard acquire exclusive access that will guarantee the user db only initialize once.
match write_guard.get(user_id) {
None => {}
Some(database) => return Ok(database.get_pool()),
}
let mut dir = PathBuf::new();
dir.push(&self.db_dir);
dir.push(user_id);
let dir = dir.to_str().unwrap().to_owned();
2022-08-15 14:40:54 +00:00
tracing::trace!("open user db {} at path: {}", user_id, dir);
let db = flowy_database::init(&dir).map_err(|e| {
log::error!("open user: {} db failed, {:?}", user_id, e);
2021-12-14 10:04:51 +00:00
FlowyError::internal().context(e)
})?;
let pool = db.get_pool();
write_guard.insert(user_id.to_owned(), db);
drop(write_guard);
Ok(pool)
2021-07-09 15:31:44 +00:00
}
2021-12-14 10:04:51 +00:00
pub(crate) fn close_user_db(&self, user_id: &str) -> Result<(), FlowyError> {
match DB_MAP.try_write_for(Duration::from_millis(300)) {
2021-12-14 10:04:51 +00:00
None => Err(FlowyError::internal().context("Acquire write lock to close user db failed")),
Some(mut write_guard) => {
write_guard.remove(user_id);
Ok(())
2022-01-23 04:14:00 +00:00
}
}
2021-07-09 15:31:44 +00:00
}
2021-12-14 10:04:51 +00:00
pub(crate) fn get_connection(&self, user_id: &str) -> Result<DBConnection, FlowyError> {
2021-08-31 15:01:46 +00:00
let conn = self.get_pool(user_id)?.get()?;
Ok(conn)
}
2021-12-14 10:04:51 +00:00
pub(crate) fn get_pool(&self, user_id: &str) -> Result<Arc<ConnectionPool>, FlowyError> {
let pool = self.open_user_db_if_need(user_id)?;
Ok(pool)
2021-07-09 15:31:44 +00:00
}
}
2021-07-18 01:03:21 +00:00
lazy_static! {
static ref DB_MAP: RwLock<HashMap<String, Database>> = RwLock::new(HashMap::new());
}
2021-07-18 01:03:21 +00:00
2022-01-11 05:34:45 +00:00
#[derive(Clone, Default, Queryable, Identifiable, Insertable)]
#[table_name = "user_table"]
pub struct UserTable {
pub(crate) id: String,
pub(crate) name: String,
pub(crate) token: String,
pub(crate) email: String,
pub(crate) workspace: String, // deprecated
2022-08-08 14:19:05 +00:00
pub(crate) icon_url: String,
2022-01-11 05:34:45 +00:00
}
impl UserTable {
pub fn new(id: String, name: String, email: String, token: String) -> Self {
Self {
id,
name,
email,
token,
2022-08-08 14:19:05 +00:00
icon_url: "".to_owned(),
2022-01-11 05:34:45 +00:00
workspace: "".to_owned(),
}
}
pub fn set_workspace(mut self, workspace: String) -> Self {
self.workspace = workspace;
self
}
}
impl std::convert::From<SignUpResponse> for UserTable {
2022-01-24 09:35:58 +00:00
fn from(resp: SignUpResponse) -> Self {
UserTable::new(resp.user_id, resp.name, resp.email, resp.token)
}
2022-01-11 05:34:45 +00:00
}
impl std::convert::From<SignInResponse> for UserTable {
2022-01-24 09:35:58 +00:00
fn from(resp: SignInResponse) -> Self {
UserTable::new(resp.user_id, resp.name, resp.email, resp.token)
}
2022-01-11 05:34:45 +00:00
}
2022-07-19 06:40:56 +00:00
impl std::convert::From<UserTable> for UserProfilePB {
2022-01-11 05:34:45 +00:00
fn from(table: UserTable) -> Self {
2022-07-19 06:40:56 +00:00
UserProfilePB {
2022-01-11 05:34:45 +00:00
id: table.id,
email: table.email,
name: table.name,
token: table.token,
2022-08-08 14:19:05 +00:00
icon_url: table.icon_url,
2022-01-11 05:34:45 +00:00
}
}
}
2022-01-11 05:34:45 +00:00
#[derive(AsChangeset, Identifiable, Default, Debug)]
#[table_name = "user_table"]
pub struct UserTableChangeset {
pub id: String,
pub workspace: Option<String>, // deprecated
pub name: Option<String>,
pub email: Option<String>,
2022-08-08 14:19:05 +00:00
pub icon_url: Option<String>,
2022-01-11 05:34:45 +00:00
}
impl UserTableChangeset {
pub fn new(params: UpdateUserProfileParams) -> Self {
2022-01-11 05:34:45 +00:00
UserTableChangeset {
id: params.id,
workspace: None,
name: params.name,
email: params.email,
2022-08-08 14:19:05 +00:00
icon_url: params.icon_url,
2022-01-11 05:34:45 +00:00
}
2021-07-09 15:31:44 +00:00
}
}