From 55ea9e6cae025b6624371b3ec79904dbae083978 Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 8 Nov 2021 10:57:33 +0800 Subject: [PATCH] [rust]: create backend api crate --- .../example/lib/overlay/overlay_screen.dart | 6 +- backend/Cargo.toml | 11 +- backend/src/application.rs | 1 + backend/src/service/app/router.rs | 36 +-- backend/src/sqlx_ext/query.rs | 39 +-- backend/tests/api/auth.rs | 5 +- backend/tests/api/doc.rs | 2 +- backend/tests/api/workspace.rs | 2 +- backend/tests/helper.rs | 16 +- rust-lib/Cargo.toml | 1 + rust-lib/dart-ffi/Cargo.toml | 8 +- rust-lib/flowy-backend-api/Cargo.toml | 14 + rust-lib/flowy-backend-api/src/lib.rs | 3 + rust-lib/flowy-backend-api/src/middleware.rs | 39 +++ .../flowy-backend-api/src/user_request.rs | 52 ++++ .../src/workspace_request.rs | 176 ++++++++++++ rust-lib/flowy-log/src/lib.rs | 1 + rust-lib/flowy-sdk/Cargo.toml | 2 +- rust-lib/flowy-test/Cargo.toml | 2 +- rust-lib/flowy-user-infra/src/entities/mod.rs | 4 + rust-lib/flowy-user/Cargo.toml | 1 + .../src/services/server/server_api.rs | 131 +++------ rust-lib/flowy-workspace-infra/Cargo.toml | 8 +- .../flowy-workspace-infra/src/entities/mod.rs | 4 + rust-lib/flowy-workspace-infra/src/lib.rs | 3 + rust-lib/flowy-workspace/Cargo.toml | 3 +- .../src/services/server/middleware.rs | 35 --- .../src/services/server/mod.rs | 3 +- .../src/services/server/server_api.rs | 270 ++++++------------ .../src/services/server/server_api_mock.rs | 2 + .../src/services/workspace_controller.rs | 1 + 31 files changed, 501 insertions(+), 380 deletions(-) create mode 100644 rust-lib/flowy-backend-api/Cargo.toml create mode 100644 rust-lib/flowy-backend-api/src/lib.rs create mode 100644 rust-lib/flowy-backend-api/src/middleware.rs create mode 100644 rust-lib/flowy-backend-api/src/user_request.rs create mode 100644 rust-lib/flowy-backend-api/src/workspace_request.rs delete mode 100644 rust-lib/flowy-workspace/src/services/server/middleware.rs diff --git a/app_flowy/packages/flowy_infra_ui/example/lib/overlay/overlay_screen.dart b/app_flowy/packages/flowy_infra_ui/example/lib/overlay/overlay_screen.dart index 92cea24858..24274999db 100644 --- a/app_flowy/packages/flowy_infra_ui/example/lib/overlay/overlay_screen.dart +++ b/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: ['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().anchorDirection, diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 3e1b03ef11..470313731b 100644 --- a/backend/Cargo.toml +++ b/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-user = { path = "../rust-lib/flowy-user", features = ["http_server"] } -flowy-workspace = { path = "../rust-lib/flowy-workspace", default-features = false, features = ["http_server"] } -flowy-ws = { path = "../rust-lib/flowy-ws" } +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-document = { path = "../rust-lib/flowy-document", features = ["flowy_test", "http_server"] } + +flowy-ws = { path = "../rust-lib/flowy-ws" } 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" \ No newline at end of file diff --git a/backend/src/application.rs b/backend/src/application.rs index 2268b33e1e..0c05058935 100644 --- a/backend/src/application.rs +++ b/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") diff --git a/backend/src/service/app/router.rs b/backend/src/service/app/router.rs index c27d1841c1..a490804edc 100644 --- a/backend/src/service/app/router.rs +++ b/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) -> Result) -> -// Result { 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) -> Result { + 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()) +} diff --git a/backend/src/sqlx_ext/query.rs b/backend/src/sqlx_ext/query.rs index f92b4c005d..a0483aa62c 100644 --- a/backend/src/sqlx_ext/query.rs +++ b/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, @@ -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)) }, diff --git a/backend/tests/api/auth.rs b/backend/tests/api/auth.rs index 2644cde657..bd6103093d 100644 --- a/backend/tests/api/auth.rs +++ b/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); }, } } diff --git a/backend/tests/api/doc.rs b/backend/tests/api/doc.rs index e6667abfc4..380f6bdee8 100644 --- a/backend/tests/api/doc.rs +++ b/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() { diff --git a/backend/tests/api/workspace.rs b/backend/tests/api/workspace.rs index c010f35b00..1275b5f761 100644 --- a/backend/tests/api/workspace.rs +++ b/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}, diff --git a/backend/tests/helper.rs b/backend/tests/helper.rs index 209de43bc1..ac20827d9b 100644 --- a/backend/tests/helper.rs +++ b/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 { + pub async fn sign_in(&self, params: SignInParams) -> Result { 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 { diff --git a/rust-lib/Cargo.toml b/rust-lib/Cargo.toml index 0f70fea6ff..1b32790a68 100644 --- a/rust-lib/Cargo.toml +++ b/rust-lib/Cargo.toml @@ -19,6 +19,7 @@ members = [ "flowy-ot", "flowy-net", "flowy-ws", + "flowy-backend-api", ] exclude = ["../backend"] diff --git a/rust-lib/dart-ffi/Cargo.toml b/rust-lib/dart-ffi/Cargo.toml index 4f21cd6b56..323e66b82b 100644 --- a/rust-lib/dart-ffi/Cargo.toml +++ b/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] diff --git a/rust-lib/flowy-backend-api/Cargo.toml b/rust-lib/flowy-backend-api/Cargo.toml new file mode 100644 index 0000000000..4a5995ea5d --- /dev/null +++ b/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"] } \ No newline at end of file diff --git a/rust-lib/flowy-backend-api/src/lib.rs b/rust-lib/flowy-backend-api/src/lib.rs new file mode 100644 index 0000000000..cdd1c25f28 --- /dev/null +++ b/rust-lib/flowy-backend-api/src/lib.rs @@ -0,0 +1,3 @@ +pub mod middleware; +pub mod user_request; +pub mod workspace_request; diff --git a/rust-lib/flowy-backend-api/src/middleware.rs b/rust-lib/flowy-backend-api/src/middleware.rs new file mode 100644 index 0000000000..a0a6233e7a --- /dev/null +++ b/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 = Arc::new(WorkspaceMiddleware::new()); +} + +pub struct WorkspaceMiddleware { + invalid_token_sender: broadcast::Sender, +} + +impl WorkspaceMiddleware { + fn new() -> Self { + let (sender, _) = broadcast::channel(10); + WorkspaceMiddleware { + invalid_token_sender: sender, + } + } + + pub fn invalid_token_subscribe(&self) -> broadcast::Receiver { self.invalid_token_sender.subscribe() } +} + +impl ResponseMiddleware for WorkspaceMiddleware { + fn receive_response(&self, token: &Option, 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), + }, + } + } + } + } +} diff --git a/rust-lib/flowy-backend-api/src/user_request.rs b/rust-lib/flowy-backend-api/src/user_request.rs new file mode 100644 index 0000000000..da3f0e15fb --- /dev/null +++ b/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 { + 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 { + 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 { + 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(()) +} diff --git a/rust-lib/flowy-backend-api/src/workspace_request.rs b/rust-lib/flowy-backend-api/src/workspace_request.rs new file mode 100644 index 0000000000..7d7bd691f2 --- /dev/null +++ b/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 { + 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 { + let repeated_workspace = request_builder() + .get(&url.to_owned()) + .header(HEADER_TOKEN, token) + .protobuf(params)? + .response::() + .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 { + 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, 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 { + 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, 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 { + let repeated_trash = request_builder() + .get(&url.to_owned()) + .header(HEADER_TOKEN, token) + .response::() + .await?; + Ok(repeated_trash) +} diff --git a/rust-lib/flowy-log/src/lib.rs b/rust-lib/flowy-log/src/lib.rs index 2f4d6e459e..b2ed12fd00 100644 --- a/rust-lib/flowy-log/src/lib.rs +++ b/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, diff --git a/rust-lib/flowy-sdk/Cargo.toml b/rust-lib/flowy-sdk/Cargo.toml index a86a7f2f9d..5607f23867 100644 --- a/rust-lib/flowy-sdk/Cargo.toml +++ b/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" } diff --git a/rust-lib/flowy-test/Cargo.toml b/rust-lib/flowy-test/Cargo.toml index e6e5e3a6f5..4494c5815e 100644 --- a/rust-lib/flowy-test/Cargo.toml +++ b/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"} diff --git a/rust-lib/flowy-user-infra/src/entities/mod.rs b/rust-lib/flowy-user-infra/src/entities/mod.rs index be45c60f07..1dbd28a592 100644 --- a/rust-lib/flowy-user-infra/src/entities/mod.rs +++ b/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::*}; +} diff --git a/rust-lib/flowy-user/Cargo.toml b/rust-lib/flowy-user/Cargo.toml index bf89b05b0b..ca4578aeb3 100644 --- a/rust-lib/flowy-user/Cargo.toml +++ b/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" } diff --git a/rust-lib/flowy-user/src/services/server/server_api.rs b/rust-lib/flowy-user/src/services/server/server_api.rs index ffadd38bec..07c6004e52 100644 --- a/rust-lib/flowy-user/src/services/server/server_api.rs +++ b/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 { 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 { 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 { 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 = Arc::new(Middleware {}); -} +// 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, 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 { - 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 { - 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 { - 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(()) -} +// struct Middleware {} +// +// +// +// impl ResponseMiddleware for Middleware { +// fn receive_response(&self, token: &Option, 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() +// }, +// } +// } +// } +// } +// } diff --git a/rust-lib/flowy-workspace-infra/Cargo.toml b/rust-lib/flowy-workspace-infra/Cargo.toml index 9d98f73d08..b4fa536824 100644 --- a/rust-lib/flowy-workspace-infra/Cargo.toml +++ b/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" } \ No newline at end of file +flowy-document = { path = "../flowy-document" } + + +[features] +default = [] +backend = [] +frontend = [] \ No newline at end of file diff --git a/rust-lib/flowy-workspace-infra/src/entities/mod.rs b/rust-lib/flowy-workspace-infra/src/entities/mod.rs index 3ee0f4b591..ee9410fb1c 100644 --- a/rust-lib/flowy-workspace-infra/src/entities/mod.rs +++ b/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::*}; +} diff --git a/rust-lib/flowy-workspace-infra/src/lib.rs b/rust-lib/flowy-workspace-infra/src/lib.rs index 9c5f32ac8d..74b1c03599 100644 --- a/rust-lib/flowy-workspace-infra/src/lib.rs +++ b/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; diff --git a/rust-lib/flowy-workspace/Cargo.toml b/rust-lib/flowy-workspace/Cargo.toml index 0e6e6637ee..553d4a1027 100644 --- a/rust-lib/flowy-workspace/Cargo.toml +++ b/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 = [] \ No newline at end of file diff --git a/rust-lib/flowy-workspace/src/services/server/middleware.rs b/rust-lib/flowy-workspace/src/services/server/middleware.rs deleted file mode 100644 index e1594dbc81..0000000000 --- a/rust-lib/flowy-workspace/src/services/server/middleware.rs +++ /dev/null @@ -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 = Arc::new(WorkspaceMiddleware {}); -} - -pub(crate) struct WorkspaceMiddleware {} -impl ResponseMiddleware for WorkspaceMiddleware { - fn receive_response(&self, token: &Option, 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() - }, - } - } - } - } -} diff --git a/rust-lib/flowy-workspace/src/services/server/mod.rs b/rust-lib/flowy-workspace/src/services/server/mod.rs index cec3742bbb..f757af80f7 100644 --- a/rust-lib/flowy-workspace/src/services/server/mod.rs +++ b/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; pub trait WorkspaceServerAPI { + fn init(&self); + // Workspace fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture; diff --git a/rust-lib/flowy-workspace/src/services/server/server_api.rs b/rust-lib/flowy-workspace/src/services/server/server_api.rs index 61f23dbcdb..1f64c72672 100644 --- a/rust-lib/flowy-workspace/src/services/server/server_api.rs +++ b/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 { 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 { 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 { 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, 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 { 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, 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 { 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 { - 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 { - let repeated_workspace = request_builder() - .get(&url.to_owned()) - .header(HEADER_TOKEN, token) - .protobuf(params)? - .response::() - .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 { - 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, 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 { - 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, 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 { - let repeated_trash = request_builder() - .get(&url.to_owned()) - .header(HEADER_TOKEN, token) - .response::() - .await?; - Ok(repeated_trash) -} diff --git a/rust-lib/flowy-workspace/src/services/server/server_api_mock.rs b/rust-lib/flowy-workspace/src/services/server/server_api_mock.rs index 98b37c1afa..f562461163 100644 --- a/rust-lib/flowy-workspace/src/services/server/server_api_mock.rs +++ b/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 { let time = timestamp(); let workspace = Workspace { diff --git a/rust-lib/flowy-workspace/src/services/workspace_controller.rs b/rust-lib/flowy-workspace/src/services/workspace_controller.rs index 31894ce5fb..75a134fc49 100644 --- a/rust-lib/flowy-workspace/src/services/workspace_controller.rs +++ b/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()?;