mirror of
https://github.com/langgenius/dify.git
synced 2026-05-10 14:14:17 +08:00
parent
4da8afaed5
commit
e994009476
@ -24,7 +24,7 @@ describe('TypeSelector', () => {
|
||||
)
|
||||
|
||||
await user.click(screen.getByRole('combobox'))
|
||||
const numberOption = await screen.findByRole('option', { name: 'Number' })
|
||||
const [, numberOption] = await screen.findAllByRole('option')
|
||||
await user.click(numberOption)
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith({ value: 'number', name: 'Number' })
|
||||
@ -46,10 +46,8 @@ describe('TypeSelector', () => {
|
||||
|
||||
await user.click(screen.getByRole('combobox'))
|
||||
|
||||
const numberOption = await screen.findByRole('option', { name: 'Number' })
|
||||
const [, numberOption] = await screen.findAllByRole('option')
|
||||
const popup = numberOption.closest('[data-side]')
|
||||
if (!popup)
|
||||
throw new Error('Expected popup container to exist')
|
||||
|
||||
expect(popup).toHaveClass('w-(--anchor-width)')
|
||||
})
|
||||
|
||||
@ -1,23 +1,11 @@
|
||||
import type { Node } from '@/app/components/workflow/types'
|
||||
import { BlockEnum, SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import {
|
||||
buildInitialFeatures,
|
||||
buildTriggerStatusMap,
|
||||
coerceReplayUserInputs,
|
||||
normalizeWorkflowNodesForBackend,
|
||||
normalizeWorkflowNodesForFrontend,
|
||||
} from '../utils'
|
||||
|
||||
type HumanInputTestField = {
|
||||
type: string
|
||||
output_variable_name: string
|
||||
}
|
||||
|
||||
type HumanInputTestNode = Node<{
|
||||
inputs: HumanInputTestField[]
|
||||
}>
|
||||
|
||||
describe('workflow-app utils', () => {
|
||||
it('should map trigger statuses to enabled and disabled states', () => {
|
||||
expect(buildTriggerStatusMap([
|
||||
@ -50,51 +38,6 @@ describe('workflow-app utils', () => {
|
||||
expect(coerceReplayUserInputs(null)).toBeNull()
|
||||
})
|
||||
|
||||
it('should normalize human-input multi-file types between frontend and backend payloads', () => {
|
||||
const nodes: HumanInputTestNode[] = [
|
||||
{
|
||||
id: 'node-1',
|
||||
type: 'custom',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
title: 'Human Input',
|
||||
desc: '',
|
||||
type: BlockEnum.HumanInput,
|
||||
inputs: [
|
||||
{ type: 'paragraph', output_variable_name: 'summary' },
|
||||
{ type: 'file-list', output_variable_name: 'attachments' },
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const backendNodes = normalizeWorkflowNodesForBackend(nodes) as HumanInputTestNode[]
|
||||
expect(backendNodes[0]!.data.inputs).toEqual([
|
||||
{ type: 'paragraph', output_variable_name: 'summary' },
|
||||
{ type: 'file_list', output_variable_name: 'attachments' },
|
||||
])
|
||||
|
||||
const frontendPayloadNodes: HumanInputTestNode[] = [
|
||||
{
|
||||
...nodes[0]!,
|
||||
data: {
|
||||
...nodes[0]!.data,
|
||||
inputs: [
|
||||
{ type: 'paragraph', output_variable_name: 'summary' },
|
||||
{ type: 'file_list', output_variable_name: 'attachments' },
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const frontendNodes = normalizeWorkflowNodesForFrontend(frontendPayloadNodes) as HumanInputTestNode[]
|
||||
|
||||
expect(frontendNodes[0]!.data.inputs).toEqual([
|
||||
{ type: 'paragraph', output_variable_name: 'summary' },
|
||||
{ type: 'file-list', output_variable_name: 'attachments' },
|
||||
])
|
||||
})
|
||||
|
||||
it('should build initial features with file-upload and feature fallbacks', () => {
|
||||
const result = buildInitialFeatures({
|
||||
file_upload: {
|
||||
|
||||
@ -14,7 +14,6 @@ import { postWithKeepalive } from '@/service/fetch'
|
||||
import { systemFeaturesQueryOptions } from '@/service/system-features'
|
||||
import { syncWorkflowDraft } from '@/service/workflow'
|
||||
import { useWorkflowRefreshDraft } from '.'
|
||||
import { normalizeWorkflowNodesForBackend } from '../utils'
|
||||
|
||||
export const useNodesSyncDraft = () => {
|
||||
const store = useStoreApi()
|
||||
@ -47,14 +46,14 @@ export const useNodesSyncDraft = () => {
|
||||
return null
|
||||
|
||||
const features = featuresStore!.getState().features
|
||||
const producedNodes = normalizeWorkflowNodesForBackend(produce(nodes, (draft) => {
|
||||
const producedNodes = produce(nodes, (draft) => {
|
||||
draft.forEach((node) => {
|
||||
Object.keys(node.data).forEach((key) => {
|
||||
if (key.startsWith('_'))
|
||||
delete node.data[key]
|
||||
})
|
||||
})
|
||||
}))
|
||||
})
|
||||
const producedEdges = produce(edges.filter(edge => !edge.data?._isTemp), (draft) => {
|
||||
draft.forEach((edge) => {
|
||||
Object.keys(edge.data).forEach((key) => {
|
||||
|
||||
@ -20,7 +20,6 @@ import {
|
||||
syncWorkflowDraft,
|
||||
} from '@/service/workflow'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { normalizeWorkflowNodesForFrontend } from '../utils'
|
||||
import { useWorkflowTemplate } from './use-workflow-template'
|
||||
|
||||
const hasConnectedUserInput = (nodes: Node[] = [], edges: Edge[] = []): boolean => {
|
||||
@ -59,13 +58,7 @@ export const useWorkflowInit = () => {
|
||||
const handleGetInitialWorkflowData = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetchWorkflowDraft(`/apps/${appDetail.id}/workflows/draft`)
|
||||
setData({
|
||||
...res,
|
||||
graph: {
|
||||
...res.graph,
|
||||
nodes: normalizeWorkflowNodesForFrontend(res.graph.nodes),
|
||||
},
|
||||
})
|
||||
setData(res)
|
||||
workflowStore.setState({
|
||||
envSecrets: (res.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => {
|
||||
acc[env.id] = env.value
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
||||
import type { Node } from '@/app/components/workflow/types'
|
||||
import type { FileUploadConfigResponse } from '@/models/common'
|
||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||
import { BlockEnum, SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
|
||||
type TriggerStatusLike = {
|
||||
@ -34,15 +33,6 @@ type WorkflowFeaturesLike = {
|
||||
sensitive_word_avoidance?: { enabled?: boolean }
|
||||
}
|
||||
|
||||
type HumanInputFieldLike = {
|
||||
type: unknown
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
type HumanInputNodeExtra = {
|
||||
inputs: HumanInputFieldLike[]
|
||||
}
|
||||
|
||||
export const buildTriggerStatusMap = (triggers: TriggerStatusLike[]) => {
|
||||
return triggers.reduce<Record<string, 'enabled' | 'disabled'>>((acc, trigger) => {
|
||||
acc[trigger.node_id] = trigger.status === 'enabled' ? 'enabled' : 'disabled'
|
||||
@ -81,49 +71,6 @@ export const coerceReplayUserInputs = (rawInputs: unknown): Record<string, strin
|
||||
return userInputs
|
||||
}
|
||||
|
||||
const normalizeHumanInputFieldType = (
|
||||
type: unknown,
|
||||
direction: 'frontend' | 'backend',
|
||||
) => {
|
||||
if (direction === 'frontend')
|
||||
return type === 'file_list' ? 'file-list' : type
|
||||
|
||||
return type === 'file-list' ? 'file_list' : type
|
||||
}
|
||||
|
||||
const isHumanInputNode = (node: Node): node is Node<HumanInputNodeExtra> => {
|
||||
return node.data.type === BlockEnum.HumanInput && Array.isArray((node.data as Partial<HumanInputNodeExtra>).inputs)
|
||||
}
|
||||
|
||||
const normalizeHumanInputNode = (
|
||||
node: Node,
|
||||
direction: 'frontend' | 'backend',
|
||||
): Node => {
|
||||
if (!isHumanInputNode(node))
|
||||
return node
|
||||
|
||||
const normalizedNode: Node<HumanInputNodeExtra> = {
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
inputs: node.data.inputs.map(input => ({
|
||||
...input,
|
||||
type: normalizeHumanInputFieldType(input.type, direction),
|
||||
})),
|
||||
},
|
||||
}
|
||||
|
||||
return normalizedNode
|
||||
}
|
||||
|
||||
export const normalizeWorkflowNodesForFrontend = (nodes: Node[]) => {
|
||||
return nodes.map(node => normalizeHumanInputNode(node, 'frontend'))
|
||||
}
|
||||
|
||||
export const normalizeWorkflowNodesForBackend = (nodes: Node[]) => {
|
||||
return nodes.map(node => normalizeHumanInputNode(node, 'backend'))
|
||||
}
|
||||
|
||||
export const buildInitialFeatures = (
|
||||
featuresSource: WorkflowFeaturesLike | null | undefined,
|
||||
fileUploadConfigResponse: FileUploadConfigResponse | undefined,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user