create default workspace

This commit is contained in:
appflowy 2021-08-26 17:58:59 +08:00
parent 0734e53f07
commit dd2cec28e4
36 changed files with 775 additions and 328 deletions

View File

@ -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<Workspace>(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<Workspace>? 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<Workspaces> createRepeated() => $pb.PbList<Workspaces>();
static RepeatedWorkspace create() => RepeatedWorkspace._();
RepeatedWorkspace createEmptyInstance() => create();
static $pb.PbList<RepeatedWorkspace> createRepeated() => $pb.PbList<RepeatedWorkspace>();
@$core.pragma('dart2js:noInline')
static Workspaces getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Workspaces>(create);
static Workspaces? _defaultInstance;
static RepeatedWorkspace getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RepeatedWorkspace>(create);
static RepeatedWorkspace? _defaultInstance;
@$pb.TagNumber(1)
$core.List<Workspace> get items => $_getList(0);

View File

@ -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=');

View File

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

View File

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

View File

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

View File

@ -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<UserTable, ServerError> {
let user = sqlx::query_as::<Postgres, UserTable>("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)
}

View File

@ -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<FlowyResponse, ServerError> {
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<FlowyRespo
// transaction must be commit from caller
pub(crate) async fn read_apps_belong_to_workspace<'c>(
transaction: &mut Transaction<'c, Postgres>,
transaction: &mut DBTransaction<'_>,
workspace_id: &str,
) -> Result<Vec<App>, 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::<Vec<App>>();
Ok(apps)
}
fn check_app_id(id: String) -> Result<Uuid, ServerError> {
let app_id = AppId::parse(id).map_err(invalid_params)?;
let app_id = Uuid::parse_str(app_id.as_ref())?;
Ok(app_id)
}

View File

@ -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<u8> {
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<Uuid, ServerError> {
let app_id = AppId::parse(id).map_err(invalid_params)?;
let app_id = Uuid::parse_str(app_id.as_ref())?;
Ok(app_id)
}

View File

@ -1,2 +1,5 @@
pub mod app;
pub mod router;
mod builder;
pub use builder::*;

View File

@ -1,3 +1,4 @@
pub mod app;
pub mod user_default;
pub mod view;
pub mod workspace;

View File

@ -0,0 +1,3 @@
mod user_default;
pub use user_default::*;

View File

@ -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<Workspace, ServerError> {
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<Workspace, ServerError> {
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<App, ServerError> {
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<View, ServerError> {
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)
}

View File

@ -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<Uuid, ServerError> {
let view_id = ViewId::parse(id).map_err(invalid_params)?;
let view_id = Uuid::parse_str(view_id.as_ref())?;
Ok(view_id)
}

View File

@ -1,4 +1,6 @@
mod builder;
pub mod router;
mod view;
pub use builder::*;
pub use view::*;

View File

@ -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<Vec<View>, 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::<Postgres, ViewTable, PgArguments>(&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::<Vec<View>>();
Ok(views)
}
fn check_view_id(id: String) -> Result<Uuid, ServerError> {
let view_id = ViewId::parse(id).map_err(invalid_params)?;
let view_id = Uuid::parse_str(view_id.as_ref())?;
Ok(view_id)
}

View File

@ -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<RepeatedApp>,
) -> 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<Uuid, ServerError> {
let workspace_id = WorkspaceId::parse(id).map_err(invalid_params)?;
let workspace_id = Uuid::parse_str(workspace_id.as_ref())?;
Ok(workspace_id)
}

View File

@ -1,4 +1,6 @@
pub mod builder;
pub mod router;
mod workspace;
pub use builder::*;
pub use workspace::*;

View File

@ -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<PgPool>,
) -> Result<HttpResponse, ServerError> {
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<String>,
pool: Data<PgPool>,
) -> Result<HttpResponse, ServerError> {
let resp = read_workspace_list(pool.get_ref(), &user_id).await?;
Ok(resp.into())
}

View File

@ -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<Uuid, ServerError> {
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<FlowyResponse, ServerError> {
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::<Postgres, WorkspaceTable, PgArguments>(&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::<Vec<Workspace>>()
.into(),
);
FlowyResponse::success().pb(workspace)
}

View File

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

View File

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

View File

@ -35,7 +35,7 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
| "CreateWorkspaceRequest"
| "CreateWorkspaceParams"
| "Workspace"
| "Workspaces"
| "RepeatedWorkspace"
| "QueryWorkspaceRequest"
| "QueryWorkspaceParams"
| "CurrentWorkspace"

View File

@ -55,6 +55,10 @@ impl HttpRequestBuilder {
T1: TryInto<Bytes, Error = ProtobufError>,
{
let body: Bytes = body.try_into()?;
self.bytes(body)
}
pub fn bytes(mut self, body: Bytes) -> Result<Self, ServerError> {
self.body = Some(body);
Ok(self)
}

View File

@ -24,6 +24,12 @@ impl FlowyResponse {
self.data = bytes;
Ok(self)
}
pub fn pb<T: ::protobuf::Message>(mut self, data: T) -> Result<Self, ServerError> {
let bytes: Bytes = Bytes::from(data.write_to_bytes()?);
self.data = bytes;
Ok(self)
}
}
impl std::convert::From<protobuf::ProtobufError> for ServerError {

View File

@ -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<QueryAppParams> for QueryAppRequest {
type Error = WorkspaceError;

View File

@ -53,6 +53,30 @@ pub struct UpdateAppParams {
pub is_trash: Option<bool>,
}
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<UpdateAppParams> for UpdateAppRequest {
type Error = WorkspaceError;

View File

@ -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<QueryViewParams> for QueryViewRequest {
type Error = WorkspaceError;

View File

@ -41,6 +41,29 @@ pub struct UpdateViewParams {
pub is_trash: Option<bool>,
}
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<UpdateViewParams> for UpdateViewRequest {
type Error = WorkspaceError;

View File

@ -78,9 +78,9 @@ impl Workspace {
}
#[derive(PartialEq, Debug, Default, ProtoBuf)]
pub struct Workspaces {
pub struct RepeatedWorkspace {
#[pb(index = 1)]
pub items: Vec<Workspace>,
}
impl_def_and_def_mut!(Workspaces, Workspace);
impl_def_and_def_mut!(RepeatedWorkspace, Workspace);

View File

@ -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<QueryWorkspaceParams> for QueryWorkspaceRequest {
type Error = WorkspaceError;

View File

@ -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<Arc<WorkspaceController>>,
) -> DataResult<Workspaces, WorkspaceError> {
) -> DataResult<RepeatedWorkspace, WorkspaceError> {
let workspaces = controller.read_workspaces_belong_to_user().await?;
data_result(Workspaces { items: workspaces })
data_result(RepeatedWorkspace { items: workspaces })
}

View File

@ -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<Workspace>,
// 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 {
<Workspaces as ::protobuf::Message>::default_instance()
impl<'a> ::std::default::Default for &'a RepeatedWorkspace {
fn default() -> &'a RepeatedWorkspace {
<RepeatedWorkspace as ::protobuf::Message>::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<Workspace>>(
"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>(
"Workspaces",
::protobuf::reflect::MessageDescriptor::new_pb_name::<RepeatedWorkspace>(
"RepeatedWorkspace",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static Workspaces {
static instance: ::protobuf::rt::LazyV2<Workspaces> = ::protobuf::rt::LazyV2::INIT;
instance.get(Workspaces::new)
fn default_instance() -> &'static RepeatedWorkspace {
static instance: ::protobuf::rt::LazyV2<RepeatedWorkspace> = ::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;

View File

@ -16,6 +16,6 @@ message Workspace {
string desc = 3;
RepeatedApp apps = 4;
}
message Workspaces {
message RepeatedWorkspace {
repeated Workspace items = 1;
}

View File

@ -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<RepeatedWorkspace, WorkspaceError> {
let workspaces = HttpRequestBuilder::get(url)
.send()
.await?
.response::<RepeatedWorkspace>()
.await?;
Ok(workspaces)
}

View File

@ -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::<Workspaces>();
.parse::<RepeatedWorkspace>();
dbg!(&workspaces);
}

View File

@ -1,7 +1,7 @@
mod config;
mod dart_event;
mod proto;
mod util;
use clap::{App, Arg};
fn main() {