save user info to database after sign up

This commit is contained in:
appflowy 2021-07-10 16:27:20 +08:00
parent b9d7902acb
commit 55454c5bec
46 changed files with 458 additions and 277 deletions

View File

@ -18,6 +18,7 @@
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-user/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-database/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-sqlite/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/rust-lib/flowy-infra/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/.pub" />
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/app_flowy/packages/af_protobuf/build" />

View File

@ -11,19 +11,24 @@ import 'package:protobuf/protobuf.dart' as $pb;
class User extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'User', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'email')
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'password')
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'email')
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'password')
..hasRequiredFields = false
;
User._() : super();
factory User({
$core.String? id,
$core.String? name,
$core.String? email,
$core.String? password,
}) {
final _result = create();
if (id != null) {
_result.id = id;
}
if (name != null) {
_result.name = name;
}
@ -57,30 +62,39 @@ class User extends $pb.GeneratedMessage {
static User? _defaultInstance;
@$pb.TagNumber(1)
$core.String get name => $_getSZ(0);
$core.String get id => $_getSZ(0);
@$pb.TagNumber(1)
set name($core.String v) { $_setString(0, v); }
set id($core.String v) { $_setString(0, v); }
@$pb.TagNumber(1)
$core.bool hasName() => $_has(0);
$core.bool hasId() => $_has(0);
@$pb.TagNumber(1)
void clearName() => clearField(1);
void clearId() => clearField(1);
@$pb.TagNumber(2)
$core.String get email => $_getSZ(1);
$core.String get name => $_getSZ(1);
@$pb.TagNumber(2)
set email($core.String v) { $_setString(1, v); }
set name($core.String v) { $_setString(1, v); }
@$pb.TagNumber(2)
$core.bool hasEmail() => $_has(1);
$core.bool hasName() => $_has(1);
@$pb.TagNumber(2)
void clearEmail() => clearField(2);
void clearName() => clearField(2);
@$pb.TagNumber(3)
$core.String get password => $_getSZ(2);
$core.String get email => $_getSZ(2);
@$pb.TagNumber(3)
set password($core.String v) { $_setString(2, v); }
set email($core.String v) { $_setString(2, v); }
@$pb.TagNumber(3)
$core.bool hasPassword() => $_has(2);
$core.bool hasEmail() => $_has(2);
@$pb.TagNumber(3)
void clearPassword() => clearField(3);
void clearEmail() => clearField(3);
@$pb.TagNumber(4)
$core.String get password => $_getSZ(3);
@$pb.TagNumber(4)
set password($core.String v) { $_setString(3, v); }
@$pb.TagNumber(4)
$core.bool hasPassword() => $_has(3);
@$pb.TagNumber(4)
void clearPassword() => clearField(4);
}

View File

@ -9,9 +9,10 @@ import 'dart:core' as $core;
const User$json = const {
'1': 'User',
'2': const [
const {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
const {'1': 'email', '3': 2, '4': 1, '5': 9, '10': 'email'},
const {'1': 'password', '3': 3, '4': 1, '5': 9, '10': 'password'},
const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
const {'1': 'email', '3': 3, '4': 1, '5': 9, '10': 'email'},
const {'1': 'password', '3': 4, '4': 1, '5': 9, '10': 'password'},
],
};

View File

@ -10,6 +10,7 @@ members = [
"flowy-test",
"flowy-sqlite",
"flowy-database",
"flowy-infra",
]
[profile.dev]

View File

@ -36,8 +36,8 @@ pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) {
let request: ModuleRequest = FFIRequest::from_u8_pointer(input, len).into();
log::trace!(
"[FFI]: {} Async Event: {:?} with {} port",
&request.id(),
&request.event(),
&request.id,
&request.event,
port
);
@ -50,11 +50,7 @@ pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) {
#[no_mangle]
pub extern "C" fn sync_command(input: *const u8, len: usize) -> *const u8 {
let request: ModuleRequest = FFIRequest::from_u8_pointer(input, len).into();
log::trace!(
"[FFI]: {} Sync Event: {:?}",
&request.id(),
&request.event(),
);
log::trace!("[FFI]: {} Sync Event: {:?}", &request.id, &request.event,);
let _response = EventDispatch::sync_send(request);
// FFIResponse { }

View File

@ -1,16 +0,0 @@
use flowy_sqlite::Error;
use std::io;
#[derive(Debug)]
pub enum DataBaseError {
InitError(String),
IOError(String),
}
impl std::convert::From<flowy_sqlite::Error> for DataBaseError {
fn from(error: flowy_sqlite::Error) -> Self { DataBaseError::InitError(format!("{:?}", error)) }
}
impl std::convert::From<io::Error> for DataBaseError {
fn from(error: io::Error) -> Self { DataBaseError::IOError(format!("{:?}", error)) }
}

View File

@ -1,29 +1,37 @@
mod errors;
mod schema;
pub mod schema;
#[macro_use]
extern crate diesel;
pub use diesel::*;
#[macro_use]
extern crate diesel_derives;
pub use diesel_derives::*;
#[macro_use]
extern crate diesel_migrations;
pub use errors::*;
pub use flowy_sqlite::{DBConnection, DataBase};
pub use flowy_sqlite::{DBConnection, Database};
use diesel_migrations::*;
use flowy_sqlite::PoolConfig;
use std::path::Path;
use flowy_sqlite::{Error, PoolConfig};
use std::{io, path::Path};
embed_migrations!("../flowy-database/migrations/");
pub const DB_NAME: &str = "flowy-database.db";
pub fn init(storage_path: &str) -> Result<DataBase, DataBaseError> {
pub fn init(storage_path: &str) -> Result<Database, io::Error> {
if !Path::new(storage_path).exists() {
std::fs::create_dir_all(storage_path)?;
}
let pool_config = PoolConfig::default();
let database = DataBase::new(storage_path, DB_NAME, pool_config)?;
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);
Ok(database)
}
fn as_io_error(e: Error) -> io::Error {
let msg = format!("{:?}", e);
io::Error::new(io::ErrorKind::NotConnected, msg)
}

View File

@ -1,5 +1,5 @@
use crate::{
error::{InternalError, SystemError},
errors::{DispatchError, InternalError},
request::{unexpected_none_payload, EventRequest, FromRequest, Payload},
response::{EventResponse, Responder, ResponseBuilder, ToBytes},
util::ready::{ready, Ready},
@ -53,8 +53,8 @@ impl<T> FromRequest for Data<T>
where
T: FromBytes + 'static,
{
type Error = SystemError;
type Future = Ready<Result<Self, SystemError>>;
type Error = DispatchError;
type Future = Ready<Result<Self, DispatchError>>;
#[inline]
fn from_request(req: &EventRequest, payload: &mut Payload) -> Self::Future {
@ -76,7 +76,7 @@ where
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();
let system_err: DispatchError = InternalError::new(format!("{:?}", e)).into();
system_err.into()
},
}

View File

@ -1,5 +1,5 @@
use crate::{
error::{Error, InternalError, SystemError},
errors::{DispatchError, Error, InternalError},
module::{as_module_map, Module, ModuleMap, ModuleRequest},
response::EventResponse,
service::{Service, ServiceFactory},
@ -129,7 +129,7 @@ pub(crate) struct DispatchService {
impl Service<DispatchContext> for DispatchService {
type Response = EventResponse;
type Error = SystemError;
type Error = DispatchError;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
#[cfg_attr(

View File

@ -1,3 +0,0 @@
mod error;
pub use error::*;

View File

@ -8,45 +8,43 @@ use std::{fmt, option::NoneError};
use tokio::sync::mpsc::error::SendError;
pub trait Error: fmt::Debug + fmt::Display + DynClone + Send + Sync {
fn status_code(&self) -> StatusCode;
fn as_response(&self) -> EventResponse { EventResponse::new(self.status_code()) }
fn as_response(&self) -> EventResponse { EventResponse::new(StatusCode::Err) }
}
dyn_clone::clone_trait_object!(Error);
impl<T: Error + 'static> From<T> for SystemError {
fn from(err: T) -> SystemError {
SystemError {
impl<T: Error + 'static> From<T> for DispatchError {
fn from(err: T) -> DispatchError {
DispatchError {
inner: Box::new(err),
}
}
}
#[derive(Clone)]
pub struct SystemError {
pub struct DispatchError {
inner: Box<dyn Error>,
}
impl SystemError {
impl DispatchError {
pub fn inner_error(&self) -> &dyn Error { self.inner.as_ref() }
}
impl fmt::Display for SystemError {
impl fmt::Display for DispatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.inner, f) }
}
impl fmt::Debug for SystemError {
impl fmt::Debug for DispatchError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", &self.inner) }
}
impl std::error::Error for SystemError {
impl std::error::Error for DispatchError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
fn cause(&self) -> Option<&dyn std::error::Error> { None }
}
impl From<SendError<EventRequest>> for SystemError {
impl From<SendError<EventRequest>> for DispatchError {
fn from(err: SendError<EventRequest>) -> Self {
InternalError {
inner: format!("{}", err),
@ -55,7 +53,7 @@ impl From<SendError<EventRequest>> for SystemError {
}
}
impl From<NoneError> for SystemError {
impl From<NoneError> for DispatchError {
fn from(s: NoneError) -> Self {
InternalError {
inner: format!("Unexpected none: {:?}", s),
@ -64,16 +62,16 @@ impl From<NoneError> for SystemError {
}
}
impl From<String> for SystemError {
impl From<String> for DispatchError {
fn from(s: String) -> Self { InternalError { inner: s }.into() }
}
impl From<SystemError> for EventResponse {
fn from(err: SystemError) -> Self { err.inner_error().as_response() }
impl From<DispatchError> for EventResponse {
fn from(err: DispatchError) -> Self { err.inner_error().as_response() }
}
#[derive(Clone)]
pub struct InternalError<T: Clone> {
pub(crate) struct InternalError<T: Clone> {
inner: T,
}
@ -99,8 +97,6 @@ impl<T> Error for InternalError<T>
where
T: fmt::Debug + fmt::Display + 'static + Clone + Send + Sync,
{
fn status_code(&self) -> StatusCode { StatusCode::Err }
fn as_response(&self) -> EventResponse {
let error = InternalError {
inner: format!("{}", self.inner),
@ -111,7 +107,7 @@ where
}
}
impl Serialize for SystemError {
impl Serialize for DispatchError {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,

View File

@ -0,0 +1,3 @@
mod errors;
pub use errors::*;

View File

@ -1,6 +1,6 @@
#![feature(try_trait)]
mod error;
mod errors;
mod module;
mod request;
mod response;
@ -11,6 +11,8 @@ mod data;
mod dispatch;
mod system;
pub use errors::Error;
pub mod prelude {
pub use crate::{data::*, dispatch::*, error::*, module::*, request::*, response::*};
pub use crate::{data::*, dispatch::*, errors::*, module::*, request::*, response::*};
}

View File

@ -1,5 +1,5 @@
use crate::{
error::{InternalError, SystemError},
errors::{DispatchError, InternalError},
request::{payload::Payload, EventRequest, FromRequest},
util::ready::{ready, Ready},
};
@ -43,15 +43,18 @@ impl<T> FromRequest for ModuleData<T>
where
T: ?Sized + Send + Sync + 'static,
{
type Error = SystemError;
type Future = Ready<Result<Self, SystemError>>;
type Error = DispatchError;
type Future = Ready<Result<Self, DispatchError>>;
#[inline]
fn from_request(req: &EventRequest, _: &mut Payload) -> Self::Future {
if let Some(data) = req.module_data::<ModuleData<T>>() {
ready(Ok(data.clone()))
} else {
let msg = format!("Failed to get the module data(type: {})", type_name::<T>());
let msg = format!(
"Failed to get the module data of type: {}",
type_name::<T>()
);
log::error!("{}", msg,);
ready(Err(InternalError::new(msg).into()))
}

View File

@ -12,7 +12,7 @@ use futures_core::ready;
use pin_project::pin_project;
use crate::{
error::{InternalError, SystemError},
errors::{DispatchError, InternalError},
module::{container::ModuleDataMap, ModuleData},
request::{payload::Payload, EventRequest, FromRequest},
response::{EventResponse, Responder},
@ -51,7 +51,8 @@ impl<T: Display + Eq + Hash + Debug + Clone> std::convert::From<T> for Event {
fn from(t: T) -> Self { Event(format!("{}", t)) }
}
pub type EventServiceFactory = BoxServiceFactory<(), ServiceRequest, ServiceResponse, SystemError>;
pub type EventServiceFactory =
BoxServiceFactory<(), ServiceRequest, ServiceResponse, DispatchError>;
pub struct Module {
pub name: String,
@ -111,8 +112,8 @@ impl Module {
#[derive(Debug)]
pub struct ModuleRequest {
pub(crate) id: String,
pub(crate) event: Event,
pub id: String,
pub event: Event,
pub(crate) payload: Payload,
}
@ -145,7 +146,7 @@ impl std::fmt::Display for ModuleRequest {
impl ServiceFactory<ModuleRequest> for Module {
type Response = EventResponse;
type Error = SystemError;
type Error = DispatchError;
type Service = BoxService<ModuleRequest, Self::Response, Self::Error>;
type Context = ();
type Future = BoxFuture<'static, Result<Self::Service, Self::Error>>;
@ -171,7 +172,7 @@ pub struct ModuleService {
impl Service<ModuleRequest> for ModuleService {
type Response = EventResponse;
type Error = SystemError;
type Error = DispatchError;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn call(&self, request: ModuleRequest) -> Self::Future {
@ -196,17 +197,14 @@ impl Service<ModuleRequest> for ModuleService {
}
}
// type BoxModuleService = BoxService<ServiceRequest, ServiceResponse,
// SystemError>;
#[pin_project]
pub struct ModuleServiceFuture {
#[pin]
fut: BoxFuture<'static, Result<ServiceResponse, SystemError>>,
fut: BoxFuture<'static, Result<ServiceResponse, DispatchError>>,
}
impl Future for ModuleServiceFuture {
type Output = Result<EventResponse, SystemError>;
type Output = Result<EventResponse, DispatchError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {

View File

@ -1,7 +1,7 @@
use std::future::Future;
use crate::{
error::{InternalError, SystemError},
errors::{DispatchError, InternalError},
module::{Event, ModuleDataMap},
request::payload::Payload,
util::ready::{ready, Ready},
@ -48,7 +48,7 @@ impl EventRequest {
}
pub trait FromRequest: Sized {
type Error: Into<SystemError>;
type Error: Into<DispatchError>;
type Future: Future<Output = Result<Self, Self::Error>>;
fn from_request(req: &EventRequest, payload: &mut Payload) -> Self::Future;
@ -56,15 +56,15 @@ pub trait FromRequest: Sized {
#[doc(hidden)]
impl FromRequest for () {
type Error = SystemError;
type Future = Ready<Result<(), SystemError>>;
type Error = DispatchError;
type Future = Ready<Result<(), DispatchError>>;
fn from_request(_req: &EventRequest, _payload: &mut Payload) -> Self::Future { ready(Ok(())) }
}
#[doc(hidden)]
impl FromRequest for String {
type Error = SystemError;
type Error = DispatchError;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &EventRequest, payload: &mut Payload) -> Self::Future {
@ -75,7 +75,7 @@ impl FromRequest for String {
}
}
pub fn unexpected_none_payload(request: &EventRequest) -> SystemError {
pub fn unexpected_none_payload(request: &EventRequest) -> DispatchError {
log::warn!("{:?} expected payload", &request.event);
InternalError::new("Expected payload").into()
}
@ -85,7 +85,7 @@ impl<T> FromRequest for Result<T, T::Error>
where
T: FromRequest,
{
type Error = SystemError;
type Error = DispatchError;
type Future = FromRequestFuture<T::Future>;
fn from_request(req: &EventRequest, payload: &mut Payload) -> Self::Future {
@ -105,7 +105,7 @@ impl<Fut, T, E> Future for FromRequestFuture<Fut>
where
Fut: Future<Output = Result<T, E>>,
{
type Output = Result<Result<T, E>, SystemError>;
type Output = Result<Result<T, E>, DispatchError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();

View File

@ -1,5 +1,5 @@
use crate::{
error::SystemError,
errors::DispatchError,
request::Payload,
response::{EventResponse, StatusCode},
};
@ -14,7 +14,7 @@ macro_rules! static_response {
pub struct ResponseBuilder<T = Payload> {
pub payload: T,
pub status: StatusCode,
pub error: Option<SystemError>,
pub error: Option<DispatchError>,
}
impl ResponseBuilder {
@ -31,7 +31,7 @@ impl ResponseBuilder {
self
}
pub fn error(mut self, error: SystemError) -> Self {
pub fn error(mut self, error: DispatchError) -> Self {
self.error = Some(error);
self
}

View File

@ -1,5 +1,5 @@
#[allow(unused_imports)]
use crate::error::{InternalError, SystemError};
use crate::errors::{DispatchError, InternalError};
use crate::{
request::EventRequest,
response::{EventResponse, ResponseBuilder},
@ -28,7 +28,7 @@ impl_responder!(Bytes);
impl<T, E> Responder for Result<T, E>
where
T: Responder,
E: Into<SystemError>,
E: Into<DispatchError>,
{
fn respond_to(self, request: &EventRequest) -> EventResponse {
match self {

View File

@ -1,6 +1,6 @@
use crate::{
data::Data,
error::SystemError,
errors::DispatchError,
request::{EventRequest, Payload},
response::Responder,
};
@ -19,7 +19,7 @@ pub struct EventResponse {
#[derivative(Debug = "ignore")]
pub payload: Payload,
pub status_code: StatusCode,
pub error: Option<SystemError>,
pub error: Option<DispatchError>,
}
impl EventResponse {
@ -58,7 +58,7 @@ pub type ResponseResult<T, E> = std::result::Result<Data<T>, E>;
pub fn response_ok<T, E>(data: T) -> Result<Data<T>, E>
where
E: Into<SystemError>,
E: Into<DispatchError>,
{
Ok(Data(data))
}

View File

@ -9,7 +9,7 @@ use futures_core::ready;
use pin_project::pin_project;
use crate::{
error::SystemError,
errors::DispatchError,
request::{payload::Payload, EventRequest, FromRequest},
response::{EventResponse, Responder},
service::{Service, ServiceFactory, ServiceRequest, ServiceResponse},
@ -73,7 +73,7 @@ where
R::Output: Responder,
{
type Response = ServiceResponse;
type Error = SystemError;
type Error = DispatchError;
type Service = Self;
type Context = ();
type Future = Ready<Result<Self::Service, Self::Error>>;
@ -89,7 +89,7 @@ where
R::Output: Responder,
{
type Response = ServiceResponse;
type Error = SystemError;
type Error = DispatchError;
type Future = HandlerServiceFuture<H, T, R>;
fn call(&self, req: ServiceRequest) -> Self::Future {
@ -118,7 +118,7 @@ where
R: Future + Sync + Send,
R::Output: Responder,
{
type Output = Result<ServiceResponse, SystemError>;
type Output = Result<ServiceResponse, DispatchError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
@ -132,7 +132,7 @@ where
},
Err(err) => {
let req = req.take().unwrap();
let system_err: SystemError = err.into();
let system_err: DispatchError = err.into();
let res: EventResponse = system_err.into();
return Poll::Ready(Ok(ServiceResponse::new(req, res)));
},
@ -175,7 +175,7 @@ macro_rules! tuple_from_req ({$tuple_type:ident, $(($n:tt, $T:ident)),+} => {
#[allow(unused_parens)]
impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+)
{
type Error = SystemError;
type Error = DispatchError;
type Future = $tuple_type<$($T),+>;
fn from_request(req: &EventRequest, payload: &mut Payload) -> Self::Future {
@ -196,7 +196,7 @@ macro_rules! tuple_from_req ({$tuple_type:ident, $(($n:tt, $T:ident)),+} => {
impl<$($T: FromRequest),+> Future for $tuple_type<$($T),+>
{
type Output = Result<($($T,)+), SystemError>;
type Output = Result<($($T,)+), DispatchError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();

View File

@ -0,0 +1,9 @@
[package]
name = "flowy-infra"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
uuid = { version = "0.8", features = ["serde", "v4"] }

View File

@ -0,0 +1 @@
pub fn uuid() -> String { uuid::Uuid::new_v4().to_string() }

View File

@ -1,5 +1,5 @@
use flowy_dispatch::prelude::Module;
use flowy_user::prelude::user_session::{UserSession, UserSessionConfig};
use flowy_user::prelude::{UserSession, UserSessionBuilder, UserSessionConfig};
use std::sync::Arc;
pub struct ModuleConfig {
@ -7,8 +7,6 @@ pub struct ModuleConfig {
}
pub fn build_modules(config: ModuleConfig) -> Vec<Module> {
let user_config = UserSessionConfig::new(&config.root);
let user_session = Arc::new(UserSession::new(user_config));
vec![flowy_user::module::create(user_session)]
let user_session = UserSessionBuilder::new().root_dir(&config.root).build();
vec![flowy_user::module::create(Arc::new(user_session))]
}

View File

@ -4,16 +4,21 @@ use crate::{
};
use r2d2::PooledConnection;
pub struct DataBase {
pub struct Database {
uri: String,
pool: ConnectionPool,
}
pub type DBConnection = PooledConnection<ConnectionManager>;
impl DataBase {
impl Database {
pub fn new(dir: &str, name: &str, pool_config: PoolConfig) -> Result<Self> {
let uri = db_file_uri(dir, name);
if !std::path::PathBuf::from(dir).exists() {
log::error!("Create database failed. {} not exists", &dir);
}
let pool = ConnectionPool::new(pool_config, &uri)?;
Ok(Self { uri, pool })
}

View File

@ -115,12 +115,7 @@ impl ManageConnection for ConnectionManager {
type Connection = SqliteConnection;
type Error = crate::Error;
fn connect(&self) -> Result<Self::Connection> {
if !std::path::PathBuf::from(&self.db_uri).exists() {
log::error!("db file not exists");
}
Ok(SqliteConnection::establish(&self.db_uri)?)
}
fn connect(&self) -> Result<Self::Connection> { Ok(SqliteConnection::establish(&self.db_uri)?) }
fn is_valid(&self, conn: &mut Self::Connection) -> Result<()> {
Ok(conn.execute("SELECT 1").map(|_| ())?)

View File

@ -12,6 +12,7 @@ flowy-log = { path = "../flowy-log" }
flowy-derive = { path = "../flowy-derive" }
flowy-database = { path = "../flowy-database" }
flowy-sqlite = { path = "../flowy-sqlite" }
flowy-infra = { path = "../flowy-infra" }
tracing = { version = "0.1", features = ["log"] }
bytes = "1.0"
@ -23,6 +24,8 @@ log = "0.4.14"
protobuf = {version = "2.18.0"}
lazy_static = "1.4.0"
fancy-regex = "0.5.0"
diesel = {version = "1.4.7", features = ["sqlite"]}
diesel_derives = {version = "1.4.1", features = ["sqlite"]}
[dev-dependencies]
quickcheck = "0.9.2"

View File

@ -1,4 +1,3 @@
pub use user::*;
pub mod tables;
pub mod user;
pub mod user_db;
pub mod user_session;

View File

@ -0,0 +1,3 @@
mod user_table;
pub use user_table::*;

View File

@ -0,0 +1,30 @@
use crate::domain::{UserEmail, UserName, UserPassword};
use flowy_database::schema::user_table;
use flowy_derive::ProtoBuf;
#[derive(ProtoBuf, Clone, Default, Queryable, Identifiable, Insertable)]
#[table_name = "user_table"]
pub struct User {
#[pb(index = 1)]
pub(crate) id: String,
#[pb(index = 2)]
name: String,
#[pb(index = 3)]
email: String,
#[pb(index = 4)]
password: String,
}
impl User {
pub fn new(id: String, name: String, email: String, password: String) -> Self {
Self {
id,
name,
email,
password,
}
}
}

View File

@ -1,13 +1,11 @@
mod sign_in;
mod sign_up;
mod user;
mod user_email;
mod user_name;
mod user_password;
pub use sign_in::*;
pub use sign_up::*;
pub use user::*;
pub use user_email::*;
pub use user_name::*;
pub use user_password::*;

View File

@ -1,24 +0,0 @@
use crate::domain::{UserEmail, UserName, UserPassword};
use flowy_derive::ProtoBuf;
#[derive(ProtoBuf, Default)]
pub struct User {
#[pb(index = 1)]
name: String,
#[pb(index = 2)]
email: String,
#[pb(index = 3)]
password: String,
}
impl User {
pub fn new(name: UserName, email: UserEmail, password: UserPassword) -> Self {
Self {
name: name.0,
email: email.0,
password: password.0,
}
}
}

View File

@ -1,48 +0,0 @@
use crate::{domain::user_db::UserDB, errors::UserError};
use flowy_sqlite::DBConnection;
use lazy_static::lazy_static;
use std::sync::RwLock;
lazy_static! {
pub static ref CURRENT_USER_ID: RwLock<Option<String>> = RwLock::new(None);
}
fn get_current_user_id() -> Result<Option<String>, UserError> {
match CURRENT_USER_ID.read() {
Ok(read_guard) => Ok((*read_guard).clone()),
Err(e) => {
log::error!("Get current user id failed: {:?}", e);
Err(e.into())
},
}
}
pub struct UserSessionConfig {
root_dir: String,
}
impl UserSessionConfig {
pub fn new(root_dir: &str) -> Self {
Self {
root_dir: root_dir.to_owned(),
}
}
}
pub struct UserSession {
db: UserDB,
config: UserSessionConfig,
}
impl UserSession {
pub fn new(config: UserSessionConfig) -> Self {
let db = UserDB::new(&config.root_dir);
Self { db, config }
}
pub fn get_db_connection(&self) -> Result<DBConnection, UserError> {
match get_current_user_id()? {
None => Err(UserError::UserNotLogin),
Some(user_id) => self.db.get_connection(&user_id),
}
}
}

View File

@ -1,23 +1,34 @@
use flowy_database::DataBaseError;
use std::sync::PoisonError;
use derive_more::Display;
use flowy_dispatch::prelude::{DispatchError, EventResponse, ResponseBuilder, StatusCode};
use std::{io, sync::PoisonError};
#[derive(Debug)]
#[derive(Debug, Clone, Display)]
pub enum UserError {
DBInit(String),
DBNotInit,
UserNotLogin,
DBConnection(String),
#[display(fmt = "User db error:{}", _0)]
Database(String),
#[display(fmt = "User auth error:{}", _0)]
Auth(String),
#[display(fmt = "User sync error: {}", _0)]
PoisonError(String),
}
impl std::convert::From<DataBaseError> for UserError {
fn from(error: DataBaseError) -> Self { UserError::DBInit(format!("{:?}", error)) }
}
impl<T> std::convert::From<PoisonError<T>> for UserError {
fn from(error: PoisonError<T>) -> Self { UserError::PoisonError(format!("{:?}", error)) }
impl std::convert::From<flowy_database::result::Error> for UserError {
fn from(error: flowy_database::result::Error) -> Self {
UserError::Database(format!("{:?}", error))
}
}
impl std::convert::From<flowy_sqlite::Error> for UserError {
fn from(e: flowy_sqlite::Error) -> Self { UserError::DBConnection(format!("{:?}", e)) }
fn from(e: flowy_sqlite::Error) -> Self { UserError::Database(format!("{:?}", e)) }
}
impl std::convert::From<UserError> for String {
fn from(e: UserError) -> Self { format!("{:?}", e) }
}
impl std::convert::Into<DispatchError> for UserError {
fn into(self) -> DispatchError {
let user_error: String = self.into();
user_error.into()
}
}

View File

@ -1,4 +1,4 @@
use crate::domain::{user::*, user_session::UserSession};
use crate::{domain::user::*, services::user_session::UserSession};
use flowy_dispatch::prelude::*;
use std::{convert::TryInto, sync::Arc};
@ -22,15 +22,18 @@ pub async fn user_sign_in(data: Data<SignInRequest>) -> ResponseResult<SignInRes
skip(data, session),
fields(
email = %data.email,
name = %data.name,
)
)]
pub async fn user_sign_up(
data: Data<SignUpRequest>,
session: ModuleData<Arc<UserSession>>,
) -> ResponseResult<SignUpResponse, String> {
let _params: SignUpParams = data.into_inner().try_into()?;
let params: SignUpParams = data.into_inner().try_into()?;
// TODO: user sign up
let _user = session.sign_up(params)?;
let fake_resp = SignUpResponse::new(true);
response_ok(fake_resp)
}

View File

@ -4,7 +4,11 @@ pub mod event;
mod handlers;
pub mod module;
mod protobuf;
mod services;
#[macro_use]
extern crate flowy_database;
pub mod prelude {
pub use crate::{domain::*, handlers::auth::*};
pub use crate::{domain::*, handlers::auth::*, services::user_session::*};
}

View File

@ -1,10 +1,6 @@
use flowy_dispatch::prelude::*;
use crate::{
domain::{user_db::*, user_session::UserSession},
event::UserEvent,
handlers::*,
};
use crate::{event::UserEvent, handlers::*, services::user_session::UserSession};
use std::sync::Arc;
pub fn create(user_session: Arc<UserSession>) -> Module {

View File

@ -26,6 +26,7 @@
#[derive(PartialEq,Clone,Default)]
pub struct User {
// message fields
pub id: ::std::string::String,
pub name: ::std::string::String,
pub email: ::std::string::String,
pub password: ::std::string::String,
@ -45,7 +46,33 @@ impl User {
::std::default::Default::default()
}
// string name = 1;
// string id = 1;
pub fn get_id(&self) -> &str {
&self.id
}
pub fn clear_id(&mut self) {
self.id.clear();
}
// Param is passed by value, moved
pub fn set_id(&mut self, v: ::std::string::String) {
self.id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_id(&mut self) -> &mut ::std::string::String {
&mut self.id
}
// Take field
pub fn take_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.id, ::std::string::String::new())
}
// string name = 2;
pub fn get_name(&self) -> &str {
@ -71,7 +98,7 @@ impl User {
::std::mem::replace(&mut self.name, ::std::string::String::new())
}
// string email = 2;
// string email = 3;
pub fn get_email(&self) -> &str {
@ -97,7 +124,7 @@ impl User {
::std::mem::replace(&mut self.email, ::std::string::String::new())
}
// string password = 3;
// string password = 4;
pub fn get_password(&self) -> &str {
@ -134,12 +161,15 @@ impl ::protobuf::Message for User {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
},
3 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
},
4 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.password)?;
},
_ => {
@ -154,14 +184,17 @@ impl ::protobuf::Message for User {
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.id);
}
if !self.name.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.name);
my_size += ::protobuf::rt::string_size(2, &self.name);
}
if !self.email.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.email);
my_size += ::protobuf::rt::string_size(3, &self.email);
}
if !self.password.is_empty() {
my_size += ::protobuf::rt::string_size(3, &self.password);
my_size += ::protobuf::rt::string_size(4, &self.password);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
@ -169,14 +202,17 @@ impl ::protobuf::Message for User {
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.id.is_empty() {
os.write_string(1, &self.id)?;
}
if !self.name.is_empty() {
os.write_string(1, &self.name)?;
os.write_string(2, &self.name)?;
}
if !self.email.is_empty() {
os.write_string(2, &self.email)?;
os.write_string(3, &self.email)?;
}
if !self.password.is_empty() {
os.write_string(3, &self.password)?;
os.write_string(4, &self.password)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
@ -216,6 +252,11 @@ impl ::protobuf::Message for User {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"id",
|m: &User| { &m.id },
|m: &mut User| { &mut m.id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"name",
|m: &User| { &m.name },
@ -247,6 +288,7 @@ impl ::protobuf::Message for User {
impl ::protobuf::Clear for User {
fn clear(&mut self) {
self.id.clear();
self.name.clear();
self.email.clear();
self.password.clear();
@ -267,19 +309,23 @@ impl ::protobuf::reflect::ProtobufValue for User {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\nuser.proto\"L\n\x04User\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04nam\
e\x12\x14\n\x05email\x18\x02\x20\x01(\tR\x05email\x12\x1a\n\x08password\
\x18\x03\x20\x01(\tR\x08passwordJ\xcf\x01\n\x06\x12\x04\0\0\x06\x01\n\
\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x06\x01\n\n\
\n\x03\x04\0\x01\x12\x03\x02\x08\x0c\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\
\x04\x14\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\
\x02\0\x01\x12\x03\x03\x0b\x0f\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\
\x12\x13\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x15\n\x0c\n\x05\x04\0\
\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\
\x0b\x10\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x13\x14\n\x0b\n\x04\
\x04\0\x02\x02\x12\x03\x05\x04\x18\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\
\x05\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x05\x0b\x13\n\x0c\n\x05\
\x04\0\x02\x02\x03\x12\x03\x05\x16\x17b\x06proto3\
\n\nuser.proto\"\\\n\x04User\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\
\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x14\n\x05email\x18\
\x03\x20\x01(\tR\x05email\x12\x1a\n\x08password\x18\x04\x20\x01(\tR\x08p\
asswordJ\x86\x02\n\x06\x12\x04\0\0\x07\x01\n\x08\n\x01\x0c\x12\x03\0\0\
\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x07\x01\n\n\n\x03\x04\0\x01\x12\x03\
\x02\x08\x0c\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x12\n\x0c\n\x05\x04\
\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\
\x0b\r\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x10\x11\n\x0b\n\x04\x04\0\
\x02\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\
\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\
\0\x02\x01\x03\x12\x03\x04\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\
\x04\x15\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\
\0\x02\x02\x01\x12\x03\x05\x0b\x10\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\
\x05\x13\x14\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x04\x18\n\x0c\n\x05\
\x04\0\x02\x03\x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\0\x02\x03\x01\x12\
\x03\x06\x0b\x13\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06\x16\x17b\x06p\
roto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -1,7 +1,8 @@
syntax = "proto3";
message User {
string name = 1;
string email = 2;
string password = 3;
string id = 1;
string name = 2;
string email = 3;
string password = 4;
}

View File

@ -1,5 +1,5 @@
use crate::errors::UserError;
use flowy_database::{DBConnection, DataBase};
use flowy_database::{DBConnection, Database};
use lazy_static::lazy_static;
use std::{
cell::RefCell,
@ -22,7 +22,7 @@ fn get_user_id() -> Option<String> { USER_ID.with(|id| id.borrow().clone()) }
static IS_USER_DB_INIT: AtomicBool = AtomicBool::new(false);
lazy_static! {
static ref USER_DB_INNER: RwLock<Option<DataBase>> = RwLock::new(None);
static ref DB: RwLock<Option<Database>> = RwLock::new(None);
}
pub(crate) struct UserDB {
@ -37,17 +37,23 @@ impl UserDB {
}
fn open_user_db(&self, user_id: &str) -> Result<(), UserError> {
let user_dir = format!("{}/{}", self.db_dir, user_id);
let database = flowy_database::init(&user_dir)?;
let mut write_guard = USER_DB_INNER.write()?;
let dir = format!("{}/{}", self.db_dir, user_id);
let db = flowy_database::init(&dir).map_err(|e| UserError::Database(format!("{:?}", e)))?;
let mut user_db = DB
.write()
.map_err(|e| UserError::Database(format!("Open user db failed. {:?}", e)))?;
*(user_db) = Some(db);
set_user_id(Some(user_id.to_owned()));
*(write_guard) = Some(database);
IS_USER_DB_INIT.store(true, Ordering::SeqCst);
Ok(())
}
pub(crate) fn close_user_db(&mut self) -> Result<(), UserError> {
let mut write_guard = USER_DB_INNER.write()?;
let mut write_guard = DB
.write()
.map_err(|e| UserError::Database(format!("Close user db failed. {:?}", e)))?;
*write_guard = None;
set_user_id(None);
IS_USER_DB_INIT.store(false, Ordering::SeqCst);
@ -62,16 +68,20 @@ impl UserDB {
let thread_user_id = get_user_id();
if thread_user_id != Some(user_id.to_owned()) {
let msg = format!(
"DataBase owner does not match. origin: {:?}, current: {}",
"Database owner does not match. origin: {:?}, current: {}",
thread_user_id, user_id
);
log::error!("{}", msg);
return Err(UserError::DBConnection(msg));
return Err(UserError::Database(msg));
}
let read_guard = USER_DB_INNER.read()?;
let read_guard = DB
.read()
.map_err(|e| UserError::Database(format!("Get user db connection fail. {:?}", e)))?;
match read_guard.as_ref() {
None => Err(UserError::DBNotInit),
None => Err(UserError::Database(
"Database is not initialization".to_owned(),
)),
Some(database) => Ok(database.get_connection()?),
}
}

View File

@ -0,0 +1,3 @@
pub mod database;
mod register;
pub mod user_session;

View File

@ -0,0 +1,22 @@
use crate::{
domain::{tables::User, SignUpParams},
errors::UserError,
};
pub trait UserRegister {
fn register_user(&self, params: SignUpParams) -> Result<User, UserError>;
}
pub struct MockUserRegister {}
impl UserRegister for MockUserRegister {
fn register_user(&self, params: SignUpParams) -> Result<User, UserError> {
let user_id = "9527".to_owned();
Ok(User::new(
user_id,
params.name,
params.email,
params.password,
))
}
}

View File

@ -0,0 +1,23 @@
use crate::services::{
register::MockUserRegister,
user_session::{UserSession, UserSessionConfig},
};
pub struct UserSessionBuilder {
config: Option<UserSessionConfig>,
}
impl UserSessionBuilder {
pub fn new() -> Self { Self { config: None } }
pub fn root_dir(mut self, dir: &str) -> Self {
self.config = Some(UserSessionConfig::new(dir));
self
}
pub fn build(mut self) -> UserSession {
let config = self.config.take().unwrap();
let register = MockUserRegister {};
UserSession::new(config, register)
}
}

View File

@ -0,0 +1,5 @@
mod builder;
mod user_session;
pub use builder::*;
pub use user_session::*;

View File

@ -0,0 +1,86 @@
use crate::{
domain::{tables::User, SignUpParams},
errors::UserError,
services::{database::UserDB, register::UserRegister},
};
use ::diesel::query_dsl::*;
use flowy_database::schema::user_table;
use flowy_sqlite::DBConnection;
use lazy_static::lazy_static;
use std::sync::RwLock;
pub struct UserSessionConfig {
root_dir: String,
}
impl UserSessionConfig {
pub fn new(root_dir: &str) -> Self {
Self {
root_dir: root_dir.to_owned(),
}
}
}
pub struct UserSession {
database: UserDB,
config: UserSessionConfig,
register: Box<dyn UserRegister + Send + Sync>,
}
impl UserSession {
pub fn new<R>(config: UserSessionConfig, register: R) -> Self
where
R: 'static + UserRegister + Send + Sync,
{
let db = UserDB::new(&config.root_dir);
Self {
database: db,
config,
register: Box::new(register),
}
}
pub fn get_db_connection(&self) -> Result<DBConnection, UserError> {
match get_current_user_id()? {
None => Err(UserError::Auth("User is not login yet".to_owned())),
Some(user_id) => self.database.get_connection(&user_id),
}
}
pub fn sign_up(&self, params: SignUpParams) -> Result<User, UserError> {
let user = self.register.register_user(params)?;
set_current_user_id(Some(user.id.clone()));
let conn = self.get_db_connection()?;
let _ = diesel::insert_into(user_table::table)
.values(user.clone())
.execute(&*conn)?;
Ok(user)
}
pub fn sign_out(&self) -> Result<(), UserError> {
set_current_user_id(None);
// TODO: close the db
unimplemented!()
}
}
lazy_static! {
pub static ref CURRENT_USER_ID: RwLock<Option<String>> = RwLock::new(None);
}
fn get_current_user_id() -> Result<Option<String>, UserError> {
let current_user_id = CURRENT_USER_ID
.read()
.map_err(|e| UserError::Auth(format!("Read current user id failed. {:?}", e)))?;
Ok((*current_user_id).clone())
}
pub fn set_current_user_id(user_id: Option<String>) -> Result<(), UserError> {
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(())
}

View File

@ -9,11 +9,9 @@ fn sign_up_success() {
password: valid_password(),
};
let response = EventTester::new(SignUp)
.request(request)
.sync_send()
.parse::<SignUpResponse>();
dbg!(&response);
let response = EventTester::new(SignUp).request(request).sync_send();
// .parse::<SignUpResponse>();
// dbg!(&response);
}
#[test]

View File

@ -1 +1 @@
mod auth;
mod auth_test;