2024-06-04 05:36:27 -07:00
|
|
|
import type { IConnection, IConnections, INodeTypeDescription } from 'n8n-workflow';
|
2024-05-23 01:42:10 -07:00
|
|
|
import type { INodeUi } from '@/Interface';
|
2024-07-11 07:05:51 -07:00
|
|
|
import type { CanvasConnection, CanvasConnectionPort } from '@/types';
|
2024-07-08 03:25:18 -07:00
|
|
|
import { CanvasConnectionMode } from '@/types';
|
2024-06-04 05:36:27 -07:00
|
|
|
import type { Connection } from '@vue-flow/core';
|
2024-05-23 01:42:10 -07:00
|
|
|
import { v4 as uuid } from 'uuid';
|
2024-07-08 03:25:18 -07:00
|
|
|
import { isValidCanvasConnectionMode, isValidNodeConnectionType } from '@/utils/typeGuards';
|
2024-06-04 05:36:27 -07:00
|
|
|
import { NodeConnectionType } from 'n8n-workflow';
|
2024-05-23 01:42:10 -07:00
|
|
|
|
|
|
|
export function mapLegacyConnectionsToCanvasConnections(
|
|
|
|
legacyConnections: IConnections,
|
|
|
|
nodes: INodeUi[],
|
|
|
|
): CanvasConnection[] {
|
|
|
|
const mappedConnections: CanvasConnection[] = [];
|
|
|
|
|
|
|
|
Object.keys(legacyConnections).forEach((fromNodeName) => {
|
2024-07-11 07:05:51 -07:00
|
|
|
const fromId = nodes.find((node) => node.name === fromNodeName)?.id ?? '';
|
|
|
|
const fromConnectionTypes = Object.keys(
|
|
|
|
legacyConnections[fromNodeName],
|
|
|
|
) as NodeConnectionType[];
|
2024-05-23 01:42:10 -07:00
|
|
|
|
|
|
|
fromConnectionTypes.forEach((fromConnectionType) => {
|
|
|
|
const fromPorts = legacyConnections[fromNodeName][fromConnectionType];
|
|
|
|
fromPorts.forEach((toPorts, fromIndex) => {
|
|
|
|
toPorts.forEach((toPort) => {
|
2024-07-11 07:05:51 -07:00
|
|
|
const toId = nodes.find((node) => node.name === toPort.node)?.id ?? '';
|
|
|
|
const toConnectionType = toPort.type as NodeConnectionType;
|
2024-05-23 01:42:10 -07:00
|
|
|
const toIndex = toPort.index;
|
|
|
|
|
2024-07-11 07:05:51 -07:00
|
|
|
const sourceHandle = createCanvasConnectionHandleString({
|
|
|
|
mode: CanvasConnectionMode.Output,
|
|
|
|
type: fromConnectionType,
|
|
|
|
index: fromIndex,
|
|
|
|
});
|
|
|
|
|
|
|
|
const targetHandle = createCanvasConnectionHandleString({
|
|
|
|
mode: CanvasConnectionMode.Input,
|
|
|
|
type: toConnectionType,
|
|
|
|
index: toIndex,
|
|
|
|
});
|
|
|
|
|
|
|
|
const connectionId = createCanvasConnectionId({
|
|
|
|
source: fromId,
|
|
|
|
sourceHandle,
|
|
|
|
target: toId,
|
|
|
|
targetHandle,
|
|
|
|
});
|
|
|
|
|
2024-05-23 01:42:10 -07:00
|
|
|
if (fromId && toId) {
|
|
|
|
mappedConnections.push({
|
2024-07-11 07:05:51 -07:00
|
|
|
id: connectionId,
|
2024-05-23 01:42:10 -07:00
|
|
|
source: fromId,
|
|
|
|
target: toId,
|
2024-07-11 07:05:51 -07:00
|
|
|
sourceHandle,
|
|
|
|
targetHandle,
|
2024-05-23 01:42:10 -07:00
|
|
|
data: {
|
|
|
|
fromNodeName,
|
|
|
|
source: {
|
|
|
|
index: fromIndex,
|
2024-07-11 07:05:51 -07:00
|
|
|
type: fromConnectionType,
|
2024-05-23 01:42:10 -07:00
|
|
|
},
|
|
|
|
target: {
|
|
|
|
index: toIndex,
|
2024-07-11 07:05:51 -07:00
|
|
|
type: toConnectionType,
|
2024-05-23 01:42:10 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return mappedConnections;
|
|
|
|
}
|
|
|
|
|
2024-07-22 10:26:47 -07:00
|
|
|
export function mapLegacyConnectionToCanvasConnection(
|
|
|
|
sourceNode: INodeUi,
|
|
|
|
targetNode: INodeUi,
|
|
|
|
legacyConnection: [IConnection, IConnection],
|
|
|
|
): Connection {
|
|
|
|
const source = sourceNode.id;
|
|
|
|
const sourceHandle = createCanvasConnectionHandleString({
|
|
|
|
mode: CanvasConnectionMode.Output,
|
|
|
|
type: legacyConnection[0].type,
|
|
|
|
index: legacyConnection[0].index,
|
|
|
|
});
|
|
|
|
const target = targetNode.id;
|
|
|
|
const targetHandle = createCanvasConnectionHandleString({
|
|
|
|
mode: CanvasConnectionMode.Input,
|
|
|
|
type: legacyConnection[1].type,
|
|
|
|
index: legacyConnection[1].index,
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
source,
|
|
|
|
sourceHandle,
|
|
|
|
target,
|
|
|
|
targetHandle,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-06-04 05:36:27 -07:00
|
|
|
export function parseCanvasConnectionHandleString(handle: string | null | undefined) {
|
2024-07-08 03:25:18 -07:00
|
|
|
const [mode, type, index] = (handle ?? '').split('/');
|
2024-06-04 05:36:27 -07:00
|
|
|
|
|
|
|
const resolvedType = isValidNodeConnectionType(type) ? type : NodeConnectionType.Main;
|
2024-07-08 03:25:18 -07:00
|
|
|
const resolvedMode = isValidCanvasConnectionMode(mode) ? mode : CanvasConnectionMode.Output;
|
2024-06-04 05:36:27 -07:00
|
|
|
|
|
|
|
let resolvedIndex = parseInt(index, 10);
|
|
|
|
if (isNaN(resolvedIndex)) {
|
|
|
|
resolvedIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2024-07-08 03:25:18 -07:00
|
|
|
mode: resolvedMode,
|
2024-06-04 05:36:27 -07:00
|
|
|
type: resolvedType,
|
|
|
|
index: resolvedIndex,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-06-25 02:11:44 -07:00
|
|
|
export function createCanvasConnectionHandleString({
|
|
|
|
mode,
|
|
|
|
type = NodeConnectionType.Main,
|
|
|
|
index = 0,
|
|
|
|
}: {
|
|
|
|
mode: 'inputs' | 'outputs';
|
|
|
|
type?: NodeConnectionType;
|
|
|
|
index?: number;
|
|
|
|
}) {
|
|
|
|
return `${mode}/${type}/${index}`;
|
|
|
|
}
|
|
|
|
|
2024-07-11 07:05:51 -07:00
|
|
|
export function createCanvasConnectionId(connection: Connection) {
|
|
|
|
return `[${connection.source}/${connection.sourceHandle}][${connection.target}/${connection.targetHandle}]`;
|
|
|
|
}
|
|
|
|
|
2024-06-04 05:36:27 -07:00
|
|
|
export function mapCanvasConnectionToLegacyConnection(
|
|
|
|
sourceNode: INodeUi,
|
|
|
|
targetNode: INodeUi,
|
|
|
|
connection: Connection,
|
|
|
|
): [IConnection, IConnection] {
|
|
|
|
// Output
|
|
|
|
const sourceNodeName = sourceNode?.name ?? '';
|
|
|
|
const { type: sourceType, index: sourceIndex } = parseCanvasConnectionHandleString(
|
|
|
|
connection.sourceHandle,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Input
|
|
|
|
const targetNodeName = targetNode?.name ?? '';
|
|
|
|
const { type: targetType, index: targetIndex } = parseCanvasConnectionHandleString(
|
|
|
|
connection.targetHandle,
|
|
|
|
);
|
|
|
|
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
node: sourceNodeName,
|
|
|
|
type: sourceType,
|
|
|
|
index: sourceIndex,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
node: targetNodeName,
|
|
|
|
type: targetType,
|
|
|
|
index: targetIndex,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2024-05-23 01:42:10 -07:00
|
|
|
export function mapLegacyEndpointsToCanvasConnectionPort(
|
|
|
|
endpoints: INodeTypeDescription['inputs'],
|
|
|
|
): CanvasConnectionPort[] {
|
|
|
|
if (typeof endpoints === 'string') {
|
|
|
|
console.warn('Node endpoints have not been evaluated', endpoints);
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return endpoints.map((endpoint, endpointIndex) => {
|
2024-07-18 09:01:14 -07:00
|
|
|
const typeValue = typeof endpoint === 'string' ? endpoint : endpoint.type;
|
|
|
|
const type = isValidNodeConnectionType(typeValue) ? typeValue : NodeConnectionType.Main;
|
2024-05-23 01:42:10 -07:00
|
|
|
const label = typeof endpoint === 'string' ? undefined : endpoint.displayName;
|
|
|
|
const index =
|
|
|
|
endpoints
|
|
|
|
.slice(0, endpointIndex + 1)
|
|
|
|
.filter((e) => (typeof e === 'string' ? e : e.type) === type).length - 1;
|
|
|
|
const required = typeof endpoint === 'string' ? false : endpoint.required;
|
|
|
|
|
|
|
|
return {
|
|
|
|
type,
|
|
|
|
index,
|
|
|
|
label,
|
|
|
|
...(required ? { required } : {}),
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getUniqueNodeName(name: string, existingNames: Set<string>): string {
|
|
|
|
if (!existingNames.has(name)) {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 1; i < 100; i++) {
|
|
|
|
const newName = `${name} ${i}`;
|
|
|
|
if (!existingNames.has(newName)) {
|
|
|
|
return newName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return `${name} ${uuid()}`;
|
|
|
|
}
|