mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[feat]: get user detail & logout
This commit is contained in:
parent
a6e32627d4
commit
3b06cde2d2
@ -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<Database, io::Error> {
|
||||
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: E) -> io::Error
|
||||
where
|
||||
E: Into<Error> + Debug,
|
||||
{
|
||||
let msg = format!("{:?}", e);
|
||||
io::Error::new(io::ErrorKind::NotConnected, msg)
|
||||
}
|
||||
|
54
rust-lib/flowy-dispatch/src/byte_trait.rs
Normal file
54
rust-lib/flowy-dispatch/src/byte_trait.rs
Normal file
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
@ -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<T> ops::DerefMut for Data<T> {
|
||||
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>
|
||||
where
|
||||
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>
|
||||
where
|
||||
T: FromBytes,
|
||||
@ -131,3 +98,7 @@ where
|
||||
Ok(Payload::Bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBytes for Data<String> {
|
||||
fn into_bytes(self) -> Result<Vec<u8>, String> { Ok(self.0.into_bytes()) }
|
||||
}
|
||||
|
@ -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::*,
|
||||
};
|
||||
}
|
||||
|
@ -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> {
|
||||
fn into(self) -> Payload { Payload::Bytes(self) }
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ impl_responder!(&'static str);
|
||||
impl_responder!(String);
|
||||
impl_responder!(&'_ String);
|
||||
impl_responder!(Bytes);
|
||||
impl_responder!(());
|
||||
|
||||
impl<T, E> Responder for Result<T, E>
|
||||
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,7 +1,7 @@
|
||||
use flowy_dispatch::prelude::*;
|
||||
pub use flowy_sdk::*;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::{Debug, Display},
|
||||
fs,
|
||||
hash::Hash,
|
||||
|
@ -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"
|
||||
futures = "0.3.15"
|
||||
serial_test = "0.5.1"
|
@ -39,9 +39,13 @@ pub async fn user_sign_up(
|
||||
}
|
||||
|
||||
pub async fn user_get_status(
|
||||
user_id: String,
|
||||
session: ModuleData<Arc<UserSession>>,
|
||||
) -> 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)
|
||||
}
|
||||
|
||||
pub async fn user_sign_out(session: ModuleData<Arc<UserSession>>) -> Result<(), String> {
|
||||
let _ = session.sign_out().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -10,4 +10,5 @@ pub fn create(user_session: Arc<UserSession>) -> 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)
|
||||
}
|
||||
|
@ -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<UserSessionConfig>,
|
||||
|
@ -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
|
||||
|
@ -3,5 +3,5 @@ pub use user_session::*;
|
||||
|
||||
mod builder;
|
||||
pub mod database;
|
||||
mod register;
|
||||
mod user_server;
|
||||
mod user_session;
|
||||
|
@ -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
rust-lib/flowy-user/src/services/user_session/user_server.rs
Normal file
46
rust-lib/flowy-user/src/services/user_session/user_server.rs
Normal file
@ -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()))
|
||||
}
|
||||
}
|
@ -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<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 user = dsl::user_table
|
||||
.filter(user_table::id.eq(user_id.as_ref()))
|
||||
.filter(user_table::id.eq(&user_id))
|
||||
.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))
|
||||
}
|
||||
|
||||
@ -96,4 +115,45 @@ impl UserSession {
|
||||
|
||||
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(())
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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::<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>();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user