mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: save user metadata (#3754)
* feat: save user metadata * chore: update client api * refactor: separate test methods * chore: save updated at * chore: clippy * chore: fix test
This commit is contained in:
parent
16a4babdca
commit
71f80be8f7
@ -1,23 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ProtoBuf_Gen" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --manifest-path ${flowy_tool} -- pb-gen --rust_sources=${rust_lib},${shared_lib} --derive_meta=${derive_meta} --flutter_package_lib=${flutter_package_lib}" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/frontend" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs>
|
||||
<env name="flowy_tool" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/scripts/flowy-tool/Cargo.toml" />
|
||||
<env name="rust_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/" />
|
||||
<env name="shared_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/../shared_lib" />
|
||||
<env name="flutter_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/packages" />
|
||||
<env name="derive_meta" value="${shared_lib}/flowy-derive/src/derive_cache/derive_cache.rs" />
|
||||
<env name="flutter_package_lib" value="${flutter_lib}/flowy_sdk/lib" />
|
||||
</envs>
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,18 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run backend" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --package backend --bin backend" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/backend" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs>
|
||||
<env name="APP_ENVIRONMENT" value="production" />
|
||||
</envs>
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -1,16 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="dart-event" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --manifest-path $PROJECT_DIR$/scripts/flowy-tool/Cargo.toml -- dart-event --rust_source=$PROJECT_DIR$/rust-lib/ --output=$PROJECT_DIR$/appflowy_flutter/packages/flowy_sdk/lib/dispatch/dart_event.dart" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -42,10 +42,7 @@ class PluginSandbox {
|
||||
PluginConfig? config,
|
||||
}) {
|
||||
if (_pluginBuilders.containsKey(pluginType)) {
|
||||
throw PlatformException(
|
||||
code: '-1',
|
||||
message: "$pluginType was registered before",
|
||||
);
|
||||
return;
|
||||
}
|
||||
_pluginBuilders[pluginType] = builder;
|
||||
|
||||
|
@ -2,6 +2,7 @@ import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/application/user_auth_listener.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
|
||||
class InitAppFlowyCloudTask extends LaunchTask {
|
||||
final _authStateListener = UserAuthStateListener();
|
||||
@ -18,6 +19,7 @@ class InitAppFlowyCloudTask extends LaunchTask {
|
||||
isLoggingOut = false;
|
||||
},
|
||||
onInvalidAuth: (message) async {
|
||||
Log.error(message);
|
||||
await getIt<AuthService>().signOut();
|
||||
// TODO(nathan): Show a dialog to notify the user that the session is expired.
|
||||
if (!isLoggingOut) {
|
||||
|
@ -53,7 +53,7 @@ class UserAuthStateListener {
|
||||
_didSignIn?.call();
|
||||
break;
|
||||
case AuthStatePB.InvalidAuth:
|
||||
_onInvalidAuth?.call("");
|
||||
_onInvalidAuth?.call(pb.message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -14,16 +14,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
// [[diagram: splash screen]]
|
||||
// ┌────────────────┐1.get user ┌──────────┐ ┌────────────┐ 2.send UserEventCheckUser
|
||||
// │ SplashScreen │──────────▶│SplashBloc│────▶│ISplashUser │─────┐
|
||||
// └────────────────┘ └──────────┘ └────────────┘ │
|
||||
// │
|
||||
// ▼
|
||||
// ┌───────────┐ ┌─────────────┐ ┌────────┐
|
||||
// │HomeScreen │◀───────────│BlocListener │◀────────────────│RustSDK │
|
||||
// └───────────┘ └─────────────┘ └────────┘
|
||||
// 4. Show HomeScreen or SignIn 3.return AuthState
|
||||
class SplashScreen extends StatelessWidget {
|
||||
/// Root Page of the app.
|
||||
const SplashScreen({
|
||||
@ -82,7 +72,7 @@ class SplashScreen extends StatelessWidget {
|
||||
(check) async {
|
||||
/// If encryption is needed, the user is navigated to the encryption screen.
|
||||
/// Otherwise, it fetches the current workspace for the user and navigates them
|
||||
if (check.isNeedSecret) {
|
||||
if (check.requireSecret) {
|
||||
getIt<AuthRouter>().pushEncryptionScreen(context, userProfile);
|
||||
} else {
|
||||
final result = await FolderEventGetCurrentWorkspace().send();
|
||||
|
15
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
15
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -762,7 +762,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -1438,7 +1438,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -2781,7 +2781,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2797,7 +2797,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"jsonwebtoken",
|
||||
@ -3232,7 +3232,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -4915,7 +4915,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab",
|
||||
@ -5637,9 +5637,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab-entity",
|
||||
"database-entity",
|
||||
"gotrue-entity",
|
||||
"opener",
|
||||
|
@ -38,7 +38,7 @@ custom-protocol = ["tauri/custom-protocol"]
|
||||
# Run the script:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "7a309c6f69d8b34709292052e9ef0561e16c82a1" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "141354bfa9d08c387cf9beb9697b89b052790e89" }
|
||||
# Please use the following script to update collab.
|
||||
# Working directory: frontend
|
||||
#
|
||||
|
33
frontend/rust-lib/Cargo.lock
generated
33
frontend/rust-lib/Cargo.lock
generated
@ -660,7 +660,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -1138,7 +1138,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa",
|
||||
"phf 0.8.0",
|
||||
"phf 0.11.2",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -1265,7 +1265,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -2440,7 +2440,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2456,7 +2456,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"jsonwebtoken",
|
||||
@ -2816,7 +2816,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -3623,7 +3623,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_macros 0.8.0",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
@ -3643,6 +3643,7 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -3710,6 +3711,19 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.31",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -4251,7 +4265,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab",
|
||||
@ -4872,9 +4886,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=7a309c6f69d8b34709292052e9ef0561e16c82a1#7a309c6f69d8b34709292052e9ef0561e16c82a1"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab-entity",
|
||||
"database-entity",
|
||||
"gotrue-entity",
|
||||
"opener",
|
||||
|
@ -82,7 +82,7 @@ incremental = false
|
||||
# Run the script:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "7a309c6f69d8b34709292052e9ef0561e16c82a1" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "141354bfa9d08c387cf9beb9697b89b052790e89" }
|
||||
# Please use the following script to update collab.
|
||||
# Working directory: frontend
|
||||
#
|
||||
|
@ -1,18 +1,20 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use flowy_document2::entities::*;
|
||||
use flowy_document2::event_map::DocumentEvent;
|
||||
use flowy_folder2::entities::{CreateViewPayloadPB, ViewLayoutPB, ViewPB};
|
||||
use flowy_folder2::event_map::FolderEvent;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::document::utils::{gen_delta_str, gen_id, gen_text_block_data};
|
||||
use crate::event_builder::EventBuilder;
|
||||
use crate::FlowyCoreTest;
|
||||
use crate::EventIntegrationTest;
|
||||
|
||||
const TEXT_BLOCK_TY: &str = "paragraph";
|
||||
|
||||
pub struct DocumentEventTest {
|
||||
inner: FlowyCoreTest,
|
||||
inner: EventIntegrationTest,
|
||||
}
|
||||
|
||||
pub struct OpenDocumentData {
|
||||
@ -22,11 +24,11 @@ pub struct OpenDocumentData {
|
||||
|
||||
impl DocumentEventTest {
|
||||
pub async fn new() -> Self {
|
||||
let sdk = FlowyCoreTest::new_with_guest_user().await;
|
||||
let sdk = EventIntegrationTest::new_with_guest_user().await;
|
||||
Self { inner: sdk }
|
||||
}
|
||||
|
||||
pub fn new_with_core(core: FlowyCoreTest) -> Self {
|
||||
pub fn new_with_core(core: EventIntegrationTest) -> Self {
|
||||
Self { inner: core }
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use lib_dispatch::prelude::{
|
||||
AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, ToBytes, *,
|
||||
};
|
||||
|
||||
use crate::FlowyCoreTest;
|
||||
use crate::EventIntegrationTest;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventBuilder {
|
||||
@ -18,7 +18,7 @@ pub struct EventBuilder {
|
||||
}
|
||||
|
||||
impl EventBuilder {
|
||||
pub fn new(sdk: FlowyCoreTest) -> Self {
|
||||
pub fn new(sdk: EventIntegrationTest) -> Self {
|
||||
Self {
|
||||
context: TestContext::new(sdk),
|
||||
}
|
||||
@ -121,13 +121,13 @@ impl EventBuilder {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestContext {
|
||||
pub sdk: FlowyCoreTest,
|
||||
pub sdk: EventIntegrationTest,
|
||||
request: Option<AFPluginRequest>,
|
||||
response: Option<AFPluginEventResponse>,
|
||||
}
|
||||
|
||||
impl TestContext {
|
||||
pub fn new(sdk: FlowyCoreTest) -> Self {
|
||||
pub fn new(sdk: EventIntegrationTest) -> Self {
|
||||
Self {
|
||||
sdk,
|
||||
request: None,
|
||||
|
@ -1,115 +0,0 @@
|
||||
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 {
|
||||
parent_view_id: workspace_id.to_owned(),
|
||||
name: name.to_string(),
|
||||
desc: desc.to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Document,
|
||||
initial_data: vec![],
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
|
||||
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 {
|
||||
parent_view_id: app_id.to_string(),
|
||||
name: "View A".to_string(),
|
||||
desc: "".to_string(),
|
||||
thumbnail: Some("http://1.png".to_string()),
|
||||
layout,
|
||||
initial_data: data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
|
||||
EventBuilder::new(sdk.clone())
|
||||
.event(CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
@ -1,59 +1,33 @@
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::env::temp_dir;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytes::Bytes;
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab::core::origin::CollabOrigin;
|
||||
use collab::preclude::updates::decoder::Decode;
|
||||
use collab::preclude::{merge_updates_v1, Update};
|
||||
use collab_document::blocks::DocumentData;
|
||||
use collab_document::document::Document;
|
||||
use nanoid::nanoid;
|
||||
use parking_lot::RwLock;
|
||||
use protobuf::ProtobufError;
|
||||
use tokio::sync::broadcast::{channel, Sender};
|
||||
use uuid::Uuid;
|
||||
|
||||
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
|
||||
use flowy_database2::entities::*;
|
||||
use flowy_database2::event_map::DatabaseEvent;
|
||||
use flowy_document2::entities::{DocumentDataPB, OpenDocumentPayloadPB};
|
||||
use flowy_document2::event_map::DocumentEvent;
|
||||
use flowy_folder2::entities::icon::UpdateViewIconPayloadPB;
|
||||
use flowy_folder2::entities::*;
|
||||
use flowy_folder2::event_map::FolderEvent;
|
||||
use flowy_notification::entities::SubscribeObject;
|
||||
use flowy_notification::{register_notification_sender, NotificationSender};
|
||||
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID};
|
||||
use flowy_user::entities::{
|
||||
AuthTypePB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, UpdateCloudConfigPB,
|
||||
UserCloudConfigPB, UserProfilePB,
|
||||
};
|
||||
use flowy_user::errors::{FlowyError, FlowyResult};
|
||||
use flowy_user::event_map::UserEvent::*;
|
||||
use flowy_notification::register_notification_sender;
|
||||
use flowy_user::entities::AuthTypePB;
|
||||
|
||||
use crate::document::document_event::{DocumentEventTest, OpenDocumentData};
|
||||
use crate::event_builder::EventBuilder;
|
||||
use crate::user_event::{async_sign_up, SignUpContext};
|
||||
use crate::test_user::TestNotificationSender;
|
||||
|
||||
pub mod document;
|
||||
pub mod event_builder;
|
||||
pub mod folder_event;
|
||||
pub mod user_event;
|
||||
pub mod test_database;
|
||||
pub mod test_document;
|
||||
pub mod test_folder;
|
||||
pub mod test_user;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FlowyCoreTest {
|
||||
auth_type: Arc<RwLock<AuthTypePB>>,
|
||||
inner: AppFlowyCore,
|
||||
pub struct EventIntegrationTest {
|
||||
pub auth_type: Arc<RwLock<AuthTypePB>>,
|
||||
pub inner: AppFlowyCore,
|
||||
#[allow(dead_code)]
|
||||
cleaner: Arc<Cleaner>,
|
||||
pub notification_sender: TestNotificationSender,
|
||||
}
|
||||
|
||||
impl Default for FlowyCoreTest {
|
||||
impl Default for EventIntegrationTest {
|
||||
fn default() -> Self {
|
||||
let temp_dir = temp_dir().join(nanoid!(6));
|
||||
std::fs::create_dir_all(&temp_dir).unwrap();
|
||||
@ -61,51 +35,10 @@ impl Default for FlowyCoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowyCoreTest {
|
||||
impl EventIntegrationTest {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub async fn insert_document_text(&self, document_id: &str, text: &str, index: usize) {
|
||||
let document_event = DocumentEventTest::new_with_core(self.clone());
|
||||
document_event
|
||||
.insert_index(document_id, text, index, None)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn get_document_data(&self, view_id: &str) -> DocumentData {
|
||||
let pb = EventBuilder::new(self.clone())
|
||||
.event(DocumentEvent::GetDocumentData)
|
||||
.payload(OpenDocumentPayloadPB {
|
||||
document_id: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<DocumentDataPB>();
|
||||
|
||||
DocumentData::from(pb)
|
||||
}
|
||||
|
||||
pub async fn get_document_update(&self, document_id: &str) -> Vec<u8> {
|
||||
let workspace_id = self.user_manager.workspace_id().unwrap();
|
||||
let cloud_service = self.document_manager.get_cloud_service().clone();
|
||||
let remote_updates = cloud_service
|
||||
.get_document_updates(document_id, &workspace_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if remote_updates.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let updates = remote_updates
|
||||
.iter()
|
||||
.map(|update| update.as_ref())
|
||||
.collect::<Vec<&[u8]>>();
|
||||
|
||||
merge_updates_v1(&updates).unwrap()
|
||||
}
|
||||
|
||||
pub fn new_with_user_data_path(path: PathBuf, name: String) -> Self {
|
||||
let config = AppFlowyCoreConfig::new(path.to_str().unwrap(), name).log_filter(
|
||||
"trace",
|
||||
@ -129,711 +62,9 @@ impl FlowyCoreTest {
|
||||
cleaner: Arc::new(Cleaner(path)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn enable_encryption(&self) -> String {
|
||||
let config = EventBuilder::new(self.clone())
|
||||
.event(GetCloudConfig)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<UserCloudConfigPB>();
|
||||
let update = UpdateCloudConfigPB {
|
||||
enable_sync: None,
|
||||
enable_encrypt: Some(true),
|
||||
};
|
||||
let error = EventBuilder::new(self.clone())
|
||||
.event(SetCloudConfig)
|
||||
.payload(update)
|
||||
.async_send()
|
||||
.await
|
||||
.error();
|
||||
assert!(error.is_none());
|
||||
config.encrypt_secret
|
||||
}
|
||||
|
||||
pub async fn get_user_profile(&self) -> Result<UserProfilePB, FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(GetUserProfile)
|
||||
.async_send()
|
||||
.await
|
||||
.try_parse::<UserProfilePB>()
|
||||
}
|
||||
|
||||
pub async fn new_with_guest_user() -> Self {
|
||||
let test = Self::default();
|
||||
test.sign_up_as_guest().await;
|
||||
test
|
||||
}
|
||||
|
||||
pub async fn sign_up_as_guest(&self) -> SignUpContext {
|
||||
async_sign_up(self.inner.dispatcher(), AuthTypePB::Local).await
|
||||
}
|
||||
|
||||
pub async fn supabase_party_sign_up(&self) -> UserProfilePB {
|
||||
let map = third_party_sign_up_param(Uuid::new_v4().to_string());
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthTypePB::Supabase,
|
||||
};
|
||||
|
||||
EventBuilder::new(self.clone())
|
||||
.event(OauthSignIn)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<UserProfilePB>()
|
||||
}
|
||||
|
||||
pub async fn sign_out(&self) {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(SignOut)
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn set_auth_type(&self, auth_type: AuthTypePB) {
|
||||
*self.auth_type.write() = auth_type;
|
||||
}
|
||||
|
||||
pub async fn init_user(&self) -> UserProfilePB {
|
||||
self.sign_up_as_guest().await.user_profile
|
||||
}
|
||||
|
||||
pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
|
||||
let payload = SignInUrlPayloadPB {
|
||||
email: email.to_string(),
|
||||
auth_type: AuthTypePB::AFCloud,
|
||||
};
|
||||
let sign_in_url = EventBuilder::new(self.clone())
|
||||
.event(GetSignInURL)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.try_parse::<SignInUrlPB>()?
|
||||
.sign_in_url;
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(USER_SIGN_IN_URL.to_string(), sign_in_url);
|
||||
map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string());
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthTypePB::AFCloud,
|
||||
};
|
||||
|
||||
let user_profile = EventBuilder::new(self.clone())
|
||||
.event(OauthSignIn)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.try_parse::<UserProfilePB>()?;
|
||||
|
||||
Ok(user_profile)
|
||||
}
|
||||
|
||||
pub async fn supabase_sign_up_with_uuid(
|
||||
&self,
|
||||
uuid: &str,
|
||||
email: Option<String>,
|
||||
) -> FlowyResult<UserProfilePB> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(USER_UUID.to_string(), uuid.to_string());
|
||||
map.insert(USER_DEVICE_ID.to_string(), uuid.to_string());
|
||||
map.insert(
|
||||
USER_EMAIL.to_string(),
|
||||
email.unwrap_or_else(|| format!("{}@appflowy.io", nanoid!(10))),
|
||||
);
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthTypePB::Supabase,
|
||||
};
|
||||
|
||||
let user_profile = EventBuilder::new(self.clone())
|
||||
.event(OauthSignIn)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.try_parse::<UserProfilePB>()?;
|
||||
|
||||
Ok(user_profile)
|
||||
}
|
||||
|
||||
// Must sign up/ sign in first
|
||||
pub async fn get_current_workspace(&self) -> WorkspaceSettingPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::GetCurrentWorkspace)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<WorkspaceSettingPB>()
|
||||
}
|
||||
|
||||
pub async fn get_all_workspace_views(&self) -> Vec<ViewPB> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::ReadWorkspaceViews)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RepeatedViewPB>()
|
||||
.items
|
||||
}
|
||||
|
||||
pub async fn get_views(&self, parent_view_id: &str) -> ViewPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::ReadView)
|
||||
.payload(ViewIdPB {
|
||||
value: parent_view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn delete_view(&self, view_id: &str) {
|
||||
let payload = RepeatedViewIdPB {
|
||||
items: vec![view_id.to_string()],
|
||||
};
|
||||
|
||||
// delete the view. the view will be moved to trash
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::DeleteView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn update_view(&self, changeset: UpdateViewPayloadPB) -> Option<FlowyError> {
|
||||
// delete the view. the view will be moved to trash
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::UpdateView)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_view_icon(&self, payload: UpdateViewIconPayloadPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::UpdateViewIcon)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn create_view(&self, parent_id: &str, name: String) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: Default::default(),
|
||||
initial_data: vec![],
|
||||
meta: Default::default(),
|
||||
set_as_current: false,
|
||||
index: None,
|
||||
};
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn create_document(
|
||||
&self,
|
||||
parent_id: &str,
|
||||
name: String,
|
||||
initial_data: Vec<u8>,
|
||||
) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Document,
|
||||
initial_data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
let view = EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>();
|
||||
|
||||
let payload = OpenDocumentPayloadPB {
|
||||
document_id: view.id.clone(),
|
||||
};
|
||||
|
||||
let _ = EventBuilder::new(self.clone())
|
||||
.event(DocumentEvent::OpenDocument)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<DocumentDataPB>();
|
||||
|
||||
view
|
||||
}
|
||||
|
||||
pub async fn create_grid(&self, parent_id: &str, name: String, initial_data: Vec<u8>) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Grid,
|
||||
initial_data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn open_database(&self, view_id: &str) {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetDatabase)
|
||||
.payload(DatabaseViewIdPB {
|
||||
value: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn open_document(&self, doc_id: String) -> OpenDocumentData {
|
||||
let payload = OpenDocumentPayloadPB {
|
||||
document_id: doc_id.clone(),
|
||||
};
|
||||
let data = EventBuilder::new(self.clone())
|
||||
.event(DocumentEvent::OpenDocument)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<DocumentDataPB>();
|
||||
OpenDocumentData { id: doc_id, data }
|
||||
}
|
||||
|
||||
pub async fn create_board(&self, parent_id: &str, name: String, initial_data: Vec<u8>) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Board,
|
||||
initial_data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn create_calendar(
|
||||
&self,
|
||||
parent_id: &str,
|
||||
name: String,
|
||||
initial_data: Vec<u8>,
|
||||
) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Calendar,
|
||||
initial_data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn get_database(&self, view_id: &str) -> DatabasePB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetDatabase)
|
||||
.payload(DatabaseViewIdPB {
|
||||
value: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<DatabasePB>()
|
||||
}
|
||||
|
||||
pub async fn get_all_database_fields(&self, view_id: &str) -> RepeatedFieldPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetFields)
|
||||
.payload(GetFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_ids: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RepeatedFieldPB>()
|
||||
}
|
||||
|
||||
pub async fn create_field(&self, view_id: &str, field_type: FieldType) -> FieldPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::CreateTypeOption)
|
||||
.payload(CreateFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_type,
|
||||
type_option_data: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<TypeOptionPB>()
|
||||
.field
|
||||
}
|
||||
|
||||
pub async fn update_field(&self, changeset: FieldChangesetPB) {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateField)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn delete_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::DeleteField)
|
||||
.payload(DeleteFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_field_type(
|
||||
&self,
|
||||
view_id: &str,
|
||||
field_id: &str,
|
||||
field_type: FieldType,
|
||||
) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateFieldType)
|
||||
.payload(UpdateFieldTypePayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
field_type,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn duplicate_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::DuplicateField)
|
||||
.payload(DuplicateFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_primary_field(&self, database_view_id: &str) -> FieldPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetPrimaryField)
|
||||
.payload(DatabaseViewIdPB {
|
||||
value: database_view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<FieldPB>()
|
||||
}
|
||||
|
||||
pub async fn create_row(
|
||||
&self,
|
||||
view_id: &str,
|
||||
start_row_id: Option<String>,
|
||||
data: Option<RowDataPB>,
|
||||
) -> RowMetaPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::CreateRow)
|
||||
.payload(CreateRowPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
start_row_id,
|
||||
group_id: None,
|
||||
data,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RowMetaPB>()
|
||||
}
|
||||
|
||||
pub async fn delete_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::DeleteRow)
|
||||
.payload(RowIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
group_id: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_row(&self, view_id: &str, row_id: &str) -> OptionalRowPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetRow)
|
||||
.payload(RowIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
group_id: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<OptionalRowPB>()
|
||||
}
|
||||
|
||||
pub async fn get_row_meta(&self, view_id: &str, row_id: &str) -> RowMetaPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetRowMeta)
|
||||
.payload(RowIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
group_id: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RowMetaPB>()
|
||||
}
|
||||
|
||||
pub async fn update_row_meta(&self, changeset: UpdateRowMetaChangesetPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateRowMeta)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::DuplicateRow)
|
||||
.payload(RowIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
group_id: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn move_row(&self, view_id: &str, row_id: &str, to_row_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::MoveRow)
|
||||
.payload(MoveRowPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
from_row_id: row_id.to_string(),
|
||||
to_row_id: to_row_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_cell(&self, changeset: CellChangesetPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateCell)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_date_cell(&self, changeset: DateChangesetPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateDateCell)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> CellPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetCell)
|
||||
.payload(CellIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<CellPB>()
|
||||
}
|
||||
|
||||
pub async fn get_date_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> DateCellDataPB {
|
||||
let cell = self.get_cell(view_id, row_id, field_id).await;
|
||||
DateCellDataPB::try_from(Bytes::from(cell.data)).unwrap()
|
||||
}
|
||||
|
||||
pub async fn get_checklist_cell(
|
||||
&self,
|
||||
view_id: &str,
|
||||
field_id: &str,
|
||||
row_id: &str,
|
||||
) -> ChecklistCellDataPB {
|
||||
let cell = self.get_cell(view_id, row_id, field_id).await;
|
||||
ChecklistCellDataPB::try_from(Bytes::from(cell.data)).unwrap()
|
||||
}
|
||||
|
||||
pub async fn update_checklist_cell(
|
||||
&self,
|
||||
changeset: ChecklistCellDataChangesetPB,
|
||||
) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateChecklistCell)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn insert_option(
|
||||
&self,
|
||||
view_id: &str,
|
||||
field_id: &str,
|
||||
row_id: &str,
|
||||
name: &str,
|
||||
) -> Option<FlowyError> {
|
||||
let option = EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::CreateSelectOption)
|
||||
.payload(CreateSelectOptionPayloadPB {
|
||||
field_id: field_id.to_string(),
|
||||
view_id: view_id.to_string(),
|
||||
option_name: name.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<SelectOptionPB>();
|
||||
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::InsertOrUpdateSelectOption)
|
||||
.payload(RepeatedSelectOptionPayload {
|
||||
view_id: view_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
items: vec![option],
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_groups(&self, view_id: &str) -> Vec<GroupPB> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetGroups)
|
||||
.payload(DatabaseViewIdPB {
|
||||
value: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RepeatedGroupPB>()
|
||||
.items
|
||||
}
|
||||
|
||||
pub async fn move_group(&self, view_id: &str, from_id: &str, to_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::MoveGroup)
|
||||
.payload(MoveGroupPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
from_group_id: from_id.to_string(),
|
||||
to_group_id: to_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::SetGroupByField)
|
||||
.payload(GroupByFieldPayloadPB {
|
||||
field_id: field_id.to_string(),
|
||||
view_id: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_group(
|
||||
&self,
|
||||
view_id: &str,
|
||||
group_id: &str,
|
||||
name: Option<String>,
|
||||
visible: Option<bool>,
|
||||
) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateGroup)
|
||||
.payload(UpdateGroupPB {
|
||||
view_id: view_id.to_string(),
|
||||
group_id: group_id.to_string(),
|
||||
name,
|
||||
visible,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_setting(&self, changeset: DatabaseSettingChangesetPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateDatabaseSetting)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_all_calendar_events(&self, view_id: &str) -> Vec<CalendarEventPB> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetAllCalendarEvents)
|
||||
.payload(CalendarEventRequestPB {
|
||||
view_id: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RepeatedCalendarEventPB>()
|
||||
.items
|
||||
}
|
||||
|
||||
pub async fn get_view(&self, view_id: &str) -> ViewPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::ReadView)
|
||||
.payload(ViewIdPB {
|
||||
value: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for FlowyCoreTest {
|
||||
impl std::ops::Deref for EventIntegrationTest {
|
||||
type Target = AppFlowyCore;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -841,89 +72,6 @@ impl std::ops::Deref for FlowyCoreTest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestNotificationSender {
|
||||
sender: Arc<Sender<SubscribeObject>>,
|
||||
}
|
||||
|
||||
impl Default for TestNotificationSender {
|
||||
fn default() -> Self {
|
||||
let (sender, _) = channel(1000);
|
||||
Self {
|
||||
sender: Arc::new(sender),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TestNotificationSender {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn subscribe<T>(&self, id: &str, ty: impl Into<i32> + Send) -> tokio::sync::mpsc::Receiver<T>
|
||||
where
|
||||
T: TryFrom<Bytes, Error = ProtobufError> + Send + 'static,
|
||||
{
|
||||
let id = id.to_string();
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<T>(10);
|
||||
let mut receiver = self.sender.subscribe();
|
||||
let ty = ty.into();
|
||||
tokio::spawn(async move {
|
||||
// DatabaseNotification::DidUpdateDatabaseSnapshotState
|
||||
while let Ok(value) = receiver.recv().await {
|
||||
if value.id == id && value.ty == ty {
|
||||
if let Some(payload) = value.payload {
|
||||
match T::try_from(Bytes::from(payload)) {
|
||||
Ok(object) => {
|
||||
let _ = tx.send(object).await;
|
||||
},
|
||||
Err(e) => {
|
||||
panic!(
|
||||
"Failed to parse notification payload to type: {:?} with error: {}",
|
||||
std::any::type_name::<T>(),
|
||||
e
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
rx
|
||||
}
|
||||
|
||||
pub fn subscribe_with_condition<T, F>(&self, id: &str, when: F) -> tokio::sync::mpsc::Receiver<T>
|
||||
where
|
||||
T: TryFrom<Bytes, Error = ProtobufError> + Send + 'static,
|
||||
F: Fn(&T) -> bool + Send + 'static,
|
||||
{
|
||||
let id = id.to_string();
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<T>(10);
|
||||
let mut receiver = self.sender.subscribe();
|
||||
tokio::spawn(async move {
|
||||
while let Ok(value) = receiver.recv().await {
|
||||
if value.id == id {
|
||||
if let Some(payload) = value.payload {
|
||||
if let Ok(object) = T::try_from(Bytes::from(payload)) {
|
||||
if when(&object) {
|
||||
let _ = tx.send(object).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
rx
|
||||
}
|
||||
}
|
||||
|
||||
impl NotificationSender for TestNotificationSender {
|
||||
fn send_subject(&self, subject: SubscribeObject) -> Result<(), String> {
|
||||
let _ = self.sender.send(subject);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Cleaner(PathBuf);
|
||||
|
||||
impl Cleaner {
|
||||
@ -941,25 +89,3 @@ impl Drop for Cleaner {
|
||||
Self::cleanup(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn third_party_sign_up_param(uuid: String) -> HashMap<String, String> {
|
||||
let mut params = HashMap::new();
|
||||
params.insert(USER_UUID.to_string(), uuid);
|
||||
params.insert(
|
||||
USER_EMAIL.to_string(),
|
||||
format!("{}@test.com", Uuid::new_v4()),
|
||||
);
|
||||
params.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string());
|
||||
params
|
||||
}
|
||||
|
||||
pub fn assert_document_data_equal(collab_update: &[u8], doc_id: &str, expected: DocumentData) {
|
||||
let collab = MutexCollab::new(CollabOrigin::Server, doc_id, vec![]);
|
||||
collab.lock().with_origin_transact_mut(|txn| {
|
||||
let update = Update::decode_v1(collab_update).unwrap();
|
||||
txn.apply_update(update);
|
||||
});
|
||||
let document = Document::open(Arc::new(collab)).unwrap();
|
||||
let actual = document.get_document_data().unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
447
frontend/rust-lib/event-integration/src/test_database.rs
Normal file
447
frontend/rust-lib/event-integration/src/test_database.rs
Normal file
@ -0,0 +1,447 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use flowy_database2::entities::*;
|
||||
use flowy_database2::event_map::DatabaseEvent;
|
||||
use flowy_folder2::entities::*;
|
||||
use flowy_folder2::event_map::FolderEvent;
|
||||
use flowy_user::errors::FlowyError;
|
||||
|
||||
use crate::event_builder::EventBuilder;
|
||||
use crate::EventIntegrationTest;
|
||||
|
||||
impl EventIntegrationTest {
|
||||
pub async fn create_grid(&self, parent_id: &str, name: String, initial_data: Vec<u8>) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Grid,
|
||||
initial_data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn open_database(&self, view_id: &str) {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetDatabase)
|
||||
.payload(DatabaseViewIdPB {
|
||||
value: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn create_board(&self, parent_id: &str, name: String, initial_data: Vec<u8>) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Board,
|
||||
initial_data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn create_calendar(
|
||||
&self,
|
||||
parent_id: &str,
|
||||
name: String,
|
||||
initial_data: Vec<u8>,
|
||||
) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Calendar,
|
||||
initial_data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn get_database(&self, view_id: &str) -> DatabasePB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetDatabase)
|
||||
.payload(DatabaseViewIdPB {
|
||||
value: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<DatabasePB>()
|
||||
}
|
||||
|
||||
pub async fn get_all_database_fields(&self, view_id: &str) -> RepeatedFieldPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetFields)
|
||||
.payload(GetFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_ids: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RepeatedFieldPB>()
|
||||
}
|
||||
|
||||
pub async fn create_field(&self, view_id: &str, field_type: FieldType) -> FieldPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::CreateTypeOption)
|
||||
.payload(CreateFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_type,
|
||||
type_option_data: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<TypeOptionPB>()
|
||||
.field
|
||||
}
|
||||
|
||||
pub async fn update_field(&self, changeset: FieldChangesetPB) {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateField)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn delete_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::DeleteField)
|
||||
.payload(DeleteFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_field_type(
|
||||
&self,
|
||||
view_id: &str,
|
||||
field_id: &str,
|
||||
field_type: FieldType,
|
||||
) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateFieldType)
|
||||
.payload(UpdateFieldTypePayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
field_type,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn duplicate_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::DuplicateField)
|
||||
.payload(DuplicateFieldPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_primary_field(&self, database_view_id: &str) -> FieldPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetPrimaryField)
|
||||
.payload(DatabaseViewIdPB {
|
||||
value: database_view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<FieldPB>()
|
||||
}
|
||||
|
||||
pub async fn create_row(
|
||||
&self,
|
||||
view_id: &str,
|
||||
start_row_id: Option<String>,
|
||||
data: Option<RowDataPB>,
|
||||
) -> RowMetaPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::CreateRow)
|
||||
.payload(CreateRowPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
start_row_id,
|
||||
group_id: None,
|
||||
data,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RowMetaPB>()
|
||||
}
|
||||
|
||||
pub async fn delete_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::DeleteRow)
|
||||
.payload(RowIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
group_id: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_row(&self, view_id: &str, row_id: &str) -> OptionalRowPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetRow)
|
||||
.payload(RowIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
group_id: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<OptionalRowPB>()
|
||||
}
|
||||
|
||||
pub async fn get_row_meta(&self, view_id: &str, row_id: &str) -> RowMetaPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetRowMeta)
|
||||
.payload(RowIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
group_id: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RowMetaPB>()
|
||||
}
|
||||
|
||||
pub async fn update_row_meta(&self, changeset: UpdateRowMetaChangesetPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateRowMeta)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn duplicate_row(&self, view_id: &str, row_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::DuplicateRow)
|
||||
.payload(RowIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
group_id: None,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn move_row(&self, view_id: &str, row_id: &str, to_row_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::MoveRow)
|
||||
.payload(MoveRowPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
from_row_id: row_id.to_string(),
|
||||
to_row_id: to_row_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_cell(&self, changeset: CellChangesetPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateCell)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_date_cell(&self, changeset: DateChangesetPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateDateCell)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> CellPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetCell)
|
||||
.payload(CellIdPB {
|
||||
view_id: view_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<CellPB>()
|
||||
}
|
||||
|
||||
pub async fn get_date_cell(&self, view_id: &str, row_id: &str, field_id: &str) -> DateCellDataPB {
|
||||
let cell = self.get_cell(view_id, row_id, field_id).await;
|
||||
DateCellDataPB::try_from(Bytes::from(cell.data)).unwrap()
|
||||
}
|
||||
|
||||
pub async fn get_checklist_cell(
|
||||
&self,
|
||||
view_id: &str,
|
||||
field_id: &str,
|
||||
row_id: &str,
|
||||
) -> ChecklistCellDataPB {
|
||||
let cell = self.get_cell(view_id, row_id, field_id).await;
|
||||
ChecklistCellDataPB::try_from(Bytes::from(cell.data)).unwrap()
|
||||
}
|
||||
|
||||
pub async fn update_checklist_cell(
|
||||
&self,
|
||||
changeset: ChecklistCellDataChangesetPB,
|
||||
) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateChecklistCell)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn insert_option(
|
||||
&self,
|
||||
view_id: &str,
|
||||
field_id: &str,
|
||||
row_id: &str,
|
||||
name: &str,
|
||||
) -> Option<FlowyError> {
|
||||
let option = EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::CreateSelectOption)
|
||||
.payload(CreateSelectOptionPayloadPB {
|
||||
field_id: field_id.to_string(),
|
||||
view_id: view_id.to_string(),
|
||||
option_name: name.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<SelectOptionPB>();
|
||||
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::InsertOrUpdateSelectOption)
|
||||
.payload(RepeatedSelectOptionPayload {
|
||||
view_id: view_id.to_string(),
|
||||
field_id: field_id.to_string(),
|
||||
row_id: row_id.to_string(),
|
||||
items: vec![option],
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_groups(&self, view_id: &str) -> Vec<GroupPB> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetGroups)
|
||||
.payload(DatabaseViewIdPB {
|
||||
value: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RepeatedGroupPB>()
|
||||
.items
|
||||
}
|
||||
|
||||
pub async fn move_group(&self, view_id: &str, from_id: &str, to_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::MoveGroup)
|
||||
.payload(MoveGroupPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
from_group_id: from_id.to_string(),
|
||||
to_group_id: to_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn set_group_by_field(&self, view_id: &str, field_id: &str) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::SetGroupByField)
|
||||
.payload(GroupByFieldPayloadPB {
|
||||
field_id: field_id.to_string(),
|
||||
view_id: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_group(
|
||||
&self,
|
||||
view_id: &str,
|
||||
group_id: &str,
|
||||
name: Option<String>,
|
||||
visible: Option<bool>,
|
||||
) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateGroup)
|
||||
.payload(UpdateGroupPB {
|
||||
view_id: view_id.to_string(),
|
||||
group_id: group_id.to_string(),
|
||||
name,
|
||||
visible,
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_setting(&self, changeset: DatabaseSettingChangesetPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::UpdateDatabaseSetting)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn get_all_calendar_events(&self, view_id: &str) -> Vec<CalendarEventPB> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(DatabaseEvent::GetAllCalendarEvents)
|
||||
.payload(CalendarEventRequestPB {
|
||||
view_id: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RepeatedCalendarEventPB>()
|
||||
.items
|
||||
}
|
||||
}
|
119
frontend/rust-lib/event-integration/src/test_document.rs
Normal file
119
frontend/rust-lib/event-integration/src/test_document.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab::core::collab::MutexCollab;
|
||||
use collab::core::origin::CollabOrigin;
|
||||
use collab::preclude::updates::decoder::Decode;
|
||||
use collab::preclude::{merge_updates_v1, Update};
|
||||
use collab_document::blocks::DocumentData;
|
||||
use collab_document::document::Document;
|
||||
|
||||
use flowy_document2::entities::{DocumentDataPB, OpenDocumentPayloadPB};
|
||||
use flowy_document2::event_map::DocumentEvent;
|
||||
use flowy_folder2::entities::{CreateViewPayloadPB, ViewLayoutPB, ViewPB};
|
||||
use flowy_folder2::event_map::FolderEvent;
|
||||
|
||||
use crate::document::document_event::{DocumentEventTest, OpenDocumentData};
|
||||
use crate::event_builder::EventBuilder;
|
||||
use crate::EventIntegrationTest;
|
||||
|
||||
impl EventIntegrationTest {
|
||||
pub async fn create_document(
|
||||
&self,
|
||||
parent_id: &str,
|
||||
name: String,
|
||||
initial_data: Vec<u8>,
|
||||
) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: ViewLayoutPB::Document,
|
||||
initial_data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
let view = EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>();
|
||||
|
||||
let payload = OpenDocumentPayloadPB {
|
||||
document_id: view.id.clone(),
|
||||
};
|
||||
|
||||
let _ = EventBuilder::new(self.clone())
|
||||
.event(DocumentEvent::OpenDocument)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<DocumentDataPB>();
|
||||
|
||||
view
|
||||
}
|
||||
pub async fn open_document(&self, doc_id: String) -> OpenDocumentData {
|
||||
let payload = OpenDocumentPayloadPB {
|
||||
document_id: doc_id.clone(),
|
||||
};
|
||||
let data = EventBuilder::new(self.clone())
|
||||
.event(DocumentEvent::OpenDocument)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<DocumentDataPB>();
|
||||
OpenDocumentData { id: doc_id, data }
|
||||
}
|
||||
pub async fn insert_document_text(&self, document_id: &str, text: &str, index: usize) {
|
||||
let document_event = DocumentEventTest::new_with_core(self.clone());
|
||||
document_event
|
||||
.insert_index(document_id, text, index, None)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn get_document_data(&self, view_id: &str) -> DocumentData {
|
||||
let pb = EventBuilder::new(self.clone())
|
||||
.event(DocumentEvent::GetDocumentData)
|
||||
.payload(OpenDocumentPayloadPB {
|
||||
document_id: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<DocumentDataPB>();
|
||||
|
||||
DocumentData::from(pb)
|
||||
}
|
||||
|
||||
pub async fn get_document_update(&self, document_id: &str) -> Vec<u8> {
|
||||
let workspace_id = self.user_manager.workspace_id().unwrap();
|
||||
let cloud_service = self.document_manager.get_cloud_service().clone();
|
||||
let remote_updates = cloud_service
|
||||
.get_document_updates(document_id, &workspace_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if remote_updates.is_empty() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let updates = remote_updates
|
||||
.iter()
|
||||
.map(|update| update.as_ref())
|
||||
.collect::<Vec<&[u8]>>();
|
||||
|
||||
merge_updates_v1(&updates).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_document_data_equal(collab_update: &[u8], doc_id: &str, expected: DocumentData) {
|
||||
let collab = MutexCollab::new(CollabOrigin::Server, doc_id, vec![]);
|
||||
collab.lock().with_origin_transact_mut(|txn| {
|
||||
let update = Update::decode_v1(collab_update).unwrap();
|
||||
txn.apply_update(update);
|
||||
});
|
||||
let document = Document::open(Arc::new(collab)).unwrap();
|
||||
let actual = document.get_document_data().unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
173
frontend/rust-lib/event-integration/src/test_folder.rs
Normal file
173
frontend/rust-lib/event-integration/src/test_folder.rs
Normal file
@ -0,0 +1,173 @@
|
||||
use flowy_folder2::entities::icon::UpdateViewIconPayloadPB;
|
||||
use flowy_folder2::entities::*;
|
||||
use flowy_folder2::event_map::FolderEvent;
|
||||
use flowy_folder2::event_map::FolderEvent::*;
|
||||
use flowy_user::errors::FlowyError;
|
||||
|
||||
use crate::event_builder::EventBuilder;
|
||||
use crate::EventIntegrationTest;
|
||||
|
||||
impl EventIntegrationTest {
|
||||
// Must sign up/ sign in first
|
||||
pub async fn get_current_workspace(&self) -> WorkspaceSettingPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::GetCurrentWorkspace)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<WorkspaceSettingPB>()
|
||||
}
|
||||
|
||||
pub async fn get_all_workspace_views(&self) -> Vec<ViewPB> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::ReadWorkspaceViews)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<RepeatedViewPB>()
|
||||
.items
|
||||
}
|
||||
|
||||
pub async fn get_views(&self, parent_view_id: &str) -> ViewPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::ReadView)
|
||||
.payload(ViewIdPB {
|
||||
value: parent_view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn delete_view(&self, view_id: &str) {
|
||||
let payload = RepeatedViewIdPB {
|
||||
items: vec![view_id.to_string()],
|
||||
};
|
||||
|
||||
// delete the view. the view will be moved to trash
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::DeleteView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn update_view(&self, changeset: UpdateViewPayloadPB) -> Option<FlowyError> {
|
||||
// delete the view. the view will be moved to trash
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::UpdateView)
|
||||
.payload(changeset)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn update_view_icon(&self, payload: UpdateViewIconPayloadPB) -> Option<FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::UpdateViewIcon)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.error()
|
||||
}
|
||||
|
||||
pub async fn create_view(&self, parent_id: &str, name: String) -> ViewPB {
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: parent_id.to_string(),
|
||||
name,
|
||||
desc: "".to_string(),
|
||||
thumbnail: None,
|
||||
layout: Default::default(),
|
||||
initial_data: vec![],
|
||||
meta: Default::default(),
|
||||
set_as_current: false,
|
||||
index: None,
|
||||
};
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn get_view(&self, view_id: &str) -> ViewPB {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(FolderEvent::ReadView)
|
||||
.payload(ViewIdPB {
|
||||
value: view_id.to_string(),
|
||||
})
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ViewTest {
|
||||
pub sdk: EventIntegrationTest,
|
||||
pub workspace: WorkspacePB,
|
||||
pub child_view: ViewPB,
|
||||
}
|
||||
|
||||
impl ViewTest {
|
||||
#[allow(dead_code)]
|
||||
pub async fn new(sdk: &EventIntegrationTest, layout: ViewLayoutPB, data: Vec<u8>) -> Self {
|
||||
let workspace = create_workspace(sdk, "Workspace", "").await;
|
||||
let payload = WorkspaceIdPB {
|
||||
value: Some(workspace.id.clone()),
|
||||
};
|
||||
let _ = EventBuilder::new(sdk.clone())
|
||||
.event(OpenWorkspace)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await;
|
||||
|
||||
let payload = CreateViewPayloadPB {
|
||||
parent_view_id: workspace.id.clone(),
|
||||
name: "View A".to_string(),
|
||||
desc: "".to_string(),
|
||||
thumbnail: Some("http://1.png".to_string()),
|
||||
layout,
|
||||
initial_data: data,
|
||||
meta: Default::default(),
|
||||
set_as_current: true,
|
||||
index: None,
|
||||
};
|
||||
|
||||
let view = EventBuilder::new(sdk.clone())
|
||||
.event(CreateView)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<ViewPB>();
|
||||
Self {
|
||||
sdk: sdk.clone(),
|
||||
workspace,
|
||||
child_view: view,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn new_grid_view(sdk: &EventIntegrationTest, data: Vec<u8>) -> Self {
|
||||
Self::new(sdk, ViewLayoutPB::Grid, data).await
|
||||
}
|
||||
|
||||
pub async fn new_board_view(sdk: &EventIntegrationTest, data: Vec<u8>) -> Self {
|
||||
Self::new(sdk, ViewLayoutPB::Board, data).await
|
||||
}
|
||||
|
||||
pub async fn new_calendar_view(sdk: &EventIntegrationTest, data: Vec<u8>) -> Self {
|
||||
Self::new(sdk, ViewLayoutPB::Calendar, data).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_workspace(sdk: &EventIntegrationTest, 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>()
|
||||
}
|
296
frontend/rust-lib/event-integration/src/test_user.rs
Normal file
296
frontend/rust-lib/event-integration/src/test_user.rs
Normal file
@ -0,0 +1,296 @@
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytes::Bytes;
|
||||
use nanoid::nanoid;
|
||||
use protobuf::ProtobufError;
|
||||
use tokio::sync::broadcast::{channel, Sender};
|
||||
use uuid::Uuid;
|
||||
|
||||
use flowy_notification::entities::SubscribeObject;
|
||||
use flowy_notification::NotificationSender;
|
||||
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID};
|
||||
use flowy_user::entities::{
|
||||
AuthTypePB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, SignUpPayloadPB, UpdateCloudConfigPB,
|
||||
UpdateUserProfilePayloadPB, UserCloudConfigPB, UserProfilePB,
|
||||
};
|
||||
use flowy_user::errors::{FlowyError, FlowyResult};
|
||||
use flowy_user::event_map::UserEvent::*;
|
||||
use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes};
|
||||
|
||||
use crate::event_builder::EventBuilder;
|
||||
use crate::EventIntegrationTest;
|
||||
|
||||
impl EventIntegrationTest {
|
||||
pub async fn enable_encryption(&self) -> String {
|
||||
let config = EventBuilder::new(self.clone())
|
||||
.event(GetCloudConfig)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<UserCloudConfigPB>();
|
||||
let update = UpdateCloudConfigPB {
|
||||
enable_sync: None,
|
||||
enable_encrypt: Some(true),
|
||||
};
|
||||
let error = EventBuilder::new(self.clone())
|
||||
.event(SetCloudConfig)
|
||||
.payload(update)
|
||||
.async_send()
|
||||
.await
|
||||
.error();
|
||||
assert!(error.is_none());
|
||||
config.encrypt_secret
|
||||
}
|
||||
|
||||
pub async fn new_with_guest_user() -> Self {
|
||||
let test = Self::default();
|
||||
test.sign_up_as_guest().await;
|
||||
test
|
||||
}
|
||||
|
||||
pub async fn sign_up_as_guest(&self) -> SignUpContext {
|
||||
let password = login_password();
|
||||
let email = unique_email();
|
||||
let payload = SignUpPayloadPB {
|
||||
email,
|
||||
name: "appflowy".to_string(),
|
||||
password: password.clone(),
|
||||
auth_type: AuthTypePB::Local,
|
||||
device_id: uuid::Uuid::new_v4().to_string(),
|
||||
}
|
||||
.into_bytes()
|
||||
.unwrap();
|
||||
|
||||
let request = AFPluginRequest::new(SignUp).payload(payload);
|
||||
let user_profile = AFPluginDispatcher::async_send(self.inner.dispatcher(), 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 af_cloud_sign_up(&self) -> UserProfilePB {
|
||||
let email = unique_email();
|
||||
self.af_cloud_sign_in_with_email(&email).await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn supabase_party_sign_up(&self) -> UserProfilePB {
|
||||
let map = third_party_sign_up_param(Uuid::new_v4().to_string());
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthTypePB::Supabase,
|
||||
};
|
||||
|
||||
EventBuilder::new(self.clone())
|
||||
.event(OauthSignIn)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<UserProfilePB>()
|
||||
}
|
||||
|
||||
pub async fn sign_out(&self) {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(SignOut)
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn set_auth_type(&self, auth_type: AuthTypePB) {
|
||||
*self.auth_type.write() = auth_type;
|
||||
}
|
||||
|
||||
pub async fn init_user(&self) -> UserProfilePB {
|
||||
self.sign_up_as_guest().await.user_profile
|
||||
}
|
||||
|
||||
pub async fn get_user_profile(&self) -> Result<UserProfilePB, FlowyError> {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(GetUserProfile)
|
||||
.async_send()
|
||||
.await
|
||||
.try_parse::<UserProfilePB>()
|
||||
}
|
||||
|
||||
pub async fn update_user_profile(&self, params: UpdateUserProfilePayloadPB) {
|
||||
EventBuilder::new(self.clone())
|
||||
.event(UpdateUserProfile)
|
||||
.payload(params)
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
|
||||
let payload = SignInUrlPayloadPB {
|
||||
email: email.to_string(),
|
||||
auth_type: AuthTypePB::AFCloud,
|
||||
};
|
||||
let sign_in_url = EventBuilder::new(self.clone())
|
||||
.event(GetSignInURL)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.try_parse::<SignInUrlPB>()?
|
||||
.sign_in_url;
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(USER_SIGN_IN_URL.to_string(), sign_in_url);
|
||||
map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string());
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthTypePB::AFCloud,
|
||||
};
|
||||
|
||||
let user_profile = EventBuilder::new(self.clone())
|
||||
.event(OauthSignIn)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.try_parse::<UserProfilePB>()?;
|
||||
|
||||
Ok(user_profile)
|
||||
}
|
||||
|
||||
pub async fn supabase_sign_up_with_uuid(
|
||||
&self,
|
||||
uuid: &str,
|
||||
email: Option<String>,
|
||||
) -> FlowyResult<UserProfilePB> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(USER_UUID.to_string(), uuid.to_string());
|
||||
map.insert(USER_DEVICE_ID.to_string(), uuid.to_string());
|
||||
map.insert(
|
||||
USER_EMAIL.to_string(),
|
||||
email.unwrap_or_else(|| format!("{}@appflowy.io", nanoid!(10))),
|
||||
);
|
||||
let payload = OauthSignInPB {
|
||||
map,
|
||||
auth_type: AuthTypePB::Supabase,
|
||||
};
|
||||
|
||||
let user_profile = EventBuilder::new(self.clone())
|
||||
.event(OauthSignIn)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
.try_parse::<UserProfilePB>()?;
|
||||
|
||||
Ok(user_profile)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestNotificationSender {
|
||||
sender: Arc<Sender<SubscribeObject>>,
|
||||
}
|
||||
|
||||
impl Default for TestNotificationSender {
|
||||
fn default() -> Self {
|
||||
let (sender, _) = channel(1000);
|
||||
Self {
|
||||
sender: Arc::new(sender),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TestNotificationSender {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn subscribe<T>(&self, id: &str, ty: impl Into<i32> + Send) -> tokio::sync::mpsc::Receiver<T>
|
||||
where
|
||||
T: TryFrom<Bytes, Error = ProtobufError> + Send + 'static,
|
||||
{
|
||||
let id = id.to_string();
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<T>(10);
|
||||
let mut receiver = self.sender.subscribe();
|
||||
let ty = ty.into();
|
||||
tokio::spawn(async move {
|
||||
// DatabaseNotification::DidUpdateDatabaseSnapshotState
|
||||
while let Ok(value) = receiver.recv().await {
|
||||
if value.id == id && value.ty == ty {
|
||||
if let Some(payload) = value.payload {
|
||||
match T::try_from(Bytes::from(payload)) {
|
||||
Ok(object) => {
|
||||
let _ = tx.send(object).await;
|
||||
},
|
||||
Err(e) => {
|
||||
panic!(
|
||||
"Failed to parse notification payload to type: {:?} with error: {}",
|
||||
std::any::type_name::<T>(),
|
||||
e
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
rx
|
||||
}
|
||||
|
||||
pub fn subscribe_with_condition<T, F>(&self, id: &str, when: F) -> tokio::sync::mpsc::Receiver<T>
|
||||
where
|
||||
T: TryFrom<Bytes, Error = ProtobufError> + Send + 'static,
|
||||
F: Fn(&T) -> bool + Send + 'static,
|
||||
{
|
||||
let id = id.to_string();
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<T>(10);
|
||||
let mut receiver = self.sender.subscribe();
|
||||
tokio::spawn(async move {
|
||||
while let Ok(value) = receiver.recv().await {
|
||||
if value.id == id {
|
||||
if let Some(payload) = value.payload {
|
||||
if let Ok(object) = T::try_from(Bytes::from(payload)) {
|
||||
if when(&object) {
|
||||
let _ = tx.send(object).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
rx
|
||||
}
|
||||
}
|
||||
impl NotificationSender for TestNotificationSender {
|
||||
fn send_subject(&self, subject: SubscribeObject) -> Result<(), String> {
|
||||
let _ = self.sender.send(subject);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn third_party_sign_up_param(uuid: String) -> HashMap<String, String> {
|
||||
let mut params = HashMap::new();
|
||||
params.insert(USER_UUID.to_string(), uuid);
|
||||
params.insert(
|
||||
USER_EMAIL.to_string(),
|
||||
format!("{}@test.com", Uuid::new_v4()),
|
||||
);
|
||||
params.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string());
|
||||
params
|
||||
}
|
||||
|
||||
pub fn unique_email() -> String {
|
||||
format!("{}@appflowy.io", Uuid::new_v4())
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use nanoid::nanoid;
|
||||
|
||||
use flowy_user::entities::{AuthTypePB, SignUpPayloadPB, UserProfilePB};
|
||||
use flowy_user::errors::FlowyError;
|
||||
use flowy_user::event_map::UserEvent::*;
|
||||
use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes};
|
||||
|
||||
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,
|
||||
device_id: uuid::Uuid::new_v4().to_string(),
|
||||
}
|
||||
.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: "appflowy".to_string(),
|
||||
password: password.clone(),
|
||||
auth_type,
|
||||
device_id: uuid::Uuid::new_v4().to_string(),
|
||||
}
|
||||
.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;
|
||||
}
|
@ -3,7 +3,7 @@ use std::convert::TryFrom;
|
||||
use bytes::Bytes;
|
||||
|
||||
use event_integration::event_builder::EventBuilder;
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_database2::entities::{
|
||||
CellChangesetPB, CellIdPB, ChecklistCellDataChangesetPB, DatabaseLayoutPB,
|
||||
DatabaseSettingChangesetPB, DatabaseViewIdPB, DateChangesetPB, FieldType, SelectOptionCellDataPB,
|
||||
@ -13,7 +13,7 @@ use lib_infra::util::timestamp;
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_database_id_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -35,7 +35,7 @@ async fn get_database_id_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_database_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -49,7 +49,7 @@ async fn get_database_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_field_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -64,7 +64,7 @@ async fn get_field_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_field_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -78,7 +78,7 @@ async fn create_field_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_field_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -99,7 +99,7 @@ async fn delete_field_event_test() {
|
||||
// The primary field is not allowed to be deleted.
|
||||
#[tokio::test]
|
||||
async fn delete_primary_field_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -114,7 +114,7 @@ async fn delete_primary_field_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_field_type_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -132,7 +132,7 @@ async fn update_field_type_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_primary_field_type_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -151,7 +151,7 @@ async fn update_primary_field_type_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn duplicate_field_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -169,7 +169,7 @@ async fn duplicate_field_event_test() {
|
||||
// The primary field is not allowed to be duplicated. So this test should return an error.
|
||||
#[tokio::test]
|
||||
async fn duplicate_primary_field_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -183,7 +183,7 @@ async fn duplicate_primary_field_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_primary_field_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -196,7 +196,7 @@ async fn get_primary_field_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_row_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -209,7 +209,7 @@ async fn create_row_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_row_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -232,7 +232,7 @@ async fn delete_row_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_row_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -248,7 +248,7 @@ async fn get_row_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_row_meta_event_with_url_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -276,7 +276,7 @@ async fn update_row_meta_event_with_url_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_row_meta_event_with_cover_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -304,7 +304,7 @@ async fn update_row_meta_event_with_cover_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_row_event_with_invalid_row_id_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -317,7 +317,7 @@ async fn delete_row_event_with_invalid_row_id_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn duplicate_row_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -334,7 +334,7 @@ async fn duplicate_row_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn duplicate_row_event_with_invalid_row_id_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -351,7 +351,7 @@ async fn duplicate_row_event_with_invalid_row_id_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_row_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -371,7 +371,7 @@ async fn move_row_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_row_event_test2() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -391,7 +391,7 @@ async fn move_row_event_test2() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_row_event_with_invalid_row_id_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -418,7 +418,7 @@ async fn move_row_event_with_invalid_row_id_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_text_cell_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -448,7 +448,7 @@ async fn update_text_cell_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_checkbox_cell_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -479,7 +479,7 @@ async fn update_checkbox_cell_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_single_select_cell_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -506,7 +506,7 @@ async fn update_single_select_cell_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_date_cell_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -543,7 +543,7 @@ async fn update_date_cell_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_date_cell_event_with_empty_time_str_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -579,7 +579,7 @@ async fn update_date_cell_event_with_empty_time_str_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_checklist_field_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -600,7 +600,7 @@ async fn create_checklist_field_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_checklist_cell_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -657,7 +657,7 @@ async fn update_checklist_cell_test() {
|
||||
// The number of groups should be 0 if there is no group by field in grid
|
||||
#[tokio::test]
|
||||
async fn get_groups_event_with_grid_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my board view".to_owned(), vec![])
|
||||
@ -669,7 +669,7 @@ async fn get_groups_event_with_grid_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_groups_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let board_view = test
|
||||
.create_board(¤t_workspace.id, "my board view".to_owned(), vec![])
|
||||
@ -681,7 +681,7 @@ async fn get_groups_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_group_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let board_view = test
|
||||
.create_board(¤t_workspace.id, "my board view".to_owned(), vec![])
|
||||
@ -715,7 +715,7 @@ async fn move_group_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_group_event_with_invalid_id_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let board_view = test
|
||||
.create_board(¤t_workspace.id, "my board view".to_owned(), vec![])
|
||||
@ -737,7 +737,7 @@ async fn move_group_event_with_invalid_id_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn rename_group_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let board_view = test
|
||||
.create_board(¤t_workspace.id, "my board view".to_owned(), vec![])
|
||||
@ -761,7 +761,7 @@ async fn rename_group_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn hide_group_event_test2() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let board_view = test
|
||||
.create_board(¤t_workspace.id, "my board view".to_owned(), vec![])
|
||||
@ -783,7 +783,7 @@ async fn hide_group_event_test2() {
|
||||
// Update the database layout type from grid to board
|
||||
#[tokio::test]
|
||||
async fn update_database_layout_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -805,7 +805,7 @@ async fn update_database_layout_event_test() {
|
||||
// Update the database layout type from grid to board. Set the checkbox field as the grouping field
|
||||
#[tokio::test]
|
||||
async fn update_database_layout_event_test2() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let grid_view = test
|
||||
.create_grid(¤t_workspace.id, "my grid view".to_owned(), vec![])
|
||||
@ -837,7 +837,7 @@ async fn update_database_layout_event_test2() {
|
||||
// Create a checkbox field in the default board and then set it as the grouping field.
|
||||
#[tokio::test]
|
||||
async fn set_group_by_checkbox_field_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let board_view = test
|
||||
.create_board(¤t_workspace.id, "my board view".to_owned(), vec![])
|
||||
@ -854,7 +854,7 @@ async fn set_group_by_checkbox_field_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_all_calendar_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let calendar_view = test
|
||||
.create_calendar(¤t_workspace.id, "my calendar view".to_owned(), vec![])
|
||||
@ -867,7 +867,7 @@ async fn get_all_calendar_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_calendar_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let calendar_view = test
|
||||
.create_calendar(¤t_workspace.id, "my calendar view".to_owned(), vec![])
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use event_integration::assert_document_data_equal;
|
||||
use event_integration::test_document::assert_document_data_equal;
|
||||
use flowy_document2::entities::DocumentSyncStatePB;
|
||||
|
||||
use crate::document::af_cloud_test::util::AFCloudDocumentTest;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use event_integration::assert_document_data_equal;
|
||||
use event_integration::test_document::assert_document_data_equal;
|
||||
use flowy_document2::entities::DocumentSyncStatePB;
|
||||
|
||||
use crate::document::supabase_test::helper::FlowySupabaseDocumentTest;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use collab_folder::core::ViewLayout;
|
||||
|
||||
use event_integration::event_builder::EventBuilder;
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_folder2::entities::icon::{UpdateViewIconPayloadPB, ViewIconPB};
|
||||
use flowy_folder2::entities::*;
|
||||
use flowy_folder2::event_map::FolderEvent::*;
|
||||
@ -64,7 +64,7 @@ pub enum FolderScript {
|
||||
}
|
||||
|
||||
pub struct FolderTest {
|
||||
pub sdk: FlowyCoreTest,
|
||||
pub sdk: EventIntegrationTest,
|
||||
pub all_workspace: Vec<WorkspacePB>,
|
||||
pub workspace: WorkspacePB,
|
||||
pub parent_view: ViewPB,
|
||||
@ -75,7 +75,7 @@ pub struct FolderTest {
|
||||
|
||||
impl FolderTest {
|
||||
pub async fn new() -> Self {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let _ = sdk.init_user().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;
|
||||
@ -201,7 +201,7 @@ impl FolderTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn create_workspace(sdk: &FlowyCoreTest, name: &str, desc: &str) -> WorkspacePB {
|
||||
pub async fn create_workspace(sdk: &EventIntegrationTest, name: &str, desc: &str) -> WorkspacePB {
|
||||
let request = CreateWorkspacePayloadPB {
|
||||
name: name.to_owned(),
|
||||
desc: desc.to_owned(),
|
||||
@ -215,7 +215,10 @@ pub async fn create_workspace(sdk: &FlowyCoreTest, name: &str, desc: &str) -> Wo
|
||||
.parse::<WorkspacePB>()
|
||||
}
|
||||
|
||||
pub async fn read_workspace(sdk: &FlowyCoreTest, workspace_id: Option<String>) -> Vec<WorkspacePB> {
|
||||
pub async fn read_workspace(
|
||||
sdk: &EventIntegrationTest,
|
||||
workspace_id: Option<String>,
|
||||
) -> Vec<WorkspacePB> {
|
||||
let request = WorkspaceIdPB {
|
||||
value: workspace_id,
|
||||
};
|
||||
@ -241,7 +244,12 @@ pub async fn read_workspace(sdk: &FlowyCoreTest, workspace_id: Option<String>) -
|
||||
workspaces
|
||||
}
|
||||
|
||||
pub async fn create_app(sdk: &FlowyCoreTest, workspace_id: &str, name: &str, desc: &str) -> ViewPB {
|
||||
pub async fn create_app(
|
||||
sdk: &EventIntegrationTest,
|
||||
workspace_id: &str,
|
||||
name: &str,
|
||||
desc: &str,
|
||||
) -> ViewPB {
|
||||
let create_view_request = CreateViewPayloadPB {
|
||||
parent_view_id: workspace_id.to_owned(),
|
||||
name: name.to_string(),
|
||||
@ -263,7 +271,7 @@ pub async fn create_app(sdk: &FlowyCoreTest, workspace_id: &str, name: &str, des
|
||||
}
|
||||
|
||||
pub async fn create_view(
|
||||
sdk: &FlowyCoreTest,
|
||||
sdk: &EventIntegrationTest,
|
||||
app_id: &str,
|
||||
name: &str,
|
||||
desc: &str,
|
||||
@ -288,7 +296,7 @@ pub async fn create_view(
|
||||
.parse::<ViewPB>()
|
||||
}
|
||||
|
||||
pub async fn read_view(sdk: &FlowyCoreTest, view_id: &str) -> ViewPB {
|
||||
pub async fn read_view(sdk: &EventIntegrationTest, view_id: &str) -> ViewPB {
|
||||
let view_id = ViewIdPB::from(view_id);
|
||||
EventBuilder::new(sdk.clone())
|
||||
.event(ReadView)
|
||||
@ -299,7 +307,7 @@ pub async fn read_view(sdk: &FlowyCoreTest, view_id: &str) -> ViewPB {
|
||||
}
|
||||
|
||||
pub async fn move_view(
|
||||
sdk: &FlowyCoreTest,
|
||||
sdk: &EventIntegrationTest,
|
||||
view_id: String,
|
||||
parent_id: String,
|
||||
prev_view_id: Option<String>,
|
||||
@ -319,7 +327,7 @@ pub async fn move_view(
|
||||
assert!(error.is_none());
|
||||
}
|
||||
pub async fn update_view(
|
||||
sdk: &FlowyCoreTest,
|
||||
sdk: &EventIntegrationTest,
|
||||
view_id: &str,
|
||||
name: Option<String>,
|
||||
desc: Option<String>,
|
||||
@ -340,7 +348,7 @@ pub async fn update_view(
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn update_view_icon(sdk: &FlowyCoreTest, view_id: &str, icon: Option<ViewIconPB>) {
|
||||
pub async fn update_view_icon(sdk: &EventIntegrationTest, view_id: &str, icon: Option<ViewIconPB>) {
|
||||
let request = UpdateViewIconPayloadPB {
|
||||
view_id: view_id.to_string(),
|
||||
icon,
|
||||
@ -352,7 +360,7 @@ pub async fn update_view_icon(sdk: &FlowyCoreTest, view_id: &str, icon: Option<V
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn delete_view(sdk: &FlowyCoreTest, view_ids: Vec<String>) {
|
||||
pub async fn delete_view(sdk: &EventIntegrationTest, view_ids: Vec<String>) {
|
||||
let request = RepeatedViewIdPB { items: view_ids };
|
||||
EventBuilder::new(sdk.clone())
|
||||
.event(DeleteView)
|
||||
@ -361,7 +369,7 @@ pub async fn delete_view(sdk: &FlowyCoreTest, view_ids: Vec<String>) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn read_trash(sdk: &FlowyCoreTest) -> RepeatedTrashPB {
|
||||
pub async fn read_trash(sdk: &EventIntegrationTest) -> RepeatedTrashPB {
|
||||
EventBuilder::new(sdk.clone())
|
||||
.event(ReadTrash)
|
||||
.async_send()
|
||||
@ -369,7 +377,7 @@ pub async fn read_trash(sdk: &FlowyCoreTest) -> RepeatedTrashPB {
|
||||
.parse::<RepeatedTrashPB>()
|
||||
}
|
||||
|
||||
pub async fn restore_app_from_trash(sdk: &FlowyCoreTest, app_id: &str) {
|
||||
pub async fn restore_app_from_trash(sdk: &EventIntegrationTest, app_id: &str) {
|
||||
let id = TrashIdPB {
|
||||
id: app_id.to_owned(),
|
||||
};
|
||||
@ -380,7 +388,7 @@ pub async fn restore_app_from_trash(sdk: &FlowyCoreTest, app_id: &str) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn restore_view_from_trash(sdk: &FlowyCoreTest, view_id: &str) {
|
||||
pub async fn restore_view_from_trash(sdk: &EventIntegrationTest, view_id: &str) {
|
||||
let id = TrashIdPB {
|
||||
id: view_id.to_owned(),
|
||||
};
|
||||
@ -391,14 +399,14 @@ pub async fn restore_view_from_trash(sdk: &FlowyCoreTest, view_id: &str) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn delete_all_trash(sdk: &FlowyCoreTest) {
|
||||
pub async fn delete_all_trash(sdk: &EventIntegrationTest) {
|
||||
EventBuilder::new(sdk.clone())
|
||||
.event(DeleteAllTrash)
|
||||
.async_send()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn toggle_favorites(sdk: &FlowyCoreTest, view_id: Vec<String>) {
|
||||
pub async fn toggle_favorites(sdk: &EventIntegrationTest, view_id: Vec<String>) {
|
||||
let request = RepeatedViewIdPB { items: view_id };
|
||||
EventBuilder::new(sdk.clone())
|
||||
.event(ToggleFavorite)
|
||||
@ -407,7 +415,7 @@ pub async fn toggle_favorites(sdk: &FlowyCoreTest, view_id: Vec<String>) {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn read_favorites(sdk: &FlowyCoreTest) -> RepeatedViewPB {
|
||||
pub async fn read_favorites(sdk: &EventIntegrationTest) -> RepeatedViewPB {
|
||||
EventBuilder::new(sdk.clone())
|
||||
.event(ReadFavorites)
|
||||
.async_send()
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_folder2::entities::{ChildViewUpdatePB, RepeatedViewPB, UpdateViewPayloadPB};
|
||||
use flowy_folder2::notification::FolderNotification;
|
||||
|
||||
@ -16,7 +16,7 @@ use crate::util::receive_with_timeout;
|
||||
/// 5. Await the notification for workspace view updates with a timeout of 30 seconds.
|
||||
/// 6. Ensure that the received views contain the newly created "test_view".
|
||||
async fn create_child_view_in_workspace_subscription_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let workspace = test.get_current_workspace().await.workspace;
|
||||
let mut rx = test
|
||||
.notification_sender
|
||||
@ -40,7 +40,7 @@ async fn create_child_view_in_workspace_subscription_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_child_view_in_view_subscription_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let mut workspace = test.get_current_workspace().await.workspace;
|
||||
let workspace_child_view = workspace.views.pop().unwrap();
|
||||
let mut rx = test.notification_sender.subscribe::<ChildViewUpdatePB>(
|
||||
@ -72,7 +72,7 @@ async fn create_child_view_in_view_subscription_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_view_subscription_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let workspace = test.get_current_workspace().await.workspace;
|
||||
let mut rx = test
|
||||
.notification_sender
|
||||
@ -94,7 +94,7 @@ async fn delete_view_subscription_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_view_subscription_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let mut workspace = test.get_current_workspace().await.workspace;
|
||||
let mut rx = test
|
||||
.notification_sender
|
||||
|
@ -1,12 +1,12 @@
|
||||
use event_integration::event_builder::EventBuilder;
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_folder2::entities::icon::{UpdateViewIconPayloadPB, ViewIconPB, ViewIconTypePB};
|
||||
use flowy_folder2::entities::*;
|
||||
use flowy_user::errors::ErrorCode;
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_workspace_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let request = CreateWorkspacePayloadPB {
|
||||
name: "my second workspace".to_owned(),
|
||||
desc: "".to_owned(),
|
||||
@ -22,7 +22,7 @@ async fn create_workspace_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn open_workspace_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let payload = CreateWorkspacePayloadPB {
|
||||
name: "my second workspace".to_owned(),
|
||||
desc: "".to_owned(),
|
||||
@ -52,7 +52,7 @@ async fn open_workspace_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_view_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let view = test
|
||||
.create_view(¤t_workspace.id, "My first view".to_string())
|
||||
@ -64,7 +64,7 @@ async fn create_view_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_view_event_with_name_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let view = test
|
||||
.create_view(¤t_workspace.id, "My first view".to_string())
|
||||
@ -85,7 +85,7 @@ async fn update_view_event_with_name_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_view_icon_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let view = test
|
||||
.create_view(¤t_workspace.id, "My first view".to_string())
|
||||
@ -109,7 +109,7 @@ async fn update_view_icon_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_view_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let view = test
|
||||
.create_view(¤t_workspace.id, "My first view".to_string())
|
||||
@ -132,7 +132,7 @@ async fn delete_view_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn put_back_trash_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let view = test
|
||||
.create_view(¤t_workspace.id, "My first view".to_string())
|
||||
@ -175,7 +175,7 @@ async fn put_back_trash_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_view_permanently_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let view = test
|
||||
.create_view(¤t_workspace.id, "My first view".to_string())
|
||||
@ -224,7 +224,7 @@ async fn delete_view_permanently_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_all_trash_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
|
||||
for i in 0..3 {
|
||||
@ -268,7 +268,7 @@ async fn delete_all_trash_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn multiple_hierarchy_view_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
for i in 1..4 {
|
||||
let parent = test
|
||||
@ -344,7 +344,7 @@ async fn multiple_hierarchy_view_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_view_event_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
for i in 1..4 {
|
||||
let parent = test
|
||||
@ -382,7 +382,7 @@ async fn move_view_event_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_view_event_after_delete_view_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
for i in 1..6 {
|
||||
let _ = test
|
||||
@ -424,7 +424,7 @@ async fn move_view_event_after_delete_view_test() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_view_event_after_delete_view_test2() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let parent = test
|
||||
.create_view(¤t_workspace.id, "My view".to_string())
|
||||
@ -466,7 +466,7 @@ async fn move_view_event_after_delete_view_test2() {
|
||||
#[tokio::test]
|
||||
async fn create_parent_view_with_invalid_name() {
|
||||
for (name, code) in invalid_workspace_name_test_case() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let request = CreateWorkspacePayloadPB {
|
||||
name,
|
||||
desc: "".to_owned(),
|
||||
@ -494,7 +494,7 @@ fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> {
|
||||
|
||||
#[tokio::test]
|
||||
async fn move_view_across_parent_test() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let current_workspace = test.get_current_workspace().await.workspace;
|
||||
let parent_1 = test
|
||||
.create_view(¤t_workspace.id, "My view 1".to_string())
|
||||
@ -539,7 +539,7 @@ async fn move_view_across_parent_test() {
|
||||
}
|
||||
|
||||
async fn move_folder_nested_view(
|
||||
sdk: FlowyCoreTest,
|
||||
sdk: EventIntegrationTest,
|
||||
view_id: String,
|
||||
new_parent_id: String,
|
||||
prev_view_id: Option<String>,
|
||||
|
@ -1,13 +1,36 @@
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_user::entities::UpdateUserProfilePayloadPB;
|
||||
|
||||
use crate::util::{generate_test_email, get_af_cloud_config};
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_sign_up_test() {
|
||||
if get_af_cloud_config().is_some() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let email = generate_test_email();
|
||||
let user = test.af_cloud_sign_in_with_email(&email).await.unwrap();
|
||||
assert_eq!(user.email, email);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_update_user_metadata_of_open_ai_key() {
|
||||
if get_af_cloud_config().is_some() {
|
||||
let test = EventIntegrationTest::new();
|
||||
let user = test.af_cloud_sign_up().await;
|
||||
|
||||
let old_profile = test.get_user_profile().await.unwrap();
|
||||
assert_eq!(old_profile.openai_key, "".to_string());
|
||||
|
||||
test
|
||||
.update_user_profile(UpdateUserProfilePayloadPB {
|
||||
id: user.id,
|
||||
openai_key: Some("new openai_key".to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await;
|
||||
|
||||
let new_profile = test.get_user_profile().await.unwrap();
|
||||
assert_eq!(new_profile.openai_key, "new openai_key".to_string());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use event_integration::user_event::*;
|
||||
use event_integration::{event_builder::EventBuilder, FlowyCoreTest};
|
||||
use event_integration::test_user::{login_password, unique_email};
|
||||
use event_integration::{event_builder::EventBuilder, EventIntegrationTest};
|
||||
use flowy_user::entities::{AuthTypePB, SignInPayloadPB, SignUpPayloadPB};
|
||||
use flowy_user::errors::ErrorCode;
|
||||
use flowy_user::event_map::UserEvent::*;
|
||||
@ -9,7 +9,7 @@ use crate::user::local_test::helper::*;
|
||||
#[tokio::test]
|
||||
async fn sign_up_with_invalid_email() {
|
||||
for email in invalid_email_test_case() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let request = SignUpPayloadPB {
|
||||
email: email.to_string(),
|
||||
name: valid_name(),
|
||||
@ -33,9 +33,9 @@ async fn sign_up_with_invalid_email() {
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn sign_up_with_long_password() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let request = SignUpPayloadPB {
|
||||
email: random_email(),
|
||||
email: unique_email(),
|
||||
name: valid_name(),
|
||||
password: "1234".repeat(100).as_str().to_string(),
|
||||
auth_type: AuthTypePB::Local,
|
||||
@ -58,7 +58,7 @@ async fn sign_up_with_long_password() {
|
||||
#[tokio::test]
|
||||
async fn sign_in_with_invalid_email() {
|
||||
for email in invalid_email_test_case() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let request = SignInPayloadPB {
|
||||
email: email.to_string(),
|
||||
password: login_password(),
|
||||
@ -84,10 +84,10 @@ async fn sign_in_with_invalid_email() {
|
||||
#[tokio::test]
|
||||
async fn sign_in_with_invalid_password() {
|
||||
for password in invalid_password_test_case() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
|
||||
let request = SignInPayloadPB {
|
||||
email: random_email(),
|
||||
email: unique_email(),
|
||||
password,
|
||||
name: "".to_string(),
|
||||
auth_type: AuthTypePB::Local,
|
||||
|
@ -1,13 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use event_integration::event_builder::EventBuilder;
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_user::entities::{ReminderPB, RepeatedReminderPB};
|
||||
use flowy_user::event_map::UserEvent::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_update_with_name() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let _ = sdk.sign_up_as_guest().await;
|
||||
let mut meta = HashMap::new();
|
||||
meta.insert("object_id".to_string(), "".to_string());
|
||||
|
@ -1,14 +1,16 @@
|
||||
use crate::user::local_test::helper::*;
|
||||
use event_integration::{event_builder::EventBuilder, FlowyCoreTest};
|
||||
use nanoid::nanoid;
|
||||
|
||||
use event_integration::{event_builder::EventBuilder, EventIntegrationTest};
|
||||
use flowy_user::entities::{UpdateUserProfilePayloadPB, UserProfilePB};
|
||||
use flowy_user::{errors::ErrorCode, event_map::UserEvent::*};
|
||||
use nanoid::nanoid;
|
||||
|
||||
use crate::user::local_test::helper::*;
|
||||
|
||||
// use serial_test::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_profile_get_failed() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let result = EventBuilder::new(sdk)
|
||||
.event(GetUserProfile)
|
||||
.async_send()
|
||||
@ -19,7 +21,7 @@ async fn user_profile_get_failed() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_profile_get() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let user_profile = test.init_user().await;
|
||||
let user = EventBuilder::new(test.clone())
|
||||
.event(GetUserProfile)
|
||||
@ -30,7 +32,7 @@ async fn user_profile_get() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_update_with_name() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let user = sdk.init_user().await;
|
||||
let new_name = "hello_world".to_owned();
|
||||
let request = UpdateUserProfilePayloadPB::new(user.id).name(&new_name);
|
||||
@ -49,7 +51,7 @@ async fn user_update_with_name() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_update_with_ai_key() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let user = sdk.init_user().await;
|
||||
let openai_key = "openai_key".to_owned();
|
||||
let stability_ai_key = "stability_ai_key".to_owned();
|
||||
@ -72,7 +74,7 @@ async fn user_update_with_ai_key() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_update_with_email() {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let user = sdk.init_user().await;
|
||||
let new_email = format!("{}@gmail.com", nanoid!(6));
|
||||
let request = UpdateUserProfilePayloadPB::new(user.id).email(&new_email);
|
||||
@ -90,7 +92,7 @@ async fn user_update_with_email() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_update_with_invalid_email() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let user = test.init_user().await;
|
||||
for email in invalid_email_test_case() {
|
||||
let request = UpdateUserProfilePayloadPB::new(user.id).email(&email);
|
||||
@ -109,7 +111,7 @@ async fn user_update_with_invalid_email() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_update_with_invalid_password() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let user = test.init_user().await;
|
||||
for password in invalid_password_test_case() {
|
||||
let request = UpdateUserProfilePayloadPB::new(user.id).password(&password);
|
||||
@ -126,7 +128,7 @@ async fn user_update_with_invalid_password() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_update_with_invalid_name() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let user = test.init_user().await;
|
||||
let request = UpdateUserProfilePayloadPB::new(user.id).name("");
|
||||
assert!(EventBuilder::new(test.clone())
|
||||
|
@ -1,4 +1,4 @@
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_core::DEFAULT_NAME;
|
||||
use flowy_folder2::entities::ViewLayoutPB;
|
||||
|
||||
@ -11,7 +11,7 @@ async fn migrate_historical_empty_document_test() {
|
||||
"historical_empty_document",
|
||||
)
|
||||
.unwrap();
|
||||
let test = FlowyCoreTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string());
|
||||
let test = EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string());
|
||||
|
||||
let views = test.get_all_workspace_views().await;
|
||||
assert_eq!(views.len(), 3);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_core::DEFAULT_NAME;
|
||||
use flowy_folder2::entities::ViewLayoutPB;
|
||||
|
||||
@ -11,7 +11,7 @@ async fn migrate_020_historical_empty_document_test() {
|
||||
"020_historical_user_data",
|
||||
)
|
||||
.unwrap();
|
||||
let test = FlowyCoreTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string());
|
||||
let test = EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string());
|
||||
|
||||
let mut views = test.get_all_workspace_views().await;
|
||||
assert_eq!(views.len(), 1);
|
||||
|
@ -10,7 +10,7 @@ use serde_json::json;
|
||||
|
||||
use event_integration::document::document_event::DocumentEventTest;
|
||||
use event_integration::event_builder::EventBuilder;
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_core::DEFAULT_NAME;
|
||||
use flowy_encrypt::decrypt_text;
|
||||
use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
|
||||
@ -23,7 +23,7 @@ use crate::util::*;
|
||||
#[tokio::test]
|
||||
async fn third_party_sign_up_test() {
|
||||
if get_supabase_config().is_some() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let mut map = HashMap::new();
|
||||
map.insert(USER_UUID.to_string(), uuid::Uuid::new_v4().to_string());
|
||||
map.insert(
|
||||
@ -48,7 +48,7 @@ async fn third_party_sign_up_test() {
|
||||
#[tokio::test]
|
||||
async fn third_party_sign_up_with_encrypt_test() {
|
||||
if get_supabase_config().is_some() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
test.supabase_party_sign_up().await;
|
||||
let user_profile = test.get_user_profile().await.unwrap();
|
||||
assert!(user_profile.encryption_sign.is_empty());
|
||||
@ -65,7 +65,7 @@ async fn third_party_sign_up_with_encrypt_test() {
|
||||
#[tokio::test]
|
||||
async fn third_party_sign_up_with_duplicated_uuid() {
|
||||
if get_supabase_config().is_some() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let email = format!("{}@appflowy.io", nanoid!(6));
|
||||
let mut map = HashMap::new();
|
||||
map.insert(USER_UUID.to_string(), uuid::Uuid::new_v4().to_string());
|
||||
@ -98,7 +98,7 @@ async fn third_party_sign_up_with_duplicated_uuid() {
|
||||
#[tokio::test]
|
||||
async fn third_party_sign_up_with_duplicated_email() {
|
||||
if get_supabase_config().is_some() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let email = format!("{}@appflowy.io", nanoid!(6));
|
||||
test
|
||||
.supabase_sign_up_with_uuid(&uuid::Uuid::new_v4().to_string(), Some(email.clone()))
|
||||
@ -116,7 +116,7 @@ async fn third_party_sign_up_with_duplicated_email() {
|
||||
#[tokio::test]
|
||||
async fn sign_up_as_guest_and_then_update_to_new_cloud_user_test() {
|
||||
if get_supabase_config().is_some() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let old_views = test
|
||||
.folder_manager
|
||||
.get_current_workspace_views()
|
||||
@ -148,7 +148,7 @@ async fn sign_up_as_guest_and_then_update_to_new_cloud_user_test() {
|
||||
#[tokio::test]
|
||||
async fn sign_up_as_guest_and_then_update_to_existing_cloud_user_test() {
|
||||
if get_supabase_config().is_some() {
|
||||
let test = FlowyCoreTest::new_with_guest_user().await;
|
||||
let test = EventIntegrationTest::new_with_guest_user().await;
|
||||
let uuid = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let email = format!("{}@appflowy.io", nanoid!(6));
|
||||
@ -260,7 +260,7 @@ async fn update_user_profile_with_existing_email_test() {
|
||||
#[tokio::test]
|
||||
async fn migrate_anon_document_on_cloud_signup() {
|
||||
if get_supabase_config().is_some() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let user_profile = test.sign_up_as_guest().await.user_profile;
|
||||
|
||||
let view = test
|
||||
@ -305,7 +305,8 @@ async fn migrate_anon_data_on_cloud_signup() {
|
||||
"workspace_sync",
|
||||
)
|
||||
.unwrap();
|
||||
let test = FlowyCoreTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string());
|
||||
let test =
|
||||
EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string());
|
||||
let user_profile = test.supabase_party_sign_up().await;
|
||||
|
||||
// Get the folder data from remote
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use event_integration::{event_builder::EventBuilder, FlowyCoreTest};
|
||||
use event_integration::{event_builder::EventBuilder, EventIntegrationTest};
|
||||
use flowy_folder2::entities::WorkspaceSettingPB;
|
||||
use flowy_folder2::event_map::FolderEvent::GetCurrentWorkspace;
|
||||
use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
|
||||
@ -12,7 +12,7 @@ use crate::util::*;
|
||||
#[tokio::test]
|
||||
async fn initial_workspace_test() {
|
||||
if get_supabase_config().is_some() {
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
let mut map = HashMap::new();
|
||||
map.insert(USER_UUID.to_string(), uuid::Uuid::new_v4().to_string());
|
||||
map.insert(
|
||||
|
@ -16,7 +16,7 @@ use zip::ZipArchive;
|
||||
|
||||
use event_integration::event_builder::EventBuilder;
|
||||
use event_integration::Cleaner;
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_database_deps::cloud::DatabaseCloudService;
|
||||
use flowy_folder_deps::cloud::{FolderCloudService, FolderSnapshot};
|
||||
use flowy_server::supabase::api::*;
|
||||
@ -36,13 +36,13 @@ pub fn get_supabase_config() -> Option<SupabaseConfiguration> {
|
||||
}
|
||||
|
||||
pub struct FlowySupabaseTest {
|
||||
inner: FlowyCoreTest,
|
||||
inner: EventIntegrationTest,
|
||||
}
|
||||
|
||||
impl FlowySupabaseTest {
|
||||
pub fn new() -> Option<Self> {
|
||||
let _ = get_supabase_config()?;
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
test.set_auth_type(AuthTypePB::Supabase);
|
||||
test.server_provider.set_auth_type(AuthType::Supabase);
|
||||
|
||||
@ -76,7 +76,7 @@ impl FlowySupabaseTest {
|
||||
}
|
||||
|
||||
impl Deref for FlowySupabaseTest {
|
||||
type Target = FlowyCoreTest;
|
||||
type Target = EventIntegrationTest;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
@ -215,13 +215,13 @@ pub fn unzip_history_user_db(root: &str, folder_name: &str) -> std::io::Result<(
|
||||
}
|
||||
|
||||
pub struct AFCloudTest {
|
||||
inner: FlowyCoreTest,
|
||||
inner: EventIntegrationTest,
|
||||
}
|
||||
|
||||
impl AFCloudTest {
|
||||
pub fn new() -> Option<Self> {
|
||||
let _ = get_af_cloud_config()?;
|
||||
let test = FlowyCoreTest::new();
|
||||
let test = EventIntegrationTest::new();
|
||||
test.set_auth_type(AuthTypePB::AFCloud);
|
||||
test.server_provider.set_auth_type(AuthType::AFCloud);
|
||||
|
||||
@ -230,7 +230,7 @@ impl AFCloudTest {
|
||||
}
|
||||
|
||||
impl Deref for AFCloudTest {
|
||||
type Target = FlowyCoreTest;
|
||||
type Target = EventIntegrationTest;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
|
@ -132,7 +132,12 @@ impl DatabaseManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
#[instrument(
|
||||
name = "database_initialize_with_new_user",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
err
|
||||
)]
|
||||
pub async fn initialize_with_new_user(
|
||||
&self,
|
||||
user_id: i64,
|
||||
|
@ -6,8 +6,8 @@ use collab_database::fields::Field;
|
||||
use collab_database::rows::{CreateRowParams, RowDetail, RowId};
|
||||
use strum::EnumCount;
|
||||
|
||||
use event_integration::folder_event::ViewTest;
|
||||
use event_integration::FlowyCoreTest;
|
||||
use event_integration::test_folder::ViewTest;
|
||||
use event_integration::EventIntegrationTest;
|
||||
use flowy_database2::entities::{FieldType, FilterPB, RowMetaPB};
|
||||
use flowy_database2::services::cell::{CellBuilder, ToCellChangeset};
|
||||
use flowy_database2::services::database::DatabaseEditor;
|
||||
@ -26,8 +26,7 @@ use crate::database::mock_data::{
|
||||
};
|
||||
|
||||
pub struct DatabaseEditorTest {
|
||||
pub sdk: FlowyCoreTest,
|
||||
pub app_id: String,
|
||||
pub sdk: EventIntegrationTest,
|
||||
pub view_id: String,
|
||||
pub editor: Arc<DatabaseEditor>,
|
||||
pub fields: Vec<Arc<Field>>,
|
||||
@ -38,7 +37,7 @@ pub struct DatabaseEditorTest {
|
||||
|
||||
impl DatabaseEditorTest {
|
||||
pub async fn new_grid() -> Self {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let _ = sdk.init_user().await;
|
||||
|
||||
let params = make_test_grid();
|
||||
@ -47,7 +46,7 @@ impl DatabaseEditorTest {
|
||||
}
|
||||
|
||||
pub async fn new_no_date_grid() -> Self {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let _ = sdk.init_user().await;
|
||||
|
||||
let params = make_no_date_test_grid();
|
||||
@ -56,7 +55,7 @@ impl DatabaseEditorTest {
|
||||
}
|
||||
|
||||
pub async fn new_board() -> Self {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let _ = sdk.init_user().await;
|
||||
|
||||
let params = make_test_board();
|
||||
@ -65,7 +64,7 @@ impl DatabaseEditorTest {
|
||||
}
|
||||
|
||||
pub async fn new_calendar() -> Self {
|
||||
let sdk = FlowyCoreTest::new();
|
||||
let sdk = EventIntegrationTest::new();
|
||||
let _ = sdk.init_user().await;
|
||||
|
||||
let params = make_test_calendar();
|
||||
@ -73,7 +72,7 @@ impl DatabaseEditorTest {
|
||||
Self::new(sdk, view_test).await
|
||||
}
|
||||
|
||||
pub async fn new(sdk: FlowyCoreTest, test: ViewTest) -> Self {
|
||||
pub async fn new(sdk: EventIntegrationTest, test: ViewTest) -> Self {
|
||||
let editor = sdk
|
||||
.database_manager
|
||||
.get_database_with_view_id(&test.child_view.id)
|
||||
@ -92,10 +91,8 @@ impl DatabaseEditorTest {
|
||||
.collect();
|
||||
|
||||
let view_id = test.child_view.id;
|
||||
let app_id = test.parent_view.id;
|
||||
Self {
|
||||
sdk,
|
||||
app_id,
|
||||
view_id,
|
||||
editor,
|
||||
fields,
|
||||
|
@ -59,7 +59,12 @@ impl DocumentManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
#[instrument(
|
||||
name = "document_initialize_with_new_user",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
err
|
||||
)]
|
||||
pub async fn initialize_with_new_user(&self, uid: i64, workspace_id: String) -> FlowyResult<()> {
|
||||
self.initialize(uid, workspace_id).await?;
|
||||
Ok(())
|
||||
|
@ -29,6 +29,7 @@ impl std::convert::From<i32> for DocumentNotification {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace")]
|
||||
pub(crate) fn send_notification(id: &str, ty: DocumentNotification) -> NotificationBuilder {
|
||||
NotificationBuilder::new(id, ty, DOCUMENT_OBSERVABLE_SOURCE)
|
||||
}
|
||||
|
@ -265,7 +265,12 @@ impl FolderManager {
|
||||
|
||||
/// Initialize the folder for the new user.
|
||||
/// Using the [DefaultFolderBuilder] to create the default workspace for the new user.
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
#[instrument(
|
||||
name = "folder_initialize_with_new_user",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
err
|
||||
)]
|
||||
pub async fn initialize_with_new_user(
|
||||
&self,
|
||||
user_id: i64,
|
||||
|
@ -23,7 +23,7 @@ bytes = { version = "1.5", features = ["serde"] }
|
||||
tokio-retry = "0.3"
|
||||
anyhow = "1.0"
|
||||
uuid = { version = "1.3.3", features = ["v4"] }
|
||||
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
||||
chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"] }
|
||||
collab = { version = "0.1.0" }
|
||||
collab-plugins = { version = "0.1.0"}
|
||||
collab-document = { version = "0.1.0" }
|
||||
|
@ -2,11 +2,8 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use client_api::entity::dto::auth_dto::UpdateUsernameParams;
|
||||
use client_api::entity::dto::workspace_dto::CreateWorkspaceMember;
|
||||
use client_api::entity::{
|
||||
AFRole, AFUserProfileView, AFWorkspace, AFWorkspaces, InsertCollabParams, OAuthProvider,
|
||||
};
|
||||
use client_api::entity::workspace_dto::CreateWorkspaceMember;
|
||||
use client_api::entity::{AFRole, AFWorkspace, InsertCollabParams, OAuthProvider};
|
||||
use collab_entity::CollabObject;
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
@ -15,6 +12,10 @@ use flowy_user_deps::entities::*;
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
use crate::af_cloud::impls::user::dto::{
|
||||
af_update_from_update_params, user_profile_from_af_profile,
|
||||
};
|
||||
use crate::af_cloud::impls::user::util::encryption_type_from_profile;
|
||||
use crate::af_cloud::{AFCloudClient, AFServer};
|
||||
use crate::supabase::define::{USER_DEVICE_ID, USER_SIGN_IN_URL};
|
||||
|
||||
@ -92,12 +93,7 @@ where
|
||||
FutureResult::new(async move {
|
||||
let client = try_get_client?;
|
||||
client
|
||||
.update_user(UpdateUsernameParams {
|
||||
name: params.name,
|
||||
email: params.email,
|
||||
password: params.password,
|
||||
metadata: None,
|
||||
})
|
||||
.update_user(af_update_from_update_params(params))
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
@ -111,30 +107,18 @@ where
|
||||
FutureResult::new(async move {
|
||||
let client = try_get_client?;
|
||||
let profile = client.get_profile().await?;
|
||||
let encryption_type = encryption_type_from_profile(&profile);
|
||||
Ok(Some(UserProfile {
|
||||
email: profile.email.unwrap_or("".to_string()),
|
||||
name: profile.name.unwrap_or("".to_string()),
|
||||
token: client.get_token()?,
|
||||
icon_url: "".to_owned(),
|
||||
openai_key: "".to_owned(),
|
||||
stability_ai_key: "".to_owned(),
|
||||
workspace_id: match profile.latest_workspace_id {
|
||||
Some(w) => w.to_string(),
|
||||
None => "".to_string(),
|
||||
},
|
||||
auth_type: AuthType::AFCloud,
|
||||
encryption_type,
|
||||
uid: profile.uid.ok_or(anyhow!("no uid found"))?,
|
||||
}))
|
||||
let token = client.get_token()?;
|
||||
let profile = user_profile_from_af_profile(token, profile)?;
|
||||
|
||||
Ok(Some(profile))
|
||||
})
|
||||
}
|
||||
|
||||
fn get_user_workspaces(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
fn get_all_user_workspaces(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let workspaces = try_get_client?.get_workspaces().await?;
|
||||
Ok(to_user_workspaces(workspaces)?)
|
||||
Ok(to_user_workspaces(workspaces.0)?)
|
||||
})
|
||||
}
|
||||
|
||||
@ -151,7 +135,7 @@ where
|
||||
let client_token = client.access_token()?;
|
||||
|
||||
// compare and check
|
||||
if uid != profile.uid.ok_or(anyhow!("expecting uid"))? {
|
||||
if uid != profile.uid {
|
||||
return Err(anyhow!("uid mismatch"));
|
||||
}
|
||||
if token != client_token {
|
||||
@ -238,59 +222,42 @@ pub async fn user_sign_in_with_url(
|
||||
params: AFCloudOAuthParams,
|
||||
) -> Result<AuthResponse, FlowyError> {
|
||||
let is_new_user = client.sign_in_with_url(¶ms.sign_in_url).await?;
|
||||
let (profile, af_workspaces) = tokio::try_join!(client.get_profile(), client.get_workspaces())?;
|
||||
|
||||
let latest_workspace = to_user_workspace(
|
||||
af_workspaces
|
||||
.get_latest(&profile)
|
||||
.or(af_workspaces.first().cloned())
|
||||
.ok_or(anyhow!("no workspace found"))?,
|
||||
)?;
|
||||
let workspace_profile = client.get_user_workspace_info().await?;
|
||||
let user_profile = workspace_profile.user_profile;
|
||||
|
||||
let user_workspaces = to_user_workspaces(af_workspaces)?;
|
||||
let encryption_type = encryption_type_from_profile(&profile);
|
||||
let latest_workspace = to_user_workspace(workspace_profile.visiting_workspace);
|
||||
let user_workspaces = to_user_workspaces(workspace_profile.workspaces)?;
|
||||
let encryption_type = encryption_type_from_profile(&user_profile);
|
||||
|
||||
Ok(AuthResponse {
|
||||
user_id: profile.uid.ok_or(anyhow!("no uid found"))?,
|
||||
name: profile.name.ok_or(anyhow!("no name found"))?,
|
||||
user_id: user_profile.uid,
|
||||
name: user_profile.name.unwrap_or_default(),
|
||||
latest_workspace,
|
||||
user_workspaces,
|
||||
email: profile.email,
|
||||
email: user_profile.email,
|
||||
token: Some(client.get_token()?),
|
||||
device_id: params.device_id,
|
||||
encryption_type,
|
||||
is_new_user,
|
||||
updated_at: user_profile.updated_at,
|
||||
metadata: user_profile.metadata,
|
||||
})
|
||||
}
|
||||
|
||||
fn encryption_type_from_profile(profile: &AFUserProfileView) -> EncryptionType {
|
||||
match &profile.encryption_sign {
|
||||
Some(e) => EncryptionType::SelfEncryption(e.to_string()),
|
||||
None => EncryptionType::NoEncryption,
|
||||
fn to_user_workspace(af_workspace: AFWorkspace) -> UserWorkspace {
|
||||
UserWorkspace {
|
||||
id: af_workspace.workspace_id.to_string(),
|
||||
name: af_workspace.workspace_name,
|
||||
created_at: af_workspace.created_at,
|
||||
database_views_aggregate_id: af_workspace.database_storage_id.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_user_workspace(af_workspace: AFWorkspace) -> Result<UserWorkspace, FlowyError> {
|
||||
Ok(UserWorkspace {
|
||||
id: af_workspace.workspace_id.to_string(),
|
||||
name: af_workspace
|
||||
.workspace_name
|
||||
.ok_or(anyhow!("no workspace_name found"))?,
|
||||
created_at: af_workspace
|
||||
.created_at
|
||||
.ok_or(anyhow!("no created_at found"))?,
|
||||
database_views_aggregate_id: af_workspace
|
||||
.database_storage_id
|
||||
.ok_or(anyhow!("no database_views_aggregate_id found"))?
|
||||
.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
fn to_user_workspaces(af_workspaces: AFWorkspaces) -> Result<Vec<UserWorkspace>, FlowyError> {
|
||||
let mut result = Vec::with_capacity(af_workspaces.len());
|
||||
for item in af_workspaces.0.into_iter() {
|
||||
let user_workspace = to_user_workspace(item)?;
|
||||
result.push(user_workspace);
|
||||
fn to_user_workspaces(workspaces: Vec<AFWorkspace>) -> Result<Vec<UserWorkspace>, FlowyError> {
|
||||
let mut result = Vec::with_capacity(workspaces.len());
|
||||
for item in workspaces.into_iter() {
|
||||
result.push(to_user_workspace(item));
|
||||
}
|
||||
Ok(result)
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
use anyhow::Error;
|
||||
use client_api::entity::auth_dto::{UpdateUserParams, UserMetaData};
|
||||
use client_api::entity::AFUserProfile;
|
||||
|
||||
use flowy_user_deps::entities::{
|
||||
AuthType, UpdateUserProfileParams, UserProfile, USER_METADATA_ICON_URL,
|
||||
USER_METADATA_OPEN_AI_KEY, USER_METADATA_STABILITY_AI_KEY,
|
||||
};
|
||||
|
||||
use crate::af_cloud::impls::user::util::encryption_type_from_profile;
|
||||
|
||||
pub fn af_update_from_update_params(update: UpdateUserProfileParams) -> UpdateUserParams {
|
||||
let mut user_metadata = UserMetaData::new();
|
||||
if let Some(openai_key) = update.openai_key {
|
||||
user_metadata.insert(USER_METADATA_OPEN_AI_KEY, openai_key);
|
||||
}
|
||||
|
||||
if let Some(stability_ai_key) = update.stability_ai_key {
|
||||
user_metadata.insert(USER_METADATA_STABILITY_AI_KEY, stability_ai_key);
|
||||
}
|
||||
|
||||
if let Some(icon_url) = update.icon_url {
|
||||
user_metadata.insert(USER_METADATA_ICON_URL, icon_url);
|
||||
}
|
||||
|
||||
UpdateUserParams {
|
||||
name: update.name,
|
||||
email: update.email,
|
||||
password: update.password,
|
||||
metadata: Some(user_metadata),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_profile_from_af_profile(
|
||||
token: String,
|
||||
profile: AFUserProfile,
|
||||
) -> Result<UserProfile, Error> {
|
||||
let encryption_type = encryption_type_from_profile(&profile);
|
||||
let (icon_url, openai_key, stability_ai_key) = {
|
||||
profile
|
||||
.metadata
|
||||
.map(|m| {
|
||||
(
|
||||
m.get(USER_METADATA_ICON_URL).map(|v| v.to_string()),
|
||||
m.get(USER_METADATA_OPEN_AI_KEY).map(|v| v.to_string()),
|
||||
m.get(USER_METADATA_STABILITY_AI_KEY).map(|v| v.to_string()),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
Ok(UserProfile {
|
||||
email: profile.email.unwrap_or("".to_string()),
|
||||
name: profile.name.unwrap_or("".to_string()),
|
||||
token,
|
||||
icon_url: icon_url.unwrap_or_default(),
|
||||
openai_key: openai_key.unwrap_or_default(),
|
||||
stability_ai_key: stability_ai_key.unwrap_or_default(),
|
||||
workspace_id: profile.latest_workspace_id.to_string(),
|
||||
auth_type: AuthType::AFCloud,
|
||||
encryption_type,
|
||||
uid: profile.uid,
|
||||
updated_at: profile.updated_at,
|
||||
})
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
pub use cloud_service_impl::*;
|
||||
|
||||
mod cloud_service_impl;
|
||||
mod dto;
|
||||
mod util;
|
@ -0,0 +1,10 @@
|
||||
use client_api::entity::AFUserProfile;
|
||||
|
||||
use flowy_user_deps::entities::EncryptionType;
|
||||
|
||||
pub fn encryption_type_from_profile(profile: &AFUserProfile) -> EncryptionType {
|
||||
match &profile.encryption_sign {
|
||||
Some(e) => EncryptionType::SelfEncryption(e.to_string()),
|
||||
None => EncryptionType::NoEncryption,
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ use flowy_user_deps::entities::*;
|
||||
use flowy_user_deps::DEFAULT_USER_NAME;
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use lib_infra::future::FutureResult;
|
||||
use lib_infra::util::timestamp;
|
||||
|
||||
use crate::local_server::uid::UserIDGenerator;
|
||||
use crate::local_server::LocalServerDB;
|
||||
@ -46,6 +47,8 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
token: None,
|
||||
device_id: params.device_id,
|
||||
encryption_type: EncryptionType::NoEncryption,
|
||||
updated_at: timestamp(),
|
||||
metadata: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -69,6 +72,8 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
token: None,
|
||||
device_id: params.device_id,
|
||||
encryption_type: EncryptionType::NoEncryption,
|
||||
updated_at: timestamp(),
|
||||
metadata: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -104,7 +109,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
FutureResult::new(async { Ok(None) })
|
||||
}
|
||||
|
||||
fn get_user_workspaces(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
fn get_all_user_workspaces(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
FutureResult::new(async { Ok(vec![]) })
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,8 @@ where
|
||||
token: None,
|
||||
device_id: params.device_id,
|
||||
encryption_type: EncryptionType::from_sign(&user_profile.encryption_sign),
|
||||
updated_at: user_profile.updated_at.timestamp(),
|
||||
metadata: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -158,6 +160,8 @@ where
|
||||
token: None,
|
||||
device_id: params.device_id,
|
||||
encryption_type: EncryptionType::from_sign(&response.encryption_sign),
|
||||
updated_at: response.updated_at.timestamp(),
|
||||
metadata: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -220,12 +224,13 @@ where
|
||||
workspace_id: response.latest_workspace_id,
|
||||
auth_type: AuthType::Supabase,
|
||||
encryption_type: EncryptionType::from_sign(&response.encryption_sign),
|
||||
updated_at: response.updated_at.timestamp(),
|
||||
})),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_user_workspaces(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
fn get_all_user_workspaces(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
let try_get_postgrest = self.server.try_get_postgrest();
|
||||
FutureResult::new(async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
@ -419,7 +424,7 @@ async fn get_user_profile(
|
||||
) -> Result<Option<UserProfileResponse>, Error> {
|
||||
let mut builder = postgrest
|
||||
.from(USER_PROFILE_VIEW)
|
||||
.select("uid, email, name, encryption_sign, latest_workspace_id");
|
||||
.select("uid, email, name, encryption_sign, latest_workspace_id, updated_at");
|
||||
|
||||
match params {
|
||||
GetUserProfileParams::Uid(uid) => builder = builder.eq("uid", uid.to_string()),
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
@ -27,6 +28,8 @@ pub(crate) struct UserProfileResponse {
|
||||
|
||||
#[serde(deserialize_with = "deserialize_null_or_default")]
|
||||
pub encryption_sign: String,
|
||||
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -0,0 +1,3 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
ALTER TABLE user_table
|
||||
DROP COLUMN updated_at;
|
@ -0,0 +1,3 @@
|
||||
-- Your SQL goes here
|
||||
ALTER TABLE user_table
|
||||
ADD COLUMN updated_at BIGINT NOT NULL DEFAULT 0;
|
@ -32,6 +32,7 @@ diesel::table! {
|
||||
auth_type -> Integer,
|
||||
encryption_type -> Text,
|
||||
stability_ai_key -> Text,
|
||||
updated_at -> BigInt,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ pub trait UserCloudService: Send + Sync + 'static {
|
||||
) -> FutureResult<Option<UserProfile>, FlowyError>;
|
||||
|
||||
/// Return the all the workspaces of the user
|
||||
fn get_user_workspaces(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error>;
|
||||
fn get_all_user_workspaces(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error>;
|
||||
|
||||
fn check_user(&self, credential: UserCredentials) -> FutureResult<(), Error>;
|
||||
|
||||
|
@ -2,9 +2,15 @@ use std::str::FromStr;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use serde_repr::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub const USER_METADATA_OPEN_AI_KEY: &str = "openai_key";
|
||||
pub const USER_METADATA_STABILITY_AI_KEY: &str = "stability_ai_key";
|
||||
pub const USER_METADATA_ICON_URL: &str = "icon_url";
|
||||
pub const USER_METADATA_UPDATE_AT: &str = "updated_at";
|
||||
|
||||
pub trait UserAuthResponse {
|
||||
fn user_id(&self) -> i64;
|
||||
fn user_name(&self) -> &str;
|
||||
@ -14,52 +20,8 @@ pub trait UserAuthResponse {
|
||||
fn user_token(&self) -> Option<String>;
|
||||
fn user_email(&self) -> Option<String>;
|
||||
fn encryption_type(&self) -> EncryptionType;
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct SignInResponse {
|
||||
pub user_id: i64,
|
||||
pub name: String,
|
||||
pub latest_workspace: UserWorkspace,
|
||||
pub user_workspaces: Vec<UserWorkspace>,
|
||||
pub email: Option<String>,
|
||||
pub token: Option<String>,
|
||||
pub device_id: String,
|
||||
pub encryption_type: EncryptionType,
|
||||
}
|
||||
|
||||
impl UserAuthResponse for SignInResponse {
|
||||
fn user_id(&self) -> i64 {
|
||||
self.user_id
|
||||
}
|
||||
|
||||
fn user_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn latest_workspace(&self) -> &UserWorkspace {
|
||||
&self.latest_workspace
|
||||
}
|
||||
|
||||
fn user_workspaces(&self) -> &[UserWorkspace] {
|
||||
&self.user_workspaces
|
||||
}
|
||||
|
||||
fn device_id(&self) -> &str {
|
||||
&self.device_id
|
||||
}
|
||||
|
||||
fn user_token(&self) -> Option<String> {
|
||||
self.token.clone()
|
||||
}
|
||||
|
||||
fn user_email(&self) -> Option<String> {
|
||||
self.email.clone()
|
||||
}
|
||||
|
||||
fn encryption_type(&self) -> EncryptionType {
|
||||
self.encryption_type.clone()
|
||||
}
|
||||
fn metadata(&self) -> &Option<serde_json::Value>;
|
||||
fn updated_at(&self) -> i64;
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Debug)]
|
||||
@ -91,6 +53,8 @@ pub struct AuthResponse {
|
||||
pub token: Option<String>,
|
||||
pub device_id: String,
|
||||
pub encryption_type: EncryptionType,
|
||||
pub updated_at: i64,
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl UserAuthResponse for AuthResponse {
|
||||
@ -125,6 +89,14 @@ impl UserAuthResponse for AuthResponse {
|
||||
fn encryption_type(&self) -> EncryptionType {
|
||||
self.encryption_type.clone()
|
||||
}
|
||||
|
||||
fn metadata(&self) -> &Option<Value> {
|
||||
&self.metadata
|
||||
}
|
||||
|
||||
fn updated_at(&self) -> i64 {
|
||||
self.updated_at
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -196,6 +168,7 @@ pub struct UserProfile {
|
||||
pub auth_type: AuthType,
|
||||
// If the encryption_sign is not empty, which means the user has enabled the encryption.
|
||||
pub encryption_type: EncryptionType,
|
||||
pub updated_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
|
||||
@ -243,17 +216,37 @@ where
|
||||
{
|
||||
fn from(params: (&T, &AuthType)) -> Self {
|
||||
let (value, auth_type) = params;
|
||||
let (icon_url, openai_key, stability_ai_key) = {
|
||||
value
|
||||
.metadata()
|
||||
.as_ref()
|
||||
.map(|m| {
|
||||
(
|
||||
m.get(USER_METADATA_ICON_URL)
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
m.get(USER_METADATA_OPEN_AI_KEY)
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
m.get(USER_METADATA_STABILITY_AI_KEY)
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
};
|
||||
Self {
|
||||
uid: value.user_id(),
|
||||
email: value.user_email().unwrap_or_default(),
|
||||
name: value.user_name().to_owned(),
|
||||
token: value.user_token().unwrap_or_default(),
|
||||
icon_url: "".to_owned(),
|
||||
openai_key: "".to_owned(),
|
||||
icon_url,
|
||||
openai_key,
|
||||
workspace_id: value.latest_workspace().id.to_owned(),
|
||||
auth_type: auth_type.clone(),
|
||||
encryption_type: value.encryption_type(),
|
||||
stability_ai_key: "".to_owned(),
|
||||
stability_ai_key,
|
||||
updated_at: value.updated_at(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,6 +240,9 @@ pub struct UserStatePB {
|
||||
pub struct AuthStateChangedPB {
|
||||
#[pb(index = 1)]
|
||||
pub state: AuthStatePB,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf_Enum, Debug, Clone)]
|
||||
|
@ -173,9 +173,9 @@ pub struct UserSecretPB {
|
||||
}
|
||||
|
||||
#[derive(Default, ProtoBuf)]
|
||||
pub struct UserEncryptionSecretCheckPB {
|
||||
pub struct UserEncryptionConfigurationPB {
|
||||
#[pb(index = 1)]
|
||||
pub is_need_secret: bool,
|
||||
pub require_secret: bool,
|
||||
}
|
||||
|
||||
impl From<UserCloudConfig> for UserCloudConfigPB {
|
||||
|
@ -2,6 +2,7 @@ use std::sync::Weak;
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
|
||||
use serde_json::Value;
|
||||
use tracing::event;
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_sqlite::kv::StorePreferences;
|
||||
@ -89,7 +90,7 @@ pub async fn check_user_handler(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(manager))]
|
||||
#[tracing::instrument(level = "debug", skip(manager), err)]
|
||||
pub async fn get_user_profile_handler(
|
||||
manager: AFPluginState<Weak<UserManager>>,
|
||||
) -> DataResult<UserProfilePB, FlowyError> {
|
||||
@ -105,6 +106,12 @@ pub async fn get_user_profile_handler(
|
||||
}
|
||||
});
|
||||
|
||||
event!(
|
||||
tracing::Level::DEBUG,
|
||||
"Get user profile: {:?}",
|
||||
user_profile
|
||||
);
|
||||
|
||||
data_result_ok(user_profile.into())
|
||||
}
|
||||
|
||||
@ -330,7 +337,7 @@ pub async fn set_encrypt_secret_handler(
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
pub async fn check_encrypt_secret_handler(
|
||||
manager: AFPluginState<Weak<UserManager>>,
|
||||
) -> DataResult<UserEncryptionSecretCheckPB, FlowyError> {
|
||||
) -> DataResult<UserEncryptionConfigurationPB, FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let uid = manager.get_session()?.user_id;
|
||||
let profile = manager.get_user_profile(uid).await?;
|
||||
@ -346,7 +353,9 @@ pub async fn check_encrypt_secret_handler(
|
||||
},
|
||||
};
|
||||
|
||||
data_result_ok(UserEncryptionSecretCheckPB { is_need_secret })
|
||||
data_result_ok(UserEncryptionConfigurationPB {
|
||||
require_secret: is_need_secret,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
|
@ -272,7 +272,7 @@ pub enum UserEvent {
|
||||
#[event(input = "UserSecretPB")]
|
||||
SetEncryptionSecret = 15,
|
||||
|
||||
#[event(output = "UserEncryptionSecretCheckPB")]
|
||||
#[event(output = "UserEncryptionConfigurationPB")]
|
||||
CheckEncryptionSign = 16,
|
||||
|
||||
/// Return the all the workspaces of the user
|
||||
|
@ -149,6 +149,7 @@ impl UserManager {
|
||||
UserTokenState::Invalid => {
|
||||
send_auth_state_notification(AuthStateChangedPB {
|
||||
state: AuthStatePB::InvalidAuth,
|
||||
message: "Token is invalid".to_string(),
|
||||
})
|
||||
.send();
|
||||
},
|
||||
@ -256,6 +257,7 @@ impl UserManager {
|
||||
}
|
||||
send_auth_state_notification(AuthStateChangedPB {
|
||||
state: AuthStatePB::AuthStateSignIn,
|
||||
message: "Sign in success".to_string(),
|
||||
})
|
||||
.send();
|
||||
Ok(user_profile)
|
||||
@ -387,6 +389,7 @@ impl UserManager {
|
||||
|
||||
send_auth_state_notification(AuthStateChangedPB {
|
||||
state: AuthStatePB::AuthStateSignIn,
|
||||
message: "Sign up success".to_string(),
|
||||
})
|
||||
.send();
|
||||
Ok(())
|
||||
@ -402,7 +405,7 @@ impl UserManager {
|
||||
tokio::spawn(async move {
|
||||
match server.sign_out(None).await {
|
||||
Ok(_) => {},
|
||||
Err(e) => tracing::error!("Sign out failed: {:?}", e),
|
||||
Err(e) => event!(tracing::Level::ERROR, "{:?}", e),
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
@ -421,8 +424,12 @@ impl UserManager {
|
||||
) -> Result<(), FlowyError> {
|
||||
let changeset = UserTableChangeset::new(params.clone());
|
||||
let session = self.get_session()?;
|
||||
save_user_profile_change(session.user_id, self.db_pool(session.user_id)?, changeset)?;
|
||||
self.update_user(session.user_id, None, params).await?;
|
||||
upsert_user_profile_change(session.user_id, self.db_pool(session.user_id)?, changeset)?;
|
||||
|
||||
let profile = self.get_user_profile(session.user_id).await?;
|
||||
self
|
||||
.update_user(session.user_id, profile.token, params)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -462,15 +469,14 @@ impl UserManager {
|
||||
.await?
|
||||
.ok_or_else(|| FlowyError::new(ErrorCode::RecordNotFound, "User not found"))?;
|
||||
|
||||
if !is_user_encryption_sign_valid(old_user_profile, &new_user_profile.encryption_type.sign()) {
|
||||
return Err(FlowyError::new(
|
||||
ErrorCode::InvalidEncryptSecret,
|
||||
"Invalid encryption sign",
|
||||
));
|
||||
if new_user_profile.updated_at > old_user_profile.updated_at {
|
||||
check_encryption_sign(old_user_profile, &new_user_profile.encryption_type.sign());
|
||||
|
||||
// Save the new user profile
|
||||
let changeset = UserTableChangeset::from_user_profile(new_user_profile.clone());
|
||||
let _ = upsert_user_profile_change(uid, self.database.get_pool(uid)?, changeset);
|
||||
}
|
||||
|
||||
let changeset = UserTableChangeset::from_user_profile(new_user_profile.clone());
|
||||
let _ = save_user_profile_change(uid, self.database.get_pool(uid)?, changeset);
|
||||
Ok(new_user_profile)
|
||||
}
|
||||
|
||||
@ -501,13 +507,12 @@ impl UserManager {
|
||||
async fn update_user(
|
||||
&self,
|
||||
uid: i64,
|
||||
token: Option<String>,
|
||||
token: String,
|
||||
params: UpdateUserProfileParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.cloud_services.get_user_service()?;
|
||||
let token = token.to_owned();
|
||||
tokio::spawn(async move {
|
||||
let credentials = UserCredentials::new(token, Some(uid), None);
|
||||
let credentials = UserCredentials::new(Some(token), Some(uid), None);
|
||||
server.update_user(credentials, params).await
|
||||
})
|
||||
.await
|
||||
@ -613,6 +618,7 @@ impl UserManager {
|
||||
) -> Result<(), FlowyError> {
|
||||
let user_profile = UserProfile::from((response, auth_type));
|
||||
let uid = user_profile.uid;
|
||||
event!(tracing::Level::DEBUG, "Save new history user: {:?}", uid);
|
||||
self.add_historical_user(
|
||||
uid,
|
||||
response.device_id(),
|
||||
@ -620,7 +626,9 @@ impl UserManager {
|
||||
auth_type,
|
||||
self.user_dir(uid),
|
||||
);
|
||||
event!(tracing::Level::DEBUG, "Save new history user workspace");
|
||||
save_user_workspaces(uid, self.db_pool(uid)?, response.user_workspaces())?;
|
||||
event!(tracing::Level::INFO, "Save new user profile to disk");
|
||||
self
|
||||
.save_user(uid, (user_profile, auth_type.clone()).into())
|
||||
.await?;
|
||||
@ -640,12 +648,12 @@ impl UserManager {
|
||||
if session.user_id == user_update.uid {
|
||||
debug!("Receive user update: {:?}", user_update);
|
||||
let user_profile = self.get_user_profile(user_update.uid).await?;
|
||||
if !is_user_encryption_sign_valid(&user_profile, &user_update.encryption_sign) {
|
||||
if !check_encryption_sign(&user_profile, &user_update.encryption_sign) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Save the user profile change
|
||||
save_user_profile_change(
|
||||
upsert_user_profile_change(
|
||||
user_update.uid,
|
||||
self.db_pool(user_update.uid)?,
|
||||
UserTableChangeset::from(user_update),
|
||||
@ -685,24 +693,30 @@ impl UserManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_user_encryption_sign_valid(user_profile: &UserProfile, encryption_sign: &str) -> bool {
|
||||
fn check_encryption_sign(user_profile: &UserProfile, encryption_sign: &str) -> bool {
|
||||
// If the local user profile's encryption sign is not equal to the user update's encryption sign,
|
||||
// which means the user enable encryption in another device, we should logout the current user.
|
||||
let is_valid = user_profile.encryption_type.sign() == encryption_sign;
|
||||
if !is_valid {
|
||||
send_auth_state_notification(AuthStateChangedPB {
|
||||
state: AuthStatePB::InvalidAuth,
|
||||
message: "Encryption configuration was changed".to_string(),
|
||||
})
|
||||
.send();
|
||||
}
|
||||
is_valid
|
||||
}
|
||||
|
||||
fn save_user_profile_change(
|
||||
fn upsert_user_profile_change(
|
||||
uid: i64,
|
||||
pool: Arc<ConnectionPool>,
|
||||
changeset: UserTableChangeset,
|
||||
) -> FlowyResult<()> {
|
||||
event!(
|
||||
tracing::Level::DEBUG,
|
||||
"Update user profile with changeset: {:?}",
|
||||
changeset
|
||||
);
|
||||
let conn = pool.get()?;
|
||||
diesel_update_table!(user_table, changeset, &*conn);
|
||||
let user: UserProfile = user_table::dsl::user_table
|
||||
@ -719,5 +733,5 @@ fn save_user_profile_change(
|
||||
fn save_user_token(uid: i64, pool: Arc<ConnectionPool>, token: String) -> FlowyResult<()> {
|
||||
let params = UpdateUserProfileParams::new(uid).with_token(token);
|
||||
let changeset = UserTableChangeset::new(params);
|
||||
save_user_profile_change(uid, pool, changeset)
|
||||
upsert_user_profile_change(uid, pool, changeset)
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ impl std::convert::From<UserNotification> for i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace")]
|
||||
pub(crate) fn send_notification(id: &str, ty: UserNotification) -> NotificationBuilder {
|
||||
NotificationBuilder::new(id, ty, USER_OBSERVABLE_SOURCE)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ pub struct UserTable {
|
||||
pub(crate) auth_type: i32,
|
||||
pub(crate) encryption_type: String,
|
||||
pub(crate) stability_ai_key: String,
|
||||
pub(crate) updated_at: i64,
|
||||
}
|
||||
|
||||
impl UserTable {
|
||||
@ -43,6 +44,7 @@ impl From<(UserProfile, AuthType)> for UserTable {
|
||||
auth_type: auth_type as i32,
|
||||
encryption_type,
|
||||
stability_ai_key: user_profile.stability_ai_key,
|
||||
updated_at: user_profile.updated_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,6 +62,7 @@ impl From<UserTable> for UserProfile {
|
||||
auth_type: AuthType::from(table.auth_type),
|
||||
encryption_type: EncryptionType::from_str(&table.encryption_type).unwrap_or_default(),
|
||||
stability_ai_key: table.stability_ai_key,
|
||||
updated_at: table.updated_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ impl UserManager {
|
||||
if let Ok(service) = self.cloud_services.get_user_service() {
|
||||
if let Ok(pool) = self.db_pool(uid) {
|
||||
tokio::spawn(async move {
|
||||
if let Ok(new_user_workspaces) = service.get_user_workspaces(uid).await {
|
||||
if let Ok(new_user_workspaces) = service.get_all_user_workspaces(uid).await {
|
||||
let _ = save_user_workspaces(uid, pool, &new_user_workspaces);
|
||||
let repeated_workspace_pbs = RepeatedUserWorkspacePB::from(new_user_workspaces);
|
||||
send_notification(&uid.to_string(), UserNotification::DidUpdateUserWorkspaces)
|
||||
|
@ -62,7 +62,7 @@ flutter test --dart-define=RUST_LOG=${RUST_LOG} -j, --concurrency=1 --coverage
|
||||
script_runner = "@shell"
|
||||
|
||||
[tasks.rust_unit_test]
|
||||
run_task = { name = ["rust_lib_unit_test", "shared_lib_unit_test"] }
|
||||
run_task = { name = ["rust_lib_unit_test"] }
|
||||
|
||||
[tasks.supabase_unit_test]
|
||||
env = { RUST_LOG = "info" }
|
||||
|
Loading…
Reference in New Issue
Block a user