fix(workflow): correct env variable picker validation (#34666)

This commit is contained in:
yyh
2026-04-07 17:34:25 +08:00
committed by GitHub
parent 63b1d0c1ea
commit 3891c0a255
5 changed files with 61 additions and 12 deletions

View File

@@ -93,6 +93,9 @@ describe('VarReferencePicker branches', () => {
nodes: [startNode, sourceNode, currentNode],
edges: [],
hooksStoreProps: {},
initialStoreState: {
isWorkflowDataLoaded: true,
},
},
)
@@ -222,5 +225,25 @@ describe('VarReferencePicker branches', () => {
})
expect(screen.getByText('answer')).toBeInTheDocument()
expect(screen.getByTestId('var-reference-picker-error-icon')).toBeInTheDocument()
})
it('should not show an error icon for env variables that still exist in the workflow env list', () => {
const envVars: NodeOutPutVar[] = [
...availableVars,
{
nodeId: 'env',
title: 'ENVIRONMENT',
vars: [{ variable: 'env.API_KEY', type: VarType.string }],
},
]
renderPicker({
availableVars: envVars,
value: ['env', 'API_KEY'],
})
expect(screen.getByText('API_KEY')).toBeInTheDocument()
expect(screen.queryByTestId('var-reference-picker-error-icon')).not.toBeInTheDocument()
})
})

View File

@@ -1,5 +1,5 @@
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { CommonNodeType, Node, ValueSelector } from '@/app/components/workflow/types'
import type { CommonNodeType, Node, NodeOutPutVar, ValueSelector } from '@/app/components/workflow/types'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { createLoopNode, createNode, createStartNode } from '@/app/components/workflow/__tests__/fixtures'
import { BlockEnum, VarType } from '@/app/components/workflow/types'
@@ -114,11 +114,24 @@ describe('var-reference-picker.helpers', () => {
})
it('should derive variable meta and category from selectors', () => {
const meta = getVariableMeta({ type: BlockEnum.Code }, ['env', 'API_KEY'], 'API_KEY')
const envVars: NodeOutPutVar[] = [{
nodeId: 'env',
title: 'ENVIRONMENT',
vars: [{ variable: 'env.API_KEY', type: VarType.string }],
}]
const meta = getVariableMeta(null, ['env', 'API_KEY'], 'API_KEY', envVars, true)
expect(meta).toMatchObject({
isEnv: true,
isValidVar: true,
isException: true,
isException: false,
})
expect(getVariableMeta(null, ['env', 'MISSING_KEY'], 'MISSING_KEY', envVars, true)).toMatchObject({
isEnv: true,
isValidVar: false,
})
expect(getVariableMeta(null, ['env', 'MISSING_KEY'], 'MISSING_KEY', envVars)).toMatchObject({
isEnv: true,
isValidVar: true,
})
expect(getVariableCategory({

View File

@@ -2,7 +2,7 @@
import type { VarType as VarKindType } from '../../../tool/types'
import type { CredentialFormSchema, FormOption } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { CommonNodeType, Node, ValueSelector } from '@/app/components/workflow/types'
import type { CommonNodeType, Node, NodeOutPutVar, ValueSelector } from '@/app/components/workflow/types'
import { VAR_SHOW_NAME_MAP } from '@/app/components/workflow/constants'
import { getNodeInfoById, isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar } from './utils'
@@ -116,13 +116,20 @@ export const getVariableMeta = (
outputVarNode: { type?: string } | null,
value: ValueSelector | string,
varName: string,
availableVars: NodeOutPutVar[] = [],
canValidateSpecialVars = false,
) => {
const selector = value as ValueSelector
const isEnv = isENV(selector)
const isChatVar = isConversationVar(selector)
const isGlobal = isGlobalVar(selector)
const isRagVar = isRagVariableVar(selector)
const isValidVar = Boolean(outputVarNode) || isEnv || isChatVar || isGlobal || isRagVar
const isSelectorValue = Array.isArray(selector)
const isEnv = isSelectorValue && isENV(selector)
const isChatVar = isSelectorValue && isConversationVar(selector)
const isGlobal = isSelectorValue && isGlobalVar(selector)
const isRagVar = isSelectorValue && isRagVariableVar(selector)
const isSpecialVar = isEnv || isChatVar || isRagVar
const hasAvailableSpecialVar = !canValidateSpecialVars || !isSelectorValue || availableVars.some(nodeWithVars =>
nodeWithVars.vars.some(variable => variable.variable === selector.join('.')),
)
const isValidVar = Boolean(outputVarNode) || isGlobal || (isSpecialVar && hasAvailableSpecialVar)
return {
isChatVar,
isEnv,

View File

@@ -53,6 +53,7 @@ type Props = {
schemaWithDynamicSelect?: Partial<CredentialFormSchema>
setControlFocus: (value: number) => void
setOpen: (value: boolean) => void
showErrorIcon?: boolean
tooltipPopup: ReactNode
triggerRef: React.RefObject<HTMLDivElement | null>
type?: string
@@ -98,6 +99,7 @@ const VarReferencePickerTrigger: FC<Props> = ({
schemaWithDynamicSelect,
setControlFocus,
setOpen,
showErrorIcon = false,
tooltipPopup,
triggerRef,
type,
@@ -250,7 +252,7 @@ const VarReferencePickerTrigger: FC<Props> = ({
>
{type}
</div>
{!('title' in (outputVarNode || {})) && <RiErrorWarningFill className="ml-0.5 h-3 w-3 text-text-destructive" />}
{showErrorIcon && <RiErrorWarningFill data-testid="var-reference-picker-error-icon" className="ml-0.5 h-3 w-3 text-text-destructive" />}
</>
)
: (

View File

@@ -26,6 +26,7 @@ import {
} from '@/app/components/workflow/hooks'
// import type { BaseResource, BaseResourceProvider } from '@/app/components/workflow/nodes/_base/types'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import { useStore as useWorkflowStore } from '@/app/components/workflow/store'
import { BlockEnum } from '@/app/components/workflow/types'
import { isExceptionVariable } from '@/app/components/workflow/utils'
import { useFetchDynamicOptions } from '@/service/use-plugins'
@@ -124,6 +125,7 @@ const VarReferencePicker: FC<Props> = ({
const store = useStoreApi()
const nodes = useNodes<CommonNodeType>()
const isChatMode = useIsChatMode()
const isWorkflowDataLoaded = useWorkflowStore(s => s.isWorkflowDataLoaded)
const { getCurrentVariableType } = useWorkflowVariables()
const { availableVars, availableNodesWithParent: availableNodes } = useAvailableVarList(nodeId, {
onlyLeafNodeVar,
@@ -270,13 +272,14 @@ const VarReferencePicker: FC<Props> = ({
})
const { isEnv, isChatVar, isGlobal, isRagVar, isValidVar } = useMemo(
() => getVariableMeta(outputVarNode, value, varName),
[outputVarNode, value, varName],
() => getVariableMeta(outputVarNode, value, varName, outputVars, isWorkflowDataLoaded),
[isWorkflowDataLoaded, outputVarNode, outputVars, value, varName],
)
const isException = useMemo(
() => isExceptionVariable(varName, outputVarNode?.type),
[outputVarNode?.type, varName],
)
const showErrorIcon = hasValue && !isValidVar
// 8(left/right-padding) + 14(icon) + 4 + 14 + 2 = 42 + 17 buff
const {
@@ -385,6 +388,7 @@ const VarReferencePicker: FC<Props> = ({
schemaWithDynamicSelect={schemaWithDynamicSelect}
setControlFocus={setControlFocus}
setOpen={setOpen}
showErrorIcon={showErrorIcon}
tooltipPopup={tooltipPopup}
triggerRef={triggerRef}
type={type}