mirror of https://github.com/buster-so/buster.git
Refactor workspace sharing permissions across assets
- Updated the `WorkspaceSharing` enum to use camelCase for serialization. - Introduced `workspace_permissions` field in update requests for chats, collections, dashboards, and metrics. - Implemented handling of workspace sharing permissions in respective update handlers, allowing for setting and removing permissions. - Adjusted frontend components and API interfaces to align with the new `workspace_sharing` naming convention. This change enhances the consistency and usability of workspace sharing across different asset types.
This commit is contained in:
parent
d9f9182ab2
commit
4e2b6c235e
|
@ -742,15 +742,15 @@ impl FromSql<sql_types::MessageFeedbackEnum, Pg> for MessageFeedback {
|
|||
Serialize,
|
||||
)]
|
||||
#[diesel(sql_type = sql_types::WorkspaceSharingEnum)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum WorkspaceSharing {
|
||||
#[serde(alias = "none")]
|
||||
None,
|
||||
#[serde(alias = "canView")]
|
||||
#[serde(alias = "can_view")]
|
||||
CanView,
|
||||
#[serde(alias = "canEdit")]
|
||||
#[serde(alias = "can_edit")]
|
||||
CanEdit,
|
||||
#[serde(alias = "fullAccess")]
|
||||
#[serde(alias = "full_access")]
|
||||
FullAccess,
|
||||
}
|
||||
|
||||
|
@ -776,4 +776,4 @@ impl FromSql<sql_types::WorkspaceSharingEnum, Pg> for WorkspaceSharing {
|
|||
_ => Err("Unrecognized WorkspaceSharing variant".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,14 @@ use anyhow::{anyhow, Result};
|
|||
use chrono::{DateTime, Utc};
|
||||
use database::{
|
||||
chats::fetch_chat_with_permission,
|
||||
enums::{AssetPermissionRole, AssetType},
|
||||
enums::{AssetPermissionRole, AssetType, WorkspaceSharing},
|
||||
pool::get_pg_pool,
|
||||
schema::chats::dsl,
|
||||
};
|
||||
use middleware::AuthenticatedUser;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use diesel::ExpressionMethods;
|
||||
use diesel_async::RunQueryDsl as AsyncRunQueryDsl;
|
||||
use sharing::{check_permission_access, create_share_by_email, types::UpdateField};
|
||||
use tracing::info;
|
||||
use uuid::Uuid;
|
||||
|
@ -30,6 +34,9 @@ pub struct UpdateChatSharingRequest {
|
|||
/// Expiration date for public access
|
||||
#[serde(default)]
|
||||
pub public_expiry_date: UpdateField<DateTime<Utc>>,
|
||||
/// Workspace sharing permissions
|
||||
#[serde(rename = "workspace_sharing")]
|
||||
pub workspace_permissions: Option<Option<WorkspaceSharing>>,
|
||||
}
|
||||
|
||||
/// Updates sharing permissions for a chat
|
||||
|
@ -115,6 +122,54 @@ pub async fn update_chat_sharing_handler(
|
|||
// If public sharing for chats is implemented in the future, this section will need to be updated
|
||||
// Following the pattern from metric_sharing_handler.rs and dashboard_sharing_handler.rs
|
||||
|
||||
// 4. Handle workspace_permissions if provided
|
||||
if let Some(workspace_perm) = request.workspace_permissions {
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
|
||||
match workspace_perm {
|
||||
Some(perm) => {
|
||||
info!(
|
||||
chat_id = %chat_id,
|
||||
"Setting workspace permissions for chat to {:?}",
|
||||
perm
|
||||
);
|
||||
diesel::update(dsl::chats)
|
||||
.filter(dsl::id.eq(chat_id))
|
||||
.set((
|
||||
dsl::workspace_sharing.eq(perm),
|
||||
dsl::workspace_sharing_enabled_by.eq(if perm != WorkspaceSharing::None {
|
||||
Some(user.id)
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
dsl::workspace_sharing_enabled_at.eq(if perm != WorkspaceSharing::None {
|
||||
Some(Utc::now())
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
}
|
||||
None => {
|
||||
// Setting to None means removing workspace sharing
|
||||
info!(
|
||||
chat_id = %chat_id,
|
||||
"Removing workspace permissions for chat"
|
||||
);
|
||||
diesel::update(dsl::chats)
|
||||
.filter(dsl::id.eq(chat_id))
|
||||
.set((
|
||||
dsl::workspace_sharing.eq(WorkspaceSharing::None),
|
||||
dsl::workspace_sharing_enabled_by.eq(None::<Uuid>),
|
||||
dsl::workspace_sharing_enabled_at.eq(None::<DateTime<Utc>>),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
use database::{
|
||||
enums::{AssetPermissionRole, AssetType},
|
||||
enums::{AssetPermissionRole, AssetType, WorkspaceSharing},
|
||||
helpers::collections::fetch_collection_with_permission,
|
||||
pool::get_pg_pool,
|
||||
schema::collections::dsl,
|
||||
};
|
||||
use middleware::AuthenticatedUser;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use diesel::ExpressionMethods;
|
||||
use diesel_async::RunQueryDsl as AsyncRunQueryDsl;
|
||||
use sharing::{
|
||||
check_permission_access,
|
||||
create_asset_permission::create_share_by_email,
|
||||
|
@ -37,6 +41,9 @@ pub struct UpdateCollectionSharingRequest {
|
|||
/// Note: Collections are not publicly accessible, this field is ignored
|
||||
#[serde(default)]
|
||||
pub public_expiry_date: UpdateField<DateTime<Utc>>,
|
||||
/// Workspace sharing permissions
|
||||
#[serde(rename = "workspace_sharing")]
|
||||
pub workspace_permissions: Option<Option<WorkspaceSharing>>,
|
||||
}
|
||||
|
||||
/// Update sharing permissions for a collection
|
||||
|
@ -112,5 +119,56 @@ pub async fn update_collection_sharing_handler(
|
|||
// 4. Public access settings are ignored for collections
|
||||
// Collections are not publicly accessible, so we ignore the public_* fields
|
||||
|
||||
// 5. Handle workspace_permissions if provided
|
||||
if let Some(workspace_perm) = request.workspace_permissions {
|
||||
let mut conn = get_pg_pool().get().await?;
|
||||
|
||||
// Load current collection data for updates
|
||||
let collection = collection_with_permission.collection;
|
||||
|
||||
match workspace_perm {
|
||||
Some(perm) => {
|
||||
info!(
|
||||
collection_id = %collection_id,
|
||||
"Setting workspace permissions for collection to {:?}",
|
||||
perm
|
||||
);
|
||||
diesel::update(dsl::collections)
|
||||
.filter(dsl::id.eq(collection_id))
|
||||
.set((
|
||||
dsl::workspace_sharing.eq(perm),
|
||||
dsl::workspace_sharing_enabled_by.eq(if perm != WorkspaceSharing::None {
|
||||
Some(user.id)
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
dsl::workspace_sharing_enabled_at.eq(if perm != WorkspaceSharing::None {
|
||||
Some(Utc::now())
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
}
|
||||
None => {
|
||||
// Setting to None means removing workspace sharing
|
||||
info!(
|
||||
collection_id = %collection_id,
|
||||
"Removing workspace permissions for collection"
|
||||
);
|
||||
diesel::update(dsl::collections)
|
||||
.filter(dsl::id.eq(collection_id))
|
||||
.set((
|
||||
dsl::workspace_sharing.eq(WorkspaceSharing::None),
|
||||
dsl::workspace_sharing_enabled_by.eq(None::<Uuid>),
|
||||
dsl::workspace_sharing_enabled_at.eq(None::<DateTime<Utc>>),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
use database::{
|
||||
enums::{AssetPermissionRole, AssetType},
|
||||
enums::{AssetPermissionRole, AssetType, WorkspaceSharing},
|
||||
helpers::dashboard_files::fetch_dashboard_file_with_permission,
|
||||
schema::dashboard_files::dsl,
|
||||
pool::get_pg_pool,
|
||||
|
@ -38,6 +38,9 @@ pub struct UpdateDashboardSharingRequest {
|
|||
/// Expiration date for public access
|
||||
#[serde(default)]
|
||||
pub public_expiry_date: UpdateField<DateTime<Utc>>,
|
||||
/// Workspace sharing permissions
|
||||
#[serde(rename = "workspace_sharing")]
|
||||
pub workspace_permissions: Option<Option<WorkspaceSharing>>,
|
||||
}
|
||||
|
||||
/// Updates sharing permissions for a dashboard
|
||||
|
@ -138,6 +141,9 @@ pub async fn update_dashboard_sharing_handler(
|
|||
let mut publicly_enabled_by = dashboard.publicly_enabled_by;
|
||||
let mut public_password = dashboard.public_password;
|
||||
let mut public_expiry_date = dashboard.public_expiry_date;
|
||||
let mut workspace_sharing = dashboard.workspace_sharing;
|
||||
let mut workspace_sharing_enabled_by = dashboard.workspace_sharing_enabled_by;
|
||||
let mut workspace_sharing_enabled_at = dashboard.workspace_sharing_enabled_at;
|
||||
let mut update_needed = false;
|
||||
|
||||
// Update publicly_accessible if provided
|
||||
|
@ -208,6 +214,42 @@ pub async fn update_dashboard_sharing_handler(
|
|||
UpdateField::NoChange => {}
|
||||
}
|
||||
|
||||
// Handle workspace_permissions
|
||||
if let Some(workspace_perm) = request.workspace_permissions {
|
||||
match workspace_perm {
|
||||
Some(perm) => {
|
||||
info!(
|
||||
dashboard_id = %dashboard_id,
|
||||
"Setting workspace permissions for dashboard to {:?}",
|
||||
perm
|
||||
);
|
||||
workspace_sharing = perm;
|
||||
workspace_sharing_enabled_by = if perm != WorkspaceSharing::None {
|
||||
Some(user.id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
workspace_sharing_enabled_at = if perm != WorkspaceSharing::None {
|
||||
Some(Utc::now())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
update_needed = true;
|
||||
}
|
||||
None => {
|
||||
// Setting to None means removing workspace sharing
|
||||
info!(
|
||||
dashboard_id = %dashboard_id,
|
||||
"Removing workspace permissions for dashboard"
|
||||
);
|
||||
workspace_sharing = WorkspaceSharing::None;
|
||||
workspace_sharing_enabled_by = None;
|
||||
workspace_sharing_enabled_at = None;
|
||||
update_needed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the update if any changes were made
|
||||
if update_needed {
|
||||
diesel::update(dsl::dashboard_files)
|
||||
|
@ -217,6 +259,9 @@ pub async fn update_dashboard_sharing_handler(
|
|||
dsl::publicly_enabled_by.eq(publicly_enabled_by),
|
||||
dsl::public_password.eq(public_password),
|
||||
dsl::public_expiry_date.eq(public_expiry_date),
|
||||
dsl::workspace_sharing.eq(workspace_sharing),
|
||||
dsl::workspace_sharing_enabled_by.eq(workspace_sharing_enabled_by),
|
||||
dsl::workspace_sharing_enabled_at.eq(workspace_sharing_enabled_at),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
use database::{
|
||||
enums::{AssetPermissionRole, AssetType},
|
||||
enums::{AssetPermissionRole, AssetType, WorkspaceSharing},
|
||||
helpers::metric_files::fetch_metric_file_with_permissions,
|
||||
pool::get_pg_pool,
|
||||
schema::metric_files::dsl,
|
||||
|
@ -38,6 +38,9 @@ pub struct UpdateMetricSharingRequest {
|
|||
/// Expiration date for public access
|
||||
#[serde(default)]
|
||||
pub public_expiry_date: UpdateField<DateTime<Utc>>,
|
||||
/// Workspace sharing permissions
|
||||
#[serde(rename = "workspace_sharing")]
|
||||
pub workspace_permissions: Option<Option<WorkspaceSharing>>,
|
||||
}
|
||||
|
||||
/// Handler to update sharing permissions for a metric
|
||||
|
@ -130,6 +133,9 @@ pub async fn update_metric_sharing_handler(
|
|||
let mut publicly_enabled_by = metric.publicly_enabled_by;
|
||||
let mut public_password = metric.public_password;
|
||||
let mut public_expiry_date = metric.public_expiry_date;
|
||||
let mut workspace_sharing = metric.workspace_sharing;
|
||||
let mut workspace_sharing_enabled_by = metric.workspace_sharing_enabled_by;
|
||||
let mut workspace_sharing_enabled_at = metric.workspace_sharing_enabled_at;
|
||||
let mut update_needed = false;
|
||||
|
||||
// Update publicly_accessible if provided
|
||||
|
@ -200,6 +206,42 @@ pub async fn update_metric_sharing_handler(
|
|||
UpdateField::NoChange => {}
|
||||
}
|
||||
|
||||
// Handle workspace_permissions
|
||||
if let Some(workspace_perm) = request.workspace_permissions {
|
||||
match workspace_perm {
|
||||
Some(perm) => {
|
||||
info!(
|
||||
metric_id = %metric_id,
|
||||
"Setting workspace permissions for metric to {:?}",
|
||||
perm
|
||||
);
|
||||
workspace_sharing = perm;
|
||||
workspace_sharing_enabled_by = if perm != WorkspaceSharing::None {
|
||||
Some(user.id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
workspace_sharing_enabled_at = if perm != WorkspaceSharing::None {
|
||||
Some(Utc::now())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
update_needed = true;
|
||||
}
|
||||
None => {
|
||||
// Setting to None means removing workspace sharing
|
||||
info!(
|
||||
metric_id = %metric_id,
|
||||
"Removing workspace permissions for metric"
|
||||
);
|
||||
workspace_sharing = WorkspaceSharing::None;
|
||||
workspace_sharing_enabled_by = None;
|
||||
workspace_sharing_enabled_at = None;
|
||||
update_needed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the update if any changes were made
|
||||
if update_needed {
|
||||
diesel::update(dsl::metric_files)
|
||||
|
@ -209,6 +251,9 @@ pub async fn update_metric_sharing_handler(
|
|||
dsl::publicly_enabled_by.eq(publicly_enabled_by),
|
||||
dsl::public_password.eq(public_password),
|
||||
dsl::public_expiry_date.eq(public_expiry_date),
|
||||
dsl::workspace_sharing.eq(workspace_sharing),
|
||||
dsl::workspace_sharing_enabled_by.eq(workspace_sharing_enabled_by),
|
||||
dsl::workspace_sharing_enabled_at.eq(workspace_sharing_enabled_at),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await?;
|
||||
|
|
|
@ -19,7 +19,7 @@ export type ShareUpdateRequest = {
|
|||
email: string;
|
||||
role: ShareRole;
|
||||
}[];
|
||||
workspace_permissions?: WorkspaceShareRole | null;
|
||||
workspace_sharing?: WorkspaceShareRole | null;
|
||||
publicly_accessible?: boolean;
|
||||
public_password?: string | null;
|
||||
public_expiry_date?: string | null;
|
||||
|
|
|
@ -246,8 +246,8 @@ export const useUpdateCollectionShare = () => {
|
|||
if (params.public_expiry_date !== undefined) {
|
||||
draft.public_expiry_date = params.public_expiry_date;
|
||||
}
|
||||
if (params.workspace_permissions !== undefined) {
|
||||
draft.workspace_permissions = params.workspace_permissions ? [params.workspace_permissions] : [];
|
||||
if (params.workspace_sharing !== undefined) {
|
||||
draft.workspace_sharing = params.workspace_sharing;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -437,8 +437,8 @@ export const useUpdateDashboardShare = () => {
|
|||
if (params.public_expiry_date !== undefined) {
|
||||
draft.public_expiry_date = params.public_expiry_date;
|
||||
}
|
||||
if (params.workspace_permissions !== undefined) {
|
||||
draft.workspace_permissions = params.workspace_permissions ? [params.workspace_permissions] : [];
|
||||
if (params.workspace_sharing !== undefined) {
|
||||
draft.workspace_sharing = params.workspace_sharing;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -316,8 +316,8 @@ export const useUpdateMetricShare = () => {
|
|||
if (variables.params.public_expiry_date !== undefined) {
|
||||
draft.public_expiry_date = variables.params.public_expiry_date;
|
||||
}
|
||||
if (variables.params.workspace_permissions !== undefined) {
|
||||
draft.workspace_permissions = variables.params.workspace_permissions ? [variables.params.workspace_permissions] : [];
|
||||
if (variables.params.workspace_sharing !== undefined) {
|
||||
draft.workspace_sharing = variables.params.workspace_sharing;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -34,6 +34,7 @@ const mockShareConfig: ShareConfig = {
|
|||
publicly_accessible: false,
|
||||
public_password: null,
|
||||
permission: 'owner',
|
||||
workspace_sharing: 'none'
|
||||
};
|
||||
|
||||
export const MetricShare: Story = {
|
||||
|
|
|
@ -104,7 +104,7 @@ const ShareMenuContentShare: React.FC<ShareMenuContentBodyProps> = React.memo(
|
|||
const payload: Parameters<typeof onUpdateMetricShare>[0] = {
|
||||
id: assetId,
|
||||
params: {
|
||||
workspace_permissions: role
|
||||
workspace_sharing: role
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -127,16 +127,6 @@ const ShareMenuContentShare: React.FC<ShareMenuContentBodyProps> = React.memo(
|
|||
/>
|
||||
)}
|
||||
|
||||
{canEditPermissions && (
|
||||
<WorkspaceShareSection
|
||||
shareAssetConfig={shareAssetConfig}
|
||||
assetType={assetType}
|
||||
assetId={assetId}
|
||||
canEditPermissions={canEditPermissions}
|
||||
onUpdateWorkspacePermissions={onUpdateWorkspacePermissions}
|
||||
/>
|
||||
)}
|
||||
|
||||
{hasIndividualPermissions && (
|
||||
<div className="flex flex-col space-y-2 overflow-hidden">
|
||||
{individual_permissions?.map((permission) => (
|
||||
|
@ -150,6 +140,16 @@ const ShareMenuContentShare: React.FC<ShareMenuContentBodyProps> = React.memo(
|
|||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{canEditPermissions && (
|
||||
<WorkspaceShareSection
|
||||
shareAssetConfig={shareAssetConfig}
|
||||
assetType={assetType}
|
||||
assetId={assetId}
|
||||
canEditPermissions={canEditPermissions}
|
||||
onUpdateWorkspacePermissions={onUpdateWorkspacePermissions}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import type { ShareAssetType, ShareConfig, WorkspaceShareRole } from '@buster/se
|
|||
import { Dropdown } from '@/components/ui/dropdown';
|
||||
import type { DropdownItem } from '@/components/ui/dropdown';
|
||||
import { ChevronDown } from '@/components/ui/icons/NucleoIconFilled';
|
||||
import { Office } from '@/components/ui/icons/NucleoIconOutlined';
|
||||
import { ApartmentBuilding } from '@/components/ui/icons/NucleoIconOutlined';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { cn } from '@/lib/classMerge';
|
||||
|
@ -44,7 +44,7 @@ export const WorkspaceShareSection: React.FC<WorkspaceShareSectionProps> = React
|
|||
canEditPermissions,
|
||||
onUpdateWorkspacePermissions
|
||||
}) => {
|
||||
const currentRole = shareAssetConfig.workspace_permissions?.[0] || 'none';
|
||||
const currentRole = shareAssetConfig.workspace_sharing || 'none';
|
||||
|
||||
const selectedLabel = React.useMemo(() => {
|
||||
const selectedItem = workspaceShareRoleItems.find(item => item.value === currentRole);
|
||||
|
@ -66,7 +66,7 @@ export const WorkspaceShareSection: React.FC<WorkspaceShareSectionProps> = React
|
|||
<div className="flex h-8 items-center justify-between space-x-2 overflow-hidden">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="flex h-6 w-6 items-center justify-center rounded bg-gray-100 text-gray-600">
|
||||
<Office />
|
||||
<ApartmentBuilding />
|
||||
</div>
|
||||
<div className="flex flex-col overflow-hidden">
|
||||
<Text className="truncate font-medium">Workspace</Text>
|
||||
|
|
|
@ -17,7 +17,7 @@ export const getShareAssetConfig = (
|
|||
public_enabled_by,
|
||||
publicly_accessible,
|
||||
public_password,
|
||||
workspace_permissions
|
||||
workspace_sharing
|
||||
} = message;
|
||||
|
||||
return {
|
||||
|
@ -27,6 +27,6 @@ export const getShareAssetConfig = (
|
|||
public_enabled_by,
|
||||
publicly_accessible,
|
||||
public_password,
|
||||
workspace_permissions
|
||||
workspace_sharing
|
||||
};
|
||||
};
|
||||
|
|
|
@ -76,7 +76,7 @@ export const ShareUpdateRequestSchema = z.object({
|
|||
publicly_accessible: z.boolean().optional(),
|
||||
public_password: z.string().nullable().optional(),
|
||||
public_expiry_date: z.string().nullable().optional(),
|
||||
workspace_permissions: WorkspaceShareRoleSchema.nullable().optional(),
|
||||
workspace_sharing: WorkspaceShareRoleSchema.nullable().optional(),
|
||||
});
|
||||
|
||||
export type GetMetricDataRequest = z.infer<typeof GetMetricDataRequestSchema>;
|
||||
|
|
|
@ -32,7 +32,7 @@ export const ShareConfigSchema = z.object({
|
|||
publicly_accessible: z.boolean(),
|
||||
public_password: z.string().nullable(),
|
||||
permission: ShareRoleSchema, //this is the permission the user has to the metric, dashboard or collection
|
||||
workspace_permissions: z.array(WorkspaceShareRoleSchema).nullable(),
|
||||
workspace_sharing: WorkspaceShareRoleSchema.nullable(),
|
||||
});
|
||||
|
||||
// Export the inferred types
|
||||
|
|
Loading…
Reference in New Issue