Add avatar_url to user-related responses across multiple endpoints

Co-authored-by: dallin <dallin@buster.so>
This commit is contained in:
Cursor Agent 2025-07-09 13:50:45 +00:00
parent 0015a7b0e5
commit 33afab8729
7 changed files with 36 additions and 15 deletions

View File

@ -82,6 +82,7 @@ async fn get_permissioned_collections(
users::id, users::id,
users::name.nullable(), users::name.nullable(),
users::email, users::email,
users::avatar_url.nullable(),
collections::organization_id, collections::organization_id,
)) ))
.filter(collections::deleted_at.is_null()) .filter(collections::deleted_at.is_null())
@ -124,6 +125,7 @@ async fn get_permissioned_collections(
Uuid, Uuid,
Option<String>, Option<String>,
String, String,
Option<String>,
Uuid, Uuid,
)>(&mut conn) )>(&mut conn)
.await .await
@ -136,7 +138,7 @@ async fn get_permissioned_collections(
// Filter collections based on user permissions // Filter collections based on user permissions
// We'll include collections where the user has at least CanView permission // We'll include collections where the user has at least CanView permission
for (id, name, updated_at, created_at, role, creator_id, creator_name, email, org_id) in collection_results { for (id, name, updated_at, created_at, role, creator_id, creator_name, email, creator_avatar_url, org_id) in collection_results {
// Check if user has at least CanView permission // Check if user has at least CanView permission
let has_permission = check_permission_access( let has_permission = check_permission_access(
Some(role), Some(role),
@ -157,7 +159,7 @@ async fn get_permissioned_collections(
let owner = ListCollectionsUser { let owner = ListCollectionsUser {
id: creator_id, id: creator_id,
name: creator_name.unwrap_or(email), name: creator_name.unwrap_or(email),
avatar_url: None, avatar_url: creator_avatar_url,
}; };
let collection = ListCollectionsCollection { let collection = ListCollectionsCollection {

View File

@ -64,6 +64,7 @@ pub async fn list_dashboard_handler(
dashboard_files::updated_at, dashboard_files::updated_at,
asset_permissions::role, asset_permissions::role,
users::name.nullable(), users::name.nullable(),
users::avatar_url.nullable(),
dashboard_files::organization_id, dashboard_files::organization_id,
)) ))
.filter(dashboard_files::deleted_at.is_null()) .filter(dashboard_files::deleted_at.is_null())
@ -106,6 +107,7 @@ pub async fn list_dashboard_handler(
DateTime<Utc>, DateTime<Utc>,
AssetPermissionRole, AssetPermissionRole,
Option<String>, Option<String>,
Option<String>,
Uuid, Uuid,
)>(&mut conn) )>(&mut conn)
.await .await
@ -118,7 +120,7 @@ pub async fn list_dashboard_handler(
// We'll include dashboards where the user has at least CanView permission // We'll include dashboards where the user has at least CanView permission
let mut dashboards = Vec::new(); let mut dashboards = Vec::new();
for (id, name, created_by, created_at, updated_at, role, creator_name, org_id) in for (id, name, created_by, created_at, updated_at, role, creator_name, creator_avatar_url, org_id) in
dashboard_results dashboard_results
{ {
// Check if user has at least CanView permission // Check if user has at least CanView permission
@ -141,7 +143,7 @@ pub async fn list_dashboard_handler(
let owner = DashboardMember { let owner = DashboardMember {
id: created_by, id: created_by,
name: creator_name.unwrap_or_else(|| "Unknown".to_string()), name: creator_name.unwrap_or_else(|| "Unknown".to_string()),
avatar_url: None, avatar_url: creator_avatar_url,
}; };
let dashboard_item = BusterDashboardListItem { let dashboard_item = BusterDashboardListItem {

View File

@ -20,6 +20,7 @@ pub struct UserInfo {
pub id: Uuid, pub id: Uuid,
pub name: String, pub name: String,
pub email: String, pub email: String,
pub avatar_url: Option<String>,
pub assigned: bool, pub assigned: bool,
} }
@ -77,20 +78,22 @@ async fn list_users_handler(user: AuthenticatedUser, dataset_group_id: Uuid) ->
users::id, users::id,
users::name.nullable(), users::name.nullable(),
users::email, users::email,
users::avatar_url.nullable(),
diesel::dsl::sql::<diesel::sql_types::Bool>( diesel::dsl::sql::<diesel::sql_types::Bool>(
"dataset_groups_permissions.id IS NOT NULL", "dataset_groups_permissions.id IS NOT NULL",
), ),
)) ))
.order_by(users::created_at.desc()) .order_by(users::created_at.desc())
.load::<(Uuid, Option<String>, String, bool)>(&mut *conn) .load::<(Uuid, Option<String>, String, Option<String>, bool)>(&mut *conn)
.await?; .await?;
Ok(users Ok(users
.into_iter() .into_iter()
.map(|(id, name, email, assigned)| UserInfo { .map(|(id, name, email, avatar_url, assigned)| UserInfo {
id, id,
name: name.unwrap_or("".to_string()), name: name.unwrap_or_else(|| email.clone()),
email, email,
avatar_url,
assigned, assigned,
}) })
.collect()) .collect())

View File

@ -34,6 +34,7 @@ pub struct UserOverviewItem {
pub id: Uuid, pub id: Uuid,
pub name: String, pub name: String,
pub email: String, pub email: String,
pub avatar_url: Option<String>,
pub can_query: bool, pub can_query: bool,
pub lineage: Vec<Vec<UserPermissionLineage>>, pub lineage: Vec<Vec<UserPermissionLineage>>,
} }
@ -86,15 +87,16 @@ pub async fn get_dataset_overview(
users::email, users::email,
users_to_organizations::role, users_to_organizations::role,
users::name.nullable(), users::name.nullable(),
users::avatar_url.nullable(),
)) ))
.load::<(Uuid, String, UserOrganizationRole, Option<String>)>(&mut conn) .load::<(Uuid, String, UserOrganizationRole, Option<String>, Option<String>)>(&mut conn)
.await .await
.map_err(|e| { .map_err(|e| {
tracing::error!("Error getting users: {:?}", e); tracing::error!("Error getting users: {:?}", e);
(StatusCode::INTERNAL_SERVER_ERROR, "Database error") (StatusCode::INTERNAL_SERVER_ERROR, "Database error")
})?; })?;
let user_ids = users.iter().map(|(id, _, _, _)| *id).collect::<Vec<_>>(); let user_ids = users.iter().map(|(id, _, _, _, _)| *id).collect::<Vec<_>>();
// Direct dataset access // Direct dataset access
let datasets_query: Vec<(Uuid, String, Uuid)> = dataset_permissions::table let datasets_query: Vec<(Uuid, String, Uuid)> = dataset_permissions::table
@ -207,7 +209,7 @@ pub async fn get_dataset_overview(
let users = users let users = users
.into_iter() .into_iter()
.map(|(id, email, role, name)| { .map(|(id, email, role, name, avatar_url)| {
let can_query = match role { let can_query = match role {
UserOrganizationRole::WorkspaceAdmin | UserOrganizationRole::DataAdmin | UserOrganizationRole::Querier => true, UserOrganizationRole::WorkspaceAdmin | UserOrganizationRole::DataAdmin | UserOrganizationRole::Querier => true,
UserOrganizationRole::RestrictedQuerier => { UserOrganizationRole::RestrictedQuerier => {
@ -375,6 +377,7 @@ pub async fn get_dataset_overview(
id, id,
name: name.unwrap_or(email.clone()), name: name.unwrap_or(email.clone()),
email, email,
avatar_url,
can_query, can_query,
lineage, lineage,
} }

View File

@ -181,6 +181,7 @@ async fn get_org_datasets(
users::id, users::id,
users::name.nullable(), users::name.nullable(),
users::email, users::email,
users::avatar_url.nullable(),
data_sources::id, data_sources::id,
data_sources::name, data_sources::name,
)) ))
@ -194,6 +195,7 @@ async fn get_org_datasets(
users::id, users::id,
users::name, users::name,
users::email, users::email,
users::avatar_url,
data_sources::id, data_sources::id,
data_sources::name, data_sources::name,
)) ))
@ -228,6 +230,7 @@ async fn get_org_datasets(
Uuid, Uuid,
Option<String>, Option<String>,
String, String,
Option<String>,
Uuid, Uuid,
String, String,
)>(&mut conn) )>(&mut conn)
@ -250,6 +253,7 @@ async fn get_org_datasets(
user_id, user_id,
user_name, user_name,
user_email, user_email,
user_avatar_url,
data_source_id, data_source_id,
data_source_name, data_source_name,
)| { )| {
@ -268,7 +272,7 @@ async fn get_org_datasets(
owner: Some(ListDatasetOwner { owner: Some(ListDatasetOwner {
id: user_id, id: user_id,
name: user_name.unwrap_or(user_email), name: user_name.unwrap_or(user_email),
avatar_url: None, avatar_url: user_avatar_url,
}), }),
belongs_to: None, belongs_to: None,
} }

View File

@ -19,6 +19,7 @@ pub struct UserResponse {
pub id: Uuid, pub id: Uuid,
pub name: Option<String>, pub name: Option<String>,
pub email: String, pub email: String,
pub avatar_url: Option<String>,
pub role: UserOrganizationRole, pub role: UserOrganizationRole,
pub status: UserOrganizationStatus, pub status: UserOrganizationStatus,
} }
@ -50,6 +51,7 @@ async fn list_organization_users_handler(organization_id: Uuid) -> Result<Vec<Us
users::id, users::id,
users::email, users::email,
users::name.nullable(), users::name.nullable(),
users::avatar_url.nullable(),
users_to_organizations::role, users_to_organizations::role,
users_to_organizations::status, users_to_organizations::status,
)) ))
@ -59,6 +61,7 @@ async fn list_organization_users_handler(organization_id: Uuid) -> Result<Vec<Us
Uuid, Uuid,
String, String,
Option<String>, Option<String>,
Option<String>,
UserOrganizationRole, UserOrganizationRole,
UserOrganizationStatus, UserOrganizationStatus,
)>(&mut conn) )>(&mut conn)
@ -66,10 +69,11 @@ async fn list_organization_users_handler(organization_id: Uuid) -> Result<Vec<Us
Ok(users Ok(users
.into_iter() .into_iter()
.map(|(id, email, name, role, status)| UserResponse { .map(|(id, email, name, avatar_url, role, status)| UserResponse {
id, id,
name, name,
email, email,
avatar_url,
role, role,
status, status,
}) })

View File

@ -21,6 +21,7 @@ pub struct UserInfo {
pub id: Uuid, pub id: Uuid,
pub name: String, pub name: String,
pub email: String, pub email: String,
pub avatar_url: Option<String>,
pub assigned: bool, pub assigned: bool,
} }
@ -76,20 +77,22 @@ async fn list_users_handler(user: AuthenticatedUser, permission_group_id: Uuid)
users::id, users::id,
users::name.nullable(), users::name.nullable(),
users::email, users::email,
users::avatar_url.nullable(),
diesel::dsl::sql::<diesel::sql_types::Bool>( diesel::dsl::sql::<diesel::sql_types::Bool>(
"permission_groups_to_identities.identity_id IS NOT NULL", "permission_groups_to_identities.identity_id IS NOT NULL",
), ),
)) ))
.order_by(users::created_at.desc()) .order_by(users::created_at.desc())
.load::<(Uuid, Option<String>, String, bool)>(&mut *conn) .load::<(Uuid, Option<String>, String, Option<String>, bool)>(&mut *conn)
.await?; .await?;
Ok(users Ok(users
.into_iter() .into_iter()
.map(|(id, name, email, assigned)| UserInfo { .map(|(id, name, email, avatar_url, assigned)| UserInfo {
id, id,
name: name.unwrap_or("".to_string()), name: name.unwrap_or_else(|| email.clone()),
email, email,
avatar_url,
assigned, assigned,
}) })
.collect()) .collect())