mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 02:28:30 +08:00
fix: allow workflow comments for layout users (#39014)
This commit is contained in:
parent
e5b7281eb9
commit
1121a17c11
@ -114,10 +114,9 @@ vi.mock('@/app/components/workflow', () => ({
|
||||
hooksStore,
|
||||
}: PropsWithChildren<{
|
||||
onWorkflowDataUpdate?: (payload: unknown) => void
|
||||
hooksStore?: { accessControl?: { canComment?: boolean; canEdit?: boolean } }
|
||||
hooksStore?: { accessControl?: { canEdit?: boolean } }
|
||||
}>) => (
|
||||
<div data-testid="workflow-inner-context">
|
||||
<div data-testid="can-comment">{String(hooksStore?.accessControl?.canComment)}</div>
|
||||
<div data-testid="can-edit">{String(hooksStore?.accessControl?.canEdit)}</div>
|
||||
{children}
|
||||
<button
|
||||
@ -235,15 +234,6 @@ describe('RagPipelineMain', () => {
|
||||
})
|
||||
|
||||
describe('hooks integration', () => {
|
||||
it('should pass readonly ACL permission as comment-only workflow access', () => {
|
||||
mockPermissionState.permissionKeys = [DatasetACLPermission.Readonly]
|
||||
|
||||
render(<RagPipelineMain {...defaultProps} />)
|
||||
|
||||
expect(screen.getByTestId('can-comment')).toHaveTextContent('true')
|
||||
expect(screen.getByTestId('can-edit')).toHaveTextContent('false')
|
||||
})
|
||||
|
||||
it('should use useNodesSyncDraft hook', () => {
|
||||
render(<RagPipelineMain {...defaultProps} />)
|
||||
|
||||
|
||||
@ -134,7 +134,6 @@ const RagPipelineMain = ({ nodes, edges, viewport }: RagPipelineMainProps) => {
|
||||
invalidateConversationVarValues,
|
||||
accessControl: {
|
||||
canEdit: datasetACLCapabilities.canEdit,
|
||||
canComment: datasetACLCapabilities.canReadonly || datasetACLCapabilities.canEdit,
|
||||
canRun: datasetACLCapabilities.canPipelineTest,
|
||||
canImportExportDSL: datasetACLCapabilities.canImportExportDSL,
|
||||
canReleaseAndVersion: datasetACLCapabilities.canPipelineRelease,
|
||||
|
||||
@ -339,7 +339,6 @@ const SnippetMain = ({
|
||||
invalidateConversationVarValues,
|
||||
accessControl: {
|
||||
canEdit: canEditSnippet,
|
||||
canComment: true,
|
||||
canRun: true,
|
||||
canImportExportDSL: canEditSnippet,
|
||||
canReleaseAndVersion: canEditSnippet,
|
||||
|
||||
@ -4,7 +4,6 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { AppACLPermission } from '@/utils/permission'
|
||||
import WorkflowMain from '../workflow-main'
|
||||
|
||||
const mockSetFeatures = vi.fn()
|
||||
@ -445,24 +444,6 @@ describe('WorkflowMain', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should pass view-layout ACL permission as comment-only workflow access', () => {
|
||||
useAppStore.setState({
|
||||
appDetail: {
|
||||
permission_keys: [AppACLPermission.ViewLayout],
|
||||
} as never,
|
||||
})
|
||||
|
||||
render(<WorkflowMain nodes={[]} edges={[]} viewport={{ x: 0, y: 0, zoom: 1 }} />)
|
||||
|
||||
expect(capturedContextProps?.hooksStore).toMatchObject({
|
||||
accessControl: {
|
||||
canEdit: false,
|
||||
canComment: true,
|
||||
canRun: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('passes collaboration props and tracks cursors when collaboration is enabled', () => {
|
||||
collaborationRuntime.isEnabled = true
|
||||
collaborationRuntime.isConnected = true
|
||||
|
||||
@ -297,7 +297,6 @@ const WorkflowMain = ({ nodes, edges, viewport }: WorkflowMainProps) => {
|
||||
invalidateConversationVarValues,
|
||||
accessControl: {
|
||||
canEdit: appACLCapabilities.canEdit,
|
||||
canComment: appACLCapabilities.canComment,
|
||||
canRun: appACLCapabilities.canTestAndRun,
|
||||
canImportExportDSL: appACLCapabilities.canImportExportDSL,
|
||||
canReleaseAndVersion: appACLCapabilities.canReleaseAndVersion,
|
||||
|
||||
@ -189,7 +189,7 @@ describe('PanelContextmenu', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should hide add note when editing is denied but comments are allowed', async () => {
|
||||
it('should hide add note but keep comments available when editing is denied', async () => {
|
||||
mockUseWorkflowMoveMode.mockReturnValue({
|
||||
isCommentModeAvailable: true,
|
||||
})
|
||||
@ -202,7 +202,6 @@ describe('PanelContextmenu', () => {
|
||||
accessControl: {
|
||||
...fullWorkflowAccessControl,
|
||||
canEdit: false,
|
||||
canComment: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@ -28,7 +28,6 @@ export type SyncDraftCallback = {
|
||||
|
||||
export type WorkflowAccessControl = {
|
||||
canEdit: boolean
|
||||
canComment: boolean
|
||||
canRun: boolean
|
||||
canImportExportDSL: boolean
|
||||
canReleaseAndVersion: boolean
|
||||
@ -36,7 +35,6 @@ export type WorkflowAccessControl = {
|
||||
|
||||
export const fullWorkflowAccessControl: WorkflowAccessControl = {
|
||||
canEdit: true,
|
||||
canComment: true,
|
||||
canRun: true,
|
||||
canImportExportDSL: true,
|
||||
canReleaseAndVersion: true,
|
||||
|
||||
@ -142,28 +142,4 @@ describe('useWorkflowMoveMode', () => {
|
||||
expect(store.getState().controlMode).toBe(ControlMode.Pointer)
|
||||
expect(mockHandleSelectionCancel).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not switch to comment mode when commenting is denied', () => {
|
||||
const { result, store } = renderWorkflowHook(() => useWorkflowMoveMode(), {
|
||||
initialStoreState: {
|
||||
controlMode: ControlMode.Pointer,
|
||||
},
|
||||
hooksStoreProps: {
|
||||
accessControl: {
|
||||
canEdit: true,
|
||||
canComment: false,
|
||||
canRun: true,
|
||||
canImportExportDSL: true,
|
||||
canReleaseAndVersion: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.handleModeComment()
|
||||
})
|
||||
|
||||
expect(store.getState().controlMode).toBe(ControlMode.Pointer)
|
||||
expect(mockHandleSelectionCancel).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@ -225,7 +225,6 @@ describe('useNodesReadOnly', () => {
|
||||
hooksStoreProps: {
|
||||
accessControl: {
|
||||
canEdit: false,
|
||||
canComment: true,
|
||||
canRun: true,
|
||||
canImportExportDSL: true,
|
||||
canReleaseAndVersion: true,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useCallback } from 'react'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { useHooksStore } from '../hooks-store'
|
||||
import { useStore, useWorkflowStore } from '../store'
|
||||
import { ControlMode, WorkflowRunningStatus } from '../types'
|
||||
import { useEdgesInteractionsWithoutSync } from './use-edges-interactions-without-sync'
|
||||
@ -33,7 +32,6 @@ export const useWorkflowMoveMode = () => {
|
||||
const workflowRunningData = useStore((s) => s.workflowRunningData)
|
||||
const historyWorkflowData = useStore((s) => s.historyWorkflowData)
|
||||
const isRestoring = useStore((s) => s.isRestoring)
|
||||
const canComment = useHooksStore((s) => s.accessControl.canComment)
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const { handleSelectionCancel } = useSelectionInteractions()
|
||||
const { data: isCommentModeAvailable } = useSuspenseQuery({
|
||||
@ -46,11 +44,7 @@ export const useWorkflowMoveMode = () => {
|
||||
historyWorkflowData ||
|
||||
isRestoring
|
||||
)
|
||||
const canUseCommentMode = !!(
|
||||
canComment &&
|
||||
!isCommentModeOperationBlocked &&
|
||||
isCommentModeAvailable
|
||||
)
|
||||
const canUseCommentMode = !!(!isCommentModeOperationBlocked && isCommentModeAvailable)
|
||||
|
||||
const handleModePointer = useCallback(() => {
|
||||
if (getNodesReadOnly()) return
|
||||
|
||||
@ -147,7 +147,6 @@ describe('NodeControl', () => {
|
||||
it('should hide the run control when workflow run permission is missing', () => {
|
||||
renderNodeControl(<NodeControlHarness id="node-5" data={makeData()} />, {
|
||||
canEdit: false,
|
||||
canComment: true,
|
||||
canRun: false,
|
||||
canImportExportDSL: false,
|
||||
canReleaseAndVersion: false,
|
||||
|
||||
@ -22,8 +22,6 @@ const {
|
||||
}))
|
||||
|
||||
let mockNodesReadOnly = false
|
||||
let mockCanComment = true
|
||||
let mockCanEdit = true
|
||||
let mockCanUseCommentMode = true
|
||||
let mockIsCommentModeAvailable = true
|
||||
let mockStoreState: WorkflowStoreState
|
||||
@ -55,18 +53,6 @@ vi.mock('../../store', () => ({
|
||||
useStore: (selector: (state: WorkflowStoreState) => unknown) => selector(mockStoreState),
|
||||
}))
|
||||
|
||||
vi.mock('../../hooks-store', () => ({
|
||||
useHooksStore: <T,>(
|
||||
selector: (state: { accessControl: { canComment: boolean; canEdit: boolean } }) => T,
|
||||
): T =>
|
||||
selector({
|
||||
accessControl: {
|
||||
canComment: mockCanComment,
|
||||
canEdit: mockCanEdit,
|
||||
},
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('../add-block', () => ({
|
||||
default: () => <div data-testid="add-block" />,
|
||||
}))
|
||||
@ -85,8 +71,6 @@ describe('Control', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockNodesReadOnly = false
|
||||
mockCanComment = true
|
||||
mockCanEdit = true
|
||||
mockCanUseCommentMode = true
|
||||
mockIsCommentModeAvailable = true
|
||||
mockStoreState = {
|
||||
@ -151,8 +135,6 @@ describe('Control', () => {
|
||||
})
|
||||
|
||||
it('should block note creation when editing is not allowed', () => {
|
||||
mockCanEdit = false
|
||||
mockCanComment = true
|
||||
mockNodesReadOnly = true
|
||||
|
||||
render(<Control />)
|
||||
@ -164,7 +146,7 @@ describe('Control', () => {
|
||||
expect(mockHandleAddNote).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should keep comment mode enabled for readonly layout users who can comment', () => {
|
||||
it('should keep comment mode enabled when nodes are read-only', () => {
|
||||
mockNodesReadOnly = true
|
||||
mockCanUseCommentMode = true
|
||||
|
||||
|
||||
@ -50,7 +50,6 @@ export function PanelContextmenu({ onClose }: { onClose: () => void }) {
|
||||
isRestoring
|
||||
)
|
||||
const canEditWorkflow = accessControl.canEdit && !workflowOperationReadOnly
|
||||
const canCommentWorkflow = accessControl.canComment && !workflowOperationReadOnly
|
||||
const shouldHideImportApp = flowType === FlowType.snippet || isSnippetCanvas()
|
||||
|
||||
const renderAddBlockTrigger = useCallback(() => {
|
||||
@ -105,7 +104,7 @@ export function PanelContextmenu({ onClose }: { onClose: () => void }) {
|
||||
{t(($) => $['nodes.note.addNote'], { ns: 'workflow' })}
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
{canCommentWorkflow && isCommentModeAvailable && (
|
||||
{!workflowOperationReadOnly && isCommentModeAvailable && (
|
||||
<ContextMenuItem
|
||||
disabled={!!pendingComment}
|
||||
className={cn(
|
||||
|
||||
@ -311,7 +311,6 @@ describe('InputsPanel', () => {
|
||||
handleRun,
|
||||
accessControl: {
|
||||
canEdit: true,
|
||||
canComment: true,
|
||||
canRun: false,
|
||||
canImportExportDSL: true,
|
||||
canReleaseAndVersion: true,
|
||||
|
||||
@ -25,21 +25,19 @@ describe('permission', () => {
|
||||
})
|
||||
|
||||
describe('getAppACLCapabilities', () => {
|
||||
it('allows test-and-run users to access layout without edit or comment', () => {
|
||||
it('allows test-and-run users to access layout without edit', () => {
|
||||
const capabilities = getAppACLCapabilities([AppACLPermission.TestAndRun])
|
||||
|
||||
expect(capabilities.canTestAndRun).toBe(true)
|
||||
expect(capabilities.canAccessLayout).toBe(true)
|
||||
expect(capabilities.canComment).toBe(false)
|
||||
expect(capabilities.canEdit).toBe(false)
|
||||
})
|
||||
|
||||
it('allows view-layout users to preview the app and comment but not run/debug', () => {
|
||||
it('allows view-layout users to preview the app but not run/debug', () => {
|
||||
const capabilities = getAppACLCapabilities([AppACLPermission.ViewLayout])
|
||||
|
||||
expect(capabilities.canPreviewApp).toBe(true)
|
||||
expect(capabilities.canAccessLayout).toBe(true)
|
||||
expect(capabilities.canComment).toBe(true)
|
||||
expect(capabilities.canTestAndRun).toBe(false)
|
||||
})
|
||||
|
||||
|
||||
@ -47,7 +47,6 @@ type AppACLCapabilities = {
|
||||
canTestAndRun: boolean
|
||||
canEdit: boolean
|
||||
canAccessLayout: boolean
|
||||
canComment: boolean
|
||||
canPreviewApp: boolean
|
||||
canImportExportDSL: boolean
|
||||
canDelete: boolean
|
||||
@ -144,7 +143,6 @@ export const getAppACLCapabilities = (
|
||||
canTestAndRun,
|
||||
canEdit,
|
||||
canAccessLayout: canViewLayout || canTestAndRun || canEdit,
|
||||
canComment: canViewLayout || canEdit,
|
||||
canPreviewApp: canViewLayout || canTestAndRun,
|
||||
canImportExportDSL: hasResourcePermission(
|
||||
permissionKeys,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user