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 icon: String,
pub member_count: Option<i64>,
#[serde(default)]
pub role: Option<AFRole>, // role of the user requesting the workspace
}
#[derive(Serialize, Deserialize)]

View file

@ -53,6 +53,7 @@ impl TryFrom<AFWorkspaceRow> for AFWorkspace {
created_at,
icon,
member_count: None,
role: None,
})
}
}
@ -98,6 +99,7 @@ impl TryFrom<AFWorkspaceWithMemberCountRow> for AFWorkspace {
created_at,
icon,
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);
}
for workspace_id in workspace_ids.iter() {
if !ret.contains_key(workspace_id) {
ret.insert(*workspace_id, 0);
}
Ok(ret)
}
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)

View file

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

View file

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

View file

@ -256,6 +256,7 @@ pub async fn get_all_user_workspaces(
pg_pool: &PgPool,
user_uuid: &Uuid,
include_member_count: bool,
include_role: bool,
) -> Result<Vec<AFWorkspace>, AppResponseError> {
let workspaces = select_all_user_workspaces(pg_pool, user_uuid).await?;
let mut workspaces = workspaces
@ -273,10 +274,23 @@ pub async fn get_all_user_workspaces(
.iter()
.map(|row| row.workspace_id)
.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() {
if let Some(member_count) = member_count_by_workspace_id.get(&workspace.workspace_id) {
workspace.member_count = Some(*member_count);
if let Some(member_count) = member_count_by_workspace_id.remove(&workspace.workspace_id) {
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;
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
.invite_workspace_members(
alice_workspace_id.to_string().as_str(),
@ -94,16 +103,49 @@ async fn invite_workspace_crud() {
.unwrap();
assert_eq!(accepted_invs.len(), 1);
// workspace now have 2 members
let member_count = alice_client
.get_workspaces_opt(QueryWorkspaceParam {
include_member_count: Some(true),
})
.await
.unwrap()
.first()
.unwrap()
.member_count
.unwrap();
assert_eq!(member_count, 2);
{
// alice's view of the workspaces
let workspaces = alice_client
.get_workspaces_opt(QueryWorkspaceParam {
include_member_count: Some(true),
include_role: Some(true),
})
.await
.unwrap();
assert_eq!(workspaces.len(), 1);
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));
}
}
}