浏览代码

[rust]: create backend api crate

appflowy 3 年之前
父节点
当前提交
55ea9e6cae
共有 31 个文件被更改,包括 501 次插入380 次删除
  1. 3 3
      app_flowy/packages/flowy_infra_ui/example/lib/overlay/overlay_screen.dart
  2. 5 4
      backend/Cargo.toml
  3. 1 0
      backend/src/application.rs
  4. 18 18
      backend/src/service/app/router.rs
  5. 14 25
      backend/src/sqlx_ext/query.rs
  6. 3 2
      backend/tests/api/auth.rs
  7. 1 1
      backend/tests/api/doc.rs
  8. 1 1
      backend/tests/api/workspace.rs
  9. 10 6
      backend/tests/helper.rs
  10. 1 0
      rust-lib/Cargo.toml
  11. 4 4
      rust-lib/dart-ffi/Cargo.toml
  12. 14 0
      rust-lib/flowy-backend-api/Cargo.toml
  13. 3 0
      rust-lib/flowy-backend-api/src/lib.rs
  14. 39 0
      rust-lib/flowy-backend-api/src/middleware.rs
  15. 52 0
      rust-lib/flowy-backend-api/src/user_request.rs
  16. 176 0
      rust-lib/flowy-backend-api/src/workspace_request.rs
  17. 1 0
      rust-lib/flowy-log/src/lib.rs
  18. 1 1
      rust-lib/flowy-sdk/Cargo.toml
  19. 1 1
      rust-lib/flowy-test/Cargo.toml
  20. 4 0
      rust-lib/flowy-user-infra/src/entities/mod.rs
  21. 1 0
      rust-lib/flowy-user/Cargo.toml
  22. 46 87
      rust-lib/flowy-user/src/services/server/server_api.rs
  23. 7 1
      rust-lib/flowy-workspace-infra/Cargo.toml
  24. 4 0
      rust-lib/flowy-workspace-infra/src/entities/mod.rs
  25. 3 0
      rust-lib/flowy-workspace-infra/src/lib.rs
  26. 2 1
      rust-lib/flowy-workspace/Cargo.toml
  27. 0 35
      rust-lib/flowy-workspace/src/services/server/middleware.rs
  28. 2 1
      rust-lib/flowy-workspace/src/services/server/mod.rs
  29. 81 189
      rust-lib/flowy-workspace/src/services/server/server_api.rs
  30. 2 0
      rust-lib/flowy-workspace/src/services/server/server_api_mock.rs
  31. 1 0
      rust-lib/flowy-workspace/src/services/workspace_controller.rs

+ 3 - 3
app_flowy/packages/flowy_infra_ui/example/lib/overlay/overlay_screen.dart

@@ -154,7 +154,7 @@ class OverlayScreen extends StatelessWidget {
                             color: Colors.orange[200],
                             child: GestureDetector(
                               // ignore: avoid_print
-                              onTapDown: (_) => print('Hello Flutter'),
+                              onTapDown: (_) => debugPrint('Hello Flutter'),
                               child: const Center(child: FlutterLogo(size: 100)),
                             ),
                           ),
@@ -202,8 +202,8 @@ class OverlayScreen extends StatelessWidget {
                         OptionOverlay.showWithAnchor(
                           context,
                           items: <String>['Alpha', 'Beta', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel'],
-                          onHover: (value, index) => print('Did hover option $index, value $value'),
-                          onTap: (value, index) => print('Did tap option $index, value $value'),
+                          onHover: (value, index) => debugPrint('Did hover option $index, value $value'),
+                          onTap: (value, index) => debugPrint('Did tap option $index, value $value'),
                           identifier: 'overlay_options',
                           anchorContext: buttonContext,
                           anchorDirection: providerContext.read<OverlayDemoConfiguration>().anchorDirection,

+ 5 - 4
backend/Cargo.toml

@@ -61,7 +61,7 @@ byteorder = {version = "1.3.4"}
 async-stream = "0.3.2"
 
 flowy-user-infra = { path = "../rust-lib/flowy-user-infra" }
-flowy-workspace-infra = { path = "../rust-lib/flowy-workspace-infra"}
+flowy-workspace-infra = { path = "../rust-lib/flowy-workspace-infra" }
 flowy-document = { path = "../rust-lib/flowy-document" }
 flowy-ws = { path = "../rust-lib/flowy-ws" }
 flowy-ot = { path = "../rust-lib/flowy-ot" }
@@ -99,13 +99,14 @@ parking_lot = "0.11"
 once_cell = "1.7.2"
 linkify = "0.5.0"
 backend = { path = ".", features = ["flowy_test"]}
+flowy-backend-api = { path = "../rust-lib/flowy-backend-api"}
+flowy-sdk = { path = "../rust-lib/flowy-sdk", features = ["http_server"] }
 flowy-user = { path = "../rust-lib/flowy-user", features = ["http_server"] }
-flowy-workspace = { path = "../rust-lib/flowy-workspace", default-features = false, features = ["http_server"] }
+flowy-document = { path = "../rust-lib/flowy-document", features = ["flowy_test", "http_server"] }
+
 flowy-ws = { path = "../rust-lib/flowy-ws" }
-flowy-sdk = { path = "../rust-lib/flowy-sdk", features = ["http_server"] }
 flowy-test = { path = "../rust-lib/flowy-test" }
 flowy-infra = { path = "../rust-lib/flowy-infra" }
 flowy-ot = { path = "../rust-lib/flowy-ot" }
-flowy-document = { path = "../rust-lib/flowy-document", features = ["flowy_test", "http_server"] }
 flowy-sqlite = { path = "../rust-lib/flowy-sqlite" }
 futures-util = "0.3.15"

+ 1 - 0
backend/src/application.rs

@@ -105,6 +105,7 @@ fn user_scope() -> Scope {
         .service(web::resource("/app")
             .route(web::post().to(app::create_handler))
             .route(web::get().to(app::read_handler))
+            .route(web::delete().to(app::delete_handler))
             .route(web::patch().to(app::update_handler))
         )
         .service(web::resource("/view")

+ 18 - 18
backend/src/service/app/router.rs

@@ -9,7 +9,7 @@ use sqlx::PgPool;
 
 use crate::service::{
     app::{
-        app::{create_app, read_app, update_app},
+        app::{create_app, delete_app, read_app, update_app},
         sql_builder::check_app_id,
     },
     user::LoggedUser,
@@ -92,20 +92,20 @@ pub async fn update_handler(payload: Payload, pool: Data<PgPool>) -> Result<Http
     Ok(FlowyResponse::success().into())
 }
 
-// pub async fn delete_handler(payload: Payload, pool: Data<PgPool>) ->
-// Result<HttpResponse, ServerError> {     let params: DeleteAppParams =
-// parse_from_payload(payload).await?;     let app_id =
-// check_app_id(params.app_id.to_owned())?;     let mut transaction = pool
-//         .begin()
-//         .await
-//         .context("Failed to acquire a Postgres connection to delete app")?;
-//
-//     let _ = delete_app(&mut transaction, app_id).await?;
-//
-//     transaction
-//         .commit()
-//         .await
-//         .context("Failed to commit SQL transaction to delete app.")?;
-//
-//     Ok(FlowyResponse::success().into())
-// }
+pub async fn delete_handler(payload: Payload, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
+    let params: AppIdentifier = parse_from_payload(payload).await?;
+    let app_id = check_app_id(params.app_id.to_owned())?;
+    let mut transaction = pool
+        .begin()
+        .await
+        .context("Failed to acquire a Postgres connection to delete app")?;
+
+    let _ = delete_app(&mut transaction, app_id).await?;
+
+    transaction
+        .commit()
+        .await
+        .context("Failed to commit SQL transaction to delete app.")?;
+
+    Ok(FlowyResponse::success().into())
+}

+ 14 - 25
backend/src/sqlx_ext/query.rs

@@ -61,6 +61,7 @@ impl SqlBuilder {
         self
     }
 
+    #[allow(dead_code)]
     pub fn add_arg_if<'a, T>(self, add: bool, field: &str, arg: T) -> Self
     where
         T: 'a + Send + Encode<'a, Postgres> + Type<Postgres>,
@@ -123,12 +124,9 @@ impl SqlBuilder {
                     inner.field(field);
                 });
 
-                self.filters
-                    .into_iter()
-                    .enumerate()
-                    .for_each(|(index, filter)| {
-                        inner.and_where_eq(filter, format!("${}", index + 1));
-                    });
+                self.filters.into_iter().enumerate().for_each(|(index, filter)| {
+                    inner.and_where_eq(filter, format!("${}", index + 1));
+                });
 
                 let sql = inner.sql()?;
                 Ok((sql, self.fields_args))
@@ -136,32 +134,23 @@ impl SqlBuilder {
             BuilderType::Update => {
                 let mut inner = InnerBuilder::update_table(&self.table);
                 let field_len = self.fields.len();
-                self.fields
-                    .into_iter()
-                    .enumerate()
-                    .for_each(|(index, field)| {
-                        inner.set(&field, format!("${}", index + 1));
-                    });
+                self.fields.into_iter().enumerate().for_each(|(index, field)| {
+                    inner.set(&field, format!("${}", index + 1));
+                });
 
-                self.filters
-                    .into_iter()
-                    .enumerate()
-                    .for_each(|(index, filter)| {
-                        let index = index + field_len;
-                        inner.and_where_eq(filter, format!("${}", index + 1));
-                    });
+                self.filters.into_iter().enumerate().for_each(|(index, filter)| {
+                    let index = index + field_len;
+                    inner.and_where_eq(filter, format!("${}", index + 1));
+                });
 
                 let sql = inner.sql()?;
                 Ok((sql, self.fields_args))
             },
             BuilderType::Delete => {
                 let mut inner = InnerBuilder::delete_from(&self.table);
-                self.filters
-                    .into_iter()
-                    .enumerate()
-                    .for_each(|(index, filter)| {
-                        inner.and_where_eq(filter, format!("${}", index + 1));
-                    });
+                self.filters.into_iter().enumerate().for_each(|(index, filter)| {
+                    inner.and_where_eq(filter, format!("${}", index + 1));
+                });
                 let sql = inner.sql()?;
                 Ok((sql, self.fields_args))
             },

+ 3 - 2
backend/tests/api/auth.rs

@@ -1,5 +1,6 @@
 use crate::helper::{spawn_user_server, TestUserServer};
-use flowy_user::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams};
+use flowy_net::errors::ErrorCode;
+use flowy_user_infra::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams};
 
 #[actix_rt::test]
 async fn user_register() {
@@ -77,7 +78,7 @@ async fn user_update_password() {
     match server.sign_in(sign_in_params).await {
         Ok(_) => {},
         Err(e) => {
-            assert_eq!(e.code, flowy_user::errors::ErrorCode::PasswordNotMatch.value());
+            assert_eq!(e.code, ErrorCode::PasswordNotMatch);
         },
     }
 }

+ 1 - 1
backend/tests/api/doc.rs

@@ -1,6 +1,6 @@
 use crate::helper::ViewTest;
 use flowy_document::entities::doc::DocIdentifier;
-use flowy_workspace::entities::view::ViewIdentifiers;
+use flowy_workspace_infra::entities::view::ViewIdentifiers;
 
 #[actix_rt::test]
 async fn doc_read() {

+ 1 - 1
backend/tests/api/workspace.rs

@@ -1,5 +1,5 @@
 use crate::helper::*;
-use flowy_workspace::entities::{
+use flowy_workspace_infra::entities::{
     app::{AppIdentifier, UpdateAppParams},
     trash::{TrashIdentifier, TrashIdentifiers, TrashType},
     view::{UpdateViewParams, ViewIdentifier},

+ 10 - 6
backend/tests/helper.rs

@@ -5,12 +5,14 @@ use backend::{
 };
 
 use backend::application::init_app_context;
+use flowy_backend_api::{user_request::*, workspace_request::*};
 use flowy_document::{
     entities::doc::{Doc, DocIdentifier},
     prelude::*,
 };
-use flowy_user::{errors::UserError, prelude::*};
-use flowy_workspace::prelude::{server::*, *};
+use flowy_net::errors::ServerError;
+use flowy_user_infra::entities::*;
+use flowy_workspace_infra::entities::prelude::*;
 use sqlx::{Connection, Executor, PgConnection, PgPool};
 use uuid::Uuid;
 
@@ -31,9 +33,10 @@ impl TestUserServer {
         server
     }
 
-    pub async fn sign_in(&self, params: SignInParams) -> Result<SignInResponse, UserError> {
+    pub async fn sign_in(&self, params: SignInParams) -> Result<SignInResponse, ServerError> {
         let url = format!("{}/api/auth", self.http_addr());
-        user_sign_in_request(params, &url).await
+        let resp = user_sign_in_request(params, &url).await?;
+        Ok(resp)
     }
 
     pub async fn sign_out(&self) {
@@ -51,9 +54,10 @@ impl TestUserServer {
         user_profile
     }
 
-    pub async fn update_user_profile(&self, params: UpdateUserParams) -> Result<(), UserError> {
+    pub async fn update_user_profile(&self, params: UpdateUserParams) -> Result<(), ServerError> {
         let url = format!("{}/api/user", self.http_addr());
-        update_user_profile_request(self.user_token(), params, &url).await
+        let _ = update_user_profile_request(self.user_token(), params, &url).await?;
+        Ok(())
     }
 
     pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace {

+ 1 - 0
rust-lib/Cargo.toml

@@ -19,6 +19,7 @@ members = [
   "flowy-ot",
   "flowy-net",
   "flowy-ws",
+  "flowy-backend-api",
 ]
 
 exclude = ["../backend"]

+ 4 - 4
rust-lib/dart-ffi/Cargo.toml

@@ -7,11 +7,11 @@ edition = "2018"
 [lib]
 name = "dart_ffi"
 # this value will change depending on the target os
-# for iOS it would be `cdylib`
-# for Macos it would be `cdylib`
+# for iOS it would be `rlib`
+# for Macos it would be `rlib`
 # for android it would be `c-dylib`
-# default cdylib
-crate-type = ["cdylib"]
+# default rlib
+crate-type = ["rlib"]
 
 
 [dependencies]

+ 14 - 0
rust-lib/flowy-backend-api/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "flowy-backend-api"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+flowy-net = { path = "../flowy-net", features = ["flowy_request"] }
+flowy-workspace-infra = { path = "../flowy-workspace-infra" }
+flowy-user-infra = { path = "../flowy-user-infra" }
+log = "0.4.14"
+lazy_static = "1.4.0"
+tokio = { version = "1", features = ["rt"] }

+ 3 - 0
rust-lib/flowy-backend-api/src/lib.rs

@@ -0,0 +1,3 @@
+pub mod middleware;
+pub mod user_request;
+pub mod workspace_request;

+ 39 - 0
rust-lib/flowy-backend-api/src/middleware.rs

@@ -0,0 +1,39 @@
+use flowy_net::{request::ResponseMiddleware, response::FlowyResponse};
+use lazy_static::lazy_static;
+use std::sync::Arc;
+use tokio::sync::broadcast;
+lazy_static! {
+    pub static ref BACKEND_API_MIDDLEWARE: Arc<WorkspaceMiddleware> = Arc::new(WorkspaceMiddleware::new());
+}
+
+pub struct WorkspaceMiddleware {
+    invalid_token_sender: broadcast::Sender<String>,
+}
+
+impl WorkspaceMiddleware {
+    fn new() -> Self {
+        let (sender, _) = broadcast::channel(10);
+        WorkspaceMiddleware {
+            invalid_token_sender: sender,
+        }
+    }
+
+    pub fn invalid_token_subscribe(&self) -> broadcast::Receiver<String> { self.invalid_token_sender.subscribe() }
+}
+
+impl ResponseMiddleware for WorkspaceMiddleware {
+    fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
+        if let Some(error) = &response.error {
+            if error.is_unauthorized() {
+                log::error!("user is unauthorized");
+                match token {
+                    None => {},
+                    Some(token) => match self.invalid_token_sender.send(token.clone()) {
+                        Ok(_) => {},
+                        Err(e) => log::error!("{:?}", e),
+                    },
+                }
+            }
+        }
+    }
+}

+ 52 - 0
rust-lib/flowy-backend-api/src/user_request.rs

@@ -0,0 +1,52 @@
+use flowy_net::{config::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
+use flowy_user_infra::entities::prelude::*;
+
+pub(crate) fn request_builder() -> HttpRequestBuilder {
+    HttpRequestBuilder::new().middleware(super::middleware::BACKEND_API_MIDDLEWARE.clone())
+}
+
+pub async fn user_sign_up_request(params: SignUpParams, url: &str) -> Result<SignUpResponse, ServerError> {
+    let response = request_builder()
+        .post(&url.to_owned())
+        .protobuf(params)?
+        .response()
+        .await?;
+    Ok(response)
+}
+
+pub async fn user_sign_in_request(params: SignInParams, url: &str) -> Result<SignInResponse, ServerError> {
+    let response = request_builder()
+        .post(&url.to_owned())
+        .protobuf(params)?
+        .response()
+        .await?;
+    Ok(response)
+}
+
+pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .delete(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .send()
+        .await?;
+    Ok(())
+}
+
+pub async fn get_user_profile_request(token: &str, url: &str) -> Result<UserProfile, ServerError> {
+    let user_profile = request_builder()
+        .get(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .response()
+        .await?;
+    Ok(user_profile)
+}
+
+pub async fn update_user_profile_request(token: &str, params: UpdateUserParams, url: &str) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .patch(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}

+ 176 - 0
rust-lib/flowy-backend-api/src/workspace_request.rs

@@ -0,0 +1,176 @@
+use flowy_net::{config::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
+use flowy_workspace_infra::entities::prelude::*;
+
+pub(crate) fn request_builder() -> HttpRequestBuilder {
+    HttpRequestBuilder::new().middleware(super::middleware::BACKEND_API_MIDDLEWARE.clone())
+}
+
+pub async fn create_workspace_request(
+    token: &str,
+    params: CreateWorkspaceParams,
+    url: &str,
+) -> Result<Workspace, ServerError> {
+    let workspace = request_builder()
+        .post(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .response()
+        .await?;
+    Ok(workspace)
+}
+
+pub async fn read_workspaces_request(
+    token: &str,
+    params: QueryWorkspaceParams,
+    url: &str,
+) -> Result<RepeatedWorkspace, ServerError> {
+    let repeated_workspace = request_builder()
+        .get(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .response::<RepeatedWorkspace>()
+        .await?;
+
+    Ok(repeated_workspace)
+}
+
+pub async fn update_workspace_request(
+    token: &str,
+    params: UpdateWorkspaceParams,
+    url: &str,
+) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .patch(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}
+
+pub async fn delete_workspace_request(
+    token: &str,
+    params: DeleteWorkspaceParams,
+    url: &str,
+) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .delete(url)
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}
+
+// App
+pub async fn create_app_request(token: &str, params: CreateAppParams, url: &str) -> Result<App, ServerError> {
+    let app = request_builder()
+        .post(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .response()
+        .await?;
+    Ok(app)
+}
+
+pub async fn read_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<Option<App>, ServerError> {
+    let app = request_builder()
+        .get(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .option_response()
+        .await?;
+
+    Ok(app)
+}
+
+pub async fn update_app_request(token: &str, params: UpdateAppParams, url: &str) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .patch(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}
+
+pub async fn delete_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .delete(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}
+
+// View
+pub async fn create_view_request(token: &str, params: CreateViewParams, url: &str) -> Result<View, ServerError> {
+    let view = request_builder()
+        .post(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .response()
+        .await?;
+    Ok(view)
+}
+
+pub async fn read_view_request(token: &str, params: ViewIdentifier, url: &str) -> Result<Option<View>, ServerError> {
+    let view = request_builder()
+        .get(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .option_response()
+        .await?;
+
+    Ok(view)
+}
+
+pub async fn update_view_request(token: &str, params: UpdateViewParams, url: &str) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .patch(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}
+
+pub async fn delete_view_request(token: &str, params: ViewIdentifiers, url: &str) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .delete(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}
+
+pub async fn create_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .post(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}
+
+pub async fn delete_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), ServerError> {
+    let _ = request_builder()
+        .delete(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .protobuf(params)?
+        .send()
+        .await?;
+    Ok(())
+}
+
+pub async fn read_trash_request(token: &str, url: &str) -> Result<RepeatedTrash, ServerError> {
+    let repeated_trash = request_builder()
+        .get(&url.to_owned())
+        .header(HEADER_TOKEN, token)
+        .response::<RepeatedTrash>()
+        .await?;
+    Ok(repeated_trash)
+}

+ 1 - 0
rust-lib/flowy-log/src/lib.rs

@@ -17,6 +17,7 @@ lazy_static! {
 }
 
 pub struct Builder {
+    #[allow(dead_code)]
     name: String,
     env_filter: String,
     file_appender: RollingFileAppender,

+ 1 - 1
rust-lib/flowy-sdk/Cargo.toml

@@ -10,7 +10,7 @@ flowy-dispatch = { path = "../flowy-dispatch"}
 flowy-log = { path = "../flowy-log" }
 flowy-user = { path = "../flowy-user" }
 flowy-infra = { path = "../flowy-infra" }
-flowy-workspace = { path = "../flowy-workspace" }
+flowy-workspace = { path = "../flowy-workspace", default-features = false }
 flowy-database = { path = "../flowy-database" }
 flowy-document = { path = "../flowy-document" }
 flowy-ws = { path = "../flowy-ws" }

+ 1 - 1
rust-lib/flowy-test/Cargo.toml

@@ -9,7 +9,7 @@ edition = "2018"
 flowy-sdk = { path = "../flowy-sdk"}
 flowy-dispatch = { path = "../flowy-dispatch"}
 flowy-user = { path = "../flowy-user"}
-flowy-workspace = { path = "../flowy-workspace"}
+flowy-workspace = { path = "../flowy-workspace", default-features = false}
 flowy-infra = { path = "../flowy-infra"}
 flowy-document = { path = "../flowy-document"}
 flowy-net = { path = "../flowy-net"}

+ 4 - 0
rust-lib/flowy-user-infra/src/entities/mod.rs

@@ -3,3 +3,7 @@ pub use user_profile::*;
 
 pub mod auth;
 mod user_profile;
+
+pub mod prelude {
+    pub use crate::entities::{auth::*, user_profile::*};
+}

+ 1 - 0
rust-lib/flowy-user/Cargo.toml

@@ -7,6 +7,7 @@ edition = "2018"
 
 [dependencies]
 flowy-user-infra = { path = "../flowy-user-infra" }
+flowy-backend-api = { path = "../flowy-backend-api" }
 derive_more = {version = "0.99", features = ["display"]}
 flowy-dispatch = { path = "../flowy-dispatch" }
 flowy-derive = { path = "../flowy-derive" }

+ 46 - 87
rust-lib/flowy-user/src/services/server/server_api.rs

@@ -1,14 +1,11 @@
 use crate::{
-    entities::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UserProfile},
+    entities::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile},
     errors::UserError,
+    services::server::UserServerAPI,
 };
-
-use crate::{entities::UpdateUserParams, services::server::UserServerAPI};
+use flowy_backend_api::user_request::*;
 use flowy_infra::future::ResultFuture;
-use flowy_net::{
-    config::*,
-    request::{HttpRequestBuilder, ResponseMiddleware},
-};
+use flowy_net::config::*;
 
 pub struct UserServer {
     config: ServerConfig,
@@ -20,12 +17,18 @@ impl UserServer {
 impl UserServerAPI for UserServer {
     fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError> {
         let url = self.config.sign_up_url();
-        ResultFuture::new(async move { user_sign_up_request(params, &url).await })
+        ResultFuture::new(async move {
+            let resp = user_sign_up_request(params, &url).await?;
+            Ok(resp)
+        })
     }
 
     fn sign_in(&self, params: SignInParams) -> ResultFuture<SignInResponse, UserError> {
         let url = self.config.sign_in_url();
-        ResultFuture::new(async move { user_sign_in_request(params, &url).await })
+        ResultFuture::new(async move {
+            let resp = user_sign_in_request(params, &url).await?;
+            Ok(resp)
+        })
     }
 
     fn sign_out(&self, token: &str) -> ResultFuture<(), UserError> {
@@ -40,91 +43,47 @@ impl UserServerAPI for UserServer {
     fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError> {
         let token = token.to_owned();
         let url = self.config.user_profile_url();
-        ResultFuture::new(async move { update_user_profile_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = update_user_profile_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn get_user(&self, token: &str) -> ResultFuture<UserProfile, UserError> {
         let token = token.to_owned();
         let url = self.config.user_profile_url();
-        ResultFuture::new(async move { get_user_profile_request(&token, &url).await })
+        ResultFuture::new(async move {
+            let profile = get_user_profile_request(&token, &url).await?;
+            Ok(profile)
+        })
     }
 
     fn ws_addr(&self) -> String { self.config.ws_addr() }
 }
 
-use crate::notify::*;
-use flowy_net::response::FlowyResponse;
-use flowy_user_infra::errors::ErrorCode;
-use lazy_static::lazy_static;
-use std::sync::Arc;
-lazy_static! {
-    static ref MIDDLEWARE: Arc<Middleware> = Arc::new(Middleware {});
-}
-
-struct Middleware {}
-impl ResponseMiddleware for Middleware {
-    fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
-        if let Some(error) = &response.error {
-            if error.is_unauthorized() {
-                log::error!("user unauthorized");
-                match token {
-                    None => {},
-                    Some(token) => {
-                        let error = UserError::new(ErrorCode::UserUnauthorized, "");
-                        dart_notify(token, UserNotification::UserUnauthorized)
-                            .error(error)
-                            .send()
-                    },
-                }
-            }
-        }
-    }
-}
-
-pub(crate) fn request_builder() -> HttpRequestBuilder { HttpRequestBuilder::new().middleware(MIDDLEWARE.clone()) }
-
-pub async fn user_sign_up_request(params: SignUpParams, url: &str) -> Result<SignUpResponse, UserError> {
-    let response = request_builder()
-        .post(&url.to_owned())
-        .protobuf(params)?
-        .response()
-        .await?;
-    Ok(response)
-}
-
-pub async fn user_sign_in_request(params: SignInParams, url: &str) -> Result<SignInResponse, UserError> {
-    let response = request_builder()
-        .post(&url.to_owned())
-        .protobuf(params)?
-        .response()
-        .await?;
-    Ok(response)
-}
-
-pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), UserError> {
-    let _ = request_builder()
-        .delete(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .send()
-        .await?;
-    Ok(())
-}
-
-pub async fn get_user_profile_request(token: &str, url: &str) -> Result<UserProfile, UserError> {
-    let user_profile = request_builder()
-        .get(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .response()
-        .await?;
-    Ok(user_profile)
-}
-
-pub async fn update_user_profile_request(token: &str, params: UpdateUserParams, url: &str) -> Result<(), UserError> {
-    let _ = request_builder()
-        .patch(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
+// use crate::notify::*;
+// use flowy_net::response::FlowyResponse;
+// use flowy_user_infra::errors::ErrorCode;
+
+// struct Middleware {}
+//
+//
+//
+// impl ResponseMiddleware for Middleware {
+//     fn receive_response(&self, token: &Option<String>, response:
+// &FlowyResponse) {         if let Some(error) = &response.error {
+//             if error.is_unauthorized() {
+//                 log::error!("user unauthorized");
+//                 match token {
+//                     None => {},
+//                     Some(token) => {
+//                         let error =
+// UserError::new(ErrorCode::UserUnauthorized, "");
+// dart_notify(token, UserNotification::UserUnauthorized)
+// .error(error)                             .send()
+//                     },
+//                 }
+//             }
+//         }
+//     }
+// }

+ 7 - 1
rust-lib/flowy-workspace-infra/Cargo.toml

@@ -14,4 +14,10 @@ strum = "0.21"
 strum_macros = "0.21"
 derive_more = {version = "0.99", features = ["display"]}
 log = "0.4.14"
-flowy-document = { path = "../flowy-document" }
+flowy-document = { path = "../flowy-document" }
+
+
+[features]
+default = []
+backend = []
+frontend = []

+ 4 - 0
rust-lib/flowy-workspace-infra/src/entities/mod.rs

@@ -2,3 +2,7 @@ pub mod app;
 pub mod trash;
 pub mod view;
 pub mod workspace;
+
+pub mod prelude {
+    pub use crate::entities::{app::*, trash::*, view::*, workspace::*};
+}

+ 3 - 0
rust-lib/flowy-workspace-infra/src/lib.rs

@@ -1,6 +1,9 @@
 pub mod entities;
 pub mod errors;
 pub mod parser;
+
 #[macro_use]
 mod macros;
+
+// #[cfg(feature = "backend")]
 pub mod protobuf;

+ 2 - 1
rust-lib/flowy-workspace/Cargo.toml

@@ -16,6 +16,7 @@ flowy-dart-notify = { path = "../flowy-dart-notify" }
 flowy-document = { path = "../flowy-document" }
 flowy-ot = { path = "../flowy-ot" }
 flowy-net = { path = "../flowy-net", features = ["flowy_request"] }
+flowy-backend-api = { path = "../flowy-backend-api"}
 
 parking_lot = "0.11"
 protobuf = {version = "2.18.0"}
@@ -44,6 +45,6 @@ crossbeam-utils = "0.8"
 flowy-test = { path = "../flowy-test" }
 serial_test = "0.5.1"
 
-
 [features]
+default = []
 http_server = []

+ 0 - 35
rust-lib/flowy-workspace/src/services/server/middleware.rs

@@ -1,35 +0,0 @@
-use std::sync::Arc;
-
-use lazy_static::lazy_static;
-
-use flowy_net::{request::ResponseMiddleware, response::FlowyResponse};
-
-use crate::{
-    errors::{ErrorCode, WorkspaceError},
-    notify::*,
-};
-
-lazy_static! {
-    pub(crate) static ref MIDDLEWARE: Arc<WorkspaceMiddleware> = Arc::new(WorkspaceMiddleware {});
-}
-
-pub(crate) struct WorkspaceMiddleware {}
-impl ResponseMiddleware for WorkspaceMiddleware {
-    fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
-        if let Some(error) = &response.error {
-            if error.is_unauthorized() {
-                log::error!("workspace user is unauthorized");
-
-                match token {
-                    None => {},
-                    Some(token) => {
-                        let error = WorkspaceError::new(ErrorCode::UserUnauthorized, "");
-                        send_dart_notification(token, WorkspaceNotification::UserUnauthorized)
-                            .error(error)
-                            .send()
-                    },
-                }
-            }
-        }
-    }
-}

+ 2 - 1
rust-lib/flowy-workspace/src/services/server/mod.rs

@@ -1,4 +1,3 @@
-mod middleware;
 mod server_api;
 mod server_api_mock;
 
@@ -29,6 +28,8 @@ use std::sync::Arc;
 pub(crate) type Server = Arc<dyn WorkspaceServerAPI + Send + Sync>;
 
 pub trait WorkspaceServerAPI {
+    fn init(&self);
+
     // Workspace
     fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError>;
 

+ 81 - 189
rust-lib/flowy-workspace/src/services/server/server_api.rs

@@ -13,10 +13,13 @@ use crate::{
         },
     },
     errors::WorkspaceError,
+    notify::{send_dart_notification, WorkspaceNotification},
     services::server::WorkspaceServerAPI,
 };
+use flowy_backend_api::{middleware::*, workspace_request::*};
 use flowy_infra::future::ResultFuture;
-use flowy_net::{config::*, request::HttpRequestBuilder};
+use flowy_net::config::ServerConfig;
+use flowy_workspace_infra::errors::ErrorCode;
 
 pub struct WorkspaceServer {
     config: ServerConfig,
@@ -27,10 +30,30 @@ impl WorkspaceServer {
 }
 
 impl WorkspaceServerAPI for WorkspaceServer {
+    fn init(&self) {
+        let mut rx = BACKEND_API_MIDDLEWARE.invalid_token_subscribe();
+        tokio::spawn(async move {
+            loop {
+                match rx.recv().await {
+                    Ok(invalid_token) => {
+                        let error = WorkspaceError::new(ErrorCode::UserUnauthorized, "");
+                        send_dart_notification(&invalid_token, WorkspaceNotification::UserUnauthorized)
+                            .error(error)
+                            .send()
+                    },
+                    Err(_) => {},
+                }
+            }
+        });
+    }
+
     fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.workspace_url();
-        ResultFuture::new(async move { create_workspace_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let workspace = create_workspace_request(&token, params, &url).await?;
+            Ok(workspace)
+        })
     }
 
     fn read_workspace(
@@ -40,257 +63,126 @@ impl WorkspaceServerAPI for WorkspaceServer {
     ) -> ResultFuture<RepeatedWorkspace, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.workspace_url();
-        ResultFuture::new(async move { read_workspaces_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let repeated_workspace = read_workspaces_request(&token, params, &url).await?;
+            Ok(repeated_workspace)
+        })
     }
 
     fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.workspace_url();
-        ResultFuture::new(async move { update_workspace_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = update_workspace_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn delete_workspace(&self, token: &str, params: DeleteWorkspaceParams) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.workspace_url();
-        ResultFuture::new(async move { delete_workspace_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = delete_workspace_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn create_view(&self, token: &str, params: CreateViewParams) -> ResultFuture<View, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
-        ResultFuture::new(async move { create_view_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let view = create_view_request(&token, params, &url).await?;
+            Ok(view)
+        })
     }
 
     fn read_view(&self, token: &str, params: ViewIdentifier) -> ResultFuture<Option<View>, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
-        ResultFuture::new(async move { read_view_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let view = read_view_request(&token, params, &url).await?;
+            Ok(view)
+        })
     }
 
     fn delete_view(&self, token: &str, params: ViewIdentifiers) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
-        ResultFuture::new(async move { delete_view_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = delete_view_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn update_view(&self, token: &str, params: UpdateViewParams) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
-        ResultFuture::new(async move { update_view_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = update_view_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn create_app(&self, token: &str, params: CreateAppParams) -> ResultFuture<App, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.app_url();
-        ResultFuture::new(async move { create_app_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let app = create_app_request(&token, params, &url).await?;
+            Ok(app)
+        })
     }
 
     fn read_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<Option<App>, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.app_url();
-        ResultFuture::new(async move { read_app_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let app = read_app_request(&token, params, &url).await?;
+            Ok(app)
+        })
     }
 
     fn update_app(&self, token: &str, params: UpdateAppParams) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.app_url();
-        ResultFuture::new(async move { update_app_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = update_app_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn delete_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.app_url();
-        ResultFuture::new(async move { delete_app_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = delete_app_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn create_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.trash_url();
-        ResultFuture::new(async move { create_trash_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = create_trash_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn delete_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.trash_url();
-        ResultFuture::new(async move { delete_trash_request(&token, params, &url).await })
+        ResultFuture::new(async move {
+            let _ = delete_trash_request(&token, params, &url).await?;
+            Ok(())
+        })
     }
 
     fn read_trash(&self, token: &str) -> ResultFuture<RepeatedTrash, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.trash_url();
-        ResultFuture::new(async move { read_trash_request(&token, &url).await })
+        ResultFuture::new(async move {
+            let repeated_trash = read_trash_request(&token, &url).await?;
+            Ok(repeated_trash)
+        })
     }
 }
-
-pub(crate) fn request_builder() -> HttpRequestBuilder {
-    HttpRequestBuilder::new().middleware(super::middleware::MIDDLEWARE.clone())
-}
-pub async fn create_workspace_request(
-    token: &str,
-    params: CreateWorkspaceParams,
-    url: &str,
-) -> Result<Workspace, WorkspaceError> {
-    let workspace = request_builder()
-        .post(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .response()
-        .await?;
-    Ok(workspace)
-}
-
-pub async fn read_workspaces_request(
-    token: &str,
-    params: QueryWorkspaceParams,
-    url: &str,
-) -> Result<RepeatedWorkspace, WorkspaceError> {
-    let repeated_workspace = request_builder()
-        .get(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .response::<RepeatedWorkspace>()
-        .await?;
-
-    Ok(repeated_workspace)
-}
-
-pub async fn update_workspace_request(
-    token: &str,
-    params: UpdateWorkspaceParams,
-    url: &str,
-) -> Result<(), WorkspaceError> {
-    let _ = request_builder()
-        .patch(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
-
-pub async fn delete_workspace_request(
-    token: &str,
-    params: DeleteWorkspaceParams,
-    url: &str,
-) -> Result<(), WorkspaceError> {
-    let _ = request_builder()
-        .delete(url)
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
-
-// App
-pub async fn create_app_request(token: &str, params: CreateAppParams, url: &str) -> Result<App, WorkspaceError> {
-    let app = request_builder()
-        .post(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .response()
-        .await?;
-    Ok(app)
-}
-
-pub async fn read_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<Option<App>, WorkspaceError> {
-    let app = request_builder()
-        .get(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .option_response()
-        .await?;
-
-    Ok(app)
-}
-
-pub async fn update_app_request(token: &str, params: UpdateAppParams, url: &str) -> Result<(), WorkspaceError> {
-    let _ = request_builder()
-        .patch(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
-
-pub async fn delete_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<(), WorkspaceError> {
-    let _ = request_builder()
-        .delete(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
-
-// View
-pub async fn create_view_request(token: &str, params: CreateViewParams, url: &str) -> Result<View, WorkspaceError> {
-    let view = request_builder()
-        .post(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .response()
-        .await?;
-    Ok(view)
-}
-
-pub async fn read_view_request(token: &str, params: ViewIdentifier, url: &str) -> Result<Option<View>, WorkspaceError> {
-    let view = request_builder()
-        .get(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .option_response()
-        .await?;
-
-    Ok(view)
-}
-
-pub async fn update_view_request(token: &str, params: UpdateViewParams, url: &str) -> Result<(), WorkspaceError> {
-    let _ = request_builder()
-        .patch(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
-
-pub async fn delete_view_request(token: &str, params: ViewIdentifiers, url: &str) -> Result<(), WorkspaceError> {
-    let _ = request_builder()
-        .delete(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
-
-pub async fn create_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), WorkspaceError> {
-    let _ = request_builder()
-        .post(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
-
-pub async fn delete_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), WorkspaceError> {
-    let _ = request_builder()
-        .delete(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .protobuf(params)?
-        .send()
-        .await?;
-    Ok(())
-}
-
-pub async fn read_trash_request(token: &str, url: &str) -> Result<RepeatedTrash, WorkspaceError> {
-    let repeated_trash = request_builder()
-        .get(&url.to_owned())
-        .header(HEADER_TOKEN, token)
-        .response::<RepeatedTrash>()
-        .await?;
-    Ok(repeated_trash)
-}

+ 2 - 0
rust-lib/flowy-workspace/src/services/server/server_api_mock.rs

@@ -20,6 +20,8 @@ use flowy_infra::{future::ResultFuture, timestamp, uuid};
 pub struct WorkspaceServerMock {}
 
 impl WorkspaceServerAPI for WorkspaceServerMock {
+    fn init(&self) {}
+
     fn create_workspace(&self, _token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> {
         let time = timestamp();
         let workspace = Workspace {

+ 1 - 0
rust-lib/flowy-workspace/src/services/workspace_controller.rs

@@ -44,6 +44,7 @@ impl WorkspaceController {
     }
 
     pub fn init(&self) -> Result<(), WorkspaceError> {
+        let _ = self.server.init();
         let _ = self.trash_can.init()?;
         let _ = self.view_controller.init()?;
         let _ = self.app_controller.init()?;