From dfc2cbff4f4c5af25ba010b16f876b9b773a483d Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 6 Jul 2021 14:14:47 +0800 Subject: [PATCH] add flowy-test crate --- .idea/appflowy_client.iml | 2 + rust-lib/.gitignore | 1 + rust-lib/Cargo.toml | 1 + rust-lib/dart-ffi/src/lib.rs | 2 +- rust-lib/flowy-derive/src/lib.rs | 1 - rust-lib/flowy-sdk/tests/sdk/helper.rs | 79 ------------ rust-lib/flowy-sdk/tests/sdk/main.rs | 2 - rust-lib/flowy-sdk/tests/sdk/user/sign_in.rs | 23 ---- rust-lib/flowy-sys/src/data.rs | 121 ++++++++++++++++++ rust-lib/flowy-sys/src/dispatch.rs | 20 ++- rust-lib/flowy-sys/src/error/mod.rs | 2 - rust-lib/flowy-sys/src/lib.rs | 3 +- rust-lib/flowy-sys/src/request/payload.rs | 10 ++ rust-lib/flowy-sys/src/request/request.rs | 65 +--------- rust-lib/flowy-sys/src/response/responder.rs | 24 +--- rust-lib/flowy-sys/src/response/response.rs | 5 +- rust-lib/flowy-sys/tests/api/module.rs | 2 +- rust-lib/flowy-test/Cargo.toml | 17 +++ rust-lib/flowy-test/src/lib.rs | 105 +++++++++++++++ rust-lib/flowy-user/Cargo.toml | 5 +- rust-lib/flowy-user/src/domain/user/user.rs | 1 - .../flowy-user/src/domain/user/user_email.rs | 6 +- .../flowy-user/src/domain/user/user_name.rs | 2 +- .../src/domain/user/user_password.rs | 39 +++++- rust-lib/flowy-user/src/event.rs | 13 -- rust-lib/flowy-user/src/lib.rs | 7 +- rust-lib/flowy-user/src/module.rs | 20 ++- .../user/mod.rs => flowy-user/tests/main.rs} | 0 rust-lib/flowy-user/tests/sign_in.rs | 32 +++++ 29 files changed, 383 insertions(+), 227 deletions(-) delete mode 100644 rust-lib/flowy-sdk/tests/sdk/helper.rs delete mode 100644 rust-lib/flowy-sdk/tests/sdk/main.rs delete mode 100644 rust-lib/flowy-sdk/tests/sdk/user/sign_in.rs create mode 100644 rust-lib/flowy-sys/src/data.rs create mode 100644 rust-lib/flowy-test/Cargo.toml create mode 100644 rust-lib/flowy-test/src/lib.rs delete mode 100644 rust-lib/flowy-user/src/event.rs rename rust-lib/{flowy-sdk/tests/sdk/user/mod.rs => flowy-user/tests/main.rs} (100%) create mode 100644 rust-lib/flowy-user/tests/sign_in.rs diff --git a/.idea/appflowy_client.iml b/.idea/appflowy_client.iml index b16b5270ed..2f29ad0a6f 100644 --- a/.idea/appflowy_client.iml +++ b/.idea/appflowy_client.iml @@ -14,6 +14,8 @@ + + diff --git a/rust-lib/.gitignore b/rust-lib/.gitignore index 0f1114ec62..e2b01170f3 100644 --- a/rust-lib/.gitignore +++ b/rust-lib/.gitignore @@ -9,4 +9,5 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk **/**/*.log* +**/**/temp bin/ \ No newline at end of file diff --git a/rust-lib/Cargo.toml b/rust-lib/Cargo.toml index 1e07ba2a25..072d81dbfb 100644 --- a/rust-lib/Cargo.toml +++ b/rust-lib/Cargo.toml @@ -7,6 +7,7 @@ members = [ "flowy-user", "flowy-ast", "flowy-derive", + "flowy-test", ] [profile.dev] diff --git a/rust-lib/dart-ffi/src/lib.rs b/rust-lib/dart-ffi/src/lib.rs index 44619d9cd8..4efcc4538d 100644 --- a/rust-lib/dart-ffi/src/lib.rs +++ b/rust-lib/dart-ffi/src/lib.rs @@ -93,7 +93,7 @@ impl std::convert::From for DispatchRequest { } else { Payload::None }; - let request = DispatchRequest::new(ffi_request.event, payload); + let request = DispatchRequest::new(ffi_request.event).payload(payload); request } } diff --git a/rust-lib/flowy-derive/src/lib.rs b/rust-lib/flowy-derive/src/lib.rs index 870438d6a6..e12193cd93 100644 --- a/rust-lib/flowy-derive/src/lib.rs +++ b/rust-lib/flowy-derive/src/lib.rs @@ -1,5 +1,4 @@ // https://docs.rs/syn/1.0.48/syn/struct.DeriveInput.html -#![feature(str_split_once)] extern crate proc_macro; use proc_macro::TokenStream; diff --git a/rust-lib/flowy-sdk/tests/sdk/helper.rs b/rust-lib/flowy-sdk/tests/sdk/helper.rs deleted file mode 100644 index f79f5f0e1a..0000000000 --- a/rust-lib/flowy-sdk/tests/sdk/helper.rs +++ /dev/null @@ -1,79 +0,0 @@ -pub use flowy_sdk::*; -use flowy_sys::prelude::*; -use std::{ - fmt::{Debug, Display}, - fs, - hash::Hash, - sync::Once, -}; - -static INIT: Once = Once::new(); -pub fn init_sdk() { - let root_dir = root_dir(); - - INIT.call_once(|| { - FlowySDK::init_log(&root_dir); - }); - FlowySDK::init(&root_dir); -} - -fn root_dir() -> String { - let mut path = fs::canonicalize(".").unwrap(); - path.push("tests/temp/flowy/"); - let path_str = path.to_str().unwrap().to_string(); - if !std::path::Path::new(&path).exists() { - std::fs::create_dir_all(path).unwrap(); - } - path_str -} - -pub struct EventTester { - request: DispatchRequest, -} - -impl EventTester { - pub fn new(event: E, payload: P) -> Self - where - E: Eq + Hash + Debug + Clone + Display, - P: std::convert::Into, - { - init_sdk(); - Self { - request: DispatchRequest::new(event, payload.into()), - } - } - - // #[allow(dead_code)] - // pub fn bytes_payload(mut self, payload: T) -> Self - // where - // T: serde::Serialize, - // { - // let bytes: Vec = bincode::serialize(&payload).unwrap(); - // self.request = self.request.payload(Payload::Bytes(bytes)); - // self - // } - // - // #[allow(dead_code)] - // pub fn protobuf_payload(mut self, payload: T) -> Self - // where - // T: ::protobuf::Message, - // { - // let bytes: Vec = payload.write_to_bytes().unwrap(); - // self.request = self.request.payload(Payload::Bytes(bytes)); - // self - // } - - #[allow(dead_code)] - pub async fn async_send(self) -> EventResponse { - let resp = async_send(self.request).await; - dbg!(&resp); - resp - } - - #[allow(dead_code)] - pub fn sync_send(self) -> EventResponse { - let resp = sync_send(self.request); - dbg!(&resp); - resp - } -} diff --git a/rust-lib/flowy-sdk/tests/sdk/main.rs b/rust-lib/flowy-sdk/tests/sdk/main.rs deleted file mode 100644 index d4d0cd0c6c..0000000000 --- a/rust-lib/flowy-sdk/tests/sdk/main.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod helper; -mod user; diff --git a/rust-lib/flowy-sdk/tests/sdk/user/sign_in.rs b/rust-lib/flowy-sdk/tests/sdk/user/sign_in.rs deleted file mode 100644 index 29bb1d446d..0000000000 --- a/rust-lib/flowy-sdk/tests/sdk/user/sign_in.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::helper::*; -use flowy_sys::prelude::*; -use flowy_user::prelude::*; -use std::convert::{TryFrom, TryInto}; - -#[test] -fn sign_in_without_password() { - let params = UserSignInParams { - email: "annie@appflowy.io".to_string(), - password: "".to_string(), - }; - let bytes: Vec = params.try_into().unwrap(); - let resp = EventTester::new(SignIn, Payload::Bytes(bytes)).sync_send(); - match resp.payload { - Payload::None => {}, - Payload::Bytes(bytes) => { - let result = UserSignInResult::try_from(&bytes).unwrap(); - dbg!(&result); - }, - } - - assert_eq!(resp.status_code, StatusCode::Ok); -} diff --git a/rust-lib/flowy-sys/src/data.rs b/rust-lib/flowy-sys/src/data.rs new file mode 100644 index 0000000000..e8f6cdf0a4 --- /dev/null +++ b/rust-lib/flowy-sys/src/data.rs @@ -0,0 +1,121 @@ +use crate::{ + error::{InternalError, SystemError}, + request::{unexpected_none_payload, EventRequest, FromRequest, Payload}, + response::{EventResponse, Responder, ResponseBuilder, ToBytes}, + util::ready::{ready, Ready}, +}; +use std::ops; + +pub struct Data(pub T); + +impl Data { + pub fn into_inner(self) -> T { self.0 } +} + +impl ops::Deref for Data { + type Target = T; + + fn deref(&self) -> &T { &self.0 } +} + +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, +{ + type Error = SystemError; + type Future = Ready>; + + #[inline] + fn from_request(req: &EventRequest, payload: &mut Payload) -> Self::Future { + match payload { + Payload::None => ready(Err(unexpected_none_payload(req))), + Payload::Bytes(bytes) => match T::parse_from_bytes(bytes) { + Ok(data) => ready(Ok(Data(data))), + Err(e) => ready(Err(InternalError::new(format!("{:?}", e)).into())), + }, + } + } +} + +impl Responder for Data +where + T: ToBytes, +{ + fn respond_to(self, _request: &EventRequest) -> EventResponse { + match self.into_inner().into_bytes() { + Ok(bytes) => ResponseBuilder::Ok().data(bytes.to_vec()).build(), + Err(e) => { + let system_err: SystemError = InternalError::new(format!("{:?}", e)).into(); + system_err.into() + }, + } + } +} + +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, +{ + type Error = String; + + fn try_from(payload: &Payload) -> Result, Self::Error> { + match payload { + Payload::None => Err(format!("Expected payload")), + Payload::Bytes(bytes) => match T::parse_from_bytes(bytes) { + Ok(data) => Ok(Data(data)), + Err(e) => Err(e), + }, + } + } +} + +impl std::convert::TryInto for Data +where + T: ToBytes, +{ + type Error = String; + + fn try_into(self) -> Result { + let inner = self.into_inner(); + let bytes = inner.into_bytes()?; + Ok(Payload::Bytes(bytes)) + } +} diff --git a/rust-lib/flowy-sys/src/dispatch.rs b/rust-lib/flowy-sys/src/dispatch.rs index b7367b9a9c..84a764562c 100644 --- a/rust-lib/flowy-sys/src/dispatch.rs +++ b/rust-lib/flowy-sys/src/dispatch.rs @@ -12,6 +12,7 @@ use futures_util::task::Context; use lazy_static::lazy_static; use pin_project::pin_project; use std::{ + convert::TryInto, fmt::{Debug, Display}, future::Future, hash::Hash, @@ -115,18 +116,33 @@ pub struct DispatchRequest { } impl DispatchRequest { - pub fn new(event: E, payload: Payload) -> Self + pub fn new(event: E) -> Self where E: Eq + Hash + Debug + Clone + Display, { Self { - payload, + payload: Payload::None, event: event.into(), id: uuid::Uuid::new_v4().to_string(), callback: None, } } + pub fn payload

(mut self, payload: P) -> Self + where + P: TryInto, + { + let payload = match payload.try_into() { + Ok(payload) => payload, + Err(e) => { + log::error!("{}", e); + Payload::None + }, + }; + self.payload = payload; + self + } + pub fn callback(mut self, callback: BoxFutureCallback) -> Self { self.callback = Some(callback); self diff --git a/rust-lib/flowy-sys/src/error/mod.rs b/rust-lib/flowy-sys/src/error/mod.rs index feb53da209..3925f93ff7 100644 --- a/rust-lib/flowy-sys/src/error/mod.rs +++ b/rust-lib/flowy-sys/src/error/mod.rs @@ -1,5 +1,3 @@ mod error; -pub type ResponseResult = std::result::Result, E>; - pub use error::*; diff --git a/rust-lib/flowy-sys/src/lib.rs b/rust-lib/flowy-sys/src/lib.rs index a89876bb3e..71cdcaa15a 100644 --- a/rust-lib/flowy-sys/src/lib.rs +++ b/rust-lib/flowy-sys/src/lib.rs @@ -8,9 +8,10 @@ mod rt; mod service; mod util; +mod data; mod dispatch; mod system; pub mod prelude { - pub use crate::{dispatch::*, error::*, module::*, request::*, response::*, rt::*}; + pub use crate::{data::*, dispatch::*, error::*, module::*, request::*, response::*, rt::*}; } diff --git a/rust-lib/flowy-sys/src/request/payload.rs b/rust-lib/flowy-sys/src/request/payload.rs index 19b3ffdf5b..318897f669 100644 --- a/rust-lib/flowy-sys/src/request/payload.rs +++ b/rust-lib/flowy-sys/src/request/payload.rs @@ -38,6 +38,16 @@ impl std::convert::Into for Vec { fn into(self) -> Payload { Payload::Bytes(self) } } +// = note: conflicting implementation in crate `core`: +// - impl TryInto for T where U: TryFrom; +// +// impl std::convert::TryInto for Vec { +// type Error = String; +// fn try_into(self) -> Result { +// Ok(Payload::Bytes(self)) +// } +// } + impl std::convert::Into for &str { fn into(self) -> Payload { self.to_string().into() } } diff --git a/rust-lib/flowy-sys/src/request/request.rs b/rust-lib/flowy-sys/src/request/request.rs index 0da8823c40..6f1d82ac89 100644 --- a/rust-lib/flowy-sys/src/request/request.rs +++ b/rust-lib/flowy-sys/src/request/request.rs @@ -10,7 +10,6 @@ use crate::{ use futures_core::ready; use std::{ fmt::Debug, - ops, pin::Pin, task::{Context, Poll}, }; @@ -61,7 +60,7 @@ impl FromRequest for String { } } -fn unexpected_none_payload(request: &EventRequest) -> SystemError { +pub fn unexpected_none_payload(request: &EventRequest) -> SystemError { log::warn!("{:?} expected payload", &request.event); InternalError::new("Expected payload").into() } @@ -99,65 +98,3 @@ where Poll::Ready(Ok(res)) } } - -pub struct Data(pub T); - -impl Data { - pub fn into_inner(self) -> T { self.0 } -} - -impl ops::Deref for Data { - type Target = T; - - fn deref(&self) -> &T { &self.0 } -} - -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, -{ - type Error = SystemError; - type Future = Ready>; - - #[inline] - fn from_request(req: &EventRequest, payload: &mut Payload) -> Self::Future { - match payload { - Payload::None => ready(Err(unexpected_none_payload(req))), - Payload::Bytes(bytes) => match T::parse_from_bytes(bytes) { - Ok(data) => ready(Ok(Data(data))), - Err(e) => ready(Err(InternalError::new(format!("{:?}", e)).into())), - }, - } - } -} diff --git a/rust-lib/flowy-sys/src/response/responder.rs b/rust-lib/flowy-sys/src/response/responder.rs index b397b4342f..7acbba0266 100644 --- a/rust-lib/flowy-sys/src/response/responder.rs +++ b/rust-lib/flowy-sys/src/response/responder.rs @@ -1,7 +1,7 @@ #[allow(unused_imports)] use crate::error::{InternalError, SystemError}; use crate::{ - request::{Data, EventRequest}, + request::EventRequest, response::{EventResponse, ResponseBuilder}, }; use bytes::Bytes; @@ -62,25 +62,3 @@ where } } } - -impl Responder for Data -where - T: ToBytes, -{ - fn respond_to(self, _request: &EventRequest) -> EventResponse { - match self.into_inner().into_bytes() { - Ok(bytes) => ResponseBuilder::Ok().data(bytes.to_vec()).build(), - Err(e) => { - let system_err: SystemError = InternalError::new(format!("{:?}", e)).into(); - system_err.into() - }, - } - } -} - -impl std::convert::From for Data -where - T: ToBytes, -{ - fn from(val: T) -> Self { Data(val) } -} diff --git a/rust-lib/flowy-sys/src/response/response.rs b/rust-lib/flowy-sys/src/response/response.rs index 3b9ef88fd3..c3c9a2e035 100644 --- a/rust-lib/flowy-sys/src/response/response.rs +++ b/rust-lib/flowy-sys/src/response/response.rs @@ -1,6 +1,7 @@ use crate::{ + data::Data, error::SystemError, - request::{Data, EventRequest, Payload}, + request::{EventRequest, Payload}, response::Responder, }; use std::{fmt, fmt::Formatter}; @@ -51,6 +52,8 @@ impl Responder for EventResponse { fn respond_to(self, _: &EventRequest) -> EventResponse { self } } +pub type ResponseResult = std::result::Result, E>; + pub fn response_ok(data: T) -> Result, E> where E: Into, diff --git a/rust-lib/flowy-sys/tests/api/module.rs b/rust-lib/flowy-sys/tests/api/module.rs index 015ac65b38..b5d7ecf066 100644 --- a/rust-lib/flowy-sys/tests/api/module.rs +++ b/rust-lib/flowy-sys/tests/api/module.rs @@ -9,7 +9,7 @@ async fn test_init() { let event = "1"; init_dispatch(|| vec![Module::new().event(event, hello)]); - let request = DispatchRequest::new(event, Payload::None); + let request = DispatchRequest::new(event); let resp = async_send(request).await; dbg!(&resp); } diff --git a/rust-lib/flowy-test/Cargo.toml b/rust-lib/flowy-test/Cargo.toml new file mode 100644 index 0000000000..d0d1a6f7f3 --- /dev/null +++ b/rust-lib/flowy-test/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "flowy-test" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +flowy-sdk = { path = "../flowy-sdk"} +flowy-sys = { path = "../flowy-sys"} + +serde = { version = "1.0", features = ["derive"] } +bincode = { version = "1.3"} +protobuf = {version = "2.24.1"} +claim = "0.5.0" +tokio = { version = "1", features = ["full"]} +futures-util = "0.3.15" \ No newline at end of file diff --git a/rust-lib/flowy-test/src/lib.rs b/rust-lib/flowy-test/src/lib.rs new file mode 100644 index 0000000000..92c5162ac9 --- /dev/null +++ b/rust-lib/flowy-test/src/lib.rs @@ -0,0 +1,105 @@ +pub use flowy_sdk::*; +use flowy_sys::prelude::*; +use std::{ + convert::TryFrom, + fmt::{Debug, Display}, + fs, + hash::Hash, + path::PathBuf, + sync::Once, +}; + +pub mod prelude { + pub use crate::EventTester; + pub use flowy_sys::prelude::*; + pub use std::convert::TryFrom; +} + +static INIT: Once = Once::new(); +pub fn init_sdk() { + let root_dir = root_dir(); + + INIT.call_once(|| { + FlowySDK::init_log(&root_dir); + }); + FlowySDK::init(&root_dir); +} + +fn root_dir() -> String { + // https://doc.rust-lang.org/cargo/reference/environment-variables.html + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or("./".to_owned()); + let mut path_buf = fs::canonicalize(&PathBuf::from(&manifest_dir)).unwrap(); + path_buf.pop(); // rust-lib + path_buf.push("flowy-test"); + path_buf.push("temp"); + path_buf.push("flowy"); + + let root_dir = path_buf.to_str().unwrap().to_string(); + if !std::path::Path::new(&root_dir).exists() { + std::fs::create_dir_all(&root_dir).unwrap(); + } + root_dir +} + +pub struct EventTester { + request: DispatchRequest, + assert_status_code: Option, +} + +impl EventTester { + pub fn new(event: E) -> Self + where + E: Eq + Hash + Debug + Clone + Display, + { + init_sdk(); + let request = DispatchRequest::new(event); + Self { + request, + assert_status_code: None, + } + } + + pub fn payload

(mut self, payload: P) -> Self + where + P: ToBytes, + { + self.request = self.request.payload(Data(payload)); + self + } + + pub fn assert_status_code(mut self, status_code: StatusCode) -> Self { + self.assert_status_code = Some(status_code); + self + } + + #[allow(dead_code)] + pub async fn async_send(self) -> R + where + R: FromBytes, + { + let resp = async_send(self.request).await; + dbg!(&resp); + data_from_response(&resp) + } + + pub fn sync_send(self) -> R + where + R: FromBytes, + { + let resp = sync_send(self.request); + if let Some(status_code) = self.assert_status_code { + assert_eq!(resp.status_code, status_code) + } + + dbg!(&resp); + data_from_response(&resp) + } +} + +fn data_from_response(response: &EventResponse) -> R +where + R: FromBytes, +{ + let result = >::try_from(&response.payload).unwrap().into_inner(); + result +} diff --git a/rust-lib/flowy-user/Cargo.toml b/rust-lib/flowy-user/Cargo.toml index 04e23fdb60..4510fadd60 100644 --- a/rust-lib/flowy-user/Cargo.toml +++ b/rust-lib/flowy-user/Cargo.toml @@ -18,9 +18,12 @@ rand = { version = "0.8", features=["std_rng"] } unicode-segmentation = "1.7.1" log = "0.4.14" protobuf = {version = "2.18.0"} +lazy_static = "1.4.0" +fancy-regex = "0.5.0" [dev-dependencies] quickcheck = "0.9.2" quickcheck_macros = "0.9.1" fake = "~2.3.0" -claim = "0.4.0" \ No newline at end of file +claim = "0.4.0" +flowy-test = { path = "../flowy-test" } \ No newline at end of file diff --git a/rust-lib/flowy-user/src/domain/user/user.rs b/rust-lib/flowy-user/src/domain/user/user.rs index 8198426f94..b2aaead93d 100644 --- a/rust-lib/flowy-user/src/domain/user/user.rs +++ b/rust-lib/flowy-user/src/domain/user/user.rs @@ -1,6 +1,5 @@ use crate::domain::{UserEmail, UserName, UserPassword}; use flowy_derive::ProtoBuf; -use std::convert::TryInto; #[derive(ProtoBuf, Default)] pub struct User { diff --git a/rust-lib/flowy-user/src/domain/user/user_email.rs b/rust-lib/flowy-user/src/domain/user/user_email.rs index 67806fed76..c75d438a30 100644 --- a/rust-lib/flowy-user/src/domain/user/user_email.rs +++ b/rust-lib/flowy-user/src/domain/user/user_email.rs @@ -5,10 +5,14 @@ pub struct UserEmail(pub String); impl UserEmail { pub fn parse(s: String) -> Result { + if s.trim().is_empty() { + return Err(format!("Email can not be empty or whitespace")); + } + if validate_email(&s) { Ok(Self(s)) } else { - Err(format!("{} is not a valid subscriber email.", s)) + Err(format!("{} is not a valid email.", s)) } } } diff --git a/rust-lib/flowy-user/src/domain/user/user_name.rs b/rust-lib/flowy-user/src/domain/user/user_name.rs index 77dcd669f7..01970de4d0 100644 --- a/rust-lib/flowy-user/src/domain/user/user_name.rs +++ b/rust-lib/flowy-user/src/domain/user/user_name.rs @@ -19,7 +19,7 @@ impl UserName { let contains_forbidden_characters = s.chars().any(|g| forbidden_characters.contains(&g)); if is_empty_or_whitespace || is_too_long || contains_forbidden_characters { - Err(format!("{} is not a valid subscriber name.", s)) + Err(format!("{} is not a valid name.", s)) } else { Ok(Self(s)) } diff --git a/rust-lib/flowy-user/src/domain/user/user_password.rs b/rust-lib/flowy-user/src/domain/user/user_password.rs index b1d2b6e400..e29b167978 100644 --- a/rust-lib/flowy-user/src/domain/user/user_password.rs +++ b/rust-lib/flowy-user/src/domain/user/user_password.rs @@ -1,6 +1,43 @@ +use fancy_regex::Regex; +use lazy_static::lazy_static; +use unicode_segmentation::UnicodeSegmentation; #[derive(Debug)] pub struct UserPassword(pub String); impl UserPassword { - pub fn parse(s: String) -> Result { Ok(Self(s)) } + pub fn parse(s: String) -> Result { + let is_empty_or_whitespace = s.trim().is_empty(); + if is_empty_or_whitespace { + return Err(format!("Password can not be empty or whitespace.")); + } + let is_too_long = s.graphemes(true).count() > 100; + let forbidden_characters = ['/', '(', ')', '"', '<', '>', '\\', '{', '}']; + let contains_forbidden_characters = s.chars().any(|g| forbidden_characters.contains(&g)); + let is_invalid_password = !validate_password(&s); + + if is_too_long || contains_forbidden_characters || is_invalid_password { + Err(format!("{} is not a valid password.", s)) + } else { + Ok(Self(s)) + } + } } + +lazy_static! { + // Test it in https://regex101.com/ + // https://stackoverflow.com/questions/2370015/regular-expression-for-password-validation/2370045 + // Hell1! + // [invalid, less than 6] + // Hel1! + // + // Hello1! + // [invalid, must include number] + // Hello! + // + // Hello12! + // [invalid must include upper case] + // hello12! + static ref PASSWORD: Regex = Regex::new("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\\W]).{6,20})").unwrap(); +} + +pub fn validate_password(password: &str) -> bool { PASSWORD.is_match(password).is_ok() } diff --git a/rust-lib/flowy-user/src/event.rs b/rust-lib/flowy-user/src/event.rs deleted file mode 100644 index 97e0dea582..0000000000 --- a/rust-lib/flowy-user/src/event.rs +++ /dev/null @@ -1,13 +0,0 @@ -use derive_more::Display; - -#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash)] -pub enum UserEvent { - #[display(fmt = "AuthCheck")] - AuthCheck = 0, - #[display(fmt = "SignIn")] - SignIn = 1, - #[display(fmt = "SignUp")] - SignUp = 2, - #[display(fmt = "SignOut")] - SignOut = 3, -} diff --git a/rust-lib/flowy-user/src/lib.rs b/rust-lib/flowy-user/src/lib.rs index 498e7e3ebd..f975fd7947 100644 --- a/rust-lib/flowy-user/src/lib.rs +++ b/rust-lib/flowy-user/src/lib.rs @@ -1,14 +1,9 @@ mod domain; mod error; -pub mod event; mod handlers; pub mod module; mod protobuf; pub mod prelude { - pub use crate::{ - domain::*, - event::{UserEvent::*, *}, - handlers::auth::*, - }; + pub use crate::{domain::*, handlers::auth::*, module::UserEvent::*}; } diff --git a/rust-lib/flowy-user/src/module.rs b/rust-lib/flowy-user/src/module.rs index 7eed0568dd..4d4863ec54 100644 --- a/rust-lib/flowy-user/src/module.rs +++ b/rust-lib/flowy-user/src/module.rs @@ -1,9 +1,23 @@ -use crate::{event::UserEvent::*, handlers::*}; +use crate::handlers::*; use flowy_sys::prelude::*; +use derive_more::Display; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash)] +pub enum UserEvent { + #[display(fmt = "AuthCheck")] + AuthCheck = 0, + #[display(fmt = "SignIn")] + SignIn = 1, + #[display(fmt = "SignUp")] + SignUp = 2, + #[display(fmt = "SignOut")] + SignOut = 3, +} + pub fn create() -> Module { Module::new() .name("Flowy-User") - .event(SignIn, user_sign_in) - .event(SignUp, user_sign_up) + .event(UserEvent::SignIn, user_sign_in) + .event(UserEvent::SignUp, user_sign_up) } diff --git a/rust-lib/flowy-sdk/tests/sdk/user/mod.rs b/rust-lib/flowy-user/tests/main.rs similarity index 100% rename from rust-lib/flowy-sdk/tests/sdk/user/mod.rs rename to rust-lib/flowy-user/tests/main.rs diff --git a/rust-lib/flowy-user/tests/sign_in.rs b/rust-lib/flowy-user/tests/sign_in.rs new file mode 100644 index 0000000000..c625114b44 --- /dev/null +++ b/rust-lib/flowy-user/tests/sign_in.rs @@ -0,0 +1,32 @@ +use flowy_test::prelude::*; +use flowy_user::prelude::*; + +#[test] +#[should_panic] +fn sign_in_without_password() { + let params = UserSignInParams { + email: "annie@appflowy.io".to_string(), + password: "".to_string(), + }; + + let result = EventTester::new(SignIn) + .payload(params) + .assert_status_code(StatusCode::Err) + .sync_send::(); + dbg!(&result); +} + +#[test] +#[should_panic] +fn sign_in_without_email() { + let params = UserSignInParams { + email: "".to_string(), + password: "HelloWorld!123".to_string(), + }; + + let result = EventTester::new(SignIn) + .payload(params) + .assert_status_code(StatusCode::Err) + .sync_send::(); + dbg!(&result); +}