mirror of
https://github.com/langgenius/dify.git
synced 2026-04-15 09:57:03 +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(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.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 { VarType } from '../../types'
|
||||
import { WriteMode } from './types'
|
||||
import { normalizeOperationItems } from './utils'
|
||||
|
||||
export const filterVarByType = (varType: VarType) => {
|
||||
return (variable: Var) => {
|
||||
@ -86,5 +87,5 @@ export const updateOperationItems = (
|
||||
inputs: AssignerNodeType,
|
||||
items: AssignerNodeOperation[],
|
||||
) => 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 { 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 => {
|
||||
if (payload.version === '2' && payload.items)
|
||||
return payload as AssignerNodeType
|
||||
if (payload.version === '2' && payload.items) {
|
||||
return {
|
||||
...payload,
|
||||
items: normalizeOperationItems(payload.items),
|
||||
} as AssignerNodeType
|
||||
}
|
||||
|
||||
return {
|
||||
...payload,
|
||||
version: '2',
|
||||
items: [{
|
||||
items: normalizeOperationItems([{
|
||||
variable_selector: payload.assigned_variable_selector || [],
|
||||
input_type: AssignerNodeInputType.variable,
|
||||
operation: convertOldWriteMode(payload.write_mode),
|
||||
value: payload.input_variable_selector || [],
|
||||
}],
|
||||
...payload,
|
||||
}]),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user