mirror of
https://github.com/langgenius/dify.git
synced 2026-04-25 09:36:40 +08:00
sanitize assigner collaboration payloads
Collaboration restores some empty assigner variable selectors as null, which later reaches the assigner panel and crashes variable-selector reads that expect arrays. Normalize assigner operation items when converting versioned payloads and when applying operation list updates so variable-mode selectors always stay arrays. Add targeted tests for both the helper path and useConfig exposure.
This commit is contained in:
parent
ed6fc3f4a1
commit
7e044fc602
@ -65,4 +65,20 @@ describe('assigner use-config helpers', () => {
|
|||||||
expect(updateOperationItems(legacyInputs, nextItems).items).toEqual(nextItems)
|
expect(updateOperationItems(legacyInputs, nextItems).items).toEqual(nextItems)
|
||||||
expect(legacyInputs.items).toHaveLength(1)
|
expect(legacyInputs.items).toHaveLength(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('sanitizes variable-selector items restored from collaboration payloads', () => {
|
||||||
|
const dirtyItems = [{
|
||||||
|
variable_selector: null as unknown as AssignerNodeType['items'][number]['variable_selector'],
|
||||||
|
input_type: AssignerNodeInputType.variable,
|
||||||
|
operation: WriteMode.overwrite,
|
||||||
|
value: null,
|
||||||
|
}]
|
||||||
|
|
||||||
|
expect(updateOperationItems(createInputs('2'), dirtyItems).items).toEqual([{
|
||||||
|
variable_selector: [],
|
||||||
|
input_type: AssignerNodeInputType.variable,
|
||||||
|
operation: WriteMode.overwrite,
|
||||||
|
value: [],
|
||||||
|
}])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -95,4 +95,24 @@ describe('useConfig', () => {
|
|||||||
expect(result.current.filterToAssignedVar({ type: VarType.string } as never, VarType.arrayString, WriteMode.append)).toBe(true)
|
expect(result.current.filterToAssignedVar({ type: VarType.string } as never, VarType.arrayString, WriteMode.append)).toBe(true)
|
||||||
expect(result.current.filterToAssignedVar({ type: VarType.number } as never, VarType.arrayString, WriteMode.append)).toBe(false)
|
expect(result.current.filterToAssignedVar({ type: VarType.number } as never, VarType.arrayString, WriteMode.append)).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should normalize collaboration-restored null selectors before exposing inputs', () => {
|
||||||
|
const dirtyPayload = createPayload({
|
||||||
|
version: '2',
|
||||||
|
items: [createOperation({
|
||||||
|
variable_selector: null as unknown as AssignerNodeOperation['variable_selector'],
|
||||||
|
input_type: AssignerNodeInputType.variable,
|
||||||
|
value: null,
|
||||||
|
})],
|
||||||
|
})
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useConfig('assigner-node', dirtyPayload))
|
||||||
|
|
||||||
|
expect(result.current.inputs.items).toEqual([expect.objectContaining({
|
||||||
|
variable_selector: [],
|
||||||
|
input_type: AssignerNodeInputType.variable,
|
||||||
|
operation: WriteMode.overwrite,
|
||||||
|
value: [],
|
||||||
|
})])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { AssignerNodeOperation, AssignerNodeType } from './types'
|
|||||||
import { produce } from 'immer'
|
import { produce } from 'immer'
|
||||||
import { VarType } from '../../types'
|
import { VarType } from '../../types'
|
||||||
import { WriteMode } from './types'
|
import { WriteMode } from './types'
|
||||||
|
import { normalizeOperationItems } from './utils'
|
||||||
|
|
||||||
export const filterVarByType = (varType: VarType) => {
|
export const filterVarByType = (varType: VarType) => {
|
||||||
return (variable: Var) => {
|
return (variable: Var) => {
|
||||||
@ -86,5 +87,5 @@ export const updateOperationItems = (
|
|||||||
inputs: AssignerNodeType,
|
inputs: AssignerNodeType,
|
||||||
items: AssignerNodeOperation[],
|
items: AssignerNodeOperation[],
|
||||||
) => produce(inputs, (draft) => {
|
) => produce(inputs, (draft) => {
|
||||||
draft.items = [...items]
|
draft.items = normalizeOperationItems(items)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { AssignerNodeType } from './types'
|
import type { AssignerNodeOperation, AssignerNodeType } from './types'
|
||||||
import type { I18nKeysByPrefix } from '@/types/i18n'
|
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||||
import { AssignerNodeInputType, WriteMode } from './types'
|
import { AssignerNodeInputType, WriteMode } from './types'
|
||||||
|
|
||||||
@ -62,18 +62,49 @@ const convertOldWriteMode = (oldMode: string): WriteMode => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizeVariableSelector = (value: unknown) => {
|
||||||
|
return Array.isArray(value) ? value : []
|
||||||
|
}
|
||||||
|
|
||||||
|
export const normalizeOperationItems = (items: unknown): AssignerNodeOperation[] => {
|
||||||
|
if (!Array.isArray(items))
|
||||||
|
return []
|
||||||
|
|
||||||
|
return items.map((item) => {
|
||||||
|
const operationItem = (item || {}) as Partial<AssignerNodeOperation>
|
||||||
|
const inputType = operationItem.input_type === AssignerNodeInputType.constant
|
||||||
|
? AssignerNodeInputType.constant
|
||||||
|
: AssignerNodeInputType.variable
|
||||||
|
|
||||||
|
return {
|
||||||
|
variable_selector: normalizeVariableSelector(operationItem.variable_selector),
|
||||||
|
input_type: inputType,
|
||||||
|
operation: Object.values(WriteMode).includes(operationItem.operation as WriteMode)
|
||||||
|
? operationItem.operation as WriteMode
|
||||||
|
: WriteMode.overwrite,
|
||||||
|
value: inputType === AssignerNodeInputType.variable
|
||||||
|
? normalizeVariableSelector(operationItem.value)
|
||||||
|
: operationItem.value,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const convertV1ToV2 = (payload: any): AssignerNodeType => {
|
export const convertV1ToV2 = (payload: any): AssignerNodeType => {
|
||||||
if (payload.version === '2' && payload.items)
|
if (payload.version === '2' && payload.items) {
|
||||||
return payload as AssignerNodeType
|
return {
|
||||||
|
...payload,
|
||||||
|
items: normalizeOperationItems(payload.items),
|
||||||
|
} as AssignerNodeType
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
...payload,
|
||||||
version: '2',
|
version: '2',
|
||||||
items: [{
|
items: normalizeOperationItems([{
|
||||||
variable_selector: payload.assigned_variable_selector || [],
|
variable_selector: payload.assigned_variable_selector || [],
|
||||||
input_type: AssignerNodeInputType.variable,
|
input_type: AssignerNodeInputType.variable,
|
||||||
operation: convertOldWriteMode(payload.write_mode),
|
operation: convertOldWriteMode(payload.write_mode),
|
||||||
value: payload.input_variable_selector || [],
|
value: payload.input_variable_selector || [],
|
||||||
}],
|
}]),
|
||||||
...payload,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user