Yeuoly b76e17b25d
feat: introduce trigger functionality (#27644)
Signed-off-by: lyzno1 <yuanyouhuilyz@gmail.com>
Co-authored-by: Stream <Stream_2@qq.com>
Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: zhsama <torvalds@linux.do>
Co-authored-by: Harry <xh001x@hotmail.com>
Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com>
Co-authored-by: yessenia <yessenia.contact@gmail.com>
Co-authored-by: hjlarry <hjlarry@163.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: WTW0313 <twwu@dify.ai>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-12 17:59:37 +08:00

234 lines
6.9 KiB
TypeScript

import { useCallback, useEffect, useMemo } from 'react'
import { produce } from 'immer'
import type { PluginTriggerNodeType } from './types'
import type { PluginTriggerVarInputs } from './types'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import { useNodesReadOnly } from '@/app/components/workflow/hooks'
import {
useAllTriggerPlugins,
useTriggerSubscriptions,
} from '@/service/use-triggers'
import {
getConfiguredValue,
toolParametersToFormSchemas,
} from '@/app/components/tools/utils/to-form-schema'
import type { InputVar } from '@/app/components/workflow/types'
import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types'
import type { Event } from '@/app/components/tools/types'
import { VarKindType } from '../_base/types'
const normalizeEventParameters = (
params: PluginTriggerVarInputs | Record<string, unknown> | null | undefined,
{ allowScalars = false }: { allowScalars?: boolean } = {},
): PluginTriggerVarInputs => {
if (!params || typeof params !== 'object' || Array.isArray(params))
return {} as PluginTriggerVarInputs
return Object.entries(params).reduce((acc, [key, entry]) => {
if (!entry && entry !== 0 && entry !== false)
return acc
if (
typeof entry === 'object'
&& !Array.isArray(entry)
&& 'type' in entry
&& 'value' in entry
) {
const normalizedEntry = { ...(entry as PluginTriggerVarInputs[string]) }
if (normalizedEntry.type === VarKindType.mixed)
normalizedEntry.type = VarKindType.constant
acc[key] = normalizedEntry
return acc
}
if (!allowScalars)
return acc
if (typeof entry === 'string') {
acc[key] = {
type: VarKindType.constant,
value: entry,
}
return acc
}
if (typeof entry === 'number' || typeof entry === 'boolean') {
acc[key] = {
type: VarKindType.constant,
value: entry,
}
return acc
}
if (Array.isArray(entry) && entry.every(item => typeof item === 'string')) {
acc[key] = {
type: VarKindType.variable,
value: entry,
}
}
return acc
}, {} as PluginTriggerVarInputs)
}
const useConfig = (id: string, payload: PluginTriggerNodeType) => {
const { nodesReadOnly: readOnly } = useNodesReadOnly()
const { data: triggerPlugins = [] } = useAllTriggerPlugins()
const { inputs, setInputs: doSetInputs } = useNodeCrud<PluginTriggerNodeType>(
id,
payload,
)
const {
provider_id,
provider_name,
event_name,
config = {},
event_parameters: rawEventParameters = {},
subscription_id,
} = inputs
const event_parameters = useMemo(
() => normalizeEventParameters(rawEventParameters as PluginTriggerVarInputs),
[rawEventParameters],
)
const legacy_config_parameters = useMemo(
() => normalizeEventParameters(config as PluginTriggerVarInputs, { allowScalars: true }),
[config],
)
const currentProvider = useMemo<TriggerWithProvider | undefined>(() => {
return triggerPlugins.find(
provider =>
provider.name === provider_name
|| provider.id === provider_id
|| (provider_id && provider.plugin_id === provider_id),
)
}, [triggerPlugins, provider_name, provider_id])
const { data: subscriptions = [] } = useTriggerSubscriptions(provider_id || '')
const subscriptionSelected = useMemo(() => {
return subscriptions?.find(s => s.id === subscription_id)
}, [subscriptions, subscription_id])
const currentEvent = useMemo<Event | undefined>(() => {
return currentProvider?.events.find(
event => event.name === event_name,
)
}, [currentProvider, event_name])
// Dynamic trigger parameters (from specific trigger.parameters)
const triggerSpecificParameterSchema = useMemo(() => {
if (!currentEvent) return []
return toolParametersToFormSchemas(currentEvent.parameters)
}, [currentEvent])
// Combined parameter schema (subscription + trigger specific)
const triggerParameterSchema = useMemo(() => {
const schemaMap = new Map()
triggerSpecificParameterSchema.forEach((schema) => {
schemaMap.set(schema.variable || schema.name, schema)
})
return Array.from(schemaMap.values())
}, [triggerSpecificParameterSchema])
const triggerParameterValue = useMemo(() => {
if (!triggerParameterSchema.length)
return {} as PluginTriggerVarInputs
const hasStoredParameters = event_parameters && Object.keys(event_parameters).length > 0
const baseValue = hasStoredParameters ? event_parameters : legacy_config_parameters
const configuredValue = getConfiguredValue(baseValue, triggerParameterSchema) as PluginTriggerVarInputs
return normalizeEventParameters(configuredValue)
}, [triggerParameterSchema, event_parameters, legacy_config_parameters])
useEffect(() => {
if (!triggerParameterSchema.length)
return
if (event_parameters && Object.keys(event_parameters).length > 0)
return
if (!triggerParameterValue || Object.keys(triggerParameterValue).length === 0)
return
const newInputs = produce(inputs, (draft) => {
draft.event_parameters = triggerParameterValue
draft.config = triggerParameterValue
})
doSetInputs(newInputs)
}, [
doSetInputs,
event_parameters,
inputs,
triggerParameterSchema,
triggerParameterValue,
])
const setTriggerParameterValue = useCallback(
(value: PluginTriggerVarInputs) => {
const sanitizedValue = normalizeEventParameters(value)
const newInputs = produce(inputs, (draft) => {
draft.event_parameters = sanitizedValue
draft.config = sanitizedValue
})
doSetInputs(newInputs)
},
[inputs, doSetInputs],
)
const setInputVar = useCallback(
(variable: InputVar, varDetail: InputVar) => {
const newInputs = produce(inputs, (draft) => {
const nextEventParameters = normalizeEventParameters({
...draft.event_parameters,
[variable.variable]: {
type: VarKindType.variable,
value: varDetail.variable,
},
} as PluginTriggerVarInputs)
draft.event_parameters = nextEventParameters
draft.config = nextEventParameters
})
doSetInputs(newInputs)
},
[inputs, doSetInputs],
)
// Get output schema
const outputSchema = useMemo(() => {
return currentEvent?.output_schema || {}
}, [currentEvent])
// Check if trigger has complex output structure
const hasObjectOutput = useMemo(() => {
const properties = outputSchema.properties || {}
return Object.values(properties).some(
(prop: any) => prop.type === 'object',
)
}, [outputSchema])
return {
readOnly,
inputs,
currentProvider,
currentEvent,
triggerParameterSchema,
triggerParameterValue,
setTriggerParameterValue,
setInputVar,
outputSchema,
hasObjectOutput,
subscriptions,
subscriptionSelected,
}
}
export default useConfig