Merge branch 'main' into no-lock-collab

This commit is contained in:
nathan 2024-08-19 10:36:36 +08:00
commit 2d7a4bde92
13 changed files with 152 additions and 19 deletions

27
Cargo.lock generated
View File

@ -579,6 +579,7 @@ dependencies = [
"collab-rt-entity", "collab-rt-entity",
"collab-rt-protocol", "collab-rt-protocol",
"collab-stream", "collab-stream",
"collab-user",
"dashmap", "dashmap",
"database", "database",
"database-entity", "database-entity",
@ -1954,6 +1955,7 @@ dependencies = [
"gotrue-entity", "gotrue-entity",
"infra", "infra",
"shared-entity", "shared-entity",
"uuid",
] ]
[[package]] [[package]]
@ -1969,6 +1971,7 @@ dependencies = [
"collab-document", "collab-document",
"collab-entity", "collab-entity",
"collab-folder", "collab-folder",
"collab-user",
"database-entity", "database-entity",
"dotenvy", "dotenvy",
"futures", "futures",
@ -2038,7 +2041,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=46b9594#46b9594a20d93ebe476b5c3b183862882f296c3f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2ba00c1e430f6157a2b6cbda89992d3b154ea6fb#2ba00c1e430f6157a2b6cbda89992d3b154ea6fb"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -2063,7 +2066,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=46b9594#46b9594a20d93ebe476b5c3b183862882f296c3f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2ba00c1e430f6157a2b6cbda89992d3b154ea6fb#2ba00c1e430f6157a2b6cbda89992d3b154ea6fb"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -2083,7 +2086,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=46b9594#46b9594a20d93ebe476b5c3b183862882f296c3f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2ba00c1e430f6157a2b6cbda89992d3b154ea6fb#2ba00c1e430f6157a2b6cbda89992d3b154ea6fb"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -2102,7 +2105,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=46b9594#46b9594a20d93ebe476b5c3b183862882f296c3f" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2ba00c1e430f6157a2b6cbda89992d3b154ea6fb#2ba00c1e430f6157a2b6cbda89992d3b154ea6fb"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
@ -2183,6 +2186,22 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "collab-user"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=2ba00c1e430f6157a2b6cbda89992d3b154ea6fb#2ba00c1e430f6157a2b6cbda89992d3b154ea6fb"
dependencies = [
"anyhow",
"collab",
"collab-entity",
"getrandom 0.2.15",
"serde",
"serde_json",
"tokio",
"tokio-stream",
"tracing",
]
[[package]] [[package]]
name = "color_quant" name = "color_quant"
version = "1.1.0" version = "1.1.0"

View File

@ -114,6 +114,7 @@ collab = { workspace = true }
collab-document = { workspace = true } collab-document = { workspace = true }
collab-entity = { workspace = true } collab-entity = { workspace = true }
collab-folder = { workspace = true } collab-folder = { workspace = true }
collab-user = { workspace = true }
collab-rt-protocol.workspace = true collab-rt-protocol.workspace = true
#Local crate #Local crate
@ -230,7 +231,7 @@ serde_repr = "0.1.18"
serde = { version = "1.0.195", features = ["derive"] } serde = { version = "1.0.195", features = ["derive"] }
bytes = "1.5.0" bytes = "1.5.0"
workspace-template = { path = "libs/workspace-template" } workspace-template = { path = "libs/workspace-template" }
uuid = { version = "1.6.1", features = ["v4"] } uuid = { version = "1.6.1", features = ["v4", "v5"] }
anyhow = "1.0.79" anyhow = "1.0.79"
actix = "0.13.3" actix = "0.13.3"
actix-web = { version = "4.5.1", default-features = false, features = [ actix-web = { version = "4.5.1", default-features = false, features = [
@ -267,6 +268,7 @@ collab = { version = "0.2.0" }
collab-entity = { version = "0.2.0" } collab-entity = { version = "0.2.0" }
collab-folder = { version = "0.2.0" } collab-folder = { version = "0.2.0" }
collab-document = { version = "0.2.0" } collab-document = { version = "0.2.0" }
collab-user = { version = "0.2.0" }
[profile.release] [profile.release]
lto = true lto = true
@ -280,10 +282,11 @@ debug = true
[patch.crates-io] [patch.crates-io]
# It's diffcult to resovle different version with the same crate used in AppFlowy Frontend and the Client-API crate. # It's diffcult to resovle different version with the same crate used in AppFlowy Frontend and the Client-API crate.
# So using patch to workaround this issue. # So using patch to workaround this issue.
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "46b9594" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2ba00c1e430f6157a2b6cbda89992d3b154ea6fb" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "46b9594" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2ba00c1e430f6157a2b6cbda89992d3b154ea6fb" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "46b9594" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2ba00c1e430f6157a2b6cbda89992d3b154ea6fb" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "46b9594" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2ba00c1e430f6157a2b6cbda89992d3b154ea6fb" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "2ba00c1e430f6157a2b6cbda89992d3b154ea6fb" }
[features] [features]
history = [] history = []

View File

@ -11,6 +11,7 @@ gotrue-entity = { workspace = true }
shared-entity = { workspace = true } shared-entity = { workspace = true }
collab-rt-entity = { workspace = true } collab-rt-entity = { workspace = true }
database-entity.workspace = true database-entity.workspace = true
uuid.workspace = true
infra = { workspace = true, optional = true } infra = { workspace = true, optional = true }

View File

@ -0,0 +1,8 @@
use uuid::Uuid;
pub fn user_awareness_object_id(user_uuid: &Uuid, workspace_id: &str) -> Uuid {
Uuid::new_v5(
user_uuid,
format!("user_awareness:{}", workspace_id).as_bytes(),
)
}

View File

@ -1,3 +1,5 @@
pub mod id;
pub use collab_entity::*; pub use collab_entity::*;
pub use collab_rt_entity::user::*; pub use collab_rt_entity::user::*;
pub use database_entity::dto::*; pub use database_entity::dto::*;

View File

@ -15,6 +15,7 @@ tracing.workspace = true
collab-folder.workspace = true collab-folder.workspace = true
collab = { workspace = true } collab = { workspace = true }
collab-document.workspace = true collab-document.workspace = true
collab-user.workspace = true
client-api = { path = "../client-api", features = ["test_util"] } client-api = { path = "../client-api", features = ["test_util"] }
once_cell = "1.19.0" once_cell = "1.19.0"
tempfile = "3.9.0" tempfile = "3.9.0"

View File

@ -16,6 +16,7 @@ use collab::entity::EncodedCollab;
use collab::preclude::{Collab, Prelim}; use collab::preclude::{Collab, Prelim};
use collab_entity::CollabType; use collab_entity::CollabType;
use collab_folder::Folder; use collab_folder::Folder;
use collab_user::core::UserAwareness;
use mime::Mime; use mime::Mime;
use serde::Deserialize; use serde::Deserialize;
use serde_json::{json, Value}; use serde_json::{json, Value};
@ -27,12 +28,13 @@ use uuid::Uuid;
#[cfg(feature = "collab-sync")] #[cfg(feature = "collab-sync")]
use client_api::collab_sync::{SinkConfig, SyncObject, SyncPlugin}; use client_api::collab_sync::{SinkConfig, SyncObject, SyncPlugin};
use client_api::entity::id::user_awareness_object_id;
use client_api::entity::QueryWorkspaceMember; use client_api::entity::QueryWorkspaceMember;
use client_api::ws::{WSClient, WSClientConfig}; use client_api::ws::{WSClient, WSClientConfig};
use database_entity::dto::{ use database_entity::dto::{
AFAccessLevel, AFRole, AFSnapshotMeta, AFSnapshotMetas, AFUserWorkspaceInfo, AFWorkspace, AFAccessLevel, AFRole, AFSnapshotMeta, AFSnapshotMetas, AFUserProfile, AFUserWorkspaceInfo,
AFWorkspaceInvitationStatus, AFWorkspaceMember, BatchQueryCollabResult, CollabParams, AFWorkspace, AFWorkspaceInvitationStatus, AFWorkspaceMember, BatchQueryCollabResult,
CreateCollabParams, InsertCollabMemberParams, QueryCollab, QueryCollabParams, CollabParams, CreateCollabParams, InsertCollabMemberParams, QueryCollab, QueryCollabParams,
QuerySnapshotParams, SnapshotData, UpdateCollabMemberParams, QuerySnapshotParams, SnapshotData, UpdateCollabMemberParams,
}; };
use shared_entity::dto::workspace_dto::{ use shared_entity::dto::workspace_dto::{
@ -188,6 +190,31 @@ impl TestClient {
.unwrap() .unwrap()
} }
pub async fn get_user_awareness(&self) -> UserAwareness {
let workspace_id = self.workspace_id().await;
let profile = self.get_user_profile().await;
let awareness_object_id = user_awareness_object_id(&profile.uuid, &workspace_id).to_string();
let data = self
.api_client
.get_collab(QueryCollabParams::new(
&awareness_object_id,
CollabType::UserAwareness,
&workspace_id,
))
.await
.unwrap();
let collab = Collab::new_with_source(
CollabOrigin::Empty,
&awareness_object_id,
DataSource::DocStateV1(data.encode_collab.doc_state.to_vec()),
vec![],
false,
)
.unwrap();
UserAwareness::open(collab, None)
}
pub async fn try_update_workspace_member( pub async fn try_update_workspace_member(
&self, &self,
workspace_id: &str, workspace_id: &str,
@ -405,6 +432,10 @@ impl TestClient {
self.api_client.get_profile().await.unwrap().uid self.api_client.get_profile().await.unwrap().uid
} }
pub async fn get_user_profile(&self) -> AFUserProfile {
self.api_client.get_profile().await.unwrap()
}
pub async fn get_snapshot( pub async fn get_snapshot(
&self, &self,
workspace_id: &str, workspace_id: &str,

View File

@ -65,7 +65,9 @@ impl Indexer for DocumentIndexer {
let mut params = match Self::get_document_contents(&document) { let mut params = match Self::get_document_contents(&document) {
Ok(result) => result, Ok(result) => result,
Err(err) => { Err(err) => {
tracing::warn!("failed to get document data: {}", err); if cfg!(debug_assertions) {
tracing::warn!("failed to get document:{} error:{}", object_id, err);
}
return Ok(None); return Ok(None);
}, },
}; };

View File

@ -1,17 +1,18 @@
use std::sync::Arc; use std::sync::Arc;
use app_error::AppError;
use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab::preclude::{ArrayPrelim, Collab, Map}; use collab::preclude::{ArrayPrelim, Collab, Map};
use collab_entity::define::WORKSPACE_DATABASES; use collab_entity::define::WORKSPACE_DATABASES;
use collab_entity::CollabType; use collab_entity::CollabType;
use sqlx::Transaction; use collab_user::core::UserAwareness;
use tracing::{debug, instrument};
use app_error::AppError;
use appflowy_collaborate::collab::storage::CollabAccessControlStorage;
use database::collab::CollabStorage; use database::collab::CollabStorage;
use database::pg_row::AFWorkspaceRow; use database::pg_row::AFWorkspaceRow;
use database_entity::dto::CollabParams; use database_entity::dto::CollabParams;
use sqlx::Transaction;
use tracing::{debug, error, instrument, trace};
use uuid::Uuid;
use workspace_template::{WorkspaceTemplate, WorkspaceTemplateBuilder}; use workspace_template::{WorkspaceTemplate, WorkspaceTemplateBuilder};
/// This function generates templates for a workspace and stores them in the database. /// This function generates templates for a workspace and stores them in the database.
@ -19,6 +20,7 @@ use workspace_template::{WorkspaceTemplate, WorkspaceTemplateBuilder};
#[instrument(level = "debug", skip_all, err)] #[instrument(level = "debug", skip_all, err)]
pub async fn initialize_workspace_for_user<T>( pub async fn initialize_workspace_for_user<T>(
uid: i64, uid: i64,
user_uuid: &Uuid,
row: &AFWorkspaceRow, row: &AFWorkspaceRow,
txn: &mut Transaction<'_, sqlx::Postgres>, txn: &mut Transaction<'_, sqlx::Postgres>,
templates: Vec<T>, templates: Vec<T>,
@ -45,6 +47,16 @@ where
txn, txn,
) )
.await?; .await?;
match create_user_awareness(&uid, user_uuid, &workspace_id, collab_storage, txn).await {
Ok(object_id) => trace!("User awareness created successfully: {}", object_id),
Err(err) => {
error!(
"Failed to create user awareness for workspace: {}, {}",
workspace_id, err
);
},
}
} else { } else {
return Err(AppError::Internal(anyhow::anyhow!( return Err(AppError::Internal(anyhow::anyhow!(
"Workspace database object id is missing" "Workspace database object id is missing"
@ -76,6 +88,42 @@ where
Ok(()) Ok(())
} }
async fn create_user_awareness(
uid: &i64,
user_uuid: &Uuid,
workspace_id: &str,
storage: &Arc<CollabAccessControlStorage>,
txn: &mut Transaction<'_, sqlx::Postgres>,
) -> Result<String, AppError> {
let object_id = user_awareness_object_id(user_uuid, workspace_id).to_string();
let collab_type = CollabType::UserAwareness;
let collab = Collab::new_with_origin(CollabOrigin::Empty, object_id.clone(), vec![], false);
// TODO(nathan): Maybe using hardcode encoded collab
let user_awareness = UserAwareness::open(collab, None);
let encode_collab = user_awareness
.encode_collab_v1(|collab| collab_type.validate_require_data(collab))
.map_err(AppError::Internal)?;
let encoded_collab_v1 = encode_collab
.encode_to_bytes()
.map_err(|err| AppError::Internal(anyhow::Error::from(err)))?;
storage
.insert_new_collab_with_transaction(
workspace_id,
uid,
CollabParams {
object_id: object_id.to_string(),
encoded_collab_v1,
collab_type,
embeddings: None,
},
txn,
)
.await?;
Ok(object_id)
}
async fn create_workspace_database_collab( async fn create_workspace_database_collab(
workspace_id: &str, workspace_id: &str,
uid: &i64, uid: &i64,
@ -86,7 +134,7 @@ async fn create_workspace_database_collab(
let collab_type = CollabType::WorkspaceDatabase; let collab_type = CollabType::WorkspaceDatabase;
let mut collab = Collab::new_with_origin(CollabOrigin::Empty, object_id, vec![], false); let mut collab = Collab::new_with_origin(CollabOrigin::Empty, object_id, vec![], false);
{ {
let mut txn = collab.context.transact_mut(); let mut txn = collab.transact_mut();
collab collab
.data .data
.insert(&mut txn, WORKSPACE_DATABASES, ArrayPrelim::default()); .insert(&mut txn, WORKSPACE_DATABASES, ArrayPrelim::default());
@ -116,3 +164,10 @@ async fn create_workspace_database_collab(
Ok(()) Ok(())
} }
pub fn user_awareness_object_id(user_uuid: &Uuid, workspace_id: &str) -> Uuid {
Uuid::new_v5(
user_uuid,
format!("user_awareness:{}", workspace_id).as_bytes(),
)
}

View File

@ -46,6 +46,7 @@ pub async fn verify_token(access_token: &str, state: &AppState) -> Result<bool,
// Create a workspace with the GetStarted template // Create a workspace with the GetStarted template
initialize_workspace_for_user( initialize_workspace_for_user(
new_uid, new_uid,
&user_uuid,
&workspace_row, &workspace_row,
&mut txn, &mut txn,
vec![GetStartedDocumentTemplate], vec![GetStartedDocumentTemplate],

View File

@ -74,6 +74,7 @@ pub async fn create_workspace_for_user(
// add create initial collab for user // add create initial collab for user
initialize_workspace_for_user( initialize_workspace_for_user(
user_uid, user_uid,
user_uuid,
&new_workspace_row, &new_workspace_row,
&mut txn, &mut txn,
vec![GetStartedDocumentTemplate], vec![GetStartedDocumentTemplate],

View File

@ -4,3 +4,4 @@ mod sign_in;
mod sign_out; mod sign_out;
mod sign_up; mod sign_up;
mod update; mod update;
mod user_awareness_test;

View File

@ -0,0 +1,8 @@
use client_api_test::TestClient;
#[tokio::test]
async fn edit_workspace_without_permission() {
let client = TestClient::new_user().await;
let user_awareness = client.get_user_awareness().await;
println!("user_awareness: {:?}", user_awareness.to_json());
}