add mock server

This commit is contained in:
appflowy
2021-12-13 13:55:44 +08:00
parent df432d11c3
commit 31086ad4df
51 changed files with 423 additions and 292 deletions

8
backend/Cargo.lock generated
View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
services::{ services::{
doc::{editor::ServerDocUser, read_doc}, doc::editor::ServerDocUser,
util::{md5, parse_from_bytes}, util::{md5, parse_from_bytes},
}, },
web_socket::{entities::Socket, WsClientData, WsUser}, 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 backend_service::errors::{internal_error, Result as DocResult, ServerError};
use flowy_collaboration::{ use flowy_collaboration::{
core::sync::{OpenDocHandle, ServerDocManager}, core::sync::{OpenDocHandle, ServerDocManager},
protobuf::{DocIdentifier, NewDocUser, WsDataType, WsDocumentData}, protobuf::{NewDocUser, WsDataType, WsDocumentData},
}; };
use futures::stream::StreamExt; use futures::stream::StreamExt;
use lib_ot::protobuf::Revision; use lib_ot::protobuf::Revision;
@ -128,32 +128,15 @@ impl DocWsActor {
Ok(()) Ok(())
} }
async fn get_doc_handle(&self, doc_id: &str, pg_pool: Data<PgPool>) -> Option<Arc<OpenDocHandle>> { async fn get_doc_handle(&self, doc_id: &str, _pg_pool: Data<PgPool>) -> Option<Arc<OpenDocHandle>> {
match self.doc_manager.get(doc_id) { match self.doc_manager.get(doc_id).await {
Some(edit_doc) => Some(edit_doc), Ok(Some(edit_doc)) => Some(edit_doc),
None => { Ok(None) => 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) => { Err(e) => {
log::error!("{}", e); log::error!("{}", e);
None None
}, },
} }
},
}
} }
} }

View File

@ -9,7 +9,7 @@ use crate::{
use crate::services::view::{create_view_with_args, sql_builder::NewViewSqlBuilder}; use crate::services::view::{create_view_with_args, sql_builder::NewViewSqlBuilder};
use backend_service::errors::ServerError; use backend_service::errors::ServerError;
use chrono::Utc; 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 flowy_core_infra::protobuf::Workspace;
use std::convert::TryInto; use std::convert::TryInto;
@ -42,7 +42,7 @@ pub async fn create_default_workspace(
for view in views.take_items() { for view in views.take_items() {
let (sql, args, view) = NewViewSqlBuilder::from_view(view)?.build()?; 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) Ok(workspace)

View File

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

View File

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

View File

@ -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::{ use crate::{
entities::workspace::RepeatedWorkspace, entities::workspace::RepeatedWorkspace,
errors::{WorkspaceError, WorkspaceResult}, errors::{WorkspaceError, WorkspaceResult},
@ -5,13 +15,7 @@ use crate::{
notify::{send_dart_notification, WorkspaceNotification}, notify::{send_dart_notification, WorkspaceNotification},
services::{server::Server, AppController, TrashController, ViewController, WorkspaceController}, 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! { lazy_static! {
static ref INIT_WORKSPACE: RwLock<HashMap<String, bool>> = RwLock::new(HashMap::new()); static ref INIT_WORKSPACE: RwLock<HashMap<String, bool>> = RwLock::new(HashMap::new());
} }

View File

@ -15,7 +15,7 @@ use crate::{
errors::WorkspaceError, errors::WorkspaceError,
}; };
use backend_service::configuration::ClientServerConfiguration; use backend_service::configuration::ClientServerConfiguration;
use lib_infra::future::ResultFuture; use lib_infra::future::FutureResult;
use std::sync::Arc; use std::sync::Arc;
pub(crate) type Server = Arc<dyn WorkspaceServerAPI + Send + Sync>; pub(crate) type Server = Arc<dyn WorkspaceServerAPI + Send + Sync>;
@ -24,42 +24,42 @@ pub trait WorkspaceServerAPI {
fn init(&self); fn init(&self);
// Workspace // 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( fn read_workspace(
&self, &self,
token: &str, token: &str,
params: WorkspaceIdentifier, 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 // 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 // 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 // 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( pub(crate) fn construct_workspace_server(

View File

@ -11,7 +11,7 @@ use crate::{
}; };
use backend_service::{configuration::ClientServerConfiguration, middleware::*, workspace_request::*}; use backend_service::{configuration::ClientServerConfiguration, middleware::*, workspace_request::*};
use flowy_core_infra::errors::ErrorCode; use flowy_core_infra::errors::ErrorCode;
use lib_infra::future::ResultFuture; use lib_infra::future::FutureResult;
pub struct WorkspaceHttpServer { pub struct WorkspaceHttpServer {
config: ClientServerConfiguration, 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 token = token.to_owned();
let url = self.config.workspace_url(); let url = self.config.workspace_url();
ResultFuture::new(async move { FutureResult::new(async move {
let workspace = create_workspace_request(&token, params, &url).await?; let workspace = create_workspace_request(&token, params, &url).await?;
Ok(workspace) Ok(workspace)
}) })
@ -47,127 +47,127 @@ impl WorkspaceServerAPI for WorkspaceHttpServer {
&self, &self,
token: &str, token: &str,
params: WorkspaceIdentifier, params: WorkspaceIdentifier,
) -> ResultFuture<RepeatedWorkspace, WorkspaceError> { ) -> FutureResult<RepeatedWorkspace, WorkspaceError> {
let token = token.to_owned(); let token = token.to_owned();
let url = self.config.workspace_url(); let url = self.config.workspace_url();
ResultFuture::new(async move { FutureResult::new(async move {
let repeated_workspace = read_workspaces_request(&token, params, &url).await?; let repeated_workspace = read_workspaces_request(&token, params, &url).await?;
Ok(repeated_workspace) 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 token = token.to_owned();
let url = self.config.workspace_url(); let url = self.config.workspace_url();
ResultFuture::new(async move { FutureResult::new(async move {
let _ = update_workspace_request(&token, params, &url).await?; let _ = update_workspace_request(&token, params, &url).await?;
Ok(()) 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 token = token.to_owned();
let url = self.config.workspace_url(); let url = self.config.workspace_url();
ResultFuture::new(async move { FutureResult::new(async move {
let _ = delete_workspace_request(&token, params, &url).await?; let _ = delete_workspace_request(&token, params, &url).await?;
Ok(()) 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 token = token.to_owned();
let url = self.config.view_url(); let url = self.config.view_url();
ResultFuture::new(async move { FutureResult::new(async move {
let view = create_view_request(&token, params, &url).await?; let view = create_view_request(&token, params, &url).await?;
Ok(view) 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 token = token.to_owned();
let url = self.config.view_url(); let url = self.config.view_url();
ResultFuture::new(async move { FutureResult::new(async move {
let view = read_view_request(&token, params, &url).await?; let view = read_view_request(&token, params, &url).await?;
Ok(view) 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 token = token.to_owned();
let url = self.config.view_url(); let url = self.config.view_url();
ResultFuture::new(async move { FutureResult::new(async move {
let _ = delete_view_request(&token, params, &url).await?; let _ = delete_view_request(&token, params, &url).await?;
Ok(()) 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 token = token.to_owned();
let url = self.config.view_url(); let url = self.config.view_url();
ResultFuture::new(async move { FutureResult::new(async move {
let _ = update_view_request(&token, params, &url).await?; let _ = update_view_request(&token, params, &url).await?;
Ok(()) 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 token = token.to_owned();
let url = self.config.app_url(); let url = self.config.app_url();
ResultFuture::new(async move { FutureResult::new(async move {
let app = create_app_request(&token, params, &url).await?; let app = create_app_request(&token, params, &url).await?;
Ok(app) 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 token = token.to_owned();
let url = self.config.app_url(); let url = self.config.app_url();
ResultFuture::new(async move { FutureResult::new(async move {
let app = read_app_request(&token, params, &url).await?; let app = read_app_request(&token, params, &url).await?;
Ok(app) 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 token = token.to_owned();
let url = self.config.app_url(); let url = self.config.app_url();
ResultFuture::new(async move { FutureResult::new(async move {
let _ = update_app_request(&token, params, &url).await?; let _ = update_app_request(&token, params, &url).await?;
Ok(()) 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 token = token.to_owned();
let url = self.config.app_url(); let url = self.config.app_url();
ResultFuture::new(async move { FutureResult::new(async move {
let _ = delete_app_request(&token, params, &url).await?; let _ = delete_app_request(&token, params, &url).await?;
Ok(()) 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 token = token.to_owned();
let url = self.config.trash_url(); let url = self.config.trash_url();
ResultFuture::new(async move { FutureResult::new(async move {
let _ = create_trash_request(&token, params, &url).await?; let _ = create_trash_request(&token, params, &url).await?;
Ok(()) 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 token = token.to_owned();
let url = self.config.trash_url(); let url = self.config.trash_url();
ResultFuture::new(async move { FutureResult::new(async move {
let _ = delete_trash_request(&token, params, &url).await?; let _ = delete_trash_request(&token, params, &url).await?;
Ok(()) 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 token = token.to_owned();
let url = self.config.trash_url(); let url = self.config.trash_url();
ResultFuture::new(async move { FutureResult::new(async move {
let repeated_trash = read_trash_request(&token, &url).await?; let repeated_trash = read_trash_request(&token, &url).await?;
Ok(repeated_trash) Ok(repeated_trash)
}) })

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ use crate::{
}, },
}; };
use flowy_collaboration::{entities::doc::Doc, util::RevIdCounter}; use flowy_collaboration::{entities::doc::Doc, util::RevIdCounter};
use lib_infra::future::ResultFuture; use lib_infra::future::FutureResult;
use lib_ot::{ use lib_ot::{
core::OperationTransformable, core::OperationTransformable,
revision::{RevId, RevType, Revision, RevisionRange}, revision::{RevId, RevType, Revision, RevisionRange},
@ -15,7 +15,7 @@ use lib_ot::{
use std::sync::Arc; use std::sync::Arc;
pub trait RevisionServer: Send + Sync { 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 { pub struct RevisionManager {

View File

@ -7,17 +7,17 @@ pub use server_api::*;
use crate::errors::DocError; use crate::errors::DocError;
use backend_service::configuration::ClientServerConfiguration; use backend_service::configuration::ClientServerConfiguration;
use flowy_collaboration::entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams}; use flowy_collaboration::entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams};
use lib_infra::future::ResultFuture; use lib_infra::future::FutureResult;
pub use server_api_mock::*; pub use server_api_mock::*;
use std::sync::Arc; use std::sync::Arc;
pub(crate) type Server = Arc<dyn DocumentServerAPI + Send + Sync>; pub(crate) type Server = Arc<dyn DocumentServerAPI + Send + Sync>;
pub trait DocumentServerAPI { 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( pub(crate) fn construct_doc_server(

View File

@ -1,7 +1,7 @@
use crate::{errors::DocError, services::server::DocumentServerAPI}; use crate::{errors::DocError, services::server::DocumentServerAPI};
use backend_service::{configuration::*, request::HttpRequestBuilder}; use backend_service::{configuration::*, request::HttpRequestBuilder};
use flowy_collaboration::entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams}; use flowy_collaboration::entities::doc::{CreateDocParams, Doc, DocIdentifier, UpdateDocParams};
use lib_infra::future::ResultFuture; use lib_infra::future::FutureResult;
pub struct DocServer { pub struct DocServer {
config: ClientServerConfiguration, config: ClientServerConfiguration,
@ -12,22 +12,22 @@ impl DocServer {
} }
impl DocumentServerAPI for 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 token = token.to_owned();
let url = self.config.doc_url(); 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 token = token.to_owned();
let url = self.config.doc_url(); 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 token = token.to_owned();
let url = self.config.doc_url(); 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 })
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,17 +8,21 @@ edition = "2018"
[dependencies] [dependencies]
flowy-user-infra = { path = "../../../shared-lib/flowy-user-infra" } flowy-user-infra = { path = "../../../shared-lib/flowy-user-infra" }
backend-service = { path = "../../../shared-lib/backend-service" } backend-service = { path = "../../../shared-lib/backend-service" }
flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" }
flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-derive = { path = "../../../shared-lib/flowy-derive" }
lib-ws = { path = "../../../shared-lib/lib-ws" } lib-ws = { path = "../../../shared-lib/lib-ws" }
lib-sqlite = { path = "../../../shared-lib/lib-sqlite" } 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"]} derive_more = {version = "0.99", features = ["display"]}
flowy-database = { path = "../flowy-database" } flowy-database = { path = "../flowy-database" }
flowy-net = { path = "../flowy-net" } flowy-net = { path = "../flowy-net" }
dart-notify = { path = "../dart-notify" } dart-notify = { path = "../dart-notify" }
lib-dispatch = { path = "../lib-dispatch" } lib-dispatch = { path = "../lib-dispatch" }
lib-infra = { path = "../lib-infra" }
tracing = { version = "0.1", features = ["log"] } tracing = { version = "0.1", features = ["log"] }
bytes = "1.0" bytes = "1.0"
@ -48,3 +52,4 @@ serial_test = "0.5.1"
[features] [features]
http_server = [] http_server = []
ws_mock = ["flowy-collaboration", "lib-ot"]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ use tera::Tera;
use walkdir::WalkDir; use walkdir::WalkDir;
pub fn read_file(path: &str) -> Option<String> { 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(); let mut content = String::new();
match file.read_to_string(&mut content) { match file.read_to_string(&mut content) {
Ok(_) => Some(content), Ok(_) => Some(content),

4
shared-lib/Cargo.lock generated
View File

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

View File

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

View File

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

View File

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

View File

@ -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}}]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ use crate::{
view::{ViewName, ViewThumbnail}, 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 flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use std::convert::TryInto; use std::convert::TryInto;
@ -81,7 +81,7 @@ impl CreateViewParams {
desc, desc,
thumbnail, thumbnail,
view_type, view_type,
data: doc_initial_string(), data: initial_string(),
} }
} }
} }

View File

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

View File

@ -33,12 +33,12 @@ where
} }
#[pin_project] #[pin_project]
pub struct ResultFuture<T, E> { pub struct FutureResult<T, E> {
#[pin] #[pin]
pub fut: Pin<Box<dyn Future<Output = Result<T, E>> + Sync + Send>>, 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 pub fn new<F>(f: F) -> Self
where where
F: Future<Output = Result<T, E>> + Send + Sync + 'static, 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 where
T: Send + Sync, T: Send + Sync,
E: Debug, E: Debug,
@ -62,3 +62,34 @@ where
Poll::Ready(result) 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)
}
}

View File

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