dify/web/app/components/workflow/nodes/trigger-webhook/use-config.helpers.ts
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

221 lines
5.9 KiB
TypeScript

import type { HttpMethod, WebhookHeader, WebhookParameter, WebhookTriggerNodeType } from './types'
import type { Variable } from '@/app/components/workflow/types'
import { produce } from 'immer'
import { VarType } from '@/app/components/workflow/types'
import { checkKeys, hasDuplicateStr } from '@/utils/var'
import { WEBHOOK_RAW_VARIABLE_NAME } from './utils/raw-variable'
type VariableSyncSource = 'param' | 'header' | 'body'
type SanitizedEntry = {
item: WebhookParameter | WebhookHeader
sanitizedName: string
}
type NotifyError = (key: string) => void
const sanitizeEntryName = (
item: WebhookParameter | WebhookHeader,
sourceType: VariableSyncSource,
) => {
return sourceType === 'header' ? item.name.replace(/-/g, '_') : item.name
}
const getSanitizedEntries = (
newData: (WebhookParameter | WebhookHeader)[],
sourceType: VariableSyncSource,
): SanitizedEntry[] => {
return newData.map((item) => ({
item,
sanitizedName: sanitizeEntryName(item, sourceType),
}))
}
const createVariable = (
item: WebhookParameter | WebhookHeader,
sourceType: VariableSyncSource,
sanitizedName: string,
): Variable => {
const inputVarType: VarType = 'type' in item ? item.type : VarType.string
return {
value_type: inputVarType,
label: sourceType,
variable: sanitizedName,
value_selector: [],
required: item.required,
}
}
export const syncVariables = ({
draft,
id,
newData,
sourceType,
notifyError,
isVarUsedInNodes,
removeUsedVarInNodes,
}: {
draft: WebhookTriggerNodeType
id: string
newData: (WebhookParameter | WebhookHeader)[]
sourceType: VariableSyncSource
notifyError: NotifyError
isVarUsedInNodes: (selector: [string, string]) => boolean
removeUsedVarInNodes: (selector: [string, string]) => void
}) => {
if (!draft.variables) draft.variables = []
const sanitizedEntries = getSanitizedEntries(newData, sourceType)
if (sanitizedEntries.some((entry) => entry.sanitizedName === WEBHOOK_RAW_VARIABLE_NAME)) {
notifyError('variableConfig.varName')
return false
}
const existingOtherVarNames = new Set(
draft.variables
.filter((v) => v.label !== sourceType && v.variable !== WEBHOOK_RAW_VARIABLE_NAME)
.map((v) => v.variable),
)
const crossScopeConflict = sanitizedEntries.find((entry) =>
existingOtherVarNames.has(entry.sanitizedName),
)
if (crossScopeConflict) {
notifyError(crossScopeConflict.sanitizedName)
return false
}
if (hasDuplicateStr(sanitizedEntries.map((entry) => entry.sanitizedName))) {
notifyError('variableConfig.varName')
return false
}
for (const { sanitizedName } of sanitizedEntries) {
const { isValid, errorMessageKey } = checkKeys([sanitizedName], false)
if (!isValid) {
notifyError(`varKeyError.${errorMessageKey}`)
return false
}
}
const nextNames = new Set(sanitizedEntries.map((entry) => entry.sanitizedName))
draft.variables
.filter((v) => v.label === sourceType && !nextNames.has(v.variable))
.forEach((variable) => {
if (isVarUsedInNodes([id, variable.variable])) removeUsedVarInNodes([id, variable.variable])
})
draft.variables = draft.variables.filter((variable) => {
if (variable.label !== sourceType) return true
return nextNames.has(variable.variable)
})
sanitizedEntries.forEach(({ item, sanitizedName }) => {
const existingVarIndex = draft.variables.findIndex((v) => v.variable === sanitizedName)
const variable = createVariable(item, sourceType, sanitizedName)
if (existingVarIndex >= 0) draft.variables[existingVarIndex] = variable
else draft.variables.push(variable)
})
return true
}
export const updateMethod = (inputs: WebhookTriggerNodeType, method: HttpMethod) =>
produce(inputs, (draft) => {
draft.method = method
})
export const updateSimpleField = <K extends 'async_mode' | 'status_code' | 'response_body'>(
inputs: WebhookTriggerNodeType,
key: K,
value: WebhookTriggerNodeType[K],
) =>
produce(inputs, (draft) => {
draft[key] = value
})
export const updateContentType = ({
inputs,
id,
contentType,
isVarUsedInNodes,
removeUsedVarInNodes,
}: {
inputs: WebhookTriggerNodeType
id: string
contentType: string
isVarUsedInNodes: (selector: [string, string]) => boolean
removeUsedVarInNodes: (selector: [string, string]) => void
}) =>
produce(inputs, (draft) => {
const previousContentType = draft.content_type
draft.content_type = contentType
if (previousContentType === contentType) return
draft.body = []
if (!draft.variables) return
draft.variables
.filter((v) => v.label === 'body')
.forEach((variable) => {
if (isVarUsedInNodes([id, variable.variable])) removeUsedVarInNodes([id, variable.variable])
})
draft.variables = draft.variables.filter((v) => v.label !== 'body')
})
type SourceField = 'params' | 'headers' | 'body'
const getSourceField = (sourceType: VariableSyncSource): SourceField => {
switch (sourceType) {
case 'param':
return 'params'
case 'header':
return 'headers'
default:
return 'body'
}
}
export const updateSourceFields = ({
inputs,
id,
sourceType,
nextData,
notifyError,
isVarUsedInNodes,
removeUsedVarInNodes,
}: {
inputs: WebhookTriggerNodeType
id: string
sourceType: VariableSyncSource
nextData: WebhookParameter[] | WebhookHeader[]
notifyError: NotifyError
isVarUsedInNodes: (selector: [string, string]) => boolean
removeUsedVarInNodes: (selector: [string, string]) => void
}) =>
produce(inputs, (draft) => {
draft[getSourceField(sourceType)] = nextData as never
syncVariables({
draft,
id,
newData: nextData,
sourceType,
notifyError,
isVarUsedInNodes,
removeUsedVarInNodes,
})
})
export const updateWebhookUrls = (
inputs: WebhookTriggerNodeType,
webhookUrl: string,
webhookDebugUrl?: string,
) =>
produce(inputs, (draft) => {
draft.webhook_url = webhookUrl
draft.webhook_debug_url = webhookDebugUrl
})