add workspace/app/view router

This commit is contained in:
appflowy 2021-08-24 13:10:53 +08:00
parent d6c761917b
commit 74800278f9
36 changed files with 385 additions and 142 deletions

View File

@ -34,7 +34,7 @@ class UserErrCode extends $pb.ProtobufEnum {
static const UserErrCode UserIdInvalid = UserErrCode._(51, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserIdInvalid');
static const UserErrCode CreateDefaultWorkspaceFailed = UserErrCode._(52, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateDefaultWorkspaceFailed');
static const UserErrCode DefaultWorkspaceAlreadyExist = UserErrCode._(53, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DefaultWorkspaceAlreadyExist');
static const UserErrCode NetworkError = UserErrCode._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NetworkError');
static const UserErrCode ServerError = UserErrCode._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ServerError');
static const $core.List<UserErrCode> values = <UserErrCode> [
Unknown,
@ -61,7 +61,7 @@ class UserErrCode extends $pb.ProtobufEnum {
UserIdInvalid,
CreateDefaultWorkspaceFailed,
DefaultWorkspaceAlreadyExist,
NetworkError,
ServerError,
];
static final $core.Map<$core.int, UserErrCode> _byValue = $pb.ProtobufEnum.initByValue(values);

View File

@ -36,12 +36,12 @@ const UserErrCode$json = const {
const {'1': 'UserIdInvalid', '2': 51},
const {'1': 'CreateDefaultWorkspaceFailed', '2': 52},
const {'1': 'DefaultWorkspaceAlreadyExist', '2': 53},
const {'1': 'NetworkError', '2': 100},
const {'1': 'ServerError', '2': 100},
],
};
/// Descriptor for `UserErrCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List userErrCodeDescriptor = $convert.base64Decode('CgtVc2VyRXJyQ29kZRILCgdVbmtub3duEAASGgoWVXNlckRhdGFiYXNlSW5pdEZhaWxlZBABEhsKF1VzZXJEYXRhYmFzZVdyaXRlTG9ja2VkEAISGgoWVXNlckRhdGFiYXNlUmVhZExvY2tlZBADEhsKF1VzZXJEYXRhYmFzZURpZE5vdE1hdGNoEAQSHQoZVXNlckRhdGFiYXNlSW50ZXJuYWxFcnJvchAFEhQKEFNxbEludGVybmFsRXJyb3IQBhITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbElzRW1wdHkQFBIWChJFbWFpbEZvcm1hdEludmFsaWQQFRIWChJFbWFpbEFscmVhZHlFeGlzdHMQFhITCg9QYXNzd29yZElzRW1wdHkQHhITCg9QYXNzd29yZFRvb0xvbmcQHxIkCiBQYXNzd29yZENvbnRhaW5zRm9yYmlkQ2hhcmFjdGVycxAgEhkKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBAhEhMKD1VzZXJOYW1lVG9vTG9uZxAoEicKI1VzZXJOYW1lQ29udGFpbnNGb3JiaWRkZW5DaGFyYWN0ZXJzECkSEwoPVXNlck5hbWVJc0VtcHR5ECoSGAoUVXNlcldvcmtzcGFjZUludmFsaWQQMhIRCg1Vc2VySWRJbnZhbGlkEDMSIAocQ3JlYXRlRGVmYXVsdFdvcmtzcGFjZUZhaWxlZBA0EiAKHERlZmF1bHRXb3Jrc3BhY2VBbHJlYWR5RXhpc3QQNRIQCgxOZXR3b3JrRXJyb3IQZA==');
final $typed_data.Uint8List userErrCodeDescriptor = $convert.base64Decode('CgtVc2VyRXJyQ29kZRILCgdVbmtub3duEAASGgoWVXNlckRhdGFiYXNlSW5pdEZhaWxlZBABEhsKF1VzZXJEYXRhYmFzZVdyaXRlTG9ja2VkEAISGgoWVXNlckRhdGFiYXNlUmVhZExvY2tlZBADEhsKF1VzZXJEYXRhYmFzZURpZE5vdE1hdGNoEAQSHQoZVXNlckRhdGFiYXNlSW50ZXJuYWxFcnJvchAFEhQKEFNxbEludGVybmFsRXJyb3IQBhITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbElzRW1wdHkQFBIWChJFbWFpbEZvcm1hdEludmFsaWQQFRIWChJFbWFpbEFscmVhZHlFeGlzdHMQFhITCg9QYXNzd29yZElzRW1wdHkQHhITCg9QYXNzd29yZFRvb0xvbmcQHxIkCiBQYXNzd29yZENvbnRhaW5zRm9yYmlkQ2hhcmFjdGVycxAgEhkKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBAhEhMKD1VzZXJOYW1lVG9vTG9uZxAoEicKI1VzZXJOYW1lQ29udGFpbnNGb3JiaWRkZW5DaGFyYWN0ZXJzECkSEwoPVXNlck5hbWVJc0VtcHR5ECoSGAoUVXNlcldvcmtzcGFjZUludmFsaWQQMhIRCg1Vc2VySWRJbnZhbGlkEDMSIAocQ3JlYXRlRGVmYXVsdFdvcmtzcGFjZUZhaWxlZBA0EiAKHERlZmF1bHRXb3Jrc3BhY2VBbHJlYWR5RXhpc3QQNRIPCgtTZXJ2ZXJFcnJvchBk');
@$core.Deprecated('Use userErrorDescriptor instead')
const UserError$json = const {
'1': 'UserError',

View File

@ -31,4 +31,24 @@ export DB_PORT=5433
### Run
By default, Docker images do not expose their ports to the underlying host machine. We need to do it explicitly using the -p flag.
`docker run -p 8000:8000 backend`
`docker run -p 8000:8000 backend`
### Sqlx
**Sqlx and Diesel commands**
* create migration
* sqlx: sqlx migrate add $(table)
* diesel: diesel migration generation $(table)
* run migration
* sqlx: sqlx migrate run
* diesel: diesel migration run
* reset database
* sqlx: sqlx database reset
* diesel: diesel database reset
**Type mapping**
* [postgres type map](https://docs.rs/sqlx/0.5.7/sqlx/postgres/types/index.html)
* [postgres and diesel type map](https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html)

View File

@ -0,0 +1,10 @@
-- Add migration script here
CREATE TABLE workspace_table(
id uuid NOT NULL,
PRIMARY KEY (id),
name TEXT NOT NULL,
description TEXT NOT NULL,
modified_time timestamptz NOT NULL,
create_time timestamptz NOT NULL,
user_id TEXT NOT NULL
);

View File

@ -0,0 +1,14 @@
-- Add migration script here
CREATE TABLE app_table(
id uuid NOT NULL,
PRIMARY KEY (id),
workspace_id TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT NOT NULL,
color_style BYTEA NOT NULL,
last_view_id TEXT DEFAULT '',
modified_time timestamptz NOT NULL,
create_time timestamptz NOT NULL,
user_id TEXT NOT NULL,
is_trash BOOL NOT NULL DEFAULT false
);

View File

@ -0,0 +1,13 @@
-- Add migration script here
CREATE TABLE view_table(
id uuid NOT NULL,
PRIMARY KEY (id),
belong_to_id TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT NOT NULL,
modified_time timestamptz NOT NULL,
create_time timestamptz NOT NULL,
thumbnail TEXT NOT NULL,
view_type INTEGER NOT NULL,
is_trash BOOL NOT NULL DEFAULT false
);

View File

@ -13,13 +13,16 @@ init_database:
${ROOT}/db_init.sh
reset_db:
#diesel database reset
sqlx database reset
add_migrations:
#make table="the name of your table" add_migrations
# diesel migration generation $(table)
sqlx migrate add $(table)
run_migrations:
# diesel migration run
sqlx migrate run
echo_db_url:

View File

@ -7,6 +7,9 @@ use crate::{
},
context::AppContext,
routers::*,
user_service::router as user,
workspace_service::{app::router as app, view::router as view, workspace::router as workspace},
ws_service,
ws_service::WSServer,
};
use actix::Actor;
@ -60,23 +63,41 @@ pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io
Ok(server)
}
fn ws_scope() -> Scope { web::scope("/ws").service(ws_router::start_connection) }
fn ws_scope() -> Scope { web::scope("/ws").service(ws_service::router::start_connection) }
fn user_scope() -> Scope {
web::scope("/api")
// authentication
.service(web::resource("/auth")
.route(web::post().to(user_router::sign_in_handler))
.route(web::delete().to(user_router::sign_out_handler))
.route(web::get().to(user_router::user_profile))
.route(web::post().to(user::sign_in_handler))
.route(web::delete().to(user::sign_out_handler))
.route(web::get().to(user::user_profile))
)
.service(web::resource("/workspace")
.route(web::post().to(workspace::create_workspace))
.route(web::delete().to(workspace::delete_workspace))
.route(web::get().to(workspace::read_workspace))
.route(web::patch().to(workspace::update_workspace))
)
.service(web::resource("/app")
.route(web::post().to(app::create_app))
.route(web::delete().to(app::delete_app))
.route(web::get().to(app::read_app))
.route(web::patch().to(app::update_app))
)
.service(web::resource("/view")
.route(web::post().to(view::create_view))
.route(web::delete().to(view::delete_view))
.route(web::get().to(view::read_view))
.route(web::patch().to(view::update_view))
)
// password
.service(web::resource("/password_change")
.route(web::post().to(user_router::change_password))
.route(web::post().to(user::change_password))
)
// register
.service(web::resource("/register")
.route(web::post().to(user_router::register_user_handler))
.route(web::post().to(user::register_user_handler))
)
}

View File

@ -4,4 +4,5 @@ mod context;
mod entities;
mod routers;
pub mod user_service;
pub mod workspace_service;
pub mod ws_service;

View File

@ -1,6 +1 @@
pub(crate) mod user_router;
mod utils;
pub(crate) mod ws_router;
pub use user_router::*;
pub use ws_router::*;
pub(crate) mod utils;

View File

@ -1,7 +1,7 @@
use crate::config::MAX_PAYLOAD_SIZE;
use actix_web::web;
use flowy_net::{
errors::{ErrorCode, ServerError},
errors::{ErrorCode, Kind, ServerError},
response::*,
};
use futures::StreamExt;
@ -26,10 +26,11 @@ pub async fn poll_payload(mut payload: web::Payload) -> Result<web::BytesMut, Se
let chunk = chunk.map_err(|err| ServerError::internal().context(err))?;
if (body.len() + chunk.len()) > MAX_PAYLOAD_SIZE {
return Err(ServerError {
code: ErrorCode::PayloadOverflow,
msg: "Payload overflow".to_string(),
});
return Err(ServerError::new(
"Payload overflow".to_string(),
ErrorCode::PayloadOverflow,
Kind::Other,
));
}
body.extend_from_slice(&chunk);
}

View File

@ -1,16 +1,16 @@
use crate::{
entities::{token::Token, user::User},
user_service::utils::{hash_password, verify_password},
user_service::{hash_password, verify_password},
};
use actix_identity::Identity;
use anyhow::Context;
use chrono::Utc;
use flowy_net::{
errors::{ErrorCode, ServerError},
errors::{ErrorCode, Kind, ServerError},
response::FlowyResponse,
};
use flowy_user::{
entities::{SignInResponse, SignUpResponse},
entities::{parser::UserName, SignInResponse, SignUpResponse},
prelude::parser::{UserEmail, UserPassword},
protobuf::{SignInParams, SignUpParams},
};
@ -58,15 +58,27 @@ pub async fn register_user(
pool: &PgPool,
params: SignUpParams,
) -> Result<FlowyResponse, ServerError> {
let name =
UserName::parse(params.name).map_err(|e| ServerError::params_invalid().context(e))?;
let email =
UserEmail::parse(params.email).map_err(|e| ServerError::params_invalid().context(e))?;
let password = UserPassword::parse(params.password)
.map_err(|e| ServerError::params_invalid().context(e))?;
let mut transaction = pool
.begin()
.await
.context("Failed to acquire a Postgres connection to register user")?;
let _ = is_email_exist(&mut transaction, &params.email).await?;
let data = insert_user(&mut transaction, params)
.await
.context("Failed to insert user")?;
let _ = is_email_exist(&mut transaction, email.as_ref()).await?;
let data = insert_user(
&mut transaction,
name.as_ref(),
email.as_ref(),
password.as_ref(),
)
.await
.context("Failed to insert user")?;
transaction
.commit()
@ -90,6 +102,7 @@ async fn is_email_exist(
Some(_) => Err(ServerError {
code: ErrorCode::EmailAlreadyExists,
msg: format!("{} already exists", email),
kind: Kind::User,
}),
None => Ok(()),
}
@ -110,18 +123,20 @@ async fn read_user(
async fn insert_user(
transaction: &mut Transaction<'_, Postgres>,
params: SignUpParams,
name: &str,
email: &str,
password: &str,
) -> Result<SignUpResponse, ServerError> {
let uuid = uuid::Uuid::new_v4();
let password = hash_password(&params.password)?;
let password = hash_password(password)?;
let _ = sqlx::query!(
r#"
INSERT INTO user_table (id, email, name, create_time, password)
VALUES ($1, $2, $3, $4, $5)
"#,
uuid,
params.email,
params.name,
email,
name,
Utc::now(),
password,
)
@ -131,8 +146,8 @@ async fn insert_user(
let data = SignUpResponse {
uid: uuid.to_string(),
name: params.name,
email: params.email,
name: name.to_string(),
email: email.to_string(),
};
Ok(data)

View File

@ -1,2 +1,8 @@
pub mod auth_service;
pub mod utils;
mod auth;
mod profile;
pub mod router;
mod utils;
pub use auth::*;
pub use profile::*;
pub use utils::*;

View File

View File

@ -8,7 +8,7 @@ use actix_web::{
use flowy_net::response::*;
use flowy_user::protobuf::{SignInParams, SignUpParams};
use crate::user_service::auth_service::{register_user, sign_in};
use crate::user_service::{register_user, sign_in};
use actix_identity::Identity;
use flowy_net::errors::ServerError;
use sqlx::PgPool;

View File

@ -1,5 +1,5 @@
use bcrypt::{hash, verify, BcryptError, DEFAULT_COST};
use flowy_net::errors::{ErrorCode, ServerError};
use flowy_net::errors::{ErrorCode, Kind, ServerError};
use jsonwebtoken::Algorithm;
pub fn uuid() -> String { uuid::Uuid::new_v4().to_string() }
@ -16,9 +16,10 @@ pub fn hash_password(plain: &str) -> Result<String, ServerError> {
pub fn verify_password(source: &str, hash: &str) -> Result<bool, ServerError> {
match verify(source, hash) {
Ok(true) => Ok(true),
_ => Err(ServerError {
code: ErrorCode::PasswordNotMatch,
msg: "Username and password don't match".to_string(),
}),
_ => Err(ServerError::new(
"Username and password don't match".to_string(),
ErrorCode::PasswordNotMatch,
Kind::User,
)),
}
}

View File

@ -0,0 +1 @@
pub mod router;

View File

@ -0,0 +1,41 @@
use actix_identity::Identity;
use actix_web::{
web::{Data, Payload},
Error,
HttpRequest,
HttpResponse,
};
use flowy_net::errors::ServerError;
use sqlx::PgPool;
pub async fn create_app(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn read_app(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn update_app(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn delete_app(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}

View File

@ -0,0 +1,3 @@
pub mod app;
pub mod view;
pub mod workspace;

View File

@ -0,0 +1 @@
pub mod router;

View File

@ -0,0 +1,41 @@
use actix_identity::Identity;
use actix_web::{
web::{Data, Payload},
Error,
HttpRequest,
HttpResponse,
};
use flowy_net::errors::ServerError;
use sqlx::PgPool;
pub async fn create_view(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn read_view(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn update_view(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn delete_view(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}

View File

@ -0,0 +1 @@
pub mod router;

View File

@ -0,0 +1,41 @@
use actix_identity::Identity;
use actix_web::{
web::{Data, Payload},
Error,
HttpRequest,
HttpResponse,
};
use flowy_net::errors::ServerError;
use sqlx::PgPool;
pub async fn create_workspace(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn read_workspace(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn delete_workspace(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}
pub async fn update_workspace(
payload: Payload,
id: Identity,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
unimplemented!()
}

View File

@ -3,5 +3,6 @@ pub use ws_client::*;
pub use ws_server::*;
pub(crate) mod entities;
pub mod router;
mod ws_client;
mod ws_server;

View File

@ -1,33 +1,37 @@
use crate::helper::spawn_app;
use flowy_user::entities::{SignInParams, SignInResponse, SignUpParams};
use crate::helper::{spawn_app, TestApp};
use flowy_user::entities::{SignInParams, SignInResponse, SignUpParams, SignUpResponse};
#[actix_rt::test]
async fn user_register() {
let app = spawn_app().await;
let params = SignUpParams {
email: "annie@appflowy.io".to_string(),
name: "annie".to_string(),
password: "123".to_string(),
};
let response = app.register_user(params).await;
let response = register_user(&app, "annie@appflowy.io", "HelloWork123!").await;
log::info!("{:?}", response);
}
#[actix_rt::test]
#[should_panic]
async fn user_sign_in_with_invalid_password() {
let app = spawn_app().await;
let email = "annie@appflowy.io";
let password = "123";
let _ = register_user(&app, email, password).await;
}
#[actix_rt::test]
#[should_panic]
async fn user_sign_in_with_invalid_email() {
let app = spawn_app().await;
let email = "annie@gmail@";
let password = "HelloWork123!";
let _ = register_user(&app, email, password).await;
}
#[actix_rt::test]
async fn user_sign_in() {
let app = spawn_app().await;
let email = "annie@appflowy.io";
let password = "123";
let _ = app
.register_user(SignUpParams {
email: email.to_string(),
name: "annie".to_string(),
password: password.to_string(),
})
.await;
let password = "HelloWork123!";
let _ = register_user(&app, email, password).await;
let response = app
.sign_in(SignInParams {
email: email.to_string(),
@ -37,3 +41,14 @@ async fn user_sign_in() {
log::info!("{:?}", response);
}
async fn register_user(app: &TestApp, email: &str, password: &str) -> SignUpResponse {
let params = SignUpParams {
email: email.to_string(),
name: "annie".to_string(),
password: password.to_string(),
};
let response = app.register_user(params).await;
response
}

View File

@ -9,6 +9,7 @@ use crate::response::FlowyResponse;
pub struct ServerError {
pub code: ErrorCode,
pub msg: String,
pub kind: Kind,
}
macro_rules! static_error {
@ -18,6 +19,7 @@ macro_rules! static_error {
ServerError {
code: $status,
msg: format!("{}", $status),
kind: Kind::Other,
}
}
};
@ -30,6 +32,17 @@ impl ServerError {
static_error!(unauthorized, ErrorCode::Unauthorized);
static_error!(password_not_match, ErrorCode::PasswordNotMatch);
static_error!(params_invalid, ErrorCode::ParamsInvalid);
static_error!(connect_timeout, ErrorCode::ConnectTimeout);
static_error!(connect_close, ErrorCode::ConnectClose);
static_error!(connect_cancel, ErrorCode::ConnectCancel);
static_error!(connect_refused, ErrorCode::ConnectRefused);
pub fn new(msg: String, code: ErrorCode, kind: Kind) -> Self { Self { code, msg, kind } }
pub fn kind(mut self, kind: Kind) -> Self {
self.kind = kind;
self
}
pub fn context<T: Debug>(mut self, error: T) -> Self {
self.msg = format!("{:?}", error);
@ -53,6 +66,12 @@ impl std::convert::From<&ServerError> for FlowyResponse {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Kind {
User,
Other,
}
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone, derive_more::Display)]
#[repr(u16)]
pub enum ErrorCode {

View File

@ -1,4 +1,4 @@
use crate::errors::{ErrorCode, ServerError};
use crate::errors::{ErrorCode, Kind, ServerError};
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use std::{convert::TryInto, error::Error, fmt::Debug};
@ -23,12 +23,7 @@ impl FlowyResponse {
}
impl std::convert::From<protobuf::ProtobufError> for ServerError {
fn from(err: protobuf::ProtobufError) -> Self {
ServerError {
code: ErrorCode::ProtobufError,
msg: format!("{}", err),
}
}
fn from(e: protobuf::ProtobufError) -> Self { ServerError::internal().context(e) }
}
impl std::convert::From<RecvError> for ServerError {
@ -36,13 +31,7 @@ impl std::convert::From<RecvError> for ServerError {
}
impl std::convert::From<serde_json::Error> for ServerError {
fn from(e: serde_json::Error) -> Self {
let msg = format!("Serial error: {:?}", e);
ServerError {
code: ErrorCode::SerdeError,
msg,
}
}
fn from(e: serde_json::Error) -> Self { ServerError::internal().context(e) }
}
impl std::convert::From<anyhow::Error> for ServerError {
@ -52,19 +41,13 @@ impl std::convert::From<anyhow::Error> for ServerError {
impl std::convert::From<reqwest::Error> for ServerError {
fn from(error: reqwest::Error) -> Self {
if error.is_timeout() {
return ServerError {
code: ErrorCode::ConnectTimeout,
msg: format!("{}", error),
};
return ServerError::connect_timeout().context(error);
}
if error.is_request() {
let hyper_error: Option<&hyper::Error> = error.source().unwrap().downcast_ref();
return match hyper_error {
None => ServerError {
code: ErrorCode::ConnectRefused,
msg: format!("{:?}", error),
},
None => ServerError::connect_refused().context(error),
Some(hyper_error) => {
let mut code = ErrorCode::InternalError;
let msg = format!("{}", error);
@ -82,15 +65,15 @@ impl std::convert::From<reqwest::Error> for ServerError {
if hyper_error.is_timeout() {}
ServerError { code, msg }
ServerError {
code,
msg,
kind: Kind::Other,
}
},
};
}
let msg = format!("{:?}", error);
ServerError {
code: ErrorCode::ProtobufError,
msg,
}
ServerError::internal().context(error)
}
}

View File

@ -30,6 +30,10 @@ impl UserPassword {
}
}
impl AsRef<str> for UserPassword {
fn as_ref(&self) -> &str { &self.0 }
}
lazy_static! {
// Test it in https://regex101.com/
// https://stackoverflow.com/questions/2370015/regular-expression-for-password-validation/2370045

View File

@ -2,7 +2,11 @@ use bytes::Bytes;
use derive_more::Display;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_dispatch::prelude::{EventResponse, ResponseBuilder};
use std::convert::TryInto;
use flowy_net::errors::Kind;
use std::{
convert::TryInto,
fmt::{Debug, Formatter},
};
#[derive(Debug, Default, Clone, ProtoBuf)]
pub struct UserError {
@ -22,7 +26,7 @@ impl UserError {
}
}
#[derive(Debug, Clone, ProtoBuf_Enum, Display, PartialEq, Eq)]
#[derive(Clone, ProtoBuf_Enum, Display, PartialEq, Eq)]
pub enum UserErrCode {
#[display(fmt = "Unknown")]
Unknown = 0,
@ -80,8 +84,12 @@ pub enum UserErrCode {
#[display(fmt = "User default workspace already exists")]
DefaultWorkspaceAlreadyExist = 53,
#[display(fmt = "Network error")]
NetworkError = 100,
#[display(fmt = "Server error")]
ServerError = 100,
}
impl Debug for UserErrCode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&format!("{}", self)) }
}
impl UserErrCode {
@ -139,8 +147,8 @@ impl std::convert::From<flowy_sqlite::Error> for UserError {
impl std::convert::From<flowy_net::errors::ServerError> for UserError {
fn from(error: flowy_net::errors::ServerError) -> Self {
ErrorBuilder::new(UserErrCode::NetworkError)
.error(error)
ErrorBuilder::new(UserErrCode::ServerError)
.error(error.msg)
.build()
}
}

View File

@ -1,9 +1,9 @@
mod event;
mod handlers;
mod sql_tables;
pub mod entities;
pub mod errors;
pub mod event;
pub mod module;
pub mod protobuf;
pub mod services;

View File

@ -239,7 +239,7 @@ pub enum UserErrCode {
UserIdInvalid = 51,
CreateDefaultWorkspaceFailed = 52,
DefaultWorkspaceAlreadyExist = 53,
NetworkError = 100,
ServerError = 100,
}
impl ::protobuf::ProtobufEnum for UserErrCode {
@ -273,7 +273,7 @@ impl ::protobuf::ProtobufEnum for UserErrCode {
51 => ::std::option::Option::Some(UserErrCode::UserIdInvalid),
52 => ::std::option::Option::Some(UserErrCode::CreateDefaultWorkspaceFailed),
53 => ::std::option::Option::Some(UserErrCode::DefaultWorkspaceAlreadyExist),
100 => ::std::option::Option::Some(UserErrCode::NetworkError),
100 => ::std::option::Option::Some(UserErrCode::ServerError),
_ => ::std::option::Option::None
}
}
@ -304,7 +304,7 @@ impl ::protobuf::ProtobufEnum for UserErrCode {
UserErrCode::UserIdInvalid,
UserErrCode::CreateDefaultWorkspaceFailed,
UserErrCode::DefaultWorkspaceAlreadyExist,
UserErrCode::NetworkError,
UserErrCode::ServerError,
];
values
}
@ -335,7 +335,7 @@ impl ::protobuf::reflect::ProtobufValue for UserErrCode {
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0cerrors.proto\"?\n\tUserError\x12\x20\n\x04code\x18\x01\x20\x01(\
\x0e2\x0c.UserErrCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03m\
sg*\x8c\x05\n\x0bUserErrCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16Use\
sg*\x8b\x05\n\x0bUserErrCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16Use\
rDatabaseInitFailed\x10\x01\x12\x1b\n\x17UserDatabaseWriteLocked\x10\x02\
\x12\x1a\n\x16UserDatabaseReadLocked\x10\x03\x12\x1b\n\x17UserDatabaseDi\
dNotMatch\x10\x04\x12\x1d\n\x19UserDatabaseInternalError\x10\x05\x12\x14\
@ -348,8 +348,8 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x12\x13\n\x0fUserNameTooLong\x10(\x12'\n#UserNameContainsForbiddenChara\
cters\x10)\x12\x13\n\x0fUserNameIsEmpty\x10*\x12\x18\n\x14UserWorkspaceI\
nvalid\x102\x12\x11\n\rUserIdInvalid\x103\x12\x20\n\x1cCreateDefaultWork\
spaceFailed\x104\x12\x20\n\x1cDefaultWorkspaceAlreadyExist\x105\x12\x10\
\n\x0cNetworkError\x10dJ\xb1\t\n\x06\x12\x04\0\0\x20\x01\n\x08\n\x01\x0c\
spaceFailed\x104\x12\x20\n\x1cDefaultWorkspaceAlreadyExist\x105\x12\x0f\
\n\x0bServerError\x10dJ\xb1\t\n\x06\x12\x04\0\0\x20\x01\n\x08\n\x01\x0c\
\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\
\x01\x12\x03\x02\x08\x11\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x19\n\
\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\x0f\n\x0c\n\x05\x04\0\x02\0\
@ -406,8 +406,8 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x12\x03\x1d\x04\x20\n\x0c\n\x05\x05\0\x02\x16\x02\x12\x03\x1d#%\n\x0b\n\
\x04\x05\0\x02\x17\x12\x03\x1e\x04&\n\x0c\n\x05\x05\0\x02\x17\x01\x12\
\x03\x1e\x04\x20\n\x0c\n\x05\x05\0\x02\x17\x02\x12\x03\x1e#%\n\x0b\n\x04\
\x05\0\x02\x18\x12\x03\x1f\x04\x17\n\x0c\n\x05\x05\0\x02\x18\x01\x12\x03\
\x1f\x04\x10\n\x0c\n\x05\x05\0\x02\x18\x02\x12\x03\x1f\x13\x16b\x06proto\
\x05\0\x02\x18\x12\x03\x1f\x04\x16\n\x0c\n\x05\x05\0\x02\x18\x01\x12\x03\
\x1f\x04\x0f\n\x0c\n\x05\x05\0\x02\x18\x02\x12\x03\x1f\x12\x15b\x06proto\
3\
";

View File

@ -29,5 +29,5 @@ enum UserErrCode {
UserIdInvalid = 51;
CreateDefaultWorkspaceFailed = 52;
DefaultWorkspaceAlreadyExist = 53;
NetworkError = 100;
ServerError = 100;
}

View File

@ -49,14 +49,10 @@ fn sign_in_with_invalid_password() {
password,
};
assert_eq!(
UserTestBuilder::new()
.event(SignIn)
.request(request)
.sync_send()
.error()
.code,
UserErrCode::PasswordFormatInvalid
);
UserTestBuilder::new()
.event(SignIn)
.request(request)
.sync_send()
.assert_error();
}
}

View File

@ -49,14 +49,10 @@ fn sign_up_with_invalid_password() {
password,
};
assert_eq!(
UserTestBuilder::new()
.event(SignUp)
.request(request)
.sync_send()
.error()
.code,
UserErrCode::PasswordFormatInvalid
);
UserTestBuilder::new()
.event(SignUp)
.request(request)
.sync_send()
.assert_error();
}
}

View File

@ -104,15 +104,11 @@ fn user_update_with_invalid_password() {
password: Some(password),
};
assert_eq!(
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.error()
.code,
UserErrCode::PasswordFormatInvalid
);
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.assert_error();
}
}
@ -128,13 +124,9 @@ fn user_update_with_invalid_name() {
password: None,
};
assert_eq!(
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.error()
.code,
UserErrCode::UserIdInvalid
);
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.assert_error();
}