Browse Source

add mock server

appflowy 3 years ago
parent
commit
31086ad4df
51 changed files with 416 additions and 285 deletions
  1. 5 3
      backend/Cargo.lock
  2. 1 0
      backend/Cargo.toml
  3. 42 8
      backend/src/services/doc/manager.rs
  4. 9 26
      backend/src/services/doc/ws_actor.rs
  5. 2 2
      backend/src/services/user/user_default.rs
  6. 0 1
      frontend/rust-lib/Cargo.toml
  7. 2 3
      frontend/rust-lib/flowy-core/Cargo.toml
  8. 11 7
      frontend/rust-lib/flowy-core/src/core/core_context.rs
  9. 16 16
      frontend/rust-lib/flowy-core/src/services/server/mod.rs
  10. 31 31
      frontend/rust-lib/flowy-core/src/services/server/server_api.rs
  11. 31 31
      frontend/rust-lib/flowy-core/src/services/server/server_api_mock.rs
  12. 2 2
      frontend/rust-lib/flowy-document/Cargo.toml
  13. 3 3
      frontend/rust-lib/flowy-document/src/services/doc/controller.rs
  14. 4 4
      frontend/rust-lib/flowy-document/src/services/doc/revision/cache.rs
  15. 2 2
      frontend/rust-lib/flowy-document/src/services/doc/revision/manager.rs
  16. 4 4
      frontend/rust-lib/flowy-document/src/services/server/mod.rs
  17. 7 7
      frontend/rust-lib/flowy-document/src/services/server/server_api.rs
  18. 11 10
      frontend/rust-lib/flowy-document/src/services/server/server_api_mock.rs
  19. 2 1
      frontend/rust-lib/flowy-net/Cargo.toml
  20. 3 2
      frontend/rust-lib/flowy-sdk/Cargo.toml
  21. 1 1
      frontend/rust-lib/flowy-sdk/src/lib.rs
  22. 1 1
      frontend/rust-lib/flowy-test/Cargo.toml
  23. 8 3
      frontend/rust-lib/flowy-user/Cargo.toml
  24. 15 16
      frontend/rust-lib/flowy-user/src/services/server/mod.rs
  25. 11 11
      frontend/rust-lib/flowy-user/src/services/server/server_api.rs
  26. 10 10
      frontend/rust-lib/flowy-user/src/services/server/server_api_mock.rs
  27. 43 0
      frontend/rust-lib/flowy-user/src/services/server/ws_local.rs
  28. 54 38
      frontend/rust-lib/flowy-user/src/services/server/ws_mock.rs
  29. 7 7
      frontend/rust-lib/flowy-user/src/services/user/ws_manager.rs
  30. 1 1
      frontend/scripts/flowy-tool/src/util/file.rs
  31. 2 2
      shared-lib/Cargo.lock
  32. 1 0
      shared-lib/Cargo.toml
  33. 1 0
      shared-lib/backend-service/Cargo.toml
  34. 1 0
      shared-lib/flowy-collaboration/Cargo.toml
  35. 1 0
      shared-lib/flowy-collaboration/src/core/document/default/READ_ME.json
  36. 4 4
      shared-lib/flowy-collaboration/src/core/document/default/mod.rs
  37. 9 7
      shared-lib/flowy-collaboration/src/core/document/document.rs
  38. 6 4
      shared-lib/flowy-collaboration/src/core/document/mod.rs
  39. 15 8
      shared-lib/flowy-collaboration/src/core/sync/server_editor.rs
  40. 0 1
      shared-lib/flowy-collaboration/src/lib.rs
  41. 2 2
      shared-lib/flowy-core-infra/src/entities/view/view_create.rs
  42. 0 2
      shared-lib/lib-infra/Cargo.toml
  43. 34 3
      shared-lib/lib-infra/src/future.rs
  44. 0 0
      shared-lib/lib-infra/src/lib.rs
  45. 0 0
      shared-lib/lib-infra/src/retry/future.rs
  46. 0 0
      shared-lib/lib-infra/src/retry/mod.rs
  47. 0 0
      shared-lib/lib-infra/src/retry/strategy/exponential_backoff.rs
  48. 0 0
      shared-lib/lib-infra/src/retry/strategy/fixed_interval.rs
  49. 0 0
      shared-lib/lib-infra/src/retry/strategy/jitter.rs
  50. 0 0
      shared-lib/lib-infra/src/retry/strategy/mod.rs
  51. 1 1
      shared-lib/lib-ws/Cargo.toml

+ 5 - 3
backend/Cargo.lock

@@ -467,6 +467,7 @@ dependencies = [
  "futures-util",
  "jsonwebtoken",
  "lazy_static",
+ "lib-infra",
  "lib-ot",
  "lib-ws",
  "linkify",
@@ -505,6 +506,7 @@ dependencies = [
  "bytes",
  "config",
  "derive_more",
+ "flowy-collaboration",
  "flowy-core-infra",
  "flowy-user-infra",
  "hyper",
@@ -1205,6 +1207,7 @@ dependencies = [
  "dashmap",
  "flowy-derive",
  "futures",
+ "lib-infra",
  "lib-ot",
  "log",
  "md5",
@@ -1339,6 +1342,7 @@ dependencies = [
 name = "flowy-net"
 version = "0.1.0"
 dependencies = [
+ "bytes",
  "flowy-derive",
  "protobuf",
 ]
@@ -1354,6 +1358,7 @@ dependencies = [
  "flowy-core",
  "flowy-database",
  "flowy-document",
+ "flowy-net",
  "flowy-user",
  "futures-core",
  "lib-dispatch",
@@ -1402,7 +1407,6 @@ dependencies = [
  "derive_more",
  "diesel",
  "diesel_derives",
- "flowy-collaboration",
  "flowy-database",
  "flowy-derive",
  "flowy-net",
@@ -1954,11 +1958,9 @@ version = "0.1.0"
 dependencies = [
  "bytes",
  "chrono",
- "flowy-derive",
  "futures-core",
  "log",
  "pin-project 1.0.8",
- "protobuf",
  "rand",
  "tokio",
  "uuid",

+ 1 - 0
backend/Cargo.toml

@@ -64,6 +64,7 @@ flowy-core-infra = { path = "../shared-lib/flowy-core-infra" }
 flowy-collaboration = { path = "../shared-lib/flowy-collaboration" }
 lib-ws = { path = "../shared-lib/lib-ws" }
 lib-ot = { path = "../shared-lib/lib-ot" }
+lib-infra = { path = "../shared-lib/lib-infra" }
 backend-service = { path = "../shared-lib/backend-service", features = ["http_server"] }
 
 ormx = { version = "0.7", features = ["postgres"]}

+ 42 - 8
backend/src/services/doc/manager.rs

@@ -1,16 +1,23 @@
 use crate::{
-    services::doc::ws_actor::{DocWsActor, DocWsMsg},
+    services::doc::{
+        read_doc,
+        update_doc,
+        ws_actor::{DocWsActor, DocWsMsg},
+    },
     web_socket::{WsBizHandler, WsClientData},
 };
 use actix_web::web::Data;
+
 use flowy_collaboration::{
     core::sync::{ServerDocManager, ServerDocPersistence},
     entities::doc::Doc,
-    errors::CollaborateResult,
+    errors::CollaborateError,
+    protobuf::{DocIdentifier, UpdateDocParams},
 };
+use lib_infra::future::FutureResultSend;
 use lib_ot::rich_text::RichTextDelta;
 use sqlx::PgPool;
-use std::sync::Arc;
+use std::{convert::TryInto, sync::Arc};
 use tokio::sync::{mpsc, oneshot};
 
 pub struct DocumentCore {
@@ -21,7 +28,7 @@ pub struct DocumentCore {
 
 impl DocumentCore {
     pub fn new(pg_pool: Data<PgPool>) -> Self {
-        let manager = Arc::new(ServerDocManager::new(Arc::new(DocPersistenceImpl())));
+        let manager = Arc::new(ServerDocManager::new(Arc::new(DocPersistenceImpl(pg_pool.clone()))));
         let (ws_sender, rx) = mpsc::channel(100);
         let actor = DocWsActor::new(rx, manager.clone());
         tokio::task::spawn(actor.run());
@@ -57,11 +64,38 @@ impl WsBizHandler for DocumentCore {
     }
 }
 
-struct DocPersistenceImpl();
+struct DocPersistenceImpl(Data<PgPool>);
 impl ServerDocPersistence for DocPersistenceImpl {
-    fn create_doc(&self, doc_id: &str, delta: RichTextDelta) -> CollaborateResult<()> { unimplemented!() }
+    fn update_doc(&self, doc_id: &str, rev_id: i64, delta: RichTextDelta) -> FutureResultSend<(), CollaborateError> {
+        let pg_pool = self.0.clone();
+        let mut params = UpdateDocParams::new();
+        let doc_json = delta.to_json();
+        params.set_doc_id(doc_id.to_string());
+        params.set_data(doc_json);
+        params.set_rev_id(rev_id);
 
-    fn update_doc(&self, doc_id: &str, delta: RichTextDelta) -> CollaborateResult<()> { unimplemented!() }
+        FutureResultSend::new(async move {
+            let _ = update_doc(pg_pool.get_ref(), params)
+                .await
+                .map_err(|e| CollaborateError::internal().context(e))?;
+            Ok(())
+        })
+    }
 
-    fn read_doc(&self, doc_id: &str) -> CollaborateResult<Doc> { unimplemented!() }
+    fn read_doc(&self, doc_id: &str) -> FutureResultSend<Doc, CollaborateError> {
+        let params = DocIdentifier {
+            doc_id: doc_id.to_string(),
+            ..Default::default()
+        };
+        let pg_pool = self.0.clone();
+        FutureResultSend::new(async move {
+            let mut pb_doc = read_doc(pg_pool.get_ref(), params)
+                .await
+                .map_err(|e| CollaborateError::internal().context(e))?;
+            let doc = (&mut pb_doc)
+                .try_into()
+                .map_err(|e| CollaborateError::internal().context(e))?;
+            Ok(doc)
+        })
+    }
 }

+ 9 - 26
backend/src/services/doc/ws_actor.rs

@@ -1,6 +1,6 @@
 use crate::{
     services::{
-        doc::{editor::ServerDocUser, read_doc},
+        doc::editor::ServerDocUser,
         util::{md5, parse_from_bytes},
     },
     web_socket::{entities::Socket, WsClientData, WsUser},
@@ -11,7 +11,7 @@ use async_stream::stream;
 use backend_service::errors::{internal_error, Result as DocResult, ServerError};
 use flowy_collaboration::{
     core::sync::{OpenDocHandle, ServerDocManager},
-    protobuf::{DocIdentifier, NewDocUser, WsDataType, WsDocumentData},
+    protobuf::{NewDocUser, WsDataType, WsDocumentData},
 };
 use futures::stream::StreamExt;
 use lib_ot::protobuf::Revision;
@@ -128,30 +128,13 @@ impl DocWsActor {
         Ok(())
     }
 
-    async fn get_doc_handle(&self, doc_id: &str, pg_pool: Data<PgPool>) -> Option<Arc<OpenDocHandle>> {
-        match self.doc_manager.get(doc_id) {
-            Some(edit_doc) => Some(edit_doc),
-            None => {
-                let params = DocIdentifier {
-                    doc_id: doc_id.to_string(),
-                    ..Default::default()
-                };
-
-                let f = || async {
-                    let mut pb_doc = read_doc(pg_pool.get_ref(), params).await?;
-                    let doc = (&mut pb_doc).try_into().map_err(internal_error)?;
-                    self.doc_manager.cache(doc).await.map_err(internal_error)?;
-                    let handler = self.doc_manager.get(doc_id);
-                    Ok::<Option<Arc<OpenDocHandle>>, ServerError>(handler)
-                };
-
-                match f().await {
-                    Ok(handler) => handler,
-                    Err(e) => {
-                        log::error!("{}", e);
-                        None
-                    },
-                }
+    async fn get_doc_handle(&self, doc_id: &str, _pg_pool: Data<PgPool>) -> Option<Arc<OpenDocHandle>> {
+        match self.doc_manager.get(doc_id).await {
+            Ok(Some(edit_doc)) => Some(edit_doc),
+            Ok(None) => None,
+            Err(e) => {
+                log::error!("{}", e);
+                None
             },
         }
     }

+ 2 - 2
backend/src/services/user/user_default.rs

@@ -9,7 +9,7 @@ use crate::{
 use crate::services::view::{create_view_with_args, sql_builder::NewViewSqlBuilder};
 use backend_service::errors::ServerError;
 use chrono::Utc;
-use flowy_collaboration::user_default::doc_initial_string;
+use flowy_collaboration::core::document::default::initial_string;
 use flowy_core_infra::protobuf::Workspace;
 use std::convert::TryInto;
 
@@ -42,7 +42,7 @@ pub async fn create_default_workspace(
 
         for view in views.take_items() {
             let (sql, args, view) = NewViewSqlBuilder::from_view(view)?.build()?;
-            let _ = create_view_with_args(transaction, sql, args, view, doc_initial_string()).await?;
+            let _ = create_view_with_args(transaction, sql, args, view, initial_string()).await?;
         }
     }
     Ok(workspace)

+ 0 - 1
frontend/rust-lib/Cargo.toml

@@ -2,7 +2,6 @@
 members = [
   "lib-dispatch",
   "lib-log",
-  "lib-infra",
   "flowy-net",
   "flowy-sdk",
   "dart-ffi",

+ 2 - 3
frontend/rust-lib/flowy-core/Cargo.toml

@@ -12,14 +12,13 @@ flowy-derive = { path = "../../../shared-lib/flowy-derive" }
 lib-ot = { path = "../../../shared-lib/lib-ot" }
 lib-sqlite = { path = "../../../shared-lib/lib-sqlite" }
 backend-service = { path = "../../../shared-lib/backend-service" }
-
+lib-infra = { path = "../../../shared-lib/lib-infra" }
 
 flowy-document = { path = "../flowy-document" }
 flowy-database = { path = "../flowy-database" }
 flowy-net = { path = "../flowy-net" }
 dart-notify = { path = "../dart-notify" }
 lib-dispatch = { path = "../lib-dispatch" }
-lib-infra = { path = "../lib-infra" }
 
 
 parking_lot = "0.11"
@@ -41,7 +40,7 @@ derive_more = {version = "0.99", features = ["display"]}
 bincode = { version = "1.3"}
 tracing = { version = "0.1", features = ["log"] }
 bytes = { version = "1.0" }
-crossbeam = "0.8.1"
+crossbeam = "0.8"
 crossbeam-utils = "0.8"
 chrono = "0.4"
 

+ 11 - 7
frontend/rust-lib/flowy-core/src/core/core_context.rs

@@ -1,3 +1,13 @@
+use std::{collections::HashMap, sync::Arc};
+
+use chrono::Utc;
+use lazy_static::lazy_static;
+use parking_lot::RwLock;
+
+use flowy_collaboration::{core::document::default::initial_read_me, entities::doc::DocDelta};
+use flowy_core_infra::user_default;
+use flowy_net::entities::NetworkType;
+
 use crate::{
     entities::workspace::RepeatedWorkspace,
     errors::{WorkspaceError, WorkspaceResult},
@@ -5,13 +15,7 @@ use crate::{
     notify::{send_dart_notification, WorkspaceNotification},
     services::{server::Server, AppController, TrashController, ViewController, WorkspaceController},
 };
-use chrono::Utc;
-use flowy_collaboration::{entities::doc::DocDelta, user_default::initial_read_me};
-use flowy_core_infra::user_default;
-use flowy_net::entities::NetworkType;
-use lazy_static::lazy_static;
-use parking_lot::RwLock;
-use std::{collections::HashMap, sync::Arc};
+
 lazy_static! {
     static ref INIT_WORKSPACE: RwLock<HashMap<String, bool>> = RwLock::new(HashMap::new());
 }

+ 16 - 16
frontend/rust-lib/flowy-core/src/services/server/mod.rs

@@ -15,7 +15,7 @@ use crate::{
     errors::WorkspaceError,
 };
 use backend_service::configuration::ClientServerConfiguration;
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 use std::sync::Arc;
 
 pub(crate) type Server = Arc<dyn WorkspaceServerAPI + Send + Sync>;
@@ -24,42 +24,42 @@ pub trait WorkspaceServerAPI {
     fn init(&self);
 
     // Workspace
-    fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError>;
+    fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, WorkspaceError>;
 
     fn read_workspace(
         &self,
         token: &str,
         params: WorkspaceIdentifier,
-    ) -> ResultFuture<RepeatedWorkspace, WorkspaceError>;
+    ) -> FutureResult<RepeatedWorkspace, WorkspaceError>;
 
-    fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> ResultFuture<(), WorkspaceError>;
+    fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> FutureResult<(), WorkspaceError>;
 
-    fn delete_workspace(&self, token: &str, params: WorkspaceIdentifier) -> ResultFuture<(), WorkspaceError>;
+    fn delete_workspace(&self, token: &str, params: WorkspaceIdentifier) -> FutureResult<(), WorkspaceError>;
 
     // View
-    fn create_view(&self, token: &str, params: CreateViewParams) -> ResultFuture<View, WorkspaceError>;
+    fn create_view(&self, token: &str, params: CreateViewParams) -> FutureResult<View, WorkspaceError>;
 
-    fn read_view(&self, token: &str, params: ViewIdentifier) -> ResultFuture<Option<View>, WorkspaceError>;
+    fn read_view(&self, token: &str, params: ViewIdentifier) -> FutureResult<Option<View>, WorkspaceError>;
 
-    fn delete_view(&self, token: &str, params: ViewIdentifiers) -> ResultFuture<(), WorkspaceError>;
+    fn delete_view(&self, token: &str, params: ViewIdentifiers) -> FutureResult<(), WorkspaceError>;
 
-    fn update_view(&self, token: &str, params: UpdateViewParams) -> ResultFuture<(), WorkspaceError>;
+    fn update_view(&self, token: &str, params: UpdateViewParams) -> FutureResult<(), WorkspaceError>;
 
     // App
-    fn create_app(&self, token: &str, params: CreateAppParams) -> ResultFuture<App, WorkspaceError>;
+    fn create_app(&self, token: &str, params: CreateAppParams) -> FutureResult<App, WorkspaceError>;
 
-    fn read_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<Option<App>, WorkspaceError>;
+    fn read_app(&self, token: &str, params: AppIdentifier) -> FutureResult<Option<App>, WorkspaceError>;
 
-    fn update_app(&self, token: &str, params: UpdateAppParams) -> ResultFuture<(), WorkspaceError>;
+    fn update_app(&self, token: &str, params: UpdateAppParams) -> FutureResult<(), WorkspaceError>;
 
-    fn delete_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<(), WorkspaceError>;
+    fn delete_app(&self, token: &str, params: AppIdentifier) -> FutureResult<(), WorkspaceError>;
 
     // Trash
-    fn create_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError>;
+    fn create_trash(&self, token: &str, params: TrashIdentifiers) -> FutureResult<(), WorkspaceError>;
 
-    fn delete_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError>;
+    fn delete_trash(&self, token: &str, params: TrashIdentifiers) -> FutureResult<(), WorkspaceError>;
 
-    fn read_trash(&self, token: &str) -> ResultFuture<RepeatedTrash, WorkspaceError>;
+    fn read_trash(&self, token: &str) -> FutureResult<RepeatedTrash, WorkspaceError>;
 }
 
 pub(crate) fn construct_workspace_server(

+ 31 - 31
frontend/rust-lib/flowy-core/src/services/server/server_api.rs

@@ -11,7 +11,7 @@ use crate::{
 };
 use backend_service::{configuration::ClientServerConfiguration, middleware::*, workspace_request::*};
 use flowy_core_infra::errors::ErrorCode;
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 
 pub struct WorkspaceHttpServer {
     config: ClientServerConfiguration,
@@ -34,10 +34,10 @@ impl WorkspaceServerAPI for WorkspaceHttpServer {
         });
     }
 
-    fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> {
+    fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.workspace_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let workspace = create_workspace_request(&token, params, &url).await?;
             Ok(workspace)
         })
@@ -47,127 +47,127 @@ impl WorkspaceServerAPI for WorkspaceHttpServer {
         &self,
         token: &str,
         params: WorkspaceIdentifier,
-    ) -> ResultFuture<RepeatedWorkspace, WorkspaceError> {
+    ) -> FutureResult<RepeatedWorkspace, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.workspace_url();
-        ResultFuture::new(async move {
+        FutureResult::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> {
+    fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> FutureResult<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.workspace_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = update_workspace_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn delete_workspace(&self, token: &str, params: WorkspaceIdentifier) -> ResultFuture<(), WorkspaceError> {
+    fn delete_workspace(&self, token: &str, params: WorkspaceIdentifier) -> FutureResult<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.workspace_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = delete_workspace_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn create_view(&self, token: &str, params: CreateViewParams) -> ResultFuture<View, WorkspaceError> {
+    fn create_view(&self, token: &str, params: CreateViewParams) -> FutureResult<View, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
-        ResultFuture::new(async move {
+        FutureResult::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> {
+    fn read_view(&self, token: &str, params: ViewIdentifier) -> FutureResult<Option<View>, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let view = read_view_request(&token, params, &url).await?;
             Ok(view)
         })
     }
 
-    fn delete_view(&self, token: &str, params: ViewIdentifiers) -> ResultFuture<(), WorkspaceError> {
+    fn delete_view(&self, token: &str, params: ViewIdentifiers) -> FutureResult<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = delete_view_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn update_view(&self, token: &str, params: UpdateViewParams) -> ResultFuture<(), WorkspaceError> {
+    fn update_view(&self, token: &str, params: UpdateViewParams) -> FutureResult<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.view_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = update_view_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn create_app(&self, token: &str, params: CreateAppParams) -> ResultFuture<App, WorkspaceError> {
+    fn create_app(&self, token: &str, params: CreateAppParams) -> FutureResult<App, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.app_url();
-        ResultFuture::new(async move {
+        FutureResult::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> {
+    fn read_app(&self, token: &str, params: AppIdentifier) -> FutureResult<Option<App>, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.app_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let app = read_app_request(&token, params, &url).await?;
             Ok(app)
         })
     }
 
-    fn update_app(&self, token: &str, params: UpdateAppParams) -> ResultFuture<(), WorkspaceError> {
+    fn update_app(&self, token: &str, params: UpdateAppParams) -> FutureResult<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.app_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = update_app_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn delete_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<(), WorkspaceError> {
+    fn delete_app(&self, token: &str, params: AppIdentifier) -> FutureResult<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.app_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = delete_app_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn create_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError> {
+    fn create_trash(&self, token: &str, params: TrashIdentifiers) -> FutureResult<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.trash_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = create_trash_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn delete_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError> {
+    fn delete_trash(&self, token: &str, params: TrashIdentifiers) -> FutureResult<(), WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.trash_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = delete_trash_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn read_trash(&self, token: &str) -> ResultFuture<RepeatedTrash, WorkspaceError> {
+    fn read_trash(&self, token: &str) -> FutureResult<RepeatedTrash, WorkspaceError> {
         let token = token.to_owned();
         let url = self.config.trash_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let repeated_trash = read_trash_request(&token, &url).await?;
             Ok(repeated_trash)
         })

+ 31 - 31
frontend/rust-lib/flowy-core/src/services/server/server_api_mock.rs

@@ -8,14 +8,14 @@ use crate::{
     errors::WorkspaceError,
     services::server::WorkspaceServerAPI,
 };
-use lib_infra::{future::ResultFuture, timestamp, uuid};
+use lib_infra::{future::FutureResult, timestamp, uuid};
 
 pub struct WorkspaceServerMock {}
 
 impl WorkspaceServerAPI for WorkspaceServerMock {
     fn init(&self) {}
 
-    fn create_workspace(&self, _token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> {
+    fn create_workspace(&self, _token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, WorkspaceError> {
         let time = timestamp();
         let workspace = Workspace {
             id: uuid(),
@@ -26,29 +26,29 @@ impl WorkspaceServerAPI for WorkspaceServerMock {
             create_time: time,
         };
 
-        ResultFuture::new(async { Ok(workspace) })
+        FutureResult::new(async { Ok(workspace) })
     }
 
     fn read_workspace(
         &self,
         _token: &str,
         _params: WorkspaceIdentifier,
-    ) -> ResultFuture<RepeatedWorkspace, WorkspaceError> {
-        ResultFuture::new(async {
+    ) -> FutureResult<RepeatedWorkspace, WorkspaceError> {
+        FutureResult::new(async {
             let repeated_workspace = RepeatedWorkspace { items: vec![] };
             Ok(repeated_workspace)
         })
     }
 
-    fn update_workspace(&self, _token: &str, _params: UpdateWorkspaceParams) -> ResultFuture<(), WorkspaceError> {
-        ResultFuture::new(async { Ok(()) })
+    fn update_workspace(&self, _token: &str, _params: UpdateWorkspaceParams) -> FutureResult<(), WorkspaceError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn delete_workspace(&self, _token: &str, _params: WorkspaceIdentifier) -> ResultFuture<(), WorkspaceError> {
-        ResultFuture::new(async { Ok(()) })
+    fn delete_workspace(&self, _token: &str, _params: WorkspaceIdentifier) -> FutureResult<(), WorkspaceError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn create_view(&self, _token: &str, params: CreateViewParams) -> ResultFuture<View, WorkspaceError> {
+    fn create_view(&self, _token: &str, params: CreateViewParams) -> FutureResult<View, WorkspaceError> {
         let time = timestamp();
         let view = View {
             id: uuid(),
@@ -61,22 +61,22 @@ impl WorkspaceServerAPI for WorkspaceServerMock {
             modified_time: time,
             create_time: time,
         };
-        ResultFuture::new(async { Ok(view) })
+        FutureResult::new(async { Ok(view) })
     }
 
-    fn read_view(&self, _token: &str, _params: ViewIdentifier) -> ResultFuture<Option<View>, WorkspaceError> {
-        ResultFuture::new(async { Ok(None) })
+    fn read_view(&self, _token: &str, _params: ViewIdentifier) -> FutureResult<Option<View>, WorkspaceError> {
+        FutureResult::new(async { Ok(None) })
     }
 
-    fn delete_view(&self, _token: &str, _params: ViewIdentifiers) -> ResultFuture<(), WorkspaceError> {
-        ResultFuture::new(async { Ok(()) })
+    fn delete_view(&self, _token: &str, _params: ViewIdentifiers) -> FutureResult<(), WorkspaceError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn update_view(&self, _token: &str, _params: UpdateViewParams) -> ResultFuture<(), WorkspaceError> {
-        ResultFuture::new(async { Ok(()) })
+    fn update_view(&self, _token: &str, _params: UpdateViewParams) -> FutureResult<(), WorkspaceError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn create_app(&self, _token: &str, params: CreateAppParams) -> ResultFuture<App, WorkspaceError> {
+    fn create_app(&self, _token: &str, params: CreateAppParams) -> FutureResult<App, WorkspaceError> {
         let time = timestamp();
         let app = App {
             id: uuid(),
@@ -88,31 +88,31 @@ impl WorkspaceServerAPI for WorkspaceServerMock {
             modified_time: time,
             create_time: time,
         };
-        ResultFuture::new(async { Ok(app) })
+        FutureResult::new(async { Ok(app) })
     }
 
-    fn read_app(&self, _token: &str, _params: AppIdentifier) -> ResultFuture<Option<App>, WorkspaceError> {
-        ResultFuture::new(async { Ok(None) })
+    fn read_app(&self, _token: &str, _params: AppIdentifier) -> FutureResult<Option<App>, WorkspaceError> {
+        FutureResult::new(async { Ok(None) })
     }
 
-    fn update_app(&self, _token: &str, _params: UpdateAppParams) -> ResultFuture<(), WorkspaceError> {
-        ResultFuture::new(async { Ok(()) })
+    fn update_app(&self, _token: &str, _params: UpdateAppParams) -> FutureResult<(), WorkspaceError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn delete_app(&self, _token: &str, _params: AppIdentifier) -> ResultFuture<(), WorkspaceError> {
-        ResultFuture::new(async { Ok(()) })
+    fn delete_app(&self, _token: &str, _params: AppIdentifier) -> FutureResult<(), WorkspaceError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn create_trash(&self, _token: &str, _params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError> {
-        ResultFuture::new(async { Ok(()) })
+    fn create_trash(&self, _token: &str, _params: TrashIdentifiers) -> FutureResult<(), WorkspaceError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn delete_trash(&self, _token: &str, _params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError> {
-        ResultFuture::new(async { Ok(()) })
+    fn delete_trash(&self, _token: &str, _params: TrashIdentifiers) -> FutureResult<(), WorkspaceError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn read_trash(&self, _token: &str) -> ResultFuture<RepeatedTrash, WorkspaceError> {
-        ResultFuture::new(async {
+    fn read_trash(&self, _token: &str) -> FutureResult<RepeatedTrash, WorkspaceError> {
+        FutureResult::new(async {
             let repeated_trash = RepeatedTrash { items: vec![] };
             Ok(repeated_trash)
         })

+ 2 - 2
frontend/rust-lib/flowy-document/Cargo.toml

@@ -12,10 +12,10 @@ flowy-derive = { path = "../../../shared-lib/flowy-derive" }
 lib-ot = { path = "../../../shared-lib/lib-ot" }
 lib-ws = { path = "../../../shared-lib/lib-ws" }
 backend-service = { path = "../../../shared-lib/backend-service" }
+lib-infra = { path = "../../../shared-lib/lib-infra" }
 
 derive_more = {version = "0.99", features = ["display"]}
 lib-dispatch = { path = "../lib-dispatch" }
-lib-infra = { path = "../lib-infra" }
 flowy-database = { path = "../flowy-database" }
 dart-notify = { path = "../dart-notify" }
 
@@ -52,7 +52,7 @@ color-eyre = { version = "0.5", default-features = false }
 criterion = "0.3"
 rand = "0.7.3"
 env_logger = "0.8.2"
-
+flowy-user = { path = "../flowy-user", features = ["ws_mock"] }
 
 [features]
 http_server = []

+ 3 - 3
frontend/rust-lib/flowy-document/src/services/doc/controller.rs

@@ -14,7 +14,7 @@ use bytes::Bytes;
 use dashmap::DashMap;
 use flowy_collaboration::entities::doc::{Doc, DocDelta, DocIdentifier};
 use flowy_database::ConnectionPool;
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 use std::sync::Arc;
 
 pub(crate) struct DocController {
@@ -128,14 +128,14 @@ struct RevisionServerImpl {
 
 impl RevisionServer for RevisionServerImpl {
     #[tracing::instrument(level = "debug", skip(self))]
-    fn fetch_document(&self, doc_id: &str) -> ResultFuture<Doc, DocError> {
+    fn fetch_document(&self, doc_id: &str) -> FutureResult<Doc, DocError> {
         let params = DocIdentifier {
             doc_id: doc_id.to_string(),
         };
         let server = self.server.clone();
         let token = self.token.clone();
 
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             match server.read_doc(&token, params).await? {
                 None => Err(DocError::doc_not_found().context("Remote doesn't have this document")),
                 Some(doc) => Ok(doc),

+ 4 - 4
frontend/rust-lib/flowy-document/src/services/doc/revision/cache.rs

@@ -5,7 +5,7 @@ use crate::{
 };
 use flowy_collaboration::entities::doc::Doc;
 use flowy_database::ConnectionPool;
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 use lib_ot::{
     core::{Operation, OperationTransformable},
     revision::{RevState, RevType, Revision, RevisionDiskCache, RevisionMemoryCache, RevisionRange, RevisionRecord},
@@ -18,7 +18,7 @@ use tokio::{
 };
 
 pub trait RevisionIterator: Send + Sync {
-    fn next(&self) -> ResultFuture<Option<RevisionRecord>, DocError>;
+    fn next(&self) -> FutureResult<Option<RevisionRecord>, DocError>;
 }
 
 type DocRevisionDeskCache = dyn RevisionDiskCache<Error = DocError>;
@@ -136,11 +136,11 @@ impl RevisionCache {
 }
 
 impl RevisionIterator for RevisionCache {
-    fn next(&self) -> ResultFuture<Option<RevisionRecord>, DocError> {
+    fn next(&self) -> FutureResult<Option<RevisionRecord>, DocError> {
         let memory_cache = self.memory_cache.clone();
         let disk_cache = self.dish_cache.clone();
         let doc_id = self.doc_id.clone();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             match memory_cache.front_revision().await {
                 None => {
                     //

+ 2 - 2
frontend/rust-lib/flowy-document/src/services/doc/revision/manager.rs

@@ -6,7 +6,7 @@ use crate::{
     },
 };
 use flowy_collaboration::{entities::doc::Doc, util::RevIdCounter};
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 use lib_ot::{
     core::OperationTransformable,
     revision::{RevId, RevType, Revision, RevisionRange},
@@ -15,7 +15,7 @@ use lib_ot::{
 use std::sync::Arc;
 
 pub trait RevisionServer: Send + Sync {
-    fn fetch_document(&self, doc_id: &str) -> ResultFuture<Doc, DocError>;
+    fn fetch_document(&self, doc_id: &str) -> FutureResult<Doc, DocError>;
 }
 
 pub struct RevisionManager {

+ 4 - 4
frontend/rust-lib/flowy-document/src/services/server/mod.rs

@@ -7,17 +7,17 @@ pub use server_api::*;
 use crate::errors::DocError;
 use backend_service::configuration::ClientServerConfiguration;
 use flowy_collaboration::entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams};
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 pub use server_api_mock::*;
 use std::sync::Arc;
 
 pub(crate) type Server = Arc<dyn DocumentServerAPI + Send + Sync>;
 pub trait DocumentServerAPI {
-    fn create_doc(&self, token: &str, params: CreateDocParams) -> ResultFuture<(), DocError>;
+    fn create_doc(&self, token: &str, params: CreateDocParams) -> FutureResult<(), DocError>;
 
-    fn read_doc(&self, token: &str, params: DocIdentifier) -> ResultFuture<Option<Doc>, DocError>;
+    fn read_doc(&self, token: &str, params: DocIdentifier) -> FutureResult<Option<Doc>, DocError>;
 
-    fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError>;
+    fn update_doc(&self, token: &str, params: UpdateDocParams) -> FutureResult<(), DocError>;
 }
 
 pub(crate) fn construct_doc_server(

+ 7 - 7
frontend/rust-lib/flowy-document/src/services/server/server_api.rs

@@ -1,7 +1,7 @@
 use crate::{errors::DocError, services::server::DocumentServerAPI};
 use backend_service::{configuration::*, request::HttpRequestBuilder};
 use flowy_collaboration::entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams};
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 
 pub struct DocServer {
     config: ClientServerConfiguration,
@@ -12,22 +12,22 @@ impl DocServer {
 }
 
 impl DocumentServerAPI for DocServer {
-    fn create_doc(&self, token: &str, params: CreateDocParams) -> ResultFuture<(), DocError> {
+    fn create_doc(&self, token: &str, params: CreateDocParams) -> FutureResult<(), DocError> {
         let token = token.to_owned();
         let url = self.config.doc_url();
-        ResultFuture::new(async move { create_doc_request(&token, params, &url).await })
+        FutureResult::new(async move { create_doc_request(&token, params, &url).await })
     }
 
-    fn read_doc(&self, token: &str, params: DocIdentifier) -> ResultFuture<Option<Doc>, DocError> {
+    fn read_doc(&self, token: &str, params: DocIdentifier) -> FutureResult<Option<Doc>, DocError> {
         let token = token.to_owned();
         let url = self.config.doc_url();
-        ResultFuture::new(async move { read_doc_request(&token, params, &url).await })
+        FutureResult::new(async move { read_doc_request(&token, params, &url).await })
     }
 
-    fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError> {
+    fn update_doc(&self, token: &str, params: UpdateDocParams) -> FutureResult<(), DocError> {
         let token = token.to_owned();
         let url = self.config.doc_url();
-        ResultFuture::new(async move { update_doc_request(&token, params, &url).await })
+        FutureResult::new(async move { update_doc_request(&token, params, &url).await })
     }
 }
 

+ 11 - 10
frontend/rust-lib/flowy-document/src/services/server/server_api_mock.rs

@@ -1,28 +1,29 @@
-use crate::{errors::DocError, services::server::DocumentServerAPI};
 use flowy_collaboration::{
+    core::document::default::initial_string,
     entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams},
-    user_default::doc_initial_string,
 };
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
+
+use crate::{errors::DocError, services::server::DocumentServerAPI};
 
 pub struct DocServerMock {}
 
 impl DocumentServerAPI for DocServerMock {
-    fn create_doc(&self, _token: &str, _params: CreateDocParams) -> ResultFuture<(), DocError> {
-        ResultFuture::new(async { Ok(()) })
+    fn create_doc(&self, _token: &str, _params: CreateDocParams) -> FutureResult<(), DocError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn read_doc(&self, _token: &str, params: DocIdentifier) -> ResultFuture<Option<Doc>, DocError> {
+    fn read_doc(&self, _token: &str, params: DocIdentifier) -> FutureResult<Option<Doc>, DocError> {
         let doc = Doc {
             id: params.doc_id,
-            data: doc_initial_string(),
+            data: initial_string(),
             rev_id: 0,
             base_rev_id: 0,
         };
-        ResultFuture::new(async { Ok(Some(doc)) })
+        FutureResult::new(async { Ok(Some(doc)) })
     }
 
-    fn update_doc(&self, _token: &str, _params: UpdateDocParams) -> ResultFuture<(), DocError> {
-        ResultFuture::new(async { Ok(()) })
+    fn update_doc(&self, _token: &str, _params: UpdateDocParams) -> FutureResult<(), DocError> {
+        FutureResult::new(async { Ok(()) })
     }
 }

+ 2 - 1
frontend/rust-lib/flowy-net/Cargo.toml

@@ -7,4 +7,5 @@ edition = "2018"
 
 [dependencies]
 flowy-derive = { path = "../../../shared-lib/flowy-derive" }
-protobuf = {version = "2.18.0"}
+protobuf = {version = "2.18.0"}
+bytes = { version = "1.0" }

+ 3 - 2
frontend/rust-lib/flowy-sdk/Cargo.toml

@@ -9,10 +9,11 @@ edition = "2018"
 lib-dispatch = { path = "../lib-dispatch" }
 lib-log = { path = "../lib-log" }
 flowy-user = { path = "../flowy-user" }
+flowy-net = { path = "../flowy-net" }
 flowy-core = { path = "../flowy-core", default-features = false }
 flowy-database = { path = "../flowy-database" }
 flowy-document = { path = "../flowy-document" }
-lib-infra = { path = "../lib-infra" }
+
 tracing = { version = "0.1" }
 log = "0.4.14"
 futures-core = { version = "0.3", default-features = false }
@@ -25,7 +26,7 @@ parking_lot = "0.11"
 flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" }
 lib-ws = { path = "../../../shared-lib/lib-ws" }
 backend-service = { path = "../../../shared-lib/backend-service" }
-
+lib-infra = { path = "../../../shared-lib/lib-infra" }
 
 [dev-dependencies]
 serde = { version = "1.0", features = ["derive"] }

+ 1 - 1
frontend/rust-lib/flowy-sdk/src/lib.rs

@@ -5,12 +5,12 @@ use crate::deps_resolve::WorkspaceDepsResolver;
 use backend_service::configuration::ClientServerConfiguration;
 use flowy_core::{errors::WorkspaceError, module::init_core, prelude::CoreContext};
 use flowy_document::module::FlowyDocument;
+use flowy_net::entities::NetworkType;
 use flowy_user::{
     prelude::UserStatus,
     services::user::{UserSession, UserSessionConfig},
 };
 use lib_dispatch::prelude::*;
-use lib_infra::entities::network_state::NetworkType;
 use module::mk_modules;
 pub use module::*;
 use std::sync::{

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

@@ -11,11 +11,11 @@ flowy-user = { path = "../flowy-user"}
 flowy-core = { path = "../flowy-core", default-features = false}
 flowy-document = { path = "../flowy-document"}
 lib-dispatch = { path = "../lib-dispatch" }
-lib-infra = { path = "../lib-infra" }
 
 flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" }
 backend-service = { path = "../../../shared-lib/backend-service" }
 lib-ot = { path = "../../../shared-lib/lib-ot" }
+lib-infra = { path = "../../../shared-lib/lib-infra" }
 
 serde = { version = "1.0", features = ["derive"] }
 serde_json = {version = "1.0"}

+ 8 - 3
frontend/rust-lib/flowy-user/Cargo.toml

@@ -8,17 +8,21 @@ edition = "2018"
 [dependencies]
 flowy-user-infra = { path = "../../../shared-lib/flowy-user-infra" }
 backend-service = { path = "../../../shared-lib/backend-service" }
-flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" }
 flowy-derive = { path = "../../../shared-lib/flowy-derive" }
 lib-ws = { path = "../../../shared-lib/lib-ws" }
 lib-sqlite = { path = "../../../shared-lib/lib-sqlite" }
+lib-infra = { path = "../../../shared-lib/lib-infra" }
+
+
+flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration", optional = true}
+lib-ot = { path = "../../../shared-lib/lib-ot", optional = true }
 
 derive_more = {version = "0.99", features = ["display"]}
 flowy-database = { path = "../flowy-database" }
 flowy-net = { path = "../flowy-net" }
 dart-notify = { path = "../dart-notify" }
 lib-dispatch = { path = "../lib-dispatch" }
-lib-infra = { path = "../lib-infra" }
+
 
 tracing = { version = "0.1", features = ["log"] }
 bytes = "1.0"
@@ -47,4 +51,5 @@ futures = "0.3.15"
 serial_test = "0.5.1"
 
 [features]
-http_server = []
+http_server = []
+ws_mock = ["flowy-collaboration", "lib-ot"]

+ 15 - 16
frontend/rust-lib/flowy-user/src/services/server/mod.rs

@@ -1,8 +1,6 @@
 mod server_api;
 mod server_api_mock;
-
-// #[cfg(feature = "http_server")]
-pub(crate) mod ws_mock;
+mod ws_local;
 
 pub use server_api::*;
 pub use server_api_mock::*;
@@ -15,14 +13,14 @@ use crate::{
     services::user::ws_manager::FlowyWebSocket,
 };
 use backend_service::configuration::ClientServerConfiguration;
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 
 pub trait UserServerAPI {
-    fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError>;
-    fn sign_in(&self, params: SignInParams) -> ResultFuture<SignInResponse, UserError>;
-    fn sign_out(&self, token: &str) -> ResultFuture<(), UserError>;
-    fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError>;
-    fn get_user(&self, token: &str) -> ResultFuture<UserProfile, UserError>;
+    fn sign_up(&self, params: SignUpParams) -> FutureResult<SignUpResponse, UserError>;
+    fn sign_in(&self, params: SignInParams) -> FutureResult<SignInResponse, UserError>;
+    fn sign_out(&self, token: &str) -> FutureResult<(), UserError>;
+    fn update_user(&self, token: &str, params: UpdateUserParams) -> FutureResult<(), UserError>;
+    fn get_user(&self, token: &str) -> FutureResult<UserProfile, UserError>;
     fn ws_addr(&self) -> String;
 }
 
@@ -34,10 +32,11 @@ pub(crate) fn construct_user_server(config: &ClientServerConfiguration) -> Arc<d
     }
 }
 
-pub(crate) fn local_web_socket() -> Arc<dyn FlowyWebSocket> {
-    if cfg!(debug_assertions) {
-        Arc::new(Arc::new(ws_mock::MockWebSocket::default()))
-    } else {
-        Arc::new(Arc::new(ws_mock::LocalWebSocket::default()))
-    }
-}
+#[cfg(feature = "ws_mock")]
+mod ws_mock;
+
+#[cfg(not(feature = "ws_mock"))]
+pub(crate) fn local_web_socket() -> Arc<dyn FlowyWebSocket> { Arc::new(Arc::new(ws_local::LocalWebSocket::default())) }
+
+#[cfg(feature = "ws_mock")]
+pub(crate) fn local_web_socket() -> Arc<dyn FlowyWebSocket> { Arc::new(Arc::new(ws_mock::MockWebSocket::default())) }

+ 11 - 11
frontend/rust-lib/flowy-user/src/services/server/server_api.rs

@@ -4,7 +4,7 @@ use crate::{
     services::server::UserServerAPI,
 };
 use backend_service::{configuration::*, user_request::*};
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 
 pub struct UserHttpServer {
     config: ClientServerConfiguration,
@@ -14,44 +14,44 @@ impl UserHttpServer {
 }
 
 impl UserServerAPI for UserHttpServer {
-    fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError> {
+    fn sign_up(&self, params: SignUpParams) -> FutureResult<SignUpResponse, UserError> {
         let url = self.config.sign_up_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let resp = user_sign_up_request(params, &url).await?;
             Ok(resp)
         })
     }
 
-    fn sign_in(&self, params: SignInParams) -> ResultFuture<SignInResponse, UserError> {
+    fn sign_in(&self, params: SignInParams) -> FutureResult<SignInResponse, UserError> {
         let url = self.config.sign_in_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let resp = user_sign_in_request(params, &url).await?;
             Ok(resp)
         })
     }
 
-    fn sign_out(&self, token: &str) -> ResultFuture<(), UserError> {
+    fn sign_out(&self, token: &str) -> FutureResult<(), UserError> {
         let token = token.to_owned();
         let url = self.config.sign_out_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = user_sign_out_request(&token, &url).await;
             Ok(())
         })
     }
 
-    fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError> {
+    fn update_user(&self, token: &str, params: UpdateUserParams) -> FutureResult<(), UserError> {
         let token = token.to_owned();
         let url = self.config.user_profile_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = update_user_profile_request(&token, params, &url).await?;
             Ok(())
         })
     }
 
-    fn get_user(&self, token: &str) -> ResultFuture<UserProfile, UserError> {
+    fn get_user(&self, token: &str) -> FutureResult<UserProfile, UserError> {
         let token = token.to_owned();
         let url = self.config.user_profile_url();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let profile = get_user_profile_request(&token, &url).await?;
             Ok(profile)
         })

+ 10 - 10
frontend/rust-lib/flowy-user/src/services/server/server_api_mock.rs

@@ -4,16 +4,16 @@ use crate::{
 };
 
 use crate::services::server::UserServerAPI;
-use lib_infra::{future::ResultFuture, uuid};
+use lib_infra::{future::FutureResult, uuid};
 
 pub struct UserServerMock {}
 
 impl UserServerMock {}
 
 impl UserServerAPI for UserServerMock {
-    fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError> {
+    fn sign_up(&self, params: SignUpParams) -> FutureResult<SignUpResponse, UserError> {
         let uid = uuid();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             Ok(SignUpResponse {
                 user_id: uid.clone(),
                 name: params.name,
@@ -23,9 +23,9 @@ impl UserServerAPI for UserServerMock {
         })
     }
 
-    fn sign_in(&self, params: SignInParams) -> ResultFuture<SignInResponse, UserError> {
+    fn sign_in(&self, params: SignInParams) -> FutureResult<SignInResponse, UserError> {
         let user_id = uuid();
-        ResultFuture::new(async {
+        FutureResult::new(async {
             Ok(SignInResponse {
                 user_id: user_id.clone(),
                 name: params.name,
@@ -35,14 +35,14 @@ impl UserServerAPI for UserServerMock {
         })
     }
 
-    fn sign_out(&self, _token: &str) -> ResultFuture<(), UserError> { ResultFuture::new(async { Ok(()) }) }
+    fn sign_out(&self, _token: &str) -> FutureResult<(), UserError> { FutureResult::new(async { Ok(()) }) }
 
-    fn update_user(&self, _token: &str, _params: UpdateUserParams) -> ResultFuture<(), UserError> {
-        ResultFuture::new(async { Ok(()) })
+    fn update_user(&self, _token: &str, _params: UpdateUserParams) -> FutureResult<(), UserError> {
+        FutureResult::new(async { Ok(()) })
     }
 
-    fn get_user(&self, _token: &str) -> ResultFuture<UserProfile, UserError> {
-        ResultFuture::new(async { Ok(UserProfile::default()) })
+    fn get_user(&self, _token: &str) -> FutureResult<UserProfile, UserError> {
+        FutureResult::new(async { Ok(UserProfile::default()) })
     }
 
     fn ws_addr(&self) -> String { "ws://localhost:8000/ws/".to_owned() }

+ 43 - 0
frontend/rust-lib/flowy-user/src/services/server/ws_local.rs

@@ -0,0 +1,43 @@
+use crate::{
+    errors::UserError,
+    services::user::ws_manager::{FlowyWebSocket, FlowyWsSender},
+};
+use lib_infra::future::FutureResult;
+use lib_ws::{WsConnectState, WsMessage, WsMessageHandler};
+use std::sync::Arc;
+use tokio::sync::{broadcast, broadcast::Receiver};
+
+pub(crate) struct LocalWebSocket {
+    state_sender: broadcast::Sender<WsConnectState>,
+    ws_sender: broadcast::Sender<WsMessage>,
+}
+
+impl std::default::Default for LocalWebSocket {
+    fn default() -> Self {
+        let (state_sender, _) = broadcast::channel(16);
+        let (ws_sender, _) = broadcast::channel(16);
+        LocalWebSocket {
+            state_sender,
+            ws_sender,
+        }
+    }
+}
+
+impl FlowyWebSocket for Arc<LocalWebSocket> {
+    fn start_connect(&self, _addr: String) -> FutureResult<(), UserError> { FutureResult::new(async { Ok(()) }) }
+
+    fn conn_state_subscribe(&self) -> Receiver<WsConnectState> { self.state_sender.subscribe() }
+
+    fn reconnect(&self, _count: usize) -> FutureResult<(), UserError> { FutureResult::new(async { Ok(()) }) }
+
+    fn add_handler(&self, _handler: Arc<dyn WsMessageHandler>) -> Result<(), UserError> { Ok(()) }
+
+    fn ws_sender(&self) -> Result<Arc<dyn FlowyWsSender>, UserError> { Ok(Arc::new(self.ws_sender.clone())) }
+}
+
+impl FlowyWsSender for broadcast::Sender<WsMessage> {
+    fn send(&self, msg: WsMessage) -> Result<(), UserError> {
+        let _ = self.send(msg);
+        Ok(())
+    }
+}

+ 54 - 38
frontend/rust-lib/flowy-user/src/services/server/ws_mock.rs

@@ -4,9 +4,19 @@ use crate::{
 };
 use bytes::Bytes;
 use dashmap::DashMap;
-use flowy_collaboration::entities::ws::{WsDataType, WsDocumentData};
-use lib_infra::future::ResultFuture;
+use flowy_collaboration::{
+    core::sync::{ServerDocManager, ServerDocPersistence},
+    entities::{
+        doc::{Doc, NewDocUser},
+        ws::{WsDataType, WsDocumentData},
+    },
+    errors::CollaborateError,
+};
+use lazy_static::lazy_static;
+use lib_infra::future::{FutureResult, FutureResultSend};
+use lib_ot::{revision::Revision, rich_text::RichTextDelta};
 use lib_ws::{WsConnectState, WsMessage, WsMessageHandler, WsModule};
+use parking_lot::RwLock;
 use std::{convert::TryFrom, sync::Arc};
 use tokio::sync::{broadcast, broadcast::Receiver};
 
@@ -33,33 +43,28 @@ impl MockWebSocket {
 }
 
 impl FlowyWebSocket for Arc<MockWebSocket> {
-    fn start_connect(&self, _addr: String) -> ResultFuture<(), UserError> {
+    fn start_connect(&self, _addr: String) -> FutureResult<(), UserError> {
         let mut ws_receiver = self.ws_sender.subscribe();
         let cloned_ws = self.clone();
         tokio::spawn(async move {
             while let Ok(message) = ws_receiver.recv().await {
                 let ws_data = WsDocumentData::try_from(Bytes::from(message.data.clone())).unwrap();
-                match ws_data.ty {
-                    WsDataType::Acked => {},
-                    WsDataType::PushRev => {},
-                    WsDataType::PullRev => {},
-                    WsDataType::Conflict => {},
-                    WsDataType::NewDocUser => {},
-                }
-
-                match cloned_ws.handlers.get(&message.module) {
-                    None => log::error!("Can't find any handler for message: {:?}", message),
-                    Some(handler) => handler.receive_message(message.clone()),
+                match DOC_SERVER.handle_ws_data(ws_data).await {
+                    None => {},
+                    Some(new_ws_message) => match cloned_ws.handlers.get(&new_ws_message.module) {
+                        None => log::error!("Can't find any handler for message: {:?}", new_ws_message),
+                        Some(handler) => handler.receive_message(new_ws_message.clone()),
+                    },
                 }
             }
         });
 
-        ResultFuture::new(async { Ok(()) })
+        FutureResult::new(async { Ok(()) })
     }
 
     fn conn_state_subscribe(&self) -> Receiver<WsConnectState> { self.state_sender.subscribe() }
 
-    fn reconnect(&self, _count: usize) -> ResultFuture<(), UserError> { ResultFuture::new(async { Ok(()) }) }
+    fn reconnect(&self, _count: usize) -> FutureResult<(), UserError> { FutureResult::new(async { Ok(()) }) }
 
     fn add_handler(&self, handler: Arc<dyn WsMessageHandler>) -> Result<(), UserError> {
         let source = handler.source();
@@ -73,37 +78,48 @@ impl FlowyWebSocket for Arc<MockWebSocket> {
     fn ws_sender(&self) -> Result<Arc<dyn FlowyWsSender>, UserError> { Ok(Arc::new(self.ws_sender.clone())) }
 }
 
-impl FlowyWsSender for broadcast::Sender<WsMessage> {
-    fn send(&self, msg: WsMessage) -> Result<(), UserError> {
-        let _ = self.send(msg).unwrap();
-        Ok(())
-    }
+lazy_static! {
+    static ref DOC_SERVER: Arc<MockDocServer> = Arc::new(MockDocServer::default());
 }
 
-pub(crate) struct LocalWebSocket {
-    state_sender: broadcast::Sender<WsConnectState>,
-    ws_sender: broadcast::Sender<WsMessage>,
+struct MockDocServer {
+    pub manager: Arc<ServerDocManager>,
 }
 
-impl std::default::Default for LocalWebSocket {
+impl std::default::Default for MockDocServer {
     fn default() -> Self {
-        let (state_sender, _) = broadcast::channel(16);
-        let (ws_sender, _) = broadcast::channel(16);
-        LocalWebSocket {
-            state_sender,
-            ws_sender,
-        }
+        let manager = Arc::new(ServerDocManager::new(Arc::new(MockDocServerPersistence {})));
+        MockDocServer { manager }
     }
 }
 
-impl FlowyWebSocket for Arc<LocalWebSocket> {
-    fn start_connect(&self, _addr: String) -> ResultFuture<(), UserError> { ResultFuture::new(async { Ok(()) }) }
-
-    fn conn_state_subscribe(&self) -> Receiver<WsConnectState> { self.state_sender.subscribe() }
+impl MockDocServer {
+    async fn handle_ws_data(&self, ws_data: WsDocumentData) -> Option<WsMessage> {
+        let bytes = Bytes::from(ws_data.data);
+        match ws_data.ty {
+            WsDataType::Acked => {},
+            WsDataType::PushRev => {
+                let revision = Revision::try_from(bytes).unwrap();
+                log::info!("{:?}", revision);
+            },
+            WsDataType::PullRev => {},
+            WsDataType::Conflict => {},
+            WsDataType::NewDocUser => {
+                let new_doc_user = NewDocUser::try_from(bytes).unwrap();
+                log::info!("{:?}", new_doc_user);
+                // NewDocUser
+            },
+        }
+        None
+    }
+}
 
-    fn reconnect(&self, _count: usize) -> ResultFuture<(), UserError> { ResultFuture::new(async { Ok(()) }) }
+struct MockDocServerPersistence {}
 
-    fn add_handler(&self, _handler: Arc<dyn WsMessageHandler>) -> Result<(), UserError> { Ok(()) }
+impl ServerDocPersistence for MockDocServerPersistence {
+    fn update_doc(&self, doc_id: &str, rev_id: i64, delta: RichTextDelta) -> FutureResultSend<(), CollaborateError> {
+        unimplemented!()
+    }
 
-    fn ws_sender(&self) -> Result<Arc<dyn FlowyWsSender>, UserError> { Ok(Arc::new(self.ws_sender.clone())) }
+    fn read_doc(&self, doc_id: &str) -> FutureResultSend<Doc, CollaborateError> { unimplemented!() }
 }

+ 7 - 7
frontend/rust-lib/flowy-user/src/services/user/ws_manager.rs

@@ -1,16 +1,16 @@
 use crate::errors::UserError;
 
 use flowy_net::entities::NetworkType;
-use lib_infra::future::ResultFuture;
+use lib_infra::future::FutureResult;
 use lib_ws::{WsConnectState, WsController, WsMessage, WsMessageHandler, WsSender};
 use parking_lot::RwLock;
 use std::sync::Arc;
 use tokio::sync::{broadcast, broadcast::Receiver};
 
 pub trait FlowyWebSocket: Send + Sync {
-    fn start_connect(&self, addr: String) -> ResultFuture<(), UserError>;
+    fn start_connect(&self, addr: String) -> FutureResult<(), UserError>;
     fn conn_state_subscribe(&self) -> broadcast::Receiver<WsConnectState>;
-    fn reconnect(&self, count: usize) -> ResultFuture<(), UserError>;
+    fn reconnect(&self, count: usize) -> FutureResult<(), UserError>;
     fn add_handler(&self, handler: Arc<dyn WsMessageHandler>) -> Result<(), UserError>;
     fn ws_sender(&self) -> Result<Arc<dyn FlowyWsSender>, UserError>;
 }
@@ -115,9 +115,9 @@ impl std::default::Default for WsManager {
 }
 
 impl FlowyWebSocket for Arc<WsController> {
-    fn start_connect(&self, addr: String) -> ResultFuture<(), UserError> {
+    fn start_connect(&self, addr: String) -> FutureResult<(), UserError> {
         let cloned_ws = self.clone();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = cloned_ws.start(addr).await?;
             Ok(())
         })
@@ -125,9 +125,9 @@ impl FlowyWebSocket for Arc<WsController> {
 
     fn conn_state_subscribe(&self) -> Receiver<WsConnectState> { self.state_subscribe() }
 
-    fn reconnect(&self, count: usize) -> ResultFuture<(), UserError> {
+    fn reconnect(&self, count: usize) -> FutureResult<(), UserError> {
         let cloned_ws = self.clone();
-        ResultFuture::new(async move {
+        FutureResult::new(async move {
             let _ = cloned_ws.retry(count).await?;
             Ok(())
         })

+ 1 - 1
frontend/scripts/flowy-tool/src/util/file.rs

@@ -10,7 +10,7 @@ use tera::Tera;
 use walkdir::WalkDir;
 
 pub fn read_file(path: &str) -> Option<String> {
-    let mut file = File::open(path).expect(&format!("Unable to open file at {}", path));
+    let mut file = File::open(path).unwrap_or_else(|_| panic!("Unable to open file at {}", path));
     let mut content = String::new();
     match file.read_to_string(&mut content) {
         Ok(_) => Some(content),

+ 2 - 2
shared-lib/Cargo.lock

@@ -279,6 +279,7 @@ dependencies = [
  "bytes",
  "config",
  "derive_more",
+ "flowy-collaboration",
  "flowy-core-infra",
  "flowy-user-infra",
  "hyper",
@@ -688,6 +689,7 @@ dependencies = [
  "dashmap",
  "flowy-derive",
  "futures",
+ "lib-infra",
  "lib-ot",
  "log",
  "md5",
@@ -1132,11 +1134,9 @@ version = "0.1.0"
 dependencies = [
  "bytes",
  "chrono",
- "flowy-derive",
  "futures-core",
  "log",
  "pin-project",
- "protobuf",
  "rand 0.8.4",
  "tokio",
  "uuid",

+ 1 - 0
shared-lib/Cargo.toml

@@ -6,6 +6,7 @@ members = [
   "lib-ot",
   "lib-ws",
   "lib-sqlite",
+  "lib-infra",
   "backend-service",
   "flowy-derive",
   "flowy-ast",

+ 1 - 0
shared-lib/backend-service/Cargo.toml

@@ -8,6 +8,7 @@ edition = "2018"
 [dependencies]
 flowy-core-infra = { path = "../flowy-core-infra" }
 flowy-user-infra = { path = "../flowy-user-infra" }
+flowy-collaboration = { path = "../flowy-collaboration" }
 
 log = "0.4.14"
 lazy_static = "1.4.0"

+ 1 - 0
shared-lib/flowy-collaboration/Cargo.toml

@@ -7,6 +7,7 @@ edition = "2018"
 
 [dependencies]
 lib-ot = { path = "../lib-ot" }
+lib-infra = { path = "../lib-infra" }
 flowy-derive = { path = "../flowy-derive" }
 protobuf = {version = "2.18.0"}
 bytes = "1.0"

+ 1 - 0
shared-lib/flowy-collaboration/src/core/document/default/READ_ME.json

@@ -0,0 +1 @@
+[{"insert":"\n👋 Welcome to AppFlowy!"},{"insert":"\n","attributes":{"header":1}},{"insert":"\nHere are the basics"},{"insert":"\n","attributes":{"header":2}},{"insert":"Click anywhere and just start typing"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"Highlight","attributes":{"background":"#fff2cd"}},{"insert":" any text, and use the menu at the bottom to "},{"insert":"style","attributes":{"italic":true}},{"insert":" "},{"insert":"your","attributes":{"bold":true}},{"insert":" "},{"insert":"writing","attributes":{"underline":true}},{"insert":" "},{"insert":"however","attributes":{"code":true}},{"insert":" "},{"insert":"you","attributes":{"strike":true}},{"insert":" "},{"insert":"like","attributes":{"background":"#e8e0ff"}},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"Click "},{"insert":"+ New Page","attributes":{"background":"#defff1","bold":true}},{"insert":" button at the bottom of your sidebar to add a new page"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"Click the "},{"insert":"'","attributes":{"background":"#defff1"}},{"insert":"+'","attributes":{"background":"#defff1","bold":true}},{"insert":"  next to any page title in the sidebar to quickly add a new subpage"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"\nHave a question? "},{"insert":"\n","attributes":{"header":2}},{"insert":"Click the "},{"insert":"'?'","attributes":{"background":"#defff1","bold":true}},{"insert":" at the bottom right for help and support.\n\nLike AppFlowy? Follow us:"},{"insert":"\n","attributes":{"header":2}},{"insert":"GitHub: https://github.com/AppFlowy-IO/appflowy"},{"insert":"\n","attributes":{"blockquote":true}},{"insert":"Twitter: https://twitter.com/appflowy"},{"insert":"\n","attributes":{"blockquote":true}},{"insert":"Newsletter: https://www.appflowy.io/blog"},{"insert":"\n","attributes":{"blockquote":true}}]

+ 4 - 4
shared-lib/flowy-collaboration/src/user_default.rs → shared-lib/flowy-collaboration/src/core/document/default/mod.rs

@@ -1,20 +1,20 @@
 use lib_ot::{core::DeltaBuilder, rich_text::RichTextDelta};
 
 #[inline]
-pub fn doc_initial_delta() -> RichTextDelta { DeltaBuilder::new().insert("\n").build() }
+pub fn initial_delta() -> RichTextDelta { DeltaBuilder::new().insert("\n").build() }
 
 #[inline]
-pub fn doc_initial_string() -> String { doc_initial_delta().to_json() }
+pub fn initial_string() -> String { initial_delta().to_json() }
 
 #[inline]
 pub fn initial_read_me() -> RichTextDelta {
-    let json = include_str!("READ_ME.json");
+    let json = include_str!("./READ_ME.json");
     RichTextDelta::from_json(json).unwrap()
 }
 
 #[cfg(test)]
 mod tests {
-    use crate::user_default::initial_read_me;
+    use crate::core::document::default::initial_read_me;
 
     #[test]
     fn load_read_me() {

+ 9 - 7
shared-lib/flowy-collaboration/src/core/document/document.rs

@@ -1,16 +1,18 @@
+use tokio::sync::mpsc;
+
+use lib_ot::{
+    core::*,
+    rich_text::{RichTextAttribute, RichTextDelta},
+};
+
 use crate::{
     core::document::{
+        default::initial_delta,
         history::{History, UndoResult},
         view::{View, RECORD_THRESHOLD},
     },
     errors::CollaborateError,
-    user_default::doc_initial_delta,
-};
-use lib_ot::{
-    core::*,
-    rich_text::{RichTextAttribute, RichTextDelta},
 };
-use tokio::sync::mpsc;
 
 pub trait CustomDocument {
     fn init_delta() -> RichTextDelta;
@@ -23,7 +25,7 @@ impl CustomDocument for PlainDoc {
 
 pub struct FlowyDoc();
 impl CustomDocument for FlowyDoc {
-    fn init_delta() -> RichTextDelta { doc_initial_delta() }
+    fn init_delta() -> RichTextDelta { initial_delta() }
 }
 
 pub struct Document {

+ 6 - 4
shared-lib/flowy-collaboration/src/core/document/mod.rs

@@ -1,10 +1,12 @@
 #![allow(clippy::module_inception)]
+
+pub use document::*;
+pub(crate) use extensions::*;
+pub use view::*;
+
 mod data;
+pub mod default;
 mod document;
 mod extensions;
 pub mod history;
 mod view;
-
-pub use document::*;
-pub(crate) use extensions::*;
-pub use view::*;

+ 15 - 8
shared-lib/flowy-collaboration/src/core/sync/server_editor.rs

@@ -9,6 +9,7 @@ use crate::{
 use async_stream::stream;
 use dashmap::DashMap;
 use futures::stream::StreamExt;
+use lib_infra::future::FutureResultSend;
 use lib_ot::{errors::OTError, revision::Revision, rich_text::RichTextDelta};
 use std::sync::{
     atomic::{AtomicI64, Ordering::SeqCst},
@@ -20,9 +21,8 @@ use tokio::{
 };
 
 pub trait ServerDocPersistence: Send + Sync {
-    fn create_doc(&self, doc_id: &str, delta: RichTextDelta) -> CollaborateResult<()>;
-    fn update_doc(&self, doc_id: &str, delta: RichTextDelta) -> CollaborateResult<()>;
-    fn read_doc(&self, doc_id: &str) -> CollaborateResult<Doc>;
+    fn update_doc(&self, doc_id: &str, rev_id: i64, delta: RichTextDelta) -> FutureResultSend<(), CollaborateError>;
+    fn read_doc(&self, doc_id: &str) -> FutureResultSend<Doc, CollaborateError>;
 }
 
 #[rustfmt::skip]
@@ -59,18 +59,25 @@ impl ServerDocManager {
         }
     }
 
-    pub fn get(&self, doc_id: &str) -> Option<Arc<OpenDocHandle>> {
-        self.open_doc_map.get(doc_id).map(|ctx| ctx.clone())
+    pub async fn get(&self, doc_id: &str) -> Result<Option<Arc<OpenDocHandle>>, CollaborateError> {
+        match self.open_doc_map.get(doc_id).map(|ctx| ctx.clone()) {
+            Some(edit_doc) => Ok(Some(edit_doc)),
+            None => {
+                let doc = self.persistence.read_doc(doc_id).await?;
+                let handler = self.cache(doc).await.map_err(internal_error)?;
+                Ok(Some(handler))
+            },
+        }
     }
 
-    pub async fn cache(&self, doc: Doc) -> Result<(), CollaborateError> {
+    async fn cache(&self, doc: Doc) -> Result<Arc<OpenDocHandle>, CollaborateError> {
         let doc_id = doc.id.clone();
         let handle = spawn_blocking(|| OpenDocHandle::new(doc))
             .await
             .map_err(internal_error)?;
         let handle = Arc::new(handle?);
-        self.open_doc_map.insert(doc_id, handle);
-        Ok(())
+        self.open_doc_map.insert(doc_id, handle.clone());
+        Ok(handle)
     }
 }
 

+ 0 - 1
shared-lib/flowy-collaboration/src/lib.rs

@@ -2,5 +2,4 @@ pub mod core;
 pub mod entities;
 pub mod errors;
 pub mod protobuf;
-pub mod user_default;
 pub mod util;

+ 2 - 2
shared-lib/flowy-core-infra/src/entities/view/view_create.rs

@@ -7,7 +7,7 @@ use crate::{
         view::{ViewName, ViewThumbnail},
     },
 };
-use flowy_collaboration::user_default::doc_initial_string;
+use flowy_collaboration::core::document::default::initial_string;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use std::convert::TryInto;
 
@@ -81,7 +81,7 @@ impl CreateViewParams {
             desc,
             thumbnail,
             view_type,
-            data: doc_initial_string(),
+            data: initial_string(),
         }
     }
 }

+ 0 - 2
frontend/rust-lib/lib-infra/Cargo.toml → shared-lib/lib-infra/Cargo.toml

@@ -7,8 +7,6 @@ edition = "2018"
 
 [dependencies]
 uuid = { version = "0.8", features = ["serde", "v4"] }
-flowy-derive = { path = "../../../shared-lib/flowy-derive" }
-protobuf = {version = "2.18.0"}
 log = "0.4.14"
 chrono = "0.4.19"
 bytes = { version = "1.0" }

+ 34 - 3
frontend/rust-lib/lib-infra/src/future.rs → shared-lib/lib-infra/src/future.rs

@@ -33,12 +33,12 @@ where
 }
 
 #[pin_project]
-pub struct ResultFuture<T, E> {
+pub struct FutureResult<T, E> {
     #[pin]
     pub fut: Pin<Box<dyn Future<Output = Result<T, E>> + Sync + Send>>,
 }
 
-impl<T, E> ResultFuture<T, E> {
+impl<T, E> FutureResult<T, E> {
     pub fn new<F>(f: F) -> Self
     where
         F: Future<Output = Result<T, E>> + Send + Sync + 'static,
@@ -49,7 +49,7 @@ impl<T, E> ResultFuture<T, E> {
     }
 }
 
-impl<T, E> Future for ResultFuture<T, E>
+impl<T, E> Future for FutureResult<T, E>
 where
     T: Send + Sync,
     E: Debug,
@@ -62,3 +62,34 @@ where
         Poll::Ready(result)
     }
 }
+
+#[pin_project]
+pub struct FutureResultSend<T, E> {
+    #[pin]
+    pub fut: Pin<Box<dyn Future<Output = Result<T, E>> + Send>>,
+}
+
+impl<T, E> FutureResultSend<T, E> {
+    pub fn new<F>(f: F) -> Self
+    where
+        F: Future<Output = Result<T, E>> + Send + 'static,
+    {
+        Self {
+            fut: Box::pin(async { f.await }),
+        }
+    }
+}
+
+impl<T, E> Future for FutureResultSend<T, E>
+where
+    T: Send,
+    E: Debug,
+{
+    type Output = Result<T, E>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.as_mut().project();
+        let result = ready!(this.fut.poll(cx));
+        Poll::Ready(result)
+    }
+}

+ 0 - 0
frontend/rust-lib/lib-infra/src/lib.rs → shared-lib/lib-infra/src/lib.rs


+ 0 - 0
frontend/rust-lib/lib-infra/src/retry/future.rs → shared-lib/lib-infra/src/retry/future.rs


+ 0 - 0
frontend/rust-lib/lib-infra/src/retry/mod.rs → shared-lib/lib-infra/src/retry/mod.rs


+ 0 - 0
frontend/rust-lib/lib-infra/src/retry/strategy/exponential_backoff.rs → shared-lib/lib-infra/src/retry/strategy/exponential_backoff.rs


+ 0 - 0
frontend/rust-lib/lib-infra/src/retry/strategy/fixed_interval.rs → shared-lib/lib-infra/src/retry/strategy/fixed_interval.rs


+ 0 - 0
frontend/rust-lib/lib-infra/src/retry/strategy/jitter.rs → shared-lib/lib-infra/src/retry/strategy/jitter.rs


+ 0 - 0
frontend/rust-lib/lib-infra/src/retry/strategy/mod.rs → shared-lib/lib-infra/src/retry/strategy/mod.rs


+ 1 - 1
shared-lib/lib-ws/Cargo.toml

@@ -8,7 +8,7 @@ edition = "2018"
 [dependencies]
 flowy-derive = { path = "../flowy-derive" }
 backend-service = { path = "../backend-service" }
-lib-infra = { path = "../../frontend/rust-lib/lib-infra" }
+lib-infra = { path = "../lib-infra" }
 
 tokio-tungstenite = "0.15"
 futures-util = "0.3.17"