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",
"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",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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::{
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());
}

View File

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

View File

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

View File

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

View File

@ -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 = []

View File

@ -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),

View File

@ -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 => {
//

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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::{

View File

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

View File

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

View File

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

View File

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

View File

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

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 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() }
fn reconnect(&self, _count: usize) -> ResultFuture<(), UserError> { ResultFuture::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 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
}
}
struct MockDocServerPersistence {}
impl ServerDocPersistence for MockDocServerPersistence {
fn update_doc(&self, doc_id: &str, rev_id: i64, delta: RichTextDelta) -> FutureResultSend<(), CollaborateError> {
unimplemented!()
}
fn read_doc(&self, doc_id: &str) -> FutureResultSend<Doc, CollaborateError> { unimplemented!() }
}

View File

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

View File

@ -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),

4
shared-lib/Cargo.lock generated
View File

@ -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",

View File

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

View File

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

View File

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

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};
#[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() {

View File

@ -1,16 +1,18 @@
use crate::{
core::document::{
history::{History, UndoResult},
view::{View, RECORD_THRESHOLD},
},
errors::CollaborateError,
user_default::doc_initial_delta,
};
use tokio::sync::mpsc;
use lib_ot::{
core::*,
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 {
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 {

View File

@ -1,10 +1,12 @@
#![allow(clippy::module_inception)]
mod data;
mod document;
mod extensions;
pub mod history;
mod view;
pub use document::*;
pub(crate) use extensions::*;
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 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)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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