mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 19:32:15 +00:00
feat(editor): Add execute workflow functionality and statuses to new canvas (no-changelog) (#9902)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
/**
|
||||
* Canvas V2 Only
|
||||
* @TODO Remove this notice when Canvas V2 is the only one in use
|
||||
*/
|
||||
|
||||
import type { CanvasElement } from '@/types';
|
||||
import { CanvasConnectionMode } from '@/types';
|
||||
import type {
|
||||
AddedNodesAndConnections,
|
||||
INodeUi,
|
||||
@@ -170,9 +176,9 @@ export function useCanvasOperations({
|
||||
historyStore.startRecordingUndo();
|
||||
}
|
||||
|
||||
workflowsStore.removeNodeById(id);
|
||||
workflowsStore.removeNodeConnectionsById(id);
|
||||
workflowsStore.removeNodeExecutionDataById(id);
|
||||
workflowsStore.removeNodeById(id);
|
||||
|
||||
if (trackHistory) {
|
||||
historyStore.pushCommandToUndo(new RemoveNodeCommand(node));
|
||||
@@ -215,7 +221,7 @@ export function useCanvasOperations({
|
||||
return;
|
||||
}
|
||||
|
||||
ndvStore.activeNodeName = node.name;
|
||||
setNodeActiveByName(node.name);
|
||||
}
|
||||
|
||||
function setNodeActiveByName(name: string) {
|
||||
@@ -334,18 +340,32 @@ export function useCanvasOperations({
|
||||
const outputIndex = lastSelectedNodeOutputIndex ?? 0;
|
||||
const targetEndpoint = lastSelectedNodeEndpointUuid ?? '';
|
||||
|
||||
// Handle connection of scoped_endpoint types
|
||||
// Create a connection between the last selected node and the new one
|
||||
if (lastSelectedNode && !options.isAutoAdd) {
|
||||
// If we have a specific endpoint to connect to
|
||||
if (lastSelectedNodeEndpointUuid) {
|
||||
const { type: connectionType } = parseCanvasConnectionHandleString(
|
||||
const { type: connectionType, mode } = parseCanvasConnectionHandleString(
|
||||
lastSelectedNodeEndpointUuid,
|
||||
);
|
||||
if (isConnectionAllowed(lastSelectedNode, newNodeData, connectionType)) {
|
||||
|
||||
const newNodeId = newNodeData.id;
|
||||
const newNodeHandle = `${CanvasConnectionMode.Input}/${connectionType}/0`;
|
||||
const lasSelectedNodeId = lastSelectedNode.id;
|
||||
const lastSelectedNodeHandle = targetEndpoint;
|
||||
|
||||
if (mode === CanvasConnectionMode.Input) {
|
||||
createConnection({
|
||||
source: lastSelectedNode.id,
|
||||
sourceHandle: targetEndpoint,
|
||||
target: newNodeData.id,
|
||||
targetHandle: `inputs/${connectionType}/0`,
|
||||
source: newNodeId,
|
||||
sourceHandle: newNodeHandle,
|
||||
target: lasSelectedNodeId,
|
||||
targetHandle: lastSelectedNodeHandle,
|
||||
});
|
||||
} else {
|
||||
createConnection({
|
||||
source: lasSelectedNodeId,
|
||||
sourceHandle: lastSelectedNodeHandle,
|
||||
target: newNodeId,
|
||||
targetHandle: newNodeHandle,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -510,8 +530,6 @@ export function useCanvasOperations({
|
||||
canvasStore.newNodeInsertPosition = null;
|
||||
} else {
|
||||
let yOffset = 0;
|
||||
const workflow = workflowsStore.getCurrentWorkflow();
|
||||
|
||||
if (lastSelectedConnection) {
|
||||
const sourceNodeType = nodeTypesStore.getNodeType(
|
||||
lastSelectedNode.type,
|
||||
@@ -526,7 +544,7 @@ export function useCanvasOperations({
|
||||
];
|
||||
|
||||
const sourceNodeOutputs = NodeHelpers.getNodeOutputs(
|
||||
workflow,
|
||||
editableWorkflowObject.value,
|
||||
lastSelectedNode,
|
||||
sourceNodeType,
|
||||
);
|
||||
@@ -553,7 +571,11 @@ export function useCanvasOperations({
|
||||
// outputs here is to calculate the position, it is fine to assume
|
||||
// that they have no outputs and are so treated as a regular node
|
||||
// with only "main" outputs.
|
||||
outputs = NodeHelpers.getNodeOutputs(workflow, newNodeData, nodeTypeDescription);
|
||||
outputs = NodeHelpers.getNodeOutputs(
|
||||
editableWorkflowObject.value,
|
||||
newNodeData,
|
||||
nodeTypeDescription,
|
||||
);
|
||||
} catch (e) {}
|
||||
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
|
||||
const lastSelectedNodeType = nodeTypesStore.getNodeType(
|
||||
@@ -566,13 +588,15 @@ export function useCanvasOperations({
|
||||
outputTypes.length > 0 &&
|
||||
outputTypes.every((outputName) => outputName !== NodeConnectionType.Main)
|
||||
) {
|
||||
const lastSelectedNodeWorkflow = workflow.getNode(lastSelectedNode.name);
|
||||
const lastSelectedNodeWorkflow = editableWorkflowObject.value.getNode(
|
||||
lastSelectedNode.name,
|
||||
);
|
||||
if (!lastSelectedNodeWorkflow || !lastSelectedNodeType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastSelectedInputs = NodeHelpers.getNodeInputs(
|
||||
workflow,
|
||||
editableWorkflowObject.value,
|
||||
lastSelectedNodeWorkflow,
|
||||
lastSelectedNodeType,
|
||||
);
|
||||
@@ -600,7 +624,7 @@ export function useCanvasOperations({
|
||||
|
||||
// Has only main outputs or no outputs at all
|
||||
const inputs = NodeHelpers.getNodeInputs(
|
||||
workflow,
|
||||
editableWorkflowObject.value,
|
||||
lastSelectedNode,
|
||||
lastSelectedNodeType,
|
||||
);
|
||||
@@ -683,10 +707,11 @@ export function useCanvasOperations({
|
||||
{ trackHistory = false }: { trackHistory?: boolean },
|
||||
) {
|
||||
const sourceNode = workflowsStore.nodesByName[sourceNodeName];
|
||||
|
||||
const workflow = workflowHelpers.getCurrentWorkflow();
|
||||
|
||||
const checkNodes = workflowHelpers.getConnectedNodes('downstream', workflow, sourceNodeName);
|
||||
const checkNodes = workflowHelpers.getConnectedNodes(
|
||||
'downstream',
|
||||
editableWorkflowObject.value,
|
||||
sourceNodeName,
|
||||
);
|
||||
for (const nodeName of checkNodes) {
|
||||
const node = workflowsStore.nodesByName[nodeName];
|
||||
const oldPosition = node.position;
|
||||
@@ -784,6 +809,9 @@ export function useCanvasOperations({
|
||||
connection: mappedConnection,
|
||||
});
|
||||
|
||||
nodeHelpers.updateNodeInputIssues(sourceNode);
|
||||
nodeHelpers.updateNodeInputIssues(targetNode);
|
||||
|
||||
uiStore.stateIsDirty = true;
|
||||
}
|
||||
|
||||
@@ -829,46 +857,54 @@ export function useCanvasOperations({
|
||||
function isConnectionAllowed(
|
||||
sourceNode: INodeUi,
|
||||
targetNode: INodeUi,
|
||||
targetNodeConnectionType: NodeConnectionType,
|
||||
connectionType: NodeConnectionType,
|
||||
): boolean {
|
||||
const targetNodeType = nodeTypesStore.getNodeType(targetNode.type, targetNode.typeVersion);
|
||||
if (sourceNode.id === targetNode.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetNodeType = nodeTypesStore.getNodeType(targetNode.type, targetNode.typeVersion);
|
||||
if (targetNodeType?.inputs?.length) {
|
||||
const workflow = workflowsStore.getCurrentWorkflow();
|
||||
const workflowNode = workflow.getNode(targetNode.name);
|
||||
const workflowNode = editableWorkflowObject.value.getNode(targetNode.name);
|
||||
if (!workflowNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let inputs: Array<ConnectionTypes | INodeInputConfiguration> = [];
|
||||
if (targetNodeType) {
|
||||
inputs = NodeHelpers.getNodeInputs(workflow, workflowNode, targetNodeType) || [];
|
||||
inputs =
|
||||
NodeHelpers.getNodeInputs(editableWorkflowObject.value, workflowNode, targetNodeType) ||
|
||||
[];
|
||||
}
|
||||
|
||||
let targetHasConnectionTypeAsInput = false;
|
||||
for (const input of inputs) {
|
||||
if (typeof input === 'string' || input.type !== targetNodeConnectionType || !input.filter) {
|
||||
// No filters defined or wrong connection type
|
||||
continue;
|
||||
}
|
||||
const inputType = typeof input === 'string' ? input : input.type;
|
||||
if (inputType === connectionType) {
|
||||
if (typeof input === 'object' && 'filter' in input && input.filter?.nodes.length) {
|
||||
if (!input.filter.nodes.includes(sourceNode.type)) {
|
||||
// this.dropPrevented = true;
|
||||
toast.showToast({
|
||||
title: i18n.baseText('nodeView.showError.nodeNodeCompatible.title'),
|
||||
message: i18n.baseText('nodeView.showError.nodeNodeCompatible.message', {
|
||||
interpolate: { sourceNodeName: sourceNode.name, targetNodeName: targetNode.name },
|
||||
}),
|
||||
type: 'error',
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
if (input.filter.nodes.length) {
|
||||
if (!input.filter.nodes.includes(sourceNode.type)) {
|
||||
// this.dropPrevented = true;
|
||||
toast.showToast({
|
||||
title: i18n.baseText('nodeView.showError.nodeNodeCompatible.title'),
|
||||
message: i18n.baseText('nodeView.showError.nodeNodeCompatible.message', {
|
||||
interpolate: { sourceNodeName: sourceNode.name, targetNodeName: targetNode.name },
|
||||
}),
|
||||
type: 'error',
|
||||
duration: 5000,
|
||||
});
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
targetHasConnectionTypeAsInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
return targetHasConnectionTypeAsInput;
|
||||
}
|
||||
|
||||
return sourceNode.id !== targetNode.id;
|
||||
return false;
|
||||
}
|
||||
|
||||
async function addConnections(
|
||||
@@ -907,5 +943,6 @@ export function useCanvasOperations({
|
||||
createConnection,
|
||||
deleteConnection,
|
||||
revertDeleteConnection,
|
||||
isConnectionAllowed,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user