add server with flowy_document test

This commit is contained in:
appflowy 2021-09-27 23:23:23 +08:00
parent 11bc536df8
commit 01c9620e03
43 changed files with 515 additions and 340 deletions

View File

@ -96,3 +96,7 @@ linkify = "0.5.0"
flowy-user = { path = "../rust-lib/flowy-user" }
flowy-workspace = { path = "../rust-lib/flowy-workspace" }
flowy-ws = { path = "../rust-lib/flowy-ws" }
flowy-sdk = { path = "../rust-lib/flowy-sdk" }
flowy-test = { path = "../rust-lib/flowy-test" }
flowy-infra = { path = "../rust-lib/flowy-infra" }
flowy-ot = { path = "../rust-lib/flowy-ot" }

View File

@ -22,7 +22,6 @@ use parking_lot::RwLock;
use protobuf::Message;
use sqlx::PgPool;
use std::{
cmp::min,
convert::TryInto,
sync::{
atomic::{AtomicI64, Ordering::SeqCst},

View File

@ -6,8 +6,7 @@ use crate::service::{
};
use actix_web::web::Data;
use crate::service::ws::WsUser;
use flowy_document::protobuf::{QueryDocParams, Revision, RevisionRange, WsDataType, WsDocumentData};
use flowy_document::protobuf::{QueryDocParams, Revision, WsDataType, WsDocumentData};
use flowy_net::errors::ServerError;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use protobuf::Message;

View File

@ -28,7 +28,7 @@ impl Builder {
let env_filter = EnvFilter::new(self.env_filter);
let subscriber = tracing_subscriber::fmt()
.with_target(true)
.with_max_level(tracing::Level::TRACE)
.with_max_level(tracing::Level::DEBUG)
.with_writer(std::io::stderr)
.with_thread_ids(false)
.compact()

View File

@ -8,7 +8,7 @@ use crate::{
};
use flowy_net::errors::ServerError;
use flowy_workspace::{
entities::view::VIEW_DEFAULT_DATA,
entities::view::DOC_DEFAULT_DATA,
protobuf::{App, CreateViewParams, View, ViewType, Workspace},
};
@ -62,7 +62,7 @@ async fn create_view(transaction: &mut DBTransaction<'_>, app: &App) -> Result<V
desc: "View created by AppFlowy".to_string(),
thumbnail: "123.png".to_string(),
view_type: ViewType::Doc,
data: VIEW_DEFAULT_DATA.to_string(),
data: DOC_DEFAULT_DATA.to_string(),
unknown_fields: Default::default(),
cached_size: Default::default(),
};

View File

@ -19,6 +19,7 @@ pub async fn establish_ws_connection(
server: Data<Addr<WsServer>>,
biz_handlers: Data<WsBizHandlers>,
) -> Result<HttpResponse, Error> {
log::debug!("establish_ws_connection");
match LoggedUser::from_token(token.clone()) {
Ok(user) => {
let ws_user = WsUser::new(user.clone());

View File

@ -3,7 +3,7 @@ use crate::{
service::{
user::LoggedUser,
ws::{
entities::{Connect, Disconnect, SessionId, Socket},
entities::{Connect, Disconnect, Socket},
WsBizHandler,
WsBizHandlers,
WsMessageAdaptor,

View File

@ -1,9 +1,9 @@
use crate::helper::{spawn_server, TestServer};
use crate::helper::{spawn_user_server, TestUserServer};
use flowy_user::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams};
#[actix_rt::test]
async fn user_register() {
let app = spawn_server().await;
let app = spawn_user_server().await;
let response = register_user(&app, "annie@appflowy.io", "HelloWorld123!").await;
log::info!("{:?}", response);
}
@ -11,7 +11,7 @@ async fn user_register() {
#[actix_rt::test]
#[should_panic]
async fn user_sign_in_with_invalid_password() {
let app = spawn_server().await;
let app = spawn_user_server().await;
let email = "annie@appflowy.io";
let password = "123";
let _ = register_user(&app, email, password).await;
@ -20,7 +20,7 @@ async fn user_sign_in_with_invalid_password() {
#[actix_rt::test]
#[should_panic]
async fn user_sign_in_with_invalid_email() {
let app = spawn_server().await;
let app = spawn_user_server().await;
let email = "annie@gmail@";
let password = "HelloWorld123!";
let _ = register_user(&app, email, password).await;
@ -28,7 +28,7 @@ async fn user_sign_in_with_invalid_email() {
#[actix_rt::test]
async fn user_sign_in() {
let app = spawn_server().await;
let app = spawn_user_server().await;
let email = "annie@appflowy.io";
let password = "HelloWorld123!";
let _ = register_user(&app, email, password).await;
@ -42,7 +42,7 @@ async fn user_sign_in() {
#[actix_rt::test]
#[should_panic]
async fn user_sign_out() {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
server.sign_out().await;
// user_detail will be empty because use was sign out.
@ -51,13 +51,13 @@ async fn user_sign_out() {
#[actix_rt::test]
async fn user_get_detail() {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
log::info!("{:?}", server.get_user_profile().await);
}
#[actix_rt::test]
async fn user_update_password() {
let mut server = spawn_server().await;
let mut server = spawn_user_server().await;
let email = "annie@appflowy.io";
let password = "HelloWorld123!";
let sign_up_resp = register_user(&server, email, password).await;
@ -82,7 +82,7 @@ async fn user_update_password() {
#[actix_rt::test]
async fn user_update_name() {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let name = "tom".to_string();
let params = UpdateUserParams::new(&server.user_id()).name(&name);
@ -94,7 +94,7 @@ async fn user_update_name() {
#[actix_rt::test]
async fn user_update_email() {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let email = "123@gmail.com".to_string();
let params = UpdateUserParams::new(server.user_id()).email(&email);
server.update_user_profile(params).await.unwrap();
@ -104,14 +104,14 @@ async fn user_update_email() {
}
#[allow(dead_code)]
async fn sign_up_user(server: &TestServer) -> SignUpResponse {
async fn sign_up_user(server: &TestUserServer) -> SignUpResponse {
let email = "annie@appflowy.io";
let password = "HelloWorld123!";
let response = register_user(server, email, password).await;
response
}
async fn register_user(server: &TestServer, email: &str, password: &str) -> SignUpResponse {
async fn register_user(server: &TestUserServer, email: &str, password: &str) -> SignUpResponse {
let params = SignUpParams {
email: email.to_string(),
name: "annie".to_string(),

View File

@ -2,12 +2,7 @@ use crate::helper::*;
use flowy_workspace::entities::{
app::{DeleteAppParams, QueryAppParams, UpdateAppParams},
view::{DeleteViewParams, QueryViewParams, UpdateViewParams},
workspace::{
CreateWorkspaceParams,
DeleteWorkspaceParams,
QueryWorkspaceParams,
UpdateWorkspaceParams,
},
workspace::{CreateWorkspaceParams, DeleteWorkspaceParams, QueryWorkspaceParams, UpdateWorkspaceParams},
};
#[actix_rt::test]
@ -173,7 +168,7 @@ async fn view_delete() {
#[actix_rt::test]
async fn workspace_list_read() {
let mut server = spawn_server().await;
let mut server = spawn_user_server().await;
let token = server.register_user().await.token;
server.user_token = Some(token);
for i in 0..3 {

View File

@ -0,0 +1,97 @@
use crate::helper::*;
use flowy_document::{
entities::doc::{CreateDocParams, DocDelta, QueryDocParams},
module::FlowyDocument,
services::doc::edit_doc_context::EditDocContext,
};
use flowy_infra::uuid;
use flowy_ot::core::Delta;
use flowy_sdk::{FlowySDK, FlowySDKConfig};
use flowy_test::{prelude::root_dir, FlowyTestSDK};
use flowy_user::{entities::SignUpParams, services::user::UserSession};
use flowy_workspace::prelude::DOC_DEFAULT_DATA;
use std::{str::FromStr, sync::Arc};
pub struct DocumentTest {
server: TestServer,
sdk: FlowyTestSDK,
flowy_document: Arc<FlowyDocument>,
user_session: Arc<UserSession>,
edit_context: Arc<EditDocContext>,
}
#[derive(Clone)]
pub enum DocScript {
SendText(&'static str),
SendBinary(Vec<u8>),
}
async fn create_doc(user_session: Arc<UserSession>, flowy_document: Arc<FlowyDocument>) -> Arc<EditDocContext> {
let conn = user_session.db_pool().unwrap().get().unwrap();
let doc_id = uuid();
let params = CreateDocParams {
id: doc_id.clone(),
data: DOC_DEFAULT_DATA.to_string(),
};
let _ = flowy_document.create(params, &*conn).unwrap();
let edit_context = flowy_document
.open(QueryDocParams { doc_id }, user_session.db_pool().unwrap())
.await
.unwrap();
edit_context
}
async fn init_user(user_session: Arc<UserSession>) {
let params = SignUpParams {
email: format!("{}@gmail.com", uuid()),
name: "nathan".to_string(),
password: "HelloWorld!@12".to_string(),
};
user_session.sign_up(params).await.unwrap();
user_session.init_user().await.unwrap();
}
impl DocumentTest {
pub async fn new() -> Self {
let server = spawn_server().await;
let config = FlowySDKConfig::new(&root_dir(), &server.host, "http", "ws").log_filter("debug");
let sdk = FlowySDK::new(config);
let flowy_document = sdk.flowy_document.clone();
let user_session = sdk.user_session.clone();
init_user(user_session.clone()).await;
let edit_context = create_doc(user_session.clone(), flowy_document.clone()).await;
Self {
server,
sdk,
flowy_document,
user_session,
edit_context,
}
}
pub async fn run_scripts(self, scripts: Vec<DocScript>) {
for script in scripts {
match script {
DocScript::SendText(s) => {
let delta = Delta::from_str(s).unwrap();
let data = delta.to_json();
let doc_delta = DocDelta {
doc_id: self.edit_context.doc_id.clone(),
data,
};
self.flowy_document.apply_doc_delta(doc_delta).await;
},
DocScript::SendBinary(_bytes) => {},
}
}
std::mem::forget(self);
}
}

View File

@ -0,0 +1,12 @@
use crate::document::helper::{DocScript, DocumentTest};
use tokio::time::{interval, Duration};
#[actix_rt::test]
async fn ws_connect() {
let test = DocumentTest::new().await;
test.run_scripts(vec![DocScript::SendText("abc")]).await;
let mut interval = interval(Duration::from_secs(10));
interval.tick().await;
interval.tick().await;
}

View File

@ -12,7 +12,7 @@ use flowy_workspace::prelude::{server::*, *};
use sqlx::{Connection, Executor, PgConnection, PgPool};
use uuid::Uuid;
pub struct TestServer {
pub struct TestUserServer {
pub host: String,
pub port: u16,
pub pg_pool: PgPool,
@ -20,9 +20,9 @@ pub struct TestServer {
pub user_id: Option<String>,
}
impl TestServer {
impl TestUserServer {
pub async fn new() -> Self {
let mut server = spawn_server().await;
let mut server: TestUserServer = spawn_server().await.into();
let response = server.register_user().await;
server.user_token = Some(response.token);
server.user_id = Some(response.user_id);
@ -36,28 +36,16 @@ impl TestServer {
pub async fn sign_out(&self) {
let url = format!("{}/api/auth", self.http_addr());
let _ = user_sign_out_request(self.user_token(), &url)
.await
.unwrap();
let _ = user_sign_out_request(self.user_token(), &url).await.unwrap();
}
pub fn user_token(&self) -> &str {
self.user_token
.as_ref()
.expect("must call register_user first ")
}
pub fn user_token(&self) -> &str { self.user_token.as_ref().expect("must call register_user first ") }
pub fn user_id(&self) -> &str {
self.user_id
.as_ref()
.expect("must call register_user first ")
}
pub fn user_id(&self) -> &str { self.user_id.as_ref().expect("must call register_user first ") }
pub async fn get_user_profile(&self) -> UserProfile {
let url = format!("{}/api/user", self.http_addr());
let user_profile = get_user_profile_request(self.user_token(), &url)
.await
.unwrap();
let user_profile = get_user_profile_request(self.user_token(), &url).await.unwrap();
user_profile
}
@ -68,99 +56,73 @@ impl TestServer {
pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace {
let url = format!("{}/api/workspace", self.http_addr());
let workspace = create_workspace_request(self.user_token(), params, &url)
.await
.unwrap();
let workspace = create_workspace_request(self.user_token(), params, &url).await.unwrap();
workspace
}
pub async fn read_workspaces(&self, params: QueryWorkspaceParams) -> RepeatedWorkspace {
let url = format!("{}/api/workspace", self.http_addr());
let workspaces = read_workspaces_request(self.user_token(), params, &url)
.await
.unwrap();
let workspaces = read_workspaces_request(self.user_token(), params, &url).await.unwrap();
workspaces
}
pub async fn update_workspace(&self, params: UpdateWorkspaceParams) {
let url = format!("{}/api/workspace", self.http_addr());
update_workspace_request(self.user_token(), params, &url)
.await
.unwrap();
update_workspace_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn delete_workspace(&self, params: DeleteWorkspaceParams) {
let url = format!("{}/api/workspace", self.http_addr());
delete_workspace_request(self.user_token(), params, &url)
.await
.unwrap();
delete_workspace_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn create_app(&self, params: CreateAppParams) -> App {
let url = format!("{}/api/app", self.http_addr());
let app = create_app_request(self.user_token(), params, &url)
.await
.unwrap();
let app = create_app_request(self.user_token(), params, &url).await.unwrap();
app
}
pub async fn read_app(&self, params: QueryAppParams) -> Option<App> {
let url = format!("{}/api/app", self.http_addr());
let app = read_app_request(self.user_token(), params, &url)
.await
.unwrap();
let app = read_app_request(self.user_token(), params, &url).await.unwrap();
app
}
pub async fn update_app(&self, params: UpdateAppParams) {
let url = format!("{}/api/app", self.http_addr());
update_app_request(self.user_token(), params, &url)
.await
.unwrap();
update_app_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn delete_app(&self, params: DeleteAppParams) {
let url = format!("{}/api/app", self.http_addr());
delete_app_request(self.user_token(), params, &url)
.await
.unwrap();
delete_app_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn create_view(&self, params: CreateViewParams) -> View {
let url = format!("{}/api/view", self.http_addr());
let view = create_view_request(self.user_token(), params, &url)
.await
.unwrap();
let view = create_view_request(self.user_token(), params, &url).await.unwrap();
view
}
pub async fn read_view(&self, params: QueryViewParams) -> Option<View> {
let url = format!("{}/api/view", self.http_addr());
let view = read_view_request(self.user_token(), params, &url)
.await
.unwrap();
let view = read_view_request(self.user_token(), params, &url).await.unwrap();
view
}
pub async fn update_view(&self, params: UpdateViewParams) {
let url = format!("{}/api/view", self.http_addr());
update_view_request(self.user_token(), params, &url)
.await
.unwrap();
update_view_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn delete_view(&self, params: DeleteViewParams) {
let url = format!("{}/api/view", self.http_addr());
delete_view_request(self.user_token(), params, &url)
.await
.unwrap();
delete_view_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn read_doc(&self, params: QueryDocParams) -> Option<Doc> {
let url = format!("{}/api/doc", self.http_addr());
let doc = read_doc_request(self.user_token(), params, &url)
.await
.unwrap();
let doc = read_doc_request(self.user_token(), params, &url).await.unwrap();
doc
}
@ -182,14 +144,32 @@ impl TestServer {
pub fn http_addr(&self) -> String { format!("http://{}", self.host) }
pub fn ws_addr(&self) -> String {
format!(
"ws://{}/ws/{}",
self.host,
self.user_token.as_ref().unwrap()
)
pub fn ws_addr(&self) -> String { format!("ws://{}/ws/{}", self.host, self.user_token.as_ref().unwrap()) }
}
impl std::convert::From<TestServer> for TestUserServer {
fn from(server: TestServer) -> Self {
TestUserServer {
host: server.host,
port: server.port,
pg_pool: server.pg_pool,
user_token: None,
user_id: None,
}
}
}
pub async fn spawn_user_server() -> TestUserServer {
let server: TestUserServer = spawn_server().await.into();
server
}
pub struct TestServer {
pub host: String,
pub port: u16,
pub pg_pool: PgPool,
}
pub async fn spawn_server() -> TestServer {
let database_name = format!("{}", Uuid::new_v4().to_string());
let configuration = {
@ -217,8 +197,6 @@ pub async fn spawn_server() -> TestServer {
pg_pool: get_connection_pool(&configuration.database)
.await
.expect("Failed to connect to the database"),
user_token: None,
user_id: None,
}
}
@ -265,7 +243,7 @@ async fn drop_test_database(database_name: String) {
.expect("Failed to drop database.");
}
pub async fn create_test_workspace(server: &TestServer) -> Workspace {
pub async fn create_test_workspace(server: &TestUserServer) -> Workspace {
let params = CreateWorkspaceParams {
name: "My first workspace".to_string(),
desc: "This is my first workspace".to_string(),
@ -275,7 +253,7 @@ pub async fn create_test_workspace(server: &TestServer) -> Workspace {
workspace
}
pub async fn create_test_app(server: &TestServer, workspace_id: &str) -> App {
pub async fn create_test_app(server: &TestUserServer, workspace_id: &str) -> App {
let params = CreateAppParams {
workspace_id: workspace_id.to_owned(),
name: "My first app".to_string(),
@ -287,7 +265,7 @@ pub async fn create_test_app(server: &TestServer, workspace_id: &str) -> App {
app
}
pub async fn create_test_view(application: &TestServer, app_id: &str) -> View {
pub async fn create_test_view(application: &TestUserServer, app_id: &str) -> View {
let name = "My first view".to_string();
let desc = "This is my first view".to_string();
let thumbnail = "http://1.png".to_string();
@ -298,43 +276,37 @@ pub async fn create_test_view(application: &TestServer, app_id: &str) -> View {
}
pub struct WorkspaceTest {
pub server: TestServer,
pub server: TestUserServer,
pub workspace: Workspace,
}
impl WorkspaceTest {
pub async fn new() -> Self {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let workspace = create_test_workspace(&server).await;
Self { server, workspace }
}
pub async fn create_app(&self) -> App {
create_test_app(&self.server, &self.workspace.id).await
}
pub async fn create_app(&self) -> App { create_test_app(&self.server, &self.workspace.id).await }
}
pub struct AppTest {
pub server: TestServer,
pub server: TestUserServer,
pub workspace: Workspace,
pub app: App,
}
impl AppTest {
pub async fn new() -> Self {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let workspace = create_test_workspace(&server).await;
let app = create_test_app(&server, &workspace.id).await;
Self {
server,
workspace,
app,
}
Self { server, workspace, app }
}
}
pub struct ViewTest {
pub server: TestServer,
pub server: TestUserServer,
pub workspace: Workspace,
pub app: App,
pub view: View,
@ -342,7 +314,7 @@ pub struct ViewTest {
impl ViewTest {
pub async fn new() -> Self {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let workspace = create_test_workspace(&server).await;
let app = create_test_app(&server, &workspace.id).await;
let view = create_test_view(&server, &app.id).await;

View File

@ -1,3 +1,3 @@
mod api;
mod document;
pub mod helper;
mod ws;

View File

@ -1,76 +0,0 @@
use crate::helper::TestServer;
use flowy_ws::{WsController, WsModule, WsSender, WsState};
use parking_lot::RwLock;
use std::sync::Arc;
pub struct WsTest {
server: TestServer,
ws_controller: Arc<RwLock<WsController>>,
}
#[derive(Clone)]
pub enum WsScript {
SendText(&'static str),
SendBinary(Vec<u8>),
Disconnect(&'static str),
}
impl WsTest {
pub async fn new(scripts: Vec<WsScript>) -> Self {
let server = TestServer::new().await;
let ws_controller = Arc::new(RwLock::new(WsController::new()));
ws_controller
.write()
.state_callback(move |state| match state {
WsState::Connected(sender) => {
WsScriptRunner {
scripts: scripts.clone(),
sender: sender.clone(),
source: WsModule::Doc,
}
.run();
},
_ => {},
})
.await;
Self {
server,
ws_controller,
}
}
pub async fn run_scripts(&mut self) {
let addr = self.server.ws_addr();
self.ws_controller
.write()
.connect(addr)
.unwrap()
.await
.unwrap();
}
}
struct WsScriptRunner {
scripts: Vec<WsScript>,
sender: Arc<WsSender>,
source: WsModule,
}
impl WsScriptRunner {
fn run(self) {
for script in self.scripts {
match script {
WsScript::SendText(text) => {
self.sender.send_text(&self.source, text).unwrap();
},
WsScript::SendBinary(bytes) => {
self.sender.send_binary(&self.source, bytes).unwrap();
},
WsScript::Disconnect(reason) => {
self.sender.send_disconnect(reason).unwrap();
},
}
}
}
}

View File

@ -1,13 +0,0 @@
use crate::ws::helper::{WsScript, WsTest};
#[actix_rt::test]
async fn ws_connect() {
let mut ws = WsTest::new(vec![
WsScript::SendText("abc"),
WsScript::SendText("abc"),
WsScript::SendText("abc"),
WsScript::Disconnect("close by user"),
])
.await;
ws.run_scripts().await
}

View File

@ -14,7 +14,6 @@ members = [
"flowy-workspace",
"flowy-observable",
"flowy-document",
"flowy-editor",
"flowy-ot",
"flowy-net",
"flowy-ws",

View File

@ -24,7 +24,10 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
let c_str: &CStr = unsafe { CStr::from_ptr(path) };
let path: &str = c_str.to_str().unwrap();
let config = FlowySDKConfig::new(path).log_filter("debug");
let host = "localhost";
let http_schema = "http";
let ws_schema = "ws";
let config = FlowySDKConfig::new(path, host, http_schema, ws_schema).log_filter("debug");
*FLOWY_SDK.write() = Some(Arc::new(FlowySDK::new(config)));
return 1;
@ -33,7 +36,12 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
#[no_mangle]
pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) {
let request: ModuleRequest = FFIRequest::from_u8_pointer(input, len).into();
log::trace!("[FFI]: {} Async Event: {:?} with {} port", &request.id, &request.event, port);
log::trace!(
"[FFI]: {} Async Event: {:?} with {} port",
&request.id,
&request.event,
port
);
let _ = EventDispatch::async_send_with_callback(dispatch(), request, move |resp: EventResponse| {
log::trace!("[FFI]: Post data to dart through {} port", port);

View File

@ -8,7 +8,11 @@ use flowy_database::ConnectionPool;
use crate::{
entities::doc::{CreateDocParams, Doc, DocDelta, QueryDocParams},
errors::DocError,
services::{doc::doc_controller::DocController, server::construct_doc_server, ws::WsDocumentManager},
services::{
doc::{doc_controller::DocController, edit_doc_context::EditDocContext},
server::construct_doc_server,
ws::WsDocumentManager,
},
};
pub trait DocumentUser: Send + Sync {
@ -38,9 +42,13 @@ impl FlowyDocument {
Ok(())
}
pub async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
let open_doc = self.doc_ctrl.open(params, pool).await?;
Ok(open_doc.doc())
pub async fn open(
&self,
params: QueryDocParams,
pool: Arc<ConnectionPool>,
) -> Result<Arc<EditDocContext>, DocError> {
let edit_context = self.doc_ctrl.open(params, pool).await?;
Ok(edit_context)
}
pub async fn apply_doc_delta(&self, params: DocDelta) -> Result<Doc, DocError> {

View File

@ -2,18 +2,13 @@ use crate::{
entities::doc::{CreateDocParams, Doc, DocDelta, QueryDocParams},
errors::{internal_error, DocError},
module::DocumentUser,
services::{
cache::DocCache,
doc::{edit_doc_context::EditDocContext, rev_manager::RevisionManager},
server::Server,
ws::WsDocumentManager,
},
sql_tables::doc::{DocTable, DocTableSql, OpTableSql},
services::{cache::DocCache, doc::edit_doc_context::EditDocContext, server::Server, ws::WsDocumentManager},
sql_tables::doc::{DocTable, DocTableSql},
};
use bytes::Bytes;
use flowy_database::{ConnectionPool, SqliteConnection};
use flowy_infra::future::{wrap_future, FnFuture};
use flowy_ot::core::Delta;
use parking_lot::RwLock;
use std::sync::Arc;
use tokio::time::{interval, Duration};

View File

@ -24,8 +24,8 @@ use std::{convert::TryFrom, sync::Arc};
pub type DocId = String;
pub(crate) struct EditDocContext {
pub(crate) doc_id: DocId,
pub struct EditDocContext {
pub doc_id: DocId,
document: Arc<RwLock<Document>>,
rev_manager: Arc<RevisionManager>,
pool: Arc<ConnectionPool>,
@ -49,7 +49,7 @@ impl EditDocContext {
Ok(edit_context)
}
pub(crate) fn doc(&self) -> Doc {
pub fn doc(&self) -> Doc {
Doc {
id: self.doc_id.clone(),
data: self.document.read().to_json(),

View File

@ -1,15 +1,12 @@
use crate::{
entities::doc::{RevType, Revision, RevisionRange},
errors::{internal_error, DocError},
services::{
util::RevIdCounter,
ws::{WsDocumentHandler, WsDocumentSender},
},
sql_tables::{OpTableSql, RevChangeset, RevState, RevTable},
services::{util::RevIdCounter, ws::WsDocumentSender},
sql_tables::{OpTableSql, RevChangeset, RevState},
};
use dashmap::{DashMap, DashSet};
use dashmap::DashSet;
use flowy_database::ConnectionPool;
use parking_lot::{lock_api::RwLockWriteGuard, RawRwLock, RwLock};
use parking_lot::RwLock;
use std::{
collections::{HashMap, VecDeque},
sync::Arc,

View File

@ -1,15 +1,12 @@
use crate::{
entities::doc::{RevType, Revision},
entities::doc::Revision,
errors::DocError,
sql_tables::{doc::RevTable, RevChangeset, RevState, RevTableType},
};
use diesel::{insert_into, select, update};
use diesel::{insert_into, update};
use flowy_database::{
prelude::*,
schema::{
rev_table,
rev_table::{columns::*, dsl, dsl::doc_id},
},
schema::rev_table::{columns::*, dsl, dsl::doc_id},
SqliteConnection,
};

View File

@ -1,8 +0,0 @@
[package]
name = "flowy-editor"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,7 +0,0 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View File

@ -4,6 +4,43 @@ pub const HOST: &'static str = "localhost:8000";
pub const SCHEMA: &'static str = "http://";
pub const HEADER_TOKEN: &'static str = "token";
#[derive(Debug, Clone)]
pub struct ServerConfig {
http_schema: String,
host: String,
ws_schema: String,
}
impl ServerConfig {
pub fn new(host: &str, http_schema: &str, ws_schema: &str) -> Self {
Self {
http_schema: http_schema.to_owned(),
host: host.to_owned(),
ws_schema: ws_schema.to_owned(),
}
}
fn scheme(&self) -> String { format!("{}://", self.http_schema) }
pub fn sign_up_url(&self) -> String { format!("{}{}/api/register", self.scheme(), self.host) }
pub fn sign_in_url(&self) -> String { format!("{}{}/api/auth", self.scheme(), self.host) }
pub fn sign_out_url(&self) -> String { format!("{}{}/api/auth", self.scheme(), self.host) }
pub fn user_profile_url(&self) -> String { format!("{}{}/api/user", self.scheme(), self.host) }
pub fn workspace_url(&self) -> String { format!("{}{}/api/workspace", self.scheme(), self.host) }
pub fn app_url(&self) -> String { format!("{}{}/api/app", self.scheme(), self.host) }
pub fn view_url(&self) -> String { format!("{}{}/api/view", self.scheme(), self.host) }
pub fn doc_url(&self) -> String { format!("{}{}/api/doc", self.scheme(), self.host) }
pub fn ws_addr(&self) -> String { format!("{}://{}/ws", self.ws_schema, self.host) }
}
lazy_static! {
pub static ref SIGN_UP_URL: String = format!("{}/{}/api/register", SCHEMA, HOST);
pub static ref SIGN_IN_URL: String = format!("{}/{}/api/auth", SCHEMA, HOST);
@ -16,5 +53,6 @@ lazy_static! {
pub static ref VIEW_URL: String = format!("{}/{}/api/view", SCHEMA, HOST);
pub static ref DOC_URL: String = format!("{}/{}/api/doc", SCHEMA, HOST);
//
pub static ref WS_ADDR: String = format!("ws://{}/ws", HOST);
}

View File

@ -14,6 +14,7 @@ flowy-workspace = { path = "../flowy-workspace" }
flowy-database = { path = "../flowy-database" }
flowy-document = { path = "../flowy-document" }
flowy-ws = { path = "../flowy-ws" }
flowy-net = { path = "../flowy-net" }
tracing = { version = "0.1" }
log = "0.4.14"
futures-core = { version = "0.3", default-features = false }

View File

@ -3,6 +3,9 @@ mod deps_resolve;
pub mod module;
use flowy_dispatch::prelude::*;
use flowy_document::prelude::FlowyDocument;
use flowy_net::config::ServerConfig;
use flowy_user::services::user::{UserSession, UserSessionBuilder};
use module::build_modules;
pub use module::*;
use std::sync::{
@ -16,13 +19,16 @@ static INIT_LOG: AtomicBool = AtomicBool::new(false);
pub struct FlowySDKConfig {
root: String,
log_filter: String,
server_config: ServerConfig,
}
impl FlowySDKConfig {
pub fn new(root: &str) -> Self {
pub fn new(root: &str, host: &str, http_schema: &str, ws_schema: &str) -> Self {
let server_config = ServerConfig::new(host, http_schema, ws_schema);
FlowySDKConfig {
root: root.to_owned(),
log_filter: crate_log_filter(None),
server_config,
}
}
@ -49,7 +55,9 @@ fn crate_log_filter(level: Option<String>) -> String {
#[derive(Clone)]
pub struct FlowySDK {
config: FlowySDKConfig,
dispatch: Arc<EventDispatch>,
pub user_session: Arc<UserSession>,
pub flowy_document: Arc<FlowyDocument>,
pub dispatch: Arc<EventDispatch>,
}
impl FlowySDK {
@ -58,9 +66,21 @@ impl FlowySDK {
init_kv(&config.root);
tracing::debug!("🔥 {:?}", config);
let dispatch = Arc::new(init_dispatch(&config.root));
let user_session = Arc::new(
UserSessionBuilder::new()
.root_dir(&config.root, &config.server_config)
.build(),
);
let flowy_document = build_document_module(user_session.clone());
let modules = build_modules(&config.server_config, user_session.clone(), flowy_document.clone());
let dispatch = Arc::new(EventDispatch::construct(|| modules));
Self { config, dispatch }
Self {
config,
user_session,
flowy_document,
dispatch,
}
}
pub fn dispatch(&self) -> Arc<EventDispatch> { self.dispatch.clone() }
@ -83,9 +103,3 @@ fn init_log(config: &FlowySDKConfig) {
.build();
}
}
fn init_dispatch(root: &str) -> EventDispatch {
let config = ModuleConfig { root: root.to_owned() };
let dispatch = EventDispatch::construct(|| build_modules(config));
dispatch
}

View File

@ -1,31 +1,34 @@
use flowy_dispatch::prelude::Module;
use crate::deps_resolve::{DocumentDepsResolver, WorkspaceDepsResolver};
use flowy_dispatch::prelude::Module;
use flowy_document::module::FlowyDocument;
use flowy_user::services::user::{UserSession, UserSessionBuilder};
use flowy_net::config::ServerConfig;
use flowy_user::services::user::UserSession;
use std::sync::Arc;
pub struct ModuleConfig {
pub root: String,
}
pub fn build_modules(config: ModuleConfig) -> Vec<Module> {
let user_session = Arc::new(UserSessionBuilder::new().root_dir(&config.root).build());
vec![build_user_module(user_session.clone()), build_workspace_module(user_session)]
pub fn build_modules(
server_config: &ServerConfig,
user_session: Arc<UserSession>,
flowy_document: Arc<FlowyDocument>,
) -> Vec<Module> {
vec![
build_user_module(user_session.clone()),
build_workspace_module(&server_config, user_session, flowy_document),
]
}
fn build_user_module(user_session: Arc<UserSession>) -> Module { flowy_user::module::create(user_session.clone()) }
fn build_workspace_module(user_session: Arc<UserSession>) -> Module {
fn build_workspace_module(
server_config: &ServerConfig,
user_session: Arc<UserSession>,
flowy_document: Arc<FlowyDocument>,
) -> Module {
let workspace_deps = WorkspaceDepsResolver::new(user_session.clone());
let (user, database) = workspace_deps.split_into();
let document = build_document_module(user_session.clone());
flowy_workspace::module::create(user, database, document)
flowy_workspace::module::create(user, database, flowy_document, server_config)
}
fn build_document_module(user_session: Arc<UserSession>) -> Arc<FlowyDocument> {
pub fn build_document_module(user_session: Arc<UserSession>) -> Arc<FlowyDocument> {
let document_deps = DocumentDepsResolver::new(user_session.clone());
let (user, ws_manager) = document_deps.split_into();
let document = Arc::new(FlowyDocument::new(user, ws_manager));

View File

@ -21,7 +21,12 @@ pub struct FlowyEnv {
impl FlowyEnv {
pub fn setup() -> Self {
let sdk = init_test_sdk();
let host = "localhost";
let http_schema = "http";
let ws_schema = "ws";
let config = FlowySDKConfig::new(&root_dir(), host, http_schema, ws_schema).log_filter("debug");
let sdk = FlowySDK::new(config);
let result = sign_up(sdk.dispatch());
let env = Self {
sdk,
@ -35,6 +40,10 @@ impl FlowyEnv {
}
pub fn init_test_sdk() -> FlowyTestSDK {
let config = FlowySDKConfig::new(&root_dir()).log_filter("debug");
let host = "localhost";
let http_schema = "http";
let ws_schema = "ws";
let config = FlowySDKConfig::new(&root_dir(), host, http_schema, ws_schema).log_filter("debug");
FlowySDK::new(config)
}

View File

@ -2,7 +2,6 @@ use bytes::Bytes;
use derive_more::Display;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_dispatch::prelude::{EventResponse, ResponseBuilder};
use std::{convert::TryInto, fmt::Debug};
#[derive(Debug, Default, Clone, ProtoBuf)]
@ -27,9 +26,19 @@ macro_rules! static_user_error {
}
impl UserError {
pub(crate) fn new(code: ErrorCode, msg: &str) -> Self { Self { code, msg: msg.to_owned() } }
pub(crate) fn new(code: ErrorCode, msg: &str) -> Self {
Self {
code,
msg: msg.to_owned(),
}
}
pub(crate) fn code(code: ErrorCode) -> Self { Self { code, msg: "".to_owned() } }
pub(crate) fn code(code: ErrorCode) -> Self {
Self {
code,
msg: "".to_owned(),
}
}
pub fn context<T: Debug>(mut self, error: T) -> Self {
self.msg = format!("{:?}", error);

View File

@ -10,6 +10,7 @@ use crate::{
errors::UserError,
};
use flowy_infra::future::ResultFuture;
use flowy_net::config::ServerConfig;
pub trait UserServerAPI {
fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError>;
@ -17,12 +18,14 @@ pub trait UserServerAPI {
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 ws_addr(&self) -> String;
}
pub(crate) fn construct_user_server() -> Arc<dyn UserServerAPI + Send + Sync> {
pub(crate) fn construct_user_server(config: &ServerConfig) -> Arc<dyn UserServerAPI + Send + Sync> {
if cfg!(feature = "http_server") {
Arc::new(UserServer {})
Arc::new(UserServer::new(config.clone()))
} else {
Arc::new(UserServerMock {})
// Arc::new(UserServerMock {})
Arc::new(UserServer::new(config.clone()))
}
}

View File

@ -10,37 +10,46 @@ use flowy_net::{
request::{HttpRequestBuilder, ResponseMiddleware},
};
pub struct UserServer {}
pub struct UserServer {
config: ServerConfig,
}
impl UserServer {
pub fn new() -> Self { Self {} }
pub fn new(config: ServerConfig) -> Self { Self { config } }
}
impl UserServerAPI for UserServer {
fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError> {
ResultFuture::new(async move { user_sign_up_request(params, SIGN_UP_URL.as_ref()).await })
let url = self.config.sign_up_url();
ResultFuture::new(async move { user_sign_up_request(params, &url).await })
}
fn sign_in(&self, params: SignInParams) -> ResultFuture<SignInResponse, UserError> {
ResultFuture::new(async move { user_sign_in_request(params, SIGN_IN_URL.as_ref()).await })
let url = self.config.sign_in_url();
ResultFuture::new(async move { user_sign_in_request(params, &url).await })
}
fn sign_out(&self, token: &str) -> ResultFuture<(), UserError> {
let token = token.to_owned();
let url = self.config.sign_out_url();
ResultFuture::new(async move {
let _ = user_sign_out_request(&token, SIGN_OUT_URL.as_ref()).await;
let _ = user_sign_out_request(&token, &url).await;
Ok(())
})
}
fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError> {
let token = token.to_owned();
ResultFuture::new(async move { update_user_profile_request(&token, params, USER_PROFILE_URL.as_ref()).await })
let url = self.config.user_profile_url();
ResultFuture::new(async move { update_user_profile_request(&token, params, &url).await })
}
fn get_user(&self, token: &str) -> ResultFuture<UserProfile, UserError> {
let token = token.to_owned();
ResultFuture::new(async move { get_user_profile_request(&token, USER_PROFILE_URL.as_ref()).await })
let url = self.config.user_profile_url();
ResultFuture::new(async move { get_user_profile_request(&token, &url).await })
}
fn ws_addr(&self) -> String { self.config.ws_addr() }
}
use crate::{errors::ErrorCode, observable::*};
@ -72,17 +81,29 @@ impl ResponseMiddleware for Middleware {
pub(crate) fn request_builder() -> HttpRequestBuilder { HttpRequestBuilder::new().middleware(MIDDLEWARE.clone()) }
pub async fn user_sign_up_request(params: SignUpParams, url: &str) -> Result<SignUpResponse, UserError> {
let response = request_builder().post(&url.to_owned()).protobuf(params)?.response().await?;
let response = request_builder()
.post(&url.to_owned())
.protobuf(params)?
.response()
.await?;
Ok(response)
}
pub async fn user_sign_in_request(params: SignInParams, url: &str) -> Result<SignInResponse, UserError> {
let response = request_builder().post(&url.to_owned()).protobuf(params)?.response().await?;
let response = request_builder()
.post(&url.to_owned())
.protobuf(params)?
.response()
.await?;
Ok(response)
}
pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), UserError> {
let _ = request_builder().delete(&url.to_owned()).header(HEADER_TOKEN, token).send().await?;
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.send()
.await?;
Ok(())
}

View File

@ -37,9 +37,13 @@ impl UserServerAPI for UserServerMock {
fn sign_out(&self, _token: &str) -> ResultFuture<(), UserError> { ResultFuture::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) -> ResultFuture<(), UserError> {
ResultFuture::new(async { Ok(()) })
}
fn get_user(&self, _token: &str) -> ResultFuture<UserProfile, UserError> {
ResultFuture::new(async { Err(UserError::internal().context("mock data, ignore this error")) })
}
fn ws_addr(&self) -> String { "ws://localhost:8000/ws/".to_owned() }
}

View File

@ -1,4 +1,5 @@
use crate::services::user::{SessionStatusCallback, UserSession, UserSessionConfig};
use flowy_net::config::ServerConfig;
use std::sync::Arc;
pub struct UserSessionBuilder {
@ -14,8 +15,8 @@ impl UserSessionBuilder {
}
}
pub fn root_dir(mut self, dir: &str) -> Self {
self.config = Some(UserSessionConfig::new(dir));
pub fn root_dir(mut self, dir: &str, server_config: &ServerConfig) -> Self {
self.config = Some(UserSessionConfig::new(dir, server_config));
self
}

View File

@ -17,6 +17,7 @@ use flowy_database::{
UserDatabaseConnection,
};
use flowy_infra::kv::KV;
use flowy_net::config::ServerConfig;
use flowy_sqlite::ConnectionPool;
use flowy_ws::{connect::Retry, WsController, WsMessage, WsMessageHandler, WsSender};
use parking_lot::RwLock;
@ -25,12 +26,14 @@ use std::{sync::Arc, time::Duration};
pub struct UserSessionConfig {
root_dir: String,
server_config: ServerConfig,
}
impl UserSessionConfig {
pub fn new(root_dir: &str) -> Self {
pub fn new(root_dir: &str, server_config: &ServerConfig) -> Self {
Self {
root_dir: root_dir.to_owned(),
server_config: server_config.clone(),
}
}
}
@ -54,7 +57,7 @@ pub struct UserSession {
impl UserSession {
pub fn new(config: UserSessionConfig, status_callback: SessionStatusCallback) -> Self {
let db = UserDB::new(&config.root_dir);
let server = construct_user_server();
let server = construct_user_server(&config.server_config);
let ws_controller = Arc::new(RwLock::new(WsController::new()));
let user_session = Self {
database: db,
@ -120,7 +123,8 @@ impl UserSession {
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_out(&self) -> Result<(), UserError> {
let session = self.get_session()?;
let _ = diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id))).execute(&*(self.db_connection()?))?;
let _ =
diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id))).execute(&*(self.db_connection()?))?;
let _ = self.database.close_user_db(&session.user_id)?;
let _ = self.set_session(None)?;
(self.status_callback)(SessionStatus::Expired {
@ -178,7 +182,9 @@ impl UserSession {
pub fn token(&self) -> Result<String, UserError> { Ok(self.get_session()?.token) }
pub fn add_ws_handler(&self, handler: Arc<dyn WsMessageHandler>) { let _ = self.ws_controller.write().add_handler(handler); }
pub fn add_ws_handler(&self, handler: Arc<dyn WsMessageHandler>) {
let _ = self.ws_controller.write().add_handler(handler);
}
pub fn get_ws_sender(&self) -> Result<Arc<WsSender>, UserError> {
match self.ws_controller.try_read_for(Duration::from_millis(300)) {
@ -204,7 +210,9 @@ impl UserSession {
tokio::spawn(async move {
match server.get_user(&token).await {
Ok(profile) => {
notify(&token, UserObservable::UserProfileUpdated).payload(profile).send();
notify(&token, UserObservable::UserProfileUpdated)
.payload(profile)
.send();
},
Err(e) => {
notify(&token, UserObservable::UserProfileUpdated).error(e).send();
@ -245,7 +253,9 @@ impl UserSession {
async fn save_user(&self, user: UserTable) -> Result<UserTable, UserError> {
let conn = self.db_connection()?;
let _ = diesel::insert_into(user_table::table).values(user.clone()).execute(&*conn)?;
let _ = diesel::insert_into(user_table::table)
.values(user.clone())
.execute(&*conn)?;
Ok(user)
}
@ -285,7 +295,7 @@ impl UserSession {
}
fn start_ws_connection(&self, token: &str) -> Result<(), UserError> {
let addr = format!("{}/{}", flowy_net::config::WS_ADDR.as_str(), token);
let addr = format!("{}/{}", self.server.ws_addr(), token);
let ws_controller = self.ws_controller.clone();
let retry = Retry::new(&addr, move |addr| {
let _ = ws_controller.write().connect(addr.to_owned());
@ -296,7 +306,11 @@ impl UserSession {
}
}
pub async fn update_user(_server: Server, pool: Arc<ConnectionPool>, params: UpdateUserParams) -> Result<(), UserError> {
pub async fn update_user(
_server: Server,
pool: Arc<ConnectionPool>,
params: UpdateUserParams,
) -> Result<(), UserError> {
let changeset = UserTableChangeset::new(params);
let conn = pool.get()?;
diesel_update_table!(user_table, changeset, &*conn);

View File

@ -68,9 +68,9 @@ pub struct CreateViewParams {
pub data: String,
}
pub const VIEW_DEFAULT_DATA: &str = "[{\"insert\":\"\\n\"}]";
pub const DOC_DEFAULT_DATA: &str = "[{\"insert\":\"\\n\"}]";
#[allow(dead_code)]
pub fn default_delta() -> Vec<u8> { VIEW_DEFAULT_DATA.as_bytes().to_vec() }
pub fn default_delta() -> Vec<u8> { DOC_DEFAULT_DATA.as_bytes().to_vec() }
impl CreateViewParams {
pub fn new(belong_to_id: String, name: String, desc: String, view_type: ViewType, thumbnail: String) -> Self {
@ -80,7 +80,7 @@ impl CreateViewParams {
desc,
thumbnail,
view_type,
data: VIEW_DEFAULT_DATA.to_string(),
data: DOC_DEFAULT_DATA.to_string(),
}
}
}

View File

@ -8,6 +8,7 @@ use crate::{
use flowy_database::DBConnection;
use flowy_dispatch::prelude::*;
use flowy_document::module::FlowyDocument;
use flowy_net::config::ServerConfig;
use flowy_sqlite::ConnectionPool;
use std::sync::Arc;
@ -28,9 +29,19 @@ pub trait WorkspaceDatabase: Send + Sync {
}
}
pub fn create(user: Arc<dyn WorkspaceUser>, database: Arc<dyn WorkspaceDatabase>, document: Arc<FlowyDocument>) -> Module {
let server = construct_workspace_server();
let view_controller = Arc::new(ViewController::new(user.clone(), database.clone(), server.clone(), document));
pub fn create(
user: Arc<dyn WorkspaceUser>,
database: Arc<dyn WorkspaceDatabase>,
flowy_document: Arc<FlowyDocument>,
server_config: &ServerConfig,
) -> Module {
let server = construct_workspace_server(server_config);
let view_controller = Arc::new(ViewController::new(
user.clone(),
database.clone(),
server.clone(),
flowy_document,
));
let app_controller = Arc::new(AppController::new(user.clone(), database.clone(), server.clone()));
let workspace_controller = Arc::new(WorkspaceController::new(

View File

@ -22,6 +22,7 @@ use crate::{
errors::WorkspaceError,
};
use flowy_infra::future::ResultFuture;
use flowy_net::config::ServerConfig;
use std::sync::Arc;
pub(crate) type Server = Arc<dyn WorkspaceServerAPI + Send + Sync>;
@ -30,7 +31,11 @@ pub trait WorkspaceServerAPI {
// Workspace
fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError>;
fn read_workspace(&self, token: &str, params: QueryWorkspaceParams) -> ResultFuture<RepeatedWorkspace, WorkspaceError>;
fn read_workspace(
&self,
token: &str,
params: QueryWorkspaceParams,
) -> ResultFuture<RepeatedWorkspace, WorkspaceError>;
fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> ResultFuture<(), WorkspaceError>;
@ -55,9 +60,9 @@ pub trait WorkspaceServerAPI {
fn delete_app(&self, token: &str, params: DeleteAppParams) -> ResultFuture<(), WorkspaceError>;
}
pub(crate) fn construct_workspace_server() -> Arc<dyn WorkspaceServerAPI + Send + Sync> {
pub(crate) fn construct_workspace_server(config: &ServerConfig) -> Arc<dyn WorkspaceServerAPI + Send + Sync> {
if cfg!(feature = "http_server") {
Arc::new(WorkspaceServer {})
Arc::new(WorkspaceServer::new(config.clone()))
} else {
Arc::new(WorkspaceServerMock {})
}

View File

@ -17,72 +17,100 @@ use crate::{
use flowy_infra::future::ResultFuture;
use flowy_net::{config::*, request::HttpRequestBuilder};
pub struct WorkspaceServer {}
pub struct WorkspaceServer {
config: ServerConfig,
}
impl WorkspaceServer {
pub fn new(config: ServerConfig) -> WorkspaceServer { Self { config } }
}
impl WorkspaceServerAPI for WorkspaceServer {
fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { create_workspace_request(&token, params, WORKSPACE_URL.as_ref()).await })
let url = self.config.workspace_url();
ResultFuture::new(async move { create_workspace_request(&token, params, &url).await })
}
fn read_workspace(&self, token: &str, params: QueryWorkspaceParams) -> ResultFuture<RepeatedWorkspace, WorkspaceError> {
fn read_workspace(
&self,
token: &str,
params: QueryWorkspaceParams,
) -> ResultFuture<RepeatedWorkspace, WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { read_workspaces_request(&token, params, WORKSPACE_URL.as_ref()).await })
let url = self.config.workspace_url();
ResultFuture::new(async move { read_workspaces_request(&token, params, &url).await })
}
fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { update_workspace_request(&token, params, WORKSPACE_URL.as_ref()).await })
let url = self.config.workspace_url();
ResultFuture::new(async move { update_workspace_request(&token, params, &url).await })
}
fn delete_workspace(&self, token: &str, params: DeleteWorkspaceParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { delete_workspace_request(&token, params, WORKSPACE_URL.as_ref()).await })
let url = self.config.workspace_url();
ResultFuture::new(async move { delete_workspace_request(&token, params, &url).await })
}
fn create_view(&self, token: &str, params: CreateViewParams) -> ResultFuture<View, WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { create_view_request(&token, params, VIEW_URL.as_ref()).await })
let url = self.config.view_url();
ResultFuture::new(async move { create_view_request(&token, params, &url).await })
}
fn read_view(&self, token: &str, params: QueryViewParams) -> ResultFuture<Option<View>, WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { read_view_request(&token, params, VIEW_URL.as_ref()).await })
let url = self.config.view_url();
ResultFuture::new(async move { read_view_request(&token, params, &url).await })
}
fn delete_view(&self, token: &str, params: DeleteViewParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { delete_view_request(&token, params, VIEW_URL.as_ref()).await })
let url = self.config.view_url();
ResultFuture::new(async move { delete_view_request(&token, params, &url).await })
}
fn update_view(&self, token: &str, params: UpdateViewParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { update_view_request(&token, params, VIEW_URL.as_ref()).await })
let url = self.config.view_url();
ResultFuture::new(async move { update_view_request(&token, params, &url).await })
}
fn create_app(&self, token: &str, params: CreateAppParams) -> ResultFuture<App, WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { create_app_request(&token, params, APP_URL.as_ref()).await })
let url = self.config.app_url();
ResultFuture::new(async move { create_app_request(&token, params, &url).await })
}
fn read_app(&self, token: &str, params: QueryAppParams) -> ResultFuture<Option<App>, WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { read_app_request(&token, params, APP_URL.as_ref()).await })
let url = self.config.app_url();
ResultFuture::new(async move { read_app_request(&token, params, &url).await })
}
fn update_app(&self, token: &str, params: UpdateAppParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { update_app_request(&token, params, APP_URL.as_ref()).await })
let url = self.config.app_url();
ResultFuture::new(async move { update_app_request(&token, params, &url).await })
}
fn delete_app(&self, token: &str, params: DeleteAppParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
ResultFuture::new(async move { delete_app_request(&token, params, APP_URL.as_ref()).await })
let url = self.config.app_url();
ResultFuture::new(async move { delete_app_request(&token, params, &url).await })
}
}
pub(crate) fn request_builder() -> HttpRequestBuilder { HttpRequestBuilder::new().middleware(super::middleware::MIDDLEWARE.clone()) }
pub async fn create_workspace_request(token: &str, params: CreateWorkspaceParams, url: &str) -> Result<Workspace, WorkspaceError> {
pub(crate) fn request_builder() -> HttpRequestBuilder {
HttpRequestBuilder::new().middleware(super::middleware::MIDDLEWARE.clone())
}
pub async fn create_workspace_request(
token: &str,
params: CreateWorkspaceParams,
url: &str,
) -> Result<Workspace, WorkspaceError> {
let workspace = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
@ -92,7 +120,11 @@ pub async fn create_workspace_request(token: &str, params: CreateWorkspaceParams
Ok(workspace)
}
pub async fn read_workspaces_request(token: &str, params: QueryWorkspaceParams, url: &str) -> Result<RepeatedWorkspace, WorkspaceError> {
pub async fn read_workspaces_request(
token: &str,
params: QueryWorkspaceParams,
url: &str,
) -> Result<RepeatedWorkspace, WorkspaceError> {
let repeated_workspace = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
@ -103,7 +135,11 @@ pub async fn read_workspaces_request(token: &str, params: QueryWorkspaceParams,
Ok(repeated_workspace)
}
pub async fn update_workspace_request(token: &str, params: UpdateWorkspaceParams, url: &str) -> Result<(), WorkspaceError> {
pub async fn update_workspace_request(
token: &str,
params: UpdateWorkspaceParams,
url: &str,
) -> Result<(), WorkspaceError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
@ -113,7 +149,11 @@ pub async fn update_workspace_request(token: &str, params: UpdateWorkspaceParams
Ok(())
}
pub async fn delete_workspace_request(token: &str, params: DeleteWorkspaceParams, url: &str) -> Result<(), WorkspaceError> {
pub async fn delete_workspace_request(
token: &str,
params: DeleteWorkspaceParams,
url: &str,
) -> Result<(), WorkspaceError> {
let _ = request_builder()
.delete(url)
.header(HEADER_TOKEN, token)
@ -176,7 +216,11 @@ pub async fn create_view_request(token: &str, params: CreateViewParams, url: &st
Ok(view)
}
pub async fn read_view_request(token: &str, params: QueryViewParams, url: &str) -> Result<Option<View>, WorkspaceError> {
pub async fn read_view_request(
token: &str,
params: QueryViewParams,
url: &str,
) -> Result<Option<View>, WorkspaceError> {
let view = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)

View File

@ -50,7 +50,8 @@ impl ViewController {
// TODO: rollback anything created before if failed?
conn.immediate_transaction::<_, WorkspaceError, _>(|| {
let _ = self.save_view(view.clone(), conn)?;
self.document.create(CreateDocParams::new(&view.id, params.data), conn)?;
self.document
.create(CreateDocParams::new(&view.id, params.data), conn)?;
let repeated_view = self.read_local_views_belong_to(&view.belong_to_id, conn)?;
notify(&view.belong_to_id, WorkspaceObservable::AppCreateView)
@ -78,8 +79,8 @@ impl ViewController {
#[tracing::instrument(level = "debug", skip(self), err)]
pub(crate) async fn open_view(&self, params: QueryDocParams) -> Result<Doc, WorkspaceError> {
let doc = self.document.open(params, self.database.db_pool()?).await?;
Ok(doc)
let edit_context = self.document.open(params, self.database.db_pool()?).await?;
Ok(edit_context.doc())
}
pub(crate) async fn delete_view(&self, params: DeleteViewParams) -> Result<(), WorkspaceError> {
@ -191,7 +192,11 @@ impl ViewController {
}
// belong_to_id will be the app_id or view_id.
fn read_local_views_belong_to(&self, belong_to_id: &str, conn: &SqliteConnection) -> Result<RepeatedView, WorkspaceError> {
fn read_local_views_belong_to(
&self,
belong_to_id: &str,
conn: &SqliteConnection,
) -> Result<RepeatedView, WorkspaceError> {
let views = self
.sql
.read_views_belong_to(belong_to_id, conn)?

View File

@ -103,7 +103,7 @@ impl WsController {
pub fn get_sender(&self) -> Result<Arc<WsSender>, WsError> {
match &self.sender {
None => Err(WsError::internal().context("WsSender is not initialized")),
None => Err(WsError::internal().context("WsSender is not initialized, should call connect first")),
Some(sender) => Ok(sender.clone()),
}
}
@ -112,7 +112,10 @@ impl WsController {
log::debug!("🐴 ws connect: {}", &addr);
let (connection, handlers) = self.make_connect(addr.clone());
let state_notify = self.state_notify.clone();
let sender = self.sender.clone().expect("Sender should be not empty after calling make_connect");
let sender = self
.sender
.clone()
.expect("Sender should be not empty after calling make_connect");
Ok(tokio::spawn(async move {
match connection.await {
Ok(stream) => {
@ -158,7 +161,10 @@ impl WsController {
let handlers = self.handlers.clone();
self.sender = Some(Arc::new(WsSender { ws_tx }));
self.addr = Some(addr.clone());
(WsConnectionFuture::new(msg_tx, ws_rx, addr), WsHandlerFuture::new(handlers, msg_rx))
(
WsConnectionFuture::new(msg_tx, ws_rx, addr),
WsHandlerFuture::new(handlers, msg_rx),
)
}
}
@ -170,7 +176,9 @@ pub struct WsHandlerFuture {
}
impl WsHandlerFuture {
fn new(handlers: HashMap<WsModule, Arc<dyn WsMessageHandler>>, msg_rx: MsgReceiver) -> Self { Self { msg_rx, handlers } }
fn new(handlers: HashMap<WsModule, Arc<dyn WsMessageHandler>>, msg_rx: MsgReceiver) -> Self {
Self { msg_rx, handlers }
}
fn handler_ws_message(&self, message: Message) {
match message {
@ -215,7 +223,10 @@ pub struct WsSender {
impl WsSender {
pub fn send_msg<T: Into<WsMessage>>(&self, msg: T) -> Result<(), WsError> {
let msg = msg.into();
let _ = self.ws_tx.unbounded_send(msg.into()).map_err(|e| WsError::internal().context(e))?;
let _ = self
.ws_tx
.unbounded_send(msg.into())
.map_err(|e| WsError::internal().context(e))?;
Ok(())
}
@ -241,7 +252,10 @@ impl WsSender {
reason: reason.to_owned().into(),
};
let msg = Message::Close(Some(frame));
let _ = self.ws_tx.unbounded_send(msg).map_err(|e| WsError::internal().context(e))?;
let _ = self
.ws_tx
.unbounded_send(msg)
.map_err(|e| WsError::internal().context(e))?;
Ok(())
}
}