diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pb.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pb.dart index acc12b9d11..e256f55b79 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pb.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pb.dart @@ -238,14 +238,14 @@ class Workspace extends $pb.GeneratedMessage { $0.RepeatedApp ensureApps() => $_ensure(3); } -class Workspaces extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Workspaces', createEmptyInstance: create) +class RepeatedWorkspace extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RepeatedWorkspace', createEmptyInstance: create) ..pc(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'items', $pb.PbFieldType.PM, subBuilder: Workspace.create) ..hasRequiredFields = false ; - Workspaces._() : super(); - factory Workspaces({ + RepeatedWorkspace._() : super(); + factory RepeatedWorkspace({ $core.Iterable? items, }) { final _result = create(); @@ -254,26 +254,26 @@ class Workspaces extends $pb.GeneratedMessage { } return _result; } - factory Workspaces.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory Workspaces.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + factory RepeatedWorkspace.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RepeatedWorkspace.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - Workspaces clone() => Workspaces()..mergeFromMessage(this); + RepeatedWorkspace clone() => RepeatedWorkspace()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - Workspaces copyWith(void Function(Workspaces) updates) => super.copyWith((message) => updates(message as Workspaces)) as Workspaces; // ignore: deprecated_member_use + RepeatedWorkspace copyWith(void Function(RepeatedWorkspace) updates) => super.copyWith((message) => updates(message as RepeatedWorkspace)) as RepeatedWorkspace; // ignore: deprecated_member_use $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static Workspaces create() => Workspaces._(); - Workspaces createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static RepeatedWorkspace create() => RepeatedWorkspace._(); + RepeatedWorkspace createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static Workspaces getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Workspaces? _defaultInstance; + static RepeatedWorkspace getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RepeatedWorkspace? _defaultInstance; @$pb.TagNumber(1) $core.List get items => $_getList(0); diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbjson.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbjson.dart index 9485073d14..88b6ff9ca1 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbjson.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbjson.dart @@ -44,13 +44,13 @@ const Workspace$json = const { /// Descriptor for `Workspace`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List workspaceDescriptor = $convert.base64Decode('CglXb3Jrc3BhY2USDgoCaWQYASABKAlSAmlkEhIKBG5hbWUYAiABKAlSBG5hbWUSEgoEZGVzYxgDIAEoCVIEZGVzYxIgCgRhcHBzGAQgASgLMgwuUmVwZWF0ZWRBcHBSBGFwcHM='); -@$core.Deprecated('Use workspacesDescriptor instead') -const Workspaces$json = const { - '1': 'Workspaces', +@$core.Deprecated('Use repeatedWorkspaceDescriptor instead') +const RepeatedWorkspace$json = const { + '1': 'RepeatedWorkspace', '2': const [ const {'1': 'items', '3': 1, '4': 3, '5': 11, '6': '.Workspace', '10': 'items'}, ], }; -/// Descriptor for `Workspaces`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List workspacesDescriptor = $convert.base64Decode('CgpXb3Jrc3BhY2VzEiAKBWl0ZW1zGAEgAygLMgouV29ya3NwYWNlUgVpdGVtcw=='); +/// Descriptor for `RepeatedWorkspace`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List repeatedWorkspaceDescriptor = $convert.base64Decode('ChFSZXBlYXRlZFdvcmtzcGFjZRIgCgVpdGVtcxgBIAMoCzIKLldvcmtzcGFjZVIFaXRlbXM='); diff --git a/backend/src/application.rs b/backend/src/application.rs index e3d2dd13bb..716e30f37d 100644 --- a/backend/src/application.rs +++ b/backend/src/application.rs @@ -78,6 +78,9 @@ fn user_scope() -> Scope { .route(web::get().to(workspace::read_handler)) .route(web::patch().to(workspace::update_handler)) ) + .service(web::resource("/workspace_list/{user_id}") + .route(web::get().to(workspace::workspace_list)) + ) .service(web::resource("/app") .route(web::post().to(app::create_handler)) .route(web::delete().to(app::delete_handler)) diff --git a/backend/src/entities/workspace.rs b/backend/src/entities/workspace.rs index 193edd40a8..2c4f1ae54e 100644 --- a/backend/src/entities/workspace.rs +++ b/backend/src/entities/workspace.rs @@ -1,8 +1,4 @@ use chrono::Utc; -use flowy_workspace::entities::{ - app::App, - view::{RepeatedView, View, ViewType}, -}; #[derive(Debug, Clone, sqlx::FromRow)] pub struct WorkspaceTable { @@ -28,19 +24,6 @@ pub struct AppTable { pub(crate) is_trash: bool, } -impl std::convert::Into for AppTable { - fn into(self) -> App { - App { - id: self.id.to_string(), - workspace_id: self.workspace_id, - name: self.name, - desc: self.description, - belongings: RepeatedView::default(), - version: 0, - } - } -} - #[derive(Debug, Clone, sqlx::FromRow)] pub struct ViewTable { pub(crate) id: uuid::Uuid, @@ -53,17 +36,16 @@ pub struct ViewTable { pub(crate) view_type: i32, pub(crate) is_trash: bool, } - -impl std::convert::Into for ViewTable { - fn into(self) -> View { - View { - id: self.id.to_string(), - belong_to_id: self.belong_to_id, - name: self.name, - desc: self.description, - view_type: ViewType::from(self.view_type), - version: 0, - belongings: RepeatedView::default(), - } - } -} +// impl std::convert::Into for ViewTable { +// fn into(self) -> View { +// View { +// id: self.id.to_string(), +// belong_to_id: self.belong_to_id, +// name: self.name, +// desc: self.description, +// view_type: ViewType::from(self.view_type), +// version: 0, +// belongings: RepeatedView::default(), +// } +// } +// } diff --git a/backend/src/sqlx_ext/utils.rs b/backend/src/sqlx_ext/utils.rs index 53113a1656..bff0e0881f 100644 --- a/backend/src/sqlx_ext/utils.rs +++ b/backend/src/sqlx_ext/utils.rs @@ -1,5 +1,7 @@ use flowy_net::errors::{ErrorCode, ServerError}; -use sqlx::Error; +use sqlx::{Error, Postgres, Transaction}; + +pub type DBTransaction<'a> = Transaction<'a, Postgres>; pub fn map_sqlx_error(error: sqlx::Error) -> ServerError { match error { diff --git a/backend/src/user_service/auth.rs b/backend/src/user_service/auth.rs index e9dfd8b15d..7f0f8b4978 100644 --- a/backend/src/user_service/auth.rs +++ b/backend/src/user_service/auth.rs @@ -1,6 +1,8 @@ use crate::{ entities::{token::Token, user::UserTable}, + sqlx_ext::DBTransaction, user_service::{hash_password, verify_password}, + workspace_service::user_default::create_default_workspace, }; use actix_identity::Identity; use anyhow::Context; @@ -10,9 +12,8 @@ use flowy_net::{ response::FlowyResponse, }; use flowy_user::{ - entities::{parser::UserName, SignInResponse, SignUpResponse}, - prelude::parser::{UserEmail, UserPassword}, - protobuf::{SignInParams, SignUpParams}, + entities::parser::{UserEmail, UserName, UserPassword}, + protobuf::{SignInParams, SignInResponse, SignUpParams, SignUpResponse}, }; use sqlx::{PgPool, Postgres, Transaction}; @@ -40,14 +41,14 @@ pub async fn sign_in( match verify_password(&password.0, &user.password) { Ok(true) => { let token = Token::create_token(&user)?; - let data = SignInResponse { - uid: user.id.to_string(), - name: user.name, - email: user.email, - token: token.into(), - }; - id.remember(data.token.clone()); - FlowyResponse::success().data(data) + let mut response_data = SignInResponse::default(); + response_data.set_uid(user.id.to_string()); + response_data.set_name(user.name); + response_data.set_email(user.email); + response_data.set_token(token.into()); + + id.remember(response_data.token.clone()); + FlowyResponse::success().pb(response_data) }, _ => Err(ServerError::password_not_match()), } @@ -70,7 +71,7 @@ pub async fn register_user( .context("Failed to acquire a Postgres connection to register user")?; let _ = is_email_exist(&mut transaction, email.as_ref()).await?; - let data = insert_user( + let response_data = insert_new_user( &mut transaction, name.as_ref(), email.as_ref(), @@ -79,16 +80,18 @@ pub async fn register_user( .await .context("Failed to insert user")?; + let _ = create_default_workspace(&mut transaction, response_data.get_uid()).await?; + transaction .commit() .await .context("Failed to commit SQL transaction to register user.")?; - FlowyResponse::success().data(data) + FlowyResponse::success().pb(response_data) } async fn is_email_exist( - transaction: &mut Transaction<'_, Postgres>, + transaction: &mut DBTransaction<'_>, email: &str, ) -> Result<(), ServerError> { let result = sqlx::query(r#"SELECT email FROM user_table WHERE email = $1"#) @@ -107,7 +110,7 @@ async fn is_email_exist( } async fn read_user( - transaction: &mut Transaction<'_, Postgres>, + transaction: &mut DBTransaction<'_>, email: &str, ) -> Result { let user = sqlx::query_as::("SELECT * FROM user_table WHERE email = $1") @@ -119,8 +122,8 @@ async fn read_user( Ok(user) } -async fn insert_user( - transaction: &mut Transaction<'_, Postgres>, +async fn insert_new_user( + transaction: &mut DBTransaction<'_>, name: &str, email: &str, password: &str, @@ -142,11 +145,10 @@ async fn insert_user( .await .map_err(|e| ServerError::internal().context(e))?; - let data = SignUpResponse { - uid: uuid.to_string(), - name: name.to_string(), - email: email.to_string(), - }; + let mut response = SignUpResponse::default(); + response.set_uid(uuid.to_string()); + response.set_name(name.to_string()); + response.set_email(email.to_string()); - Ok(data) + Ok(response) } diff --git a/backend/src/workspace_service/app/app.rs b/backend/src/workspace_service/app/app.rs index 3097471445..e84ebaf546 100644 --- a/backend/src/workspace_service/app/app.rs +++ b/backend/src/workspace_service/app/app.rs @@ -2,8 +2,11 @@ use flowy_net::{errors::ServerError, response::FlowyResponse}; use crate::{ entities::workspace::AppTable, - sqlx_ext::{map_sqlx_error, SqlBuilder}, - workspace_service::view::read_views_belong_to_id, + sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, + workspace_service::{ + app::{check_app_id, make_app_from_table, Builder}, + view::read_views_belong_to_id, + }, }; use anyhow::Context; use chrono::Utc; @@ -13,13 +16,11 @@ use flowy_workspace::{ entities::{ app::{ parser::{AppDesc, AppId, AppName}, - App, RepeatedApp, }, - view::RepeatedView, workspace::parser::WorkspaceId, }, - protobuf::{CreateAppParams, QueryAppParams, UpdateAppParams}, + protobuf::{App, CreateAppParams, QueryAppParams, RepeatedView, UpdateAppParams}, }; use protobuf::Message; use sqlx::{postgres::PgArguments, PgPool, Postgres, Transaction}; @@ -27,31 +28,21 @@ use uuid::Uuid; pub(crate) async fn create_app( pool: &PgPool, - params: CreateAppParams, + mut params: CreateAppParams, ) -> Result { - let color_bytes = params.get_color_style().write_to_bytes()?; - let name = AppName::parse(params.name).map_err(invalid_params)?; - let workspace_id = WorkspaceId::parse(params.workspace_id).map_err(invalid_params)?; - let user_id = UserId::parse(params.user_id).map_err(invalid_params)?; - let desc = AppDesc::parse(params.desc).map_err(invalid_params)?; - + let name = AppName::parse(params.take_name()).map_err(invalid_params)?; + let workspace_id = WorkspaceId::parse(params.take_workspace_id()).map_err(invalid_params)?; + let user_id = UserId::parse(params.take_user_id()).map_err(invalid_params)?; + let desc = AppDesc::parse(params.take_desc()).map_err(invalid_params)?; let mut transaction = pool .begin() .await .context("Failed to acquire a Postgres connection to create app")?; - let uuid = uuid::Uuid::new_v4(); - let time = Utc::now(); - - let (sql, args) = SqlBuilder::create("app_table") - .add_arg("id", uuid) - .add_arg("workspace_id", workspace_id.as_ref()) - .add_arg("name", name.as_ref()) - .add_arg("description", desc.as_ref()) - .add_arg("color_style", color_bytes) - .add_arg("modified_time", &time) - .add_arg("create_time", &time) - .add_arg("user_id", user_id.as_ref()) + let (sql, args, app) = Builder::new(user_id.as_ref(), workspace_id.as_ref()) + .name(name.as_ref()) + .desc(desc.as_ref()) + .color_style(params.take_color_style()) .build()?; let _ = sqlx::query_with(&sql, args) @@ -64,16 +55,7 @@ pub(crate) async fn create_app( .await .context("Failed to commit SQL transaction to create app.")?; - let app = App { - id: uuid.to_string(), - workspace_id: workspace_id.as_ref().to_owned(), - name: name.as_ref().to_string(), - desc: desc.as_ref().to_string(), - belongings: RepeatedView::default(), - version: 0, - }; - - FlowyResponse::success().data(app) + FlowyResponse::success().pb(app) } pub(crate) async fn read_app( @@ -99,7 +81,11 @@ pub(crate) async fn read_app( let mut views = RepeatedView::default(); if params.read_belongings { - views.items = read_views_belong_to_id(&mut transaction, &table.id.to_string()).await?; + views.set_items( + read_views_belong_to_id(&mut transaction, &table.id.to_string()) + .await? + .into(), + ); } transaction @@ -107,10 +93,8 @@ pub(crate) async fn read_app( .await .context("Failed to commit SQL transaction to read app.")?; - let mut app: App = table.into(); - app.belongings = views; - - FlowyResponse::success().data(app) + let app = make_app_from_table(table, views); + FlowyResponse::success().pb(app) } pub(crate) async fn update_app( @@ -207,7 +191,7 @@ pub(crate) async fn delete_app(pool: &PgPool, app_id: &str) -> Result( - transaction: &mut Transaction<'c, Postgres>, + transaction: &mut DBTransaction<'_>, workspace_id: &str, ) -> Result, ServerError> { let workspace_id = WorkspaceId::parse(workspace_id.to_owned()).map_err(invalid_params)?; @@ -223,14 +207,8 @@ pub(crate) async fn read_apps_belong_to_workspace<'c>( let apps = tables .into_iter() - .map(|table| table.into()) + .map(|table| make_app_from_table(table, RepeatedView::default())) .collect::>(); Ok(apps) } - -fn check_app_id(id: String) -> Result { - let app_id = AppId::parse(id).map_err(invalid_params)?; - let app_id = Uuid::parse_str(app_id.as_ref())?; - Ok(app_id) -} diff --git a/backend/src/workspace_service/app/builder.rs b/backend/src/workspace_service/app/builder.rs new file mode 100644 index 0000000000..be4b730dd3 --- /dev/null +++ b/backend/src/workspace_service/app/builder.rs @@ -0,0 +1,103 @@ +use crate::{entities::workspace::AppTable, sqlx_ext::SqlBuilder}; +use chrono::Utc; +use flowy_net::errors::{invalid_params, ServerError}; +use flowy_workspace::{ + entities::app::parser::AppId, + protobuf::{App, ColorStyle, RepeatedView}, +}; +use protobuf::Message; +use sqlx::postgres::PgArguments; +use uuid::Uuid; + +pub struct Builder { + table: AppTable, +} + +impl Builder { + pub fn new(user_id: &str, workspace_id: &str) -> Self { + let uuid = uuid::Uuid::new_v4(); + let time = Utc::now(); + + let table = AppTable { + id: uuid, + workspace_id: workspace_id.to_string(), + name: "".to_string(), + description: "".to_string(), + color_style: default_color_style(), + last_view_id: "".to_string(), + modified_time: time, + create_time: time, + user_id: user_id.to_string(), + is_trash: false, + }; + + Self { table } + } + + pub fn name(mut self, name: &str) -> Self { + self.table.name = name.to_string(); + self + } + + pub fn last_view_id(mut self, view_id: &str) -> Self { + self.table.last_view_id = view_id.to_string(); + self + } + + pub fn desc(mut self, desc: &str) -> Self { + self.table.description = desc.to_owned(); + self + } + + pub fn color_style(mut self, color_style: ColorStyle) -> Self { + self.table.color_style = color_style + .write_to_bytes() + .unwrap_or(default_color_style()); + self + } + + pub fn build(self) -> Result<(String, PgArguments, App), ServerError> { + let app = make_app_from_table(self.table.clone(), RepeatedView::default()); + + let (sql, args) = SqlBuilder::create("app_table") + .add_arg("id", self.table.id) + .add_arg("workspace_id", self.table.workspace_id) + .add_arg("name", self.table.name) + .add_arg("description", self.table.description) + .add_arg("color_style", self.table.color_style) + .add_arg("modified_time", self.table.modified_time) + .add_arg("create_time", self.table.create_time) + .add_arg("user_id", self.table.user_id) + .build()?; + + Ok((sql, args, app)) + } +} + +fn default_color_style() -> Vec { + let mut style = ColorStyle::default(); + match style.write_to_bytes() { + Ok(bytes) => bytes, + Err(e) => { + log::error!("Serialize color style failed: {:?}", e); + vec![] + }, + } +} + +pub(crate) fn make_app_from_table(table: AppTable, views: RepeatedView) -> App { + let mut app = App::default(); + app.set_id(table.id.to_string()); + app.set_workspace_id(table.workspace_id.to_string()); + app.set_name(table.name.clone()); + app.set_desc(table.description.clone()); + app.set_belongings(views); + + app +} + +pub(crate) fn check_app_id(id: String) -> Result { + let app_id = AppId::parse(id).map_err(invalid_params)?; + let app_id = Uuid::parse_str(app_id.as_ref())?; + Ok(app_id) +} diff --git a/backend/src/workspace_service/app/mod.rs b/backend/src/workspace_service/app/mod.rs index 0b70d79c55..0ea9bfb058 100644 --- a/backend/src/workspace_service/app/mod.rs +++ b/backend/src/workspace_service/app/mod.rs @@ -1,2 +1,5 @@ pub mod app; pub mod router; + +mod builder; +pub use builder::*; diff --git a/backend/src/workspace_service/mod.rs b/backend/src/workspace_service/mod.rs index aae9164e81..6da0e61849 100644 --- a/backend/src/workspace_service/mod.rs +++ b/backend/src/workspace_service/mod.rs @@ -1,3 +1,4 @@ pub mod app; +pub mod user_default; pub mod view; pub mod workspace; diff --git a/backend/src/workspace_service/user_default/mod.rs b/backend/src/workspace_service/user_default/mod.rs new file mode 100644 index 0000000000..b6df24eb64 --- /dev/null +++ b/backend/src/workspace_service/user_default/mod.rs @@ -0,0 +1,3 @@ +mod user_default; + +pub use user_default::*; diff --git a/backend/src/workspace_service/user_default/user_default.rs b/backend/src/workspace_service/user_default/user_default.rs new file mode 100644 index 0000000000..82aecb9165 --- /dev/null +++ b/backend/src/workspace_service/user_default/user_default.rs @@ -0,0 +1,73 @@ +use crate::{ + sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, + workspace_service::{ + app::Builder as AppBuilder, + view::Builder as ViewBuilder, + workspace::Builder as WorkspaceBuilder, + }, +}; +use chrono::Utc; +use flowy_net::errors::ServerError; +use flowy_workspace::protobuf::{App, View, ViewType, Workspace}; +use sqlx::{Postgres, Transaction}; + +pub async fn create_default_workspace( + transaction: &mut DBTransaction<'_>, + user_id: &str, +) -> Result { + let workspace = create_workspace(transaction, user_id).await?; + let app = create_app(transaction, user_id, &workspace).await?; + let _ = create_view(transaction, &app).await?; + + Ok(workspace) +} + +async fn create_workspace( + transaction: &mut DBTransaction<'_>, + user_id: &str, +) -> Result { + let (sql, args, workspace) = WorkspaceBuilder::new(user_id.as_ref()) + .name("DefaultWorkspace") + .desc("Workspace created by AppFlowy") + .build()?; + + let _ = sqlx::query_with(&sql, args) + .execute(transaction) + .await + .map_err(map_sqlx_error)?; + + Ok(workspace) +} + +async fn create_app( + transaction: &mut DBTransaction<'_>, + user_id: &str, + workspace: &Workspace, +) -> Result { + let (sql, args, app) = AppBuilder::new(user_id, &workspace.id) + .name("DefaultApp") + .desc("App created by AppFlowy") + .build()?; + + let _ = sqlx::query_with(&sql, args) + .execute(transaction) + .await + .map_err(map_sqlx_error)?; + + Ok(app) +} + +async fn create_view(transaction: &mut DBTransaction<'_>, app: &App) -> Result { + let (sql, args, view) = ViewBuilder::new(&app.id) + .name("DefaultView") + .desc("View created by AppFlowy") + .thumbnail("https://view.png") + .view_type(ViewType::Doc) + .build()?; + + let _ = sqlx::query_with(&sql, args) + .execute(transaction) + .await + .map_err(map_sqlx_error)?; + Ok(view) +} diff --git a/backend/src/workspace_service/view/builder.rs b/backend/src/workspace_service/view/builder.rs new file mode 100644 index 0000000000..31c2e52e17 --- /dev/null +++ b/backend/src/workspace_service/view/builder.rs @@ -0,0 +1,92 @@ +use crate::{entities::workspace::ViewTable, sqlx_ext::SqlBuilder}; +use chrono::Utc; +use flowy_net::errors::{invalid_params, ServerError}; +use flowy_workspace::{ + entities::view::parser::ViewId, + protobuf::{RepeatedView, View, ViewType}, +}; +use protobuf::ProtobufEnum; +use sqlx::postgres::PgArguments; +use uuid::Uuid; + +pub struct Builder { + table: ViewTable, +} + +impl Builder { + pub fn new(belong_to_id: &str) -> Self { + let uuid = uuid::Uuid::new_v4(); + let time = Utc::now(); + + let table = ViewTable { + id: uuid, + belong_to_id: belong_to_id.to_string(), + name: "".to_string(), + description: "".to_string(), + modified_time: time, + create_time: time, + thumbnail: "".to_string(), + view_type: ViewType::Doc.value(), + is_trash: false, + }; + + Self { table } + } + + pub fn name(mut self, name: &str) -> Self { + self.table.name = name.to_string(); + self + } + + pub fn desc(mut self, desc: &str) -> Self { + self.table.description = desc.to_owned(); + self + } + + pub fn thumbnail(mut self, thumbnail: &str) -> Self { + self.table.thumbnail = thumbnail.to_owned(); + self + } + + pub fn view_type(mut self, view_type: ViewType) -> Self { + self.table.view_type = view_type.value(); + self + } + + pub fn build(self) -> Result<(String, PgArguments, View), ServerError> { + let view = make_view_from_table(self.table.clone(), RepeatedView::default()); + + let (sql, args) = SqlBuilder::create("view_table") + .add_arg("id", self.table.id) + .add_arg("belong_to_id", self.table.belong_to_id) + .add_arg("name", self.table.name) + .add_arg("description", self.table.description) + .add_arg("modified_time", self.table.modified_time) + .add_arg("create_time", self.table.create_time) + .add_arg("thumbnail", self.table.thumbnail) + .add_arg("view_type", self.table.view_type) + .build()?; + + Ok((sql, args, view)) + } +} + +pub(crate) fn make_view_from_table(table: ViewTable, views: RepeatedView) -> View { + let view_type = ViewType::from_i32(table.view_type).unwrap_or(ViewType::Doc); + + let mut view = View::default(); + view.set_id(table.id.to_string()); + view.set_belong_to_id(table.belong_to_id); + view.set_name(table.name); + view.set_desc(table.description); + view.set_view_type(view_type); + view.set_belongings(views); + + view +} + +pub(crate) fn check_view_id(id: String) -> Result { + let view_id = ViewId::parse(id).map_err(invalid_params)?; + let view_id = Uuid::parse_str(view_id.as_ref())?; + Ok(view_id) +} diff --git a/backend/src/workspace_service/view/mod.rs b/backend/src/workspace_service/view/mod.rs index 7478f5a85d..d55599dbf6 100644 --- a/backend/src/workspace_service/view/mod.rs +++ b/backend/src/workspace_service/view/mod.rs @@ -1,4 +1,6 @@ +mod builder; pub mod router; mod view; +pub use builder::*; pub use view::*; diff --git a/backend/src/workspace_service/view/view.rs b/backend/src/workspace_service/view/view.rs index 48b338cbef..0801e3bbe3 100644 --- a/backend/src/workspace_service/view/view.rs +++ b/backend/src/workspace_service/view/view.rs @@ -1,6 +1,7 @@ use crate::{ entities::workspace::ViewTable, - sqlx_ext::{map_sqlx_error, SqlBuilder}, + sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder}, + workspace_service::view::{check_view_id, make_view_from_table, Builder}, }; use anyhow::Context; use chrono::Utc; @@ -11,13 +12,9 @@ use flowy_net::{ use flowy_workspace::{ entities::{ app::parser::AppId, - view::{ - parser::{ViewDesc, ViewId, ViewName, ViewThumbnail}, - RepeatedView, - View, - }, + view::parser::{ViewDesc, ViewId, ViewName, ViewThumbnail}, }, - protobuf::{CreateViewParams, QueryViewParams, UpdateViewParams}, + protobuf::{CreateViewParams, QueryViewParams, RepeatedView, UpdateViewParams, View}, }; use protobuf::ProtobufEnum; use sqlx::{postgres::PgArguments, PgPool, Postgres, Transaction}; @@ -37,18 +34,11 @@ pub(crate) async fn create_view( .await .context("Failed to acquire a Postgres connection to create view")?; - let uuid = uuid::Uuid::new_v4(); - let time = Utc::now(); - - let (sql, args) = SqlBuilder::create("view_table") - .add_arg("id", uuid) - .add_arg("belong_to_id", belong_to_id.as_ref()) - .add_arg("name", name.as_ref()) - .add_arg("description", desc.as_ref()) - .add_arg("modified_time", &time) - .add_arg("create_time", &time) - .add_arg("thumbnail", thumbnail.as_ref()) - .add_arg("view_type", params.view_type.value()) + let (sql, args, view) = Builder::new(belong_to_id.as_ref()) + .name(name.as_ref()) + .desc(desc.as_ref()) + .thumbnail(thumbnail.as_ref()) + .view_type(params.view_type) .build()?; let _ = sqlx::query_with(&sql, args) @@ -61,17 +51,7 @@ pub(crate) async fn create_view( .await .context("Failed to commit SQL transaction to create view.")?; - let view = View { - id: uuid.to_string(), - belong_to_id: belong_to_id.as_ref().to_owned(), - name: name.as_ref().to_owned(), - desc: desc.as_ref().to_owned(), - view_type: params.view_type.value().into(), - version: 0, - belongings: RepeatedView::default(), - }; - - FlowyResponse::success().data(view) + FlowyResponse::success().pb(view) } pub(crate) async fn read_view( @@ -96,7 +76,11 @@ pub(crate) async fn read_view( let mut views = RepeatedView::default(); if params.read_belongings { - views.items = read_views_belong_to_id(&mut transaction, &table.id.to_string()).await?; + views.set_items( + read_views_belong_to_id(&mut transaction, &table.id.to_string()) + .await? + .into(), + ) } transaction @@ -104,10 +88,9 @@ pub(crate) async fn read_view( .await .context("Failed to commit SQL transaction to read view.")?; - let mut view: View = table.into(); - view.belongings = views; + let view = make_view_from_table(table, views); - FlowyResponse::success().data(view) + FlowyResponse::success().pb(view) } pub(crate) async fn update_view( @@ -199,13 +182,14 @@ pub(crate) async fn delete_view( // transaction must be commit from caller pub(crate) async fn read_views_belong_to_id<'c>( - transaction: &mut Transaction<'c, Postgres>, + transaction: &mut DBTransaction<'_>, id: &str, ) -> Result, ServerError> { // TODO: add index for app_table let (sql, args) = SqlBuilder::select("view_table") .add_field("*") .and_where_eq("belong_to_id", id) + .and_where_eq("is_trash", false) .build()?; let tables = sqlx::query_as_with::(&sql, args) @@ -215,14 +199,8 @@ pub(crate) async fn read_views_belong_to_id<'c>( let views = tables .into_iter() - .map(|table| table.into()) + .map(|table| make_view_from_table(table, RepeatedView::default())) .collect::>(); Ok(views) } - -fn check_view_id(id: String) -> Result { - let view_id = ViewId::parse(id).map_err(invalid_params)?; - let view_id = Uuid::parse_str(view_id.as_ref())?; - Ok(view_id) -} diff --git a/backend/src/workspace_service/workspace/builder.rs b/backend/src/workspace_service/workspace/builder.rs new file mode 100644 index 0000000000..94668a5a32 --- /dev/null +++ b/backend/src/workspace_service/workspace/builder.rs @@ -0,0 +1,82 @@ +use crate::{entities::workspace::WorkspaceTable, sqlx_ext::SqlBuilder}; +use chrono::Utc; +use flowy_net::errors::{invalid_params, ServerError}; +use flowy_workspace::{ + entities::workspace::parser::WorkspaceId, + protobuf::{RepeatedApp, Workspace}, +}; +use sqlx::postgres::PgArguments; +use uuid::Uuid; + +pub struct Builder { + table: WorkspaceTable, +} + +impl Builder { + pub fn new(user_id: &str) -> Self { + let uuid = uuid::Uuid::new_v4(); + let time = Utc::now(); + + let table = WorkspaceTable { + id: uuid, + name: "".to_string(), + description: "".to_string(), + modified_time: time, + create_time: time, + user_id: user_id.to_string(), + }; + Self { table } + } + + pub fn name(mut self, name: &str) -> Self { + self.table.name = name.to_string(); + self + } + + pub fn desc(mut self, desc: &str) -> Self { + self.table.description = desc.to_owned(); + self + } + + pub fn build(self) -> Result<(String, PgArguments, Workspace), ServerError> { + let workspace = make_workspace_from_table(self.table.clone(), None); + + // TODO: use macro to fetch each field from struct + let (sql, args) = SqlBuilder::create("workspace_table") + .add_arg("id", self.table.id) + .add_arg("name", self.table.name) + .add_arg("description", self.table.description) + .add_arg("modified_time", self.table.modified_time) + .add_arg("create_time", self.table.create_time) + .add_arg("user_id", self.table.user_id) + .build()?; + + Ok((sql, args, workspace)) + } +} + +pub(crate) fn make_workspace_from_table( + table: WorkspaceTable, + apps: Option, +) -> Workspace { + let mut workspace = Workspace { + id: table.id.to_string(), + name: table.name, + desc: table.description, + apps: Default::default(), + unknown_fields: Default::default(), + cached_size: Default::default(), + }; + + if let Some(apps) = apps { + workspace.set_apps(apps); + } + + workspace +} + +pub(crate) fn check_workspace_id(id: String) -> Result { + let workspace_id = WorkspaceId::parse(id).map_err(invalid_params)?; + let workspace_id = Uuid::parse_str(workspace_id.as_ref())?; + Ok(workspace_id) +} diff --git a/backend/src/workspace_service/workspace/mod.rs b/backend/src/workspace_service/workspace/mod.rs index e26e527a5f..aecbf72766 100644 --- a/backend/src/workspace_service/workspace/mod.rs +++ b/backend/src/workspace_service/workspace/mod.rs @@ -1,4 +1,6 @@ +pub mod builder; pub mod router; mod workspace; +pub use builder::*; pub use workspace::*; diff --git a/backend/src/workspace_service/workspace/router.rs b/backend/src/workspace_service/workspace/router.rs index 909009dc87..2518bec715 100644 --- a/backend/src/workspace_service/workspace/router.rs +++ b/backend/src/workspace_service/workspace/router.rs @@ -4,12 +4,13 @@ use crate::{ create_workspace, delete_workspace, read_workspace, + read_workspace_list, update_workspace, }, }; use actix_identity::Identity; use actix_web::{ - web::{Data, Payload}, + web::{Data, Path, Payload}, HttpResponse, }; use flowy_net::errors::ServerError; @@ -50,10 +51,17 @@ pub async fn delete_handler( pub async fn update_handler( payload: Payload, - _id: Identity, pool: Data, ) -> Result { let params: UpdateWorkspaceParams = parse_from_payload(payload).await?; let resp = update_workspace(pool.get_ref(), params).await?; Ok(resp.into()) } + +pub async fn workspace_list( + user_id: Path, + pool: Data, +) -> Result { + let resp = read_workspace_list(pool.get_ref(), &user_id).await?; + Ok(resp.into()) +} diff --git a/backend/src/workspace_service/workspace/workspace.rs b/backend/src/workspace_service/workspace/workspace.rs index 1a630222a5..e20c704c73 100644 --- a/backend/src/workspace_service/workspace/workspace.rs +++ b/backend/src/workspace_service/workspace/workspace.rs @@ -1,3 +1,4 @@ +use super::builder::Builder; use crate::{ entities::workspace::WorkspaceTable, sqlx_ext::*, @@ -10,19 +11,18 @@ use flowy_net::{ response::FlowyResponse, }; use flowy_user::entities::parser::UserId; + +use crate::workspace_service::workspace::{check_workspace_id, make_workspace_from_table}; use flowy_workspace::{ - entities::{ - app::RepeatedApp, - workspace::{ - parser::{WorkspaceDesc, WorkspaceId, WorkspaceName}, - Workspace, - }, - }, + entities::workspace::parser::{WorkspaceDesc, WorkspaceId, WorkspaceName}, protobuf::{ CreateWorkspaceParams, DeleteWorkspaceParams, QueryWorkspaceParams, + RepeatedApp, + RepeatedWorkspace, UpdateWorkspaceParams, + Workspace, }, }; use sqlx::{postgres::PgArguments, PgPool, Postgres, Transaction}; @@ -41,17 +41,9 @@ pub(crate) async fn create_workspace( .await .context("Failed to acquire a Postgres connection to create workspace")?; - let uuid = uuid::Uuid::new_v4(); - let time = Utc::now(); - - // TODO: use macro to fetch each field from struct - let (sql, args) = SqlBuilder::create("workspace_table") - .add_arg("id", uuid) - .add_arg("name", name.as_ref()) - .add_arg("description", desc.as_ref()) - .add_arg("modified_time", &time) - .add_arg("create_time", &time) - .add_arg("user_id", user_id.as_ref()) + let (sql, args, workspace) = Builder::new(user_id.as_ref()) + .name(name.as_ref()) + .desc(desc.as_ref()) .build()?; let _ = sqlx::query_with(&sql, args) @@ -64,14 +56,7 @@ pub(crate) async fn create_workspace( .await .context("Failed to commit SQL transaction to create workspace.")?; - let workspace = Workspace { - id: uuid.to_string(), - name: name.as_ref().to_owned(), - desc: desc.as_ref().to_owned(), - apps: RepeatedApp::default(), - }; - - FlowyResponse::success().data(workspace) + FlowyResponse::success().pb(workspace) } pub(crate) async fn read_workspace( @@ -94,9 +79,13 @@ pub(crate) async fn read_workspace( .await .map_err(map_sqlx_error)?; - let mut apps = RepeatedApp { items: vec![] }; + let mut repeated_app = RepeatedApp::default(); if params.read_apps { - apps.items = read_apps_belong_to_workspace(&mut transaction, &table.id.to_string()).await?; + repeated_app.set_items( + read_apps_belong_to_workspace(&mut transaction, &table.id.to_string()) + .await? + .into(), + ); } transaction @@ -104,10 +93,8 @@ pub(crate) async fn read_workspace( .await .context("Failed to commit SQL transaction to read workspace.")?; - let mut workspace = Workspace::new(table.id.to_string(), table.name, table.description); - workspace.apps = apps; - - FlowyResponse::success().data(workspace) + let workspace = make_workspace_from_table(table, Some(repeated_app)); + FlowyResponse::success().pb(workspace) } pub(crate) async fn update_workspace( @@ -186,8 +173,38 @@ pub(crate) async fn delete_workspace( Ok(FlowyResponse::success()) } -fn check_workspace_id(id: String) -> Result { - let workspace_id = WorkspaceId::parse(id).map_err(invalid_params)?; - let workspace_id = Uuid::parse_str(workspace_id.as_ref())?; - Ok(workspace_id) +pub async fn read_workspace_list( + pool: &PgPool, + user_id: &str, +) -> Result { + let mut transaction = pool + .begin() + .await + .context("Failed to acquire a Postgres connection to delete workspace")?; + + let (sql, args) = SqlBuilder::select("workspace_table") + .add_field("*") + .and_where_eq("user_id", user_id) + .build()?; + + let tables = sqlx::query_as_with::(&sql, args) + .fetch_all(&mut transaction) + .await + .map_err(map_sqlx_error)?; + + transaction + .commit() + .await + .context("Failed to commit SQL transaction to delete workspace.")?; + + let mut workspace = RepeatedWorkspace::default(); + workspace.set_items( + tables + .into_iter() + .map(|table| make_workspace_from_table(table, None)) + .collect::>() + .into(), + ); + + FlowyResponse::success().pb(workspace) } diff --git a/backend/tests/api/helper.rs b/backend/tests/api/helper.rs index 33007ee96a..2efc09a71d 100644 --- a/backend/tests/api/helper.rs +++ b/backend/tests/api/helper.rs @@ -83,6 +83,12 @@ impl TestApp { view } + pub async fn read_workspace_list(&self, user_id: &str) -> RepeatedWorkspace { + let url = format!("{}/api/workspace_list/{}", self.address, user_id); + let workspaces = read_workspace_list_request(&url).await.unwrap(); + workspaces + } + pub async fn update_view(&self, params: UpdateViewParams) { let url = format!("{}/api/view", self.address); update_view_request(params, &url).await.unwrap(); diff --git a/backend/tests/api/workspace.rs b/backend/tests/api/workspace.rs index b5ba06766c..7a65cc94a6 100644 --- a/backend/tests/api/workspace.rs +++ b/backend/tests/api/workspace.rs @@ -22,12 +22,7 @@ async fn workspace_create() { async fn workspace_read() { let app = spawn_app().await; let (workspace_1, _) = create_test_workspace(&app).await; - - let read_params = QueryWorkspaceParams { - workspace_id: workspace_1.id.clone(), - read_apps: false, - }; - + let read_params = QueryWorkspaceParams::new(&workspace_1.id); log::info!("{:?}", app.read_workspace(read_params).await.unwrap()); } @@ -39,11 +34,7 @@ async fn workspace_read_with_belongs() { let _ = create_test_app(&application, &workspace.id, &user_id).await; let _ = create_test_app(&application, &workspace.id, &user_id).await; - let read_params = QueryWorkspaceParams { - workspace_id: workspace.id.clone(), - read_apps: true, - }; - + let read_params = QueryWorkspaceParams::new(&workspace.id).read_apps(); let workspace = application.read_workspace(read_params).await.unwrap(); assert_eq!(workspace.apps.len(), 3); } @@ -108,13 +99,7 @@ async fn app_read() { let application = spawn_app().await; let (workspace, user_id) = create_test_workspace(&application).await; let app = create_test_app(&application, &workspace.id, &user_id).await; - - let read_params = QueryAppParams { - app_id: app.id, - read_belongings: false, - is_trash: false, - }; - + let read_params = QueryAppParams::new(&app.id); log::info!("{:?}", application.read_app(read_params).await.unwrap()); } @@ -127,38 +112,38 @@ async fn app_read_with_belongs() { let _ = create_test_view(&application, &app.id).await; let _ = create_test_view(&application, &app.id).await; - let read_params = QueryAppParams { - app_id: app.id, - read_belongings: true, - is_trash: false, - }; - + let read_params = QueryAppParams::new(&app.id).read_belongings(); let app = application.read_app(read_params).await.unwrap(); assert_eq!(app.belongings.len(), 2); } +#[actix_rt::test] +async fn app_read_with_belongs_in_trash() { + let application = spawn_app().await; + let (workspace, user_id) = create_test_workspace(&application).await; + let app = create_test_app(&application, &workspace.id, &user_id).await; + + let _ = create_test_view(&application, &app.id).await; + let view = create_test_view(&application, &app.id).await; + + let update_params = UpdateViewParams::new(&view.id).trash(); + application.update_view(update_params).await; + + let read_params = QueryAppParams::new(&app.id).read_belongings(); + let app = application.read_app(read_params).await.unwrap(); + assert_eq!(app.belongings.len(), 1); +} + #[actix_rt::test] async fn app_update() { let application = spawn_app().await; let (workspace, user_id) = create_test_workspace(&application).await; let app = create_test_app(&application, &workspace.id, &user_id).await; - let update_params = UpdateAppParams { - app_id: app.id.clone(), - workspace_id: None, - name: Some("flowy".to_owned()), - desc: None, - color_style: None, - is_trash: None, - }; + let update_params = UpdateAppParams::new(&app.id).name("flowy"); application.update_app(update_params).await; - let read_params = QueryAppParams { - app_id: app.id, - read_belongings: false, - is_trash: false, - }; - + let read_params = QueryAppParams::new(&app.id); let app = application.read_app(read_params).await.unwrap(); log::info!("{:?}", app); } @@ -174,12 +159,7 @@ async fn app_delete() { }; application.delete_app(delete_params).await; - let read_params = QueryAppParams { - app_id: app.id, - read_belongings: false, - is_trash: false, - }; - + let read_params = QueryAppParams::new(&app.id); assert_eq!(application.read_app(read_params).await.is_none(), true); } @@ -214,21 +194,13 @@ async fn view_update() { let view = create_test_view(&application, &app.id).await; // update - let update_params = UpdateViewParams { - view_id: view.id.clone(), - name: Some("new view name".to_string()), - desc: None, - thumbnail: None, - is_trash: Some(true), - }; + let update_params = UpdateViewParams::new(&view.id) + .trash() + .name("new view name"); application.update_view(update_params).await; // read - let read_params = QueryViewParams { - view_id: view.id.clone(), - is_trash: true, - read_belongings: false, - }; + let read_params = QueryViewParams::new(&view.id).trash(); let view = application.read_view(read_params).await; log::info!("{:?}", view); } @@ -247,11 +219,7 @@ async fn view_delete() { application.delete_view(delete_params).await; // read - let read_params = QueryViewParams { - view_id: view.id.clone(), - is_trash: true, - read_belongings: false, - }; + let read_params = QueryViewParams::new(&view.id).trash(); assert_eq!(application.read_view(read_params).await.is_none(), true); } @@ -266,3 +234,22 @@ async fn create_test_view(application: &TestApp, app_id: &str) -> View { let app = application.create_view(params).await; app } + +#[actix_rt::test] +async fn workspace_list_read() { + let application = spawn_app().await; + let response = application.register_test_user().await; + for i in 0..3 { + let params = CreateWorkspaceParams { + name: format!("{} workspace", i), + desc: format!("This is my {} workspace", i), + user_id: response.uid.clone(), + }; + let _ = application.create_workspace(params).await; + } + + let workspaces = application.read_workspace_list(&response.uid).await; + // 3 + 1 (created by default) + assert_eq!(workspaces.len(), 4); + log::info!("{:?}", workspaces); +} diff --git a/rust-lib/flowy-derive/src/derive_cache/derive_cache.rs b/rust-lib/flowy-derive/src/derive_cache/derive_cache.rs index 34cdfe19d5..d00c2e1040 100644 --- a/rust-lib/flowy-derive/src/derive_cache/derive_cache.rs +++ b/rust-lib/flowy-derive/src/derive_cache/derive_cache.rs @@ -35,7 +35,7 @@ pub fn category_from_str(type_str: &str) -> TypeCategory { | "CreateWorkspaceRequest" | "CreateWorkspaceParams" | "Workspace" - | "Workspaces" + | "RepeatedWorkspace" | "QueryWorkspaceRequest" | "QueryWorkspaceParams" | "CurrentWorkspace" diff --git a/rust-lib/flowy-net/src/request/request.rs b/rust-lib/flowy-net/src/request/request.rs index 7a6f110f06..ad1a7636bb 100644 --- a/rust-lib/flowy-net/src/request/request.rs +++ b/rust-lib/flowy-net/src/request/request.rs @@ -55,6 +55,10 @@ impl HttpRequestBuilder { T1: TryInto, { let body: Bytes = body.try_into()?; + self.bytes(body) + } + + pub fn bytes(mut self, body: Bytes) -> Result { self.body = Some(body); Ok(self) } diff --git a/rust-lib/flowy-net/src/response/response.rs b/rust-lib/flowy-net/src/response/response.rs index 7423f9569a..511a8adaac 100644 --- a/rust-lib/flowy-net/src/response/response.rs +++ b/rust-lib/flowy-net/src/response/response.rs @@ -24,6 +24,12 @@ impl FlowyResponse { self.data = bytes; Ok(self) } + + pub fn pb(mut self, data: T) -> Result { + let bytes: Bytes = Bytes::from(data.write_to_bytes()?); + self.data = bytes; + Ok(self) + } } impl std::convert::From for ServerError { diff --git a/rust-lib/flowy-workspace/src/entities/app/app_query.rs b/rust-lib/flowy-workspace/src/entities/app/app_query.rs index d356a11213..d98b6fede5 100644 --- a/rust-lib/flowy-workspace/src/entities/app/app_query.rs +++ b/rust-lib/flowy-workspace/src/entities/app/app_query.rs @@ -46,6 +46,25 @@ pub struct QueryAppParams { pub is_trash: bool, } +impl QueryAppParams { + pub fn new(app_id: &str) -> Self { + Self { + app_id: app_id.to_string(), + ..Default::default() + } + } + + pub fn read_belongings(mut self) -> Self { + self.read_belongings = true; + self + } + + pub fn trash(mut self) -> Self { + self.is_trash = true; + self + } +} + impl TryInto for QueryAppRequest { type Error = WorkspaceError; diff --git a/rust-lib/flowy-workspace/src/entities/app/app_update.rs b/rust-lib/flowy-workspace/src/entities/app/app_update.rs index 713bbc4538..32a94f5dd0 100644 --- a/rust-lib/flowy-workspace/src/entities/app/app_update.rs +++ b/rust-lib/flowy-workspace/src/entities/app/app_update.rs @@ -53,6 +53,30 @@ pub struct UpdateAppParams { pub is_trash: Option, } +impl UpdateAppParams { + pub fn new(app_id: &str) -> Self { + Self { + app_id: app_id.to_string(), + ..Default::default() + } + } + + pub fn name(mut self, name: &str) -> Self { + self.name = Some(name.to_string()); + self + } + + pub fn desc(mut self, desc: &str) -> Self { + self.desc = Some(desc.to_string()); + self + } + + pub fn trash(mut self) -> Self { + self.is_trash = Some(true); + self + } +} + impl TryInto for UpdateAppRequest { type Error = WorkspaceError; diff --git a/rust-lib/flowy-workspace/src/entities/view/view_query.rs b/rust-lib/flowy-workspace/src/entities/view/view_query.rs index f5e4a53f62..500053299f 100644 --- a/rust-lib/flowy-workspace/src/entities/view/view_query.rs +++ b/rust-lib/flowy-workspace/src/entities/view/view_query.rs @@ -44,6 +44,25 @@ pub struct QueryViewParams { pub read_belongings: bool, } +impl QueryViewParams { + pub fn new(view_id: &str) -> Self { + Self { + view_id: view_id.to_owned(), + ..Default::default() + } + } + + pub fn trash(mut self) -> Self { + self.is_trash = true; + self + } + + pub fn read_belongings(mut self) -> Self { + self.read_belongings = true; + self + } +} + impl TryInto for QueryViewRequest { type Error = WorkspaceError; diff --git a/rust-lib/flowy-workspace/src/entities/view/view_update.rs b/rust-lib/flowy-workspace/src/entities/view/view_update.rs index e7bf1618c2..1f50547e90 100644 --- a/rust-lib/flowy-workspace/src/entities/view/view_update.rs +++ b/rust-lib/flowy-workspace/src/entities/view/view_update.rs @@ -41,6 +41,29 @@ pub struct UpdateViewParams { pub is_trash: Option, } +impl UpdateViewParams { + pub fn new(view_id: &str) -> Self { + Self { + view_id: view_id.to_owned(), + ..Default::default() + } + } + + pub fn trash(mut self) -> Self { + self.is_trash = Some(true); + self + } + pub fn name(mut self, name: &str) -> Self { + self.name = Some(name.to_owned()); + self + } + + pub fn desc(mut self, desc: &str) -> Self { + self.desc = Some(desc.to_owned()); + self + } +} + impl TryInto for UpdateViewRequest { type Error = WorkspaceError; diff --git a/rust-lib/flowy-workspace/src/entities/workspace/workspace_create.rs b/rust-lib/flowy-workspace/src/entities/workspace/workspace_create.rs index 083ee1dc68..b82e041ea8 100644 --- a/rust-lib/flowy-workspace/src/entities/workspace/workspace_create.rs +++ b/rust-lib/flowy-workspace/src/entities/workspace/workspace_create.rs @@ -78,9 +78,9 @@ impl Workspace { } #[derive(PartialEq, Debug, Default, ProtoBuf)] -pub struct Workspaces { +pub struct RepeatedWorkspace { #[pb(index = 1)] pub items: Vec, } -impl_def_and_def_mut!(Workspaces, Workspace); +impl_def_and_def_mut!(RepeatedWorkspace, Workspace); diff --git a/rust-lib/flowy-workspace/src/entities/workspace/workspace_query.rs b/rust-lib/flowy-workspace/src/entities/workspace/workspace_query.rs index 577b14611d..4fdd0c2b51 100644 --- a/rust-lib/flowy-workspace/src/entities/workspace/workspace_query.rs +++ b/rust-lib/flowy-workspace/src/entities/workspace/workspace_query.rs @@ -20,6 +20,20 @@ pub struct QueryWorkspaceParams { pub read_apps: bool, } +impl QueryWorkspaceParams { + pub fn new(workspace_id: &str) -> Self { + Self { + workspace_id: workspace_id.to_owned(), + ..Default::default() + } + } + + pub fn read_apps(mut self) -> Self { + self.read_apps = true; + self + } +} + impl TryInto for QueryWorkspaceRequest { type Error = WorkspaceError; diff --git a/rust-lib/flowy-workspace/src/handlers/workspace_handler.rs b/rust-lib/flowy-workspace/src/handlers/workspace_handler.rs index 0853c6a8f3..1bf9bbe73f 100644 --- a/rust-lib/flowy-workspace/src/handlers/workspace_handler.rs +++ b/rust-lib/flowy-workspace/src/handlers/workspace_handler.rs @@ -44,8 +44,8 @@ pub async fn read_workspace( #[tracing::instrument(name = "get_all_workspaces", skip(controller))] pub async fn read_all_workspaces( controller: Unit>, -) -> DataResult { +) -> DataResult { let workspaces = controller.read_workspaces_belong_to_user().await?; - data_result(Workspaces { items: workspaces }) + data_result(RepeatedWorkspace { items: workspaces }) } diff --git a/rust-lib/flowy-workspace/src/protobuf/model/workspace_create.rs b/rust-lib/flowy-workspace/src/protobuf/model/workspace_create.rs index 348ec8eefa..ef6308cd6e 100644 --- a/rust-lib/flowy-workspace/src/protobuf/model/workspace_create.rs +++ b/rust-lib/flowy-workspace/src/protobuf/model/workspace_create.rs @@ -768,7 +768,7 @@ impl ::protobuf::reflect::ProtobufValue for Workspace { } #[derive(PartialEq,Clone,Default)] -pub struct Workspaces { +pub struct RepeatedWorkspace { // message fields pub items: ::protobuf::RepeatedField, // special fields @@ -776,14 +776,14 @@ pub struct Workspaces { pub cached_size: ::protobuf::CachedSize, } -impl<'a> ::std::default::Default for &'a Workspaces { - fn default() -> &'a Workspaces { - ::default_instance() +impl<'a> ::std::default::Default for &'a RepeatedWorkspace { + fn default() -> &'a RepeatedWorkspace { + ::default_instance() } } -impl Workspaces { - pub fn new() -> Workspaces { +impl RepeatedWorkspace { + pub fn new() -> RepeatedWorkspace { ::std::default::Default::default() } @@ -813,7 +813,7 @@ impl Workspaces { } } -impl ::protobuf::Message for Workspaces { +impl ::protobuf::Message for RepeatedWorkspace { fn is_initialized(&self) -> bool { for v in &self.items { if !v.is_initialized() { @@ -887,8 +887,8 @@ impl ::protobuf::Message for Workspaces { Self::descriptor_static() } - fn new() -> Workspaces { - Workspaces::new() + fn new() -> RepeatedWorkspace { + RepeatedWorkspace::new() } fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { @@ -897,37 +897,37 @@ impl ::protobuf::Message for Workspaces { let mut fields = ::std::vec::Vec::new(); fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( "items", - |m: &Workspaces| { &m.items }, - |m: &mut Workspaces| { &mut m.items }, + |m: &RepeatedWorkspace| { &m.items }, + |m: &mut RepeatedWorkspace| { &mut m.items }, )); - ::protobuf::reflect::MessageDescriptor::new_pb_name::( - "Workspaces", + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "RepeatedWorkspace", fields, file_descriptor_proto() ) }) } - fn default_instance() -> &'static Workspaces { - static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; - instance.get(Workspaces::new) + fn default_instance() -> &'static RepeatedWorkspace { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(RepeatedWorkspace::new) } } -impl ::protobuf::Clear for Workspaces { +impl ::protobuf::Clear for RepeatedWorkspace { fn clear(&mut self) { self.items.clear(); self.unknown_fields.clear(); } } -impl ::std::fmt::Debug for Workspaces { +impl ::std::fmt::Debug for RepeatedWorkspace { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { ::protobuf::text_format::fmt(self, f) } } -impl ::protobuf::reflect::ProtobufValue for Workspaces { +impl ::protobuf::reflect::ProtobufValue for RepeatedWorkspace { fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { ::protobuf::reflect::ReflectValueRef::Message(self) } @@ -941,43 +941,43 @@ static file_descriptor_proto_data: &'static [u8] = b"\ \tR\x04desc\x12\x17\n\x07user_id\x18\x03\x20\x01(\tR\x06userId\"e\n\tWor\ kspace\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04name\x18\ \x02\x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\ - \x12\x20\n\x04apps\x18\x04\x20\x01(\x0b2\x0c.RepeatedAppR\x04apps\".\n\n\ - Workspaces\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.WorkspaceR\x05item\ - sJ\xb1\x05\n\x06\x12\x04\0\0\x14\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\t\ - \n\x02\x03\0\x12\x03\x01\0\x1a\n\n\n\x02\x04\0\x12\x04\x03\0\x06\x01\n\n\ - \n\x03\x04\0\x01\x12\x03\x03\x08\x1e\n\x0b\n\x04\x04\0\x02\0\x12\x03\x04\ - \x04\x14\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\ - \x02\0\x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x04\ - \x12\x13\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x05\x04\x14\n\x0c\n\x05\x04\0\ - \x02\x01\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x05\ - \x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x05\x12\x13\n\n\n\x02\x04\ - \x01\x12\x04\x07\0\x0b\x01\n\n\n\x03\x04\x01\x01\x12\x03\x07\x08\x1d\n\ - \x0b\n\x04\x04\x01\x02\0\x12\x03\x08\x04\x14\n\x0c\n\x05\x04\x01\x02\0\ - \x05\x12\x03\x08\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x08\x0b\x0f\ - \n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x08\x12\x13\n\x0b\n\x04\x04\x01\ - \x02\x01\x12\x03\t\x04\x14\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\t\x04\ - \n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\t\x0b\x0f\n\x0c\n\x05\x04\x01\ - \x02\x01\x03\x12\x03\t\x12\x13\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\n\x04\ - \x17\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\x01\ - \x02\x02\x01\x12\x03\n\x0b\x12\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\n\ - \x15\x16\n\n\n\x02\x04\x02\x12\x04\x0c\0\x11\x01\n\n\n\x03\x04\x02\x01\ - \x12\x03\x0c\x08\x11\n\x0b\n\x04\x04\x02\x02\0\x12\x03\r\x04\x12\n\x0c\n\ - \x05\x04\x02\x02\0\x05\x12\x03\r\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\ - \x03\r\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\r\x10\x11\n\x0b\n\x04\ - \x04\x02\x02\x01\x12\x03\x0e\x04\x14\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\ - \x03\x0e\x04\n\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\x0e\x0b\x0f\n\x0c\ - \n\x05\x04\x02\x02\x01\x03\x12\x03\x0e\x12\x13\n\x0b\n\x04\x04\x02\x02\ - \x02\x12\x03\x0f\x04\x14\n\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\x0f\x04\ - \n\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\x0f\x0b\x0f\n\x0c\n\x05\x04\ - \x02\x02\x02\x03\x12\x03\x0f\x12\x13\n\x0b\n\x04\x04\x02\x02\x03\x12\x03\ - \x10\x04\x19\n\x0c\n\x05\x04\x02\x02\x03\x06\x12\x03\x10\x04\x0f\n\x0c\n\ - \x05\x04\x02\x02\x03\x01\x12\x03\x10\x10\x14\n\x0c\n\x05\x04\x02\x02\x03\ - \x03\x12\x03\x10\x17\x18\n\n\n\x02\x04\x03\x12\x04\x12\0\x14\x01\n\n\n\ - \x03\x04\x03\x01\x12\x03\x12\x08\x12\n\x0b\n\x04\x04\x03\x02\0\x12\x03\ - \x13\x04!\n\x0c\n\x05\x04\x03\x02\0\x04\x12\x03\x13\x04\x0c\n\x0c\n\x05\ - \x04\x03\x02\0\x06\x12\x03\x13\r\x16\n\x0c\n\x05\x04\x03\x02\0\x01\x12\ - \x03\x13\x17\x1c\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x13\x1f\x20b\x06p\ - roto3\ + \x12\x20\n\x04apps\x18\x04\x20\x01(\x0b2\x0c.RepeatedAppR\x04apps\"5\n\ + \x11RepeatedWorkspace\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.Workspa\ + ceR\x05itemsJ\xb1\x05\n\x06\x12\x04\0\0\x14\x01\n\x08\n\x01\x0c\x12\x03\ + \0\0\x12\n\t\n\x02\x03\0\x12\x03\x01\0\x1a\n\n\n\x02\x04\0\x12\x04\x03\0\ + \x06\x01\n\n\n\x03\x04\0\x01\x12\x03\x03\x08\x1e\n\x0b\n\x04\x04\0\x02\0\ + \x12\x03\x04\x04\x14\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x04\x04\n\n\x0c\ + \n\x05\x04\0\x02\0\x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\0\x02\0\x03\ + \x12\x03\x04\x12\x13\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x05\x04\x14\n\x0c\ + \n\x05\x04\0\x02\x01\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\ + \x12\x03\x05\x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x05\x12\x13\n\ + \n\n\x02\x04\x01\x12\x04\x07\0\x0b\x01\n\n\n\x03\x04\x01\x01\x12\x03\x07\ + \x08\x1d\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x08\x04\x14\n\x0c\n\x05\x04\ + \x01\x02\0\x05\x12\x03\x08\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\ + \x08\x0b\x0f\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x08\x12\x13\n\x0b\n\ + \x04\x04\x01\x02\x01\x12\x03\t\x04\x14\n\x0c\n\x05\x04\x01\x02\x01\x05\ + \x12\x03\t\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\t\x0b\x0f\n\x0c\ + \n\x05\x04\x01\x02\x01\x03\x12\x03\t\x12\x13\n\x0b\n\x04\x04\x01\x02\x02\ + \x12\x03\n\x04\x17\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\n\x04\n\n\x0c\ + \n\x05\x04\x01\x02\x02\x01\x12\x03\n\x0b\x12\n\x0c\n\x05\x04\x01\x02\x02\ + \x03\x12\x03\n\x15\x16\n\n\n\x02\x04\x02\x12\x04\x0c\0\x11\x01\n\n\n\x03\ + \x04\x02\x01\x12\x03\x0c\x08\x11\n\x0b\n\x04\x04\x02\x02\0\x12\x03\r\x04\ + \x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\r\x04\n\n\x0c\n\x05\x04\x02\ + \x02\0\x01\x12\x03\r\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\r\x10\ + \x11\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\x0e\x04\x14\n\x0c\n\x05\x04\x02\ + \x02\x01\x05\x12\x03\x0e\x04\n\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\ + \x0e\x0b\x0f\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\x0e\x12\x13\n\x0b\n\ + \x04\x04\x02\x02\x02\x12\x03\x0f\x04\x14\n\x0c\n\x05\x04\x02\x02\x02\x05\ + \x12\x03\x0f\x04\n\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\x0f\x0b\x0f\n\ + \x0c\n\x05\x04\x02\x02\x02\x03\x12\x03\x0f\x12\x13\n\x0b\n\x04\x04\x02\ + \x02\x03\x12\x03\x10\x04\x19\n\x0c\n\x05\x04\x02\x02\x03\x06\x12\x03\x10\ + \x04\x0f\n\x0c\n\x05\x04\x02\x02\x03\x01\x12\x03\x10\x10\x14\n\x0c\n\x05\ + \x04\x02\x02\x03\x03\x12\x03\x10\x17\x18\n\n\n\x02\x04\x03\x12\x04\x12\0\ + \x14\x01\n\n\n\x03\x04\x03\x01\x12\x03\x12\x08\x19\n\x0b\n\x04\x04\x03\ + \x02\0\x12\x03\x13\x04!\n\x0c\n\x05\x04\x03\x02\0\x04\x12\x03\x13\x04\ + \x0c\n\x0c\n\x05\x04\x03\x02\0\x06\x12\x03\x13\r\x16\n\x0c\n\x05\x04\x03\ + \x02\0\x01\x12\x03\x13\x17\x1c\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x13\ + \x1f\x20b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/rust-lib/flowy-workspace/src/protobuf/proto/workspace_create.proto b/rust-lib/flowy-workspace/src/protobuf/proto/workspace_create.proto index 06871e2ff4..efbc95e362 100644 --- a/rust-lib/flowy-workspace/src/protobuf/proto/workspace_create.proto +++ b/rust-lib/flowy-workspace/src/protobuf/proto/workspace_create.proto @@ -16,6 +16,6 @@ message Workspace { string desc = 3; RepeatedApp apps = 4; } -message Workspaces { +message RepeatedWorkspace { repeated Workspace items = 1; } diff --git a/rust-lib/flowy-workspace/src/services/workspace_controller.rs b/rust-lib/flowy-workspace/src/services/workspace_controller.rs index 782f1d1912..259c29699c 100644 --- a/rust-lib/flowy-workspace/src/services/workspace_controller.rs +++ b/rust-lib/flowy-workspace/src/services/workspace_controller.rs @@ -169,9 +169,18 @@ pub async fn delete_workspace_request( params: DeleteWorkspaceParams, url: &str, ) -> Result<(), WorkspaceError> { - let _ = HttpRequestBuilder::delete(&url.to_owned()) + let _ = HttpRequestBuilder::delete(url) .protobuf(params)? .send() .await?; Ok(()) } + +pub async fn read_workspace_list_request(url: &str) -> Result { + let workspaces = HttpRequestBuilder::get(url) + .send() + .await? + .response::() + .await?; + Ok(workspaces) +} diff --git a/rust-lib/flowy-workspace/tests/event/workspace_test.rs b/rust-lib/flowy-workspace/tests/event/workspace_test.rs index e9d973004d..d06dcf0543 100644 --- a/rust-lib/flowy-workspace/tests/event/workspace_test.rs +++ b/rust-lib/flowy-workspace/tests/event/workspace_test.rs @@ -1,6 +1,11 @@ use crate::helper::*; use flowy_workspace::{ - entities::workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest, Workspace, Workspaces}, + entities::workspace::{ + CreateWorkspaceRequest, + QueryWorkspaceRequest, + RepeatedWorkspace, + Workspace, + }, event::WorkspaceEvent::*, prelude::*, }; @@ -23,7 +28,7 @@ fn workspace_read_all_success() { let workspaces = SingleUserTestBuilder::new() .event(ReadAllWorkspace) .sync_send() - .parse::(); + .parse::(); dbg!(&workspaces); } diff --git a/scripts/flowy-tool/src/main.rs b/scripts/flowy-tool/src/main.rs index 7259651f6e..752192301b 100644 --- a/scripts/flowy-tool/src/main.rs +++ b/scripts/flowy-tool/src/main.rs @@ -1,7 +1,7 @@ mod config; - +mod dart_event; +mod proto; mod util; - use clap::{App, Arg}; fn main() {