feat: run rustfmt with custom defined fmt configuration (#1848)

* chore: update rustfmt

* chore: apply rustfmt format
This commit is contained in:
Nathan.fooo
2023-02-13 09:29:49 +08:00
committed by GitHub
parent e2496e734c
commit 6bb1c4e89c
459 changed files with 50554 additions and 46600 deletions

View File

@ -1,10 +1,10 @@
fn main() {
let crate_name = env!("CARGO_PKG_NAME");
flowy_codegen::protobuf_file::gen(crate_name);
let crate_name = env!("CARGO_PKG_NAME");
flowy_codegen::protobuf_file::gen(crate_name);
#[cfg(feature = "dart")]
flowy_codegen::dart_event::gen(crate_name);
#[cfg(feature = "dart")]
flowy_codegen::dart_event::gen(crate_name);
#[cfg(feature = "ts")]
flowy_codegen::ts_event::gen(crate_name);
#[cfg(feature = "ts")]
flowy_codegen::ts_event::gen(crate_name);
}

View File

@ -5,54 +5,54 @@ use user_model::{SignInParams, SignUpParams, UserEmail, UserName, UserPassword};
#[derive(ProtoBuf, Default)]
pub struct SignInPayloadPB {
#[pb(index = 1)]
pub email: String,
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub password: String,
#[pb(index = 2)]
pub password: String,
#[pb(index = 3)]
pub name: String,
#[pb(index = 3)]
pub name: String,
}
impl TryInto<SignInParams> for SignInPayloadPB {
type Error = ErrorCode;
type Error = ErrorCode;
fn try_into(self) -> Result<SignInParams, Self::Error> {
let email = UserEmail::parse(self.email)?;
let password = UserPassword::parse(self.password)?;
fn try_into(self) -> Result<SignInParams, Self::Error> {
let email = UserEmail::parse(self.email)?;
let password = UserPassword::parse(self.password)?;
Ok(SignInParams {
email: email.0,
password: password.0,
name: self.name,
})
}
Ok(SignInParams {
email: email.0,
password: password.0,
name: self.name,
})
}
}
#[derive(ProtoBuf, Default)]
pub struct SignUpPayloadPB {
#[pb(index = 1)]
pub email: String,
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub name: String,
#[pb(index = 2)]
pub name: String,
#[pb(index = 3)]
pub password: String,
#[pb(index = 3)]
pub password: String,
}
impl TryInto<SignUpParams> for SignUpPayloadPB {
type Error = ErrorCode;
type Error = ErrorCode;
fn try_into(self) -> Result<SignUpParams, Self::Error> {
let email = UserEmail::parse(self.email)?;
let password = UserPassword::parse(self.password)?;
let name = UserName::parse(self.name)?;
fn try_into(self) -> Result<SignUpParams, Self::Error> {
let email = UserEmail::parse(self.email)?;
let password = UserPassword::parse(self.password)?;
let name = UserName::parse(self.name)?;
Ok(SignUpParams {
email: email.0,
name: name.0,
password: password.0,
})
}
Ok(SignUpParams {
email: email.0,
name: name.0,
password: password.0,
})
}
}

View File

@ -1,129 +1,131 @@
use crate::errors::ErrorCode;
use flowy_derive::ProtoBuf;
use std::convert::TryInto;
use user_model::{UpdateUserProfileParams, UserEmail, UserIcon, UserId, UserName, UserPassword, UserProfile};
use user_model::{
UpdateUserProfileParams, UserEmail, UserIcon, UserId, UserName, UserPassword, UserProfile,
};
#[derive(Default, ProtoBuf)]
pub struct UserTokenPB {
#[pb(index = 1)]
pub token: String,
#[pb(index = 1)]
pub token: String,
}
#[derive(ProtoBuf, Default, Clone)]
pub struct UserSettingPB {
#[pb(index = 1)]
pub(crate) user_folder: String,
#[pb(index = 1)]
pub(crate) user_folder: String,
}
#[derive(ProtoBuf, Default, Debug, PartialEq, Eq, Clone)]
pub struct UserProfilePB {
#[pb(index = 1)]
pub id: String,
#[pb(index = 1)]
pub id: String,
#[pb(index = 2)]
pub email: String,
#[pb(index = 2)]
pub email: String,
#[pb(index = 3)]
pub name: String,
#[pb(index = 3)]
pub name: String,
#[pb(index = 4)]
pub token: String,
#[pb(index = 4)]
pub token: String,
#[pb(index = 5)]
pub icon_url: String,
#[pb(index = 5)]
pub icon_url: String,
}
impl std::convert::From<UserProfile> for UserProfilePB {
fn from(user_profile: UserProfile) -> Self {
Self {
id: user_profile.id,
email: user_profile.email,
name: user_profile.name,
token: user_profile.token,
icon_url: user_profile.icon_url,
}
fn from(user_profile: UserProfile) -> Self {
Self {
id: user_profile.id,
email: user_profile.email,
name: user_profile.name,
token: user_profile.token,
icon_url: user_profile.icon_url,
}
}
}
#[derive(ProtoBuf, Default)]
pub struct UpdateUserProfilePayloadPB {
#[pb(index = 1)]
pub id: String,
#[pb(index = 1)]
pub id: String,
#[pb(index = 2, one_of)]
pub name: Option<String>,
#[pb(index = 2, one_of)]
pub name: Option<String>,
#[pb(index = 3, one_of)]
pub email: Option<String>,
#[pb(index = 3, one_of)]
pub email: Option<String>,
#[pb(index = 4, one_of)]
pub password: Option<String>,
#[pb(index = 4, one_of)]
pub password: Option<String>,
#[pb(index = 5, one_of)]
pub icon_url: Option<String>,
#[pb(index = 5, one_of)]
pub icon_url: Option<String>,
}
impl UpdateUserProfilePayloadPB {
pub fn new(id: &str) -> Self {
Self {
id: id.to_owned(),
..Default::default()
}
pub fn new(id: &str) -> Self {
Self {
id: id.to_owned(),
..Default::default()
}
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_owned());
self
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_owned());
self
}
pub fn email(mut self, email: &str) -> Self {
self.email = Some(email.to_owned());
self
}
pub fn email(mut self, email: &str) -> Self {
self.email = Some(email.to_owned());
self
}
pub fn password(mut self, password: &str) -> Self {
self.password = Some(password.to_owned());
self
}
pub fn password(mut self, password: &str) -> Self {
self.password = Some(password.to_owned());
self
}
pub fn icon_url(mut self, icon_url: &str) -> Self {
self.icon_url = Some(icon_url.to_owned());
self
}
pub fn icon_url(mut self, icon_url: &str) -> Self {
self.icon_url = Some(icon_url.to_owned());
self
}
}
impl TryInto<UpdateUserProfileParams> for UpdateUserProfilePayloadPB {
type Error = ErrorCode;
type Error = ErrorCode;
fn try_into(self) -> Result<UpdateUserProfileParams, Self::Error> {
let id = UserId::parse(self.id)?.0;
fn try_into(self) -> Result<UpdateUserProfileParams, Self::Error> {
let id = UserId::parse(self.id)?.0;
let name = match self.name {
None => None,
Some(name) => Some(UserName::parse(name)?.0),
};
let name = match self.name {
None => None,
Some(name) => Some(UserName::parse(name)?.0),
};
let email = match self.email {
None => None,
Some(email) => Some(UserEmail::parse(email)?.0),
};
let email = match self.email {
None => None,
Some(email) => Some(UserEmail::parse(email)?.0),
};
let password = match self.password {
None => None,
Some(password) => Some(UserPassword::parse(password)?.0),
};
let password = match self.password {
None => None,
Some(password) => Some(UserPassword::parse(password)?.0),
};
let icon_url = match self.icon_url {
None => None,
Some(icon_url) => Some(UserIcon::parse(icon_url)?.0),
};
let icon_url = match self.icon_url {
None => None,
Some(icon_url) => Some(UserIcon::parse(icon_url)?.0),
};
Ok(UpdateUserProfileParams {
id,
name,
email,
password,
icon_url,
})
}
Ok(UpdateUserProfileParams {
id,
name,
email,
password,
icon_url,
})
}
}

View File

@ -4,80 +4,80 @@ use std::collections::HashMap;
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct UserPreferencesPB {
#[pb(index = 1)]
user_id: String,
#[pb(index = 1)]
user_id: String,
#[pb(index = 2)]
appearance_setting: AppearanceSettingsPB,
#[pb(index = 2)]
appearance_setting: AppearanceSettingsPB,
}
#[derive(ProtoBuf, Serialize, Deserialize, Debug, Clone)]
pub struct AppearanceSettingsPB {
#[pb(index = 1)]
pub theme: String,
#[pb(index = 1)]
pub theme: String,
#[pb(index = 2)]
#[serde(default)]
pub theme_mode: ThemeModePB,
#[pb(index = 2)]
#[serde(default)]
pub theme_mode: ThemeModePB,
#[pb(index = 3)]
pub font: String,
#[pb(index = 3)]
pub font: String,
#[pb(index = 4)]
pub monospace_font: String,
#[pb(index = 4)]
pub monospace_font: String,
#[pb(index = 5)]
#[serde(default)]
pub locale: LocaleSettingsPB,
#[pb(index = 5)]
#[serde(default)]
pub locale: LocaleSettingsPB,
#[pb(index = 6)]
#[serde(default = "DEFAULT_RESET_VALUE")]
pub reset_to_default: bool,
#[pb(index = 6)]
#[serde(default = "DEFAULT_RESET_VALUE")]
pub reset_to_default: bool,
#[pb(index = 7)]
#[serde(default)]
pub setting_key_value: HashMap<String, String>,
#[pb(index = 7)]
#[serde(default)]
pub setting_key_value: HashMap<String, String>,
#[pb(index = 8)]
#[serde(default)]
pub is_menu_collapsed: bool,
#[pb(index = 8)]
#[serde(default)]
pub is_menu_collapsed: bool,
#[pb(index = 9)]
#[serde(default)]
pub menu_offset: f64,
#[pb(index = 9)]
#[serde(default)]
pub menu_offset: f64,
}
const DEFAULT_RESET_VALUE: fn() -> bool = || APPEARANCE_RESET_AS_DEFAULT;
#[derive(ProtoBuf_Enum, Serialize, Deserialize, Clone, Debug)]
pub enum ThemeModePB {
Light = 0,
Dark = 1,
System = 2,
Light = 0,
Dark = 1,
System = 2,
}
impl std::default::Default for ThemeModePB {
fn default() -> Self {
ThemeModePB::System
}
fn default() -> Self {
ThemeModePB::System
}
}
#[derive(ProtoBuf, Serialize, Deserialize, Debug, Clone)]
pub struct LocaleSettingsPB {
#[pb(index = 1)]
pub language_code: String,
#[pb(index = 1)]
pub language_code: String,
#[pb(index = 2)]
pub country_code: String,
#[pb(index = 2)]
pub country_code: String,
}
impl std::default::Default for LocaleSettingsPB {
fn default() -> Self {
Self {
language_code: "en".to_owned(),
country_code: "".to_owned(),
}
fn default() -> Self {
Self {
language_code: "en".to_owned(),
country_code: "".to_owned(),
}
}
}
pub const APPEARANCE_DEFAULT_THEME: &str = "light";
@ -88,17 +88,17 @@ const APPEARANCE_DEFAULT_IS_MENU_COLLAPSED: bool = false;
const APPEARANCE_DEFAULT_MENU_OFFSET: f64 = 0.0;
impl std::default::Default for AppearanceSettingsPB {
fn default() -> Self {
AppearanceSettingsPB {
theme: APPEARANCE_DEFAULT_THEME.to_owned(),
theme_mode: ThemeModePB::default(),
font: APPEARANCE_DEFAULT_FONT.to_owned(),
monospace_font: APPEARANCE_DEFAULT_MONOSPACE_FONT.to_owned(),
locale: LocaleSettingsPB::default(),
reset_to_default: APPEARANCE_RESET_AS_DEFAULT,
setting_key_value: HashMap::default(),
is_menu_collapsed: APPEARANCE_DEFAULT_IS_MENU_COLLAPSED,
menu_offset: APPEARANCE_DEFAULT_MENU_OFFSET,
}
fn default() -> Self {
AppearanceSettingsPB {
theme: APPEARANCE_DEFAULT_THEME.to_owned(),
theme_mode: ThemeModePB::default(),
font: APPEARANCE_DEFAULT_FONT.to_owned(),
monospace_font: APPEARANCE_DEFAULT_MONOSPACE_FONT.to_owned(),
locale: LocaleSettingsPB::default(),
reset_to_default: APPEARANCE_RESET_AS_DEFAULT,
setting_key_value: HashMap::default(),
is_menu_collapsed: APPEARANCE_DEFAULT_IS_MENU_COLLAPSED,
menu_offset: APPEARANCE_DEFAULT_MENU_OFFSET,
}
}
}

View File

@ -4,37 +4,43 @@ use lib_dispatch::prelude::*;
use lib_infra::future::{Fut, FutureResult};
use std::sync::Arc;
use user_model::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfile};
use user_model::{
SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfile,
};
pub fn init(user_session: Arc<UserSession>) -> AFPlugin {
AFPlugin::new()
.name("Flowy-User")
.state(user_session)
.event(UserEvent::SignIn, sign_in)
.event(UserEvent::SignUp, sign_up)
.event(UserEvent::InitUser, init_user_handler)
.event(UserEvent::GetUserProfile, get_user_profile_handler)
.event(UserEvent::SignOut, sign_out)
.event(UserEvent::UpdateUserProfile, update_user_profile_handler)
.event(UserEvent::CheckUser, check_user_handler)
.event(UserEvent::SetAppearanceSetting, set_appearance_setting)
.event(UserEvent::GetAppearanceSetting, get_appearance_setting)
.event(UserEvent::GetUserSetting, get_user_setting)
AFPlugin::new()
.name("Flowy-User")
.state(user_session)
.event(UserEvent::SignIn, sign_in)
.event(UserEvent::SignUp, sign_up)
.event(UserEvent::InitUser, init_user_handler)
.event(UserEvent::GetUserProfile, get_user_profile_handler)
.event(UserEvent::SignOut, sign_out)
.event(UserEvent::UpdateUserProfile, update_user_profile_handler)
.event(UserEvent::CheckUser, check_user_handler)
.event(UserEvent::SetAppearanceSetting, set_appearance_setting)
.event(UserEvent::GetAppearanceSetting, get_appearance_setting)
.event(UserEvent::GetUserSetting, get_user_setting)
}
pub trait UserStatusCallback: Send + Sync + 'static {
fn did_sign_in(&self, token: &str, user_id: &str) -> Fut<FlowyResult<()>>;
fn did_sign_up(&self, user_profile: &UserProfile) -> Fut<FlowyResult<()>>;
fn did_expired(&self, token: &str, user_id: &str) -> Fut<FlowyResult<()>>;
fn did_sign_in(&self, token: &str, user_id: &str) -> Fut<FlowyResult<()>>;
fn did_sign_up(&self, user_profile: &UserProfile) -> Fut<FlowyResult<()>>;
fn did_expired(&self, token: &str, user_id: &str) -> Fut<FlowyResult<()>>;
}
pub trait UserCloudService: Send + Sync {
fn sign_up(&self, params: SignUpParams) -> FutureResult<SignUpResponse, FlowyError>;
fn sign_in(&self, params: SignInParams) -> FutureResult<SignInResponse, FlowyError>;
fn sign_out(&self, token: &str) -> FutureResult<(), FlowyError>;
fn update_user(&self, token: &str, params: UpdateUserProfileParams) -> FutureResult<(), FlowyError>;
fn get_user(&self, token: &str) -> FutureResult<UserProfilePB, FlowyError>;
fn ws_addr(&self) -> String;
fn sign_up(&self, params: SignUpParams) -> FutureResult<SignUpResponse, FlowyError>;
fn sign_in(&self, params: SignInParams) -> FutureResult<SignInResponse, FlowyError>;
fn sign_out(&self, token: &str) -> FutureResult<(), FlowyError>;
fn update_user(
&self,
token: &str,
params: UpdateUserProfileParams,
) -> FutureResult<(), FlowyError>;
fn get_user(&self, token: &str) -> FutureResult<UserProfilePB, FlowyError>;
fn ws_addr(&self) -> String;
}
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
@ -44,43 +50,43 @@ use strum_macros::Display;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
#[event_err = "FlowyError"]
pub enum UserEvent {
/// Logging into an account using a register email and password
#[event(input = "SignInPayloadPB", output = "UserProfilePB")]
SignIn = 0,
/// Logging into an account using a register email and password
#[event(input = "SignInPayloadPB", output = "UserProfilePB")]
SignIn = 0,
/// Creating a new account
#[event(input = "SignUpPayloadPB", output = "UserProfilePB")]
SignUp = 1,
/// Creating a new account
#[event(input = "SignUpPayloadPB", output = "UserProfilePB")]
SignUp = 1,
/// Logging out fo an account
#[event(passthrough)]
SignOut = 2,
/// Logging out fo an account
#[event(passthrough)]
SignOut = 2,
/// Update the user information
#[event(input = "UpdateUserProfilePayloadPB")]
UpdateUserProfile = 3,
/// Update the user information
#[event(input = "UpdateUserProfilePayloadPB")]
UpdateUserProfile = 3,
/// Get the user information
#[event(output = "UserProfilePB")]
GetUserProfile = 4,
/// Get the user information
#[event(output = "UserProfilePB")]
GetUserProfile = 4,
/// Check the user current session is valid or not
#[event(output = "UserProfilePB")]
CheckUser = 5,
/// Check the user current session is valid or not
#[event(output = "UserProfilePB")]
CheckUser = 5,
/// Initialize resources for the current user after launching the application
#[event()]
InitUser = 6,
/// Initialize resources for the current user after launching the application
#[event()]
InitUser = 6,
/// Change the visual elements of the interface, such as theme, font and more
#[event(input = "AppearanceSettingsPB")]
SetAppearanceSetting = 7,
/// Change the visual elements of the interface, such as theme, font and more
#[event(input = "AppearanceSettingsPB")]
SetAppearanceSetting = 7,
/// Get the appearance setting
#[event(output = "AppearanceSettingsPB")]
GetAppearanceSetting = 8,
/// Get the appearance setting
#[event(output = "AppearanceSettingsPB")]
GetAppearanceSetting = 8,
/// Get the settings of the user, such as the user storage folder
#[event(output = "UserSettingPB")]
GetUserSetting = 9,
/// Get the settings of the user, such as the user storage folder
#[event(output = "UserSettingPB")]
GetUserSetting = 9,
}

View File

@ -8,12 +8,12 @@ use user_model::{SignInParams, SignUpParams};
// tracing instrument 👉🏻 https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html
#[tracing::instrument(level = "debug", name = "sign_in", skip(data, session), fields(email = %data.email), err)]
pub async fn sign_in(
data: AFPluginData<SignInPayloadPB>,
session: AFPluginState<Arc<UserSession>>,
data: AFPluginData<SignInPayloadPB>,
session: AFPluginState<Arc<UserSession>>,
) -> DataResult<UserProfilePB, FlowyError> {
let params: SignInParams = data.into_inner().try_into()?;
let user_profile: UserProfilePB = session.sign_in(params).await?.into();
data_result(user_profile)
let params: SignInParams = data.into_inner().try_into()?;
let user_profile: UserProfilePB = session.sign_in(params).await?.into();
data_result(user_profile)
}
#[tracing::instrument(
@ -27,11 +27,11 @@ pub async fn sign_in(
err
)]
pub async fn sign_up(
data: AFPluginData<SignUpPayloadPB>,
session: AFPluginState<Arc<UserSession>>,
data: AFPluginData<SignUpPayloadPB>,
session: AFPluginState<Arc<UserSession>>,
) -> DataResult<UserProfilePB, FlowyError> {
let params: SignUpParams = data.into_inner().try_into()?;
let user_profile: UserProfilePB = session.sign_up(params).await?.into();
let params: SignUpParams = data.into_inner().try_into()?;
let user_profile: UserProfilePB = session.sign_up(params).await?.into();
data_result(user_profile)
data_result(user_profile)
}

View File

@ -1,5 +1,6 @@
use crate::entities::{
AppearanceSettingsPB, UpdateUserProfilePayloadPB, UserProfilePB, UserSettingPB, APPEARANCE_DEFAULT_THEME,
AppearanceSettingsPB, UpdateUserProfilePayloadPB, UserProfilePB, UserSettingPB,
APPEARANCE_DEFAULT_THEME,
};
use crate::{errors::FlowyError, services::UserSession};
use flowy_sqlite::kv::KV;
@ -9,73 +10,82 @@ use user_model::UpdateUserProfileParams;
#[tracing::instrument(level = "debug", skip(session))]
pub async fn init_user_handler(session: AFPluginState<Arc<UserSession>>) -> Result<(), FlowyError> {
session.init_user().await?;
Ok(())
session.init_user().await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(session))]
pub async fn check_user_handler(session: AFPluginState<Arc<UserSession>>) -> DataResult<UserProfilePB, FlowyError> {
let user_profile: UserProfilePB = session.check_user().await?.into();
data_result(user_profile)
pub async fn check_user_handler(
session: AFPluginState<Arc<UserSession>>,
) -> DataResult<UserProfilePB, FlowyError> {
let user_profile: UserProfilePB = session.check_user().await?.into();
data_result(user_profile)
}
#[tracing::instrument(level = "debug", skip(session))]
pub async fn get_user_profile_handler(
session: AFPluginState<Arc<UserSession>>,
session: AFPluginState<Arc<UserSession>>,
) -> DataResult<UserProfilePB, FlowyError> {
let user_profile: UserProfilePB = session.get_user_profile().await?.into();
data_result(user_profile)
let user_profile: UserProfilePB = session.get_user_profile().await?.into();
data_result(user_profile)
}
#[tracing::instrument(level = "debug", name = "sign_out", skip(session))]
pub async fn sign_out(session: AFPluginState<Arc<UserSession>>) -> Result<(), FlowyError> {
session.sign_out().await?;
Ok(())
session.sign_out().await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, session))]
pub async fn update_user_profile_handler(
data: AFPluginData<UpdateUserProfilePayloadPB>,
session: AFPluginState<Arc<UserSession>>,
data: AFPluginData<UpdateUserProfilePayloadPB>,
session: AFPluginState<Arc<UserSession>>,
) -> Result<(), FlowyError> {
let params: UpdateUserProfileParams = data.into_inner().try_into()?;
session.update_user_profile(params).await?;
Ok(())
let params: UpdateUserProfileParams = data.into_inner().try_into()?;
session.update_user_profile(params).await?;
Ok(())
}
const APPEARANCE_SETTING_CACHE_KEY: &str = "appearance_settings";
#[tracing::instrument(level = "debug", skip(data), err)]
pub async fn set_appearance_setting(data: AFPluginData<AppearanceSettingsPB>) -> Result<(), FlowyError> {
let mut setting = data.into_inner();
if setting.theme.is_empty() {
setting.theme = APPEARANCE_DEFAULT_THEME.to_string();
}
pub async fn set_appearance_setting(
data: AFPluginData<AppearanceSettingsPB>,
) -> Result<(), FlowyError> {
let mut setting = data.into_inner();
if setting.theme.is_empty() {
setting.theme = APPEARANCE_DEFAULT_THEME.to_string();
}
let s = serde_json::to_string(&setting)?;
KV::set_str(APPEARANCE_SETTING_CACHE_KEY, s);
Ok(())
let s = serde_json::to_string(&setting)?;
KV::set_str(APPEARANCE_SETTING_CACHE_KEY, s);
Ok(())
}
#[tracing::instrument(level = "debug", err)]
pub async fn get_appearance_setting() -> DataResult<AppearanceSettingsPB, FlowyError> {
match KV::get_str(APPEARANCE_SETTING_CACHE_KEY) {
None => data_result(AppearanceSettingsPB::default()),
Some(s) => {
let setting = match serde_json::from_str(&s) {
Ok(setting) => setting,
Err(e) => {
tracing::error!("Deserialize AppearanceSettings failed: {:?}, fallback to default", e);
AppearanceSettingsPB::default()
}
};
data_result(setting)
}
}
match KV::get_str(APPEARANCE_SETTING_CACHE_KEY) {
None => data_result(AppearanceSettingsPB::default()),
Some(s) => {
let setting = match serde_json::from_str(&s) {
Ok(setting) => setting,
Err(e) => {
tracing::error!(
"Deserialize AppearanceSettings failed: {:?}, fallback to default",
e
);
AppearanceSettingsPB::default()
},
};
data_result(setting)
},
}
}
#[tracing::instrument(level = "debug", skip_all, err)]
pub async fn get_user_setting(session: AFPluginState<Arc<UserSession>>) -> DataResult<UserSettingPB, FlowyError> {
let user_setting = session.user_setting()?;
data_result(user_setting)
pub async fn get_user_setting(
session: AFPluginState<Arc<UserSession>>,
) -> DataResult<UserSettingPB, FlowyError> {
let user_setting = session.user_setting()?;
data_result(user_setting)
}

View File

@ -10,5 +10,5 @@ pub mod services;
extern crate flowy_sqlite;
pub mod errors {
pub use flowy_error::*;
pub use flowy_error::*;
}

View File

@ -4,27 +4,27 @@ const OBSERVABLE_CATEGORY: &str = "User";
#[derive(ProtoBuf_Enum, Debug)]
pub(crate) enum UserNotification {
Unknown = 0,
DidUserSignIn = 1,
DidUpdateUserProfile = 2,
Unknown = 0,
DidUserSignIn = 1,
DidUpdateUserProfile = 2,
}
impl std::default::Default for UserNotification {
fn default() -> Self {
UserNotification::Unknown
}
fn default() -> Self {
UserNotification::Unknown
}
}
impl std::convert::From<UserNotification> for i32 {
fn from(notification: UserNotification) -> Self {
notification as i32
}
fn from(notification: UserNotification) -> Self {
notification as i32
}
}
pub(crate) fn send_notification(id: &str, ty: UserNotification) -> NotificationBuilder {
NotificationBuilder::new(id, ty, OBSERVABLE_CATEGORY)
NotificationBuilder::new(id, ty, OBSERVABLE_CATEGORY)
}
pub(crate) fn send_sign_in_notification() -> NotificationBuilder {
NotificationBuilder::new("", UserNotification::DidUserSignIn, OBSERVABLE_CATEGORY)
NotificationBuilder::new("", UserNotification::DidUserSignIn, OBSERVABLE_CATEGORY)
}

View File

@ -8,144 +8,144 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
use user_model::{SignInResponse, SignUpResponse, UpdateUserProfileParams, UserProfile};
pub struct UserDB {
db_dir: String,
db_dir: String,
}
impl UserDB {
pub fn new(db_dir: &str) -> Self {
Self {
db_dir: db_dir.to_owned(),
}
pub fn new(db_dir: &str) -> Self {
Self {
db_dir: db_dir.to_owned(),
}
}
fn open_user_db_if_need(&self, user_id: &str) -> Result<Arc<ConnectionPool>, FlowyError> {
if user_id.is_empty() {
return Err(ErrorCode::UserIdIsEmpty.into());
}
fn open_user_db_if_need(&self, user_id: &str) -> Result<Arc<ConnectionPool>, FlowyError> {
if user_id.is_empty() {
return Err(ErrorCode::UserIdIsEmpty.into());
}
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();
tracing::trace!("open user db {} at path: {}", user_id, dir);
let db = flowy_sqlite::init(&dir).map_err(|e| {
tracing::error!("open user: {} db failed, {:?}", user_id, e);
FlowyError::internal().context(e)
})?;
let pool = db.get_pool();
write_guard.insert(user_id.to_owned(), db);
drop(write_guard);
Ok(pool)
if let Some(database) = DB_MAP.read().get(user_id) {
return Ok(database.get_pool());
}
pub(crate) fn close_user_db(&self, user_id: &str) -> Result<(), FlowyError> {
match DB_MAP.try_write_for(Duration::from_millis(300)) {
None => Err(FlowyError::internal().context("Acquire write lock to close user db failed")),
Some(mut write_guard) => {
write_guard.remove(user_id);
Ok(())
}
}
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()),
}
pub(crate) fn get_connection(&self, user_id: &str) -> Result<DBConnection, FlowyError> {
let conn = self.get_pool(user_id)?.get()?;
Ok(conn)
}
let mut dir = PathBuf::new();
dir.push(&self.db_dir);
dir.push(user_id);
let dir = dir.to_str().unwrap().to_owned();
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)
tracing::trace!("open user db {} at path: {}", user_id, dir);
let db = flowy_sqlite::init(&dir).map_err(|e| {
tracing::error!("open user: {} db failed, {:?}", user_id, e);
FlowyError::internal().context(e)
})?;
let pool = db.get_pool();
write_guard.insert(user_id.to_owned(), db);
drop(write_guard);
Ok(pool)
}
pub(crate) fn close_user_db(&self, user_id: &str) -> Result<(), FlowyError> {
match DB_MAP.try_write_for(Duration::from_millis(300)) {
None => Err(FlowyError::internal().context("Acquire write lock to close user db failed")),
Some(mut write_guard) => {
write_guard.remove(user_id);
Ok(())
},
}
}
pub(crate) fn get_connection(&self, user_id: &str) -> Result<DBConnection, FlowyError> {
let conn = self.get_pool(user_id)?.get()?;
Ok(conn)
}
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)
}
}
lazy_static! {
static ref DB_MAP: RwLock<HashMap<String, Database>> = RwLock::new(HashMap::new());
static ref DB_MAP: RwLock<HashMap<String, Database>> = RwLock::new(HashMap::new());
}
#[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
pub(crate) icon_url: String,
pub(crate) id: String,
pub(crate) name: String,
pub(crate) token: String,
pub(crate) email: String,
pub(crate) workspace: String, // deprecated
pub(crate) icon_url: String,
}
impl UserTable {
pub fn new(id: String, name: String, email: String, token: String) -> Self {
Self {
id,
name,
email,
token,
icon_url: "".to_owned(),
workspace: "".to_owned(),
}
pub fn new(id: String, name: String, email: String, token: String) -> Self {
Self {
id,
name,
email,
token,
icon_url: "".to_owned(),
workspace: "".to_owned(),
}
}
pub fn set_workspace(mut self, workspace: String) -> Self {
self.workspace = workspace;
self
}
pub fn set_workspace(mut self, workspace: String) -> Self {
self.workspace = workspace;
self
}
}
impl std::convert::From<SignUpResponse> for UserTable {
fn from(resp: SignUpResponse) -> Self {
UserTable::new(resp.user_id, resp.name, resp.email, resp.token)
}
fn from(resp: SignUpResponse) -> Self {
UserTable::new(resp.user_id, resp.name, resp.email, resp.token)
}
}
impl std::convert::From<SignInResponse> for UserTable {
fn from(resp: SignInResponse) -> Self {
UserTable::new(resp.user_id, resp.name, resp.email, resp.token)
}
fn from(resp: SignInResponse) -> Self {
UserTable::new(resp.user_id, resp.name, resp.email, resp.token)
}
}
impl std::convert::From<UserTable> for UserProfile {
fn from(table: UserTable) -> Self {
UserProfile {
id: table.id,
email: table.email,
name: table.name,
token: table.token,
icon_url: table.icon_url,
}
fn from(table: UserTable) -> Self {
UserProfile {
id: table.id,
email: table.email,
name: table.name,
token: table.token,
icon_url: table.icon_url,
}
}
}
#[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>,
pub icon_url: Option<String>,
pub id: String,
pub workspace: Option<String>, // deprecated
pub name: Option<String>,
pub email: Option<String>,
pub icon_url: Option<String>,
}
impl UserTableChangeset {
pub fn new(params: UpdateUserProfileParams) -> Self {
UserTableChangeset {
id: params.id,
workspace: None,
name: params.name,
email: params.email,
icon_url: params.icon_url,
}
pub fn new(params: UpdateUserProfileParams) -> Self {
UserTableChangeset {
id: params.id,
workspace: None,
name: params.name,
email: params.email,
icon_url: params.icon_url,
}
}
}

View File

@ -1,362 +1,374 @@
use crate::entities::{UserProfilePB, UserSettingPB};
use crate::event_map::UserStatusCallback;
use crate::{
errors::{ErrorCode, FlowyError},
event_map::UserCloudService,
notification::*,
services::database::{UserDB, UserTable, UserTableChangeset},
errors::{ErrorCode, FlowyError},
event_map::UserCloudService,
notification::*,
services::database::{UserDB, UserTable, UserTableChangeset},
};
use flowy_sqlite::ConnectionPool;
use flowy_sqlite::{
kv::KV,
query_dsl::*,
schema::{user_table, user_table::dsl},
DBConnection, ExpressionMethods, UserDatabaseConnection,
kv::KV,
query_dsl::*,
schema::{user_table, user_table::dsl},
DBConnection, ExpressionMethods, UserDatabaseConnection,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
use user_model::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfile};
use user_model::{
SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfile,
};
pub struct UserSessionConfig {
root_dir: String,
root_dir: String,
/// Used as the key of `Session` when saving session information to KV.
session_cache_key: String,
/// Used as the key of `Session` when saving session information to KV.
session_cache_key: String,
}
impl UserSessionConfig {
/// The `root_dir` represents as the root of the user folders. It must be unique for each
/// users.
pub fn new(name: &str, root_dir: &str) -> Self {
let session_cache_key = format!("{}_session_cache", name);
Self {
root_dir: root_dir.to_owned(),
session_cache_key,
}
/// The `root_dir` represents as the root of the user folders. It must be unique for each
/// users.
pub fn new(name: &str, root_dir: &str) -> Self {
let session_cache_key = format!("{}_session_cache", name);
Self {
root_dir: root_dir.to_owned(),
session_cache_key,
}
}
}
pub struct UserSession {
database: UserDB,
config: UserSessionConfig,
cloud_service: Arc<dyn UserCloudService>,
user_status_callback: RwLock<Option<Arc<dyn UserStatusCallback>>>,
database: UserDB,
config: UserSessionConfig,
cloud_service: Arc<dyn UserCloudService>,
user_status_callback: RwLock<Option<Arc<dyn UserStatusCallback>>>,
}
impl UserSession {
pub fn new(config: UserSessionConfig, cloud_service: Arc<dyn UserCloudService>) -> Self {
let db = UserDB::new(&config.root_dir);
let user_status_callback = RwLock::new(None);
Self {
database: db,
config,
cloud_service,
user_status_callback,
}
pub fn new(config: UserSessionConfig, cloud_service: Arc<dyn UserCloudService>) -> Self {
let db = UserDB::new(&config.root_dir);
let user_status_callback = RwLock::new(None);
Self {
database: db,
config,
cloud_service,
user_status_callback,
}
}
pub async fn init<C: UserStatusCallback + 'static>(&self, user_status_callback: C) {
if let Ok(session) = self.get_session() {
let _ = user_status_callback.did_sign_in(&session.token, &session.user_id).await;
}
*self.user_status_callback.write().await = Some(Arc::new(user_status_callback));
pub async fn init<C: UserStatusCallback + 'static>(&self, user_status_callback: C) {
if let Ok(session) = self.get_session() {
let _ = user_status_callback
.did_sign_in(&session.token, &session.user_id)
.await;
}
*self.user_status_callback.write().await = Some(Arc::new(user_status_callback));
}
pub fn db_connection(&self) -> Result<DBConnection, FlowyError> {
let user_id = self.get_session()?.user_id;
self.database.get_connection(&user_id)
}
pub fn db_connection(&self) -> Result<DBConnection, FlowyError> {
let user_id = self.get_session()?.user_id;
self.database.get_connection(&user_id)
}
// The caller will be not 'Sync' before of the return value,
// PooledConnection<ConnectionManager> is not sync. You can use
// db_connection_pool function to require the ConnectionPool that is 'Sync'.
//
// let pool = self.db_connection_pool()?;
// let conn: PooledConnection<ConnectionManager> = pool.get()?;
pub fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError> {
let user_id = self.get_session()?.user_id;
self.database.get_pool(&user_id)
}
// The caller will be not 'Sync' before of the return value,
// PooledConnection<ConnectionManager> is not sync. You can use
// db_connection_pool function to require the ConnectionPool that is 'Sync'.
//
// let pool = self.db_connection_pool()?;
// let conn: PooledConnection<ConnectionManager> = pool.get()?;
pub fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError> {
let user_id = self.get_session()?.user_id;
self.database.get_pool(&user_id)
}
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_in(&self, params: SignInParams) -> Result<UserProfile, FlowyError> {
if self.is_user_login(&params.email) {
match self.get_user_profile().await {
Ok(profile) => {
send_sign_in_notification()
.payload::<UserProfilePB>(profile.clone().into())
.send();
Ok(profile)
}
Err(err) => Err(err),
}
} else {
let resp = self.cloud_service.sign_in(params).await?;
let session: Session = resp.clone().into();
self.set_session(Some(session))?;
let user_profile: UserProfile = self.save_user(resp.into()).await?.into();
let _ = self
.user_status_callback
.read()
.await
.as_ref()
.unwrap()
.did_sign_in(&user_profile.token, &user_profile.id)
.await;
send_sign_in_notification()
.payload::<UserProfilePB>(user_profile.clone().into())
.send();
Ok(user_profile)
}
}
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_up(&self, params: SignUpParams) -> Result<UserProfile, FlowyError> {
if self.is_user_login(&params.email) {
self.get_user_profile().await
} else {
let resp = self.cloud_service.sign_up(params).await?;
let session: Session = resp.clone().into();
self.set_session(Some(session))?;
let user_table = self.save_user(resp.into()).await?;
let user_profile: UserProfile = user_table.into();
let _ = self
.user_status_callback
.read()
.await
.as_ref()
.unwrap()
.did_sign_up(&user_profile)
.await;
Ok(user_profile)
}
}
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_out(&self) -> Result<(), FlowyError> {
let session = self.get_session()?;
let _ =
diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id))).execute(&*(self.db_connection()?))?;
self.database.close_user_db(&session.user_id)?;
self.set_session(None)?;
let _ = self
.user_status_callback
.read()
.await
.as_ref()
.unwrap()
.did_expired(&session.token, &session.user_id)
.await;
self.sign_out_on_server(&session.token).await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(self))]
pub async fn update_user_profile(&self, params: UpdateUserProfileParams) -> Result<(), FlowyError> {
let session = self.get_session()?;
let changeset = UserTableChangeset::new(params.clone());
diesel_update_table!(user_table, changeset, &*self.db_connection()?);
let user_profile = self.get_user_profile().await?;
let profile_pb: UserProfilePB = user_profile.into();
send_notification(&session.token, UserNotification::DidUpdateUserProfile)
.payload(profile_pb)
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_in(&self, params: SignInParams) -> Result<UserProfile, FlowyError> {
if self.is_user_login(&params.email) {
match self.get_user_profile().await {
Ok(profile) => {
send_sign_in_notification()
.payload::<UserProfilePB>(profile.clone().into())
.send();
self.update_user_on_server(&session.token, params).await?;
Ok(())
Ok(profile)
},
Err(err) => Err(err),
}
} else {
let resp = self.cloud_service.sign_in(params).await?;
let session: Session = resp.clone().into();
self.set_session(Some(session))?;
let user_profile: UserProfile = self.save_user(resp.into()).await?.into();
let _ = self
.user_status_callback
.read()
.await
.as_ref()
.unwrap()
.did_sign_in(&user_profile.token, &user_profile.id)
.await;
send_sign_in_notification()
.payload::<UserProfilePB>(user_profile.clone().into())
.send();
Ok(user_profile)
}
}
pub async fn init_user(&self) -> Result<(), FlowyError> {
Ok(())
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_up(&self, params: SignUpParams) -> Result<UserProfile, FlowyError> {
if self.is_user_login(&params.email) {
self.get_user_profile().await
} else {
let resp = self.cloud_service.sign_up(params).await?;
let session: Session = resp.clone().into();
self.set_session(Some(session))?;
let user_table = self.save_user(resp.into()).await?;
let user_profile: UserProfile = user_table.into();
let _ = self
.user_status_callback
.read()
.await
.as_ref()
.unwrap()
.did_sign_up(&user_profile)
.await;
Ok(user_profile)
}
}
pub async fn check_user(&self) -> Result<UserProfile, FlowyError> {
let (user_id, token) = self.get_session()?.into_part();
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_out(&self) -> Result<(), FlowyError> {
let session = self.get_session()?;
let _ = diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id)))
.execute(&*(self.db_connection()?))?;
self.database.close_user_db(&session.user_id)?;
self.set_session(None)?;
let _ = self
.user_status_callback
.read()
.await
.as_ref()
.unwrap()
.did_expired(&session.token, &session.user_id)
.await;
self.sign_out_on_server(&session.token).await?;
let user = dsl::user_table
.filter(user_table::id.eq(&user_id))
.first::<UserTable>(&*(self.db_connection()?))?;
Ok(())
}
self.read_user_profile_on_server(&token)?;
Ok(user.into())
}
#[tracing::instrument(level = "debug", skip(self))]
pub async fn update_user_profile(
&self,
params: UpdateUserProfileParams,
) -> Result<(), FlowyError> {
let session = self.get_session()?;
let changeset = UserTableChangeset::new(params.clone());
diesel_update_table!(user_table, changeset, &*self.db_connection()?);
pub async fn get_user_profile(&self) -> Result<UserProfile, FlowyError> {
let (user_id, token) = self.get_session()?.into_part();
let user = dsl::user_table
.filter(user_table::id.eq(&user_id))
.first::<UserTable>(&*(self.db_connection()?))?;
let user_profile = self.get_user_profile().await?;
let profile_pb: UserProfilePB = user_profile.into();
send_notification(&session.token, UserNotification::DidUpdateUserProfile)
.payload(profile_pb)
.send();
self.update_user_on_server(&session.token, params).await?;
Ok(())
}
self.read_user_profile_on_server(&token)?;
Ok(user.into())
}
pub async fn init_user(&self) -> Result<(), FlowyError> {
Ok(())
}
pub fn user_dir(&self) -> Result<String, FlowyError> {
let session = self.get_session()?;
Ok(format!("{}/{}", self.config.root_dir, session.user_id))
}
pub async fn check_user(&self) -> Result<UserProfile, FlowyError> {
let (user_id, token) = self.get_session()?.into_part();
pub fn user_setting(&self) -> Result<UserSettingPB, FlowyError> {
let user_setting = UserSettingPB {
user_folder: self.user_dir()?,
};
Ok(user_setting)
}
let user = dsl::user_table
.filter(user_table::id.eq(&user_id))
.first::<UserTable>(&*(self.db_connection()?))?;
pub fn user_id(&self) -> Result<String, FlowyError> {
Ok(self.get_session()?.user_id)
}
self.read_user_profile_on_server(&token)?;
Ok(user.into())
}
pub fn user_name(&self) -> Result<String, FlowyError> {
Ok(self.get_session()?.name)
}
pub async fn get_user_profile(&self) -> Result<UserProfile, FlowyError> {
let (user_id, token) = self.get_session()?.into_part();
let user = dsl::user_table
.filter(user_table::id.eq(&user_id))
.first::<UserTable>(&*(self.db_connection()?))?;
pub fn token(&self) -> Result<String, FlowyError> {
Ok(self.get_session()?.token)
}
self.read_user_profile_on_server(&token)?;
Ok(user.into())
}
pub fn user_dir(&self) -> Result<String, FlowyError> {
let session = self.get_session()?;
Ok(format!("{}/{}", self.config.root_dir, session.user_id))
}
pub fn user_setting(&self) -> Result<UserSettingPB, FlowyError> {
let user_setting = UserSettingPB {
user_folder: self.user_dir()?,
};
Ok(user_setting)
}
pub fn user_id(&self) -> Result<String, FlowyError> {
Ok(self.get_session()?.user_id)
}
pub fn user_name(&self) -> Result<String, FlowyError> {
Ok(self.get_session()?.name)
}
pub fn token(&self) -> Result<String, FlowyError> {
Ok(self.get_session()?.token)
}
}
impl UserSession {
fn read_user_profile_on_server(&self, _token: &str) -> Result<(), FlowyError> {
Ok(())
}
fn read_user_profile_on_server(&self, _token: &str) -> Result<(), FlowyError> {
Ok(())
}
async fn update_user_on_server(&self, token: &str, params: UpdateUserProfileParams) -> Result<(), FlowyError> {
let server = self.cloud_service.clone();
let token = token.to_owned();
let _ = tokio::spawn(async move {
match server.update_user(&token, params).await {
Ok(_) => {}
Err(e) => {
// TODO: retry?
tracing::error!("update user profile failed: {:?}", e);
}
}
})
.await;
Ok(())
}
async fn update_user_on_server(
&self,
token: &str,
params: UpdateUserProfileParams,
) -> Result<(), FlowyError> {
let server = self.cloud_service.clone();
let token = token.to_owned();
let _ = tokio::spawn(async move {
match server.update_user(&token, params).await {
Ok(_) => {},
Err(e) => {
// TODO: retry?
tracing::error!("update user profile failed: {:?}", e);
},
}
})
.await;
Ok(())
}
async fn sign_out_on_server(&self, token: &str) -> Result<(), FlowyError> {
let server = self.cloud_service.clone();
let token = token.to_owned();
let _ = tokio::spawn(async move {
match server.sign_out(&token).await {
Ok(_) => {}
Err(e) => tracing::error!("Sign out failed: {:?}", e),
}
})
.await;
Ok(())
}
async fn sign_out_on_server(&self, token: &str) -> Result<(), FlowyError> {
let server = self.cloud_service.clone();
let token = token.to_owned();
let _ = tokio::spawn(async move {
match server.sign_out(&token).await {
Ok(_) => {},
Err(e) => tracing::error!("Sign out failed: {:?}", e),
}
})
.await;
Ok(())
}
async fn save_user(&self, user: UserTable) -> Result<UserTable, FlowyError> {
let conn = self.db_connection()?;
let _ = diesel::insert_into(user_table::table)
.values(user.clone())
.execute(&*conn)?;
Ok(user)
}
async fn save_user(&self, user: UserTable) -> Result<UserTable, FlowyError> {
let conn = self.db_connection()?;
let _ = diesel::insert_into(user_table::table)
.values(user.clone())
.execute(&*conn)?;
Ok(user)
}
fn set_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
tracing::debug!("Set user session: {:?}", session);
match &session {
None => KV::remove(&self.config.session_cache_key).map_err(|e| FlowyError::new(ErrorCode::Internal, &e))?,
Some(session) => KV::set_str(&self.config.session_cache_key, session.clone().into()),
}
Ok(())
fn set_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
tracing::debug!("Set user session: {:?}", session);
match &session {
None => KV::remove(&self.config.session_cache_key)
.map_err(|e| FlowyError::new(ErrorCode::Internal, &e))?,
Some(session) => KV::set_str(&self.config.session_cache_key, session.clone().into()),
}
Ok(())
}
fn get_session(&self) -> Result<Session, FlowyError> {
match KV::get_str(&self.config.session_cache_key) {
None => Err(FlowyError::unauthorized()),
Some(s) => Ok(Session::from(s)),
}
fn get_session(&self) -> Result<Session, FlowyError> {
match KV::get_str(&self.config.session_cache_key) {
None => Err(FlowyError::unauthorized()),
Some(s) => Ok(Session::from(s)),
}
}
fn is_user_login(&self, email: &str) -> bool {
match self.get_session() {
Ok(session) => session.email == email,
Err(_) => false,
}
fn is_user_login(&self, email: &str) -> bool {
match self.get_session() {
Ok(session) => session.email == email,
Err(_) => false,
}
}
}
pub async fn update_user(
_cloud_service: Arc<dyn UserCloudService>,
pool: Arc<ConnectionPool>,
params: UpdateUserProfileParams,
_cloud_service: Arc<dyn UserCloudService>,
pool: Arc<ConnectionPool>,
params: UpdateUserProfileParams,
) -> Result<(), FlowyError> {
let changeset = UserTableChangeset::new(params);
let conn = pool.get()?;
diesel_update_table!(user_table, changeset, &*conn);
Ok(())
let changeset = UserTableChangeset::new(params);
let conn = pool.get()?;
diesel_update_table!(user_table, changeset, &*conn);
Ok(())
}
impl UserDatabaseConnection for UserSession {
fn get_connection(&self) -> Result<DBConnection, String> {
self.db_connection().map_err(|e| format!("{:?}", e))
}
fn get_connection(&self) -> Result<DBConnection, String> {
self.db_connection().map_err(|e| format!("{:?}", e))
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
struct Session {
user_id: String,
token: String,
email: String,
#[serde(default)]
name: String,
user_id: String,
token: String,
email: String,
#[serde(default)]
name: String,
}
impl std::convert::From<SignInResponse> for Session {
fn from(resp: SignInResponse) -> Self {
Session {
user_id: resp.user_id,
token: resp.token,
email: resp.email,
name: resp.name,
}
fn from(resp: SignInResponse) -> Self {
Session {
user_id: resp.user_id,
token: resp.token,
email: resp.email,
name: resp.name,
}
}
}
impl std::convert::From<SignUpResponse> for Session {
fn from(resp: SignUpResponse) -> Self {
Session {
user_id: resp.user_id,
token: resp.token,
email: resp.email,
name: resp.name,
}
fn from(resp: SignUpResponse) -> Self {
Session {
user_id: resp.user_id,
token: resp.token,
email: resp.email,
name: resp.name,
}
}
}
impl Session {
pub fn into_part(self) -> (String, String) {
(self.user_id, self.token)
}
pub fn into_part(self) -> (String, String) {
(self.user_id, self.token)
}
}
impl std::convert::From<String> for Session {
fn from(s: String) -> Self {
match serde_json::from_str(&s) {
Ok(s) => s,
Err(e) => {
tracing::error!("Deserialize string to Session failed: {:?}", e);
Session::default()
}
}
fn from(s: String) -> Self {
match serde_json::from_str(&s) {
Ok(s) => s,
Err(e) => {
tracing::error!("Deserialize string to Session failed: {:?}", e);
Session::default()
},
}
}
}
impl std::convert::From<Session> for String {
fn from(session: Session) -> Self {
match serde_json::to_string(&session) {
Ok(s) => s,
Err(e) => {
tracing::error!("Serialize session to string failed: {:?}", e);
"".to_string()
}
}
fn from(session: Session) -> Self {
match serde_json::to_string(&session) {
Ok(s) => s,
Err(e) => {
tracing::error!("Serialize session to string failed: {:?}", e);
"".to_string()
},
}
}
}

View File

@ -5,105 +5,107 @@ use flowy_user::{errors::ErrorCode, event_map::UserEvent::*};
#[tokio::test]
async fn sign_up_with_invalid_email() {
for email in invalid_email_test_case() {
let sdk = FlowySDKTest::default();
let request = SignUpPayloadPB {
email: email.to_string(),
name: valid_name(),
password: login_password(),
};
for email in invalid_email_test_case() {
let sdk = FlowySDKTest::default();
let request = SignUpPayloadPB {
email: email.to_string(),
name: valid_name(),
password: login_password(),
};
assert_eq!(
UserModuleEventBuilder::new(sdk)
.event(SignUp)
.payload(request)
.async_send()
.await
.error()
.code,
ErrorCode::EmailFormatInvalid.value()
);
}
assert_eq!(
UserModuleEventBuilder::new(sdk)
.event(SignUp)
.payload(request)
.async_send()
.await
.error()
.code,
ErrorCode::EmailFormatInvalid.value()
);
}
}
#[tokio::test]
async fn sign_up_with_invalid_password() {
for password in invalid_password_test_case() {
let sdk = FlowySDKTest::default();
let request = SignUpPayloadPB {
email: random_email(),
name: valid_name(),
password,
};
for password in invalid_password_test_case() {
let sdk = FlowySDKTest::default();
let request = SignUpPayloadPB {
email: random_email(),
name: valid_name(),
password,
};
UserModuleEventBuilder::new(sdk)
.event(SignUp)
.payload(request)
.async_send()
.await
.assert_error();
}
UserModuleEventBuilder::new(sdk)
.event(SignUp)
.payload(request)
.async_send()
.await
.assert_error();
}
}
#[tokio::test]
async fn sign_in_success() {
let test = FlowySDKTest::default();
let _ = UserModuleEventBuilder::new(test.clone()).event(SignOut).sync_send();
let sign_up_context = test.sign_up().await;
let test = FlowySDKTest::default();
let _ = UserModuleEventBuilder::new(test.clone())
.event(SignOut)
.sync_send();
let sign_up_context = test.sign_up().await;
let request = SignInPayloadPB {
email: sign_up_context.user_profile.email.clone(),
password: sign_up_context.password.clone(),
name: "".to_string(),
};
let request = SignInPayloadPB {
email: sign_up_context.user_profile.email.clone(),
password: sign_up_context.password.clone(),
name: "".to_string(),
};
let response = UserModuleEventBuilder::new(test.clone())
.event(SignIn)
.payload(request)
.async_send()
.await
.parse::<UserProfilePB>();
dbg!(&response);
let response = UserModuleEventBuilder::new(test.clone())
.event(SignIn)
.payload(request)
.async_send()
.await
.parse::<UserProfilePB>();
dbg!(&response);
}
#[tokio::test]
async fn sign_in_with_invalid_email() {
for email in invalid_email_test_case() {
let sdk = FlowySDKTest::default();
let request = SignInPayloadPB {
email: email.to_string(),
password: login_password(),
name: "".to_string(),
};
for email in invalid_email_test_case() {
let sdk = FlowySDKTest::default();
let request = SignInPayloadPB {
email: email.to_string(),
password: login_password(),
name: "".to_string(),
};
assert_eq!(
UserModuleEventBuilder::new(sdk)
.event(SignIn)
.payload(request)
.async_send()
.await
.error()
.code,
ErrorCode::EmailFormatInvalid.value()
);
}
assert_eq!(
UserModuleEventBuilder::new(sdk)
.event(SignIn)
.payload(request)
.async_send()
.await
.error()
.code,
ErrorCode::EmailFormatInvalid.value()
);
}
}
#[tokio::test]
async fn sign_in_with_invalid_password() {
for password in invalid_password_test_case() {
let sdk = FlowySDKTest::default();
for password in invalid_password_test_case() {
let sdk = FlowySDKTest::default();
let request = SignInPayloadPB {
email: random_email(),
password,
name: "".to_string(),
};
let request = SignInPayloadPB {
email: random_email(),
password,
name: "".to_string(),
};
UserModuleEventBuilder::new(sdk)
.event(SignIn)
.payload(request)
.async_send()
.await
.assert_error();
}
UserModuleEventBuilder::new(sdk)
.event(SignIn)
.payload(request)
.async_send()
.await
.assert_error();
}
}

View File

@ -1,42 +1,42 @@
pub use flowy_test::{
event_builder::*,
prelude::{login_password, random_email},
event_builder::*,
prelude::{login_password, random_email},
};
pub(crate) fn invalid_email_test_case() -> Vec<String> {
// https://gist.github.com/cjaoude/fd9910626629b53c4d25
vec![
"annie@",
"annie@gmail@",
"#@%^%#$@#$@#.com",
"@example.com",
"Joe Smith <email@example.com>",
"email.example.com",
"email@example@example.com",
"email@-example.com",
"email@example..com",
"あいうえお@example.com",
/* The following email is valid according to the validate_email function return
* ".email@example.com",
* "email.@example.com",
* "email..email@example.com",
* "email@example",
* "email@example.web",
* "email@111.222.333.44444",
* "Abc..123@example.com", */
]
// https://gist.github.com/cjaoude/fd9910626629b53c4d25
vec![
"annie@",
"annie@gmail@",
"#@%^%#$@#$@#.com",
"@example.com",
"Joe Smith <email@example.com>",
"email.example.com",
"email@example@example.com",
"email@-example.com",
"email@example..com",
"あいうえお@example.com",
/* The following email is valid according to the validate_email function return
* ".email@example.com",
* "email.@example.com",
* "email..email@example.com",
* "email@example",
* "email@example.web",
* "email@111.222.333.44444",
* "Abc..123@example.com", */
]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
}
pub(crate) fn invalid_password_test_case() -> Vec<String> {
vec!["123456", "1234".repeat(100).as_str()]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
}
pub(crate) fn invalid_password_test_case() -> Vec<String> {
vec!["123456", "1234".repeat(100).as_str()]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>()
}
pub(crate) fn valid_name() -> String {
"AppFlowy".to_string()
"AppFlowy".to_string()
}

View File

@ -8,120 +8,120 @@ use nanoid::nanoid;
#[tokio::test]
async fn user_profile_get_failed() {
let sdk = FlowySDKTest::default();
let result = UserModuleEventBuilder::new(sdk)
.event(GetUserProfile)
.assert_error()
.async_send()
.await;
assert!(result.user_profile().is_none())
let sdk = FlowySDKTest::default();
let result = UserModuleEventBuilder::new(sdk)
.event(GetUserProfile)
.assert_error()
.async_send()
.await;
assert!(result.user_profile().is_none())
}
#[tokio::test]
async fn user_profile_get() {
let test = FlowySDKTest::default();
let user_profile = test.init_user().await;
let user = UserModuleEventBuilder::new(test.clone())
.event(GetUserProfile)
.sync_send()
.parse::<UserProfilePB>();
assert_eq!(user_profile, user);
let test = FlowySDKTest::default();
let user_profile = test.init_user().await;
let user = UserModuleEventBuilder::new(test.clone())
.event(GetUserProfile)
.sync_send()
.parse::<UserProfilePB>();
assert_eq!(user_profile, user);
}
#[tokio::test]
async fn user_update_with_name() {
let sdk = FlowySDKTest::default();
let user = sdk.init_user().await;
let new_name = "hello_world".to_owned();
let request = UpdateUserProfilePayloadPB::new(&user.id).name(&new_name);
let _ = UserModuleEventBuilder::new(sdk.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send();
let sdk = FlowySDKTest::default();
let user = sdk.init_user().await;
let new_name = "hello_world".to_owned();
let request = UpdateUserProfilePayloadPB::new(&user.id).name(&new_name);
let _ = UserModuleEventBuilder::new(sdk.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send();
let user_profile = UserModuleEventBuilder::new(sdk.clone())
.event(GetUserProfile)
.assert_error()
.sync_send()
.parse::<UserProfilePB>();
let user_profile = UserModuleEventBuilder::new(sdk.clone())
.event(GetUserProfile)
.assert_error()
.sync_send()
.parse::<UserProfilePB>();
assert_eq!(user_profile.name, new_name,);
assert_eq!(user_profile.name, new_name,);
}
#[tokio::test]
async fn user_update_with_email() {
let sdk = FlowySDKTest::default();
let user = sdk.init_user().await;
let new_email = format!("{}@gmail.com", nanoid!(6));
let request = UpdateUserProfilePayloadPB::new(&user.id).email(&new_email);
let _ = UserModuleEventBuilder::new(sdk.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send();
let user_profile = UserModuleEventBuilder::new(sdk.clone())
.event(GetUserProfile)
.assert_error()
.sync_send()
.parse::<UserProfilePB>();
let sdk = FlowySDKTest::default();
let user = sdk.init_user().await;
let new_email = format!("{}@gmail.com", nanoid!(6));
let request = UpdateUserProfilePayloadPB::new(&user.id).email(&new_email);
let _ = UserModuleEventBuilder::new(sdk.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send();
let user_profile = UserModuleEventBuilder::new(sdk.clone())
.event(GetUserProfile)
.assert_error()
.sync_send()
.parse::<UserProfilePB>();
assert_eq!(user_profile.email, new_email,);
assert_eq!(user_profile.email, new_email,);
}
#[tokio::test]
async fn user_update_with_password() {
let sdk = FlowySDKTest::default();
let user = sdk.init_user().await;
let new_password = "H123world!".to_owned();
let request = UpdateUserProfilePayloadPB::new(&user.id).password(&new_password);
let sdk = FlowySDKTest::default();
let user = sdk.init_user().await;
let new_password = "H123world!".to_owned();
let request = UpdateUserProfilePayloadPB::new(&user.id).password(&new_password);
let _ = UserModuleEventBuilder::new(sdk.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send()
.assert_success();
let _ = UserModuleEventBuilder::new(sdk.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send()
.assert_success();
}
#[tokio::test]
async fn user_update_with_invalid_email() {
let test = FlowySDKTest::default();
let user = test.init_user().await;
for email in invalid_email_test_case() {
let request = UpdateUserProfilePayloadPB::new(&user.id).email(&email);
assert_eq!(
UserModuleEventBuilder::new(test.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send()
.error()
.code,
ErrorCode::EmailFormatInvalid.value()
);
}
let test = FlowySDKTest::default();
let user = test.init_user().await;
for email in invalid_email_test_case() {
let request = UpdateUserProfilePayloadPB::new(&user.id).email(&email);
assert_eq!(
UserModuleEventBuilder::new(test.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send()
.error()
.code,
ErrorCode::EmailFormatInvalid.value()
);
}
}
#[tokio::test]
async fn user_update_with_invalid_password() {
let test = FlowySDKTest::default();
let user = test.init_user().await;
for password in invalid_password_test_case() {
let request = UpdateUserProfilePayloadPB::new(&user.id).password(&password);
let test = FlowySDKTest::default();
let user = test.init_user().await;
for password in invalid_password_test_case() {
let request = UpdateUserProfilePayloadPB::new(&user.id).password(&password);
UserModuleEventBuilder::new(test.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send()
.assert_error();
}
UserModuleEventBuilder::new(test.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send()
.assert_error();
}
}
#[tokio::test]
async fn user_update_with_invalid_name() {
let test = FlowySDKTest::default();
let user = test.init_user().await;
let request = UpdateUserProfilePayloadPB::new(&user.id).name("");
UserModuleEventBuilder::new(test.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send()
.assert_error();
let test = FlowySDKTest::default();
let user = test.init_user().await;
let request = UpdateUserProfilePayloadPB::new(&user.id).name("");
UserModuleEventBuilder::new(test.clone())
.event(UpdateUserProfile)
.payload(request)
.sync_send()
.assert_error();
}