mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: run rustfmt with custom defined fmt configuration (#1848)
* chore: update rustfmt * chore: apply rustfmt format
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -10,5 +10,5 @@ pub mod services;
|
||||
extern crate flowy_sqlite;
|
||||
|
||||
pub mod errors {
|
||||
pub use flowy_error::*;
|
||||
pub use flowy_error::*;
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(¶ms.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(¶ms.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(¶ms.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(¶ms.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()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
Reference in New Issue
Block a user