diff --git a/nodes/workflow-monitor.html b/nodes/workflow-monitor.html
index 77b8fed..aedbd74 100644
--- a/nodes/workflow-monitor.html
+++ b/nodes/workflow-monitor.html
@@ -48,18 +48,20 @@
### Outputs
-1. Status updates (active workflows)
-: payload (object) : The workflow details from the API, sent on every status poll while workflow is active.
+1. All status updates
+: payload (object) : The workflow details from the API, sent on **every** status poll including terminal states. Use this output for tracking/monitoring all workflow state changes.
: workflowId (string) : The ID of the workflow.
2. Success
-: payload (object) : The workflow details from the API, sent once when the workflow completes successfully.
+: payload (object) : The workflow details from the API, sent once when the workflow completes successfully. Use this output for flow control to trigger downstream success actions.
: workflowId (string) : The ID of the workflow.
3. Error
-: payload (object) : The workflow details from the API, sent once when the workflow fails or is cancelled.
+: payload (object) : The workflow details from the API, sent once when the workflow fails or is cancelled. Use this output for flow control to trigger downstream error handling.
: workflowId (string) : The ID of the workflow.
+**Note:** When a workflow succeeds, both outputs 1 and 2 fire. When a workflow fails/cancels, both outputs 1 and 3 fire. This allows output 1 to be used for comprehensive status tracking while outputs 2 and 3 can be used for flow control.
+
### Details
This node queries the Seqera Platform API for the current status of a workflow run. When *Keep polling status* is enabled (default) the node will keep polling at the configured interval until the workflow reaches a terminal state (succeeded, failed, cancelled, or unknown).
diff --git a/nodes/workflow-monitor.js b/nodes/workflow-monitor.js
index 49ec77a..ffd6d27 100644
--- a/nodes/workflow-monitor.js
+++ b/nodes/workflow-monitor.js
@@ -122,16 +122,17 @@ module.exports = function (RED) {
};
// Decide which output to send to
- // Output 1: Active (submitted, running)
- // Output 2: Succeeded
- // Output 3: Failed/Cancelled/Unknown
+ // Output 1: All status updates (always fires for tracking/monitoring)
+ // Output 2: Succeeded (terminal state, for flow control)
+ // Output 3: Failed/Cancelled/Unknown (terminal state, for flow control)
if (/^(submitted|running)$/.test(statusLower)) {
send([outMsg, null, null]);
} else if (/^(succeeded)$/.test(statusLower)) {
- send([null, outMsg, null]);
+ // Send to both output 1 (status tracking) and output 2 (success flow)
+ send([outMsg, outMsg, null]);
} else {
- // failed, cancelled, unknown
- send([null, null, outMsg]);
+ // failed, cancelled, unknown - send to both output 1 (status tracking) and output 3 (failure flow)
+ send([outMsg, null, outMsg]);
}
// If keepPolling disabled OR workflow reached a final state, stop polling THIS workflow
diff --git a/test/workflow-monitor_spec.js b/test/workflow-monitor_spec.js
index faabc24..5d806c9 100644
--- a/test/workflow-monitor_spec.js
+++ b/test/workflow-monitor_spec.js
@@ -414,6 +414,204 @@ describe("seqera-workflow-monitor Node", function () {
monitorNode.receive({ workflowId: "wf-123" });
});
});
+
+ it("should send to BOTH output 1 and output 2 when status is succeeded", function (done) {
+ const flow = [
+ createConfigNode(),
+ {
+ id: "monitor1",
+ type: "seqera-workflow-monitor",
+ name: "Test Monitor",
+ seqera: "config-node-1",
+ workflowId: "workflowId",
+ workflowIdType: "msg",
+ keepPolling: false,
+ wires: [["helper1"], ["helper2"], ["helper3"]],
+ },
+ { id: "helper1", type: "helper" },
+ { id: "helper2", type: "helper" },
+ { id: "helper3", type: "helper" },
+ ];
+
+ nock(DEFAULT_BASE_URL)
+ .get("/workflow/wf-123")
+ .query(true)
+ .reply(200, createWorkflowResponse({ status: "succeeded" }));
+
+ helper.load([configNode, workflowMonitorNode], flow, createCredentials(), function () {
+ const monitorNode = helper.getNode("monitor1");
+ const helper1 = helper.getNode("helper1");
+ const helper2 = helper.getNode("helper2");
+ const helper3 = helper.getNode("helper3");
+
+ let output1Received = false;
+ let output2Received = false;
+
+ const checkDone = () => {
+ if (output1Received && output2Received) {
+ done();
+ }
+ };
+
+ helper1.on("input", function (msg) {
+ try {
+ expect(msg.payload.workflow.status).to.equal("succeeded");
+ output1Received = true;
+ checkDone();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ helper2.on("input", function (msg) {
+ try {
+ expect(msg.payload.workflow.status).to.equal("succeeded");
+ output2Received = true;
+ checkDone();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ helper3.on("input", function () {
+ done(new Error("Should not receive on output 3 for succeeded status"));
+ });
+
+ monitorNode.receive({ workflowId: "wf-123" });
+ });
+ });
+
+ it("should send to BOTH output 1 and output 3 when status is failed", function (done) {
+ const flow = [
+ createConfigNode(),
+ {
+ id: "monitor1",
+ type: "seqera-workflow-monitor",
+ name: "Test Monitor",
+ seqera: "config-node-1",
+ workflowId: "workflowId",
+ workflowIdType: "msg",
+ keepPolling: false,
+ wires: [["helper1"], ["helper2"], ["helper3"]],
+ },
+ { id: "helper1", type: "helper" },
+ { id: "helper2", type: "helper" },
+ { id: "helper3", type: "helper" },
+ ];
+
+ nock(DEFAULT_BASE_URL)
+ .get("/workflow/wf-123")
+ .query(true)
+ .reply(200, createWorkflowResponse({ status: "failed" }));
+
+ helper.load([configNode, workflowMonitorNode], flow, createCredentials(), function () {
+ const monitorNode = helper.getNode("monitor1");
+ const helper1 = helper.getNode("helper1");
+ const helper2 = helper.getNode("helper2");
+ const helper3 = helper.getNode("helper3");
+
+ let output1Received = false;
+ let output3Received = false;
+
+ const checkDone = () => {
+ if (output1Received && output3Received) {
+ done();
+ }
+ };
+
+ helper1.on("input", function (msg) {
+ try {
+ expect(msg.payload.workflow.status).to.equal("failed");
+ output1Received = true;
+ checkDone();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ helper2.on("input", function () {
+ done(new Error("Should not receive on output 2 for failed status"));
+ });
+
+ helper3.on("input", function (msg) {
+ try {
+ expect(msg.payload.workflow.status).to.equal("failed");
+ output3Received = true;
+ checkDone();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ monitorNode.receive({ workflowId: "wf-123" });
+ });
+ });
+
+ it("should send to BOTH output 1 and output 3 when status is cancelled", function (done) {
+ const flow = [
+ createConfigNode(),
+ {
+ id: "monitor1",
+ type: "seqera-workflow-monitor",
+ name: "Test Monitor",
+ seqera: "config-node-1",
+ workflowId: "workflowId",
+ workflowIdType: "msg",
+ keepPolling: false,
+ wires: [["helper1"], ["helper2"], ["helper3"]],
+ },
+ { id: "helper1", type: "helper" },
+ { id: "helper2", type: "helper" },
+ { id: "helper3", type: "helper" },
+ ];
+
+ nock(DEFAULT_BASE_URL)
+ .get("/workflow/wf-123")
+ .query(true)
+ .reply(200, createWorkflowResponse({ status: "cancelled" }));
+
+ helper.load([configNode, workflowMonitorNode], flow, createCredentials(), function () {
+ const monitorNode = helper.getNode("monitor1");
+ const helper1 = helper.getNode("helper1");
+ const helper2 = helper.getNode("helper2");
+ const helper3 = helper.getNode("helper3");
+
+ let output1Received = false;
+ let output3Received = false;
+
+ const checkDone = () => {
+ if (output1Received && output3Received) {
+ done();
+ }
+ };
+
+ helper1.on("input", function (msg) {
+ try {
+ expect(msg.payload.workflow.status).to.equal("cancelled");
+ output1Received = true;
+ checkDone();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ helper2.on("input", function () {
+ done(new Error("Should not receive on output 2 for cancelled status"));
+ });
+
+ helper3.on("input", function (msg) {
+ try {
+ expect(msg.payload.workflow.status).to.equal("cancelled");
+ output3Received = true;
+ checkDone();
+ } catch (err) {
+ done(err);
+ }
+ });
+
+ monitorNode.receive({ workflowId: "wf-123" });
+ });
+ });
});
describe("polling behavior", function () {