fix: anon user error (#3772)

* fix: anon user error

* ci: fix tests
This commit is contained in:
Nathan.fooo 2023-10-24 23:13:51 +08:00 committed by GitHub
parent 71f80be8f7
commit cceee80799
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 227 additions and 230 deletions

View File

@ -27,8 +27,8 @@ class NetworkListener {
return _updateConnectionStatus(result);
}
void stop() {
_connectivitySubscription.cancel();
Future<void> stop() async {
await _connectivitySubscription.cancel();
}
Future<void> _updateConnectionStatus(ConnectivityResult result) async {

View File

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy_backend/appflowy_backend.dart';
import 'package:appflowy_backend/log.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
@ -104,6 +105,9 @@ Future<void> initGetIt(
config,
),
),
dispose: (launcher) async {
await launcher.dispose();
},
);
getIt.registerSingleton<PluginSandbox>(PluginSandbox());
@ -130,6 +134,7 @@ abstract class LaunchTask {
LaunchTaskType get type => LaunchTaskType.dataProcessing;
Future<void> initialize(LaunchContext context);
Future<void> dispose();
}
class AppLauncher {
@ -153,6 +158,14 @@ class AppLauncher {
await task.initialize(context);
}
}
Future<void> dispose() async {
Log.info('AppLauncher dispose');
for (final task in tasks) {
await task.dispose();
}
tasks.clear();
}
}
enum IntegrationMode {

View File

@ -86,6 +86,9 @@ class InitAppWidgetTask extends LaunchTask {
return;
}
@override
Future<void> dispose() async {}
}
class ApplicationWidget extends StatefulWidget {

View File

@ -1,11 +1,10 @@
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();
UserAuthStateListener? _authStateListener;
bool isLoggingOut = false;
@override
@ -13,19 +12,24 @@ class InitAppFlowyCloudTask extends LaunchTask {
if (!isAppFlowyCloudEnabled) {
return;
}
_authStateListener = UserAuthStateListener();
_authStateListener.start(
_authStateListener?.start(
didSignIn: () {
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) {
await runAppFlowy();
}
},
);
}
@override
Future<void> dispose() async {
await _authStateListener?.stop();
_authStateListener = null;
}
}

View File

@ -14,4 +14,7 @@ class HotKeyTask extends LaunchTask {
}
await hotKeyManager.unregisterAll();
}
@override
Future<void> dispose() async {}
}

View File

@ -25,4 +25,7 @@ class PluginLoadTask extends LaunchTask {
config: CalendarPluginConfig(),
);
}
@override
Future<void> dispose() async {}
}

View File

@ -10,4 +10,7 @@ class InitLocalizationTask extends LaunchTask {
await EasyLocalization.ensureInitialized();
EasyLocalization.logger.enableBuildModes = [];
}
@override
Future<void> dispose() async {}
}

View File

@ -18,4 +18,7 @@ class PlatformErrorCatcherTask extends LaunchTask {
};
}
}
@override
Future<void> dispose() async {}
}

View File

@ -11,4 +11,9 @@ class InitPlatformServiceTask extends LaunchTask {
Future<void> initialize(LaunchContext context) async {
getIt<NetworkListener>().start();
}
@override
Future<void> dispose() async {
await getIt<NetworkListener>().stop();
}
}

View File

@ -27,6 +27,9 @@ class InitRustSDKTask extends LaunchTask {
context.getIt<FlowySDK>().setEnv(env);
await context.getIt<FlowySDK>().init(dir);
}
@override
Future<void> dispose() async {}
}
AppFlowyEnv getAppFlowyEnv() {

View File

@ -54,6 +54,12 @@ class InitSupabaseTask extends LaunchTask {
registerProtocolHandler(appflowyDeepLinkSchema);
}
}
@override
Future<void> dispose() async {
await realtimeService?.dispose();
supabase?.dispose();
}
}
/// customize the supabase auth storage

View File

@ -71,4 +71,7 @@ class InitAppWindowTask extends LaunchTask with WindowListener {
final position = await windowManager.getPosition();
WindowSizeManager().setPosition(position);
}
@override
Future<void> dispose() async {}
}

View File

@ -63,15 +63,22 @@ class BackendAuthService implements AuthService {
@override
Future<Either<FlowyError, UserProfilePB>> signUpAsGuest({
Map<String, String> params = const {},
}) {
}) async {
const password = "Guest!@123456";
final uid = uuid();
final userEmail = "$uid@appflowy.io";
return signUp(
name: LocaleKeys.defaultUsername.tr(),
password: password,
email: userEmail,
);
final request = SignUpPayloadPB.create()
..name = LocaleKeys.defaultUsername.tr()
..email = userEmail
..password = password
// When sign up as guest, the auth type is always local.
..authType = AuthTypePB.Local
..deviceId = await getDeviceId();
final response = await UserEventSignUp(request).send().then(
(value) => value.swap(),
);
return response;
}
@override

View File

@ -36,6 +36,7 @@ class SupbaseRealtimeService {
isLoggingOut = false;
},
onInvalidAuth: (message) async {
Log.error(message);
await getIt<AuthService>().signOut();
channel?.unsubscribe();
channel = null;

View File

@ -54,7 +54,7 @@ class SettingsUserView extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
_buildUserIconSetting(context),
if (isCloudEnabled) ...[
if (isCloudEnabled && user.authType != AuthTypePB.Local) ...[
const VSpace(12),
UserEmailInput(user.email)
],

View File

@ -762,7 +762,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
dependencies = [
"anyhow",
"bytes",
@ -1292,7 +1292,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa 1.0.6",
"phf 0.11.2",
"phf 0.8.0",
"smallvec",
]
@ -1438,7 +1438,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
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=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
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=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
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=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
dependencies = [
"anyhow",
"reqwest",
@ -4307,7 +4307,6 @@ 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",
]
@ -4399,19 +4398,6 @@ 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.29",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
@ -4915,7 +4901,7 @@ dependencies = [
[[package]]
name = "realtime-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
dependencies = [
"bytes",
"collab",
@ -5637,7 +5623,7 @@ dependencies = [
[[package]]
name = "shared_entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
dependencies = [
"anyhow",
"collab-entity",

View File

@ -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 = "141354bfa9d08c387cf9beb9697b89b052790e89" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cc6b451104e7154b38df5ae9c4e7215a61fcf172" }
# Please use the following script to update collab.
# Working directory: frontend
#

View File

@ -660,7 +660,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
dependencies = [
"anyhow",
"bytes",
@ -1138,7 +1138,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"phf 0.11.2",
"phf 0.8.0",
"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=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
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=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
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=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
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=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
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 0.8.0",
"phf_macros",
"phf_shared 0.8.0",
"proc-macro-hack",
]
@ -3643,7 +3643,6 @@ 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",
]
@ -3711,19 +3710,6 @@ 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"
@ -4265,7 +4251,7 @@ dependencies = [
[[package]]
name = "realtime-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
dependencies = [
"bytes",
"collab",
@ -4886,7 +4872,7 @@ dependencies = [
[[package]]
name = "shared_entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=141354bfa9d08c387cf9beb9697b89b052790e89#141354bfa9d08c387cf9beb9697b89b052790e89"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cc6b451104e7154b38df5ae9c4e7215a61fcf172#cc6b451104e7154b38df5ae9c4e7215a61fcf172"
dependencies = [
"anyhow",
"collab-entity",

View File

@ -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 = "141354bfa9d08c387cf9beb9697b89b052790e89" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cc6b451104e7154b38df5ae9c4e7215a61fcf172" }
# Please use the following script to update collab.
# Working directory: frontend
#

View File

@ -107,7 +107,7 @@ impl EventIntegrationTest {
*self.auth_type.write() = auth_type;
}
pub async fn init_user(&self) -> UserProfilePB {
pub async fn init_anon_user(&self) -> UserProfilePB {
self.sign_up_as_guest().await.user_profile
}

View File

@ -76,7 +76,7 @@ pub struct FolderTest {
impl FolderTest {
pub async fn new() -> Self {
let sdk = EventIntegrationTest::new();
let _ = sdk.init_user().await;
let _ = sdk.init_anon_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;
let view = create_view(

View File

@ -14,7 +14,7 @@ async fn af_cloud_sign_up_test() {
}
#[tokio::test]
async fn af_cloud_update_user_metadata_of_open_ai_key() {
async fn af_cloud_update_user_metadata() {
if get_af_cloud_config().is_some() {
let test = EventIntegrationTest::new();
let user = test.af_cloud_sign_up().await;
@ -25,12 +25,17 @@ async fn af_cloud_update_user_metadata_of_open_ai_key() {
test
.update_user_profile(UpdateUserProfilePayloadPB {
id: user.id,
openai_key: Some("new openai_key".to_string()),
openai_key: Some("new openai key".to_string()),
stability_ai_key: Some("new stability ai 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());
assert_eq!(new_profile.openai_key, "new openai key".to_string());
assert_eq!(
new_profile.stability_ai_key,
"new stability ai key".to_string()
);
}
}

View File

@ -1,7 +1,7 @@
use nanoid::nanoid;
use event_integration::{event_builder::EventBuilder, EventIntegrationTest};
use flowy_user::entities::{UpdateUserProfilePayloadPB, UserProfilePB};
use flowy_user::entities::{AuthTypePB, UpdateUserProfilePayloadPB, UserProfilePB};
use flowy_user::{errors::ErrorCode, event_map::UserEvent::*};
use crate::user::local_test::helper::*;
@ -20,20 +20,24 @@ async fn user_profile_get_failed() {
}
#[tokio::test]
async fn user_profile_get() {
async fn anon_user_profile_get() {
let test = EventIntegrationTest::new();
let user_profile = test.init_user().await;
let user_profile = test.init_anon_user().await;
let user = EventBuilder::new(test.clone())
.event(GetUserProfile)
.sync_send()
.parse::<UserProfilePB>();
assert_eq!(user_profile, user);
assert_eq!(user_profile.id, user.id);
assert_eq!(user_profile.openai_key, user.openai_key);
assert_eq!(user_profile.stability_ai_key, user.stability_ai_key);
assert_eq!(user_profile.workspace_id, user.workspace_id);
assert_eq!(user_profile.auth_type, AuthTypePB::Local);
}
#[tokio::test]
async fn user_update_with_name() {
let sdk = EventIntegrationTest::new();
let user = sdk.init_user().await;
let user = sdk.init_anon_user().await;
let new_name = "hello_world".to_owned();
let request = UpdateUserProfilePayloadPB::new(user.id).name(&new_name);
let _ = EventBuilder::new(sdk.clone())
@ -52,7 +56,7 @@ async fn user_update_with_name() {
#[tokio::test]
async fn user_update_with_ai_key() {
let sdk = EventIntegrationTest::new();
let user = sdk.init_user().await;
let user = sdk.init_anon_user().await;
let openai_key = "openai_key".to_owned();
let stability_ai_key = "stability_ai_key".to_owned();
let request = UpdateUserProfilePayloadPB::new(user.id)
@ -73,9 +77,9 @@ async fn user_update_with_ai_key() {
}
#[tokio::test]
async fn user_update_with_email() {
async fn anon_user_update_with_email() {
let sdk = EventIntegrationTest::new();
let user = sdk.init_user().await;
let user = sdk.init_anon_user().await;
let new_email = format!("{}@gmail.com", nanoid!(6));
let request = UpdateUserProfilePayloadPB::new(user.id).email(&new_email);
let _ = EventBuilder::new(sdk.clone())
@ -87,13 +91,14 @@ async fn user_update_with_email() {
.sync_send()
.parse::<UserProfilePB>();
assert_eq!(user_profile.email, new_email,);
// When the user is anonymous, the email is empty no matter what you set
assert!(user_profile.email.is_empty());
}
#[tokio::test]
async fn user_update_with_invalid_email() {
let test = EventIntegrationTest::new();
let user = test.init_user().await;
let user = test.init_anon_user().await;
for email in invalid_email_test_case() {
let request = UpdateUserProfilePayloadPB::new(user.id).email(&email);
assert_eq!(
@ -112,7 +117,7 @@ async fn user_update_with_invalid_email() {
#[tokio::test]
async fn user_update_with_invalid_password() {
let test = EventIntegrationTest::new();
let user = test.init_user().await;
let user = test.init_anon_user().await;
for password in invalid_password_test_case() {
let request = UpdateUserProfilePayloadPB::new(user.id).password(&password);
@ -129,7 +134,7 @@ async fn user_update_with_invalid_password() {
#[tokio::test]
async fn user_update_with_invalid_name() {
let test = EventIntegrationTest::new();
let user = test.init_user().await;
let user = test.init_anon_user().await;
let request = UpdateUserProfilePayloadPB::new(user.id).name("");
assert!(EventBuilder::new(test.clone())
.event(UpdateUserProfile)

View File

@ -194,17 +194,6 @@ async fn sign_up_as_guest_and_then_update_to_existing_cloud_user_test() {
}
}
#[tokio::test]
async fn check_not_exist_user_test() {
if let Some(test) = FlowySupabaseTest::new() {
let err = test
.check_user_with_uuid(&uuid::Uuid::new_v4().to_string())
.await
.unwrap_err();
assert_eq!(err.code, ErrorCode::RecordNotFound);
}
}
#[tokio::test]
async fn get_user_profile_test() {
if let Some(test) = FlowySupabaseTest::new() {

View File

@ -23,7 +23,7 @@ use flowy_server::supabase::api::*;
use flowy_server::{AppFlowyEncryption, EncryptionImpl};
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
use flowy_server_config::supabase_config::SupabaseConfiguration;
use flowy_user::entities::{AuthTypePB, UpdateUserProfilePayloadPB, UserCredentialsPB};
use flowy_user::entities::{AuthTypePB, UpdateUserProfilePayloadPB};
use flowy_user::errors::FlowyError;
use flowy_user::event_map::UserCloudServiceProvider;
use flowy_user::event_map::UserEvent::*;
@ -49,19 +49,6 @@ impl FlowySupabaseTest {
Some(Self { inner: test })
}
pub async fn check_user_with_uuid(&self, uuid: &str) -> Result<(), FlowyError> {
match EventBuilder::new(self.inner.clone())
.event(CheckUser)
.payload(UserCredentialsPB::from_uuid(uuid))
.async_send()
.await
.error()
{
None => Ok(()),
Some(error) => Err(error),
}
}
pub async fn update_user_profile(
&self,
payload: UpdateUserProfilePayloadPB,

View File

@ -91,6 +91,11 @@ impl ServerProvider {
}
pub fn set_server_type(&self, server_type: ServerType) {
let old_server_type = self.server_type.read().clone();
if server_type != old_server_type {
self.providers.write().remove(&old_server_type);
}
*self.server_type.write() = server_type;
}
@ -179,9 +184,9 @@ struct LocalServerDBImpl {
}
impl LocalServerDB for LocalServerDBImpl {
fn get_user_profile(&self, uid: i64) -> Result<Option<UserProfile>, FlowyError> {
fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
let sqlite_db = open_user_db(&self.storage_path, uid)?;
let user_profile = get_user_profile(&sqlite_db, uid).ok();
let user_profile = get_user_profile(&sqlite_db, uid)?;
Ok(user_profile)
}

View File

@ -38,7 +38,7 @@ pub struct DatabaseEditorTest {
impl DatabaseEditorTest {
pub async fn new_grid() -> Self {
let sdk = EventIntegrationTest::new();
let _ = sdk.init_user().await;
let _ = sdk.init_anon_user().await;
let params = make_test_grid();
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
@ -47,7 +47,7 @@ impl DatabaseEditorTest {
pub async fn new_no_date_grid() -> Self {
let sdk = EventIntegrationTest::new();
let _ = sdk.init_user().await;
let _ = sdk.init_anon_user().await;
let params = make_no_date_test_grid();
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
@ -56,7 +56,7 @@ impl DatabaseEditorTest {
pub async fn new_board() -> Self {
let sdk = EventIntegrationTest::new();
let _ = sdk.init_user().await;
let _ = sdk.init_anon_user().await;
let params = make_test_board();
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;
@ -65,7 +65,7 @@ impl DatabaseEditorTest {
pub async fn new_calendar() -> Self {
let sdk = EventIntegrationTest::new();
let _ = sdk.init_user().await;
let _ = sdk.init_anon_user().await;
let params = make_test_calendar();
let view_test = ViewTest::new_grid_view(&sdk, params.to_json_bytes().unwrap()).await;

View File

@ -58,10 +58,9 @@ async fn get_checklist_cell_options(test: &DatabaseFilterTest) -> Vec<String> {
.get_cell(&field.id, &test.row_details[0].row.id)
.await;
row_cell
.map_or_else(
|| ChecklistCellData::default(),
|cell| ChecklistCellData::from(&cell),
)
.map_or(ChecklistCellData::default(), |cell| {
ChecklistCellData::from(&cell)
})
.options
.into_iter()
.map(|option| option.id)

View File

@ -102,15 +102,14 @@ where
fn get_user_profile(
&self,
_credential: UserCredentials,
) -> FutureResult<Option<UserProfile>, FlowyError> {
) -> FutureResult<UserProfile, FlowyError> {
let try_get_client = self.server.try_get_client();
FutureResult::new(async move {
let client = try_get_client?;
let profile = client.get_profile().await?;
let token = client.get_token()?;
let profile = user_profile_from_af_profile(token, profile)?;
Ok(Some(profile))
Ok(profile)
})
}
@ -122,29 +121,6 @@ where
})
}
fn check_user(&self, credential: UserCredentials) -> FutureResult<(), Error> {
let try_get_client = self.server.try_get_client();
FutureResult::new(async move {
// from params
let token = credential.token.ok_or(anyhow!("expecting token"))?;
let uid = credential.uid.ok_or(anyhow!("expecting uid"))?;
// from cloud
let client = try_get_client?;
let profile = client.get_profile().await?;
let client_token = client.access_token()?;
// compare and check
if uid != profile.uid {
return Err(anyhow!("uid mismatch"));
}
if token != client_token {
return Err(anyhow!("token mismatch"));
}
Ok(())
})
}
fn add_workspace_member(
&self,
user_email: String,

View File

@ -86,7 +86,7 @@ impl AppFlowyServer for AFCloudServer {
fn set_token(&self, token: &str) -> Result<(), Error> {
self
.client
.set_token(token)
.restore_token(token)
.map_err(|err| Error::new(FlowyError::unauthorized().with_context(err)))
}

View File

@ -102,21 +102,24 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
FutureResult::new(async { Ok(()) })
}
fn get_user_profile(
&self,
_credential: UserCredentials,
) -> FutureResult<Option<UserProfile>, FlowyError> {
FutureResult::new(async { Ok(None) })
fn get_user_profile(&self, credential: UserCredentials) -> FutureResult<UserProfile, FlowyError> {
let result = match credential.uid {
None => Err(FlowyError::record_not_found()),
Some(uid) => {
self.db.get_user_profile(uid).map(|mut profile| {
// We don't want to expose the email in the local server
profile.email = "".to_string();
profile
})
},
};
FutureResult::new(async { result })
}
fn get_all_user_workspaces(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
FutureResult::new(async { Ok(vec![]) })
}
fn check_user(&self, _credential: UserCredentials) -> FutureResult<(), Error> {
FutureResult::new(async { Ok(()) })
}
fn add_workspace_member(
&self,
_user_email: String,

View File

@ -21,7 +21,7 @@ use crate::local_server::impls::{
use crate::AppFlowyServer;
pub trait LocalServerDB: Send + Sync + 'static {
fn get_user_profile(&self, uid: i64) -> Result<Option<UserProfile>, FlowyError>;
fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError>;
fn get_user_workspace(&self, uid: i64) -> Result<Option<UserWorkspace>, FlowyError>;
fn get_collab_updates(&self, uid: i64, object_id: &str) -> Result<Vec<Vec<u8>>, FlowyError>;
}

View File

@ -2,7 +2,6 @@ use std::collections::HashMap;
use std::future::Future;
use std::iter::Take;
use std::pin::Pin;
use std::str::FromStr;
use std::sync::{Arc, Weak};
use std::time::Duration;
@ -199,10 +198,7 @@ where
})
}
fn get_user_profile(
&self,
credential: UserCredentials,
) -> FutureResult<Option<UserProfile>, FlowyError> {
fn get_user_profile(&self, credential: UserCredentials) -> FutureResult<UserProfile, FlowyError> {
let try_get_postgrest = self.server.try_get_postgrest();
let uid = credential
.uid
@ -212,8 +208,8 @@ where
let postgrest = try_get_postgrest?;
let user_profile_resp = get_user_profile(postgrest, GetUserProfileParams::Uid(uid)).await?;
match user_profile_resp {
None => Ok(None),
Some(response) => Ok(Some(UserProfile {
None => Err(FlowyError::record_not_found()),
Some(response) => Ok(UserProfile {
uid: response.uid,
email: response.email,
name: response.name,
@ -225,7 +221,7 @@ where
auth_type: AuthType::Supabase,
encryption_type: EncryptionType::from_sign(&response.encryption_sign),
updated_at: response.updated_at.timestamp(),
})),
}),
}
})
}
@ -238,18 +234,6 @@ where
Ok(user_workspaces)
})
}
fn check_user(&self, credential: UserCredentials) -> FutureResult<(), Error> {
let try_get_postgrest = self.server.try_get_postgrest();
let uuid = credential.uuid.and_then(|uuid| Uuid::from_str(&uuid).ok());
let uid = credential.uid;
FutureResult::new(async move {
let postgrest = try_get_postgrest?;
check_user(postgrest, uid, uuid).await?;
Ok(())
})
}
fn add_workspace_member(
&self,
_user_email: String,
@ -512,6 +496,7 @@ async fn update_user_profile(
Ok(())
}
#[allow(dead_code)]
async fn check_user(
postgrest: Arc<PostgresWrapper>,
uid: Option<i64>,

View File

@ -1,6 +1,7 @@
use uuid::Uuid;
use flowy_encrypt::{encrypt_text, generate_encryption_secret};
use flowy_error::FlowyError;
use flowy_user_deps::entities::*;
use lib_infra::box_any::BoxAny;
@ -66,7 +67,6 @@ async fn supabase_update_user_profile_test() {
let user_profile = user_service
.get_user_profile(UserCredentials::from_uid(user.user_id))
.await
.unwrap()
.unwrap();
assert_eq!(user_profile.name, "123");
@ -89,7 +89,6 @@ async fn supabase_get_user_profile_test() {
user_service
.get_user_profile(credential.clone())
.await
.unwrap()
.unwrap();
}
@ -100,12 +99,12 @@ async fn supabase_get_not_exist_user_profile_test() {
}
let user_service = user_auth_service();
let result = user_service
let result: FlowyError = user_service
.get_user_profile(UserCredentials::from_uid(i64::MAX))
.await
.unwrap();
.unwrap_err();
// user not found
assert!(result.is_none());
assert!(result.is_record_not_found());
}
#[tokio::test]
@ -134,7 +133,6 @@ async fn user_encryption_sign_test() {
let user_profile: UserProfile = user_service
.get_user_profile(UserCredentials::from_uid(user.user_id))
.await
.unwrap()
.unwrap();
assert_eq!(
user_profile.encryption_type,

View File

@ -89,16 +89,11 @@ pub trait UserCloudService: Send + Sync + 'static {
/// Get the user information using the user's token or uid
/// return None if the user is not found
fn get_user_profile(
&self,
credential: UserCredentials,
) -> FutureResult<Option<UserProfile>, FlowyError>;
fn get_user_profile(&self, credential: UserCredentials) -> FutureResult<UserProfile, FlowyError>;
/// Return the all the workspaces of the user
fn get_all_user_workspaces(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error>;
fn check_user(&self, credential: UserCredentials) -> FutureResult<(), Error>;
fn add_workspace_member(
&self,
user_email: String,

View File

@ -82,30 +82,29 @@ pub async fn init_user_handler(
}
#[tracing::instrument(level = "debug", skip(manager))]
pub async fn check_user_handler(
manager: AFPluginState<Weak<UserManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
manager.check_user().await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(manager), err)]
pub async fn get_user_profile_handler(
manager: AFPluginState<Weak<UserManager>>,
) -> DataResult<UserProfilePB, FlowyError> {
let manager = upgrade_manager(manager)?;
let uid = manager.get_session()?.user_id;
let user_profile = manager.get_user_profile(uid).await?;
let mut user_profile = manager.get_user_profile(uid).await?;
let weak_manager = Arc::downgrade(&manager);
let cloned_user_profile = user_profile.clone();
// Refresh the user profile in the background
tokio::spawn(async move {
if let Some(manager) = weak_manager.upgrade() {
let _ = manager.refresh_user_profile(&cloned_user_profile).await;
}
});
// When the user is logged in with a local account, the email field is a placeholder and should
// not be exposed to the client. So we set the email field to an empty string.
if user_profile.auth_type == AuthType::Local {
user_profile.email = "".to_string();
}
event!(
tracing::Level::DEBUG,
"Get user profile: {:?}",

View File

@ -30,7 +30,6 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
.event(UserEvent::GetUserProfile, get_user_profile_handler)
.event(UserEvent::SignOut, sign_out)
.event(UserEvent::UpdateUserProfile, update_user_profile_handler)
.event(UserEvent::CheckUser, check_user_handler)
.event(UserEvent::SetAppearanceSetting, set_appearance_setting)
.event(UserEvent::GetAppearanceSetting, get_appearance_setting)
.event(UserEvent::GetUserSetting, get_user_setting)
@ -232,11 +231,8 @@ pub enum UserEvent {
#[event(output = "UserProfilePB")]
GetUserProfile = 4,
/// Check the user current session is valid or not
#[event(output = "UserProfilePB")]
CheckUser = 5,
/// Initialize resources for the current user after launching the application
///
#[event()]
InitUser = 6,

View File

@ -97,7 +97,7 @@ impl UserManager {
while let Ok(update) = rx.recv().await {
if let Some(user_manager) = weak_user_manager.upgrade() {
if let Err(err) = user_manager.handler_user_update(update).await {
tracing::error!("handler_user_update failed: {:?}", err);
error!("handler_user_update failed: {:?}", err);
}
}
}
@ -172,10 +172,10 @@ impl UserManager {
info!("Did apply migrations: {:?}", applied_migrations);
}
},
Err(e) => tracing::error!("User data migration failed: {:?}", e),
Err(e) => error!("User data migration failed: {:?}", e),
}
},
_ => tracing::error!("Failed to get collab db or sqlite pool"),
_ => error!("Failed to get collab db or sqlite pool"),
}
self.set_collab_config(&session);
// Init the user awareness
@ -193,7 +193,7 @@ impl UserManager {
)
.await
{
tracing::error!("Failed to call did_init callback: {:?}", e);
error!("Failed to call did_init callback: {:?}", e);
}
}
*self.user_status_callback.write().await = Arc::new(user_status_callback);
@ -253,7 +253,7 @@ impl UserManager {
.did_sign_in(user_profile.uid, &latest_workspace, &session.device_id)
.await
{
tracing::error!("Failed to call did_sign_in callback: {:?}", e);
error!("Failed to call did_sign_in callback: {:?}", e);
}
send_auth_state_notification(AuthStateChangedPB {
state: AuthStatePB::AuthStateSignIn,
@ -349,7 +349,6 @@ impl UserManager {
UserAwarenessDataSource::Remote
};
event!(tracing::Level::DEBUG, "Sign up response: {:?}", response);
if response.is_new_user {
if let Some(old_user) = migration_user {
let new_user = MigrationUser {
@ -403,9 +402,8 @@ impl UserManager {
let server = self.cloud_services.get_user_service()?;
tokio::spawn(async move {
match server.sign_out(None).await {
Ok(_) => {},
Err(e) => event!(tracing::Level::ERROR, "{:?}", e),
if let Err(err) = server.sign_out(None).await {
event!(tracing::Level::ERROR, "{:?}", err);
}
});
Ok(())
@ -437,15 +435,6 @@ impl UserManager {
Ok(())
}
pub async fn check_user(&self) -> Result<(), FlowyError> {
let user_id = self.get_session()?.user_id;
let user = self.get_user_profile(user_id).await?;
let credential = UserCredentials::new(Some(user.token), Some(user_id), None);
let auth_service = self.cloud_services.get_user_service()?;
auth_service.check_user(credential).await?;
Ok(())
}
/// Fetches the user profile for the given user ID.
pub async fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
let user: UserProfile = user_table::dsl::user_table
@ -457,27 +446,63 @@ impl UserManager {
}
#[tracing::instrument(level = "info", skip_all)]
pub async fn refresh_user_profile(
&self,
old_user_profile: &UserProfile,
) -> FlowyResult<UserProfile> {
pub async fn refresh_user_profile(&self, old_user_profile: &UserProfile) -> FlowyResult<()> {
let uid = old_user_profile.uid;
let new_user_profile: UserProfile = self
let result: Result<UserProfile, FlowyError> = self
.cloud_services
.get_user_service()?
.get_user_profile(UserCredentials::from_uid(uid))
.await?
.ok_or_else(|| FlowyError::new(ErrorCode::RecordNotFound, "User not found"))?;
.await;
if new_user_profile.updated_at > old_user_profile.updated_at {
check_encryption_sign(old_user_profile, &new_user_profile.encryption_type.sign());
match result {
Ok(new_user_profile) => {
// If the authentication type has changed, it indicates that the user has signed in
// using a different release package but is sharing the same data folder.
// In such cases, notify the frontend to log out.
if old_user_profile.auth_type != AuthType::Local
&& new_user_profile.auth_type != old_user_profile.auth_type
{
event!(
tracing::Level::INFO,
"User login with different cloud: {:?} -> {:?}",
old_user_profile.auth_type,
new_user_profile.auth_type
);
// 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);
send_auth_state_notification(AuthStateChangedPB {
state: AuthStatePB::InvalidAuth,
message: "User login with different cloud".to_string(),
})
.send();
return Ok(());
}
// If the user profile is updated, save the new user profile
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);
let _ = upsert_user_profile_change(uid, self.database.get_pool(uid)?, changeset);
}
Ok(())
},
Err(err) => {
// If the user is not found, notify the frontend to logout
if err.is_record_not_found() {
event!(
tracing::Level::INFO,
"User is not found on the server when refreshing profile"
);
send_auth_state_notification(AuthStateChangedPB {
state: AuthStatePB::InvalidAuth,
message: "User is not found on the server".to_string(),
})
.send();
}
Err(err)
},
}
Ok(new_user_profile)
}
pub fn user_dir(&self, uid: i64) -> String {
@ -680,7 +705,7 @@ impl UserManager {
)
.await
{
tracing::error!("Sync user data to cloud failed: {:?}", err);
error!("Sync user data to cloud failed: {:?}", err);
}
// Save the old user workspace setting.

View File

@ -26,6 +26,7 @@ pub(crate) fn send_notification(id: &str, ty: UserNotification) -> NotificationB
NotificationBuilder::new(id, ty, USER_OBSERVABLE_SOURCE)
}
#[tracing::instrument(level = "trace")]
pub(crate) fn send_auth_state_notification(payload: AuthStateChangedPB) -> NotificationBuilder {
NotificationBuilder::new(
"auth_state_change_notification",