mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2026-09-27 12:11:43 +02:00
refactor(core): Reorganize n8n-core and enforce file-name casing (no-changelog) (#12667)
This commit is contained in:
+219
@@ -0,0 +1,219 @@
|
||||
import * as a from 'assert/strict';
|
||||
import {
|
||||
NodeConnectionType,
|
||||
type IExecuteData,
|
||||
type INode,
|
||||
type INodeExecutionData,
|
||||
type IPinData,
|
||||
type IRunData,
|
||||
type ISourceData,
|
||||
type ITaskDataConnectionsSource,
|
||||
type IWaitingForExecution,
|
||||
type IWaitingForExecutionSource,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import type { DirectedGraph } from './directed-graph';
|
||||
import { getIncomingDataFromAnyRun } from './get-incoming-data';
|
||||
import { getSourceDataGroups } from './get-source-data-groups';
|
||||
|
||||
export function addWaitingExecution(
|
||||
waitingExecution: IWaitingForExecution,
|
||||
nodeName: string,
|
||||
runIndex: number,
|
||||
inputType: NodeConnectionType,
|
||||
inputIndex: number,
|
||||
executionData: INodeExecutionData[] | null,
|
||||
) {
|
||||
const waitingExecutionObject = waitingExecution[nodeName] ?? {};
|
||||
const taskDataConnections = waitingExecutionObject[runIndex] ?? {};
|
||||
const executionDataList = taskDataConnections[inputType] ?? [];
|
||||
|
||||
executionDataList[inputIndex] = executionData;
|
||||
|
||||
taskDataConnections[inputType] = executionDataList;
|
||||
waitingExecutionObject[runIndex] = taskDataConnections;
|
||||
waitingExecution[nodeName] = waitingExecutionObject;
|
||||
}
|
||||
|
||||
export function addWaitingExecutionSource(
|
||||
waitingExecutionSource: IWaitingForExecutionSource,
|
||||
nodeName: string,
|
||||
runIndex: number,
|
||||
inputType: NodeConnectionType,
|
||||
inputIndex: number,
|
||||
sourceData: ISourceData | null,
|
||||
) {
|
||||
const waitingExecutionSourceObject = waitingExecutionSource[nodeName] ?? {};
|
||||
const taskDataConnectionsSource = waitingExecutionSourceObject[runIndex] ?? {};
|
||||
const sourceDataList = taskDataConnectionsSource[inputType] ?? [];
|
||||
|
||||
sourceDataList[inputIndex] = sourceData;
|
||||
|
||||
taskDataConnectionsSource[inputType] = sourceDataList;
|
||||
waitingExecutionSourceObject[runIndex] = taskDataConnectionsSource;
|
||||
waitingExecutionSource[nodeName] = waitingExecutionSourceObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreates the node execution stack, waiting executions and waiting
|
||||
* execution sources from a directed graph, start nodes, the destination node,
|
||||
* run and pinned data.
|
||||
*
|
||||
* This function aims to be able to recreate the internal state of the
|
||||
* WorkflowExecute class at any point of time during an execution based on the
|
||||
* data that is already available. Specifically it will recreate the
|
||||
* `WorkflowExecute.runExecutionData.executionData` properties.
|
||||
*
|
||||
* This allows "restarting" an execution and having it only execute what's
|
||||
* necessary to be able to execute the destination node accurately, e.g. as
|
||||
* close as possible to what would happen in a production execution.
|
||||
*/
|
||||
export function recreateNodeExecutionStack(
|
||||
graph: DirectedGraph,
|
||||
startNodes: Set<INode>,
|
||||
runData: IRunData,
|
||||
pinData: IPinData,
|
||||
): {
|
||||
nodeExecutionStack: IExecuteData[];
|
||||
waitingExecution: IWaitingForExecution;
|
||||
waitingExecutionSource: IWaitingForExecutionSource;
|
||||
} {
|
||||
// Validate invariants.
|
||||
|
||||
// The graph needs to be free of disabled nodes. If it's not it hasn't been
|
||||
// passed through findSubgraph.
|
||||
for (const node of graph.getNodes().values()) {
|
||||
a.notEqual(
|
||||
node.disabled,
|
||||
true,
|
||||
`Graph contains disabled nodes. This is not supported. Make sure to pass the graph through "findSubgraph" before calling "recreateNodeExecutionStack". The node in question is "${node.name}"`,
|
||||
);
|
||||
}
|
||||
|
||||
// Initialize the nodeExecutionStack and waitingExecution with
|
||||
// the data from runData
|
||||
const nodeExecutionStack: IExecuteData[] = [];
|
||||
const waitingExecution: IWaitingForExecution = {};
|
||||
const waitingExecutionSource: IWaitingForExecutionSource = {};
|
||||
|
||||
for (const startNode of startNodes) {
|
||||
const incomingStartNodeConnections = graph
|
||||
.getDirectParentConnections(startNode)
|
||||
.filter((c) => c.type === NodeConnectionType.Main);
|
||||
|
||||
let incomingData: INodeExecutionData[][] = [];
|
||||
let incomingSourceData: ITaskDataConnectionsSource | null = null;
|
||||
|
||||
if (incomingStartNodeConnections.length === 0) {
|
||||
incomingData.push([{ json: {} }]);
|
||||
|
||||
const executeData: IExecuteData = {
|
||||
node: startNode,
|
||||
data: { main: incomingData },
|
||||
source: incomingSourceData,
|
||||
};
|
||||
|
||||
nodeExecutionStack.push(executeData);
|
||||
} else {
|
||||
const sourceDataSets = getSourceDataGroups(graph, startNode, runData, pinData);
|
||||
|
||||
for (const sourceData of sourceDataSets) {
|
||||
if (sourceData.complete) {
|
||||
// All incoming connections have data, so let's put the node on the
|
||||
// stack!
|
||||
incomingData = [];
|
||||
|
||||
incomingSourceData = { main: [] };
|
||||
|
||||
for (const incomingConnection of sourceData.connections) {
|
||||
let runIndex = 0;
|
||||
const sourceNode = incomingConnection.from;
|
||||
|
||||
if (pinData[sourceNode.name]) {
|
||||
incomingData.push(pinData[sourceNode.name]);
|
||||
} else {
|
||||
a.ok(
|
||||
runData[sourceNode.name],
|
||||
`Start node(${incomingConnection.to.name}) has an incoming connection with no run or pinned data. This is not supported. The connection in question is "${sourceNode.name}->${startNode.name}". Are you sure the start nodes come from the "findStartNodes" function?`,
|
||||
);
|
||||
|
||||
const nodeIncomingData = getIncomingDataFromAnyRun(
|
||||
runData,
|
||||
sourceNode.name,
|
||||
incomingConnection.type,
|
||||
incomingConnection.outputIndex,
|
||||
);
|
||||
|
||||
if (nodeIncomingData) {
|
||||
runIndex = nodeIncomingData.runIndex;
|
||||
incomingData.push(nodeIncomingData.data);
|
||||
}
|
||||
}
|
||||
|
||||
incomingSourceData.main.push({
|
||||
previousNode: incomingConnection.from.name,
|
||||
previousNodeOutput: incomingConnection.outputIndex,
|
||||
previousNodeRun: runIndex,
|
||||
});
|
||||
}
|
||||
|
||||
const executeData: IExecuteData = {
|
||||
node: startNode,
|
||||
data: { main: incomingData },
|
||||
source: incomingSourceData,
|
||||
};
|
||||
|
||||
nodeExecutionStack.push(executeData);
|
||||
} else {
|
||||
const nodeName = startNode.name;
|
||||
const nextRunIndex = waitingExecution[nodeName]
|
||||
? Object.keys(waitingExecution[nodeName]).length
|
||||
: 0;
|
||||
|
||||
for (const incomingConnection of sourceData.connections) {
|
||||
const sourceNode = incomingConnection.from;
|
||||
const maybeNodeIncomingData = getIncomingDataFromAnyRun(
|
||||
runData,
|
||||
sourceNode.name,
|
||||
incomingConnection.type,
|
||||
incomingConnection.outputIndex,
|
||||
);
|
||||
const nodeIncomingData = maybeNodeIncomingData?.data ?? null;
|
||||
|
||||
if (nodeIncomingData) {
|
||||
addWaitingExecution(
|
||||
waitingExecution,
|
||||
nodeName,
|
||||
nextRunIndex,
|
||||
incomingConnection.type,
|
||||
incomingConnection.inputIndex,
|
||||
nodeIncomingData,
|
||||
);
|
||||
|
||||
addWaitingExecutionSource(
|
||||
waitingExecutionSource,
|
||||
nodeName,
|
||||
nextRunIndex,
|
||||
incomingConnection.type,
|
||||
incomingConnection.inputIndex,
|
||||
nodeIncomingData
|
||||
? {
|
||||
previousNode: incomingConnection.from.name,
|
||||
previousNodeRun: nextRunIndex,
|
||||
previousNodeOutput: incomingConnection.outputIndex,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
nodeExecutionStack,
|
||||
waitingExecution,
|
||||
waitingExecutionSource,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user