From 87ab417f89501ba66393e3dbbb069b974231a958 Mon Sep 17 00:00:00 2001 From: mkriskovic Date: Mon, 17 Nov 2025 11:01:48 +0100 Subject: [PATCH 1/7] 2867 - ai agent plus icons working in task dispatchers --- .../hooks/useClusterElementsLayout.ts | 18 +++--- .../components/WorkflowNodeDetailsPanel.tsx | 10 +++- .../WorkflowNodesPopoverMenuOperationList.tsx | 10 +++- .../workflow-editor/utils/handleDeleteTask.ts | 30 +++++++--- .../utils/saveClusterElementFieldChange.ts | 13 ++++- .../utils/saveClusterElementNodesPosition.ts | 8 ++- .../utils/saveWorkflowDefinition.ts | 56 ++++++++++++------- 7 files changed, 97 insertions(+), 48 deletions(-) diff --git a/client/src/pages/platform/cluster-element-editor/hooks/useClusterElementsLayout.ts b/client/src/pages/platform/cluster-element-editor/hooks/useClusterElementsLayout.ts index ff067f8f79..0b5cf12727 100644 --- a/client/src/pages/platform/cluster-element-editor/hooks/useClusterElementsLayout.ts +++ b/client/src/pages/platform/cluster-element-editor/hooks/useClusterElementsLayout.ts @@ -15,6 +15,7 @@ import useWorkflowDataStore from '../../workflow-editor/stores/useWorkflowDataSt import useWorkflowEditorStore from '../../workflow-editor/stores/useWorkflowEditorStore'; import useWorkflowNodeDetailsPanelStore from '../../workflow-editor/stores/useWorkflowNodeDetailsPanelStore'; import {getLayoutedElements} from '../../workflow-editor/utils/layoutUtils'; +import {getTaskDispatcherTask} from '../../workflow-editor/utils/taskDispatcherConfig'; import useClusterElementsDataStore from '../stores/useClusterElementsDataStore'; import {isPlainObject} from '../utils/clusterElementsUtils'; import createClusterElementsEdges from '../utils/createClusterElementsEdges'; @@ -106,13 +107,16 @@ const useClusterElementsLayout = () => { return JSON.parse(workflow.definition).tasks; }, [workflow.definition]); - const mainRootClusterElementTask = useMemo( - () => - workflowDefinitionTasks.find( - (task: {name: string}) => task.name === rootClusterElementNodeData?.workflowNodeName - ), - [workflowDefinitionTasks, rootClusterElementNodeData?.workflowNodeName] - ); + const mainRootClusterElementTask = useMemo(() => { + if (!rootClusterElementNodeData?.workflowNodeName || !workflowDefinitionTasks.length) { + return undefined; + } + + return getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }); + }, [workflowDefinitionTasks, rootClusterElementNodeData?.workflowNodeName]); const clusterElements = useMemo( () => mainRootClusterElementTask?.clusterElements || {}, diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx index 6629669e88..34ae837934 100644 --- a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx +++ b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx @@ -83,6 +83,7 @@ import getParametersWithDefaultValues from '../utils/getParametersWithDefaultVal import saveClusterElementFieldChange from '../utils/saveClusterElementFieldChange'; import saveTaskDispatcherSubtaskFieldChange from '../utils/saveTaskDispatcherSubtaskFieldChange'; import saveWorkflowDefinition from '../utils/saveWorkflowDefinition'; +import {getTaskDispatcherTask} from '../utils/taskDispatcherConfig'; import {DescriptionTabSkeleton, FieldsetSkeleton, PropertiesTabSkeleton} from './WorkflowEditorSkeletons'; const TABS: Array<{label: string; name: TabNameType}> = [ @@ -985,9 +986,12 @@ const WorkflowNodeDetailsPanel = ({ const workflowDefinitionTasks = JSON.parse(workflow.definition).tasks; - const mainClusterRootTask = workflowDefinitionTasks?.find( - (task: {name: string}) => task.name === rootClusterElementNodeData?.workflowNodeName - ); + const mainClusterRootTask = rootClusterElementNodeData?.workflowNodeName + ? getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }) + : undefined; if (!mainClusterRootTask) { return; diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx index 0be2ad3aa0..bc26bbabd3 100644 --- a/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx +++ b/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx @@ -34,6 +34,7 @@ import handleComponentAddedSuccess from '../utils/handleComponentAddedSuccess'; import handleTaskDispatcherSubtaskOperationClick from '../utils/handleTaskDispatcherSubtaskOperationClick'; import processClusterElementsHierarchy from '../utils/processClusterElementsHierarchy'; import saveWorkflowDefinition from '../utils/saveWorkflowDefinition'; +import {getTaskDispatcherTask} from '../utils/taskDispatcherConfig'; interface WorkflowNodesPopoverMenuOperationListProps { clusterElementType?: string; @@ -189,9 +190,12 @@ const WorkflowNodesPopoverMenuOperationList = ({ const workflowDefinitionTasks = JSON.parse(workflow.definition).tasks; - const mainClusterRootTask = workflowDefinitionTasks?.find( - (task: {name: string}) => task.name === rootClusterElementNodeData?.workflowNodeName - ); + const mainClusterRootTask = rootClusterElementNodeData?.workflowNodeName + ? getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }) + : undefined; if (!mainClusterRootTask) { return; diff --git a/client/src/pages/platform/workflow-editor/utils/handleDeleteTask.ts b/client/src/pages/platform/workflow-editor/utils/handleDeleteTask.ts index 62cb9d22e2..be421fa553 100644 --- a/client/src/pages/platform/workflow-editor/utils/handleDeleteTask.ts +++ b/client/src/pages/platform/workflow-editor/utils/handleDeleteTask.ts @@ -9,7 +9,8 @@ import {QueryClient, UseMutationResult} from '@tanstack/react-query'; import {WorkflowDataType} from '../stores/useWorkflowDataStore'; import useWorkflowNodeDetailsPanelStore from '../stores/useWorkflowNodeDetailsPanelStore'; import findAndRemoveClusterElement from './findAndRemoveClusterElement'; -import {TASK_DISPATCHER_CONFIG} from './taskDispatcherConfig'; +import getRecursivelyUpdatedTasks from './getRecursivelyUpdatedTasks'; +import {TASK_DISPATCHER_CONFIG, getTaskDispatcherTask} from './taskDispatcherConfig'; interface HandleDeleteTaskProps { rootClusterElementNodeData?: NodeDataType; @@ -193,7 +194,10 @@ export default function handleDeleteTask({ return parentForkJoinTask; }) as Array; } else if (clusterElementsCanvasOpen && rootClusterElementNodeData) { - const mainRootClusterElementTask = workflowTasks.find((task) => task.name === rootClusterElementNodeData?.name); + const mainRootClusterElementTask = getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.name, + tasks: workflowTasks, + }); if (!mainRootClusterElementTask || !mainRootClusterElementTask.clusterElements) { return; @@ -237,13 +241,23 @@ export default function handleDeleteTask({ } } - updatedTasks = workflowTasks.map((task) => { - if (task.name !== mainRootClusterElementTask?.name) { - return task; - } + // Check if the task is at top level + const topLevelTaskIndex = workflowTasks.findIndex((task) => task.name === mainRootClusterElementTask.name); - return updatedRootClusterElementTask; - }) as Array; + if (topLevelTaskIndex !== -1) { + updatedTasks = workflowTasks.map((task) => { + if (task.name !== mainRootClusterElementTask?.name) { + return task; + } + + return updatedRootClusterElementTask; + }) as Array; + } else { + updatedTasks = getRecursivelyUpdatedTasks( + workflowTasks as Array, + updatedRootClusterElementTask + ) as Array; + } } else { updatedTasks = workflowTasks.filter((task: WorkflowTask) => task.name !== data.name); } diff --git a/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts b/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts index 105e9d96f8..3def663fff 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts @@ -8,6 +8,7 @@ import useWorkflowNodeDetailsPanelStore from '../stores/useWorkflowNodeDetailsPa import {updateClusterRootElementField, updateNestedClusterElementField} from './clusterElementsFieldChangeUtils'; import getParametersWithDefaultValues from './getParametersWithDefaultValues'; import saveWorkflowDefinition from './saveWorkflowDefinition'; +import {getTaskDispatcherTask} from './taskDispatcherConfig'; type FieldUpdateType = { field: 'operation' | 'label' | 'description'; @@ -42,9 +43,15 @@ export default function saveClusterElementFieldChange({ const workflowDefinitionTasks = JSON.parse(workflow.definition).tasks; - const mainClusterRootTask = workflowDefinitionTasks?.find( - (task: {name: string}) => task.name === rootClusterElementNodeData?.workflowNodeName - ); + const mainClusterRootTask = rootClusterElementNodeData?.workflowNodeName + ? getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }) + : undefined; + // const mainClusterRootTask = workflowDefinitionTasks?.find( + // (task: {name: string}) => task.name === rootClusterElementNodeData?.workflowNodeName + // ); if (!mainClusterRootTask) { return; diff --git a/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts b/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts index 392537722e..ec69975363 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts @@ -5,6 +5,7 @@ import useClusterElementsDataStore from '../../cluster-element-editor/stores/use import useWorkflowEditorStore from '../stores/useWorkflowEditorStore'; import {removeClusterElementPosition} from './removeClusterElementPosition'; import saveWorkflowDefinition from './saveWorkflowDefinition'; +import {getTaskDispatcherTask} from './taskDispatcherConfig'; import updateClusterElementsPositions from './updateClusterElementsPositions'; interface SaveClusterElementNodesPositionProps { @@ -33,9 +34,10 @@ export default function saveClusterElementNodesPosition({ const workflowDefinitionTasks = JSON.parse(workflow.definition).tasks; - const mainClusterRootTask = workflowDefinitionTasks.find( - (task: {name: string}) => task.name === rootClusterElementNodeData?.workflowNodeName - ); + const mainClusterRootTask = getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }); if (!mainClusterRootTask || !mainClusterRootTask.clusterElements) { console.error('Main cluster root task or cluster elements not found'); diff --git a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts index 62f62eeaca..8962af61ba 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts @@ -3,7 +3,9 @@ import {BranchCaseType, NodeDataType, TaskDispatcherContextType, WorkflowDefinit import {UseMutationResult} from '@tanstack/react-query'; import useWorkflowDataStore from '../stores/useWorkflowDataStore'; +import getRecursivelyUpdatedTasks from './getRecursivelyUpdatedTasks'; import insertTaskDispatcherSubtask from './insertTaskDispatcherSubtask'; +import {getTaskDispatcherTask} from './taskDispatcherConfig'; const SPACE = 4; @@ -153,41 +155,53 @@ export default async function saveWorkflowDefinition({ (task) => task.name === existingWorkflowTask.name ); - if (existingTaskIndex === undefined || existingTaskIndex === -1) { - return; - } - let combinedParameters = { ...existingWorkflowTask.parameters, ...newTask.parameters, }; if (existingWorkflowTask.type !== newTask.type) { - delete updatedWorkflowDefinitionTasks[existingTaskIndex].parameters; - combinedParameters = newTask.parameters ?? {}; } - if (existingWorkflowTask.clusterRoot) { - const rootClusterElementTask: WorkflowTask = { - ...newTask, - clusterElements: { - ...(newTask.clusterElements || {}), - }, - }; - - updatedWorkflowDefinitionTasks[existingTaskIndex] = rootClusterElementTask; - } else { - const combinedTask: WorkflowTask = { - ...newTask, - parameters: combinedParameters, - }; + const taskToUpdate = existingWorkflowTask.clusterRoot + ? { + ...newTask, + clusterElements: { + ...(newTask.clusterElements || {}), + }, + } + : { + ...newTask, + parameters: combinedParameters, + }; + + if (existingTaskIndex !== undefined && existingTaskIndex !== -1) { + if (existingWorkflowTask.type !== newTask.type) { + delete updatedWorkflowDefinitionTasks[existingTaskIndex].parameters; + } updatedWorkflowDefinitionTasks = [ ...updatedWorkflowDefinitionTasks.slice(0, existingTaskIndex), - combinedTask, + taskToUpdate, ...updatedWorkflowDefinitionTasks.slice(existingTaskIndex + 1), ]; + } else { + const nestedTask = getTaskDispatcherTask({ + taskDispatcherId: existingWorkflowTask.name, + tasks: workflowDefinitionTasks, + }); + + if (!nestedTask) { + console.error(`Task ${existingWorkflowTask.name} not found in workflow definition`); + + return; + } + + updatedWorkflowDefinitionTasks = getRecursivelyUpdatedTasks( + updatedWorkflowDefinitionTasks, + taskToUpdate + ); } } else { updatedWorkflowDefinitionTasks = [...(workflowDefinitionTasks || [])]; From 1167a2696b1dabe9d43859a6f0cfe1104cd10fd7 Mon Sep 17 00:00:00 2001 From: mkriskovic Date: Mon, 17 Nov 2025 16:21:07 +0100 Subject: [PATCH 2/7] 2867 - WorkflowTask initialized in condition loop and map task dispatchers --- .../condition/ConditionTaskDispatcher.java | 19 ++++++++++++++----- .../ConditionTaskCompletionHandler.java | 9 +++++---- .../dispatcher/loop/LoopTaskDispatcher.java | 7 +++++-- .../completion/LoopTaskCompletionHandler.java | 7 +++++-- .../dispatcher/map/MapTaskDispatcher.java | 7 +++++-- .../completion/MapTaskCompletionHandler.java | 7 +++++-- 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/ConditionTaskDispatcher.java b/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/ConditionTaskDispatcher.java index afc44fcd1b..6afb1ffe61 100644 --- a/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/ConditionTaskDispatcher.java +++ b/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/ConditionTaskDispatcher.java @@ -23,7 +23,7 @@ import com.bytechef.atlas.configuration.domain.Task; import com.bytechef.atlas.configuration.domain.WorkflowTask; import com.bytechef.atlas.coordinator.event.TaskExecutionCompleteEvent; -import com.bytechef.atlas.coordinator.task.dispatcher.ErrorHandlingTaskDispatcher; +import com.bytechef.atlas.coordinator.event.TaskExecutionErrorEvent; import com.bytechef.atlas.coordinator.task.dispatcher.TaskDispatcher; import com.bytechef.atlas.coordinator.task.dispatcher.TaskDispatcherResolver; import com.bytechef.atlas.execution.domain.Context; @@ -32,15 +32,18 @@ import com.bytechef.atlas.execution.service.TaskExecutionService; import com.bytechef.atlas.file.storage.TaskFileStorage; import com.bytechef.commons.util.MapUtils; +import com.bytechef.error.ExecutionError; import com.bytechef.evaluator.Evaluator; import com.bytechef.task.dispatcher.condition.util.ConditionTaskUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.time.Instant; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.context.ApplicationEventPublisher; /** @@ -82,11 +85,9 @@ public void doDispatch(TaskExecution taskExecution) { List subWorkflowTasks; if (ConditionTaskUtils.resolveCase(taskExecution)) { - subWorkflowTasks = MapUtils.getList( - taskExecution.getParameters(), CASE_TRUE, WorkflowTask.class, Collections.emptyList()); + subWorkflowTasks = getSubWorkflowTasks(taskExecution, CASE_TRUE); } else { - subWorkflowTasks = MapUtils.getList( - taskExecution.getParameters(), CASE_FALSE, WorkflowTask.class, Collections.emptyList()); + subWorkflowTasks = getSubWorkflowTasks(taskExecution, CASE_FALSE); } if (!subWorkflowTasks.isEmpty()) { @@ -124,6 +125,14 @@ public void doDispatch(TaskExecution taskExecution) { } + private static List getSubWorkflowTasks(TaskExecution conditionTaskExecution, String caseTrue) { + return ((List>) conditionTaskExecution.getParameters() + .get(caseTrue)) + .stream() + .map(WorkflowTask::new) + .toList(); + } + @Override public TaskDispatcher resolve(Task task) { if (Objects.equals(task.getType(), CONDITION + "/v1")) { diff --git a/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/completion/ConditionTaskCompletionHandler.java b/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/completion/ConditionTaskCompletionHandler.java index 5edd0adffb..25dac012af 100644 --- a/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/completion/ConditionTaskCompletionHandler.java +++ b/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/completion/ConditionTaskCompletionHandler.java @@ -29,12 +29,10 @@ import com.bytechef.atlas.execution.service.ContextService; import com.bytechef.atlas.execution.service.TaskExecutionService; import com.bytechef.atlas.file.storage.TaskFileStorage; -import com.bytechef.commons.util.MapUtils; import com.bytechef.evaluator.Evaluator; import com.bytechef.task.dispatcher.condition.util.ConditionTaskUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.time.Instant; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -153,7 +151,10 @@ public void handle(TaskExecution taskExecution) { } private static List getSubWorkflowTasks(TaskExecution conditionTaskExecution, String caseTrue) { - return MapUtils.getList( - conditionTaskExecution.getParameters(), caseTrue, WorkflowTask.class, Collections.emptyList()); + return ((List>) conditionTaskExecution.getParameters() + .get(caseTrue)) + .stream() + .map(WorkflowTask::new) + .toList(); } } diff --git a/server/libs/modules/task-dispatchers/loop/src/main/java/com/bytechef/task/dispatcher/loop/LoopTaskDispatcher.java b/server/libs/modules/task-dispatchers/loop/src/main/java/com/bytechef/task/dispatcher/loop/LoopTaskDispatcher.java index 4bb5b544a9..1fb6f10304 100644 --- a/server/libs/modules/task-dispatchers/loop/src/main/java/com/bytechef/task/dispatcher/loop/LoopTaskDispatcher.java +++ b/server/libs/modules/task-dispatchers/loop/src/main/java/com/bytechef/task/dispatcher/loop/LoopTaskDispatcher.java @@ -81,8 +81,11 @@ public LoopTaskDispatcher( @Override public void doDispatch(TaskExecution taskExecution) { boolean loopForever = MapUtils.getBoolean(taskExecution.getParameters(), LOOP_FOREVER, false); - List iterateeWorkflowTasks = MapUtils.getRequiredList( - taskExecution.getParameters(), ITERATEE, WorkflowTask.class); + List iterateeWorkflowTasks = ((List>) taskExecution.getParameters() + .get(ITERATEE)) + .stream() + .map(WorkflowTask::new) + .toList(); List items = MapUtils.getList(taskExecution.getParameters(), ITEMS, Collections.emptyList()); taskExecution.setStartDate(Instant.now()); diff --git a/server/libs/modules/task-dispatchers/loop/src/main/java/com/bytechef/task/dispatcher/loop/completion/LoopTaskCompletionHandler.java b/server/libs/modules/task-dispatchers/loop/src/main/java/com/bytechef/task/dispatcher/loop/completion/LoopTaskCompletionHandler.java index 0c52a6f22f..f9e67c2f5e 100644 --- a/server/libs/modules/task-dispatchers/loop/src/main/java/com/bytechef/task/dispatcher/loop/completion/LoopTaskCompletionHandler.java +++ b/server/libs/modules/task-dispatchers/loop/src/main/java/com/bytechef/task/dispatcher/loop/completion/LoopTaskCompletionHandler.java @@ -95,8 +95,11 @@ public void handle(TaskExecution taskExecution) { taskExecution = taskExecutionService.update(taskExecution); - List iterateeWorkflowTasks = MapUtils.getRequiredList( - loopTaskExecution.getParameters(), ITERATEE, WorkflowTask.class); + List iterateeWorkflowTasks = ((List>) loopTaskExecution.getParameters() + .get(ITERATEE)) + .stream() + .map(WorkflowTask::new) + .toList(); Map parentContextValue = updateParentContextValue(taskExecution, loopTaskExecution); diff --git a/server/libs/modules/task-dispatchers/map/src/main/java/com/bytechef/task/dispatcher/map/MapTaskDispatcher.java b/server/libs/modules/task-dispatchers/map/src/main/java/com/bytechef/task/dispatcher/map/MapTaskDispatcher.java index a36be213d7..b4e9d20935 100644 --- a/server/libs/modules/task-dispatchers/map/src/main/java/com/bytechef/task/dispatcher/map/MapTaskDispatcher.java +++ b/server/libs/modules/task-dispatchers/map/src/main/java/com/bytechef/task/dispatcher/map/MapTaskDispatcher.java @@ -83,8 +83,11 @@ public MapTaskDispatcher( @Override public void doDispatch(TaskExecution taskExecution) { List items = MapUtils.getRequiredList(taskExecution.getParameters(), ITEMS, Object.class); - List iterateeWorkflowTasks = MapUtils.getRequiredList( - taskExecution.getParameters(), ITERATEE, WorkflowTask.class); + List iterateeWorkflowTasks = ((List>) taskExecution.getParameters() + .get(ITERATEE)) + .stream() + .map(WorkflowTask::new) + .toList(); taskExecution.setStartDate(Instant.now()); taskExecution.setStatus(TaskExecution.Status.STARTED); diff --git a/server/libs/modules/task-dispatchers/map/src/main/java/com/bytechef/task/dispatcher/map/completion/MapTaskCompletionHandler.java b/server/libs/modules/task-dispatchers/map/src/main/java/com/bytechef/task/dispatcher/map/completion/MapTaskCompletionHandler.java index 60a9b28b9e..e51c811f6b 100644 --- a/server/libs/modules/task-dispatchers/map/src/main/java/com/bytechef/task/dispatcher/map/completion/MapTaskCompletionHandler.java +++ b/server/libs/modules/task-dispatchers/map/src/main/java/com/bytechef/task/dispatcher/map/completion/MapTaskCompletionHandler.java @@ -117,8 +117,11 @@ public void handle(TaskExecution taskExecution) { TaskExecution mapTaskExecution = taskExecutionService.getTaskExecution(taskExecutionParentId); - List iterateeWorkflowTasks = MapUtils.getRequiredList( - mapTaskExecution.getParameters(), ITERATEE, WorkflowTask.class); + List iterateeWorkflowTasks = ((List>) mapTaskExecution.getParameters() + .get(ITERATEE)) + .stream() + .map(WorkflowTask::new) + .toList(); if (taskExecution.getTaskNumber() < iterateeWorkflowTasks.size()) { WorkflowTask iterationWorkflowTask = iterateeWorkflowTasks.get(taskExecution.getTaskNumber()); From 01397411b12a304c5650949f952c690d311f8f44 Mon Sep 17 00:00:00 2001 From: mkriskovic Date: Mon, 17 Nov 2025 23:49:03 +0100 Subject: [PATCH 3/7] 2867 - typecheck. this probably needs to be looked into and changed!!! --- .../utils/clusterElementsFieldChangeUtils.ts | 17 ++++++++++++---- .../utils/handleComponentAddedSuccess.ts | 15 ++++++++------ .../utils/saveClusterElementFieldChange.ts | 20 +++++++++++-------- .../utils/saveWorkflowDefinition.ts | 5 ++++- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/client/src/pages/platform/workflow-editor/utils/clusterElementsFieldChangeUtils.ts b/client/src/pages/platform/workflow-editor/utils/clusterElementsFieldChangeUtils.ts index f162baf89d..85dbd82928 100644 --- a/client/src/pages/platform/workflow-editor/utils/clusterElementsFieldChangeUtils.ts +++ b/client/src/pages/platform/workflow-editor/utils/clusterElementsFieldChangeUtils.ts @@ -12,7 +12,13 @@ type FieldUpdateType = { interface CreateUpdatedElementProps { currentComponentDefinition: ComponentDefinition; currentOperationProperties?: Array; - element: ClusterElementItemType | NodeDataType; + element: + | ClusterElementItemType + | NodeDataType + | (Omit & { + componentName?: string; + workflowNodeName?: string; + }); fieldUpdate: FieldUpdateType; } @@ -59,7 +65,10 @@ interface UpdateClusterRootElementFieldProps { currentComponentDefinition: ComponentDefinition; currentOperationProperties?: Array; fieldUpdate: FieldUpdateType; - mainRootElement: NodeDataType; + mainRootElement: Omit & { + componentName?: string; + workflowNodeName?: string; + }; } export function updateClusterRootElementField({ @@ -78,9 +87,9 @@ export function updateClusterRootElementField({ return { ...mainRootElement, ...updatedElementData, - componentName: mainRootElement.componentName, + ...(mainRootElement.componentName ? {componentName: mainRootElement.componentName} : {}), name: mainRootElement.name, - workflowNodeName: mainRootElement.workflowNodeName, + ...(mainRootElement.workflowNodeName ? {workflowNodeName: mainRootElement.workflowNodeName} : {}), }; } diff --git a/client/src/pages/platform/workflow-editor/utils/handleComponentAddedSuccess.ts b/client/src/pages/platform/workflow-editor/utils/handleComponentAddedSuccess.ts index c07f635b05..3ff395fc82 100644 --- a/client/src/pages/platform/workflow-editor/utils/handleComponentAddedSuccess.ts +++ b/client/src/pages/platform/workflow-editor/utils/handleComponentAddedSuccess.ts @@ -2,11 +2,14 @@ import useWorkflowNodeDetailsPanelStore from '@/pages/platform/workflow-editor/s import {Workflow} from '@/shared/middleware/platform/configuration'; import {WorkflowNodeOutputKeys} from '@/shared/queries/platform/workflowNodeOutputs.queries'; import {environmentStore} from '@/shared/stores/useEnvironmentStore'; -import {NodeDataType} from '@/shared/types'; +import {ComponentType, NodeDataType} from '@/shared/types'; import {QueryClient} from '@tanstack/react-query'; interface HandleComponentAddedSuccessProps { - nodeData: NodeDataType; + nodeData: Omit & { + componentName?: string; + workflowNodeName?: string; + }; queryClient: QueryClient; workflow: Workflow; } @@ -33,12 +36,12 @@ export default function handleComponentAddedSuccess({ if (useWorkflowNodeDetailsPanelStore.getState().workflowNodeDetailsPanelOpen) { if (currentNode?.trigger && nodeData.trigger) { - setCurrentNode({...currentNode, ...nodeData}); - setCurrentComponent({...currentComponent, ...nodeData}); + setCurrentNode({...currentNode, ...nodeData} as NodeDataType); + setCurrentComponent({...currentComponent, ...nodeData} as ComponentType); } } else if (!nodeData.clusterElements) { - setCurrentNode({...nodeData, description: ''}); - setCurrentComponent({...nodeData, description: ''}); + setCurrentNode({...nodeData, description: ''} as NodeDataType); + setCurrentComponent({...nodeData, description: ''} as ComponentType); setWorkflowNodeDetailsPanelOpen(true); } } diff --git a/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts b/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts index 3def663fff..10818a5670 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts @@ -1,5 +1,5 @@ import {ComponentDefinition, Workflow} from '@/shared/middleware/platform/configuration'; -import {ClusterElementsType, NodeDataType, PropertyAllType} from '@/shared/types'; +import {ClusterElementsType, ComponentType, NodeDataType, PropertyAllType} from '@/shared/types'; import {UseMutationResult} from '@tanstack/react-query'; import useWorkflowDataStore from '../stores/useWorkflowDataStore'; @@ -49,15 +49,15 @@ export default function saveClusterElementFieldChange({ tasks: workflowDefinitionTasks, }) : undefined; - // const mainClusterRootTask = workflowDefinitionTasks?.find( - // (task: {name: string}) => task.name === rootClusterElementNodeData?.workflowNodeName - // ); if (!mainClusterRootTask) { return; } - let updatedMainRootData: NodeDataType; + let updatedMainRootData: Omit & { + componentName?: string; + workflowNodeName?: string; + }; let updatedClusterElements: ClusterElementsType; if ( @@ -104,7 +104,11 @@ export default function saveClusterElementFieldChange({ invalidateWorkflowQueries, nodeData: updatedMainRootData, onSuccess: () => { - let commonUpdates: NodeDataType = { + let commonUpdates: Omit & { + componentName?: string; + name: string; + workflowNodeName?: string; + } = { componentName, name, workflowNodeName, @@ -134,12 +138,12 @@ export default function saveClusterElementFieldChange({ setCurrentNode({ ...currentNode, ...commonUpdates, - }); + } as NodeDataType); setCurrentComponent({ ...currentComponent, ...commonUpdates, - }); + } as ComponentType); if (rootClusterElementNodeData) { if (currentNode.clusterRoot && !currentNode.isNestedClusterRoot) { diff --git a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts index 8962af61ba..dfa3e31cbd 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts @@ -17,7 +17,10 @@ type UpdateWorkflowRequestType = { interface SaveWorkflowDefinitionProps { decorative?: boolean; invalidateWorkflowQueries: () => void; - nodeData?: NodeDataType; + nodeData?: Omit & { + componentName?: string; + workflowNodeName?: string; + }; nodeIndex?: number; onSuccess?: () => void; placeholderId?: string; From eb08b247c54266fd76c0bba8b5c62d29d3430eaf Mon Sep 17 00:00:00 2001 From: katarina-cala Date: Wed, 19 Nov 2025 15:41:47 +0100 Subject: [PATCH 4/7] 2867-fixed typescript issues --- .../WorkflowNodesPopoverMenuOperationList.tsx | 42 ++++++++++------- .../utils/clusterElementsFieldChangeUtils.ts | 17 ++----- .../utils/handleComponentAddedSuccess.ts | 15 +++--- .../utils/saveClusterElementFieldChange.ts | 47 ++++++++++--------- .../utils/saveClusterElementNodesPosition.ts | 12 ++++- .../utils/saveWorkflowDefinition.ts | 5 +- 6 files changed, 72 insertions(+), 66 deletions(-) diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx index bc26bbabd3..d05c2d9fd4 100644 --- a/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx +++ b/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx @@ -188,21 +188,25 @@ const WorkflowNodesPopoverMenuOperationList = ({ return; } + if (!rootClusterElementNodeData?.workflowNodeName || !rootClusterElementNodeData?.componentName) { + console.error('Root cluster element node data is missing required properties'); + + return; + } + const workflowDefinitionTasks = JSON.parse(workflow.definition).tasks; - const mainClusterRootTask = rootClusterElementNodeData?.workflowNodeName - ? getTaskDispatcherTask({ - taskDispatcherId: rootClusterElementNodeData.workflowNodeName, - tasks: workflowDefinitionTasks, - }) - : undefined; + const mainClusterRootTask = getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }); if (!mainClusterRootTask) { return; } const clusterElements = initializeClusterElementsObject({ - clusterElementsData: mainClusterRootTask?.clusterElements || {}, + clusterElementsData: mainClusterRootTask.clusterElements || {}, mainClusterRootComponentDefinition, mainClusterRootTask, }); @@ -212,7 +216,7 @@ const WorkflowNodesPopoverMenuOperationList = ({ clusterElements, elementType: clusterElementType, isMultipleElements, - mainRootId: rootClusterElementNodeData?.workflowNodeName, + mainRootId: rootClusterElementNodeData.workflowNodeName, sourceNodeId, }); @@ -234,22 +238,28 @@ const WorkflowNodesPopoverMenuOperationList = ({ } if (workflowNodeDetailsPanelOpen && currentNode?.workflowNodeName === sourceNodeName) { - if (rootClusterElementNodeData) { - setCurrentNode({ - ...rootClusterElementNodeData, - clusterElements: updatedClusterElements.nestedClusterElements, - }); - } + setCurrentNode({ + ...rootClusterElementNodeData, + clusterElements: updatedClusterElements.nestedClusterElements, + }); setWorkflowNodeDetailsPanelOpen(false); } saveWorkflowDefinition({ invalidateWorkflowQueries, - nodeData: updatedNodeData, + nodeData: { + ...updatedNodeData, + componentName: rootClusterElementNodeData.componentName, + workflowNodeName: rootClusterElementNodeData.workflowNodeName, + }, onSuccess: () => { handleComponentAddedSuccess({ - nodeData: updatedNodeData, + nodeData: { + ...updatedNodeData, + componentName: rootClusterElementNodeData.componentName, + workflowNodeName: rootClusterElementNodeData.workflowNodeName, + }, queryClient, workflow, }); diff --git a/client/src/pages/platform/workflow-editor/utils/clusterElementsFieldChangeUtils.ts b/client/src/pages/platform/workflow-editor/utils/clusterElementsFieldChangeUtils.ts index 85dbd82928..f162baf89d 100644 --- a/client/src/pages/platform/workflow-editor/utils/clusterElementsFieldChangeUtils.ts +++ b/client/src/pages/platform/workflow-editor/utils/clusterElementsFieldChangeUtils.ts @@ -12,13 +12,7 @@ type FieldUpdateType = { interface CreateUpdatedElementProps { currentComponentDefinition: ComponentDefinition; currentOperationProperties?: Array; - element: - | ClusterElementItemType - | NodeDataType - | (Omit & { - componentName?: string; - workflowNodeName?: string; - }); + element: ClusterElementItemType | NodeDataType; fieldUpdate: FieldUpdateType; } @@ -65,10 +59,7 @@ interface UpdateClusterRootElementFieldProps { currentComponentDefinition: ComponentDefinition; currentOperationProperties?: Array; fieldUpdate: FieldUpdateType; - mainRootElement: Omit & { - componentName?: string; - workflowNodeName?: string; - }; + mainRootElement: NodeDataType; } export function updateClusterRootElementField({ @@ -87,9 +78,9 @@ export function updateClusterRootElementField({ return { ...mainRootElement, ...updatedElementData, - ...(mainRootElement.componentName ? {componentName: mainRootElement.componentName} : {}), + componentName: mainRootElement.componentName, name: mainRootElement.name, - ...(mainRootElement.workflowNodeName ? {workflowNodeName: mainRootElement.workflowNodeName} : {}), + workflowNodeName: mainRootElement.workflowNodeName, }; } diff --git a/client/src/pages/platform/workflow-editor/utils/handleComponentAddedSuccess.ts b/client/src/pages/platform/workflow-editor/utils/handleComponentAddedSuccess.ts index 3ff395fc82..c07f635b05 100644 --- a/client/src/pages/platform/workflow-editor/utils/handleComponentAddedSuccess.ts +++ b/client/src/pages/platform/workflow-editor/utils/handleComponentAddedSuccess.ts @@ -2,14 +2,11 @@ import useWorkflowNodeDetailsPanelStore from '@/pages/platform/workflow-editor/s import {Workflow} from '@/shared/middleware/platform/configuration'; import {WorkflowNodeOutputKeys} from '@/shared/queries/platform/workflowNodeOutputs.queries'; import {environmentStore} from '@/shared/stores/useEnvironmentStore'; -import {ComponentType, NodeDataType} from '@/shared/types'; +import {NodeDataType} from '@/shared/types'; import {QueryClient} from '@tanstack/react-query'; interface HandleComponentAddedSuccessProps { - nodeData: Omit & { - componentName?: string; - workflowNodeName?: string; - }; + nodeData: NodeDataType; queryClient: QueryClient; workflow: Workflow; } @@ -36,12 +33,12 @@ export default function handleComponentAddedSuccess({ if (useWorkflowNodeDetailsPanelStore.getState().workflowNodeDetailsPanelOpen) { if (currentNode?.trigger && nodeData.trigger) { - setCurrentNode({...currentNode, ...nodeData} as NodeDataType); - setCurrentComponent({...currentComponent, ...nodeData} as ComponentType); + setCurrentNode({...currentNode, ...nodeData}); + setCurrentComponent({...currentComponent, ...nodeData}); } } else if (!nodeData.clusterElements) { - setCurrentNode({...nodeData, description: ''} as NodeDataType); - setCurrentComponent({...nodeData, description: ''} as ComponentType); + setCurrentNode({...nodeData, description: ''}); + setCurrentComponent({...nodeData, description: ''}); setWorkflowNodeDetailsPanelOpen(true); } } diff --git a/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts b/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts index 10818a5670..bd1761f565 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts @@ -1,5 +1,5 @@ import {ComponentDefinition, Workflow} from '@/shared/middleware/platform/configuration'; -import {ClusterElementsType, ComponentType, NodeDataType, PropertyAllType} from '@/shared/types'; +import {ClusterElementsType, NodeDataType, PropertyAllType} from '@/shared/types'; import {UseMutationResult} from '@tanstack/react-query'; import useWorkflowDataStore from '../stores/useWorkflowDataStore'; @@ -41,41 +41,46 @@ export default function saveClusterElementFieldChange({ const {componentName, name, workflowNodeName} = currentNode; + if (!rootClusterElementNodeData?.workflowNodeName || !rootClusterElementNodeData?.componentName) { + console.error('Root cluster element node data is missing required properties'); + + return; + } + const workflowDefinitionTasks = JSON.parse(workflow.definition).tasks; - const mainClusterRootTask = rootClusterElementNodeData?.workflowNodeName - ? getTaskDispatcherTask({ - taskDispatcherId: rootClusterElementNodeData.workflowNodeName, - tasks: workflowDefinitionTasks, - }) - : undefined; + const mainClusterRootTask = getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }); if (!mainClusterRootTask) { return; } - let updatedMainRootData: Omit & { - componentName?: string; - workflowNodeName?: string; - }; + let updatedMainRootData: NodeDataType; let updatedClusterElements: ClusterElementsType; if ( currentNode.clusterRoot && - currentNode.workflowNodeName === rootClusterElementNodeData?.workflowNodeName && + currentNode.workflowNodeName === rootClusterElementNodeData.workflowNodeName && !currentNode.isNestedClusterRoot ) { updatedMainRootData = updateClusterRootElementField({ currentComponentDefinition, currentOperationProperties, fieldUpdate, - mainRootElement: mainClusterRootTask, + mainRootElement: { + ...mainClusterRootTask, + componentName: rootClusterElementNodeData.componentName, + workflowNodeName: rootClusterElementNodeData.workflowNodeName, + }, }); } else if ( currentNode.clusterElementType && - currentNode.workflowNodeName !== rootClusterElementNodeData?.workflowNodeName + currentNode.workflowNodeName !== rootClusterElementNodeData.workflowNodeName ) { - const clusterElements = mainClusterRootTask?.clusterElements; + const clusterElements = mainClusterRootTask.clusterElements; if (!clusterElements || Object.keys(clusterElements).length === 0) { return; @@ -92,6 +97,8 @@ export default function saveClusterElementFieldChange({ updatedMainRootData = { ...mainClusterRootTask, clusterElements: updatedClusterElements, + componentName: rootClusterElementNodeData.componentName, + workflowNodeName: rootClusterElementNodeData.workflowNodeName, }; } else { console.error('Unknown cluster element type or root element mismatch'); @@ -104,11 +111,7 @@ export default function saveClusterElementFieldChange({ invalidateWorkflowQueries, nodeData: updatedMainRootData, onSuccess: () => { - let commonUpdates: Omit & { - componentName?: string; - name: string; - workflowNodeName?: string; - } = { + let commonUpdates: NodeDataType = { componentName, name, workflowNodeName, @@ -138,12 +141,12 @@ export default function saveClusterElementFieldChange({ setCurrentNode({ ...currentNode, ...commonUpdates, - } as NodeDataType); + }); setCurrentComponent({ ...currentComponent, ...commonUpdates, - } as ComponentType); + }); if (rootClusterElementNodeData) { if (currentNode.clusterRoot && !currentNode.isNestedClusterRoot) { diff --git a/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts b/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts index ec69975363..e77b445fe2 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts @@ -82,7 +82,11 @@ export default function saveClusterElementNodesPosition({ // Save updated data but reset the position saving flag even when there are errors saveWorkflowDefinition({ invalidateWorkflowQueries, - nodeData: updatedNodeData, + nodeData: { + ...updatedNodeData, + componentName: rootClusterElementNodeData.componentName, + workflowNodeName: rootClusterElementNodeData.workflowNodeName, + }, updateWorkflowMutation, }) .catch((error) => { @@ -109,7 +113,11 @@ export default function saveClusterElementNodesPosition({ saveWorkflowDefinition({ invalidateWorkflowQueries, - nodeData: updatedNodeData, + nodeData: { + ...updatedNodeData, + componentName: rootClusterElementNodeData.componentName, + workflowNodeName: rootClusterElementNodeData.workflowNodeName, + }, updateWorkflowMutation, }); } diff --git a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts index dfa3e31cbd..8962af61ba 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts @@ -17,10 +17,7 @@ type UpdateWorkflowRequestType = { interface SaveWorkflowDefinitionProps { decorative?: boolean; invalidateWorkflowQueries: () => void; - nodeData?: Omit & { - componentName?: string; - workflowNodeName?: string; - }; + nodeData?: NodeDataType; nodeIndex?: number; onSuccess?: () => void; placeholderId?: string; From fdcf685ededd6dd070c39a0711a25c0a8fc7088b Mon Sep 17 00:00:00 2001 From: mkriskovic Date: Thu, 20 Nov 2025 09:13:21 +0100 Subject: [PATCH 5/7] SA --- .../task/dispatcher/condition/ConditionTaskDispatcher.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/ConditionTaskDispatcher.java b/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/ConditionTaskDispatcher.java index 6afb1ffe61..48877aea71 100644 --- a/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/ConditionTaskDispatcher.java +++ b/server/libs/modules/task-dispatchers/condition/src/main/java/com/bytechef/task/dispatcher/condition/ConditionTaskDispatcher.java @@ -23,7 +23,7 @@ import com.bytechef.atlas.configuration.domain.Task; import com.bytechef.atlas.configuration.domain.WorkflowTask; import com.bytechef.atlas.coordinator.event.TaskExecutionCompleteEvent; -import com.bytechef.atlas.coordinator.event.TaskExecutionErrorEvent; +import com.bytechef.atlas.coordinator.task.dispatcher.ErrorHandlingTaskDispatcher; import com.bytechef.atlas.coordinator.task.dispatcher.TaskDispatcher; import com.bytechef.atlas.coordinator.task.dispatcher.TaskDispatcherResolver; import com.bytechef.atlas.execution.domain.Context; @@ -31,19 +31,14 @@ import com.bytechef.atlas.execution.service.ContextService; import com.bytechef.atlas.execution.service.TaskExecutionService; import com.bytechef.atlas.file.storage.TaskFileStorage; -import com.bytechef.commons.util.MapUtils; -import com.bytechef.error.ExecutionError; import com.bytechef.evaluator.Evaluator; import com.bytechef.task.dispatcher.condition.util.ConditionTaskUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import org.apache.commons.lang3.Validate; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.context.ApplicationEventPublisher; /** From 2f52a6d4a90cee07512ab697849fec2b53b0e310 Mon Sep 17 00:00:00 2001 From: mkriskovic Date: Thu, 20 Nov 2025 12:06:55 +0100 Subject: [PATCH 6/7] 2867 - put getTaskDispatcherTask on various classes to fix retrieving data in cluster elements panel --- .../components/WorkflowNodeDetailsPanel.tsx | 33 ++++++++++++++----- .../node-details-tabs/DescriptionTab.tsx | 12 +++++-- .../components/properties/Property.tsx | 10 ++++-- .../PropertyCodeEditorSheet.tsx | 7 +++- .../PropertyCodeEditorSheetRightPanel.tsx | 11 +++++-- .../PropertyMentionsInputEditor.tsx | 10 ++++-- 6 files changed, 62 insertions(+), 21 deletions(-) diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx index 34ae837934..c3e60f0e23 100644 --- a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx +++ b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx @@ -429,14 +429,23 @@ const WorkflowNodeDetailsPanel = ({ ); const currentWorkflowTask = useMemo( - () => workflow.tasks?.find((task) => task.name === currentNode?.workflowNodeName), + () => + currentNode?.workflowNodeName + ? getTaskDispatcherTask({ + taskDispatcherId: currentNode.workflowNodeName, + tasks: workflow.tasks || [], + }) + : undefined, [workflow.tasks, currentNode] ); const currentClusterElementsConnections = useMemo(() => { - const mainClusterRootTask = workflow.tasks?.find( - (task) => task.name === rootClusterElementNodeData?.workflowNodeName - ); + const mainClusterRootTask = rootClusterElementNodeData?.workflowNodeName + ? getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflow.tasks || [], + }) + : undefined; if (mainClusterRootTask?.connections && currentNode?.clusterElementType) { return mainClusterRootTask.connections.filter( @@ -561,9 +570,12 @@ const WorkflowNodeDetailsPanel = ({ let filteredNodeNames = workflowNodeOutputs?.map((output) => output.workflowNodeName) || []; if (currentNode?.conditionData) { - const parentConditionTask = workflow.tasks?.find( - (task) => task.name === currentNode.conditionData?.conditionId - ); + const parentConditionTask = currentNode.conditionData.conditionId + ? getTaskDispatcherTask({ + taskDispatcherId: currentNode.conditionData.conditionId, + tasks: workflow.tasks || [], + }) + : undefined; if (!parentConditionTask) { return []; @@ -582,7 +594,12 @@ const WorkflowNodeDetailsPanel = ({ (nodeName) => !oppositeConditionCaseNodeNames?.includes(nodeName) ); } else if (currentNode?.branchData) { - const parentBranchTask = workflow.tasks?.find((task) => task.name === currentNode.branchData?.branchId); + const parentBranchTask = currentNode.branchData.branchId + ? getTaskDispatcherTask({ + taskDispatcherId: currentNode.branchData.branchId, + tasks: workflow.tasks || [], + }) + : undefined; if (!parentBranchTask || !parentBranchTask.parameters) { return []; diff --git a/client/src/pages/platform/workflow-editor/components/node-details-tabs/DescriptionTab.tsx b/client/src/pages/platform/workflow-editor/components/node-details-tabs/DescriptionTab.tsx index 4666f81610..b1554e3a3b 100644 --- a/client/src/pages/platform/workflow-editor/components/node-details-tabs/DescriptionTab.tsx +++ b/client/src/pages/platform/workflow-editor/components/node-details-tabs/DescriptionTab.tsx @@ -18,6 +18,7 @@ import {useShallow} from 'zustand/react/shallow'; import saveClusterElementFieldChange from '../../utils/saveClusterElementFieldChange'; import saveTaskDispatcherSubtaskFieldChange from '../../utils/saveTaskDispatcherSubtaskFieldChange'; import saveWorkflowDefinition from '../../utils/saveWorkflowDefinition'; +import {getTaskDispatcherTask} from '../../utils/taskDispatcherConfig'; interface DescriptionTabProps { invalidateWorkflowQueries: () => void; @@ -179,9 +180,14 @@ const DescriptionTab = ({invalidateWorkflowQueries, nodeDefinition, updateWorkfl }); }, 300); - let workflowTaskOrTrigger = [...(workflow.tasks ?? []), ...(workflow.triggers ?? [])]?.find( - (task) => task.name === currentNode?.workflowNodeName - ); + let workflowTaskOrTrigger = + workflow.triggers?.find((trigger) => trigger.name === currentNode?.workflowNodeName) || + (currentNode?.workflowNodeName + ? getTaskDispatcherTask({ + taskDispatcherId: currentNode.workflowNodeName, + tasks: workflow.tasks || [], + }) + : undefined); if (!workflowTaskOrTrigger && currentNode?.clusterElementType) { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/client/src/pages/platform/workflow-editor/components/properties/Property.tsx b/client/src/pages/platform/workflow-editor/components/properties/Property.tsx index 617076b5fc..7267a0a5e6 100644 --- a/client/src/pages/platform/workflow-editor/components/properties/Property.tsx +++ b/client/src/pages/platform/workflow-editor/components/properties/Property.tsx @@ -24,6 +24,7 @@ import useWorkflowNodeDetailsPanelStore from '@/pages/platform/workflow-editor/s import deleteProperty from '@/pages/platform/workflow-editor/utils/deleteProperty'; import getInputHTMLType from '@/pages/platform/workflow-editor/utils/getInputHTMLType'; import saveProperty from '@/pages/platform/workflow-editor/utils/saveProperty'; +import {getTaskDispatcherTask} from '@/pages/platform/workflow-editor/utils/taskDispatcherConfig'; import { GetClusterElementParameterDisplayConditions200Response, Option, @@ -622,9 +623,12 @@ const Property = ({ if (currentNode.clusterElementType) { const workflowDefinitionTasks = JSON.parse(workflow.definition).tasks; - const mainClusterRootTask = workflowDefinitionTasks?.find( - (task: {name: string}) => task.name === rootClusterElementNodeData?.workflowNodeName - ); + const mainClusterRootTask = rootClusterElementNodeData?.workflowNodeName + ? getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }) + : undefined; if (mainClusterRootTask?.clusterElements) { return getClusterElementByName(mainClusterRootTask.clusterElements, currentNode.name); diff --git a/client/src/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheet.tsx b/client/src/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheet.tsx index a3fbd48366..c5dd130287 100644 --- a/client/src/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheet.tsx +++ b/client/src/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheet.tsx @@ -23,6 +23,8 @@ import {PlayIcon, RefreshCwIcon, SaveIcon, SquareIcon} from 'lucide-react'; import {Suspense, lazy, useEffect, useState} from 'react'; import {twMerge} from 'tailwind-merge'; +import {getTaskDispatcherTask} from '../../../../utils/taskDispatcherConfig'; + const MonacoEditor = lazy(() => import('@/shared/components/MonacoEditorWrapper')); const ReactJson = lazy(() => import('react-json-view')); @@ -54,7 +56,10 @@ const PropertyCodeEditorSheet = ({ const copilotPanelOpen = useCopilotStore((state) => state.copilotPanelOpen); const currentEnvironmentId = useEnvironmentStore((state) => state.currentEnvironmentId); - const currentWorkflowTask = workflow.tasks?.find((task) => task.name === workflowNodeName); + const currentWorkflowTask = getTaskDispatcherTask({ + taskDispatcherId: workflowNodeName, + tasks: workflow.tasks || [], + }); const handleRunClick = () => { setScriptIsRunning(true); diff --git a/client/src/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheetRightPanel.tsx b/client/src/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheetRightPanel.tsx index 103f145358..a70999c46f 100644 --- a/client/src/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheetRightPanel.tsx +++ b/client/src/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheetRightPanel.tsx @@ -2,6 +2,8 @@ import PropertyCodeEditorSheetRightPanelConnections from '@/pages/platform/workf import PropertyCodeEditorSheetRightPanelInputs from '@/pages/platform/workflow-editor/components/properties/components/property-code-editor/PropertyCodeEditorSheetRightPanelInputs'; import {ComponentConnection, Workflow} from '@/shared/middleware/platform/configuration'; +import {getTaskDispatcherTask} from '../../../../utils/taskDispatcherConfig'; + interface PropertyCodeEditorSheetConnectionsSheetRightPanelProps { componentConnections: ComponentConnection[]; workflow: Workflow; @@ -13,12 +15,15 @@ const PropertyCodeEditorSheetRightPanel = ({ workflow, workflowNodeName, }: PropertyCodeEditorSheetConnectionsSheetRightPanelProps) => { + const currentTask = getTaskDispatcherTask({ + taskDispatcherId: workflowNodeName, + tasks: workflow.tasks || [], + }); + return (
- task.name === workflowNodeName)?.parameters?.input ?? {}} - /> +
diff --git a/client/src/pages/platform/workflow-editor/components/properties/components/property-mentions-input/PropertyMentionsInputEditor.tsx b/client/src/pages/platform/workflow-editor/components/properties/components/property-mentions-input/PropertyMentionsInputEditor.tsx index 04b1ff723a..1f10e0790f 100644 --- a/client/src/pages/platform/workflow-editor/components/properties/components/property-mentions-input/PropertyMentionsInputEditor.tsx +++ b/client/src/pages/platform/workflow-editor/components/properties/components/property-mentions-input/PropertyMentionsInputEditor.tsx @@ -10,6 +10,7 @@ import { transformValueForObjectAccess, } from '@/pages/platform/workflow-editor/utils/encodingUtils'; import saveProperty from '@/pages/platform/workflow-editor/utils/saveProperty'; +import {getTaskDispatcherTask} from '@/pages/platform/workflow-editor/utils/taskDispatcherConfig'; import {TASK_DISPATCHER_NAMES} from '@/shared/constants'; import { ComponentDefinitionBasic, @@ -132,9 +133,12 @@ const PropertyMentionsInputEditor = forwardRef task.name === rootClusterElementNodeData?.workflowNodeName - ); + const mainClusterRootTask = rootClusterElementNodeData?.workflowNodeName + ? getTaskDispatcherTask({ + taskDispatcherId: rootClusterElementNodeData.workflowNodeName, + tasks: workflowDefinitionTasks, + }) + : undefined; if (mainClusterRootTask?.clusterElements) { return getClusterElementByName(mainClusterRootTask.clusterElements, currentNode.name); From cb93b32cd2763841d77b5f8a7802d94b642d1bbb Mon Sep 17 00:00:00 2001 From: mkriskovic Date: Thu, 20 Nov 2025 13:59:28 +0100 Subject: [PATCH 7/7] 2867 - created getAllTasksRecursively because the initial implementation didn't account for tasks in cluster elements, rag and tools --- .../utils/getAllTasksRecursively.ts | 36 +++++++++++++++++++ .../workflow-editor/utils/getFormattedName.ts | 5 ++- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 client/src/pages/platform/workflow-editor/utils/getAllTasksRecursively.ts diff --git a/client/src/pages/platform/workflow-editor/utils/getAllTasksRecursively.ts b/client/src/pages/platform/workflow-editor/utils/getAllTasksRecursively.ts new file mode 100644 index 0000000000..7542e8edc0 --- /dev/null +++ b/client/src/pages/platform/workflow-editor/utils/getAllTasksRecursively.ts @@ -0,0 +1,36 @@ +import {TASK_DISPATCHER_NAMES} from '@/shared/constants'; +import {WorkflowTask} from '@/shared/middleware/platform/configuration'; + +import {TASK_DISPATCHER_CONFIG} from './taskDispatcherConfig'; + +/** + * Recursively gets all tasks from a workflow, including tasks nested inside task dispatchers + */ +export default function getAllTasksRecursively(tasks: Array): Array { + const allTasks: Array = []; + + const extractTasks = (taskList: Array) => { + taskList.forEach((task) => { + allTasks.push(task); + + // Check if this is a task dispatcher + const componentName = task.type?.split('/')[0]; + + if (componentName && TASK_DISPATCHER_NAMES.includes(componentName)) { + const config = TASK_DISPATCHER_CONFIG[componentName as keyof typeof TASK_DISPATCHER_CONFIG]; + + if (config) { + const subtasks = config.getSubtasks({getAllSubtasks: true, task}); + + if (subtasks && subtasks.length > 0) { + extractTasks(subtasks); + } + } + } + }); + }; + + extractTasks(tasks); + + return allTasks; +} diff --git a/client/src/pages/platform/workflow-editor/utils/getFormattedName.ts b/client/src/pages/platform/workflow-editor/utils/getFormattedName.ts index ddacc5c970..b0cd17ad0e 100644 --- a/client/src/pages/platform/workflow-editor/utils/getFormattedName.ts +++ b/client/src/pages/platform/workflow-editor/utils/getFormattedName.ts @@ -3,6 +3,7 @@ import {ClusterElementsType, NodeDataType} from '@/shared/types'; import {isPlainObject} from '../../cluster-element-editor/utils/clusterElementsUtils'; import useWorkflowDataStore from '../stores/useWorkflowDataStore'; +import getAllTasksRecursively from './getAllTasksRecursively'; export default function getFormattedName(itemName: string): string { const {nodes, workflow} = useWorkflowDataStore.getState(); @@ -13,7 +14,9 @@ export default function getFormattedName(itemName: string): string { const workflowDefinition = JSON.parse(workflow.definition!); - const clusterElementNames = workflowDefinition.tasks.map((task: WorkflowTask) => { + const allTasks = getAllTasksRecursively(workflowDefinition.tasks); + + const clusterElementNames = allTasks.map((task: WorkflowTask) => { const elementNames: string[] = []; const {clusterElements} = task;