mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-10-31 10:03:18 +00:00 
			
		
		
		
	add server with flowy_document test
This commit is contained in:
		
							parent
							
								
									11bc536df8
								
							
						
					
					
						commit
						01c9620e03
					
				| @ -96,3 +96,7 @@ linkify = "0.5.0" | |||||||
| flowy-user = { path = "../rust-lib/flowy-user" } | flowy-user = { path = "../rust-lib/flowy-user" } | ||||||
| flowy-workspace = { path = "../rust-lib/flowy-workspace" } | flowy-workspace = { path = "../rust-lib/flowy-workspace" } | ||||||
| flowy-ws = { path = "../rust-lib/flowy-ws" } | 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" } | ||||||
| @ -22,7 +22,6 @@ use parking_lot::RwLock; | |||||||
| use protobuf::Message; | use protobuf::Message; | ||||||
| use sqlx::PgPool; | use sqlx::PgPool; | ||||||
| use std::{ | use std::{ | ||||||
|     cmp::min, |  | ||||||
|     convert::TryInto, |     convert::TryInto, | ||||||
|     sync::{ |     sync::{ | ||||||
|         atomic::{AtomicI64, Ordering::SeqCst}, |         atomic::{AtomicI64, Ordering::SeqCst}, | ||||||
|  | |||||||
| @ -6,8 +6,7 @@ use crate::service::{ | |||||||
| }; | }; | ||||||
| use actix_web::web::Data; | use actix_web::web::Data; | ||||||
| 
 | 
 | ||||||
| use crate::service::ws::WsUser; | use flowy_document::protobuf::{QueryDocParams, Revision, WsDataType, WsDocumentData}; | ||||||
| use flowy_document::protobuf::{QueryDocParams, Revision, RevisionRange, WsDataType, WsDocumentData}; |  | ||||||
| use flowy_net::errors::ServerError; | use flowy_net::errors::ServerError; | ||||||
| use parking_lot::{RwLock, RwLockUpgradableReadGuard}; | use parking_lot::{RwLock, RwLockUpgradableReadGuard}; | ||||||
| use protobuf::Message; | use protobuf::Message; | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ impl Builder { | |||||||
|         let env_filter = EnvFilter::new(self.env_filter); |         let env_filter = EnvFilter::new(self.env_filter); | ||||||
|         let subscriber = tracing_subscriber::fmt() |         let subscriber = tracing_subscriber::fmt() | ||||||
|             .with_target(true) |             .with_target(true) | ||||||
|             .with_max_level(tracing::Level::TRACE) |             .with_max_level(tracing::Level::DEBUG) | ||||||
|             .with_writer(std::io::stderr) |             .with_writer(std::io::stderr) | ||||||
|             .with_thread_ids(false) |             .with_thread_ids(false) | ||||||
|             .compact() |             .compact() | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ use crate::{ | |||||||
| }; | }; | ||||||
| use flowy_net::errors::ServerError; | use flowy_net::errors::ServerError; | ||||||
| use flowy_workspace::{ | use flowy_workspace::{ | ||||||
|     entities::view::VIEW_DEFAULT_DATA, |     entities::view::DOC_DEFAULT_DATA, | ||||||
|     protobuf::{App, CreateViewParams, View, ViewType, Workspace}, |     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(), |         desc: "View created by AppFlowy".to_string(), | ||||||
|         thumbnail: "123.png".to_string(), |         thumbnail: "123.png".to_string(), | ||||||
|         view_type: ViewType::Doc, |         view_type: ViewType::Doc, | ||||||
|         data: VIEW_DEFAULT_DATA.to_string(), |         data: DOC_DEFAULT_DATA.to_string(), | ||||||
|         unknown_fields: Default::default(), |         unknown_fields: Default::default(), | ||||||
|         cached_size: Default::default(), |         cached_size: Default::default(), | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ pub async fn establish_ws_connection( | |||||||
|     server: Data<Addr<WsServer>>, |     server: Data<Addr<WsServer>>, | ||||||
|     biz_handlers: Data<WsBizHandlers>, |     biz_handlers: Data<WsBizHandlers>, | ||||||
| ) -> Result<HttpResponse, Error> { | ) -> Result<HttpResponse, Error> { | ||||||
|  |     log::debug!("establish_ws_connection"); | ||||||
|     match LoggedUser::from_token(token.clone()) { |     match LoggedUser::from_token(token.clone()) { | ||||||
|         Ok(user) => { |         Ok(user) => { | ||||||
|             let ws_user = WsUser::new(user.clone()); |             let ws_user = WsUser::new(user.clone()); | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ use crate::{ | |||||||
|     service::{ |     service::{ | ||||||
|         user::LoggedUser, |         user::LoggedUser, | ||||||
|         ws::{ |         ws::{ | ||||||
|             entities::{Connect, Disconnect, SessionId, Socket}, |             entities::{Connect, Disconnect, Socket}, | ||||||
|             WsBizHandler, |             WsBizHandler, | ||||||
|             WsBizHandlers, |             WsBizHandlers, | ||||||
|             WsMessageAdaptor, |             WsMessageAdaptor, | ||||||
|  | |||||||
| @ -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}; | use flowy_user::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams}; | ||||||
| 
 | 
 | ||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| async fn user_register() { | 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; |     let response = register_user(&app, "annie@appflowy.io", "HelloWorld123!").await; | ||||||
|     log::info!("{:?}", response); |     log::info!("{:?}", response); | ||||||
| } | } | ||||||
| @ -11,7 +11,7 @@ async fn user_register() { | |||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| #[should_panic] | #[should_panic] | ||||||
| async fn user_sign_in_with_invalid_password() { | 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 email = "annie@appflowy.io"; | ||||||
|     let password = "123"; |     let password = "123"; | ||||||
|     let _ = register_user(&app, email, password).await; |     let _ = register_user(&app, email, password).await; | ||||||
| @ -20,7 +20,7 @@ async fn user_sign_in_with_invalid_password() { | |||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| #[should_panic] | #[should_panic] | ||||||
| async fn user_sign_in_with_invalid_email() { | async fn user_sign_in_with_invalid_email() { | ||||||
|     let app = spawn_server().await; |     let app = spawn_user_server().await; | ||||||
|     let email = "annie@gmail@"; |     let email = "annie@gmail@"; | ||||||
|     let password = "HelloWorld123!"; |     let password = "HelloWorld123!"; | ||||||
|     let _ = register_user(&app, email, password).await; |     let _ = register_user(&app, email, password).await; | ||||||
| @ -28,7 +28,7 @@ async fn user_sign_in_with_invalid_email() { | |||||||
| 
 | 
 | ||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| async fn user_sign_in() { | async fn user_sign_in() { | ||||||
|     let app = spawn_server().await; |     let app = spawn_user_server().await; | ||||||
|     let email = "annie@appflowy.io"; |     let email = "annie@appflowy.io"; | ||||||
|     let password = "HelloWorld123!"; |     let password = "HelloWorld123!"; | ||||||
|     let _ = register_user(&app, email, password).await; |     let _ = register_user(&app, email, password).await; | ||||||
| @ -42,7 +42,7 @@ async fn user_sign_in() { | |||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| #[should_panic] | #[should_panic] | ||||||
| async fn user_sign_out() { | async fn user_sign_out() { | ||||||
|     let server = TestServer::new().await; |     let server = TestUserServer::new().await; | ||||||
|     server.sign_out().await; |     server.sign_out().await; | ||||||
| 
 | 
 | ||||||
|     // user_detail will be empty because use was sign out.
 |     // user_detail will be empty because use was sign out.
 | ||||||
| @ -51,13 +51,13 @@ async fn user_sign_out() { | |||||||
| 
 | 
 | ||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| async fn user_get_detail() { | async fn user_get_detail() { | ||||||
|     let server = TestServer::new().await; |     let server = TestUserServer::new().await; | ||||||
|     log::info!("{:?}", server.get_user_profile().await); |     log::info!("{:?}", server.get_user_profile().await); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| async fn user_update_password() { | async fn user_update_password() { | ||||||
|     let mut server = spawn_server().await; |     let mut server = spawn_user_server().await; | ||||||
|     let email = "annie@appflowy.io"; |     let email = "annie@appflowy.io"; | ||||||
|     let password = "HelloWorld123!"; |     let password = "HelloWorld123!"; | ||||||
|     let sign_up_resp = register_user(&server, email, password).await; |     let sign_up_resp = register_user(&server, email, password).await; | ||||||
| @ -82,7 +82,7 @@ async fn user_update_password() { | |||||||
| 
 | 
 | ||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| async fn user_update_name() { | async fn user_update_name() { | ||||||
|     let server = TestServer::new().await; |     let server = TestUserServer::new().await; | ||||||
| 
 | 
 | ||||||
|     let name = "tom".to_string(); |     let name = "tom".to_string(); | ||||||
|     let params = UpdateUserParams::new(&server.user_id()).name(&name); |     let params = UpdateUserParams::new(&server.user_id()).name(&name); | ||||||
| @ -94,7 +94,7 @@ async fn user_update_name() { | |||||||
| 
 | 
 | ||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| async fn user_update_email() { | async fn user_update_email() { | ||||||
|     let server = TestServer::new().await; |     let server = TestUserServer::new().await; | ||||||
|     let email = "123@gmail.com".to_string(); |     let email = "123@gmail.com".to_string(); | ||||||
|     let params = UpdateUserParams::new(server.user_id()).email(&email); |     let params = UpdateUserParams::new(server.user_id()).email(&email); | ||||||
|     server.update_user_profile(params).await.unwrap(); |     server.update_user_profile(params).await.unwrap(); | ||||||
| @ -104,14 +104,14 @@ async fn user_update_email() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[allow(dead_code)] | #[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 email = "annie@appflowy.io"; | ||||||
|     let password = "HelloWorld123!"; |     let password = "HelloWorld123!"; | ||||||
|     let response = register_user(server, email, password).await; |     let response = register_user(server, email, password).await; | ||||||
|     response |     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 { |     let params = SignUpParams { | ||||||
|         email: email.to_string(), |         email: email.to_string(), | ||||||
|         name: "annie".to_string(), |         name: "annie".to_string(), | ||||||
|  | |||||||
| @ -2,12 +2,7 @@ use crate::helper::*; | |||||||
| use flowy_workspace::entities::{ | use flowy_workspace::entities::{ | ||||||
|     app::{DeleteAppParams, QueryAppParams, UpdateAppParams}, |     app::{DeleteAppParams, QueryAppParams, UpdateAppParams}, | ||||||
|     view::{DeleteViewParams, QueryViewParams, UpdateViewParams}, |     view::{DeleteViewParams, QueryViewParams, UpdateViewParams}, | ||||||
|     workspace::{ |     workspace::{CreateWorkspaceParams, DeleteWorkspaceParams, QueryWorkspaceParams, UpdateWorkspaceParams}, | ||||||
|         CreateWorkspaceParams, |  | ||||||
|         DeleteWorkspaceParams, |  | ||||||
|         QueryWorkspaceParams, |  | ||||||
|         UpdateWorkspaceParams, |  | ||||||
|     }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| @ -173,7 +168,7 @@ async fn view_delete() { | |||||||
| 
 | 
 | ||||||
| #[actix_rt::test] | #[actix_rt::test] | ||||||
| async fn workspace_list_read() { | 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; |     let token = server.register_user().await.token; | ||||||
|     server.user_token = Some(token); |     server.user_token = Some(token); | ||||||
|     for i in 0..3 { |     for i in 0..3 { | ||||||
|  | |||||||
							
								
								
									
										97
									
								
								backend/tests/document/helper.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								backend/tests/document/helper.rs
									
									
									
									
									
										Normal 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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								backend/tests/document/ws.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								backend/tests/document/ws.rs
									
									
									
									
									
										Normal 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; | ||||||
|  | } | ||||||
| @ -12,7 +12,7 @@ use flowy_workspace::prelude::{server::*, *}; | |||||||
| use sqlx::{Connection, Executor, PgConnection, PgPool}; | use sqlx::{Connection, Executor, PgConnection, PgPool}; | ||||||
| use uuid::Uuid; | use uuid::Uuid; | ||||||
| 
 | 
 | ||||||
| pub struct TestServer { | pub struct TestUserServer { | ||||||
|     pub host: String, |     pub host: String, | ||||||
|     pub port: u16, |     pub port: u16, | ||||||
|     pub pg_pool: PgPool, |     pub pg_pool: PgPool, | ||||||
| @ -20,9 +20,9 @@ pub struct TestServer { | |||||||
|     pub user_id: Option<String>, |     pub user_id: Option<String>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl TestServer { | impl TestUserServer { | ||||||
|     pub async fn new() -> Self { |     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; |         let response = server.register_user().await; | ||||||
|         server.user_token = Some(response.token); |         server.user_token = Some(response.token); | ||||||
|         server.user_id = Some(response.user_id); |         server.user_id = Some(response.user_id); | ||||||
| @ -36,28 +36,16 @@ impl TestServer { | |||||||
| 
 | 
 | ||||||
|     pub async fn sign_out(&self) { |     pub async fn sign_out(&self) { | ||||||
|         let url = format!("{}/api/auth", self.http_addr()); |         let url = format!("{}/api/auth", self.http_addr()); | ||||||
|         let _ = user_sign_out_request(self.user_token(), &url) |         let _ = user_sign_out_request(self.user_token(), &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn user_token(&self) -> &str { |     pub fn user_token(&self) -> &str { self.user_token.as_ref().expect("must call register_user first ") } | ||||||
|         self.user_token |  | ||||||
|             .as_ref() |  | ||||||
|             .expect("must call register_user first ") |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     pub fn user_id(&self) -> &str { |     pub fn user_id(&self) -> &str { self.user_id.as_ref().expect("must call register_user first ") } | ||||||
|         self.user_id |  | ||||||
|             .as_ref() |  | ||||||
|             .expect("must call register_user first ") |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     pub async fn get_user_profile(&self) -> UserProfile { |     pub async fn get_user_profile(&self) -> UserProfile { | ||||||
|         let url = format!("{}/api/user", self.http_addr()); |         let url = format!("{}/api/user", self.http_addr()); | ||||||
|         let user_profile = get_user_profile_request(self.user_token(), &url) |         let user_profile = get_user_profile_request(self.user_token(), &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|         user_profile |         user_profile | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -68,99 +56,73 @@ impl TestServer { | |||||||
| 
 | 
 | ||||||
|     pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace { |     pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace { | ||||||
|         let url = format!("{}/api/workspace", self.http_addr()); |         let url = format!("{}/api/workspace", self.http_addr()); | ||||||
|         let workspace = create_workspace_request(self.user_token(), params, &url) |         let workspace = create_workspace_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|         workspace |         workspace | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn read_workspaces(&self, params: QueryWorkspaceParams) -> RepeatedWorkspace { |     pub async fn read_workspaces(&self, params: QueryWorkspaceParams) -> RepeatedWorkspace { | ||||||
|         let url = format!("{}/api/workspace", self.http_addr()); |         let url = format!("{}/api/workspace", self.http_addr()); | ||||||
|         let workspaces = read_workspaces_request(self.user_token(), params, &url) |         let workspaces = read_workspaces_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|         workspaces |         workspaces | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn update_workspace(&self, params: UpdateWorkspaceParams) { |     pub async fn update_workspace(&self, params: UpdateWorkspaceParams) { | ||||||
|         let url = format!("{}/api/workspace", self.http_addr()); |         let url = format!("{}/api/workspace", self.http_addr()); | ||||||
|         update_workspace_request(self.user_token(), params, &url) |         update_workspace_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn delete_workspace(&self, params: DeleteWorkspaceParams) { |     pub async fn delete_workspace(&self, params: DeleteWorkspaceParams) { | ||||||
|         let url = format!("{}/api/workspace", self.http_addr()); |         let url = format!("{}/api/workspace", self.http_addr()); | ||||||
|         delete_workspace_request(self.user_token(), params, &url) |         delete_workspace_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn create_app(&self, params: CreateAppParams) -> App { |     pub async fn create_app(&self, params: CreateAppParams) -> App { | ||||||
|         let url = format!("{}/api/app", self.http_addr()); |         let url = format!("{}/api/app", self.http_addr()); | ||||||
|         let app = create_app_request(self.user_token(), params, &url) |         let app = create_app_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|         app |         app | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn read_app(&self, params: QueryAppParams) -> Option<App> { |     pub async fn read_app(&self, params: QueryAppParams) -> Option<App> { | ||||||
|         let url = format!("{}/api/app", self.http_addr()); |         let url = format!("{}/api/app", self.http_addr()); | ||||||
|         let app = read_app_request(self.user_token(), params, &url) |         let app = read_app_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|         app |         app | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn update_app(&self, params: UpdateAppParams) { |     pub async fn update_app(&self, params: UpdateAppParams) { | ||||||
|         let url = format!("{}/api/app", self.http_addr()); |         let url = format!("{}/api/app", self.http_addr()); | ||||||
|         update_app_request(self.user_token(), params, &url) |         update_app_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn delete_app(&self, params: DeleteAppParams) { |     pub async fn delete_app(&self, params: DeleteAppParams) { | ||||||
|         let url = format!("{}/api/app", self.http_addr()); |         let url = format!("{}/api/app", self.http_addr()); | ||||||
|         delete_app_request(self.user_token(), params, &url) |         delete_app_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn create_view(&self, params: CreateViewParams) -> View { |     pub async fn create_view(&self, params: CreateViewParams) -> View { | ||||||
|         let url = format!("{}/api/view", self.http_addr()); |         let url = format!("{}/api/view", self.http_addr()); | ||||||
|         let view = create_view_request(self.user_token(), params, &url) |         let view = create_view_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|         view |         view | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn read_view(&self, params: QueryViewParams) -> Option<View> { |     pub async fn read_view(&self, params: QueryViewParams) -> Option<View> { | ||||||
|         let url = format!("{}/api/view", self.http_addr()); |         let url = format!("{}/api/view", self.http_addr()); | ||||||
|         let view = read_view_request(self.user_token(), params, &url) |         let view = read_view_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|         view |         view | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn update_view(&self, params: UpdateViewParams) { |     pub async fn update_view(&self, params: UpdateViewParams) { | ||||||
|         let url = format!("{}/api/view", self.http_addr()); |         let url = format!("{}/api/view", self.http_addr()); | ||||||
|         update_view_request(self.user_token(), params, &url) |         update_view_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn delete_view(&self, params: DeleteViewParams) { |     pub async fn delete_view(&self, params: DeleteViewParams) { | ||||||
|         let url = format!("{}/api/view", self.http_addr()); |         let url = format!("{}/api/view", self.http_addr()); | ||||||
|         delete_view_request(self.user_token(), params, &url) |         delete_view_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn read_doc(&self, params: QueryDocParams) -> Option<Doc> { |     pub async fn read_doc(&self, params: QueryDocParams) -> Option<Doc> { | ||||||
|         let url = format!("{}/api/doc", self.http_addr()); |         let url = format!("{}/api/doc", self.http_addr()); | ||||||
|         let doc = read_doc_request(self.user_token(), params, &url) |         let doc = read_doc_request(self.user_token(), params, &url).await.unwrap(); | ||||||
|             .await |  | ||||||
|             .unwrap(); |  | ||||||
|         doc |         doc | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -182,14 +144,32 @@ impl TestServer { | |||||||
| 
 | 
 | ||||||
|     pub fn http_addr(&self) -> String { format!("http://{}", self.host) } |     pub fn http_addr(&self) -> String { format!("http://{}", self.host) } | ||||||
| 
 | 
 | ||||||
|     pub fn ws_addr(&self) -> String { |     pub fn ws_addr(&self) -> String { format!("ws://{}/ws/{}", self.host, self.user_token.as_ref().unwrap()) } | ||||||
|         format!( | } | ||||||
|             "ws://{}/ws/{}", | 
 | ||||||
|             self.host, | impl std::convert::From<TestServer> for TestUserServer { | ||||||
|             self.user_token.as_ref().unwrap() |     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 { | pub async fn spawn_server() -> TestServer { | ||||||
|     let database_name = format!("{}", Uuid::new_v4().to_string()); |     let database_name = format!("{}", Uuid::new_v4().to_string()); | ||||||
|     let configuration = { |     let configuration = { | ||||||
| @ -217,8 +197,6 @@ pub async fn spawn_server() -> TestServer { | |||||||
|         pg_pool: get_connection_pool(&configuration.database) |         pg_pool: get_connection_pool(&configuration.database) | ||||||
|             .await |             .await | ||||||
|             .expect("Failed to connect to the database"), |             .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."); |         .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 { |     let params = CreateWorkspaceParams { | ||||||
|         name: "My first workspace".to_string(), |         name: "My first workspace".to_string(), | ||||||
|         desc: "This is 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 |     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 { |     let params = CreateAppParams { | ||||||
|         workspace_id: workspace_id.to_owned(), |         workspace_id: workspace_id.to_owned(), | ||||||
|         name: "My first app".to_string(), |         name: "My first app".to_string(), | ||||||
| @ -287,7 +265,7 @@ pub async fn create_test_app(server: &TestServer, workspace_id: &str) -> App { | |||||||
|     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 name = "My first view".to_string(); | ||||||
|     let desc = "This is my first view".to_string(); |     let desc = "This is my first view".to_string(); | ||||||
|     let thumbnail = "http://1.png".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 struct WorkspaceTest { | ||||||
|     pub server: TestServer, |     pub server: TestUserServer, | ||||||
|     pub workspace: Workspace, |     pub workspace: Workspace, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl WorkspaceTest { | impl WorkspaceTest { | ||||||
|     pub async fn new() -> Self { |     pub async fn new() -> Self { | ||||||
|         let server = TestServer::new().await; |         let server = TestUserServer::new().await; | ||||||
|         let workspace = create_test_workspace(&server).await; |         let workspace = create_test_workspace(&server).await; | ||||||
|         Self { server, workspace } |         Self { server, workspace } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn create_app(&self) -> App { |     pub async fn create_app(&self) -> App { create_test_app(&self.server, &self.workspace.id).await } | ||||||
|         create_test_app(&self.server, &self.workspace.id).await |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct AppTest { | pub struct AppTest { | ||||||
|     pub server: TestServer, |     pub server: TestUserServer, | ||||||
|     pub workspace: Workspace, |     pub workspace: Workspace, | ||||||
|     pub app: App, |     pub app: App, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl AppTest { | impl AppTest { | ||||||
|     pub async fn new() -> Self { |     pub async fn new() -> Self { | ||||||
|         let server = TestServer::new().await; |         let server = TestUserServer::new().await; | ||||||
|         let workspace = create_test_workspace(&server).await; |         let workspace = create_test_workspace(&server).await; | ||||||
|         let app = create_test_app(&server, &workspace.id).await; |         let app = create_test_app(&server, &workspace.id).await; | ||||||
|         Self { |         Self { server, workspace, app } | ||||||
|             server, |  | ||||||
|             workspace, |  | ||||||
|             app, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct ViewTest { | pub struct ViewTest { | ||||||
|     pub server: TestServer, |     pub server: TestUserServer, | ||||||
|     pub workspace: Workspace, |     pub workspace: Workspace, | ||||||
|     pub app: App, |     pub app: App, | ||||||
|     pub view: View, |     pub view: View, | ||||||
| @ -342,7 +314,7 @@ pub struct ViewTest { | |||||||
| 
 | 
 | ||||||
| impl ViewTest { | impl ViewTest { | ||||||
|     pub async fn new() -> Self { |     pub async fn new() -> Self { | ||||||
|         let server = TestServer::new().await; |         let server = TestUserServer::new().await; | ||||||
|         let workspace = create_test_workspace(&server).await; |         let workspace = create_test_workspace(&server).await; | ||||||
|         let app = create_test_app(&server, &workspace.id).await; |         let app = create_test_app(&server, &workspace.id).await; | ||||||
|         let view = create_test_view(&server, &app.id).await; |         let view = create_test_view(&server, &app.id).await; | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| mod api; | mod api; | ||||||
|  | mod document; | ||||||
| pub mod helper; | pub mod helper; | ||||||
| mod ws; |  | ||||||
|  | |||||||
| @ -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(); |  | ||||||
|                 }, |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -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 |  | ||||||
| } |  | ||||||
							
								
								
									
										0
									
								
								flowy-test/temp/flowy/flowy.log.2021-09-27
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								flowy-test/temp/flowy/flowy.log.2021-09-27
									
									
									
									
									
										Normal file
									
								
							| @ -14,7 +14,6 @@ members = [ | |||||||
|   "flowy-workspace", |   "flowy-workspace", | ||||||
|   "flowy-observable", |   "flowy-observable", | ||||||
|   "flowy-document", |   "flowy-document", | ||||||
|   "flowy-editor", |  | ||||||
|   "flowy-ot", |   "flowy-ot", | ||||||
|   "flowy-net", |   "flowy-net", | ||||||
|   "flowy-ws", |   "flowy-ws", | ||||||
|  | |||||||
| @ -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 c_str: &CStr = unsafe { CStr::from_ptr(path) }; | ||||||
|     let path: &str = c_str.to_str().unwrap(); |     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))); |     *FLOWY_SDK.write() = Some(Arc::new(FlowySDK::new(config))); | ||||||
| 
 | 
 | ||||||
|     return 1; |     return 1; | ||||||
| @ -33,7 +36,12 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 { | |||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) { | pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) { | ||||||
|     let request: ModuleRequest = FFIRequest::from_u8_pointer(input, len).into(); |     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| { |     let _ = EventDispatch::async_send_with_callback(dispatch(), request, move |resp: EventResponse| { | ||||||
|         log::trace!("[FFI]: Post data to dart through {} port", port); |         log::trace!("[FFI]: Post data to dart through {} port", port); | ||||||
|  | |||||||
| @ -8,7 +8,11 @@ use flowy_database::ConnectionPool; | |||||||
| use crate::{ | use crate::{ | ||||||
|     entities::doc::{CreateDocParams, Doc, DocDelta, QueryDocParams}, |     entities::doc::{CreateDocParams, Doc, DocDelta, QueryDocParams}, | ||||||
|     errors::DocError, |     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 { | pub trait DocumentUser: Send + Sync { | ||||||
| @ -38,9 +42,13 @@ impl FlowyDocument { | |||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> { |     pub async fn open( | ||||||
|         let open_doc = self.doc_ctrl.open(params, pool).await?; |         &self, | ||||||
|         Ok(open_doc.doc()) |         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> { |     pub async fn apply_doc_delta(&self, params: DocDelta) -> Result<Doc, DocError> { | ||||||
|  | |||||||
| @ -2,18 +2,13 @@ use crate::{ | |||||||
|     entities::doc::{CreateDocParams, Doc, DocDelta, QueryDocParams}, |     entities::doc::{CreateDocParams, Doc, DocDelta, QueryDocParams}, | ||||||
|     errors::{internal_error, DocError}, |     errors::{internal_error, DocError}, | ||||||
|     module::DocumentUser, |     module::DocumentUser, | ||||||
|     services::{ |     services::{cache::DocCache, doc::edit_doc_context::EditDocContext, server::Server, ws::WsDocumentManager}, | ||||||
|         cache::DocCache, |     sql_tables::doc::{DocTable, DocTableSql}, | ||||||
|         doc::{edit_doc_context::EditDocContext, rev_manager::RevisionManager}, |  | ||||||
|         server::Server, |  | ||||||
|         ws::WsDocumentManager, |  | ||||||
|     }, |  | ||||||
|     sql_tables::doc::{DocTable, DocTableSql, OpTableSql}, |  | ||||||
| }; | }; | ||||||
| use bytes::Bytes; | use bytes::Bytes; | ||||||
| use flowy_database::{ConnectionPool, SqliteConnection}; | use flowy_database::{ConnectionPool, SqliteConnection}; | ||||||
| use flowy_infra::future::{wrap_future, FnFuture}; | use flowy_infra::future::{wrap_future, FnFuture}; | ||||||
| use flowy_ot::core::Delta; | 
 | ||||||
| use parking_lot::RwLock; | use parking_lot::RwLock; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| use tokio::time::{interval, Duration}; | use tokio::time::{interval, Duration}; | ||||||
|  | |||||||
| @ -24,8 +24,8 @@ use std::{convert::TryFrom, sync::Arc}; | |||||||
| 
 | 
 | ||||||
| pub type DocId = String; | pub type DocId = String; | ||||||
| 
 | 
 | ||||||
| pub(crate) struct EditDocContext { | pub struct EditDocContext { | ||||||
|     pub(crate) doc_id: DocId, |     pub doc_id: DocId, | ||||||
|     document: Arc<RwLock<Document>>, |     document: Arc<RwLock<Document>>, | ||||||
|     rev_manager: Arc<RevisionManager>, |     rev_manager: Arc<RevisionManager>, | ||||||
|     pool: Arc<ConnectionPool>, |     pool: Arc<ConnectionPool>, | ||||||
| @ -49,7 +49,7 @@ impl EditDocContext { | |||||||
|         Ok(edit_context) |         Ok(edit_context) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub(crate) fn doc(&self) -> Doc { |     pub fn doc(&self) -> Doc { | ||||||
|         Doc { |         Doc { | ||||||
|             id: self.doc_id.clone(), |             id: self.doc_id.clone(), | ||||||
|             data: self.document.read().to_json(), |             data: self.document.read().to_json(), | ||||||
|  | |||||||
| @ -1,15 +1,12 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|     entities::doc::{RevType, Revision, RevisionRange}, |     entities::doc::{RevType, Revision, RevisionRange}, | ||||||
|     errors::{internal_error, DocError}, |     errors::{internal_error, DocError}, | ||||||
|     services::{ |     services::{util::RevIdCounter, ws::WsDocumentSender}, | ||||||
|         util::RevIdCounter, |     sql_tables::{OpTableSql, RevChangeset, RevState}, | ||||||
|         ws::{WsDocumentHandler, WsDocumentSender}, |  | ||||||
|     }, |  | ||||||
|     sql_tables::{OpTableSql, RevChangeset, RevState, RevTable}, |  | ||||||
| }; | }; | ||||||
| use dashmap::{DashMap, DashSet}; | use dashmap::DashSet; | ||||||
| use flowy_database::ConnectionPool; | use flowy_database::ConnectionPool; | ||||||
| use parking_lot::{lock_api::RwLockWriteGuard, RawRwLock, RwLock}; | use parking_lot::RwLock; | ||||||
| use std::{ | use std::{ | ||||||
|     collections::{HashMap, VecDeque}, |     collections::{HashMap, VecDeque}, | ||||||
|     sync::Arc, |     sync::Arc, | ||||||
|  | |||||||
| @ -1,15 +1,12 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|     entities::doc::{RevType, Revision}, |     entities::doc::Revision, | ||||||
|     errors::DocError, |     errors::DocError, | ||||||
|     sql_tables::{doc::RevTable, RevChangeset, RevState, RevTableType}, |     sql_tables::{doc::RevTable, RevChangeset, RevState, RevTableType}, | ||||||
| }; | }; | ||||||
| use diesel::{insert_into, select, update}; | use diesel::{insert_into, update}; | ||||||
| use flowy_database::{ | use flowy_database::{ | ||||||
|     prelude::*, |     prelude::*, | ||||||
|     schema::{ |     schema::rev_table::{columns::*, dsl, dsl::doc_id}, | ||||||
|         rev_table, |  | ||||||
|         rev_table::{columns::*, dsl, dsl::doc_id}, |  | ||||||
|     }, |  | ||||||
|     SqliteConnection, |     SqliteConnection, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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] |  | ||||||
| @ -1,7 +0,0 @@ | |||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     #[test] |  | ||||||
|     fn it_works() { |  | ||||||
|         assert_eq!(2 + 2, 4); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -4,6 +4,43 @@ pub const HOST: &'static str = "localhost:8000"; | |||||||
| pub const SCHEMA: &'static str = "http://"; | pub const SCHEMA: &'static str = "http://"; | ||||||
| pub const HEADER_TOKEN: &'static str = "token"; | 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! { | lazy_static! { | ||||||
|     pub static ref SIGN_UP_URL: String = format!("{}/{}/api/register", SCHEMA, HOST); |     pub static ref SIGN_UP_URL: String = format!("{}/{}/api/register", SCHEMA, HOST); | ||||||
|     pub static ref SIGN_IN_URL: String = format!("{}/{}/api/auth", 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 VIEW_URL: String = format!("{}/{}/api/view", SCHEMA, HOST); | ||||||
|     pub static ref DOC_URL: String = format!("{}/{}/api/doc", SCHEMA, HOST); |     pub static ref DOC_URL: String = format!("{}/{}/api/doc", SCHEMA, HOST); | ||||||
| 
 | 
 | ||||||
|  |     //
 | ||||||
|     pub static ref WS_ADDR: String = format!("ws://{}/ws", HOST); |     pub static ref WS_ADDR: String = format!("ws://{}/ws", HOST); | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ flowy-workspace = { path = "../flowy-workspace" } | |||||||
| flowy-database = { path = "../flowy-database" } | flowy-database = { path = "../flowy-database" } | ||||||
| flowy-document = { path = "../flowy-document" } | flowy-document = { path = "../flowy-document" } | ||||||
| flowy-ws = { path = "../flowy-ws" } | flowy-ws = { path = "../flowy-ws" } | ||||||
|  | flowy-net = { path = "../flowy-net" } | ||||||
| tracing = { version = "0.1" } | tracing = { version = "0.1" } | ||||||
| log = "0.4.14" | log = "0.4.14" | ||||||
| futures-core = { version = "0.3", default-features = false } | futures-core = { version = "0.3", default-features = false } | ||||||
|  | |||||||
| @ -3,6 +3,9 @@ mod deps_resolve; | |||||||
| pub mod module; | pub mod module; | ||||||
| 
 | 
 | ||||||
| use flowy_dispatch::prelude::*; | 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; | use module::build_modules; | ||||||
| pub use module::*; | pub use module::*; | ||||||
| use std::sync::{ | use std::sync::{ | ||||||
| @ -16,13 +19,16 @@ static INIT_LOG: AtomicBool = AtomicBool::new(false); | |||||||
| pub struct FlowySDKConfig { | pub struct FlowySDKConfig { | ||||||
|     root: String, |     root: String, | ||||||
|     log_filter: String, |     log_filter: String, | ||||||
|  |     server_config: ServerConfig, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FlowySDKConfig { | 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 { |         FlowySDKConfig { | ||||||
|             root: root.to_owned(), |             root: root.to_owned(), | ||||||
|             log_filter: crate_log_filter(None), |             log_filter: crate_log_filter(None), | ||||||
|  |             server_config, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -49,7 +55,9 @@ fn crate_log_filter(level: Option<String>) -> String { | |||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub struct FlowySDK { | pub struct FlowySDK { | ||||||
|     config: FlowySDKConfig, |     config: FlowySDKConfig, | ||||||
|     dispatch: Arc<EventDispatch>, |     pub user_session: Arc<UserSession>, | ||||||
|  |     pub flowy_document: Arc<FlowyDocument>, | ||||||
|  |     pub dispatch: Arc<EventDispatch>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FlowySDK { | impl FlowySDK { | ||||||
| @ -58,9 +66,21 @@ impl FlowySDK { | |||||||
|         init_kv(&config.root); |         init_kv(&config.root); | ||||||
| 
 | 
 | ||||||
|         tracing::debug!("🔥 {:?}", config); |         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() } |     pub fn dispatch(&self) -> Arc<EventDispatch> { self.dispatch.clone() } | ||||||
| @ -83,9 +103,3 @@ fn init_log(config: &FlowySDKConfig) { | |||||||
|             .build(); |             .build(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| fn init_dispatch(root: &str) -> EventDispatch { |  | ||||||
|     let config = ModuleConfig { root: root.to_owned() }; |  | ||||||
|     let dispatch = EventDispatch::construct(|| build_modules(config)); |  | ||||||
|     dispatch |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,31 +1,34 @@ | |||||||
| use flowy_dispatch::prelude::Module; |  | ||||||
| 
 |  | ||||||
| use crate::deps_resolve::{DocumentDepsResolver, WorkspaceDepsResolver}; | use crate::deps_resolve::{DocumentDepsResolver, WorkspaceDepsResolver}; | ||||||
|  | use flowy_dispatch::prelude::Module; | ||||||
| use flowy_document::module::FlowyDocument; | 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; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| pub struct ModuleConfig { | pub fn build_modules( | ||||||
|     pub root: String, |     server_config: &ServerConfig, | ||||||
| } |     user_session: Arc<UserSession>, | ||||||
| 
 |     flowy_document: Arc<FlowyDocument>, | ||||||
| pub fn build_modules(config: ModuleConfig) -> Vec<Module> { | ) -> Vec<Module> { | ||||||
|     let user_session = Arc::new(UserSessionBuilder::new().root_dir(&config.root).build()); |     vec![ | ||||||
|     vec![build_user_module(user_session.clone()), build_workspace_module(user_session)] |         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_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 workspace_deps = WorkspaceDepsResolver::new(user_session.clone()); | ||||||
|     let (user, database) = workspace_deps.split_into(); |     let (user, database) = workspace_deps.split_into(); | ||||||
|     let document = build_document_module(user_session.clone()); |     flowy_workspace::module::create(user, database, flowy_document, server_config) | ||||||
| 
 |  | ||||||
|     flowy_workspace::module::create(user, database, document) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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 document_deps = DocumentDepsResolver::new(user_session.clone()); | ||||||
|     let (user, ws_manager) = document_deps.split_into(); |     let (user, ws_manager) = document_deps.split_into(); | ||||||
|     let document = Arc::new(FlowyDocument::new(user, ws_manager)); |     let document = Arc::new(FlowyDocument::new(user, ws_manager)); | ||||||
|  | |||||||
| @ -21,7 +21,12 @@ pub struct FlowyEnv { | |||||||
| 
 | 
 | ||||||
| impl FlowyEnv { | impl FlowyEnv { | ||||||
|     pub fn setup() -> Self { |     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 result = sign_up(sdk.dispatch()); | ||||||
|         let env = Self { |         let env = Self { | ||||||
|             sdk, |             sdk, | ||||||
| @ -35,6 +40,10 @@ impl FlowyEnv { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn init_test_sdk() -> FlowyTestSDK { | 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) |     FlowySDK::new(config) | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ use bytes::Bytes; | |||||||
| use derive_more::Display; | use derive_more::Display; | ||||||
| use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; | use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; | ||||||
| use flowy_dispatch::prelude::{EventResponse, ResponseBuilder}; | use flowy_dispatch::prelude::{EventResponse, ResponseBuilder}; | ||||||
| 
 |  | ||||||
| use std::{convert::TryInto, fmt::Debug}; | use std::{convert::TryInto, fmt::Debug}; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Default, Clone, ProtoBuf)] | #[derive(Debug, Default, Clone, ProtoBuf)] | ||||||
| @ -27,9 +26,19 @@ macro_rules! static_user_error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl UserError { | 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 { |     pub fn context<T: Debug>(mut self, error: T) -> Self { | ||||||
|         self.msg = format!("{:?}", error); |         self.msg = format!("{:?}", error); | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ use crate::{ | |||||||
|     errors::UserError, |     errors::UserError, | ||||||
| }; | }; | ||||||
| use flowy_infra::future::ResultFuture; | use flowy_infra::future::ResultFuture; | ||||||
|  | use flowy_net::config::ServerConfig; | ||||||
| 
 | 
 | ||||||
| pub trait UserServerAPI { | pub trait UserServerAPI { | ||||||
|     fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError>; |     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 sign_out(&self, token: &str) -> ResultFuture<(), UserError>; | ||||||
|     fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError>; |     fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError>; | ||||||
|     fn get_user(&self, token: &str) -> ResultFuture<UserProfile, 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") { |     if cfg!(feature = "http_server") { | ||||||
|         Arc::new(UserServer {}) |         Arc::new(UserServer::new(config.clone())) | ||||||
|     } else { |     } else { | ||||||
|         Arc::new(UserServerMock {}) |         // Arc::new(UserServerMock {})
 | ||||||
|  |         Arc::new(UserServer::new(config.clone())) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -10,37 +10,46 @@ use flowy_net::{ | |||||||
|     request::{HttpRequestBuilder, ResponseMiddleware}, |     request::{HttpRequestBuilder, ResponseMiddleware}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub struct UserServer {} | pub struct UserServer { | ||||||
|  |     config: ServerConfig, | ||||||
|  | } | ||||||
| impl UserServer { | impl UserServer { | ||||||
|     pub fn new() -> Self { Self {} } |     pub fn new(config: ServerConfig) -> Self { Self { config } } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl UserServerAPI for UserServer { | impl UserServerAPI for UserServer { | ||||||
|     fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError> { |     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> { |     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> { |     fn sign_out(&self, token: &str) -> ResultFuture<(), UserError> { | ||||||
|         let token = token.to_owned(); |         let token = token.to_owned(); | ||||||
|  |         let url = self.config.sign_out_url(); | ||||||
|         ResultFuture::new(async move { |         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(()) |             Ok(()) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError> { |     fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn get_user(&self, token: &str) -> ResultFuture<UserProfile, UserError> { | ||||||
|         let token = token.to_owned(); |         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::*}; | 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(crate) fn request_builder() -> HttpRequestBuilder { HttpRequestBuilder::new().middleware(MIDDLEWARE.clone()) } | ||||||
| 
 | 
 | ||||||
| pub async fn user_sign_up_request(params: SignUpParams, url: &str) -> Result<SignUpResponse, UserError> { | 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) |     Ok(response) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn user_sign_in_request(params: SignInParams, url: &str) -> Result<SignInResponse, UserError> { | 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) |     Ok(response) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), UserError> { | 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(()) |     Ok(()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -37,9 +37,13 @@ impl UserServerAPI for UserServerMock { | |||||||
| 
 | 
 | ||||||
|     fn sign_out(&self, _token: &str) -> ResultFuture<(), UserError> { ResultFuture::new(async { Ok(()) }) } |     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> { |     fn get_user(&self, _token: &str) -> ResultFuture<UserProfile, UserError> { | ||||||
|         ResultFuture::new(async { Err(UserError::internal().context("mock data, ignore this error")) }) |         ResultFuture::new(async { Err(UserError::internal().context("mock data, ignore this error")) }) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     fn ws_addr(&self) -> String { "ws://localhost:8000/ws/".to_owned() } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| use crate::services::user::{SessionStatusCallback, UserSession, UserSessionConfig}; | use crate::services::user::{SessionStatusCallback, UserSession, UserSessionConfig}; | ||||||
|  | use flowy_net::config::ServerConfig; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| pub struct UserSessionBuilder { | pub struct UserSessionBuilder { | ||||||
| @ -14,8 +15,8 @@ impl UserSessionBuilder { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn root_dir(mut self, dir: &str) -> Self { |     pub fn root_dir(mut self, dir: &str, server_config: &ServerConfig) -> Self { | ||||||
|         self.config = Some(UserSessionConfig::new(dir)); |         self.config = Some(UserSessionConfig::new(dir, server_config)); | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ use flowy_database::{ | |||||||
|     UserDatabaseConnection, |     UserDatabaseConnection, | ||||||
| }; | }; | ||||||
| use flowy_infra::kv::KV; | use flowy_infra::kv::KV; | ||||||
|  | use flowy_net::config::ServerConfig; | ||||||
| use flowy_sqlite::ConnectionPool; | use flowy_sqlite::ConnectionPool; | ||||||
| use flowy_ws::{connect::Retry, WsController, WsMessage, WsMessageHandler, WsSender}; | use flowy_ws::{connect::Retry, WsController, WsMessage, WsMessageHandler, WsSender}; | ||||||
| use parking_lot::RwLock; | use parking_lot::RwLock; | ||||||
| @ -25,12 +26,14 @@ use std::{sync::Arc, time::Duration}; | |||||||
| 
 | 
 | ||||||
| pub struct UserSessionConfig { | pub struct UserSessionConfig { | ||||||
|     root_dir: String, |     root_dir: String, | ||||||
|  |     server_config: ServerConfig, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl UserSessionConfig { | impl UserSessionConfig { | ||||||
|     pub fn new(root_dir: &str) -> Self { |     pub fn new(root_dir: &str, server_config: &ServerConfig) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             root_dir: root_dir.to_owned(), |             root_dir: root_dir.to_owned(), | ||||||
|  |             server_config: server_config.clone(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -54,7 +57,7 @@ pub struct UserSession { | |||||||
| impl UserSession { | impl UserSession { | ||||||
|     pub fn new(config: UserSessionConfig, status_callback: SessionStatusCallback) -> Self { |     pub fn new(config: UserSessionConfig, status_callback: SessionStatusCallback) -> Self { | ||||||
|         let db = UserDB::new(&config.root_dir); |         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 ws_controller = Arc::new(RwLock::new(WsController::new())); | ||||||
|         let user_session = Self { |         let user_session = Self { | ||||||
|             database: db, |             database: db, | ||||||
| @ -120,7 +123,8 @@ impl UserSession { | |||||||
|     #[tracing::instrument(level = "debug", skip(self))] |     #[tracing::instrument(level = "debug", skip(self))] | ||||||
|     pub async fn sign_out(&self) -> Result<(), UserError> { |     pub async fn sign_out(&self) -> Result<(), UserError> { | ||||||
|         let session = self.get_session()?; |         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.database.close_user_db(&session.user_id)?; | ||||||
|         let _ = self.set_session(None)?; |         let _ = self.set_session(None)?; | ||||||
|         (self.status_callback)(SessionStatus::Expired { |         (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 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> { |     pub fn get_ws_sender(&self) -> Result<Arc<WsSender>, UserError> { | ||||||
|         match self.ws_controller.try_read_for(Duration::from_millis(300)) { |         match self.ws_controller.try_read_for(Duration::from_millis(300)) { | ||||||
| @ -204,7 +210,9 @@ impl UserSession { | |||||||
|         tokio::spawn(async move { |         tokio::spawn(async move { | ||||||
|             match server.get_user(&token).await { |             match server.get_user(&token).await { | ||||||
|                 Ok(profile) => { |                 Ok(profile) => { | ||||||
|                     notify(&token, UserObservable::UserProfileUpdated).payload(profile).send(); |                     notify(&token, UserObservable::UserProfileUpdated) | ||||||
|  |                         .payload(profile) | ||||||
|  |                         .send(); | ||||||
|                 }, |                 }, | ||||||
|                 Err(e) => { |                 Err(e) => { | ||||||
|                     notify(&token, UserObservable::UserProfileUpdated).error(e).send(); |                     notify(&token, UserObservable::UserProfileUpdated).error(e).send(); | ||||||
| @ -245,7 +253,9 @@ impl UserSession { | |||||||
| 
 | 
 | ||||||
|     async fn save_user(&self, user: UserTable) -> Result<UserTable, UserError> { |     async fn save_user(&self, user: UserTable) -> Result<UserTable, UserError> { | ||||||
|         let conn = self.db_connection()?; |         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) |         Ok(user) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -285,7 +295,7 @@ impl UserSession { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn start_ws_connection(&self, token: &str) -> Result<(), UserError> { |     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 ws_controller = self.ws_controller.clone(); | ||||||
|         let retry = Retry::new(&addr, move |addr| { |         let retry = Retry::new(&addr, move |addr| { | ||||||
|             let _ = ws_controller.write().connect(addr.to_owned()); |             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 changeset = UserTableChangeset::new(params); | ||||||
|     let conn = pool.get()?; |     let conn = pool.get()?; | ||||||
|     diesel_update_table!(user_table, changeset, &*conn); |     diesel_update_table!(user_table, changeset, &*conn); | ||||||
|  | |||||||
| @ -68,9 +68,9 @@ pub struct CreateViewParams { | |||||||
|     pub data: String, |     pub data: String, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub const VIEW_DEFAULT_DATA: &str = "[{\"insert\":\"\\n\"}]"; | pub const DOC_DEFAULT_DATA: &str = "[{\"insert\":\"\\n\"}]"; | ||||||
| #[allow(dead_code)] | #[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 { | impl CreateViewParams { | ||||||
|     pub fn new(belong_to_id: String, name: String, desc: String, view_type: ViewType, thumbnail: String) -> Self { |     pub fn new(belong_to_id: String, name: String, desc: String, view_type: ViewType, thumbnail: String) -> Self { | ||||||
| @ -80,7 +80,7 @@ impl CreateViewParams { | |||||||
|             desc, |             desc, | ||||||
|             thumbnail, |             thumbnail, | ||||||
|             view_type, |             view_type, | ||||||
|             data: VIEW_DEFAULT_DATA.to_string(), |             data: DOC_DEFAULT_DATA.to_string(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ use crate::{ | |||||||
| use flowy_database::DBConnection; | use flowy_database::DBConnection; | ||||||
| use flowy_dispatch::prelude::*; | use flowy_dispatch::prelude::*; | ||||||
| use flowy_document::module::FlowyDocument; | use flowy_document::module::FlowyDocument; | ||||||
|  | use flowy_net::config::ServerConfig; | ||||||
| use flowy_sqlite::ConnectionPool; | use flowy_sqlite::ConnectionPool; | ||||||
| use std::sync::Arc; | 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 { | pub fn create( | ||||||
|     let server = construct_workspace_server(); |     user: Arc<dyn WorkspaceUser>, | ||||||
|     let view_controller = Arc::new(ViewController::new(user.clone(), database.clone(), server.clone(), document)); |     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 app_controller = Arc::new(AppController::new(user.clone(), database.clone(), server.clone())); | ||||||
| 
 | 
 | ||||||
|     let workspace_controller = Arc::new(WorkspaceController::new( |     let workspace_controller = Arc::new(WorkspaceController::new( | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ use crate::{ | |||||||
|     errors::WorkspaceError, |     errors::WorkspaceError, | ||||||
| }; | }; | ||||||
| use flowy_infra::future::ResultFuture; | use flowy_infra::future::ResultFuture; | ||||||
|  | use flowy_net::config::ServerConfig; | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| pub(crate) type Server = Arc<dyn WorkspaceServerAPI + Send + Sync>; | pub(crate) type Server = Arc<dyn WorkspaceServerAPI + Send + Sync>; | ||||||
| @ -30,7 +31,11 @@ pub trait WorkspaceServerAPI { | |||||||
|     // Workspace
 |     // Workspace
 | ||||||
|     fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError>; |     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>; |     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>; |     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") { |     if cfg!(feature = "http_server") { | ||||||
|         Arc::new(WorkspaceServer {}) |         Arc::new(WorkspaceServer::new(config.clone())) | ||||||
|     } else { |     } else { | ||||||
|         Arc::new(WorkspaceServerMock {}) |         Arc::new(WorkspaceServerMock {}) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -17,72 +17,100 @@ use crate::{ | |||||||
| use flowy_infra::future::ResultFuture; | use flowy_infra::future::ResultFuture; | ||||||
| use flowy_net::{config::*, request::HttpRequestBuilder}; | 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 { | impl WorkspaceServerAPI for WorkspaceServer { | ||||||
|     fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> { |     fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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(); |         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> { |     fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> ResultFuture<(), WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn delete_workspace(&self, token: &str, params: DeleteWorkspaceParams) -> ResultFuture<(), WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn create_view(&self, token: &str, params: CreateViewParams) -> ResultFuture<View, WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn read_view(&self, token: &str, params: QueryViewParams) -> ResultFuture<Option<View>, WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn delete_view(&self, token: &str, params: DeleteViewParams) -> ResultFuture<(), WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn update_view(&self, token: &str, params: UpdateViewParams) -> ResultFuture<(), WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn create_app(&self, token: &str, params: CreateAppParams) -> ResultFuture<App, WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn read_app(&self, token: &str, params: QueryAppParams) -> ResultFuture<Option<App>, WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn update_app(&self, token: &str, params: UpdateAppParams) -> ResultFuture<(), WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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> { |     fn delete_app(&self, token: &str, params: DeleteAppParams) -> ResultFuture<(), WorkspaceError> { | ||||||
|         let token = token.to_owned(); |         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(crate) fn request_builder() -> HttpRequestBuilder { | ||||||
| pub async fn create_workspace_request(token: &str, params: CreateWorkspaceParams, url: &str) -> Result<Workspace, WorkspaceError> { |     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() |     let workspace = request_builder() | ||||||
|         .post(&url.to_owned()) |         .post(&url.to_owned()) | ||||||
|         .header(HEADER_TOKEN, token) |         .header(HEADER_TOKEN, token) | ||||||
| @ -92,7 +120,11 @@ pub async fn create_workspace_request(token: &str, params: CreateWorkspaceParams | |||||||
|     Ok(workspace) |     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() |     let repeated_workspace = request_builder() | ||||||
|         .get(&url.to_owned()) |         .get(&url.to_owned()) | ||||||
|         .header(HEADER_TOKEN, token) |         .header(HEADER_TOKEN, token) | ||||||
| @ -103,7 +135,11 @@ pub async fn read_workspaces_request(token: &str, params: QueryWorkspaceParams, | |||||||
|     Ok(repeated_workspace) |     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() |     let _ = request_builder() | ||||||
|         .patch(&url.to_owned()) |         .patch(&url.to_owned()) | ||||||
|         .header(HEADER_TOKEN, token) |         .header(HEADER_TOKEN, token) | ||||||
| @ -113,7 +149,11 @@ pub async fn update_workspace_request(token: &str, params: UpdateWorkspaceParams | |||||||
|     Ok(()) |     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() |     let _ = request_builder() | ||||||
|         .delete(url) |         .delete(url) | ||||||
|         .header(HEADER_TOKEN, token) |         .header(HEADER_TOKEN, token) | ||||||
| @ -176,7 +216,11 @@ pub async fn create_view_request(token: &str, params: CreateViewParams, url: &st | |||||||
|     Ok(view) |     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() |     let view = request_builder() | ||||||
|         .get(&url.to_owned()) |         .get(&url.to_owned()) | ||||||
|         .header(HEADER_TOKEN, token) |         .header(HEADER_TOKEN, token) | ||||||
|  | |||||||
| @ -50,7 +50,8 @@ impl ViewController { | |||||||
|         // TODO: rollback anything created before if failed?
 |         // TODO: rollback anything created before if failed?
 | ||||||
|         conn.immediate_transaction::<_, WorkspaceError, _>(|| { |         conn.immediate_transaction::<_, WorkspaceError, _>(|| { | ||||||
|             let _ = self.save_view(view.clone(), conn)?; |             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)?; |             let repeated_view = self.read_local_views_belong_to(&view.belong_to_id, conn)?; | ||||||
|             notify(&view.belong_to_id, WorkspaceObservable::AppCreateView) |             notify(&view.belong_to_id, WorkspaceObservable::AppCreateView) | ||||||
| @ -78,8 +79,8 @@ impl ViewController { | |||||||
| 
 | 
 | ||||||
|     #[tracing::instrument(level = "debug", skip(self), err)] |     #[tracing::instrument(level = "debug", skip(self), err)] | ||||||
|     pub(crate) async fn open_view(&self, params: QueryDocParams) -> Result<Doc, WorkspaceError> { |     pub(crate) async fn open_view(&self, params: QueryDocParams) -> Result<Doc, WorkspaceError> { | ||||||
|         let doc = self.document.open(params, self.database.db_pool()?).await?; |         let edit_context = self.document.open(params, self.database.db_pool()?).await?; | ||||||
|         Ok(doc) |         Ok(edit_context.doc()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub(crate) async fn delete_view(&self, params: DeleteViewParams) -> Result<(), WorkspaceError> { |     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.
 |     // 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 |         let views = self | ||||||
|             .sql |             .sql | ||||||
|             .read_views_belong_to(belong_to_id, conn)? |             .read_views_belong_to(belong_to_id, conn)? | ||||||
|  | |||||||
| @ -103,7 +103,7 @@ impl WsController { | |||||||
| 
 | 
 | ||||||
|     pub fn get_sender(&self) -> Result<Arc<WsSender>, WsError> { |     pub fn get_sender(&self) -> Result<Arc<WsSender>, WsError> { | ||||||
|         match &self.sender { |         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()), |             Some(sender) => Ok(sender.clone()), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -112,7 +112,10 @@ impl WsController { | |||||||
|         log::debug!("🐴 ws connect: {}", &addr); |         log::debug!("🐴 ws connect: {}", &addr); | ||||||
|         let (connection, handlers) = self.make_connect(addr.clone()); |         let (connection, handlers) = self.make_connect(addr.clone()); | ||||||
|         let state_notify = self.state_notify.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 { |         Ok(tokio::spawn(async move { | ||||||
|             match connection.await { |             match connection.await { | ||||||
|                 Ok(stream) => { |                 Ok(stream) => { | ||||||
| @ -158,7 +161,10 @@ impl WsController { | |||||||
|         let handlers = self.handlers.clone(); |         let handlers = self.handlers.clone(); | ||||||
|         self.sender = Some(Arc::new(WsSender { ws_tx })); |         self.sender = Some(Arc::new(WsSender { ws_tx })); | ||||||
|         self.addr = Some(addr.clone()); |         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 { | 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) { |     fn handler_ws_message(&self, message: Message) { | ||||||
|         match message { |         match message { | ||||||
| @ -215,7 +223,10 @@ pub struct WsSender { | |||||||
| impl WsSender { | impl WsSender { | ||||||
|     pub fn send_msg<T: Into<WsMessage>>(&self, msg: T) -> Result<(), WsError> { |     pub fn send_msg<T: Into<WsMessage>>(&self, msg: T) -> Result<(), WsError> { | ||||||
|         let msg = msg.into(); |         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(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -241,7 +252,10 @@ impl WsSender { | |||||||
|             reason: reason.to_owned().into(), |             reason: reason.to_owned().into(), | ||||||
|         }; |         }; | ||||||
|         let msg = Message::Close(Some(frame)); |         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(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 appflowy
						appflowy