fix(trigger): implement plugin single run functionality and update node status handling

This commit is contained in:
zhsama 2025-10-13 17:02:30 +08:00
parent b283a2b3d9
commit 654adccfbf
2 changed files with 129 additions and 3 deletions

View File

@ -362,6 +362,114 @@ const useOneStepRun = <T>({
return null
}, [flowId, id, data, handleNodeDataUpdate, cancelWebhookSingleRun])
const runPluginSingleRun = useCallback(async (): Promise<any | null> => {
const urlPath = `/apps/${flowId}/workflows/draft/nodes/${id}/trigger`
const urlWithPrefix = `${API_PREFIX}${urlPath.startsWith('/') ? urlPath : `/${urlPath}`}`
webhookSingleRunActiveRef.current = true
const token = ++webhookSingleRunTokenRef.current
while (webhookSingleRunActiveRef.current && token === webhookSingleRunTokenRef.current) {
const controller = new AbortController()
webhookSingleRunAbortRef.current = controller
try {
const baseOptions = getBaseOptions()
const headers = new Headers(baseOptions.headers as Headers)
const accessToken = await getAccessToken()
headers.set('Authorization', `Bearer ${accessToken}`)
headers.set('Content-Type', 'application/json')
// Reason: Plugin trigger requires event_name, subscription_id, provider_id from node data
const requestBody = {
event_name: (data as any).event_name,
subscription_id: (data as any).subscription_id,
provider_id: (data as any).provider_id,
}
const response = await fetch(urlWithPrefix, {
...baseOptions,
method: 'POST',
headers,
body: JSON.stringify(requestBody),
signal: controller.signal,
})
if (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current)
return null
const contentType = response.headers.get('Content-Type')?.toLowerCase() || ''
const responseData = contentType.includes('application/json') ? await response.json() : undefined
if (!response.ok) {
const message = responseData?.message || 'Plugin debug failed'
Toast.notify({ type: 'error', message })
cancelWebhookSingleRun()
throw new Error(message)
}
if (responseData?.status === 'waiting') {
const delay = Number(responseData.retry_in) || 2000
webhookSingleRunAbortRef.current = null
if (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current)
return null
await new Promise<void>((resolve) => {
const timeoutId = window.setTimeout(resolve, delay)
webhookSingleRunTimeoutRef.current = timeoutId
webhookSingleRunDelayResolveRef.current = resolve
controller.signal.addEventListener('abort', () => {
window.clearTimeout(timeoutId)
resolve()
}, { once: true })
})
webhookSingleRunTimeoutRef.current = undefined
webhookSingleRunDelayResolveRef.current = null
continue
}
if (responseData?.status === 'error') {
const message = responseData.message || 'Plugin debug failed'
Toast.notify({ type: 'error', message })
cancelWebhookSingleRun()
throw new Error(message)
}
handleNodeDataUpdate({
id,
data: {
...data,
_isSingleRun: false,
_singleRunningStatus: NodeRunningStatus.Running,
},
})
cancelWebhookSingleRun()
return responseData
}
catch (error) {
if (controller.signal.aborted && (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current))
return null
if (controller.signal.aborted)
return null
console.error('handleRun: plugin debug polling error', error)
Toast.notify({ type: 'error', message: 'Plugin debug request failed' })
cancelWebhookSingleRun()
if (error instanceof Error)
throw error
throw new Error(String(error))
}
finally {
webhookSingleRunAbortRef.current = null
}
}
return null
}, [flowId, id, data, handleNodeDataUpdate, cancelWebhookSingleRun])
const checkValidWrap = () => {
if (!checkValid)
return { isValid: true, errorMessage: '' }
@ -424,7 +532,10 @@ const useOneStepRun = <T>({
const handleRun = async (submitData: Record<string, any>) => {
const isWebhookNode = data.type === BlockEnum.TriggerWebhook
if (isWebhookNode)
const isPluginNode = data.type === BlockEnum.TriggerPlugin
const isTriggerNode = isWebhookNode || isPluginNode
if (isTriggerNode)
cancelWebhookSingleRun()
handleNodeDataUpdate({
@ -432,7 +543,7 @@ const useOneStepRun = <T>({
data: {
...data,
_isSingleRun: false,
_singleRunningStatus: isWebhookNode ? NodeRunningStatus.Waiting : NodeRunningStatus.Running,
_singleRunningStatus: isTriggerNode ? NodeRunningStatus.Waiting : NodeRunningStatus.Running,
},
})
let res: any
@ -453,6 +564,20 @@ const useOneStepRun = <T>({
return false
}
}
else if (isPluginNode) {
res = await runPluginSingleRun()
if (!res) {
handleNodeDataUpdate({
id,
data: {
...data,
_isSingleRun: false,
_singleRunningStatus: NodeRunningStatus.NotStart,
},
})
return false
}
}
else {
const isStartNode = data.type === BlockEnum.Start
const postData: Record<string, any> = {}
@ -700,7 +825,7 @@ const useOneStepRun = <T>({
}
}
finally {
if (data.type === BlockEnum.TriggerWebhook)
if (isTriggerNode)
cancelWebhookSingleRun()
if (!isPausedRef.current && !isIteration && !isLoop && res) {
setRunResult({

View File

@ -35,6 +35,7 @@ export const canRunBySingle = (nodeType: BlockEnum, isChildNode: boolean) => {
|| nodeType === BlockEnum.Assigner
|| nodeType === BlockEnum.DataSource
|| nodeType === BlockEnum.TriggerWebhook
|| nodeType === BlockEnum.TriggerPlugin
}
export const isSupportCustomRunForm = (nodeType: BlockEnum) => {