mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: integrate postgres storage (#2604)
* chore: env config * chore: get user workspace * feat: enable postgres storage * chore: add new env * chore: add set env ffi * chore: pass env before backend init * chore: update * fix: ci tests * chore: commit the generate env file * chore: remove unused import
This commit is contained in:
parent
51a7954af7
commit
056e2d49d0
@ -16,4 +16,23 @@ class Config {
|
|||||||
..jwtSecret = secret,
|
..jwtSecret = secret,
|
||||||
).send();
|
).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> setSupabaseCollabPluginConfig({
|
||||||
|
required String url,
|
||||||
|
required String key,
|
||||||
|
required String jwtSecret,
|
||||||
|
required String collabTable,
|
||||||
|
}) async {
|
||||||
|
final payload = CollabPluginConfigPB.create();
|
||||||
|
final collabTableConfig = CollabTableConfigPB.create()
|
||||||
|
..tableName = collabTable;
|
||||||
|
|
||||||
|
payload.supabaseConfig = SupabaseDBConfigPB.create()
|
||||||
|
..supabaseUrl = url
|
||||||
|
..key = key
|
||||||
|
..jwtSecret = jwtSecret
|
||||||
|
..collabTableConfig = collabTableConfig;
|
||||||
|
|
||||||
|
await ConfigEventSetCollabPluginConfig(payload).send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
7
frontend/appflowy_flutter/lib/env/env.dart
vendored
7
frontend/appflowy_flutter/lib/env/env.dart
vendored
@ -29,6 +29,13 @@ abstract class Env {
|
|||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
)
|
)
|
||||||
static final supabaseJwtSecret = _Env.supabaseJwtSecret;
|
static final supabaseJwtSecret = _Env.supabaseJwtSecret;
|
||||||
|
|
||||||
|
@EnviedField(
|
||||||
|
obfuscate: true,
|
||||||
|
varName: 'SUPABASE_COLLAB_TABLE',
|
||||||
|
defaultValue: '',
|
||||||
|
)
|
||||||
|
static final supabaseCollabTable = _Env.supabaseCollabTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isSupabaseEnable =>
|
bool get isSupabaseEnable =>
|
||||||
|
@ -6,7 +6,6 @@ import 'package:appflowy/util/json_print.dart';
|
|||||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||||
import 'package:appflowy/workspace/application/doc/doc_listener.dart';
|
import 'package:appflowy/workspace/application/doc/doc_listener.dart';
|
||||||
import 'package:appflowy/plugins/document/application/doc_service.dart';
|
import 'package:appflowy/plugins/document/application/doc_service.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-document2/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-document2/protobuf.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pbserver.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pbserver.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||||
@ -153,7 +152,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
editorState.logConfiguration
|
editorState.logConfiguration
|
||||||
..level = LogLevel.all
|
..level = LogLevel.all
|
||||||
..handler = (log) {
|
..handler = (log) {
|
||||||
Log.debug(log);
|
// Log.debug(log);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ class FlowyRunner {
|
|||||||
anonKey: Env.supabaseAnonKey,
|
anonKey: Env.supabaseAnonKey,
|
||||||
key: Env.supabaseKey,
|
key: Env.supabaseKey,
|
||||||
jwtSecret: Env.supabaseJwtSecret,
|
jwtSecret: Env.supabaseJwtSecret,
|
||||||
|
collabTable: Env.supabaseCollabTable,
|
||||||
),
|
),
|
||||||
const InitAppWidgetTask(),
|
const InitAppWidgetTask(),
|
||||||
const InitPlatformServiceTask()
|
const InitPlatformServiceTask()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:appflowy/env/env.dart';
|
||||||
import 'package:appflowy_backend/appflowy_backend.dart';
|
import 'package:appflowy_backend/appflowy_backend.dart';
|
||||||
|
import 'package:appflowy_backend/env_serde.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
@ -20,10 +22,35 @@ class InitRustSDKTask extends LaunchTask {
|
|||||||
@override
|
@override
|
||||||
Future<void> initialize(LaunchContext context) async {
|
Future<void> initialize(LaunchContext context) async {
|
||||||
final dir = directory ?? await appFlowyDocumentDirectory();
|
final dir = directory ?? await appFlowyDocumentDirectory();
|
||||||
|
|
||||||
|
context.getIt<FlowySDK>().setEnv(getAppFlowyEnv());
|
||||||
await context.getIt<FlowySDK>().init(dir);
|
await context.getIt<FlowySDK>().init(dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppFlowyEnv getAppFlowyEnv() {
|
||||||
|
final supabaseConfig = SupabaseConfiguration(
|
||||||
|
url: Env.supabaseUrl,
|
||||||
|
key: Env.supabaseKey,
|
||||||
|
jwt_secret: Env.supabaseJwtSecret,
|
||||||
|
);
|
||||||
|
|
||||||
|
final collabTableConfig =
|
||||||
|
CollabTableConfig(enable: true, table_name: Env.supabaseCollabTable);
|
||||||
|
|
||||||
|
final supbaseDBConfig = SupabaseDBConfig(
|
||||||
|
url: Env.supabaseUrl,
|
||||||
|
key: Env.supabaseKey,
|
||||||
|
jwt_secret: Env.supabaseJwtSecret,
|
||||||
|
collab_table_config: collabTableConfig,
|
||||||
|
);
|
||||||
|
|
||||||
|
return AppFlowyEnv(
|
||||||
|
supabase_config: supabaseConfig,
|
||||||
|
supabase_db_config: supbaseDBConfig,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Directory> appFlowyDocumentDirectory() async {
|
Future<Directory> appFlowyDocumentDirectory() async {
|
||||||
switch (integrationEnv()) {
|
switch (integrationEnv()) {
|
||||||
case IntegrationMode.develop:
|
case IntegrationMode.develop:
|
||||||
|
@ -13,12 +13,14 @@ class InitSupabaseTask extends LaunchTask {
|
|||||||
required this.anonKey,
|
required this.anonKey,
|
||||||
required this.key,
|
required this.key,
|
||||||
required this.jwtSecret,
|
required this.jwtSecret,
|
||||||
|
this.collabTable = "",
|
||||||
});
|
});
|
||||||
|
|
||||||
final String url;
|
final String url;
|
||||||
final String anonKey;
|
final String anonKey;
|
||||||
final String key;
|
final String key;
|
||||||
final String jwtSecret;
|
final String jwtSecret;
|
||||||
|
final String collabTable;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> initialize(LaunchContext context) async {
|
Future<void> initialize(LaunchContext context) async {
|
||||||
@ -33,6 +35,7 @@ class InitSupabaseTask extends LaunchTask {
|
|||||||
await Supabase.initialize(
|
await Supabase.initialize(
|
||||||
url: url,
|
url: url,
|
||||||
anonKey: anonKey,
|
anonKey: anonKey,
|
||||||
|
debug: false,
|
||||||
);
|
);
|
||||||
await Config.setSupabaseConfig(
|
await Config.setSupabaseConfig(
|
||||||
url: url,
|
url: url,
|
||||||
|
@ -14,3 +14,5 @@ int32_t set_stream_port(int64_t port);
|
|||||||
void link_me_please(void);
|
void link_me_please(void);
|
||||||
|
|
||||||
void backend_log(int64_t level, const char *data);
|
void backend_log(int64_t level, const char *data);
|
||||||
|
|
||||||
|
void set_env(const char *data);
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
export 'package:async/async.dart';
|
export 'package:async/async.dart';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:appflowy_backend/rust_stream.dart';
|
import 'package:appflowy_backend/rust_stream.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
|
import 'env_serde.dart';
|
||||||
import 'ffi.dart' as ffi;
|
import 'ffi.dart' as ffi;
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
@ -34,4 +36,9 @@ class FlowySDK {
|
|||||||
ffi.store_dart_post_cobject(NativeApi.postCObject);
|
ffi.store_dart_post_cobject(NativeApi.postCObject);
|
||||||
ffi.init_sdk(sdkDir.path.toNativeUtf8());
|
ffi.init_sdk(sdkDir.path.toNativeUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setEnv(AppFlowyEnv env) {
|
||||||
|
final jsonStr = jsonEncode(env.toJson());
|
||||||
|
ffi.set_env(jsonStr.toNativeUtf8());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'env_serde.l.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class AppFlowyEnv {
|
||||||
|
final SupabaseConfiguration supabase_config;
|
||||||
|
final SupabaseDBConfig supabase_db_config;
|
||||||
|
|
||||||
|
AppFlowyEnv(
|
||||||
|
{required this.supabase_config, required this.supabase_db_config});
|
||||||
|
|
||||||
|
factory AppFlowyEnv.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AppFlowyEnvFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$AppFlowyEnvToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class SupabaseConfiguration {
|
||||||
|
final String url;
|
||||||
|
final String key;
|
||||||
|
final String jwt_secret;
|
||||||
|
|
||||||
|
SupabaseConfiguration(
|
||||||
|
{required this.url, required this.key, required this.jwt_secret});
|
||||||
|
|
||||||
|
factory SupabaseConfiguration.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SupabaseConfigurationFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$SupabaseConfigurationToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class SupabaseDBConfig {
|
||||||
|
final String url;
|
||||||
|
final String key;
|
||||||
|
final String jwt_secret;
|
||||||
|
final CollabTableConfig collab_table_config;
|
||||||
|
|
||||||
|
SupabaseDBConfig(
|
||||||
|
{required this.url,
|
||||||
|
required this.key,
|
||||||
|
required this.jwt_secret,
|
||||||
|
required this.collab_table_config});
|
||||||
|
|
||||||
|
factory SupabaseDBConfig.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SupabaseDBConfigFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$SupabaseDBConfigToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class CollabTableConfig {
|
||||||
|
final String table_name;
|
||||||
|
final bool enable;
|
||||||
|
|
||||||
|
CollabTableConfig({required this.table_name, required this.enable});
|
||||||
|
|
||||||
|
factory CollabTableConfig.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$CollabTableConfigFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$CollabTableConfigToJson(this);
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'env_serde.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
AppFlowyEnv _$AppFlowyEnvFromJson(Map<String, dynamic> json) => AppFlowyEnv(
|
||||||
|
supabase_config: SupabaseConfiguration.fromJson(
|
||||||
|
json['supabase_config'] as Map<String, dynamic>),
|
||||||
|
supabase_db_config: SupabaseDBConfig.fromJson(
|
||||||
|
json['supabase_db_config'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$AppFlowyEnvToJson(AppFlowyEnv instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'supabase_config': instance.supabase_config,
|
||||||
|
'supabase_db_config': instance.supabase_db_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
SupabaseConfiguration _$SupabaseConfigurationFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
SupabaseConfiguration(
|
||||||
|
url: json['url'] as String,
|
||||||
|
key: json['key'] as String,
|
||||||
|
jwt_secret: json['jwt_secret'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SupabaseConfigurationToJson(
|
||||||
|
SupabaseConfiguration instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'url': instance.url,
|
||||||
|
'key': instance.key,
|
||||||
|
'jwt_secret': instance.jwt_secret,
|
||||||
|
};
|
||||||
|
|
||||||
|
SupabaseDBConfig _$SupabaseDBConfigFromJson(Map<String, dynamic> json) =>
|
||||||
|
SupabaseDBConfig(
|
||||||
|
url: json['url'] as String,
|
||||||
|
key: json['key'] as String,
|
||||||
|
jwt_secret: json['jwt_secret'] as String,
|
||||||
|
collab_table_config: CollabTableConfig.fromJson(
|
||||||
|
json['collab_table_config'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SupabaseDBConfigToJson(SupabaseDBConfig instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'url': instance.url,
|
||||||
|
'key': instance.key,
|
||||||
|
'jwt_secret': instance.jwt_secret,
|
||||||
|
'collab_table_config': instance.collab_table_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
CollabTableConfig _$CollabTableConfigFromJson(Map<String, dynamic> json) =>
|
||||||
|
CollabTableConfig(
|
||||||
|
table_name: json['table_name'] as String,
|
||||||
|
enable: json['enable'] as bool,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$CollabTableConfigToJson(CollabTableConfig instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'table_name': instance.table_name,
|
||||||
|
'enable': instance.enable,
|
||||||
|
};
|
@ -151,3 +151,19 @@ typedef _invoke_log_Dart = void Function(
|
|||||||
int level,
|
int level,
|
||||||
Pointer<ffi.Utf8>,
|
Pointer<ffi.Utf8>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// C function `set_env`.
|
||||||
|
void set_env(
|
||||||
|
Pointer<ffi.Utf8> data,
|
||||||
|
) {
|
||||||
|
_set_env(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
final _set_env_Dart _set_env =
|
||||||
|
_dart_ffi_lib.lookupFunction<_set_env_C, _set_env_Dart>('set_env');
|
||||||
|
typedef _set_env_C = Void Function(
|
||||||
|
Pointer<ffi.Utf8> data,
|
||||||
|
);
|
||||||
|
typedef _set_env_Dart = void Function(
|
||||||
|
Pointer<ffi.Utf8> data,
|
||||||
|
);
|
||||||
|
@ -15,3 +15,5 @@ int32_t set_stream_port(int64_t port);
|
|||||||
void link_me_please(void);
|
void link_me_please(void);
|
||||||
|
|
||||||
void backend_log(int64_t level, const char *data);
|
void backend_log(int64_t level, const char *data);
|
||||||
|
|
||||||
|
void set_env(const char *data);
|
||||||
|
@ -14,3 +14,5 @@ int32_t set_stream_port(int64_t port);
|
|||||||
void link_me_please(void);
|
void link_me_please(void);
|
||||||
|
|
||||||
void backend_log(int64_t level, const char *data);
|
void backend_log(int64_t level, const char *data);
|
||||||
|
|
||||||
|
void set_env(const char *data);
|
||||||
|
@ -18,6 +18,7 @@ dependencies:
|
|||||||
freezed_annotation:
|
freezed_annotation:
|
||||||
logger: ^1.0.0
|
logger: ^1.0.0
|
||||||
plugin_platform_interface: ^2.1.3
|
plugin_platform_interface: ^2.1.3
|
||||||
|
json_annotation: ^4.7.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -25,6 +26,7 @@ dev_dependencies:
|
|||||||
build_runner:
|
build_runner:
|
||||||
freezed:
|
freezed:
|
||||||
flutter_lints: ^2.0.1
|
flutter_lints: ^2.0.1
|
||||||
|
json_serializable: ^6.6.2
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
32
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
32
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -99,8 +99,9 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-integrate"
|
name = "appflowy-integrate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
"collab-database",
|
"collab-database",
|
||||||
"collab-document",
|
"collab-document",
|
||||||
@ -109,6 +110,7 @@ dependencies = [
|
|||||||
"collab-plugins",
|
"collab-plugins",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1021,7 +1023,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1038,7 +1040,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-client-ws"
|
name = "collab-client-ws"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab-sync",
|
"collab-sync",
|
||||||
@ -1056,7 +1058,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1075,12 +1077,13 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1092,7 +1095,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1109,7 +1112,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1127,7 +1130,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1147,21 +1150,25 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"aws-config",
|
"aws-config",
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
"aws-sdk-dynamodb",
|
"aws-sdk-dynamodb",
|
||||||
|
"base64 0.21.0",
|
||||||
"collab",
|
"collab",
|
||||||
"collab-client-ws",
|
"collab-client-ws",
|
||||||
"collab-persistence",
|
"collab-persistence",
|
||||||
"collab-sync",
|
"collab-sync",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
|
"postgrest",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rusoto_credential",
|
"rusoto_credential",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-retry",
|
"tokio-retry",
|
||||||
@ -1173,7 +1180,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-sync"
|
name = "collab-sync"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1773,6 +1780,7 @@ dependencies = [
|
|||||||
"flowy-codegen",
|
"flowy-codegen",
|
||||||
"flowy-derive",
|
"flowy-derive",
|
||||||
"flowy-error",
|
"flowy-error",
|
||||||
|
"flowy-server",
|
||||||
"flowy-sqlite",
|
"flowy-sqlite",
|
||||||
"lib-dispatch",
|
"lib-dispatch",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
@ -1803,6 +1811,7 @@ dependencies = [
|
|||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_repr",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@ -1972,9 +1981,10 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"chrono",
|
||||||
"config",
|
"config",
|
||||||
"flowy-config",
|
|
||||||
"flowy-error",
|
"flowy-error",
|
||||||
|
"flowy-folder2",
|
||||||
"flowy-user",
|
"flowy-user",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
@ -34,12 +34,12 @@ default = ["custom-protocol"]
|
|||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
|
|
||||||
#collab = { path = "../../AppFlowy-Collab/collab" }
|
#collab = { path = "../../AppFlowy-Collab/collab" }
|
||||||
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }
|
#collab-folder = { path = "../../AppFlowy-Collab/collab-folder" }
|
||||||
|
90
frontend/rust-lib/Cargo.lock
generated
90
frontend/rust-lib/Cargo.lock
generated
@ -85,8 +85,9 @@ checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-integrate"
|
name = "appflowy-integrate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
"collab-database",
|
"collab-database",
|
||||||
"collab-document",
|
"collab-document",
|
||||||
@ -95,6 +96,7 @@ dependencies = [
|
|||||||
"collab-plugins",
|
"collab-plugins",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -884,7 +886,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -901,7 +903,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-client-ws"
|
name = "collab-client-ws"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab-sync",
|
"collab-sync",
|
||||||
@ -919,7 +921,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -938,12 +940,13 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -955,7 +958,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -972,7 +975,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -990,7 +993,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1010,21 +1013,25 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"aws-config",
|
"aws-config",
|
||||||
"aws-credential-types",
|
"aws-credential-types",
|
||||||
"aws-sdk-dynamodb",
|
"aws-sdk-dynamodb",
|
||||||
|
"base64 0.21.0",
|
||||||
"collab",
|
"collab",
|
||||||
"collab-client-ws",
|
"collab-client-ws",
|
||||||
"collab-persistence",
|
"collab-persistence",
|
||||||
"collab-sync",
|
"collab-sync",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
|
"postgrest",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rusoto_credential",
|
"rusoto_credential",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-retry",
|
"tokio-retry",
|
||||||
@ -1036,7 +1043,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-sync"
|
name = "collab-sync"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=7a2e97#7a2e97d9bfe746f5db18753ab0b59347ec5bf23f"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bff164#bff1647076b2370c34f19d57d8d0da4bbb80ee55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1254,6 +1261,7 @@ name = "dart-ffi"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allo-isolate",
|
"allo-isolate",
|
||||||
|
"appflowy-integrate",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
@ -1463,11 +1471,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fake"
|
name = "fake"
|
||||||
version = "2.5.0"
|
version = "2.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d68f517805463f3a896a9d29c1d6ff09d3579ded64a7201b4069f8f9c0d52fd"
|
checksum = "0a44c765350db469b774425ff1c833890b16ceb9612fb5d7c4bbdf4a1b55f876"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"unidecode",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1551,6 +1560,7 @@ dependencies = [
|
|||||||
"flowy-codegen",
|
"flowy-codegen",
|
||||||
"flowy-derive",
|
"flowy-derive",
|
||||||
"flowy-error",
|
"flowy-error",
|
||||||
|
"flowy-server",
|
||||||
"flowy-sqlite",
|
"flowy-sqlite",
|
||||||
"lib-dispatch",
|
"lib-dispatch",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
@ -1582,6 +1592,7 @@ dependencies = [
|
|||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_repr",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@ -1757,10 +1768,11 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"chrono",
|
||||||
"config",
|
"config",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"flowy-config",
|
|
||||||
"flowy-error",
|
"flowy-error",
|
||||||
|
"flowy-folder2",
|
||||||
"flowy-user",
|
"flowy-user",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
@ -1820,28 +1832,26 @@ name = "flowy-test"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fake",
|
"dotenv",
|
||||||
"flowy-core",
|
"flowy-core",
|
||||||
"flowy-folder2",
|
"flowy-folder2",
|
||||||
"flowy-net",
|
"flowy-net",
|
||||||
"flowy-server",
|
"flowy-server",
|
||||||
"flowy-user",
|
"flowy-user",
|
||||||
"futures",
|
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"lib-dispatch",
|
"lib-dispatch",
|
||||||
"lib-infra",
|
"lib-infra",
|
||||||
"lib-ot",
|
"lib-ot",
|
||||||
"log",
|
|
||||||
"nanoid",
|
"nanoid",
|
||||||
|
"parking_lot 0.12.1",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"quickcheck",
|
|
||||||
"quickcheck_macros 0.9.1",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serial_test",
|
|
||||||
"tempdir",
|
"tempdir",
|
||||||
"thread-id",
|
"thread-id",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1859,7 +1869,6 @@ dependencies = [
|
|||||||
"flowy-error",
|
"flowy-error",
|
||||||
"flowy-notification",
|
"flowy-notification",
|
||||||
"flowy-sqlite",
|
"flowy-sqlite",
|
||||||
"flowy-test",
|
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lib-dispatch",
|
"lib-dispatch",
|
||||||
"lib-infra",
|
"lib-infra",
|
||||||
@ -1869,7 +1878,7 @@ dependencies = [
|
|||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros 1.0.0",
|
"quickcheck_macros",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3483,17 +3492,6 @@ dependencies = [
|
|||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quickcheck_macros"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quickcheck_macros"
|
name = "quickcheck_macros"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -4121,28 +4119,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serial_test"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"parking_lot 0.11.2",
|
|
||||||
"serial_test_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serial_test_derive"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha-1"
|
name = "sha-1"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
@ -4978,6 +4954,12 @@ version = "0.1.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unidecode"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -33,11 +33,11 @@ opt-level = 3
|
|||||||
incremental = false
|
incremental = false
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "7a2e97" }
|
appflowy-integrate = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bff164" }
|
||||||
|
|
||||||
#collab = { path = "../AppFlowy-Collab/collab" }
|
#collab = { path = "../AppFlowy-Collab/collab" }
|
||||||
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
|
#collab-folder = { path = "../AppFlowy-Collab/collab-folder" }
|
||||||
|
@ -24,7 +24,7 @@ crossbeam-utils = "0.8.15"
|
|||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
tracing = { version = "0.1", features = ["log"] }
|
tracing = { version = "0.1", features = ["log"] }
|
||||||
|
appflowy-integrate = {version = "0.1.0" }
|
||||||
|
|
||||||
lib-dispatch = { path = "../lib-dispatch" }
|
lib-dispatch = { path = "../lib-dispatch" }
|
||||||
flowy-core = { path = "../flowy-core" }
|
flowy-core = { path = "../flowy-core" }
|
||||||
|
@ -14,3 +14,5 @@ int32_t set_stream_port(int64_t port);
|
|||||||
void link_me_please(void);
|
void link_me_please(void);
|
||||||
|
|
||||||
void backend_log(int64_t level, const char *data);
|
void backend_log(int64_t level, const char *data);
|
||||||
|
|
||||||
|
void set_env(const char *data);
|
||||||
|
19
frontend/rust-lib/dart-ffi/src/env_serde.rs
Normal file
19
frontend/rust-lib/dart-ffi/src/env_serde.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use appflowy_integrate::SupabaseDBConfig;
|
||||||
|
use flowy_server::supabase::SupabaseConfiguration;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct AppFlowyEnv {
|
||||||
|
supabase_config: SupabaseConfiguration,
|
||||||
|
supabase_db_config: SupabaseDBConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppFlowyEnv {
|
||||||
|
pub fn parser(env_str: &str) {
|
||||||
|
if let Ok(env) = serde_json::from_str::<AppFlowyEnv>(env_str) {
|
||||||
|
dbg!(&env);
|
||||||
|
env.supabase_config.write_env();
|
||||||
|
env.supabase_db_config.write_env();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ use flowy_notification::register_notification_sender;
|
|||||||
use lib_dispatch::prelude::ToBytes;
|
use lib_dispatch::prelude::ToBytes;
|
||||||
use lib_dispatch::prelude::*;
|
use lib_dispatch::prelude::*;
|
||||||
|
|
||||||
|
use crate::env_serde::AppFlowyEnv;
|
||||||
use crate::notification::DartNotificationSender;
|
use crate::notification::DartNotificationSender;
|
||||||
use crate::{
|
use crate::{
|
||||||
c::{extend_front_four_bytes_into_bytes, forget_rust},
|
c::{extend_front_four_bytes_into_bytes, forget_rust},
|
||||||
@ -17,6 +18,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod c;
|
mod c;
|
||||||
|
mod env_serde;
|
||||||
mod model;
|
mod model;
|
||||||
mod notification;
|
mod notification;
|
||||||
mod protobuf;
|
mod protobuf;
|
||||||
@ -134,3 +136,10 @@ pub extern "C" fn backend_log(level: i64, data: *const c_char) {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn set_env(data: *const c_char) {
|
||||||
|
let c_str = unsafe { CStr::from_ptr(data) };
|
||||||
|
let serde_str = c_str.to_str().unwrap();
|
||||||
|
AppFlowyEnv::parser(serde_str);
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ bytes = { version = "1.4" }
|
|||||||
flowy-error = { path = "../flowy-error" }
|
flowy-error = { path = "../flowy-error" }
|
||||||
strum_macros = "0.21"
|
strum_macros = "0.21"
|
||||||
appflowy-integrate = {version = "0.1.0" }
|
appflowy-integrate = {version = "0.1.0" }
|
||||||
|
flowy-server = { path = "../flowy-server" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
flowy-codegen = { path = "../../../shared-lib/flowy-codegen"}
|
flowy-codegen = { path = "../../../shared-lib/flowy-codegen"}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
use appflowy_integrate::config::AWSDynamoDBConfig;
|
||||||
|
use appflowy_integrate::{CollabTableConfig, SupabaseDBConfig};
|
||||||
use flowy_derive::ProtoBuf;
|
use flowy_derive::ProtoBuf;
|
||||||
|
use flowy_error::FlowyError;
|
||||||
|
use flowy_server::supabase::SupabaseConfiguration;
|
||||||
|
|
||||||
#[derive(Default, ProtoBuf)]
|
#[derive(Default, ProtoBuf)]
|
||||||
pub struct KeyValuePB {
|
pub struct KeyValuePB {
|
||||||
@ -15,11 +19,6 @@ pub struct KeyPB {
|
|||||||
pub key: String,
|
pub key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const SUPABASE_URL: &str = "SUPABASE_URL";
|
|
||||||
pub const SUPABASE_ANON_KEY: &str = "SUPABASE_ANON_KEY";
|
|
||||||
pub const SUPABASE_KEY: &str = "SUPABASE_KEY";
|
|
||||||
pub const SUPABASE_JWT_SECRET: &str = "SUPABASE_JWT_SECRET";
|
|
||||||
|
|
||||||
#[derive(Default, ProtoBuf)]
|
#[derive(Default, ProtoBuf)]
|
||||||
pub struct SupabaseConfigPB {
|
pub struct SupabaseConfigPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
@ -35,28 +34,97 @@ pub struct SupabaseConfigPB {
|
|||||||
jwt_secret: String,
|
jwt_secret: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SupabaseConfigPB {
|
impl TryFrom<SupabaseConfigPB> for SupabaseConfiguration {
|
||||||
pub(crate) fn write_to_env(self) {
|
type Error = FlowyError;
|
||||||
std::env::set_var(SUPABASE_URL, self.supabase_url);
|
|
||||||
std::env::set_var(SUPABASE_ANON_KEY, self.anon_key);
|
fn try_from(value: SupabaseConfigPB) -> Result<Self, Self::Error> {
|
||||||
std::env::set_var(SUPABASE_KEY, self.key);
|
Ok(Self {
|
||||||
std::env::set_var(SUPABASE_JWT_SECRET, self.jwt_secret);
|
url: value.supabase_url,
|
||||||
|
key: value.key,
|
||||||
|
jwt_secret: value.jwt_secret,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, ProtoBuf)]
|
#[derive(Default, ProtoBuf)]
|
||||||
pub struct AppFlowyCollabConfigPB {
|
pub struct CollabPluginConfigPB {
|
||||||
#[pb(index = 1, one_of)]
|
#[pb(index = 1, one_of)]
|
||||||
aws_config: Option<AWSDynamoDBConfigPB>,
|
pub aws_config: Option<AWSDynamoDBConfigPB>,
|
||||||
|
|
||||||
|
#[pb(index = 2, one_of)]
|
||||||
|
pub supabase_config: Option<SupabaseDBConfigPB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, ProtoBuf)]
|
#[derive(Default, ProtoBuf)]
|
||||||
pub struct AWSDynamoDBConfigPB {
|
pub struct AWSDynamoDBConfigPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub access_key_id: String,
|
pub access_key_id: String,
|
||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub secret_access_key: String,
|
pub secret_access_key: String,
|
||||||
// Region list: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html
|
// Region list: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html
|
||||||
#[pb(index = 3)]
|
#[pb(index = 3)]
|
||||||
pub region: String,
|
pub region: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<AWSDynamoDBConfigPB> for AWSDynamoDBConfig {
|
||||||
|
type Error = FlowyError;
|
||||||
|
|
||||||
|
fn try_from(config: AWSDynamoDBConfigPB) -> Result<Self, Self::Error> {
|
||||||
|
Ok(AWSDynamoDBConfig {
|
||||||
|
access_key_id: config.access_key_id,
|
||||||
|
secret_access_key: config.secret_access_key,
|
||||||
|
region: config.region,
|
||||||
|
enable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, ProtoBuf)]
|
||||||
|
pub struct SupabaseDBConfigPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub supabase_url: String,
|
||||||
|
|
||||||
|
#[pb(index = 2)]
|
||||||
|
pub key: String,
|
||||||
|
|
||||||
|
#[pb(index = 3)]
|
||||||
|
pub jwt_secret: String,
|
||||||
|
|
||||||
|
#[pb(index = 4)]
|
||||||
|
pub collab_table_config: CollabTableConfigPB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SupabaseDBConfigPB> for SupabaseDBConfig {
|
||||||
|
type Error = FlowyError;
|
||||||
|
|
||||||
|
fn try_from(config: SupabaseDBConfigPB) -> Result<Self, Self::Error> {
|
||||||
|
let update_table_config = CollabTableConfig::try_from(config.collab_table_config)?;
|
||||||
|
Ok(SupabaseDBConfig {
|
||||||
|
url: config.supabase_url,
|
||||||
|
key: config.key,
|
||||||
|
jwt_secret: config.jwt_secret,
|
||||||
|
collab_table_config: update_table_config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, ProtoBuf)]
|
||||||
|
pub struct CollabTableConfigPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub table_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<CollabTableConfigPB> for CollabTableConfig {
|
||||||
|
type Error = FlowyError;
|
||||||
|
|
||||||
|
fn try_from(config: CollabTableConfigPB) -> Result<Self, Self::Error> {
|
||||||
|
if config.table_name.is_empty() {
|
||||||
|
return Err(FlowyError::internal().context("table_name is empty"));
|
||||||
|
}
|
||||||
|
Ok(CollabTableConfig {
|
||||||
|
table_name: config.table_name,
|
||||||
|
enable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
use appflowy_integrate::config::AWSDynamoDBConfig;
|
||||||
|
use appflowy_integrate::SupabaseDBConfig;
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
|
use flowy_server::supabase::SupabaseConfiguration;
|
||||||
use flowy_sqlite::kv::KV;
|
use flowy_sqlite::kv::KV;
|
||||||
use lib_dispatch::prelude::{data_result_ok, AFPluginData, DataResult};
|
use lib_dispatch::prelude::{data_result_ok, AFPluginData, DataResult};
|
||||||
|
|
||||||
use crate::entities::{KeyPB, KeyValuePB, SupabaseConfigPB};
|
use crate::entities::{CollabPluginConfigPB, KeyPB, KeyValuePB, SupabaseConfigPB};
|
||||||
|
|
||||||
pub(crate) async fn set_key_value_handler(data: AFPluginData<KeyValuePB>) -> FlowyResult<()> {
|
pub(crate) async fn set_key_value_handler(data: AFPluginData<KeyValuePB>) -> FlowyResult<()> {
|
||||||
let data = data.into_inner();
|
let data = data.into_inner();
|
||||||
@ -35,7 +38,24 @@ pub(crate) async fn remove_key_value_handler(data: AFPluginData<KeyPB>) -> Flowy
|
|||||||
pub(crate) async fn set_supabase_config_handler(
|
pub(crate) async fn set_supabase_config_handler(
|
||||||
data: AFPluginData<SupabaseConfigPB>,
|
data: AFPluginData<SupabaseConfigPB>,
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
let config = data.into_inner();
|
let config = SupabaseConfiguration::try_from(data.into_inner())?;
|
||||||
config.write_to_env();
|
config.write_env();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn set_collab_plugin_config_handler(
|
||||||
|
data: AFPluginData<CollabPluginConfigPB>,
|
||||||
|
) -> FlowyResult<()> {
|
||||||
|
let config = data.into_inner();
|
||||||
|
if let Some(aws_config_pb) = config.aws_config {
|
||||||
|
if let Ok(aws_config) = AWSDynamoDBConfig::try_from(aws_config_pb) {
|
||||||
|
aws_config.write_env();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(supabase_config_pb) = config.supabase_config {
|
||||||
|
if let Ok(supabase_config) = SupabaseDBConfig::try_from(supabase_config_pb) {
|
||||||
|
supabase_config.write_env();
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ pub fn init() -> AFPlugin {
|
|||||||
.event(ConfigEvent::GetKeyValue, get_key_value_handler)
|
.event(ConfigEvent::GetKeyValue, get_key_value_handler)
|
||||||
.event(ConfigEvent::RemoveKeyValue, remove_key_value_handler)
|
.event(ConfigEvent::RemoveKeyValue, remove_key_value_handler)
|
||||||
.event(ConfigEvent::SetSupabaseConfig, set_supabase_config_handler)
|
.event(ConfigEvent::SetSupabaseConfig, set_supabase_config_handler)
|
||||||
|
.event(
|
||||||
|
ConfigEvent::SetCollabPluginConfig,
|
||||||
|
set_collab_plugin_config_handler,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Display, ProtoBuf_Enum, Flowy_Event)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Display, ProtoBuf_Enum, Flowy_Event)]
|
||||||
@ -30,4 +34,7 @@ pub enum ConfigEvent {
|
|||||||
/// Check out the `write_to_env` of [SupabaseConfigPB].
|
/// Check out the `write_to_env` of [SupabaseConfigPB].
|
||||||
#[event(input = "SupabaseConfigPB")]
|
#[event(input = "SupabaseConfigPB")]
|
||||||
SetSupabaseConfig = 3,
|
SetSupabaseConfig = 3,
|
||||||
|
|
||||||
|
#[event(input = "CollabPluginConfigPB")]
|
||||||
|
SetCollabPluginConfig = 4,
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,9 @@ lib-log = { path = "../lib-log" }
|
|||||||
flowy-user = { path = "../flowy-user" }
|
flowy-user = { path = "../flowy-user" }
|
||||||
flowy-net = { path = "../flowy-net" }
|
flowy-net = { path = "../flowy-net" }
|
||||||
flowy-folder2 = { path = "../flowy-folder2" }
|
flowy-folder2 = { path = "../flowy-folder2" }
|
||||||
#flowy-database = { path = "../flowy-database" }
|
|
||||||
flowy-database2 = { path = "../flowy-database2" }
|
flowy-database2 = { path = "../flowy-database2" }
|
||||||
flowy-sqlite = { path = "../flowy-sqlite", optional = true }
|
flowy-sqlite = { path = "../flowy-sqlite", optional = true }
|
||||||
#flowy-document = { path = "../flowy-document" }
|
|
||||||
flowy-document2 = { path = "../flowy-document2" }
|
flowy-document2 = { path = "../flowy-document2" }
|
||||||
#flowy-revision = { path = "../flowy-revision" }
|
|
||||||
flowy-error = { path = "../flowy-error" }
|
flowy-error = { path = "../flowy-error" }
|
||||||
flowy-task = { path = "../flowy-task" }
|
flowy-task = { path = "../flowy-task" }
|
||||||
flowy-server = { path = "../flowy-server" }
|
flowy-server = { path = "../flowy-server" }
|
||||||
@ -34,6 +31,7 @@ lib-ws = { path = "../../../shared-lib/lib-ws" }
|
|||||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
serde_repr = "0.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rev-sqlite"]
|
default = ["rev-sqlite"]
|
||||||
|
@ -13,8 +13,9 @@ use flowy_document2::document_data::DocumentDataWrapper;
|
|||||||
use flowy_document2::entities::DocumentDataPB;
|
use flowy_document2::entities::DocumentDataPB;
|
||||||
use flowy_document2::manager::DocumentManager;
|
use flowy_document2::manager::DocumentManager;
|
||||||
use flowy_error::FlowyError;
|
use flowy_error::FlowyError;
|
||||||
|
use flowy_folder2::deps::{FolderCloudService, FolderUser};
|
||||||
use flowy_folder2::entities::ViewLayoutPB;
|
use flowy_folder2::entities::ViewLayoutPB;
|
||||||
use flowy_folder2::manager::{Folder2Manager, FolderUser};
|
use flowy_folder2::manager::Folder2Manager;
|
||||||
use flowy_folder2::view_ext::{ViewDataProcessor, ViewDataProcessorMap};
|
use flowy_folder2::view_ext::{ViewDataProcessor, ViewDataProcessorMap};
|
||||||
use flowy_folder2::ViewLayout;
|
use flowy_folder2::ViewLayout;
|
||||||
use flowy_user::services::UserSession;
|
use flowy_user::services::UserSession;
|
||||||
@ -27,13 +28,14 @@ impl Folder2DepsResolver {
|
|||||||
document_manager: &Arc<DocumentManager>,
|
document_manager: &Arc<DocumentManager>,
|
||||||
database_manager: &Arc<DatabaseManager2>,
|
database_manager: &Arc<DatabaseManager2>,
|
||||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||||
|
folder_cloud: Arc<dyn FolderCloudService>,
|
||||||
) -> Arc<Folder2Manager> {
|
) -> Arc<Folder2Manager> {
|
||||||
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl(user_session.clone()));
|
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl(user_session.clone()));
|
||||||
|
|
||||||
let view_data_processor =
|
let view_processors =
|
||||||
make_view_data_processor(document_manager.clone(), database_manager.clone());
|
make_view_data_processor(document_manager.clone(), database_manager.clone());
|
||||||
Arc::new(
|
Arc::new(
|
||||||
Folder2Manager::new(user.clone(), collab_builder, view_data_processor)
|
Folder2Manager::new(user.clone(), collab_builder, view_processors, folder_cloud)
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
|
@ -1,64 +1,134 @@
|
|||||||
|
use lib_infra::future::FutureResult;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use flowy_error::{ErrorCode, FlowyError};
|
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||||
|
use flowy_folder2::deps::{FolderCloudService, Workspace};
|
||||||
use flowy_server::local_server::LocalServer;
|
use flowy_server::local_server::LocalServer;
|
||||||
use flowy_server::self_host::configuration::self_host_server_configuration;
|
use flowy_server::self_host::configuration::self_host_server_configuration;
|
||||||
use flowy_server::self_host::SelfHostServer;
|
use flowy_server::self_host::SelfHostServer;
|
||||||
use flowy_server::supabase::{SupabaseConfiguration, SupabaseServer};
|
use flowy_server::supabase::{SupabaseConfiguration, SupabaseServer};
|
||||||
use flowy_server::AppFlowyServer;
|
use flowy_server::AppFlowyServer;
|
||||||
|
use flowy_sqlite::kv::KV;
|
||||||
use flowy_user::event_map::{UserAuthService, UserCloudServiceProvider};
|
use flowy_user::event_map::{UserAuthService, UserCloudServiceProvider};
|
||||||
use flowy_user::services::AuthType;
|
use flowy_user::services::AuthType;
|
||||||
|
|
||||||
|
use serde_repr::*;
|
||||||
|
|
||||||
|
const SERVER_PROVIDER_TYPE_KEY: &str = "server_provider_type";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum ServerProviderType {
|
||||||
|
/// Local server provider.
|
||||||
|
/// Offline mode, no user authentication and the data is stored locally.
|
||||||
|
Local = 0,
|
||||||
|
/// Self-hosted server provider.
|
||||||
|
/// The [AppFlowy-Server](https://github.com/AppFlowy-IO/AppFlowy-Server) is still a work in
|
||||||
|
/// progress.
|
||||||
|
SelfHosted = 1,
|
||||||
|
/// Supabase server provider.
|
||||||
|
/// It uses supabase's postgresql database to store data and user authentication.
|
||||||
|
Supabase = 2,
|
||||||
|
}
|
||||||
|
|
||||||
/// The [AppFlowyServerProvider] provides list of [AppFlowyServer] base on the [AuthType]. Using
|
/// The [AppFlowyServerProvider] provides list of [AppFlowyServer] base on the [AuthType]. Using
|
||||||
/// the auth type, the [AppFlowyServerProvider] will create a new [AppFlowyServer] if it doesn't
|
/// the auth type, the [AppFlowyServerProvider] will create a new [AppFlowyServer] if it doesn't
|
||||||
/// exist.
|
/// exist.
|
||||||
/// Each server implements the [AppFlowyServer] trait, which provides the [UserAuthService], etc.
|
/// Each server implements the [AppFlowyServer] trait, which provides the [UserAuthService], etc.
|
||||||
#[derive(Default)]
|
|
||||||
pub struct AppFlowyServerProvider {
|
pub struct AppFlowyServerProvider {
|
||||||
providers: RwLock<HashMap<AuthType, Arc<dyn AppFlowyServer>>>,
|
provider_type: RwLock<ServerProviderType>,
|
||||||
|
providers: RwLock<HashMap<ServerProviderType, Arc<dyn AppFlowyServer>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppFlowyServerProvider {
|
impl AppFlowyServerProvider {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn provider_type(&self) -> ServerProviderType {
|
||||||
|
self.provider_type.read().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a [AppFlowyServer] trait implementation base on the provider_type.
|
||||||
|
fn get_provider(
|
||||||
|
&self,
|
||||||
|
provider_type: &ServerProviderType,
|
||||||
|
) -> FlowyResult<Arc<dyn AppFlowyServer>> {
|
||||||
|
if let Some(provider) = self.providers.read().get(provider_type) {
|
||||||
|
return Ok(provider.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let server = server_from_auth_type(provider_type)?;
|
||||||
|
self
|
||||||
|
.providers
|
||||||
|
.write()
|
||||||
|
.insert(provider_type.clone(), server.clone());
|
||||||
|
Ok(server)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppFlowyServerProvider {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
provider_type: RwLock::new(current_server_provider()),
|
||||||
|
providers: RwLock::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserCloudServiceProvider for AppFlowyServerProvider {
|
impl UserCloudServiceProvider for AppFlowyServerProvider {
|
||||||
/// Returns the [UserAuthService] base on the current [AuthType].
|
/// When user login, the provider type is set by the [AuthType].
|
||||||
/// Creates a new [AppFlowyServer] if it doesn't exist.
|
/// Each [AuthType] has a corresponding [ServerProviderType]. The [ServerProviderType] is used
|
||||||
fn get_auth_service(&self, auth_type: &AuthType) -> Result<Arc<dyn UserAuthService>, FlowyError> {
|
/// to create a new [AppFlowyServer] if it doesn't exist. Once the [ServerProviderType] is set,
|
||||||
if let Some(provider) = self.providers.read().get(auth_type) {
|
/// it will be used when user open the app again.
|
||||||
return Ok(provider.user_service());
|
fn set_auth_type(&self, auth_type: AuthType) {
|
||||||
|
let provider_type: ServerProviderType = auth_type.into();
|
||||||
|
match KV::set_object(SERVER_PROVIDER_TYPE_KEY, provider_type.clone()) {
|
||||||
|
Ok(_) => tracing::trace!("Update server provider type to: {:?}", provider_type),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("🔴Failed to update server provider type: {:?}", e);
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let server = server_from_auth_type(auth_type)?;
|
/// Returns the [UserAuthService] base on the current [ServerProviderType].
|
||||||
let user_service = server.user_service();
|
/// Creates a new [AppFlowyServer] if it doesn't exist.
|
||||||
self.providers.write().insert(auth_type.clone(), server);
|
fn get_auth_service(&self, auth_type: &AuthType) -> Result<Arc<dyn UserAuthService>, FlowyError> {
|
||||||
Ok(user_service)
|
let provider_type: ServerProviderType = auth_type.into();
|
||||||
|
Ok(self.get_provider(&provider_type)?.user_service())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn server_from_auth_type(auth_type: &AuthType) -> Result<Arc<dyn AppFlowyServer>, FlowyError> {
|
impl FolderCloudService for AppFlowyServerProvider {
|
||||||
match auth_type {
|
fn create_workspace(&self, uid: i64, name: &str) -> FutureResult<Workspace, FlowyError> {
|
||||||
AuthType::Local => {
|
let server = self.get_provider(&self.provider_type.read());
|
||||||
|
let name = name.to_string();
|
||||||
|
FutureResult::new(async move { server?.folder_service().create_workspace(uid, &name).await })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn server_from_auth_type(
|
||||||
|
provider: &ServerProviderType,
|
||||||
|
) -> Result<Arc<dyn AppFlowyServer>, FlowyError> {
|
||||||
|
match provider {
|
||||||
|
ServerProviderType::Local => {
|
||||||
let server = Arc::new(LocalServer::new());
|
let server = Arc::new(LocalServer::new());
|
||||||
Ok(server)
|
Ok(server)
|
||||||
},
|
},
|
||||||
AuthType::SelfHosted => {
|
ServerProviderType::SelfHosted => {
|
||||||
let config = self_host_server_configuration().map_err(|e| {
|
let config = self_host_server_configuration().map_err(|e| {
|
||||||
FlowyError::new(
|
FlowyError::new(
|
||||||
ErrorCode::InvalidAuthConfig,
|
ErrorCode::InvalidAuthConfig,
|
||||||
format!("Missing self host config: {:?}. Error: {:?}", auth_type, e),
|
format!("Missing self host config: {:?}. Error: {:?}", provider, e),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let server = Arc::new(SelfHostServer::new(config));
|
let server = Arc::new(SelfHostServer::new(config));
|
||||||
Ok(server)
|
Ok(server)
|
||||||
},
|
},
|
||||||
AuthType::Supabase => {
|
ServerProviderType::Supabase => {
|
||||||
// init the SupabaseServerConfiguration from the environment variables.
|
// init the SupabaseServerConfiguration from the environment variables.
|
||||||
let config = SupabaseConfiguration::from_env()?;
|
let config = SupabaseConfiguration::from_env()?;
|
||||||
let server = Arc::new(SupabaseServer::new(config));
|
let server = Arc::new(SupabaseServer::new(config));
|
||||||
@ -66,3 +136,26 @@ fn server_from_auth_type(auth_type: &AuthType) -> Result<Arc<dyn AppFlowyServer>
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<AuthType> for ServerProviderType {
|
||||||
|
fn from(auth_provider: AuthType) -> Self {
|
||||||
|
match auth_provider {
|
||||||
|
AuthType::Local => ServerProviderType::Local,
|
||||||
|
AuthType::SelfHosted => ServerProviderType::SelfHosted,
|
||||||
|
AuthType::Supabase => ServerProviderType::Supabase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&AuthType> for ServerProviderType {
|
||||||
|
fn from(auth_provider: &AuthType) -> Self {
|
||||||
|
Self::from(auth_provider.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_server_provider() -> ServerProviderType {
|
||||||
|
match KV::get_object::<ServerProviderType>(SERVER_PROVIDER_TYPE_KEY) {
|
||||||
|
None => ServerProviderType::Local,
|
||||||
|
Some(provider_type) => provider_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#![allow(unused_doc_comments)]
|
#![allow(unused_doc_comments)]
|
||||||
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
@ -10,8 +9,8 @@ use std::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
use appflowy_integrate::collab_builder::{AppFlowyCollabBuilder, CloudStorageType};
|
||||||
use appflowy_integrate::config::{AWSDynamoDBConfig, AppFlowyCollabConfig};
|
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use flowy_database2::DatabaseManager2;
|
use flowy_database2::DatabaseManager2;
|
||||||
@ -28,9 +27,10 @@ use lib_dispatch::runtime::tokio_default_runtime;
|
|||||||
use lib_infra::future::{to_fut, Fut};
|
use lib_infra::future::{to_fut, Fut};
|
||||||
use module::make_plugins;
|
use module::make_plugins;
|
||||||
pub use module::*;
|
pub use module::*;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::deps_resolve::*;
|
use crate::deps_resolve::*;
|
||||||
use crate::integrate::server::AppFlowyServerProvider;
|
use crate::integrate::server::{AppFlowyServerProvider, ServerProviderType};
|
||||||
|
|
||||||
mod deps_resolve;
|
mod deps_resolve;
|
||||||
mod integrate;
|
mod integrate;
|
||||||
@ -92,10 +92,8 @@ fn create_log_filter(level: String, with_crates: Vec<String>) -> String {
|
|||||||
filters.push(format!("flowy_document2={}", level));
|
filters.push(format!("flowy_document2={}", level));
|
||||||
filters.push(format!("flowy_database2={}", level));
|
filters.push(format!("flowy_database2={}", level));
|
||||||
filters.push(format!("flowy_notification={}", "info"));
|
filters.push(format!("flowy_notification={}", "info"));
|
||||||
filters.push(format!("lib_ot={}", level));
|
|
||||||
filters.push(format!("lib_infra={}", level));
|
filters.push(format!("lib_infra={}", level));
|
||||||
filters.push(format!("flowy_task={}", level));
|
filters.push(format!("flowy_task={}", level));
|
||||||
// filters.push(format!("lib_dispatch={}", level));
|
|
||||||
|
|
||||||
filters.push(format!("dart_ffi={}", "info"));
|
filters.push(format!("dart_ffi={}", "info"));
|
||||||
filters.push(format!("flowy_sqlite={}", "info"));
|
filters.push(format!("flowy_sqlite={}", "info"));
|
||||||
@ -136,22 +134,19 @@ impl AppFlowyCore {
|
|||||||
// Init the key value database
|
// Init the key value database
|
||||||
init_kv(&config.storage_path);
|
init_kv(&config.storage_path);
|
||||||
|
|
||||||
// The collab config is used to build the [Collab] instance that used in document,
|
debug!("🔥 {:?}", &config);
|
||||||
// database, folder, etc.
|
|
||||||
let collab_config = get_collab_config();
|
|
||||||
inject_aws_env(collab_config.aws_config());
|
|
||||||
|
|
||||||
/// The shared collab builder is used to build the [Collab] instance. The plugins will be loaded
|
|
||||||
/// on demand based on the [AppFlowyCollabConfig].
|
|
||||||
let collab_builder = Arc::new(AppFlowyCollabBuilder::new(collab_config));
|
|
||||||
|
|
||||||
tracing::debug!("🔥 {:?}", config);
|
|
||||||
let runtime = tokio_default_runtime().unwrap();
|
let runtime = tokio_default_runtime().unwrap();
|
||||||
let task_scheduler = TaskDispatcher::new(Duration::from_secs(2));
|
let task_scheduler = TaskDispatcher::new(Duration::from_secs(2));
|
||||||
let task_dispatcher = Arc::new(RwLock::new(task_scheduler));
|
let task_dispatcher = Arc::new(RwLock::new(task_scheduler));
|
||||||
runtime.spawn(TaskRunner::run(task_dispatcher.clone()));
|
runtime.spawn(TaskRunner::run(task_dispatcher.clone()));
|
||||||
|
|
||||||
let server_provider = Arc::new(AppFlowyServerProvider::new());
|
let server_provider = Arc::new(AppFlowyServerProvider::new());
|
||||||
|
/// The shared collab builder is used to build the [Collab] instance. The plugins will be loaded
|
||||||
|
/// on demand based on the [CollabPluginConfig].
|
||||||
|
let cloud_storage_type =
|
||||||
|
collab_storage_type_from_server_provider_type(&server_provider.provider_type());
|
||||||
|
let collab_builder = Arc::new(AppFlowyCollabBuilder::new(cloud_storage_type));
|
||||||
|
|
||||||
let (user_session, folder_manager, server_provider, database_manager, document_manager2) =
|
let (user_session, folder_manager, server_provider, database_manager, document_manager2) =
|
||||||
runtime.block_on(async {
|
runtime.block_on(async {
|
||||||
let user_session = mk_user_session(&config, server_provider.clone());
|
let user_session = mk_user_session(&config, server_provider.clone());
|
||||||
@ -173,6 +168,7 @@ impl AppFlowyCore {
|
|||||||
&document_manager2,
|
&document_manager2,
|
||||||
&database_manager2,
|
&database_manager2,
|
||||||
collab_builder.clone(),
|
collab_builder.clone(),
|
||||||
|
server_provider.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -233,23 +229,6 @@ fn init_kv(root: &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_collab_config() -> AppFlowyCollabConfig {
|
|
||||||
match KV::get_str("collab_config") {
|
|
||||||
None => AppFlowyCollabConfig::default(),
|
|
||||||
Some(s) => AppFlowyCollabConfig::from_str(&s).unwrap_or_default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inject_aws_env(aws_config: Option<&AWSDynamoDBConfig>) {
|
|
||||||
if let Some(aws_config) = aws_config {
|
|
||||||
std::env::set_var("AWS_ACCESS_KEY_ID", aws_config.access_key_id.clone());
|
|
||||||
std::env::set_var(
|
|
||||||
"AWS_SECRET_ACCESS_KEY",
|
|
||||||
aws_config.secret_access_key.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_log(config: &AppFlowyCoreConfig) {
|
fn init_log(config: &AppFlowyCoreConfig) {
|
||||||
if !INIT_LOG.load(Ordering::SeqCst) {
|
if !INIT_LOG.load(Ordering::SeqCst) {
|
||||||
INIT_LOG.store(true, Ordering::SeqCst);
|
INIT_LOG.store(true, Ordering::SeqCst);
|
||||||
@ -334,3 +313,13 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
|||||||
to_fut(async move { listener.did_expired(&token, user_id).await })
|
to_fut(async move { listener.did_expired(&token, user_id).await })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collab_storage_type_from_server_provider_type(
|
||||||
|
server_provider_type: &ServerProviderType,
|
||||||
|
) -> CloudStorageType {
|
||||||
|
match server_provider_type {
|
||||||
|
ServerProviderType::Local => CloudStorageType::Local,
|
||||||
|
ServerProviderType::SelfHosted => CloudStorageType::Local,
|
||||||
|
ServerProviderType::Supabase => CloudStorageType::Supabase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -334,7 +334,7 @@ pub(crate) async fn create_row_handler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip_all, err)]
|
// #[tracing::instrument(level = "trace", skip_all, err)]
|
||||||
pub(crate) async fn get_cell_handler(
|
pub(crate) async fn get_cell_handler(
|
||||||
data: AFPluginData<CellIdPB>,
|
data: AFPluginData<CellIdPB>,
|
||||||
manager: AFPluginState<Arc<DatabaseManager2>>,
|
manager: AFPluginState<Arc<DatabaseManager2>>,
|
||||||
@ -560,7 +560,6 @@ pub(crate) async fn set_layout_setting_handler(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
|
||||||
pub(crate) async fn get_layout_setting_handler(
|
pub(crate) async fn get_layout_setting_handler(
|
||||||
data: AFPluginData<DatabaseLayoutIdPB>,
|
data: AFPluginData<DatabaseLayoutIdPB>,
|
||||||
manager: AFPluginState<Arc<DatabaseManager2>>,
|
manager: AFPluginState<Arc<DatabaseManager2>>,
|
||||||
|
@ -3,7 +3,7 @@ use std::ops::Deref;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use appflowy_integrate::{RocksCollabDB, RocksDBConfig};
|
use appflowy_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||||
use collab::core::collab::MutexCollab;
|
use collab::core::collab::MutexCollab;
|
||||||
use collab_database::database::DatabaseData;
|
use collab_database::database::DatabaseData;
|
||||||
use collab_database::user::{UserDatabase as InnerUserDatabase, UserDatabaseCollabBuilder};
|
use collab_database::user::{UserDatabase as InnerUserDatabase, UserDatabaseCollabBuilder};
|
||||||
@ -51,7 +51,7 @@ impl DatabaseManager2 {
|
|||||||
*self.user_database.lock() = Some(InnerUserDatabase::new(
|
*self.user_database.lock() = Some(InnerUserDatabase::new(
|
||||||
user_id,
|
user_id,
|
||||||
db,
|
db,
|
||||||
RocksDBConfig::default(),
|
CollabPersistenceConfig::default(),
|
||||||
UserDatabaseCollabBuilderImpl(self.collab_builder.clone()),
|
UserDatabaseCollabBuilderImpl(self.collab_builder.clone()),
|
||||||
));
|
));
|
||||||
// do nothing
|
// do nothing
|
||||||
@ -229,7 +229,7 @@ impl UserDatabaseCollabBuilder for UserDatabaseCollabBuilderImpl {
|
|||||||
uid: i64,
|
uid: i64,
|
||||||
object_id: &str,
|
object_id: &str,
|
||||||
db: Arc<RocksCollabDB>,
|
db: Arc<RocksCollabDB>,
|
||||||
config: &RocksDBConfig,
|
config: &CollabPersistenceConfig,
|
||||||
) -> Arc<MutexCollab> {
|
) -> Arc<MutexCollab> {
|
||||||
self.0.build_with_config(uid, object_id, db, config)
|
self.0.build_with_config(uid, object_id, db, config)
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ use std::sync::Arc;
|
|||||||
use collab_database::fields::Field;
|
use collab_database::fields::Field;
|
||||||
use collab_database::rows::RowId;
|
use collab_database::rows::RowId;
|
||||||
|
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use lib_infra::future::{to_fut, Fut};
|
use lib_infra::future::{to_fut, Fut};
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::entities::FieldType;
|
use crate::entities::FieldType;
|
||||||
use crate::services::database_view::DatabaseViewData;
|
use crate::services::database_view::DatabaseViewData;
|
||||||
@ -42,9 +43,10 @@ pub async fn new_group_controller(
|
|||||||
let fields = delegate.get_fields(&view_id, None).await;
|
let fields = delegate.get_fields(&view_id, None).await;
|
||||||
let rows = delegate.get_rows(&view_id).await;
|
let rows = delegate.get_rows(&view_id).await;
|
||||||
let layout = delegate.get_layout_for_view(&view_id);
|
let layout = delegate.get_layout_for_view(&view_id);
|
||||||
|
trace!(?fields, ?rows, ?layout, "new_group_controller");
|
||||||
|
|
||||||
// Read the grouping field or find a new grouping field
|
// Read the grouping field or find a new grouping field
|
||||||
let grouping_field = setting_reader
|
let mut grouping_field = setting_reader
|
||||||
.get_group_setting(&view_id)
|
.get_group_setting(&view_id)
|
||||||
.await
|
.await
|
||||||
.and_then(|setting| {
|
.and_then(|setting| {
|
||||||
@ -52,17 +54,25 @@ pub async fn new_group_controller(
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|field| field.id == setting.field_id)
|
.find(|field| field.id == setting.field_id)
|
||||||
.cloned()
|
.cloned()
|
||||||
})
|
});
|
||||||
.unwrap_or_else(|| find_new_grouping_field(&fields, &layout).unwrap());
|
|
||||||
|
|
||||||
|
if grouping_field.is_none() {
|
||||||
|
grouping_field = find_new_grouping_field(&fields, &layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
match grouping_field {
|
||||||
|
None => Err(FlowyError::internal().context("No grouping field found".to_owned())),
|
||||||
|
Some(_) => {
|
||||||
make_group_controller(
|
make_group_controller(
|
||||||
view_id,
|
view_id,
|
||||||
grouping_field,
|
grouping_field.unwrap(),
|
||||||
rows,
|
rows,
|
||||||
setting_reader,
|
setting_reader,
|
||||||
setting_writer,
|
setting_writer,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct GroupSettingReaderImpl(pub Arc<dyn DatabaseViewData>);
|
pub(crate) struct GroupSettingReaderImpl(pub Arc<dyn DatabaseViewData>);
|
||||||
|
@ -7,15 +7,9 @@ use strum::IntoEnumIterator;
|
|||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CURRENCY_SYMBOL: Vec<String> = sorted_symbol();
|
pub static ref CURRENCY_SYMBOL: Vec<String> = NumberFormat::iter()
|
||||||
}
|
|
||||||
|
|
||||||
fn sorted_symbol() -> Vec<String> {
|
|
||||||
let mut symbols = NumberFormat::iter()
|
|
||||||
.map(|format| format.symbol())
|
.map(|format| format.symbol())
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
symbols.sort_by(|a, b| b.len().cmp(&a.len()));
|
|
||||||
symbols
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, Serialize, Deserialize)]
|
||||||
|
@ -137,9 +137,7 @@ impl NumberTypeOption {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match Decimal::from_str(&num_str) {
|
match Decimal::from_str(&num_str) {
|
||||||
Ok(decimal, ..) => {
|
Ok(decimal, ..) => Ok(NumberCellFormat::from_decimal(decimal)),
|
||||||
return Ok(NumberCellFormat::from_decimal(decimal));
|
|
||||||
},
|
|
||||||
Err(_) => Ok(NumberCellFormat::new()),
|
Err(_) => Ok(NumberCellFormat::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ impl NumberCellFormat {
|
|||||||
return Ok(Self::default());
|
return Ok(Self::default());
|
||||||
}
|
}
|
||||||
// If the first char is not '-', then it is a sign.
|
// If the first char is not '-', then it is a sign.
|
||||||
let sign_positive = match num_str.find("-") {
|
let sign_positive = match num_str.find('-') {
|
||||||
None => true,
|
None => true,
|
||||||
Some(offset) => offset != 0,
|
Some(offset) => offset != 0,
|
||||||
};
|
};
|
||||||
|
@ -162,7 +162,7 @@ where
|
|||||||
) {
|
) {
|
||||||
if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
|
if let Some(cell_data_cache) = self.cell_data_cache.as_ref() {
|
||||||
let field_type = FieldType::from(field.field_type);
|
let field_type = FieldType::from(field.field_type);
|
||||||
let key = CellDataCacheKey::new(field, field_type.clone(), cell);
|
let key = CellDataCacheKey::new(field, field_type, cell);
|
||||||
// tracing::trace!(
|
// tracing::trace!(
|
||||||
// "Cell cache update: field_type:{}, cell: {:?}, cell_data: {:?}",
|
// "Cell cache update: field_type:{}, cell: {:?}, cell_data: {:?}",
|
||||||
// field_type,
|
// field_type,
|
||||||
|
@ -85,7 +85,7 @@ impl SortController {
|
|||||||
self.gen_task(task_type, QualityOfService::Background).await;
|
self.gen_task(task_type, QualityOfService::Background).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "process_sort_task", level = "debug", skip_all, err)]
|
// #[tracing::instrument(name = "process_sort_task", level = "trace", skip_all, err)]
|
||||||
pub async fn process(&mut self, predicate: &str) -> FlowyResult<()> {
|
pub async fn process(&mut self, predicate: &str) -> FlowyResult<()> {
|
||||||
let event_type = SortEvent::from_str(predicate).unwrap();
|
let event_type = SortEvent::from_str(predicate).unwrap();
|
||||||
let mut rows = self.delegate.get_rows(&self.view_id).await;
|
let mut rows = self.delegate.get_rows(&self.view_id).await;
|
||||||
|
@ -13,13 +13,13 @@ use flowy_database2::services::field::{
|
|||||||
SelectOptionCellChangeset, SingleSelectTypeOption,
|
SelectOptionCellChangeset, SingleSelectTypeOption,
|
||||||
};
|
};
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
use flowy_test::helper::ViewTest;
|
use flowy_test::folder_event::ViewTest;
|
||||||
use flowy_test::FlowySDKTest;
|
use flowy_test::FlowyCoreTest;
|
||||||
|
|
||||||
use crate::database::mock_data::{make_test_board, make_test_calendar, make_test_grid};
|
use crate::database::mock_data::{make_test_board, make_test_calendar, make_test_grid};
|
||||||
|
|
||||||
pub struct DatabaseEditorTest {
|
pub struct DatabaseEditorTest {
|
||||||
pub sdk: FlowySDKTest,
|
pub sdk: FlowyCoreTest,
|
||||||
pub app_id: String,
|
pub app_id: String,
|
||||||
pub view_id: String,
|
pub view_id: String,
|
||||||
pub editor: Arc<DatabaseEditor>,
|
pub editor: Arc<DatabaseEditor>,
|
||||||
@ -43,7 +43,7 @@ impl DatabaseEditorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new(layout: DatabaseLayoutPB) -> Self {
|
pub async fn new(layout: DatabaseLayoutPB) -> Self {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
let _ = sdk.init_user().await;
|
let _ = sdk.init_user().await;
|
||||||
let test = match layout {
|
let test = match layout {
|
||||||
DatabaseLayoutPB::Grid => {
|
DatabaseLayoutPB::Grid => {
|
||||||
|
@ -1,425 +0,0 @@
|
|||||||
use bytes::Bytes;
|
|
||||||
use database_model::entities::{
|
|
||||||
BuildGridContext, CellChangeset, Field, FieldChangesetParams, FieldMeta, FieldOrder, FieldType,
|
|
||||||
GridBlockInfoChangeset, GridBlockMetaSnapshot, InsertFieldParams, RowMeta, RowMetaChangeset,
|
|
||||||
RowOrder, TypeOptionDataFormat,
|
|
||||||
};
|
|
||||||
use flowy_client_sync::client_grid::GridBuilder;
|
|
||||||
use flowy_database::services::field::*;
|
|
||||||
use flowy_database::services::grid_meta_editor::{GridMetaEditor, GridPadBuilder};
|
|
||||||
use flowy_database::services::row::CreateRowMetaPayload;
|
|
||||||
use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
|
|
||||||
use flowy_test::helper::ViewTest;
|
|
||||||
use flowy_test::FlowySDKTest;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
|
||||||
use strum::EnumCount;
|
|
||||||
use tokio::time::sleep;
|
|
||||||
|
|
||||||
pub enum EditorScript {
|
|
||||||
CreateField {
|
|
||||||
params: InsertFieldParams,
|
|
||||||
},
|
|
||||||
UpdateField {
|
|
||||||
changeset: FieldChangesetParams,
|
|
||||||
},
|
|
||||||
DeleteField {
|
|
||||||
field_meta: FieldMeta,
|
|
||||||
},
|
|
||||||
AssertFieldCount(usize),
|
|
||||||
AssertFieldEqual {
|
|
||||||
field_index: usize,
|
|
||||||
field_meta: FieldMeta,
|
|
||||||
},
|
|
||||||
CreateBlock {
|
|
||||||
block: GridBlockMetaSnapshot,
|
|
||||||
},
|
|
||||||
UpdateBlock {
|
|
||||||
changeset: GridBlockInfoChangeset,
|
|
||||||
},
|
|
||||||
AssertBlockCount(usize),
|
|
||||||
AssertBlock {
|
|
||||||
block_index: usize,
|
|
||||||
row_count: i32,
|
|
||||||
start_row_index: i32,
|
|
||||||
},
|
|
||||||
AssertBlockEqual {
|
|
||||||
block_index: usize,
|
|
||||||
block: GridBlockMetaSnapshot,
|
|
||||||
},
|
|
||||||
CreateEmptyRow,
|
|
||||||
CreateRow {
|
|
||||||
context: CreateRowMetaPayload,
|
|
||||||
},
|
|
||||||
UpdateRow {
|
|
||||||
changeset: RowMetaChangeset,
|
|
||||||
},
|
|
||||||
AssertRow {
|
|
||||||
changeset: RowMetaChangeset,
|
|
||||||
},
|
|
||||||
DeleteRow {
|
|
||||||
row_ids: Vec<String>,
|
|
||||||
},
|
|
||||||
UpdateCell {
|
|
||||||
changeset: CellChangeset,
|
|
||||||
is_err: bool,
|
|
||||||
},
|
|
||||||
AssertRowCount(usize),
|
|
||||||
// AssertRowEqual{ row_index: usize, row: RowMeta},
|
|
||||||
AssertGridMetaPad,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GridEditorTest {
|
|
||||||
pub sdk: FlowySDKTest,
|
|
||||||
pub grid_id: String,
|
|
||||||
pub editor: Arc<GridMetaEditor>,
|
|
||||||
pub field_metas: Vec<FieldMeta>,
|
|
||||||
pub grid_blocks: Vec<GridBlockMetaSnapshot>,
|
|
||||||
pub row_metas: Vec<Arc<RowMeta>>,
|
|
||||||
pub field_count: usize,
|
|
||||||
|
|
||||||
pub row_order_by_row_id: HashMap<String, RowOrder>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GridEditorTest {
|
|
||||||
pub async fn new() -> Self {
|
|
||||||
let sdk = FlowySDKTest::default();
|
|
||||||
let _ = sdk.init_user().await;
|
|
||||||
let build_context = make_template_1_grid();
|
|
||||||
let view_data: Bytes = build_context.into();
|
|
||||||
let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
|
|
||||||
let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
|
|
||||||
let field_metas = editor.get_field_metas::<FieldOrder>(None).await.unwrap();
|
|
||||||
let grid_blocks = editor.get_block_metas().await.unwrap();
|
|
||||||
let row_metas = get_row_metas(&editor).await;
|
|
||||||
|
|
||||||
let grid_id = test.view.id;
|
|
||||||
Self {
|
|
||||||
sdk,
|
|
||||||
grid_id,
|
|
||||||
editor,
|
|
||||||
field_metas,
|
|
||||||
grid_blocks,
|
|
||||||
row_metas,
|
|
||||||
field_count: FieldType::COUNT,
|
|
||||||
row_order_by_row_id: HashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_scripts(&mut self, scripts: Vec<EditorScript>) {
|
|
||||||
for script in scripts {
|
|
||||||
self.run_script(script).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_script(&mut self, script: EditorScript) {
|
|
||||||
let grid_manager = self.sdk.grid_manager.clone();
|
|
||||||
let pool = self.sdk.user_session.db_pool().unwrap();
|
|
||||||
let rev_manager = self.editor.rev_manager();
|
|
||||||
let _cache = rev_manager.revision_cache().await;
|
|
||||||
|
|
||||||
match script {
|
|
||||||
EditorScript::CreateField { params } => {
|
|
||||||
if !self.editor.contain_field(¶ms.field.id).await {
|
|
||||||
self.field_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.editor.insert_field(params).await.unwrap();
|
|
||||||
self.field_metas = self
|
|
||||||
.editor
|
|
||||||
.get_field_metas::<FieldOrder>(None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(self.field_count, self.field_metas.len());
|
|
||||||
},
|
|
||||||
EditorScript::UpdateField { changeset: change } => {
|
|
||||||
self.editor.update_field(change).await.unwrap();
|
|
||||||
self.field_metas = self
|
|
||||||
.editor
|
|
||||||
.get_field_metas::<FieldOrder>(None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
},
|
|
||||||
EditorScript::DeleteField { field_meta } => {
|
|
||||||
if self.editor.contain_field(&field_meta.id).await {
|
|
||||||
self.field_count -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.editor.delete_field(&field_meta.id).await.unwrap();
|
|
||||||
self.field_metas = self
|
|
||||||
.editor
|
|
||||||
.get_field_metas::<FieldOrder>(None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(self.field_count, self.field_metas.len());
|
|
||||||
},
|
|
||||||
EditorScript::AssertFieldCount(count) => {
|
|
||||||
assert_eq!(
|
|
||||||
self
|
|
||||||
.editor
|
|
||||||
.get_field_metas::<FieldOrder>(None)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.len(),
|
|
||||||
count
|
|
||||||
);
|
|
||||||
},
|
|
||||||
EditorScript::AssertFieldEqual {
|
|
||||||
field_index,
|
|
||||||
field_meta,
|
|
||||||
} => {
|
|
||||||
let field_metas = self
|
|
||||||
.editor
|
|
||||||
.get_field_metas::<FieldOrder>(None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(field_metas[field_index].clone(), field_meta);
|
|
||||||
},
|
|
||||||
EditorScript::CreateBlock { block } => {
|
|
||||||
self.editor.create_block(block).await.unwrap();
|
|
||||||
self.grid_blocks = self.editor.get_block_metas().await.unwrap();
|
|
||||||
},
|
|
||||||
EditorScript::UpdateBlock { changeset: change } => {
|
|
||||||
self.editor.update_block(change).await.unwrap();
|
|
||||||
},
|
|
||||||
EditorScript::AssertBlockCount(count) => {
|
|
||||||
assert_eq!(self.editor.get_block_metas().await.unwrap().len(), count);
|
|
||||||
},
|
|
||||||
EditorScript::AssertBlock {
|
|
||||||
block_index,
|
|
||||||
row_count,
|
|
||||||
start_row_index,
|
|
||||||
} => {
|
|
||||||
assert_eq!(self.grid_blocks[block_index].row_count, row_count);
|
|
||||||
assert_eq!(
|
|
||||||
self.grid_blocks[block_index].start_row_index,
|
|
||||||
start_row_index
|
|
||||||
);
|
|
||||||
},
|
|
||||||
EditorScript::AssertBlockEqual { block_index, block } => {
|
|
||||||
let blocks = self.editor.get_block_metas().await.unwrap();
|
|
||||||
let compared_block = blocks[block_index].clone();
|
|
||||||
assert_eq!(compared_block, block);
|
|
||||||
},
|
|
||||||
EditorScript::CreateEmptyRow => {
|
|
||||||
let row_order = self.editor.create_row(None).await.unwrap();
|
|
||||||
self
|
|
||||||
.row_order_by_row_id
|
|
||||||
.insert(row_order.row_id.clone(), row_order);
|
|
||||||
self.row_metas = self.get_row_metas().await;
|
|
||||||
self.grid_blocks = self.editor.get_block_metas().await.unwrap();
|
|
||||||
},
|
|
||||||
EditorScript::CreateRow { context } => {
|
|
||||||
let row_orders = self.editor.insert_rows(vec![context]).await.unwrap();
|
|
||||||
for row_order in row_orders {
|
|
||||||
self
|
|
||||||
.row_order_by_row_id
|
|
||||||
.insert(row_order.row_id.clone(), row_order);
|
|
||||||
}
|
|
||||||
self.row_metas = self.get_row_metas().await;
|
|
||||||
self.grid_blocks = self.editor.get_block_metas().await.unwrap();
|
|
||||||
},
|
|
||||||
EditorScript::UpdateRow { changeset: change } => {
|
|
||||||
self.editor.update_row(change).await.unwrap()
|
|
||||||
},
|
|
||||||
EditorScript::DeleteRow { row_ids } => {
|
|
||||||
let row_orders = row_ids
|
|
||||||
.into_iter()
|
|
||||||
.map(|row_id| self.row_order_by_row_id.get(&row_id).unwrap().clone())
|
|
||||||
.collect::<Vec<RowOrder>>();
|
|
||||||
|
|
||||||
self.editor.delete_rows(row_orders).await.unwrap();
|
|
||||||
self.row_metas = self.get_row_metas().await;
|
|
||||||
self.grid_blocks = self.editor.get_block_metas().await.unwrap();
|
|
||||||
},
|
|
||||||
EditorScript::AssertRow { changeset } => {
|
|
||||||
let row = self
|
|
||||||
.row_metas
|
|
||||||
.iter()
|
|
||||||
.find(|row| row.id == changeset.row_id)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Some(visibility) = changeset.visibility {
|
|
||||||
assert_eq!(row.visibility, visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(height) = changeset.height {
|
|
||||||
assert_eq!(row.height, height);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
EditorScript::UpdateCell { changeset, is_err } => {
|
|
||||||
let result = self.editor.update_cell(changeset).await;
|
|
||||||
if is_err {
|
|
||||||
assert!(result.is_err())
|
|
||||||
} else {
|
|
||||||
let _ = result.unwrap();
|
|
||||||
self.row_metas = self.get_row_metas().await;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
EditorScript::AssertRowCount(count) => {
|
|
||||||
assert_eq!(self.row_metas.len(), count);
|
|
||||||
},
|
|
||||||
EditorScript::AssertGridMetaPad => {
|
|
||||||
sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;
|
|
||||||
let mut grid_rev_manager = grid_manager
|
|
||||||
.make_grid_rev_manager(&self.grid_id, pool.clone())
|
|
||||||
.unwrap();
|
|
||||||
let grid_pad = grid_rev_manager.load::<GridPadBuilder>(None).await.unwrap();
|
|
||||||
println!("{}", grid_pad.delta_str());
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_row_metas(&self) -> Vec<Arc<RowMeta>> {
|
|
||||||
get_row_metas(&self.editor).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_row_metas(editor: &Arc<GridMetaEditor>) -> Vec<Arc<RowMeta>> {
|
|
||||||
editor
|
|
||||||
.grid_block_snapshots(None)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.pop()
|
|
||||||
.unwrap()
|
|
||||||
.row_metas
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_text_field(grid_id: &str) -> (InsertFieldParams, FieldMeta) {
|
|
||||||
let field_meta = FieldBuilder::new(RichTextTypeOptionBuilder::default())
|
|
||||||
.name("Name")
|
|
||||||
.visibility(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let cloned_field_meta = field_meta.clone();
|
|
||||||
|
|
||||||
let type_option_data = field_meta
|
|
||||||
.get_type_option_entry::<RichTextTypeOptionPB>(&field_meta.field_type)
|
|
||||||
.unwrap()
|
|
||||||
.protobuf_bytes()
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
let field = Field {
|
|
||||||
id: field_meta.id,
|
|
||||||
name: field_meta.name,
|
|
||||||
desc: field_meta.desc,
|
|
||||||
field_type: field_meta.field_type,
|
|
||||||
frozen: field_meta.frozen,
|
|
||||||
visibility: field_meta.visibility,
|
|
||||||
width: field_meta.width,
|
|
||||||
is_primary: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let params = InsertFieldParams {
|
|
||||||
grid_id: grid_id.to_owned(),
|
|
||||||
field,
|
|
||||||
type_option_data,
|
|
||||||
start_field_id: None,
|
|
||||||
};
|
|
||||||
(params, cloned_field_meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_single_select_field(grid_id: &str) -> (InsertFieldParams, FieldMeta) {
|
|
||||||
let single_select = SingleSelectTypeOptionBuilder::default()
|
|
||||||
.option(SelectOption::new("Done"))
|
|
||||||
.option(SelectOption::new("Progress"));
|
|
||||||
|
|
||||||
let field_meta = FieldBuilder::new(single_select)
|
|
||||||
.name("Name")
|
|
||||||
.visibility(true)
|
|
||||||
.build();
|
|
||||||
let cloned_field_meta = field_meta.clone();
|
|
||||||
let type_option_data = field_meta
|
|
||||||
.get_type_option_entry::<SingleSelectTypeOption>(&field_meta.field_type)
|
|
||||||
.unwrap()
|
|
||||||
.protobuf_bytes()
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
let field = Field {
|
|
||||||
id: field_meta.id,
|
|
||||||
name: field_meta.name,
|
|
||||||
desc: field_meta.desc,
|
|
||||||
field_type: field_meta.field_type,
|
|
||||||
frozen: field_meta.frozen,
|
|
||||||
visibility: field_meta.visibility,
|
|
||||||
width: field_meta.width,
|
|
||||||
is_primary: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let params = InsertFieldParams {
|
|
||||||
grid_id: grid_id.to_owned(),
|
|
||||||
field,
|
|
||||||
type_option_data,
|
|
||||||
start_field_id: None,
|
|
||||||
};
|
|
||||||
(params, cloned_field_meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_template_1_grid() -> BuildGridContext {
|
|
||||||
let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
|
|
||||||
.name("Name")
|
|
||||||
.visibility(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// Single Select
|
|
||||||
let single_select = SingleSelectTypeOptionBuilder::default()
|
|
||||||
.option(SelectOption::new("Live"))
|
|
||||||
.option(SelectOption::new("Completed"))
|
|
||||||
.option(SelectOption::new("Planned"))
|
|
||||||
.option(SelectOption::new("Paused"));
|
|
||||||
let single_select_field = FieldBuilder::new(single_select)
|
|
||||||
.name("Status")
|
|
||||||
.visibility(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// MultiSelect
|
|
||||||
let multi_select = MultiSelectTypeOptionBuilder::default()
|
|
||||||
.option(SelectOption::new("Google"))
|
|
||||||
.option(SelectOption::new("Facebook"))
|
|
||||||
.option(SelectOption::new("Twitter"));
|
|
||||||
let multi_select_field = FieldBuilder::new(multi_select)
|
|
||||||
.name("Platform")
|
|
||||||
.visibility(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// Number
|
|
||||||
let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD);
|
|
||||||
let number_field = FieldBuilder::new(number)
|
|
||||||
.name("Price")
|
|
||||||
.visibility(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// Date
|
|
||||||
let date = DateTypeOptionBuilder::default()
|
|
||||||
.date_format(DateFormat::US)
|
|
||||||
.time_format(TimeFormat::TwentyFourHour);
|
|
||||||
let date_field = FieldBuilder::new(date)
|
|
||||||
.name("Time")
|
|
||||||
.visibility(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// Checkbox
|
|
||||||
let checkbox = CheckboxTypeOptionBuilder::default();
|
|
||||||
let checkbox_field = FieldBuilder::new(checkbox)
|
|
||||||
.name("is done")
|
|
||||||
.visibility(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// URL
|
|
||||||
let url = URLTypeOptionBuilder::default();
|
|
||||||
let url_field = FieldBuilder::new(url).name("link").visibility(true).build();
|
|
||||||
|
|
||||||
GridBuilder::default()
|
|
||||||
.add_field(text_field)
|
|
||||||
.add_field(single_select_field)
|
|
||||||
.add_field(multi_select_field)
|
|
||||||
.add_field(number_field)
|
|
||||||
.add_field(date_field)
|
|
||||||
.add_field(checkbox_field)
|
|
||||||
.add_field(url_field)
|
|
||||||
.add_empty_row()
|
|
||||||
.add_empty_row()
|
|
||||||
.add_empty_row()
|
|
||||||
.build()
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
use appflowy_integrate::collab_builder::{AppFlowyCollabBuilder, CloudStorageType};
|
||||||
use appflowy_integrate::config::AppFlowyCollabConfig;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use appflowy_integrate::RocksCollabDB;
|
use appflowy_integrate::RocksCollabDB;
|
||||||
@ -50,6 +50,6 @@ pub fn db() -> Arc<RocksCollabDB> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> {
|
pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> {
|
||||||
let builder = AppFlowyCollabBuilder::new(AppFlowyCollabConfig::default());
|
let builder = AppFlowyCollabBuilder::new(CloudStorageType::Local);
|
||||||
Arc::new(builder)
|
Arc::new(builder)
|
||||||
}
|
}
|
||||||
|
17
frontend/rust-lib/flowy-folder2/src/deps.rs
Normal file
17
frontend/rust-lib/flowy-folder2/src/deps.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use appflowy_integrate::RocksCollabDB;
|
||||||
|
pub use collab_folder::core::Workspace;
|
||||||
|
use flowy_error::FlowyError;
|
||||||
|
use lib_infra::future::FutureResult;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// [FolderUser] represents the user for folder.
|
||||||
|
pub trait FolderUser: Send + Sync {
|
||||||
|
fn user_id(&self) -> Result<i64, FlowyError>;
|
||||||
|
fn token(&self) -> Result<Option<String>, FlowyError>;
|
||||||
|
fn collab_db(&self) -> Result<Arc<RocksCollabDB>, FlowyError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [FolderCloudService] represents the cloud service for folder.
|
||||||
|
pub trait FolderCloudService: Send + Sync + 'static {
|
||||||
|
fn create_workspace(&self, uid: i64, name: &str) -> FutureResult<Workspace, FlowyError>;
|
||||||
|
}
|
@ -106,7 +106,7 @@ impl WorkspaceIdPB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, ProtoBuf, Clone)]
|
#[derive(Default, ProtoBuf, Debug, Clone)]
|
||||||
pub struct WorkspaceSettingPB {
|
pub struct WorkspaceSettingPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub workspace: WorkspacePB,
|
pub workspace: WorkspacePB,
|
||||||
|
@ -3,6 +3,7 @@ use crate::manager::Folder2Manager;
|
|||||||
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
|
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
|
||||||
|
|
||||||
use lib_dispatch::prelude::*;
|
use lib_dispatch::prelude::*;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum_macros::Display;
|
use strum_macros::Display;
|
||||||
|
|
||||||
|
@ -7,7 +7,9 @@ pub mod protobuf;
|
|||||||
mod user_default;
|
mod user_default;
|
||||||
pub mod view_ext;
|
pub mod view_ext;
|
||||||
|
|
||||||
|
pub mod deps;
|
||||||
#[cfg(feature = "test_helper")]
|
#[cfg(feature = "test_helper")]
|
||||||
mod test_helper;
|
mod test_helper;
|
||||||
|
|
||||||
pub use collab_folder::core::ViewLayout;
|
pub use collab_folder::core::ViewLayout;
|
||||||
|
pub use user_default::gen_workspace_id;
|
||||||
|
@ -3,7 +3,7 @@ use std::ops::Deref;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
use appflowy_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use appflowy_integrate::RocksCollabDB;
|
|
||||||
use collab_folder::core::{
|
use collab_folder::core::{
|
||||||
Folder as InnerFolder, FolderContext, TrashChange, TrashChangeReceiver, TrashInfo, TrashRecord,
|
Folder as InnerFolder, FolderContext, TrashChange, TrashChangeReceiver, TrashInfo, TrashRecord,
|
||||||
View, ViewChange, ViewChangeReceiver, ViewLayout, Workspace,
|
View, ViewChange, ViewChangeReceiver, ViewLayout, Workspace,
|
||||||
@ -11,6 +11,7 @@ use collab_folder::core::{
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use tracing::{event, Level};
|
use tracing::{event, Level};
|
||||||
|
|
||||||
|
use crate::deps::{FolderCloudService, FolderUser};
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use lib_infra::util::timestamp;
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
@ -22,22 +23,17 @@ use crate::notification::{
|
|||||||
send_notification, send_workspace_notification, send_workspace_setting_notification,
|
send_notification, send_workspace_notification, send_workspace_setting_notification,
|
||||||
FolderNotification,
|
FolderNotification,
|
||||||
};
|
};
|
||||||
use crate::user_default::{gen_workspace_id, DefaultFolderBuilder};
|
use crate::user_default::DefaultFolderBuilder;
|
||||||
use crate::view_ext::{
|
use crate::view_ext::{
|
||||||
gen_view_id, view_from_create_view_params, ViewDataProcessor, ViewDataProcessorMap,
|
gen_view_id, view_from_create_view_params, ViewDataProcessor, ViewDataProcessorMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait FolderUser: Send + Sync {
|
|
||||||
fn user_id(&self) -> Result<i64, FlowyError>;
|
|
||||||
fn token(&self) -> Result<Option<String>, FlowyError>;
|
|
||||||
fn collab_db(&self) -> Result<Arc<RocksCollabDB>, FlowyError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Folder2Manager {
|
pub struct Folder2Manager {
|
||||||
folder: Folder,
|
folder: Folder,
|
||||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||||
user: Arc<dyn FolderUser>,
|
user: Arc<dyn FolderUser>,
|
||||||
view_processors: ViewDataProcessorMap,
|
view_processors: ViewDataProcessorMap,
|
||||||
|
cloud_service: Arc<dyn FolderCloudService>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Folder2Manager {}
|
unsafe impl Send for Folder2Manager {}
|
||||||
@ -48,6 +44,7 @@ impl Folder2Manager {
|
|||||||
user: Arc<dyn FolderUser>,
|
user: Arc<dyn FolderUser>,
|
||||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||||
view_processors: ViewDataProcessorMap,
|
view_processors: ViewDataProcessorMap,
|
||||||
|
cloud_service: Arc<dyn FolderCloudService>,
|
||||||
) -> FlowyResult<Self> {
|
) -> FlowyResult<Self> {
|
||||||
let folder = Folder::default();
|
let folder = Folder::default();
|
||||||
let manager = Self {
|
let manager = Self {
|
||||||
@ -55,6 +52,7 @@ impl Folder2Manager {
|
|||||||
folder,
|
folder,
|
||||||
collab_builder,
|
collab_builder,
|
||||||
view_processors,
|
view_processors,
|
||||||
|
cloud_service,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(manager)
|
Ok(manager)
|
||||||
@ -90,7 +88,7 @@ impl Folder2Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Called immediately after the application launched fi the user already sign in/sign up.
|
/// Called immediately after the application launched fi the user already sign in/sign up.
|
||||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||||
pub async fn initialize(&self, uid: i64, workspace_id: &str) -> FlowyResult<()> {
|
pub async fn initialize(&self, uid: i64, workspace_id: &str) -> FlowyResult<()> {
|
||||||
if let Ok(collab_db) = self.user.collab_db() {
|
if let Ok(collab_db) = self.user.collab_db() {
|
||||||
let collab = self.collab_builder.build(uid, workspace_id, collab_db);
|
let collab = self.collab_builder.build(uid, workspace_id, collab_db);
|
||||||
@ -139,13 +137,10 @@ impl Folder2Manager {
|
|||||||
pub async fn clear(&self, _user_id: i64) {}
|
pub async fn clear(&self, _user_id: i64) {}
|
||||||
|
|
||||||
pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> FlowyResult<Workspace> {
|
pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> FlowyResult<Workspace> {
|
||||||
let workspace = Workspace {
|
let workspace = self
|
||||||
id: gen_workspace_id(),
|
.cloud_service
|
||||||
name: params.name,
|
.create_workspace(self.user.user_id()?, ¶ms.name)
|
||||||
belongings: Default::default(),
|
.await?;
|
||||||
created_at: timestamp(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.with_folder((), |folder| {
|
self.with_folder((), |folder| {
|
||||||
folder.workspaces.create_workspace(workspace.clone());
|
folder.workspaces.create_workspace(workspace.clone());
|
||||||
folder.set_current_workspace(&workspace.id);
|
folder.set_current_workspace(&workspace.id);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::script::{invalid_workspace_name_test_case, FolderScript::*, FolderTest};
|
use crate::script::{invalid_workspace_name_test_case, FolderScript::*, FolderTest};
|
||||||
use collab_folder::core::ViewLayout;
|
use collab_folder::core::ViewLayout;
|
||||||
use flowy_folder2::entities::CreateWorkspacePayloadPB;
|
use flowy_folder2::entities::CreateWorkspacePayloadPB;
|
||||||
use flowy_test::{event_builder::*, FlowySDKTest};
|
use flowy_test::{event_builder::*, FlowyCoreTest};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn workspace_read_all() {
|
async fn workspace_read_all() {
|
||||||
@ -63,18 +63,19 @@ async fn workspace_create_with_apps() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn workspace_create_with_invalid_name() {
|
async fn workspace_create_with_invalid_name() {
|
||||||
for (name, code) in invalid_workspace_name_test_case() {
|
for (name, code) in invalid_workspace_name_test_case() {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
let request = CreateWorkspacePayloadPB {
|
let request = CreateWorkspacePayloadPB {
|
||||||
name,
|
name,
|
||||||
desc: "".to_owned(),
|
desc: "".to_owned(),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Folder2EventBuilder::new(sdk)
|
EventBuilder::new(sdk)
|
||||||
.event(flowy_folder2::event_map::FolderEvent::CreateWorkspace)
|
.event(flowy_folder2::event_map::FolderEvent::CreateWorkspace)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.error()
|
.error()
|
||||||
|
.unwrap()
|
||||||
.code,
|
.code,
|
||||||
code.value()
|
code.value()
|
||||||
)
|
)
|
||||||
|
@ -2,8 +2,8 @@ use collab_folder::core::ViewLayout;
|
|||||||
use flowy_error::ErrorCode;
|
use flowy_error::ErrorCode;
|
||||||
use flowy_folder2::entities::*;
|
use flowy_folder2::entities::*;
|
||||||
use flowy_folder2::event_map::FolderEvent::*;
|
use flowy_folder2::event_map::FolderEvent::*;
|
||||||
use flowy_test::event_builder::Folder2EventBuilder;
|
use flowy_test::event_builder::EventBuilder;
|
||||||
use flowy_test::FlowySDKTest;
|
use flowy_test::FlowyCoreTest;
|
||||||
|
|
||||||
pub enum FolderScript {
|
pub enum FolderScript {
|
||||||
// Workspace
|
// Workspace
|
||||||
@ -51,7 +51,7 @@ pub enum FolderScript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct FolderTest {
|
pub struct FolderTest {
|
||||||
pub sdk: FlowySDKTest,
|
pub sdk: FlowyCoreTest,
|
||||||
pub all_workspace: Vec<WorkspacePB>,
|
pub all_workspace: Vec<WorkspacePB>,
|
||||||
pub workspace: WorkspacePB,
|
pub workspace: WorkspacePB,
|
||||||
pub parent_view: ViewPB,
|
pub parent_view: ViewPB,
|
||||||
@ -61,7 +61,7 @@ pub struct FolderTest {
|
|||||||
|
|
||||||
impl FolderTest {
|
impl FolderTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
let _ = sdk.init_user().await;
|
let _ = sdk.init_user().await;
|
||||||
let workspace = create_workspace(&sdk, "FolderWorkspace", "Folder test workspace").await;
|
let workspace = create_workspace(&sdk, "FolderWorkspace", "Folder test workspace").await;
|
||||||
let parent_view = create_app(&sdk, &workspace.id, "Folder App", "Folder test app").await;
|
let parent_view = create_app(&sdk, &workspace.id, "Folder App", "Folder test app").await;
|
||||||
@ -170,13 +170,13 @@ pub fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_workspace(sdk: &FlowySDKTest, name: &str, desc: &str) -> WorkspacePB {
|
pub async fn create_workspace(sdk: &FlowyCoreTest, name: &str, desc: &str) -> WorkspacePB {
|
||||||
let request = CreateWorkspacePayloadPB {
|
let request = CreateWorkspacePayloadPB {
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
desc: desc.to_owned(),
|
desc: desc.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(CreateWorkspace)
|
.event(CreateWorkspace)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -184,11 +184,11 @@ pub async fn create_workspace(sdk: &FlowySDKTest, name: &str, desc: &str) -> Wor
|
|||||||
.parse::<WorkspacePB>()
|
.parse::<WorkspacePB>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_workspace(sdk: &FlowySDKTest, workspace_id: Option<String>) -> Vec<WorkspacePB> {
|
pub async fn read_workspace(sdk: &FlowyCoreTest, workspace_id: Option<String>) -> Vec<WorkspacePB> {
|
||||||
let request = WorkspaceIdPB {
|
let request = WorkspaceIdPB {
|
||||||
value: workspace_id,
|
value: workspace_id,
|
||||||
};
|
};
|
||||||
let repeated_workspace = Folder2EventBuilder::new(sdk.clone())
|
let repeated_workspace = EventBuilder::new(sdk.clone())
|
||||||
.event(ReadWorkspaces)
|
.event(ReadWorkspaces)
|
||||||
.payload(request.clone())
|
.payload(request.clone())
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -210,7 +210,7 @@ pub async fn read_workspace(sdk: &FlowySDKTest, workspace_id: Option<String>) ->
|
|||||||
workspaces
|
workspaces
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_app(sdk: &FlowySDKTest, workspace_id: &str, name: &str, desc: &str) -> ViewPB {
|
pub async fn create_app(sdk: &FlowyCoreTest, workspace_id: &str, name: &str, desc: &str) -> ViewPB {
|
||||||
let create_view_request = CreateViewPayloadPB {
|
let create_view_request = CreateViewPayloadPB {
|
||||||
belong_to_id: workspace_id.to_owned(),
|
belong_to_id: workspace_id.to_owned(),
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
@ -221,7 +221,7 @@ pub async fn create_app(sdk: &FlowySDKTest, workspace_id: &str, name: &str, desc
|
|||||||
ext: Default::default(),
|
ext: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(CreateView)
|
.event(CreateView)
|
||||||
.payload(create_view_request)
|
.payload(create_view_request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -230,7 +230,7 @@ pub async fn create_app(sdk: &FlowySDKTest, workspace_id: &str, name: &str, desc
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_view(
|
pub async fn create_view(
|
||||||
sdk: &FlowySDKTest,
|
sdk: &FlowyCoreTest,
|
||||||
app_id: &str,
|
app_id: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
desc: &str,
|
desc: &str,
|
||||||
@ -245,7 +245,7 @@ pub async fn create_view(
|
|||||||
initial_data: vec![],
|
initial_data: vec![],
|
||||||
ext: Default::default(),
|
ext: Default::default(),
|
||||||
};
|
};
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(CreateView)
|
.event(CreateView)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -253,9 +253,9 @@ pub async fn create_view(
|
|||||||
.parse::<ViewPB>()
|
.parse::<ViewPB>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_view(sdk: &FlowySDKTest, view_id: &str) -> ViewPB {
|
pub async fn read_view(sdk: &FlowyCoreTest, view_id: &str) -> ViewPB {
|
||||||
let view_id: ViewIdPB = view_id.into();
|
let view_id: ViewIdPB = view_id.into();
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(ReadView)
|
.event(ReadView)
|
||||||
.payload(view_id)
|
.payload(view_id)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -264,7 +264,7 @@ pub async fn read_view(sdk: &FlowySDKTest, view_id: &str) -> ViewPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_view(
|
pub async fn update_view(
|
||||||
sdk: &FlowySDKTest,
|
sdk: &FlowyCoreTest,
|
||||||
view_id: &str,
|
view_id: &str,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
desc: Option<String>,
|
desc: Option<String>,
|
||||||
@ -275,54 +275,54 @@ pub async fn update_view(
|
|||||||
desc,
|
desc,
|
||||||
thumbnail: None,
|
thumbnail: None,
|
||||||
};
|
};
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(UpdateView)
|
.event(UpdateView)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_view(sdk: &FlowySDKTest, view_ids: Vec<String>) {
|
pub async fn delete_view(sdk: &FlowyCoreTest, view_ids: Vec<String>) {
|
||||||
let request = RepeatedViewIdPB { items: view_ids };
|
let request = RepeatedViewIdPB { items: view_ids };
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(DeleteView)
|
.event(DeleteView)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrashPB {
|
pub async fn read_trash(sdk: &FlowyCoreTest) -> RepeatedTrashPB {
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(ReadTrash)
|
.event(ReadTrash)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.parse::<RepeatedTrashPB>()
|
.parse::<RepeatedTrashPB>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn restore_app_from_trash(sdk: &FlowySDKTest, app_id: &str) {
|
pub async fn restore_app_from_trash(sdk: &FlowyCoreTest, app_id: &str) {
|
||||||
let id = TrashIdPB {
|
let id = TrashIdPB {
|
||||||
id: app_id.to_owned(),
|
id: app_id.to_owned(),
|
||||||
};
|
};
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(PutbackTrash)
|
.event(PutbackTrash)
|
||||||
.payload(id)
|
.payload(id)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn restore_view_from_trash(sdk: &FlowySDKTest, view_id: &str) {
|
pub async fn restore_view_from_trash(sdk: &FlowyCoreTest, view_id: &str) {
|
||||||
let id = TrashIdPB {
|
let id = TrashIdPB {
|
||||||
id: view_id.to_owned(),
|
id: view_id.to_owned(),
|
||||||
};
|
};
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(PutbackTrash)
|
.event(PutbackTrash)
|
||||||
.payload(id)
|
.payload(id)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_all_trash(sdk: &FlowySDKTest) {
|
pub async fn delete_all_trash(sdk: &FlowyCoreTest) {
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
EventBuilder::new(sdk.clone())
|
||||||
.event(DeleteAllTrash)
|
.event(DeleteAllTrash)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await;
|
.await;
|
||||||
|
@ -24,11 +24,12 @@ postgrest = "1.0"
|
|||||||
tokio-retry = "0.3"
|
tokio-retry = "0.3"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
uuid = { version = "1.3.3", features = ["v4"] }
|
uuid = { version = "1.3.3", features = ["v4"] }
|
||||||
|
chrono = "0.4.24"
|
||||||
|
|
||||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||||
flowy-user = { path = "../flowy-user" }
|
flowy-user = { path = "../flowy-user" }
|
||||||
|
flowy-folder2 = { path = "../flowy-folder2" }
|
||||||
flowy-error = { path = "../flowy-error" }
|
flowy-error = { path = "../flowy-error" }
|
||||||
flowy-config = { path = "../flowy-config" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
uuid = { version = "1.3.3", features = ["v4"] }
|
uuid = { version = "1.3.3", features = ["v4"] }
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use flowy_folder2::deps::FolderCloudService;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use flowy_user::event_map::UserAuthService;
|
use flowy_user::event_map::UserAuthService;
|
||||||
@ -8,6 +9,21 @@ mod response;
|
|||||||
pub mod self_host;
|
pub mod self_host;
|
||||||
pub mod supabase;
|
pub mod supabase;
|
||||||
|
|
||||||
|
/// In order to run this the supabase test, you need to create a .env file in the root directory of this project
|
||||||
|
/// and add the following environment variables:
|
||||||
|
/// - SUPABASE_URL
|
||||||
|
/// - SUPABASE_ANON_KEY
|
||||||
|
/// - SUPABASE_KEY
|
||||||
|
/// - SUPABASE_JWT_SECRET
|
||||||
|
///
|
||||||
|
/// the .env file should look like this:
|
||||||
|
/// SUPABASE_URL=https://<your-supabase-url>.supabase.co
|
||||||
|
/// SUPABASE_ANON_KEY=<your-supabase-anon-key>
|
||||||
|
/// SUPABASE_KEY=<your-supabase-key>
|
||||||
|
/// SUPABASE_JWT_SECRET=<your-supabase-jwt-secret>
|
||||||
|
///
|
||||||
|
|
||||||
pub trait AppFlowyServer: Send + Sync + 'static {
|
pub trait AppFlowyServer: Send + Sync + 'static {
|
||||||
fn user_service(&self) -> Arc<dyn UserAuthService>;
|
fn user_service(&self) -> Arc<dyn UserAuthService>;
|
||||||
|
fn folder_service(&self) -> Arc<dyn FolderCloudService>;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
use flowy_error::FlowyError;
|
||||||
|
use flowy_folder2::deps::{FolderCloudService, Workspace};
|
||||||
|
use flowy_folder2::gen_workspace_id;
|
||||||
|
use lib_infra::future::FutureResult;
|
||||||
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
|
pub(crate) struct LocalServerFolderCloudServiceImpl();
|
||||||
|
|
||||||
|
impl FolderCloudService for LocalServerFolderCloudServiceImpl {
|
||||||
|
fn create_workspace(&self, _uid: i64, name: &str) -> FutureResult<Workspace, FlowyError> {
|
||||||
|
let name = name.to_string();
|
||||||
|
FutureResult::new(async move {
|
||||||
|
Ok(Workspace {
|
||||||
|
id: gen_workspace_id(),
|
||||||
|
name: name.to_string(),
|
||||||
|
belongings: Default::default(),
|
||||||
|
created_at: timestamp(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
mod folder;
|
||||||
|
mod user;
|
||||||
|
|
||||||
|
pub(crate) use folder::*;
|
||||||
|
pub(crate) use user::*;
|
@ -1,5 +1,5 @@
|
|||||||
pub use server::*;
|
pub use server::*;
|
||||||
|
|
||||||
|
pub mod impls;
|
||||||
mod server;
|
mod server;
|
||||||
pub(crate) mod uid;
|
pub(crate) mod uid;
|
||||||
mod user;
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use flowy_folder2::deps::FolderCloudService;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use flowy_user::event_map::UserAuthService;
|
use flowy_user::event_map::UserAuthService;
|
||||||
|
|
||||||
use crate::local_server::user::LocalServerUserAuthServiceImpl;
|
use crate::local_server::impls::{
|
||||||
|
LocalServerFolderCloudServiceImpl, LocalServerUserAuthServiceImpl,
|
||||||
|
};
|
||||||
use crate::AppFlowyServer;
|
use crate::AppFlowyServer;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -31,4 +34,8 @@ impl AppFlowyServer for LocalServer {
|
|||||||
fn user_service(&self) -> Arc<dyn UserAuthService> {
|
fn user_service(&self) -> Arc<dyn UserAuthService> {
|
||||||
Arc::new(LocalServerUserAuthServiceImpl())
|
Arc::new(LocalServerUserAuthServiceImpl())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn folder_service(&self) -> Arc<dyn FolderCloudService> {
|
||||||
|
Arc::new(LocalServerFolderCloudServiceImpl())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
21
frontend/rust-lib/flowy-server/src/self_host/impls/folder.rs
Normal file
21
frontend/rust-lib/flowy-server/src/self_host/impls/folder.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use flowy_error::FlowyError;
|
||||||
|
use flowy_folder2::deps::{FolderCloudService, Workspace};
|
||||||
|
use flowy_folder2::gen_workspace_id;
|
||||||
|
use lib_infra::future::FutureResult;
|
||||||
|
use lib_infra::util::timestamp;
|
||||||
|
|
||||||
|
pub(crate) struct SelfHostedServerFolderCloudServiceImpl();
|
||||||
|
|
||||||
|
impl FolderCloudService for SelfHostedServerFolderCloudServiceImpl {
|
||||||
|
fn create_workspace(&self, _uid: i64, name: &str) -> FutureResult<Workspace, FlowyError> {
|
||||||
|
let name = name.to_string();
|
||||||
|
FutureResult::new(async move {
|
||||||
|
Ok(Workspace {
|
||||||
|
id: gen_workspace_id(),
|
||||||
|
name: name.to_string(),
|
||||||
|
belongings: Default::default(),
|
||||||
|
created_at: timestamp(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
mod folder;
|
||||||
|
mod user;
|
||||||
|
|
||||||
|
pub(crate) use folder::*;
|
||||||
|
pub(crate) use user::*;
|
@ -1,6 +1,5 @@
|
|||||||
pub use server::*;
|
pub use server::*;
|
||||||
pub use user::*;
|
|
||||||
|
|
||||||
pub mod configuration;
|
pub mod configuration;
|
||||||
|
pub mod impls;
|
||||||
mod server;
|
mod server;
|
||||||
mod user;
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
use flowy_folder2::deps::FolderCloudService;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use flowy_user::event_map::UserAuthService;
|
use flowy_user::event_map::UserAuthService;
|
||||||
|
|
||||||
use crate::self_host::configuration::SelfHostedConfiguration;
|
use crate::self_host::configuration::SelfHostedConfiguration;
|
||||||
use crate::self_host::SelfHostedUserAuthServiceImpl;
|
use crate::self_host::impls::{
|
||||||
|
SelfHostedServerFolderCloudServiceImpl, SelfHostedUserAuthServiceImpl,
|
||||||
|
};
|
||||||
use crate::AppFlowyServer;
|
use crate::AppFlowyServer;
|
||||||
|
|
||||||
pub struct SelfHostServer {
|
pub struct SelfHostServer {
|
||||||
@ -20,4 +23,8 @@ impl AppFlowyServer for SelfHostServer {
|
|||||||
fn user_service(&self) -> Arc<dyn UserAuthService> {
|
fn user_service(&self) -> Arc<dyn UserAuthService> {
|
||||||
Arc::new(SelfHostedUserAuthServiceImpl::new(self.config.clone()))
|
Arc::new(SelfHostedUserAuthServiceImpl::new(self.config.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn folder_service(&self) -> Arc<dyn FolderCloudService> {
|
||||||
|
Arc::new(SelfHostedServerFolderCloudServiceImpl())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
54
frontend/rust-lib/flowy-server/src/supabase/impls/folder.rs
Normal file
54
frontend/rust-lib/flowy-server/src/supabase/impls/folder.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use crate::supabase::request::create_workspace_with_uid;
|
||||||
|
use flowy_error::FlowyError;
|
||||||
|
use flowy_folder2::deps::{FolderCloudService, Workspace};
|
||||||
|
use lib_infra::future::FutureResult;
|
||||||
|
use postgrest::Postgrest;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub(crate) const WORKSPACE_TABLE: &str = "af_workspace";
|
||||||
|
pub(crate) const WORKSPACE_NAME_COLUMN: &str = "workspace_name";
|
||||||
|
pub(crate) struct SupabaseFolderCloudServiceImpl {
|
||||||
|
postgrest: Arc<Postgrest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderCloudService for SupabaseFolderCloudServiceImpl {
|
||||||
|
fn create_workspace(&self, uid: i64, name: &str) -> FutureResult<Workspace, FlowyError> {
|
||||||
|
let name = name.to_string();
|
||||||
|
let postgrest = self.postgrest.clone();
|
||||||
|
FutureResult::new(async move { create_workspace_with_uid(postgrest, uid, &name).await })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::supabase::request::{
|
||||||
|
create_user_with_uuid, create_workspace_with_uid, get_user_workspace_with_uid,
|
||||||
|
};
|
||||||
|
use crate::supabase::{SupabaseConfiguration, SupabaseServer};
|
||||||
|
use dotenv::dotenv;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_user_workspace() {
|
||||||
|
dotenv().ok();
|
||||||
|
if let Ok(config) = SupabaseConfiguration::from_env() {
|
||||||
|
let server = Arc::new(SupabaseServer::new(config));
|
||||||
|
let uuid = uuid::Uuid::new_v4();
|
||||||
|
let uid = create_user_with_uuid(server.postgres.clone(), uuid.to_string())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.uid;
|
||||||
|
|
||||||
|
create_workspace_with_uid(server.postgres.clone(), uid, "test")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let workspaces = get_user_workspace_with_uid(server.postgres.clone(), uid)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(workspaces.len(), 2);
|
||||||
|
assert_eq!(workspaces[0].name, "My workspace");
|
||||||
|
assert_eq!(workspaces[1].name, "test");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
frontend/rust-lib/flowy-server/src/supabase/impls/mod.rs
Normal file
5
frontend/rust-lib/flowy-server/src/supabase/impls/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod folder;
|
||||||
|
mod user;
|
||||||
|
|
||||||
|
pub(crate) use folder::*;
|
||||||
|
pub(crate) use user::*;
|
@ -12,7 +12,8 @@ use crate::supabase::request::*;
|
|||||||
|
|
||||||
pub(crate) const USER_TABLE: &str = "af_user";
|
pub(crate) const USER_TABLE: &str = "af_user";
|
||||||
pub(crate) const USER_PROFILE_TABLE: &str = "af_user_profile";
|
pub(crate) const USER_PROFILE_TABLE: &str = "af_user_profile";
|
||||||
pub(crate) const USER_WORKSPACE_TABLE: &str = "af_user_workspace_view";
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const USER_WORKSPACE_TABLE: &str = "af_workspace";
|
||||||
pub(crate) struct PostgrestUserAuthServiceImpl {
|
pub(crate) struct PostgrestUserAuthServiceImpl {
|
||||||
postgrest: Arc<Postgrest>,
|
postgrest: Arc<Postgrest>,
|
||||||
}
|
}
|
||||||
@ -41,14 +42,12 @@ impl UserAuthService for PostgrestUserAuthServiceImpl {
|
|||||||
let postgrest = self.postgrest.clone();
|
let postgrest = self.postgrest.clone();
|
||||||
FutureResult::new(async move {
|
FutureResult::new(async move {
|
||||||
let uuid = uuid_from_box_any(params)?;
|
let uuid = uuid_from_box_any(params)?;
|
||||||
match get_user_workspace_with_uuid(postgrest, uuid).await? {
|
let user_profile = get_user_profile(postgrest, GetUserProfileParams::Uuid(uuid)).await?;
|
||||||
None => Err(FlowyError::user_not_exist()),
|
Ok(SignInResponse {
|
||||||
Some(user) => Ok(SignInResponse {
|
user_id: user_profile.uid,
|
||||||
user_id: user.uid,
|
workspace_id: user_profile.workspace_id,
|
||||||
workspace_id: user.workspace_id,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
})
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,18 +75,19 @@ impl UserAuthService for PostgrestUserAuthServiceImpl {
|
|||||||
) -> FutureResult<Option<UserProfile>, FlowyError> {
|
) -> FutureResult<Option<UserProfile>, FlowyError> {
|
||||||
let postgrest = self.postgrest.clone();
|
let postgrest = self.postgrest.clone();
|
||||||
FutureResult::new(async move {
|
FutureResult::new(async move {
|
||||||
let profile = get_user_workspace_with_uid(postgrest, uid)
|
let user_profile_resp = get_user_profile(postgrest, GetUserProfileParams::Uid(uid)).await?;
|
||||||
.await?
|
|
||||||
.map(|user_workspace| UserProfile {
|
let profile = UserProfile {
|
||||||
id: user_workspace.uid,
|
id: user_profile_resp.uid,
|
||||||
email: "".to_string(),
|
email: user_profile_resp.email,
|
||||||
name: user_workspace.name,
|
name: user_profile_resp.name,
|
||||||
token: "".to_string(),
|
token: "".to_string(),
|
||||||
icon_url: "".to_string(),
|
icon_url: "".to_string(),
|
||||||
openai_key: "".to_string(),
|
openai_key: "".to_string(),
|
||||||
workspace_id: user_workspace.workspace_id,
|
workspace_id: user_profile_resp.workspace_id,
|
||||||
});
|
};
|
||||||
Ok(profile)
|
|
||||||
|
Ok(Some(profile))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,8 +100,10 @@ mod tests {
|
|||||||
|
|
||||||
use flowy_user::entities::UpdateUserProfileParams;
|
use flowy_user::entities::UpdateUserProfileParams;
|
||||||
|
|
||||||
use crate::supabase::request::{get_user_profile, get_user_workspace_with_uid};
|
use crate::supabase::request::{
|
||||||
use crate::supabase::user::{create_user_with_uuid, get_user_id_with_uuid, update_user_profile};
|
create_user_with_uuid, get_user_id_with_uuid, get_user_profile, get_user_workspace_with_uid,
|
||||||
|
update_user_profile, GetUserProfileParams,
|
||||||
|
};
|
||||||
use crate::supabase::{SupabaseConfiguration, SupabaseServer};
|
use crate::supabase::{SupabaseConfiguration, SupabaseServer};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -151,17 +153,15 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
println!("result: {:?}", result);
|
println!("result: {:?}", result);
|
||||||
|
|
||||||
let result = get_user_profile(server.postgres.clone(), uid)
|
let result = get_user_profile(server.postgres.clone(), GetUserProfileParams::Uid(uid))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(result.name, "nathan".to_string());
|
assert_eq!(result.name, "nathan".to_string());
|
||||||
|
|
||||||
let result = get_user_workspace_with_uid(server.postgres.clone(), uid)
|
let result = get_user_workspace_with_uid(server.postgres.clone(), uid)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(!result.workspace_id.is_empty());
|
assert!(!result.is_empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
pub use server::*;
|
pub use server::*;
|
||||||
|
|
||||||
|
pub mod impls;
|
||||||
mod request;
|
mod request;
|
||||||
mod response;
|
mod response;
|
||||||
mod retry;
|
mod retry;
|
||||||
mod server;
|
mod server;
|
||||||
pub mod user;
|
|
||||||
|
@ -5,13 +5,16 @@ use postgrest::Postgrest;
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use flowy_error::{ErrorCode, FlowyError};
|
use flowy_error::{ErrorCode, FlowyError};
|
||||||
|
use flowy_folder2::deps::Workspace;
|
||||||
use flowy_user::entities::UpdateUserProfileParams;
|
use flowy_user::entities::UpdateUserProfileParams;
|
||||||
use lib_infra::box_any::BoxAny;
|
use lib_infra::box_any::BoxAny;
|
||||||
|
|
||||||
use crate::supabase::response::{
|
use crate::supabase::impls::{
|
||||||
InsertResponse, PostgrestError, UserProfile, UserProfileList, UserWorkspace, UserWorkspaceList,
|
USER_PROFILE_TABLE, USER_TABLE, USER_WORKSPACE_TABLE, WORKSPACE_NAME_COLUMN, WORKSPACE_TABLE,
|
||||||
|
};
|
||||||
|
use crate::supabase::response::{
|
||||||
|
InsertResponse, PostgrestError, UserProfileResponse, UserProfileResponseList, UserWorkspaceList,
|
||||||
};
|
};
|
||||||
use crate::supabase::user::{USER_PROFILE_TABLE, USER_TABLE, USER_WORKSPACE_TABLE};
|
|
||||||
|
|
||||||
const USER_ID: &str = "uid";
|
const USER_ID: &str = "uid";
|
||||||
const USER_UUID: &str = "uuid";
|
const USER_UUID: &str = "uuid";
|
||||||
@ -19,13 +22,15 @@ const USER_UUID: &str = "uuid";
|
|||||||
pub(crate) async fn create_user_with_uuid(
|
pub(crate) async fn create_user_with_uuid(
|
||||||
postgrest: Arc<Postgrest>,
|
postgrest: Arc<Postgrest>,
|
||||||
uuid: String,
|
uuid: String,
|
||||||
) -> Result<UserWorkspace, FlowyError> {
|
) -> Result<UserProfileResponse, FlowyError> {
|
||||||
let insert = format!("{{\"{}\": \"{}\"}}", USER_UUID, &uuid);
|
let mut insert = serde_json::Map::new();
|
||||||
|
insert.insert(USER_UUID.to_string(), json!(&uuid));
|
||||||
|
let insert_query = serde_json::to_string(&insert).unwrap();
|
||||||
|
|
||||||
// Create a new user with uuid.
|
// Create a new user with uuid.
|
||||||
let resp = postgrest
|
let resp = postgrest
|
||||||
.from(USER_TABLE)
|
.from(USER_TABLE)
|
||||||
.insert(insert)
|
.insert(insert_query)
|
||||||
.execute()
|
.execute()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| FlowyError::new(ErrorCode::HttpError, e))?;
|
.map_err(|e| FlowyError::new(ErrorCode::HttpError, e))?;
|
||||||
@ -44,13 +49,7 @@ pub(crate) async fn create_user_with_uuid(
|
|||||||
.map_err(|e| FlowyError::serde().context(e))?
|
.map_err(|e| FlowyError::serde().context(e))?
|
||||||
.first_or_error()?;
|
.first_or_error()?;
|
||||||
|
|
||||||
match get_user_workspace_with_uid(postgrest, record.uid).await {
|
get_user_profile(postgrest, GetUserProfileParams::Uid(record.uid)).await
|
||||||
Ok(Some(user)) => Ok(user),
|
|
||||||
_ => Err(FlowyError::new(
|
|
||||||
ErrorCode::Internal,
|
|
||||||
"Failed to get user workspace",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let err = serde_json::from_str::<PostgrestError>(&content)
|
let err = serde_json::from_str::<PostgrestError>(&content)
|
||||||
.map_err(|e| FlowyError::serde().context(e))?;
|
.map_err(|e| FlowyError::serde().context(e))?;
|
||||||
@ -58,8 +57,8 @@ pub(crate) async fn create_user_with_uuid(
|
|||||||
// If there is a unique violation, try to get the user id with uuid. At this point, the user
|
// If there is a unique violation, try to get the user id with uuid. At this point, the user
|
||||||
// should exist.
|
// should exist.
|
||||||
if err.is_unique_violation() {
|
if err.is_unique_violation() {
|
||||||
match get_user_workspace_with_uuid(postgrest, uuid).await {
|
match get_user_profile(postgrest, GetUserProfileParams::Uuid(uuid)).await {
|
||||||
Ok(Some(user)) => Ok(user),
|
Ok(user) => Ok(user),
|
||||||
_ => Err(FlowyError::new(
|
_ => Err(FlowyError::new(
|
||||||
ErrorCode::Internal,
|
ErrorCode::Internal,
|
||||||
"Failed to get user workspace",
|
"Failed to get user workspace",
|
||||||
@ -112,14 +111,21 @@ pub(crate) fn uuid_from_box_any(any: BoxAny) -> Result<String, FlowyError> {
|
|||||||
Ok(uuid.to_string())
|
Ok(uuid.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub enum GetUserProfileParams {
|
||||||
|
Uid(i64),
|
||||||
|
Uuid(String),
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_user_profile(
|
pub(crate) async fn get_user_profile(
|
||||||
postgrest: Arc<Postgrest>,
|
postgrest: Arc<Postgrest>,
|
||||||
uid: i64,
|
params: GetUserProfileParams,
|
||||||
) -> Result<Option<UserProfile>, FlowyError> {
|
) -> Result<UserProfileResponse, FlowyError> {
|
||||||
let resp = postgrest
|
let mut builder = postgrest.from(USER_PROFILE_TABLE);
|
||||||
.from(USER_PROFILE_TABLE)
|
match params {
|
||||||
.eq(USER_ID, uid.to_string())
|
GetUserProfileParams::Uid(uid) => builder = builder.eq(USER_ID, uid.to_string()),
|
||||||
|
GetUserProfileParams::Uuid(uuid) => builder = builder.eq(USER_UUID, uuid),
|
||||||
|
}
|
||||||
|
let resp = builder
|
||||||
.select("*")
|
.select("*")
|
||||||
.execute()
|
.execute()
|
||||||
.await
|
.await
|
||||||
@ -129,19 +135,35 @@ pub(crate) async fn get_user_profile(
|
|||||||
.text()
|
.text()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| FlowyError::new(ErrorCode::UnexpectedEmpty, e))?;
|
.map_err(|e| FlowyError::new(ErrorCode::UnexpectedEmpty, e))?;
|
||||||
let resp = serde_json::from_str::<UserProfileList>(&content)
|
let mut user_profiles =
|
||||||
.map_err(|_e| FlowyError::new(ErrorCode::Serde, "Deserialize UserProfileList failed"))?;
|
serde_json::from_str::<UserProfileResponseList>(&content).map_err(|_e| {
|
||||||
Ok(resp.0.first().cloned())
|
FlowyError::new(
|
||||||
|
ErrorCode::Serde,
|
||||||
|
"Deserialize UserProfileResponseList failed",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
if user_profiles.0.is_empty() {
|
||||||
|
return Err(FlowyError::new(
|
||||||
|
ErrorCode::Internal,
|
||||||
|
"Failed to get user profile",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(user_profiles.0.remove(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_user_workspace_with_uuid(
|
pub(crate) async fn create_workspace_with_uid(
|
||||||
postgrest: Arc<Postgrest>,
|
postgrest: Arc<Postgrest>,
|
||||||
uuid: String,
|
uid: i64,
|
||||||
) -> Result<Option<UserWorkspace>, FlowyError> {
|
name: &str,
|
||||||
|
) -> Result<Workspace, FlowyError> {
|
||||||
|
let mut insert = serde_json::Map::new();
|
||||||
|
insert.insert(USER_ID.to_string(), json!(uid));
|
||||||
|
insert.insert(WORKSPACE_NAME_COLUMN.to_string(), json!(name));
|
||||||
|
let insert_query = serde_json::to_string(&insert).unwrap();
|
||||||
|
|
||||||
let resp = postgrest
|
let resp = postgrest
|
||||||
.from(USER_WORKSPACE_TABLE)
|
.from(WORKSPACE_TABLE)
|
||||||
.eq(USER_UUID, uuid)
|
.insert(insert_query)
|
||||||
.select("*")
|
|
||||||
.execute()
|
.execute()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| FlowyError::new(ErrorCode::HttpError, e))?;
|
.map_err(|e| FlowyError::new(ErrorCode::HttpError, e))?;
|
||||||
@ -150,15 +172,31 @@ pub(crate) async fn get_user_workspace_with_uuid(
|
|||||||
.text()
|
.text()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| FlowyError::new(ErrorCode::UnexpectedEmpty, e))?;
|
.map_err(|e| FlowyError::new(ErrorCode::UnexpectedEmpty, e))?;
|
||||||
let resp = serde_json::from_str::<UserWorkspaceList>(&content)
|
let mut workspace_list = serde_json::from_str::<UserWorkspaceList>(&content)
|
||||||
.map_err(|_e| FlowyError::new(ErrorCode::Serde, "Deserialize UserWorkspaceList failed"))?;
|
.map_err(|_e| FlowyError::new(ErrorCode::Serde, "Deserialize UserWorkspaceList failed"))?
|
||||||
Ok(resp.0.first().cloned())
|
.into_inner();
|
||||||
|
|
||||||
|
debug_assert!(workspace_list.len() == 1);
|
||||||
|
if workspace_list.is_empty() {
|
||||||
|
return Err(FlowyError::new(
|
||||||
|
ErrorCode::Internal,
|
||||||
|
"Failed to create workspace",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let user_workspace = workspace_list.remove(0);
|
||||||
|
Ok(Workspace {
|
||||||
|
id: user_workspace.workspace_id,
|
||||||
|
name: user_workspace.workspace_name,
|
||||||
|
belongings: Default::default(),
|
||||||
|
created_at: user_workspace.created_at.timestamp(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) async fn get_user_workspace_with_uid(
|
pub(crate) async fn get_user_workspace_with_uid(
|
||||||
postgrest: Arc<Postgrest>,
|
postgrest: Arc<Postgrest>,
|
||||||
uid: i64,
|
uid: i64,
|
||||||
) -> Result<Option<UserWorkspace>, FlowyError> {
|
) -> Result<Vec<Workspace>, FlowyError> {
|
||||||
let resp = postgrest
|
let resp = postgrest
|
||||||
.from(USER_WORKSPACE_TABLE)
|
.from(USER_WORKSPACE_TABLE)
|
||||||
.eq(USER_ID, uid.to_string())
|
.eq(USER_ID, uid.to_string())
|
||||||
@ -171,16 +209,27 @@ pub(crate) async fn get_user_workspace_with_uid(
|
|||||||
.text()
|
.text()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| FlowyError::new(ErrorCode::UnexpectedEmpty, e))?;
|
.map_err(|e| FlowyError::new(ErrorCode::UnexpectedEmpty, e))?;
|
||||||
let resp = serde_json::from_str::<UserWorkspaceList>(&content)
|
let user_workspaces = serde_json::from_str::<UserWorkspaceList>(&content)
|
||||||
.map_err(|_e| FlowyError::new(ErrorCode::Serde, "Deserialize UserWorkspaceList failed"))?;
|
.map_err(|_e| FlowyError::new(ErrorCode::Serde, "Deserialize UserWorkspaceList failed"))?
|
||||||
Ok(resp.0.first().cloned())
|
.0;
|
||||||
|
Ok(
|
||||||
|
user_workspaces
|
||||||
|
.into_iter()
|
||||||
|
.map(|user_workspace| Workspace {
|
||||||
|
id: user_workspace.workspace_id,
|
||||||
|
name: user_workspace.workspace_name,
|
||||||
|
belongings: Default::default(),
|
||||||
|
created_at: user_workspace.created_at.timestamp(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) async fn update_user_profile(
|
pub(crate) async fn update_user_profile(
|
||||||
postgrest: Arc<Postgrest>,
|
postgrest: Arc<Postgrest>,
|
||||||
params: UpdateUserProfileParams,
|
params: UpdateUserProfileParams,
|
||||||
) -> Result<Option<UserProfile>, FlowyError> {
|
) -> Result<Option<UserProfileResponse>, FlowyError> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
return Err(FlowyError::new(
|
return Err(FlowyError::new(
|
||||||
ErrorCode::UnexpectedEmpty,
|
ErrorCode::UnexpectedEmpty,
|
||||||
@ -206,7 +255,7 @@ pub(crate) async fn update_user_profile(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| FlowyError::new(ErrorCode::UnexpectedEmpty, e))?;
|
.map_err(|e| FlowyError::new(ErrorCode::UnexpectedEmpty, e))?;
|
||||||
|
|
||||||
let resp = serde_json::from_str::<UserProfileList>(&content)
|
let resp = serde_json::from_str::<UserProfileResponseList>(&content)
|
||||||
.map_err(|_e| FlowyError::new(ErrorCode::Serde, "Deserialize UserProfileList failed"))?;
|
.map_err(|_e| FlowyError::new(ErrorCode::Serde, "Deserialize UserProfileList failed"))?;
|
||||||
Ok(resp.0.first().cloned())
|
Ok(resp.0.first().cloned())
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@ -56,27 +57,39 @@ pub(crate) struct InsertRecord {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub(crate) struct UserProfile {
|
pub(crate) struct UserProfileResponse {
|
||||||
pub uid: i64,
|
pub uid: i64,
|
||||||
#[serde(deserialize_with = "deserialize_null_or_default")]
|
#[serde(deserialize_with = "deserialize_null_or_default")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
#[serde(deserialize_with = "deserialize_null_or_default")]
|
#[serde(deserialize_with = "deserialize_null_or_default")]
|
||||||
pub email: String,
|
pub email: String,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub(crate) struct UserProfileList(pub Vec<UserProfile>);
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub(crate) struct UserWorkspace {
|
|
||||||
pub uid: i64,
|
|
||||||
#[serde(deserialize_with = "deserialize_null_or_default")]
|
#[serde(deserialize_with = "deserialize_null_or_default")]
|
||||||
pub name: String,
|
|
||||||
pub workspace_id: String,
|
pub workspace_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub(crate) struct UserWorkspaceList(pub Vec<UserWorkspace>);
|
pub(crate) struct UserProfileResponseList(pub Vec<UserProfileResponse>);
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub(crate) struct UserWorkspace {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub uid: i64,
|
||||||
|
#[serde(deserialize_with = "deserialize_null_or_default")]
|
||||||
|
pub workspace_name: String,
|
||||||
|
pub created_at: DateTime<Utc>,
|
||||||
|
pub workspace_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub(crate) struct UserWorkspaceList(pub(crate) Vec<UserWorkspace>);
|
||||||
|
|
||||||
|
impl UserWorkspaceList {
|
||||||
|
pub(crate) fn into_inner(self) -> Vec<UserWorkspace> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles the case where the value is null. If the value is null, return the default value of the
|
/// Handles the case where the value is null. If the value is null, return the default value of the
|
||||||
/// type. Otherwise, deserialize the value.
|
/// type. Otherwise, deserialize the value.
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use postgrest::Postgrest;
|
use postgrest::Postgrest;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use flowy_config::entities::{SUPABASE_JWT_SECRET, SUPABASE_KEY, SUPABASE_URL};
|
|
||||||
use flowy_error::{ErrorCode, FlowyError};
|
use flowy_error::{ErrorCode, FlowyError};
|
||||||
|
use flowy_folder2::deps::FolderCloudService;
|
||||||
use flowy_user::event_map::UserAuthService;
|
use flowy_user::event_map::UserAuthService;
|
||||||
|
|
||||||
use crate::supabase::user::PostgrestUserAuthServiceImpl;
|
use crate::supabase::impls::PostgrestUserAuthServiceImpl;
|
||||||
use crate::AppFlowyServer;
|
use crate::AppFlowyServer;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub const SUPABASE_URL: &str = "SUPABASE_URL";
|
||||||
|
pub const SUPABASE_ANON_KEY: &str = "SUPABASE_ANON_KEY";
|
||||||
|
pub const SUPABASE_KEY: &str = "SUPABASE_KEY";
|
||||||
|
pub const SUPABASE_JWT_SECRET: &str = "SUPABASE_JWT_SECRET";
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct SupabaseConfiguration {
|
pub struct SupabaseConfiguration {
|
||||||
/// The url of the supabase server.
|
/// The url of the supabase server.
|
||||||
pub url: String,
|
pub url: String,
|
||||||
@ -20,6 +26,11 @@ pub struct SupabaseConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SupabaseConfiguration {
|
impl SupabaseConfiguration {
|
||||||
|
/// Load the configuration from the environment variables.
|
||||||
|
/// SUPABASE_URL=https://<your-supabase-url>.supabase.co
|
||||||
|
/// SUPABASE_KEY=<your-supabase-key>
|
||||||
|
/// SUPABASE_JWT_SECRET=<your-supabase-jwt-secret>
|
||||||
|
///
|
||||||
pub fn from_env() -> Result<Self, FlowyError> {
|
pub fn from_env() -> Result<Self, FlowyError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
url: std::env::var(SUPABASE_URL)
|
url: std::env::var(SUPABASE_URL)
|
||||||
@ -31,6 +42,12 @@ impl SupabaseConfiguration {
|
|||||||
})?,
|
})?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_env(&self) {
|
||||||
|
std::env::set_var(SUPABASE_URL, &self.url);
|
||||||
|
std::env::set_var(SUPABASE_KEY, &self.key);
|
||||||
|
std::env::set_var(SUPABASE_JWT_SECRET, &self.jwt_secret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SupabaseServer {
|
pub struct SupabaseServer {
|
||||||
@ -53,4 +70,8 @@ impl AppFlowyServer for SupabaseServer {
|
|||||||
fn user_service(&self) -> Arc<dyn UserAuthService> {
|
fn user_service(&self) -> Arc<dyn UserAuthService> {
|
||||||
Arc::new(PostgrestUserAuthServiceImpl::new(self.postgres.clone()))
|
Arc::new(PostgrestUserAuthServiceImpl::new(self.postgres.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn folder_service(&self) -> Arc<dyn FolderCloudService> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ flowy-core = { path = "../flowy-core" }
|
|||||||
flowy-user = { path = "../flowy-user"}
|
flowy-user = { path = "../flowy-user"}
|
||||||
flowy-net = { path = "../flowy-net"}
|
flowy-net = { path = "../flowy-net"}
|
||||||
flowy-folder2 = { path = "../flowy-folder2", features = ["test_helper"] }
|
flowy-folder2 = { path = "../flowy-folder2", features = ["test_helper"] }
|
||||||
#flowy-document= { path = "../flowy-document" }
|
|
||||||
lib-dispatch = { path = "../lib-dispatch" }
|
lib-dispatch = { path = "../lib-dispatch" }
|
||||||
lib-ot = { path = "../../../shared-lib/lib-ot" }
|
lib-ot = { path = "../../../shared-lib/lib-ot" }
|
||||||
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||||
@ -19,21 +18,18 @@ flowy-server = { path = "../flowy-server" }
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = {version = "1.0"}
|
serde_json = {version = "1.0"}
|
||||||
protobuf = {version = "2.28.0"}
|
protobuf = {version = "2.28.0"}
|
||||||
#claim = "0.5.0"
|
|
||||||
tokio = { version = "1.26", features = ["full"]}
|
tokio = { version = "1.26", features = ["full"]}
|
||||||
futures-util = "0.3.26"
|
futures-util = "0.3.26"
|
||||||
thread-id = "3.3.0"
|
thread-id = "3.3.0"
|
||||||
log = "0.4"
|
|
||||||
bytes = "1.4"
|
bytes = "1.4"
|
||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
tempdir = "0.3.7"
|
tempdir = "0.3.7"
|
||||||
|
tracing = { version = "0.1.27" }
|
||||||
|
parking_lot = "0.12.1"
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = "1.0.3"
|
uuid = { version = "1.3.3", features = ["v4"] }
|
||||||
quickcheck_macros = "0.9.1"
|
|
||||||
fake = "2.5.0"
|
|
||||||
futures = "0.3.26"
|
|
||||||
serial_test = "0.5.1"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dart = ["flowy-core/dart"]
|
dart = ["flowy-core/dart"]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::FlowySDKTest;
|
use crate::FlowyCoreTest;
|
||||||
use flowy_user::{entities::UserProfilePB, errors::FlowyError};
|
use flowy_user::errors::FlowyError;
|
||||||
use lib_dispatch::prelude::{
|
use lib_dispatch::prelude::{
|
||||||
AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, StatusCode,
|
AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, StatusCode,
|
||||||
ToBytes, *,
|
ToBytes, *,
|
||||||
@ -8,38 +8,18 @@ use std::{
|
|||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
marker::PhantomData,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Folder2EventBuilder = EventBuilder<FlowyError>;
|
|
||||||
impl Folder2EventBuilder {
|
|
||||||
pub fn new(sdk: FlowySDKTest) -> Self {
|
|
||||||
EventBuilder::test(TestContext::new(sdk))
|
|
||||||
}
|
|
||||||
pub fn user_profile(&self) -> &Option<UserProfilePB> {
|
|
||||||
&self.user_profile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type UserModuleEventBuilder = Folder2EventBuilder;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EventBuilder<E> {
|
pub struct EventBuilder {
|
||||||
context: TestContext,
|
context: TestContext,
|
||||||
user_profile: Option<UserProfilePB>,
|
|
||||||
err_phantom: PhantomData<E>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> EventBuilder<E>
|
impl EventBuilder {
|
||||||
where
|
pub fn new(sdk: FlowyCoreTest) -> Self {
|
||||||
E: AFPluginFromBytes + Debug,
|
|
||||||
{
|
|
||||||
fn test(context: TestContext) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
context,
|
context: TestContext::new(sdk),
|
||||||
user_profile: None,
|
|
||||||
err_phantom: PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +33,7 @@ where
|
|||||||
self.context.request = Some(module_request.payload(bytes))
|
self.context.request = Some(module_request.payload(bytes))
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Set payload failed: {:?}", e);
|
tracing::error!("Set payload failed: {:?}", e);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
@ -86,7 +66,7 @@ where
|
|||||||
R: AFPluginFromBytes,
|
R: AFPluginFromBytes,
|
||||||
{
|
{
|
||||||
let response = self.get_response();
|
let response = self.get_response();
|
||||||
match response.clone().parse::<R, E>() {
|
match response.clone().parse::<R, FlowyError>() {
|
||||||
Ok(Ok(data)) => data,
|
Ok(Ok(data)) => data,
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
panic!(
|
panic!(
|
||||||
@ -105,22 +85,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error(self) -> E {
|
pub fn error(self) -> Option<FlowyError> {
|
||||||
let response = self.get_response();
|
let response = self.get_response();
|
||||||
assert_eq!(response.status_code, StatusCode::Err);
|
assert_eq!(response.status_code, StatusCode::Err);
|
||||||
<AFPluginData<E>>::try_from(response.payload)
|
<AFPluginData<FlowyError>>::try_from(response.payload)
|
||||||
.unwrap()
|
.ok()
|
||||||
.into_inner()
|
.map(|data| data.into_inner())
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_error(self) -> Self {
|
|
||||||
// self.context.assert_error();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_success(self) -> Self {
|
|
||||||
// self.context.assert_success();
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch(&self) -> Arc<AFPluginDispatcher> {
|
fn dispatch(&self) -> Arc<AFPluginDispatcher> {
|
||||||
@ -132,7 +102,7 @@ where
|
|||||||
.context
|
.context
|
||||||
.response
|
.response
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("must call sync_send first")
|
.expect("must call sync_send/async_send first")
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,13 +113,13 @@ where
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TestContext {
|
pub struct TestContext {
|
||||||
pub sdk: FlowySDKTest,
|
pub sdk: FlowyCoreTest,
|
||||||
request: Option<AFPluginRequest>,
|
request: Option<AFPluginRequest>,
|
||||||
response: Option<AFPluginEventResponse>,
|
response: Option<AFPluginEventResponse>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestContext {
|
impl TestContext {
|
||||||
pub fn new(sdk: FlowySDKTest) -> Self {
|
pub fn new(sdk: FlowyCoreTest) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sdk,
|
sdk,
|
||||||
request: None,
|
request: None,
|
||||||
|
111
frontend/rust-lib/flowy-test/src/folder_event.rs
Normal file
111
frontend/rust-lib/flowy-test/src/folder_event.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
use crate::event_builder::EventBuilder;
|
||||||
|
use crate::FlowyCoreTest;
|
||||||
|
use flowy_folder2::entities::*;
|
||||||
|
use flowy_folder2::event_map::FolderEvent::*;
|
||||||
|
|
||||||
|
pub struct ViewTest {
|
||||||
|
pub sdk: FlowyCoreTest,
|
||||||
|
pub workspace: WorkspacePB,
|
||||||
|
pub parent_view: ViewPB,
|
||||||
|
pub child_view: ViewPB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewTest {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub async fn new(sdk: &FlowyCoreTest, layout: ViewLayoutPB, data: Vec<u8>) -> Self {
|
||||||
|
let workspace = create_workspace(sdk, "Workspace", "").await;
|
||||||
|
open_workspace(sdk, &workspace.id).await;
|
||||||
|
let app = create_app(sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
||||||
|
let view = create_view(sdk, &app.id, layout, data).await;
|
||||||
|
Self {
|
||||||
|
sdk: sdk.clone(),
|
||||||
|
workspace,
|
||||||
|
parent_view: app,
|
||||||
|
child_view: view,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_grid_view(sdk: &FlowyCoreTest, data: Vec<u8>) -> Self {
|
||||||
|
Self::new(sdk, ViewLayoutPB::Grid, data).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_board_view(sdk: &FlowyCoreTest, data: Vec<u8>) -> Self {
|
||||||
|
Self::new(sdk, ViewLayoutPB::Board, data).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_calendar_view(sdk: &FlowyCoreTest, data: Vec<u8>) -> Self {
|
||||||
|
Self::new(sdk, ViewLayoutPB::Calendar, data).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn new_document_view(sdk: &FlowyCoreTest) -> Self {
|
||||||
|
Self::new(sdk, ViewLayoutPB::Document, vec![]).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_workspace(sdk: &FlowyCoreTest, name: &str, desc: &str) -> WorkspacePB {
|
||||||
|
let request = CreateWorkspacePayloadPB {
|
||||||
|
name: name.to_owned(),
|
||||||
|
desc: desc.to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
EventBuilder::new(sdk.clone())
|
||||||
|
.event(CreateWorkspace)
|
||||||
|
.payload(request)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<WorkspacePB>()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn open_workspace(sdk: &FlowyCoreTest, workspace_id: &str) {
|
||||||
|
let payload = WorkspaceIdPB {
|
||||||
|
value: Some(workspace_id.to_owned()),
|
||||||
|
};
|
||||||
|
let _ = EventBuilder::new(sdk.clone())
|
||||||
|
.event(OpenWorkspace)
|
||||||
|
.payload(payload)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_app(sdk: &FlowyCoreTest, name: &str, desc: &str, workspace_id: &str) -> ViewPB {
|
||||||
|
let create_app_request = CreateViewPayloadPB {
|
||||||
|
belong_to_id: workspace_id.to_owned(),
|
||||||
|
name: name.to_string(),
|
||||||
|
desc: desc.to_string(),
|
||||||
|
thumbnail: None,
|
||||||
|
layout: ViewLayoutPB::Document,
|
||||||
|
initial_data: vec![],
|
||||||
|
ext: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
EventBuilder::new(sdk.clone())
|
||||||
|
.event(CreateView)
|
||||||
|
.payload(create_app_request)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<ViewPB>()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_view(
|
||||||
|
sdk: &FlowyCoreTest,
|
||||||
|
app_id: &str,
|
||||||
|
layout: ViewLayoutPB,
|
||||||
|
data: Vec<u8>,
|
||||||
|
) -> ViewPB {
|
||||||
|
let payload = CreateViewPayloadPB {
|
||||||
|
belong_to_id: app_id.to_string(),
|
||||||
|
name: "View A".to_string(),
|
||||||
|
desc: "".to_string(),
|
||||||
|
thumbnail: Some("http://1.png".to_string()),
|
||||||
|
layout,
|
||||||
|
initial_data: data,
|
||||||
|
ext: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
EventBuilder::new(sdk.clone())
|
||||||
|
.event(CreateView)
|
||||||
|
.payload(payload)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<ViewPB>()
|
||||||
|
}
|
@ -1,216 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use flowy_folder2::entities::{
|
|
||||||
CreateViewPayloadPB, CreateWorkspacePayloadPB, ViewLayoutPB, ViewPB, WorkspaceIdPB, WorkspacePB,
|
|
||||||
};
|
|
||||||
use flowy_folder2::event_map::FolderEvent::{CreateView, CreateWorkspace, OpenWorkspace};
|
|
||||||
use flowy_user::entities::AuthTypePB;
|
|
||||||
use flowy_user::{
|
|
||||||
entities::{SignInPayloadPB, SignUpPayloadPB, UserProfilePB},
|
|
||||||
errors::FlowyError,
|
|
||||||
event_map::UserEvent::{InitUser, SignIn, SignOut, SignUp},
|
|
||||||
};
|
|
||||||
use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
pub struct ViewTest {
|
|
||||||
pub sdk: FlowySDKTest,
|
|
||||||
pub workspace: WorkspacePB,
|
|
||||||
pub parent_view: ViewPB,
|
|
||||||
pub child_view: ViewPB,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ViewTest {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn new(sdk: &FlowySDKTest, layout: ViewLayoutPB, data: Vec<u8>) -> Self {
|
|
||||||
let workspace = create_workspace(sdk, "Workspace", "").await;
|
|
||||||
open_workspace(sdk, &workspace.id).await;
|
|
||||||
let app = create_app(sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
|
||||||
let view = create_view(sdk, &app.id, layout, data).await;
|
|
||||||
Self {
|
|
||||||
sdk: sdk.clone(),
|
|
||||||
workspace,
|
|
||||||
parent_view: app,
|
|
||||||
child_view: view,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new_grid_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
|
|
||||||
Self::new(sdk, ViewLayoutPB::Grid, data).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new_board_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
|
|
||||||
Self::new(sdk, ViewLayoutPB::Board, data).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new_calendar_view(sdk: &FlowySDKTest, data: Vec<u8>) -> Self {
|
|
||||||
Self::new(sdk, ViewLayoutPB::Calendar, data).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new_document_view(sdk: &FlowySDKTest) -> Self {
|
|
||||||
Self::new(sdk, ViewLayoutPB::Document, vec![]).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_workspace(sdk: &FlowySDKTest, name: &str, desc: &str) -> WorkspacePB {
|
|
||||||
let request = CreateWorkspacePayloadPB {
|
|
||||||
name: name.to_owned(),
|
|
||||||
desc: desc.to_owned(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
|
||||||
.event(CreateWorkspace)
|
|
||||||
.payload(request)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<WorkspacePB>()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn open_workspace(sdk: &FlowySDKTest, workspace_id: &str) {
|
|
||||||
let payload = WorkspaceIdPB {
|
|
||||||
value: Some(workspace_id.to_owned()),
|
|
||||||
};
|
|
||||||
let _ = Folder2EventBuilder::new(sdk.clone())
|
|
||||||
.event(OpenWorkspace)
|
|
||||||
.payload(payload)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_app(sdk: &FlowySDKTest, name: &str, desc: &str, workspace_id: &str) -> ViewPB {
|
|
||||||
let create_app_request = CreateViewPayloadPB {
|
|
||||||
belong_to_id: workspace_id.to_owned(),
|
|
||||||
name: name.to_string(),
|
|
||||||
desc: desc.to_string(),
|
|
||||||
thumbnail: None,
|
|
||||||
layout: ViewLayoutPB::Document,
|
|
||||||
initial_data: vec![],
|
|
||||||
ext: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
|
||||||
.event(CreateView)
|
|
||||||
.payload(create_app_request)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<ViewPB>()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_view(
|
|
||||||
sdk: &FlowySDKTest,
|
|
||||||
app_id: &str,
|
|
||||||
layout: ViewLayoutPB,
|
|
||||||
data: Vec<u8>,
|
|
||||||
) -> ViewPB {
|
|
||||||
let payload = CreateViewPayloadPB {
|
|
||||||
belong_to_id: app_id.to_string(),
|
|
||||||
name: "View A".to_string(),
|
|
||||||
desc: "".to_string(),
|
|
||||||
thumbnail: Some("http://1.png".to_string()),
|
|
||||||
layout,
|
|
||||||
initial_data: data,
|
|
||||||
ext: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Folder2EventBuilder::new(sdk.clone())
|
|
||||||
.event(CreateView)
|
|
||||||
.payload(payload)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<ViewPB>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn random_email() -> String {
|
|
||||||
format!("{}@appflowy.io", nanoid!(20))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn login_email() -> String {
|
|
||||||
"annie2@appflowy.io".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn login_password() -> String {
|
|
||||||
"HelloWorld!123".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SignUpContext {
|
|
||||||
pub user_profile: UserProfilePB,
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sign_up(dispatch: Arc<AFPluginDispatcher>) -> SignUpContext {
|
|
||||||
let password = login_password();
|
|
||||||
let payload = SignUpPayloadPB {
|
|
||||||
email: random_email(),
|
|
||||||
name: "app flowy".to_string(),
|
|
||||||
password: password.clone(),
|
|
||||||
auth_type: AuthTypePB::Local,
|
|
||||||
}
|
|
||||||
.into_bytes()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let request = AFPluginRequest::new(SignUp).payload(payload);
|
|
||||||
let user_profile = AFPluginDispatcher::sync_send(dispatch, request)
|
|
||||||
.parse::<UserProfilePB, FlowyError>()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
SignUpContext {
|
|
||||||
user_profile,
|
|
||||||
password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn async_sign_up(dispatch: Arc<AFPluginDispatcher>) -> SignUpContext {
|
|
||||||
let password = login_password();
|
|
||||||
let email = random_email();
|
|
||||||
let payload = SignUpPayloadPB {
|
|
||||||
email,
|
|
||||||
name: "app flowy".to_string(),
|
|
||||||
password: password.clone(),
|
|
||||||
auth_type: AuthTypePB::Local,
|
|
||||||
}
|
|
||||||
.into_bytes()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let request = AFPluginRequest::new(SignUp).payload(payload);
|
|
||||||
let user_profile = AFPluginDispatcher::async_send(dispatch.clone(), request)
|
|
||||||
.await
|
|
||||||
.parse::<UserProfilePB, FlowyError>()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// let _ = create_default_workspace_if_need(dispatch.clone(), &user_profile.id);
|
|
||||||
SignUpContext {
|
|
||||||
user_profile,
|
|
||||||
password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn init_user_setting(dispatch: Arc<AFPluginDispatcher>) {
|
|
||||||
let request = AFPluginRequest::new(InitUser);
|
|
||||||
let _ = AFPluginDispatcher::async_send(dispatch.clone(), request).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn sign_in(dispatch: Arc<AFPluginDispatcher>) -> UserProfilePB {
|
|
||||||
let payload = SignInPayloadPB {
|
|
||||||
email: login_email(),
|
|
||||||
password: login_password(),
|
|
||||||
name: "rust".to_owned(),
|
|
||||||
auth_type: AuthTypePB::Local,
|
|
||||||
}
|
|
||||||
.into_bytes()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let request = AFPluginRequest::new(SignIn).payload(payload);
|
|
||||||
AFPluginDispatcher::sync_send(dispatch, request)
|
|
||||||
.parse::<UserProfilePB, FlowyError>()
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn logout(dispatch: Arc<AFPluginDispatcher>) {
|
|
||||||
let _ = AFPluginDispatcher::sync_send(dispatch, AFPluginRequest::new(SignOut));
|
|
||||||
}
|
|
@ -1,57 +1,62 @@
|
|||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
|
use parking_lot::RwLock;
|
||||||
use std::env::temp_dir;
|
use std::env::temp_dir;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
|
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
|
||||||
use flowy_user::entities::UserProfilePB;
|
use flowy_user::entities::{AuthTypePB, UserProfilePB};
|
||||||
|
|
||||||
use crate::helper::*;
|
|
||||||
|
|
||||||
|
use crate::user_event::{async_sign_up, init_user_setting, SignUpContext};
|
||||||
pub mod event_builder;
|
pub mod event_builder;
|
||||||
pub mod helper;
|
pub mod folder_event;
|
||||||
|
pub mod user_event;
|
||||||
pub mod prelude {
|
|
||||||
pub use lib_dispatch::prelude::*;
|
|
||||||
|
|
||||||
pub use crate::{event_builder::*, helper::*, *};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FlowySDKTest {
|
pub struct FlowyCoreTest {
|
||||||
pub inner: AppFlowyCore,
|
auth_type: Arc<RwLock<AuthTypePB>>,
|
||||||
|
inner: AppFlowyCore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for FlowySDKTest {
|
impl Default for FlowyCoreTest {
|
||||||
|
fn default() -> Self {
|
||||||
|
let temp_dir = temp_dir();
|
||||||
|
let config =
|
||||||
|
AppFlowyCoreConfig::new(temp_dir.to_str().unwrap(), nanoid!(6)).log_filter("info", vec![]);
|
||||||
|
let inner = std::thread::spawn(|| AppFlowyCore::new(config))
|
||||||
|
.join()
|
||||||
|
.unwrap();
|
||||||
|
let auth_type = Arc::new(RwLock::new(AuthTypePB::Local));
|
||||||
|
std::mem::forget(inner.dispatcher());
|
||||||
|
Self { inner, auth_type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowyCoreTest {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn sign_up(&self) -> SignUpContext {
|
||||||
|
let auth_type = self.auth_type.read().clone();
|
||||||
|
async_sign_up(self.inner.dispatcher(), auth_type).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_auth_type(&self, auth_type: AuthTypePB) {
|
||||||
|
*self.auth_type.write() = auth_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init_user(&self) -> UserProfilePB {
|
||||||
|
let auth_type = self.auth_type.read().clone();
|
||||||
|
let context = async_sign_up(self.inner.dispatcher(), auth_type).await;
|
||||||
|
init_user_setting(self.inner.dispatcher()).await;
|
||||||
|
context.user_profile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for FlowyCoreTest {
|
||||||
type Target = AppFlowyCore;
|
type Target = AppFlowyCore;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for FlowySDKTest {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlowySDKTest {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let config =
|
|
||||||
AppFlowyCoreConfig::new(temp_dir().to_str().unwrap(), nanoid!(6)).log_filter("info", vec![]);
|
|
||||||
let sdk = std::thread::spawn(|| AppFlowyCore::new(config))
|
|
||||||
.join()
|
|
||||||
.unwrap();
|
|
||||||
std::mem::forget(sdk.dispatcher());
|
|
||||||
Self { inner: sdk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn sign_up(&self) -> SignUpContext {
|
|
||||||
async_sign_up(self.inner.dispatcher()).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn init_user(&self) -> UserProfilePB {
|
|
||||||
let context = async_sign_up(self.inner.dispatcher()).await;
|
|
||||||
init_user_setting(self.inner.dispatcher()).await;
|
|
||||||
context.user_profile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
103
frontend/rust-lib/flowy-test/src/user_event.rs
Normal file
103
frontend/rust-lib/flowy-test/src/user_event.rs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
use flowy_user::entities::{AuthTypePB, SignInPayloadPB, SignUpPayloadPB, UserProfilePB};
|
||||||
|
use flowy_user::errors::FlowyError;
|
||||||
|
use flowy_user::event_map::UserEvent::*;
|
||||||
|
use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes};
|
||||||
|
use nanoid::nanoid;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub fn random_email() -> String {
|
||||||
|
format!("{}@appflowy.io", nanoid!(20))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn login_email() -> String {
|
||||||
|
"annie2@appflowy.io".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn login_password() -> String {
|
||||||
|
"HelloWorld!123".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SignUpContext {
|
||||||
|
pub user_profile: UserProfilePB,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign_up(dispatch: Arc<AFPluginDispatcher>) -> SignUpContext {
|
||||||
|
let password = login_password();
|
||||||
|
let payload = SignUpPayloadPB {
|
||||||
|
email: random_email(),
|
||||||
|
name: "app flowy".to_string(),
|
||||||
|
password: password.clone(),
|
||||||
|
auth_type: AuthTypePB::Local,
|
||||||
|
}
|
||||||
|
.into_bytes()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let request = AFPluginRequest::new(SignUp).payload(payload);
|
||||||
|
let user_profile = AFPluginDispatcher::sync_send(dispatch, request)
|
||||||
|
.parse::<UserProfilePB, FlowyError>()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
SignUpContext {
|
||||||
|
user_profile,
|
||||||
|
password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn async_sign_up(
|
||||||
|
dispatch: Arc<AFPluginDispatcher>,
|
||||||
|
auth_type: AuthTypePB,
|
||||||
|
) -> SignUpContext {
|
||||||
|
let password = login_password();
|
||||||
|
let email = random_email();
|
||||||
|
let payload = SignUpPayloadPB {
|
||||||
|
email,
|
||||||
|
name: "app flowy".to_string(),
|
||||||
|
password: password.clone(),
|
||||||
|
auth_type,
|
||||||
|
}
|
||||||
|
.into_bytes()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let request = AFPluginRequest::new(SignUp).payload(payload);
|
||||||
|
let user_profile = AFPluginDispatcher::async_send(dispatch.clone(), request)
|
||||||
|
.await
|
||||||
|
.parse::<UserProfilePB, FlowyError>()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// let _ = create_default_workspace_if_need(dispatch.clone(), &user_profile.id);
|
||||||
|
SignUpContext {
|
||||||
|
user_profile,
|
||||||
|
password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init_user_setting(dispatch: Arc<AFPluginDispatcher>) {
|
||||||
|
let request = AFPluginRequest::new(InitUser);
|
||||||
|
let _ = AFPluginDispatcher::async_send(dispatch.clone(), request).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn sign_in(dispatch: Arc<AFPluginDispatcher>) -> UserProfilePB {
|
||||||
|
let payload = SignInPayloadPB {
|
||||||
|
email: login_email(),
|
||||||
|
password: login_password(),
|
||||||
|
name: "rust".to_owned(),
|
||||||
|
auth_type: AuthTypePB::Local,
|
||||||
|
}
|
||||||
|
.into_bytes()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let request = AFPluginRequest::new(SignIn).payload(payload);
|
||||||
|
AFPluginDispatcher::sync_send(dispatch, request)
|
||||||
|
.parse::<UserProfilePB, FlowyError>()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn logout(dispatch: Arc<AFPluginDispatcher>) {
|
||||||
|
let _ = AFPluginDispatcher::sync_send(dispatch, AFPluginRequest::new(SignOut));
|
||||||
|
}
|
1
frontend/rust-lib/flowy-test/tests/main.rs
Normal file
1
frontend/rust-lib/flowy-test/tests/main.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
mod user;
|
@ -1,13 +1,15 @@
|
|||||||
use flowy_test::{event_builder::UserModuleEventBuilder, FlowySDKTest};
|
use flowy_test::user_event::*;
|
||||||
|
use flowy_test::{event_builder::EventBuilder, FlowyCoreTest};
|
||||||
use flowy_user::entities::{AuthTypePB, SignInPayloadPB, SignUpPayloadPB, UserProfilePB};
|
use flowy_user::entities::{AuthTypePB, SignInPayloadPB, SignUpPayloadPB, UserProfilePB};
|
||||||
use flowy_user::{errors::ErrorCode, event_map::UserEvent::*};
|
use flowy_user::errors::ErrorCode;
|
||||||
|
use flowy_user::event_map::UserEvent::*;
|
||||||
|
|
||||||
use crate::helper::*;
|
use crate::user::local_test::helper::*;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_up_with_invalid_email() {
|
async fn sign_up_with_invalid_email() {
|
||||||
for email in invalid_email_test_case() {
|
for email in invalid_email_test_case() {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
let request = SignUpPayloadPB {
|
let request = SignUpPayloadPB {
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
name: valid_name(),
|
name: valid_name(),
|
||||||
@ -16,43 +18,45 @@ async fn sign_up_with_invalid_email() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UserModuleEventBuilder::new(sdk)
|
EventBuilder::new(sdk)
|
||||||
.event(SignUp)
|
.event(SignUp)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.error()
|
.error()
|
||||||
|
.unwrap()
|
||||||
.code,
|
.code,
|
||||||
ErrorCode::EmailFormatInvalid.value()
|
ErrorCode::EmailFormatInvalid.value()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_up_with_invalid_password() {
|
async fn sign_up_with_long_password() {
|
||||||
for password in invalid_password_test_case() {
|
let sdk = FlowyCoreTest::new();
|
||||||
let sdk = FlowySDKTest::default();
|
|
||||||
let request = SignUpPayloadPB {
|
let request = SignUpPayloadPB {
|
||||||
email: random_email(),
|
email: random_email(),
|
||||||
name: valid_name(),
|
name: valid_name(),
|
||||||
password,
|
password: "1234".repeat(100).as_str().to_string(),
|
||||||
auth_type: AuthTypePB::Local,
|
auth_type: AuthTypePB::Local,
|
||||||
};
|
};
|
||||||
|
|
||||||
UserModuleEventBuilder::new(sdk)
|
assert_eq!(
|
||||||
|
EventBuilder::new(sdk)
|
||||||
.event(SignUp)
|
.event(SignUp)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.assert_error();
|
.error()
|
||||||
}
|
.unwrap()
|
||||||
|
.code,
|
||||||
|
ErrorCode::PasswordTooLong.value()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_in_success() {
|
async fn sign_in_success() {
|
||||||
let test = FlowySDKTest::default();
|
let test = FlowyCoreTest::new();
|
||||||
let _ = UserModuleEventBuilder::new(test.clone())
|
let _ = EventBuilder::new(test.clone()).event(SignOut).sync_send();
|
||||||
.event(SignOut)
|
|
||||||
.sync_send();
|
|
||||||
let sign_up_context = test.sign_up().await;
|
let sign_up_context = test.sign_up().await;
|
||||||
|
|
||||||
let request = SignInPayloadPB {
|
let request = SignInPayloadPB {
|
||||||
@ -62,7 +66,7 @@ async fn sign_in_success() {
|
|||||||
auth_type: AuthTypePB::Local,
|
auth_type: AuthTypePB::Local,
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = UserModuleEventBuilder::new(test.clone())
|
let response = EventBuilder::new(test.clone())
|
||||||
.event(SignIn)
|
.event(SignIn)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -74,7 +78,7 @@ async fn sign_in_success() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_in_with_invalid_email() {
|
async fn sign_in_with_invalid_email() {
|
||||||
for email in invalid_email_test_case() {
|
for email in invalid_email_test_case() {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
let request = SignInPayloadPB {
|
let request = SignInPayloadPB {
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
password: login_password(),
|
password: login_password(),
|
||||||
@ -83,12 +87,13 @@ async fn sign_in_with_invalid_email() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UserModuleEventBuilder::new(sdk)
|
EventBuilder::new(sdk)
|
||||||
.event(SignIn)
|
.event(SignIn)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.error()
|
.error()
|
||||||
|
.unwrap()
|
||||||
.code,
|
.code,
|
||||||
ErrorCode::EmailFormatInvalid.value()
|
ErrorCode::EmailFormatInvalid.value()
|
||||||
);
|
);
|
||||||
@ -98,7 +103,7 @@ async fn sign_in_with_invalid_email() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_in_with_invalid_password() {
|
async fn sign_in_with_invalid_password() {
|
||||||
for password in invalid_password_test_case() {
|
for password in invalid_password_test_case() {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
|
|
||||||
let request = SignInPayloadPB {
|
let request = SignInPayloadPB {
|
||||||
email: random_email(),
|
email: random_email(),
|
||||||
@ -107,11 +112,12 @@ async fn sign_in_with_invalid_password() {
|
|||||||
auth_type: AuthTypePB::Local,
|
auth_type: AuthTypePB::Local,
|
||||||
};
|
};
|
||||||
|
|
||||||
UserModuleEventBuilder::new(sdk)
|
assert!(EventBuilder::new(sdk)
|
||||||
.event(SignIn)
|
.event(SignIn)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.assert_error();
|
.error()
|
||||||
|
.is_some())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,3 @@
|
|||||||
pub use flowy_test::{
|
|
||||||
event_builder::*,
|
|
||||||
prelude::{login_password, random_email},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) fn invalid_email_test_case() -> Vec<String> {
|
pub(crate) fn invalid_email_test_case() -> Vec<String> {
|
||||||
// https://gist.github.com/cjaoude/fd9910626629b53c4d25
|
// https://gist.github.com/cjaoude/fd9910626629b53c4d25
|
||||||
vec![
|
vec![
|
@ -1,5 +1,5 @@
|
|||||||
use crate::helper::*;
|
use crate::user::local_test::helper::*;
|
||||||
use flowy_test::{event_builder::UserModuleEventBuilder, FlowySDKTest};
|
use flowy_test::{event_builder::EventBuilder, FlowyCoreTest};
|
||||||
use flowy_user::entities::{UpdateUserProfilePayloadPB, UserProfilePB};
|
use flowy_user::entities::{UpdateUserProfilePayloadPB, UserProfilePB};
|
||||||
use flowy_user::{errors::ErrorCode, event_map::UserEvent::*};
|
use flowy_user::{errors::ErrorCode, event_map::UserEvent::*};
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
@ -8,20 +8,20 @@ use nanoid::nanoid;
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn user_profile_get_failed() {
|
async fn user_profile_get_failed() {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
let result = UserModuleEventBuilder::new(sdk)
|
let result = EventBuilder::new(sdk)
|
||||||
.event(GetUserProfile)
|
.event(GetUserProfile)
|
||||||
.assert_error()
|
|
||||||
.async_send()
|
.async_send()
|
||||||
.await;
|
.await
|
||||||
assert!(result.user_profile().is_none())
|
.error();
|
||||||
|
assert!(result.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn user_profile_get() {
|
async fn user_profile_get() {
|
||||||
let test = FlowySDKTest::default();
|
let test = FlowyCoreTest::new();
|
||||||
let user_profile = test.init_user().await;
|
let user_profile = test.init_user().await;
|
||||||
let user = UserModuleEventBuilder::new(test.clone())
|
let user = EventBuilder::new(test.clone())
|
||||||
.event(GetUserProfile)
|
.event(GetUserProfile)
|
||||||
.sync_send()
|
.sync_send()
|
||||||
.parse::<UserProfilePB>();
|
.parse::<UserProfilePB>();
|
||||||
@ -30,18 +30,17 @@ async fn user_profile_get() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn user_update_with_name() {
|
async fn user_update_with_name() {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
let user = sdk.init_user().await;
|
let user = sdk.init_user().await;
|
||||||
let new_name = "hello_world".to_owned();
|
let new_name = "hello_world".to_owned();
|
||||||
let request = UpdateUserProfilePayloadPB::new(user.id).name(&new_name);
|
let request = UpdateUserProfilePayloadPB::new(user.id).name(&new_name);
|
||||||
let _ = UserModuleEventBuilder::new(sdk.clone())
|
let _ = EventBuilder::new(sdk.clone())
|
||||||
.event(UpdateUserProfile)
|
.event(UpdateUserProfile)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.sync_send();
|
.sync_send();
|
||||||
|
|
||||||
let user_profile = UserModuleEventBuilder::new(sdk.clone())
|
let user_profile = EventBuilder::new(sdk.clone())
|
||||||
.event(GetUserProfile)
|
.event(GetUserProfile)
|
||||||
.assert_error()
|
|
||||||
.sync_send()
|
.sync_send()
|
||||||
.parse::<UserProfilePB>();
|
.parse::<UserProfilePB>();
|
||||||
|
|
||||||
@ -50,49 +49,35 @@ async fn user_update_with_name() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn user_update_with_email() {
|
async fn user_update_with_email() {
|
||||||
let sdk = FlowySDKTest::default();
|
let sdk = FlowyCoreTest::new();
|
||||||
let user = sdk.init_user().await;
|
let user = sdk.init_user().await;
|
||||||
let new_email = format!("{}@gmail.com", nanoid!(6));
|
let new_email = format!("{}@gmail.com", nanoid!(6));
|
||||||
let request = UpdateUserProfilePayloadPB::new(user.id).email(&new_email);
|
let request = UpdateUserProfilePayloadPB::new(user.id).email(&new_email);
|
||||||
let _ = UserModuleEventBuilder::new(sdk.clone())
|
let _ = EventBuilder::new(sdk.clone())
|
||||||
.event(UpdateUserProfile)
|
.event(UpdateUserProfile)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.sync_send();
|
.sync_send();
|
||||||
let user_profile = UserModuleEventBuilder::new(sdk.clone())
|
let user_profile = EventBuilder::new(sdk.clone())
|
||||||
.event(GetUserProfile)
|
.event(GetUserProfile)
|
||||||
.assert_error()
|
|
||||||
.sync_send()
|
.sync_send()
|
||||||
.parse::<UserProfilePB>();
|
.parse::<UserProfilePB>();
|
||||||
|
|
||||||
assert_eq!(user_profile.email, new_email,);
|
assert_eq!(user_profile.email, new_email,);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn user_update_with_password() {
|
|
||||||
let sdk = FlowySDKTest::default();
|
|
||||||
let user = sdk.init_user().await;
|
|
||||||
let new_password = "H123world!".to_owned();
|
|
||||||
let request = UpdateUserProfilePayloadPB::new(user.id).password(&new_password);
|
|
||||||
|
|
||||||
let _ = UserModuleEventBuilder::new(sdk.clone())
|
|
||||||
.event(UpdateUserProfile)
|
|
||||||
.payload(request)
|
|
||||||
.sync_send()
|
|
||||||
.assert_success();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn user_update_with_invalid_email() {
|
async fn user_update_with_invalid_email() {
|
||||||
let test = FlowySDKTest::default();
|
let test = FlowyCoreTest::new();
|
||||||
let user = test.init_user().await;
|
let user = test.init_user().await;
|
||||||
for email in invalid_email_test_case() {
|
for email in invalid_email_test_case() {
|
||||||
let request = UpdateUserProfilePayloadPB::new(user.id).email(&email);
|
let request = UpdateUserProfilePayloadPB::new(user.id).email(&email);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UserModuleEventBuilder::new(test.clone())
|
EventBuilder::new(test.clone())
|
||||||
.event(UpdateUserProfile)
|
.event(UpdateUserProfile)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.sync_send()
|
.sync_send()
|
||||||
.error()
|
.error()
|
||||||
|
.unwrap()
|
||||||
.code,
|
.code,
|
||||||
ErrorCode::EmailFormatInvalid.value()
|
ErrorCode::EmailFormatInvalid.value()
|
||||||
);
|
);
|
||||||
@ -101,27 +86,30 @@ async fn user_update_with_invalid_email() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn user_update_with_invalid_password() {
|
async fn user_update_with_invalid_password() {
|
||||||
let test = FlowySDKTest::default();
|
let test = FlowyCoreTest::new();
|
||||||
let user = test.init_user().await;
|
let user = test.init_user().await;
|
||||||
for password in invalid_password_test_case() {
|
for password in invalid_password_test_case() {
|
||||||
let request = UpdateUserProfilePayloadPB::new(user.id).password(&password);
|
let request = UpdateUserProfilePayloadPB::new(user.id).password(&password);
|
||||||
|
|
||||||
UserModuleEventBuilder::new(test.clone())
|
assert!(EventBuilder::new(test.clone())
|
||||||
.event(UpdateUserProfile)
|
.event(UpdateUserProfile)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.sync_send()
|
.async_send()
|
||||||
.assert_error();
|
.await
|
||||||
|
.error()
|
||||||
|
.is_some());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn user_update_with_invalid_name() {
|
async fn user_update_with_invalid_name() {
|
||||||
let test = FlowySDKTest::default();
|
let test = FlowyCoreTest::new();
|
||||||
let user = test.init_user().await;
|
let user = test.init_user().await;
|
||||||
let request = UpdateUserProfilePayloadPB::new(user.id).name("");
|
let request = UpdateUserProfilePayloadPB::new(user.id).name("");
|
||||||
UserModuleEventBuilder::new(test.clone())
|
assert!(EventBuilder::new(test.clone())
|
||||||
.event(UpdateUserProfile)
|
.event(UpdateUserProfile)
|
||||||
.payload(request)
|
.payload(request)
|
||||||
.sync_send()
|
.sync_send()
|
||||||
.assert_error();
|
.error()
|
||||||
|
.is_some())
|
||||||
}
|
}
|
2
frontend/rust-lib/flowy-test/tests/user/mod.rs
Normal file
2
frontend/rust-lib/flowy-test/tests/user/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mod local_test;
|
||||||
|
mod supabase_test;
|
@ -0,0 +1,28 @@
|
|||||||
|
use crate::user::supabase_test::helper::get_supabase_config;
|
||||||
|
|
||||||
|
use flowy_test::{event_builder::EventBuilder, FlowyCoreTest};
|
||||||
|
use flowy_user::entities::{AuthTypePB, ThirdPartyAuthPB, UserProfilePB};
|
||||||
|
|
||||||
|
use flowy_user::event_map::UserEvent::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn sign_up_test() {
|
||||||
|
if get_supabase_config().is_some() {
|
||||||
|
let test = FlowyCoreTest::new();
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert("uuid".to_string(), uuid::Uuid::new_v4().to_string());
|
||||||
|
let payload = ThirdPartyAuthPB {
|
||||||
|
map,
|
||||||
|
auth_type: AuthTypePB::Supabase,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = EventBuilder::new(test.clone())
|
||||||
|
.event(ThirdPartyAuth)
|
||||||
|
.payload(payload)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<UserProfilePB>();
|
||||||
|
dbg!(&response);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
use dotenv::dotenv;
|
||||||
|
use flowy_server::supabase::SupabaseConfiguration;
|
||||||
|
|
||||||
|
/// In order to run this test, you need to create a .env file in the root directory of this project
|
||||||
|
/// and add the following environment variables:
|
||||||
|
/// - SUPABASE_URL
|
||||||
|
/// - SUPABASE_ANON_KEY
|
||||||
|
/// - SUPABASE_KEY
|
||||||
|
/// - SUPABASE_JWT_SECRET
|
||||||
|
///
|
||||||
|
/// the .env file should look like this:
|
||||||
|
/// SUPABASE_URL=https://<your-supabase-url>.supabase.co
|
||||||
|
/// SUPABASE_ANON_KEY=<your-supabase-anon-key>
|
||||||
|
/// SUPABASE_KEY=<your-supabase-key>
|
||||||
|
/// SUPABASE_JWT_SECRET=<your-supabase-jwt-secret>
|
||||||
|
///
|
||||||
|
pub fn get_supabase_config() -> Option<SupabaseConfiguration> {
|
||||||
|
dotenv().ok()?;
|
||||||
|
SupabaseConfiguration::from_env().ok()
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
mod auth_test;
|
||||||
|
mod helper;
|
||||||
|
mod workspace_test;
|
@ -0,0 +1,38 @@
|
|||||||
|
use crate::user::supabase_test::helper::get_supabase_config;
|
||||||
|
use flowy_folder2::entities::WorkspaceSettingPB;
|
||||||
|
use flowy_folder2::event_map::FolderEvent::ReadCurrentWorkspace;
|
||||||
|
|
||||||
|
use flowy_test::{event_builder::EventBuilder, FlowyCoreTest};
|
||||||
|
use flowy_user::entities::{AuthTypePB, ThirdPartyAuthPB, UserProfilePB};
|
||||||
|
|
||||||
|
use flowy_user::event_map::UserEvent::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn initial_workspace_test() {
|
||||||
|
if get_supabase_config().is_some() {
|
||||||
|
let test = FlowyCoreTest::new();
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert("uuid".to_string(), uuid::Uuid::new_v4().to_string());
|
||||||
|
let payload = ThirdPartyAuthPB {
|
||||||
|
map,
|
||||||
|
auth_type: AuthTypePB::Supabase,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = EventBuilder::new(test.clone())
|
||||||
|
.event(ThirdPartyAuth)
|
||||||
|
.payload(payload)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<UserProfilePB>();
|
||||||
|
|
||||||
|
let workspace_settings = EventBuilder::new(test.clone())
|
||||||
|
.event(ReadCurrentWorkspace)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<WorkspaceSettingPB>();
|
||||||
|
|
||||||
|
assert!(workspace_settings.latest_view.is_some());
|
||||||
|
dbg!(&workspace_settings);
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,6 @@ unicode-segmentation = "1.10"
|
|||||||
fancy-regex = "0.11.0"
|
fancy-regex = "0.11.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
flowy-test = { path = "../flowy-test" }
|
|
||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
fake = "2.0.0"
|
fake = "2.0.0"
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
|
@ -16,6 +16,7 @@ pub async fn sign_in(
|
|||||||
) -> DataResult<UserProfilePB, FlowyError> {
|
) -> DataResult<UserProfilePB, FlowyError> {
|
||||||
let params: SignInParams = data.into_inner().try_into()?;
|
let params: SignInParams = data.into_inner().try_into()?;
|
||||||
let auth_type = params.auth_type.clone();
|
let auth_type = params.auth_type.clone();
|
||||||
|
|
||||||
let user_profile: UserProfilePB = session
|
let user_profile: UserProfilePB = session
|
||||||
.sign_in(&auth_type, BoxAny::new(params))
|
.sign_in(&auth_type, BoxAny::new(params))
|
||||||
.await?
|
.await?
|
||||||
|
@ -40,6 +40,7 @@ pub trait UserStatusCallback: Send + Sync + 'static {
|
|||||||
/// The user cloud service provider.
|
/// The user cloud service provider.
|
||||||
/// The provider can be supabase, firebase, aws, or any other cloud service.
|
/// The provider can be supabase, firebase, aws, or any other cloud service.
|
||||||
pub trait UserCloudServiceProvider: Send + Sync + 'static {
|
pub trait UserCloudServiceProvider: Send + Sync + 'static {
|
||||||
|
fn set_auth_type(&self, auth_type: AuthType);
|
||||||
fn get_auth_service(&self, auth_type: &AuthType) -> Result<Arc<dyn UserAuthService>, FlowyError>;
|
fn get_auth_service(&self, auth_type: &AuthType) -> Result<Arc<dyn UserAuthService>, FlowyError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +48,10 @@ impl<T> UserCloudServiceProvider for Arc<T>
|
|||||||
where
|
where
|
||||||
T: UserCloudServiceProvider,
|
T: UserCloudServiceProvider,
|
||||||
{
|
{
|
||||||
|
fn set_auth_type(&self, auth_type: AuthType) {
|
||||||
|
(**self).set_auth_type(auth_type)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_auth_service(&self, auth_type: &AuthType) -> Result<Arc<dyn UserAuthService>, FlowyError> {
|
fn get_auth_service(&self, auth_type: &AuthType) -> Result<Arc<dyn UserAuthService>, FlowyError> {
|
||||||
(**self).get_auth_service(auth_type)
|
(**self).get_auth_service(auth_type)
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,7 @@ impl UserSession {
|
|||||||
auth_type: &AuthType,
|
auth_type: &AuthType,
|
||||||
params: BoxAny,
|
params: BoxAny,
|
||||||
) -> Result<UserProfile, FlowyError> {
|
) -> Result<UserProfile, FlowyError> {
|
||||||
|
self.cloud_services.set_auth_type(auth_type.clone());
|
||||||
let resp = self
|
let resp = self
|
||||||
.cloud_services
|
.cloud_services
|
||||||
.get_auth_service(auth_type)?
|
.get_auth_service(auth_type)?
|
||||||
@ -134,6 +135,7 @@ impl UserSession {
|
|||||||
auth_type: &AuthType,
|
auth_type: &AuthType,
|
||||||
params: BoxAny,
|
params: BoxAny,
|
||||||
) -> Result<UserProfile, FlowyError> {
|
) -> Result<UserProfile, FlowyError> {
|
||||||
|
self.cloud_services.set_auth_type(auth_type.clone());
|
||||||
let resp = self
|
let resp = self
|
||||||
.cloud_services
|
.cloud_services
|
||||||
.get_auth_service(auth_type)?
|
.get_auth_service(auth_type)?
|
||||||
@ -163,6 +165,7 @@ impl UserSession {
|
|||||||
.execute(&*(self.db_connection()?))?;
|
.execute(&*(self.db_connection()?))?;
|
||||||
self.database.close_user_db(session.user_id)?;
|
self.database.close_user_db(session.user_id)?;
|
||||||
self.set_session(None)?;
|
self.set_session(None)?;
|
||||||
|
|
||||||
let server = self.cloud_services.get_auth_service(auth_type)?;
|
let server = self.cloud_services.get_auth_service(auth_type)?;
|
||||||
let token = session.token;
|
let token = session.token;
|
||||||
let _ = tokio::spawn(async move {
|
let _ = tokio::spawn(async move {
|
||||||
|
Loading…
Reference in New Issue
Block a user