diff --git a/server/libs/atlas/atlas-configuration/atlas-configuration-api/src/main/java/com/bytechef/atlas/configuration/util/WorkflowTaskUtils.java b/server/libs/atlas/atlas-configuration/atlas-configuration-api/src/main/java/com/bytechef/atlas/configuration/util/WorkflowTaskUtils.java index fa057e0ca4d..5b0c404853f 100644 --- a/server/libs/atlas/atlas-configuration/atlas-configuration-api/src/main/java/com/bytechef/atlas/configuration/util/WorkflowTaskUtils.java +++ b/server/libs/atlas/atlas-configuration/atlas-configuration-api/src/main/java/com/bytechef/atlas/configuration/util/WorkflowTaskUtils.java @@ -54,8 +54,7 @@ public static List getTasks(List workflowTasks, Stri returnedWorkflowTasks.addAll(getTasks(curWorkflowTasks, lastWorkflowNodeName)); } - if (firstItem instanceof Map map && map.containsKey(WorkflowConstants.NAME) && - map.containsKey(WorkflowConstants.TYPE)) { + if (firstItem instanceof Map map && isWorkflowTaskMap(map)) { List curWorkflowTasks = curList.stream() .map(item -> new WorkflowTask((Map) item)) @@ -75,8 +74,7 @@ public static List getTasks(List workflowTasks, Stri } // Fork/join support } else if (firstItem instanceof List list && !list.isEmpty() && - list.getFirst() instanceof Map map && map.containsKey(WorkflowConstants.NAME) && - map.containsKey(WorkflowConstants.TYPE)) { + list.getFirst() instanceof Map map && isWorkflowTaskMap(map)) { for (Object curItem : curList) { List curSubList = (List) curItem; @@ -90,8 +88,7 @@ public static List getTasks(List workflowTasks, Stri } } // Each support - } else if (entry.getValue() instanceof Map curMap && - curMap.containsKey(WorkflowConstants.NAME) && curMap.containsKey(WorkflowConstants.TYPE)) { + } else if (entry.getValue() instanceof Map curMap && isWorkflowTaskMap(curMap)) { returnedWorkflowTasks.addAll( getTasks(List.of(new WorkflowTask((Map) curMap)), lastWorkflowNodeName)); @@ -124,6 +121,31 @@ public static List getTasks(List workflowTasks, Stri return resultWorkflowTasks; } + private static boolean isWorkflowTaskMap(Map map) { + if (!map.containsKey(WorkflowConstants.NAME) || !map.containsKey(WorkflowConstants.TYPE)) { + return false; + } + + // Workflow task types always follow the format: componentName/vVersion[/operation]. + // This distinguishes real tasks from user-defined data structures that happen to have + // name/type keys (e.g., PostgreSQL column definitions like {"name": "ime", "type": "STRING"}). + return map.get(WorkflowConstants.TYPE) instanceof String type && isWorkflowNodeType(type); + } + + private static boolean isWorkflowNodeType(String type) { + String[] parts = type.split("/", -1); + + if (parts.length < 2 || parts.length > 3) { + return false; + } + + if (parts[0].isEmpty() || !parts[1].matches("v\\d+")) { + return false; + } + + return parts.length == 2 || !parts[2].isEmpty(); + } + private static List getPrevious(List workflowTasks, String workflowTaskName) { List previousWorkflowTasks = new ArrayList<>(); diff --git a/server/libs/atlas/atlas-configuration/atlas-configuration-api/src/test/java/com/bytechef/atlas/configuration/util/WorkflowTaskUtilsTest.java b/server/libs/atlas/atlas-configuration/atlas-configuration-api/src/test/java/com/bytechef/atlas/configuration/util/WorkflowTaskUtilsTest.java index 7ced2d45e51..70efbdacd7d 100644 --- a/server/libs/atlas/atlas-configuration/atlas-configuration-api/src/test/java/com/bytechef/atlas/configuration/util/WorkflowTaskUtilsTest.java +++ b/server/libs/atlas/atlas-configuration/atlas-configuration-api/src/test/java/com/bytechef/atlas/configuration/util/WorkflowTaskUtilsTest.java @@ -142,6 +142,28 @@ void testNonTaskListWithTypeKeyIsIgnored() { assertEquals(Set.of("dataStorage_1"), names); } + @Test + void testNonTaskListWithNameAndTypeKeysIsIgnored() { + // A parameter value that's List whose maps carry BOTH `name` and `type` + // must not be mistaken for tasks if the `type` value isn't a valid workflow node + // type (e.g., PostgreSQL column definitions use {"name": "col", "type": "STRING"}). + WorkflowTask task = new WorkflowTask(Map.of( + "name", "postgresql_1", + "type", "postgresql/v1/insert", + "parameters", Map.of( + "schema", "public", + "columns", List.of( + Map.of("type", "STRING", "name", "ime"), + Map.of("type", "NUMBER", "name", "age"))))); + + Set names = WorkflowTaskUtils.getTasks(List.of(task), null) + .stream() + .map(WorkflowTask::getName) + .collect(Collectors.toSet()); + + assertEquals(Set.of("postgresql_1"), names); + } + @Test void testFlattenLoopIterateeStartingWithTaskWithoutParameters() { WorkflowTask loop = new WorkflowTask(Map.of( diff --git a/server/libs/modules/components/var/src/main/java/com/bytechef/component/var/action/VarSetAction.java b/server/libs/modules/components/var/src/main/java/com/bytechef/component/var/action/VarSetAction.java index 0ffe6637d7e..446538af0d2 100644 --- a/server/libs/modules/components/var/src/main/java/com/bytechef/component/var/action/VarSetAction.java +++ b/server/libs/modules/components/var/src/main/java/com/bytechef/component/var/action/VarSetAction.java @@ -122,7 +122,7 @@ private enum ValueType { protected static OutputResponse output( Parameters inputParameters, Parameters connectionParameters, Context context) { - if (!inputParameters.containsKey(VALUE)) { + if (!inputParameters.containsKey(VALUE) || inputParameters.get(VALUE) == null) { return null; }