feat: add role for user when querying workspaces (#983)

This commit is contained in:
Zack 2024-11-12 21:12:58 +08:00 committed by GitHub
parent 288fd59d3b
commit 85452ddfab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 138 additions and 20 deletions

View File

@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT workspace_id, role_id\n FROM af_workspace_member\n WHERE workspace_id = ANY($1)\n AND uid = (SELECT uid FROM public.af_user WHERE uuid = $2)\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "workspace_id",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "role_id",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"UuidArray",
"Uuid"
]
},
"nullable": [
false,
false
]
},
"hash": "aa75996ca6aa12f0bcaa5fb092ac279f8a94aadcc29d0e2b652dc420506835e7"
}

View File

@ -624,6 +624,8 @@ pub struct AFWorkspace {
pub created_at: DateTime<Utc>, pub created_at: DateTime<Utc>,
pub icon: String, pub icon: String,
pub member_count: Option<i64>, pub member_count: Option<i64>,
#[serde(default)]
pub role: Option<AFRole>, // role of the user requesting the workspace
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]

View File

@ -53,6 +53,7 @@ impl TryFrom<AFWorkspaceRow> for AFWorkspace {
created_at, created_at,
icon, icon,
member_count: None, member_count: None,
role: None,
}) })
} }
} }
@ -98,6 +99,7 @@ impl TryFrom<AFWorkspaceWithMemberCountRow> for AFWorkspace {
created_at, created_at,
icon, icon,
member_count: Some(value.member_count), member_count: Some(value.member_count),
role: None,
}) })
} }
} }

View File

@ -851,10 +851,32 @@ pub async fn select_member_count_for_workspaces<'a, E: Executor<'a, Database = P
}; };
ret.insert(row.workspace_id, count); ret.insert(row.workspace_id, count);
} }
for workspace_id in workspace_ids.iter() {
if !ret.contains_key(workspace_id) { Ok(ret)
ret.insert(*workspace_id, 0); }
}
pub async fn select_roles_for_workspaces(
pg_pool: &PgPool,
user_uuid: &Uuid,
workspace_ids: &[Uuid],
) -> Result<HashMap<Uuid, AFRole>, AppError> {
let query_res = sqlx::query!(
r#"
SELECT workspace_id, role_id
FROM af_workspace_member
WHERE workspace_id = ANY($1)
AND uid = (SELECT uid FROM public.af_user WHERE uuid = $2)
"#,
workspace_ids,
user_uuid,
)
.fetch_all(pg_pool)
.await?;
let mut ret = HashMap::with_capacity(workspace_ids.len());
for row in query_res {
let role = AFRole::from(row.role_id);
ret.insert(row.workspace_id, role);
} }
Ok(ret) Ok(ret)

View File

@ -261,6 +261,7 @@ impl Default for ViewLayout {
#[derive(Default, Debug, Deserialize, Serialize)] #[derive(Default, Debug, Deserialize, Serialize)]
pub struct QueryWorkspaceParam { pub struct QueryWorkspaceParam {
pub include_member_count: Option<bool>, pub include_member_count: Option<bool>,
pub include_role: Option<bool>,
} }
#[derive(Default, Debug, Deserialize, Serialize)] #[derive(Default, Debug, Deserialize, Serialize)]

View File

@ -338,10 +338,16 @@ async fn list_workspace_handler(
state: Data<AppState>, state: Data<AppState>,
query: web::Query<QueryWorkspaceParam>, query: web::Query<QueryWorkspaceParam>,
) -> Result<JsonAppResponse<Vec<AFWorkspace>>> { ) -> Result<JsonAppResponse<Vec<AFWorkspace>>> {
let QueryWorkspaceParam {
include_member_count,
include_role,
} = query.into_inner();
let workspaces = workspace::ops::get_all_user_workspaces( let workspaces = workspace::ops::get_all_user_workspaces(
&state.pg_pool, &state.pg_pool,
&uuid, &uuid,
query.into_inner().include_member_count.unwrap_or(false), include_member_count.unwrap_or(false),
include_role.unwrap_or(false),
) )
.await?; .await?;
Ok(AppResponse::Ok().with_data(workspaces).into()) Ok(AppResponse::Ok().with_data(workspaces).into())

View File

@ -256,6 +256,7 @@ pub async fn get_all_user_workspaces(
pg_pool: &PgPool, pg_pool: &PgPool,
user_uuid: &Uuid, user_uuid: &Uuid,
include_member_count: bool, include_member_count: bool,
include_role: bool,
) -> Result<Vec<AFWorkspace>, AppResponseError> { ) -> Result<Vec<AFWorkspace>, AppResponseError> {
let workspaces = select_all_user_workspaces(pg_pool, user_uuid).await?; let workspaces = select_all_user_workspaces(pg_pool, user_uuid).await?;
let mut workspaces = workspaces let mut workspaces = workspaces
@ -273,10 +274,23 @@ pub async fn get_all_user_workspaces(
.iter() .iter()
.map(|row| row.workspace_id) .map(|row| row.workspace_id)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let member_count_by_workspace_id = select_member_count_for_workspaces(pg_pool, &ids).await?; let mut member_count_by_workspace_id =
select_member_count_for_workspaces(pg_pool, &ids).await?;
for workspace in workspaces.iter_mut() { for workspace in workspaces.iter_mut() {
if let Some(member_count) = member_count_by_workspace_id.get(&workspace.workspace_id) { if let Some(member_count) = member_count_by_workspace_id.remove(&workspace.workspace_id) {
workspace.member_count = Some(*member_count); workspace.member_count = Some(member_count);
}
}
}
if include_role {
let ids = workspaces
.iter()
.map(|row| row.workspace_id)
.collect::<Vec<_>>();
let mut roles_by_workspace_id = select_roles_for_workspaces(pg_pool, user_uuid, &ids).await?;
for workspace in workspaces.iter_mut() {
if let Some(role) = roles_by_workspace_id.remove(&workspace.workspace_id) {
workspace.role = Some(role.clone());
} }
} }
} }

View File

@ -15,6 +15,15 @@ async fn invite_workspace_crud() {
.workspace_id; .workspace_id;
let (bob_client, bob) = generate_unique_registered_user_client().await; let (bob_client, bob) = generate_unique_registered_user_client().await;
let bob_workspace_id = bob_client
.get_workspaces()
.await
.unwrap()
.first()
.unwrap()
.workspace_id;
// alice invite bob to alice's workspace
alice_client alice_client
.invite_workspace_members( .invite_workspace_members(
alice_workspace_id.to_string().as_str(), alice_workspace_id.to_string().as_str(),
@ -94,16 +103,49 @@ async fn invite_workspace_crud() {
.unwrap(); .unwrap();
assert_eq!(accepted_invs.len(), 1); assert_eq!(accepted_invs.len(), 1);
// workspace now have 2 members {
let member_count = alice_client // alice's view of the workspaces
.get_workspaces_opt(QueryWorkspaceParam { let workspaces = alice_client
include_member_count: Some(true), .get_workspaces_opt(QueryWorkspaceParam {
}) include_member_count: Some(true),
.await include_role: Some(true),
.unwrap() })
.first() .await
.unwrap() .unwrap();
.member_count
.unwrap(); assert_eq!(workspaces.len(), 1);
assert_eq!(member_count, 2); assert_eq!(workspaces[0].workspace_id, alice_workspace_id);
assert_eq!(workspaces[0].member_count, Some(2));
assert_eq!(workspaces[0].role, Some(AFRole::Owner));
}
{
// bob's view of the workspaces
// bob should see 2 workspaces, one is his own and the other is alice's
let workspaces = bob_client
.get_workspaces_opt(QueryWorkspaceParam {
include_member_count: Some(true),
include_role: Some(true),
})
.await
.unwrap();
assert_eq!(workspaces.len(), 2);
{
let alice_workspace = workspaces
.iter()
.find(|w| w.workspace_id == alice_workspace_id)
.unwrap();
assert_eq!(alice_workspace.member_count, Some(2));
assert_eq!(alice_workspace.role, Some(AFRole::Member));
}
{
let bob_workspace = workspaces
.iter()
.find(|w| w.workspace_id == bob_workspace_id)
.unwrap();
println!("{:?}", bob_workspace);
assert_eq!(bob_workspace.member_count, Some(1));
assert_eq!(bob_workspace.role, Some(AFRole::Owner));
}
}
} }