chore: replace str with uuid

This commit is contained in:
Nathan 2025-04-07 19:24:58 +08:00
parent e870481ef9
commit 0975f6c8c0
100 changed files with 1557 additions and 1346 deletions

View file

@ -496,7 +496,7 @@ checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"anyhow",
"bincode",
@ -516,16 +516,16 @@ dependencies = [
[[package]]
name = "appflowy-ai-client"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"anyhow",
"bytes",
"futures",
"pin-project",
"serde",
"serde_json",
"serde_repr",
"thiserror 1.0.64",
"uuid",
]
[[package]]
@ -1137,7 +1137,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"again",
"anyhow",
@ -1192,7 +1192,7 @@ dependencies = [
[[package]]
name = "client-api-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"collab-entity",
"collab-rt-entity",
@ -1205,7 +1205,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"futures-channel",
"futures-util",
@ -1248,7 +1248,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4a0e2cc07f50f17d1b6605c579e622a431e94998#4a0e2cc07f50f17d1b6605c579e622a431e94998"
dependencies = [
"anyhow",
"arc-swap",
@ -1273,7 +1273,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4a0e2cc07f50f17d1b6605c579e622a431e94998#4a0e2cc07f50f17d1b6605c579e622a431e94998"
dependencies = [
"anyhow",
"async-trait",
@ -1313,7 +1313,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4a0e2cc07f50f17d1b6605c579e622a431e94998#4a0e2cc07f50f17d1b6605c579e622a431e94998"
dependencies = [
"anyhow",
"arc-swap",
@ -1334,7 +1334,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4a0e2cc07f50f17d1b6605c579e622a431e94998#4a0e2cc07f50f17d1b6605c579e622a431e94998"
dependencies = [
"anyhow",
"bytes",
@ -1354,7 +1354,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4a0e2cc07f50f17d1b6605c579e622a431e94998#4a0e2cc07f50f17d1b6605c579e622a431e94998"
dependencies = [
"anyhow",
"arc-swap",
@ -1376,7 +1376,7 @@ dependencies = [
[[package]]
name = "collab-importer"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4a0e2cc07f50f17d1b6605c579e622a431e94998#4a0e2cc07f50f17d1b6605c579e622a431e94998"
dependencies = [
"anyhow",
"async-recursion",
@ -1418,7 +1418,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"arc-swap",
"async-trait",
"collab",
"collab-database",
"collab-document",
@ -1429,18 +1428,18 @@ dependencies = [
"diesel",
"flowy-error",
"flowy-sqlite",
"futures",
"lib-infra",
"serde",
"serde_json",
"tokio",
"tracing",
"uuid",
]
[[package]]
name = "collab-plugins"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4a0e2cc07f50f17d1b6605c579e622a431e94998#4a0e2cc07f50f17d1b6605c579e622a431e94998"
dependencies = [
"anyhow",
"async-stream",
@ -1478,7 +1477,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"anyhow",
"bincode",
@ -1500,7 +1499,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"anyhow",
"async-trait",
@ -1511,13 +1510,14 @@ dependencies = [
"thiserror 1.0.64",
"tokio",
"tracing",
"uuid",
"yrs",
]
[[package]]
name = "collab-user"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4a0e2cc07f50f17d1b6605c579e622a431e94998#4a0e2cc07f50f17d1b6605c579e622a431e94998"
dependencies = [
"anyhow",
"collab",
@ -1764,7 +1764,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"phf 0.11.2",
"phf 0.8.0",
"smallvec",
]
@ -1947,7 +1947,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"bincode",
"bytes",
@ -2513,13 +2513,13 @@ dependencies = [
name = "flowy-ai-pub"
version = "0.1.0"
dependencies = [
"bytes",
"client-api",
"flowy-error",
"futures",
"lib-infra",
"serde",
"serde_json",
"uuid",
]
[[package]]
@ -2592,7 +2592,6 @@ dependencies = [
"flowy-storage-pub",
"flowy-user",
"flowy-user-pub",
"futures",
"futures-core",
"lib-dispatch",
"lib-infra",
@ -2606,19 +2605,18 @@ dependencies = [
"tokio-stream",
"tracing",
"uuid",
"walkdir",
]
[[package]]
name = "flowy-database-pub"
version = "0.1.0"
dependencies = [
"anyhow",
"client-api",
"collab",
"collab-entity",
"flowy-error",
"lib-infra",
"uuid",
]
[[package]]
@ -2667,6 +2665,7 @@ dependencies = [
"tokio-util",
"tracing",
"url",
"uuid",
"validator 0.18.1",
]
@ -2744,11 +2743,11 @@ dependencies = [
name = "flowy-document-pub"
version = "0.1.0"
dependencies = [
"anyhow",
"collab",
"collab-document",
"flowy-error",
"lib-infra",
"uuid",
]
[[package]]
@ -2778,6 +2777,7 @@ dependencies = [
"thiserror 1.0.64",
"tokio",
"url",
"uuid",
"validator 0.18.1",
]
@ -2864,16 +2864,12 @@ dependencies = [
"bytes",
"collab",
"collab-folder",
"diesel",
"diesel_derives",
"diesel_migrations",
"flowy-codegen",
"flowy-derive",
"flowy-error",
"flowy-folder",
"flowy-notification",
"flowy-search-pub",
"flowy-sqlite",
"flowy-user",
"futures",
"lib-dispatch",
@ -2887,7 +2883,7 @@ dependencies = [
"tempfile",
"tokio",
"tracing",
"validator 0.18.1",
"uuid",
]
[[package]]
@ -2898,8 +2894,8 @@ dependencies = [
"collab",
"collab-folder",
"flowy-error",
"futures",
"lib-infra",
"uuid",
]
[[package]]
@ -2991,7 +2987,6 @@ name = "flowy-storage"
version = "0.1.0"
dependencies = [
"allo-isolate",
"anyhow",
"async-trait",
"bytes",
"chrono",
@ -3003,8 +2998,6 @@ dependencies = [
"flowy-notification",
"flowy-sqlite",
"flowy-storage-pub",
"futures-util",
"fxhash",
"lib-dispatch",
"lib-infra",
"mime_guess",
@ -3032,9 +3025,8 @@ dependencies = [
"mime",
"mime_guess",
"serde",
"serde_json",
"tokio",
"tracing",
"uuid",
]
[[package]]
@ -3432,7 +3424,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"anyhow",
"getrandom 0.2.10",
@ -3447,7 +3439,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"app-error",
"jsonwebtoken",
@ -4068,7 +4060,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"anyhow",
"bytes",
@ -5181,7 +5173,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",
]
@ -5201,7 +5193,6 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros 0.11.3",
"phf_shared 0.11.2",
]
@ -5269,19 +5260,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "phf_macros"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator 0.11.2",
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 2.0.94",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
@ -6782,7 +6760,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
dependencies = [
"anyhow",
"app-error",

View file

@ -103,8 +103,8 @@ dashmap = "6.0.1"
# Run the script.add_workspace_members:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "f7288f46c27dc8e3c7829cda1b70b61118e88336" }
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "f7288f46c27dc8e3c7829cda1b70b61118e88336" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e" }
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e" }
[profile.dev]
opt-level = 0
@ -139,14 +139,14 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4a0e2cc07f50f17d1b6605c579e622a431e94998" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4a0e2cc07f50f17d1b6605c579e622a431e94998" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4a0e2cc07f50f17d1b6605c579e622a431e94998" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4a0e2cc07f50f17d1b6605c579e622a431e94998" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4a0e2cc07f50f17d1b6605c579e622a431e94998" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4a0e2cc07f50f17d1b6605c579e622a431e94998" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4a0e2cc07f50f17d1b6605c579e622a431e94998" }
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4a0e2cc07f50f17d1b6605c579e622a431e94998" }
# Working directory: frontend
# To update the commit ID, run:

View file

@ -19,14 +19,13 @@ serde.workspace = true
serde_json.workspace = true
anyhow.workspace = true
tracing.workspace = true
async-trait.workspace = true
tokio = { workspace = true, features = ["sync"] }
lib-infra = { workspace = true }
futures = "0.3.31"
arc-swap = "1.7"
flowy-sqlite = { workspace = true }
diesel.workspace = true
flowy-error.workspace = true
uuid.workspace = true
[features]
default = []

View file

@ -1,5 +1,6 @@
use std::borrow::BorrowMut;
use std::fmt::{Debug, Display};
use std::str::FromStr;
use std::sync::{Arc, Weak};
use crate::CollabKVDB;
@ -33,8 +34,10 @@ use collab_plugins::local_storage::kv::KVTransactionDB;
use collab_plugins::local_storage::CollabPersistenceConfig;
use collab_user::core::{UserAwareness, UserAwarenessNotifier};
use flowy_error::FlowyError;
use lib_infra::{if_native, if_wasm};
use tracing::{error, instrument, trace, warn};
use uuid::Uuid;
#[derive(Clone, Debug)]
pub enum CollabPluginProviderType {
@ -66,8 +69,8 @@ impl Display for CollabPluginProviderContext {
}
pub trait WorkspaceCollabIntegrate: Send + Sync {
fn workspace_id(&self) -> Result<String, Error>;
fn device_id(&self) -> Result<String, Error>;
fn workspace_id(&self) -> Result<Uuid, FlowyError>;
fn device_id(&self) -> Result<String, FlowyError>;
}
pub struct AppFlowyCollabBuilder {
@ -119,15 +122,15 @@ impl AppFlowyCollabBuilder {
pub fn collab_object(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: i64,
object_id: &str,
object_id: &Uuid,
collab_type: CollabType,
) -> Result<CollabObject, Error> {
// Compare the workspace_id with the currently opened workspace_id. Return an error if they do not match.
// This check is crucial in asynchronous code contexts where the workspace_id might change during operation.
let actual_workspace_id = self.workspace_integrate.workspace_id()?;
if workspace_id != actual_workspace_id {
if workspace_id != &actual_workspace_id {
return Err(anyhow::anyhow!(
"workspace_id not match when build collab. expect workspace_id: {}, actual workspace_id: {}",
workspace_id,
@ -140,7 +143,7 @@ impl AppFlowyCollabBuilder {
uid,
object_id.to_string(),
collab_type,
workspace_id,
workspace_id.to_string(),
device_id,
))
}
@ -399,11 +402,11 @@ impl CollabBuilderConfig {
pub struct CollabPersistenceImpl {
pub db: Weak<CollabKVDB>,
pub uid: i64,
pub workspace_id: String,
pub workspace_id: Uuid,
}
impl CollabPersistenceImpl {
pub fn new(db: Weak<CollabKVDB>, uid: i64, workspace_id: String) -> Self {
pub fn new(db: Weak<CollabKVDB>, uid: i64, workspace_id: Uuid) -> Self {
Self {
db,
uid,
@ -423,7 +426,8 @@ impl CollabPersistence for CollabPersistenceImpl {
.upgrade()
.ok_or_else(|| CollabError::Internal(anyhow!("collab_db is dropped")))?;
let object_id = collab.object_id().to_string();
let object_id =
Uuid::from_str(collab.object_id()).map_err(|v| CollabError::Internal(v.into()))?;
let rocksdb_read = collab_db.read_txn();
if rocksdb_read.is_exist(self.uid, &self.workspace_id, &object_id) {
@ -461,7 +465,7 @@ impl CollabPersistence for CollabPersistenceImpl {
write_txn
.flush_doc(
self.uid,
self.workspace_id.as_str(),
self.workspace_id.to_string().as_str(),
object_id,
encoded_collab.state_vector.to_vec(),
encoded_collab.doc_state.to_vec(),

View file

@ -7,6 +7,8 @@ use flowy_sqlite::{
DBConnection, ExpressionMethods, Identifiable, Insertable, Queryable,
};
use std::collections::HashMap;
use std::str::FromStr;
use uuid::Uuid;
#[derive(Queryable, Insertable, Identifiable)]
#[diesel(table_name = af_collab_metadata)]
@ -43,13 +45,18 @@ pub fn batch_insert_collab_metadata(
pub fn batch_select_collab_metadata(
mut conn: DBConnection,
object_ids: &[String],
) -> FlowyResult<HashMap<String, AFCollabMetadata>> {
object_ids: &[Uuid],
) -> FlowyResult<HashMap<Uuid, AFCollabMetadata>> {
let object_ids = object_ids
.iter()
.map(|id| id.to_string())
.collect::<Vec<String>>();
let metadata = dsl::af_collab_metadata
.filter(af_collab_metadata::object_id.eq_any(object_ids))
.filter(af_collab_metadata::object_id.eq_any(&object_ids))
.load::<AFCollabMetadata>(&mut conn)?
.into_iter()
.map(|m| (m.object_id.clone(), m))
.flat_map(|m| Uuid::from_str(&m.object_id).and_then(|v| Ok((v, m))))
.collect();
Ok(metadata)
}

View file

@ -1,8 +1,6 @@
use collab::entity::EncodedCollab;
use std::collections::HashMap;
use serde_json::Value;
use flowy_document::entities::*;
use flowy_document::event_map::DocumentEvent;
use flowy_document::parser::parser_entities::{
@ -11,6 +9,8 @@ use flowy_document::parser::parser_entities::{
};
use flowy_folder::entities::{CreateViewPayloadPB, ViewLayoutPB, ViewPB};
use flowy_folder::event_map::FolderEvent;
use serde_json::Value;
use uuid::Uuid;
use crate::document::utils::{gen_delta_str, gen_id, gen_text_block_data};
use crate::event_builder::EventBuilder;
@ -37,7 +37,7 @@ impl DocumentEventTest {
Self { event_test: core }
}
pub async fn get_encoded_v1(&self, doc_id: &str) -> EncodedCollab {
pub async fn get_encoded_v1(&self, doc_id: &Uuid) -> EncodedCollab {
let doc = self
.event_test
.appflowy_core

View file

@ -1,4 +1,5 @@
use flowy_folder::view_operation::{GatherEncodedCollab, ViewData};
use std::str::FromStr;
use std::sync::Arc;
use collab_folder::{FolderData, View};
@ -16,6 +17,7 @@ use flowy_user::entities::{
use flowy_user::errors::FlowyError;
use flowy_user::event_map::UserEvent;
use flowy_user_pub::entities::Role;
use uuid::Uuid;
use crate::event_builder::EventBuilder;
use crate::EventIntegrationTest;
@ -123,10 +125,10 @@ impl EventIntegrationTest {
let create_view_params = views
.into_iter()
.map(|view| CreateViewParams {
parent_view_id: view.parent_view_id,
parent_view_id: Uuid::from_str(&view.parent_view_id).unwrap(),
name: view.name,
layout: view.layout.into(),
view_id: view.id,
view_id: Uuid::from_str(&view.id).unwrap(),
initial_data: ViewData::Empty,
meta: Default::default(),
set_as_current: false,
@ -195,9 +197,10 @@ impl EventIntegrationTest {
view_id: &str,
layout: ViewLayout,
) -> GatherEncodedCollab {
let view_id = Uuid::from_str(view_id).unwrap();
self
.folder_manager
.gather_publish_encode_collab(view_id, &layout)
.gather_publish_encode_collab(&view_id, &layout)
.await
.unwrap()
}

View file

@ -1,3 +1,4 @@
use crate::user_event::TestNotificationSender;
use collab::core::collab::DataSource;
use collab::core::origin::CollabOrigin;
use collab::preclude::Collab;
@ -15,14 +16,14 @@ use nanoid::nanoid;
use semver::Version;
use std::env::temp_dir;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::select;
use tokio::task::LocalSet;
use tokio::time::sleep;
use crate::user_event::TestNotificationSender;
use uuid::Uuid;
mod chat_event;
pub mod database_event;
@ -145,10 +146,16 @@ impl EventIntegrationTest {
) -> Result<Vec<u8>, FlowyError> {
let server = self.server_provider.get_server().unwrap();
let workspace_id = self.get_current_workspace().await.id;
let oid = Uuid::from_str(oid).unwrap();
let uid = self.get_user_profile().await?.id;
let doc_state = server
.folder_service()
.get_folder_doc_state(&workspace_id, uid, collab_type, oid)
.get_folder_doc_state(
&Uuid::from_str(&workspace_id).unwrap(),
uid,
collab_type,
&oid,
)
.await?;
Ok(doc_state)

View file

@ -3,10 +3,12 @@ use event_integration_test::user_event::use_localhost_af_cloud;
use event_integration_test::EventIntegrationTest;
use flowy_ai::entities::ChatMessageListPB;
use flowy_ai::notification::ChatNotification;
use std::str::FromStr;
use flowy_ai_pub::cloud::ChatMessageType;
use std::time::Duration;
use uuid::Uuid;
#[tokio::test]
async fn af_cloud_create_chat_message_test() {
@ -21,8 +23,8 @@ async fn af_cloud_create_chat_message_test() {
for i in 0..10 {
let _ = chat_service
.create_question(
&current_workspace.id,
&chat_id,
&Uuid::from_str(&current_workspace.id).unwrap(),
&Uuid::from_str(&chat_id).unwrap(),
&format!("hello world {}", i),
ChatMessageType::System,
&[],
@ -77,8 +79,8 @@ async fn af_cloud_load_remote_system_message_test() {
for i in 0..10 {
let _ = chat_service
.create_question(
&current_workspace.id,
&chat_id,
&Uuid::from_str(&current_workspace.id).unwrap(),
&Uuid::from_str(&chat_id).unwrap(),
&format!("hello server {}", i),
ChatMessageType::System,
&[],

View file

@ -8,6 +8,8 @@ use flowy_document::parser::parser_entities::{
};
use serde_json::{json, Value};
use std::collections::HashMap;
use std::str::FromStr;
use uuid::Uuid;
#[tokio::test]
async fn get_document_event_test() {
@ -101,8 +103,8 @@ async fn document_size_test() {
let s = generate_random_string(string_size);
test.insert_index(&view.id, &s, 1, None).await;
}
let encoded_v1 = test.get_encoded_v1(&view.id).await;
let view_id = Uuid::from_str(&view.id).unwrap();
let encoded_v1 = test.get_encoded_v1(&view_id).await;
if encoded_v1.doc_state.len() > max_size {
panic!(
"The document size is too large. {}",

View file

@ -9,7 +9,7 @@ edition = "2021"
lib-infra = { workspace = true }
flowy-error = { workspace = true }
client-api = { workspace = true }
bytes.workspace = true
futures.workspace = true
serde_json.workspace = true
serde.workspace = true
serde.workspace = true
uuid.workspace = true

View file

@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::path::Path;
use uuid::Uuid;
pub type ChatMessageStream = BoxStream<'static, Result<ChatMessage, AppResponseError>>;
pub type StreamAnswer = BoxStream<'static, Result<QuestionStreamValue, FlowyError>>;
@ -81,15 +82,15 @@ pub trait ChatCloudService: Send + Sync + 'static {
async fn create_chat(
&self,
uid: &i64,
workspace_id: &str,
chat_id: &str,
rag_ids: Vec<String>,
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
) -> Result<(), FlowyError>;
async fn create_question(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
message_type: ChatMessageType,
metadata: &[ChatMessageMetadata],
@ -97,8 +98,8 @@ pub trait ChatCloudService: Send + Sync + 'static {
async fn create_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
question_id: i64,
metadata: Option<serde_json::Value>,
@ -106,8 +107,8 @@ pub trait ChatCloudService: Send + Sync + 'static {
async fn stream_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
format: ResponseFormat,
ai_model: Option<AIModel>,
@ -115,68 +116,68 @@ pub trait ChatCloudService: Send + Sync + 'static {
async fn get_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
question_message_id: i64,
) -> Result<ChatMessage, FlowyError>;
async fn get_chat_messages(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
offset: MessageCursor,
limit: u64,
) -> Result<RepeatedChatMessage, FlowyError>;
async fn get_question_from_answer_id(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
answer_message_id: i64,
) -> Result<ChatMessage, FlowyError>;
async fn get_related_message(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
) -> Result<RepeatedRelatedQuestion, FlowyError>;
async fn stream_complete(
&self,
workspace_id: &str,
workspace_id: &Uuid,
params: CompleteTextParams,
ai_model: Option<AIModel>,
) -> Result<StreamComplete, FlowyError>;
async fn embed_file(
&self,
workspace_id: &str,
workspace_id: &Uuid,
file_path: &Path,
chat_id: &str,
chat_id: &Uuid,
metadata: Option<HashMap<String, Value>>,
) -> Result<(), FlowyError>;
async fn get_local_ai_config(&self, workspace_id: &str) -> Result<LocalAIConfig, FlowyError>;
async fn get_local_ai_config(&self, workspace_id: &Uuid) -> Result<LocalAIConfig, FlowyError>;
async fn get_workspace_plan(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<SubscriptionPlan>, FlowyError>;
async fn get_chat_settings(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
) -> Result<ChatSettings, FlowyError>;
async fn update_chat_settings(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
params: UpdateChatParams,
) -> Result<(), FlowyError>;
async fn get_available_models(&self, workspace_id: &str) -> Result<ModelList, FlowyError>;
async fn get_workspace_default_model(&self, workspace_id: &str) -> Result<String, FlowyError>;
async fn get_available_models(&self, workspace_id: &Uuid) -> Result<ModelList, FlowyError>;
async fn get_workspace_default_model(&self, workspace_id: &Uuid) -> Result<String, FlowyError>;
}

View file

@ -27,14 +27,16 @@ use flowy_storage_pub::storage::StorageService;
use lib_infra::async_trait::async_trait;
use lib_infra::util::timestamp;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Weak};
use tokio::sync::RwLock;
use tracing::{error, info, instrument, trace};
use uuid::Uuid;
pub trait AIUserService: Send + Sync + 'static {
fn user_id(&self) -> Result<i64, FlowyError>;
fn device_id(&self) -> Result<String, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn workspace_id(&self) -> Result<Uuid, FlowyError>;
fn sqlite_connection(&self, uid: i64) -> Result<DBConnection, FlowyError>;
fn application_root_dir(&self) -> Result<PathBuf, FlowyError>;
}
@ -44,18 +46,18 @@ pub trait AIUserService: Send + Sync + 'static {
pub trait AIExternalService: Send + Sync + 'static {
async fn query_chat_rag_ids(
&self,
parent_view_id: &str,
chat_id: &str,
) -> Result<Vec<String>, FlowyError>;
parent_view_id: &Uuid,
chat_id: &Uuid,
) -> Result<Vec<Uuid>, FlowyError>;
async fn sync_rag_documents(
&self,
workspace_id: &str,
rag_ids: Vec<String>,
rag_metadata_map: HashMap<String, AFCollabMetadata>,
workspace_id: &Uuid,
rag_ids: Vec<Uuid>,
rag_metadata_map: HashMap<Uuid, AFCollabMetadata>,
) -> Result<Vec<AFCollabMetadata>, FlowyError>;
async fn notify_did_send_message(&self, chat_id: &str, message: &str) -> Result<(), FlowyError>;
async fn notify_did_send_message(&self, chat_id: &Uuid, message: &str) -> Result<(), FlowyError>;
}
#[derive(Debug, Default)]
@ -70,7 +72,7 @@ pub struct AIManager {
pub cloud_service_wm: Arc<AICloudServiceMiddleware>,
pub user_service: Arc<dyn AIUserService>,
pub external_service: Arc<dyn AIExternalService>,
chats: Arc<DashMap<String, Arc<Chat>>>,
chats: Arc<DashMap<Uuid, Arc<Chat>>>,
pub local_ai: Arc<LocalAIController>,
pub store_preferences: Arc<KVStorePreferences>,
server_models: Arc<RwLock<ServerModelsCache>>,
@ -132,11 +134,11 @@ impl AIManager {
Ok(())
}
pub async fn open_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
self.chats.entry(chat_id.to_string()).or_insert_with(|| {
pub async fn open_chat(&self, chat_id: &Uuid) -> Result<(), FlowyError> {
self.chats.entry(chat_id.clone()).or_insert_with(|| {
Arc::new(Chat::new(
self.user_service.user_id().unwrap(),
chat_id.to_string(),
chat_id.clone(),
self.user_service.clone(),
self.cloud_service_wm.clone(),
))
@ -150,7 +152,7 @@ impl AIManager {
let cloud_service_wm = self.cloud_service_wm.clone();
let store_preferences = self.store_preferences.clone();
let external_service = self.external_service.clone();
let chat_id = chat_id.to_string();
let chat_id = chat_id.clone();
tokio::spawn(async move {
match refresh_chat_setting(
&user_service,
@ -161,7 +163,12 @@ impl AIManager {
.await
{
Ok(settings) => {
let _ = sync_chat_documents(user_service, external_service, settings.rag_ids).await;
let rag_ids = settings
.rag_ids
.into_iter()
.flat_map(|r| Uuid::from_str(&r).ok())
.collect();
let _ = sync_chat_documents(user_service, external_service, rag_ids).await;
},
Err(err) => {
error!("failed to refresh chat settings: {}", err);
@ -172,13 +179,13 @@ impl AIManager {
Ok(())
}
pub async fn close_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
pub async fn close_chat(&self, chat_id: &Uuid) -> Result<(), FlowyError> {
trace!("close chat: {}", chat_id);
self.local_ai.close_chat(chat_id);
Ok(())
}
pub async fn delete_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
pub async fn delete_chat(&self, chat_id: &Uuid) -> Result<(), FlowyError> {
if let Some((_, chat)) = self.chats.remove(chat_id) {
chat.close();
@ -212,8 +219,8 @@ impl AIManager {
pub async fn create_chat(
&self,
uid: &i64,
parent_view_id: &str,
chat_id: &str,
parent_view_id: &Uuid,
chat_id: &Uuid,
) -> Result<Arc<Chat>, FlowyError> {
let workspace_id = self.user_service.workspace_id()?;
let rag_ids = self
@ -231,11 +238,11 @@ impl AIManager {
let chat = Arc::new(Chat::new(
self.user_service.user_id()?,
chat_id.to_string(),
chat_id.clone(),
self.user_service.clone(),
self.cloud_service_wm.clone(),
));
self.chats.insert(chat_id.to_string(), chat.clone());
self.chats.insert(chat_id.clone(), chat.clone());
Ok(chat)
}
@ -244,7 +251,7 @@ impl AIManager {
params: StreamMessageParams,
) -> Result<ChatMessagePB, FlowyError> {
let chat = self.get_or_create_chat_instance(&params.chat_id).await?;
let ai_model = self.get_active_model(&params.chat_id).await;
let ai_model = self.get_active_model(&params.chat_id.to_string()).await;
let question = chat.stream_chat_message(&params, ai_model).await?;
let _ = self
.external_service
@ -255,7 +262,7 @@ impl AIManager {
pub async fn stream_regenerate_response(
&self,
chat_id: &str,
chat_id: &Uuid,
answer_message_id: i64,
answer_stream_port: i64,
format: Option<PredefinedFormatPB>,
@ -270,7 +277,7 @@ impl AIManager {
|| {
self
.store_preferences
.get_object::<AIModel>(&ai_available_models_key(chat_id))
.get_object::<AIModel>(&ai_available_models_key(&chat_id.to_string()))
},
|model| Some(model.into()),
);
@ -520,17 +527,17 @@ impl AIManager {
})
}
pub async fn get_or_create_chat_instance(&self, chat_id: &str) -> Result<Arc<Chat>, FlowyError> {
pub async fn get_or_create_chat_instance(&self, chat_id: &Uuid) -> Result<Arc<Chat>, FlowyError> {
let chat = self.chats.get(chat_id).as_deref().cloned();
match chat {
None => {
let chat = Arc::new(Chat::new(
self.user_service.user_id()?,
chat_id.to_string(),
chat_id.clone(),
self.user_service.clone(),
self.cloud_service_wm.clone(),
));
self.chats.insert(chat_id.to_string(), chat.clone());
self.chats.insert(chat_id.clone(), chat.clone());
Ok(chat)
},
Some(chat) => Ok(chat),
@ -554,7 +561,7 @@ impl AIManager {
pub async fn load_prev_chat_messages(
&self,
chat_id: &str,
chat_id: &Uuid,
limit: i64,
before_message_id: Option<i64>,
) -> Result<ChatMessageListPB, FlowyError> {
@ -567,7 +574,7 @@ impl AIManager {
pub async fn load_latest_chat_messages(
&self,
chat_id: &str,
chat_id: &Uuid,
limit: i64,
after_message_id: Option<i64>,
) -> Result<ChatMessageListPB, FlowyError> {
@ -580,7 +587,7 @@ impl AIManager {
pub async fn get_related_questions(
&self,
chat_id: &str,
chat_id: &Uuid,
message_id: i64,
) -> Result<RepeatedRelatedQuestionPB, FlowyError> {
let chat = self.get_or_create_chat_instance(chat_id).await?;
@ -590,7 +597,7 @@ impl AIManager {
pub async fn generate_answer(
&self,
chat_id: &str,
chat_id: &Uuid,
question_message_id: i64,
) -> Result<ChatMessagePB, FlowyError> {
let chat = self.get_or_create_chat_instance(chat_id).await?;
@ -598,19 +605,19 @@ impl AIManager {
Ok(resp)
}
pub async fn stop_stream(&self, chat_id: &str) -> Result<(), FlowyError> {
pub async fn stop_stream(&self, chat_id: &Uuid) -> Result<(), FlowyError> {
let chat = self.get_or_create_chat_instance(chat_id).await?;
chat.stop_stream_message().await;
Ok(())
}
pub async fn chat_with_file(&self, chat_id: &str, file_path: PathBuf) -> FlowyResult<()> {
pub async fn chat_with_file(&self, chat_id: &Uuid, file_path: PathBuf) -> FlowyResult<()> {
let chat = self.get_or_create_chat_instance(chat_id).await?;
chat.index_file(file_path).await?;
Ok(())
}
pub async fn get_rag_ids(&self, chat_id: &str) -> FlowyResult<Vec<String>> {
pub async fn get_rag_ids(&self, chat_id: &Uuid) -> FlowyResult<Vec<String>> {
if let Some(settings) = self
.store_preferences
.get_object::<ChatSettings>(&setting_store_key(chat_id))
@ -628,7 +635,7 @@ impl AIManager {
Ok(settings.rag_ids)
}
pub async fn update_rag_ids(&self, chat_id: &str, rag_ids: Vec<String>) -> FlowyResult<()> {
pub async fn update_rag_ids(&self, chat_id: &Uuid, rag_ids: Vec<String>) -> FlowyResult<()> {
info!("[Chat] update chat:{} rag ids: {:?}", chat_id, rag_ids);
let workspace_id = self.user_service.workspace_id()?;
let update_setting = UpdateChatParams {
@ -659,6 +666,10 @@ impl AIManager {
let user_service = self.user_service.clone();
let external_service = self.external_service.clone();
let rag_ids = rag_ids
.into_iter()
.flat_map(|r| Uuid::from_str(&r).ok())
.collect();
sync_chat_documents(user_service, external_service, rag_ids).await?;
Ok(())
}
@ -667,7 +678,7 @@ impl AIManager {
async fn sync_chat_documents(
user_service: Arc<dyn AIUserService>,
external_service: Arc<dyn AIExternalService>,
rag_ids: Vec<String>,
rag_ids: Vec<Uuid>,
) -> FlowyResult<()> {
if rag_ids.is_empty() {
return Ok(());
@ -697,7 +708,7 @@ async fn sync_chat_documents(
Ok(())
}
fn save_chat(conn: DBConnection, chat_id: &str) -> FlowyResult<()> {
fn save_chat(conn: DBConnection, chat_id: &Uuid) -> FlowyResult<()> {
let row = ChatTable {
chat_id: chat_id.to_string(),
created_at: timestamp(),
@ -716,7 +727,7 @@ async fn refresh_chat_setting(
user_service: &Arc<dyn AIUserService>,
cloud_service: &Arc<AICloudServiceMiddleware>,
store_preferences: &Arc<KVStorePreferences>,
chat_id: &str,
chat_id: &Uuid,
) -> FlowyResult<ChatSettings> {
info!("[Chat] refresh chat:{} setting", chat_id);
let workspace_id = user_service.workspace_id()?;
@ -728,15 +739,18 @@ async fn refresh_chat_setting(
error!("failed to set chat settings: {}", err);
}
chat_notification_builder(chat_id, ChatNotification::DidUpdateChatSettings)
.payload(ChatSettingsPB {
rag_ids: settings.rag_ids.clone(),
})
.send();
chat_notification_builder(
&chat_id.to_string(),
ChatNotification::DidUpdateChatSettings,
)
.payload(ChatSettingsPB {
rag_ids: settings.rag_ids.clone(),
})
.send();
Ok(settings)
}
fn setting_store_key(chat_id: &str) -> String {
format!("chat_settings_{}", chat_id)
fn setting_store_key(chat_id: &Uuid) -> String {
format!("chat_settings_{}", chat_id.to_string())
}

View file

@ -23,6 +23,7 @@ use std::sync::atomic::{AtomicBool, AtomicI64};
use std::sync::Arc;
use tokio::sync::{Mutex, RwLock};
use tracing::{error, instrument, trace};
use uuid::Uuid;
enum PrevMessageState {
HasMore,
@ -31,7 +32,7 @@ enum PrevMessageState {
}
pub struct Chat {
chat_id: String,
chat_id: Uuid,
uid: i64,
user_service: Arc<dyn AIUserService>,
chat_service: Arc<AICloudServiceMiddleware>,
@ -44,7 +45,7 @@ pub struct Chat {
impl Chat {
pub fn new(
uid: i64,
chat_id: String,
chat_id: Uuid,
user_service: Arc<dyn AIUserService>,
chat_service: Arc<AICloudServiceMiddleware>,
) -> Chat {
@ -197,7 +198,7 @@ impl Chat {
answer_stream_port: i64,
answer_stream_buffer: Arc<Mutex<StringBuffer>>,
uid: i64,
workspace_id: String,
workspace_id: Uuid,
question_id: i64,
format: ResponseFormat,
ai_model: Option<AIModel>,
@ -254,7 +255,7 @@ impl Chat {
.send(StreamMessage::OnError(err.msg.clone()).to_string())
.await;
let pb = ChatMessageErrorPB {
chat_id: chat_id.clone(),
chat_id: chat_id.to_string(),
error_message: err.to_string(),
};
chat_notification_builder(&chat_id, ChatNotification::StreamChatMessageError)
@ -293,7 +294,7 @@ impl Chat {
}
let pb = ChatMessageErrorPB {
chat_id: chat_id.clone(),
chat_id: chat_id.to_string(),
error_message: err.to_string(),
};
chat_notification_builder(&chat_id, ChatNotification::StreamChatMessageError)
@ -566,7 +567,7 @@ impl Chat {
let conn = self.user_service.sqlite_connection(self.uid)?;
let records = select_chat_messages(
conn,
&self.chat_id,
&self.chat_id.to_string(),
limit,
after_message_id,
before_message_id,
@ -628,7 +629,7 @@ impl Chat {
fn save_chat_message_disk(
conn: DBConnection,
chat_id: &str,
chat_id: &Uuid,
messages: Vec<ChatMessage>,
) -> FlowyResult<()> {
let records = messages
@ -683,7 +684,7 @@ impl StringBuffer {
pub(crate) fn save_and_notify_message(
uid: i64,
chat_id: &str,
chat_id: &Uuid,
user_service: &Arc<dyn AIUserService>,
message: ChatMessage,
) -> Result<(), FlowyError> {

View file

@ -1,6 +1,7 @@
use crate::ai_manager::AIUserService;
use crate::entities::{CompleteTextPB, CompleteTextTaskPB, CompletionTypePB};
use allo_isolate::Isolate;
use std::str::FromStr;
use dashmap::DashMap;
use flowy_ai_pub::cloud::{
@ -15,7 +16,8 @@ use lib_infra::isolate_stream::IsolateSink;
use crate::stream_message::StreamMessage;
use std::sync::{Arc, Weak};
use tokio::select;
use tracing::info;
use tracing::{error, info};
use uuid::Uuid;
pub struct AICompletion {
tasks: Arc<DashMap<String, tokio::sync::mpsc::Sender<()>>>,
@ -77,7 +79,7 @@ impl AICompletion {
}
pub struct CompletionTask {
workspace_id: String,
workspace_id: Uuid,
task_id: String,
stop_rx: tokio::sync::mpsc::Receiver<()>,
context: CompleteTextPB,
@ -87,7 +89,7 @@ pub struct CompletionTask {
impl CompletionTask {
pub fn new(
workspace_id: String,
workspace_id: Uuid,
context: CompleteTextPB,
preferred_model: Option<AIModel>,
cloud_service: Weak<dyn ChatCloudService>,
@ -122,59 +124,63 @@ impl CompletionTask {
let _ = sink.send("start:".to_string()).await;
let completion_history = Some(self.context.history.iter().map(Into::into).collect());
let format = self.context.format.map(Into::into).unwrap_or_default();
let params = CompleteTextParams {
text: self.context.text,
completion_type: Some(complete_type),
metadata: Some(CompletionMetadata {
object_id: self.context.object_id,
workspace_id: Some(self.workspace_id.clone()),
rag_ids: Some(self.context.rag_ids),
completion_history,
custom_prompt: self
.context
.custom_prompt
.map(|v| CustomPrompt { system: v }),
}),
format,
};
if let Ok(object_id) = Uuid::from_str(&self.context.object_id) {
let params = CompleteTextParams {
text: self.context.text,
completion_type: Some(complete_type),
metadata: Some(CompletionMetadata {
object_id,
workspace_id: Some(self.workspace_id.clone()),
rag_ids: Some(self.context.rag_ids),
completion_history,
custom_prompt: self
.context
.custom_prompt
.map(|v| CustomPrompt { system: v }),
}),
format,
};
info!("start completion: {:?}", params);
match cloud_service
.stream_complete(&self.workspace_id, params, self.preferred_model)
.await
{
Ok(mut stream) => loop {
select! {
_ = self.stop_rx.recv() => {
return;
},
result = stream.next() => {
match result {
Some(Ok(data)) => {
match data {
CompletionStreamValue::Answer{ value } => {
let _ = sink.send(format!("data:{}", value)).await;
info!("start completion: {:?}", params);
match cloud_service
.stream_complete(&self.workspace_id, params, self.preferred_model)
.await
{
Ok(mut stream) => loop {
select! {
_ = self.stop_rx.recv() => {
return;
},
result = stream.next() => {
match result {
Some(Ok(data)) => {
match data {
CompletionStreamValue::Answer{ value } => {
let _ = sink.send(format!("data:{}", value)).await;
}
CompletionStreamValue::Comment{ value } => {
let _ = sink.send(format!("comment:{}", value)).await;
}
}
CompletionStreamValue::Comment{ value } => {
let _ = sink.send(format!("comment:{}", value)).await;
}
}
},
Some(Err(error)) => {
handle_error(&mut sink, error).await;
return;
},
None => {
let _ = sink.send(format!("finish:{}", self.task_id)).await;
return;
},
},
Some(Err(error)) => {
handle_error(&mut sink, error).await;
return;
},
None => {
let _ = sink.send(format!("finish:{}", self.task_id)).await;
return;
},
}
}
}
}
},
Err(error) => {
handle_error(&mut sink, error).await;
},
}
},
Err(error) => {
handle_error(&mut sink, error).await;
},
}
} else {
error!("Invalid uuid: {}", self.context.object_id);
}
}
});

View file

@ -1,8 +1,6 @@
use af_plugin::core::plugin::RunningState;
use std::collections::HashMap;
use crate::local_ai::controller::LocalAISetting;
use crate::local_ai::resource::PendingResource;
use af_plugin::core::plugin::RunningState;
use flowy_ai_pub::cloud::{
AIModel, ChatMessage, ChatMessageMetadata, ChatMessageType, CompletionMessage, LLMModel,
OutputContent, OutputLayout, RelatedQuestion, RepeatedChatMessage, RepeatedRelatedQuestion,
@ -10,6 +8,8 @@ use flowy_ai_pub::cloud::{
};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use lib_infra::validator_fn::required_not_empty_str;
use std::collections::HashMap;
use uuid::Uuid;
use validator::Validate;
#[derive(Default, ProtoBuf, Validate, Clone, Debug)]
@ -78,7 +78,7 @@ pub struct StreamChatPayloadPB {
#[derive(Default, Debug)]
pub struct StreamMessageParams {
pub chat_id: String,
pub chat_id: Uuid,
pub message: String,
pub message_type: ChatMessageType,
pub answer_stream_port: i64,

View file

@ -1,6 +1,3 @@
use std::fs;
use std::path::PathBuf;
use crate::ai_manager::{AIManager, GLOBAL_ACTIVE_MODEL_KEY};
use crate::completion::AICompletion;
use crate::entities::*;
@ -10,8 +7,12 @@ use flowy_ai_pub::cloud::{
};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
use std::fs;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Weak};
use tracing::trace;
use uuid::Uuid;
use validator::Validate;
fn upgrade_ai_manager(ai_manager: AFPluginState<Weak<AIManager>>) -> FlowyResult<Arc<AIManager>> {
@ -70,6 +71,7 @@ pub(crate) async fn stream_chat_message_handler(
trace!("Stream chat message with metadata: {:?}", metadata);
let chat_id = Uuid::from_str(&chat_id)?;
let params = StreamMessageParams {
chat_id,
message,
@ -91,11 +93,12 @@ pub(crate) async fn regenerate_response_handler(
ai_manager: AFPluginState<Weak<AIManager>>,
) -> FlowyResult<()> {
let data = data.try_into_inner()?;
let chat_id = Uuid::from_str(&data.chat_id)?;
let ai_manager = upgrade_ai_manager(ai_manager)?;
ai_manager
.stream_regenerate_response(
&data.chat_id,
&chat_id,
data.answer_message_id,
data.answer_stream_port,
data.format,
@ -147,8 +150,9 @@ pub(crate) async fn load_prev_message_handler(
let data = data.into_inner();
data.validate()?;
let chat_id = Uuid::from_str(&data.chat_id)?;
let messages = ai_manager
.load_prev_chat_messages(&data.chat_id, data.limit, data.before_message_id)
.load_prev_chat_messages(&chat_id, data.limit, data.before_message_id)
.await?;
data_result_ok(messages)
}
@ -162,8 +166,9 @@ pub(crate) async fn load_next_message_handler(
let data = data.into_inner();
data.validate()?;
let chat_id = Uuid::from_str(&data.chat_id)?;
let messages = ai_manager
.load_latest_chat_messages(&data.chat_id, data.limit, data.after_message_id)
.load_latest_chat_messages(&chat_id, data.limit, data.after_message_id)
.await?;
data_result_ok(messages)
}
@ -175,8 +180,9 @@ pub(crate) async fn get_related_question_handler(
) -> DataResult<RepeatedRelatedQuestionPB, FlowyError> {
let ai_manager = upgrade_ai_manager(ai_manager)?;
let data = data.into_inner();
let chat_id = Uuid::from_str(&data.chat_id)?;
let messages = ai_manager
.get_related_questions(&data.chat_id, data.message_id)
.get_related_questions(&chat_id, data.message_id)
.await?;
data_result_ok(messages)
}
@ -188,8 +194,9 @@ pub(crate) async fn get_answer_handler(
) -> DataResult<ChatMessagePB, FlowyError> {
let ai_manager = upgrade_ai_manager(ai_manager)?;
let data = data.into_inner();
let chat_id = Uuid::from_str(&data.chat_id)?;
let message = ai_manager
.generate_answer(&data.chat_id, data.message_id)
.generate_answer(&chat_id, data.message_id)
.await?;
data_result_ok(message)
}
@ -203,7 +210,8 @@ pub(crate) async fn stop_stream_handler(
data.validate()?;
let ai_manager = upgrade_ai_manager(ai_manager)?;
ai_manager.stop_stream(&data.chat_id).await?;
let chat_id = Uuid::from_str(&data.chat_id)?;
ai_manager.stop_stream(&chat_id).await?;
Ok(())
}
@ -273,7 +281,8 @@ pub(crate) async fn chat_file_handler(
tracing::debug!("File size: {} bytes", file_size);
let ai_manager = upgrade_ai_manager(ai_manager)?;
ai_manager.chat_with_file(&data.chat_id, file_path).await?;
let chat_id = Uuid::from_str(&data.chat_id)?;
ai_manager.chat_with_file(&chat_id, file_path).await?;
Ok(())
}
@ -332,6 +341,7 @@ pub(crate) async fn get_chat_settings_handler(
ai_manager: AFPluginState<Weak<AIManager>>,
) -> DataResult<ChatSettingsPB, FlowyError> {
let chat_id = data.try_into_inner()?.value;
let chat_id = Uuid::from_str(&chat_id)?;
let ai_manager = upgrade_ai_manager(ai_manager)?;
let rag_ids = ai_manager.get_rag_ids(&chat_id).await?;
let pb = ChatSettingsPB { rag_ids };
@ -345,9 +355,8 @@ pub(crate) async fn update_chat_settings_handler(
) -> FlowyResult<()> {
let params = data.try_into_inner()?;
let ai_manager = upgrade_ai_manager(ai_manager)?;
ai_manager
.update_rag_ids(&params.chat_id.value, params.rag_ids)
.await?;
let chat_id = Uuid::from_str(&params.chat_id.value)?;
ai_manager.update_rag_ids(&chat_id, params.rag_ids).await?;
Ok(())
}

View file

@ -28,6 +28,7 @@ use std::sync::Arc;
use tokio::select;
use tokio_stream::StreamExt;
use tracing::{debug, error, info, instrument};
use uuid::Uuid;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LocalAISetting {
@ -51,7 +52,7 @@ const LOCAL_AI_SETTING_KEY: &str = "appflowy_local_ai_setting:v1";
pub struct LocalAIController {
ai_plugin: Arc<OllamaAIPlugin>,
resource: Arc<LocalAIResourceController>,
current_chat_id: ArcSwapOption<String>,
current_chat_id: ArcSwapOption<Uuid>,
store_preferences: Arc<KVStorePreferences>,
user_service: Arc<dyn AIUserService>,
#[allow(dead_code)]
@ -240,7 +241,7 @@ impl LocalAIController {
Some(self.resource.get_llm_setting().chat_model_name)
}
pub fn open_chat(&self, chat_id: &str) {
pub fn open_chat(&self, chat_id: &Uuid) {
if !self.is_enabled() {
return;
}
@ -252,9 +253,7 @@ impl LocalAIController {
self.close_chat(current_chat_id);
}
self
.current_chat_id
.store(Some(Arc::new(chat_id.to_string())));
self.current_chat_id.store(Some(Arc::new(chat_id.clone())));
let chat_id = chat_id.to_string();
let weak_ctrl = Arc::downgrade(&self.ai_plugin);
tokio::spawn(async move {
@ -266,7 +265,7 @@ impl LocalAIController {
});
}
pub fn close_chat(&self, chat_id: &str) {
pub fn close_chat(&self, chat_id: &Uuid) {
if !self.is_running() {
return;
}
@ -383,7 +382,7 @@ impl LocalAIController {
#[instrument(level = "debug", skip_all)]
pub async fn index_message_metadata(
&self,
chat_id: &str,
chat_id: &Uuid,
metadata_list: &[ChatMessageMetadata],
index_process_sink: &mut (impl Sink<String> + Unpin),
) -> FlowyResult<()> {
@ -434,7 +433,7 @@ impl LocalAIController {
async fn process_index_file(
&self,
chat_id: &str,
chat_id: &Uuid,
file_path: PathBuf,
index_metadata: &HashMap<String, serde_json::Value>,
index_process_sink: &mut (impl Sink<String> + Unpin),
@ -456,7 +455,11 @@ impl LocalAIController {
let result = self
.ai_plugin
.embed_file(chat_id, file_path, Some(index_metadata.clone()))
.embed_file(
&chat_id.to_string(),
file_path,
Some(index_metadata.clone()),
)
.await;
match result {
Ok(_) => {
@ -616,6 +619,6 @@ impl LLMResourceService for LLMResourceServiceImpl {
}
const APPFLOWY_LOCAL_AI_ENABLED: &str = "appflowy_local_ai_enabled";
fn local_ai_enabled_key(workspace_id: &str) -> String {
format!("{}:{}", APPFLOWY_LOCAL_AI_ENABLED, workspace_id)
fn local_ai_enabled_key(workspace_id: &Uuid) -> String {
format!("{}:{}", APPFLOWY_LOCAL_AI_ENABLED, workspace_id.to_string())
}

View file

@ -26,6 +26,7 @@ use serde_json::{json, Value};
use std::path::Path;
use std::sync::{Arc, Weak};
use tracing::{info, trace};
use uuid::Uuid;
pub struct AICloudServiceMiddleware {
cloud_service: Arc<dyn ChatCloudService>,
@ -55,7 +56,7 @@ impl AICloudServiceMiddleware {
pub async fn index_message_metadata(
&self,
chat_id: &str,
chat_id: &Uuid,
metadata_list: &[ChatMessageMetadata],
index_process_sink: &mut (impl Sink<String> + Unpin),
) -> Result<(), FlowyError> {
@ -114,9 +115,9 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn create_chat(
&self,
uid: &i64,
workspace_id: &str,
chat_id: &str,
rag_ids: Vec<String>,
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
) -> Result<(), FlowyError> {
self
.cloud_service
@ -126,8 +127,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn create_question(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
message_type: ChatMessageType,
metadata: &[ChatMessageMetadata],
@ -140,8 +141,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn create_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
question_id: i64,
metadata: Option<serde_json::Value>,
@ -154,8 +155,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn stream_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
format: ResponseFormat,
ai_model: Option<AIModel>,
@ -171,7 +172,12 @@ impl ChatCloudService for AICloudServiceMiddleware {
let row = self.get_message_record(message_id)?;
match self
.local_ai
.stream_question(chat_id, &row.content, Some(json!(format)), json!({}))
.stream_question(
&chat_id.to_string(),
&row.content,
Some(json!(format)),
json!({}),
)
.await
{
Ok(stream) => Ok(QuestionStream::new(stream).boxed()),
@ -195,13 +201,17 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn get_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
question_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
if self.local_ai.is_running() {
let content = self.get_message_record(question_message_id)?.content;
match self.local_ai.ask_question(chat_id, &content).await {
match self
.local_ai
.ask_question(&chat_id.to_string(), &content)
.await
{
Ok(answer) => {
let message = self
.cloud_service
@ -224,8 +234,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn get_chat_messages(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
offset: MessageCursor,
limit: u64,
) -> Result<RepeatedChatMessage, FlowyError> {
@ -237,26 +247,26 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn get_question_from_answer_id(
&self,
workspace_id: &str,
chat_id: &str,
answer_id: i64,
workspace_id: &Uuid,
chat_id: &Uuid,
answer_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
self
.cloud_service
.get_question_from_answer_id(workspace_id, chat_id, answer_id)
.get_question_from_answer_id(workspace_id, chat_id, answer_message_id)
.await
}
async fn get_related_message(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
) -> Result<RepeatedRelatedQuestion, FlowyError> {
if self.local_ai.is_running() {
let questions = self
.local_ai
.get_related_question(chat_id)
.get_related_question(&chat_id.to_string())
.await
.map_err(|err| FlowyError::local_ai().with_context(err))?;
trace!("LocalAI related questions: {:?}", questions);
@ -280,7 +290,7 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn stream_complete(
&self,
workspace_id: &str,
workspace_id: &Uuid,
params: CompleteTextParams,
ai_model: Option<AIModel>,
) -> Result<StreamComplete, FlowyError> {
@ -329,15 +339,15 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn embed_file(
&self,
workspace_id: &str,
workspace_id: &Uuid,
file_path: &Path,
chat_id: &str,
chat_id: &Uuid,
metadata: Option<HashMap<String, Value>>,
) -> Result<(), FlowyError> {
if self.local_ai.is_running() {
self
.local_ai
.embed_file(chat_id, file_path.to_path_buf(), metadata)
.embed_file(&chat_id.to_string(), file_path.to_path_buf(), metadata)
.await
.map_err(|err| FlowyError::local_ai().with_context(err))?;
Ok(())
@ -349,21 +359,21 @@ impl ChatCloudService for AICloudServiceMiddleware {
}
}
async fn get_local_ai_config(&self, workspace_id: &str) -> Result<LocalAIConfig, FlowyError> {
async fn get_local_ai_config(&self, workspace_id: &Uuid) -> Result<LocalAIConfig, FlowyError> {
self.cloud_service.get_local_ai_config(workspace_id).await
}
async fn get_workspace_plan(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
self.cloud_service.get_workspace_plan(workspace_id).await
}
async fn get_chat_settings(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
) -> Result<ChatSettings, FlowyError> {
self
.cloud_service
@ -373,8 +383,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
async fn update_chat_settings(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
params: UpdateChatParams,
) -> Result<(), FlowyError> {
self
@ -383,11 +393,11 @@ impl ChatCloudService for AICloudServiceMiddleware {
.await
}
async fn get_available_models(&self, workspace_id: &str) -> Result<ModelList, FlowyError> {
async fn get_available_models(&self, workspace_id: &Uuid) -> Result<ModelList, FlowyError> {
self.cloud_service.get_available_models(workspace_id).await
}
async fn get_workspace_default_model(&self, workspace_id: &str) -> Result<String, FlowyError> {
async fn get_workspace_default_model(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
self
.cloud_service
.get_workspace_default_model(workspace_id)

View file

@ -1,5 +1,6 @@
use flowy_derive::ProtoBuf_Enum;
use flowy_notification::NotificationBuilder;
use tracing::trace;
const CHAT_OBSERVABLE_SOURCE: &str = "Chat";
pub const APPFLOWY_AI_NOTIFICATION_KEY: &str = "appflowy_ai_plugin";
@ -39,7 +40,12 @@ impl std::convert::From<i32> for ChatNotification {
}
}
#[tracing::instrument(level = "trace")]
pub(crate) fn chat_notification_builder(id: &str, ty: ChatNotification) -> NotificationBuilder {
NotificationBuilder::new(id, ty, CHAT_OBSERVABLE_SOURCE)
#[tracing::instrument(level = "trace", skip_all)]
pub(crate) fn chat_notification_builder<T: ToString>(
id: T,
ty: ChatNotification,
) -> NotificationBuilder {
let id = id.to_string();
trace!("chat_notification_builder: id = {id}, ty = {ty:?}");
NotificationBuilder::new(&id, ty, CHAT_OBSERVABLE_SOURCE)
}

View file

@ -32,7 +32,6 @@ collab = { workspace = true }
#collab = { workspace = true, features = ["verbose_log"] }
diesel.workspace = true
uuid.workspace = true
flowy-storage = { workspace = true }
flowy-storage-pub = { workspace = true }
client-api.workspace = true
@ -56,8 +55,7 @@ lib-infra = { workspace = true }
serde.workspace = true
serde_json.workspace = true
serde_repr.workspace = true
futures.workspace = true
walkdir = "2.4.0"
uuid.workspace = true
sysinfo = "0.30.5"
semver = { version = "1.0.22", features = ["serde"] }

View file

@ -21,6 +21,7 @@ use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Weak};
use tracing::{error, info};
use uuid::Uuid;
pub struct ChatDepsResolver;
@ -56,9 +57,9 @@ struct ChatQueryServiceImpl {
impl AIExternalService for ChatQueryServiceImpl {
async fn query_chat_rag_ids(
&self,
parent_view_id: &str,
chat_id: &str,
) -> Result<Vec<String>, FlowyError> {
parent_view_id: &Uuid,
chat_id: &Uuid,
) -> Result<Vec<Uuid>, FlowyError> {
let mut ids = self
.folder_service
.get_surrounding_view_ids_with_view_layout(parent_view_id, ViewLayout::Document)
@ -72,9 +73,9 @@ impl AIExternalService for ChatQueryServiceImpl {
}
async fn sync_rag_documents(
&self,
workspace_id: &str,
rag_ids: Vec<String>,
mut rag_metadata_map: HashMap<String, AFCollabMetadata>,
workspace_id: &Uuid,
rag_ids: Vec<Uuid>,
mut rag_metadata_map: HashMap<Uuid, AFCollabMetadata>,
) -> Result<Vec<AFCollabMetadata>, FlowyError> {
let mut result = Vec::new();
@ -96,7 +97,7 @@ impl AIExternalService for ChatQueryServiceImpl {
if let Ok(prev_sv) = StateVector::decode_v1(&metadata.prev_sync_state_vector) {
let collab = Collab::new_with_source(
CollabOrigin::Empty,
&rag_id,
&rag_id.to_string(),
DataSource::DocStateV1(query_collab.encoded_collab.doc_state.to_vec()),
vec![],
false,
@ -125,7 +126,7 @@ impl AIExternalService for ChatQueryServiceImpl {
} else {
info!("[Chat] full sync rag document: {}", rag_id);
result.push(AFCollabMetadata {
object_id: rag_id,
object_id: rag_id.to_string(),
updated_at: timestamp(),
prev_sync_state_vector: query_collab.encoded_collab.state_vector.to_vec(),
collab_type: CollabType::Document as i32,
@ -136,7 +137,7 @@ impl AIExternalService for ChatQueryServiceImpl {
Ok(result)
}
async fn notify_did_send_message(&self, chat_id: &str, message: &str) -> Result<(), FlowyError> {
async fn notify_did_send_message(&self, chat_id: &Uuid, message: &str) -> Result<(), FlowyError> {
info!(
"notify_did_send_message: chat_id: {}, message: {}",
chat_id, message
@ -169,7 +170,7 @@ impl AIUserService for ChatUserServiceImpl {
self.upgrade_user()?.device_id()
}
fn workspace_id(&self) -> Result<String, FlowyError> {
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
self.upgrade_user()?.workspace_id()
}

View file

@ -7,16 +7,6 @@ use collab::core::origin::{CollabClient, CollabOrigin};
use collab::entity::EncodedCollab;
use collab::preclude::CollabPlugin;
use collab_entity::CollabType;
use flowy_search_pub::cloud::SearchCloudService;
use serde_json::Value;
use std::collections::HashMap;
use std::path::Path;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
use tokio_stream::wrappers::WatchStream;
use tracing::{debug, info};
use collab_integrate::collab_builder::{
CollabCloudPluginProvider, CollabPluginProviderContext, CollabPluginProviderType,
};
@ -37,12 +27,24 @@ use flowy_folder_pub::cloud::{
Workspace, WorkspaceRecord,
};
use flowy_folder_pub::entities::PublishPayload;
use flowy_search_pub::cloud::SearchCloudService;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use flowy_storage_pub::cloud::{ObjectIdentity, ObjectValue, StorageCloudService};
use flowy_storage_pub::storage::{CompletedPartRequest, CreateUploadResponse, UploadPartResponse};
use flowy_user_pub::cloud::{UserCloudService, UserCloudServiceProvider};
use flowy_user_pub::entities::{Authenticator, UserTokenState};
use lib_infra::async_trait::async_trait;
use serde_json::Value;
use std::collections::HashMap;
use std::path::Path;
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
use tokio_stream::wrappers::WatchStream;
use tracing::log::error;
use tracing::{debug, info};
use uuid::Uuid;
use crate::server_layer::{Server, ServerProvider};
@ -82,7 +84,7 @@ impl StorageCloudService for ServerProvider {
async fn get_object_url_v1(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
file_id: &str,
) -> FlowyResult<String> {
@ -93,7 +95,7 @@ impl StorageCloudService for ServerProvider {
.await
}
async fn parse_object_url_v1(&self, url: &str) -> Option<(String, String, String)> {
async fn parse_object_url_v1(&self, url: &str) -> Option<(Uuid, String, String)> {
self
.get_server()
.ok()?
@ -104,7 +106,7 @@ impl StorageCloudService for ServerProvider {
async fn create_upload(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
file_id: &str,
content_type: &str,
@ -119,7 +121,7 @@ impl StorageCloudService for ServerProvider {
async fn upload_part(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
upload_id: &str,
file_id: &str,
@ -142,7 +144,7 @@ impl StorageCloudService for ServerProvider {
async fn complete_upload(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
upload_id: &str,
file_id: &str,
@ -233,10 +235,9 @@ impl FolderCloudService for ServerProvider {
server.folder_service().create_workspace(uid, &name).await
}
async fn open_workspace(&self, workspace_id: &str) -> Result<(), FlowyError> {
let workspace_id = workspace_id.to_string();
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
let server = self.get_server()?;
server.folder_service().open_workspace(&workspace_id).await
server.folder_service().open_workspace(workspace_id).await
}
async fn get_all_workspace(&self) -> Result<Vec<WorkspaceRecord>, FlowyError> {
@ -246,7 +247,7 @@ impl FolderCloudService for ServerProvider {
async fn get_folder_data(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: &i64,
) -> Result<Option<FolderData>, FlowyError> {
let server = self.get_server()?;
@ -272,10 +273,10 @@ impl FolderCloudService for ServerProvider {
async fn get_folder_doc_state(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: i64,
collab_type: CollabType,
object_id: &str,
object_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
let server = self.get_server()?;
@ -287,7 +288,7 @@ impl FolderCloudService for ServerProvider {
async fn batch_create_folder_collab_objects(
&self,
workspace_id: &str,
workspace_id: &Uuid,
objects: Vec<FolderCollabParams>,
) -> Result<(), FlowyError> {
let server = self.get_server()?;
@ -307,7 +308,7 @@ impl FolderCloudService for ServerProvider {
async fn publish_view(
&self,
workspace_id: &str,
workspace_id: &Uuid,
payload: Vec<PublishPayload>,
) -> Result<(), FlowyError> {
let server = self.get_server()?;
@ -320,8 +321,8 @@ impl FolderCloudService for ServerProvider {
async fn unpublish_views(
&self,
workspace_id: &str,
view_ids: Vec<String>,
workspace_id: &Uuid,
view_ids: Vec<Uuid>,
) -> Result<(), FlowyError> {
let server = self.get_server()?;
server
@ -330,15 +331,15 @@ impl FolderCloudService for ServerProvider {
.await
}
async fn get_publish_info(&self, view_id: &str) -> Result<PublishInfo, FlowyError> {
async fn get_publish_info(&self, view_id: &Uuid) -> Result<PublishInfo, FlowyError> {
let server = self.get_server()?;
server.folder_service().get_publish_info(view_id).await
}
async fn set_publish_name(
&self,
workspace_id: &str,
view_id: String,
workspace_id: &Uuid,
view_id: Uuid,
new_name: String,
) -> Result<(), FlowyError> {
let server = self.get_server()?;
@ -350,7 +351,7 @@ impl FolderCloudService for ServerProvider {
async fn set_publish_namespace(
&self,
workspace_id: &str,
workspace_id: &Uuid,
new_namespace: String,
) -> Result<(), FlowyError> {
let server = self.get_server()?;
@ -360,7 +361,7 @@ impl FolderCloudService for ServerProvider {
.await
}
async fn get_publish_namespace(&self, workspace_id: &str) -> Result<String, FlowyError> {
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
let server = self.get_server()?;
server
.folder_service()
@ -371,7 +372,7 @@ impl FolderCloudService for ServerProvider {
/// List all published views of the current workspace.
async fn list_published_views(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<PublishInfoView>, FlowyError> {
let server = self.get_server()?;
server
@ -382,7 +383,7 @@ impl FolderCloudService for ServerProvider {
async fn get_default_published_view_info(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<PublishInfo, FlowyError> {
let server = self.get_server()?;
server
@ -393,7 +394,7 @@ impl FolderCloudService for ServerProvider {
async fn set_default_published_view(
&self,
workspace_id: &str,
workspace_id: &Uuid,
view_id: uuid::Uuid,
) -> Result<(), FlowyError> {
let server = self.get_server()?;
@ -403,7 +404,7 @@ impl FolderCloudService for ServerProvider {
.await
}
async fn remove_default_published_view(&self, workspace_id: &str) -> Result<(), FlowyError> {
async fn remove_default_published_view(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
let server = self.get_server()?;
server
.folder_service()
@ -421,7 +422,7 @@ impl FolderCloudService for ServerProvider {
async fn full_sync_collab_object(
&self,
workspace_id: &str,
workspace_id: &Uuid,
params: FullSyncCollabParams,
) -> Result<(), FlowyError> {
self
@ -436,24 +437,22 @@ impl FolderCloudService for ServerProvider {
impl DatabaseCloudService for ServerProvider {
async fn get_database_encode_collab(
&self,
object_id: &str,
object_id: &Uuid,
collab_type: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Option<EncodedCollab>, FlowyError> {
let workspace_id = workspace_id.to_string();
let server = self.get_server()?;
let database_id = object_id.to_string();
server
.database_service()
.get_database_encode_collab(&database_id, collab_type, &workspace_id)
.get_database_encode_collab(object_id, collab_type, &workspace_id)
.await
}
async fn create_database_encode_collab(
&self,
object_id: &str,
object_id: &Uuid,
collab_type: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError> {
let server = self.get_server()?;
@ -465,30 +464,28 @@ impl DatabaseCloudService for ServerProvider {
async fn batch_get_database_encode_collab(
&self,
object_ids: Vec<String>,
object_ids: Vec<Uuid>,
object_ty: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<EncodeCollabByOid, FlowyError> {
let workspace_id = workspace_id.to_string();
let server = self.get_server()?;
server
.database_service()
.batch_get_database_encode_collab(object_ids, object_ty, &workspace_id)
.batch_get_database_encode_collab(object_ids, object_ty, workspace_id)
.await
}
async fn get_database_collab_object_snapshots(
&self,
object_id: &str,
object_id: &Uuid,
limit: usize,
) -> Result<Vec<DatabaseSnapshot>, FlowyError> {
let server = self.get_server()?;
let database_id = object_id.to_string();
server
.database_service()
.get_database_collab_object_snapshots(&database_id, limit)
.get_database_collab_object_snapshots(&object_id, limit)
.await
}
}
@ -497,29 +494,29 @@ impl DatabaseCloudService for ServerProvider {
impl DatabaseAIService for ServerProvider {
async fn summary_database_row(
&self,
workspace_id: &str,
object_id: &str,
summary_row: SummaryRowContent,
_workspace_id: &Uuid,
_object_id: &Uuid,
_summary_row: SummaryRowContent,
) -> Result<String, FlowyError> {
self
.get_server()?
.database_ai_service()
.ok_or_else(FlowyError::not_support)?
.summary_database_row(workspace_id, object_id, summary_row)
.summary_database_row(_workspace_id, _object_id, _summary_row)
.await
}
async fn translate_database_row(
&self,
workspace_id: &str,
translate_row: TranslateRowContent,
language: &str,
_workspace_id: &Uuid,
_translate_row: TranslateRowContent,
_language: &str,
) -> Result<TranslateRowResponse, FlowyError> {
self
.get_server()?
.database_ai_service()
.ok_or_else(FlowyError::not_support)?
.translate_database_row(workspace_id, translate_row, language)
.translate_database_row(_workspace_id, _translate_row, _language)
.await
}
}
@ -528,8 +525,8 @@ impl DatabaseAIService for ServerProvider {
impl DocumentCloudService for ServerProvider {
async fn get_document_doc_state(
&self,
document_id: &str,
workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
let server = self.get_server()?;
server
@ -540,7 +537,7 @@ impl DocumentCloudService for ServerProvider {
async fn get_document_snapshots(
&self,
document_id: &str,
document_id: &Uuid,
limit: usize,
workspace_id: &str,
) -> Result<Vec<DocumentSnapshot>, FlowyError> {
@ -554,8 +551,8 @@ impl DocumentCloudService for ServerProvider {
async fn get_document_data(
&self,
document_id: &str,
workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Option<DocumentData>, FlowyError> {
let server = self.get_server()?;
server
@ -566,8 +563,8 @@ impl DocumentCloudService for ServerProvider {
async fn create_document_collab(
&self,
workspace_id: &str,
document_id: &str,
workspace_id: &Uuid,
document_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError> {
let server = self.get_server()?;
@ -611,26 +608,37 @@ impl CollabCloudPluginProvider for ServerProvider {
collab_object.uid,
collab_object.device_id.clone(),
));
let sync_object = SyncObject::new(
&collab_object.object_id,
&collab_object.workspace_id,
collab_object.collab_type,
&collab_object.device_id,
);
let (sink, stream) = (channel.sink(), channel.stream());
let sink_config = SinkConfig::new().send_timeout(8);
let sync_plugin = SyncPlugin::new(
origin,
sync_object,
local_collab,
sink,
sink_config,
stream,
Some(channel),
ws_connect_state,
Some(Duration::from_secs(60)),
);
plugins.push(Box::new(sync_plugin));
if let (Ok(object_id), Ok(workspace_id)) = (
Uuid::from_str(&collab_object.object_id),
Uuid::from_str(&collab_object.workspace_id),
) {
let sync_object = SyncObject::new(
object_id,
workspace_id,
collab_object.collab_type,
&collab_object.device_id,
);
let (sink, stream) = (channel.sink(), channel.stream());
let sink_config = SinkConfig::new().send_timeout(8);
let sync_plugin = SyncPlugin::new(
origin,
sync_object,
local_collab,
sink,
sink_config,
stream,
Some(channel),
ws_connect_state,
Some(Duration::from_secs(60)),
);
plugins.push(Box::new(sync_plugin));
} else {
error!(
"Failed to parse collab object id: {}",
collab_object.object_id
);
}
},
Ok(None) => {
tracing::error!("🔴Failed to get collab ws channel: channel is none");
@ -655,9 +663,9 @@ impl ChatCloudService for ServerProvider {
async fn create_chat(
&self,
uid: &i64,
workspace_id: &str,
chat_id: &str,
rag_ids: Vec<String>,
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
) -> Result<(), FlowyError> {
let server = self.get_server();
server?
@ -668,14 +676,12 @@ impl ChatCloudService for ServerProvider {
async fn create_question(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
message_type: ChatMessageType,
metadata: &[ChatMessageMetadata],
) -> Result<ChatMessage, FlowyError> {
let workspace_id = workspace_id.to_string();
let chat_id = chat_id.to_string();
let message = message.to_string();
self
.get_server()?
@ -686,8 +692,8 @@ impl ChatCloudService for ServerProvider {
async fn create_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
question_id: i64,
metadata: Option<serde_json::Value>,
@ -701,14 +707,12 @@ impl ChatCloudService for ServerProvider {
async fn stream_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
format: ResponseFormat,
ai_model: Option<AIModel>,
) -> Result<StreamAnswer, FlowyError> {
let workspace_id = workspace_id.to_string();
let chat_id = chat_id.to_string();
let server = self.get_server()?;
server
.chat_service()
@ -718,8 +722,8 @@ impl ChatCloudService for ServerProvider {
async fn get_chat_messages(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
offset: MessageCursor,
limit: u64,
) -> Result<RepeatedChatMessage, FlowyError> {
@ -732,8 +736,8 @@ impl ChatCloudService for ServerProvider {
async fn get_question_from_answer_id(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
answer_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
self
@ -745,8 +749,8 @@ impl ChatCloudService for ServerProvider {
async fn get_related_message(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
) -> Result<RepeatedRelatedQuestion, FlowyError> {
self
@ -758,8 +762,8 @@ impl ChatCloudService for ServerProvider {
async fn get_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
question_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
let server = self.get_server();
@ -771,23 +775,22 @@ impl ChatCloudService for ServerProvider {
async fn stream_complete(
&self,
workspace_id: &str,
workspace_id: &Uuid,
params: CompleteTextParams,
ai_model: Option<AIModel>,
) -> Result<StreamComplete, FlowyError> {
let workspace_id = workspace_id.to_string();
let server = self.get_server()?;
server
.chat_service()
.stream_complete(&workspace_id, params, ai_model)
.stream_complete(workspace_id, params, ai_model)
.await
}
async fn embed_file(
&self,
workspace_id: &str,
workspace_id: &Uuid,
file_path: &Path,
chat_id: &str,
chat_id: &Uuid,
metadata: Option<HashMap<String, Value>>,
) -> Result<(), FlowyError> {
self
@ -797,7 +800,7 @@ impl ChatCloudService for ServerProvider {
.await
}
async fn get_local_ai_config(&self, workspace_id: &str) -> Result<LocalAIConfig, FlowyError> {
async fn get_local_ai_config(&self, workspace_id: &Uuid) -> Result<LocalAIConfig, FlowyError> {
self
.get_server()?
.chat_service()
@ -807,7 +810,7 @@ impl ChatCloudService for ServerProvider {
async fn get_workspace_plan(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
self
.get_server()?
@ -818,8 +821,8 @@ impl ChatCloudService for ServerProvider {
async fn get_chat_settings(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
) -> Result<ChatSettings, FlowyError> {
self
.get_server()?
@ -830,8 +833,8 @@ impl ChatCloudService for ServerProvider {
async fn update_chat_settings(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
params: UpdateChatParams,
) -> Result<(), FlowyError> {
self
@ -841,7 +844,7 @@ impl ChatCloudService for ServerProvider {
.await
}
async fn get_available_models(&self, workspace_id: &str) -> Result<ModelList, FlowyError> {
async fn get_available_models(&self, workspace_id: &Uuid) -> Result<ModelList, FlowyError> {
self
.get_server()?
.chat_service()
@ -849,7 +852,7 @@ impl ChatCloudService for ServerProvider {
.await
}
async fn get_workspace_default_model(&self, workspace_id: &str) -> Result<String, FlowyError> {
async fn get_workspace_default_model(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
self
.get_server()?
.chat_service()
@ -862,7 +865,7 @@ impl ChatCloudService for ServerProvider {
impl SearchCloudService for ServerProvider {
async fn document_search(
&self,
workspace_id: &str,
workspace_id: &Uuid,
query: String,
) -> Result<Vec<SearchDocumentResponseItem>, FlowyError> {
let server = self.get_server()?;

View file

@ -13,6 +13,7 @@ use collab_integrate::collab_builder::WorkspaceCollabIntegrate;
use lib_infra::util::timestamp;
use std::sync::{Arc, Weak};
use tracing::debug;
use uuid::Uuid;
pub struct SnapshotDBImpl(pub Weak<AuthenticateUser>);
@ -222,12 +223,12 @@ impl WorkspaceCollabIntegrateImpl {
}
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
fn workspace_id(&self) -> Result<String, anyhow::Error> {
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
let workspace_id = self.upgrade_user()?.workspace_id()?;
Ok(workspace_id)
}
fn device_id(&self) -> Result<String, anyhow::Error> {
fn device_id(&self) -> Result<String, FlowyError> {
Ok(self.upgrade_user()?.user_config.device_id.clone())
}
}

View file

@ -13,6 +13,7 @@ use lib_infra::async_trait::async_trait;
use lib_infra::priority_task::TaskDispatcher;
use std::sync::{Arc, Weak};
use tokio::sync::RwLock;
use uuid::Uuid;
pub struct DatabaseDepsResolver();
@ -47,41 +48,41 @@ struct DatabaseAIServiceMiddleware {
impl DatabaseAIService for DatabaseAIServiceMiddleware {
async fn summary_database_row(
&self,
workspace_id: &str,
object_id: &str,
summary_row: SummaryRowContent,
workspace_id: &Uuid,
object_id: &Uuid,
_summary_row: SummaryRowContent,
) -> Result<String, FlowyError> {
if self.ai_manager.local_ai.is_running() {
self
.ai_manager
.local_ai
.summary_database_row(summary_row)
.summary_database_row(_summary_row)
.await
.map_err(|err| FlowyError::local_ai().with_context(err))
} else {
self
.ai_service
.summary_database_row(workspace_id, object_id, summary_row)
.summary_database_row(workspace_id, object_id, _summary_row)
.await
}
}
async fn translate_database_row(
&self,
workspace_id: &str,
translate_row: TranslateRowContent,
language: &str,
_workspace_id: &Uuid,
_translate_row: TranslateRowContent,
_language: &str,
) -> Result<TranslateRowResponse, FlowyError> {
if self.ai_manager.local_ai.is_running() {
let data = LocalAITranslateRowData {
cells: translate_row
cells: _translate_row
.into_iter()
.map(|row| LocalAITranslateItem {
title: row.title,
content: row.content,
})
.collect(),
language: language.to_string(),
language: _language.to_string(),
include_header: false,
};
let resp = self
@ -95,7 +96,7 @@ impl DatabaseAIService for DatabaseAIServiceMiddleware {
} else {
self
.ai_service
.translate_database_row(workspace_id, translate_row, language)
.translate_database_row(_workspace_id, _translate_row, _language)
.await
}
}
@ -121,11 +122,11 @@ impl DatabaseUser for DatabaseUserImpl {
self.upgrade_user()?.get_collab_db(uid)
}
fn workspace_id(&self) -> Result<String, FlowyError> {
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
self.upgrade_user()?.workspace_id()
}
fn workspace_database_object_id(&self) -> Result<String, FlowyError> {
fn workspace_database_object_id(&self) -> Result<Uuid, FlowyError> {
self.upgrade_user()?.workspace_database_object_id()
}
}

View file

@ -1,5 +1,5 @@
use std::sync::{Arc, Weak};
use uuid::Uuid;
use crate::deps_resolve::CollabSnapshotSql;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use collab_integrate::CollabKVDB;
@ -97,7 +97,7 @@ impl DocumentUserService for DocumentUserImpl {
.device_id()
}
fn workspace_id(&self) -> Result<String, FlowyError> {
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
self
.0
.upgrade()

View file

@ -4,6 +4,7 @@ use flowy_storage::manager::{StorageManager, StorageUserService};
use flowy_storage_pub::cloud::StorageCloudService;
use flowy_user::services::authenticate_user::AuthenticateUser;
use std::sync::{Arc, Weak};
use uuid::Uuid;
pub struct FileStorageResolver;
@ -40,7 +41,7 @@ impl StorageUserService for FileStorageServiceImpl {
self.upgrade_user()?.user_id()
}
fn workspace_id(&self) -> Result<String, FlowyError> {
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
self.upgrade_user()?.workspace_id()
}

View file

@ -8,6 +8,7 @@ use flowy_folder::share::ImportType;
use flowy_folder::view_operation::{FolderOperationHandler, ImportedData};
use lib_infra::async_trait::async_trait;
use std::sync::Arc;
use uuid::Uuid;
pub struct ChatFolderOperation(pub Arc<AIManager>);
@ -17,19 +18,19 @@ impl FolderOperationHandler for ChatFolderOperation {
"ChatFolderOperationHandler"
}
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
async fn open_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
self.0.open_chat(view_id).await
}
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
async fn close_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
self.0.close_chat(view_id).await
}
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
async fn delete_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
self.0.delete_chat(view_id).await
}
async fn duplicate_view(&self, _view_id: &str) -> Result<Bytes, FlowyError> {
async fn duplicate_view(&self, view_id: &Uuid) -> Result<Bytes, FlowyError> {
Err(FlowyError::not_support())
}
@ -44,10 +45,10 @@ impl FolderOperationHandler for ChatFolderOperation {
async fn create_default_view(
&self,
user_id: i64,
parent_view_id: &str,
view_id: &str,
_name: &str,
_layout: ViewLayout,
parent_view_id: &Uuid,
view_id: &Uuid,
name: &str,
layout: ViewLayout,
) -> Result<(), FlowyError> {
self
.0
@ -58,11 +59,11 @@ impl FolderOperationHandler for ChatFolderOperation {
async fn import_from_bytes(
&self,
_uid: i64,
_view_id: &str,
_name: &str,
_import_type: ImportType,
_bytes: Vec<u8>,
uid: i64,
view_id: &Uuid,
name: &str,
import_type: ImportType,
bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {
Err(FlowyError::not_support())
}

View file

@ -19,23 +19,31 @@ use lib_infra::async_trait::async_trait;
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use uuid::Uuid;
pub struct DatabaseFolderOperation(pub Arc<DatabaseManager>);
#[async_trait]
impl FolderOperationHandler for DatabaseFolderOperation {
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
async fn open_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
self.0.open_database_view(view_id).await?;
Ok(())
}
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.close_database_view(view_id).await?;
async fn close_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
self
.0
.close_database_view(view_id.to_string().as_str())
.await?;
Ok(())
}
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
match self.0.delete_database_view(view_id).await {
async fn delete_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
match self
.0
.delete_database_view(view_id.to_string().as_str())
.await
{
Ok(_) => tracing::trace!("Delete database view: {}", view_id),
Err(e) => tracing::error!("🔴delete database failed: {}", e),
}
@ -44,19 +52,23 @@ impl FolderOperationHandler for DatabaseFolderOperation {
async fn gather_publish_encode_collab(
&self,
user: &Arc<dyn FolderUser>,
view_id: &str,
_user: &Arc<dyn FolderUser>,
view_id: &Uuid,
) -> Result<GatherEncodedCollab, FlowyError> {
let workspace_id = user.workspace_id()?;
let workspace_id = _user.workspace_id()?;
let view_id_str = view_id.to_string();
// get the collab_object_id for the database.
//
// the collab object_id for the database is not the view_id,
// we should use the view_id to get the database_id
let oid = self.0.get_database_id_with_view_id(view_id).await?;
let row_oids = self.0.get_database_row_ids_with_view_id(view_id).await?;
let oid = self.0.get_database_id_with_view_id(&view_id_str).await?;
let row_oids = self
.0
.get_database_row_ids_with_view_id(&view_id_str)
.await?;
let row_metas = self
.0
.get_database_row_metas_with_view_id(view_id, row_oids.clone())
.get_database_row_metas_with_view_id(&view_id, row_oids.clone())
.await?;
let row_document_ids = row_metas
.iter()
@ -68,12 +80,12 @@ impl FolderOperationHandler for DatabaseFolderOperation {
.collect::<Vec<_>>();
let database_metas = self.0.get_all_databases_meta().await;
let uid = user
let uid = _user
.user_id()
.map_err(|e| e.with_context("unable to get the uid: {}"))?;
// get the collab db
let collab_db = user
let collab_db = _user
.collab_db(uid)
.map_err(|e| e.with_context("unable to get the collab"))?;
let collab_db = collab_db.upgrade().ok_or_else(|| {
@ -84,7 +96,7 @@ impl FolderOperationHandler for DatabaseFolderOperation {
tokio::task::spawn_blocking(move || {
let collab_read_txn = collab_db.read_txn();
let database_collab = load_collab_by_object_id(uid, &collab_read_txn, &workspace_id, &oid)
let database_collab = load_collab_by_object_id(uid, &collab_read_txn, &workspace_id.to_string(), &oid)
.map_err(|e| {
FlowyError::internal().with_context(format!("load database collab failed: {}", e))
})?;
@ -97,7 +109,7 @@ impl FolderOperationHandler for DatabaseFolderOperation {
})?;
let database_row_encoded_collabs =
load_collab_by_object_ids(uid, &workspace_id, &collab_read_txn, &row_oids)
load_collab_by_object_ids(uid, &workspace_id.to_string(), &collab_read_txn, &row_oids)
.0
.into_iter()
.map(|(oid, collab)| {
@ -123,7 +135,7 @@ impl FolderOperationHandler for DatabaseFolderOperation {
.collect::<HashMap<_, _>>();
let database_row_document_encoded_collabs =
load_collab_by_object_ids(uid, &workspace_id, &collab_read_txn, &row_document_ids)
load_collab_by_object_ids(uid, &workspace_id.to_string(), &collab_read_txn, &row_document_ids)
.0
.into_iter()
.map(|(oid, collab)| {
@ -147,7 +159,7 @@ impl FolderOperationHandler for DatabaseFolderOperation {
.await?
}
async fn duplicate_view(&self, view_id: &str) -> Result<Bytes, FlowyError> {
async fn duplicate_view(&self, view_id: &Uuid) -> Result<Bytes, FlowyError> {
Ok(Bytes::from(view_id.to_string()))
}
@ -166,14 +178,14 @@ impl FolderOperationHandler for DatabaseFolderOperation {
String::from_utf8(data.to_vec()).map_err(|_| FlowyError::invalid_data())?;
let encoded_collab = self
.0
.duplicate_database(&duplicated_view_id, &params.view_id)
.duplicate_database(&duplicated_view_id, &params.view_id.to_string())
.await?;
Ok(Some(encoded_collab))
},
ViewData::Data(data) => {
let encoded_collab = self
.0
.create_database_with_data(&params.view_id, data.to_vec())
.create_database_with_data(&params.view_id.to_string(), data.to_vec())
.await?;
Ok(Some(encoded_collab))
},
@ -212,17 +224,18 @@ impl FolderOperationHandler for DatabaseFolderOperation {
/// these references views.
async fn create_default_view(
&self,
_user_id: i64,
_parent_view_id: &str,
view_id: &str,
user_id: i64,
parent_view_id: &Uuid,
view_id: &Uuid,
name: &str,
layout: ViewLayout,
) -> Result<(), FlowyError> {
let name = name.to_string();
let view_id = view_id.to_string();
let data = match layout {
ViewLayout::Grid => make_default_grid(view_id, &name),
ViewLayout::Board => make_default_board(view_id, &name),
ViewLayout::Calendar => make_default_calendar(view_id, &name),
ViewLayout::Grid => make_default_grid(&view_id, &name),
ViewLayout::Board => make_default_board(&view_id, &name),
ViewLayout::Calendar => make_default_calendar(&view_id, &name),
ViewLayout::Document | ViewLayout::Chat => {
return Err(
FlowyError::internal().with_context(format!("Can't handle {:?} layout type", layout)),
@ -244,9 +257,9 @@ impl FolderOperationHandler for DatabaseFolderOperation {
async fn import_from_bytes(
&self,
_uid: i64,
view_id: &str,
_name: &str,
uid: i64,
view_id: &Uuid,
name: &str,
import_type: ImportType,
bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {

View file

@ -17,8 +17,10 @@ use flowy_folder::view_operation::{
use lib_dispatch::prelude::ToBytes;
use lib_infra::async_trait::async_trait;
use std::convert::TryFrom;
use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::RwLock;
use uuid::Uuid;
pub struct DocumentFolderOperation(pub Arc<DocumentManager>);
#[async_trait]
@ -33,6 +35,7 @@ impl FolderOperationHandler for DocumentFolderOperation {
workspace_view_builder: Arc<RwLock<NestedViewBuilder>>,
) -> Result<(), FlowyError> {
let manager = self.0.clone();
let mut write_guard = workspace_view_builder.write().await;
// Create a view named "Getting started" with an icon ⭐️ and the built-in README data.
// Don't modify this code unless you know what you are doing.
@ -45,8 +48,9 @@ impl FolderOperationHandler for DocumentFolderOperation {
// create a empty document
let json_str = include_str!("../../../assets/read_me.json");
let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap();
let view_id = Uuid::from_str(&view.view.id).unwrap();
manager
.create_document(uid, &view.view.id, Some(document_pb.into()))
.create_document(uid, &view_id, Some(document_pb.into()))
.await
.unwrap();
view
@ -55,18 +59,18 @@ impl FolderOperationHandler for DocumentFolderOperation {
Ok(())
}
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
async fn open_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
self.0.open_document(view_id).await?;
Ok(())
}
/// Close the document view.
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
async fn close_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
self.0.close_document(view_id).await?;
Ok(())
}
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
async fn delete_view(&self, view_id: &Uuid) -> Result<(), FlowyError> {
match self.0.delete_document(view_id).await {
Ok(_) => tracing::trace!("Delete document: {}", view_id),
Err(e) => tracing::error!("🔴delete document failed: {}", e),
@ -74,7 +78,7 @@ impl FolderOperationHandler for DocumentFolderOperation {
Ok(())
}
async fn duplicate_view(&self, view_id: &str) -> Result<Bytes, FlowyError> {
async fn duplicate_view(&self, view_id: &Uuid) -> Result<Bytes, FlowyError> {
let data: DocumentDataPB = self.0.get_document_data(view_id).await?.into();
let data_bytes = data.into_bytes().map_err(|_| FlowyError::invalid_data())?;
Ok(data_bytes)
@ -83,10 +87,11 @@ impl FolderOperationHandler for DocumentFolderOperation {
async fn gather_publish_encode_collab(
&self,
user: &Arc<dyn FolderUser>,
view_id: &str,
view_id: &Uuid,
) -> Result<GatherEncodedCollab, FlowyError> {
let encoded_collab =
get_encoded_collab_v1_from_disk(user, view_id, CollabType::Document).await?;
get_encoded_collab_v1_from_disk(user, view_id.to_string().as_str(), CollabType::Document)
.await?;
Ok(GatherEncodedCollab::Document(encoded_collab))
}
@ -112,9 +117,9 @@ impl FolderOperationHandler for DocumentFolderOperation {
async fn create_default_view(
&self,
user_id: i64,
_parent_view_id: &str,
view_id: &str,
_name: &str,
parent_view_id: &Uuid,
view_id: &Uuid,
name: &str,
layout: ViewLayout,
) -> Result<(), FlowyError> {
debug_assert_eq!(layout, ViewLayout::Document);
@ -133,9 +138,9 @@ impl FolderOperationHandler for DocumentFolderOperation {
async fn import_from_bytes(
&self,
uid: i64,
view_id: &str,
_name: &str,
_import_type: ImportType,
view_id: &Uuid,
name: &str,
import_type: ImportType,
bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {
let data = DocumentDataPB::try_from(Bytes::from(bytes))?;

View file

@ -17,6 +17,7 @@ use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_sqlite::kv::KVStorePreferences;
use flowy_user::services::authenticate_user::AuthenticateUser;
use flowy_user::services::data_import::load_collab_by_object_id;
use std::str::FromStr;
use std::sync::{Arc, Weak};
use crate::deps_resolve::folder_deps::folder_deps_chat_impl::ChatFolderOperation;
@ -25,6 +26,7 @@ use crate::deps_resolve::folder_deps::folder_deps_doc_impl::DocumentFolderOperat
use collab_plugins::local_storage::kv::KVTransactionDB;
use flowy_folder_pub::query::{FolderQueryService, FolderService, FolderViewEdit, QueryCollab};
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
pub struct FolderDepsResolver();
#[allow(clippy::too_many_arguments)]
@ -89,7 +91,7 @@ impl FolderUser for FolderUserImpl {
self.upgrade_user()?.user_id()
}
fn workspace_id(&self) -> Result<String, FlowyError> {
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
self.upgrade_user()?.workspace_id()
}
@ -97,8 +99,10 @@ impl FolderUser for FolderUserImpl {
self.upgrade_user()?.get_collab_db(uid)
}
fn is_folder_exist_on_disk(&self, uid: i64, workspace_id: &str) -> FlowyResult<bool> {
self.upgrade_user()?.is_collab_on_disk(uid, workspace_id)
fn is_folder_exist_on_disk(&self, uid: i64, workspace_id: &Uuid) -> FlowyResult<bool> {
self
.upgrade_user()?
.is_collab_on_disk(uid, workspace_id.to_string().as_str())
}
}
@ -124,13 +128,13 @@ impl FolderServiceImpl {
#[async_trait]
impl FolderViewEdit for FolderServiceImpl {
async fn set_view_title_if_empty(&self, view_id: &str, title: &str) -> FlowyResult<()> {
async fn set_view_title_if_empty(&self, view_id: &Uuid, title: &str) -> FlowyResult<()> {
if title.is_empty() {
return Ok(());
}
if let Some(folder_manager) = self.folder_manager.upgrade() {
if let Ok(view) = folder_manager.get_view(view_id).await {
if let Ok(view) = folder_manager.get_view(view_id.to_string().as_str()).await {
if view.name.is_empty() {
let title = if title.len() > 50 {
title.chars().take(50).collect()
@ -160,22 +164,25 @@ impl FolderViewEdit for FolderServiceImpl {
impl FolderQueryService for FolderServiceImpl {
async fn get_surrounding_view_ids_with_view_layout(
&self,
parent_view_id: &str,
parent_view_id: &Uuid,
view_layout: ViewLayout,
) -> Vec<String> {
) -> Vec<Uuid> {
let folder_manager = match self.folder_manager.upgrade() {
Some(folder_manager) => folder_manager,
None => return vec![],
};
if let Ok(view) = folder_manager.get_view(parent_view_id).await {
if let Ok(view) = folder_manager
.get_view(parent_view_id.to_string().as_str())
.await
{
if view.space_info().is_some() {
return vec![];
}
}
match folder_manager
.get_untrashed_views_belong_to(parent_view_id)
.get_untrashed_views_belong_to(parent_view_id.to_string().as_str())
.await
{
Ok(views) => {
@ -183,23 +190,24 @@ impl FolderQueryService for FolderServiceImpl {
.into_iter()
.filter_map(|child| {
if child.layout == view_layout {
Some(child.id.clone())
Uuid::from_str(&child.id).ok()
} else {
None
}
})
.collect::<Vec<_>>();
children.push(parent_view_id.to_string());
children.push(parent_view_id.clone());
children
},
_ => vec![],
}
}
async fn get_collab(&self, object_id: &str, collab_type: CollabType) -> Option<QueryCollab> {
let encode_collab = get_encoded_collab_v1_from_disk(&self.user, object_id, collab_type)
.await
.ok();
async fn get_collab(&self, object_id: &Uuid, collab_type: CollabType) -> Option<QueryCollab> {
let encode_collab =
get_encoded_collab_v1_from_disk(&self.user, object_id.to_string().as_str(), collab_type)
.await
.ok();
encode_collab.map(|encoded_collab| QueryCollab {
collab_type,
@ -229,8 +237,8 @@ async fn get_encoded_collab_v1_from_disk(
)
})?;
let collab_read_txn = collab_db.read_txn();
let collab =
load_collab_by_object_id(uid, &collab_read_txn, &workspace_id, view_id).map_err(|e| {
let collab = load_collab_by_object_id(uid, &collab_read_txn, &workspace_id.to_string(), view_id)
.map_err(|e| {
FlowyError::internal().with_context(format!("load document collab failed: {}", e))
})?;

View file

@ -13,6 +13,7 @@ use lib_infra::async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
use tracing::info;
use uuid::Uuid;
pub struct UserDepsResolver();
@ -81,7 +82,7 @@ impl UserWorkspaceService for UserWorkspaceServiceImpl {
Ok(())
}
fn did_delete_workspace(&self, workspace_id: String) -> FlowyResult<()> {
fn did_delete_workspace(&self, workspace_id: &Uuid) -> FlowyResult<()> {
// The remove_indices_for_workspace should not block the deletion of the workspace
// Log the error and continue
if let Err(err) = self

View file

@ -1,20 +1,20 @@
#![allow(unused_doc_comments)]
use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_search::services::manager::SearchManager;
use std::sync::{Arc, Weak};
use std::time::Duration;
use sysinfo::System;
use tokio::sync::RwLock;
use tracing::{debug, error, event, info, instrument};
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabPluginProviderType};
use flowy_ai::ai_manager::AIManager;
use flowy_database2::DatabaseManager;
use flowy_document::manager::DocumentManager;
use flowy_error::{FlowyError, FlowyResult};
use flowy_folder::manager::FolderManager;
use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_search::services::manager::SearchManager;
use flowy_server::af_cloud::define::ServerUser;
use std::sync::{Arc, Weak};
use std::time::Duration;
use sysinfo::System;
use tokio::sync::RwLock;
use tracing::{debug, error, event, info, instrument};
use uuid::Uuid;
use flowy_sqlite::kv::KVStorePreferences;
use flowy_storage::manager::StorageManager;
@ -329,7 +329,7 @@ impl ServerUserImpl {
}
}
impl ServerUser for ServerUserImpl {
fn workspace_id(&self) -> FlowyResult<String> {
fn workspace_id(&self) -> FlowyResult<Uuid> {
self.upgrade_user()?.workspace_id()
}
}

View file

@ -55,6 +55,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
_device_id: &str,
authenticator: &Authenticator,
) -> FlowyResult<()> {
let workspace_id = user_workspace.workspace_id()?;
self
.server_provider
.set_user_authenticator(user_authenticator);
@ -74,7 +75,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
.folder_manager
.initialize(
user_id,
&user_workspace.id,
&workspace_id,
FolderInitDataSource::LocalDisk {
create_if_not_exist: false,
},
@ -140,6 +141,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
user_workspace,
device_id
);
let workspace_id = user_workspace.workspace_id()?;
// In the current implementation, when a user signs up for AppFlowy Cloud, a default workspace
// is automatically created for them. However, for users who sign up through Supabase, the creation
@ -149,10 +151,10 @@ impl UserStatusCallback for UserStatusCallbackImpl {
.folder_manager
.cloud_service
.get_folder_doc_state(
&user_workspace.id,
&workspace_id,
user_profile.uid,
CollabType::Folder,
&user_workspace.id,
&workspace_id,
)
.await
{
@ -179,7 +181,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
&user_profile.token,
is_new_user,
data_source,
&user_workspace.id,
&workspace_id,
)
.await
.context("FolderManager error")?;

View file

@ -9,6 +9,6 @@ edition = "2021"
lib-infra = { workspace = true }
collab-entity = { workspace = true }
collab = { workspace = true }
anyhow.workspace = true
client-api = { workspace = true }
flowy-error = { workspace = true }
flowy-error = { workspace = true }
uuid.workspace = true

View file

@ -4,8 +4,9 @@ use collab_entity::CollabType;
use flowy_error::FlowyError;
use lib_infra::async_trait::async_trait;
use std::collections::HashMap;
use uuid::Uuid;
pub type EncodeCollabByOid = HashMap<String, EncodedCollab>;
pub type EncodeCollabByOid = HashMap<Uuid, EncodedCollab>;
pub type SummaryRowContent = HashMap<String, String>;
pub type TranslateRowContent = Vec<TranslateItem>;
@ -13,8 +14,8 @@ pub type TranslateRowContent = Vec<TranslateItem>;
pub trait DatabaseAIService: Send + Sync {
async fn summary_database_row(
&self,
_workspace_id: &str,
_object_id: &str,
_workspace_id: &Uuid,
_object_id: &Uuid,
_summary_row: SummaryRowContent,
) -> Result<String, FlowyError> {
Ok("".to_string())
@ -22,7 +23,7 @@ pub trait DatabaseAIService: Send + Sync {
async fn translate_database_row(
&self,
_workspace_id: &str,
_workspace_id: &Uuid,
_translate_row: TranslateRowContent,
_language: &str,
) -> Result<TranslateRowResponse, FlowyError> {
@ -41,29 +42,29 @@ pub trait DatabaseAIService: Send + Sync {
pub trait DatabaseCloudService: Send + Sync {
async fn get_database_encode_collab(
&self,
object_id: &str,
object_id: &Uuid,
collab_type: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Option<EncodedCollab>, FlowyError>;
async fn create_database_encode_collab(
&self,
object_id: &str,
object_id: &Uuid,
collab_type: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError>;
async fn batch_get_database_encode_collab(
&self,
object_ids: Vec<String>,
object_ids: Vec<Uuid>,
object_ty: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<EncodeCollabByOid, FlowyError>;
async fn get_database_collab_object_snapshots(
&self,
object_id: &str,
object_id: &Uuid,
limit: usize,
) -> Result<Vec<DatabaseSnapshot>, FlowyError>;
}

View file

@ -51,6 +51,7 @@ strum_macros = "0.25"
validator = { workspace = true, features = ["derive"] }
tokio-util.workspace = true
moka = { version = "0.12.8", features = ["future"] }
uuid.workspace = true
[dev-dependencies]
event-integration-test = { path = "../event-integration-test", default-features = false }

View file

@ -1261,7 +1261,7 @@ pub(crate) async fn summarize_row_handler(
let (tx, rx) = oneshot::channel();
tokio::spawn(async move {
let result = manager
.summarize_row(data.view_id, row_id, data.field_id)
.summarize_row(&data.view_id, row_id, data.field_id)
.await;
let _ = tx.send(result);
});
@ -1280,7 +1280,7 @@ pub(crate) async fn translate_row_handler(
let (tx, rx) = oneshot::channel();
tokio::spawn(async move {
let result = manager
.translate_row(data.view_id, row_id, data.field_id)
.translate_row(&data.view_id, row_id, data.field_id)
.await;
let _ = tx.send(result);
});

View file

@ -20,6 +20,7 @@ use collab_entity::{CollabObject, CollabType, EncodedCollab};
use collab_plugins::local_storage::kv::KVTransactionDB;
use rayon::prelude::*;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Arc, Weak};
use std::time::Duration;
use tokio::sync::Mutex;
@ -42,12 +43,13 @@ use crate::services::database_view::DatabaseLayoutDepsResolver;
use crate::services::field_settings::default_field_settings_by_layout_map;
use crate::services::share::csv::{CSVFormat, CSVImporter, ImportResult};
use tokio::sync::RwLock as TokioRwLock;
use uuid::Uuid;
pub trait DatabaseUser: Send + Sync {
fn user_id(&self) -> Result<i64, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn workspace_database_object_id(&self) -> Result<String, FlowyError>;
fn workspace_id(&self) -> Result<Uuid, FlowyError>;
fn workspace_database_object_id(&self) -> Result<Uuid, FlowyError>;
}
pub(crate) type DatabaseEditorMap = HashMap<String, Arc<DatabaseEditor>>;
@ -110,7 +112,7 @@ impl DatabaseManager {
let workspace_database_object_id = self.user.workspace_database_object_id()?;
let workspace_database_collab = collab_service
.build_collab(
workspace_database_object_id.as_str(),
workspace_database_object_id.to_string().as_str(),
CollabType::WorkspaceDatabase,
None,
)
@ -189,8 +191,10 @@ impl DatabaseManager {
})
}
pub async fn encode_database(&self, view_id: &str) -> FlowyResult<EncodedDatabase> {
let editor = self.get_database_editor_with_view_id(view_id).await?;
pub async fn encode_database(&self, view_id: &Uuid) -> FlowyResult<EncodedDatabase> {
let editor = self
.get_database_editor_with_view_id(view_id.to_string().as_str())
.await?;
let collabs = editor
.database
.read()
@ -207,10 +211,12 @@ impl DatabaseManager {
pub async fn get_database_row_metas_with_view_id(
&self,
view_id: &str,
view_id: &Uuid,
row_ids: Vec<RowId>,
) -> FlowyResult<Vec<RowMetaPB>> {
let database = self.get_database_editor_with_view_id(view_id).await?;
let database = self
.get_database_editor_with_view_id(view_id.to_string().as_str())
.await?;
let view_id = view_id.to_string();
let mut row_metas: Vec<RowMetaPB> = vec![];
for row_id in row_ids {
@ -275,11 +281,11 @@ impl DatabaseManager {
/// Open the database view
#[instrument(level = "trace", skip_all, err)]
pub async fn open_database_view<T: AsRef<str>>(&self, view_id: T) -> FlowyResult<()> {
let view_id = view_id.as_ref();
pub async fn open_database_view(&self, view_id: &Uuid) -> FlowyResult<()> {
let view_id = view_id.to_string();
let lock = self.workspace_database()?;
let workspace_database = lock.read().await;
let result = workspace_database.get_database_id_with_view_id(view_id);
let result = workspace_database.get_database_id_with_view_id(&view_id);
drop(workspace_database);
if let Some(database_id) = result {
@ -292,8 +298,7 @@ impl DatabaseManager {
}
#[instrument(level = "trace", skip_all, err)]
pub async fn close_database_view<T: AsRef<str>>(&self, view_id: T) -> FlowyResult<()> {
let view_id = view_id.as_ref();
pub async fn close_database_view(&self, view_id: &str) -> FlowyResult<()> {
let lock = self.workspace_database()?;
let workspace_database = lock.read().await;
let database_id = workspace_database.get_database_id_with_view_id(view_id);
@ -518,7 +523,9 @@ impl DatabaseManager {
layout: DatabaseLayoutPB,
) -> FlowyResult<()> {
let database = self.get_database_editor_with_view_id(view_id).await?;
database.update_view_layout(view_id, layout.into()).await
database
.update_view_layout(view_id.to_string().as_str(), layout.into())
.await
}
pub async fn get_database_snapshots(
@ -526,7 +533,7 @@ impl DatabaseManager {
view_id: &str,
limit: usize,
) -> FlowyResult<Vec<DatabaseSnapshotPB>> {
let database_id = self.get_database_id_with_view_id(view_id).await?;
let database_id = Uuid::from_str(&self.get_database_id_with_view_id(view_id).await?)?;
let snapshots = self
.cloud_service
.get_database_collab_object_snapshots(&database_id, limit)
@ -553,11 +560,11 @@ impl DatabaseManager {
#[instrument(level = "debug", skip_all)]
pub async fn summarize_row(
&self,
view_id: String,
view_id: &str,
row_id: RowId,
field_id: String,
) -> FlowyResult<()> {
let database = self.get_database_editor_with_view_id(&view_id).await?;
let database = self.get_database_editor_with_view_id(view_id).await?;
let mut summary_row_content = SummaryRowContent::new();
if let Some(row) = database.get_row(&view_id, &row_id).await {
let fields = database.get_fields(&view_id, None).await;
@ -583,7 +590,11 @@ impl DatabaseManager {
);
let response = self
.ai_service
.summary_database_row(&self.user.workspace_id()?, &row_id, summary_row_content)
.summary_database_row(
&self.user.workspace_id()?,
&Uuid::from_str(&row_id)?,
summary_row_content,
)
.await?;
trace!("[AI]:summarize row response: {}", response);
@ -597,11 +608,12 @@ impl DatabaseManager {
#[instrument(level = "debug", skip_all)]
pub async fn translate_row(
&self,
view_id: String,
view_id: &str,
row_id: RowId,
field_id: String,
) -> FlowyResult<()> {
let database = self.get_database_editor_with_view_id(&view_id).await?;
let database = self.get_database_editor_with_view_id(view_id).await?;
let view_id = view_id.to_string();
let mut translate_row_content = TranslateRowContent::new();
let mut language = "english".to_string();
@ -703,10 +715,13 @@ impl WorkspaceDatabaseCollabServiceImpl {
async fn get_encode_collab(
&self,
object_id: &str,
object_id: &Uuid,
object_ty: CollabType,
) -> Result<Option<EncodedCollab>, DatabaseError> {
let workspace_id = self.user.workspace_id().unwrap();
let workspace_id = self
.user
.workspace_id()
.map_err(|e| DatabaseError::Internal(e.into()))?;
trace!("[Database]: fetch {}:{} from remote", object_id, object_ty);
let encode_collab = self
.cloud_service
@ -718,7 +733,7 @@ impl WorkspaceDatabaseCollabServiceImpl {
async fn batch_get_encode_collab(
&self,
object_ids: Vec<String>,
object_ids: Vec<Uuid>,
object_ty: CollabType,
) -> Result<EncodeCollabByOid, DatabaseError> {
let workspace_id = self
@ -730,7 +745,13 @@ impl WorkspaceDatabaseCollabServiceImpl {
.batch_get_database_encode_collab(object_ids, object_ty, &workspace_id)
.await
.map_err(|err| DatabaseError::Internal(err.into()))?;
Ok(updates)
Ok(
updates
.into_iter()
.map(|(k, v)| (k.to_string(), v))
.collect(),
)
}
fn collab_db(&self) -> Result<Weak<CollabKVDB>, DatabaseError> {
@ -746,7 +767,7 @@ impl WorkspaceDatabaseCollabServiceImpl {
fn build_collab_object(
&self,
object_id: &str,
object_id: &Uuid,
object_type: CollabType,
) -> Result<CollabObject, DatabaseError> {
let uid = self
@ -776,8 +797,12 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl {
collab_type: CollabType,
encoded_collab: Option<(EncodedCollab, bool)>,
) -> Result<Collab, DatabaseError> {
let object = self.build_collab_object(object_id, collab_type)?;
let data_source = if self.persistence.is_collab_exist(object_id) {
let object_id = Uuid::parse_str(object_id)?;
let object = self.build_collab_object(&object_id, collab_type)?;
let data_source = if self
.persistence
.is_collab_exist(object_id.to_string().as_str())
{
trace!(
"build collab: {}:{} from local encode collab",
collab_type,
@ -796,7 +821,7 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl {
object_id,
encoded_collab.is_none(),
);
match self.get_encode_collab(object_id, collab_type).await {
match self.get_encode_collab(&object_id, collab_type).await {
Ok(Some(encode_collab)) => {
info!(
"build collab: {}:{} with remote encode collab, {} bytes",
@ -837,12 +862,11 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl {
);
self
.persistence
.save_collab(object_id, encoded_collab.clone())?;
.save_collab(object_id.to_string().as_str(), encoded_collab.clone())?;
// TODO(nathan): cover database rows and other database collab type
if matches!(collab_type, CollabType::Database) {
if let Ok(workspace_id) = self.user.workspace_id() {
let object_id = object_id.to_string();
let cloned_encoded_collab = encoded_collab.clone();
let cloud_service = self.cloud_service.clone();
tokio::spawn(async move {
@ -878,6 +902,7 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl {
if object_ids.is_empty() {
return Ok(EncodeCollabByOid::new());
}
let mut encoded_collab_by_id = EncodeCollabByOid::new();
// 1. Collect local disk collabs into a HashMap
let local_disk_encoded_collab: HashMap<String, EncodedCollab> = object_ids
@ -900,6 +925,10 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl {
}
if !object_ids.is_empty() {
let object_ids = object_ids
.into_iter()
.flat_map(|v| Uuid::from_str(&v).ok())
.collect::<Vec<_>>();
// 2. Fetch remaining collabs from remote
let remote_collabs = self
.batch_get_encode_collab(object_ids, collab_type)
@ -927,7 +956,7 @@ pub struct DatabasePersistenceImpl {
}
impl DatabasePersistenceImpl {
fn workspace_id(&self) -> Result<String, DatabaseError> {
fn workspace_id(&self) -> Result<Uuid, DatabaseError> {
let workspace_id = self
.user
.workspace_id()
@ -947,7 +976,7 @@ impl DatabaseCollabPersistenceService for DatabasePersistenceImpl {
if let Ok((uid, Ok(Some(collab_db)))) = result {
let object_id = collab.object_id().to_string();
let db_read = collab_db.read_txn();
if !db_read.is_exist(uid, &workspace_id, &object_id) {
if !db_read.is_exist(uid, workspace_id.to_string().as_str(), &object_id) {
trace!(
"[Database]: collab:{} not exist in local storage",
object_id
@ -957,7 +986,12 @@ impl DatabaseCollabPersistenceService for DatabasePersistenceImpl {
trace!("[Database]: start loading collab:{} from disk", object_id);
let mut txn = collab.transact_mut();
match db_read.load_doc_with_txn(uid, &workspace_id, &object_id, &mut txn) {
match db_read.load_doc_with_txn(
uid,
workspace_id.to_string().as_str(),
&object_id,
&mut txn,
) {
Ok(update_count) => {
trace!(
"[Database]: did load collab:{}, update_count:{}",
@ -976,7 +1010,7 @@ impl DatabaseCollabPersistenceService for DatabasePersistenceImpl {
}
fn get_encoded_collab(&self, object_id: &str, collab_type: CollabType) -> Option<EncodedCollab> {
let workspace_id = self.user.workspace_id().ok()?;
let workspace_id = self.user.workspace_id().ok()?.to_string();
let uid = self.user.user_id().ok()?;
let db = self.user.collab_db(uid).ok()?.upgrade()?;
let read_txn = db.read_txn();
@ -995,7 +1029,7 @@ impl DatabaseCollabPersistenceService for DatabasePersistenceImpl {
}
fn delete_collab(&self, object_id: &str) -> Result<(), DatabaseError> {
let workspace_id = self.workspace_id()?;
let workspace_id = self.workspace_id()?.to_string();
let uid = self
.user
.user_id()
@ -1017,7 +1051,7 @@ impl DatabaseCollabPersistenceService for DatabasePersistenceImpl {
object_id: &str,
encoded_collab: EncodedCollab,
) -> Result<(), DatabaseError> {
let workspace_id = self.workspace_id()?;
let workspace_id = self.workspace_id()?.to_string();
let uid = self
.user
.user_id()
@ -1051,7 +1085,7 @@ impl DatabaseCollabPersistenceService for DatabasePersistenceImpl {
Ok(uid) => {
if let Ok(Some(collab_db)) = self.user.collab_db(uid).map(|weak| weak.upgrade()) {
let read_txn = collab_db.read_txn();
return read_txn.is_exist(uid, workspace_id.as_str(), object_id);
return read_txn.is_exist(uid, workspace_id.to_string().as_str(), object_id);
}
false
},
@ -1073,7 +1107,8 @@ impl DatabaseCollabPersistenceService for DatabasePersistenceImpl {
let workspace_id = self
.user
.workspace_id()
.map_err(|err| DatabaseError::Internal(err.into()))?;
.map_err(|err| DatabaseError::Internal(err.into()))?
.to_string();
if let Ok(Some(collab_db)) = self.user.collab_db(uid).map(|weak| weak.upgrade()) {
let write_txn = collab_db.write_txn();

View file

@ -44,6 +44,7 @@ use lib_infra::box_any::BoxAny;
use lib_infra::priority_task::TaskDispatcher;
use lib_infra::util::timestamp;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Arc, Weak};
use std::time::Duration;
use tokio::select;
@ -53,11 +54,12 @@ use tokio::sync::{broadcast, oneshot};
use tokio::task::yield_now;
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, event, info, instrument, trace, warn};
use uuid::Uuid;
type OpenDatabaseResult = oneshot::Sender<FlowyResult<DatabasePB>>;
pub struct DatabaseEditor {
database_id: String,
database_id: Uuid,
pub(crate) database: Arc<RwLock<Database>>,
pub cell_cache: CellCache,
pub(crate) database_views: Arc<DatabaseViews>,
@ -117,6 +119,7 @@ impl DatabaseEditor {
.await?,
);
let database_id = Uuid::from_str(&database_id)?;
let collab_object = collab_builder.collab_object(
&user.workspace_id()?,
user.user_id()?,
@ -806,10 +809,11 @@ impl DatabaseEditor {
let is_finalized = self.finalized_rows.get(row_id.as_str()).await.is_some();
if !is_finalized {
trace!("[Database]: finalize database row: {}", row_id);
let row_id = Uuid::from_str(row_id.as_str())?;
let collab_object = self.collab_builder.collab_object(
&self.user.workspace_id()?,
self.user.user_id()?,
row_id,
&row_id,
CollabType::DatabaseRow,
)?;

View file

@ -16,6 +16,7 @@ use futures::StreamExt;
use std::sync::Arc;
use tracing::{error, trace, warn};
use uuid::Uuid;
pub(crate) async fn observe_sync_state(database_id: &str, database: &Arc<RwLock<Database>>) {
let weak_database = Arc::downgrade(database);
@ -112,7 +113,7 @@ pub(crate) async fn observe_field_change(database_id: &str, database: &Arc<RwLoc
}
#[allow(dead_code)]
pub(crate) async fn observe_view_change(database_id: &str, database_editor: &Arc<DatabaseEditor>) {
pub(crate) async fn observe_view_change(database_id: &Uuid, database_editor: &Arc<DatabaseEditor>) {
let database_id = database_id.to_string();
let weak_database_editor = Arc::downgrade(database_editor);
let view_change = database_editor
@ -289,7 +290,7 @@ async fn handle_did_update_row_orders(
}
}
pub(crate) async fn observe_block_event(database_id: &str, database_editor: &Arc<DatabaseEditor>) {
pub(crate) async fn observe_block_event(database_id: &Uuid, database_editor: &Arc<DatabaseEditor>) {
let database_id = database_id.to_string();
let mut block_event_rx = database_editor
.database

View file

@ -9,5 +9,5 @@ edition = "2021"
lib-infra = { workspace = true }
flowy-error = { workspace = true }
collab-document = { workspace = true }
anyhow.workspace = true
collab = { workspace = true }
collab = { workspace = true }
uuid.workspace = true

View file

@ -1,8 +1,8 @@
use collab::entity::EncodedCollab;
pub use collab_document::blocks::DocumentData;
use flowy_error::FlowyError;
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
/// A trait for document cloud service.
/// Each kind of server should implement this trait. Check out the [AppFlowyServerProvider] of
@ -11,27 +11,27 @@ use lib_infra::async_trait::async_trait;
pub trait DocumentCloudService: Send + Sync + 'static {
async fn get_document_doc_state(
&self,
document_id: &str,
workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Vec<u8>, FlowyError>;
async fn get_document_snapshots(
&self,
document_id: &str,
document_id: &Uuid,
limit: usize,
workspace_id: &str,
) -> Result<Vec<DocumentSnapshot>, FlowyError>;
async fn get_document_data(
&self,
document_id: &str,
workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Option<DocumentData>, FlowyError>;
async fn create_document_collab(
&self,
workspace_id: &str,
document_id: &str,
workspace_id: &Uuid,
document_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError>;
}

View file

@ -6,9 +6,10 @@ use collab::preclude::Collab;
use collab_document::document::Document;
use futures::StreamExt;
use lib_infra::sync_trace;
use uuid::Uuid;
pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) {
let doc_id_clone_for_block_changed = doc_id.to_owned();
pub fn subscribe_document_changed(doc_id: &Uuid, document: &mut Document) {
let doc_id_clone_for_block_changed = doc_id.to_string();
document.subscribe_block_changed("key", move |events, is_remote| {
sync_trace!(
"[Document] block changed in doc_id: {}, is_remote: {}, events: {:?}",
@ -35,7 +36,7 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) {
);
document_notification_builder(
&doc_id_clone_for_awareness_state,
&doc_id_clone_for_awareness_state.to_string(),
DocumentNotification::DidUpdateDocumentAwarenessState,
)
.payload::<DocumentAwarenessStatesPB>(events.into())

View file

@ -1,5 +1,3 @@
use std::collections::HashMap;
use collab::core::collab_state::SyncState;
use collab_document::{
blocks::{json_str_to_hashmap, Block, BlockAction, DocumentData},
@ -8,10 +6,12 @@ use collab_document::{
DocumentAwarenessUser,
},
};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode;
use lib_infra::validator_fn::{required_not_empty_str, required_valid_path};
use std::collections::HashMap;
use std::str::FromStr;
use uuid::Uuid;
use validator::Validate;
use crate::parse::{NotEmptyStr, NotEmptyVec};
@ -31,7 +31,7 @@ pub struct OpenDocumentPayloadPB {
}
pub struct OpenDocumentParams {
pub document_id: String,
pub document_id: Uuid,
}
impl TryInto<OpenDocumentParams> for OpenDocumentPayloadPB {
@ -39,9 +39,9 @@ impl TryInto<OpenDocumentParams> for OpenDocumentPayloadPB {
fn try_into(self) -> Result<OpenDocumentParams, Self::Error> {
let document_id =
NotEmptyStr::parse(self.document_id).map_err(|_| ErrorCode::DocumentIdIsEmpty)?;
Ok(OpenDocumentParams {
document_id: document_id.0,
})
let document_id = Uuid::from_str(&document_id.0).map_err(|_| ErrorCode::InvalidParams)?;
Ok(OpenDocumentParams { document_id })
}
}
@ -52,7 +52,7 @@ pub struct DocumentRedoUndoPayloadPB {
}
pub struct DocumentRedoUndoParams {
pub document_id: String,
pub document_id: Uuid,
}
impl TryInto<DocumentRedoUndoParams> for DocumentRedoUndoPayloadPB {
@ -60,9 +60,8 @@ impl TryInto<DocumentRedoUndoParams> for DocumentRedoUndoPayloadPB {
fn try_into(self) -> Result<DocumentRedoUndoParams, Self::Error> {
let document_id =
NotEmptyStr::parse(self.document_id).map_err(|_| ErrorCode::DocumentIdIsEmpty)?;
Ok(DocumentRedoUndoParams {
document_id: document_id.0,
})
let document_id = Uuid::from_str(&document_id.0).map_err(|_| ErrorCode::InvalidParams)?;
Ok(DocumentRedoUndoParams { document_id })
}
}
@ -132,7 +131,7 @@ pub struct CreateDocumentPayloadPB {
}
pub struct CreateDocumentParams {
pub document_id: String,
pub document_id: Uuid,
pub initial_data: Option<DocumentData>,
}
@ -141,9 +140,10 @@ impl TryInto<CreateDocumentParams> for CreateDocumentPayloadPB {
fn try_into(self) -> Result<CreateDocumentParams, Self::Error> {
let document_id =
NotEmptyStr::parse(self.document_id).map_err(|_| ErrorCode::DocumentIdIsEmpty)?;
let document_id = Uuid::from_str(&document_id.0).map_err(|_| ErrorCode::InvalidParams)?;
let initial_data = self.initial_data.map(|data| data.into());
Ok(CreateDocumentParams {
document_id: document_id.0,
document_id,
initial_data,
})
}
@ -156,7 +156,7 @@ pub struct CloseDocumentPayloadPB {
}
pub struct CloseDocumentParams {
pub document_id: String,
pub document_id: Uuid,
}
impl TryInto<CloseDocumentParams> for CloseDocumentPayloadPB {
@ -164,9 +164,8 @@ impl TryInto<CloseDocumentParams> for CloseDocumentPayloadPB {
fn try_into(self) -> Result<CloseDocumentParams, Self::Error> {
let document_id =
NotEmptyStr::parse(self.document_id).map_err(|_| ErrorCode::DocumentIdIsEmpty)?;
Ok(CloseDocumentParams {
document_id: document_id.0,
})
let document_id = Uuid::from_str(&document_id.0).map_err(|_| ErrorCode::InvalidParams)?;
Ok(CloseDocumentParams { document_id })
}
}
@ -180,7 +179,7 @@ pub struct ApplyActionPayloadPB {
}
pub struct ApplyActionParams {
pub document_id: String,
pub document_id: Uuid,
pub actions: Vec<BlockAction>,
}
@ -189,10 +188,11 @@ impl TryInto<ApplyActionParams> for ApplyActionPayloadPB {
fn try_into(self) -> Result<ApplyActionParams, Self::Error> {
let document_id =
NotEmptyStr::parse(self.document_id).map_err(|_| ErrorCode::DocumentIdIsEmpty)?;
let document_id = Uuid::from_str(&document_id.0).map_err(|_| ErrorCode::InvalidParams)?;
let actions = NotEmptyVec::parse(self.actions).map_err(|_| ErrorCode::ApplyActionsIsEmpty)?;
let actions = actions.0.into_iter().map(BlockAction::from).collect();
Ok(ApplyActionParams {
document_id: document_id.0,
document_id,
actions,
})
}
@ -525,7 +525,7 @@ pub struct TextDeltaPayloadPB {
}
pub struct TextDeltaParams {
pub document_id: String,
pub document_id: Uuid,
pub text_id: String,
pub delta: String,
}
@ -535,10 +535,11 @@ impl TryInto<TextDeltaParams> for TextDeltaPayloadPB {
fn try_into(self) -> Result<TextDeltaParams, Self::Error> {
let document_id =
NotEmptyStr::parse(self.document_id).map_err(|_| ErrorCode::DocumentIdIsEmpty)?;
let document_id = Uuid::from_str(&document_id.0).map_err(|_| ErrorCode::InvalidParams)?;
let text_id = NotEmptyStr::parse(self.text_id).map_err(|_| ErrorCode::TextIdIsEmpty)?;
let delta = self.delta.map_or_else(|| "".to_string(), |delta| delta);
Ok(TextDeltaParams {
document_id: document_id.0,
document_id,
text_id: text_id.0,
delta,
})

View file

@ -3,7 +3,7 @@
* as well as performing actions on documents. These functions make use of a DocumentManager,
* which you can think of as a higher-level interface to interact with documents.
*/
use std::str::FromStr;
use std::sync::{Arc, Weak};
use collab_document::blocks::{
@ -23,7 +23,7 @@ use flowy_error::{FlowyError, FlowyResult};
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
use lib_infra::sync_trace;
use tracing::instrument;
use uuid::Uuid;
fn upgrade_document(
document_manager: AFPluginState<Weak<DocumentManager>>,
) -> FlowyResult<Arc<DocumentManager>> {
@ -496,7 +496,7 @@ pub(crate) async fn set_awareness_local_state_handler(
) -> FlowyResult<()> {
let manager = upgrade_document(manager)?;
let data = data.into_inner();
let doc_id = data.document_id.clone();
let doc_id = Uuid::from_str(&data.document_id)?;
manager
.set_document_awareness_local_state(&doc_id, data)
.await?;

View file

@ -14,21 +14,21 @@ use collab_document::document_awareness::DocumentAwarenessUser;
use collab_document::document_data::default_document_data;
use collab_entity::CollabType;
use collab_plugins::CollabKVDB;
use dashmap::DashMap;
use lib_infra::util::timestamp;
use tracing::{event, instrument};
use tracing::{info, trace};
use crate::document::{
subscribe_document_changed, subscribe_document_snapshot_state, subscribe_document_sync_state,
};
use collab_integrate::collab_builder::{
AppFlowyCollabBuilder, CollabBuilderConfig, CollabPersistenceImpl,
};
use collab_plugins::CollabKVDB;
use dashmap::DashMap;
use flowy_document_pub::cloud::DocumentCloudService;
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_storage_pub::storage::{CreatedUpload, StorageService};
use lib_infra::util::timestamp;
use tracing::{event, instrument};
use tracing::{info, trace};
use uuid::Uuid;
use crate::entities::UpdateDocumentAwarenessStatePB;
use crate::entities::{
@ -39,7 +39,7 @@ use crate::reminder::DocumentReminderAction;
pub trait DocumentUserService: Send + Sync {
fn user_id(&self) -> Result<i64, FlowyError>;
fn device_id(&self) -> Result<String, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn workspace_id(&self) -> Result<Uuid, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
}
@ -54,8 +54,8 @@ pub trait DocumentSnapshotService: Send + Sync {
pub struct DocumentManager {
pub user_service: Arc<dyn DocumentUserService>,
collab_builder: Arc<AppFlowyCollabBuilder>,
documents: Arc<DashMap<String, Arc<RwLock<Document>>>>,
removing_documents: Arc<DashMap<String, Arc<RwLock<Document>>>>,
documents: Arc<DashMap<Uuid, Arc<RwLock<Document>>>>,
removing_documents: Arc<DashMap<Uuid, Arc<RwLock<Document>>>>,
cloud_service: Arc<dyn DocumentCloudService>,
storage_service: Weak<dyn StorageService>,
snapshot_service: Arc<dyn DocumentSnapshotService>,
@ -81,7 +81,7 @@ impl DocumentManager {
}
/// Get the encoded collab of the document.
pub async fn get_encoded_collab_with_view_id(&self, doc_id: &str) -> FlowyResult<EncodedCollab> {
pub async fn get_encoded_collab_with_view_id(&self, doc_id: &Uuid) -> FlowyResult<EncodedCollab> {
let uid = self.user_service.user_id()?;
let workspace_id = self.user_service.workspace_id()?;
let doc_state =
@ -139,7 +139,7 @@ impl DocumentManager {
pub async fn create_document(
&self,
_uid: i64,
doc_id: &str,
doc_id: &Uuid,
data: Option<DocumentData>,
) -> FlowyResult<EncodedCollab> {
if self.is_doc_exist(doc_id).await.unwrap_or(false) {
@ -151,17 +151,17 @@ impl DocumentManager {
let encoded_collab = doc_state_from_document_data(doc_id, data).await?;
self
.persistence()?
.save_collab_to_disk(doc_id, encoded_collab.clone())
.save_collab_to_disk(doc_id.to_string().as_str(), encoded_collab.clone())
.map_err(internal_error)?;
// Send the collab data to server with a background task.
let cloud_service = self.cloud_service.clone();
let cloned_encoded_collab = encoded_collab.clone();
let document_id = doc_id.to_string();
let workspace_id = self.user_service.workspace_id()?;
let doc_id = doc_id.clone();
tokio::spawn(async move {
let _ = cloud_service
.create_document_collab(&workspace_id, &document_id, cloned_encoded_collab)
.create_document_collab(&workspace_id, &doc_id, cloned_encoded_collab)
.await;
});
Ok(encoded_collab)
@ -171,7 +171,7 @@ impl DocumentManager {
async fn collab_for_document(
&self,
uid: i64,
doc_id: &str,
doc_id: &Uuid,
data_source: DataSource,
sync_enable: bool,
) -> FlowyResult<Arc<RwLock<Document>>> {
@ -195,7 +195,7 @@ impl DocumentManager {
}
/// Return a document instance if the document is already opened.
pub async fn editable_document(&self, doc_id: &str) -> FlowyResult<Arc<RwLock<Document>>> {
pub async fn editable_document(&self, doc_id: &Uuid) -> FlowyResult<Arc<RwLock<Document>>> {
if let Some(doc) = self.documents.get(doc_id).map(|item| item.value().clone()) {
return Ok(doc);
}
@ -213,7 +213,7 @@ impl DocumentManager {
#[tracing::instrument(level = "info", skip(self), err)]
async fn create_document_instance(
&self,
doc_id: &str,
doc_id: &Uuid,
enable_sync: bool,
) -> FlowyResult<Arc<RwLock<Document>>> {
let uid = self.user_service.user_id()?;
@ -260,7 +260,7 @@ impl DocumentManager {
subscribe_document_snapshot_state(&lock);
subscribe_document_sync_state(&lock);
}
self.documents.insert(doc_id.to_string(), document.clone());
self.documents.insert(doc_id.clone(), document.clone());
}
Ok(document)
},
@ -273,21 +273,21 @@ impl DocumentManager {
}
}
pub async fn get_document_data(&self, doc_id: &str) -> FlowyResult<DocumentData> {
pub async fn get_document_data(&self, doc_id: &Uuid) -> FlowyResult<DocumentData> {
let document = self.get_document(doc_id).await?;
let document = document.read().await;
document.get_document_data().map_err(internal_error)
}
pub async fn get_document_text(&self, doc_id: &str) -> FlowyResult<String> {
pub async fn get_document_text(&self, doc_id: &Uuid) -> FlowyResult<String> {
let document = self.get_document(doc_id).await?;
let document = document.read().await;
let text = document.to_plain_text(true, false)?;
let text = document.paragraphs().join("\n");
Ok(text)
}
/// Return a document instance.
/// The returned document might or might not be able to sync with the cloud.
async fn get_document(&self, doc_id: &str) -> FlowyResult<Arc<RwLock<Document>>> {
async fn get_document(&self, doc_id: &Uuid) -> FlowyResult<Arc<RwLock<Document>>> {
if let Some(doc) = self.documents.get(doc_id).map(|item| item.value().clone()) {
return Ok(doc);
}
@ -300,7 +300,7 @@ impl DocumentManager {
Ok(document)
}
pub async fn open_document(&self, doc_id: &str) -> FlowyResult<()> {
pub async fn open_document(&self, doc_id: &Uuid) -> FlowyResult<()> {
if let Some(mutex_document) = self.restore_document_from_removing(doc_id) {
let lock = mutex_document.read().await;
lock.start_init_sync();
@ -314,7 +314,7 @@ impl DocumentManager {
Ok(())
}
pub async fn close_document(&self, doc_id: &str) -> FlowyResult<()> {
pub async fn close_document(&self, doc_id: &Uuid) -> FlowyResult<()> {
if let Some((doc_id, document)) = self.documents.remove(doc_id) {
{
// clear the awareness state when close the document
@ -340,11 +340,12 @@ impl DocumentManager {
Ok(())
}
pub async fn delete_document(&self, doc_id: &str) -> FlowyResult<()> {
pub async fn delete_document(&self, doc_id: &Uuid) -> FlowyResult<()> {
let uid = self.user_service.user_id()?;
let workspace_id = self.user_service.workspace_id()?;
if let Some(db) = self.user_service.collab_db(uid)?.upgrade() {
db.delete_doc(uid, &workspace_id, doc_id).await?;
db.delete_doc(uid, &workspace_id.to_string(), &doc_id.to_string())
.await?;
// When deleting a document, we need to remove it from the cache.
self.documents.remove(doc_id);
}
@ -354,7 +355,7 @@ impl DocumentManager {
#[instrument(level = "debug", skip_all, err)]
pub async fn set_document_awareness_local_state(
&self,
doc_id: &str,
doc_id: &Uuid,
state: UpdateDocumentAwarenessStatePB,
) -> FlowyResult<bool> {
let uid = self.user_service.user_id()?;
@ -379,12 +380,12 @@ impl DocumentManager {
/// Return the list of snapshots of the document.
pub async fn get_document_snapshot_meta(
&self,
document_id: &str,
document_id: &Uuid,
_limit: usize,
) -> FlowyResult<Vec<DocumentSnapshotMetaPB>> {
let metas = self
.snapshot_service
.get_document_snapshot_metas(document_id)?
.get_document_snapshot_metas(document_id.to_string().as_str())?
.into_iter()
.map(|meta| DocumentSnapshotMetaPB {
snapshot_id: meta.snapshot_id,
@ -434,11 +435,13 @@ impl DocumentManager {
Ok(())
}
async fn is_doc_exist(&self, doc_id: &str) -> FlowyResult<bool> {
async fn is_doc_exist(&self, doc_id: &Uuid) -> FlowyResult<bool> {
let uid = self.user_service.user_id()?;
let workspace_id = self.user_service.workspace_id()?;
if let Some(collab_db) = self.user_service.collab_db(uid)?.upgrade() {
let is_exist = collab_db.is_exist(uid, &workspace_id, doc_id).await?;
let is_exist = collab_db
.is_exist(uid, &workspace_id.to_string(), &doc_id.to_string())
.await?;
Ok(is_exist)
} else {
Ok(false)
@ -463,7 +466,7 @@ impl DocumentManager {
&self.storage_service
}
fn restore_document_from_removing(&self, doc_id: &str) -> Option<Arc<RwLock<Document>>> {
fn restore_document_from_removing(&self, doc_id: &Uuid) -> Option<Arc<RwLock<Document>>> {
let (doc_id, doc) = self.removing_documents.remove(doc_id)?;
trace!(
"move document {} from removing_documents to documents",
@ -475,7 +478,7 @@ impl DocumentManager {
}
async fn doc_state_from_document_data(
doc_id: &str,
doc_id: &Uuid,
data: Option<DocumentData>,
) -> Result<EncodedCollab, FlowyError> {
let doc_id = doc_id.to_string();

View file

@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use uuid::Uuid;
use validator::Validate;
#[derive(Default, ProtoBuf)]
@ -96,7 +97,7 @@ pub struct ParseType {
}
pub struct ConvertDocumentParams {
pub document_id: String,
pub document_id: Uuid,
pub range: Option<Range>,
pub parse_types: ParseType,
}
@ -140,10 +141,11 @@ impl TryInto<ConvertDocumentParams> for ConvertDocumentPayloadPB {
fn try_into(self) -> Result<ConvertDocumentParams, Self::Error> {
let document_id =
NotEmptyStr::parse(self.document_id).map_err(|_| ErrorCode::DocumentIdIsEmpty)?;
let document_id = Uuid::parse_str(&document_id.0).map_err(|_| ErrorCode::InvalidParams)?;
let range = self.range.map(|data| data.into());
Ok(ConvertDocumentParams {
document_id: document_id.0,
document_id,
range,
parse_types: self.parse_types.into(),
})

View file

@ -9,8 +9,8 @@ use crate::document::util::{gen_document_id, gen_id, DocumentTest};
async fn undo_redo_test() {
let test = DocumentTest::new();
let doc_id: String = gen_document_id();
let data = default_document_data(&doc_id);
let doc_id = gen_document_id();
let data = default_document_data(&doc_id.to_string());
// create a document
_ = test

View file

@ -11,8 +11,8 @@ async fn restore_document() {
let test = DocumentTest::new();
// create a document
let doc_id: String = gen_document_id();
let data = default_document_data(&doc_id);
let doc_id = gen_document_id();
let data = default_document_data(&doc_id.to_string());
let uid = test.user_service.user_id().unwrap();
test
.create_document(uid, &doc_id, Some(data.clone()))
@ -55,8 +55,8 @@ async fn restore_document() {
async fn document_apply_insert_action() {
let test = DocumentTest::new();
let uid = test.user_service.user_id().unwrap();
let doc_id: String = gen_document_id();
let data = default_document_data(&doc_id);
let doc_id = gen_document_id();
let data = default_document_data(&doc_id.to_string());
// create a document
_ = test.create_document(uid, &doc_id, Some(data.clone())).await;
@ -111,9 +111,9 @@ async fn document_apply_insert_action() {
#[tokio::test]
async fn document_apply_update_page_action() {
let test = DocumentTest::new();
let doc_id: String = gen_document_id();
let doc_id = gen_document_id();
let uid = test.user_service.user_id().unwrap();
let data = default_document_data(&doc_id);
let data = default_document_data(&doc_id.to_string());
// create a document
_ = test.create_document(uid, &doc_id, Some(data.clone())).await;
@ -158,8 +158,8 @@ async fn document_apply_update_page_action() {
async fn document_apply_update_action() {
let test = DocumentTest::new();
let uid = test.user_service.user_id().unwrap();
let doc_id: String = gen_document_id();
let data = default_document_data(&doc_id);
let doc_id = gen_document_id();
let data = default_document_data(&doc_id.to_string());
// create a document
_ = test.create_document(uid, &doc_id, Some(data.clone())).await;

View file

@ -7,11 +7,6 @@ use collab::preclude::CollabPlugin;
use collab_document::blocks::DocumentData;
use collab_document::document::Document;
use collab_document::document_data::default_document_data;
use nanoid::nanoid;
use tempfile::TempDir;
use tokio::sync::RwLock;
use tracing_subscriber::{fmt::Subscriber, util::SubscriberInitExt, EnvFilter};
use collab_integrate::collab_builder::{
AppFlowyCollabBuilder, CollabCloudPluginProvider, CollabPluginProviderContext,
CollabPluginProviderType, WorkspaceCollabIntegrate,
@ -24,6 +19,11 @@ use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_storage_pub::storage::{CreatedUpload, FileProgressReceiver, StorageService};
use lib_infra::async_trait::async_trait;
use lib_infra::box_any::BoxAny;
use nanoid::nanoid;
use tempfile::TempDir;
use tokio::sync::RwLock;
use tracing_subscriber::{fmt::Subscriber, util::SubscriberInitExt, EnvFilter};
use uuid::Uuid;
pub struct DocumentTest {
inner: DocumentManager,
@ -63,7 +63,7 @@ impl Deref for DocumentTest {
}
pub struct FakeUser {
workspace_id: String,
workspace_id: Uuid,
collab_db: Arc<CollabKVDB>,
}
@ -74,7 +74,7 @@ impl FakeUser {
let tempdir = TempDir::new().unwrap();
let path = tempdir.into_path();
let collab_db = Arc::new(CollabKVDB::open(path).unwrap());
let workspace_id = uuid::Uuid::new_v4().to_string();
let workspace_id = uuid::Uuid::new_v4();
Self {
collab_db,
@ -88,7 +88,7 @@ impl DocumentUserService for FakeUser {
Ok(1)
}
fn workspace_id(&self) -> Result<String, FlowyError> {
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
Ok(self.workspace_id.clone())
}
@ -115,8 +115,8 @@ pub fn setup_log() {
pub async fn create_and_open_empty_document() -> (DocumentTest, Arc<RwLock<Document>>, String) {
let test = DocumentTest::new();
let doc_id: String = gen_document_id();
let data = default_document_data(&doc_id);
let doc_id = gen_document_id();
let data = default_document_data(&doc_id.to_string());
let uid = test.user_service.user_id().unwrap();
// create a document
test
@ -130,9 +130,8 @@ pub async fn create_and_open_empty_document() -> (DocumentTest, Arc<RwLock<Docum
(test, document, data.page_id)
}
pub fn gen_document_id() -> String {
let uuid = uuid::Uuid::new_v4();
uuid.to_string()
pub fn gen_document_id() -> Uuid {
uuid::Uuid::new_v4()
}
pub fn gen_id() -> String {
@ -145,8 +144,8 @@ pub struct LocalTestDocumentCloudServiceImpl();
impl DocumentCloudService for LocalTestDocumentCloudServiceImpl {
async fn get_document_doc_state(
&self,
document_id: &str,
_workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
let document_id = document_id.to_string();
Err(FlowyError::new(
@ -157,26 +156,26 @@ impl DocumentCloudService for LocalTestDocumentCloudServiceImpl {
async fn get_document_snapshots(
&self,
_document_id: &str,
_limit: usize,
_workspace_id: &str,
document_id: &Uuid,
limit: usize,
workspace_id: &str,
) -> Result<Vec<DocumentSnapshot>, FlowyError> {
Ok(vec![])
}
async fn get_document_data(
&self,
_document_id: &str,
_workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Option<DocumentData>, FlowyError> {
Ok(None)
}
async fn create_document_collab(
&self,
_workspace_id: &str,
_document_id: &str,
_encoded_collab: EncodedCollab,
workspace_id: &Uuid,
document_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError> {
Ok(())
}
@ -257,14 +256,14 @@ impl DocumentSnapshotService for DocumentTestSnapshot {
}
struct WorkspaceCollabIntegrateImpl {
workspace_id: String,
workspace_id: Uuid,
}
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
fn workspace_id(&self) -> Result<String, Error> {
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
Ok(self.workspace_id.clone())
}
fn device_id(&self) -> Result<String, Error> {
fn device_id(&self) -> Result<String, FlowyError> {
Ok("fake_device_id".to_string())
}
}

View file

@ -34,6 +34,7 @@ collab-plugins = { workspace = true, optional = true }
collab-folder = { workspace = true, optional = true }
client-api = { workspace = true, optional = true }
tantivy = { version = "0.22.0", optional = true }
uuid.workspace = true
[features]
default = ["impl_from_dispatch_error", "impl_from_serde", "impl_from_reqwest", "impl_from_sqlite"]

View file

@ -256,3 +256,9 @@ impl From<collab::error::CollabError> for FlowyError {
}
}
}
impl From<uuid::Error> for FlowyError {
fn from(value: uuid::Error) -> Self {
FlowyError::internal().with_context(value)
}
}

View file

@ -15,7 +15,7 @@ pub trait FolderCloudService: Send + Sync + 'static {
/// Returns error if the cloud service doesn't support multiple workspaces
async fn create_workspace(&self, uid: i64, name: &str) -> Result<Workspace, FlowyError>;
async fn open_workspace(&self, workspace_id: &str) -> Result<(), FlowyError>;
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError>;
/// Returns all workspaces of the user.
/// Returns vec![] if the cloud service doesn't support multiple workspaces
@ -23,7 +23,7 @@ pub trait FolderCloudService: Send + Sync + 'static {
async fn get_folder_data(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: &i64,
) -> Result<Option<FolderData>, FlowyError>;
@ -35,21 +35,21 @@ pub trait FolderCloudService: Send + Sync + 'static {
async fn get_folder_doc_state(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: i64,
collab_type: CollabType,
object_id: &str,
object_id: &Uuid,
) -> Result<Vec<u8>, FlowyError>;
async fn full_sync_collab_object(
&self,
workspace_id: &str,
workspace_id: &Uuid,
params: FullSyncCollabParams,
) -> Result<(), FlowyError>;
async fn batch_create_folder_collab_objects(
&self,
workspace_id: &str,
workspace_id: &Uuid,
objects: Vec<FolderCollabParams>,
) -> Result<(), FlowyError>;
@ -57,64 +57,64 @@ pub trait FolderCloudService: Send + Sync + 'static {
async fn publish_view(
&self,
workspace_id: &str,
workspace_id: &Uuid,
payload: Vec<PublishPayload>,
) -> Result<(), FlowyError>;
async fn unpublish_views(
&self,
workspace_id: &str,
view_ids: Vec<String>,
workspace_id: &Uuid,
view_ids: Vec<Uuid>,
) -> Result<(), FlowyError>;
async fn get_publish_info(&self, view_id: &str) -> Result<PublishInfo, FlowyError>;
async fn get_publish_info(&self, view_id: &Uuid) -> Result<PublishInfo, FlowyError>;
async fn set_publish_name(
&self,
workspace_id: &str,
view_id: String,
workspace_id: &Uuid,
view_id: Uuid,
new_name: String,
) -> Result<(), FlowyError>;
async fn set_publish_namespace(
&self,
workspace_id: &str,
workspace_id: &Uuid,
new_namespace: String,
) -> Result<(), FlowyError>;
async fn list_published_views(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<PublishInfoView>, FlowyError>;
async fn get_default_published_view_info(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<PublishInfo, FlowyError>;
async fn set_default_published_view(
&self,
workspace_id: &str,
workspace_id: &Uuid,
view_id: uuid::Uuid,
) -> Result<(), FlowyError>;
async fn remove_default_published_view(&self, workspace_id: &str) -> Result<(), FlowyError>;
async fn remove_default_published_view(&self, workspace_id: &Uuid) -> Result<(), FlowyError>;
async fn get_publish_namespace(&self, workspace_id: &str) -> Result<String, FlowyError>;
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError>;
async fn import_zip(&self, file_path: &str) -> Result<(), FlowyError>;
}
#[derive(Debug)]
pub struct FolderCollabParams {
pub object_id: String,
pub object_id: Uuid,
pub encoded_collab_v1: Vec<u8>,
pub collab_type: CollabType,
}
#[derive(Debug)]
pub struct FullSyncCollabParams {
pub object_id: String,
pub object_id: Uuid,
pub encoded_collab: EncodedCollab,
pub collab_type: CollabType,
}

View file

@ -3,6 +3,7 @@ use collab_entity::CollabType;
use collab_folder::ViewLayout;
use flowy_error::FlowyResult;
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
pub struct QueryCollab {
pub collab_type: CollabType,
@ -17,14 +18,14 @@ pub trait FolderQueryService: Send + Sync + 'static {
/// the provided view layout, given that the parent view is not a space
async fn get_surrounding_view_ids_with_view_layout(
&self,
parent_view_id: &str,
parent_view_id: &Uuid,
view_layout: ViewLayout,
) -> Vec<String>;
) -> Vec<Uuid>;
async fn get_collab(&self, object_id: &str, collab_type: CollabType) -> Option<QueryCollab>;
async fn get_collab(&self, object_id: &Uuid, collab_type: CollabType) -> Option<QueryCollab>;
}
#[async_trait]
pub trait FolderViewEdit: Send + Sync + 'static {
async fn set_view_title_if_empty(&self, view_id: &str, title: &str) -> FlowyResult<()>;
async fn set_view_title_if_empty(&self, view_id: &Uuid, title: &str) -> FlowyResult<()>;
}

View file

@ -4,6 +4,8 @@ use crate::share::{ImportData, ImportItem, ImportParams, ImportType};
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::FlowyError;
use lib_infra::validator_fn::required_not_empty_str;
use std::str::FromStr;
use uuid::Uuid;
use validator::Validate;
#[derive(Clone, Debug, ProtoBuf_Enum)]
@ -76,6 +78,8 @@ impl TryInto<ImportParams> for ImportPayloadPB {
.map_err(|_| FlowyError::invalid_view_id())?
.0;
let parent_view_id = Uuid::from_str(&parent_view_id)?;
let items = self
.items
.into_iter()

View file

@ -1,12 +1,13 @@
use collab_folder::{View, ViewIcon, ViewLayout};
use std::collections::HashMap;
use std::convert::TryInto;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode;
use flowy_folder_pub::cloud::gen_view_id;
use std::collections::HashMap;
use std::convert::TryInto;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use std::sync::Arc;
use uuid::Uuid;
use crate::entities::icon::ViewIconPB;
use crate::entities::parser::view::{ViewIdentify, ViewName, ViewThumbnail};
@ -322,10 +323,10 @@ pub struct CreateOrphanViewPayloadPB {
#[derive(Debug, Clone)]
pub struct CreateViewParams {
pub parent_view_id: String,
pub parent_view_id: Uuid,
pub name: String,
pub layout: ViewLayoutPB,
pub view_id: String,
pub view_id: Uuid,
pub initial_data: ViewData,
pub meta: HashMap<String, String>,
// Mark the view as current view after creation.
@ -346,9 +347,13 @@ impl TryInto<CreateViewParams> for CreateViewPayloadPB {
fn try_into(self) -> Result<CreateViewParams, Self::Error> {
let name = ViewName::parse(self.name)?.0;
let parent_view_id = ViewIdentify::parse(self.parent_view_id)?.0;
let parent_view_id = ViewIdentify::parse(self.parent_view_id)
.and_then(|id| Uuid::from_str(&id.0).map_err(|err| ErrorCode::InvalidParams))?;
// if view_id is not provided, generate a new view_id
let view_id = self.view_id.unwrap_or_else(|| gen_view_id().to_string());
let view_id = self
.view_id
.and_then(|v| Uuid::parse_str(&v).ok())
.unwrap_or_else(|| gen_view_id());
Ok(CreateViewParams {
parent_view_id,
@ -371,13 +376,13 @@ impl TryInto<CreateViewParams> for CreateOrphanViewPayloadPB {
fn try_into(self) -> Result<CreateViewParams, Self::Error> {
let name = ViewName::parse(self.name)?.0;
let parent_view_id = ViewIdentify::parse(self.view_id.clone())?.0;
let view_id = Uuid::parse_str(&self.view_id).map_err(|_| ErrorCode::InvalidParams)?;
Ok(CreateViewParams {
parent_view_id,
parent_view_id: view_id.clone(),
name,
layout: self.layout,
view_id: self.view_id,
view_id,
initial_data: ViewData::Data(self.initial_data.into()),
meta: Default::default(),
set_as_current: false,
@ -564,9 +569,9 @@ impl TryInto<MoveViewParams> for MoveViewPayloadPB {
#[derive(Debug)]
pub struct MoveNestedViewParams {
pub view_id: String,
pub new_parent_id: String,
pub prev_view_id: Option<String>,
pub view_id: Uuid,
pub new_parent_id: Uuid,
pub prev_view_id: Option<Uuid>,
pub from_section: Option<ViewSectionPB>,
pub to_section: Option<ViewSectionPB>,
}
@ -575,9 +580,20 @@ impl TryInto<MoveNestedViewParams> for MoveNestedViewPayloadPB {
type Error = ErrorCode;
fn try_into(self) -> Result<MoveNestedViewParams, Self::Error> {
let view_id = ViewIdentify::parse(self.view_id)?.0;
let view_id = Uuid::from_str(&ViewIdentify::parse(self.view_id)?.0)
.map_err(|_| ErrorCode::InvalidParams)?;
let new_parent_id = ViewIdentify::parse(self.new_parent_id)?.0;
let prev_view_id = self.prev_view_id;
let new_parent_id = Uuid::from_str(&new_parent_id).map_err(|_| ErrorCode::InvalidParams)?;
let prev_view_id = match self.prev_view_id {
Some(prev_view_id) => Some(
Uuid::from_str(&ViewIdentify::parse(prev_view_id)?.0)
.map_err(|_| ErrorCode::InvalidParams)?,
),
None => None,
};
Ok(MoveNestedViewParams {
view_id,
new_parent_id,

View file

@ -1,8 +1,9 @@
use std::sync::{Arc, Weak};
use tracing::instrument;
use flowy_error::{FlowyError, FlowyResult};
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
use std::str::FromStr;
use std::sync::{Arc, Weak};
use tracing::instrument;
use uuid::Uuid;
use crate::entities::*;
use crate::manager::FolderManager;
@ -443,7 +444,12 @@ pub(crate) async fn unpublish_views_handler(
) -> Result<(), FlowyError> {
let folder = upgrade_folder(folder)?;
let params = data.into_inner();
folder.unpublish_views(params.view_ids).await?;
let view_ids = params
.view_ids
.into_iter()
.flat_map(|id| Uuid::from_str(&id).ok())
.collect::<Vec<_>>();
folder.unpublish_views(view_ids).await?;
Ok(())
}
@ -454,6 +460,7 @@ pub(crate) async fn get_publish_info_handler(
) -> DataResult<PublishInfoResponsePB, FlowyError> {
let folder = upgrade_folder(folder)?;
let view_id = data.into_inner().value;
let view_id = Uuid::from_str(&view_id)?;
let info = folder.get_publish_info(&view_id).await?;
data_result_ok(PublishInfoResponsePB::from(info))
}
@ -465,6 +472,7 @@ pub(crate) async fn set_publish_name_handler(
) -> Result<(), FlowyError> {
let folder = upgrade_folder(folder)?;
let SetPublishNamePB { view_id, new_name } = data.into_inner();
let view_id = Uuid::from_str(&view_id)?;
folder.set_publish_name(view_id, new_name).await?;
Ok(())
}

View file

@ -44,16 +44,18 @@ use flowy_sqlite::kv::KVStorePreferences;
use futures::future;
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use std::sync::{Arc, Weak};
use tokio::sync::RwLockWriteGuard;
use tracing::{error, info, instrument};
use uuid::Uuid;
pub trait FolderUser: Send + Sync {
fn user_id(&self) -> Result<i64, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn workspace_id(&self) -> Result<Uuid, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
fn is_folder_exist_on_disk(&self, uid: i64, workspace_id: &str) -> FlowyResult<bool>;
fn is_folder_exist_on_disk(&self, uid: i64, workspace_id: &Uuid) -> FlowyResult<bool>;
}
pub struct FolderManager {
@ -111,7 +113,7 @@ impl FolderManager {
Ok::<WorkspacePB, FlowyError>(workspace)
};
match folder.get_workspace_info(&workspace_id) {
match folder.get_workspace_info(&workspace_id.to_string()) {
None => Err(FlowyError::record_not_found().with_context("Can not find the workspace")),
Some(workspace) => workspace_pb_from_workspace(workspace, &folder),
}
@ -127,14 +129,14 @@ impl FolderManager {
.ok_or_else(|| internal_error("The folder is not initialized"))?
.read()
.await
.get_folder_data(&workspace_id)
.get_folder_data(&workspace_id.to_string())
.ok_or_else(|| internal_error("Workspace id not match the id in current folder"))?;
Ok(data)
}
pub async fn gather_publish_encode_collab(
&self,
view_id: &str,
view_id: &Uuid,
layout: &ViewLayout,
) -> FlowyResult<GatherEncodedCollab> {
let handler = self.get_handler(layout)?;
@ -177,7 +179,7 @@ impl FolderManager {
pub(crate) async fn make_folder<T: Into<Option<FolderNotify>>>(
&self,
uid: i64,
workspace_id: &str,
workspace_id: &Uuid,
collab_db: Weak<CollabKVDB>,
data_source: Option<DataSource>,
folder_notifier: T,
@ -187,8 +189,7 @@ impl FolderManager {
let config = CollabBuilderConfig::default().sync_enable(true);
let data_source = data_source.unwrap_or_else(|| {
CollabPersistenceImpl::new(collab_db.clone(), uid, workspace_id.to_string())
.into_data_source()
CollabPersistenceImpl::new(collab_db.clone(), uid, workspace_id.clone()).into_data_source()
});
let object_id = workspace_id;
@ -218,8 +219,11 @@ impl FolderManager {
"Clear the folder data and try to open the folder again due to: {}",
err
);
if let Some(db) = self.user.collab_db(uid).ok().and_then(|a| a.upgrade()) {
let _ = db.delete_doc(uid, workspace_id, workspace_id).await;
let _ = db
.delete_doc(uid, &workspace_id.to_string(), &object_id.to_string())
.await;
}
Err(err.into())
},
@ -229,7 +233,7 @@ impl FolderManager {
pub(crate) async fn create_folder_with_data(
&self,
uid: i64,
workspace_id: &str,
workspace_id: &Uuid,
collab_db: Weak<CollabKVDB>,
notifier: Option<FolderNotify>,
folder_data: Option<FolderData>,
@ -240,8 +244,8 @@ impl FolderManager {
.collab_builder
.collab_object(workspace_id, uid, object_id, CollabType::Folder)?;
let doc_state = CollabPersistenceImpl::new(collab_db.clone(), uid, workspace_id.to_string())
.into_data_source();
let doc_state =
CollabPersistenceImpl::new(collab_db.clone(), uid, workspace_id.clone()).into_data_source();
let folder = self
.collab_builder
.create_folder(
@ -317,7 +321,7 @@ impl FolderManager {
_token: &str,
is_new: bool,
data_source: FolderInitDataSource,
workspace_id: &str,
workspace_id: &Uuid,
) -> FlowyResult<()> {
// Create the default workspace if the user is new
info!("initialize_when_sign_up: is_new: {}", is_new);
@ -377,7 +381,7 @@ impl FolderManager {
let workspace_id = self.user.workspace_id()?;
let latest_view = self.get_current_view().await;
Ok(WorkspaceSettingPB {
workspace_id,
workspace_id: workspace_id.to_string(),
latest_view,
})
}
@ -495,7 +499,7 @@ impl FolderManager {
.ok_or_else(|| FlowyError::internal().with_context("folder is not initialized"))?;
let folder = lock.read().await;
let workspace = folder
.get_workspace_info(&workspace_id)
.get_workspace_info(&workspace_id.to_string())
.ok_or_else(|| FlowyError::record_not_found().with_context("Can not find the workspace"))?;
let views = folder
@ -606,8 +610,9 @@ impl FolderManager {
// Drop the folder lock explicitly to avoid deadlock when following calls contains 'self'
drop(folder);
let view_id = Uuid::from_str(view_id)?;
let handler = self.get_handler(&view.layout)?;
handler.close_view(view_id).await?;
handler.close_view(&view_id).await?;
}
}
Ok(())
@ -844,24 +849,28 @@ impl FolderManager {
let prev_view_id = params.prev_view_id;
let from_section = params.from_section;
let to_section = params.to_section;
let view = self.get_view_pb(&view_id).await?;
let view = self.get_view_pb(&view_id.to_string()).await?;
// if the view is locked, the view can't be moved
if view.is_locked.unwrap_or(false) {
return Err(FlowyError::view_is_locked());
}
let old_parent_id = view.parent_view_id;
let old_parent_id = Uuid::from_str(&view.parent_view_id)?;
if let Some(lock) = self.mutex_folder.load_full() {
let mut folder = lock.write().await;
folder.move_nested_view(&view_id, &new_parent_id, prev_view_id);
folder.move_nested_view(
&view_id.to_string(),
&new_parent_id.to_string(),
prev_view_id.map(|s| s.to_string()),
);
if from_section != to_section {
if to_section == Some(ViewSectionPB::Private) {
folder.add_private_view_ids(vec![view_id.clone()]);
folder.add_private_view_ids(vec![view_id.to_string()]);
} else {
folder.delete_private_view_ids(vec![view_id.clone()]);
folder.delete_private_view_ids(vec![view_id.to_string()]);
}
}
notify_parent_view_did_change(&workspace_id, &folder, vec![new_parent_id, old_parent_id]);
notify_parent_view_did_change(workspace_id, &folder, vec![new_parent_id, old_parent_id]);
}
Ok(())
}
@ -912,7 +921,8 @@ impl FolderManager {
if let Some(lock) = self.mutex_folder.load_full() {
let mut folder = lock.write().await;
folder.move_view(view_id, actual_from_index as u32, actual_to_index as u32);
notify_parent_view_did_change(&workspace_id, &folder, vec![parent_view_id]);
let parent_view_id = Uuid::from_str(&parent_view_id)?;
notify_parent_view_did_change(workspace_id, &folder, vec![parent_view_id]);
}
}
}
@ -1115,7 +1125,8 @@ impl FolderManager {
view.name,
view.layout
);
let view_data = handler.duplicate_view(&view.id).await?;
let view_id = Uuid::from_str(&view.id)?;
let view_data = handler.duplicate_view(&view_id).await?;
let index = self
.get_view_relation(&current_parent_id)
@ -1151,12 +1162,13 @@ impl FolderManager {
view.name.clone()
};
let parent_view_id = Uuid::from_str(&current_parent_id)?;
let duplicate_params = CreateViewParams {
parent_view_id: current_parent_id.clone(),
parent_view_id,
name,
layout: view.layout.clone().into(),
initial_data: ViewData::DuplicateData(view_data),
view_id: gen_view_id().to_string(),
view_id: gen_view_id(),
meta: Default::default(),
set_as_current: is_source_view && open_after_duplicated,
index,
@ -1176,7 +1188,7 @@ impl FolderManager {
if sync_after_create {
if let Some(encoded_collab) = encoded_collab {
let object_id = duplicated_view.id.clone();
let object_id = Uuid::from_str(&duplicated_view.id)?;
let collab_type = match duplicated_view.layout {
ViewLayout::Document => CollabType::Document,
ViewLayout::Board | ViewLayout::Grid | ViewLayout::Calendar => CollabType::Database,
@ -1208,20 +1220,20 @@ impl FolderManager {
is_source_view = false
}
let workspace_id = &self.user.workspace_id()?;
let workspace_id = self.user.workspace_id()?;
let parent_view_id = Uuid::from_str(&parent_view_id)?;
// Sync the view to the cloud
if sync_after_create {
self
.cloud_service
.batch_create_folder_collab_objects(workspace_id, objects)
.batch_create_folder_collab_objects(&workspace_id, objects)
.await?;
}
// notify the update here
let folder = lock.read().await;
notify_parent_view_did_change(workspace_id, &folder, vec![parent_view_id.to_string()]);
notify_parent_view_did_change(workspace_id, &folder, vec![parent_view_id]);
let duplicated_view = self.get_view_pb(&new_view_id).await?;
Ok(duplicated_view)
@ -1242,6 +1254,7 @@ impl FolderManager {
let view_layout: ViewLayout = view.layout.clone().into();
if let Some(handle) = self.operation_handlers.get(&view_layout) {
info!("Open view: {}-{}", view.name, view.id);
let view_id = Uuid::from_str(&view.id)?;
if let Err(err) = handle.open_view(&view_id).await {
error!("Open view error: {:?}", err);
}
@ -1250,7 +1263,7 @@ impl FolderManager {
let workspace_id = self.user.workspace_id()?;
let setting = WorkspaceSettingPB {
workspace_id,
workspace_id: workspace_id.to_string(),
latest_view: view,
};
send_current_workspace_notification(FolderNotification::DidUpdateWorkspaceSetting, setting);
@ -1367,18 +1380,18 @@ impl FolderManager {
let workspace_id = self.user.workspace_id()?;
self
.cloud_service
.publish_view(workspace_id.as_str(), payload)
.publish_view(&workspace_id, payload)
.await?;
Ok(())
}
/// Unpublish the view with the given view id.
#[tracing::instrument(level = "debug", skip(self), err)]
pub async fn unpublish_views(&self, view_ids: Vec<String>) -> FlowyResult<()> {
pub async fn unpublish_views(&self, view_ids: Vec<Uuid>) -> FlowyResult<()> {
let workspace_id = self.user.workspace_id()?;
self
.cloud_service
.unpublish_views(workspace_id.as_str(), view_ids)
.unpublish_views(&workspace_id, view_ids)
.await?;
Ok(())
}
@ -1386,14 +1399,14 @@ impl FolderManager {
/// Get the publish info of the view with the given view id.
/// The publish info contains the namespace and publish_name of the view.
#[tracing::instrument(level = "debug", skip(self))]
pub async fn get_publish_info(&self, view_id: &str) -> FlowyResult<PublishInfo> {
pub async fn get_publish_info(&self, view_id: &Uuid) -> FlowyResult<PublishInfo> {
let publish_info = self.cloud_service.get_publish_info(view_id).await?;
Ok(publish_info)
}
/// Sets the publish name of the view with the given view id.
#[tracing::instrument(level = "debug", skip(self))]
pub async fn set_publish_name(&self, view_id: String, new_name: String) -> FlowyResult<()> {
pub async fn set_publish_name(&self, view_id: Uuid, new_name: String) -> FlowyResult<()> {
let workspace_id = self.user.workspace_id()?;
self
.cloud_service
@ -1409,7 +1422,7 @@ impl FolderManager {
let workspace_id = self.user.workspace_id()?;
self
.cloud_service
.set_publish_namespace(workspace_id.as_str(), new_namespace)
.set_publish_namespace(&workspace_id, new_namespace)
.await?;
Ok(())
}
@ -1420,7 +1433,7 @@ impl FolderManager {
let workspace_id = self.user.workspace_id()?;
let namespace = self
.cloud_service
.get_publish_namespace(workspace_id.as_str())
.get_publish_namespace(&workspace_id)
.await?;
Ok(namespace)
}
@ -1502,7 +1515,7 @@ impl FolderManager {
};
if let Ok(payload) = self
.get_publish_payload(&current_view_id, publish_name, layout)
.get_publish_payload(&Uuid::from_str(&current_view_id)?, publish_name, layout)
.await
{
payloads.push(payload);
@ -1551,7 +1564,7 @@ impl FolderManager {
async fn get_publish_payload(
&self,
view_id: &str,
view_id: &Uuid,
publish_name: Option<String>,
layout: ViewLayout,
) -> FlowyResult<PublishPayload> {
@ -1559,18 +1572,20 @@ impl FolderManager {
let encoded_collab_wrapper: GatherEncodedCollab = handler
.gather_publish_encode_collab(&self.user, view_id)
.await?;
let view = self.get_view_pb(view_id).await?;
let view_str_id = view_id.to_string();
let view = self.get_view_pb(&view_str_id).await?;
let publish_name = publish_name.unwrap_or_else(|| generate_publish_name(&view.id, &view.name));
let child_views = self
.build_publish_views(view_id)
.build_publish_views(&view_str_id)
.await
.and_then(|v| v.child_views)
.unwrap_or_default();
let ancestor_views = self
.get_view_ancestors_pb(view_id)
.get_view_ancestors_pb(&view_str_id)
.await?
.iter()
.map(view_pb_to_publish_view)
@ -1720,8 +1735,9 @@ impl FolderManager {
};
if let Some(view) = view {
let view_id = Uuid::from_str(view_id)?;
if let Ok(handler) = self.get_handler(&view.layout) {
handler.delete_view(view_id).await?;
handler.delete_view(&view_id).await?;
}
}
}
@ -1733,11 +1749,11 @@ impl FolderManager {
#[instrument(level = "debug", skip_all, err)]
pub(crate) async fn import_single_file(
&self,
parent_view_id: String,
parent_view_id: Uuid,
import_data: ImportItem,
) -> FlowyResult<(View, Vec<(String, CollabType, EncodedCollab)>)> {
let handler = self.get_handler(&import_data.view_layout)?;
let view_id = gen_view_id().to_string();
let view_id = gen_view_id();
let uid = self.user.user_id()?;
let mut encoded_collab = vec![];
@ -1745,7 +1761,7 @@ impl FolderManager {
match import_data.data {
ImportData::FilePath { file_path } => {
handler
.import_from_file_path(&view_id, &import_data.name, file_path)
.import_from_file_path(&view_id.to_string(), &import_data.name, file_path)
.await?;
},
ImportData::Bytes { bytes } => {
@ -1804,11 +1820,13 @@ impl FolderManager {
views.push(view_pb_without_child_views(view));
for (object_id, collab_type, encode_collab) in encoded_collabs {
match self.get_folder_collab_params(object_id, collab_type, encode_collab) {
Ok(params) => objects.push(params),
Err(e) => {
error!("import error {}", e);
},
if let Ok(object_id) = Uuid::from_str(&object_id) {
match self.get_folder_collab_params(object_id, collab_type, encode_collab) {
Ok(params) => objects.push(params),
Err(e) => {
error!("import error {}", e);
},
}
}
}
}
@ -1822,7 +1840,7 @@ impl FolderManager {
// Notify that the parent view has changed
if let Some(lock) = self.mutex_folder.load_full() {
let folder = lock.read().await;
notify_parent_view_did_change(&workspace_id, &folder, vec![import_data.parent_view_id]);
notify_parent_view_did_change(workspace_id, &folder, vec![import_data.parent_view_id]);
}
Ok(RepeatedViewPB { items: views })
@ -1887,7 +1905,7 @@ impl FolderManager {
fn get_folder_collab_params(
&self,
object_id: String,
object_id: Uuid,
collab_type: CollabType,
encoded_collab: EncodedCollab,
) -> FlowyResult<FolderCollabParams> {
@ -1911,18 +1929,20 @@ impl FolderManager {
let folder = lock.read().await;
let view = folder.get_view(view_id)?;
match folder.get_view(&view.parent_view_id) {
None => folder.get_workspace_info(&workspace_id).map(|workspace| {
(
true,
workspace.id,
workspace
.child_views
.items
.into_iter()
.map(|view| view.id)
.collect::<Vec<String>>(),
)
}),
None => folder
.get_workspace_info(&workspace_id.to_string())
.map(|workspace| {
(
true,
workspace.id,
workspace
.child_views
.items
.into_iter()
.map(|view| view.id)
.collect::<Vec<String>>(),
)
}),
Some(parent_view) => Some((
false,
parent_view.id.clone(),
@ -2031,17 +2051,17 @@ impl FolderManager {
.collect()
}
pub fn remove_indices_for_workspace(&self, workspace_id: String) -> FlowyResult<()> {
pub fn remove_indices_for_workspace(&self, workspace_id: &Uuid) -> FlowyResult<()> {
self
.folder_indexer
.remove_indices_for_workspace(workspace_id)?;
.remove_indices_for_workspace(workspace_id.clone())?;
Ok(())
}
}
/// Return the views that belong to the workspace. The views are filtered by the trash and all the private views.
pub(crate) fn get_workspace_public_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
pub(crate) fn get_workspace_public_view_pbs(workspace_id: &Uuid, folder: &Folder) -> Vec<ViewPB> {
// get the trash ids
let trash_ids = folder
.get_all_trash_sections()
@ -2056,7 +2076,7 @@ pub(crate) fn get_workspace_public_view_pbs(workspace_id: &str, folder: &Folder)
.map(|view| view.id)
.collect::<Vec<String>>();
let mut views = folder.get_views_belong_to(workspace_id);
let mut views = folder.get_views_belong_to(&workspace_id.to_string());
// filter the views that are in the trash and all the private views
views.retain(|view| !trash_ids.contains(&view.id) && !private_view_ids.contains(&view.id));
@ -2082,7 +2102,7 @@ fn get_all_child_view_ids(folder: &Folder, view_id: &str) -> Vec<String> {
}
/// Get the current private views of the user.
pub(crate) fn get_workspace_private_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
pub(crate) fn get_workspace_private_view_pbs(workspace_id: &Uuid, folder: &Folder) -> Vec<ViewPB> {
// get the trash ids
let trash_ids = folder
.get_all_trash_sections()
@ -2097,7 +2117,7 @@ pub(crate) fn get_workspace_private_view_pbs(workspace_id: &str, folder: &Folder
.map(|view| view.id)
.collect::<Vec<String>>();
let mut views = folder.get_views_belong_to(workspace_id);
let mut views = folder.get_views_belong_to(&workspace_id.to_string());
// filter the views that are in the trash and not in the private view ids
views.retain(|view| !trash_ids.contains(&view.id) && private_view_ids.contains(&view.id));

View file

@ -10,6 +10,7 @@ use flowy_error::{FlowyError, FlowyResult};
use std::sync::{Arc, Weak};
use tokio::task::spawn_blocking;
use tracing::{event, info, Level};
use uuid::Uuid;
impl FolderManager {
/// Called immediately after the application launched if the user already sign in/sign up.
@ -17,7 +18,7 @@ impl FolderManager {
pub async fn initialize(
&self,
uid: i64,
workspace_id: &str,
workspace_id: &Uuid,
initial_data: FolderInitDataSource,
) -> FlowyResult<()> {
// Update the workspace id
@ -37,7 +38,6 @@ impl FolderManager {
);
}
let workspace_id = workspace_id.to_string();
// Get the collab db for the user with given user id.
let collab_db = self.user.collab_db(uid)?;
@ -54,20 +54,20 @@ impl FolderManager {
} => {
let is_exist = self
.user
.is_folder_exist_on_disk(uid, &workspace_id)
.is_folder_exist_on_disk(uid, workspace_id)
.unwrap_or(false);
// 1. if the folder exists, open it from local disk
if is_exist {
event!(Level::INFO, "Init folder from local disk");
self
.make_folder(uid, &workspace_id, collab_db, None, folder_notifier)
.make_folder(uid, workspace_id, collab_db, None, folder_notifier)
.await?
} else if create_if_not_exist {
// 2. if the folder doesn't exist and create_if_not_exist is true, create a default folder
// Currently, this branch is only used when the server type is supabase. For appflowy cloud,
// the default workspace is already created when the user sign up.
self
.create_default_folder(uid, &workspace_id, collab_db, folder_notifier)
.create_default_folder(uid, workspace_id, collab_db, folder_notifier)
.await?
} else {
// 3. If the folder doesn't exist and create_if_not_exist is false, try to fetch the folder data from cloud/
@ -147,7 +147,7 @@ impl FolderManager {
async fn create_default_folder(
&self,
uid: i64,
workspace_id: &str,
workspace_id: &Uuid,
collab_db: Weak<CollabKVDB>,
folder_notifier: FolderNotify,
) -> Result<Arc<RwLock<Folder>>, FlowyError> {
@ -170,24 +170,22 @@ impl FolderManager {
Ok(folder)
}
fn handle_index_folder(&self, workspace_id: String, folder: &Folder) {
fn handle_index_folder(&self, workspace_id: Uuid, folder: &Folder) {
let mut index_all = true;
let encoded_collab = self
.store_preferences
.get_object::<EncodedCollab>(&workspace_id);
.get_object::<EncodedCollab>(workspace_id.to_string().as_str());
if let Some(encoded_collab) = encoded_collab {
if let Ok(changes) = folder.calculate_view_changes(encoded_collab) {
let folder_indexer = self.folder_indexer.clone();
let views = folder.get_all_views();
let wid = workspace_id.clone();
if !changes.is_empty() && !views.is_empty() {
spawn_blocking(move || {
// We index the changes
folder_indexer.index_view_changes(views, changes, wid);
folder_indexer.index_view_changes(views, changes, workspace_id);
});
index_all = false;
}
@ -197,15 +195,13 @@ impl FolderManager {
if index_all {
let views = folder.get_all_views();
let folder_indexer = self.folder_indexer.clone();
let wid = workspace_id.clone();
// We spawn a blocking task to index all views in the folder
spawn_blocking(move || {
// We remove old indexes just in case
let _ = folder_indexer.remove_indices_for_workspace(wid.clone());
let _ = folder_indexer.remove_indices_for_workspace(workspace_id.clone());
// We index all views from the workspace
folder_indexer.index_all_views(views, wid);
folder_indexer.index_all_views(views, workspace_id);
});
}

View file

@ -13,14 +13,16 @@ use collab_folder::{
use lib_infra::sync_trace;
use std::collections::HashSet;
use std::str::FromStr;
use std::sync::Weak;
use tokio_stream::wrappers::WatchStream;
use tokio_stream::StreamExt;
use tracing::{event, trace, Level};
use uuid::Uuid;
/// Listen on the [ViewChange] after create/delete/update events happened
pub(crate) fn subscribe_folder_view_changed(
workspace_id: String,
workspace_id: Uuid,
mut rx: ViewChangeReceiver,
weak_mutex_folder: Weak<RwLock<Folder>>,
user: Weak<dyn FolderUser>,
@ -46,9 +48,10 @@ pub(crate) fn subscribe_folder_view_changed(
ChildViewChangeReason::Create,
);
let folder = lock.read().await;
let parent_view_id = view.parent_view_id.clone();
notify_parent_view_did_change(&workspace_id, &folder, vec![parent_view_id]);
sync_trace!("[Folder] create view: {:?}", view);
if let Ok(parent_view_id) = Uuid::from_str(&view.parent_view_id) {
notify_parent_view_did_change(workspace_id, &folder, vec![parent_view_id]);
sync_trace!("[Folder] create view: {:?}", view);
}
},
ViewChange::DidDeleteView { views } => {
for view in views {
@ -69,7 +72,9 @@ pub(crate) fn subscribe_folder_view_changed(
ChildViewChangeReason::Update,
);
let folder = lock.read().await;
notify_parent_view_did_change(&workspace_id, &folder, vec![view.parent_view_id]);
if let Ok(parent_view_id) = Uuid::from_str(&view.parent_view_id) {
notify_parent_view_did_change(workspace_id, &folder, vec![parent_view_id]);
}
},
};
}
@ -78,7 +83,7 @@ pub(crate) fn subscribe_folder_view_changed(
}
pub(crate) fn subscribe_folder_sync_state_changed(
workspace_id: String,
workspace_id: Uuid,
mut folder_sync_state_rx: WatchStream<SyncState>,
user: Weak<dyn FolderUser>,
) {
@ -93,16 +98,19 @@ pub(crate) fn subscribe_folder_sync_state_changed(
}
}
folder_notification_builder(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
.payload(FolderSyncStatePB::from(state))
.send();
folder_notification_builder(
&workspace_id.to_string(),
FolderNotification::DidUpdateFolderSyncUpdate,
)
.payload(FolderSyncStatePB::from(state))
.send();
}
});
}
/// Listen on the [TrashChange]s and notify the frontend some views were changed.
pub(crate) fn subscribe_folder_trash_changed(
workspace_id: String,
workspace_id: Uuid,
mut rx: SectionChangeReceiver,
weak_mutex_folder: Weak<RwLock<Folder>>,
user: Weak<dyn FolderUser>,
@ -131,7 +139,9 @@ pub(crate) fn subscribe_folder_trash_changed(
let folder = lock.read().await;
let views = folder.get_views(&ids);
for view in views {
unique_ids.insert(view.parent_view_id.clone());
if let Ok(parent_view_id) = Uuid::from_str(&view.parent_view_id) {
unique_ids.insert(parent_view_id);
}
}
let repeated_trash: RepeatedTrashPB = folder.get_my_trash_info().into();
@ -140,7 +150,7 @@ pub(crate) fn subscribe_folder_trash_changed(
.send();
let parent_view_ids = unique_ids.into_iter().collect();
notify_parent_view_did_change(&workspace_id, &folder, parent_view_ids);
notify_parent_view_did_change(workspace_id, &folder, parent_view_ids);
},
}
}
@ -150,10 +160,10 @@ pub(crate) fn subscribe_folder_trash_changed(
/// Notify the list of parent view ids that its child views were changed.
#[tracing::instrument(level = "debug", skip(folder, parent_view_ids))]
pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
workspace_id: &str,
pub(crate) fn notify_parent_view_did_change(
workspace_id: Uuid,
folder: &Folder,
parent_view_ids: Vec<T>,
parent_view_ids: Vec<Uuid>,
) -> Option<()> {
let trash_ids = folder
.get_all_trash_sections()
@ -162,24 +172,23 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
.collect::<Vec<String>>();
for parent_view_id in parent_view_ids {
let parent_view_id = parent_view_id.as_ref();
// if the view's parent id equal to workspace id. Then it will fetch the current
// workspace views. Because the workspace is not a view stored in the views map.
if parent_view_id == workspace_id {
notify_did_update_workspace(workspace_id, folder);
notify_did_update_section_views(workspace_id, folder);
notify_did_update_workspace(&workspace_id, folder);
notify_did_update_section_views(&workspace_id, folder);
} else {
// Parent view can contain a list of child views. Currently, only get the first level
// child views.
let parent_view = folder.get_view(parent_view_id)?;
let mut child_views = folder.get_views_belong_to(parent_view_id);
let parent_view_id = parent_view_id.to_string();
let parent_view = folder.get_view(&parent_view_id)?;
let mut child_views = folder.get_views_belong_to(&parent_view_id);
child_views.retain(|view| !trash_ids.contains(&view.id));
event!(Level::DEBUG, child_views_count = child_views.len());
// Post the notification
let parent_view_pb = view_pb_with_child_views(parent_view, child_views);
folder_notification_builder(parent_view_id, FolderNotification::DidUpdateView)
folder_notification_builder(&parent_view_id, FolderNotification::DidUpdateView)
.payload(parent_view_pb)
.send();
}
@ -188,7 +197,7 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
None
}
pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folder) {
pub(crate) fn notify_did_update_section_views(workspace_id: &Uuid, folder: &Folder) {
let public_views = get_workspace_public_view_pbs(workspace_id, folder);
let private_views = get_workspace_private_view_pbs(workspace_id, folder);
trace!(
@ -214,7 +223,7 @@ pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folde
.send();
}
pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
pub(crate) fn notify_did_update_workspace(workspace_id: &Uuid, folder: &Folder) {
let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(workspace_id, folder).into();
folder_notification_builder(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
.payload(repeated_view)

View file

@ -1,6 +1,7 @@
use flowy_derive::ProtoBuf_Enum;
use flowy_notification::NotificationBuilder;
use lib_dispatch::prelude::ToBytes;
use tracing::trace;
const FOLDER_OBSERVABLE_SOURCE: &str = "Workspace";
@ -68,9 +69,14 @@ impl std::convert::From<i32> for FolderNotification {
}
}
#[tracing::instrument(level = "trace")]
pub(crate) fn folder_notification_builder(id: &str, ty: FolderNotification) -> NotificationBuilder {
NotificationBuilder::new(id, ty, FOLDER_OBSERVABLE_SOURCE)
#[tracing::instrument(level = "trace", skip_all)]
pub(crate) fn folder_notification_builder<T: ToString>(
id: T,
ty: FolderNotification,
) -> NotificationBuilder {
let id = id.to_string();
trace!("folder_notification_builder: id = {id}, ty = {ty:?}");
NotificationBuilder::new(&id, ty, FOLDER_OBSERVABLE_SOURCE)
}
/// The [CURRENT_WORKSPACE] represents as the current workspace that opened by the

View file

@ -1,5 +1,6 @@
use collab_folder::ViewLayout;
use std::fmt::{Display, Formatter};
use uuid::Uuid;
#[derive(Clone, Debug)]
pub enum ImportType {
@ -35,6 +36,6 @@ impl Display for ImportData {
#[derive(Clone, Debug)]
pub struct ImportParams {
pub parent_view_id: String,
pub parent_view_id: Uuid,
pub items: Vec<ImportItem>,
}

View file

@ -1,11 +1,12 @@
use crate::entities::UserFolderPB;
use flowy_error::{ErrorCode, FlowyError};
use uuid::Uuid;
pub(crate) fn folder_not_init_error() -> FlowyError {
FlowyError::internal().with_context("Folder not initialized")
}
pub(crate) fn workspace_data_not_sync_error(uid: i64, workspace_id: &str) -> FlowyError {
pub(crate) fn workspace_data_not_sync_error(uid: i64, workspace_id: &Uuid) -> FlowyError {
FlowyError::from(ErrorCode::WorkspaceDataNotSync).with_payload(UserFolderPB {
uid,
workspace_id: workspace_id.to_string(),

View file

@ -6,11 +6,11 @@ use collab_folder::hierarchy_builder::NestedViewBuilder;
pub use collab_folder::View;
use collab_folder::ViewLayout;
use dashmap::DashMap;
use flowy_error::FlowyError;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use flowy_error::FlowyError;
use uuid::Uuid;
use lib_infra::util::timestamp;
@ -51,23 +51,23 @@ pub trait FolderOperationHandler: Send + Sync {
Ok(())
}
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError>;
async fn open_view(&self, view_id: &Uuid) -> Result<(), FlowyError>;
/// Closes the view and releases the resources that this view has in
/// the backend
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError>;
async fn close_view(&self, view_id: &Uuid) -> Result<(), FlowyError>;
/// Called when the view is deleted.
/// This will called after the view is deleted from the trash.
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError>;
async fn delete_view(&self, view_id: &Uuid) -> Result<(), FlowyError>;
/// Returns the [ViewData] that can be used to create the same view.
async fn duplicate_view(&self, view_id: &str) -> Result<Bytes, FlowyError>;
async fn duplicate_view(&self, view_id: &Uuid) -> Result<Bytes, FlowyError>;
/// get the encoded collab data from the disk.
async fn gather_publish_encode_collab(
&self,
_user: &Arc<dyn FolderUser>,
_view_id: &str,
_view_id: &Uuid,
) -> Result<GatherEncodedCollab, FlowyError> {
Err(FlowyError::not_support())
}
@ -102,8 +102,8 @@ pub trait FolderOperationHandler: Send + Sync {
async fn create_default_view(
&self,
user_id: i64,
parent_view_id: &str,
view_id: &str,
parent_view_id: &Uuid,
view_id: &Uuid,
name: &str,
layout: ViewLayout,
) -> Result<(), FlowyError>;
@ -114,7 +114,7 @@ pub trait FolderOperationHandler: Send + Sync {
async fn import_from_bytes(
&self,
uid: i64,
view_id: &str,
view_id: &Uuid,
name: &str,
import_type: ImportType,
bytes: Vec<u8>,
@ -152,8 +152,8 @@ impl From<ViewLayoutPB> for ViewLayout {
pub(crate) fn create_view(uid: i64, params: CreateViewParams, layout: ViewLayout) -> View {
let time = timestamp();
View {
id: params.view_id,
parent_view_id: params.parent_view_id,
id: params.view_id.to_string(),
parent_view_id: params.parent_view_id.to_string(),
name: params.name,
created_at: time,
is_favorite: false,

View file

@ -11,4 +11,4 @@ collab = { workspace = true }
collab-folder = { workspace = true }
flowy-error = { workspace = true }
client-api = { workspace = true }
futures = { workspace = true }
uuid.workspace = true

View file

@ -1,12 +1,13 @@
use client_api::entity::search_dto::SearchDocumentResponseItem;
use flowy_error::FlowyError;
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
#[async_trait]
pub trait SearchCloudService: Send + Sync + 'static {
async fn document_search(
&self,
workspace_id: &str,
workspace_id: &Uuid,
query: String,
) -> Result<Vec<SearchDocumentResponseItem>, FlowyError>;
}

View file

@ -4,44 +4,45 @@ use std::sync::Arc;
use collab::core::collab::IndexContentReceiver;
use collab_folder::{folder_diff::FolderViewChange, View, ViewIcon, ViewLayout};
use flowy_error::FlowyError;
use uuid::Uuid;
pub struct IndexableData {
pub id: String,
pub data: String,
pub icon: Option<ViewIcon>,
pub layout: ViewLayout,
pub workspace_id: String,
pub workspace_id: Uuid,
}
impl IndexableData {
pub fn from_view(view: Arc<View>, workspace_id: String) -> Self {
pub fn from_view(view: Arc<View>, workspace_id: Uuid) -> Self {
IndexableData {
id: view.id.clone(),
data: view.name.clone(),
icon: view.icon.clone(),
layout: view.layout.clone(),
workspace_id: workspace_id.clone(),
workspace_id,
}
}
}
pub trait IndexManager: Send + Sync {
fn set_index_content_receiver(&self, rx: IndexContentReceiver, workspace_id: String);
fn set_index_content_receiver(&self, rx: IndexContentReceiver, workspace_id: Uuid);
fn add_index(&self, data: IndexableData) -> Result<(), FlowyError>;
fn update_index(&self, data: IndexableData) -> Result<(), FlowyError>;
fn remove_indices(&self, ids: Vec<String>) -> Result<(), FlowyError>;
fn remove_indices_for_workspace(&self, workspace_id: String) -> Result<(), FlowyError>;
fn remove_indices_for_workspace(&self, workspace_id: Uuid) -> Result<(), FlowyError>;
fn is_indexed(&self) -> bool;
fn as_any(&self) -> &dyn Any;
}
pub trait FolderIndexManager: IndexManager {
fn index_all_views(&self, views: Vec<Arc<View>>, workspace_id: String);
fn index_all_views(&self, views: Vec<Arc<View>>, workspace_id: Uuid);
fn index_view_changes(
&self,
views: Vec<Arc<View>>,
changes: Vec<FolderViewChange>,
workspace_id: String,
workspace_id: Uuid,
);
}

View file

@ -18,7 +18,6 @@ flowy-error = { workspace = true, features = [
"impl_from_serde",
] }
flowy-notification.workspace = true
flowy-sqlite.workspace = true
flowy-user.workspace = true
flowy-search-pub.workspace = true
flowy-folder = { workspace = true }
@ -37,12 +36,7 @@ async-stream = "0.3.4"
strsim = "0.11.0"
strum_macros = "0.26.1"
tantivy = { version = "0.22.0" }
tempfile = "3.9.0"
validator = { workspace = true, features = ["derive"] }
diesel.workspace = true
diesel_derives = { version = "2.1.0", features = ["sqlite", "r2d2"] }
diesel_migrations = { version = "2.1.0", features = ["sqlite"] }
uuid.workspace = true
[build-dependencies]
flowy-codegen.workspace = true

View file

@ -1,10 +1,11 @@
use std::sync::Arc;
use tracing::{trace, warn};
use flowy_error::FlowyResult;
use flowy_folder::{manager::FolderManager, ViewLayout};
use flowy_search_pub::cloud::SearchCloudService;
use lib_infra::async_trait::async_trait;
use std::str::FromStr;
use std::sync::Arc;
use tracing::{trace, warn};
use uuid::Uuid;
use crate::{
entities::{IndexTypePB, ResultIconPB, ResultIconTypePB, SearchFilterPB, SearchResultPB},
@ -49,6 +50,7 @@ impl SearchHandler for DocumentSearchHandler {
None => return Ok(vec![]),
};
let workspace_id = Uuid::from_str(&workspace_id)?;
let results = self
.cloud_service
.document_search(&workspace_id, query)
@ -61,7 +63,7 @@ impl SearchHandler for DocumentSearchHandler {
let mut search_results: Vec<SearchResultPB> = vec![];
for result in results {
if let Some(view) = views.iter().find(|v| v.id == result.object_id) {
if let Some(view) = views.iter().find(|v| v.id == result.object_id.to_string()) {
// If there is no View for the result, we don't add it to the results
// If possible we will extract the icon to display for the result
let icon: Option<ResultIconPB> = match view.icon.clone() {
@ -77,12 +79,12 @@ impl SearchHandler for DocumentSearchHandler {
search_results.push(SearchResultPB {
index_type: IndexTypePB::Document,
view_id: result.object_id.clone(),
id: result.object_id.clone(),
view_id: result.object_id.to_string(),
id: result.object_id.to_string(),
data: view.name.clone(),
icon,
score: result.score,
workspace_id: result.workspace_id,
workspace_id: result.workspace_id.to_string(),
preview: result.preview,
});
} else {

View file

@ -20,13 +20,13 @@ use flowy_error::{FlowyError, FlowyResult};
use flowy_search_pub::entities::{FolderIndexManager, IndexManager, IndexableData};
use flowy_user::services::authenticate_user::AuthenticateUser;
use super::entities::FolderIndexData;
use strsim::levenshtein;
use tantivy::{
collector::TopDocs, directory::MmapDirectory, doc, query::QueryParser, schema::Field, Document,
Index, IndexReader, IndexWriter, TantivyDocument, Term,
};
use super::entities::FolderIndexData;
use uuid::Uuid;
#[derive(Clone)]
pub struct FolderIndexManagerImpl {
@ -139,7 +139,7 @@ impl FolderIndexManagerImpl {
title_field => data.data.clone(),
icon_field => icon.unwrap_or_default(),
icon_ty_field => icon_ty,
workspace_id_field => data.workspace_id.clone(),
workspace_id_field => data.workspace_id.to_string(),
]);
}
@ -293,7 +293,7 @@ impl IndexManager for FolderIndexManagerImpl {
.unwrap_or(false)
}
fn set_index_content_receiver(&self, mut rx: IndexContentReceiver, workspace_id: String) {
fn set_index_content_receiver(&self, mut rx: IndexContentReceiver, workspace_id: Uuid) {
let indexer = self.clone();
let wid = workspace_id.clone();
tokio::spawn(async move {
@ -352,7 +352,7 @@ impl IndexManager for FolderIndexManagerImpl {
title_field => data.data,
icon_field => icon.unwrap_or_default(),
icon_ty_field => icon_ty,
workspace_id_field => data.workspace_id.clone(),
workspace_id_field => data.workspace_id.to_string(),
]);
index_writer.commit()?;
@ -389,7 +389,7 @@ impl IndexManager for FolderIndexManagerImpl {
title_field => data.data,
icon_field => icon.unwrap_or_default(),
icon_ty_field => icon_ty,
workspace_id_field => data.workspace_id,
workspace_id_field => data.workspace_id.to_string(),
]);
index_writer.commit()?;
@ -400,14 +400,14 @@ impl IndexManager for FolderIndexManagerImpl {
/// Removes all indexes that are related by workspace id. This is useful
/// for cleaning indexes when eg. removing/leaving a workspace.
///
fn remove_indices_for_workspace(&self, workspace_id: String) -> Result<(), FlowyError> {
fn remove_indices_for_workspace(&self, workspace_id: Uuid) -> Result<(), FlowyError> {
let mut index_writer = self.get_index_writer()?;
let folder_schema = self.get_folder_schema()?;
let id_field = folder_schema
.schema
.get_field(FOLDER_WORKSPACE_ID_FIELD_NAME)?;
let delete_term = Term::from_field_text(id_field, &workspace_id);
let delete_term = Term::from_field_text(id_field, &workspace_id.to_string());
index_writer.delete_term(delete_term);
index_writer.commit()?;
@ -421,7 +421,7 @@ impl IndexManager for FolderIndexManagerImpl {
}
impl FolderIndexManager for FolderIndexManagerImpl {
fn index_all_views(&self, views: Vec<Arc<View>>, workspace_id: String) {
fn index_all_views(&self, views: Vec<Arc<View>>, workspace_id: Uuid) {
let indexable_data = views
.into_iter()
.map(|view| IndexableData::from_view(view, workspace_id.clone()))
@ -434,7 +434,7 @@ impl FolderIndexManager for FolderIndexManagerImpl {
&self,
views: Vec<Arc<View>>,
changes: Vec<FolderViewChange>,
workspace_id: String,
workspace_id: Uuid,
) {
let mut views_iter = views.into_iter();
for change in changes {

View file

@ -1,4 +1,5 @@
use flowy_error::FlowyResult;
use uuid::Uuid;
pub const USER_SIGN_IN_URL: &str = "sign_in_url";
pub const USER_UUID: &str = "uuid";
@ -8,5 +9,5 @@ pub const USER_DEVICE_ID: &str = "device_id";
/// Represents a user that is currently using the server.
pub trait ServerUser: Send + Sync {
/// different user might return different workspace id.
fn workspace_id(&self) -> FlowyResult<String>;
fn workspace_id(&self) -> FlowyResult<Uuid>;
}

View file

@ -18,6 +18,7 @@ use serde_json::Value;
use std::collections::HashMap;
use std::path::Path;
use tracing::trace;
use uuid::Uuid;
pub(crate) struct AFCloudChatCloudServiceImpl<T> {
pub inner: T,
@ -30,10 +31,10 @@ where
{
async fn create_chat(
&self,
_uid: &i64,
workspace_id: &str,
chat_id: &str,
rag_ids: Vec<String>,
uid: &i64,
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
) -> Result<(), FlowyError> {
let chat_id = chat_id.to_string();
let try_get_client = self.inner.try_get_client();
@ -52,13 +53,12 @@ where
async fn create_question(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
message_type: ChatMessageType,
metadata: &[ChatMessageMetadata],
) -> Result<ChatMessage, FlowyError> {
let workspace_id = workspace_id.to_string();
let chat_id = chat_id.to_string();
let try_get_client = self.inner.try_get_client();
let params = CreateChatMessageParams {
@ -68,7 +68,7 @@ where
};
let message = try_get_client?
.create_question(&workspace_id, &chat_id, params)
.create_question(workspace_id, &chat_id, params)
.await
.map_err(FlowyError::from)?;
Ok(message)
@ -76,8 +76,8 @@ where
async fn create_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
question_id: i64,
metadata: Option<serde_json::Value>,
@ -89,7 +89,7 @@ where
question_message_id: question_id,
};
let message = try_get_client?
.save_answer(workspace_id, chat_id, params)
.save_answer(workspace_id, chat_id.to_string().as_str(), params)
.await
.map_err(FlowyError::from)?;
Ok(message)
@ -97,8 +97,8 @@ where
async fn stream_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
format: ResponseFormat,
ai_model: Option<AIModel>,
@ -129,13 +129,17 @@ where
async fn get_answer(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
question_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
let try_get_client = self.inner.try_get_client();
let resp = try_get_client?
.get_answer(workspace_id, chat_id, question_message_id)
.get_answer(
workspace_id,
chat_id.to_string().as_str(),
question_message_id,
)
.await
.map_err(FlowyError::from)?;
Ok(resp)
@ -143,14 +147,14 @@ where
async fn get_chat_messages(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
offset: MessageCursor,
limit: u64,
) -> Result<RepeatedChatMessage, FlowyError> {
let try_get_client = self.inner.try_get_client();
let resp = try_get_client?
.get_chat_messages(workspace_id, chat_id, offset, limit)
.get_chat_messages(workspace_id, chat_id.to_string().as_str(), offset, limit)
.await
.map_err(FlowyError::from)?;
@ -159,13 +163,17 @@ where
async fn get_question_from_answer_id(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
answer_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
let try_get_client = self.inner.try_get_client()?;
let resp = try_get_client
.get_question_message_from_answer_id(workspace_id, chat_id, answer_message_id)
.get_question_message_from_answer_id(
workspace_id,
chat_id.to_string().as_str(),
answer_message_id,
)
.await
.map_err(FlowyError::from)?
.ok_or_else(FlowyError::record_not_found)?;
@ -175,13 +183,13 @@ where
async fn get_related_message(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
) -> Result<RepeatedRelatedQuestion, FlowyError> {
let try_get_client = self.inner.try_get_client();
let resp = try_get_client?
.get_chat_related_question(workspace_id, chat_id, message_id)
.get_chat_related_question(workspace_id, chat_id.to_string().as_str(), message_id)
.await
.map_err(FlowyError::from)?;
@ -190,7 +198,7 @@ where
async fn stream_complete(
&self,
workspace_id: &str,
workspace_id: &Uuid,
params: CompleteTextParams,
ai_model: Option<AIModel>,
) -> Result<StreamComplete, FlowyError> {
@ -207,10 +215,10 @@ where
async fn embed_file(
&self,
_workspace_id: &str,
_file_path: &Path,
_chat_id: &str,
_metadata: Option<HashMap<String, Value>>,
workspace_id: &Uuid,
file_path: &Path,
chat_id: &Uuid,
metadata: Option<HashMap<String, Value>>,
) -> Result<(), FlowyError> {
return Err(
FlowyError::not_support()
@ -218,7 +226,7 @@ where
);
}
async fn get_local_ai_config(&self, workspace_id: &str) -> Result<LocalAIConfig, FlowyError> {
async fn get_local_ai_config(&self, workspace_id: &Uuid) -> Result<LocalAIConfig, FlowyError> {
let system = get_operating_system();
let platform = match system {
OperatingSystem::MacOS => "macos",
@ -232,51 +240,51 @@ where
let config = self
.inner
.try_get_client()?
.get_local_ai_config(workspace_id, platform)
.get_local_ai_config(workspace_id.to_string().as_str(), platform)
.await?;
Ok(config)
}
async fn get_workspace_plan(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
let plans = self
.inner
.try_get_client()?
.get_active_workspace_subscriptions(workspace_id)
.get_active_workspace_subscriptions(workspace_id.to_string().as_str())
.await?;
Ok(plans)
}
async fn get_chat_settings(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
) -> Result<ChatSettings, FlowyError> {
let settings = self
.inner
.try_get_client()?
.get_chat_settings(workspace_id, chat_id)
.get_chat_settings(workspace_id, chat_id.to_string().as_str())
.await?;
Ok(settings)
}
async fn update_chat_settings(
&self,
workspace_id: &str,
chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
params: UpdateChatParams,
) -> Result<(), FlowyError> {
self
.inner
.try_get_client()?
.update_chat_settings(workspace_id, chat_id, params)
.update_chat_settings(workspace_id, chat_id.to_string().as_str(), params)
.await?;
Ok(())
}
async fn get_available_models(&self, workspace_id: &str) -> Result<ModelList, FlowyError> {
async fn get_available_models(&self, workspace_id: &Uuid) -> Result<ModelList, FlowyError> {
let list = self
.inner
.try_get_client()?
@ -285,11 +293,11 @@ where
Ok(list)
}
async fn get_workspace_default_model(&self, workspace_id: &str) -> Result<String, FlowyError> {
async fn get_workspace_default_model(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
let setting = self
.inner
.try_get_client()?
.get_workspace_settings(workspace_id)
.get_workspace_settings(workspace_id.to_string().as_str())
.await?;
Ok(setting.ai_model)
}

View file

@ -1,3 +1,6 @@
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::AFServer;
use client_api::entity::ai_dto::{
SummarizeRowData, SummarizeRowParams, TranslateRowData, TranslateRowParams,
};
@ -6,20 +9,16 @@ use client_api::entity::{CreateCollabParams, QueryCollab, QueryCollabParams};
use client_api::error::ErrorCode::RecordNotFound;
use collab::entity::EncodedCollab;
use collab_entity::CollabType;
use serde_json::{Map, Value};
use std::sync::Arc;
use tracing::{error, instrument};
use flowy_database_pub::cloud::{
DatabaseAIService, DatabaseCloudService, DatabaseSnapshot, EncodeCollabByOid, SummaryRowContent,
TranslateRowContent, TranslateRowResponse,
};
use flowy_error::FlowyError;
use lib_infra::async_trait::async_trait;
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::AFServer;
use serde_json::{Map, Value};
use std::sync::Arc;
use tracing::{error, instrument};
use uuid::Uuid;
pub(crate) struct AFCloudDatabaseCloudServiceImpl<T> {
pub inner: T,
@ -35,12 +34,10 @@ where
#[allow(clippy::blocks_in_conditions)]
async fn get_database_encode_collab(
&self,
object_id: &str,
object_id: &Uuid,
collab_type: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Option<EncodedCollab>, FlowyError> {
let workspace_id = workspace_id.to_string();
let object_id = object_id.to_string();
let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
let params = QueryCollabParams {
@ -51,7 +48,7 @@ where
match result {
Ok(data) => {
check_request_workspace_id_is_match(
&workspace_id,
workspace_id,
&cloned_user,
format!("get database object: {}:{}", object_id, collab_type),
)?;
@ -71,17 +68,17 @@ where
#[allow(clippy::blocks_in_conditions)]
async fn create_database_encode_collab(
&self,
object_id: &str,
object_id: &Uuid,
collab_type: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError> {
let encoded_collab_v1 = encoded_collab
.encode_to_bytes()
.map_err(|err| FlowyError::internal().with_context(err))?;
let params = CreateCollabParams {
workspace_id: workspace_id.to_string(),
object_id: object_id.to_string(),
workspace_id: workspace_id.clone(),
object_id: object_id.clone(),
encoded_collab_v1,
collab_type,
};
@ -92,11 +89,10 @@ where
#[instrument(level = "debug", skip_all)]
async fn batch_get_database_encode_collab(
&self,
object_ids: Vec<String>,
object_ids: Vec<Uuid>,
object_ty: CollabType,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<EncodeCollabByOid, FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
let client = try_get_client?;
@ -104,8 +100,8 @@ where
.into_iter()
.map(|object_id| QueryCollab::new(object_id, object_ty))
.collect();
let results = client.batch_get_collab(&workspace_id, params).await?;
check_request_workspace_id_is_match(&workspace_id, &cloned_user, "batch get database object")?;
let results = client.batch_get_collab(workspace_id, params).await?;
check_request_workspace_id_is_match(workspace_id, &cloned_user, "batch get database object")?;
Ok(
results
.0
@ -131,8 +127,8 @@ where
async fn get_database_collab_object_snapshots(
&self,
_object_id: &str,
_limit: usize,
object_id: &Uuid,
limit: usize,
) -> Result<Vec<DatabaseSnapshot>, FlowyError> {
Ok(vec![])
}
@ -145,17 +141,17 @@ where
{
async fn summary_database_row(
&self,
workspace_id: &str,
_object_id: &str,
summary_row: SummaryRowContent,
workspace_id: &Uuid,
_object_id: &Uuid,
_summary_row: SummaryRowContent,
) -> Result<String, FlowyError> {
let try_get_client = self.inner.try_get_client();
let map: Map<String, Value> = summary_row
let map: Map<String, Value> = _summary_row
.into_iter()
.map(|(key, value)| (key, Value::String(value)))
.collect();
let params = SummarizeRowParams {
workspace_id: workspace_id.to_string(),
workspace_id: workspace_id.clone(),
data: SummarizeRowData::Content(map),
};
let data = try_get_client?.summarize_row(params).await?;
@ -164,19 +160,21 @@ where
async fn translate_database_row(
&self,
workspace_id: &str,
translate_row: TranslateRowContent,
language: &str,
workspace_id: &Uuid,
_translate_row: TranslateRowContent,
_language: &str,
) -> Result<TranslateRowResponse, FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
let data = TranslateRowData {
cells: translate_row,
language: language.to_string(),
cells: _translate_row,
language: _language.to_string(),
include_header: false,
};
let params = TranslateRowParams { workspace_id, data };
let params = TranslateRowParams {
workspace_id: workspace_id.to_string(),
data,
};
let data = try_get_client?.translate_row(params).await?;
Ok(data)
}

View file

@ -5,12 +5,12 @@ use collab::entity::EncodedCollab;
use collab::preclude::Collab;
use collab_document::document::Document;
use collab_entity::CollabType;
use std::sync::Arc;
use tracing::instrument;
use flowy_document_pub::cloud::*;
use flowy_error::FlowyError;
use lib_infra::async_trait::async_trait;
use std::sync::Arc;
use tracing::instrument;
use uuid::Uuid;
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
@ -29,12 +29,12 @@ where
#[instrument(level = "debug", skip_all, fields(document_id = %document_id))]
async fn get_document_doc_state(
&self,
document_id: &str,
workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
let params = QueryCollabParams {
workspace_id: workspace_id.to_string(),
inner: QueryCollab::new(document_id.to_string(), CollabType::Document),
workspace_id: workspace_id.clone(),
inner: QueryCollab::new(document_id.clone(), CollabType::Document),
};
let doc_state = self
.inner
@ -57,9 +57,9 @@ where
async fn get_document_snapshots(
&self,
_document_id: &str,
_limit: usize,
_workspace_id: &str,
document_id: &Uuid,
limit: usize,
workspace_id: &str,
) -> Result<Vec<DocumentSnapshot>, FlowyError> {
Ok(vec![])
}
@ -67,12 +67,12 @@ where
#[instrument(level = "debug", skip_all)]
async fn get_document_data(
&self,
document_id: &str,
workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Option<DocumentData>, FlowyError> {
let params = QueryCollabParams {
workspace_id: workspace_id.to_string(),
inner: QueryCollab::new(document_id.to_string(), CollabType::Document),
workspace_id: workspace_id.clone(),
inner: QueryCollab::new(document_id.clone(), CollabType::Document),
};
let doc_state = self
.inner
@ -89,7 +89,7 @@ where
)?;
let collab = Collab::new_with_source(
CollabOrigin::Empty,
document_id,
document_id.to_string().as_str(),
DataSource::DocStateV1(doc_state),
vec![],
false,
@ -100,13 +100,13 @@ where
async fn create_document_collab(
&self,
workspace_id: &str,
document_id: &str,
workspace_id: &Uuid,
document_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError> {
let params = CreateCollabParams {
workspace_id: workspace_id.to_string(),
object_id: document_id.to_string(),
workspace_id: workspace_id.clone(),
object_id: document_id.clone(),
encoded_collab_v1: encoded_collab
.encode_to_bytes()
.map_err(|err| FlowyError::internal().with_context(err))?,

View file

@ -1,9 +1,10 @@
use crate::af_cloud::AFServer;
use client_api::entity::{CompleteUploadRequest, CreateUploadRequest};
use flowy_error::{ErrorCode, FlowyError};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_storage_pub::cloud::{ObjectIdentity, ObjectValue, StorageCloudService};
use flowy_storage_pub::storage::{CompletedPartRequest, CreateUploadResponse, UploadPartResponse};
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
pub struct AFCloudFileStorageServiceImpl<T> {
pub client: T,
@ -56,10 +57,10 @@ where
async fn get_object_url_v1(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
file_id: &str,
) -> Result<String, FlowyError> {
) -> FlowyResult<String> {
let url = self
.client
.try_get_client()?
@ -67,14 +68,14 @@ where
Ok(url)
}
async fn parse_object_url_v1(&self, url: &str) -> Option<(String, String, String)> {
async fn parse_object_url_v1(&self, url: &str) -> Option<(Uuid, String, String)> {
let value = self.client.try_get_client().ok()?.parse_blob_url_v1(url)?;
Some(value)
}
async fn create_upload(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
file_id: &str,
content_type: &str,
@ -109,7 +110,7 @@ where
async fn upload_part(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
upload_id: &str,
file_id: &str,
@ -134,7 +135,7 @@ where
async fn complete_upload(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
upload_id: &str,
file_id: &str,

View file

@ -58,12 +58,10 @@ where
})
}
async fn open_workspace(&self, workspace_id: &str) -> Result<(), FlowyError> {
let workspace_id = workspace_id.to_string();
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
let try_get_client = self.inner.try_get_client();
let client = try_get_client?;
let _ = client.open_workspace(&workspace_id).await?;
let _ = client.open_workspace(workspace_id).await?;
Ok(())
}
@ -88,11 +86,10 @@ where
#[instrument(level = "debug", skip_all)]
async fn get_folder_data(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: &i64,
) -> Result<Option<FolderData>, FlowyError> {
let uid = *uid;
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
let params = QueryCollabParams {
@ -111,10 +108,10 @@ where
uid,
CollabOrigin::Empty,
DataSource::DocStateV1(doc_state),
&workspace_id,
&workspace_id.to_string(),
vec![],
)?;
Ok(folder.get_folder_data(&workspace_id))
Ok(folder.get_folder_data(&workspace_id.to_string()))
}
async fn get_folder_snapshots(
@ -128,18 +125,16 @@ where
#[instrument(level = "debug", skip_all)]
async fn get_folder_doc_state(
&self,
workspace_id: &str,
_uid: i64,
workspace_id: &Uuid,
uid: i64,
collab_type: CollabType,
object_id: &str,
object_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
let object_id = object_id.to_string();
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
let params = QueryCollabParams {
workspace_id: workspace_id.clone(),
inner: QueryCollab::new(object_id, collab_type),
inner: QueryCollab::new(object_id.clone(), collab_type),
};
let doc_state = try_get_client?
.get_collab(params)
@ -154,10 +149,9 @@ where
async fn full_sync_collab_object(
&self,
workspace_id: &str,
workspace_id: &Uuid,
params: FullSyncCollabParams,
) -> Result<(), FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
try_get_client?
.collab_full_sync(
@ -173,10 +167,9 @@ where
async fn batch_create_folder_collab_objects(
&self,
workspace_id: &str,
workspace_id: &Uuid,
objects: Vec<FolderCollabParams>,
) -> Result<(), FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
let params = objects
.into_iter()
@ -189,7 +182,7 @@ where
})
.collect::<Vec<_>>();
try_get_client?
.create_collab_list(&workspace_id, params)
.create_collab_list(workspace_id, params)
.await?;
Ok(())
}
@ -200,10 +193,9 @@ where
async fn publish_view(
&self,
workspace_id: &str,
workspace_id: &Uuid,
payload: Vec<PublishPayload>,
) -> Result<(), FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
let params = payload
.into_iter()
@ -235,29 +227,20 @@ where
async fn unpublish_views(
&self,
workspace_id: &str,
view_ids: Vec<String>,
workspace_id: &Uuid,
view_ids: Vec<Uuid>,
) -> Result<(), FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
let view_uuids = view_ids
.iter()
.map(|id| Uuid::parse_str(id).unwrap_or(Uuid::nil()))
.collect::<Vec<_>>();
try_get_client?
.unpublish_collabs(&workspace_id, &view_uuids)
.unpublish_collabs(&workspace_id, &view_ids)
.await?;
Ok(())
}
async fn get_publish_info(&self, view_id: &str) -> Result<PublishInfo, FlowyError> {
async fn get_publish_info(&self, view_id: &Uuid) -> Result<PublishInfo, FlowyError> {
let try_get_client = self.inner.try_get_client();
let view_id = Uuid::parse_str(view_id)
.map_err(|_| FlowyError::new(ErrorCode::InvalidParams, "Invalid view id"));
let view_id = view_id?;
let info = try_get_client?
.get_published_collab_info(&view_id)
.get_published_collab_info(view_id)
.await
.map_err(FlowyError::from)?;
Ok(info)
@ -265,14 +248,11 @@ where
async fn set_publish_name(
&self,
workspace_id: &str,
view_id: String,
workspace_id: &Uuid,
view_id: Uuid,
new_name: String,
) -> Result<(), FlowyError> {
let try_get_client = self.inner.try_get_client()?;
let view_id = Uuid::parse_str(&view_id)
.map_err(|_| FlowyError::new(ErrorCode::InvalidParams, "Invalid view id"))?;
try_get_client
.patch_published_collabs(
workspace_id,
@ -290,36 +270,33 @@ where
async fn set_publish_namespace(
&self,
workspace_id: &str,
workspace_id: &Uuid,
new_namespace: String,
) -> Result<(), FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.inner.try_get_client();
try_get_client?
.set_workspace_publish_namespace(&workspace_id, new_namespace)
.set_workspace_publish_namespace(workspace_id, new_namespace)
.await?;
Ok(())
}
async fn get_publish_namespace(&self, workspace_id: &str) -> Result<String, FlowyError> {
let workspace_id = workspace_id.to_string();
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
let namespace = self
.inner
.try_get_client()?
.get_workspace_publish_namespace(&workspace_id)
.get_workspace_publish_namespace(workspace_id)
.await?;
Ok(namespace)
}
async fn list_published_views(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<PublishInfoView>, FlowyError> {
let workspace_id = workspace_id.to_string();
let published_views = self
.inner
.try_get_client()?
.list_published_views(&workspace_id)
.list_published_views(workspace_id)
.await
.map_err(FlowyError::from)?;
Ok(published_views)
@ -327,7 +304,7 @@ where
async fn get_default_published_view_info(
&self,
workspace_id: &str,
workspace_id: &Uuid,
) -> Result<PublishInfo, FlowyError> {
let default_published_view_info = self
.inner
@ -340,7 +317,7 @@ where
async fn set_default_published_view(
&self,
workspace_id: &str,
workspace_id: &Uuid,
view_id: uuid::Uuid,
) -> Result<(), FlowyError> {
self
@ -352,7 +329,7 @@ where
Ok(())
}
async fn remove_default_published_view(&self, workspace_id: &str) -> Result<(), FlowyError> {
async fn remove_default_published_view(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
self
.inner
.try_get_client()?

View file

@ -2,6 +2,7 @@ use client_api::entity::search_dto::SearchDocumentResponseItem;
use flowy_error::FlowyError;
use flowy_search_pub::cloud::SearchCloudService;
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
use crate::af_cloud::AFServer;
@ -22,7 +23,7 @@ where
{
async fn document_search(
&self,
workspace_id: &str,
workspace_id: &Uuid,
query: String,
) -> Result<Vec<SearchDocumentResponseItem>, FlowyError> {
let client = self.inner.try_get_client()?;

View file

@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use anyhow::anyhow;
@ -189,9 +190,8 @@ where
Ok(profile)
}
async fn open_workspace(&self, workspace_id: &str) -> Result<UserWorkspace, FlowyError> {
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<UserWorkspace, FlowyError> {
let try_get_client = self.server.try_get_client();
let workspace_id = workspace_id.to_string();
let client = try_get_client?;
let af_workspace = client.open_workspace(&workspace_id).await?;
Ok(to_user_workspace(af_workspace))
@ -222,17 +222,14 @@ where
async fn patch_workspace(
&self,
workspace_id: &str,
workspace_id: &Uuid,
new_workspace_name: Option<&str>,
new_workspace_icon: Option<&str>,
) -> Result<(), FlowyError> {
let try_get_client = self.server.try_get_client();
let owned_workspace_id = workspace_id.to_owned();
let workspace_id = workspace_id.to_owned();
let owned_workspace_name = new_workspace_name.map(|s| s.to_owned());
let owned_workspace_icon = new_workspace_icon.map(|s| s.to_owned());
let workspace_id: Uuid = owned_workspace_id
.parse()
.map_err(|_| ErrorCode::InvalidParams)?;
let client = try_get_client?;
client
.patch_workspace(PatchWorkspaceParam {
@ -244,18 +241,17 @@ where
Ok(())
}
async fn delete_workspace(&self, workspace_id: &str) -> Result<(), FlowyError> {
async fn delete_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
let try_get_client = self.server.try_get_client();
let workspace_id_owned = workspace_id.to_owned();
let client = try_get_client?;
client.delete_workspace(&workspace_id_owned).await?;
client.delete_workspace(workspace_id).await?;
Ok(())
}
async fn invite_workspace_member(
&self,
invitee_email: String,
workspace_id: String,
workspace_id: Uuid,
role: Role,
) -> Result<(), FlowyError> {
let try_get_client = self.server.try_get_client();
@ -300,11 +296,11 @@ where
async fn remove_workspace_member(
&self,
user_email: String,
workspace_id: String,
workspace_id: Uuid,
) -> Result<(), FlowyError> {
let try_get_client = self.server.try_get_client();
try_get_client?
.remove_workspace_members(workspace_id, vec![user_email])
.remove_workspace_members(&workspace_id, vec![user_email])
.await?;
Ok(())
}
@ -312,20 +308,20 @@ where
async fn update_workspace_member(
&self,
user_email: String,
workspace_id: String,
workspace_id: Uuid,
role: Role,
) -> Result<(), FlowyError> {
let try_get_client = self.server.try_get_client();
let changeset = WorkspaceMemberChangeset::new(user_email).with_role(to_af_role(role));
try_get_client?
.update_workspace_member(workspace_id, changeset)
.update_workspace_member(&workspace_id, changeset)
.await?;
Ok(())
}
async fn get_workspace_members(
&self,
workspace_id: String,
workspace_id: Uuid,
) -> Result<Vec<WorkspaceMember>, FlowyError> {
let try_get_client = self.server.try_get_client();
let members = try_get_client?
@ -339,15 +335,12 @@ where
async fn get_workspace_member(
&self,
workspace_id: String,
workspace_id: Uuid,
uid: i64,
) -> Result<WorkspaceMember, FlowyError> {
let try_get_client = self.server.try_get_client();
let client = try_get_client?;
let query = QueryWorkspaceMember {
workspace_id: workspace_id.clone(),
uid,
};
let query = QueryWorkspaceMember { workspace_id, uid };
let member = client.get_workspace_member(query).await?;
Ok(from_af_workspace_member(member))
}
@ -355,17 +348,15 @@ where
#[instrument(level = "debug", skip_all)]
async fn get_user_awareness_doc_state(
&self,
_uid: i64,
workspace_id: &str,
object_id: &str,
uid: i64,
workspace_id: &Uuid,
object_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
let workspace_id = workspace_id.to_string();
let object_id = object_id.to_string();
let try_get_client = self.server.try_get_client();
let cloned_user = self.user.clone();
let params = QueryCollabParams {
workspace_id: workspace_id.clone(),
inner: QueryCollab::new(object_id, CollabType::UserAwareness),
inner: QueryCollab::new(object_id.clone(), CollabType::UserAwareness),
};
let resp = try_get_client?.get_collab(params).await?;
check_request_workspace_id_is_match(&workspace_id, &cloned_user, "get user awareness object")?;
@ -389,9 +380,12 @@ where
let try_get_client = self.server.try_get_client();
let collab_object = collab_object.clone();
let client = try_get_client?;
let workspace_id = Uuid::from_str(&collab_object.workspace_id)?;
let object_id = Uuid::from_str(&collab_object.object_id)?;
let params = CreateCollabParams {
workspace_id: collab_object.workspace_id,
object_id: collab_object.object_id,
workspace_id,
object_id,
collab_type: collab_object.collab_type,
encoded_collab_v1: data,
};
@ -401,33 +395,35 @@ where
async fn batch_create_collab_object(
&self,
workspace_id: &str,
workspace_id: &Uuid,
objects: Vec<UserCollabParams>,
) -> Result<(), FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.server.try_get_client();
let params = objects
.into_iter()
.map(|object| {
CollabParams::new(
object.object_id,
u8::from(object.collab_type).into(),
object.encoded_collab,
)
.flat_map(|object| {
Uuid::from_str(&object.object_id)
.and_then(|object_id| {
Ok(CollabParams::new(
object_id,
u8::from(object.collab_type).into(),
object.encoded_collab,
))
})
.ok()
})
.collect::<Vec<_>>();
try_get_client?
.create_collab_list(&workspace_id, params)
.create_collab_list(workspace_id, params)
.await
.map_err(FlowyError::from)?;
Ok(())
}
async fn leave_workspace(&self, workspace_id: &str) -> Result<(), FlowyError> {
async fn leave_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
let try_get_client = self.server.try_get_client();
let workspace_id = workspace_id.to_string();
let client = try_get_client?;
client.leave_workspace(&workspace_id).await?;
client.leave_workspace(workspace_id).await?;
Ok(())
}
@ -454,14 +450,13 @@ where
async fn get_workspace_member_info(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: i64,
) -> Result<WorkspaceMember, FlowyError> {
let try_get_client = self.server.try_get_client();
let workspace_id = workspace_id.to_string();
let client = try_get_client?;
let params = QueryWorkspaceMember {
workspace_id: workspace_id.to_string(),
workspace_id: workspace_id.clone(),
uid,
};
let member = client.get_workspace_member(params).await?;
@ -518,12 +513,12 @@ where
async fn get_workspace_plan(
&self,
workspace_id: String,
workspace_id: Uuid,
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
let try_get_client = self.server.try_get_client();
let client = try_get_client?;
let plans = client
.get_active_workspace_subscriptions(&workspace_id)
.get_active_workspace_subscriptions(&workspace_id.to_string())
.await?;
Ok(plans)
}

View file

@ -2,21 +2,22 @@ use crate::af_cloud::define::ServerUser;
use flowy_error::{FlowyError, FlowyResult};
use std::sync::Arc;
use tracing::warn;
use uuid::Uuid;
/// Validates the workspace_id provided in the request.
/// It checks that the workspace_id from the request matches the current user's active workspace_id.
/// This ensures that the operation is being performed in the correct workspace context, enhancing security.
pub fn check_request_workspace_id_is_match(
expected_workspace_id: &str,
expected_workspace_id: &Uuid,
user: &Arc<dyn ServerUser>,
action: impl AsRef<str>,
) -> FlowyResult<()> {
let actual_workspace_id = user.workspace_id()?;
if expected_workspace_id != actual_workspace_id {
if expected_workspace_id != &actual_workspace_id {
warn!(
"{}, expect workspace_id: {}, actual workspace_id: {}",
action.as_ref(),
expected_workspace_id,
expected_workspace_id.to_string(),
actual_workspace_id
);

View file

@ -9,6 +9,7 @@ use lib_infra::async_trait::async_trait;
use serde_json::Value;
use std::collections::HashMap;
use std::path::Path;
use uuid::Uuid;
pub(crate) struct DefaultChatCloudServiceImpl;
@ -16,104 +17,104 @@ pub(crate) struct DefaultChatCloudServiceImpl;
impl ChatCloudService for DefaultChatCloudServiceImpl {
async fn create_chat(
&self,
_uid: &i64,
_workspace_id: &str,
_chat_id: &str,
_rag_ids: Vec<String>,
uid: &i64,
workspace_id: &Uuid,
chat_id: &Uuid,
rag_ids: Vec<Uuid>,
) -> Result<(), FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn create_question(
&self,
_workspace_id: &str,
_chat_id: &str,
_message: &str,
_message_type: ChatMessageType,
_metadata: &[ChatMessageMetadata],
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
message_type: ChatMessageType,
metadata: &[ChatMessageMetadata],
) -> Result<ChatMessage, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn create_answer(
&self,
_workspace_id: &str,
_chat_id: &str,
_message: &str,
_question_id: i64,
_metadata: Option<serde_json::Value>,
workspace_id: &Uuid,
chat_id: &Uuid,
message: &str,
question_id: i64,
metadata: Option<serde_json::Value>,
) -> Result<ChatMessage, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn stream_answer(
&self,
_workspace_id: &str,
_chat_id: &str,
_message_id: i64,
_format: ResponseFormat,
_ai_model: Option<AIModel>,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
format: ResponseFormat,
ai_model: Option<AIModel>,
) -> Result<StreamAnswer, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn get_chat_messages(
&self,
_workspace_id: &str,
_chat_id: &str,
_offset: MessageCursor,
_limit: u64,
workspace_id: &Uuid,
chat_id: &Uuid,
offset: MessageCursor,
limit: u64,
) -> Result<RepeatedChatMessage, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn get_question_from_answer_id(
&self,
_workspace_id: &str,
_chat_id: &str,
_answer_id: i64,
workspace_id: &Uuid,
chat_id: &Uuid,
answer_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn get_related_message(
&self,
_workspace_id: &str,
_chat_id: &str,
_message_id: i64,
workspace_id: &Uuid,
chat_id: &Uuid,
message_id: i64,
) -> Result<RepeatedRelatedQuestion, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn get_answer(
&self,
_workspace_id: &str,
_chat_id: &str,
_question_message_id: i64,
workspace_id: &Uuid,
chat_id: &Uuid,
question_message_id: i64,
) -> Result<ChatMessage, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn stream_complete(
&self,
_workspace_id: &str,
_params: CompleteTextParams,
_ai_model: Option<AIModel>,
workspace_id: &Uuid,
params: CompleteTextParams,
ai_model: Option<AIModel>,
) -> Result<StreamComplete, FlowyError> {
Err(FlowyError::not_support().with_context("complete text is not supported in local server."))
}
async fn embed_file(
&self,
_workspace_id: &str,
_file_path: &Path,
_chat_id: &str,
_metadata: Option<HashMap<String, Value>>,
workspace_id: &Uuid,
file_path: &Path,
chat_id: &Uuid,
metadata: Option<HashMap<String, Value>>,
) -> Result<(), FlowyError> {
Err(FlowyError::not_support().with_context("indexing file is not supported in local server."))
}
async fn get_local_ai_config(&self, _workspace_id: &str) -> Result<LocalAIConfig, FlowyError> {
async fn get_local_ai_config(&self, workspace_id: &Uuid) -> Result<LocalAIConfig, FlowyError> {
Err(
FlowyError::not_support()
.with_context("Get local ai config is not supported in local server."),
@ -122,7 +123,7 @@ impl ChatCloudService for DefaultChatCloudServiceImpl {
async fn get_workspace_plan(
&self,
_workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
Err(
FlowyError::not_support()
@ -132,26 +133,26 @@ impl ChatCloudService for DefaultChatCloudServiceImpl {
async fn get_chat_settings(
&self,
_workspace_id: &str,
_chat_id: &str,
workspace_id: &Uuid,
chat_id: &Uuid,
) -> Result<ChatSettings, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn update_chat_settings(
&self,
_workspace_id: &str,
_chat_id: &str,
_params: UpdateChatParams,
workspace_id: &Uuid,
chat_id: &Uuid,
params: UpdateChatParams,
) -> Result<(), FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn get_available_models(&self, _workspace_id: &str) -> Result<ModelList, FlowyError> {
async fn get_available_models(&self, workspace_id: &Uuid) -> Result<ModelList, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
async fn get_workspace_default_model(&self, _workspace_id: &str) -> Result<String, FlowyError> {
async fn get_workspace_default_model(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
}
}

View file

@ -7,6 +7,7 @@ use collab_user::core::default_user_awareness_data;
use flowy_database_pub::cloud::{DatabaseCloudService, DatabaseSnapshot, EncodeCollabByOid};
use flowy_error::FlowyError;
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
pub(crate) struct LocalServerDatabaseCloudServiceImpl();
@ -14,50 +15,51 @@ pub(crate) struct LocalServerDatabaseCloudServiceImpl();
impl DatabaseCloudService for LocalServerDatabaseCloudServiceImpl {
async fn get_database_encode_collab(
&self,
object_id: &str,
object_id: &Uuid,
collab_type: CollabType,
_workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Option<EncodedCollab>, FlowyError> {
let object_id = object_id.to_string();
match collab_type {
CollabType::Document => {
let encode_collab = default_document_collab_data(object_id)?;
let encode_collab = default_document_collab_data(&object_id)?;
Ok(Some(encode_collab))
},
CollabType::Database => default_database_data(object_id)
CollabType::Database => default_database_data(&object_id)
.await
.map(Some)
.map_err(Into::into),
CollabType::WorkspaceDatabase => Ok(Some(default_workspace_database_data(object_id))),
CollabType::WorkspaceDatabase => Ok(Some(default_workspace_database_data(&object_id))),
CollabType::Folder => Ok(None),
CollabType::DatabaseRow => Ok(None),
CollabType::UserAwareness => Ok(Some(default_user_awareness_data(object_id))),
CollabType::UserAwareness => Ok(Some(default_user_awareness_data(&object_id))),
CollabType::Unknown => Ok(None),
}
}
async fn create_database_encode_collab(
&self,
_object_id: &str,
_collab_type: CollabType,
_workspace_id: &str,
_encoded_collab: EncodedCollab,
object_id: &Uuid,
collab_type: CollabType,
workspace_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError> {
Ok(())
}
async fn batch_get_database_encode_collab(
&self,
_object_ids: Vec<String>,
_object_ty: CollabType,
_workspace_id: &str,
object_ids: Vec<Uuid>,
object_ty: CollabType,
workspace_id: &Uuid,
) -> Result<EncodeCollabByOid, FlowyError> {
Ok(EncodeCollabByOid::default())
}
async fn get_database_collab_object_snapshots(
&self,
_object_id: &str,
_limit: usize,
object_id: &Uuid,
limit: usize,
) -> Result<Vec<DatabaseSnapshot>, FlowyError> {
Ok(vec![])
}

View file

@ -2,6 +2,7 @@ use collab::entity::EncodedCollab;
use flowy_document_pub::cloud::*;
use flowy_error::{ErrorCode, FlowyError};
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
pub(crate) struct LocalServerDocumentCloudServiceImpl();
@ -9,8 +10,8 @@ pub(crate) struct LocalServerDocumentCloudServiceImpl();
impl DocumentCloudService for LocalServerDocumentCloudServiceImpl {
async fn get_document_doc_state(
&self,
document_id: &str,
_workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
let document_id = document_id.to_string();
@ -22,26 +23,26 @@ impl DocumentCloudService for LocalServerDocumentCloudServiceImpl {
async fn get_document_snapshots(
&self,
_document_id: &str,
_limit: usize,
_workspace_id: &str,
document_id: &Uuid,
limit: usize,
workspace_id: &str,
) -> Result<Vec<DocumentSnapshot>, FlowyError> {
Ok(vec![])
}
async fn get_document_data(
&self,
_document_id: &str,
_workspace_id: &str,
document_id: &Uuid,
workspace_id: &Uuid,
) -> Result<Option<DocumentData>, FlowyError> {
Ok(None)
}
async fn create_document_collab(
&self,
_workspace_id: &str,
_document_id: &str,
_encoded_collab: EncodedCollab,
workspace_id: &Uuid,
document_id: &Uuid,
encoded_collab: EncodedCollab,
) -> Result<(), FlowyError> {
Ok(())
}

View file

@ -1,10 +1,9 @@
use std::sync::Arc;
use crate::local_server::LocalServerDB;
use client_api::entity::workspace_dto::PublishInfoView;
use client_api::entity::PublishInfo;
use collab_entity::CollabType;
use crate::local_server::LocalServerDB;
use flowy_error::FlowyError;
use flowy_folder_pub::cloud::{
gen_workspace_id, FolderCloudService, FolderCollabParams, FolderData, FolderSnapshot,
@ -12,6 +11,7 @@ use flowy_folder_pub::cloud::{
};
use flowy_folder_pub::entities::PublishPayload;
use lib_infra::async_trait::async_trait;
use uuid::Uuid;
pub(crate) struct LocalServerFolderCloudServiceImpl {
#[allow(dead_code)]
@ -29,7 +29,7 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
))
}
async fn open_workspace(&self, _workspace_id: &str) -> Result<(), FlowyError> {
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
Ok(())
}
@ -39,8 +39,8 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
async fn get_folder_data(
&self,
_workspace_id: &str,
_uid: &i64,
workspace_id: &Uuid,
uid: &i64,
) -> Result<Option<FolderData>, FlowyError> {
Ok(None)
}
@ -55,18 +55,18 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
async fn get_folder_doc_state(
&self,
_workspace_id: &str,
_uid: i64,
_collab_type: CollabType,
_object_id: &str,
workspace_id: &Uuid,
uid: i64,
collab_type: CollabType,
object_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn batch_create_folder_collab_objects(
&self,
_workspace_id: &str,
_objects: Vec<FolderCollabParams>,
workspace_id: &Uuid,
objects: Vec<FolderCollabParams>,
) -> Result<(), FlowyError> {
Ok(())
}
@ -77,68 +77,68 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
async fn publish_view(
&self,
_workspace_id: &str,
_payload: Vec<PublishPayload>,
workspace_id: &Uuid,
payload: Vec<PublishPayload>,
) -> Result<(), FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn unpublish_views(
&self,
_workspace_id: &str,
_view_ids: Vec<String>,
workspace_id: &Uuid,
view_ids: Vec<Uuid>,
) -> Result<(), FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn get_publish_info(&self, _view_id: &str) -> Result<PublishInfo, FlowyError> {
async fn get_publish_info(&self, view_id: &Uuid) -> Result<PublishInfo, FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn set_publish_namespace(
&self,
_workspace_id: &str,
_new_namespace: String,
workspace_id: &Uuid,
new_namespace: String,
) -> Result<(), FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn get_publish_namespace(&self, _workspace_id: &str) -> Result<String, FlowyError> {
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn set_publish_name(
&self,
_workspace_id: &str,
_view_id: String,
_new_name: String,
workspace_id: &Uuid,
view_id: Uuid,
new_name: String,
) -> Result<(), FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn list_published_views(
&self,
_workspace_id: &str,
workspace_id: &Uuid,
) -> Result<Vec<PublishInfoView>, FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn get_default_published_view_info(
&self,
_workspace_id: &str,
workspace_id: &Uuid,
) -> Result<PublishInfo, FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn set_default_published_view(
&self,
_workspace_id: &str,
_view_id: uuid::Uuid,
workspace_id: &Uuid,
view_id: uuid::Uuid,
) -> Result<(), FlowyError> {
Err(FlowyError::local_version_not_support())
}
async fn remove_default_published_view(&self, _workspace_id: &str) -> Result<(), FlowyError> {
async fn remove_default_published_view(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
Err(FlowyError::local_version_not_support())
}
@ -148,8 +148,8 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
async fn full_sync_collab_object(
&self,
_workspace_id: &str,
_params: FullSyncCollabParams,
workspace_id: &Uuid,
params: FullSyncCollabParams,
) -> Result<(), FlowyError> {
Ok(())
}

View file

@ -134,7 +134,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
}
}
async fn open_workspace(&self, _workspace_id: &str) -> Result<UserWorkspace, FlowyError> {
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<UserWorkspace, FlowyError> {
Err(
FlowyError::local_version_not_support()
.with_context("local server doesn't support open workspace"),
@ -147,11 +147,16 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
async fn get_user_awareness_doc_state(
&self,
_uid: i64,
_workspace_id: &str,
object_id: &str,
uid: i64,
workspace_id: &Uuid,
object_id: &Uuid,
) -> Result<Vec<u8>, FlowyError> {
let collab = Collab::new_with_origin(CollabOrigin::Empty, object_id, vec![], false);
let collab = Collab::new_with_origin(
CollabOrigin::Empty,
object_id.to_string().as_str(),
vec![],
false,
);
let awareness = UserAwareness::create(collab, None)?;
let encode_collab = awareness.encode_collab_v1(|_collab| Ok::<_, FlowyError>(()))?;
Ok(encode_collab.doc_state.to_vec())
@ -171,8 +176,8 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
async fn batch_create_collab_object(
&self,
_workspace_id: &str,
_objects: Vec<UserCollabParams>,
workspace_id: &Uuid,
objects: Vec<UserCollabParams>,
) -> Result<(), FlowyError> {
Err(
FlowyError::local_version_not_support()
@ -187,7 +192,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
)
}
async fn delete_workspace(&self, _workspace_id: &str) -> Result<(), FlowyError> {
async fn delete_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
Err(
FlowyError::local_version_not_support()
.with_context("local server doesn't support multiple workspaces"),
@ -196,9 +201,9 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
async fn patch_workspace(
&self,
_workspace_id: &str,
_new_workspace_name: Option<&str>,
_new_workspace_icon: Option<&str>,
workspace_id: &Uuid,
new_workspace_name: Option<&str>,
new_workspace_icon: Option<&str>,
) -> Result<(), FlowyError> {
Err(
FlowyError::local_version_not_support()

View file

@ -39,7 +39,7 @@ pub fn af_cloud_server(config: AFCloudConfiguration) -> Arc<AppFlowyCloudServer>
struct FakeServerUserImpl;
impl ServerUser for FakeServerUserImpl {
fn workspace_id(&self) -> FlowyResult<String> {
fn workspace_id(&self) -> FlowyResult<Uuid> {
todo!()
}
}

View file

@ -7,7 +7,6 @@ edition = "2021"
[dependencies]
lib-infra.workspace = true
serde_json.workspace = true
serde.workspace = true
async-trait.workspace = true
mime = "0.3.17"
@ -17,4 +16,4 @@ mime_guess = "2.0.4"
client-api-entity = { workspace = true }
tokio = { workspace = true, features = ["sync", "io-util"] }
anyhow = "1.0.86"
tracing.workspace = true
uuid.workspace = true

View file

@ -3,6 +3,7 @@ use async_trait::async_trait;
use bytes::Bytes;
use flowy_error::{FlowyError, FlowyResult};
use mime::Mime;
use uuid::Uuid;
#[async_trait]
pub trait StorageCloudService: Send + Sync {
@ -47,17 +48,17 @@ pub trait StorageCloudService: Send + Sync {
async fn get_object(&self, url: String) -> Result<ObjectValue, FlowyError>;
async fn get_object_url_v1(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
file_id: &str,
) -> FlowyResult<String>;
/// Return workspace_id, parent_dir, file_id
async fn parse_object_url_v1(&self, url: &str) -> Option<(String, String, String)>;
async fn parse_object_url_v1(&self, url: &str) -> Option<(Uuid, String, String)>;
async fn create_upload(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
file_id: &str,
content_type: &str,
@ -66,7 +67,7 @@ pub trait StorageCloudService: Send + Sync {
async fn upload_part(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
upload_id: &str,
file_id: &str,
@ -76,7 +77,7 @@ pub trait StorageCloudService: Send + Sync {
async fn complete_upload(
&self,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
upload_id: &str,
file_id: &str,
@ -85,7 +86,7 @@ pub trait StorageCloudService: Send + Sync {
}
pub struct ObjectIdentity {
pub workspace_id: String,
pub workspace_id: Uuid,
pub file_id: String,
pub ext: String,
}
@ -97,7 +98,7 @@ pub struct ObjectValue {
}
pub struct StorageObject {
pub workspace_id: String,
pub workspace_id: Uuid,
pub file_name: String,
pub value: ObjectValueSupabase,
}
@ -126,9 +127,9 @@ impl StorageObject {
/// * `name`: The name of the storage object.
/// * `file_path`: The file path to the storage object's data.
///
pub fn from_file<T: ToString>(workspace_id: &str, file_name: &str, file_path: T) -> Self {
pub fn from_file<T: ToString>(workspace_id: &Uuid, file_name: &str, file_path: T) -> Self {
Self {
workspace_id: workspace_id.to_string(),
workspace_id: *workspace_id,
file_name: file_name.to_string(),
value: ObjectValueSupabase::File {
file_path: file_path.to_string(),
@ -145,14 +146,14 @@ impl StorageObject {
/// * `mime`: The MIME type of the storage object.
///
pub fn from_bytes<B: Into<Bytes>>(
workspace_id: &str,
workspace_id: &Uuid,
file_name: &str,
bytes: B,
mime: String,
) -> Self {
let bytes = bytes.into();
Self {
workspace_id: workspace_id.to_string(),
workspace_id: *workspace_id,
file_name: file_name.to_string(),
value: ObjectValueSupabase::Bytes { bytes, mime },
}

View file

@ -17,8 +17,6 @@ tokio = { workspace = true, features = ["sync", "io-util"] }
tracing.workspace = true
flowy-sqlite.workspace = true
mime_guess = "2.0.4"
fxhash = "0.2.1"
anyhow = "1.0.86"
chrono = "0.4.33"
flowy-notification = { workspace = true }
flowy-derive.workspace = true
@ -26,8 +24,8 @@ protobuf = { workspace = true }
dashmap.workspace = true
strum_macros = "0.25.2"
allo-isolate = { version = "^0.1", features = ["catch-unwind"] }
futures-util = "0.3.30"
collab-importer = { workspace = true }
uuid.workspace = true
[dev-dependencies]
tokio = { workspace = true, features = ["full"] }

View file

@ -24,15 +24,17 @@ use lib_infra::box_any::BoxAny;
use lib_infra::isolate_stream::{IsolateSink, SinkExt};
use lib_infra::util::timestamp;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use tokio::io::AsyncWriteExt;
use tokio::sync::{broadcast, watch};
use tracing::{debug, error, info, instrument, trace};
use uuid::Uuid;
pub trait StorageUserService: Send + Sync + 'static {
fn user_id(&self) -> Result<i64, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn workspace_id(&self) -> Result<Uuid, FlowyError>;
fn sqlite_connection(&self, uid: i64) -> Result<DBConnection, FlowyError>;
fn get_application_root_dir(&self) -> &str;
}
@ -157,7 +159,8 @@ impl StorageManager {
let uid = self.user_service.user_id().ok()?;
let mut conn = self.user_service.sqlite_connection(uid).ok()?;
let is_finish = is_upload_completed(&mut conn, &workspace_id, &parent_dir, &file_id).ok()?;
let is_finish =
is_upload_completed(&mut conn, &workspace_id.to_string(), &parent_dir, &file_id).ok()?;
if let Err(err) = self.global_notifier.send(FileProgress::new_progress(
url.to_string(),
@ -229,7 +232,7 @@ async fn prepare_upload_task(
if let Ok(uid) = user_service.user_id() {
let workspace_id = user_service.workspace_id()?;
let conn = user_service.sqlite_connection(uid)?;
let upload_files = batch_select_upload_file(conn, &workspace_id, 100, false)?;
let upload_files = batch_select_upload_file(conn, &workspace_id.to_string(), 100, false)?;
let tasks = upload_files
.into_iter()
.map(|upload_file| UploadTask::BackgroundTask {
@ -269,7 +272,7 @@ impl StorageService for StorageServiceImpl {
self
.task_queue
.remove_task(&workspace_id, &parent_dir, &file_id)
.remove_task(&workspace_id.to_string(), &parent_dir, &file_id)
.await;
trace!("[File] delete progress notifier: {}", file_id);
@ -278,7 +281,7 @@ impl StorageService for StorageServiceImpl {
self
.user_service
.sqlite_connection(self.user_service.user_id()?)?,
&workspace_id,
&workspace_id.to_string(),
&parent_dir,
&file_id,
) {
@ -384,9 +387,10 @@ impl StorageService for StorageServiceImpl {
let conn = self
.user_service
.sqlite_connection(self.user_service.user_id()?)?;
let workspace_id = Uuid::from_str(&record.workspace_id)?;
let url = self
.cloud_service
.get_object_url_v1(&record.workspace_id, &record.parent_dir, &record.file_id)
.get_object_url_v1(&workspace_id, &record.parent_dir, &record.file_id)
.await?;
let file_id = record.file_id.clone();
match insert_upload_file(conn, &record) {
@ -478,7 +482,8 @@ impl StorageService for StorageServiceImpl {
.user_service
.sqlite_connection(self.user_service.user_id()?)?;
let workspace_id = self.user_service.workspace_id()?;
is_upload_completed(&mut conn, &workspace_id, parent_idr, file_id).unwrap_or(false)
is_upload_completed(&mut conn, &workspace_id.to_string(), parent_idr, file_id)
.unwrap_or(false)
};
if is_completed {
@ -590,9 +595,10 @@ async fn start_upload(
upload_file.file_id
);
let workspace_id = Uuid::from_str(&upload_file.workspace_id)?;
let create_upload_resp_result = cloud_service
.create_upload(
&upload_file.workspace_id,
&workspace_id,
&upload_file.parent_dir,
&upload_file.file_id,
&upload_file.content_type,
@ -601,11 +607,7 @@ async fn start_upload(
.await;
let file_url = cloud_service
.get_object_url_v1(
&upload_file.workspace_id,
&upload_file.parent_dir,
&upload_file.file_id,
)
.get_object_url_v1(&workspace_id, &upload_file.parent_dir, &upload_file.file_id)
.await?;
if let Err(err) = create_upload_resp_result.as_ref() {
@ -653,7 +655,7 @@ async fn start_upload(
match upload_part(
cloud_service,
user_service,
&upload_file.workspace_id,
&workspace_id,
&upload_file.parent_dir,
&upload_file.upload_id,
&upload_file.file_id,
@ -782,7 +784,7 @@ async fn resume_upload(
async fn upload_part(
cloud_service: &Arc<dyn StorageCloudService>,
user_service: &Arc<dyn StorageUserService>,
workspace_id: &str,
workspace_id: &Uuid,
parent_dir: &str,
upload_id: &str,
file_id: &str,
@ -822,12 +824,9 @@ async fn complete_upload(
parts: Vec<CompletedPartRequest>,
global_notifier: &GlobalNotifier,
) -> Result<(), FlowyError> {
let workspace_id = Uuid::from_str(&upload_file.workspace_id)?;
let file_url = cloud_service
.get_object_url_v1(
&upload_file.workspace_id,
&upload_file.parent_dir,
&upload_file.file_id,
)
.get_object_url_v1(&workspace_id, &upload_file.parent_dir, &upload_file.file_id)
.await?;
info!(
@ -838,7 +837,7 @@ async fn complete_upload(
);
match cloud_service
.complete_upload(
&upload_file.workspace_id,
&workspace_id,
&upload_file.parent_dir,
&upload_file.upload_id,
&upload_file.file_id,

View file

@ -171,7 +171,7 @@ pub trait UserCloudService: Send + Sync + 'static {
/// return None if the user is not found
async fn get_user_profile(&self, credential: UserCredentials) -> Result<UserProfile, FlowyError>;
async fn open_workspace(&self, workspace_id: &str) -> Result<UserWorkspace, FlowyError>;
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<UserWorkspace, FlowyError>;
/// Return the all the workspaces of the user
async fn get_all_workspace(&self, uid: i64) -> Result<Vec<UserWorkspace>, FlowyError>;
@ -183,18 +183,18 @@ pub trait UserCloudService: Send + Sync + 'static {
// Updates the workspace name and icon
async fn patch_workspace(
&self,
workspace_id: &str,
workspace_id: &Uuid,
new_workspace_name: Option<&str>,
new_workspace_icon: Option<&str>,
) -> Result<(), FlowyError>;
/// Deletes a workspace owned by the user.
async fn delete_workspace(&self, workspace_id: &str) -> Result<(), FlowyError>;
async fn delete_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError>;
async fn invite_workspace_member(
&self,
invitee_email: String,
workspace_id: String,
workspace_id: Uuid,
role: Role,
) -> Result<(), FlowyError> {
Ok(())
@ -214,7 +214,7 @@ pub trait UserCloudService: Send + Sync + 'static {
async fn remove_workspace_member(
&self,
user_email: String,
workspace_id: String,
workspace_id: Uuid,
) -> Result<(), FlowyError> {
Ok(())
}
@ -222,7 +222,7 @@ pub trait UserCloudService: Send + Sync + 'static {
async fn update_workspace_member(
&self,
user_email: String,
workspace_id: String,
workspace_id: Uuid,
role: Role,
) -> Result<(), FlowyError> {
Ok(())
@ -230,14 +230,14 @@ pub trait UserCloudService: Send + Sync + 'static {
async fn get_workspace_members(
&self,
workspace_id: String,
workspace_id: Uuid,
) -> Result<Vec<WorkspaceMember>, FlowyError> {
Ok(vec![])
}
async fn get_workspace_member(
&self,
workspace_id: String,
workspace_id: Uuid,
uid: i64,
) -> Result<WorkspaceMember, FlowyError> {
Err(FlowyError::not_support())
@ -246,8 +246,8 @@ pub trait UserCloudService: Send + Sync + 'static {
async fn get_user_awareness_doc_state(
&self,
uid: i64,
workspace_id: &str,
object_id: &str,
workspace_id: &Uuid,
object_id: &Uuid,
) -> Result<Vec<u8>, FlowyError>;
fn receive_realtime_event(&self, _json: Value) {}
@ -266,11 +266,11 @@ pub trait UserCloudService: Send + Sync + 'static {
async fn batch_create_collab_object(
&self,
workspace_id: &str,
workspace_id: &Uuid,
objects: Vec<UserCollabParams>,
) -> Result<(), FlowyError>;
async fn leave_workspace(&self, workspace_id: &str) -> Result<(), FlowyError> {
async fn leave_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
Ok(())
}
@ -286,7 +286,7 @@ pub trait UserCloudService: Send + Sync + 'static {
async fn get_workspace_member_info(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: i64,
) -> Result<WorkspaceMember, FlowyError> {
Err(FlowyError::not_support())
@ -318,7 +318,7 @@ pub trait UserCloudService: Send + Sync + 'static {
async fn get_workspace_plan(
&self,
workspace_id: String,
workspace_id: Uuid,
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
Err(FlowyError::not_support())
}

View file

@ -3,6 +3,7 @@ use std::str::FromStr;
use chrono::{DateTime, Utc};
pub use client_api::entity::billing_dto::RecurringInterval;
use client_api::entity::AFRole;
use flowy_error::FlowyResult;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_repr::*;
@ -151,6 +152,11 @@ pub struct UserWorkspace {
}
impl UserWorkspace {
pub fn workspace_id(&self) -> FlowyResult<Uuid> {
let id = Uuid::from_str(&self.id)?;
Ok(id)
}
pub fn new_local(workspace_id: &str, _uid: i64) -> Self {
Self {
id: workspace_id.to_string(),

View file

@ -3,6 +3,7 @@ use flowy_error::FlowyResult;
use flowy_folder_pub::entities::ImportFrom;
use lib_infra::async_trait::async_trait;
use std::collections::HashMap;
use uuid::Uuid;
#[async_trait]
pub trait UserWorkspaceService: Send + Sync {
@ -19,5 +20,5 @@ pub trait UserWorkspaceService: Send + Sync {
) -> FlowyResult<()>;
/// Removes local indexes when a workspace is left/deleted
fn did_delete_workspace(&self, workspace_id: String) -> FlowyResult<()>;
fn did_delete_workspace(&self, workspace_id: &Uuid) -> FlowyResult<()>;
}

View file

@ -5,9 +5,11 @@ use flowy_user_pub::entities::*;
use lib_dispatch::prelude::*;
use lib_infra::box_any::BoxAny;
use serde_json::Value;
use std::str::FromStr;
use std::sync::Weak;
use std::{convert::TryInto, sync::Arc};
use tracing::{event, trace};
use uuid::Uuid;
use crate::entities::*;
use crate::notification::{send_notification, UserNotification};
@ -515,7 +517,8 @@ pub async fn open_workspace_handler(
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.try_into_inner()?;
manager.open_workspace(&params.workspace_id).await?;
let workspace_id = Uuid::from_str(&params.workspace_id)?;
manager.open_workspace(&workspace_id).await?;
Ok(())
}
@ -645,8 +648,9 @@ pub async fn delete_workspace_member_handler(
) -> Result<(), FlowyError> {
let data = data.try_into_inner()?;
let manager = upgrade_manager(manager)?;
let workspace_id = Uuid::from_str(&data.workspace_id)?;
manager
.remove_workspace_member(data.email, data.workspace_id)
.remove_workspace_member(data.email, workspace_id)
.await?;
Ok(())
}
@ -658,8 +662,9 @@ pub async fn get_workspace_members_handler(
) -> DataResult<RepeatedWorkspaceMemberPB, FlowyError> {
let data = data.try_into_inner()?;
let manager = upgrade_manager(manager)?;
let workspace_id = Uuid::from_str(&data.workspace_id)?;
let members = manager
.get_workspace_members(data.workspace_id)
.get_workspace_members(workspace_id)
.await?
.into_iter()
.map(WorkspaceMemberPB::from)
@ -674,8 +679,9 @@ pub async fn update_workspace_member_handler(
) -> Result<(), FlowyError> {
let data = data.try_into_inner()?;
let manager = upgrade_manager(manager)?;
let workspace_id = Uuid::from_str(&data.workspace_id)?;
manager
.update_workspace_member(data.email, data.workspace_id, data.role.into())
.update_workspace_member(data.email, workspace_id, data.role.into())
.await?;
Ok(())
}
@ -698,6 +704,7 @@ pub async fn delete_workspace_handler(
) -> Result<(), FlowyError> {
let workspace_id = delete_workspace_param.try_into_inner()?.workspace_id;
let manager = upgrade_manager(manager)?;
let workspace_id = Uuid::from_str(&workspace_id)?;
manager.delete_workspace(&workspace_id).await?;
Ok(())
}
@ -709,8 +716,9 @@ pub async fn rename_workspace_handler(
) -> Result<(), FlowyError> {
let params = rename_workspace_param.try_into_inner()?;
let manager = upgrade_manager(manager)?;
let workspace_id = Uuid::from_str(&params.workspace_id)?;
manager
.patch_workspace(&params.workspace_id, Some(&params.new_name), None)
.patch_workspace(&workspace_id, Some(&params.new_name), None)
.await?;
Ok(())
}
@ -722,8 +730,9 @@ pub async fn change_workspace_icon_handler(
) -> Result<(), FlowyError> {
let params = change_workspace_icon_param.try_into_inner()?;
let manager = upgrade_manager(manager)?;
let workspace_id = Uuid::from_str(&params.workspace_id)?;
manager
.patch_workspace(&params.workspace_id, None, Some(&params.new_icon))
.patch_workspace(&workspace_id, None, Some(&params.new_icon))
.await?;
Ok(())
}
@ -735,8 +744,9 @@ pub async fn invite_workspace_member_handler(
) -> Result<(), FlowyError> {
let param = param.try_into_inner()?;
let manager = upgrade_manager(manager)?;
let workspace_id = Uuid::from_str(&param.workspace_id)?;
manager
.invite_member_to_workspace(param.workspace_id, param.invitee_email, param.role.into())
.invite_member_to_workspace(workspace_id, param.invitee_email, param.role.into())
.await?;
Ok(())
}
@ -772,6 +782,7 @@ pub async fn leave_workspace_handler(
manager: AFPluginState<Weak<UserManager>>,
) -> Result<(), FlowyError> {
let workspace_id = param.into_inner().workspace_id;
let workspace_id = Uuid::from_str(&workspace_id)?;
let manager = upgrade_manager(manager)?;
manager.leave_workspace(&workspace_id).await?;
Ok(())

View file

@ -13,8 +13,10 @@ use flowy_sqlite::DBConnection;
use flowy_user_pub::entities::UserWorkspace;
use flowy_user_pub::session::Session;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Weak};
use tracing::{error, info};
use uuid::Uuid;
const SQLITE_VACUUM_042: &str = "sqlite_vacuum_042_version";
@ -68,14 +70,16 @@ impl AuthenticateUser {
Ok(self.user_config.device_id.to_string())
}
pub fn workspace_id(&self) -> FlowyResult<String> {
pub fn workspace_id(&self) -> FlowyResult<Uuid> {
let session = self.get_session()?;
Ok(session.user_workspace.id.clone())
let workspace_uuid = Uuid::from_str(&session.user_workspace.id)?;
Ok(workspace_uuid)
}
pub fn workspace_database_object_id(&self) -> FlowyResult<String> {
pub fn workspace_database_object_id(&self) -> FlowyResult<Uuid> {
let session = self.get_session()?;
Ok(session.user_workspace.workspace_database_id.clone())
let id = Uuid::from_str(&session.user_workspace.workspace_database_id)?;
Ok(id)
}
pub fn get_collab_db(&self, uid: i64) -> FlowyResult<Weak<CollabKVDB>> {

View file

@ -4,6 +4,7 @@ use flowy_error::{FlowyError, FlowyResult};
use flowy_user_pub::cloud::UserCloudServiceProvider;
use std::sync::Weak;
use std::time::Duration;
use uuid::Uuid;
/// `PeriodicallyCheckBillingState` is designed to periodically verify the subscription
/// plan of a given workspace. It utilizes a cloud service provider to fetch the current
@ -13,7 +14,7 @@ use std::time::Duration;
/// at specified intervals until the expected plan is found or the maximum number of
/// attempts is reached.
pub struct PeriodicallyCheckBillingState {
workspace_id: String,
workspace_id: Uuid,
cloud_service: Weak<dyn UserCloudServiceProvider>,
expected_plan: Option<SubscriptionPlan>,
user: Weak<AuthenticateUser>,
@ -21,7 +22,7 @@ pub struct PeriodicallyCheckBillingState {
impl PeriodicallyCheckBillingState {
pub fn new(
workspace_id: String,
workspace_id: Uuid,
expected_plan: Option<SubscriptionPlan>,
cloud_service: Weak<dyn UserCloudServiceProvider>,
user: Weak<AuthenticateUser>,

View file

@ -43,6 +43,8 @@ use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::sync::{Arc, Weak};
use tracing::{error, event, info, instrument, warn};
use uuid::Uuid;
pub(crate) struct ImportedFolder {
pub imported_session: Session,
pub imported_collab_db: Arc<CollabKVDB>,
@ -1172,7 +1174,7 @@ impl DerefMut for OldToNewIdMap {
pub async fn upload_collab_objects_data(
uid: i64,
user_collab_db: Weak<CollabKVDB>,
workspace_id: &str,
workspace_id: &Uuid,
user_authenticator: &Authenticator,
collab_data: ImportedCollabData,
user_cloud_service: Arc<dyn UserCloudService>,
@ -1275,7 +1277,7 @@ pub async fn upload_collab_objects_data(
async fn batch_create(
uid: i64,
workspace_id: &str,
workspace_id: &Uuid,
user_cloud_service: &Arc<dyn UserCloudService>,
size_counter: &usize,
objects: Vec<UserCollabParams>,

View file

@ -14,6 +14,7 @@ use flowy_sqlite::{query_dsl::*, DBConnection, ExpressionMethods};
use flowy_user_pub::cloud::{UserCloudServiceProvider, UserUpdate};
use flowy_user_pub::entities::*;
use flowy_user_pub::workspace_service::UserWorkspaceService;
use lib_infra::box_any::BoxAny;
use semver::Version;
use serde_json::Value;
use std::string::ToString;
@ -22,8 +23,7 @@ use std::sync::{Arc, Weak};
use tokio::sync::Mutex;
use tokio_stream::StreamExt;
use tracing::{debug, error, event, info, instrument, warn};
use lib_infra::box_any::BoxAny;
use uuid::Uuid;
use crate::entities::{AuthStateChangedPB, AuthStatePB, UserProfilePB, UserSettingPB};
use crate::event_map::{DefaultUserStatusCallback, UserStatusCallback};
@ -58,7 +58,7 @@ pub struct UserManager {
auth_process: Mutex<Option<UserAuthProcess>>,
pub(crate) authenticate_user: Arc<AuthenticateUser>,
refresh_user_profile_since: AtomicI64,
pub(crate) is_loading_awareness: Arc<DashMap<String, bool>>,
pub(crate) is_loading_awareness: Arc<DashMap<Uuid, bool>>,
}
impl UserManager {

View file

@ -8,13 +8,13 @@ use collab_entity::CollabType;
use collab_integrate::collab_builder::{
AppFlowyCollabBuilder, CollabBuilderConfig, CollabPersistenceImpl,
};
use collab_integrate::CollabKVDB;
use collab_user::core::{UserAwareness, UserAwarenessNotifier};
use dashmap::try_result::TryResult;
use tracing::{error, info, instrument, trace};
use collab_integrate::CollabKVDB;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_user_pub::entities::{user_awareness_object_id, Authenticator};
use tracing::{error, info, instrument, trace};
use uuid::Uuid;
use crate::entities::ReminderPB;
use crate::user_manager::UserManager;
@ -122,8 +122,7 @@ impl UserManager {
authenticator: &Authenticator,
) -> FlowyResult<()> {
let authenticator = authenticator.clone();
let object_id =
user_awareness_object_id(&session.user_uuid, &session.user_workspace.id).to_string();
let object_id = user_awareness_object_id(&session.user_uuid, &session.user_workspace.id);
// Try to acquire mutable access to `is_loading_awareness`.
// Thread-safety is ensured by DashMap
@ -156,7 +155,7 @@ impl UserManager {
let is_exist_on_disk = self
.authenticate_user
.is_collab_on_disk(session.user_id, &object_id)?;
.is_collab_on_disk(session.user_id, &object_id.to_string())?;
if authenticator.is_local() || is_exist_on_disk {
trace!(
"Initializing new user awareness from disk:{}, {:?}",
@ -164,15 +163,13 @@ impl UserManager {
authenticator
);
let collab_db = self.get_collab_db(session.user_id)?;
let doc_state = CollabPersistenceImpl::new(
collab_db.clone(),
session.user_id,
session.user_workspace.id.clone(),
)
.into_data_source();
let workspace_id = session.user_workspace.workspace_id()?;
let doc_state =
CollabPersistenceImpl::new(collab_db.clone(), session.user_id, workspace_id)
.into_data_source();
let awareness = Self::collab_for_user_awareness(
&self.collab_builder.clone(),
&session.user_workspace.id,
&workspace_id,
session.user_id,
&object_id,
collab_db,
@ -211,7 +208,7 @@ impl UserManager {
fn load_awareness_from_server(
&self,
session: &Session,
object_id: String,
object_id: Uuid,
authenticator: Authenticator,
) -> FlowyResult<()> {
// Clone necessary data
@ -231,16 +228,14 @@ impl UserManager {
}
};
let workspace_id = session.user_workspace.workspace_id()?;
let create_awareness = if authenticator.is_local() {
let doc_state = CollabPersistenceImpl::new(
collab_db.clone(),
session.user_id,
session.user_workspace.id.clone(),
)
.into_data_source();
let doc_state =
CollabPersistenceImpl::new(collab_db.clone(), session.user_id, workspace_id.clone())
.into_data_source();
Self::collab_for_user_awareness(
&weak_builder,
&session.user_workspace.id,
&workspace_id,
session.user_id,
&object_id,
collab_db,
@ -251,7 +246,7 @@ impl UserManager {
} else {
let result = cloud_services
.get_user_service()?
.get_user_awareness_doc_state(session.user_id, &session.user_workspace.id, &object_id)
.get_user_awareness_doc_state(session.user_id, &workspace_id, &object_id)
.await;
match result {
@ -259,7 +254,7 @@ impl UserManager {
trace!("Fetched user awareness collab from remote: {}", data.len());
Self::collab_for_user_awareness(
&weak_builder,
&session.user_workspace.id,
&workspace_id,
session.user_id,
&object_id,
collab_db,
@ -274,12 +269,12 @@ impl UserManager {
let doc_state = CollabPersistenceImpl::new(
collab_db.clone(),
session.user_id,
session.user_workspace.id.clone(),
workspace_id.clone(),
)
.into_data_source();
Self::collab_for_user_awareness(
&weak_builder,
&session.user_workspace.id,
&workspace_id,
session.user_id,
&object_id,
collab_db,
@ -329,9 +324,9 @@ impl UserManager {
/// user awareness.
async fn collab_for_user_awareness(
collab_builder: &Weak<AppFlowyCollabBuilder>,
workspace_id: &str,
workspace_id: &Uuid,
uid: i64,
object_id: &str,
object_id: &Uuid,
collab_db: Weak<CollabKVDB>,
doc_state: DataSource,
notifier: Option<UserAwarenessNotifier>,
@ -375,8 +370,7 @@ impl UserManager {
info!("User awareness is not loaded when trying to access it");
let session = self.get_session()?;
let object_id =
user_awareness_object_id(&session.user_uuid, &session.user_workspace.id).to_string();
let object_id = user_awareness_object_id(&session.user_uuid, &session.user_workspace.id);
let is_loading = self
.is_loading_awareness
.get(&object_id)

View file

@ -3,12 +3,11 @@ use client_api::entity::billing_dto::{RecurringInterval, SubscriptionPlanDetail}
use client_api::entity::billing_dto::{SubscriptionPlan, WorkspaceUsageAndLimit};
use std::convert::TryFrom;
use std::str::FromStr;
use std::sync::Arc;
use collab_entity::{CollabObject, CollabType};
use collab_integrate::CollabKVDB;
use tracing::{error, info, instrument, trace, warn};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_folder_pub::entities::{ImportFrom, ImportedCollabData, ImportedFolderData};
use flowy_sqlite::schema::user_workspace_table;
@ -17,6 +16,8 @@ use flowy_user_pub::entities::{
Role, UpdateUserProfileParams, UserWorkspace, WorkspaceInvitation, WorkspaceInvitationStatus,
WorkspaceMember,
};
use tracing::{error, info, instrument, trace, warn};
use uuid::Uuid;
use crate::entities::{
RepeatedUserWorkspacePB, ResetWorkspacePB, SubscribeWorkspacePB, SuccessWorkspaceSubscriptionPB,
@ -123,7 +124,7 @@ impl UserManager {
match upload_collab_objects_data(
user_id,
weak_user_collab_db,
&current_session.user_workspace.id,
&current_session.user_workspace.workspace_id()?,
&user.authenticator,
collab_data,
weak_user_cloud_service,
@ -161,7 +162,7 @@ impl UserManager {
}
#[instrument(skip(self), err)]
pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
pub async fn open_workspace(&self, workspace_id: &Uuid) -> FlowyResult<()> {
info!("open workspace: {}", workspace_id);
let user_workspace = self
.cloud_services
@ -221,7 +222,7 @@ impl UserManager {
pub async fn patch_workspace(
&self,
workspace_id: &str,
workspace_id: &Uuid,
new_workspace_name: Option<&str>,
new_workspace_icon: Option<&str>,
) -> FlowyResult<()> {
@ -262,7 +263,7 @@ impl UserManager {
}
#[instrument(level = "info", skip(self), err)]
pub async fn leave_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
pub async fn leave_workspace(&self, workspace_id: &Uuid) -> FlowyResult<()> {
info!("leave workspace: {}", workspace_id);
self
.cloud_services
@ -273,15 +274,15 @@ impl UserManager {
// delete workspace from local sqlite db
let uid = self.user_id()?;
let conn = self.db_connection(uid)?;
delete_user_workspaces(conn, workspace_id)?;
delete_user_workspaces(conn, workspace_id.to_string().as_str())?;
self
.user_workspace_service
.did_delete_workspace(workspace_id.to_string())
.did_delete_workspace(workspace_id)
}
#[instrument(level = "info", skip(self), err)]
pub async fn delete_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
pub async fn delete_workspace(&self, workspace_id: &Uuid) -> FlowyResult<()> {
info!("delete workspace: {}", workspace_id);
self
.cloud_services
@ -290,18 +291,18 @@ impl UserManager {
.await?;
let uid = self.user_id()?;
let conn = self.db_connection(uid)?;
delete_user_workspaces(conn, workspace_id)?;
delete_user_workspaces(conn, workspace_id.to_string().as_str())?;
self
.user_workspace_service
.did_delete_workspace(workspace_id.to_string())?;
.did_delete_workspace(workspace_id)?;
Ok(())
}
pub async fn invite_member_to_workspace(
&self,
workspace_id: String,
workspace_id: Uuid,
invitee_email: String,
role: Role,
) -> FlowyResult<()> {
@ -335,7 +336,7 @@ impl UserManager {
pub async fn remove_workspace_member(
&self,
user_email: String,
workspace_id: String,
workspace_id: Uuid,
) -> FlowyResult<()> {
self
.cloud_services
@ -347,7 +348,7 @@ impl UserManager {
pub async fn get_workspace_members(
&self,
workspace_id: String,
workspace_id: Uuid,
) -> FlowyResult<Vec<WorkspaceMember>> {
let members = self
.cloud_services
@ -359,7 +360,7 @@ impl UserManager {
pub async fn get_workspace_member(
&self,
workspace_id: String,
workspace_id: Uuid,
uid: i64,
) -> FlowyResult<WorkspaceMember> {
let member = self
@ -373,7 +374,7 @@ impl UserManager {
pub async fn update_workspace_member(
&self,
user_email: String,
workspace_id: String,
workspace_id: Uuid,
role: Role,
) -> FlowyResult<()> {
self
@ -384,9 +385,9 @@ impl UserManager {
Ok(())
}
pub fn get_user_workspace(&self, uid: i64, workspace_id: &str) -> Option<UserWorkspace> {
pub fn get_user_workspace(&self, uid: i64, workspace_id: &Uuid) -> Option<UserWorkspace> {
let conn = self.db_connection(uid).ok()?;
get_user_workspace_op(workspace_id, conn)
get_user_workspace_op(workspace_id.to_string().as_str(), conn)
}
pub async fn get_all_user_workspaces(&self, uid: i64) -> FlowyResult<Vec<UserWorkspace>> {
@ -581,10 +582,10 @@ impl UserManager {
}
pub async fn get_workspace_member_info(&self, uid: i64) -> FlowyResult<WorkspaceMember> {
let workspace_id = self.get_session()?.user_workspace.id.clone();
let workspace_id = self.get_session()?.user_workspace.workspace_id()?;
let db = self.authenticate_user.get_sqlite_connection(uid)?;
// Can opt in using memory cache
if let Ok(member_record) = select_workspace_member(db, &workspace_id, uid) {
if let Ok(member_record) = select_workspace_member(db, &workspace_id.to_string(), uid) {
if is_older_than_n_minutes(member_record.updated_at, 10) {
self
.get_workspace_member_info_from_remote(&workspace_id, uid)
@ -608,7 +609,7 @@ impl UserManager {
async fn get_workspace_member_info_from_remote(
&self,
workspace_id: &str,
workspace_id: &Uuid,
uid: i64,
) -> FlowyResult<WorkspaceMember> {
trace!("get workspace member info from remote: {}", workspace_id);
@ -638,8 +639,9 @@ impl UserManager {
success: SuccessWorkspaceSubscriptionPB,
) -> FlowyResult<()> {
// periodically check the billing state
let workspace_id = Uuid::from_str(&success.workspace_id)?;
let plans = PeriodicallyCheckBillingState::new(
success.workspace_id,
workspace_id,
success.plan.map(SubscriptionPlan::from),
Arc::downgrade(&self.cloud_services),
Arc::downgrade(&self.authenticate_user),