mirror of
https://github.com/langgenius/dify.git
synced 2026-04-07 12:00:38 -04:00
fix(workflow): correct env variable picker validation (#34666)
This commit is contained in:
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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" />}
|
||||
</>
|
||||
)
|
||||
: (
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user