Bladeren bron

[feat]: get user detail & logout

appflowy 3 jaren geleden
bovenliggende
commit
3b06cde2d2

+ 6 - 3
rust-lib/flowy-database/src/lib.rs

@@ -15,7 +15,7 @@ pub use flowy_sqlite::{DBConnection, Database};
 
 
 use diesel_migrations::*;
 use diesel_migrations::*;
 use flowy_sqlite::{Error, PoolConfig};
 use flowy_sqlite::{Error, PoolConfig};
-use std::{io, path::Path};
+use std::{fmt::Debug, io, path::Path};
 
 
 embed_migrations!("../flowy-database/migrations/");
 embed_migrations!("../flowy-database/migrations/");
 pub const DB_NAME: &str = "flowy-database.db";
 pub const DB_NAME: &str = "flowy-database.db";
@@ -27,11 +27,14 @@ pub fn init(storage_path: &str) -> Result<Database, io::Error> {
     let pool_config = PoolConfig::default();
     let pool_config = PoolConfig::default();
     let database = Database::new(storage_path, DB_NAME, pool_config).map_err(as_io_error)?;
     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)?;
     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)
     Ok(database)
 }
 }
 
 
-fn as_io_error(e: Error) -> io::Error {
+fn as_io_error<E>(e: E) -> io::Error
+where
+    E: Into<Error> + Debug,
+{
     let msg = format!("{:?}", e);
     let msg = format!("{:?}", e);
     io::Error::new(io::ErrorKind::NotConnected, msg)
     io::Error::new(io::ErrorKind::NotConnected, msg)
 }
 }

+ 54 - 0
rust-lib/flowy-dispatch/src/byte_trait.rs

@@ -0,0 +1,54 @@
+// To bytes
+pub trait ToBytes {
+    fn into_bytes(self) -> Result<Vec<u8>, String>;
+}
+
+#[cfg(feature = "use_protobuf")]
+impl<T> ToBytes for T
+where
+    T: std::convert::TryInto<Vec<u8>, Error = String>,
+{
+    fn into_bytes(self) -> Result<Vec<u8>, String> { self.try_into() }
+}
+
+#[cfg(feature = "use_serde")]
+impl<T> ToBytes for T
+where
+    T: serde::Serialize,
+{
+    fn into_bytes(self) -> Result<Vec<u8>, 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<u8>) -> Result<Self, String>;
+}
+
+#[cfg(feature = "use_protobuf")]
+impl<T> FromBytes for T
+where
+    // https://stackoverflow.com/questions/62871045/tryfromu8-trait-bound-in-trait
+    T: for<'a> std::convert::TryFrom<&'a Vec<u8>, Error = String>,
+{
+    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, String> { T::try_from(bytes) }
+}
+
+#[cfg(feature = "use_serde")]
+impl<T> FromBytes for T
+where
+    T: serde::de::DeserializeOwned + 'static,
+{
+    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, String> {
+        let s = String::from_utf8_lossy(bytes);
+        match serde_json::from_str::<T>(s.as_ref()) {
+            Ok(data) => Ok(data),
+            Err(e) => Err(format!("{:?}", e)),
+        }
+    }
+}

+ 6 - 35
rust-lib/flowy-dispatch/src/data.rs

@@ -1,7 +1,8 @@
 use crate::{
 use crate::{
+    byte_trait::*,
     errors::{DispatchError, InternalError},
     errors::{DispatchError, InternalError},
     request::{unexpected_none_payload, EventRequest, FromRequest, Payload},
     request::{unexpected_none_payload, EventRequest, FromRequest, Payload},
-    response::{EventResponse, Responder, ResponseBuilder, ToBytes},
+    response::{EventResponse, Responder, ResponseBuilder},
     util::ready::{ready, Ready},
     util::ready::{ready, Ready},
 };
 };
 use std::ops;
 use std::ops;
@@ -22,33 +23,6 @@ impl<T> ops::DerefMut for Data<T> {
     fn deref_mut(&mut self) -> &mut T { &mut self.0 }
     fn deref_mut(&mut self) -> &mut T { &mut self.0 }
 }
 }
 
 
-pub trait FromBytes: Sized {
-    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, String>;
-}
-
-#[cfg(feature = "use_protobuf")]
-impl<T> FromBytes for T
-where
-    // https://stackoverflow.com/questions/62871045/tryfromu8-trait-bound-in-trait
-    T: for<'a> std::convert::TryFrom<&'a Vec<u8>, Error = String>,
-{
-    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, String> { T::try_from(bytes) }
-}
-
-#[cfg(feature = "use_serde")]
-impl<T> FromBytes for T
-where
-    T: serde::de::DeserializeOwned + 'static,
-{
-    fn parse_from_bytes(bytes: &Vec<u8>) -> Result<Self, String> {
-        let s = String::from_utf8_lossy(bytes);
-        match serde_json::from_str::<T>(s.as_ref()) {
-            Ok(data) => Ok(data),
-            Err(e) => Err(format!("{:?}", e)),
-        }
-    }
-}
-
 impl<T> FromRequest for Data<T>
 impl<T> FromRequest for Data<T>
 where
 where
     T: FromBytes + 'static,
     T: FromBytes + 'static,
@@ -83,13 +57,6 @@ where
     }
     }
 }
 }
 
 
-impl<T> std::convert::From<T> for Data<T>
-where
-    T: ToBytes,
-{
-    fn from(val: T) -> Self { Data(val) }
-}
-
 impl<T> std::convert::TryFrom<&Payload> for Data<T>
 impl<T> std::convert::TryFrom<&Payload> for Data<T>
 where
 where
     T: FromBytes,
     T: FromBytes,
@@ -131,3 +98,7 @@ where
         Ok(Payload::Bytes(bytes))
         Ok(Payload::Bytes(bytes))
     }
     }
 }
 }
+
+impl ToBytes for Data<String> {
+    fn into_bytes(self) -> Result<Vec<u8>, String> { Ok(self.0.into_bytes()) }
+}

+ 10 - 1
rust-lib/flowy-dispatch/src/lib.rs

@@ -7,6 +7,7 @@ mod response;
 mod service;
 mod service;
 mod util;
 mod util;
 
 
+mod byte_trait;
 mod data;
 mod data;
 mod dispatch;
 mod dispatch;
 mod system;
 mod system;
@@ -14,5 +15,13 @@ mod system;
 pub use errors::Error;
 pub use errors::Error;
 
 
 pub mod prelude {
 pub mod prelude {
-    pub use crate::{data::*, dispatch::*, errors::*, module::*, request::*, response::*};
+    pub use crate::{
+        byte_trait::*,
+        data::*,
+        dispatch::*,
+        errors::*,
+        module::*,
+        request::*,
+        response::*,
+    };
 }
 }

+ 4 - 0
rust-lib/flowy-dispatch/src/request/payload.rs

@@ -40,6 +40,10 @@ impl std::convert::Into<Payload> for Bytes {
     }
     }
 }
 }
 
 
+impl std::convert::Into<Payload> for () {
+    fn into(self) -> Payload { Payload::None }
+}
+
 impl std::convert::Into<Payload> for Vec<u8> {
 impl std::convert::Into<Payload> for Vec<u8> {
     fn into(self) -> Payload { Payload::Bytes(self) }
     fn into(self) -> Payload { Payload::Bytes(self) }
 }
 }

+ 1 - 25
rust-lib/flowy-dispatch/src/response/responder.rs

@@ -24,6 +24,7 @@ impl_responder!(&'static str);
 impl_responder!(String);
 impl_responder!(String);
 impl_responder!(&'_ String);
 impl_responder!(&'_ String);
 impl_responder!(Bytes);
 impl_responder!(Bytes);
+impl_responder!(());
 
 
 impl<T, E> Responder for Result<T, E>
 impl<T, E> Responder for Result<T, E>
 where
 where
@@ -37,28 +38,3 @@ where
         }
         }
     }
     }
 }
 }
-
-pub trait ToBytes {
-    fn into_bytes(self) -> Result<Vec<u8>, String>;
-}
-
-#[cfg(feature = "use_protobuf")]
-impl<T> ToBytes for T
-where
-    T: std::convert::TryInto<Vec<u8>, Error = String>,
-{
-    fn into_bytes(self) -> Result<Vec<u8>, String> { self.try_into() }
-}
-
-#[cfg(feature = "use_serde")]
-impl<T> ToBytes for T
-where
-    T: serde::Serialize,
-{
-    fn into_bytes(self) -> Result<Vec<u8>, String> {
-        match serde_json::to_string(&self.0) {
-            Ok(s) => Ok(s.into_bytes()),
-            Err(e) => Err(format!("{:?}", e)),
-        }
-    }
-}

+ 1 - 1
rust-lib/flowy-test/src/lib.rs

@@ -1,7 +1,7 @@
 use flowy_dispatch::prelude::*;
 use flowy_dispatch::prelude::*;
 pub use flowy_sdk::*;
 pub use flowy_sdk::*;
 use std::{
 use std::{
-    convert::TryFrom,
+    convert::{TryFrom, TryInto},
     fmt::{Debug, Display},
     fmt::{Debug, Display},
     fs,
     fs,
     hash::Hash,
     hash::Hash,

+ 2 - 1
rust-lib/flowy-user/Cargo.toml

@@ -34,4 +34,5 @@ fake = "~2.3.0"
 claim = "0.4.0"
 claim = "0.4.0"
 flowy-test = { path = "../flowy-test" }
 flowy-test = { path = "../flowy-test" }
 tokio = { version = "1", features = ["full"] }
 tokio = { version = "1", features = ["full"] }
-futures = "0.3.15"
+futures = "0.3.15"
+serial_test = "0.5.1"

+ 6 - 2
rust-lib/flowy-user/src/handlers/auth.rs

@@ -39,9 +39,13 @@ pub async fn user_sign_up(
 }
 }
 
 
 pub async fn user_get_status(
 pub async fn user_get_status(
-    user_id: String,
     session: ModuleData<Arc<UserSession>>,
     session: ModuleData<Arc<UserSession>>,
 ) -> ResponseResult<UserDetail, String> {
 ) -> ResponseResult<UserDetail, String> {
-    let user_detail = session.get_user_status(&user_id).await?;
+    let user_detail = session.current_user_detail().await?;
     response_ok(user_detail)
     response_ok(user_detail)
 }
 }
+
+pub async fn user_sign_out(session: ModuleData<Arc<UserSession>>) -> Result<(), String> {
+    let _ = session.sign_out().await?;
+    Ok(())
+}

+ 1 - 0
rust-lib/flowy-user/src/module.rs

@@ -10,4 +10,5 @@ pub fn create(user_session: Arc<UserSession>) -> Module {
         .event(UserEvent::SignIn, user_sign_in)
         .event(UserEvent::SignIn, user_sign_in)
         .event(UserEvent::SignUp, user_sign_up)
         .event(UserEvent::SignUp, user_sign_up)
         .event(UserEvent::GetStatus, user_get_status)
         .event(UserEvent::GetStatus, user_get_status)
+        .event(UserEvent::SignOut, user_sign_out)
 }
 }

+ 1 - 1
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 {
 pub struct UserSessionBuilder {
     config: Option<UserSessionConfig>,
     config: Option<UserSessionConfig>,

+ 1 - 1
rust-lib/flowy-user/src/services/user_session/database.rs

@@ -39,7 +39,7 @@ impl UserDB {
         Ok(())
         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);
         INIT_FLAG.store(false, Ordering::SeqCst);
 
 
         let mut write_guard = DB
         let mut write_guard = DB

+ 1 - 1
rust-lib/flowy-user/src/services/user_session/mod.rs

@@ -3,5 +3,5 @@ pub use user_session::*;
 
 
 mod builder;
 mod builder;
 pub mod database;
 pub mod database;
-mod register;
+mod user_server;
 mod user_session;
 mod user_session;

+ 0 - 67
rust-lib/flowy-user/src/services/user_session/register.rs

@@ -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<Option<String>> = RwLock::new(None);
-}
-const USER_ID: &str = "user_id";
-pub(crate) fn get_current_user_id() -> Result<Option<String>, 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<String>) -> 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<User, UserError>;
-    fn sign_in(&self, params: SignInParams) -> Result<User, UserError>;
-}
-
-pub struct MockUserServer {}
-
-impl UserServer for MockUserServer {
-    fn sign_up(&self, params: SignUpParams) -> Result<User, UserError> {
-        let user_id = "9527".to_owned();
-        Ok(User::new(
-            user_id,
-            params.name,
-            params.email,
-            params.password,
-        ))
-    }
-
-    fn sign_in(&self, params: SignInParams) -> Result<User, UserError> {
-        let user_id = "9527".to_owned();
-        Ok(User::new(
-            user_id,
-            "".to_owned(),
-            params.email,
-            params.password,
-        ))
-    }
-}

+ 46 - 0
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<User, UserError>;
+    fn sign_in(&self, params: SignInParams) -> Result<User, UserError>;
+    fn get_user_info(&self, user_id: &str) -> Result<UserDetail, UserError>;
+    fn sign_out(&self, user_id: &str) -> Result<(), UserError>;
+}
+
+pub struct MockUserServer {}
+
+impl UserServer for MockUserServer {
+    fn sign_up(&self, params: SignUpParams) -> Result<User, UserError> {
+        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<User, UserError> {
+        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<UserDetail, UserError> {
+        Err(UserError::Auth("WIP".to_owned()))
+    }
+
+    fn sign_out(&self, user_id: &str) -> Result<(), UserError> {
+        Err(UserError::Auth("WIP".to_owned()))
+    }
+}

+ 68 - 8
rust-lib/flowy-user/src/services/user_session/user_session.rs

@@ -13,7 +13,7 @@ use crate::{
     errors::UserError,
     errors::UserError,
     services::user_session::{
     services::user_session::{
         database::UserDB,
         database::UserDB,
-        register::{UserServer, *},
+        user_server::{UserServer, *},
     },
     },
     sql_tables::User,
     sql_tables::User,
 };
 };
@@ -61,21 +61,40 @@ impl UserSession {
         self.save_user(user)
         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)?;
         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<UserDetail, UserError> {
-        let user_id = UserId::parse(user_id.to_owned()).map_err(|e| UserError::Auth(e))?;
+    pub async fn current_user_detail(&self) -> Result<UserDetail, UserError> {
+        let user_id = self.current_user_id()?;
         let conn = self.get_db_connection()?;
         let conn = self.get_db_connection()?;
 
 
         let user = dsl::user_table
         let user = dsl::user_table
-            .filter(user_table::id.eq(user_id.as_ref()))
+            .filter(user_table::id.eq(&user_id))
             .first::<User>(&*conn)?;
             .first::<User>(&*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))
         Ok(UserDetail::from(user))
     }
     }
 
 
@@ -96,4 +115,45 @@ impl UserSession {
 
 
         Ok(user)
         Ok(user)
     }
     }
+
+    fn current_user_id(&self) -> Result<String, UserError> {
+        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<Option<String>> = RwLock::new(None);
+}
+pub(crate) fn get_current_user_id() -> Result<Option<String>, 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<String>) -> 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(())
 }
 }

+ 3 - 0
rust-lib/flowy-user/tests/event/sign_in_test.rs

@@ -1,7 +1,10 @@
 use crate::helper::*;
 use crate::helper::*;
 use flowy_test::prelude::*;
 use flowy_test::prelude::*;
 use flowy_user::{event::UserEvent::*, prelude::*};
 use flowy_user::{event::UserEvent::*, prelude::*};
+use serial_test::*;
+
 #[test]
 #[test]
+#[serial]
 fn sign_in_success() {
 fn sign_in_success() {
     let request = SignInRequest {
     let request = SignInRequest {
         email: valid_email(),
         email: valid_email(),

+ 2 - 0
rust-lib/flowy-user/tests/event/sign_up_test.rs

@@ -1,8 +1,10 @@
 use crate::helper::*;
 use crate::helper::*;
 use flowy_test::prelude::*;
 use flowy_test::prelude::*;
 use flowy_user::{event::UserEvent::*, prelude::*};
 use flowy_user::{event::UserEvent::*, prelude::*};
+use serial_test::*;
 
 
 #[test]
 #[test]
+#[serial]
 fn sign_up_success() {
 fn sign_up_success() {
     let request = SignUpRequest {
     let request = SignUpRequest {
         email: valid_email(),
         email: valid_email(),

+ 26 - 1
rust-lib/flowy-user/tests/event/user_status_test.rs

@@ -2,4 +2,29 @@ use crate::helper::*;
 use flowy_test::prelude::*;
 use flowy_test::prelude::*;
 use flowy_user::{event::UserEvent::*, prelude::*};
 use flowy_user::{event::UserEvent::*, prelude::*};
 #[test]
 #[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::<UserDetail>();
+}
+
+#[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::<UserDetail>();
+    dbg!(&response);
+
+    let _ = EventTester::new(GetStatus)
+        .sync_send()
+        .parse::<UserDetail>();
+}