Browse Source

add workspace/app/view router

appflowy 3 years ago
parent
commit
74800278f9
36 changed files with 383 additions and 140 deletions
  1. 2 2
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbenum.dart
  2. 2 2
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbjson.dart
  3. 21 1
      backend/doc/database_setup.md
  4. 10 0
      backend/migrations/20210824033032_workspace.sql
  5. 14 0
      backend/migrations/20210824033742_app.sql
  6. 13 0
      backend/migrations/20210824033748_view.sql
  7. 3 0
      backend/scripts/database/database.mk
  8. 27 6
      backend/src/application.rs
  9. 1 0
      backend/src/lib.rs
  10. 1 6
      backend/src/routers/mod.rs
  11. 6 5
      backend/src/routers/utils.rs
  12. 28 13
      backend/src/user_service/auth.rs
  13. 8 2
      backend/src/user_service/mod.rs
  14. 0 0
      backend/src/user_service/profile.rs
  15. 1 1
      backend/src/user_service/router.rs
  16. 6 5
      backend/src/user_service/utils.rs
  17. 1 0
      backend/src/workspace_service/app/mod.rs
  18. 41 0
      backend/src/workspace_service/app/router.rs
  19. 3 0
      backend/src/workspace_service/mod.rs
  20. 1 0
      backend/src/workspace_service/view/mod.rs
  21. 41 0
      backend/src/workspace_service/view/router.rs
  22. 1 0
      backend/src/workspace_service/workspace/mod.rs
  23. 41 0
      backend/src/workspace_service/workspace/router.rs
  24. 1 0
      backend/src/ws_service/mod.rs
  25. 0 0
      backend/src/ws_service/router.rs
  26. 32 17
      backend/tests/api/auth.rs
  27. 19 0
      rust-lib/flowy-net/src/errors.rs
  28. 11 28
      rust-lib/flowy-net/src/response/response.rs
  29. 4 0
      rust-lib/flowy-user/src/entities/parser/user_password.rs
  30. 14 6
      rust-lib/flowy-user/src/errors.rs
  31. 1 1
      rust-lib/flowy-user/src/lib.rs
  32. 8 8
      rust-lib/flowy-user/src/protobuf/model/errors.rs
  33. 1 1
      rust-lib/flowy-user/src/protobuf/proto/errors.proto
  34. 5 9
      rust-lib/flowy-user/tests/event/sign_in_test.rs
  35. 5 9
      rust-lib/flowy-user/tests/event/sign_up_test.rs
  36. 10 18
      rust-lib/flowy-user/tests/event/user_update_test.rs

+ 2 - 2
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbenum.dart

@@ -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 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 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 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> [
   static const $core.List<UserErrCode> values = <UserErrCode> [
     Unknown,
     Unknown,
@@ -61,7 +61,7 @@ class UserErrCode extends $pb.ProtobufEnum {
     UserIdInvalid,
     UserIdInvalid,
     CreateDefaultWorkspaceFailed,
     CreateDefaultWorkspaceFailed,
     DefaultWorkspaceAlreadyExist,
     DefaultWorkspaceAlreadyExist,
-    NetworkError,
+    ServerError,
   ];
   ];
 
 
   static final $core.Map<$core.int, UserErrCode> _byValue = $pb.ProtobufEnum.initByValue(values);
   static final $core.Map<$core.int, UserErrCode> _byValue = $pb.ProtobufEnum.initByValue(values);

+ 2 - 2
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbjson.dart

@@ -36,12 +36,12 @@ const UserErrCode$json = const {
     const {'1': 'UserIdInvalid', '2': 51},
     const {'1': 'UserIdInvalid', '2': 51},
     const {'1': 'CreateDefaultWorkspaceFailed', '2': 52},
     const {'1': 'CreateDefaultWorkspaceFailed', '2': 52},
     const {'1': 'DefaultWorkspaceAlreadyExist', '2': 53},
     const {'1': 'DefaultWorkspaceAlreadyExist', '2': 53},
-    const {'1': 'NetworkError', '2': 100},
+    const {'1': 'ServerError', '2': 100},
   ],
   ],
 };
 };
 
 
 /// Descriptor for `UserErrCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
 /// 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')
 @$core.Deprecated('Use userErrorDescriptor instead')
 const UserError$json = const {
 const UserError$json = const {
   '1': 'UserError',
   '1': 'UserError',

+ 21 - 1
backend/doc/database_setup.md

@@ -31,4 +31,24 @@ export DB_PORT=5433
 
 
 ### Run
 ### 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.
 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)

+ 10 - 0
backend/migrations/20210824033032_workspace.sql

@@ -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
+);

+ 14 - 0
backend/migrations/20210824033742_app.sql

@@ -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
+);

+ 13 - 0
backend/migrations/20210824033748_view.sql

@@ -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
+);

+ 3 - 0
backend/scripts/database/database.mk

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

+ 27 - 6
backend/src/application.rs

@@ -7,6 +7,9 @@ use crate::{
     },
     },
     context::AppContext,
     context::AppContext,
     routers::*,
     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,
     ws_service::WSServer,
 };
 };
 use actix::Actor;
 use actix::Actor;
@@ -60,23 +63,41 @@ pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result<Server, std::io
     Ok(server)
     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 {
 fn user_scope() -> Scope {
     web::scope("/api")
     web::scope("/api")
         // authentication
         // authentication
         .service(web::resource("/auth")
         .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
         // password
         .service(web::resource("/password_change")
         .service(web::resource("/password_change")
-            .route(web::post().to(user_router::change_password))
+            .route(web::post().to(user::change_password))
         )
         )
         // register
         // register
         .service(web::resource("/register")
         .service(web::resource("/register")
-            .route(web::post().to(user_router::register_user_handler))
+            .route(web::post().to(user::register_user_handler))
         )
         )
 }
 }
 
 

+ 1 - 0
backend/src/lib.rs

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

+ 1 - 6
backend/src/routers/mod.rs

@@ -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;

+ 6 - 5
backend/src/routers/utils.rs

@@ -1,7 +1,7 @@
 use crate::config::MAX_PAYLOAD_SIZE;
 use crate::config::MAX_PAYLOAD_SIZE;
 use actix_web::web;
 use actix_web::web;
 use flowy_net::{
 use flowy_net::{
-    errors::{ErrorCode, ServerError},
+    errors::{ErrorCode, Kind, ServerError},
     response::*,
     response::*,
 };
 };
 use futures::StreamExt;
 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))?;
         let chunk = chunk.map_err(|err| ServerError::internal().context(err))?;
 
 
         if (body.len() + chunk.len()) > MAX_PAYLOAD_SIZE {
         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);
         body.extend_from_slice(&chunk);
     }
     }

+ 28 - 13
backend/src/user_service/auth_service.rs → backend/src/user_service/auth.rs

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

+ 8 - 2
backend/src/user_service/mod.rs

@@ -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::*;

+ 0 - 0
backend/src/user_service/profile.rs


+ 1 - 1
backend/src/routers/user_router.rs → backend/src/user_service/router.rs

@@ -8,7 +8,7 @@ use actix_web::{
 use flowy_net::response::*;
 use flowy_net::response::*;
 use flowy_user::protobuf::{SignInParams, SignUpParams};
 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 actix_identity::Identity;
 use flowy_net::errors::ServerError;
 use flowy_net::errors::ServerError;
 use sqlx::PgPool;
 use sqlx::PgPool;

+ 6 - 5
backend/src/user_service/utils.rs

@@ -1,5 +1,5 @@
 use bcrypt::{hash, verify, BcryptError, DEFAULT_COST};
 use bcrypt::{hash, verify, BcryptError, DEFAULT_COST};
-use flowy_net::errors::{ErrorCode, ServerError};
+use flowy_net::errors::{ErrorCode, Kind, ServerError};
 use jsonwebtoken::Algorithm;
 use jsonwebtoken::Algorithm;
 
 
 pub fn uuid() -> String { uuid::Uuid::new_v4().to_string() }
 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> {
 pub fn verify_password(source: &str, hash: &str) -> Result<bool, ServerError> {
     match verify(source, hash) {
     match verify(source, hash) {
         Ok(true) => Ok(true),
         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,
+        )),
     }
     }
 }
 }

+ 1 - 0
backend/src/workspace_service/app/mod.rs

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

+ 41 - 0
backend/src/workspace_service/app/router.rs

@@ -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!()
+}

+ 3 - 0
backend/src/workspace_service/mod.rs

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

+ 1 - 0
backend/src/workspace_service/view/mod.rs

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

+ 41 - 0
backend/src/workspace_service/view/router.rs

@@ -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!()
+}

+ 1 - 0
backend/src/workspace_service/workspace/mod.rs

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

+ 41 - 0
backend/src/workspace_service/workspace/router.rs

@@ -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!()
+}

+ 1 - 0
backend/src/ws_service/mod.rs

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

+ 0 - 0
backend/src/routers/ws_router.rs → backend/src/ws_service/router.rs


+ 32 - 17
backend/tests/api/auth.rs

@@ -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]
 #[actix_rt::test]
 async fn user_register() {
 async fn user_register() {
     let app = spawn_app().await;
     let app = spawn_app().await;
-    let params = SignUpParams {
-        email: "[email protected]".to_string(),
-        name: "annie".to_string(),
-        password: "123".to_string(),
-    };
-
-    let response = app.register_user(params).await;
+    let response = register_user(&app, "[email protected]", "HelloWork123!").await;
     log::info!("{:?}", response);
     log::info!("{:?}", response);
 }
 }
 
 
 #[actix_rt::test]
 #[actix_rt::test]
-async fn user_sign_in() {
+#[should_panic]
+async fn user_sign_in_with_invalid_password() {
     let app = spawn_app().await;
     let app = spawn_app().await;
     let email = "[email protected]";
     let email = "[email protected]";
     let password = "123";
     let password = "123";
+    let _ = register_user(&app, email, password).await;
+}
 
 
-    let _ = app
-        .register_user(SignUpParams {
-            email: email.to_string(),
-            name: "annie".to_string(),
-            password: password.to_string(),
-        })
-        .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 = "[email protected]";
+    let password = "HelloWork123!";
+    let _ = register_user(&app, email, password).await;
     let response = app
     let response = app
         .sign_in(SignInParams {
         .sign_in(SignInParams {
             email: email.to_string(),
             email: email.to_string(),
@@ -37,3 +41,14 @@ async fn user_sign_in() {
 
 
     log::info!("{:?}", response);
     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
+}

+ 19 - 0
rust-lib/flowy-net/src/errors.rs

@@ -9,6 +9,7 @@ use crate::response::FlowyResponse;
 pub struct ServerError {
 pub struct ServerError {
     pub code: ErrorCode,
     pub code: ErrorCode,
     pub msg: String,
     pub msg: String,
+    pub kind: Kind,
 }
 }
 
 
 macro_rules! static_error {
 macro_rules! static_error {
@@ -18,6 +19,7 @@ macro_rules! static_error {
             ServerError {
             ServerError {
                 code: $status,
                 code: $status,
                 msg: format!("{}", $status),
                 msg: format!("{}", $status),
+                kind: Kind::Other,
             }
             }
         }
         }
     };
     };
@@ -30,6 +32,17 @@ impl ServerError {
     static_error!(unauthorized, ErrorCode::Unauthorized);
     static_error!(unauthorized, ErrorCode::Unauthorized);
     static_error!(password_not_match, ErrorCode::PasswordNotMatch);
     static_error!(password_not_match, ErrorCode::PasswordNotMatch);
     static_error!(params_invalid, ErrorCode::ParamsInvalid);
     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 {
     pub fn context<T: Debug>(mut self, error: T) -> Self {
         self.msg = format!("{:?}", error);
         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)]
 #[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone, derive_more::Display)]
 #[repr(u16)]
 #[repr(u16)]
 pub enum ErrorCode {
 pub enum ErrorCode {

+ 11 - 28
rust-lib/flowy-net/src/response/response.rs

@@ -1,4 +1,4 @@
-use crate::errors::{ErrorCode, ServerError};
+use crate::errors::{ErrorCode, Kind, ServerError};
 use bytes::Bytes;
 use bytes::Bytes;
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
 use std::{convert::TryInto, error::Error, fmt::Debug};
 use std::{convert::TryInto, error::Error, fmt::Debug};
@@ -23,12 +23,7 @@ impl FlowyResponse {
 }
 }
 
 
 impl std::convert::From<protobuf::ProtobufError> for ServerError {
 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 {
 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 {
 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 {
 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 {
 impl std::convert::From<reqwest::Error> for ServerError {
     fn from(error: reqwest::Error) -> Self {
     fn from(error: reqwest::Error) -> Self {
         if error.is_timeout() {
         if error.is_timeout() {
-            return ServerError {
-                code: ErrorCode::ConnectTimeout,
-                msg: format!("{}", error),
-            };
+            return ServerError::connect_timeout().context(error);
         }
         }
 
 
         if error.is_request() {
         if error.is_request() {
             let hyper_error: Option<&hyper::Error> = error.source().unwrap().downcast_ref();
             let hyper_error: Option<&hyper::Error> = error.source().unwrap().downcast_ref();
             return match hyper_error {
             return match hyper_error {
-                None => ServerError {
-                    code: ErrorCode::ConnectRefused,
-                    msg: format!("{:?}", error),
-                },
+                None => ServerError::connect_refused().context(error),
                 Some(hyper_error) => {
                 Some(hyper_error) => {
                     let mut code = ErrorCode::InternalError;
                     let mut code = ErrorCode::InternalError;
                     let msg = format!("{}", error);
                     let msg = format!("{}", error);
@@ -82,15 +65,15 @@ impl std::convert::From<reqwest::Error> for ServerError {
 
 
                     if hyper_error.is_timeout() {}
                     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)
     }
     }
 }
 }

+ 4 - 0
rust-lib/flowy-user/src/entities/parser/user_password.rs

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

+ 14 - 6
rust-lib/flowy-user/src/errors.rs

@@ -2,7 +2,11 @@ use bytes::Bytes;
 use derive_more::Display;
 use derive_more::Display;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_dispatch::prelude::{EventResponse, ResponseBuilder};
 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)]
 #[derive(Debug, Default, Clone, ProtoBuf)]
 pub struct UserError {
 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 {
 pub enum UserErrCode {
     #[display(fmt = "Unknown")]
     #[display(fmt = "Unknown")]
     Unknown              = 0,
     Unknown              = 0,
@@ -80,8 +84,12 @@ pub enum UserErrCode {
     #[display(fmt = "User default workspace already exists")]
     #[display(fmt = "User default workspace already exists")]
     DefaultWorkspaceAlreadyExist = 53,
     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 {
 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 {
 impl std::convert::From<flowy_net::errors::ServerError> for UserError {
     fn from(error: flowy_net::errors::ServerError) -> Self {
     fn from(error: flowy_net::errors::ServerError) -> Self {
-        ErrorBuilder::new(UserErrCode::NetworkError)
-            .error(error)
+        ErrorBuilder::new(UserErrCode::ServerError)
+            .error(error.msg)
             .build()
             .build()
     }
     }
 }
 }

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

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

+ 8 - 8
rust-lib/flowy-user/src/protobuf/model/errors.rs

@@ -239,7 +239,7 @@ pub enum UserErrCode {
     UserIdInvalid = 51,
     UserIdInvalid = 51,
     CreateDefaultWorkspaceFailed = 52,
     CreateDefaultWorkspaceFailed = 52,
     DefaultWorkspaceAlreadyExist = 53,
     DefaultWorkspaceAlreadyExist = 53,
-    NetworkError = 100,
+    ServerError = 100,
 }
 }
 
 
 impl ::protobuf::ProtobufEnum for UserErrCode {
 impl ::protobuf::ProtobufEnum for UserErrCode {
@@ -273,7 +273,7 @@ impl ::protobuf::ProtobufEnum for UserErrCode {
             51 => ::std::option::Option::Some(UserErrCode::UserIdInvalid),
             51 => ::std::option::Option::Some(UserErrCode::UserIdInvalid),
             52 => ::std::option::Option::Some(UserErrCode::CreateDefaultWorkspaceFailed),
             52 => ::std::option::Option::Some(UserErrCode::CreateDefaultWorkspaceFailed),
             53 => ::std::option::Option::Some(UserErrCode::DefaultWorkspaceAlreadyExist),
             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
             _ => ::std::option::Option::None
         }
         }
     }
     }
@@ -304,7 +304,7 @@ impl ::protobuf::ProtobufEnum for UserErrCode {
             UserErrCode::UserIdInvalid,
             UserErrCode::UserIdInvalid,
             UserErrCode::CreateDefaultWorkspaceFailed,
             UserErrCode::CreateDefaultWorkspaceFailed,
             UserErrCode::DefaultWorkspaceAlreadyExist,
             UserErrCode::DefaultWorkspaceAlreadyExist,
-            UserErrCode::NetworkError,
+            UserErrCode::ServerError,
         ];
         ];
         values
         values
     }
     }
@@ -335,7 +335,7 @@ impl ::protobuf::reflect::ProtobufValue for UserErrCode {
 static file_descriptor_proto_data: &'static [u8] = b"\
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x0cerrors.proto\"?\n\tUserError\x12\x20\n\x04code\x18\x01\x20\x01(\
     \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\
     \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\
     rDatabaseInitFailed\x10\x01\x12\x1b\n\x17UserDatabaseWriteLocked\x10\x02\
     \x12\x1a\n\x16UserDatabaseReadLocked\x10\x03\x12\x1b\n\x17UserDatabaseDi\
     \x12\x1a\n\x16UserDatabaseReadLocked\x10\x03\x12\x1b\n\x17UserDatabaseDi\
     dNotMatch\x10\x04\x12\x1d\n\x19UserDatabaseInternalError\x10\x05\x12\x14\
     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\
     \x12\x13\n\x0fUserNameTooLong\x10(\x12'\n#UserNameContainsForbiddenChara\
     cters\x10)\x12\x13\n\x0fUserNameIsEmpty\x10*\x12\x18\n\x14UserWorkspaceI\
     cters\x10)\x12\x13\n\x0fUserNameIsEmpty\x10*\x12\x18\n\x14UserWorkspaceI\
     nvalid\x102\x12\x11\n\rUserIdInvalid\x103\x12\x20\n\x1cCreateDefaultWork\
     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\
     \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\
     \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\
     \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\
     \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\
     \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\
     \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\
     3\
 ";
 ";
 
 

+ 1 - 1
rust-lib/flowy-user/src/protobuf/proto/errors.proto

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

+ 5 - 9
rust-lib/flowy-user/tests/event/sign_in_test.rs

@@ -49,14 +49,10 @@ fn sign_in_with_invalid_password() {
             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();
     }
     }
 }
 }

+ 5 - 9
rust-lib/flowy-user/tests/event/sign_up_test.rs

@@ -49,14 +49,10 @@ fn sign_up_with_invalid_password() {
             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();
     }
     }
 }
 }

+ 10 - 18
rust-lib/flowy-user/tests/event/user_update_test.rs

@@ -104,15 +104,11 @@ fn user_update_with_invalid_password() {
             password: Some(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,
         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();
 }
 }