diff --git a/cli/src/test/java/io/kestra/cli/commands/flows/FlowDotCommandTest.java b/cli/src/test/java/io/kestra/cli/commands/flows/FlowDotCommandTest.java index 3e22e5a42ef..65c77c75dc4 100644 --- a/cli/src/test/java/io/kestra/cli/commands/flows/FlowDotCommandTest.java +++ b/cli/src/test/java/io/kestra/cli/commands/flows/FlowDotCommandTest.java @@ -27,7 +27,7 @@ void run() { Integer call = PicocliRunner.call(FlowDotCommand.class, ctx, args); assertThat(call, is(0)); - assertThat(out.toString(), containsString("\"root.date\"[shape=box,label=\"date\"];")); + assertThat(out.toString(), containsString("\"root.date\"[shape=box];")); } } } \ No newline at end of file diff --git a/core/src/main/java/io/kestra/core/models/executions/Execution.java b/core/src/main/java/io/kestra/core/models/executions/Execution.java index 65ebc9cf436..1e30b1e4d1d 100644 --- a/core/src/main/java/io/kestra/core/models/executions/Execution.java +++ b/core/src/main/java/io/kestra/core/models/executions/Execution.java @@ -335,11 +335,15 @@ public TaskRun findTaskRunByTaskIdAndValue(String id, List values) * * @param resolvedTasks normal tasks * @param resolvedErrors errors tasks + * @param resolvedErrors afters tasks * @return the flow we need to follow */ - public List findTaskDependingFlowState(List resolvedTasks, - List resolvedErrors) { - return this.findTaskDependingFlowState(resolvedTasks, resolvedErrors, null); + public List findTaskDependingFlowState( + List resolvedTasks, + List resolvedErrors, + List resolvedAfters + ) { + return this.findTaskDependingFlowState(resolvedTasks, resolvedErrors, resolvedAfters, null); } /** @@ -349,15 +353,28 @@ public List findTaskDependingFlowState(List resolved * * @param resolvedTasks normal tasks * @param resolvedErrors errors tasks + * @param resolvedFinally afters tasks * @param parentTaskRun the parent task * @return the flow we need to follow */ - public List findTaskDependingFlowState(List resolvedTasks, - @Nullable List resolvedErrors, TaskRun parentTaskRun) { + public List findTaskDependingFlowState( + List resolvedTasks, + @Nullable List resolvedErrors, + @Nullable List resolvedFinally, + TaskRun parentTaskRun + ) { resolvedTasks = removeDisabled(resolvedTasks); resolvedErrors = removeDisabled(resolvedErrors); + resolvedFinally = removeDisabled(resolvedFinally); + List errorsFlow = this.findTaskRunByTasks(resolvedErrors, parentTaskRun); + List finallyFlow = this.findTaskRunByTasks(resolvedFinally, parentTaskRun); + + // finally is already started, just continue theses finally + if (!finallyFlow.isEmpty()) { + return resolvedFinally == null ? Collections.emptyList() : resolvedFinally; + } // Check if flow has failed task if (!errorsFlow.isEmpty() || this.hasFailed(resolvedTasks, parentTaskRun)) { @@ -366,8 +383,15 @@ public List findTaskDependingFlowState(List resolved return Collections.emptyList(); } - return resolvedErrors == null ? Collections.emptyList() : resolvedErrors; + if (resolvedFinally != null && resolvedErrors != null && !this.isTerminated(resolvedErrors, parentTaskRun)) { + return resolvedErrors; + } else if (resolvedFinally == null) { + return resolvedErrors == null ? Collections.emptyList() : resolvedErrors; + } + } + if (this.isTerminated(resolvedTasks, parentTaskRun) && resolvedFinally != null) { + return resolvedFinally; } return resolvedTasks; @@ -390,8 +414,7 @@ private List removeDisabled(List tasks) { .toList(); } - public List findTaskRunByTasks(List resolvedTasks, - TaskRun parentTaskRun) { + public List findTaskRunByTasks(List resolvedTasks, TaskRun parentTaskRun) { if (resolvedTasks == null || this.taskRunList == null) { return Collections.emptyList(); } diff --git a/core/src/main/java/io/kestra/core/models/flows/Flow.java b/core/src/main/java/io/kestra/core/models/flows/Flow.java index 5da97729f49..f16739a63e4 100644 --- a/core/src/main/java/io/kestra/core/models/flows/Flow.java +++ b/core/src/main/java/io/kestra/core/models/flows/Flow.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -32,10 +33,7 @@ import jakarta.validation.ConstraintViolationException; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import lombok.experimental.SuperBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,6 +79,15 @@ public boolean hasIgnoreMarker(final AnnotatedMember m) { @Valid List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Valid @Deprecated List listeners; @@ -188,6 +195,7 @@ public Stream allTasks() { return Stream.of( this.tasks != null ? this.tasks : new ArrayList(), this.errors != null ? this.errors : new ArrayList(), + this._finally != null ? this._finally : new ArrayList(), this.listenersTasks() ) .flatMap(Collection::stream); diff --git a/core/src/main/java/io/kestra/core/models/flows/FlowForExecution.java b/core/src/main/java/io/kestra/core/models/flows/FlowForExecution.java index 9458351b751..b144c56537a 100644 --- a/core/src/main/java/io/kestra/core/models/flows/FlowForExecution.java +++ b/core/src/main/java/io/kestra/core/models/flows/FlowForExecution.java @@ -24,6 +24,9 @@ public class FlowForExecution extends AbstractFlow { @Valid List errors; + @Valid + List _finally; + @Valid List triggers; @@ -36,6 +39,7 @@ public static FlowForExecution of(Flow flow) { .inputs(flow.getInputs()) .tasks(flow.getTasks().stream().map(TaskForExecution::of).toList()) .errors(ListUtils.emptyOnNull(flow.getErrors()).stream().map(TaskForExecution::of).toList()) + ._finally(ListUtils.emptyOnNull(flow.getFinally()).stream().map(TaskForExecution::of).toList()) .triggers(ListUtils.emptyOnNull(flow.getTriggers()).stream().map(AbstractTriggerForExecution::of).toList()) .disabled(flow.isDisabled()) .deleted(flow.isDeleted()) diff --git a/core/src/main/java/io/kestra/core/models/flows/FlowWithSource.java b/core/src/main/java/io/kestra/core/models/flows/FlowWithSource.java index a9f3ff5ad87..10546225495 100644 --- a/core/src/main/java/io/kestra/core/models/flows/FlowWithSource.java +++ b/core/src/main/java/io/kestra/core/models/flows/FlowWithSource.java @@ -29,6 +29,7 @@ public Flow toFlow() { .variables(this.variables) .tasks(this.tasks) .errors(this.errors) + ._finally(this._finally) .listeners(this.listeners) .triggers(this.triggers) .pluginDefaults(this.pluginDefaults) @@ -70,6 +71,7 @@ public static FlowWithSource of(Flow flow, String source) { .variables(flow.variables) .tasks(flow.tasks) .errors(flow.errors) + ._finally(flow._finally) .listeners(flow.listeners) .triggers(flow.triggers) .pluginDefaults(flow.pluginDefaults) diff --git a/core/src/main/java/io/kestra/core/models/hierarchies/AbstractGraph.java b/core/src/main/java/io/kestra/core/models/hierarchies/AbstractGraph.java index 11eedd649d1..7e07337bcd5 100644 --- a/core/src/main/java/io/kestra/core/models/hierarchies/AbstractGraph.java +++ b/core/src/main/java/io/kestra/core/models/hierarchies/AbstractGraph.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeInfo; import io.micronaut.core.annotation.Introspected; -import lombok.Builder; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -19,7 +18,7 @@ public abstract class AbstractGraph { @JsonInclude protected String type; @Setter - protected boolean error; + protected BranchType branchType; public AbstractGraph() { this.type = this.getClass().getName(); @@ -39,8 +38,8 @@ public void updateUidWithChildren(String uid) { this.uid = uid; } - public void updateErrorWithChildren(boolean error) { - this.error = error; + public void updateWithChildren(BranchType branchType) { + this.branchType = branchType; } public AbstractGraph forExecution() { @@ -53,4 +52,9 @@ public boolean equals(Object o) { if (!(o instanceof AbstractGraph)) return false; return o.hashCode() == this.hashCode(); } + + public enum BranchType { + ERROR, + FINALLY + } } diff --git a/core/src/main/java/io/kestra/core/models/hierarchies/Graph.java b/core/src/main/java/io/kestra/core/models/hierarchies/Graph.java index ec290bb42e6..a15c12f028b 100644 --- a/core/src/main/java/io/kestra/core/models/hierarchies/Graph.java +++ b/core/src/main/java/io/kestra/core/models/hierarchies/Graph.java @@ -33,6 +33,12 @@ public Graph addEdge(T previous, T next, V value) { return this; } + public Graph removeEdge(T previous, T next) { + this.graph.removeEdge(previous, next); + + return this; + } + public Set nodes() { return this.graph.nodes(); } diff --git a/core/src/main/java/io/kestra/core/models/hierarchies/GraphCluster.java b/core/src/main/java/io/kestra/core/models/hierarchies/GraphCluster.java index 1eb3ae68354..665341f065e 100644 --- a/core/src/main/java/io/kestra/core/models/hierarchies/GraphCluster.java +++ b/core/src/main/java/io/kestra/core/models/hierarchies/GraphCluster.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import io.kestra.core.models.executions.TaskRun; import io.kestra.core.models.tasks.Task; -import lombok.Builder; +import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; @@ -25,6 +25,14 @@ public class GraphCluster extends AbstractGraph { @JsonIgnore private final GraphClusterRoot root; + @JsonIgnore + @Getter(AccessLevel.NONE) + private final GraphClusterFinally _finally; + + public GraphClusterFinally getFinally() { + return _finally; + } + @JsonIgnore private final GraphClusterEnd end; @@ -41,17 +49,22 @@ public GraphCluster(String uid) { this.relationType = null; this.root = new GraphClusterRoot(); + this._finally = new GraphClusterFinally(); this.end = new GraphClusterEnd(); this.taskNode = null; this.addNode(this.root); + this.addNode(this._finally); this.addNode(this.end); + + this.addEdge(this.getFinally(), this.getEnd(), new Relation()); } public GraphCluster(Task task, TaskRun taskRun, List values, RelationType relationType) { this(new GraphTask(task.getId(), task, taskRun, values, relationType), task.getId(), relationType); this.addNode(this.taskNode, false); + this.addEdge(this.getRoot(), this.taskNode, new Relation()); } @@ -60,11 +73,15 @@ protected GraphCluster(AbstractGraphTask taskNode, String uid, RelationType rela this.relationType = relationType; this.root = new GraphClusterRoot(); + this._finally = new GraphClusterFinally(); this.end = new GraphClusterEnd(); this.taskNode = taskNode; this.addNode(this.root); + this.addNode(this._finally); this.addNode(this.end); + + this.addEdge(this.getFinally(), this.getEnd(), new Relation()); } public void addNode(AbstractGraph node) { @@ -122,12 +139,12 @@ public void updateUidWithChildren(String uid) { } @Override - public void updateErrorWithChildren(boolean error) { - this.error = error; + public void updateWithChildren(BranchType branchType) { + this.branchType = branchType; - this.taskNode.error = error; - this.root.error = error; - this.end.error = error; + this.taskNode.branchType = branchType; + this.root.branchType = branchType; + this.end.branchType = branchType; } @Override diff --git a/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterEnd.java b/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterEnd.java index 33a509c903c..52651c02e52 100644 --- a/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterEnd.java +++ b/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterEnd.java @@ -7,6 +7,6 @@ @Getter public class GraphClusterEnd extends AbstractGraph { public GraphClusterEnd() { - super(IdUtils.create()); + super("end-" + IdUtils.create()); } } diff --git a/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterFinally.java b/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterFinally.java new file mode 100644 index 00000000000..a718d15241c --- /dev/null +++ b/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterFinally.java @@ -0,0 +1,11 @@ +package io.kestra.core.models.hierarchies; + +import io.kestra.core.utils.IdUtils; +import lombok.Getter; + +@Getter +public class GraphClusterFinally extends AbstractGraph { + public GraphClusterFinally() { + super("finally-" + IdUtils.create()); + } +} diff --git a/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterRoot.java b/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterRoot.java index 7ab081baf3d..605c6ca7fda 100644 --- a/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterRoot.java +++ b/core/src/main/java/io/kestra/core/models/hierarchies/GraphClusterRoot.java @@ -6,6 +6,6 @@ @Getter public class GraphClusterRoot extends AbstractGraph { public GraphClusterRoot() { - super(IdUtils.create()); + super("root-" + IdUtils.create()); } } diff --git a/core/src/main/java/io/kestra/core/models/hierarchies/RelationType.java b/core/src/main/java/io/kestra/core/models/hierarchies/RelationType.java index 5a2a3523607..086ead91552 100644 --- a/core/src/main/java/io/kestra/core/models/hierarchies/RelationType.java +++ b/core/src/main/java/io/kestra/core/models/hierarchies/RelationType.java @@ -4,6 +4,7 @@ public enum RelationType { SEQUENTIAL, CHOICE, ERROR, + FINALLY, PARALLEL, DYNAMIC } diff --git a/core/src/main/java/io/kestra/core/models/tasks/FlowableTask.java b/core/src/main/java/io/kestra/core/models/tasks/FlowableTask.java index 47659b534b3..4231d0e1689 100644 --- a/core/src/main/java/io/kestra/core/models/tasks/FlowableTask.java +++ b/core/src/main/java/io/kestra/core/models/tasks/FlowableTask.java @@ -24,6 +24,12 @@ public interface FlowableTask { @PluginProperty List getErrors(); + @Schema( + title = "List of tasks to run after any tasks failed or success on this FlowableTask." + ) + @PluginProperty + List getFinally(); + /** * Create the topology representation of a flowable task. *

@@ -71,6 +77,7 @@ default Optional resolveState(RunContext runContext, Execution execu execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun, runContext, isAllowFailure(), diff --git a/core/src/main/java/io/kestra/core/models/templates/Template.java b/core/src/main/java/io/kestra/core/models/templates/Template.java index 49bf7750a69..dade9aa39dc 100644 --- a/core/src/main/java/io/kestra/core/models/templates/Template.java +++ b/core/src/main/java/io/kestra/core/models/templates/Template.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.AnnotatedMember; @@ -67,6 +68,15 @@ public boolean hasIgnoreMarker(final AnnotatedMember m) { @Valid private List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @NotNull @Builder.Default private final boolean deleted = false; @@ -138,6 +148,7 @@ public Template toDeleted() { this.description, this.tasks, this.errors, + this._finally, true ); } diff --git a/core/src/main/java/io/kestra/core/runners/ExecutorService.java b/core/src/main/java/io/kestra/core/runners/ExecutorService.java index 23896b52ba2..9c5da521b2a 100644 --- a/core/src/main/java/io/kestra/core/runners/ExecutorService.java +++ b/core/src/main/java/io/kestra/core/runners/ExecutorService.java @@ -270,7 +270,8 @@ private Optional childWorkerTaskResult(Flow flow, Execution ex // Then wait for completion (KILLED or whatever) on child tasks to KILLED the parent one. List currentTasks = execution.findTaskDependingFlowState( flowableParent.childTasks(runContext, parentTaskRun), - FlowableUtils.resolveTasks(flowableParent.getErrors(), parentTaskRun) + FlowableUtils.resolveTasks(flowableParent.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(flowableParent.getFinally(), parentTaskRun) ); List taskRunByTasks = execution.findTaskRunByTasks(currentTasks, parentTaskRun); @@ -426,7 +427,8 @@ private Executor handleNext(Executor executor) { .resolveSequentialNexts( executor.getExecution(), ResolvedTask.of(executor.getFlow().getTasks()), - ResolvedTask.of(executor.getFlow().getErrors()) + ResolvedTask.of(executor.getFlow().getErrors()), + ResolvedTask.of(executor.getFlow().getFinally()) ); if (nextTaskRuns.isEmpty()) { @@ -686,7 +688,8 @@ private Executor handleEnd(Executor executor) { List currentTasks = executor.getExecution().findTaskDependingFlowState( ResolvedTask.of(executor.getFlow().getTasks()), - ResolvedTask.of(executor.getFlow().getErrors()) + ResolvedTask.of(executor.getFlow().getErrors()), + ResolvedTask.of(executor.getFlow().getFinally()) ); if (!executor.getExecution().isTerminated(currentTasks)) { diff --git a/core/src/main/java/io/kestra/core/runners/FlowableUtils.java b/core/src/main/java/io/kestra/core/runners/FlowableUtils.java index 9191b4b74f1..782ba4b66c9 100644 --- a/core/src/main/java/io/kestra/core/runners/FlowableUtils.java +++ b/core/src/main/java/io/kestra/core/runners/FlowableUtils.java @@ -19,6 +19,7 @@ import java.util.stream.Stream; public class FlowableUtils { + @Deprecated(forRemoval = true) public static List resolveSequentialNexts( Execution execution, List tasks @@ -31,18 +32,20 @@ public static List resolveSequentialNexts( public static List resolveSequentialNexts( Execution execution, List tasks, - List errors + List errors, + List _finally ) { - return resolveSequentialNexts(execution, tasks, errors, null); + return resolveSequentialNexts(execution, tasks, errors, _finally, null); } public static List resolveSequentialNexts( Execution execution, List tasks, List errors, + List _finally, TaskRun parentTaskRun ) { - List currentTasks = execution.findTaskDependingFlowState(tasks, errors, parentTaskRun); + List currentTasks = execution.findTaskDependingFlowState(tasks, errors, _finally, parentTaskRun); return FlowableUtils.innerResolveSequentialNexts(execution, currentTasks, parentTaskRun); } @@ -92,9 +95,10 @@ public static List resolveWaitForNext( Execution execution, List tasks, List errors, + List _finally, TaskRun parentTaskRun ) { - List currentTasks = execution.findTaskDependingFlowState(tasks, errors, parentTaskRun); + List currentTasks = execution.findTaskDependingFlowState(tasks, errors, _finally, parentTaskRun); // nothing if (currentTasks == null || currentTasks.isEmpty() || execution.getState().getCurrent() == State.Type.KILLING) { @@ -140,12 +144,13 @@ public static Optional resolveState( Execution execution, List tasks, List errors, + List _finally, TaskRun parentTaskRun, RunContext runContext, boolean allowFailure, boolean allowWarning ) { - List currentTasks = execution.findTaskDependingFlowState(tasks, errors, parentTaskRun); + List currentTasks = execution.findTaskDependingFlowState(tasks, errors, _finally, parentTaskRun); if (currentTasks == null) { runContext.logger().warn( @@ -197,12 +202,15 @@ public static List resolveParallelNexts( Execution execution, List tasks, List errors, + List _finally, TaskRun parentTaskRun, Integer concurrency ) { return resolveParallelNexts( execution, - tasks, errors, + tasks, + errors, + _finally, parentTaskRun, concurrency, (nextTaskRunStream, taskRuns) -> nextTaskRunStream @@ -217,6 +225,7 @@ public static List resolveConcurrentNexts( Execution execution, List tasks, List errors, + List _finally, TaskRun parentTaskRun, Integer concurrency ) { @@ -227,6 +236,7 @@ public static List resolveConcurrentNexts( List allTasks = execution.findTaskDependingFlowState( tasks, errors, + _finally, parentTaskRun ); @@ -249,7 +259,7 @@ public static List resolveConcurrentNexts( if (taskRuns.isEmpty()) { Map> collect = allTasks .stream() - .collect(Collectors.groupingBy(resolvedTask -> resolvedTask.getValue(), () -> new LinkedHashMap<>(), Collectors.toList())); + .collect(Collectors.groupingBy(ResolvedTask::getValue, LinkedHashMap::new, Collectors.toList())); return collect.values().stream() .limit(concurrencySlots) .map(resolvedTasks -> resolvedTasks.getFirst().toNextTaskRun(execution)) @@ -260,7 +270,8 @@ public static List resolveConcurrentNexts( // start as many tasks as we have concurrency slots Map> collect = allTasks .stream() - .collect(Collectors.groupingBy(resolvedTask -> resolvedTask.getValue(), () -> new LinkedHashMap<>(), Collectors.toList())); + .collect(Collectors.groupingBy(ResolvedTask::getValue, LinkedHashMap::new, Collectors.toList())); + return collect.values().stream() .map(resolvedTasks -> filterCreated(resolvedTasks, taskRuns, parentTaskRun)) .filter(resolvedTasks -> !resolvedTasks.isEmpty()) @@ -281,6 +292,7 @@ public static List resolveDagNexts( Execution execution, List tasks, List errors, + List _finally, TaskRun parentTaskRun, Integer concurrency, List taskDependencies @@ -289,6 +301,7 @@ public static List resolveDagNexts( execution, tasks, errors, + _finally, parentTaskRun, concurrency, (nextTaskRunStream, taskRuns) -> nextTaskRunStream @@ -321,6 +334,7 @@ public static List resolveParallelNexts( Execution execution, List tasks, List errors, + List _finally, TaskRun parentTaskRun, Integer concurrency, BiFunction, List, Stream> nextTaskRunFunction @@ -332,6 +346,7 @@ public static List resolveParallelNexts( List currentTasks = execution.findTaskDependingFlowState( tasks, errors, + _finally, parentTaskRun ); diff --git a/core/src/main/java/io/kestra/core/services/Graph2DotService.java b/core/src/main/java/io/kestra/core/services/Graph2DotService.java index 4af87bb337d..8c2fbbe35e5 100644 --- a/core/src/main/java/io/kestra/core/services/Graph2DotService.java +++ b/core/src/main/java/io/kestra/core/services/Graph2DotService.java @@ -64,10 +64,15 @@ private static String node(AbstractGraph node) { } private static String label(AbstractGraph node) { - String shape = node instanceof GraphClusterEnd ? "point" : "box"; - String label = node instanceof GraphClusterEnd ? "end" : node.getLabel(); + String shape; - return "[shape=" + shape + ",label=\"" + label + "\"]"; + if (node instanceof GraphClusterRoot || node instanceof GraphClusterFinally || node instanceof GraphClusterEnd) { + shape = "point"; + } else { + shape = "box"; + } + + return "[shape=" + shape + "]"; } private static String nodeName(AbstractGraph node) { diff --git a/core/src/main/java/io/kestra/core/services/GraphService.java b/core/src/main/java/io/kestra/core/services/GraphService.java index 5bd12d650a6..1cbec83fa12 100644 --- a/core/src/main/java/io/kestra/core/services/GraphService.java +++ b/core/src/main/java/io/kestra/core/services/GraphService.java @@ -153,8 +153,8 @@ public void replace() { }); parentCluster.getGraph().removeNode(taskToReplace); - if (taskToReplace.isError()) { - clusterForReplacement.updateErrorWithChildren(true); + if (taskToReplace.getBranchType() != null) { + clusterForReplacement.updateWithChildren(taskToReplace.getBranchType()); } } } diff --git a/core/src/main/java/io/kestra/core/utils/GraphUtils.java b/core/src/main/java/io/kestra/core/utils/GraphUtils.java index 7f595f7d624..526a74ee19e 100644 --- a/core/src/main/java/io/kestra/core/utils/GraphUtils.java +++ b/core/src/main/java/io/kestra/core/utils/GraphUtils.java @@ -42,6 +42,7 @@ public static GraphCluster of(GraphCluster graph, Flow flow, Execution execution graph, flow.getTasks(), flow.getErrors(), + flow.getFinally(), null, execution ); @@ -78,6 +79,8 @@ public static GraphCluster triggers(GraphCluster graph, List tr triggerCluster.addEdge(triggerNode, triggerCluster.getEnd(), new Relation()); }); + removeFinally(triggerCluster); + return triggerCluster; } @@ -165,26 +168,29 @@ public static void sequential( GraphCluster graph, List tasks, List errors, + List _finally, TaskRun parent, Execution execution ) throws IllegalVariableEvaluationException { - iterate(graph, tasks, errors, parent, execution, RelationType.SEQUENTIAL); + iterate(graph, tasks, errors, _finally, parent, execution, RelationType.SEQUENTIAL); } public static void parallel( GraphCluster graph, List tasks, List errors, + List _finally, TaskRun parent, Execution execution ) throws IllegalVariableEvaluationException { - iterate(graph, tasks, errors, parent, execution, RelationType.PARALLEL); + iterate(graph, tasks, errors, _finally, parent, execution, RelationType.PARALLEL); } public static void switchCase( GraphCluster graph, Map> tasks, List errors, + List _finally, TaskRun parent, Execution execution ) throws IllegalVariableEvaluationException { @@ -192,16 +198,14 @@ public static void switchCase( fillGraph(graph, entry.getValue(), RelationType.SEQUENTIAL, parent, execution, entry.getKey()); } - // error cases - if (errors != null && errors.size() > 0) { - fillGraph(graph, errors, RelationType.ERROR, parent, execution, null); - } + fillAlternativePaths(graph, errors, _finally, parent, execution, null); } public static void ifElse( GraphCluster graph, List then, List _else, + List _finally, List errors, TaskRun parent, Execution execution @@ -211,44 +215,74 @@ public static void ifElse( fillGraph(graph, _else, RelationType.SEQUENTIAL, parent, execution, "else"); } - // error cases - if (errors != null && errors.size() > 0) { - fillGraph(graph, errors, RelationType.ERROR, parent, execution, null); - } + fillAlternativePaths(graph, errors, _finally, parent, execution, null); } public static void dag( GraphCluster graph, List tasks, List errors, + List _finally, TaskRun parent, Execution execution ) throws IllegalVariableEvaluationException { fillGraphDag(graph, tasks, parent, execution); - // error cases - if (errors != null && errors.size() > 0) { - fillGraph(graph, errors, RelationType.ERROR, parent, execution, null); - } + fillAlternativePaths(graph, errors, _finally, parent, execution, null); } private static void iterate( GraphCluster graph, List tasks, List errors, + List _finally, TaskRun parent, Execution execution, RelationType relationType ) throws IllegalVariableEvaluationException { - // standard cases fillGraph(graph, tasks, relationType, parent, execution, null); + fillAlternativePaths(graph, errors, _finally, parent, execution, null); + } + + private static void fillAlternativePaths( + GraphCluster graph, + List errors, + List _finally, + TaskRun parent, + Execution execution, + String value + ) throws IllegalVariableEvaluationException { // error cases - if (errors != null && errors.size() > 0) { - fillGraph(graph, errors, RelationType.ERROR, parent, execution, null); + if (errors != null && !errors.isEmpty()) { + fillGraph(graph, errors, RelationType.ERROR, parent, execution, value); + } + + // finally cases + if (_finally != null && !_finally.isEmpty()) { + fillGraph(graph, _finally, RelationType.FINALLY, parent, execution, value); + } else { + removeFinally(graph); } } + private static void removeFinally(GraphCluster graph) { + // we don't have finally case, so we remove the node, and link all previous link to finally to the end + graph.getGraph().edges() + .forEach(edge -> { + if (edge.getSource() instanceof GraphClusterFinally && edge.getTarget() instanceof GraphClusterEnd) { + graph.getGraph().edges().remove(edge); + } + + if (edge.getTarget() instanceof GraphClusterFinally) { + graph.getGraph().edges().remove(edge); + graph.addEdge(edge.getSource(), graph.getEnd(), edge.getValue()); + } + }); + + graph.getGraph().removeNode(graph.getFinally()); + } + private static void fillGraph( GraphCluster graph, List tasks, @@ -261,6 +295,11 @@ private static void fillGraph( AbstractGraph previous; previous = Optional.ofNullable(graph.getTaskNode()).orElse(graph.getRoot()); + if (relationType == RelationType.FINALLY) { + previous = graph.getFinally(); + + graph.getGraph().removeEdge(graph.getFinally(), graph.getEnd()); + } boolean isFirst = true; while (iterator.hasNext()) { @@ -286,7 +325,6 @@ private static void fillGraph( parentValues = execution.findParentsValues(currentTaskRun, true); } - // detect kids if (currentTask instanceof FlowableTask flowableTask) { currentGraph = flowableTask.tasksTree(execution, currentTaskRun, parentValues); @@ -299,8 +337,11 @@ private static void fillGraph( // add the node graph.addNode(currentGraph); + if (relationType == RelationType.ERROR || relationType == RelationType.FINALLY) { + currentGraph.updateWithChildren(AbstractGraph.BranchType.valueOf(relationType.name())); + } + if (relationType == RelationType.ERROR) { - currentGraph.updateErrorWithChildren(true); if (isFirst) { previous = graph.getRoot(); } @@ -324,13 +365,13 @@ private static void fillGraph( if (currentGraph instanceof GraphCluster && ((GraphCluster) currentGraph).getEnd() != null) { graph.addEdge( ((GraphCluster) currentGraph).getEnd(), - graph.getEnd(), + relationType == RelationType.FINALLY ? graph.getEnd() : graph.getFinally(), new Relation() ); } else { graph.addEdge( currentGraph, - graph.getEnd(), + relationType == RelationType.FINALLY ? graph.getEnd() : graph.getFinally(), new Relation() ); } @@ -341,7 +382,7 @@ private static void fillGraph( if (!iterator.hasNext() && !isAllLinkToEnd(relationType)) { graph.addEdge( currentGraph instanceof GraphCluster ? ((GraphCluster) currentGraph).getEnd() : currentGraph, - graph.getEnd(), + relationType == RelationType.FINALLY ? graph.getEnd() : graph.getFinally(), new Relation() ); } @@ -361,7 +402,14 @@ private static void fillGraphDag( ) throws IllegalVariableEvaluationException { List nodeTaskCreated = new ArrayList<>(); List nodeCreatedIds = new ArrayList<>(); - Set dependencies = tasks.stream().filter(taskDepend -> taskDepend.getDependsOn() != null).map(Dag.DagTask::getDependsOn).flatMap(Collection::stream).collect(Collectors.toSet()); + + Set dependencies = tasks + .stream() + .map(Dag.DagTask::getDependsOn) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + AbstractGraph previous; previous = Optional.ofNullable(graph.getTaskNode()).orElse(graph.getRoot()); @@ -463,7 +511,7 @@ private static boolean isAllLinkToEnd(RelationType relationType) { private static List findTaskRuns(Task task, Execution execution, TaskRun parent) { List taskRuns = execution != null ? execution.findTaskRunsByTaskId(task.getId()) : new ArrayList<>(); - if (taskRuns.size() == 0) { + if (taskRuns.isEmpty()) { return Collections.singletonList(null); } diff --git a/core/src/main/java/io/kestra/plugin/core/flow/AllowFailure.java b/core/src/main/java/io/kestra/plugin/core/flow/AllowFailure.java index db7f0743f18..db465486a9b 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/AllowFailure.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/AllowFailure.java @@ -59,11 +59,13 @@ public class AllowFailure extends Sequential implements FlowableTask public Optional resolveState(RunContext runContext, Execution execution, TaskRun parentTaskRun) throws IllegalVariableEvaluationException { List resolvedTasks = this.childTasks(runContext, parentTaskRun); List resolvedErrors = FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun); + List resolvedFinally = FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun); Optional type = FlowableUtils.resolveState( execution, resolvedTasks, resolvedErrors, + resolvedFinally, parentTaskRun, runContext, this.isAllowFailure(), @@ -77,6 +79,7 @@ public Optional resolveState(RunContext runContext, Execution execut execution, resolvedTasks, null, + resolvedFinally, parentTaskRun, runContext, this.isAllowFailure(), diff --git a/core/src/main/java/io/kestra/plugin/core/flow/Dag.java b/core/src/main/java/io/kestra/plugin/core/flow/Dag.java index 9c91e2654d2..2b48d08a41a 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/Dag.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/Dag.java @@ -1,5 +1,6 @@ package io.kestra.plugin.core.flow; +import com.fasterxml.jackson.annotation.JsonProperty; import io.kestra.core.exceptions.IllegalVariableEvaluationException; import io.kestra.core.models.annotations.Example; import io.kestra.core.models.annotations.Plugin; @@ -23,7 +24,6 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.Stream; @@ -104,6 +104,15 @@ public class Dag extends Task implements FlowableTask { @PluginProperty protected List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Override public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List parentValues) throws IllegalVariableEvaluationException { GraphCluster subGraph = new GraphCluster(this, taskRun, parentValues, RelationType.DYNAMIC); @@ -114,6 +123,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.getTasks(), this.errors, + this._finally, taskRun, execution ); @@ -138,7 +148,10 @@ public List allChildTasks() { return Stream .concat( this.tasks != null ? this.tasks.stream().map(DagTask::getTask) : Stream.empty(), - this.errors != null ? this.errors.stream() : Stream.empty() + Stream.concat( + this.errors != null ? this.errors.stream() : Stream.empty(), + this._finally != null ? this._finally.stream() : Stream.empty() + ) ) .toList(); } @@ -156,6 +169,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun, this.concurrent, this.tasks diff --git a/core/src/main/java/io/kestra/plugin/core/flow/EachParallel.java b/core/src/main/java/io/kestra/plugin/core/flow/EachParallel.java index 281862b9568..958340d810a 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/EachParallel.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/EachParallel.java @@ -148,6 +148,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.getTasks(), this.errors, + this._finally, taskRun, execution ); @@ -174,6 +175,7 @@ public Optional resolveState(RunContext runContext, Execution execut execution, childTasks, FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun, runContext, this.isAllowFailure(), @@ -187,6 +189,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, FlowableUtils.resolveEachTasks(runContext, parentTaskRun, this.getTasks(), this.value), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun, this.concurrent ); diff --git a/core/src/main/java/io/kestra/plugin/core/flow/EachSequential.java b/core/src/main/java/io/kestra/plugin/core/flow/EachSequential.java index ef261f0a92d..a52e7b8026e 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/EachSequential.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/EachSequential.java @@ -112,6 +112,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.getTasks(), this.errors, + this._finally, taskRun, execution ); @@ -137,6 +138,7 @@ public Optional resolveState(RunContext runContext, Execution execut execution, childTasks, FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun, runContext, this.isAllowFailure(), @@ -150,6 +152,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, FlowableUtils.resolveEachTasks(runContext, parentTaskRun, this.getTasks(), this.value), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun ); } diff --git a/core/src/main/java/io/kestra/plugin/core/flow/ForEach.java b/core/src/main/java/io/kestra/plugin/core/flow/ForEach.java index f36921ac90a..b7b97e7369f 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/ForEach.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/ForEach.java @@ -174,6 +174,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.getTasks(), this.getErrors(), + this.getFinally(), taskRun, execution ); @@ -182,6 +183,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.getTasks(), this.getErrors(), + this.getFinally(), taskRun, execution ); @@ -209,6 +211,7 @@ public Optional resolveState(RunContext runContext, Execution execut execution, childTasks, FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun, runContext, this.isAllowFailure(), @@ -223,6 +226,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun ); } @@ -231,6 +235,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, FlowableUtils.resolveEachTasks(runContext, parentTaskRun, this.getTasks(), this.values), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun, this.concurrencyLimit ); diff --git a/core/src/main/java/io/kestra/plugin/core/flow/ForEachItem.java b/core/src/main/java/io/kestra/plugin/core/flow/ForEachItem.java index 3e5fd76f621..dc680ac5905 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/ForEachItem.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/ForEachItem.java @@ -1,5 +1,6 @@ package io.kestra.plugin.core.flow; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.kestra.core.exceptions.IllegalVariableEvaluationException; @@ -33,11 +34,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import lombok.experimental.SuperBuilder; import java.io.*; @@ -317,6 +314,15 @@ public class ForEachItem extends Task implements FlowableTask, Child @Valid private List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Schema( title = "What to do when a failed execution is restarting.", description = """ @@ -336,6 +342,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.getTasks(), this.errors, + this._finally, taskRun, execution ); @@ -348,7 +355,10 @@ public List allChildTasks() { return Stream .concat( this.getTasks() != null ? this.getTasks().stream() : Stream.empty(), - this.errors != null ? this.errors.stream() : Stream.empty() + Stream.concat( + this.errors != null ? this.errors.stream() : Stream.empty(), + this._finally != null ? this._finally.stream() : Stream.empty() + ) ) .toList(); } @@ -364,6 +374,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun ); } diff --git a/core/src/main/java/io/kestra/plugin/core/flow/If.java b/core/src/main/java/io/kestra/plugin/core/flow/If.java index 6155d0fbfbf..8083175b5e7 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/If.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/If.java @@ -99,6 +99,15 @@ public class If extends Task implements FlowableTask { ) private List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Override public List getErrors() { return errors; @@ -112,6 +121,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.then, this._else, + this._finally, this.errors, taskRun, execution @@ -127,7 +137,11 @@ public List allChildTasks() { this.then != null ? this.then.stream() : Stream.empty(), Stream.concat( this._else != null ? this._else.stream() : Stream.empty(), - this.errors != null ? this.errors.stream() : Stream.empty()) + Stream.concat( + this.errors != null ? this.errors.stream() : Stream.empty(), + this._finally != null ? this._finally.stream() : Stream.empty() + ) + ) ) .toList(); } @@ -157,6 +171,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun ); } @@ -175,6 +190,7 @@ public Optional resolveState(RunContext runContext, Execution execut execution, childTasks, FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun, runContext, this.isAllowFailure(), diff --git a/core/src/main/java/io/kestra/plugin/core/flow/Parallel.java b/core/src/main/java/io/kestra/plugin/core/flow/Parallel.java index f10df2d3168..cd003fca447 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/Parallel.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/Parallel.java @@ -1,5 +1,6 @@ package io.kestra.plugin.core.flow; +import com.fasterxml.jackson.annotation.JsonProperty; import io.kestra.core.models.annotations.PluginProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; @@ -21,7 +22,6 @@ import io.kestra.core.utils.GraphUtils; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; @@ -43,7 +43,7 @@ code = """ id: parallel namespace: company.team - + tasks: - id: parallel type: io.kestra.plugin.core.flow.Parallel @@ -55,7 +55,7 @@ - id: 2nd type: io.kestra.plugin.core.debug.Return format: "{{ task.id }} > {{ taskrun.id }}" - + - id: last type: io.kestra.plugin.core.debug.Return format: "{{ task.id }} > {{ taskrun.startDate }}" @@ -83,6 +83,15 @@ public class Parallel extends Task implements FlowableTask { @Valid protected List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Override public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List parentValues) throws IllegalVariableEvaluationException { GraphCluster subGraph = new GraphCluster(this, taskRun, parentValues, RelationType.PARALLEL); @@ -91,6 +100,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.tasks, this.errors, + this._finally, taskRun, execution ); @@ -103,7 +113,10 @@ public List allChildTasks() { return Stream .concat( this.tasks != null ? this.tasks.stream() : Stream.empty(), - this.errors != null ? this.errors.stream() : Stream.empty() + Stream.concat( + this.errors != null ? this.errors.stream() : Stream.empty(), + this._finally != null ? this._finally.stream() : Stream.empty() + ) ) .toList(); } @@ -119,6 +132,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun, this.concurrent ); diff --git a/core/src/main/java/io/kestra/plugin/core/flow/Pause.java b/core/src/main/java/io/kestra/plugin/core/flow/Pause.java index c39732c34b4..f83e4d1e97a 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/Pause.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/Pause.java @@ -1,5 +1,6 @@ package io.kestra.plugin.core.flow; +import com.fasterxml.jackson.annotation.JsonProperty; import io.kestra.core.exceptions.IllegalVariableEvaluationException; import io.kestra.core.models.annotations.Example; import io.kestra.core.models.annotations.Plugin; @@ -31,7 +32,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import java.util.stream.Stream; @SuperBuilder @@ -156,6 +156,15 @@ public class Pause extends Task implements FlowableTask { @Valid protected List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Valid @PluginProperty @Deprecated @@ -173,6 +182,7 @@ public AbstractGraph tasksTree(Execution execution, TaskRun taskRun, List allChildTasks() { return Stream .concat( this.getTasks() != null ? this.getTasks().stream() : Stream.empty(), - this.getErrors() != null ? this.getErrors().stream() : Stream.empty() + Stream.concat( + this.getErrors() != null ? this.getErrors().stream() : Stream.empty(), + this.getFinally() != null ? this.getFinally().stream() : Stream.empty() + ) ) .toList(); } @@ -205,6 +218,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun ); } diff --git a/core/src/main/java/io/kestra/plugin/core/flow/Sequential.java b/core/src/main/java/io/kestra/plugin/core/flow/Sequential.java index 76903fe88ca..dc6c0907ed6 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/Sequential.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/Sequential.java @@ -1,5 +1,6 @@ package io.kestra.plugin.core.flow; +import com.fasterxml.jackson.annotation.JsonProperty; import io.kestra.core.exceptions.IllegalVariableEvaluationException; import io.kestra.core.models.annotations.Example; import io.kestra.core.models.annotations.Plugin; @@ -10,24 +11,17 @@ import io.kestra.core.models.hierarchies.AbstractGraph; import io.kestra.core.models.hierarchies.GraphCluster; import io.kestra.core.models.hierarchies.RelationType; -import io.kestra.core.models.tasks.FlowableTask; -import io.kestra.core.models.tasks.ResolvedTask; -import io.kestra.core.models.tasks.Task; -import io.kestra.core.models.tasks.VoidOutput; +import io.kestra.core.models.tasks.*; import io.kestra.core.runners.FlowableUtils; import io.kestra.core.runners.RunContext; import io.kestra.core.utils.GraphUtils; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import lombok.experimental.SuperBuilder; import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; + import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; @SuperBuilder @@ -71,6 +65,15 @@ public class Sequential extends Task implements FlowableTask { @Valid protected List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Valid @PluginProperty // FIXME -> issue with Pause @NotEmpty @@ -84,6 +87,7 @@ public AbstractGraph tasksTree(Execution execution, TaskRun taskRun, List allChildTasks() { return Stream .concat( this.getTasks() != null ? this.getTasks().stream() : Stream.empty(), - this.getErrors() != null ? this.getErrors().stream() : Stream.empty() + Stream.concat( + this.getErrors() != null ? this.getErrors().stream() : Stream.empty(), + this.getFinally() != null ? this.getFinally().stream() : Stream.empty() + ) ) .toList(); } @@ -112,6 +118,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun ); } diff --git a/core/src/main/java/io/kestra/plugin/core/flow/Switch.java b/core/src/main/java/io/kestra/plugin/core/flow/Switch.java index b694a2282e5..cdce4cd3c9f 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/Switch.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/Switch.java @@ -1,5 +1,6 @@ package io.kestra.plugin.core.flow; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; import io.kestra.core.exceptions.IllegalVariableEvaluationException; import io.kestra.core.models.annotations.Example; @@ -54,12 +55,12 @@ code = """ id: switch namespace: company.team - + inputs: - id: string type: STRING required: true - + tasks: - id: switch type: io.kestra.plugin.core.flows.Switch @@ -113,6 +114,15 @@ public class Switch extends Task implements FlowableTask { @PluginProperty protected List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + private String rendererValue(RunContext runContext) throws IllegalVariableEvaluationException { return runContext.render(this.value); } @@ -124,7 +134,10 @@ public List allChildTasks() { this.defaults != null ? this.defaults.stream() : Stream.empty(), Stream.concat( this.cases != null ? this.cases.values().stream().flatMap(Collection::stream) : Stream.empty(), - this.errors != null ? this.errors.stream() : Stream.empty() + Stream.concat( + this.errors != null ? this.errors.stream() : Stream.empty(), + this._finally != null ? this._finally.stream() : Stream.empty() + ) ) ) .toList(); @@ -143,6 +156,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List ) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), this.errors, + this._finally, taskRun, execution ); @@ -168,6 +182,7 @@ public Optional resolveState(RunContext runContext, Execution execut execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun, runContext, this.isAllowFailure(), @@ -181,6 +196,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun ); } diff --git a/core/src/main/java/io/kestra/plugin/core/flow/Template.java b/core/src/main/java/io/kestra/plugin/core/flow/Template.java index af4680ff253..90974664c10 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/Template.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/Template.java @@ -1,5 +1,6 @@ package io.kestra.plugin.core.flow; +import com.fasterxml.jackson.annotation.JsonProperty; import io.kestra.core.exceptions.DeserializationException; import io.kestra.core.exceptions.IllegalVariableEvaluationException; import io.kestra.core.exceptions.InternalException; @@ -92,6 +93,15 @@ public class Template extends Task implements FlowableTask { @PluginProperty protected List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @NotNull @Schema( title = "The namespace of the template." @@ -137,6 +147,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, template.getTasks(), template.getErrors(), + template.getFinally(), taskRun, execution ); @@ -175,6 +186,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(template.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(template.getFinally(), parentTaskRun), parentTaskRun ); } diff --git a/core/src/main/java/io/kestra/plugin/core/flow/WaitFor.java b/core/src/main/java/io/kestra/plugin/core/flow/WaitFor.java index 1c0d6a1af0b..c1479350b21 100644 --- a/core/src/main/java/io/kestra/plugin/core/flow/WaitFor.java +++ b/core/src/main/java/io/kestra/plugin/core/flow/WaitFor.java @@ -1,5 +1,6 @@ package io.kestra.plugin.core.flow; +import com.fasterxml.jackson.annotation.JsonProperty; import io.kestra.core.exceptions.IllegalVariableEvaluationException; import io.kestra.core.models.annotations.Example; import io.kestra.core.models.annotations.Plugin; @@ -73,6 +74,15 @@ public class WaitFor extends Task implements FlowableTask { @Valid protected List errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Valid @PluginProperty @NotNull @@ -108,6 +118,7 @@ public AbstractGraph tasksTree(Execution execution, TaskRun taskRun, List allChildTasks() { return Stream .concat( tasks.stream(), - this.getErrors() != null ? this.getErrors().stream() : Stream.empty() + Stream.concat( + this.getErrors() != null ? this.getErrors().stream() : Stream.empty(), + this.getFinally() != null ? this.getFinally().stream() : Stream.empty() + ) ) .toList(); } @@ -137,6 +151,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun ); } @@ -193,6 +208,7 @@ public Optional resolveState(RunContext runContext, Execution execut execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.getErrors(), parentTaskRun), + FlowableUtils.resolveTasks(this.getFinally(), parentTaskRun), parentTaskRun, runContext, isAllowFailure(), diff --git a/core/src/test/java/io/kestra/core/models/flows/FlowWithSourceTest.java b/core/src/test/java/io/kestra/core/models/flows/FlowWithSourceTest.java index 1b0bdc17172..4cbcb0db6a5 100644 --- a/core/src/test/java/io/kestra/core/models/flows/FlowWithSourceTest.java +++ b/core/src/test/java/io/kestra/core/models/flows/FlowWithSourceTest.java @@ -101,6 +101,13 @@ void of() { .message("Error") .build() )) + ._finally(List.of( + Log.builder() + .id(IdUtils.create()) + .type(Log.class.getName()) + .message("Finally") + .build() + )) .listeners(List.of( Listener.builder() .conditions(List.of(Expression.builder().expression("true").build())) diff --git a/core/src/test/java/io/kestra/core/models/hierarchies/FlowGraphTest.java b/core/src/test/java/io/kestra/core/models/hierarchies/FlowGraphTest.java index 9099fa60f3a..1711d6186a3 100644 --- a/core/src/test/java/io/kestra/core/models/hierarchies/FlowGraphTest.java +++ b/core/src/test/java/io/kestra/core/models/hierarchies/FlowGraphTest.java @@ -295,6 +295,52 @@ void dynamicIdSubflow() throws IllegalVariableEvaluationException, TimeoutExcept assertThat(flowGraph.getClusters().size(), is(4)); } + @Test + void finallySequential() throws IllegalVariableEvaluationException, IOException { + FlowWithSource flow = this.parse("flows/valids/finally-sequential.yaml"); + FlowGraph flowGraph = GraphUtils.flowGraph(flow, null); + + assertThat(flowGraph.getNodes().size(), is(13)); + assertThat(flowGraph.getEdges().size(), is(13)); + assertThat(flowGraph.getClusters().size(), is(2)); + + assertThat(edge(flowGraph, ".*seq.finally.*", ".*seq.a1").getRelation().getRelationType(), is(RelationType.SEQUENTIAL)); + assertThat(edge(flowGraph, ".*seq.a1", ".*seq.a2").getRelation().getRelationType(), is(RelationType.FINALLY)); + assertThat(edge(flowGraph, ".*seq.a2", ".*seq.end.*").getRelation().getRelationType(), is(nullValue())); + } + + @Test + void finallySequentialError() throws IllegalVariableEvaluationException, IOException { + FlowWithSource flow = this.parse("flows/valids/finally-sequential-error.yaml"); + FlowGraph flowGraph = GraphUtils.flowGraph(flow, null); + + assertThat(flowGraph.getNodes().size(), is(15)); + assertThat(flowGraph.getEdges().size(), is(16)); + assertThat(flowGraph.getClusters().size(), is(2)); + + assertThat(edge(flowGraph, ".*seq.e1", ".*seq.e2").getRelation().getRelationType(), is(RelationType.ERROR)); + assertThat(edge(flowGraph, ".*seq.e2", ".*seq.finally.*").getRelation().getRelationType(), is(nullValue())); + assertThat(edge(flowGraph, ".*seq.finally.*", ".*seq.a1").getRelation().getRelationType(), is(RelationType.SEQUENTIAL)); + assertThat(edge(flowGraph, ".*seq.a1", ".*seq.a2").getRelation().getRelationType(), is(RelationType.FINALLY)); + assertThat(edge(flowGraph, ".*seq.a2", ".*seq.end.*").getRelation().getRelationType(), is(nullValue())); + } + + @Test + void finallyDag() throws IllegalVariableEvaluationException, IOException { + FlowWithSource flow = this.parse("flows/valids/finally-dag.yaml"); + FlowGraph flowGraph = GraphUtils.flowGraph(flow, null); + + assertThat(flowGraph.getNodes().size(), is(17)); + assertThat(flowGraph.getEdges().size(), is(18)); + assertThat(flowGraph.getClusters().size(), is(2)); + + assertThat(edge(flowGraph, ".*dag.e1", ".*dag.e2").getRelation().getRelationType(), is(RelationType.ERROR)); + assertThat(edge(flowGraph, ".*dag.e2", ".*dag.finally.*").getRelation().getRelationType(), is(nullValue())); + assertThat(edge(flowGraph, ".*dag.finally.*", ".*dag.a1").getRelation().getRelationType(), is(RelationType.DYNAMIC)); + assertThat(edge(flowGraph, ".*dag.a1", ".*dag.a2").getRelation().getRelationType(), is(RelationType.DYNAMIC)); + assertThat(edge(flowGraph, ".*dag.a2", ".*dag.end.*").getRelation().getRelationType(), is(nullValue())); + } + private FlowWithSource parse(String path) throws IOException { URL resource = TestsUtils.class.getClassLoader().getResource(path); assert resource != null; @@ -304,7 +350,7 @@ private FlowWithSource parse(String path) throws IOException { return yamlParser.parse(file, Flow.class).withSource(Files.readString(file.toPath())); } - private AbstractGraph node(FlowGraph flowGraph, String taskId) { + private static AbstractGraph node(FlowGraph flowGraph, String taskId) { return flowGraph .getNodes() .stream() @@ -314,7 +360,7 @@ private AbstractGraph node(FlowGraph flowGraph, String taskId) { .orElseThrow(); } - private AbstractGraph nodeByUid(FlowGraph flowGraph, String uid) { + private static AbstractGraph nodeByUid(FlowGraph flowGraph, String uid) { return flowGraph .getNodes() .stream() @@ -323,7 +369,7 @@ private AbstractGraph nodeByUid(FlowGraph flowGraph, String uid) { .orElseThrow(); } - private FlowGraph.Edge edge(FlowGraph flowGraph, String source) { + private static FlowGraph.Edge edge(FlowGraph flowGraph, String source) { return flowGraph .getEdges() .stream() @@ -332,7 +378,7 @@ private FlowGraph.Edge edge(FlowGraph flowGraph, String source) { .orElseThrow(); } - private FlowGraph.Edge edge(FlowGraph flowGraph, String source, String target) { + private static FlowGraph.Edge edge(FlowGraph flowGraph, String source, String target) { return flowGraph .getEdges() .stream() @@ -341,7 +387,7 @@ private FlowGraph.Edge edge(FlowGraph flowGraph, String source, String target) { .orElseThrow(); } - private List edges(FlowGraph flowGraph, String source) { + private static List edges(FlowGraph flowGraph, String source) { return flowGraph .getEdges() .stream() @@ -350,11 +396,11 @@ private List edges(FlowGraph flowGraph, String source) { .toList(); } - private FlowGraph.Cluster cluster(FlowGraph flowGraph, String clusterIdRegex) { + private static FlowGraph.Cluster cluster(FlowGraph flowGraph, String clusterIdRegex) { return cluster(flowGraph, clusterIdRegex, null); } - private FlowGraph.Cluster cluster(FlowGraph flowGraph, String clusterIdRegex, String value) { + private static FlowGraph.Cluster cluster(FlowGraph flowGraph, String clusterIdRegex, String value) { if(clusterIdRegex.equals("root")) { String[] startEnd = new String[2]; flowGraph.getNodes().forEach(n -> { diff --git a/core/src/test/java/io/kestra/core/runners/PluginDefaultsCaseTest.java b/core/src/test/java/io/kestra/core/runners/PluginDefaultsCaseTest.java index bd31458737c..59c7b7e2977 100644 --- a/core/src/test/java/io/kestra/core/runners/PluginDefaultsCaseTest.java +++ b/core/src/test/java/io/kestra/core/runners/PluginDefaultsCaseTest.java @@ -1,5 +1,6 @@ package io.kestra.core.runners; +import com.fasterxml.jackson.annotation.JsonProperty; import io.kestra.core.exceptions.IllegalVariableEvaluationException; import io.kestra.core.models.executions.Execution; import io.kestra.core.models.executions.NextTaskRun; @@ -15,16 +16,12 @@ import jakarta.inject.Singleton; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import lombok.experimental.SuperBuilder; import java.time.Duration; import java.util.List; import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; import java.util.stream.Stream; import static org.hamcrest.MatcherAssert.assertThat; @@ -65,6 +62,15 @@ public static class DefaultSequential1 extends Task implements FlowableTask errors; + @Valid + @JsonProperty("finally") + @Getter(AccessLevel.NONE) + protected List _finally; + + public List getFinally() { + return this._finally; + } + @Valid @NotEmpty private List tasks; @@ -79,6 +85,7 @@ public GraphCluster tasksTree(Execution execution, TaskRun taskRun, List subGraph, this.tasks, this.errors, + this._finally, taskRun, execution ); @@ -91,7 +98,10 @@ public List allChildTasks() { return Stream .concat( this.tasks != null ? this.tasks.stream() : Stream.empty(), - this.errors != null ? this.errors.stream() : Stream.empty() + Stream.concat( + this.errors != null ? this.errors.stream() : Stream.empty(), + this._finally != null ? this._finally.stream() : Stream.empty() + ) ) .toList(); } @@ -107,6 +117,7 @@ public List resolveNexts(RunContext runContext, Execution execution execution, this.childTasks(runContext, parentTaskRun), FlowableUtils.resolveTasks(this.errors, parentTaskRun), + FlowableUtils.resolveTasks(this._finally, parentTaskRun), parentTaskRun ); } diff --git a/core/src/test/java/io/kestra/plugin/core/flow/FinallyTest.java b/core/src/test/java/io/kestra/plugin/core/flow/FinallyTest.java new file mode 100644 index 00000000000..2ca62a3d75a --- /dev/null +++ b/core/src/test/java/io/kestra/plugin/core/flow/FinallyTest.java @@ -0,0 +1,353 @@ +package io.kestra.plugin.core.flow; + +import io.kestra.core.junit.annotations.KestraTest; +import io.kestra.core.junit.annotations.LoadFlows; +import io.kestra.core.models.executions.Execution; +import io.kestra.core.models.flows.State; +import io.kestra.core.queues.QueueException; +import io.kestra.core.runners.FlowInputOutput; +import io.kestra.core.runners.RunnerUtils; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; + +@KestraTest(startRunner = true) +class FinallyTest { + @Inject + protected RunnerUtils runnerUtils; + + @Inject + private FlowInputOutput flowIO; + + @Test + @LoadFlows({"flows/valids/finally-sequential.yaml"}) + void sequentialWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-sequential", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(5)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-sequential.yaml"}) + void sequentialWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-sequential", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(5)); + assertThat(execution.getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-sequential-error.yaml"}) + void sequentialErrorBlockWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-sequential-error", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(5)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-sequential-error.yaml"}) + void sequentialErrorBlockWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-sequential-error", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(7)); + assertThat(execution.getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-allowfailure.yaml"}) + void allowFailureWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-allowfailure", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(5)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-allowfailure.yaml"}) + void allowFailureWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-allowfailure", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(7)); + assertThat(execution.getState().getCurrent(), is(State.Type.WARNING)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-parallel.yaml"}) + void parallelWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-parallel", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(8)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-parallel.yaml"}) + void parallelWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-parallel", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(10)); + assertThat(execution.getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + // @FIXME + @Disabled("ForEach is not working with errors neither finally") + @Test + @LoadFlows({"flows/valids/finally-foreach.yaml"}) + void forEachWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-foreach", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(9)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + // @FIXME + @Disabled("ForEach is not working with errors neither finally") + @Test + @LoadFlows({"flows/valids/finally-foreach.yaml"}) + void forEachWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-foreach", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(11)); + assertThat(execution.getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-eachparallel.yaml"}) + void eachParallelWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-eachparallel", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(9)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-eachparallel.yaml"}) + void eachParallelWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-eachparallel", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(11)); + assertThat(execution.getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-dag.yaml"}) + void dagWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-dag", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(7)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-dag.yaml"}) + void dagWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-dag", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(9)); + assertThat(execution.getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-flow.yaml"}) + void flowWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-flow", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(4)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-flow.yaml"}) + void flowWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-flow", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(4)); + assertThat(execution.getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-flow-error.yaml"}) + void flowErrorBlockWithoutErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-flow-error", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", false)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(4)); + assertThat(execution.getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("ok").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } + + @Test + @LoadFlows({"flows/valids/finally-flow-error.yaml"}) + void flowErrorBlockWithErrors() throws QueueException, TimeoutException { + Execution execution = runnerUtils.runOne( + null, + "io.kestra.tests", "finally-flow-error", null, + (flow, execution1) -> flowIO.readExecutionInputs(flow, execution1, Map.of("failed", true)), + Duration.ofSeconds(60) + ); + + assertThat(execution.getTaskRunList(), hasSize(6)); + assertThat(execution.getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("ko").getFirst().getState().getCurrent(), is(State.Type.FAILED)); + assertThat(execution.findTaskRunsByTaskId("a1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("a2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e1").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + assertThat(execution.findTaskRunsByTaskId("e2").getFirst().getState().getCurrent(), is(State.Type.SUCCESS)); + } +} \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-allowfailure.yaml b/core/src/test/resources/flows/valids/finally-allowfailure.yaml new file mode 100644 index 00000000000..9812dc21da6 --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-allowfailure.yaml @@ -0,0 +1,40 @@ +id: finally-allowfailure +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: seq + type: io.kestra.plugin.core.flow.AllowFailure + tasks: + - id: if + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + + errors: + - id: e1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: e2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-dag.yaml b/core/src/test/resources/flows/valids/finally-dag.yaml new file mode 100644 index 00000000000..94dad1d57ab --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-dag.yaml @@ -0,0 +1,53 @@ +id: finally-dag +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: dag + type: io.kestra.plugin.core.flow.Dag + tasks: + - task: + id: t1 + type: io.kestra.plugin.core.log.Log + message: "{{ task.id }}" + - task: + id: t2 + type: io.kestra.plugin.core.log.Log + message: "{{ task.id }}" + dependsOn: + - t1 + - task: + id: t3 + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + dependsOn: + - t2 + + errors: + - id: e1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: e2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-eachparallel.yaml b/core/src/test/resources/flows/valids/finally-eachparallel.yaml new file mode 100644 index 00000000000..72a13d45b99 --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-eachparallel.yaml @@ -0,0 +1,41 @@ +id: finally-eachparallel +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: each + type: io.kestra.plugin.core.flow.EachParallel + value: '["1", "2", "3"]' + tasks: + - id: if + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + + errors: + - id: e1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: e2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-flow-error.yaml b/core/src/test/resources/flows/valids/finally-flow-error.yaml new file mode 100644 index 00000000000..0c80a1db323 --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-flow-error.yaml @@ -0,0 +1,37 @@ +id: finally-flow-error +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: if + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + +errors: + - id: e1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: e2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + +finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-flow.yaml b/core/src/test/resources/flows/valids/finally-flow.yaml new file mode 100644 index 00000000000..0bb8848bda7 --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-flow.yaml @@ -0,0 +1,28 @@ +id: finally-flow +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: if + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + +finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-foreach.yaml b/core/src/test/resources/flows/valids/finally-foreach.yaml new file mode 100644 index 00000000000..6da024c31b1 --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-foreach.yaml @@ -0,0 +1,42 @@ +id: finally-foreach +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: each + type: io.kestra.plugin.core.flow.ForEach + values: '["1", "2", "3"]' + concurrencyLimit: 0 + tasks: + - id: if + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + + errors: + - id: e1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: e2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-parallel.yaml b/core/src/test/resources/flows/valids/finally-parallel.yaml new file mode 100644 index 00000000000..71b6e8ff45b --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-parallel.yaml @@ -0,0 +1,49 @@ +id: finally-parallel +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: seq + type: io.kestra.plugin.core.flow.Parallel + tasks: + - id: p1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + - id: p2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + - id: p3 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + - id: if + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + + errors: + - id: e1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: e2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-sequential-error.yaml b/core/src/test/resources/flows/valids/finally-sequential-error.yaml new file mode 100644 index 00000000000..6df76defa07 --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-sequential-error.yaml @@ -0,0 +1,40 @@ +id: finally-sequential-error +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: seq + type: io.kestra.plugin.core.flow.Sequential + tasks: + - id: if + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + + errors: + - id: e1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: e2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/core/src/test/resources/flows/valids/finally-sequential.yaml b/core/src/test/resources/flows/valids/finally-sequential.yaml new file mode 100644 index 00000000000..a79e50116f3 --- /dev/null +++ b/core/src/test/resources/flows/valids/finally-sequential.yaml @@ -0,0 +1,31 @@ +id: finally-sequential +namespace: io.kestra.tests + +inputs: + - id: failed + type: BOOLEAN + defaults: false + +tasks: + - id: seq + type: io.kestra.plugin.core.flow.Sequential + tasks: + - id: if + type: io.kestra.plugin.core.flow.If + condition: "{{ inputs.failed == false }}" + then: + - id: ok + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + else: + - id: ko + type: io.kestra.plugin.core.execution.Fail + + finally: + - id: a1 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" + + - id: a2 + type: io.kestra.plugin.core.debug.Return + format: "{{ task.id }}" \ No newline at end of file diff --git a/ui/package-lock.json b/ui/package-lock.json index fff30c808a3..b80f0bb9d3c 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@js-joda/core": "^5.6.3", - "@kestra-io/ui-libs": "^0.0.103", + "@kestra-io/ui-libs": "^0.0.107", "@vue-flow/background": "^1.3.2", "@vue-flow/controls": "^1.1.2", "@vue-flow/core": "^1.41.6", @@ -90,15 +90,11 @@ "lint-staged": "^15.2.11", "monaco-editor": "^0.52.2", "monaco-yaml": "^5.2.3", - "postcss-html": "^1.7.0", "prettier": "^3.4.2", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-visualizer": "^5.12.0", "sass": "^1.83.0", "storybook": "^8.4.7", - "stylelint": "^16.12.0", - "stylelint-config-recommended-scss": "^14.1.0", - "stylelint-config-recommended-vue": "^1.5.0", "typescript": "^5.7.2", "typescript-eslint": "^8.18.2", "vite": "^6.0.5", @@ -116,9 +112,6 @@ "@swc/core-linux-x64-gnu": "^1.10.4" } }, - "../ui-libs": { - "extraneous": true - }, "node_modules/@adobe/css-tools": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz", @@ -825,9 +818,10 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -961,73 +955,6 @@ "node": ">= 10.0.0" } }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/media-query-list-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", - "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" - } - }, "node_modules/@ctrl/tinycolor": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", @@ -1036,17 +963,6 @@ "node": ">=10" } }, - "node_modules/@dual-bundle/import-meta-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/@element-plus/icons-vue": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz", @@ -2520,9 +2436,9 @@ "integrity": "sha512-T1rRxzdqkEXcou0ZprN1q9yDRlvzCPLqmlNt5IIsGBzoEVgLCCYrKEwc84+TvsXuAc95VAZwtWD2zVsKPY4bcA==" }, "node_modules/@kestra-io/ui-libs": { - "version": "0.0.103", - "resolved": "https://registry.npmjs.org/@kestra-io/ui-libs/-/ui-libs-0.0.103.tgz", - "integrity": "sha512-TOpbDiQ5LwfFtoyydfCMwQagM/KfYZi4fKEfkEQp8lLGVjX6GTz8hQvMqTfe/Cocrb2zHct5KSpQc4VC9JRXxg==", + "version": "0.0.107", + "resolved": "https://registry.npmjs.org/@kestra-io/ui-libs/-/ui-libs-0.0.107.tgz", + "integrity": "sha512-mEj9E5kKfMhKmkfR9z6iQ4Quqvwsy6coON3Rdq+5930etXalgdzpQFXkJJiplli+Epkj6feIKb6L2LWWzVR4dg==", "dependencies": { "@nuxtjs/mdc": "^0.12.1", "@popperjs/core": "^2.11.8", @@ -2843,9 +2759,9 @@ } }, "node_modules/@nuxt/kit/node_modules/ignore": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.0.tgz", - "integrity": "sha512-lcX8PNQygAa22u/0BysEY8VhaFRzlOkvdlKczDPnJvrkJD1EuqzEky5VYYKM2iySIuaVIDv9N190DfSreSLw2A==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.1.tgz", + "integrity": "sha512-D1gVletsbVOoiXF963rgZnfobGAbq7Lb+dz3fcBmlOmZg6hHkpbycLqL8PLNB8f4GVv6dOVYwhPL/r7hwiH0Fw==", "license": "MIT", "engines": { "node": ">= 4" @@ -6330,16 +6246,6 @@ "node": ">=4" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/async-validator": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", @@ -7129,6 +7035,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", "dependencies": { "readdirp": "^4.0.1" }, @@ -7388,13 +7295,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "dev": true, - "license": "MIT" - }, "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", @@ -7529,33 +7429,6 @@ "layout-base": "^1.0.0" } }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -7608,30 +7481,6 @@ "node": "*" } }, - "node_modules/css-functions-list": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz", - "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12 || >=16" - } - }, - "node_modules/css-tree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", - "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.12.2", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -8912,16 +8761,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/environment": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", @@ -9700,23 +9539,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -10317,13 +10139,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globjoin": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", - "dev": true, - "license": "MIT" - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -12936,6 +12751,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -13188,16 +13004,6 @@ "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -13223,13 +13029,6 @@ "integrity": "sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==", "license": "MIT" }, - "node_modules/known-css-properties": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", - "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", - "dev": true, - "license": "MIT" - }, "node_modules/kolorist": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", @@ -13565,13 +13364,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true, - "license": "MIT" - }, "node_modules/log-update": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", @@ -13910,17 +13702,6 @@ "node": ">= 0.4" } }, - "node_modules/mathml-tag-names": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", - "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -14152,13 +13933,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdn-data": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", - "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -14179,19 +13953,6 @@ "map-or-similar": "^1.5.0" } }, - "node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -14544,6 +14305,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" @@ -14733,6 +14495,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", @@ -14789,7 +14552,8 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/micromatch": { "version": "4.0.8", @@ -15913,6 +15677,7 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "license": "MIT", "dependencies": { "entities": "^4.5.0" }, @@ -16295,283 +16060,96 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-html": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.7.0.tgz", - "integrity": "sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==", + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, - "license": "MIT", "dependencies": { - "htmlparser2": "^8.0.0", - "js-tokens": "^9.0.0", - "postcss": "^8.4.0", - "postcss-safe-parser": "^6.0.0" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": "^12 || >=14" + "node": ">=4" } }, - "node_modules/postcss-html/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, + "node_modules/posthog-js": { + "version": "1.203.1", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.203.1.tgz", + "integrity": "sha512-r/WiSyz6VNbIKEV/30+aD5gdrYkFtmZwvqNa6h9frl8hG638v098FrXaq3EYzMcCdkQf3phaZTDIAFKegpiTjw==", "license": "MIT", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, + "core-js": "^3.38.1", + "fflate": "^0.4.8", + "preact": "^10.19.3", + "web-vitals": "^4.2.0" + } + }, + "node_modules/preact": { + "version": "10.24.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.1.tgz", + "integrity": "sha512-PnBAwFI3Yjxxcxw75n6VId/5TFxNW/81zexzWD9jn1+eSrOP84NdsS38H5IkF/UH3frqRPT+MvuCoVHjTDTnDw==", "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/preact" } }, - "node_modules/postcss-html/node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/postcss-html/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 4" + "node": ">=14" }, "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/postcss-html/node_modules/domutils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.1.tgz", - "integrity": "sha512-xWXmuRnN9OMP6ptPd2+H0cCbcYBULa5YDTbMm/2lvkWvNA3O4wcW+GvzooqBuNM8yy6pl3VIAeJTUUWUbfI5Fw==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" + "fast-diff": "^1.1.2" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/postcss-html/node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], "license": "MIT", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/postcss-html/node_modules/postcss-safe-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", - "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", - "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-safe-parser": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", - "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0" - }, - "peerDependencies": { - "postcss": "^8.4.31" - } - }, - "node_modules/postcss-scss": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", - "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-scss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.4.29" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/posthog-js": { - "version": "1.203.1", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.203.1.tgz", - "integrity": "sha512-r/WiSyz6VNbIKEV/30+aD5gdrYkFtmZwvqNa6h9frl8hG638v098FrXaq3EYzMcCdkQf3phaZTDIAFKegpiTjw==", - "license": "MIT", - "dependencies": { - "core-js": "^3.38.1", - "fflate": "^0.4.8", - "preact": "^10.19.3", - "web-vitals": "^4.2.0" - } - }, - "node_modules/preact": { - "version": "10.24.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.1.tgz", - "integrity": "sha512-PnBAwFI3Yjxxcxw75n6VId/5TFxNW/81zexzWD9jn1+eSrOP84NdsS38H5IkF/UH3frqRPT+MvuCoVHjTDTnDw==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", "engines": { @@ -16991,11 +16569,12 @@ } }, "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "license": "MIT", "engines": { - "node": ">= 14.16.0" + "node": ">= 14.18.0" }, "funding": { "type": "individual", @@ -17052,6 +16631,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/regex/-/regex-5.1.1.tgz", "integrity": "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==", + "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" } @@ -17060,6 +16640,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-5.1.1.tgz", "integrity": "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==", + "license": "MIT", "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" @@ -17319,16 +16900,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -18508,421 +18079,6 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/stylelint": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.12.0.tgz", - "integrity": "sha512-F8zZ3L/rBpuoBZRvI4JVT20ZanPLXfQLzMOZg1tzPflRVh9mKpOZ8qcSIhh1my3FjAjZWG4T2POwGnmn6a6hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - }, - { - "type": "github", - "url": "https://github.com/sponsors/stylelint" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2", - "@csstools/selector-specificity": "^5.0.0", - "@dual-bundle/import-meta-resolve": "^4.1.0", - "balanced-match": "^2.0.0", - "colord": "^2.9.3", - "cosmiconfig": "^9.0.0", - "css-functions-list": "^3.2.3", - "css-tree": "^3.0.1", - "debug": "^4.3.7", - "fast-glob": "^3.3.2", - "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^9.1.0", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.3.1", - "ignore": "^6.0.2", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.35.0", - "mathml-tag-names": "^2.1.3", - "meow": "^13.2.0", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.49", - "postcss-resolve-nested-selector": "^0.1.6", - "postcss-safe-parser": "^7.0.1", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "string-width": "^4.2.3", - "supports-hyperlinks": "^3.1.0", - "svg-tags": "^1.0.0", - "table": "^6.9.0", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "stylelint": "bin/stylelint.mjs" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/stylelint-config-html": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.1.0.tgz", - "integrity": "sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12 || >=14" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "postcss-html": "^1.0.0", - "stylelint": ">=14.0.0" - } - }, - "node_modules/stylelint-config-recommended": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", - "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - }, - { - "type": "github", - "url": "https://github.com/sponsors/stylelint" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "stylelint": "^16.1.0" - } - }, - "node_modules/stylelint-config-recommended-scss": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-14.1.0.tgz", - "integrity": "sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-scss": "^4.0.9", - "stylelint-config-recommended": "^14.0.1", - "stylelint-scss": "^6.4.0" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "postcss": "^8.3.3", - "stylelint": "^16.6.1" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - } - } - }, - "node_modules/stylelint-config-recommended-vue": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended-vue/-/stylelint-config-recommended-vue-1.5.0.tgz", - "integrity": "sha512-65TAK/clUqkNtkZLcuytoxU0URQYlml+30Nhop7sRkCZ/mtWdXt7T+spPSB3KMKlb+82aEVJ4OrcstyDBdbosg==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.3.5", - "stylelint-config-html": ">=1.0.0", - "stylelint-config-recommended": ">=6.0.0" - }, - "engines": { - "node": "^12 || >=14" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "postcss-html": "^1.0.0", - "stylelint": ">=14.0.0" - } - }, - "node_modules/stylelint-scss": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.10.0.tgz", - "integrity": "sha512-y03if6Qw9xBMoVaf7tzp5BbnYhYvudIKzURkhSHzcHG0bW0fAYvQpTUVJOe7DyhHaxeThBil4ObEMvGbV7+M+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-tree": "^3.0.1", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.35.0", - "mdn-data": "^2.12.2", - "postcss-media-query-parser": "^0.2.3", - "postcss-resolve-nested-selector": "^0.1.6", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "stylelint": "^16.0.2" - } - }, - "node_modules/stylelint-scss/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stylelint-scss/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/stylelint/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/stylelint/node_modules/balanced-match": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", - "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", - "dev": true, - "license": "MIT" - }, - "node_modules/stylelint/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", - "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/stylelint/node_modules/flat-cache": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", - "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/stylelint/node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stylelint/node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stylelint/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylelint/node_modules/globby/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/stylelint/node_modules/ignore": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", - "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/stylelint/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stylelint/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/stylelint/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/stylelint/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/stylelint/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stylelint/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/stylelint/node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/stylis": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.5.tgz", @@ -18941,23 +18097,6 @@ "node": ">=8" } }, - "node_modules/supports-hyperlinks": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", - "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -19024,87 +18163,6 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/table": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", - "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/table/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/table/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -19861,6 +18919,7 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "license": "MIT", "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" diff --git a/ui/package.json b/ui/package.json index fbd88cca136..9959c89c21b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@js-joda/core": "^5.6.3", - "@kestra-io/ui-libs": "^0.0.103", + "@kestra-io/ui-libs": "^0.0.107", "@vue-flow/background": "^1.3.2", "@vue-flow/controls": "^1.1.2", "@vue-flow/core": "^1.41.6", @@ -100,15 +100,11 @@ "lint-staged": "^15.2.11", "monaco-editor": "^0.52.2", "monaco-yaml": "^5.2.3", - "postcss-html": "^1.7.0", "prettier": "^3.4.2", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-visualizer": "^5.12.0", "sass": "^1.83.0", "storybook": "^8.4.7", - "stylelint": "^16.12.0", - "stylelint-config-recommended-scss": "^14.1.0", - "stylelint-config-recommended-vue": "^1.5.0", "typescript": "^5.7.2", "typescript-eslint": "^8.18.2", "vite": "^6.0.5",