fix sqlite database is lock issue by setting busy_timeout and wal mode

This commit is contained in:
appflowy 2021-07-19 16:15:20 +08:00
parent 06be0a562c
commit ef4c4180e2
51 changed files with 1539 additions and 700 deletions

View File

@ -11,11 +11,11 @@ import 'package:protobuf/protobuf.dart' as $pb;
class UserErrorCode extends $pb.ProtobufEnum {
static const UserErrorCode Unknown = UserErrorCode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
static const UserErrorCode DatabaseInitFailed = UserErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseInitFailed');
static const UserErrorCode DatabaseWriteLocked = UserErrorCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseWriteLocked');
static const UserErrorCode DatabaseReadLocked = UserErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseReadLocked');
static const UserErrorCode DatabaseUserDidNotMatch = UserErrorCode._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseUserDidNotMatch');
static const UserErrorCode DatabaseInternalError = UserErrorCode._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DatabaseInternalError');
static const UserErrorCode UserDatabaseInitFailed = UserErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseInitFailed');
static const UserErrorCode UserDatabaseWriteLocked = UserErrorCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseWriteLocked');
static const UserErrorCode UserDatabaseReadLocked = UserErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseReadLocked');
static const UserErrorCode UserDatabaseDidNotMatch = UserErrorCode._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseDidNotMatch');
static const UserErrorCode UserDatabaseInternalError = UserErrorCode._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseInternalError');
static const UserErrorCode UserNotLoginYet = UserErrorCode._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserNotLoginYet');
static const UserErrorCode ReadCurrentIdFailed = UserErrorCode._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadCurrentIdFailed');
static const UserErrorCode WriteCurrentIdFailed = UserErrorCode._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WriteCurrentIdFailed');
@ -27,11 +27,11 @@ class UserErrorCode extends $pb.ProtobufEnum {
static const $core.List<UserErrorCode> values = <UserErrorCode> [
Unknown,
DatabaseInitFailed,
DatabaseWriteLocked,
DatabaseReadLocked,
DatabaseUserDidNotMatch,
DatabaseInternalError,
UserDatabaseInitFailed,
UserDatabaseWriteLocked,
UserDatabaseReadLocked,
UserDatabaseDidNotMatch,
UserDatabaseInternalError,
UserNotLoginYet,
ReadCurrentIdFailed,
WriteCurrentIdFailed,

View File

@ -13,11 +13,11 @@ const UserErrorCode$json = const {
'1': 'UserErrorCode',
'2': const [
const {'1': 'Unknown', '2': 0},
const {'1': 'DatabaseInitFailed', '2': 1},
const {'1': 'DatabaseWriteLocked', '2': 2},
const {'1': 'DatabaseReadLocked', '2': 3},
const {'1': 'DatabaseUserDidNotMatch', '2': 4},
const {'1': 'DatabaseInternalError', '2': 5},
const {'1': 'UserDatabaseInitFailed', '2': 1},
const {'1': 'UserDatabaseWriteLocked', '2': 2},
const {'1': 'UserDatabaseReadLocked', '2': 3},
const {'1': 'UserDatabaseDidNotMatch', '2': 4},
const {'1': 'UserDatabaseInternalError', '2': 5},
const {'1': 'UserNotLoginYet', '2': 10},
const {'1': 'ReadCurrentIdFailed', '2': 11},
const {'1': 'WriteCurrentIdFailed', '2': 12},
@ -30,7 +30,7 @@ const UserErrorCode$json = const {
};
/// Descriptor for `UserErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List userErrorCodeDescriptor = $convert.base64Decode('Cg1Vc2VyRXJyb3JDb2RlEgsKB1Vua25vd24QABIWChJEYXRhYmFzZUluaXRGYWlsZWQQARIXChNEYXRhYmFzZVdyaXRlTG9ja2VkEAISFgoSRGF0YWJhc2VSZWFkTG9ja2VkEAMSGwoXRGF0YWJhc2VVc2VyRGlkTm90TWF0Y2gQBBIZChVEYXRhYmFzZUludGVybmFsRXJyb3IQBRITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbEludmFsaWQQFBITCg9QYXNzd29yZEludmFsaWQQFRITCg9Vc2VyTmFtZUludmFsaWQQFhIYChRVc2VyV29ya3NwYWNlSW52YWxpZBAXEhEKDVVzZXJJZEludmFsaWQQGA==');
final $typed_data.Uint8List userErrorCodeDescriptor = $convert.base64Decode('Cg1Vc2VyRXJyb3JDb2RlEgsKB1Vua25vd24QABIaChZVc2VyRGF0YWJhc2VJbml0RmFpbGVkEAESGwoXVXNlckRhdGFiYXNlV3JpdGVMb2NrZWQQAhIaChZVc2VyRGF0YWJhc2VSZWFkTG9ja2VkEAMSGwoXVXNlckRhdGFiYXNlRGlkTm90TWF0Y2gQBBIdChlVc2VyRGF0YWJhc2VJbnRlcm5hbEVycm9yEAUSEwoPVXNlck5vdExvZ2luWWV0EAoSFwoTUmVhZEN1cnJlbnRJZEZhaWxlZBALEhgKFFdyaXRlQ3VycmVudElkRmFpbGVkEAwSEAoMRW1haWxJbnZhbGlkEBQSEwoPUGFzc3dvcmRJbnZhbGlkEBUSEwoPVXNlck5hbWVJbnZhbGlkEBYSGAoUVXNlcldvcmtzcGFjZUludmFsaWQQFxIRCg1Vc2VySWRJbnZhbGlkEBg=');
@$core.Deprecated('Use userErrorDescriptor instead')
const UserError$json = const {
'1': 'UserError',

View File

@ -16,7 +16,7 @@ class WorkspaceErrorCode extends $pb.ProtobufEnum {
static const WorkspaceErrorCode AppColorStyleInvalid = WorkspaceErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppColorStyleInvalid');
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 WorkspaceErrorCode WorkspaceDatabaseError = WorkspaceErrorCode._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceDatabaseError');
static const WorkspaceErrorCode UserInternalError = WorkspaceErrorCode._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserInternalError');
static const WorkspaceErrorCode UserNotLoginYet = WorkspaceErrorCode._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserNotLoginYet');
@ -27,7 +27,7 @@ class WorkspaceErrorCode extends $pb.ProtobufEnum {
AppColorStyleInvalid,
AppIdInvalid,
DatabaseConnectionFail,
DatabaseInternalError,
WorkspaceDatabaseError,
UserInternalError,
UserNotLoginYet,
];

View File

@ -18,14 +18,14 @@ const WorkspaceErrorCode$json = const {
const {'1': 'AppColorStyleInvalid', '2': 3},
const {'1': 'AppIdInvalid', '2': 4},
const {'1': 'DatabaseConnectionFail', '2': 5},
const {'1': 'DatabaseInternalError', '2': 6},
const {'1': 'WorkspaceDatabaseError', '2': 6},
const {'1': 'UserInternalError', '2': 10},
const {'1': 'UserNotLoginYet', '2': 11},
],
};
/// Descriptor for `WorkspaceErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List workspaceErrorCodeDescriptor = $convert.base64Decode('ChJXb3Jrc3BhY2VFcnJvckNvZGUSCwoHVW5rbm93bhAAEhgKFFdvcmtzcGFjZU5hbWVJbnZhbGlkEAESFgoSV29ya3NwYWNlSWRJbnZhbGlkEAISGAoUQXBwQ29sb3JTdHlsZUludmFsaWQQAxIQCgxBcHBJZEludmFsaWQQBBIaChZEYXRhYmFzZUNvbm5lY3Rpb25GYWlsEAUSGQoVRGF0YWJhc2VJbnRlcm5hbEVycm9yEAYSFQoRVXNlckludGVybmFsRXJyb3IQChITCg9Vc2VyTm90TG9naW5ZZXQQCw==');
final $typed_data.Uint8List workspaceErrorCodeDescriptor = $convert.base64Decode('ChJXb3Jrc3BhY2VFcnJvckNvZGUSCwoHVW5rbm93bhAAEhgKFFdvcmtzcGFjZU5hbWVJbnZhbGlkEAESFgoSV29ya3NwYWNlSWRJbnZhbGlkEAISGAoUQXBwQ29sb3JTdHlsZUludmFsaWQQAxIQCgxBcHBJZEludmFsaWQQBBIaChZEYXRhYmFzZUNvbm5lY3Rpb25GYWlsEAUSGgoWV29ya3NwYWNlRGF0YWJhc2VFcnJvchAGEhUKEVVzZXJJbnRlcm5hbEVycm9yEAoSEwoPVXNlck5vdExvZ2luWWV0EAs=');
@$core.Deprecated('Use workspaceErrorDescriptor instead')
const WorkspaceError$json = const {
'1': 'WorkspaceError',

View File

@ -11,11 +11,11 @@ import 'package:protobuf/protobuf.dart' as $pb;
class WorkspaceEvent extends $pb.ProtobufEnum {
static const WorkspaceEvent CreateWorkspace = WorkspaceEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateWorkspace');
static const WorkspaceEvent GetWorkspaceUserDetail = WorkspaceEvent._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetWorkspaceUserDetail');
static const WorkspaceEvent GetWorkspaceDetail = WorkspaceEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetWorkspaceDetail');
static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [
CreateWorkspace,
GetWorkspaceUserDetail,
GetWorkspaceDetail,
];
static final $core.Map<$core.int, WorkspaceEvent> _byValue = $pb.ProtobufEnum.initByValue(values);

View File

@ -13,9 +13,9 @@ const WorkspaceEvent$json = const {
'1': 'WorkspaceEvent',
'2': const [
const {'1': 'CreateWorkspace', '2': 0},
const {'1': 'GetWorkspaceUserDetail', '2': 100},
const {'1': 'GetWorkspaceDetail', '2': 1},
],
};
/// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIaChZHZXRXb3Jrc3BhY2VVc2VyRGV0YWlsEGQ=');
final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIWChJHZXRXb3Jrc3BhY2VEZXRhaWwQAQ==');

View File

@ -3,5 +3,6 @@ export './errors.pb.dart';
export './workspace_update.pb.dart';
export './app_create.pb.dart';
export './event.pb.dart';
export './workspace_user_detail.pb.dart';
export './workspace_create.pb.dart';
export './app_update.pb.dart';

View File

@ -0,0 +1,137 @@
///
// Generated code. Do not modify.
// source: workspace_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
import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
import 'workspace_create.pb.dart' as $0;
class UserWorkspace extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UserWorkspace', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'owner')
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'workspaceId')
..hasRequiredFields = false
;
UserWorkspace._() : super();
factory UserWorkspace({
$core.String? owner,
$core.String? workspaceId,
}) {
final _result = create();
if (owner != null) {
_result.owner = owner;
}
if (workspaceId != null) {
_result.workspaceId = workspaceId;
}
return _result;
}
factory UserWorkspace.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory UserWorkspace.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')
UserWorkspace clone() => UserWorkspace()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
UserWorkspace copyWith(void Function(UserWorkspace) updates) => super.copyWith((message) => updates(message as UserWorkspace)) as UserWorkspace; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static UserWorkspace create() => UserWorkspace._();
UserWorkspace createEmptyInstance() => create();
static $pb.PbList<UserWorkspace> createRepeated() => $pb.PbList<UserWorkspace>();
@$core.pragma('dart2js:noInline')
static UserWorkspace getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UserWorkspace>(create);
static UserWorkspace? _defaultInstance;
@$pb.TagNumber(1)
$core.String get owner => $_getSZ(0);
@$pb.TagNumber(1)
set owner($core.String v) { $_setString(0, v); }
@$pb.TagNumber(1)
$core.bool hasOwner() => $_has(0);
@$pb.TagNumber(1)
void clearOwner() => 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);
}
class UserWorkspaceDetail extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UserWorkspaceDetail', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'owner')
..aOM<$0.WorkspaceDetail>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'workspace', subBuilder: $0.WorkspaceDetail.create)
..hasRequiredFields = false
;
UserWorkspaceDetail._() : super();
factory UserWorkspaceDetail({
$core.String? owner,
$0.WorkspaceDetail? workspace,
}) {
final _result = create();
if (owner != null) {
_result.owner = owner;
}
if (workspace != null) {
_result.workspace = workspace;
}
return _result;
}
factory UserWorkspaceDetail.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory UserWorkspaceDetail.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')
UserWorkspaceDetail clone() => UserWorkspaceDetail()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
UserWorkspaceDetail copyWith(void Function(UserWorkspaceDetail) updates) => super.copyWith((message) => updates(message as UserWorkspaceDetail)) as UserWorkspaceDetail; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static UserWorkspaceDetail create() => UserWorkspaceDetail._();
UserWorkspaceDetail createEmptyInstance() => create();
static $pb.PbList<UserWorkspaceDetail> createRepeated() => $pb.PbList<UserWorkspaceDetail>();
@$core.pragma('dart2js:noInline')
static UserWorkspaceDetail getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UserWorkspaceDetail>(create);
static UserWorkspaceDetail? _defaultInstance;
@$pb.TagNumber(1)
$core.String get owner => $_getSZ(0);
@$pb.TagNumber(1)
set owner($core.String v) { $_setString(0, v); }
@$pb.TagNumber(1)
$core.bool hasOwner() => $_has(0);
@$pb.TagNumber(1)
void clearOwner() => clearField(1);
@$pb.TagNumber(2)
$0.WorkspaceDetail get workspace => $_getN(1);
@$pb.TagNumber(2)
set workspace($0.WorkspaceDetail v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasWorkspace() => $_has(1);
@$pb.TagNumber(2)
void clearWorkspace() => clearField(2);
@$pb.TagNumber(2)
$0.WorkspaceDetail ensureWorkspace() => $_ensure(1);
}

View File

@ -0,0 +1,7 @@
///
// Generated code. Do not modify.
// source: workspace_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

View File

@ -0,0 +1,32 @@
///
// Generated code. Do not modify.
// source: workspace_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,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 userWorkspaceDescriptor instead')
const UserWorkspace$json = const {
'1': 'UserWorkspace',
'2': const [
const {'1': 'owner', '3': 1, '4': 1, '5': 9, '10': 'owner'},
const {'1': 'workspace_id', '3': 2, '4': 1, '5': 9, '10': 'workspaceId'},
],
};
/// Descriptor for `UserWorkspace`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List userWorkspaceDescriptor = $convert.base64Decode('Cg1Vc2VyV29ya3NwYWNlEhQKBW93bmVyGAEgASgJUgVvd25lchIhCgx3b3Jrc3BhY2VfaWQYAiABKAlSC3dvcmtzcGFjZUlk');
@$core.Deprecated('Use userWorkspaceDetailDescriptor instead')
const UserWorkspaceDetail$json = const {
'1': 'UserWorkspaceDetail',
'2': const [
const {'1': 'owner', '3': 1, '4': 1, '5': 9, '10': 'owner'},
const {'1': 'workspace', '3': 2, '4': 1, '5': 11, '6': '.WorkspaceDetail', '10': 'workspace'},
],
};
/// Descriptor for `UserWorkspaceDetail`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List userWorkspaceDetailDescriptor = $convert.base64Decode('ChNVc2VyV29ya3NwYWNlRGV0YWlsEhQKBW93bmVyGAEgASgJUgVvd25lchIuCgl3b3Jrc3BhY2UYAiABKAsyEC5Xb3Jrc3BhY2VEZXRhaWxSCXdvcmtzcGFjZQ==');

View File

@ -0,0 +1,9 @@
///
// Generated code. Do not modify.
// source: workspace_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,deprecated_member_use_from_same_package
export 'workspace_user_detail.pb.dart';

View File

@ -23,6 +23,8 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
| "UpdateWorkspaceRequest"
| "CreateWorkspaceRequest"
| "WorkspaceDetail"
| "UserWorkspace"
| "UserWorkspaceDetail"
| "WorkspaceError"
| "FFIRequest"
| "FFIResponse"

View File

@ -99,14 +99,14 @@ impl EventDispatch {
}
#[pin_project]
pub struct DispatchFuture<T: Responder + Send + Sync> {
pub struct DispatchFuture<T: Send + Sync> {
#[pin]
pub fut: Pin<Box<dyn Future<Output = T> + Sync + Send>>,
}
impl<T> Future for DispatchFuture<T>
where
T: Responder + Send + Sync,
T: Send + Sync,
{
type Output = T;

View File

@ -18,22 +18,21 @@ pub struct KVStore {
impl KVStore {
fn new() -> Self { KVStore { database: None } }
pub fn set(item: KeyValue) -> Result<(), String> {
let conn = get_connection()?;
fn set(item: KeyValue) -> Result<(), String> {
let _ = diesel::replace_into(kv_table::table)
.values(&item)
.execute(&*conn)
.map_err(|e| format!("{:?}", e))?;
.execute(&*(get_connection()?))
.map_err(|e| format!("KV set error: {:?}", e))?;
Ok(())
}
pub fn get(key: &str) -> Result<KeyValue, String> {
fn get(key: &str) -> Result<KeyValue, String> {
let conn = get_connection()?;
let item = dsl::kv_table
.filter(kv_table::key.eq(key))
.first::<KeyValue>(&*conn)
.map_err(|e| format!("{:?}", e))?;
.map_err(|e| format!("KV get error: {:?}", e))?;
Ok(item)
}
@ -43,7 +42,7 @@ impl KVStore {
let sql = dsl::kv_table.filter(kv_table::key.eq(key));
let _ = diesel::delete(sql)
.execute(&*conn)
.map_err(|e| format!("{:?}", e))?;
.map_err(|e| format!("KV remove error: {:?}", e))?;
Ok(())
}
@ -125,7 +124,7 @@ fn get_connection() -> Result<DBConnection, String> {
.as_ref()
.expect("KVStore is not init")
.get_connection()
.map_err(|e| format!("{:?}", e))?;
.map_err(|e| format!("KVStore error: {:?}", e))?;
Ok(conn)
},
Err(e) => {

View File

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

View File

@ -0,0 +1,63 @@
use flowy_database::DBConnection;
use flowy_dispatch::prelude::DispatchFuture;
use flowy_user::prelude::UserSession;
use flowy_workspace::{
entities::workspace::UserWorkspace,
errors::{ErrorBuilder, WorkspaceError, WorkspaceErrorCode},
module::WorkspaceUser,
};
use std::sync::Arc;
pub struct WorkspaceUserImpl {
pub(crate) user_session: Arc<UserSession>,
}
impl WorkspaceUser for WorkspaceUserImpl {
fn set_cur_workspace_id(
&self,
workspace_id: &str,
) -> DispatchFuture<Result<(), WorkspaceError>> {
let user_session = self.user_session.clone();
let workspace_id = workspace_id.to_owned();
DispatchFuture {
fut: Box::pin(async move {
let _ = user_session
.set_current_workspace(&workspace_id)
.await
.map_err(|e| {
ErrorBuilder::new(WorkspaceErrorCode::UserInternalError)
.error(e)
.build()
});
Ok(())
}),
}
}
fn get_cur_workspace(&self) -> DispatchFuture<Result<UserWorkspace, WorkspaceError>> {
let user_session = self.user_session.clone();
DispatchFuture {
fut: Box::pin(async move {
let user_detail = user_session.user_detail().map_err(|e| {
ErrorBuilder::new(WorkspaceErrorCode::UserNotLoginYet)
.error(e)
.build()
})?;
Ok(UserWorkspace {
owner: user_detail.email,
workspace_id: user_detail.workspace,
})
}),
}
}
fn db_connection(&self) -> Result<DBConnection, WorkspaceError> {
self.user_session.get_db_connection().map_err(|e| {
ErrorBuilder::new(WorkspaceErrorCode::DatabaseConnectionFail)
.error(e)
.build()
})
}
}

View File

@ -1,7 +1,9 @@
mod deps_resolve;
mod flowy_server;
pub mod module;
pub use crate::flowy_server::{ArcFlowyServer, FlowyServerMocker};
use deps_resolve::*;
use flowy_dispatch::prelude::*;
use module::build_modules;
pub use module::*;
@ -31,7 +33,10 @@ impl FlowySDK {
FlowySDK::init_log(root);
tracing::info!("🔥 Root path: {}", root);
let _ = flowy_infra::kv::KVStore::init(root);
match flowy_infra::kv::KVStore::init(root) {
Ok(_) => {},
Err(e) => tracing::error!("Init kv store failedL: {}", e),
}
FlowySDK::init_modules(root, server);
}

View File

@ -4,6 +4,8 @@ use flowy_dispatch::prelude::{DispatchFuture, Module};
use flowy_user::prelude::*;
use flowy_workspace::prelude::*;
use crate::deps_resolve::WorkspaceUserImpl;
use flowy_workspace::entities::workspace::UserWorkspace;
use std::sync::Arc;
pub struct ModuleConfig {
@ -26,45 +28,3 @@ pub fn build_modules(config: ModuleConfig, _server: ArcFlowyServer) -> Vec<Modul
flowy_workspace::module::create(workspace_user_impl),
]
}
pub struct WorkspaceUserImpl {
user_session: Arc<UserSession>,
}
impl WorkspaceUser for WorkspaceUserImpl {
fn set_workspace(&self, workspace_id: &str) -> DispatchFuture<Result<(), WorkspaceError>> {
let user_session = self.user_session.clone();
let workspace_id = workspace_id.to_owned();
DispatchFuture {
fut: Box::pin(async move {
let _ = user_session
.set_current_workspace(&workspace_id)
.await
.map_err(|e| {
ErrorBuilder::new(WorkspaceErrorCode::UserInternalError)
.error(e)
.build()
});
Ok(())
}),
}
}
fn get_workspace(&self) -> Result<String, WorkspaceError> {
let user_detail = self.user_session.user_detail().map_err(|e| {
ErrorBuilder::new(WorkspaceErrorCode::UserNotLoginYet)
.error(e)
.build()
})?;
Ok(user_detail.id)
}
fn db_connection(&self) -> Result<DBConnection, WorkspaceError> {
self.user_session.get_db_connection().map_err(|e| {
ErrorBuilder::new(WorkspaceErrorCode::DatabaseConnectionFail)
.error(e)
.build()
})
}
}

View File

@ -0,0 +1,30 @@
use crate::errors::*;
use diesel::{
dsl::sql,
expression::SqlLiteral,
query_dsl::LoadQuery,
Connection,
RunQueryDsl,
SqliteConnection,
};
pub trait ConnectionExtension: Connection {
fn query<ST, T>(&self, query: &str) -> Result<T>
where
SqlLiteral<ST>: LoadQuery<SqliteConnection, T>;
fn exec(&self, query: impl AsRef<str>) -> Result<usize>;
}
impl ConnectionExtension for SqliteConnection {
fn query<ST, T>(&self, query: &str) -> Result<T>
where
SqlLiteral<ST>: LoadQuery<SqliteConnection, T>,
{
Ok(sql::<ST>(query).get_result(self)?)
}
fn exec(&self, query: impl AsRef<str>) -> Result<usize> {
Ok(SqliteConnection::execute(self, query.as_ref())?)
}
}

View File

@ -1,7 +1,10 @@
mod conn_ext;
mod database;
#[allow(deprecated, clippy::large_enum_variant)]
mod errors;
mod pool;
mod pragma;
pub use database::*;
pub use pool::*;

View File

@ -1,6 +1,6 @@
use crate::errors::*;
use crate::{conn_ext::*, errors::*, pragma::*};
use diesel::{connection::Connection, SqliteConnection};
use r2d2::{ManageConnection, Pool};
use r2d2::{CustomizeConnection, ManageConnection, Pool};
use scheduled_thread_pool::ScheduledThreadPool;
use std::{
sync::{
@ -34,10 +34,12 @@ impl ConnectionPool {
let manager = ConnectionManager::new(uri);
let thread_pool = DB_POOL.clone();
let config = Arc::new(config);
let customizer_config = DatabaseCustomizerConfig::default();
let pool = r2d2::Pool::builder()
.thread_pool(thread_pool)
.min_idle(Some(config.min_idle))
.connection_customizer(Box::new(DatabaseCustomizer::new(customizer_config)))
.max_size(config.max_size)
.max_lifetime(None)
.connection_timeout(config.connection_timeout)
@ -127,3 +129,48 @@ impl ManageConnection for ConnectionManager {
impl ConnectionManager {
pub fn new<S: Into<String>>(uri: S) -> Self { ConnectionManager { db_uri: uri.into() } }
}
#[derive(Debug)]
pub struct DatabaseCustomizerConfig {
pub(crate) journal_mode: SQLiteJournalMode,
pub(crate) synchronous: SQLiteSynchronous,
pub(crate) busy_timeout: i32,
pub(crate) secure_delete: bool,
}
impl Default for DatabaseCustomizerConfig {
fn default() -> Self {
Self {
journal_mode: SQLiteJournalMode::WAL,
synchronous: SQLiteSynchronous::NORMAL,
busy_timeout: 5000,
secure_delete: true,
}
}
}
#[derive(Debug)]
struct DatabaseCustomizer {
config: DatabaseCustomizerConfig,
}
impl DatabaseCustomizer {
fn new(config: DatabaseCustomizerConfig) -> Self
where
Self: Sized,
{
Self { config }
}
}
impl CustomizeConnection<SqliteConnection, crate::Error> for DatabaseCustomizer {
fn on_acquire(&self, conn: &mut SqliteConnection) -> Result<()> {
conn.pragma_set_busy_timeout(self.config.busy_timeout)?;
if self.config.journal_mode != SQLiteJournalMode::WAL {
conn.pragma_set_journal_mode(self.config.journal_mode, None)?;
}
conn.pragma_set_synchronous(self.config.synchronous, None)?;
Ok(())
}
}

View File

@ -0,0 +1,186 @@
use crate::errors::{Error, Result};
use diesel::{
expression::SqlLiteral,
query_dsl::load_dsl::LoadQuery,
sql_types::{Integer, Text},
SqliteConnection,
};
use crate::conn_ext::ConnectionExtension;
use std::{
convert::{TryFrom, TryInto},
fmt,
str::FromStr,
};
pub trait PragmaExtension: ConnectionExtension {
fn pragma<D: std::fmt::Display>(&self, key: &str, val: D, schema: Option<&str>) -> Result<()> {
let query = match schema {
Some(schema) => format!("PRAGMA {}.{} = '{}'", schema, key, val),
None => format!("PRAGMA {} = '{}'", key, val),
};
log::trace!("SQLITE {}", query);
self.exec(&query)?;
Ok(())
}
fn pragma_ret<ST, T, D: std::fmt::Display>(
&self,
key: &str,
val: D,
schema: Option<&str>,
) -> Result<T>
where
SqlLiteral<ST>: LoadQuery<SqliteConnection, T>,
{
let query = match schema {
Some(schema) => format!("PRAGMA {}.{} = '{}'", schema, key, val),
None => format!("PRAGMA {} = '{}'", key, val),
};
log::trace!("SQLITE {}", query);
Ok(self.query::<ST, T>(&query)?)
}
fn pragma_get<ST, T>(&self, key: &str, schema: Option<&str>) -> Result<T>
where
SqlLiteral<ST>: LoadQuery<SqliteConnection, T>,
{
let query = match schema {
Some(schema) => format!("PRAGMA {}.{}", schema, key),
None => format!("PRAGMA {}", key),
};
log::trace!("SQLITE {}", query);
Ok(self.query::<ST, T>(&query)?)
}
fn pragma_set_busy_timeout(&self, timeout_ms: i32) -> Result<i32> {
self.pragma_ret::<Integer, i32, i32>("busy_timeout", timeout_ms, None)
}
fn pragma_get_busy_timeout(&self) -> Result<i32> {
self.pragma_get::<Integer, i32>("busy_timeout", None)
}
fn pragma_set_journal_mode(
&self,
mode: SQLiteJournalMode,
schema: Option<&str>,
) -> Result<i32> {
self.pragma_ret::<Integer, i32, SQLiteJournalMode>("journal_mode", mode, schema)
}
fn pragma_get_journal_mode(&self, schema: Option<&str>) -> Result<SQLiteJournalMode> {
Ok(self
.pragma_get::<Text, String>("journal_mode", schema)?
.parse()?)
}
fn pragma_set_synchronous(
&self,
synchronous: SQLiteSynchronous,
schema: Option<&str>,
) -> Result<()> {
self.pragma("synchronous", synchronous as u8, schema)
}
fn pragma_get_synchronous(&self, schema: Option<&str>) -> Result<SQLiteSynchronous> {
Ok(self
.pragma_get::<Integer, i32>("synchronous", schema)?
.try_into()?)
}
}
impl PragmaExtension for SqliteConnection {}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SQLiteJournalMode {
DELETE,
TRUNCATE,
PERSIST,
MEMORY,
WAL,
OFF,
}
impl fmt::Display for SQLiteJournalMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::DELETE => "DELETE",
Self::TRUNCATE => "TRUNCATE",
Self::PERSIST => "PERSIST",
Self::MEMORY => "MEMORY",
Self::WAL => "WAL",
Self::OFF => "OFF",
}
)
}
}
impl FromStr for SQLiteJournalMode {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_uppercase().as_ref() {
"DELETE" => Ok(Self::DELETE),
"TRUNCATE" => Ok(Self::TRUNCATE),
"PERSIST" => Ok(Self::PERSIST),
"MEMORY" => Ok(Self::MEMORY),
"WAL" => Ok(Self::WAL),
"OFF" => Ok(Self::OFF),
_ => Err(format!("Unknown value {} for JournalMode", s).into()),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SQLiteSynchronous {
EXTRA = 3,
FULL = 2,
NORMAL = 1,
OFF = 0,
}
impl fmt::Display for SQLiteSynchronous {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::OFF => "OFF",
Self::NORMAL => "NORMAL",
Self::FULL => "FULL",
Self::EXTRA => "EXTRA",
}
)
}
}
impl TryFrom<i32> for SQLiteSynchronous {
type Error = Error;
fn try_from(v: i32) -> Result<Self> {
match v {
0 => Ok(Self::OFF),
1 => Ok(Self::NORMAL),
2 => Ok(Self::FULL),
3 => Ok(Self::EXTRA),
_ => Err(format!("Unknown value {} for Synchronous", v).into()),
}
}
}
impl FromStr for SQLiteSynchronous {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_uppercase().as_ref() {
"0" | "OFF" => Ok(Self::OFF),
"1" | "NORMAL" => Ok(Self::NORMAL),
"2" | "FULL" => Ok(Self::FULL),
"3" | "EXTRA" => Ok(Self::EXTRA),
_ => Err(format!("Unknown value {} for Synchronous", s).into()),
}
}
}

View File

@ -20,18 +20,23 @@ impl WorkspaceTestBuilder {
tester: Box::new(FixedUserTester::<WorkspaceError>::new()),
user_detail: None,
};
builder.login()
builder.login_if_need()
}
}
pub type UserTestBuilder = TestBuilder<RandomUserTester<UserError>>;
impl UserTestBuilder {
pub fn new() -> Self {
Self {
let builder = Self {
tester: Box::new(RandomUserTester::<UserError>::new()),
user_detail: None,
}
};
builder
}
pub fn reset(mut self) -> Self { self.logout().login() }
}
pub struct TestBuilder<T: TesterTrait> {
@ -49,6 +54,12 @@ where
self
}
pub fn login_if_need(mut self) -> Self {
let user_detail = self.tester.login_if_need();
self.user_detail = Some(user_detail);
self
}
pub fn logout(self) -> Self {
self.tester.logout();
self

View File

@ -6,7 +6,7 @@ use flowy_dispatch::prelude::*;
pub use flowy_sdk::*;
use flowy_user::{
errors::UserError,
event::UserEvent::{SignIn, SignOut},
event::UserEvent::{GetStatus, SignIn, SignOut},
prelude::*,
};
use std::{
@ -99,7 +99,6 @@ pub trait TesterTrait {
fn login(&self) -> UserDetail {
init_test_sdk(self.context().server.clone());
self.logout();
let payload = SignInRequest {
email: self.context().user_email.clone(),
password: valid_password(),
@ -116,6 +115,17 @@ pub trait TesterTrait {
user_detail
}
fn login_if_need(&self) -> UserDetail {
init_test_sdk(self.context().server.clone());
match EventDispatch::sync_send(ModuleRequest::new(GetStatus))
.parse::<UserDetail, UserError>()
.unwrap()
{
Ok(user_detail) => user_detail,
Err(e) => self.login(),
}
}
fn logout(&self) {
init_test_sdk(self.context().server.clone());
let _ = EventDispatch::sync_send(ModuleRequest::new(SignOut));

View File

@ -26,15 +26,15 @@ pub enum UserErrorCode {
#[display(fmt = "Unknown")]
Unknown = 0,
#[display(fmt = "Database init failed")]
DatabaseInitFailed = 1,
UserDatabaseInitFailed = 1,
#[display(fmt = "Get database write lock failed")]
DatabaseWriteLocked = 2,
UserDatabaseWriteLocked = 2,
#[display(fmt = "Get database read lock failed")]
DatabaseReadLocked = 3,
UserDatabaseReadLocked = 3,
#[display(fmt = "Opening database is not belonging to the current user")]
DatabaseUserDidNotMatch = 4,
UserDatabaseDidNotMatch = 4,
#[display(fmt = "Database internal error")]
DatabaseInternalError = 5,
UserDatabaseInternalError = 5,
#[display(fmt = "User not login yet")]
UserNotLoginYet = 10,
@ -61,7 +61,7 @@ impl std::default::Default for UserErrorCode {
impl std::convert::From<flowy_database::result::Error> for UserError {
fn from(error: flowy_database::result::Error) -> Self {
ErrorBuilder::new(UserErrorCode::DatabaseInternalError)
ErrorBuilder::new(UserErrorCode::UserDatabaseInternalError)
.error(error)
.build()
}
@ -69,7 +69,7 @@ impl std::convert::From<flowy_database::result::Error> for UserError {
impl std::convert::From<flowy_sqlite::Error> for UserError {
fn from(error: flowy_sqlite::Error) -> Self {
ErrorBuilder::new(UserErrorCode::DatabaseInternalError)
ErrorBuilder::new(UserErrorCode::UserDatabaseInternalError)
.error(error)
.build()
}

View File

@ -216,11 +216,11 @@ impl ::protobuf::reflect::ProtobufValue for UserError {
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum UserErrorCode {
Unknown = 0,
DatabaseInitFailed = 1,
DatabaseWriteLocked = 2,
DatabaseReadLocked = 3,
DatabaseUserDidNotMatch = 4,
DatabaseInternalError = 5,
UserDatabaseInitFailed = 1,
UserDatabaseWriteLocked = 2,
UserDatabaseReadLocked = 3,
UserDatabaseDidNotMatch = 4,
UserDatabaseInternalError = 5,
UserNotLoginYet = 10,
ReadCurrentIdFailed = 11,
WriteCurrentIdFailed = 12,
@ -239,11 +239,11 @@ impl ::protobuf::ProtobufEnum for UserErrorCode {
fn from_i32(value: i32) -> ::std::option::Option<UserErrorCode> {
match value {
0 => ::std::option::Option::Some(UserErrorCode::Unknown),
1 => ::std::option::Option::Some(UserErrorCode::DatabaseInitFailed),
2 => ::std::option::Option::Some(UserErrorCode::DatabaseWriteLocked),
3 => ::std::option::Option::Some(UserErrorCode::DatabaseReadLocked),
4 => ::std::option::Option::Some(UserErrorCode::DatabaseUserDidNotMatch),
5 => ::std::option::Option::Some(UserErrorCode::DatabaseInternalError),
1 => ::std::option::Option::Some(UserErrorCode::UserDatabaseInitFailed),
2 => ::std::option::Option::Some(UserErrorCode::UserDatabaseWriteLocked),
3 => ::std::option::Option::Some(UserErrorCode::UserDatabaseReadLocked),
4 => ::std::option::Option::Some(UserErrorCode::UserDatabaseDidNotMatch),
5 => ::std::option::Option::Some(UserErrorCode::UserDatabaseInternalError),
10 => ::std::option::Option::Some(UserErrorCode::UserNotLoginYet),
11 => ::std::option::Option::Some(UserErrorCode::ReadCurrentIdFailed),
12 => ::std::option::Option::Some(UserErrorCode::WriteCurrentIdFailed),
@ -259,11 +259,11 @@ impl ::protobuf::ProtobufEnum for UserErrorCode {
fn values() -> &'static [Self] {
static values: &'static [UserErrorCode] = &[
UserErrorCode::Unknown,
UserErrorCode::DatabaseInitFailed,
UserErrorCode::DatabaseWriteLocked,
UserErrorCode::DatabaseReadLocked,
UserErrorCode::DatabaseUserDidNotMatch,
UserErrorCode::DatabaseInternalError,
UserErrorCode::UserDatabaseInitFailed,
UserErrorCode::UserDatabaseWriteLocked,
UserErrorCode::UserDatabaseReadLocked,
UserErrorCode::UserDatabaseDidNotMatch,
UserErrorCode::UserDatabaseInternalError,
UserErrorCode::UserNotLoginYet,
UserErrorCode::ReadCurrentIdFailed,
UserErrorCode::WriteCurrentIdFailed,
@ -302,51 +302,51 @@ impl ::protobuf::reflect::ProtobufValue for UserErrorCode {
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0cerrors.proto\"A\n\tUserError\x12\"\n\x04code\x18\x01\x20\x01(\x0e2\
\x0e.UserErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03msg*\
\xce\x02\n\rUserErrorCode\x12\x0b\n\x07Unknown\x10\0\x12\x16\n\x12Databa\
seInitFailed\x10\x01\x12\x17\n\x13DatabaseWriteLocked\x10\x02\x12\x16\n\
\x12DatabaseReadLocked\x10\x03\x12\x1b\n\x17DatabaseUserDidNotMatch\x10\
\x04\x12\x19\n\x15DatabaseInternalError\x10\x05\x12\x13\n\x0fUserNotLogi\
nYet\x10\n\x12\x17\n\x13ReadCurrentIdFailed\x10\x0b\x12\x18\n\x14WriteCu\
rrentIdFailed\x10\x0c\x12\x10\n\x0cEmailInvalid\x10\x14\x12\x13\n\x0fPas\
swordInvalid\x10\x15\x12\x13\n\x0fUserNameInvalid\x10\x16\x12\x18\n\x14U\
serWorkspaceInvalid\x10\x17\x12\x11\n\rUserIdInvalid\x10\x18J\xee\x05\n\
\x06\x12\x04\0\0\x15\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\x11\n\x0b\n\
\x04\x04\0\x02\0\x12\x03\x03\x04\x1b\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\
\x03\x04\x11\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x12\x16\n\x0c\n\x05\
\x04\0\x02\0\x03\x12\x03\x03\x19\x1a\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\x15\x01\n\n\n\x03\x05\0\
\x01\x12\x03\x06\x05\x12\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\x1b\n\
\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\x04\x16\n\x0c\n\x05\x05\0\x02\
\x01\x02\x12\x03\x08\x19\x1a\n\x0b\n\x04\x05\0\x02\x02\x12\x03\t\x04\x1c\
\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\x04\x17\n\x0c\n\x05\x05\0\x02\
\x02\x02\x12\x03\t\x1a\x1b\n\x0b\n\x04\x05\0\x02\x03\x12\x03\n\x04\x1b\n\
\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\x16\n\x0c\n\x05\x05\0\x02\x03\
\x02\x12\x03\n\x19\x1a\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x0b\x04\x20\n\
\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x0b\x04\x1b\n\x0c\n\x05\x05\0\x02\
\x04\x02\x12\x03\x0b\x1e\x1f\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x0c\x04\
\x1e\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x0c\x04\x19\n\x0c\n\x05\x05\0\
\x02\x05\x02\x12\x03\x0c\x1c\x1d\n\x0b\n\x04\x05\0\x02\x06\x12\x03\r\x04\
\x19\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\r\x04\x13\n\x0c\n\x05\x05\0\
\x02\x06\x02\x12\x03\r\x16\x18\n\x0b\n\x04\x05\0\x02\x07\x12\x03\x0e\x04\
\x1d\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\x0e\x04\x17\n\x0c\n\x05\x05\0\
\x02\x07\x02\x12\x03\x0e\x1a\x1c\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0f\
\x04\x1e\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0f\x04\x18\n\x0c\n\x05\
\x05\0\x02\x08\x02\x12\x03\x0f\x1b\x1d\n\x0b\n\x04\x05\0\x02\t\x12\x03\
\x10\x04\x16\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x10\x04\x10\n\x0c\n\x05\
\x05\0\x02\t\x02\x12\x03\x10\x13\x15\n\x0b\n\x04\x05\0\x02\n\x12\x03\x11\
\x04\x19\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\x11\x04\x13\n\x0c\n\x05\x05\
\0\x02\n\x02\x12\x03\x11\x16\x18\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x12\
\x04\x19\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x12\x04\x13\n\x0c\n\x05\
\x05\0\x02\x0b\x02\x12\x03\x12\x16\x18\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\
\x13\x04\x1e\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\x13\x04\x18\n\x0c\n\
\x05\x05\0\x02\x0c\x02\x12\x03\x13\x1b\x1d\n\x0b\n\x04\x05\0\x02\r\x12\
\x03\x14\x04\x17\n\x0c\n\x05\x05\0\x02\r\x01\x12\x03\x14\x04\x11\n\x0c\n\
\x05\x05\0\x02\r\x02\x12\x03\x14\x14\x16b\x06proto3\
\xde\x02\n\rUserErrorCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16UserDa\
tabaseInitFailed\x10\x01\x12\x1b\n\x17UserDatabaseWriteLocked\x10\x02\
\x12\x1a\n\x16UserDatabaseReadLocked\x10\x03\x12\x1b\n\x17UserDatabaseDi\
dNotMatch\x10\x04\x12\x1d\n\x19UserDatabaseInternalError\x10\x05\x12\x13\
\n\x0fUserNotLoginYet\x10\n\x12\x17\n\x13ReadCurrentIdFailed\x10\x0b\x12\
\x18\n\x14WriteCurrentIdFailed\x10\x0c\x12\x10\n\x0cEmailInvalid\x10\x14\
\x12\x13\n\x0fPasswordInvalid\x10\x15\x12\x13\n\x0fUserNameInvalid\x10\
\x16\x12\x18\n\x14UserWorkspaceInvalid\x10\x17\x12\x11\n\rUserIdInvalid\
\x10\x18J\xee\x05\n\x06\x12\x04\0\0\x15\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\x11\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x1b\n\x0c\n\x05\x04\
\0\x02\0\x06\x12\x03\x03\x04\x11\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\
\x12\x16\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x19\x1a\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\x15\x01\
\n\n\n\x03\x05\0\x01\x12\x03\x06\x05\x12\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\x1f\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\x04\x1a\n\x0c\n\
\x05\x05\0\x02\x01\x02\x12\x03\x08\x1d\x1e\n\x0b\n\x04\x05\0\x02\x02\x12\
\x03\t\x04\x20\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\x04\x1b\n\x0c\n\
\x05\x05\0\x02\x02\x02\x12\x03\t\x1e\x1f\n\x0b\n\x04\x05\0\x02\x03\x12\
\x03\n\x04\x1f\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\x1a\n\x0c\n\
\x05\x05\0\x02\x03\x02\x12\x03\n\x1d\x1e\n\x0b\n\x04\x05\0\x02\x04\x12\
\x03\x0b\x04\x20\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x0b\x04\x1b\n\x0c\
\n\x05\x05\0\x02\x04\x02\x12\x03\x0b\x1e\x1f\n\x0b\n\x04\x05\0\x02\x05\
\x12\x03\x0c\x04\"\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x0c\x04\x1d\n\
\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x0c\x20!\n\x0b\n\x04\x05\0\x02\x06\
\x12\x03\r\x04\x19\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\r\x04\x13\n\x0c\
\n\x05\x05\0\x02\x06\x02\x12\x03\r\x16\x18\n\x0b\n\x04\x05\0\x02\x07\x12\
\x03\x0e\x04\x1d\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\x0e\x04\x17\n\x0c\
\n\x05\x05\0\x02\x07\x02\x12\x03\x0e\x1a\x1c\n\x0b\n\x04\x05\0\x02\x08\
\x12\x03\x0f\x04\x1e\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0f\x04\x18\n\
\x0c\n\x05\x05\0\x02\x08\x02\x12\x03\x0f\x1b\x1d\n\x0b\n\x04\x05\0\x02\t\
\x12\x03\x10\x04\x16\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x10\x04\x10\n\
\x0c\n\x05\x05\0\x02\t\x02\x12\x03\x10\x13\x15\n\x0b\n\x04\x05\0\x02\n\
\x12\x03\x11\x04\x19\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\x11\x04\x13\n\
\x0c\n\x05\x05\0\x02\n\x02\x12\x03\x11\x16\x18\n\x0b\n\x04\x05\0\x02\x0b\
\x12\x03\x12\x04\x19\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x12\x04\x13\n\
\x0c\n\x05\x05\0\x02\x0b\x02\x12\x03\x12\x16\x18\n\x0b\n\x04\x05\0\x02\
\x0c\x12\x03\x13\x04\x1e\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\x13\x04\
\x18\n\x0c\n\x05\x05\0\x02\x0c\x02\x12\x03\x13\x1b\x1d\n\x0b\n\x04\x05\0\
\x02\r\x12\x03\x14\x04\x17\n\x0c\n\x05\x05\0\x02\r\x01\x12\x03\x14\x04\
\x11\n\x0c\n\x05\x05\0\x02\r\x02\x12\x03\x14\x14\x16b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -6,11 +6,11 @@ message UserError {
}
enum UserErrorCode {
Unknown = 0;
DatabaseInitFailed = 1;
DatabaseWriteLocked = 2;
DatabaseReadLocked = 3;
DatabaseUserDidNotMatch = 4;
DatabaseInternalError = 5;
UserDatabaseInitFailed = 1;
UserDatabaseWriteLocked = 2;
UserDatabaseReadLocked = 3;
UserDatabaseDidNotMatch = 4;
UserDatabaseInternalError = 5;
UserNotLoginYet = 10;
ReadCurrentIdFailed = 11;
WriteCurrentIdFailed = 12;

View File

@ -22,20 +22,21 @@ impl UserDB {
fn open_user_db(&self, user_id: &str) -> Result<(), UserError> {
if user_id.is_empty() {
return Err(ErrorBuilder::new(UserErrorCode::DatabaseInitFailed)
return Err(ErrorBuilder::new(UserErrorCode::UserDatabaseInitFailed)
.msg("user id is empty")
.build());
}
let dir = format!("{}/{}", self.db_dir, user_id);
let db = flowy_database::init(&dir).map_err(|e| {
ErrorBuilder::new(UserErrorCode::DatabaseInitFailed)
log::error!("flowy_database::init failed, {:?}", e);
ErrorBuilder::new(UserErrorCode::UserDatabaseInitFailed)
.error(e)
.build()
})?;
let mut db_map = DB_MAP.write().map_err(|e| {
ErrorBuilder::new(UserErrorCode::DatabaseWriteLocked)
ErrorBuilder::new(UserErrorCode::UserDatabaseWriteLocked)
.error(e)
.build()
})?;
@ -46,7 +47,7 @@ impl UserDB {
pub(crate) fn close_user_db(&self, user_id: &str) -> Result<(), UserError> {
let mut db_map = DB_MAP.write().map_err(|e| {
ErrorBuilder::new(UserErrorCode::DatabaseWriteLocked)
ErrorBuilder::new(UserErrorCode::UserDatabaseWriteLocked)
.msg(format!("Close user db failed. {:?}", e))
.build()
})?;
@ -62,13 +63,13 @@ impl UserDB {
}
let db_map = DB_MAP.read().map_err(|e| {
ErrorBuilder::new(UserErrorCode::DatabaseReadLocked)
ErrorBuilder::new(UserErrorCode::UserDatabaseReadLocked)
.error(e)
.build()
})?;
match db_map.get(user_id) {
None => Err(ErrorBuilder::new(UserErrorCode::DatabaseInitFailed)
None => Err(ErrorBuilder::new(UserErrorCode::UserDatabaseInitFailed)
.msg("Get connection failed. The database is not initialization")
.build()),
Some(database) => Ok(database.get_connection()?),

View File

@ -104,11 +104,9 @@ impl UserSession {
pub fn user_detail(&self) -> Result<UserDetail, UserError> {
let user_id = self.get_user_id()?;
let conn = self.get_db_connection()?;
let user = dsl::user_table
.filter(user_table::id.eq(&user_id))
.first::<User>(&*conn)?;
.first::<User>(&*(self.get_db_connection()?))?;
match self.server.get_user_info(&user_id) {
Ok(_user_detail) => {
@ -137,14 +135,15 @@ impl UserSession {
}
pub fn get_user_id(&self) -> Result<String, UserError> {
let read_guard = self.user_id.read().map_err(|e| {
ErrorBuilder::new(UserErrorCode::ReadCurrentIdFailed)
.error(e)
.build()
})?;
let mut user_id = {
let read_guard = self.user_id.read().map_err(|e| {
ErrorBuilder::new(UserErrorCode::ReadCurrentIdFailed)
.error(e)
.build()
})?;
let mut user_id = (*read_guard).clone();
drop(read_guard);
(*read_guard).clone()
};
if user_id.is_none() {
user_id = KVStore::get_str(USER_ID_CACHE_KEY);
@ -165,11 +164,10 @@ impl UserSession {
.unwrap();
let request = ModuleRequest::new(UpdateUser).payload(payload);
let _user_detail = EventDispatch::async_send(request)
let _ = EventDispatch::async_send(request)
.await
.parse::<UserDetail, UserError>()
.unwrap()
.unwrap();
.unwrap()?;
Ok(())
}
}

View File

@ -60,233 +60,3 @@ fn sign_in_with_invalid_password() {
);
}
}
#[test]
#[serial]
fn sign_up_success() {
let _ = UserTestBuilder::new().event(SignOut).sync_send();
let request = SignUpRequest {
email: random_valid_email(),
name: valid_name(),
password: valid_password(),
};
let _response = UserTestBuilder::new()
.logout()
.event(SignUp)
.request(request)
.sync_send();
}
#[test]
#[serial]
fn sign_up_with_invalid_email() {
for email in invalid_email_test_case() {
let request = SignUpRequest {
email: email.to_string(),
name: valid_name(),
password: valid_password(),
};
assert_eq!(
UserTestBuilder::new()
.event(SignUp)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::EmailInvalid
);
}
}
#[test]
#[serial]
fn sign_up_with_invalid_password() {
for password in invalid_password_test_case() {
let request = SignUpRequest {
email: random_valid_email(),
name: valid_name(),
password,
};
assert_eq!(
UserTestBuilder::new()
.event(SignUp)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::PasswordInvalid
);
}
}
#[test]
#[should_panic]
#[serial]
fn user_status_get_failed_before_login() {
let _ = UserTestBuilder::new()
.logout()
.event(GetStatus)
.sync_send()
.parse::<UserDetail>();
}
#[test]
#[serial]
fn user_status_get_success_after_login() {
let request = SignInRequest {
email: random_valid_email(),
password: valid_password(),
};
let response = UserTestBuilder::new()
.logout()
.event(SignIn)
.request(request)
.sync_send()
.parse::<UserDetail>();
dbg!(&response);
let _ = UserTestBuilder::new()
.event(GetStatus)
.sync_send()
.parse::<UserDetail>();
}
#[test]
#[serial]
fn user_update_with_name() {
let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
let new_name = "hello_world".to_owned();
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: Some(new_name.clone()),
email: None,
workspace: None,
password: None,
};
let user_detail = UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.parse::<UserDetail>();
assert_eq!(user_detail.name, new_name,);
}
#[test]
#[serial]
fn user_update_with_email() {
let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
let new_email = "123@gmai.com".to_owned();
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: None,
email: Some(new_email.clone()),
workspace: None,
password: None,
};
let user_detail = UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.parse::<UserDetail>();
assert_eq!(user_detail.email, new_email,);
}
#[test]
#[serial]
fn user_update_with_password() {
let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
let new_password = "H123world!".to_owned();
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: None,
email: None,
workspace: None,
password: Some(new_password.clone()),
};
let _ = UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.assert_success();
}
#[test]
#[serial]
fn user_update_with_invalid_email() {
let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
for email in invalid_email_test_case() {
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: None,
email: Some(email),
workspace: None,
password: None,
};
assert_eq!(
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::EmailInvalid
);
}
}
#[test]
#[serial]
fn user_update_with_invalid_password() {
let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
for password in invalid_password_test_case() {
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: None,
email: None,
workspace: None,
password: Some(password),
};
assert_eq!(
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::PasswordInvalid
);
}
}
#[test]
#[serial]
fn user_update_with_invalid_name() {
let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: Some("".to_string()),
email: None,
workspace: None,
password: None,
};
assert_eq!(
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::UserNameInvalid
);
}

View File

@ -1,65 +1,65 @@
// use crate::helper::*;
// use flowy_user::{errors::*, event::UserEvent::*, prelude::*};
// use serial_test::*;
//
// #[test]
// #[serial]
// fn sign_up_success() {
// let _ = UserTestBuilder::new().event(SignOut).sync_send();
// let request = SignUpRequest {
// email: random_valid_email(),
// name: valid_name(),
// password: valid_password(),
// };
//
// let _response = UserTestBuilder::new()
// .logout()
// .event(SignUp)
// .request(request)
// .sync_send();
// // .parse::<SignUpResponse>();
// // dbg!(&response);
// }
//
// #[test]
// #[serial]
// fn sign_up_with_invalid_email() {
// for email in invalid_email_test_case() {
// let request = SignUpRequest {
// email: email.to_string(),
// name: valid_name(),
// password: valid_password(),
// };
//
// assert_eq!(
// UserTestBuilder::new()
// .event(SignUp)
// .request(request)
// .sync_send()
// .error()
// .code,
// UserErrorCode::EmailInvalid
// );
// }
// }
// #[test]
// #[serial]
// fn sign_up_with_invalid_password() {
// for password in invalid_password_test_case() {
// let request = SignUpRequest {
// email: random_valid_email(),
// name: valid_name(),
// password,
// };
//
// assert_eq!(
// UserTestBuilder::new()
// .event(SignUp)
// .request(request)
// .sync_send()
// .error()
// .code,
// UserErrorCode::PasswordInvalid
// );
// }
// }
use crate::helper::*;
use flowy_user::{errors::*, event::UserEvent::*, prelude::*};
use serial_test::*;
#[test]
#[serial]
fn sign_up_success() {
let _ = UserTestBuilder::new().event(SignOut).sync_send();
let request = SignUpRequest {
email: random_valid_email(),
name: valid_name(),
password: valid_password(),
};
let _response = UserTestBuilder::new()
.logout()
.event(SignUp)
.request(request)
.sync_send();
// .parse::<SignUpResponse>();
// dbg!(&response);
}
#[test]
#[serial]
fn sign_up_with_invalid_email() {
for email in invalid_email_test_case() {
let request = SignUpRequest {
email: email.to_string(),
name: valid_name(),
password: valid_password(),
};
assert_eq!(
UserTestBuilder::new()
.event(SignUp)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::EmailInvalid
);
}
}
#[test]
#[serial]
fn sign_up_with_invalid_password() {
for password in invalid_password_test_case() {
let request = SignUpRequest {
email: random_valid_email(),
name: valid_name(),
password,
};
assert_eq!(
UserTestBuilder::new()
.event(SignUp)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::PasswordInvalid
);
}
}

View File

@ -1,36 +1,36 @@
// use crate::helper::*;
// use flowy_user::{event::UserEvent::*, prelude::*};
// use serial_test::*;
//
// #[test]
// #[should_panic]
// #[serial]
// fn user_status_get_failed_before_login() {
// let _ = UserTestBuilder::new()
// .logout()
// .event(GetStatus)
// .sync_send()
// .parse::<UserDetail>();
// }
//
// #[test]
// #[serial]
// fn user_status_get_success_after_login() {
// let request = SignInRequest {
// email: random_valid_email(),
// password: valid_password(),
// };
//
// let response = UserTestBuilder::new()
// .logout()
// .event(SignIn)
// .request(request)
// .sync_send()
// .parse::<UserDetail>();
// dbg!(&response);
//
// let _ = UserTestBuilder::new()
// .event(GetStatus)
// .sync_send()
// .parse::<UserDetail>();
// }
use crate::helper::*;
use flowy_user::{event::UserEvent::*, prelude::*};
use serial_test::*;
#[test]
#[should_panic]
#[serial]
fn user_status_get_failed_before_login() {
let _ = UserTestBuilder::new()
.logout()
.event(GetStatus)
.sync_send()
.parse::<UserDetail>();
}
#[test]
#[serial]
fn user_status_get_success_after_login() {
let request = SignInRequest {
email: random_valid_email(),
password: valid_password(),
};
let response = UserTestBuilder::new()
.logout()
.event(SignIn)
.request(request)
.sync_send()
.parse::<UserDetail>();
dbg!(&response);
let _ = UserTestBuilder::new()
.event(GetStatus)
.sync_send()
.parse::<UserDetail>();
}

View File

@ -1,140 +1,140 @@
// use crate::helper::*;
// use flowy_user::{errors::UserErrorCode, event::UserEvent::*, prelude::*};
// use serial_test::*;
//
// #[test]
// #[serial]
// fn user_update_with_name() {
// let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
// let new_name = "hello_world".to_owned();
// let request = UpdateUserRequest {
// id: user_detail.id.clone(),
// name: Some(new_name.clone()),
// email: None,
// workspace: None,
// password: None,
// };
//
// let user_detail = UserTestBuilder::new()
// .event(UpdateUser)
// .request(request)
// .sync_send()
// .parse::<UserDetail>();
//
// assert_eq!(user_detail.name, new_name,);
// }
//
// #[test]
// #[serial]
// fn user_update_with_email() {
// let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
// let new_email = "123@gmai.com".to_owned();
// let request = UpdateUserRequest {
// id: user_detail.id.clone(),
// name: None,
// email: Some(new_email.clone()),
// workspace: None,
// password: None,
// };
//
// let user_detail = UserTestBuilder::new()
// .event(UpdateUser)
// .request(request)
// .sync_send()
// .parse::<UserDetail>();
//
// assert_eq!(user_detail.email, new_email,);
// }
//
// #[test]
// #[serial]
// fn user_update_with_password() {
// let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
// let new_password = "H123world!".to_owned();
// let request = UpdateUserRequest {
// id: user_detail.id.clone(),
// name: None,
// email: None,
// workspace: None,
// password: Some(new_password.clone()),
// };
//
// let _ = UserTestBuilder::new()
// .event(UpdateUser)
// .request(request)
// .sync_send()
// .assert_success();
// }
//
// #[test]
// #[serial]
// fn user_update_with_invalid_email() {
// let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
// for email in invalid_email_test_case() {
// let request = UpdateUserRequest {
// id: user_detail.id.clone(),
// name: None,
// email: Some(email),
// workspace: None,
// password: None,
// };
//
// assert_eq!(
// UserTestBuilder::new()
// .event(UpdateUser)
// .request(request)
// .sync_send()
// .error()
// .code,
// UserErrorCode::EmailInvalid
// );
// }
// }
//
// #[test]
// #[serial]
// fn user_update_with_invalid_password() {
// let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
// for password in invalid_password_test_case() {
// let request = UpdateUserRequest {
// id: user_detail.id.clone(),
// name: None,
// email: None,
// workspace: None,
// password: Some(password),
// };
//
// assert_eq!(
// UserTestBuilder::new()
// .event(UpdateUser)
// .request(request)
// .sync_send()
// .error()
// .code,
// UserErrorCode::PasswordInvalid
// );
// }
// }
//
// #[test]
// #[serial]
// fn user_update_with_invalid_name() {
// let user_detail = UserTestBuilder::new().login().user_detail.unwrap();
// let request = UpdateUserRequest {
// id: user_detail.id.clone(),
// name: Some("".to_string()),
// email: None,
// workspace: None,
// password: None,
// };
//
// assert_eq!(
// UserTestBuilder::new()
// .event(UpdateUser)
// .request(request)
// .sync_send()
// .error()
// .code,
// UserErrorCode::UserNameInvalid
// );
// }
use crate::helper::*;
use flowy_user::{errors::UserErrorCode, event::UserEvent::*, prelude::*};
use serial_test::*;
#[test]
#[serial]
fn user_update_with_name() {
let user_detail = UserTestBuilder::new().reset().user_detail.unwrap();
let new_name = "hello_world".to_owned();
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: Some(new_name.clone()),
email: None,
workspace: None,
password: None,
};
let user_detail = UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.parse::<UserDetail>();
assert_eq!(user_detail.name, new_name,);
}
#[test]
#[serial]
fn user_update_with_email() {
let user_detail = UserTestBuilder::new().reset().user_detail.unwrap();
let new_email = "123@gmai.com".to_owned();
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: None,
email: Some(new_email.clone()),
workspace: None,
password: None,
};
let user_detail = UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.parse::<UserDetail>();
assert_eq!(user_detail.email, new_email,);
}
#[test]
#[serial]
fn user_update_with_password() {
let user_detail = UserTestBuilder::new().reset().user_detail.unwrap();
let new_password = "H123world!".to_owned();
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: None,
email: None,
workspace: None,
password: Some(new_password.clone()),
};
let _ = UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.assert_success();
}
#[test]
#[serial]
fn user_update_with_invalid_email() {
let user_detail = UserTestBuilder::new().reset().user_detail.unwrap();
for email in invalid_email_test_case() {
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: None,
email: Some(email),
workspace: None,
password: None,
};
assert_eq!(
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::EmailInvalid
);
}
}
#[test]
#[serial]
fn user_update_with_invalid_password() {
let user_detail = UserTestBuilder::new().reset().user_detail.unwrap();
for password in invalid_password_test_case() {
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: None,
email: None,
workspace: None,
password: Some(password),
};
assert_eq!(
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::PasswordInvalid
);
}
}
#[test]
#[serial]
fn user_update_with_invalid_name() {
let user_detail = UserTestBuilder::new().reset().user_detail.unwrap();
let request = UpdateUserRequest {
id: user_detail.id.clone(),
name: Some("".to_string()),
email: None,
workspace: None,
password: None,
};
assert_eq!(
UserTestBuilder::new()
.event(UpdateUser)
.request(request)
.sync_send()
.error()
.code,
UserErrorCode::UserNameInvalid
);
}

View File

@ -1,6 +1,20 @@
pub struct WorkspaceUserQueryRequest {
fetch_owner: bool,
fetch_all: bool,
use crate::entities::workspace::WorkspaceDetail;
use flowy_derive::ProtoBuf;
#[derive(ProtoBuf, Default, Debug)]
pub struct UserWorkspace {
#[pb(index = 1)]
pub owner: String,
#[pb(index = 2)]
pub workspace_id: String,
}
pub struct WorkspaceUserDetail {}
#[derive(ProtoBuf, Default, Debug)]
pub struct UserWorkspaceDetail {
#[pb(index = 1)]
pub owner: String,
#[pb(index = 2)]
pub workspace: WorkspaceDetail,
}

View File

@ -42,7 +42,7 @@ pub enum WorkspaceErrorCode {
DatabaseConnectionFail = 5,
#[display(fmt = "Database internal error")]
DatabaseInternalError = 6,
WorkspaceDatabaseError = 6,
#[display(fmt = "User internal error")]
UserInternalError = 10,
@ -57,15 +57,7 @@ impl std::default::Default for WorkspaceErrorCode {
impl std::convert::From<flowy_database::result::Error> for WorkspaceError {
fn from(error: flowy_database::result::Error) -> Self {
ErrorBuilder::new(WorkspaceErrorCode::DatabaseInternalError)
.error(error)
.build()
}
}
impl std::convert::From<flowy_sqlite::Error> for WorkspaceError {
fn from(error: flowy_sqlite::Error) -> Self {
ErrorBuilder::new(WorkspaceErrorCode::DatabaseInternalError)
ErrorBuilder::new(WorkspaceErrorCode::WorkspaceDatabaseError)
.error(error)
.build()
}

View File

@ -6,9 +6,9 @@ use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
pub enum WorkspaceEvent {
#[display(fmt = "Create workspace")]
#[event(input = "CreateSpaceRequest", output = "WorkspaceDetail")]
CreateWorkspace = 0,
CreateWorkspace = 0,
#[display(fmt = "Get workspace user")]
#[event(output = "UserDetail")]
GetWorkspaceUserDetail = 100,
#[display(fmt = "Get user's workspace detail")]
#[event(output = "UserWorkspaceDetail")]
GetWorkspaceDetail = 1,
}

View File

@ -1,5 +1,11 @@
use crate::{
entities::workspace::{CreateWorkspaceParams, CreateWorkspaceRequest, WorkspaceDetail},
entities::workspace::{
CreateWorkspaceParams,
CreateWorkspaceRequest,
UserWorkspace,
UserWorkspaceDetail,
WorkspaceDetail,
},
errors::WorkspaceError,
services::WorkspaceController,
};
@ -16,12 +22,9 @@ pub async fn create_workspace(
response_ok(detail)
}
pub async fn workspace_user(
data: Data<CreateWorkspaceRequest>,
pub async fn get_workspace_detail(
controller: ModuleData<Arc<WorkspaceController>>,
) -> ResponseResult<WorkspaceDetail, WorkspaceError> {
let controller = controller.get_ref().clone();
let params: CreateWorkspaceParams = data.into_inner().try_into()?;
let detail = controller.save_workspace(params).await?;
response_ok(detail)
) -> ResponseResult<UserWorkspaceDetail, WorkspaceError> {
let user_workspace = controller.get_user_workspace_detail().await?;
response_ok(user_workspace)
}

View File

@ -7,12 +7,12 @@ use crate::{
};
use flowy_database::DBConnection;
use crate::handlers::*;
use crate::{entities::workspace::UserWorkspace, handlers::*};
use std::sync::Arc;
pub trait WorkspaceUser: Send + Sync {
fn set_workspace(&self, id: &str) -> DispatchFuture<Result<(), WorkspaceError>>;
fn get_workspace(&self) -> Result<String, WorkspaceError>;
fn set_cur_workspace_id(&self, id: &str) -> DispatchFuture<Result<(), WorkspaceError>>;
fn get_cur_workspace(&self) -> DispatchFuture<Result<UserWorkspace, WorkspaceError>>;
fn db_connection(&self) -> Result<DBConnection, WorkspaceError>;
}
@ -25,5 +25,5 @@ pub fn create(user: Arc<dyn WorkspaceUser>) -> Module {
.data(workspace_controller)
.data(app_controller)
.event(WorkspaceEvent::CreateWorkspace, create_workspace)
.event(WorkspaceEvent::GetWorkspaceUserDetail, workspace_user)
.event(WorkspaceEvent::GetWorkspaceDetail, get_workspace_detail)
}

View File

@ -221,7 +221,7 @@ pub enum WorkspaceErrorCode {
AppColorStyleInvalid = 3,
AppIdInvalid = 4,
DatabaseConnectionFail = 5,
DatabaseInternalError = 6,
WorkspaceDatabaseError = 6,
UserInternalError = 10,
UserNotLoginYet = 11,
}
@ -239,7 +239,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
3 => ::std::option::Option::Some(WorkspaceErrorCode::AppColorStyleInvalid),
4 => ::std::option::Option::Some(WorkspaceErrorCode::AppIdInvalid),
5 => ::std::option::Option::Some(WorkspaceErrorCode::DatabaseConnectionFail),
6 => ::std::option::Option::Some(WorkspaceErrorCode::DatabaseInternalError),
6 => ::std::option::Option::Some(WorkspaceErrorCode::WorkspaceDatabaseError),
10 => ::std::option::Option::Some(WorkspaceErrorCode::UserInternalError),
11 => ::std::option::Option::Some(WorkspaceErrorCode::UserNotLoginYet),
_ => ::std::option::Option::None
@ -254,7 +254,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceErrorCode {
WorkspaceErrorCode::AppColorStyleInvalid,
WorkspaceErrorCode::AppIdInvalid,
WorkspaceErrorCode::DatabaseConnectionFail,
WorkspaceErrorCode::DatabaseInternalError,
WorkspaceErrorCode::WorkspaceDatabaseError,
WorkspaceErrorCode::UserInternalError,
WorkspaceErrorCode::UserNotLoginYet,
];
@ -287,11 +287,11 @@ 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*\xe2\x01\n\x12WorkspaceErrorCode\x12\x0b\n\x07Unknown\x10\0\
\tR\x03msg*\xe3\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\x10\n\x0cAppIdI\
nvalid\x10\x04\x12\x1a\n\x16DatabaseConnectionFail\x10\x05\x12\x19\n\x15\
DatabaseInternalError\x10\x06\x12\x15\n\x11UserInternalError\x10\n\x12\
nvalid\x10\x04\x12\x1a\n\x16DatabaseConnectionFail\x10\x05\x12\x1a\n\x16\
WorkspaceDatabaseError\x10\x06\x12\x15\n\x11UserInternalError\x10\n\x12\
\x13\n\x0fUserNotLoginYet\x10\x0bJ\xa1\x04\n\x06\x12\x04\0\0\x10\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\
@ -313,8 +313,8 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\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\x1d\n\x0b\
\x0b\n\x04\x05\0\x02\x06\x12\x03\r\x04\x1f\n\x0c\n\x05\x05\0\x02\x06\x01\
\x12\x03\r\x04\x1a\n\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\r\x1d\x1e\n\x0b\
\n\x04\x05\0\x02\x07\x12\x03\x0e\x04\x1b\n\x0c\n\x05\x05\0\x02\x07\x01\
\x12\x03\x0e\x04\x15\n\x0c\n\x05\x05\0\x02\x07\x02\x12\x03\x0e\x18\x1a\n\
\x0b\n\x04\x05\0\x02\x08\x12\x03\x0f\x04\x19\n\x0c\n\x05\x05\0\x02\x08\

View File

@ -26,7 +26,7 @@
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum WorkspaceEvent {
CreateWorkspace = 0,
GetWorkspaceUserDetail = 100,
GetWorkspaceDetail = 1,
}
impl ::protobuf::ProtobufEnum for WorkspaceEvent {
@ -37,7 +37,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
fn from_i32(value: i32) -> ::std::option::Option<WorkspaceEvent> {
match value {
0 => ::std::option::Option::Some(WorkspaceEvent::CreateWorkspace),
100 => ::std::option::Option::Some(WorkspaceEvent::GetWorkspaceUserDetail),
1 => ::std::option::Option::Some(WorkspaceEvent::GetWorkspaceDetail),
_ => ::std::option::Option::None
}
}
@ -45,7 +45,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
fn values() -> &'static [Self] {
static values: &'static [WorkspaceEvent] = &[
WorkspaceEvent::CreateWorkspace,
WorkspaceEvent::GetWorkspaceUserDetail,
WorkspaceEvent::GetWorkspaceDetail,
];
values
}
@ -74,14 +74,14 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0bevent.proto*A\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorkspace\x10\
\0\x12\x1a\n\x16GetWorkspaceUserDetail\x10dJ|\n\x06\x12\x04\0\0\x05\x01\
\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x05\x01\n\
\n\n\x03\x05\0\x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\
\x03\x04\x18\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\
\x05\0\x02\0\x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\
\x04\x04!\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x1a\n\x0c\n\x05\
\x05\0\x02\x01\x02\x12\x03\x04\x1d\x20b\x06proto3\
\n\x0bevent.proto*=\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorkspace\x10\
\0\x12\x16\n\x12GetWorkspaceDetail\x10\x01J|\n\x06\x12\x04\0\0\x05\x01\n\
\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x05\x01\n\n\
\n\x03\x05\0\x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\
\x04\x18\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\x05\
\0\x02\0\x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\
\x04\x1b\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x16\n\x0c\n\x05\
\x05\0\x02\x01\x02\x12\x03\x04\x19\x1ab\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -12,6 +12,9 @@ pub use app_create::*;
mod event;
pub use event::*;
mod workspace_user_detail;
pub use workspace_user_detail::*;
mod workspace_create;
pub use workspace_create::*;

View File

@ -0,0 +1,476 @@
// 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 `workspace_user_detail.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 UserWorkspace {
// message fields
pub owner: ::std::string::String,
pub workspace_id: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a UserWorkspace {
fn default() -> &'a UserWorkspace {
<UserWorkspace as ::protobuf::Message>::default_instance()
}
}
impl UserWorkspace {
pub fn new() -> UserWorkspace {
::std::default::Default::default()
}
// string owner = 1;
pub fn get_owner(&self) -> &str {
&self.owner
}
pub fn clear_owner(&mut self) {
self.owner.clear();
}
// Param is passed by value, moved
pub fn set_owner(&mut self, v: ::std::string::String) {
self.owner = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_owner(&mut self) -> &mut ::std::string::String {
&mut self.owner
}
// Take field
pub fn take_owner(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.owner, ::std::string::String::new())
}
// string workspace_id = 2;
pub fn get_workspace_id(&self) -> &str {
&self.workspace_id
}
pub fn clear_workspace_id(&mut self) {
self.workspace_id.clear();
}
// Param is passed by value, moved
pub fn set_workspace_id(&mut self, v: ::std::string::String) {
self.workspace_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_workspace_id(&mut self) -> &mut ::std::string::String {
&mut self.workspace_id
}
// Take field
pub fn take_workspace_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.workspace_id, ::std::string::String::new())
}
}
impl ::protobuf::Message for UserWorkspace {
fn is_initialized(&self) -> bool {
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.owner)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.workspace_id)?;
},
_ => {
::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.owner.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.owner);
}
if !self.workspace_id.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.workspace_id);
}
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.owner.is_empty() {
os.write_string(1, &self.owner)?;
}
if !self.workspace_id.is_empty() {
os.write_string(2, &self.workspace_id)?;
}
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() -> UserWorkspace {
UserWorkspace::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>(
"owner",
|m: &UserWorkspace| { &m.owner },
|m: &mut UserWorkspace| { &mut m.owner },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"workspace_id",
|m: &UserWorkspace| { &m.workspace_id },
|m: &mut UserWorkspace| { &mut m.workspace_id },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<UserWorkspace>(
"UserWorkspace",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static UserWorkspace {
static instance: ::protobuf::rt::LazyV2<UserWorkspace> = ::protobuf::rt::LazyV2::INIT;
instance.get(UserWorkspace::new)
}
}
impl ::protobuf::Clear for UserWorkspace {
fn clear(&mut self) {
self.owner.clear();
self.workspace_id.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for UserWorkspace {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for UserWorkspace {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct UserWorkspaceDetail {
// message fields
pub owner: ::std::string::String,
pub workspace: ::protobuf::SingularPtrField<super::workspace_create::WorkspaceDetail>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a UserWorkspaceDetail {
fn default() -> &'a UserWorkspaceDetail {
<UserWorkspaceDetail as ::protobuf::Message>::default_instance()
}
}
impl UserWorkspaceDetail {
pub fn new() -> UserWorkspaceDetail {
::std::default::Default::default()
}
// string owner = 1;
pub fn get_owner(&self) -> &str {
&self.owner
}
pub fn clear_owner(&mut self) {
self.owner.clear();
}
// Param is passed by value, moved
pub fn set_owner(&mut self, v: ::std::string::String) {
self.owner = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_owner(&mut self) -> &mut ::std::string::String {
&mut self.owner
}
// Take field
pub fn take_owner(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.owner, ::std::string::String::new())
}
// .WorkspaceDetail workspace = 2;
pub fn get_workspace(&self) -> &super::workspace_create::WorkspaceDetail {
self.workspace.as_ref().unwrap_or_else(|| <super::workspace_create::WorkspaceDetail as ::protobuf::Message>::default_instance())
}
pub fn clear_workspace(&mut self) {
self.workspace.clear();
}
pub fn has_workspace(&self) -> bool {
self.workspace.is_some()
}
// Param is passed by value, moved
pub fn set_workspace(&mut self, v: super::workspace_create::WorkspaceDetail) {
self.workspace = ::protobuf::SingularPtrField::some(v);
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_workspace(&mut self) -> &mut super::workspace_create::WorkspaceDetail {
if self.workspace.is_none() {
self.workspace.set_default();
}
self.workspace.as_mut().unwrap()
}
// Take field
pub fn take_workspace(&mut self) -> super::workspace_create::WorkspaceDetail {
self.workspace.take().unwrap_or_else(|| super::workspace_create::WorkspaceDetail::new())
}
}
impl ::protobuf::Message for UserWorkspaceDetail {
fn is_initialized(&self) -> bool {
for v in &self.workspace {
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.owner)?;
},
2 => {
::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.workspace)?;
},
_ => {
::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.owner.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.owner);
}
if let Some(ref v) = self.workspace.as_ref() {
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.owner.is_empty() {
os.write_string(1, &self.owner)?;
}
if let Some(ref v) = self.workspace.as_ref() {
os.write_tag(2, ::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() -> UserWorkspaceDetail {
UserWorkspaceDetail::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>(
"owner",
|m: &UserWorkspaceDetail| { &m.owner },
|m: &mut UserWorkspaceDetail| { &mut m.owner },
));
fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<super::workspace_create::WorkspaceDetail>>(
"workspace",
|m: &UserWorkspaceDetail| { &m.workspace },
|m: &mut UserWorkspaceDetail| { &mut m.workspace },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<UserWorkspaceDetail>(
"UserWorkspaceDetail",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static UserWorkspaceDetail {
static instance: ::protobuf::rt::LazyV2<UserWorkspaceDetail> = ::protobuf::rt::LazyV2::INIT;
instance.get(UserWorkspaceDetail::new)
}
}
impl ::protobuf::Clear for UserWorkspaceDetail {
fn clear(&mut self) {
self.owner.clear();
self.workspace.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for UserWorkspaceDetail {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for UserWorkspaceDetail {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x1bworkspace_user_detail.proto\x1a\x16workspace_create.proto\"H\n\rUs\
erWorkspace\x12\x14\n\x05owner\x18\x01\x20\x01(\tR\x05owner\x12!\n\x0cwo\
rkspace_id\x18\x02\x20\x01(\tR\x0bworkspaceId\"[\n\x13UserWorkspaceDetai\
l\x12\x14\n\x05owner\x18\x01\x20\x01(\tR\x05owner\x12.\n\tworkspace\x18\
\x02\x20\x01(\x0b2\x10.WorkspaceDetailR\tworkspaceJ\xa9\x02\n\x06\x12\
\x04\0\0\n\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\t\n\x02\x03\0\x12\x03\
\x01\0\x20\n\n\n\x02\x04\0\x12\x04\x03\0\x06\x01\n\n\n\x03\x04\0\x01\x12\
\x03\x03\x08\x15\n\x0b\n\x04\x04\0\x02\0\x12\x03\x04\x04\x15\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\x10\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x04\x13\x14\n\x0b\n\x04\
\x04\0\x02\x01\x12\x03\x05\x04\x1c\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\
\x05\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x05\x0b\x17\n\x0c\n\x05\
\x04\0\x02\x01\x03\x12\x03\x05\x1a\x1b\n\n\n\x02\x04\x01\x12\x04\x07\0\n\
\x01\n\n\n\x03\x04\x01\x01\x12\x03\x07\x08\x1b\n\x0b\n\x04\x04\x01\x02\0\
\x12\x03\x08\x04\x15\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x08\x04\n\n\
\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x08\x0b\x10\n\x0c\n\x05\x04\x01\x02\
\0\x03\x12\x03\x08\x13\x14\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\t\x04\"\n\
\x0c\n\x05\x04\x01\x02\x01\x06\x12\x03\t\x04\x13\n\x0c\n\x05\x04\x01\x02\
\x01\x01\x12\x03\t\x14\x1d\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\t\x20\
!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()
})
}

View File

@ -11,7 +11,7 @@ enum WorkspaceErrorCode {
AppColorStyleInvalid = 3;
AppIdInvalid = 4;
DatabaseConnectionFail = 5;
DatabaseInternalError = 6;
WorkspaceDatabaseError = 6;
UserInternalError = 10;
UserNotLoginYet = 11;
}

View File

@ -2,5 +2,5 @@ syntax = "proto3";
enum WorkspaceEvent {
CreateWorkspace = 0;
GetWorkspaceUserDetail = 100;
GetWorkspaceDetail = 1;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
import "workspace_create.proto";
message UserWorkspace {
string owner = 1;
string workspace_id = 2;
}
message UserWorkspaceDetail {
string owner = 1;
WorkspaceDetail workspace = 2;
}

View File

@ -1,6 +1,8 @@
use crate::{entities::workspace::*, errors::*, module::WorkspaceUser, sql_tables::workspace::*};
use flowy_database::{prelude::*, schema::workspace_table};
use flowy_database::schema::workspace_table::dsl;
use flowy_dispatch::prelude::DispatchFuture;
use std::sync::Arc;
pub struct WorkspaceController {
@ -21,11 +23,29 @@ impl WorkspaceController {
.values(workspace)
.execute(&*(self.user.db_connection()?))?;
let _ = self.user.set_workspace(&detail.id).await?;
let _ = self.user.set_cur_workspace_id(&detail.id).await?;
Ok(detail)
}
pub fn get_workspace(
&self,
workspace_id: &str,
) -> DispatchFuture<Result<Workspace, WorkspaceError>> {
let user = self.user.clone();
let workspace_id = workspace_id.to_owned();
DispatchFuture {
fut: Box::pin(async move {
let workspace = dsl::workspace_table
.filter(workspace_table::id.eq(&workspace_id))
.first::<Workspace>(&*(user.db_connection()?))?;
// TODO: fetch workspace from remote server
Ok(workspace)
}),
}
}
pub fn update_workspace(&self, params: UpdateWorkspaceParams) -> Result<(), WorkspaceError> {
let changeset = WorkspaceChangeset::new(params);
let conn = self.user.db_connection()?;
@ -33,4 +53,14 @@ impl WorkspaceController {
Ok(())
}
pub async fn get_user_workspace_detail(&self) -> Result<UserWorkspaceDetail, WorkspaceError> {
let user_workspace = self.user.get_cur_workspace().await?;
let workspace = self.get_workspace(&user_workspace.workspace_id).await?;
Ok(UserWorkspaceDetail {
owner: user_workspace.owner,
workspace: workspace.into(),
})
}
}

View File

@ -1,6 +1,6 @@
use crate::helper::*;
use flowy_workspace::{
entities::workspace::{CreateWorkspaceRequest, WorkspaceDetail},
entities::workspace::{CreateWorkspaceRequest, UserWorkspaceDetail, WorkspaceDetail},
event::WorkspaceEvent::*,
prelude::*,
};
@ -20,6 +20,27 @@ fn workspace_create_success() {
dbg!(&response);
}
#[test]
fn workspace_get_detail_success() {
let request = CreateWorkspaceRequest {
name: "Team A".to_owned(),
desc: "Team A Description".to_owned(),
};
let workspace = WorkspaceTestBuilder::new()
.event(CreateWorkspace)
.request(request)
.sync_send()
.parse::<WorkspaceDetail>();
let user_workspace = WorkspaceTestBuilder::new()
.event(GetWorkspaceDetail)
.sync_send()
.parse::<UserWorkspaceDetail>();
assert_eq!(workspace.name, user_workspace.workspace.name);
}
#[test]
fn workspace_create_with_invalid_name_test() {
for name in invalid_workspace_name_test_case() {

View File

@ -22,4 +22,5 @@ phf = { version = "0.8.0", features = ["macros"] }
similar = "1.2.2"
dialoguer = "0.8.0"
toml = "0.5.8"
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
pathdiff = "0.2.0"

View File

@ -78,6 +78,7 @@ fn parse_files_protobuf(proto_crate_path: &str, proto_output_dir: &str) -> Vec<P
if !enums.is_empty() || !structs.is_empty() {
let info = ProtoFile {
file_path: path.clone(),
file_name: file_name.clone(),
structs: structs.iter().map(|s| s.name.clone()).collect(),
enums: enums.iter().map(|e| e.name.clone()).collect(),

View File

@ -88,6 +88,7 @@ impl ProtobufCrate {
#[derive(Debug)]
pub struct ProtoFile {
pub file_path: String,
pub file_name: String,
pub structs: Vec<String>,
pub enums: Vec<String>,
@ -121,6 +122,7 @@ impl FlutterProtobufInfo {
model_dir
}
#[allow(dead_code)]
pub fn mod_file_path(&self) -> String {
let mod_file_path = format!("{}/protobuf.dart", self.package_path);
mod_file_path

View File

@ -154,3 +154,14 @@ where
path_and_name(path, name);
}
}
#[allow(dead_code)]
pub fn suffix_relative_to_path(path: &str, base: &str) -> String {
let base = Path::new(base);
let path = Path::new(path);
path.strip_prefix(base)
.unwrap()
.to_str()
.unwrap()
.to_owned()
}