Ver código fonte

workspace and app dto

appflowy 3 anos atrás
pai
commit
df1d84d43d
63 arquivos alterados com 1454 adições e 148 exclusões
  1. 6 1
      app_flowy/packages/flowy_sdk/lib/protobuf/dart-ffi/ffi_request.pbjson.dart
  2. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/dart-ffi/ffi_request.pbserver.dart
  3. 9 1
      app_flowy/packages/flowy_sdk/lib/protobuf/dart-ffi/ffi_response.pbjson.dart
  4. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/dart-ffi/ffi_response.pbserver.dart
  5. 6 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-infra/kv.pbjson.dart
  6. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-infra/kv.pbserver.dart
  7. 9 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbjson.dart
  8. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbserver.dart
  9. 6 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/event.pbjson.dart
  10. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/event.pbserver.dart
  11. 9 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/sign_in.pbjson.dart
  12. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/sign_in.pbserver.dart
  13. 12 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/sign_up.pbjson.dart
  14. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/sign_up.pbserver.dart
  15. 9 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_detail.pbjson.dart
  16. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_detail.pbserver.dart
  17. 6 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_table.pbjson.dart
  18. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_table.pbserver.dart
  19. 9 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_create.pbjson.dart
  20. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_create.pbserver.dart
  21. 170 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_update.pb.dart
  22. 7 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_update.pbenum.dart
  23. 30 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_update.pbjson.dart
  24. 9 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_update.pbserver.dart
  25. 5 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbenum.dart
  26. 12 2
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbjson.dart
  27. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbserver.dart
  28. 1 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/protobuf.dart
  29. 6 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbjson.dart
  30. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbserver.dart
  31. 26 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_update.pb.dart
  32. 12 3
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_update.pbjson.dart
  33. 1 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_update.pbserver.dart
  34. 4 0
      rust-lib/flowy-database/src/lib.rs
  35. 1 0
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  36. 1 1
      rust-lib/flowy-derive/src/proto_buf/deserialize.rs
  37. 29 23
      rust-lib/flowy-derive/src/proto_buf/serialize.rs
  38. 2 0
      rust-lib/flowy-sdk/Cargo.toml
  39. 8 3
      rust-lib/flowy-sdk/src/module.rs
  40. 7 0
      rust-lib/flowy-user/src/services/user_session/user_session.rs
  41. 16 0
      rust-lib/flowy-workspace/src/entities/app/app_id.rs
  42. 1 1
      rust-lib/flowy-workspace/src/entities/app/app_name.rs
  43. 96 0
      rust-lib/flowy-workspace/src/entities/app/app_update.rs
  44. 3 0
      rust-lib/flowy-workspace/src/entities/app/mod.rs
  45. 1 1
      rust-lib/flowy-workspace/src/entities/workspace/workspace_name.rs
  46. 19 12
      rust-lib/flowy-workspace/src/entities/workspace/workspace_update.rs
  47. 8 2
      rust-lib/flowy-workspace/src/errors.rs
  48. 5 0
      rust-lib/flowy-workspace/src/lib.rs
  49. 559 0
      rust-lib/flowy-workspace/src/protobuf/model/app_update.rs
  50. 35 23
      rust-lib/flowy-workspace/src/protobuf/model/errors.rs
  51. 3 0
      rust-lib/flowy-workspace/src/protobuf/model/mod.rs
  52. 127 46
      rust-lib/flowy-workspace/src/protobuf/model/workspace_update.rs
  53. 10 0
      rust-lib/flowy-workspace/src/protobuf/proto/app_update.proto
  54. 3 1
      rust-lib/flowy-workspace/src/protobuf/proto/errors.proto
  55. 2 2
      rust-lib/flowy-workspace/src/protobuf/proto/workspace_update.proto
  56. 0 0
      rust-lib/flowy-workspace/src/services/database.rs
  57. 4 0
      rust-lib/flowy-workspace/src/services/mod.rs
  58. 81 0
      rust-lib/flowy-workspace/src/services/workspace_controller.rs
  59. 51 1
      rust-lib/flowy-workspace/src/sql_tables/app/app.rs
  60. 1 0
      rust-lib/flowy-workspace/src/sql_tables/mod.rs
  61. 3 0
      rust-lib/flowy-workspace/src/sql_tables/view/mod.rs
  62. 0 0
      rust-lib/flowy-workspace/src/sql_tables/view/view.rs
  63. 2 2
      rust-lib/flowy-workspace/src/sql_tables/workspace/workspace.rs

+ 6 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/dart-ffi/ffi_request.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: ffi_request.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use fFIRequestDescriptor instead')
 const FFIRequest$json = const {
   '1': 'FFIRequest',
   '2': const [
@@ -14,3 +17,5 @@ const FFIRequest$json = const {
   ],
 };
 
+/// Descriptor for `FFIRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List fFIRequestDescriptor = $convert.base64Decode('CgpGRklSZXF1ZXN0EhQKBWV2ZW50GAEgASgJUgVldmVudBIYCgdwYXlsb2FkGAIgASgMUgdwYXlsb2Fk');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/dart-ffi/ffi_request.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: ffi_request.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'ffi_request.pb.dart';
 

+ 9 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/dart-ffi/ffi_response.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: ffi_response.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use fFIStatusCodeDescriptor instead')
 const FFIStatusCode$json = const {
   '1': 'FFIStatusCode',
   '2': const [
@@ -15,6 +18,9 @@ const FFIStatusCode$json = const {
   ],
 };
 
+/// Descriptor for `FFIStatusCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List fFIStatusCodeDescriptor = $convert.base64Decode('Cg1GRklTdGF0dXNDb2RlEgsKB1Vua25vd24QABIGCgJPaxABEgcKA0VychAC');
+@$core.Deprecated('Use fFIResponseDescriptor instead')
 const FFIResponse$json = const {
   '1': 'FFIResponse',
   '2': const [
@@ -23,3 +29,5 @@ const FFIResponse$json = const {
   ],
 };
 
+/// Descriptor for `FFIResponse`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List fFIResponseDescriptor = $convert.base64Decode('CgtGRklSZXNwb25zZRIYCgdwYXlsb2FkGAEgASgMUgdwYXlsb2FkEiIKBGNvZGUYAiABKA4yDi5GRklTdGF0dXNDb2RlUgRjb2Rl');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/dart-ffi/ffi_response.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: ffi_response.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'ffi_response.pb.dart';
 

+ 6 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-infra/kv.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: kv.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use keyValueDescriptor instead')
 const KeyValue$json = const {
   '1': 'KeyValue',
   '2': const [
@@ -23,3 +26,5 @@ const KeyValue$json = const {
   ],
 };
 
+/// Descriptor for `KeyValue`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List keyValueDescriptor = $convert.base64Decode('CghLZXlWYWx1ZRIQCgNrZXkYASABKAlSA2tleRIdCglzdHJfdmFsdWUYAiABKAlIAFIIc3RyVmFsdWUSHQoJaW50X3ZhbHVlGAMgASgDSAFSCGludFZhbHVlEiEKC2Zsb2F0X3ZhbHVlGAQgASgBSAJSCmZsb2F0VmFsdWUSHwoKYm9vbF92YWx1ZRgFIAEoCEgDUglib29sVmFsdWVCEgoQb25lX29mX3N0cl92YWx1ZUISChBvbmVfb2ZfaW50X3ZhbHVlQhQKEm9uZV9vZl9mbG9hdF92YWx1ZUITChFvbmVfb2ZfYm9vbF92YWx1ZQ==');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-infra/kv.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: kv.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'kv.pb.dart';
 

+ 9 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: errors.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use userErrorCodeDescriptor instead')
 const UserErrorCode$json = const {
   '1': 'UserErrorCode',
   '2': const [
@@ -24,6 +27,9 @@ const UserErrorCode$json = const {
   ],
 };
 
+/// Descriptor for `UserErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List userErrorCodeDescriptor = $convert.base64Decode('Cg1Vc2VyRXJyb3JDb2RlEgsKB1Vua25vd24QABIWChJEYXRhYmFzZUluaXRGYWlsZWQQARIXChNEYXRhYmFzZVdyaXRlTG9ja2VkEAISFgoSRGF0YWJhc2VSZWFkTG9ja2VkEAMSGwoXRGF0YWJhc2VVc2VyRGlkTm90TWF0Y2gQBBIZChVEYXRhYmFzZUludGVybmFsRXJyb3IQBRITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbEludmFsaWQQFBITCg9QYXNzd29yZEludmFsaWQQFRITCg9Vc2VyTmFtZUludmFsaWQQFg==');
+@$core.Deprecated('Use userErrorDescriptor instead')
 const UserError$json = const {
   '1': 'UserError',
   '2': const [
@@ -32,3 +38,5 @@ const UserError$json = const {
   ],
 };
 
+/// Descriptor for `UserError`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List userErrorDescriptor = $convert.base64Decode('CglVc2VyRXJyb3ISIgoEY29kZRgBIAEoDjIOLlVzZXJFcnJvckNvZGVSBGNvZGUSEAoDbXNnGAIgASgJUgNtc2c=');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/errors.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: errors.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'errors.pb.dart';
 

+ 6 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/event.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: event.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use userEventDescriptor instead')
 const UserEvent$json = const {
   '1': 'UserEvent',
   '2': const [
@@ -16,3 +19,5 @@ const UserEvent$json = const {
   ],
 };
 
+/// Descriptor for `UserEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List userEventDescriptor = $convert.base64Decode('CglVc2VyRXZlbnQSDQoJR2V0U3RhdHVzEAASCgoGU2lnbkluEAESCgoGU2lnblVwEAISCwoHU2lnbk91dBAD');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/event.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: event.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'event.pb.dart';
 

+ 9 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/sign_in.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: sign_in.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use signInRequestDescriptor instead')
 const SignInRequest$json = const {
   '1': 'SignInRequest',
   '2': const [
@@ -14,6 +17,9 @@ const SignInRequest$json = const {
   ],
 };
 
+/// Descriptor for `SignInRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List signInRequestDescriptor = $convert.base64Decode('Cg1TaWduSW5SZXF1ZXN0EhQKBWVtYWlsGAEgASgJUgVlbWFpbBIaCghwYXNzd29yZBgCIAEoCVIIcGFzc3dvcmQ=');
+@$core.Deprecated('Use signInParamsDescriptor instead')
 const SignInParams$json = const {
   '1': 'SignInParams',
   '2': const [
@@ -22,3 +28,5 @@ const SignInParams$json = const {
   ],
 };
 
+/// Descriptor for `SignInParams`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List signInParamsDescriptor = $convert.base64Decode('CgxTaWduSW5QYXJhbXMSFAoFZW1haWwYASABKAlSBWVtYWlsEhoKCHBhc3N3b3JkGAIgASgJUghwYXNzd29yZA==');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/sign_in.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: sign_in.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'sign_in.pb.dart';
 

+ 12 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/sign_up.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: sign_up.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use signUpRequestDescriptor instead')
 const SignUpRequest$json = const {
   '1': 'SignUpRequest',
   '2': const [
@@ -15,6 +18,9 @@ const SignUpRequest$json = const {
   ],
 };
 
+/// Descriptor for `SignUpRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List signUpRequestDescriptor = $convert.base64Decode('Cg1TaWduVXBSZXF1ZXN0EhQKBWVtYWlsGAEgASgJUgVlbWFpbBISCgRuYW1lGAIgASgJUgRuYW1lEhoKCHBhc3N3b3JkGAMgASgJUghwYXNzd29yZA==');
+@$core.Deprecated('Use signUpParamsDescriptor instead')
 const SignUpParams$json = const {
   '1': 'SignUpParams',
   '2': const [
@@ -24,6 +30,9 @@ const SignUpParams$json = const {
   ],
 };
 
+/// Descriptor for `SignUpParams`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List signUpParamsDescriptor = $convert.base64Decode('CgxTaWduVXBQYXJhbXMSFAoFZW1haWwYASABKAlSBWVtYWlsEhIKBG5hbWUYAiABKAlSBG5hbWUSGgoIcGFzc3dvcmQYAyABKAlSCHBhc3N3b3Jk');
+@$core.Deprecated('Use signUpResponseDescriptor instead')
 const SignUpResponse$json = const {
   '1': 'SignUpResponse',
   '2': const [
@@ -31,3 +40,5 @@ const SignUpResponse$json = const {
   ],
 };
 
+/// Descriptor for `SignUpResponse`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List signUpResponseDescriptor = $convert.base64Decode('Cg5TaWduVXBSZXNwb25zZRIdCgppc19zdWNjZXNzGAEgASgIUglpc1N1Y2Nlc3M=');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/sign_up.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: sign_up.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'sign_up.pb.dart';
 

+ 9 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_detail.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: user_detail.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use userStatusDescriptor instead')
 const UserStatus$json = const {
   '1': 'UserStatus',
   '2': const [
@@ -15,6 +18,9 @@ const UserStatus$json = const {
   ],
 };
 
+/// Descriptor for `UserStatus`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List userStatusDescriptor = $convert.base64Decode('CgpVc2VyU3RhdHVzEgsKB1Vua25vd24QABIJCgVMb2dpbhABEgsKB0V4cGlyZWQQAg==');
+@$core.Deprecated('Use userDetailDescriptor instead')
 const UserDetail$json = const {
   '1': 'UserDetail',
   '2': const [
@@ -24,3 +30,5 @@ const UserDetail$json = const {
   ],
 };
 
+/// Descriptor for `UserDetail`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List userDetailDescriptor = $convert.base64Decode('CgpVc2VyRGV0YWlsEhQKBWVtYWlsGAEgASgJUgVlbWFpbBISCgRuYW1lGAIgASgJUgRuYW1lEiMKBnN0YXR1cxgDIAEoDjILLlVzZXJTdGF0dXNSBnN0YXR1cw==');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_detail.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: user_detail.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'user_detail.pb.dart';
 

+ 6 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_table.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: user_table.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use userDescriptor instead')
 const User$json = const {
   '1': 'User',
   '2': const [
@@ -16,3 +19,5 @@ const User$json = const {
   ],
 };
 
+/// Descriptor for `User`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List userDescriptor = $convert.base64Decode('CgRVc2VyEg4KAmlkGAEgASgJUgJpZBISCgRuYW1lGAIgASgJUgRuYW1lEhQKBWVtYWlsGAMgASgJUgVlbWFpbBIaCghwYXNzd29yZBgEIAEoCVIIcGFzc3dvcmQ=');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-user/user_table.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: user_table.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'user_table.pb.dart';
 

+ 9 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_create.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: app_create.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use createAppRequestDescriptor instead')
 const CreateAppRequest$json = const {
   '1': 'CreateAppRequest',
   '2': const [
@@ -16,6 +19,9 @@ const CreateAppRequest$json = const {
   ],
 };
 
+/// Descriptor for `CreateAppRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List createAppRequestDescriptor = $convert.base64Decode('ChBDcmVhdGVBcHBSZXF1ZXN0EiEKDHdvcmtzcGFjZV9pZBgBIAEoCVILd29ya3NwYWNlSWQSEgoEbmFtZRgCIAEoCVIEbmFtZRISCgRkZXNjGAMgASgJUgRkZXNjEiwKC2NvbG9yX3N0eWxlGAQgASgLMgsuQ29sb3JTdHlsZVIKY29sb3JTdHlsZQ==');
+@$core.Deprecated('Use colorStyleDescriptor instead')
 const ColorStyle$json = const {
   '1': 'ColorStyle',
   '2': const [
@@ -23,3 +29,5 @@ const ColorStyle$json = const {
   ],
 };
 
+/// Descriptor for `ColorStyle`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List colorStyleDescriptor = $convert.base64Decode('CgpDb2xvclN0eWxlEh8KC3RoZW1lX2NvbG9yGAEgASgJUgp0aGVtZUNvbG9y');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_create.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: app_create.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'app_create.pb.dart';
 

+ 170 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_update.pb.dart

@@ -0,0 +1,170 @@
+///
+//  Generated code. Do not modify.
+//  source: app_update.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+import 'app_create.pb.dart' as $0;
+
+enum UpdateAppRequest_OneOfWorkspaceId {
+  workspaceId, 
+  notSet
+}
+
+enum UpdateAppRequest_OneOfName {
+  name, 
+  notSet
+}
+
+enum UpdateAppRequest_OneOfDesc {
+  desc, 
+  notSet
+}
+
+enum UpdateAppRequest_OneOfColorStyle {
+  colorStyle, 
+  notSet
+}
+
+class UpdateAppRequest extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, UpdateAppRequest_OneOfWorkspaceId> _UpdateAppRequest_OneOfWorkspaceIdByTag = {
+    2 : UpdateAppRequest_OneOfWorkspaceId.workspaceId,
+    0 : UpdateAppRequest_OneOfWorkspaceId.notSet
+  };
+  static const $core.Map<$core.int, UpdateAppRequest_OneOfName> _UpdateAppRequest_OneOfNameByTag = {
+    3 : UpdateAppRequest_OneOfName.name,
+    0 : UpdateAppRequest_OneOfName.notSet
+  };
+  static const $core.Map<$core.int, UpdateAppRequest_OneOfDesc> _UpdateAppRequest_OneOfDescByTag = {
+    4 : UpdateAppRequest_OneOfDesc.desc,
+    0 : UpdateAppRequest_OneOfDesc.notSet
+  };
+  static const $core.Map<$core.int, UpdateAppRequest_OneOfColorStyle> _UpdateAppRequest_OneOfColorStyleByTag = {
+    5 : UpdateAppRequest_OneOfColorStyle.colorStyle,
+    0 : UpdateAppRequest_OneOfColorStyle.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateAppRequest', createEmptyInstance: create)
+    ..oo(0, [2])
+    ..oo(1, [3])
+    ..oo(2, [4])
+    ..oo(3, [5])
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'appId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'workspaceId')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
+    ..aOM<$0.ColorStyle>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'colorStyle', subBuilder: $0.ColorStyle.create)
+    ..hasRequiredFields = false
+  ;
+
+  UpdateAppRequest._() : super();
+  factory UpdateAppRequest({
+    $core.String? appId,
+    $core.String? workspaceId,
+    $core.String? name,
+    $core.String? desc,
+    $0.ColorStyle? colorStyle,
+  }) {
+    final _result = create();
+    if (appId != null) {
+      _result.appId = appId;
+    }
+    if (workspaceId != null) {
+      _result.workspaceId = workspaceId;
+    }
+    if (name != null) {
+      _result.name = name;
+    }
+    if (desc != null) {
+      _result.desc = desc;
+    }
+    if (colorStyle != null) {
+      _result.colorStyle = colorStyle;
+    }
+    return _result;
+  }
+  factory UpdateAppRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory UpdateAppRequest.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')
+  UpdateAppRequest clone() => UpdateAppRequest()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  UpdateAppRequest copyWith(void Function(UpdateAppRequest) updates) => super.copyWith((message) => updates(message as UpdateAppRequest)) as UpdateAppRequest; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static UpdateAppRequest create() => UpdateAppRequest._();
+  UpdateAppRequest createEmptyInstance() => create();
+  static $pb.PbList<UpdateAppRequest> createRepeated() => $pb.PbList<UpdateAppRequest>();
+  @$core.pragma('dart2js:noInline')
+  static UpdateAppRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UpdateAppRequest>(create);
+  static UpdateAppRequest? _defaultInstance;
+
+  UpdateAppRequest_OneOfWorkspaceId whichOneOfWorkspaceId() => _UpdateAppRequest_OneOfWorkspaceIdByTag[$_whichOneof(0)]!;
+  void clearOneOfWorkspaceId() => clearField($_whichOneof(0));
+
+  UpdateAppRequest_OneOfName whichOneOfName() => _UpdateAppRequest_OneOfNameByTag[$_whichOneof(1)]!;
+  void clearOneOfName() => clearField($_whichOneof(1));
+
+  UpdateAppRequest_OneOfDesc whichOneOfDesc() => _UpdateAppRequest_OneOfDescByTag[$_whichOneof(2)]!;
+  void clearOneOfDesc() => clearField($_whichOneof(2));
+
+  UpdateAppRequest_OneOfColorStyle whichOneOfColorStyle() => _UpdateAppRequest_OneOfColorStyleByTag[$_whichOneof(3)]!;
+  void clearOneOfColorStyle() => clearField($_whichOneof(3));
+
+  @$pb.TagNumber(1)
+  $core.String get appId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set appId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasAppId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearAppId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get workspaceId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set workspaceId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasWorkspaceId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearWorkspaceId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get name => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set name($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasName() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearName() => clearField(3);
+
+  @$pb.TagNumber(4)
+  $core.String get desc => $_getSZ(3);
+  @$pb.TagNumber(4)
+  set desc($core.String v) { $_setString(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasDesc() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearDesc() => clearField(4);
+
+  @$pb.TagNumber(5)
+  $0.ColorStyle get colorStyle => $_getN(4);
+  @$pb.TagNumber(5)
+  set colorStyle($0.ColorStyle v) { setField(5, v); }
+  @$pb.TagNumber(5)
+  $core.bool hasColorStyle() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearColorStyle() => clearField(5);
+  @$pb.TagNumber(5)
+  $0.ColorStyle ensureColorStyle() => $_ensure(4);
+}
+

+ 7 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_update.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: app_update.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+

+ 30 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_update.pbjson.dart

@@ -0,0 +1,30 @@
+///
+//  Generated code. Do not modify.
+//  source: app_update.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use updateAppRequestDescriptor instead')
+const UpdateAppRequest$json = const {
+  '1': 'UpdateAppRequest',
+  '2': const [
+    const {'1': 'app_id', '3': 1, '4': 1, '5': 9, '10': 'appId'},
+    const {'1': 'workspace_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'workspaceId'},
+    const {'1': 'name', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'name'},
+    const {'1': 'desc', '3': 4, '4': 1, '5': 9, '9': 2, '10': 'desc'},
+    const {'1': 'color_style', '3': 5, '4': 1, '5': 11, '6': '.ColorStyle', '9': 3, '10': 'colorStyle'},
+  ],
+  '8': const [
+    const {'1': 'one_of_workspace_id'},
+    const {'1': 'one_of_name'},
+    const {'1': 'one_of_desc'},
+    const {'1': 'one_of_color_style'},
+  ],
+};
+
+/// Descriptor for `UpdateAppRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List updateAppRequestDescriptor = $convert.base64Decode('ChBVcGRhdGVBcHBSZXF1ZXN0EhUKBmFwcF9pZBgBIAEoCVIFYXBwSWQSIwoMd29ya3NwYWNlX2lkGAIgASgJSABSC3dvcmtzcGFjZUlkEhQKBG5hbWUYAyABKAlIAVIEbmFtZRIUCgRkZXNjGAQgASgJSAJSBGRlc2MSLgoLY29sb3Jfc3R5bGUYBSABKAsyCy5Db2xvclN0eWxlSANSCmNvbG9yU3R5bGVCFQoTb25lX29mX3dvcmtzcGFjZV9pZEINCgtvbmVfb2ZfbmFtZUINCgtvbmVfb2ZfZGVzY0IUChJvbmVfb2ZfY29sb3Jfc3R5bGU=');

+ 9 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/app_update.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: app_update.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+export 'app_update.pb.dart';
+

+ 5 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbenum.dart

@@ -14,13 +14,17 @@ class WorkspaceErrorCode extends $pb.ProtobufEnum {
   static const WorkspaceErrorCode WorkspaceNameInvalid = WorkspaceErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceNameInvalid');
   static const WorkspaceErrorCode WorkspaceIdInvalid = WorkspaceErrorCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceIdInvalid');
   static const WorkspaceErrorCode AppColorStyleInvalid = WorkspaceErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppColorStyleInvalid');
-  static const WorkspaceErrorCode DatabaseInternalError = WorkspaceErrorCode._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseInternalError');
+  static const WorkspaceErrorCode AppIdInvalid = WorkspaceErrorCode._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppIdInvalid');
+  static const WorkspaceErrorCode DatabaseConnectionFail = WorkspaceErrorCode._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseConnectionFail');
+  static const WorkspaceErrorCode DatabaseInternalError = WorkspaceErrorCode._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseInternalError');
 
   static const $core.List<WorkspaceErrorCode> values = <WorkspaceErrorCode> [
     Unknown,
     WorkspaceNameInvalid,
     WorkspaceIdInvalid,
     AppColorStyleInvalid,
+    AppIdInvalid,
+    DatabaseConnectionFail,
     DatabaseInternalError,
   ];
 

+ 12 - 2
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: errors.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use workspaceErrorCodeDescriptor instead')
 const WorkspaceErrorCode$json = const {
   '1': 'WorkspaceErrorCode',
   '2': const [
@@ -13,10 +16,15 @@ const WorkspaceErrorCode$json = const {
     const {'1': 'WorkspaceNameInvalid', '2': 1},
     const {'1': 'WorkspaceIdInvalid', '2': 2},
     const {'1': 'AppColorStyleInvalid', '2': 3},
-    const {'1': 'DatabaseInternalError', '2': 5},
+    const {'1': 'AppIdInvalid', '2': 4},
+    const {'1': 'DatabaseConnectionFail', '2': 5},
+    const {'1': 'DatabaseInternalError', '2': 6},
   ],
 };
 
+/// Descriptor for `WorkspaceErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List workspaceErrorCodeDescriptor = $convert.base64Decode('ChJXb3Jrc3BhY2VFcnJvckNvZGUSCwoHVW5rbm93bhAAEhgKFFdvcmtzcGFjZU5hbWVJbnZhbGlkEAESFgoSV29ya3NwYWNlSWRJbnZhbGlkEAISGAoUQXBwQ29sb3JTdHlsZUludmFsaWQQAxIQCgxBcHBJZEludmFsaWQQBBIaChZEYXRhYmFzZUNvbm5lY3Rpb25GYWlsEAUSGQoVRGF0YWJhc2VJbnRlcm5hbEVycm9yEAY=');
+@$core.Deprecated('Use workspaceErrorDescriptor instead')
 const WorkspaceError$json = const {
   '1': 'WorkspaceError',
   '2': const [
@@ -25,3 +33,5 @@ const WorkspaceError$json = const {
   ],
 };
 
+/// Descriptor for `WorkspaceError`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List workspaceErrorDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFcnJvchInCgRjb2RlGAEgASgOMhMuV29ya3NwYWNlRXJyb3JDb2RlUgRjb2RlEhAKA21zZxgCIAEoCVIDbXNn');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/errors.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: errors.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'errors.pb.dart';
 

+ 1 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/protobuf.dart

@@ -3,3 +3,4 @@ export './errors.pb.dart';
 export './workspace_update.pb.dart';
 export './app_create.pb.dart';
 export './workspace_create.pb.dart';
+export './app_update.pb.dart';

+ 6 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbjson.dart

@@ -3,9 +3,12 @@
 //  source: workspace_create.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use createWorkspaceRequestDescriptor instead')
 const CreateWorkspaceRequest$json = const {
   '1': 'CreateWorkspaceRequest',
   '2': const [
@@ -14,3 +17,5 @@ const CreateWorkspaceRequest$json = const {
   ],
 };
 
+/// Descriptor for `CreateWorkspaceRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List createWorkspaceRequestDescriptor = $convert.base64Decode('ChZDcmVhdGVXb3Jrc3BhY2VSZXF1ZXN0EhIKBG5hbWUYASABKAlSBG5hbWUSEgoEZGVzYxgCIAEoCVIEZGVzYw==');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_create.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: workspace_create.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'workspace_create.pb.dart';
 

+ 26 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_update.pb.dart

@@ -9,8 +9,28 @@ import 'dart:core' as $core;
 
 import 'package:protobuf/protobuf.dart' as $pb;
 
+enum UpdateWorkspaceRequest_OneOfName {
+  name, 
+  notSet
+}
+
+enum UpdateWorkspaceRequest_OneOfDesc {
+  desc, 
+  notSet
+}
+
 class UpdateWorkspaceRequest extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, UpdateWorkspaceRequest_OneOfName> _UpdateWorkspaceRequest_OneOfNameByTag = {
+    2 : UpdateWorkspaceRequest_OneOfName.name,
+    0 : UpdateWorkspaceRequest_OneOfName.notSet
+  };
+  static const $core.Map<$core.int, UpdateWorkspaceRequest_OneOfDesc> _UpdateWorkspaceRequest_OneOfDescByTag = {
+    3 : UpdateWorkspaceRequest_OneOfDesc.desc,
+    0 : UpdateWorkspaceRequest_OneOfDesc.notSet
+  };
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateWorkspaceRequest', createEmptyInstance: create)
+    ..oo(0, [2])
+    ..oo(1, [3])
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
     ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
@@ -56,6 +76,12 @@ class UpdateWorkspaceRequest extends $pb.GeneratedMessage {
   static UpdateWorkspaceRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UpdateWorkspaceRequest>(create);
   static UpdateWorkspaceRequest? _defaultInstance;
 
+  UpdateWorkspaceRequest_OneOfName whichOneOfName() => _UpdateWorkspaceRequest_OneOfNameByTag[$_whichOneof(0)]!;
+  void clearOneOfName() => clearField($_whichOneof(0));
+
+  UpdateWorkspaceRequest_OneOfDesc whichOneOfDesc() => _UpdateWorkspaceRequest_OneOfDescByTag[$_whichOneof(1)]!;
+  void clearOneOfDesc() => clearField($_whichOneof(1));
+
   @$pb.TagNumber(1)
   $core.String get id => $_getSZ(0);
   @$pb.TagNumber(1)

+ 12 - 3
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_update.pbjson.dart

@@ -3,15 +3,24 @@
 //  source: workspace_update.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use updateWorkspaceRequestDescriptor instead')
 const UpdateWorkspaceRequest$json = const {
   '1': 'UpdateWorkspaceRequest',
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
-    const {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
-    const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
+    const {'1': 'name', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'name'},
+    const {'1': 'desc', '3': 3, '4': 1, '5': 9, '9': 1, '10': 'desc'},
+  ],
+  '8': const [
+    const {'1': 'one_of_name'},
+    const {'1': 'one_of_desc'},
   ],
 };
 
+/// Descriptor for `UpdateWorkspaceRequest`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List updateWorkspaceRequestDescriptor = $convert.base64Decode('ChZVcGRhdGVXb3Jrc3BhY2VSZXF1ZXN0Eg4KAmlkGAEgASgJUgJpZBIUCgRuYW1lGAIgASgJSABSBG5hbWUSFAoEZGVzYxgDIAEoCUgBUgRkZXNjQg0KC29uZV9vZl9uYW1lQg0KC29uZV9vZl9kZXNj');

+ 1 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/workspace_update.pbserver.dart

@@ -3,7 +3,7 @@
 //  source: workspace_update.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
 
 export 'workspace_update.pb.dart';
 

+ 4 - 0
rust-lib/flowy-database/src/lib.rs

@@ -38,3 +38,7 @@ where
     let msg = format!("{:?}", e);
     io::Error::new(io::ErrorKind::NotConnected, msg)
 }
+
+pub trait UserDatabaseConnection {
+    fn get_connection(&self) -> Result<DBConnection, String>;
+}

+ 1 - 0
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -18,6 +18,7 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         "KeyValue"
         | "CreateAppRequest"
         | "ColorStyle"
+        | "UpdateAppRequest"
         | "UpdateWorkspaceRequest"
         | "CreateWorkspaceRequest"
         | "WorkspaceError"

+ 1 - 1
rust-lib/flowy-derive/src/proto_buf/deserialize.rs

@@ -88,7 +88,7 @@ fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream>
         },
         _ => {
             let take_func = format_ident!("take_{}", ident.to_string());
-            let ty = ty_info.ty;
+            let ty = bracketed_ty_info.unwrap().ty;
             Some(quote! {
                 if pb.#has_func() {
                     let val = #ty::try_from(&mut pb.#take_func()).unwrap();

+ 29 - 23
rust-lib/flowy-derive/src/proto_buf/serialize.rs

@@ -1,4 +1,7 @@
-use crate::{derive_cache::TypeCategory, proto_buf::util::ident_category};
+use crate::{
+    derive_cache::TypeCategory,
+    proto_buf::util::{get_member_ident, ident_category},
+};
 use flowy_ast::*;
 use proc_macro2::TokenStream;
 
@@ -12,12 +15,6 @@ pub fn make_se_token_stream(ctxt: &Ctxt, ast: &ASTContainer) -> Option<TokenStre
         .filter(|f| !f.attrs.skip_serializing())
         .flat_map(|field| se_token_stream_for_field(&ctxt, &field, false));
 
-    // let _build_set_fields = ast
-    //     .data
-    //     .all_fields()
-    //     .filter(|f| !f.attrs.skip_serializing())
-    //     .flat_map(|field| se_token_stream_for_field(&ctxt, &field, false));
-
     let se_token_stream: TokenStream = quote! {
 
         impl std::convert::TryInto<Vec<u8>> for #struct_ident {
@@ -52,27 +49,36 @@ fn se_token_stream_for_field(ctxt: &Ctxt, field: &ASTField, _take: bool) -> Opti
         let member = &field.member;
         Some(quote! { pb.#member=self.#func(); })
     } else if field.attrs.is_one_of() {
-        let member = &field.member;
-        match &field.member {
-            syn::Member::Named(ref ident) => {
-                let set_func = format_ident!("set_{}", ident.to_string());
-                Some(quote! {
-                    match self.#member {
-                        Some(ref s) => { pb.#set_func(s.clone()) }
-                        None => {}
-                    }
-                })
-            },
-            _ => {
-                ctxt.error_spanned_by(member, format!("Unsupported member, get member ident fail"));
-                None
-            },
-        }
+        token_stream_for_one_of(ctxt, field)
     } else {
         gen_token_stream(ctxt, &field.member, &field.ty, false)
     }
 }
 
+fn token_stream_for_one_of(ctxt: &Ctxt, field: &ASTField) -> Option<TokenStream> {
+    let member = &field.member;
+    let ident = get_member_ident(ctxt, member)?;
+    let ty_info = parse_ty(ctxt, &field.ty)?;
+    let bracketed_ty_info = ty_info.bracket_ty_info.as_ref().as_ref();
+
+    let set_func = format_ident!("set_{}", ident.to_string());
+
+    match ident_category(bracketed_ty_info.unwrap().ident) {
+        TypeCategory::Protobuf => Some(quote! {
+            match self.#member {
+                Some(s) => { pb.#set_func(s.try_into().unwrap()) }
+                None => {}
+            }
+        }),
+        _ => Some(quote! {
+            match self.#member {
+                Some(ref s) => { pb.#set_func(s.clone()) }
+                None => {}
+            }
+        }),
+    }
+}
+
 fn gen_token_stream(
     ctxt: &Ctxt,
     member: &syn::Member,

+ 2 - 0
rust-lib/flowy-sdk/Cargo.toml

@@ -11,6 +11,8 @@ flowy-log = { path = "../flowy-log" }
 #flowy-log = { path = "../flowy-log", features = ["use_bunyan"] }
 flowy-user = { path = "../flowy-user" }
 flowy-infra = { path = "../flowy-infra" }
+flowy-workspace = { path = "../flowy-workspace" }
+flowy-database = { path = "../flowy-database" }
 tracing = { version = "0.1" }
 log = "0.4.14"
 

+ 8 - 3
rust-lib/flowy-sdk/src/module.rs

@@ -1,5 +1,7 @@
+use flowy_database::{DBConnection, UserDatabaseConnection};
 use flowy_dispatch::prelude::Module;
-use flowy_user::prelude::UserSessionBuilder;
+use flowy_user::prelude::*;
+use flowy_workspace::prelude::*;
 use std::sync::Arc;
 
 pub struct ModuleConfig {
@@ -7,6 +9,9 @@ pub struct ModuleConfig {
 }
 
 pub fn build_modules(config: ModuleConfig) -> Vec<Module> {
-    let user_session = UserSessionBuilder::new().root_dir(&config.root).build();
-    vec![flowy_user::module::create(Arc::new(user_session))]
+    let user_session = Arc::new(UserSessionBuilder::new().root_dir(&config.root).build());
+
+    let workspace_controller = WorkspaceController::new(user_session.clone());
+
+    vec![flowy_user::module::create(user_session)]
 }

+ 7 - 0
rust-lib/flowy-user/src/services/user_session/user_session.rs

@@ -3,6 +3,7 @@ use flowy_database::{
     schema::{user_table, user_table::dsl},
     DBConnection,
     ExpressionMethods,
+    UserDatabaseConnection,
 };
 use flowy_infra::kv::KVStore;
 use lazy_static::lazy_static;
@@ -122,6 +123,12 @@ impl UserSession {
     }
 }
 
+impl UserDatabaseConnection for UserSession {
+    fn get_connection(&self) -> Result<DBConnection, String> {
+        self.get_db_connection().map_err(|e| format!("{:?}", e))
+    }
+}
+
 const USER_ID_DISK_CACHE_KEY: &str = "user_id";
 lazy_static! {
     pub static ref CURRENT_USER_ID: RwLock<Option<String>> = RwLock::new(None);

+ 16 - 0
rust-lib/flowy-workspace/src/entities/app/app_id.rs

@@ -0,0 +1,16 @@
+#[derive(Debug)]
+pub struct AppId(pub String);
+
+impl AppId {
+    pub fn parse(s: String) -> Result<AppId, String> {
+        if s.trim().is_empty() {
+            return Err(format!("App id can not be empty or whitespace"));
+        }
+
+        Ok(Self(s))
+    }
+}
+
+impl AsRef<str> for AppId {
+    fn as_ref(&self) -> &str { &self.0 }
+}

+ 1 - 1
rust-lib/flowy-workspace/src/entities/app/app_name.rs

@@ -4,7 +4,7 @@ pub struct AppName(pub String);
 impl AppName {
     pub fn parse(s: String) -> Result<AppName, String> {
         if s.trim().is_empty() {
-            return Err(format!("Workspace can not be empty or whitespace"));
+            return Err(format!("App name can not be empty or whitespace"));
         }
 
         Ok(Self(s))

+ 96 - 0
rust-lib/flowy-workspace/src/entities/app/app_update.rs

@@ -0,0 +1,96 @@
+use crate::{
+    entities::{
+        app::{app_color_style::AppColorStyle, app_id::AppId, app_name::AppName, ColorStyle},
+        workspace::WorkspaceId,
+    },
+    errors::{ErrorBuilder, WorkspaceError, WorkspaceErrorCode},
+};
+use flowy_derive::ProtoBuf;
+use std::convert::TryInto;
+
+#[derive(ProtoBuf, Default)]
+pub struct UpdateAppRequest {
+    #[pb(index = 1)]
+    pub app_id: String,
+
+    #[pb(index = 2, one_of)]
+    pub workspace_id: Option<String>,
+
+    #[pb(index = 3, one_of)]
+    pub name: Option<String>,
+
+    #[pb(index = 4, one_of)]
+    pub desc: Option<String>,
+
+    #[pb(index = 5, one_of)]
+    pub color_style: Option<ColorStyle>,
+}
+
+pub struct UpdateAppParams {
+    pub app_id: String,
+    pub workspace_id: Option<String>,
+    pub name: Option<String>,
+    pub desc: Option<String>,
+    pub color_style: Option<ColorStyle>,
+}
+
+impl TryInto<UpdateAppParams> for UpdateAppRequest {
+    type Error = WorkspaceError;
+
+    fn try_into(self) -> Result<UpdateAppParams, Self::Error> {
+        let app_id = AppId::parse(self.app_id)
+            .map_err(|e| {
+                ErrorBuilder::new(WorkspaceErrorCode::AppIdInvalid)
+                    .msg(e)
+                    .build()
+            })?
+            .0;
+
+        let name = match self.name {
+            None => None,
+            Some(name) => Some(
+                AppName::parse(name)
+                    .map_err(|e| {
+                        ErrorBuilder::new(WorkspaceErrorCode::WorkspaceNameInvalid)
+                            .msg(e)
+                            .build()
+                    })?
+                    .0,
+            ),
+        };
+
+        let workspace_id = match self.workspace_id {
+            None => None,
+            Some(wid) => Some(
+                WorkspaceId::parse(wid)
+                    .map_err(|e| {
+                        ErrorBuilder::new(WorkspaceErrorCode::WorkspaceIdInvalid)
+                            .msg(e)
+                            .build()
+                    })?
+                    .0,
+            ),
+        };
+
+        let color_style = match self.color_style {
+            None => None,
+            Some(color_style) => Some(
+                AppColorStyle::parse(color_style)
+                    .map_err(|e| {
+                        ErrorBuilder::new(WorkspaceErrorCode::AppColorStyleInvalid)
+                            .msg(e)
+                            .build()
+                    })?
+                    .0,
+            ),
+        };
+
+        Ok(UpdateAppParams {
+            app_id,
+            workspace_id,
+            name,
+            desc: self.desc,
+            color_style,
+        })
+    }
+}

+ 3 - 0
rust-lib/flowy-workspace/src/entities/app/mod.rs

@@ -1,5 +1,8 @@
 mod app_color_style;
 mod app_create;
+mod app_id;
 mod app_name;
+mod app_update;
 
 pub use app_create::*;
+pub use app_update::*;

+ 1 - 1
rust-lib/flowy-workspace/src/entities/workspace/workspace_name.rs

@@ -4,7 +4,7 @@ pub struct WorkspaceName(pub String);
 impl WorkspaceName {
     pub fn parse(s: String) -> Result<WorkspaceName, String> {
         if s.trim().is_empty() {
-            return Err(format!("Workspace can not be empty or whitespace"));
+            return Err(format!("Workspace name can not be empty or whitespace"));
         }
 
         Ok(Self(s))

+ 19 - 12
rust-lib/flowy-workspace/src/entities/workspace/workspace_update.rs

@@ -10,28 +10,35 @@ pub struct UpdateWorkspaceRequest {
     #[pb(index = 1)]
     id: String,
 
-    #[pb(index = 2)]
-    name: String,
+    #[pb(index = 2, one_of)]
+    name: Option<String>,
 
-    #[pb(index = 3)]
-    desc: String,
+    #[pb(index = 3, one_of)]
+    desc: Option<String>,
 }
 
 pub struct UpdateWorkspaceParams {
     pub id: String,
-    pub name: String,
-    pub desc: String,
+    pub name: Option<String>,
+    pub desc: Option<String>,
 }
 
 impl TryInto<UpdateWorkspaceParams> for UpdateWorkspaceRequest {
     type Error = WorkspaceError;
 
     fn try_into(self) -> Result<UpdateWorkspaceParams, Self::Error> {
-        let name = WorkspaceName::parse(self.name).map_err(|e| {
-            ErrorBuilder::new(WorkspaceErrorCode::WorkspaceNameInvalid)
-                .msg(e)
-                .build()
-        })?;
+        let name = match self.name {
+            None => None,
+            Some(name) => Some(
+                WorkspaceName::parse(name)
+                    .map_err(|e| {
+                        ErrorBuilder::new(WorkspaceErrorCode::WorkspaceNameInvalid)
+                            .msg(e)
+                            .build()
+                    })?
+                    .0,
+            ),
+        };
 
         let id = WorkspaceId::parse(self.id).map_err(|e| {
             ErrorBuilder::new(WorkspaceErrorCode::WorkspaceIdInvalid)
@@ -41,7 +48,7 @@ impl TryInto<UpdateWorkspaceParams> for UpdateWorkspaceRequest {
 
         Ok(UpdateWorkspaceParams {
             id: id.0,
-            name: name.0,
+            name,
             desc: self.desc,
         })
     }

+ 8 - 2
rust-lib/flowy-workspace/src/errors.rs

@@ -13,7 +13,7 @@ pub struct WorkspaceError {
 }
 
 impl WorkspaceError {
-    fn new(code: WorkspaceErrorCode, msg: &str) -> Self {
+    pub fn new(code: WorkspaceErrorCode, msg: &str) -> Self {
         Self {
             code,
             msg: msg.to_owned(),
@@ -35,8 +35,14 @@ pub enum WorkspaceErrorCode {
     #[display(fmt = "App color style format error")]
     AppColorStyleInvalid = 3,
 
+    #[display(fmt = "App id is invalid")]
+    AppIdInvalid         = 4,
+
+    #[display(fmt = "Get database connection failed")]
+    DatabaseConnectionFail = 5,
+
     #[display(fmt = "Database internal error")]
-    DatabaseInternalError = 5,
+    DatabaseInternalError = 6,
 }
 
 impl std::default::Default for WorkspaceErrorCode {

+ 5 - 0
rust-lib/flowy-workspace/src/lib.rs

@@ -8,6 +8,11 @@ mod sql_tables;
 #[macro_use]
 mod macros;
 mod protobuf;
+mod services;
 
 #[macro_use]
 extern crate flowy_database;
+
+pub mod prelude {
+    pub use crate::{errors::*, module::*, services::*};
+}

+ 559 - 0
rust-lib/flowy-workspace/src/protobuf/model/app_update.rs

@@ -0,0 +1,559 @@
+// This file is generated by rust-protobuf 2.22.1. Do not edit
+// @generated
+
+// https://github.com/rust-lang/rust-clippy/issues/702
+#![allow(unknown_lints)]
+#![allow(clippy::all)]
+
+#![allow(unused_attributes)]
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+#![allow(box_pointers)]
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![allow(trivial_casts)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
+//! Generated file from `app_update.proto`
+
+/// Generated files are compatible only with the same version
+/// of protobuf runtime.
+// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_22_1;
+
+#[derive(PartialEq,Clone,Default)]
+pub struct UpdateAppRequest {
+    // message fields
+    pub app_id: ::std::string::String,
+    // message oneof groups
+    pub one_of_workspace_id: ::std::option::Option<UpdateAppRequest_oneof_one_of_workspace_id>,
+    pub one_of_name: ::std::option::Option<UpdateAppRequest_oneof_one_of_name>,
+    pub one_of_desc: ::std::option::Option<UpdateAppRequest_oneof_one_of_desc>,
+    pub one_of_color_style: ::std::option::Option<UpdateAppRequest_oneof_one_of_color_style>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a UpdateAppRequest {
+    fn default() -> &'a UpdateAppRequest {
+        <UpdateAppRequest as ::protobuf::Message>::default_instance()
+    }
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateAppRequest_oneof_one_of_workspace_id {
+    workspace_id(::std::string::String),
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateAppRequest_oneof_one_of_name {
+    name(::std::string::String),
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateAppRequest_oneof_one_of_desc {
+    desc(::std::string::String),
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateAppRequest_oneof_one_of_color_style {
+    color_style(super::app_create::ColorStyle),
+}
+
+impl UpdateAppRequest {
+    pub fn new() -> UpdateAppRequest {
+        ::std::default::Default::default()
+    }
+
+    // string app_id = 1;
+
+
+    pub fn get_app_id(&self) -> &str {
+        &self.app_id
+    }
+    pub fn clear_app_id(&mut self) {
+        self.app_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_app_id(&mut self, v: ::std::string::String) {
+        self.app_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_app_id(&mut self) -> &mut ::std::string::String {
+        &mut self.app_id
+    }
+
+    // Take field
+    pub fn take_app_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.app_id, ::std::string::String::new())
+    }
+
+    // string workspace_id = 2;
+
+
+    pub fn get_workspace_id(&self) -> &str {
+        match self.one_of_workspace_id {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_workspace_id(&mut self) {
+        self.one_of_workspace_id = ::std::option::Option::None;
+    }
+
+    pub fn has_workspace_id(&self) -> bool {
+        match self.one_of_workspace_id {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_workspace_id(&mut self, v: ::std::string::String) {
+        self.one_of_workspace_id = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_workspace_id(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(_)) = self.one_of_workspace_id {
+        } else {
+            self.one_of_workspace_id = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(::std::string::String::new()));
+        }
+        match self.one_of_workspace_id {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_workspace_id(&mut self) -> ::std::string::String {
+        if self.has_workspace_id() {
+            match self.one_of_workspace_id.take() {
+                ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+
+    // string name = 3;
+
+
+    pub fn get_name(&self) -> &str {
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_name::name(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_name(&mut self) {
+        self.one_of_name = ::std::option::Option::None;
+    }
+
+    pub fn has_name(&self) -> bool {
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_name::name(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_name(&mut self, v: ::std::string::String) {
+        self.one_of_name = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_name::name(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_name(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_name::name(_)) = self.one_of_name {
+        } else {
+            self.one_of_name = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_name::name(::std::string::String::new()));
+        }
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_name::name(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_name(&mut self) -> ::std::string::String {
+        if self.has_name() {
+            match self.one_of_name.take() {
+                ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_name::name(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+
+    // string desc = 4;
+
+
+    pub fn get_desc(&self) -> &str {
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_desc::desc(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_desc(&mut self) {
+        self.one_of_desc = ::std::option::Option::None;
+    }
+
+    pub fn has_desc(&self) -> bool {
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_desc::desc(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_desc(&mut self, v: ::std::string::String) {
+        self.one_of_desc = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_desc::desc(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_desc(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_desc::desc(_)) = self.one_of_desc {
+        } else {
+            self.one_of_desc = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_desc::desc(::std::string::String::new()));
+        }
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_desc::desc(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_desc(&mut self) -> ::std::string::String {
+        if self.has_desc() {
+            match self.one_of_desc.take() {
+                ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_desc::desc(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+
+    // .ColorStyle color_style = 5;
+
+
+    pub fn get_color_style(&self) -> &super::app_create::ColorStyle {
+        match self.one_of_color_style {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_color_style::color_style(ref v)) => v,
+            _ => <super::app_create::ColorStyle as ::protobuf::Message>::default_instance(),
+        }
+    }
+    pub fn clear_color_style(&mut self) {
+        self.one_of_color_style = ::std::option::Option::None;
+    }
+
+    pub fn has_color_style(&self) -> bool {
+        match self.one_of_color_style {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_color_style::color_style(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_color_style(&mut self, v: super::app_create::ColorStyle) {
+        self.one_of_color_style = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_color_style::color_style(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_color_style(&mut self) -> &mut super::app_create::ColorStyle {
+        if let ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_color_style::color_style(_)) = self.one_of_color_style {
+        } else {
+            self.one_of_color_style = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_color_style::color_style(super::app_create::ColorStyle::new()));
+        }
+        match self.one_of_color_style {
+            ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_color_style::color_style(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_color_style(&mut self) -> super::app_create::ColorStyle {
+        if self.has_color_style() {
+            match self.one_of_color_style.take() {
+                ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_color_style::color_style(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            super::app_create::ColorStyle::new()
+        }
+    }
+}
+
+impl ::protobuf::Message for UpdateAppRequest {
+    fn is_initialized(&self) -> bool {
+        if let Some(UpdateAppRequest_oneof_one_of_color_style::color_style(ref v)) = self.one_of_color_style {
+            if !v.is_initialized() {
+                return false;
+            }
+        }
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.app_id)?;
+                },
+                2 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_workspace_id = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(is.read_string()?));
+                },
+                3 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_name = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_name::name(is.read_string()?));
+                },
+                4 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_desc = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_desc::desc(is.read_string()?));
+                },
+                5 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_color_style = ::std::option::Option::Some(UpdateAppRequest_oneof_one_of_color_style::color_style(is.read_message()?));
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.app_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.app_id);
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_workspace_id {
+            match v {
+                &UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(ref v) => {
+                    my_size += ::protobuf::rt::string_size(2, &v);
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_name {
+            match v {
+                &UpdateAppRequest_oneof_one_of_name::name(ref v) => {
+                    my_size += ::protobuf::rt::string_size(3, &v);
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_desc {
+            match v {
+                &UpdateAppRequest_oneof_one_of_desc::desc(ref v) => {
+                    my_size += ::protobuf::rt::string_size(4, &v);
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_color_style {
+            match v {
+                &UpdateAppRequest_oneof_one_of_color_style::color_style(ref v) => {
+                    let len = v.compute_size();
+                    my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+                },
+            };
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.app_id.is_empty() {
+            os.write_string(1, &self.app_id)?;
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_workspace_id {
+            match v {
+                &UpdateAppRequest_oneof_one_of_workspace_id::workspace_id(ref v) => {
+                    os.write_string(2, v)?;
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_name {
+            match v {
+                &UpdateAppRequest_oneof_one_of_name::name(ref v) => {
+                    os.write_string(3, v)?;
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_desc {
+            match v {
+                &UpdateAppRequest_oneof_one_of_desc::desc(ref v) => {
+                    os.write_string(4, v)?;
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_color_style {
+            match v {
+                &UpdateAppRequest_oneof_one_of_color_style::color_style(ref v) => {
+                    os.write_tag(5, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+                    os.write_raw_varint32(v.get_cached_size())?;
+                    v.write_to_with_cached_sizes(os)?;
+                },
+            };
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> UpdateAppRequest {
+        UpdateAppRequest::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "app_id",
+                |m: &UpdateAppRequest| { &m.app_id },
+                |m: &mut UpdateAppRequest| { &mut m.app_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "workspace_id",
+                UpdateAppRequest::has_workspace_id,
+                UpdateAppRequest::get_workspace_id,
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "name",
+                UpdateAppRequest::has_name,
+                UpdateAppRequest::get_name,
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "desc",
+                UpdateAppRequest::has_desc,
+                UpdateAppRequest::get_desc,
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_message_accessor::<_, super::app_create::ColorStyle>(
+                "color_style",
+                UpdateAppRequest::has_color_style,
+                UpdateAppRequest::get_color_style,
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<UpdateAppRequest>(
+                "UpdateAppRequest",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static UpdateAppRequest {
+        static instance: ::protobuf::rt::LazyV2<UpdateAppRequest> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(UpdateAppRequest::new)
+    }
+}
+
+impl ::protobuf::Clear for UpdateAppRequest {
+    fn clear(&mut self) {
+        self.app_id.clear();
+        self.one_of_workspace_id = ::std::option::Option::None;
+        self.one_of_name = ::std::option::Option::None;
+        self.one_of_desc = ::std::option::Option::None;
+        self.one_of_color_style = ::std::option::Option::None;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for UpdateAppRequest {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for UpdateAppRequest {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x10app_update.proto\x1a\x10app_create.proto\"\xf5\x01\n\x10UpdateAppR\
+    equest\x12\x15\n\x06app_id\x18\x01\x20\x01(\tR\x05appId\x12#\n\x0cworksp\
+    ace_id\x18\x02\x20\x01(\tH\0R\x0bworkspaceId\x12\x14\n\x04name\x18\x03\
+    \x20\x01(\tH\x01R\x04name\x12\x14\n\x04desc\x18\x04\x20\x01(\tH\x02R\x04\
+    desc\x12.\n\x0bcolor_style\x18\x05\x20\x01(\x0b2\x0b.ColorStyleH\x03R\nc\
+    olorStyleB\x15\n\x13one_of_workspace_idB\r\n\x0bone_of_nameB\r\n\x0bone_\
+    of_descB\x14\n\x12one_of_color_styleJ\xb4\x03\n\x06\x12\x04\0\0\t\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\t\x01\n\n\n\x03\x04\0\x01\x12\x03\x03\x08\x18\n\
+    \x0b\n\x04\x04\0\x02\0\x12\x03\x04\x04\x16\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\x11\n\x0c\
+    \n\x05\x04\0\x02\0\x03\x12\x03\x04\x14\x15\n\x0b\n\x04\x04\0\x08\0\x12\
+    \x03\x05\x04:\n\x0c\n\x05\x04\0\x08\0\x01\x12\x03\x05\n\x1d\n\x0b\n\x04\
+    \x04\0\x02\x01\x12\x03\x05\x208\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\
+    \x05\x20&\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x05'3\n\x0c\n\x05\x04\0\
+    \x02\x01\x03\x12\x03\x0567\n\x0b\n\x04\x04\0\x08\x01\x12\x03\x06\x04*\n\
+    \x0c\n\x05\x04\0\x08\x01\x01\x12\x03\x06\n\x15\n\x0b\n\x04\x04\0\x02\x02\
+    \x12\x03\x06\x18(\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x06\x18\x1e\n\
+    \x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x06\x1f#\n\x0c\n\x05\x04\0\x02\x02\
+    \x03\x12\x03\x06&'\n\x0b\n\x04\x04\0\x08\x02\x12\x03\x07\x04*\n\x0c\n\
+    \x05\x04\0\x08\x02\x01\x12\x03\x07\n\x15\n\x0b\n\x04\x04\0\x02\x03\x12\
+    \x03\x07\x18(\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x07\x18\x1e\n\x0c\n\
+    \x05\x04\0\x02\x03\x01\x12\x03\x07\x1f#\n\x0c\n\x05\x04\0\x02\x03\x03\
+    \x12\x03\x07&'\n\x0b\n\x04\x04\0\x08\x03\x12\x03\x08\x04<\n\x0c\n\x05\
+    \x04\0\x08\x03\x01\x12\x03\x08\n\x1c\n\x0b\n\x04\x04\0\x02\x04\x12\x03\
+    \x08\x1f:\n\x0c\n\x05\x04\0\x02\x04\x06\x12\x03\x08\x1f)\n\x0c\n\x05\x04\
+    \0\x02\x04\x01\x12\x03\x08*5\n\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x0889\
+    b\x06proto3\
+";
+
+static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
+
+fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
+    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
+}
+
+pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
+    file_descriptor_proto_lazy.get(|| {
+        parse_descriptor_proto()
+    })
+}

+ 35 - 23
rust-lib/flowy-workspace/src/protobuf/model/errors.rs

@@ -219,7 +219,9 @@ pub enum WorkspaceErrorCode {
     WorkspaceNameInvalid = 1,
     WorkspaceIdInvalid = 2,
     AppColorStyleInvalid = 3,
-    DatabaseInternalError = 5,
+    AppIdInvalid = 4,
+    DatabaseConnectionFail = 5,
+    DatabaseInternalError = 6,
 }
 
 impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
@@ -233,7 +235,9 @@ impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
             1 => ::std::option::Option::Some(WorkspaceErrorCode::WorkspaceNameInvalid),
             2 => ::std::option::Option::Some(WorkspaceErrorCode::WorkspaceIdInvalid),
             3 => ::std::option::Option::Some(WorkspaceErrorCode::AppColorStyleInvalid),
-            5 => ::std::option::Option::Some(WorkspaceErrorCode::DatabaseInternalError),
+            4 => ::std::option::Option::Some(WorkspaceErrorCode::AppIdInvalid),
+            5 => ::std::option::Option::Some(WorkspaceErrorCode::DatabaseConnectionFail),
+            6 => ::std::option::Option::Some(WorkspaceErrorCode::DatabaseInternalError),
             _ => ::std::option::Option::None
         }
     }
@@ -244,6 +248,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
             WorkspaceErrorCode::WorkspaceNameInvalid,
             WorkspaceErrorCode::WorkspaceIdInvalid,
             WorkspaceErrorCode::AppColorStyleInvalid,
+            WorkspaceErrorCode::AppIdInvalid,
+            WorkspaceErrorCode::DatabaseConnectionFail,
             WorkspaceErrorCode::DatabaseInternalError,
         ];
         values
@@ -275,28 +281,34 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceErrorCode {
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x0cerrors.proto\"K\n\x0eWorkspaceError\x12'\n\x04code\x18\x01\x20\x01\
     (\x0e2\x13.WorkspaceErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\
-    \tR\x03msg*\x88\x01\n\x12WorkspaceErrorCode\x12\x0b\n\x07Unknown\x10\0\
+    \tR\x03msg*\xb6\x01\n\x12WorkspaceErrorCode\x12\x0b\n\x07Unknown\x10\0\
     \x12\x18\n\x14WorkspaceNameInvalid\x10\x01\x12\x16\n\x12WorkspaceIdInval\
-    id\x10\x02\x12\x18\n\x14AppColorStyleInvalid\x10\x03\x12\x19\n\x15Databa\
-    seInternalError\x10\x05J\xfd\x02\n\x06\x12\x04\0\0\x0c\x01\n\x08\n\x01\
-    \x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\
-    \0\x01\x12\x03\x02\x08\x16\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x20\n\
-    \x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\0\x02\0\
-    \x01\x12\x03\x03\x17\x1b\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x1e\x1f\
-    \n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\x02\x01\
-    \x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\x0e\
-    \n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x05\0\x12\
-    \x04\x06\0\x0c\x01\n\n\n\x03\x05\0\x01\x12\x03\x06\x05\x17\n\x0b\n\x04\
-    \x05\0\x02\0\x12\x03\x07\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x07\
-    \x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x07\x0e\x0f\n\x0b\n\x04\x05\
-    \0\x02\x01\x12\x03\x08\x04\x1d\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\
-    \x04\x18\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x08\x1b\x1c\n\x0b\n\x04\
-    \x05\0\x02\x02\x12\x03\t\x04\x1b\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\
-    \x04\x16\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\x19\x1a\n\x0b\n\x04\x05\
-    \0\x02\x03\x12\x03\n\x04\x1d\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\
-    \x18\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\x1b\x1c\n\x0b\n\x04\x05\0\
-    \x02\x04\x12\x03\x0b\x04\x1e\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x0b\
-    \x04\x19\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x0b\x1c\x1db\x06proto3\
+    id\x10\x02\x12\x18\n\x14AppColorStyleInvalid\x10\x03\x12\x10\n\x0cAppIdI\
+    nvalid\x10\x04\x12\x1a\n\x16DatabaseConnectionFail\x10\x05\x12\x19\n\x15\
+    DatabaseInternalError\x10\x06J\xcf\x03\n\x06\x12\x04\0\0\x0e\x01\n\x08\n\
+    \x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\
+    \x04\0\x01\x12\x03\x02\x08\x16\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\
+    \x20\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\0\
+    \x02\0\x01\x12\x03\x03\x17\x1b\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\
+    \x1e\x1f\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\
+    \x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\
+    \x0b\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x05\
+    \0\x12\x04\x06\0\x0e\x01\n\n\n\x03\x05\0\x01\x12\x03\x06\x05\x17\n\x0b\n\
+    \x04\x05\0\x02\0\x12\x03\x07\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\
+    \x07\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x07\x0e\x0f\n\x0b\n\x04\
+    \x05\0\x02\x01\x12\x03\x08\x04\x1d\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\
+    \x08\x04\x18\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x08\x1b\x1c\n\x0b\n\
+    \x04\x05\0\x02\x02\x12\x03\t\x04\x1b\n\x0c\n\x05\x05\0\x02\x02\x01\x12\
+    \x03\t\x04\x16\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\x19\x1a\n\x0b\n\
+    \x04\x05\0\x02\x03\x12\x03\n\x04\x1d\n\x0c\n\x05\x05\0\x02\x03\x01\x12\
+    \x03\n\x04\x18\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\x1b\x1c\n\x0b\n\
+    \x04\x05\0\x02\x04\x12\x03\x0b\x04\x15\n\x0c\n\x05\x05\0\x02\x04\x01\x12\
+    \x03\x0b\x04\x10\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x0b\x13\x14\n\x0b\
+    \n\x04\x05\0\x02\x05\x12\x03\x0c\x04\x1f\n\x0c\n\x05\x05\0\x02\x05\x01\
+    \x12\x03\x0c\x04\x1a\n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x0c\x1d\x1e\n\
+    \x0b\n\x04\x05\0\x02\x06\x12\x03\r\x04\x1e\n\x0c\n\x05\x05\0\x02\x06\x01\
+    \x12\x03\r\x04\x19\n\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\r\x1c\x1db\x06p\
+    roto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 3 - 0
rust-lib/flowy-workspace/src/protobuf/model/mod.rs

@@ -11,3 +11,6 @@ pub use app_create::*;
 
 mod workspace_create; 
 pub use workspace_create::*; 
+
+mod app_update; 
+pub use app_update::*; 

+ 127 - 46
rust-lib/flowy-workspace/src/protobuf/model/workspace_update.rs

@@ -27,8 +27,9 @@
 pub struct UpdateWorkspaceRequest {
     // message fields
     pub id: ::std::string::String,
-    pub name: ::std::string::String,
-    pub desc: ::std::string::String,
+    // message oneof groups
+    pub one_of_name: ::std::option::Option<UpdateWorkspaceRequest_oneof_one_of_name>,
+    pub one_of_desc: ::std::option::Option<UpdateWorkspaceRequest_oneof_one_of_desc>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -40,6 +41,16 @@ impl<'a> ::std::default::Default for &'a UpdateWorkspaceRequest {
     }
 }
 
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateWorkspaceRequest_oneof_one_of_name {
+    name(::std::string::String),
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum UpdateWorkspaceRequest_oneof_one_of_desc {
+    desc(::std::string::String),
+}
+
 impl UpdateWorkspaceRequest {
     pub fn new() -> UpdateWorkspaceRequest {
         ::std::default::Default::default()
@@ -75,52 +86,98 @@ impl UpdateWorkspaceRequest {
 
 
     pub fn get_name(&self) -> &str {
-        &self.name
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_name::name(ref v)) => v,
+            _ => "",
+        }
     }
     pub fn clear_name(&mut self) {
-        self.name.clear();
+        self.one_of_name = ::std::option::Option::None;
+    }
+
+    pub fn has_name(&self) -> bool {
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_name::name(..)) => true,
+            _ => false,
+        }
     }
 
     // Param is passed by value, moved
     pub fn set_name(&mut self, v: ::std::string::String) {
-        self.name = v;
+        self.one_of_name = ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_name::name(v))
     }
 
     // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
     pub fn mut_name(&mut self) -> &mut ::std::string::String {
-        &mut self.name
+        if let ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_name::name(_)) = self.one_of_name {
+        } else {
+            self.one_of_name = ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_name::name(::std::string::String::new()));
+        }
+        match self.one_of_name {
+            ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_name::name(ref mut v)) => v,
+            _ => panic!(),
+        }
     }
 
     // Take field
     pub fn take_name(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.name, ::std::string::String::new())
+        if self.has_name() {
+            match self.one_of_name.take() {
+                ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_name::name(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
     }
 
     // string desc = 3;
 
 
     pub fn get_desc(&self) -> &str {
-        &self.desc
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_desc::desc(ref v)) => v,
+            _ => "",
+        }
     }
     pub fn clear_desc(&mut self) {
-        self.desc.clear();
+        self.one_of_desc = ::std::option::Option::None;
+    }
+
+    pub fn has_desc(&self) -> bool {
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_desc::desc(..)) => true,
+            _ => false,
+        }
     }
 
     // Param is passed by value, moved
     pub fn set_desc(&mut self, v: ::std::string::String) {
-        self.desc = v;
+        self.one_of_desc = ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_desc::desc(v))
     }
 
     // Mutable pointer to the field.
-    // If field is not initialized, it is initialized with default value first.
     pub fn mut_desc(&mut self) -> &mut ::std::string::String {
-        &mut self.desc
+        if let ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_desc::desc(_)) = self.one_of_desc {
+        } else {
+            self.one_of_desc = ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_desc::desc(::std::string::String::new()));
+        }
+        match self.one_of_desc {
+            ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_desc::desc(ref mut v)) => v,
+            _ => panic!(),
+        }
     }
 
     // Take field
     pub fn take_desc(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.desc, ::std::string::String::new())
+        if self.has_desc() {
+            match self.one_of_desc.take() {
+                ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_desc::desc(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
     }
 }
 
@@ -137,10 +194,16 @@ impl ::protobuf::Message for UpdateWorkspaceRequest {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_name = ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_name::name(is.read_string()?));
                 },
                 3 => {
-                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.desc)?;
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_desc = ::std::option::Option::Some(UpdateWorkspaceRequest_oneof_one_of_desc::desc(is.read_string()?));
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -157,11 +220,19 @@ impl ::protobuf::Message for UpdateWorkspaceRequest {
         if !self.id.is_empty() {
             my_size += ::protobuf::rt::string_size(1, &self.id);
         }
-        if !self.name.is_empty() {
-            my_size += ::protobuf::rt::string_size(2, &self.name);
+        if let ::std::option::Option::Some(ref v) = self.one_of_name {
+            match v {
+                &UpdateWorkspaceRequest_oneof_one_of_name::name(ref v) => {
+                    my_size += ::protobuf::rt::string_size(2, &v);
+                },
+            };
         }
-        if !self.desc.is_empty() {
-            my_size += ::protobuf::rt::string_size(3, &self.desc);
+        if let ::std::option::Option::Some(ref v) = self.one_of_desc {
+            match v {
+                &UpdateWorkspaceRequest_oneof_one_of_desc::desc(ref v) => {
+                    my_size += ::protobuf::rt::string_size(3, &v);
+                },
+            };
         }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
@@ -172,11 +243,19 @@ impl ::protobuf::Message for UpdateWorkspaceRequest {
         if !self.id.is_empty() {
             os.write_string(1, &self.id)?;
         }
-        if !self.name.is_empty() {
-            os.write_string(2, &self.name)?;
+        if let ::std::option::Option::Some(ref v) = self.one_of_name {
+            match v {
+                &UpdateWorkspaceRequest_oneof_one_of_name::name(ref v) => {
+                    os.write_string(2, v)?;
+                },
+            };
         }
-        if !self.desc.is_empty() {
-            os.write_string(3, &self.desc)?;
+        if let ::std::option::Option::Some(ref v) = self.one_of_desc {
+            match v {
+                &UpdateWorkspaceRequest_oneof_one_of_desc::desc(ref v) => {
+                    os.write_string(3, v)?;
+                },
+            };
         }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
@@ -221,15 +300,15 @@ impl ::protobuf::Message for UpdateWorkspaceRequest {
                 |m: &UpdateWorkspaceRequest| { &m.id },
                 |m: &mut UpdateWorkspaceRequest| { &mut m.id },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
                 "name",
-                |m: &UpdateWorkspaceRequest| { &m.name },
-                |m: &mut UpdateWorkspaceRequest| { &mut m.name },
+                UpdateWorkspaceRequest::has_name,
+                UpdateWorkspaceRequest::get_name,
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
                 "desc",
-                |m: &UpdateWorkspaceRequest| { &m.desc },
-                |m: &mut UpdateWorkspaceRequest| { &mut m.desc },
+                UpdateWorkspaceRequest::has_desc,
+                UpdateWorkspaceRequest::get_desc,
             ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<UpdateWorkspaceRequest>(
                 "UpdateWorkspaceRequest",
@@ -248,8 +327,8 @@ impl ::protobuf::Message for UpdateWorkspaceRequest {
 impl ::protobuf::Clear for UpdateWorkspaceRequest {
     fn clear(&mut self) {
         self.id.clear();
-        self.name.clear();
-        self.desc.clear();
+        self.one_of_name = ::std::option::Option::None;
+        self.one_of_desc = ::std::option::Option::None;
         self.unknown_fields.clear();
     }
 }
@@ -267,20 +346,22 @@ impl ::protobuf::reflect::ProtobufValue for UpdateWorkspaceRequest {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x16workspace_update.proto\"P\n\x16UpdateWorkspaceRequest\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\x04descJ\xcf\x01\n\x06\
-    \x12\x04\0\0\x06\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\
-    \x04\x02\0\x06\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x1e\n\x0b\n\x04\
-    \x04\0\x02\0\x12\x03\x03\x04\x12\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\
-    \x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\r\n\x0c\n\x05\x04\0\
-    \x02\0\x03\x12\x03\x03\x10\x11\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\
-    \x14\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\
-    \x02\x01\x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\
-    \x04\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x14\n\x0c\n\x05\
-    \x04\0\x02\x02\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\
-    \x03\x05\x0b\x0f\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x12\x13b\x06p\
-    roto3\
+    \n\x16workspace_update.proto\"r\n\x16UpdateWorkspaceRequest\x12\x0e\n\
+    \x02id\x18\x01\x20\x01(\tR\x02id\x12\x14\n\x04name\x18\x02\x20\x01(\tH\0\
+    R\x04name\x12\x14\n\x04desc\x18\x03\x20\x01(\tH\x01R\x04descB\r\n\x0bone\
+    _of_nameB\r\n\x0bone_of_descJ\x85\x02\n\x06\x12\x04\0\0\x06\x01\n\x08\n\
+    \x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x06\x01\n\n\n\x03\
+    \x04\0\x01\x12\x03\x02\x08\x1e\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\
+    \x12\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\
+    \0\x01\x12\x03\x03\x0b\r\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x10\x11\
+    \n\x0b\n\x04\x04\0\x08\0\x12\x03\x04\x04*\n\x0c\n\x05\x04\0\x08\0\x01\
+    \x12\x03\x04\n\x15\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x18(\n\x0c\n\
+    \x05\x04\0\x02\x01\x05\x12\x03\x04\x18\x1e\n\x0c\n\x05\x04\0\x02\x01\x01\
+    \x12\x03\x04\x1f#\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04&'\n\x0b\n\
+    \x04\x04\0\x08\x01\x12\x03\x05\x04*\n\x0c\n\x05\x04\0\x08\x01\x01\x12\
+    \x03\x05\n\x15\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x18(\n\x0c\n\x05\
+    \x04\0\x02\x02\x05\x12\x03\x05\x18\x1e\n\x0c\n\x05\x04\0\x02\x02\x01\x12\
+    \x03\x05\x1f#\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05&'b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 10 - 0
rust-lib/flowy-workspace/src/protobuf/proto/app_update.proto

@@ -0,0 +1,10 @@
+syntax = "proto3";
+import "app_create.proto";
+
+message UpdateAppRequest {
+    string app_id = 1;
+    oneof one_of_workspace_id { string workspace_id = 2; };
+    oneof one_of_name { string name = 3; };
+    oneof one_of_desc { string desc = 4; };
+    oneof one_of_color_style { ColorStyle color_style = 5; };
+}

+ 3 - 1
rust-lib/flowy-workspace/src/protobuf/proto/errors.proto

@@ -9,5 +9,7 @@ enum WorkspaceErrorCode {
     WorkspaceNameInvalid = 1;
     WorkspaceIdInvalid = 2;
     AppColorStyleInvalid = 3;
-    DatabaseInternalError = 5;
+    AppIdInvalid = 4;
+    DatabaseConnectionFail = 5;
+    DatabaseInternalError = 6;
 }

+ 2 - 2
rust-lib/flowy-workspace/src/protobuf/proto/workspace_update.proto

@@ -2,6 +2,6 @@ syntax = "proto3";
 
 message UpdateWorkspaceRequest {
     string id = 1;
-    string name = 2;
-    string desc = 3;
+    oneof one_of_name { string name = 2; };
+    oneof one_of_desc { string desc = 3; };
 }

+ 0 - 0
rust-lib/flowy-workspace/src/services/database.rs


+ 4 - 0
rust-lib/flowy-workspace/src/services/mod.rs

@@ -0,0 +1,4 @@
+mod database;
+mod workspace_controller;
+
+pub use workspace_controller::*;

+ 81 - 0
rust-lib/flowy-workspace/src/services/workspace_controller.rs

@@ -0,0 +1,81 @@
+use crate::{
+    entities::{app::*, workspace::*},
+    errors::*,
+    sql_tables::{app::*, workspace::*},
+};
+use diesel::ExpressionMethods;
+use flowy_database::{
+    query_dsl::*,
+    schema::{app_table, workspace_table},
+    DBConnection,
+    RunQueryDsl,
+    UserDatabaseConnection,
+};
+use std::sync::Arc;
+
+macro_rules! diesel_update_table {
+    (
+        $table_name:ident,
+        $changeset:ident,
+        $connection:ident
+    ) => {
+        let filter =
+            $table_name::dsl::$table_name.filter($table_name::dsl::id.eq($changeset.id.clone()));
+        let affected_row = diesel::update(filter)
+            .set($changeset)
+            .execute(&*$connection)?;
+        debug_assert_eq!(affected_row, 1);
+    };
+}
+
+pub struct WorkspaceController {
+    db: Arc<dyn UserDatabaseConnection>,
+}
+
+impl WorkspaceController {
+    pub fn new(db: Arc<dyn UserDatabaseConnection>) -> Self { Self { db } }
+
+    pub fn save_workspace(&self, params: CreateWorkspaceParams) -> Result<(), WorkspaceError> {
+        let workspace = Workspace::new(params);
+        let conn = self.get_connection()?;
+        let _ = diesel::insert_into(workspace_table::table)
+            .values(workspace)
+            .execute(&*conn)?;
+
+        Ok(())
+    }
+
+    pub fn update_workspace(&self, params: UpdateWorkspaceParams) -> Result<(), WorkspaceError> {
+        let changeset = WorkspaceChangeset::new(params);
+        let conn = self.get_connection()?;
+        diesel_update_table!(workspace_table, changeset, conn);
+
+        Ok(())
+    }
+
+    pub fn save_app(&self, params: CreateAppParams) -> Result<(), WorkspaceError> {
+        let app = App::new(params);
+        let conn = self.get_connection()?;
+        let _ = diesel::insert_into(app_table::table)
+            .values(app)
+            .execute(&*conn)?;
+        Ok(())
+    }
+
+    pub fn update_app(&self, params: UpdateAppParams) -> Result<(), WorkspaceError> {
+        let changeset = AppChangeset::new(params);
+        let conn = self.get_connection()?;
+        diesel_update_table!(app_table, changeset, conn);
+        Ok(())
+    }
+}
+
+impl WorkspaceController {
+    fn get_connection(&self) -> Result<DBConnection, WorkspaceError> {
+        self.db.get_connection().map_err(|e| {
+            ErrorBuilder::new(WorkspaceErrorCode::DatabaseConnectionFail)
+                .msg(e)
+                .build()
+        })
+    }
+}

+ 51 - 1
rust-lib/flowy-workspace/src/sql_tables/app/app.rs

@@ -1,4 +1,8 @@
-use crate::{impl_sql_binary_expression, sql_tables::workspace::Workspace};
+use crate::{
+    entities::app::{ColorStyle, CreateAppParams, UpdateAppParams},
+    impl_sql_binary_expression,
+    sql_tables::workspace::Workspace,
+};
 use diesel::sql_types::Binary;
 use flowy_database::schema::{app_table, app_table::dsl};
 use flowy_infra::{timestamp, uuid};
@@ -31,12 +35,38 @@ pub(crate) struct App {
     pub version: i64,
 }
 
+impl App {
+    pub fn new(params: CreateAppParams) -> Self {
+        let app_id = uuid();
+        let time = timestamp();
+        Self {
+            id: app_id,
+            workspace_id: params.workspace_id,
+            name: params.name,
+            desc: params.desc,
+            color_style: params.color_style.into(),
+            last_view_id: None,
+            modified_time: time,
+            create_time: time,
+            version: 0,
+        }
+    }
+}
+
 #[derive(Clone, PartialEq, Serialize, Deserialize, Debug, Default, FromSqlRow, AsExpression)]
 #[sql_type = "Binary"]
 pub(crate) struct ColorStyleCol {
     pub(crate) theme_color: String,
 }
 
+impl std::convert::From<ColorStyle> for ColorStyleCol {
+    fn from(s: ColorStyle) -> Self {
+        Self {
+            theme_color: s.theme_color,
+        }
+    }
+}
+
 impl std::convert::TryInto<Vec<u8>> for &ColorStyleCol {
     type Error = String;
 
@@ -54,3 +84,23 @@ impl std::convert::TryFrom<&[u8]> for ColorStyleCol {
 }
 
 impl_sql_binary_expression!(ColorStyleCol);
+
+#[derive(AsChangeset, Identifiable, Default, Debug)]
+#[table_name = "app_table"]
+pub struct AppChangeset {
+    pub id: String,
+    pub workspace_id: Option<String>,
+    pub name: Option<String>,
+    pub desc: Option<String>,
+}
+
+impl AppChangeset {
+    pub fn new(params: UpdateAppParams) -> Self {
+        AppChangeset {
+            id: params.app_id,
+            workspace_id: params.workspace_id,
+            name: params.name,
+            desc: params.desc,
+        }
+    }
+}

+ 1 - 0
rust-lib/flowy-workspace/src/sql_tables/mod.rs

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

+ 3 - 0
rust-lib/flowy-workspace/src/sql_tables/view/mod.rs

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

+ 0 - 0
rust-lib/flowy-workspace/src/sql_tables/view/view.rs


+ 2 - 2
rust-lib/flowy-workspace/src/sql_tables/workspace/workspace.rs

@@ -56,8 +56,8 @@ impl WorkspaceChangeset {
     pub fn new(params: UpdateWorkspaceParams) -> Self {
         WorkspaceChangeset {
             id: params.id,
-            name: Some(params.name),
-            desc: Some(params.desc),
+            name: params.name,
+            desc: params.desc,
         }
     }
 }