mirror of
https://github.com/AppFlowy-IO/AppFlowy-Cloud.git
synced 2025-04-19 03:24:42 -04:00
feat: mask comment and reaction's email if profile name is not set
This commit is contained in:
parent
041df0549c
commit
82242e339f
7 changed files with 113 additions and 36 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n avr.comment_id,\n avr.reaction_type,\n ARRAY_AGG((au.uuid, au.name, au.metadata ->> 'icon_url')) AS \"react_users!: Vec<AFWebUserColumn>\"\n FROM af_published_view_reaction avr\n INNER JOIN af_user au ON avr.created_by = au.uid\n WHERE view_id = $1\n GROUP BY comment_id, reaction_type\n ORDER BY MIN(avr.created_at)\n ",
|
||||
"query": "\n SELECT\n avr.comment_id,\n avr.reaction_type,\n ARRAY_AGG((au.uuid, au.name, au.email, au.metadata ->> 'icon_url')) AS \"react_users!: Vec<AFWebUserWithEmailColumn>\"\n FROM af_published_view_reaction avr\n INNER JOIN af_user au ON avr.created_by = au.uid\n WHERE view_id = $1\n GROUP BY comment_id, reaction_type\n ORDER BY MIN(avr.created_at)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -15,7 +15,7 @@
|
|||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "react_users!: Vec<AFWebUserColumn>",
|
||||
"name": "react_users!: Vec<AFWebUserWithEmailColumn>",
|
||||
"type_info": "RecordArray"
|
||||
}
|
||||
],
|
||||
|
@ -30,5 +30,5 @@
|
|||
null
|
||||
]
|
||||
},
|
||||
"hash": "056174448a2ff0744b5943ba6d303b180ca9016cd26d284686f445c060cec4c5"
|
||||
"hash": "53d87db17bb9c1d002adc82ba9f2c07ff33ea987a1157d7f6fd2344091b98deb"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n avr.reaction_type,\n ARRAY_AGG((au.uuid, au.name, au.metadata ->> 'icon_url')) AS \"react_users!: Vec<AFWebUserColumn>\",\n avr.comment_id\n FROM af_published_view_reaction avr\n INNER JOIN af_user au ON avr.created_by = au.uid\n WHERE comment_id = $1\n GROUP BY comment_id, reaction_type\n ORDER BY MIN(avr.created_at)\n ",
|
||||
"query": "\n SELECT\n avr.reaction_type,\n ARRAY_AGG((au.uuid, au.name, au.email, au.metadata ->> 'icon_url')) AS \"react_users!: Vec<AFWebUserWithEmailColumn>\",\n avr.comment_id\n FROM af_published_view_reaction avr\n INNER JOIN af_user au ON avr.created_by = au.uid\n WHERE comment_id = $1\n GROUP BY comment_id, reaction_type\n ORDER BY MIN(avr.created_at)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "react_users!: Vec<AFWebUserColumn>",
|
||||
"name": "react_users!: Vec<AFWebUserWithEmailColumn>",
|
||||
"type_info": "RecordArray"
|
||||
},
|
||||
{
|
||||
|
@ -30,5 +30,5 @@
|
|||
false
|
||||
]
|
||||
},
|
||||
"hash": "b58432fffcf04a9485a7db5908c1801b34f51e51f3b06f679dc62e068e1cc721"
|
||||
"hash": "63f0871525ed70bd980223de574d241c0b738cfb7b0ea1fc808f02c0e05b9a2f"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n avc.comment_id,\n avc.created_at,\n avc.updated_at AS last_updated_at,\n avc.content,\n avc.reply_comment_id,\n avc.is_deleted,\n (au.uuid, au.name, au.metadata ->> 'icon_url') AS \"user: AFWebUserColumn\",\n (NOT avc.is_deleted AND ($2 OR au.uuid = $3)) AS \"can_be_deleted!\"\n FROM af_published_view_comment avc\n LEFT OUTER JOIN af_user au ON avc.created_by = au.uid\n WHERE view_id = $1\n ORDER BY avc.created_at DESC\n ",
|
||||
"query": "\n SELECT\n avc.comment_id,\n avc.created_at,\n avc.updated_at AS last_updated_at,\n avc.content,\n avc.reply_comment_id,\n avc.is_deleted,\n (au.uuid, au.name, au.email, au.metadata ->> 'icon_url') AS \"user: AFWebUserWithEmailColumn\",\n (NOT avc.is_deleted AND ($2 OR au.uuid = $3)) AS \"can_be_deleted!\"\n FROM af_published_view_comment avc\n LEFT OUTER JOIN af_user au ON avc.created_by = au.uid\n WHERE view_id = $1\n ORDER BY avc.created_at DESC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -35,7 +35,7 @@
|
|||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "user: AFWebUserColumn",
|
||||
"name": "user: AFWebUserWithEmailColumn",
|
||||
"type_info": "Record"
|
||||
},
|
||||
{
|
||||
|
@ -62,5 +62,5 @@
|
|||
null
|
||||
]
|
||||
},
|
||||
"hash": "c5c72869f44067d90c3224a17ec0e32b10cdf9378947e2c7a8409e48423377eb"
|
||||
"hash": "95c00cd1ce7cdb8f5c8f45d5262d371b1b3c3f903f4eab9c0070d9916e3f8c12"
|
||||
}
|
|
@ -854,9 +854,16 @@ pub struct AFWebUser {
|
|||
pub avatar_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct AFWebUserWithObfuscatedName {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
pub avatar_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GlobalComment {
|
||||
pub user: Option<AFWebUser>,
|
||||
pub user: Option<AFWebUserWithObfuscatedName>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub last_updated_at: DateTime<Utc>,
|
||||
pub content: String,
|
||||
|
@ -885,7 +892,7 @@ pub struct Reactions {
|
|||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Reaction {
|
||||
pub reaction_type: String,
|
||||
pub react_users: Vec<AFWebUser>,
|
||||
pub react_users: Vec<AFWebUserWithObfuscatedName>,
|
||||
pub comment_id: Uuid,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ use app_error::AppError;
|
|||
use chrono::{DateTime, Utc};
|
||||
|
||||
use database_entity::dto::{
|
||||
AFAccessLevel, AFRole, AFUserProfile, AFWebUser, AFWorkspace, AFWorkspaceInvitationStatus,
|
||||
AccessRequestMinimal, AccessRequestStatus, AccessRequestWithViewId, AccessRequesterInfo,
|
||||
AccountLink, GlobalComment, QuickNote, Reaction, Template, TemplateCategory,
|
||||
AFAccessLevel, AFRole, AFUserProfile, AFWebUser, AFWebUserWithObfuscatedName, AFWorkspace,
|
||||
AFWorkspaceInvitationStatus, AccessRequestMinimal, AccessRequestStatus, AccessRequestWithViewId,
|
||||
AccessRequesterInfo, AccountLink, GlobalComment, QuickNote, Reaction, Template, TemplateCategory,
|
||||
TemplateCategoryMinimal, TemplateCategoryType, TemplateCreator, TemplateCreatorMinimal,
|
||||
TemplateGroup, TemplateMinimal,
|
||||
};
|
||||
|
@ -330,8 +330,40 @@ impl From<AFWebUserColumn> for AFWebUser {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Serialize, Debug)]
|
||||
pub struct AFWebUserWithEmailColumn {
|
||||
uuid: Uuid,
|
||||
name: String,
|
||||
email: String,
|
||||
avatar_url: Option<String>,
|
||||
}
|
||||
|
||||
fn mask_web_user_email(email: &str) -> String {
|
||||
email
|
||||
.split('@')
|
||||
.next()
|
||||
.map(|part| part.chars().take(6).collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
impl From<AFWebUserWithEmailColumn> for AFWebUserWithObfuscatedName {
|
||||
fn from(val: AFWebUserWithEmailColumn) -> Self {
|
||||
let obfuscated_name = if val.name == val.email {
|
||||
mask_web_user_email(&val.email)
|
||||
} else {
|
||||
val.name.clone()
|
||||
};
|
||||
|
||||
AFWebUserWithObfuscatedName {
|
||||
uuid: val.uuid,
|
||||
name: obfuscated_name,
|
||||
avatar_url: val.avatar_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AFGlobalCommentRow {
|
||||
pub user: Option<AFWebUserColumn>,
|
||||
pub user: Option<AFWebUserWithEmailColumn>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub last_updated_at: DateTime<Utc>,
|
||||
pub content: String,
|
||||
|
@ -358,7 +390,7 @@ impl From<AFGlobalCommentRow> for GlobalComment {
|
|||
|
||||
pub struct AFReactionRow {
|
||||
pub reaction_type: String,
|
||||
pub react_users: Vec<AFWebUserColumn>,
|
||||
pub react_users: Vec<AFWebUserWithEmailColumn>,
|
||||
pub comment_id: Uuid,
|
||||
}
|
||||
|
||||
|
@ -720,3 +752,23 @@ pub struct AFPublishViewWithPublishInfo {
|
|||
pub comments_enabled: bool,
|
||||
pub duplicate_enabled: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_mask_web_user_email() {
|
||||
let name = "";
|
||||
let masked = mask_web_user_email(name);
|
||||
assert_eq!(masked, "");
|
||||
|
||||
let name = "john@domain.com";
|
||||
let masked = mask_web_user_email(name);
|
||||
assert_eq!(masked, "john");
|
||||
|
||||
let name = "jonathan@domain.com";
|
||||
let masked = mask_web_user_email(name);
|
||||
assert_eq!(masked, "jonath");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ use uuid::Uuid;
|
|||
|
||||
use crate::pg_row::{
|
||||
AFGlobalCommentRow, AFImportTask, AFPermissionRow, AFReactionRow, AFUserProfileRow,
|
||||
AFWebUserColumn, AFWorkspaceInvitationMinimal, AFWorkspaceMemberPermRow, AFWorkspaceMemberRow,
|
||||
AFWorkspaceRow,
|
||||
AFWebUserWithEmailColumn, AFWorkspaceInvitationMinimal, AFWorkspaceMemberPermRow,
|
||||
AFWorkspaceMemberRow, AFWorkspaceRow,
|
||||
};
|
||||
use crate::user::select_uid_from_email;
|
||||
use app_error::AppError;
|
||||
|
@ -1102,7 +1102,7 @@ pub async fn select_comments_for_published_view_ordered_by_recency<
|
|||
avc.content,
|
||||
avc.reply_comment_id,
|
||||
avc.is_deleted,
|
||||
(au.uuid, au.name, au.metadata ->> 'icon_url') AS "user: AFWebUserColumn",
|
||||
(au.uuid, au.name, au.email, au.metadata ->> 'icon_url') AS "user: AFWebUserWithEmailColumn",
|
||||
(NOT avc.is_deleted AND ($2 OR au.uuid = $3)) AS "can_be_deleted!"
|
||||
FROM af_published_view_comment avc
|
||||
LEFT OUTER JOIN af_user au ON avc.created_by = au.uid
|
||||
|
@ -1188,7 +1188,7 @@ pub async fn select_reactions_for_published_view_ordered_by_reaction_type_creati
|
|||
SELECT
|
||||
avr.comment_id,
|
||||
avr.reaction_type,
|
||||
ARRAY_AGG((au.uuid, au.name, au.metadata ->> 'icon_url')) AS "react_users!: Vec<AFWebUserColumn>"
|
||||
ARRAY_AGG((au.uuid, au.name, au.email, au.metadata ->> 'icon_url')) AS "react_users!: Vec<AFWebUserWithEmailColumn>"
|
||||
FROM af_published_view_reaction avr
|
||||
INNER JOIN af_user au ON avr.created_by = au.uid
|
||||
WHERE view_id = $1
|
||||
|
@ -1216,7 +1216,7 @@ pub async fn select_reactions_for_comment_ordered_by_reaction_type_creation_time
|
|||
r#"
|
||||
SELECT
|
||||
avr.reaction_type,
|
||||
ARRAY_AGG((au.uuid, au.name, au.metadata ->> 'icon_url')) AS "react_users!: Vec<AFWebUserColumn>",
|
||||
ARRAY_AGG((au.uuid, au.name, au.email, au.metadata ->> 'icon_url')) AS "react_users!: Vec<AFWebUserWithEmailColumn>",
|
||||
avr.comment_id
|
||||
FROM af_published_view_reaction avr
|
||||
INNER JOIN af_user au ON avr.created_by = au.uid
|
||||
|
|
|
@ -18,6 +18,7 @@ use collab_entity::CollabType;
|
|||
use collab_folder::{CollabOrigin, Folder, UserId};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shared_entity::dto::auth_dto::UpdateUserParams;
|
||||
use shared_entity::dto::publish_dto::PublishDatabaseData;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
@ -471,13 +472,22 @@ async fn test_publish_doc() {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_publish_comments() {
|
||||
let (page_owner_client, page_owner) = generate_unique_registered_user_client().await;
|
||||
let (page_owner_client, _) = generate_unique_registered_user_client().await;
|
||||
let workspace_id = get_first_workspace(&page_owner_client).await;
|
||||
let published_view_namespace = Uuid::new_v4().to_string();
|
||||
page_owner_client
|
||||
.set_workspace_publish_namespace(&workspace_id, published_view_namespace)
|
||||
.await
|
||||
.unwrap();
|
||||
page_owner_client
|
||||
.update_user(UpdateUserParams {
|
||||
name: Some("PageOwner".to_string()),
|
||||
password: None,
|
||||
email: None,
|
||||
metadata: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let publish_name = "published-view";
|
||||
let view_id = Uuid::new_v4();
|
||||
|
@ -516,7 +526,7 @@ async fn test_publish_comments() {
|
|||
assert_eq!(comments[0].content, page_owner_comment_content);
|
||||
}
|
||||
|
||||
let (first_user_client, first_user) = generate_unique_registered_user_client().await;
|
||||
let (first_user_client, _) = generate_unique_registered_user_client().await;
|
||||
let first_user_comment_content = "comment from first authenticated user";
|
||||
// This is to ensure that the second comment creation timestamp is later than the first one
|
||||
sleep(Duration::from_millis(1));
|
||||
|
@ -524,6 +534,15 @@ async fn test_publish_comments() {
|
|||
.create_comment_on_published_view(&view_id, first_user_comment_content, &None)
|
||||
.await
|
||||
.unwrap();
|
||||
first_user_client
|
||||
.update_user(UpdateUserParams {
|
||||
name: Some("User1".to_string()),
|
||||
password: None,
|
||||
email: None,
|
||||
metadata: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let guest_client = localhost_client();
|
||||
let result = guest_client
|
||||
.create_comment_on_published_view(&view_id, "comment from anonymous", &None)
|
||||
|
@ -571,10 +590,7 @@ async fn test_publish_comments() {
|
|||
.unwrap_or("".to_string())
|
||||
})
|
||||
.collect_vec();
|
||||
assert_eq!(
|
||||
comment_creators,
|
||||
vec![first_user.email.clone(), page_owner.email.clone()]
|
||||
);
|
||||
assert_eq!(comment_creators, vec!["User1", "PageOwner"]);
|
||||
let comment_content = published_view_comments
|
||||
.iter()
|
||||
.map(|c| c.content.clone())
|
||||
|
@ -586,7 +602,16 @@ async fn test_publish_comments() {
|
|||
|
||||
// Test if it's possible to reply to another user's comment
|
||||
let second_user_comment_content = "comment from second authenticated user";
|
||||
let (second_user_client, second_user) = generate_unique_registered_user_client().await;
|
||||
let (second_user_client, _) = generate_unique_registered_user_client().await;
|
||||
second_user_client
|
||||
.update_user(UpdateUserParams {
|
||||
name: Some("User2".to_string()),
|
||||
password: None,
|
||||
email: None,
|
||||
metadata: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
let published_view_comments: Vec<GlobalComment> = guest_client
|
||||
.get_published_view_comments(&view_id)
|
||||
|
@ -626,14 +651,7 @@ async fn test_publish_comments() {
|
|||
.unwrap_or("".to_string())
|
||||
})
|
||||
.collect_vec();
|
||||
assert_eq!(
|
||||
comment_creators,
|
||||
vec![
|
||||
second_user.email.clone(),
|
||||
first_user.email.clone(),
|
||||
page_owner.email.clone()
|
||||
]
|
||||
);
|
||||
assert_eq!(comment_creators, vec!["User2", "User1", "PageOwner"]);
|
||||
assert_eq!(
|
||||
published_view_comments[0].reply_comment_id,
|
||||
Some(published_view_comments[1].comment_id)
|
||||
|
|
Loading…
Add table
Reference in a new issue