Merge pull request #821 from AppFlowy-IO/feat/workspace-invite-fields
feat: additional fields for workspace invitation
This commit is contained in:
commit
eed23a353a
|
|
@ -1,59 +0,0 @@
|
||||||
{
|
|
||||||
"db_name": "PostgreSQL",
|
|
||||||
"query": "\n SELECT\n id AS invite_id,\n workspace_id,\n (SELECT workspace_name FROM public.af_workspace WHERE workspace_id = af_workspace_invitation.workspace_id),\n (SELECT email FROM public.af_user WHERE uid = af_workspace_invitation.inviter) AS inviter_email,\n (SELECT name FROM public.af_user WHERE uid = af_workspace_invitation.inviter) AS inviter_name,\n status,\n updated_at\n FROM\n public.af_workspace_invitation\n WHERE af_workspace_invitation.invitee_email = (SELECT email FROM public.af_user WHERE uuid = $1)\n AND ($2::SMALLINT IS NULL OR status = $2)\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"ordinal": 0,
|
|
||||||
"name": "invite_id",
|
|
||||||
"type_info": "Uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 1,
|
|
||||||
"name": "workspace_id",
|
|
||||||
"type_info": "Uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 2,
|
|
||||||
"name": "workspace_name",
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 3,
|
|
||||||
"name": "inviter_email",
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 4,
|
|
||||||
"name": "inviter_name",
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 5,
|
|
||||||
"name": "status",
|
|
||||||
"type_info": "Int2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 6,
|
|
||||||
"name": "updated_at",
|
|
||||||
"type_info": "Timestamptz"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid",
|
|
||||||
"Int2"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hash": "2c1152b8867bebcb63b637820eb44ac932ee88c6326ecaf9b4b4c7f690eff41c"
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT\n i.id AS invite_id,\n i.workspace_id,\n w.workspace_name,\n u_inviter.email AS inviter_email,\n u_inviter.name AS inviter_name,\n i.status,\n i.updated_at,\n u_inviter.metadata->>'icon_url' AS inviter_icon,\n w.icon AS workspace_icon,\n (SELECT COUNT(*) FROM public.af_workspace_member m WHERE m.workspace_id = i.workspace_id) AS member_count\n FROM\n public.af_workspace_invitation i\n JOIN public.af_workspace w ON i.workspace_id = w.workspace_id\n JOIN public.af_user u_inviter ON i.inviter = u_inviter.uid\n JOIN public.af_user u_invitee ON u_invitee.uuid = $1\n WHERE\n i.invitee_email = u_invitee.email\n AND i.id = $2;\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "invite_id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "workspace_id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "workspace_name",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "inviter_email",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "inviter_name",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "status",
|
||||||
|
"type_info": "Int2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "updated_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 7,
|
||||||
|
"name": "inviter_icon",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 8,
|
||||||
|
"name": "workspace_icon",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 9,
|
||||||
|
"name": "member_count",
|
||||||
|
"type_info": "Int8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid",
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "2ee385e58e042071290226289646965553938838c83085273f68f687c976767a"
|
||||||
|
}
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
{
|
|
||||||
"db_name": "PostgreSQL",
|
|
||||||
"query": "\n SELECT\n id AS invite_id,\n workspace_id,\n (SELECT workspace_name FROM public.af_workspace WHERE workspace_id = af_workspace_invitation.workspace_id),\n (SELECT email FROM public.af_user WHERE uid = af_workspace_invitation.inviter) AS inviter_email,\n (SELECT name FROM public.af_user WHERE uid = af_workspace_invitation.inviter) AS inviter_name,\n status,\n updated_at\n FROM public.af_workspace_invitation\n WHERE af_workspace_invitation.invitee_email = (SELECT email FROM public.af_user WHERE uuid = $1)\n AND id = $2\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"ordinal": 0,
|
|
||||||
"name": "invite_id",
|
|
||||||
"type_info": "Uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 1,
|
|
||||||
"name": "workspace_id",
|
|
||||||
"type_info": "Uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 2,
|
|
||||||
"name": "workspace_name",
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 3,
|
|
||||||
"name": "inviter_email",
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 4,
|
|
||||||
"name": "inviter_name",
|
|
||||||
"type_info": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 5,
|
|
||||||
"name": "status",
|
|
||||||
"type_info": "Int2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ordinal": 6,
|
|
||||||
"name": "updated_at",
|
|
||||||
"type_info": "Timestamptz"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Uuid",
|
|
||||||
"Uuid"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hash": "6dd7f6db2d364cc37b1b46c611fe111d06e327edd9f3a98a4b0636bfe8fd6319"
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT EXISTS(\n SELECT 1\n FROM af_workspace_invitation\n WHERE id = $1 AND invitee_email = (SELECT email FROM af_user WHERE uuid = $2)\n )\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid",
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "7c8b84da6d70cb4ae59ae618e6f7aa7bde3dbd6630bd7e7fcafe1606d63651c8"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT\n i.id AS invite_id,\n i.workspace_id,\n w.workspace_name,\n u_inviter.email AS inviter_email,\n u_inviter.name AS inviter_name,\n i.status,\n i.updated_at,\n u_inviter.metadata->>'icon_url' AS inviter_icon,\n w.icon AS workspace_icon,\n (SELECT COUNT(*) FROM public.af_workspace_member m WHERE m.workspace_id = i.workspace_id) AS member_count\n FROM\n public.af_workspace_invitation i\n JOIN public.af_workspace w ON i.workspace_id = w.workspace_id\n JOIN public.af_user u_inviter ON i.inviter = u_inviter.uid\n JOIN public.af_user u_invitee ON u_invitee.uuid = $1\n WHERE\n i.invitee_email = u_invitee.email\n AND ($2::SMALLINT IS NULL OR i.status = $2);\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "invite_id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "workspace_id",
|
||||||
|
"type_info": "Uuid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "workspace_name",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "inviter_email",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "inviter_name",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "status",
|
||||||
|
"type_info": "Int2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "updated_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 7,
|
||||||
|
"name": "inviter_icon",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 8,
|
||||||
|
"name": "workspace_icon",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 9,
|
||||||
|
"name": "member_count",
|
||||||
|
"type_info": "Int8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid",
|
||||||
|
"Int2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "de595bd6554d8e1f58c9c4bb94ea14b5ae2fd15b3c5d2b84d5dd5a551954ecde"
|
||||||
|
}
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<h4>Pending Invitation</h4>
|
<h4>Invitation(s) from other user(s)</h4>
|
||||||
<table class="purple-table table">
|
<table class="purple-table table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,9 @@ pub enum AppError {
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InvalidFolderView(String),
|
InvalidFolderView(String),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
NotInviteeOfWorkspaceInvitation(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppError {
|
impl AppError {
|
||||||
|
|
@ -208,6 +211,7 @@ impl AppError {
|
||||||
AppError::InvalidContentType(_) => ErrorCode::InvalidContentType,
|
AppError::InvalidContentType(_) => ErrorCode::InvalidContentType,
|
||||||
AppError::InvalidPublishedOutline(_) => ErrorCode::InvalidPublishedOutline,
|
AppError::InvalidPublishedOutline(_) => ErrorCode::InvalidPublishedOutline,
|
||||||
AppError::InvalidFolderView(_) => ErrorCode::InvalidFolderView,
|
AppError::InvalidFolderView(_) => ErrorCode::InvalidFolderView,
|
||||||
|
AppError::NotInviteeOfWorkspaceInvitation(_) => ErrorCode::NotInviteeOfWorkspaceInvitation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,6 +332,7 @@ pub enum ErrorCode {
|
||||||
AppleRevokeTokenError = 1038,
|
AppleRevokeTokenError = 1038,
|
||||||
InvalidPublishedOutline = 1039,
|
InvalidPublishedOutline = 1039,
|
||||||
InvalidFolderView = 1040,
|
InvalidFolderView = 1040,
|
||||||
|
NotInviteeOfWorkspaceInvitation = 1041,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorCode {
|
impl ErrorCode {
|
||||||
|
|
|
||||||
|
|
@ -601,6 +601,9 @@ pub struct AFWorkspaceInvitation {
|
||||||
pub inviter_name: Option<String>,
|
pub inviter_name: Option<String>,
|
||||||
pub status: AFWorkspaceInvitationStatus,
|
pub status: AFWorkspaceInvitationStatus,
|
||||||
pub updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
|
pub inviter_icon: Option<String>,
|
||||||
|
pub workspace_icon: String,
|
||||||
|
pub member_count: Option<i64>, // use unwrap_or(0) to get the value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -410,21 +410,30 @@ pub async fn select_workspace_invitations_for_user(
|
||||||
AFWorkspaceInvitation,
|
AFWorkspaceInvitation,
|
||||||
r#"
|
r#"
|
||||||
SELECT
|
SELECT
|
||||||
id AS invite_id,
|
i.id AS invite_id,
|
||||||
workspace_id,
|
i.workspace_id,
|
||||||
(SELECT workspace_name FROM public.af_workspace WHERE workspace_id = af_workspace_invitation.workspace_id),
|
w.workspace_name,
|
||||||
(SELECT email FROM public.af_user WHERE uid = af_workspace_invitation.inviter) AS inviter_email,
|
u_inviter.email AS inviter_email,
|
||||||
(SELECT name FROM public.af_user WHERE uid = af_workspace_invitation.inviter) AS inviter_name,
|
u_inviter.name AS inviter_name,
|
||||||
status,
|
i.status,
|
||||||
updated_at
|
i.updated_at,
|
||||||
|
u_inviter.metadata->>'icon_url' AS inviter_icon,
|
||||||
|
w.icon AS workspace_icon,
|
||||||
|
(SELECT COUNT(*) FROM public.af_workspace_member m WHERE m.workspace_id = i.workspace_id) AS member_count
|
||||||
FROM
|
FROM
|
||||||
public.af_workspace_invitation
|
public.af_workspace_invitation i
|
||||||
WHERE af_workspace_invitation.invitee_email = (SELECT email FROM public.af_user WHERE uuid = $1)
|
JOIN public.af_workspace w ON i.workspace_id = w.workspace_id
|
||||||
AND ($2::SMALLINT IS NULL OR status = $2)
|
JOIN public.af_user u_inviter ON i.inviter = u_inviter.uid
|
||||||
|
JOIN public.af_user u_invitee ON u_invitee.uuid = $1
|
||||||
|
WHERE
|
||||||
|
i.invitee_email = u_invitee.email
|
||||||
|
AND ($2::SMALLINT IS NULL OR i.status = $2);
|
||||||
"#,
|
"#,
|
||||||
invitee_uuid,
|
invitee_uuid,
|
||||||
status_filter.map(|s| s as i16)
|
status_filter.map(|s| s as i16)
|
||||||
).fetch_all(pg_pool).await?;
|
)
|
||||||
|
.fetch_all(pg_pool)
|
||||||
|
.await?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,20 +447,30 @@ pub async fn select_workspace_invitation_for_user(
|
||||||
AFWorkspaceInvitation,
|
AFWorkspaceInvitation,
|
||||||
r#"
|
r#"
|
||||||
SELECT
|
SELECT
|
||||||
id AS invite_id,
|
i.id AS invite_id,
|
||||||
workspace_id,
|
i.workspace_id,
|
||||||
(SELECT workspace_name FROM public.af_workspace WHERE workspace_id = af_workspace_invitation.workspace_id),
|
w.workspace_name,
|
||||||
(SELECT email FROM public.af_user WHERE uid = af_workspace_invitation.inviter) AS inviter_email,
|
u_inviter.email AS inviter_email,
|
||||||
(SELECT name FROM public.af_user WHERE uid = af_workspace_invitation.inviter) AS inviter_name,
|
u_inviter.name AS inviter_name,
|
||||||
status,
|
i.status,
|
||||||
updated_at
|
i.updated_at,
|
||||||
FROM public.af_workspace_invitation
|
u_inviter.metadata->>'icon_url' AS inviter_icon,
|
||||||
WHERE af_workspace_invitation.invitee_email = (SELECT email FROM public.af_user WHERE uuid = $1)
|
w.icon AS workspace_icon,
|
||||||
AND id = $2
|
(SELECT COUNT(*) FROM public.af_workspace_member m WHERE m.workspace_id = i.workspace_id) AS member_count
|
||||||
|
FROM
|
||||||
|
public.af_workspace_invitation i
|
||||||
|
JOIN public.af_workspace w ON i.workspace_id = w.workspace_id
|
||||||
|
JOIN public.af_user u_inviter ON i.inviter = u_inviter.uid
|
||||||
|
JOIN public.af_user u_invitee ON u_invitee.uuid = $1
|
||||||
|
WHERE
|
||||||
|
i.invitee_email = u_invitee.email
|
||||||
|
AND i.id = $2;
|
||||||
"#,
|
"#,
|
||||||
invitee_uuid,
|
invitee_uuid,
|
||||||
invite_id,
|
invite_id,
|
||||||
).fetch_one(pg_pool).await?;
|
)
|
||||||
|
.fetch_one(pg_pool)
|
||||||
|
.await?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1357,3 +1376,24 @@ pub async fn delete_reaction_from_comment<'a, E: Executor<'a, Database = Postgre
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn select_user_is_invitee_for_workspace_invitation(
|
||||||
|
pg_pool: &PgPool,
|
||||||
|
invitee_uuid: &Uuid,
|
||||||
|
invite_id: &Uuid,
|
||||||
|
) -> Result<bool, AppError> {
|
||||||
|
let res = sqlx::query_scalar!(
|
||||||
|
r#"
|
||||||
|
SELECT EXISTS(
|
||||||
|
SELECT 1
|
||||||
|
FROM af_workspace_invitation
|
||||||
|
WHERE id = $1 AND invitee_email = (SELECT email FROM af_user WHERE uuid = $2)
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
invite_id,
|
||||||
|
invitee_uuid,
|
||||||
|
)
|
||||||
|
.fetch_one(pg_pool)
|
||||||
|
.await?;
|
||||||
|
res.map_or(Ok(false), Ok)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use access_control::collab::CollabAccessControl;
|
||||||
use app_error::AppError;
|
use app_error::AppError;
|
||||||
use appflowy_collaborate::actix_ws::entities::ClientStreamMessage;
|
use appflowy_collaborate::actix_ws::entities::ClientStreamMessage;
|
||||||
use appflowy_collaborate::indexer::IndexerProvider;
|
use appflowy_collaborate::indexer::IndexerProvider;
|
||||||
use authentication::jwt::{OptionalUserUuid, UserUuid};
|
use authentication::jwt::{Authorization, OptionalUserUuid, UserUuid};
|
||||||
use collab_rt_entity::realtime_proto::HttpRealtimeMessage;
|
use collab_rt_entity::realtime_proto::HttpRealtimeMessage;
|
||||||
use collab_rt_entity::RealtimeMessage;
|
use collab_rt_entity::RealtimeMessage;
|
||||||
use collab_rt_protocol::validate_encode_collab;
|
use collab_rt_protocol::validate_encode_collab;
|
||||||
|
|
@ -41,6 +41,7 @@ use crate::biz;
|
||||||
use crate::biz::collab::ops::{
|
use crate::biz::collab::ops::{
|
||||||
get_user_favorite_folder_views, get_user_recent_folder_views, get_user_trash_folder_views,
|
get_user_favorite_folder_views, get_user_recent_folder_views, get_user_trash_folder_views,
|
||||||
};
|
};
|
||||||
|
use crate::biz::user::user_verify::verify_token;
|
||||||
use crate::biz::workspace;
|
use crate::biz::workspace;
|
||||||
use crate::biz::workspace::ops::{
|
use crate::biz::workspace::ops::{
|
||||||
create_comment_on_published_view, create_reaction_on_comment, get_comments_on_published_view,
|
create_comment_on_published_view, create_reaction_on_comment, get_comments_on_published_view,
|
||||||
|
|
@ -333,16 +334,20 @@ async fn get_workspace_invite_by_id_handler(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_accept_workspace_invite_handler(
|
async fn post_accept_workspace_invite_handler(
|
||||||
user_uuid: UserUuid,
|
auth: Authorization,
|
||||||
invite_id: web::Path<Uuid>,
|
invite_id: web::Path<Uuid>,
|
||||||
state: Data<AppState>,
|
state: Data<AppState>,
|
||||||
) -> Result<JsonAppResponse<()>> {
|
) -> Result<JsonAppResponse<()>> {
|
||||||
|
let _is_new = verify_token(&auth.token, state.as_ref()).await?;
|
||||||
|
let user_uuid = auth.uuid()?;
|
||||||
|
let user_uid = state.user_cache.get_user_uid(&user_uuid).await?;
|
||||||
let invite_id = invite_id.into_inner();
|
let invite_id = invite_id.into_inner();
|
||||||
// TODO(zack): insert a workspace member in the af_workspace_member by calling workspace::ops::add_workspace_members.
|
// TODO(zack): insert a workspace member in the af_workspace_member by calling workspace::ops::add_workspace_members.
|
||||||
// Currently, when the server get restarted, the policy in access control will be lost.
|
// Currently, when the server get restarted, the policy in access control will be lost.
|
||||||
workspace::ops::accept_workspace_invite(
|
workspace::ops::accept_workspace_invite(
|
||||||
&state.pg_pool,
|
&state.pg_pool,
|
||||||
&state.workspace_access_control,
|
&state.workspace_access_control,
|
||||||
|
user_uid,
|
||||||
&user_uuid,
|
&user_uuid,
|
||||||
&invite_id,
|
&invite_id,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -302,12 +302,21 @@ pub async fn open_workspace(
|
||||||
pub async fn accept_workspace_invite(
|
pub async fn accept_workspace_invite(
|
||||||
pg_pool: &PgPool,
|
pg_pool: &PgPool,
|
||||||
workspace_access_control: &impl WorkspaceAccessControl,
|
workspace_access_control: &impl WorkspaceAccessControl,
|
||||||
|
user_uid: i64,
|
||||||
user_uuid: &Uuid,
|
user_uuid: &Uuid,
|
||||||
invite_id: &Uuid,
|
invite_id: &Uuid,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
let mut txn = pg_pool.begin().await?;
|
let mut txn = pg_pool.begin().await?;
|
||||||
update_workspace_invitation_set_status_accepted(&mut txn, user_uuid, invite_id).await?;
|
|
||||||
let inv = get_invitation_by_id(&mut txn, invite_id).await?;
|
let inv = get_invitation_by_id(&mut txn, invite_id).await?;
|
||||||
|
if let Some(invitee_uid) = inv.invitee_uid {
|
||||||
|
if invitee_uid != user_uid {
|
||||||
|
return Err(AppError::NotInviteeOfWorkspaceInvitation(format!(
|
||||||
|
"User with uid {} is not the invitee for invite_id {}",
|
||||||
|
user_uid, invite_id
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_workspace_invitation_set_status_accepted(&mut txn, user_uuid, invite_id).await?;
|
||||||
let invited_uid = inv
|
let invited_uid = inv
|
||||||
.invitee_uid
|
.invitee_uid
|
||||||
.ok_or_else(|| AppError::Internal(anyhow::anyhow!("Invitee uid is missing for {:?}", inv)))?;
|
.ok_or_else(|| AppError::Internal(anyhow::anyhow!("Invitee uid is missing for {:?}", inv)))?;
|
||||||
|
|
@ -400,7 +409,7 @@ pub async fn invite_workspace_members(
|
||||||
// Generate a link such that when clicked, the user is added to the workspace.
|
// Generate a link such that when clicked, the user is added to the workspace.
|
||||||
let accept_url = {
|
let accept_url = {
|
||||||
match appflowy_web_url {
|
match appflowy_web_url {
|
||||||
Some(appflowy_web_url) => format!("{}/accept-invitation?invitated_id={}", appflowy_web_url, invite_id),
|
Some(appflowy_web_url) => format!("{}/accept-invitation?invited_id={}", appflowy_web_url, invite_id),
|
||||||
None => {
|
None => {
|
||||||
gotrue_client
|
gotrue_client
|
||||||
.admin_generate_link(
|
.admin_generate_link(
|
||||||
|
|
@ -469,6 +478,14 @@ pub async fn get_workspace_invitations_for_user(
|
||||||
user_uuid: &Uuid,
|
user_uuid: &Uuid,
|
||||||
invite_id: &Uuid,
|
invite_id: &Uuid,
|
||||||
) -> Result<AFWorkspaceInvitation, AppError> {
|
) -> Result<AFWorkspaceInvitation, AppError> {
|
||||||
|
let user_is_invitee =
|
||||||
|
select_user_is_invitee_for_workspace_invitation(pg_pool, user_uuid, invite_id).await?;
|
||||||
|
if !user_is_invitee {
|
||||||
|
return Err(AppError::NotInviteeOfWorkspaceInvitation(format!(
|
||||||
|
"User with uuid {} is not the invitee for invite_id {}",
|
||||||
|
user_uuid, invite_id
|
||||||
|
)));
|
||||||
|
}
|
||||||
let invitation = select_workspace_invitation_for_user(pg_pool, user_uuid, invite_id).await?;
|
let invitation = select_workspace_invitation_for_user(pg_pool, user_uuid, invite_id).await?;
|
||||||
Ok(invitation)
|
Ok(invitation)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,9 @@ impl Mailer {
|
||||||
|
|
||||||
HANDLEBARS
|
HANDLEBARS
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.map_err(|err| anyhow::anyhow!(format!("Failed to write handlebars: {}", err)))?
|
||||||
.register_template_string("workspace_invite", workspace_invite_template)
|
.register_template_string("workspace_invite", workspace_invite_template)
|
||||||
.unwrap();
|
.map_err(|err| anyhow::anyhow!(format!("Failed to register handlebars template: {}", err)))?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
smtp_transport,
|
smtp_transport,
|
||||||
|
|
@ -51,10 +51,10 @@ impl Mailer {
|
||||||
email: String,
|
email: String,
|
||||||
param: WorkspaceInviteMailerParam,
|
param: WorkspaceInviteMailerParam,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
let rendered = HANDLEBARS
|
let rendered = match HANDLEBARS.read() {
|
||||||
.read()
|
Ok(registory) => registory.render("workspace_invite", ¶m)?,
|
||||||
.unwrap()
|
Err(err) => anyhow::bail!(format!("Failed to render handlebars template: {}", err)),
|
||||||
.render("workspace_invite", ¶m)?;
|
};
|
||||||
|
|
||||||
let email = Message::builder()
|
let email = Message::builder()
|
||||||
.from(lettre::message::Mailbox::new(
|
.from(lettre::message::Mailbox::new(
|
||||||
|
|
@ -63,7 +63,7 @@ impl Mailer {
|
||||||
))
|
))
|
||||||
.to(lettre::message::Mailbox::new(
|
.to(lettre::message::Mailbox::new(
|
||||||
Some(param.username.clone()),
|
Some(param.username.clone()),
|
||||||
email.parse().unwrap(),
|
email.parse()?,
|
||||||
))
|
))
|
||||||
.subject(format!(
|
.subject(format!(
|
||||||
"Action required: {} invited you to {} in AppFlowy",
|
"Action required: {} invited you to {} in AppFlowy",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use app_error::ErrorCode;
|
||||||
use client_api_test::generate_unique_registered_user_client;
|
use client_api_test::generate_unique_registered_user_client;
|
||||||
use database_entity::dto::{AFRole, AFWorkspaceInvitationStatus};
|
use database_entity::dto::{AFRole, AFWorkspaceInvitationStatus};
|
||||||
use shared_entity::dto::workspace_dto::{QueryWorkspaceParam, WorkspaceMemberInvitation};
|
use shared_entity::dto::workspace_dto::{QueryWorkspaceParam, WorkspaceMemberInvitation};
|
||||||
|
|
@ -59,12 +60,33 @@ async fn invite_workspace_crud() {
|
||||||
|
|
||||||
assert_eq!(invitation.inviter_email, Some(alice.email));
|
assert_eq!(invitation.inviter_email, Some(alice.email));
|
||||||
assert_eq!(invitation.status, AFWorkspaceInvitationStatus::Pending);
|
assert_eq!(invitation.status, AFWorkspaceInvitationStatus::Pending);
|
||||||
|
assert_eq!(invitation.member_count.unwrap_or(0), 1);
|
||||||
|
|
||||||
|
let (charlie_client, _charlie) = generate_unique_registered_user_client().await;
|
||||||
|
let err = charlie_client
|
||||||
|
.get_workspace_invitation(&invite_id)
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(err.code, ErrorCode::NotInviteeOfWorkspaceInvitation);
|
||||||
|
let err = charlie_client
|
||||||
|
.accept_workspace_invitation(&invite_id)
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(err.code, ErrorCode::NotInviteeOfWorkspaceInvitation);
|
||||||
|
|
||||||
bob_client
|
bob_client
|
||||||
.accept_workspace_invitation(&invite_id)
|
.accept_workspace_invitation(&invite_id)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let invitation = bob_client
|
||||||
|
.get_workspace_invitation(&invite_id)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(invitation.status, AFWorkspaceInvitationStatus::Accepted);
|
||||||
|
assert_eq!(invitation.member_count.unwrap_or(0), 2);
|
||||||
|
|
||||||
// list invitation with accepted filter
|
// list invitation with accepted filter
|
||||||
let accepted_invs = bob_client
|
let accepted_invs = bob_client
|
||||||
.list_workspace_invitations(Some(AFWorkspaceInvitationStatus::Accepted))
|
.list_workspace_invitations(Some(AFWorkspaceInvitationStatus::Accepted))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue