diff --git a/rust-lib/flowy-database/src/lib.rs b/rust-lib/flowy-database/src/lib.rs index ce3a43f690..c36467fd41 100644 --- a/rust-lib/flowy-database/src/lib.rs +++ b/rust-lib/flowy-database/src/lib.rs @@ -15,7 +15,7 @@ pub use flowy_sqlite::{DBConnection, Database}; use diesel_migrations::*; use flowy_sqlite::{Error, PoolConfig}; -use std::{io, path::Path}; +use std::{fmt::Debug, io, path::Path}; embed_migrations!("../flowy-database/migrations/"); pub const DB_NAME: &str = "flowy-database.db"; @@ -27,11 +27,14 @@ pub fn init(storage_path: &str) -> Result { let pool_config = PoolConfig::default(); let database = Database::new(storage_path, DB_NAME, pool_config).map_err(as_io_error)?; let conn = database.get_connection().map_err(as_io_error)?; - embedded_migrations::run(&*conn); + let _ = embedded_migrations::run(&*conn).map_err(as_io_error)?; Ok(database) } -fn as_io_error(e: Error) -> io::Error { +fn as_io_error(e: E) -> io::Error +where + E: Into + Debug, +{ let msg = format!("{:?}", e); io::Error::new(io::ErrorKind::NotConnected, msg) } diff --git a/rust-lib/flowy-dispatch/src/byte_trait.rs b/rust-lib/flowy-dispatch/src/byte_trait.rs new file mode 100644 index 0000000000..6198cbef0f --- /dev/null +++ b/rust-lib/flowy-dispatch/src/byte_trait.rs @@ -0,0 +1,54 @@ +// To bytes +pub trait ToBytes { + fn into_bytes(self) -> Result, String>; +} + +#[cfg(feature = "use_protobuf")] +impl ToBytes for T +where + T: std::convert::TryInto, Error = String>, +{ + fn into_bytes(self) -> Result, String> { self.try_into() } +} + +#[cfg(feature = "use_serde")] +impl ToBytes for T +where + T: serde::Serialize, +{ + fn into_bytes(self) -> Result, String> { + match serde_json::to_string(&self.0) { + Ok(s) => Ok(s.into_bytes()), + Err(e) => Err(format!("{:?}", e)), + } + } +} + +// From bytes + +pub trait FromBytes: Sized { + fn parse_from_bytes(bytes: &Vec) -> Result; +} + +#[cfg(feature = "use_protobuf")] +impl FromBytes for T +where + // https://stackoverflow.com/questions/62871045/tryfromu8-trait-bound-in-trait + T: for<'a> std::convert::TryFrom<&'a Vec, Error = String>, +{ + fn parse_from_bytes(bytes: &Vec) -> Result { T::try_from(bytes) } +} + +#[cfg(feature = "use_serde")] +impl FromBytes for T +where + T: serde::de::DeserializeOwned + 'static, +{ + fn parse_from_bytes(bytes: &Vec) -> Result { + let s = String::from_utf8_lossy(bytes); + match serde_json::from_str::(s.as_ref()) { + Ok(data) => Ok(data), + Err(e) => Err(format!("{:?}", e)), + } + } +} diff --git a/rust-lib/flowy-dispatch/src/data.rs b/rust-lib/flowy-dispatch/src/data.rs index 8167e73421..0aaa94d41c 100644 --- a/rust-lib/flowy-dispatch/src/data.rs +++ b/rust-lib/flowy-dispatch/src/data.rs @@ -1,7 +1,8 @@ use crate::{ + byte_trait::*, errors::{DispatchError, InternalError}, request::{unexpected_none_payload, EventRequest, FromRequest, Payload}, - response::{EventResponse, Responder, ResponseBuilder, ToBytes}, + response::{EventResponse, Responder, ResponseBuilder}, util::ready::{ready, Ready}, }; use std::ops; @@ -22,33 +23,6 @@ impl ops::DerefMut for Data { fn deref_mut(&mut self) -> &mut T { &mut self.0 } } -pub trait FromBytes: Sized { - fn parse_from_bytes(bytes: &Vec) -> Result; -} - -#[cfg(feature = "use_protobuf")] -impl FromBytes for T -where - // https://stackoverflow.com/questions/62871045/tryfromu8-trait-bound-in-trait - T: for<'a> std::convert::TryFrom<&'a Vec, Error = String>, -{ - fn parse_from_bytes(bytes: &Vec) -> Result { T::try_from(bytes) } -} - -#[cfg(feature = "use_serde")] -impl FromBytes for T -where - T: serde::de::DeserializeOwned + 'static, -{ - fn parse_from_bytes(bytes: &Vec) -> Result { - let s = String::from_utf8_lossy(bytes); - match serde_json::from_str::(s.as_ref()) { - Ok(data) => Ok(data), - Err(e) => Err(format!("{:?}", e)), - } - } -} - impl FromRequest for Data where T: FromBytes + 'static, @@ -83,13 +57,6 @@ where } } -impl std::convert::From for Data -where - T: ToBytes, -{ - fn from(val: T) -> Self { Data(val) } -} - impl std::convert::TryFrom<&Payload> for Data where T: FromBytes, @@ -131,3 +98,7 @@ where Ok(Payload::Bytes(bytes)) } } + +impl ToBytes for Data { + fn into_bytes(self) -> Result, String> { Ok(self.0.into_bytes()) } +} diff --git a/rust-lib/flowy-dispatch/src/lib.rs b/rust-lib/flowy-dispatch/src/lib.rs index 25947f8b68..744f7abc0e 100644 --- a/rust-lib/flowy-dispatch/src/lib.rs +++ b/rust-lib/flowy-dispatch/src/lib.rs @@ -7,6 +7,7 @@ mod response; mod service; mod util; +mod byte_trait; mod data; mod dispatch; mod system; @@ -14,5 +15,13 @@ mod system; pub use errors::Error; pub mod prelude { - pub use crate::{data::*, dispatch::*, errors::*, module::*, request::*, response::*}; + pub use crate::{ + byte_trait::*, + data::*, + dispatch::*, + errors::*, + module::*, + request::*, + response::*, + }; } diff --git a/rust-lib/flowy-dispatch/src/request/payload.rs b/rust-lib/flowy-dispatch/src/request/payload.rs index 9145a0a845..049d1d93e8 100644 --- a/rust-lib/flowy-dispatch/src/request/payload.rs +++ b/rust-lib/flowy-dispatch/src/request/payload.rs @@ -40,6 +40,10 @@ impl std::convert::Into for Bytes { } } +impl std::convert::Into for () { + fn into(self) -> Payload { Payload::None } +} + impl std::convert::Into for Vec { fn into(self) -> Payload { Payload::Bytes(self) } } diff --git a/rust-lib/flowy-dispatch/src/response/responder.rs b/rust-lib/flowy-dispatch/src/response/responder.rs index 8a2bacdd53..00327072d9 100644 --- a/rust-lib/flowy-dispatch/src/response/responder.rs +++ b/rust-lib/flowy-dispatch/src/response/responder.rs @@ -24,6 +24,7 @@ impl_responder!(&'static str); impl_responder!(String); impl_responder!(&'_ String); impl_responder!(Bytes); +impl_responder!(()); impl Responder for Result where @@ -37,28 +38,3 @@ where } } } - -pub trait ToBytes { - fn into_bytes(self) -> Result, String>; -} - -#[cfg(feature = "use_protobuf")] -impl ToBytes for T -where - T: std::convert::TryInto, Error = String>, -{ - fn into_bytes(self) -> Result, String> { self.try_into() } -} - -#[cfg(feature = "use_serde")] -impl ToBytes for T -where - T: serde::Serialize, -{ - fn into_bytes(self) -> Result, String> { - match serde_json::to_string(&self.0) { - Ok(s) => Ok(s.into_bytes()), - Err(e) => Err(format!("{:?}", e)), - } - } -} diff --git a/rust-lib/flowy-test/src/lib.rs b/rust-lib/flowy-test/src/lib.rs index 0ce47e0bff..651951e813 100644 --- a/rust-lib/flowy-test/src/lib.rs +++ b/rust-lib/flowy-test/src/lib.rs @@ -1,7 +1,7 @@ use flowy_dispatch::prelude::*; pub use flowy_sdk::*; use std::{ - convert::TryFrom, + convert::{TryFrom, TryInto}, fmt::{Debug, Display}, fs, hash::Hash, diff --git a/rust-lib/flowy-user/Cargo.toml b/rust-lib/flowy-user/Cargo.toml index b9ea3c4d56..44dffac38f 100644 --- a/rust-lib/flowy-user/Cargo.toml +++ b/rust-lib/flowy-user/Cargo.toml @@ -34,4 +34,5 @@ fake = "~2.3.0" claim = "0.4.0" flowy-test = { path = "../flowy-test" } tokio = { version = "1", features = ["full"] } -futures = "0.3.15" \ No newline at end of file +futures = "0.3.15" +serial_test = "0.5.1" \ No newline at end of file diff --git a/rust-lib/flowy-user/src/handlers/auth.rs b/rust-lib/flowy-user/src/handlers/auth.rs index 23cc31c3b7..a173baa583 100644 --- a/rust-lib/flowy-user/src/handlers/auth.rs +++ b/rust-lib/flowy-user/src/handlers/auth.rs @@ -39,9 +39,13 @@ pub async fn user_sign_up( } pub async fn user_get_status( - user_id: String, session: ModuleData>, ) -> ResponseResult { - let user_detail = session.get_user_status(&user_id).await?; + let user_detail = session.current_user_detail().await?; response_ok(user_detail) } + +pub async fn user_sign_out(session: ModuleData>) -> Result<(), String> { + let _ = session.sign_out().await?; + Ok(()) +} diff --git a/rust-lib/flowy-user/src/module.rs b/rust-lib/flowy-user/src/module.rs index 025bdabc48..3b54441067 100644 --- a/rust-lib/flowy-user/src/module.rs +++ b/rust-lib/flowy-user/src/module.rs @@ -10,4 +10,5 @@ pub fn create(user_session: Arc) -> Module { .event(UserEvent::SignIn, user_sign_in) .event(UserEvent::SignUp, user_sign_up) .event(UserEvent::GetStatus, user_get_status) + .event(UserEvent::SignOut, user_sign_out) } diff --git a/rust-lib/flowy-user/src/services/user_session/builder.rs b/rust-lib/flowy-user/src/services/user_session/builder.rs index 042f4b2f72..fe893a71b4 100644 --- a/rust-lib/flowy-user/src/services/user_session/builder.rs +++ b/rust-lib/flowy-user/src/services/user_session/builder.rs @@ -1,4 +1,4 @@ -use crate::services::user_session::{register::MockUserServer, UserSession, UserSessionConfig}; +use crate::services::user_session::{user_server::MockUserServer, UserSession, UserSessionConfig}; pub struct UserSessionBuilder { config: Option, diff --git a/rust-lib/flowy-user/src/services/user_session/database.rs b/rust-lib/flowy-user/src/services/user_session/database.rs index a7749ea2c5..61fe9ea615 100644 --- a/rust-lib/flowy-user/src/services/user_session/database.rs +++ b/rust-lib/flowy-user/src/services/user_session/database.rs @@ -39,7 +39,7 @@ impl UserDB { Ok(()) } - pub(crate) fn close_user_db(&mut self) -> Result<(), UserError> { + pub(crate) fn close_user_db(&self) -> Result<(), UserError> { INIT_FLAG.store(false, Ordering::SeqCst); let mut write_guard = DB diff --git a/rust-lib/flowy-user/src/services/user_session/mod.rs b/rust-lib/flowy-user/src/services/user_session/mod.rs index ece4cb8ac0..c5a4914720 100644 --- a/rust-lib/flowy-user/src/services/user_session/mod.rs +++ b/rust-lib/flowy-user/src/services/user_session/mod.rs @@ -3,5 +3,5 @@ pub use user_session::*; mod builder; pub mod database; -mod register; +mod user_server; mod user_session; diff --git a/rust-lib/flowy-user/src/services/user_session/register.rs b/rust-lib/flowy-user/src/services/user_session/register.rs deleted file mode 100644 index 5baddb6b78..0000000000 --- a/rust-lib/flowy-user/src/services/user_session/register.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::sync::RwLock; - -use lazy_static::lazy_static; - -use flowy_infra::kv::KVStore; - -use crate::{ - entities::{SignInParams, SignUpParams}, - errors::UserError, - sql_tables::User, -}; - -lazy_static! { - pub static ref CURRENT_USER_ID: RwLock> = RwLock::new(None); -} -const USER_ID: &str = "user_id"; -pub(crate) fn get_current_user_id() -> Result, UserError> { - let read_guard = CURRENT_USER_ID - .read() - .map_err(|e| UserError::Auth(format!("Read current user id failed. {:?}", e)))?; - - let mut current_user_id = (*read_guard).clone(); - if current_user_id.is_none() { - current_user_id = KVStore::get_str(USER_ID); - } - - Ok(current_user_id) -} - -pub(crate) fn set_current_user_id(user_id: Option) -> Result<(), UserError> { - KVStore::set_str(USER_ID, user_id.clone().unwrap_or("".to_owned())); - - let mut current_user_id = CURRENT_USER_ID - .write() - .map_err(|e| UserError::Auth(format!("Write current user id failed. {:?}", e)))?; - *current_user_id = user_id; - Ok(()) -} - -pub trait UserServer { - fn sign_up(&self, params: SignUpParams) -> Result; - fn sign_in(&self, params: SignInParams) -> Result; -} - -pub struct MockUserServer {} - -impl UserServer for MockUserServer { - fn sign_up(&self, params: SignUpParams) -> Result { - let user_id = "9527".to_owned(); - Ok(User::new( - user_id, - params.name, - params.email, - params.password, - )) - } - - fn sign_in(&self, params: SignInParams) -> Result { - let user_id = "9527".to_owned(); - Ok(User::new( - user_id, - "".to_owned(), - params.email, - params.password, - )) - } -} diff --git a/rust-lib/flowy-user/src/services/user_session/user_server.rs b/rust-lib/flowy-user/src/services/user_session/user_server.rs new file mode 100644 index 0000000000..ab7b0ae409 --- /dev/null +++ b/rust-lib/flowy-user/src/services/user_session/user_server.rs @@ -0,0 +1,46 @@ +use crate::{ + entities::{SignInParams, SignUpParams, UserDetail}, + errors::UserError, + sql_tables::User, +}; +use std::sync::RwLock; + +pub trait UserServer { + fn sign_up(&self, params: SignUpParams) -> Result; + fn sign_in(&self, params: SignInParams) -> Result; + fn get_user_info(&self, user_id: &str) -> Result; + fn sign_out(&self, user_id: &str) -> Result<(), UserError>; +} + +pub struct MockUserServer {} + +impl UserServer for MockUserServer { + fn sign_up(&self, params: SignUpParams) -> Result { + let user_id = "9527".to_owned(); + // let user_id = uuid(); + Ok(User::new( + user_id, + params.name, + params.email, + params.password, + )) + } + + fn sign_in(&self, params: SignInParams) -> Result { + let user_id = "9527".to_owned(); + Ok(User::new( + user_id, + "".to_owned(), + params.email, + params.password, + )) + } + + fn get_user_info(&self, user_id: &str) -> Result { + Err(UserError::Auth("WIP".to_owned())) + } + + fn sign_out(&self, user_id: &str) -> Result<(), UserError> { + Err(UserError::Auth("WIP".to_owned())) + } +} diff --git a/rust-lib/flowy-user/src/services/user_session/user_session.rs b/rust-lib/flowy-user/src/services/user_session/user_session.rs index 45e7f99fbe..4d542c5083 100644 --- a/rust-lib/flowy-user/src/services/user_session/user_session.rs +++ b/rust-lib/flowy-user/src/services/user_session/user_session.rs @@ -13,7 +13,7 @@ use crate::{ errors::UserError, services::user_session::{ database::UserDB, - register::{UserServer, *}, + user_server::{UserServer, *}, }, sql_tables::User, }; @@ -61,21 +61,40 @@ impl UserSession { self.save_user(user) } - pub fn sign_out(&self) -> Result<(), UserError> { + pub async fn sign_out(&self) -> Result<(), UserError> { + let user_id = self.current_user_id()?; + let conn = self.get_db_connection()?; + let affected = + diesel::delete(dsl::user_table.filter(dsl::id.eq(&user_id))).execute(&*conn)?; + + match self.server.sign_out(&user_id) { + Ok(_) => {}, + Err(_) => {}, + } + let _ = self.database.close_user_db()?; let _ = set_current_user_id(None)?; - // TODO: close the db - unimplemented!() + // debug_assert_eq!(affected, 1); + + Ok(()) } - pub async fn get_user_status(&self, user_id: &str) -> Result { - let user_id = UserId::parse(user_id.to_owned()).map_err(|e| UserError::Auth(e))?; + pub async fn current_user_detail(&self) -> Result { + let user_id = self.current_user_id()?; let conn = self.get_db_connection()?; let user = dsl::user_table - .filter(user_table::id.eq(user_id.as_ref())) + .filter(user_table::id.eq(&user_id)) .first::(&*conn)?; - // TODO: getting user detail from remote + match self.server.get_user_info(&user_id) { + Ok(_user_detail) => { + // TODO: post latest user_detail to upper layer + }, + Err(_e) => { + // log::debug!("Get user details failed. {:?}", e); + }, + } + Ok(UserDetail::from(user)) } @@ -96,4 +115,45 @@ impl UserSession { Ok(user) } + + fn current_user_id(&self) -> Result { + match KVStore::get_str(USER_ID_DISK_CACHE_KEY) { + None => Err(UserError::Auth("No login user found".to_owned())), + Some(user_id) => Ok(user_id), + } + } +} + +const USER_ID_DISK_CACHE_KEY: &str = "user_id"; +lazy_static! { + pub static ref CURRENT_USER_ID: RwLock> = RwLock::new(None); +} +pub(crate) fn get_current_user_id() -> Result, UserError> { + let read_guard = CURRENT_USER_ID + .read() + .map_err(|e| UserError::Auth(format!("Read current user id failed. {:?}", e)))?; + + let mut user_id = (*read_guard).clone(); + // explicitly drop the read_guard in case of dead lock + drop(read_guard); + + if user_id.is_none() { + user_id = KVStore::get_str(USER_ID_DISK_CACHE_KEY); + *(CURRENT_USER_ID.write().unwrap()) = user_id.clone(); + } + + Ok(user_id) +} + +pub(crate) fn set_current_user_id(user_id: Option) -> Result<(), UserError> { + KVStore::set_str( + USER_ID_DISK_CACHE_KEY, + user_id.clone().unwrap_or("".to_owned()), + ); + + let mut current_user_id = CURRENT_USER_ID + .write() + .map_err(|e| UserError::Auth(format!("Write current user id failed. {:?}", e)))?; + *current_user_id = user_id; + Ok(()) } diff --git a/rust-lib/flowy-user/tests/event/sign_in_test.rs b/rust-lib/flowy-user/tests/event/sign_in_test.rs index 38d6229608..c5f4a33350 100644 --- a/rust-lib/flowy-user/tests/event/sign_in_test.rs +++ b/rust-lib/flowy-user/tests/event/sign_in_test.rs @@ -1,7 +1,10 @@ use crate::helper::*; use flowy_test::prelude::*; use flowy_user::{event::UserEvent::*, prelude::*}; +use serial_test::*; + #[test] +#[serial] fn sign_in_success() { let request = SignInRequest { email: valid_email(), diff --git a/rust-lib/flowy-user/tests/event/sign_up_test.rs b/rust-lib/flowy-user/tests/event/sign_up_test.rs index ddaadeb8bd..9c7924ad77 100644 --- a/rust-lib/flowy-user/tests/event/sign_up_test.rs +++ b/rust-lib/flowy-user/tests/event/sign_up_test.rs @@ -1,8 +1,10 @@ use crate::helper::*; use flowy_test::prelude::*; use flowy_user::{event::UserEvent::*, prelude::*}; +use serial_test::*; #[test] +#[serial] fn sign_up_success() { let request = SignUpRequest { email: valid_email(), diff --git a/rust-lib/flowy-user/tests/event/user_status_test.rs b/rust-lib/flowy-user/tests/event/user_status_test.rs index 1eccf5fc5e..05b7adad9a 100644 --- a/rust-lib/flowy-user/tests/event/user_status_test.rs +++ b/rust-lib/flowy-user/tests/event/user_status_test.rs @@ -2,4 +2,29 @@ use crate::helper::*; use flowy_test::prelude::*; use flowy_user::{event::UserEvent::*, prelude::*}; #[test] -fn user_status_get_after_login() {} +#[should_panic] +fn user_status_not_found_before_login() { + let _ = EventTester::new(SignOut).sync_send(); + let _ = EventTester::new(GetStatus) + .sync_send() + .parse::(); +} + +#[test] +fn user_status_did_found_after_login() { + let _ = EventTester::new(SignOut).sync_send(); + let request = SignInRequest { + email: valid_email(), + password: valid_password(), + }; + + let response = EventTester::new(SignIn) + .request(request) + .sync_send() + .parse::(); + dbg!(&response); + + let _ = EventTester::new(GetStatus) + .sync_send() + .parse::(); +}