refactor(editor): Fix NodeView/Canvas related TS errors (#9581)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import {
|
||||
mapLegacyEndpointsToCanvasConnectionPort,
|
||||
getUniqueNodeName,
|
||||
} from '@/utils/canvasUtilsV2';
|
||||
import type { IConnections, INodeTypeDescription } from 'n8n-workflow';
|
||||
import { NodeConnectionType, type IConnections, type INodeTypeDescription } from 'n8n-workflow';
|
||||
import type { CanvasConnection } from '@/types';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
it('should map legacy connections to canvas connections', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [[{ node: 'Node B', type: 'main', index: 0 }]],
|
||||
main: [[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
};
|
||||
const nodes: INodeUi[] = [
|
||||
@@ -53,11 +53,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -67,7 +67,7 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
it('should return empty array when no matching nodes found', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [[{ node: 'Node B', type: 'main', index: 0 }]],
|
||||
main: [[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
};
|
||||
const nodes: INodeUi[] = [];
|
||||
@@ -113,8 +113,8 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [
|
||||
[{ node: 'Node B', type: 'main', index: 0 }],
|
||||
[{ node: 'Node B', type: 'main', index: 1 }],
|
||||
[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }],
|
||||
[{ node: 'Node B', type: NodeConnectionType.Main, index: 1 }],
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -153,11 +153,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -171,11 +171,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 1,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 1,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -186,8 +186,8 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [
|
||||
[{ node: 'Node B', type: 'main', index: 0 }],
|
||||
[{ node: 'Node C', type: 'main', index: 0 }],
|
||||
[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }],
|
||||
[{ node: 'Node C', type: NodeConnectionType.Main, index: 0 }],
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -234,11 +234,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -252,11 +252,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 1,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -266,11 +266,13 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
it('should map complex node setup with mixed inputs and outputs', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [[{ node: 'Node B', type: 'main', index: 0 }]],
|
||||
other: [[{ node: 'Node C', type: 'other', index: 1 }]],
|
||||
main: [[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }]],
|
||||
[NodeConnectionType.AiMemory]: [
|
||||
[{ node: 'Node C', type: NodeConnectionType.AiMemory, index: 1 }],
|
||||
],
|
||||
},
|
||||
'Node B': {
|
||||
main: [[{ node: 'Node C', type: 'main', index: 0 }]],
|
||||
main: [[{ node: 'Node C', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
};
|
||||
const nodes: INodeUi[] = [
|
||||
@@ -316,29 +318,29 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '[1/other/0][3/other/1]',
|
||||
id: `[1/${NodeConnectionType.AiMemory}/0][3/${NodeConnectionType.AiMemory}/1]`,
|
||||
source: '1',
|
||||
target: '3',
|
||||
sourceHandle: 'outputs/other/0',
|
||||
targetHandle: 'inputs/other/1',
|
||||
sourceHandle: `outputs/${NodeConnectionType.AiMemory}/0`,
|
||||
targetHandle: `inputs/${NodeConnectionType.AiMemory}/1`,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'other',
|
||||
type: NodeConnectionType.AiMemory,
|
||||
},
|
||||
target: {
|
||||
index: 1,
|
||||
type: 'other',
|
||||
type: NodeConnectionType.AiMemory,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -352,11 +354,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node B',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -367,8 +369,8 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [
|
||||
[{ node: 'Nonexistent Node', type: 'main', index: 0 }],
|
||||
[{ node: 'Node B', type: 'main', index: 0 }],
|
||||
[{ node: 'Nonexistent Node', type: NodeConnectionType.Main, index: 0 }],
|
||||
[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }],
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -407,11 +409,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 1,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -435,66 +437,69 @@ describe('mapLegacyEndpointsToCanvasConnectionPort', () => {
|
||||
});
|
||||
|
||||
it('should map string endpoints correctly', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = ['main', 'ai_tool'];
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
NodeConnectionType.Main,
|
||||
NodeConnectionType.AiTool,
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: undefined },
|
||||
{ type: 'ai_tool', index: 0, label: undefined },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: undefined },
|
||||
{ type: NodeConnectionType.AiTool, index: 0, label: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should map object endpoints correctly', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
{ type: 'main', displayName: 'Main Input' },
|
||||
{ type: 'ai_tool', displayName: 'AI Tool', required: true },
|
||||
{ type: NodeConnectionType.Main, displayName: 'Main Input' },
|
||||
{ type: NodeConnectionType.AiTool, displayName: 'AI Tool', required: true },
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: 'Main Input' },
|
||||
{ type: 'ai_tool', index: 0, label: 'AI Tool', required: true },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: 'Main Input' },
|
||||
{ type: NodeConnectionType.AiTool, index: 0, label: 'AI Tool', required: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should map mixed string and object endpoints correctly', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
'main',
|
||||
{ type: 'ai_tool', displayName: 'AI Tool' },
|
||||
'main',
|
||||
NodeConnectionType.Main,
|
||||
{ type: NodeConnectionType.AiTool, displayName: 'AI Tool' },
|
||||
NodeConnectionType.Main,
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: undefined },
|
||||
{ type: 'ai_tool', index: 0, label: 'AI Tool' },
|
||||
{ type: 'main', index: 1, label: undefined },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: undefined },
|
||||
{ type: NodeConnectionType.AiTool, index: 0, label: 'AI Tool' },
|
||||
{ type: NodeConnectionType.Main, index: 1, label: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle multiple same type object endpoints', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
{ type: 'main', displayName: 'Main Input' },
|
||||
{ type: 'main', displayName: 'Secondary Main Input' },
|
||||
{ type: NodeConnectionType.Main, displayName: 'Main Input' },
|
||||
{ type: NodeConnectionType.Main, displayName: 'Secondary Main Input' },
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: 'Main Input' },
|
||||
{ type: 'main', index: 1, label: 'Secondary Main Input' },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: 'Main Input' },
|
||||
{ type: NodeConnectionType.Main, index: 1, label: 'Secondary Main Input' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should map required and non-required endpoints correctly', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
{ type: 'main', displayName: 'Main Input', required: true },
|
||||
{ type: 'ai_tool', displayName: 'Optional Tool', required: false },
|
||||
{ type: NodeConnectionType.Main, displayName: 'Main Input', required: true },
|
||||
{ type: NodeConnectionType.AiTool, displayName: 'Optional Tool', required: false },
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: 'Main Input', required: true },
|
||||
{ type: 'ai_tool', index: 0, label: 'Optional Tool' },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: 'Main Input', required: true },
|
||||
{ type: NodeConnectionType.AiTool, index: 0, label: 'Optional Tool' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { faker } from '@faker-js/faker/locale/en';
|
||||
import type { ITemplatesWorkflowFull, IWorkflowTemplateNode } from '@/Interface';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
export const newWorkflowTemplateNode = ({
|
||||
type,
|
||||
@@ -26,6 +27,7 @@ export const fullShopifyTelegramTwitterTemplate = {
|
||||
workflow: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991cc1',
|
||||
name: 'Twitter',
|
||||
type: 'n8n-nodes-base.twitter',
|
||||
position: [720, -220],
|
||||
@@ -39,6 +41,7 @@ export const fullShopifyTelegramTwitterTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd3',
|
||||
name: 'Telegram',
|
||||
type: 'n8n-nodes-base.telegram',
|
||||
position: [720, -20],
|
||||
@@ -53,6 +56,7 @@ export const fullShopifyTelegramTwitterTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd2',
|
||||
name: 'product created',
|
||||
type: 'n8n-nodes-base.shopifyTrigger',
|
||||
position: [540, -110],
|
||||
@@ -72,12 +76,12 @@ export const fullShopifyTelegramTwitterTemplate = {
|
||||
[
|
||||
{
|
||||
node: 'Twitter',
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
node: 'Telegram',
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
@@ -195,6 +199,7 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
workflow: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f8389911f3',
|
||||
name: 'IMAP Email',
|
||||
type: 'n8n-nodes-base.emailReadImap',
|
||||
position: [240, 420],
|
||||
@@ -206,6 +211,7 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991gg2',
|
||||
name: 'Nextcloud',
|
||||
type: 'n8n-nodes-base.nextCloud',
|
||||
position: [940, 420],
|
||||
@@ -217,6 +223,7 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991ddh',
|
||||
name: 'Map each attachment',
|
||||
type: 'n8n-nodes-base.function',
|
||||
position: [620, 420],
|
||||
@@ -228,8 +235,12 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'IMAP Email': { main: [[{ node: 'Map each attachment', type: 'main', index: 0 }]] },
|
||||
'Map each attachment': { main: [[{ node: 'Nextcloud', type: 'main', index: 0 }]] },
|
||||
'IMAP Email': {
|
||||
main: [[{ node: 'Map each attachment', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
'Map each attachment': {
|
||||
main: [[{ node: 'Nextcloud', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
},
|
||||
},
|
||||
workflowInfo: {
|
||||
@@ -303,6 +314,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
workflow: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd1',
|
||||
name: 'Webhook',
|
||||
type: 'n8n-nodes-base.webhook',
|
||||
position: [375, 115],
|
||||
@@ -315,6 +327,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd9',
|
||||
name: 'Note1',
|
||||
type: 'n8n-nodes-base.stickyNote',
|
||||
position: [355, -25],
|
||||
@@ -327,6 +340,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd5',
|
||||
name: 'Respond to Webhook',
|
||||
type: 'n8n-nodes-base.respondToWebhook',
|
||||
position: [815, 115],
|
||||
@@ -339,6 +353,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991df1',
|
||||
name: 'Create URL string',
|
||||
type: 'n8n-nodes-base.set',
|
||||
position: [595, 115],
|
||||
@@ -358,6 +373,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dbb',
|
||||
name: 'Note3',
|
||||
type: 'n8n-nodes-base.stickyNote',
|
||||
position: [355, 275],
|
||||
@@ -371,8 +387,10 @@ export const fullCreateApiEndpointTemplate = {
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
Webhook: { main: [[{ node: 'Create URL string', type: 'main', index: 0 }]] },
|
||||
'Create URL string': { main: [[{ node: 'Respond to Webhook', type: 'main', index: 0 }]] },
|
||||
Webhook: { main: [[{ node: 'Create URL string', type: NodeConnectionType.Main, index: 0 }]] },
|
||||
'Create URL string': {
|
||||
main: [[{ node: 'Respond to Webhook', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
},
|
||||
},
|
||||
lastUpdatedBy: 1,
|
||||
|
||||
@@ -6,6 +6,8 @@ import type {
|
||||
} from 'n8n-workflow';
|
||||
import { nodeConnectionTypes } from 'n8n-workflow';
|
||||
import type { ICredentialsResponse, NewCredentialsModal } from '@/Interface';
|
||||
import type { jsPlumbDOMElement } from '@jsplumb/browser-ui';
|
||||
import type { Connection } from '@jsplumb/core';
|
||||
|
||||
/*
|
||||
Type guards used in editor-ui project
|
||||
@@ -46,10 +48,14 @@ export const isResourceMapperValue = (value: unknown): value is string | number
|
||||
return ['string', 'number', 'boolean'].includes(typeof value);
|
||||
};
|
||||
|
||||
export const isJSPlumbEndpointElement = (element: Node): element is HTMLElement => {
|
||||
export const isJSPlumbEndpointElement = (element: Node): element is jsPlumbDOMElement => {
|
||||
return 'jtk' in element && 'endpoint' in (element.jtk as object);
|
||||
};
|
||||
|
||||
export const isJSPlumbConnection = (connection: unknown): connection is Connection => {
|
||||
return connection !== null && typeof connection === 'object' && 'connector' in connection;
|
||||
};
|
||||
|
||||
export function isDateObject(date: unknown): date is Date {
|
||||
return (
|
||||
!!date && Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date as number)
|
||||
|
||||
Reference in New Issue
Block a user