瀏覽代碼

feat(condition): add support for 'exists' and 'not exists' operators (#9687)

-LAN- 6 月之前
父節點
當前提交
9a5bdae07f

+ 3 - 0
api/core/workflow/utils/condition/entities.py

@@ -25,6 +25,9 @@ SupportedComparisonOperator = Literal[
     "≤",
     "null",
     "not null",
+    # for file
+    "exists",
+    "not exists",
 ]
 
 

+ 24 - 1
api/core/workflow/utils/condition/processor.py

@@ -2,7 +2,7 @@ from collections.abc import Sequence
 from typing import Any, Literal
 
 from core.file import FileAttribute, file_manager
-from core.variables.segments import ArrayFileSegment
+from core.variables import ArrayFileSegment
 from core.workflow.entities.variable_pool import VariablePool
 
 from .entities import Condition, SubCondition, SupportedComparisonOperator
@@ -21,6 +21,8 @@ class ConditionProcessor:
 
         for condition in conditions:
             variable = variable_pool.get(condition.variable_selector)
+            if variable is None:
+                raise ValueError(f"Variable {condition.variable_selector} not found")
 
             if isinstance(variable, ArrayFileSegment) and condition.comparison_operator in {
                 "contains",
@@ -35,6 +37,15 @@ class ConditionProcessor:
                     sub_conditions=condition.sub_variable_condition.conditions,
                     operator=condition.sub_variable_condition.logical_operator,
                 )
+            elif condition.comparison_operator in {
+                "exists",
+                "not exists",
+            }:
+                result = _evaluate_condition(
+                    value=variable.value,
+                    operator=condition.comparison_operator,
+                    expected=None,
+                )
             else:
                 actual_value = variable.value if variable else None
                 expected_value = condition.value
@@ -103,6 +114,10 @@ def _evaluate_condition(
             return _assert_not_in(value=value, expected=expected)
         case "all of" if isinstance(expected, list):
             return _assert_all_of(value=value, expected=expected)
+        case "exists":
+            return _assert_exists(value=value)
+        case "not exists":
+            return _assert_not_exists(value=value)
         case _:
             raise ValueError(f"Unsupported operator: {operator}")
 
@@ -338,6 +353,14 @@ def _assert_all_of(*, value: Any, expected: Sequence[str]) -> bool:
     return True
 
 
+def _assert_exists(*, value: Any) -> bool:
+    return value is not None
+
+
+def _assert_not_exists(*, value: Any) -> bool:
+    return value is None
+
+
 def _process_sub_conditions(
     variable: ArrayFileSegment,
     sub_conditions: Sequence[SubCondition],

+ 1 - 0
api/tests/unit_tests/core/workflow/nodes/test_if_else.py

@@ -55,6 +55,7 @@ def test_execute_if_else_result_true():
     pool.add(["start", "less_than"], 21)
     pool.add(["start", "greater_than_or_equal"], 22)
     pool.add(["start", "less_than_or_equal"], 21)
+    pool.add(["start", "null"], None)
     pool.add(["start", "not_null"], "1212")
 
     node = IfElseNode(