mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
refactor: Add node IDs (#3788)
* update type * add id to new nodes * update paste/import behavior * update duplicate/copy * update duplicate workflow * update import functions + templates * add instance id on copy * on download add instance id * simplify for testing * update telemetry events * add ids to nodegraph * not if same instance * update spacing * fix tests * update tests * add uuid * fix tests update tests add uuid fix ts issue * fix telemetry event * update workflow import * update public api * add sqlit migration * on workflow update * add psql migration * add mysql migration * revert to title * fix telemetry bug * remove console log * remove migration logs * fix copy/paste bug * replace node index with node id * remove console log * address PR feedback * address comment * fix type issue * fix select * update schema * fix ts issue * update tel helpers * fix eslint issues
This commit is contained in:
@@ -17,6 +17,7 @@ import fs from 'fs';
|
|||||||
import glob from 'fast-glob';
|
import glob from 'fast-glob';
|
||||||
import { UserSettings } from 'n8n-core';
|
import { UserSettings } from 'n8n-core';
|
||||||
import { EntityManager, getConnection } from 'typeorm';
|
import { EntityManager, getConnection } from 'typeorm';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
import { getLogger } from '../../src/Logger';
|
import { getLogger } from '../../src/Logger';
|
||||||
import { Db, ICredentialsDb, IWorkflowToImport } from '../../src';
|
import { Db, ICredentialsDb, IWorkflowToImport } from '../../src';
|
||||||
import { SharedWorkflow } from '../../src/databases/entities/SharedWorkflow';
|
import { SharedWorkflow } from '../../src/databases/entities/SharedWorkflow';
|
||||||
@@ -129,6 +130,11 @@ export class ImportWorkflowsCommand extends Command {
|
|||||||
if (credentials.length > 0) {
|
if (credentials.length > 0) {
|
||||||
workflow.nodes.forEach((node: INode) => {
|
workflow.nodes.forEach((node: INode) => {
|
||||||
this.transformCredentials(node, credentials);
|
this.transformCredentials(node, credentials);
|
||||||
|
|
||||||
|
if (!node.id) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
node.id = uuid();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +163,11 @@ export class ImportWorkflowsCommand extends Command {
|
|||||||
if (credentials.length > 0) {
|
if (credentials.length > 0) {
|
||||||
workflow.nodes.forEach((node: INode) => {
|
workflow.nodes.forEach((node: INode) => {
|
||||||
this.transformCredentials(node, credentials);
|
this.transformCredentials(node, credentials);
|
||||||
|
|
||||||
|
if (!node.id) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
node.id = uuid();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -586,6 +586,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const node: INode = {
|
const node: INode = {
|
||||||
|
id: 'temp',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Temp-Node',
|
name: 'Temp-Node',
|
||||||
type: nodeType.description.name,
|
type: nodeType.description.name,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
properties:
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
example: 0f5532f9-36ba-4bef-86c7-30d607400b15
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
example: Jira
|
example: Jira
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import config = require('../../../../../config');
|
|||||||
import { WorkflowEntity } from '../../../../databases/entities/WorkflowEntity';
|
import { WorkflowEntity } from '../../../../databases/entities/WorkflowEntity';
|
||||||
import { InternalHooksManager } from '../../../../InternalHooksManager';
|
import { InternalHooksManager } from '../../../../InternalHooksManager';
|
||||||
import { externalHooks } from '../../../../Server';
|
import { externalHooks } from '../../../../Server';
|
||||||
import { replaceInvalidCredentials } from '../../../../WorkflowHelpers';
|
import { addNodeIds, replaceInvalidCredentials } from '../../../../WorkflowHelpers';
|
||||||
import { WorkflowRequest } from '../../../types';
|
import { WorkflowRequest } from '../../../types';
|
||||||
import { authorize, validCursor } from '../../shared/middlewares/global.middleware';
|
import { authorize, validCursor } from '../../shared/middlewares/global.middleware';
|
||||||
import { encodeNextCursor } from '../../shared/services/pagination.service';
|
import { encodeNextCursor } from '../../shared/services/pagination.service';
|
||||||
@@ -42,6 +42,8 @@ export = {
|
|||||||
|
|
||||||
await replaceInvalidCredentials(workflow);
|
await replaceInvalidCredentials(workflow);
|
||||||
|
|
||||||
|
addNodeIds(workflow);
|
||||||
|
|
||||||
const role = await getWorkflowOwnerRole();
|
const role = await getWorkflowOwnerRole();
|
||||||
|
|
||||||
const createdWorkflow = await createWorkflow(workflow, req.user, role);
|
const createdWorkflow = await createWorkflow(workflow, req.user, role);
|
||||||
@@ -186,6 +188,7 @@ export = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await replaceInvalidCredentials(updateData);
|
await replaceInvalidCredentials(updateData);
|
||||||
|
addNodeIds(updateData);
|
||||||
|
|
||||||
const workflowRunner = ActiveWorkflowRunner.getInstance();
|
const workflowRunner = ActiveWorkflowRunner.getInstance();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { FindManyOptions, In, UpdateResult } from 'typeorm';
|
import { FindManyOptions, In, UpdateResult } from 'typeorm';
|
||||||
import intersection from 'lodash.intersection';
|
import intersection from 'lodash.intersection';
|
||||||
import type { INode } from 'n8n-workflow';
|
import type { INode } from 'n8n-workflow';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import { Db } from '../../../..';
|
import { Db } from '../../../..';
|
||||||
import { User } from '../../../../databases/entities/User';
|
import { User } from '../../../../databases/entities/User';
|
||||||
@@ -133,6 +134,7 @@ export function hasStartNode(workflow: WorkflowEntity): boolean {
|
|||||||
|
|
||||||
export function getStartNode(): INode {
|
export function getStartNode(): INode {
|
||||||
return {
|
return {
|
||||||
|
id: uuid(),
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
|
|||||||
@@ -924,6 +924,8 @@ class App {
|
|||||||
// check credentials for old format
|
// check credentials for old format
|
||||||
await WorkflowHelpers.replaceInvalidCredentials(updateData);
|
await WorkflowHelpers.replaceInvalidCredentials(updateData);
|
||||||
|
|
||||||
|
WorkflowHelpers.addNodeIds(updateData);
|
||||||
|
|
||||||
await this.externalHooks.run('workflow.update', [updateData]);
|
await this.externalHooks.run('workflow.update', [updateData]);
|
||||||
|
|
||||||
if (shared.workflow.active) {
|
if (shared.workflow.active) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
LoggerProxy as Logger,
|
LoggerProxy as Logger,
|
||||||
Workflow,
|
Workflow,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
import {
|
import {
|
||||||
CredentialTypes,
|
CredentialTypes,
|
||||||
@@ -474,6 +475,22 @@ export async function getStaticDataById(workflowId: string | number) {
|
|||||||
return workflowData.staticData || {};
|
return workflowData.staticData || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set node ids if not already set
|
||||||
|
*
|
||||||
|
* @param workflow
|
||||||
|
*/
|
||||||
|
export function addNodeIds(workflow: WorkflowEntity) {
|
||||||
|
const { nodes } = workflow;
|
||||||
|
if (!nodes) return;
|
||||||
|
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
if (!node.id) {
|
||||||
|
node.id = uuid();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Checking if credentials of old format are in use and run a DB check if they might exist uniquely
|
// Checking if credentials of old format are in use and run a DB check if they might exist uniquely
|
||||||
export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promise<WorkflowEntity> {
|
export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promise<WorkflowEntity> {
|
||||||
const { nodes } = workflow;
|
const { nodes } = workflow;
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ workflowsController.post(
|
|||||||
|
|
||||||
await WorkflowHelpers.replaceInvalidCredentials(newWorkflow);
|
await WorkflowHelpers.replaceInvalidCredentials(newWorkflow);
|
||||||
|
|
||||||
|
WorkflowHelpers.addNodeIds(newWorkflow);
|
||||||
|
|
||||||
let savedWorkflow: undefined | WorkflowEntity;
|
let savedWorkflow: undefined | WorkflowEntity;
|
||||||
|
|
||||||
await Db.transaction(async (transactionManager) => {
|
await Db.transaction(async (transactionManager) => {
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
import * as config from '../../../../config';
|
||||||
|
import { runChunked } from '../../utils/migrationHelpers';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
// add node ids in workflow objects
|
||||||
|
|
||||||
|
export class AddNodeIds1658932910559 implements MigrationInterface {
|
||||||
|
name = 'AddNodeIds1658932910559';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
const tablePrefix = config.getEnv('database.tablePrefix');
|
||||||
|
|
||||||
|
const workflowsQuery = `
|
||||||
|
SELECT id, nodes
|
||||||
|
FROM ${tablePrefix}workflow_entity
|
||||||
|
`;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await runChunked(queryRunner, workflowsQuery, (workflows) => {
|
||||||
|
workflows.forEach(async (workflow) => {
|
||||||
|
const nodes = workflow.nodes;
|
||||||
|
// @ts-ignore
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
if (!node.id) {
|
||||||
|
node.id = uuid();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [updateQuery, updateParams] =
|
||||||
|
queryRunner.connection.driver.escapeQueryWithParameters(
|
||||||
|
`
|
||||||
|
UPDATE ${tablePrefix}workflow_entity
|
||||||
|
SET nodes = :nodes
|
||||||
|
WHERE id = '${workflow.id}'
|
||||||
|
`,
|
||||||
|
{ nodes: JSON.stringify(nodes) },
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
queryRunner.query(updateQuery, updateParams);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
const tablePrefix = config.getEnv('database.tablePrefix');
|
||||||
|
|
||||||
|
const workflowsQuery = `
|
||||||
|
SELECT id, nodes
|
||||||
|
FROM ${tablePrefix}workflow_entity
|
||||||
|
`;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await runChunked(queryRunner, workflowsQuery, (workflows) => {
|
||||||
|
workflows.forEach(async (workflow) => {
|
||||||
|
const nodes = workflow.nodes;
|
||||||
|
// @ts-ignore
|
||||||
|
nodes.forEach((node) => delete node.id );
|
||||||
|
|
||||||
|
const [updateQuery, updateParams] =
|
||||||
|
queryRunner.connection.driver.escapeQueryWithParameters(
|
||||||
|
`
|
||||||
|
UPDATE ${tablePrefix}workflow_entity
|
||||||
|
SET nodes = :nodes
|
||||||
|
WHERE id = '${workflow.id}'
|
||||||
|
`,
|
||||||
|
{ nodes: JSON.stringify(nodes) },
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
queryRunner.query(updateQuery, updateParams);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings';
|
|||||||
import { CommunityNodes1652254514003 } from './1652254514003-CommunityNodes';
|
import { CommunityNodes1652254514003 } from './1652254514003-CommunityNodes';
|
||||||
import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn';
|
import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn';
|
||||||
import { IntroducePinData1654090101303 } from './1654090101303-IntroducePinData';
|
import { IntroducePinData1654090101303 } from './1654090101303-IntroducePinData';
|
||||||
|
import { AddNodeIds1658932910559 } from './1658932910559-AddNodeIds';
|
||||||
|
|
||||||
export const mysqlMigrations = [
|
export const mysqlMigrations = [
|
||||||
InitialMigration1588157391238,
|
InitialMigration1588157391238,
|
||||||
@@ -38,4 +39,5 @@ export const mysqlMigrations = [
|
|||||||
CommunityNodes1652254514003,
|
CommunityNodes1652254514003,
|
||||||
AddAPIKeyColumn1652905585850,
|
AddAPIKeyColumn1652905585850,
|
||||||
IntroducePinData1654090101303,
|
IntroducePinData1654090101303,
|
||||||
|
AddNodeIds1658932910559,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
import * as config from '../../../../config';
|
||||||
|
import { runChunked } from '../../utils/migrationHelpers';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
// add node ids in workflow objects
|
||||||
|
|
||||||
|
export class AddNodeIds1658932090381 implements MigrationInterface {
|
||||||
|
name = 'AddNodeIds1658932090381';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
let tablePrefix = config.getEnv('database.tablePrefix');
|
||||||
|
const schema = config.getEnv('database.postgresdb.schema');
|
||||||
|
if (schema) {
|
||||||
|
tablePrefix = schema + '.' + tablePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
await queryRunner.query(`SET search_path TO ${schema};`);
|
||||||
|
|
||||||
|
const workflowsQuery = `
|
||||||
|
SELECT id, nodes
|
||||||
|
FROM ${tablePrefix}workflow_entity
|
||||||
|
`;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await runChunked(queryRunner, workflowsQuery, (workflows) => {
|
||||||
|
workflows.forEach(async (workflow) => {
|
||||||
|
const nodes = workflow.nodes;
|
||||||
|
// @ts-ignore
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
if (!node.id) {
|
||||||
|
node.id = uuid();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [updateQuery, updateParams] =
|
||||||
|
queryRunner.connection.driver.escapeQueryWithParameters(
|
||||||
|
`
|
||||||
|
UPDATE ${tablePrefix}workflow_entity
|
||||||
|
SET nodes = :nodes
|
||||||
|
WHERE id = '${workflow.id}'
|
||||||
|
`,
|
||||||
|
{ nodes: JSON.stringify(nodes) },
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
queryRunner.query(updateQuery, updateParams);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
let tablePrefix = config.getEnv('database.tablePrefix');
|
||||||
|
const schema = config.getEnv('database.postgresdb.schema');
|
||||||
|
if (schema) {
|
||||||
|
tablePrefix = schema + '.' + tablePrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
await queryRunner.query(`SET search_path TO ${schema};`);
|
||||||
|
|
||||||
|
const workflowsQuery = `
|
||||||
|
SELECT id, nodes
|
||||||
|
FROM ${tablePrefix}workflow_entity
|
||||||
|
`;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await runChunked(queryRunner, workflowsQuery, (workflows) => {
|
||||||
|
workflows.forEach(async (workflow) => {
|
||||||
|
const nodes = workflow.nodes;
|
||||||
|
// @ts-ignore
|
||||||
|
nodes.forEach((node) => delete node.id );
|
||||||
|
|
||||||
|
const [updateQuery, updateParams] =
|
||||||
|
queryRunner.connection.driver.escapeQueryWithParameters(
|
||||||
|
`
|
||||||
|
UPDATE ${tablePrefix}workflow_entity
|
||||||
|
SET nodes = :nodes
|
||||||
|
WHERE id = '${workflow.id}'
|
||||||
|
`,
|
||||||
|
{ nodes: JSON.stringify(nodes) },
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
queryRunner.query(updateQuery, updateParams);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings';
|
|||||||
import { CommunityNodes1652254514002 } from './1652254514002-CommunityNodes';
|
import { CommunityNodes1652254514002 } from './1652254514002-CommunityNodes';
|
||||||
import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn';
|
import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn';
|
||||||
import { IntroducePinData1654090467022 } from './1654090467022-IntroducePinData';
|
import { IntroducePinData1654090467022 } from './1654090467022-IntroducePinData';
|
||||||
|
import { AddNodeIds1658932090381 } from './1658932090381-AddNodeIds';
|
||||||
|
|
||||||
export const postgresMigrations = [
|
export const postgresMigrations = [
|
||||||
InitialMigration1587669153312,
|
InitialMigration1587669153312,
|
||||||
@@ -34,4 +35,5 @@ export const postgresMigrations = [
|
|||||||
CommunityNodes1652254514002,
|
CommunityNodes1652254514002,
|
||||||
AddAPIKeyColumn1652905585850,
|
AddAPIKeyColumn1652905585850,
|
||||||
IntroducePinData1654090467022,
|
IntroducePinData1654090467022,
|
||||||
|
AddNodeIds1658932090381,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { INode } from 'n8n-workflow';
|
||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
import * as config from '../../../../config';
|
||||||
|
import { logMigrationEnd, logMigrationStart } from '../../utils/migrationHelpers';
|
||||||
|
import { runChunked } from '../../utils/migrationHelpers';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
|
// add node ids in workflow objects
|
||||||
|
|
||||||
|
export class AddNodeIds1658930531669 implements MigrationInterface {
|
||||||
|
name = 'AddNodeIds1658930531669';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
logMigrationStart(this.name);
|
||||||
|
|
||||||
|
const tablePrefix = config.getEnv('database.tablePrefix');
|
||||||
|
|
||||||
|
const workflowsQuery = `
|
||||||
|
SELECT id, nodes
|
||||||
|
FROM "${tablePrefix}workflow_entity"
|
||||||
|
`;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await runChunked(queryRunner, workflowsQuery, (workflows) => {
|
||||||
|
workflows.forEach(async (workflow) => {
|
||||||
|
const nodes = JSON.parse(workflow.nodes);
|
||||||
|
nodes.forEach((node: INode) => {
|
||||||
|
if (!node.id) {
|
||||||
|
node.id = uuid();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [updateQuery, updateParams] =
|
||||||
|
queryRunner.connection.driver.escapeQueryWithParameters(
|
||||||
|
`
|
||||||
|
UPDATE "${tablePrefix}workflow_entity"
|
||||||
|
SET nodes = :nodes
|
||||||
|
WHERE id = '${workflow.id}'
|
||||||
|
`,
|
||||||
|
{ nodes: JSON.stringify(nodes) },
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
queryRunner.query(updateQuery, updateParams);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
logMigrationEnd(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
const tablePrefix = config.getEnv('database.tablePrefix');
|
||||||
|
|
||||||
|
const workflowsQuery = `
|
||||||
|
SELECT id, nodes
|
||||||
|
FROM "${tablePrefix}workflow_entity"
|
||||||
|
`;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
await runChunked(queryRunner, workflowsQuery, (workflows) => {
|
||||||
|
workflows.forEach(async (workflow) => {
|
||||||
|
const nodes = JSON.parse(workflow.nodes);
|
||||||
|
// @ts-ignore
|
||||||
|
nodes.forEach((node) => delete node.id );
|
||||||
|
|
||||||
|
const [updateQuery, updateParams] =
|
||||||
|
queryRunner.connection.driver.escapeQueryWithParameters(
|
||||||
|
`
|
||||||
|
UPDATE "${tablePrefix}workflow_entity"
|
||||||
|
SET nodes = :nodes
|
||||||
|
WHERE id = '${workflow.id}'
|
||||||
|
`,
|
||||||
|
{ nodes: JSON.stringify(nodes) },
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
queryRunner.query(updateQuery, updateParams);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings';
|
|||||||
import { CommunityNodes1652254514001 } from './1652254514001-CommunityNodes'
|
import { CommunityNodes1652254514001 } from './1652254514001-CommunityNodes'
|
||||||
import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn';
|
import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn';
|
||||||
import { IntroducePinData1654089251344 } from './1654089251344-IntroducePinData';
|
import { IntroducePinData1654089251344 } from './1654089251344-IntroducePinData';
|
||||||
|
import { AddNodeIds1658930531669 } from './1658930531669-AddNodeIds';
|
||||||
|
|
||||||
const sqliteMigrations = [
|
const sqliteMigrations = [
|
||||||
InitialMigration1588102412422,
|
InitialMigration1588102412422,
|
||||||
@@ -32,6 +33,7 @@ const sqliteMigrations = [
|
|||||||
CommunityNodes1652254514001,
|
CommunityNodes1652254514001,
|
||||||
AddAPIKeyColumn1652905585850,
|
AddAPIKeyColumn1652905585850,
|
||||||
IntroducePinData1654089251344,
|
IntroducePinData1654089251344,
|
||||||
|
AddNodeIds1658930531669,
|
||||||
];
|
];
|
||||||
|
|
||||||
export { sqliteMigrations };
|
export { sqliteMigrations };
|
||||||
|
|||||||
@@ -951,6 +951,7 @@ test('POST /workflows should create workflow', async () => {
|
|||||||
name: 'testing',
|
name: 'testing',
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1234',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -1047,6 +1048,7 @@ test('PUT /workflows/:id should fail due to non-existing workflow', async () =>
|
|||||||
name: 'testing',
|
name: 'testing',
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1234',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -1082,6 +1084,7 @@ test('PUT /workflows/:id should fail due to invalid body', async () => {
|
|||||||
const response = await authOwnerAgent.put(`/workflows/1`).send({
|
const response = await authOwnerAgent.put(`/workflows/1`).send({
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1234',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -1120,6 +1123,7 @@ test('PUT /workflows/:id should update workflow', async () => {
|
|||||||
name: 'name updated',
|
name: 'name updated',
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1234',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -1127,6 +1131,7 @@ test('PUT /workflows/:id should update workflow', async () => {
|
|||||||
position: [240, 300],
|
position: [240, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1234',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Cron',
|
name: 'Cron',
|
||||||
type: 'n8n-nodes-base.cron',
|
type: 'n8n-nodes-base.cron',
|
||||||
@@ -1195,6 +1200,7 @@ test('PUT /workflows/:id should update non-owned workflow if owner', async () =>
|
|||||||
name: 'name owner updated',
|
name: 'name owner updated',
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -1202,6 +1208,7 @@ test('PUT /workflows/:id should update non-owned workflow if owner', async () =>
|
|||||||
position: [240, 300],
|
position: [240, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Cron',
|
name: 'Cron',
|
||||||
type: 'n8n-nodes-base.cron',
|
type: 'n8n-nodes-base.cron',
|
||||||
|
|||||||
@@ -522,6 +522,7 @@ export async function createWorkflow(attributes: Partial<WorkflowEntity> = {}, u
|
|||||||
name: name ?? 'test workflow',
|
name: name ?? 'test workflow',
|
||||||
nodes: nodes ?? [
|
nodes: nodes ?? [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1234',
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
position: [-20, 260],
|
position: [-20, 260],
|
||||||
@@ -555,6 +556,7 @@ export async function createWorkflowWithTrigger(
|
|||||||
{
|
{
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -562,6 +564,7 @@ export async function createWorkflowWithTrigger(
|
|||||||
position: [240, 300],
|
position: [240, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: { triggerTimes: { item: [{ mode: 'everyMinute' }] } },
|
parameters: { triggerTimes: { item: [{ mode: 'everyMinute' }] } },
|
||||||
name: 'Cron',
|
name: 'Cron',
|
||||||
type: 'n8n-nodes-base.cron',
|
type: 'n8n-nodes-base.cron',
|
||||||
@@ -569,6 +572,7 @@ export async function createWorkflowWithTrigger(
|
|||||||
position: [500, 300],
|
position: [500, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-3',
|
||||||
parameters: { options: {} },
|
parameters: { options: {} },
|
||||||
name: 'Set',
|
name: 'Set',
|
||||||
type: 'n8n-nodes-base.set',
|
type: 'n8n-nodes-base.set',
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ function makeWorkflow({ withPinData }: { withPinData: boolean }) {
|
|||||||
workflow.connections = {};
|
workflow.connections = {};
|
||||||
workflow.nodes = [
|
workflow.nodes = [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1234',
|
||||||
name: 'Spotify',
|
name: 'Spotify',
|
||||||
type: 'n8n-nodes-base.spotify',
|
type: 'n8n-nodes-base.spotify',
|
||||||
parameters: { resource: 'track', operation: 'get', id: '123' },
|
parameters: { resource: 'track', operation: 'get', id: '123' },
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ describe('CredentialsHelper', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const node: INode = {
|
const node: INode = {
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'test',
|
name: 'test',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export class LoadNodeParameterOptions {
|
|||||||
|
|
||||||
const nodeData: INode = {
|
const nodeData: INode = {
|
||||||
parameters: currentNodeParameters,
|
parameters: currentNodeParameters,
|
||||||
|
id: 'uuid-1234',
|
||||||
name: TEMP_NODE_NAME,
|
name: TEMP_NODE_NAME,
|
||||||
type: nodeTypeNameAndVersion.name,
|
type: nodeTypeNameAndVersion.name,
|
||||||
typeVersion: nodeTypeNameAndVersion.version,
|
typeVersion: nodeTypeNameAndVersion.version,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ describe('WorkflowExecute', () => {
|
|||||||
workflowData: {
|
workflowData: {
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -44,6 +45,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [100, 300],
|
position: [100, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -96,6 +98,7 @@ describe('WorkflowExecute', () => {
|
|||||||
workflowData: {
|
workflowData: {
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -103,6 +106,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [100, 300],
|
position: [100, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -119,6 +123,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [300, 250],
|
position: [300, 250],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-3',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -200,6 +205,7 @@ describe('WorkflowExecute', () => {
|
|||||||
workflowData: {
|
workflowData: {
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {
|
parameters: {
|
||||||
mode: 'passThrough',
|
mode: 'passThrough',
|
||||||
},
|
},
|
||||||
@@ -209,6 +215,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [1150, 500],
|
position: [1150, 500],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -225,6 +232,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [290, 400],
|
position: [290, 400],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-3',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -241,6 +249,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [850, 200],
|
position: [850, 200],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-4',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -257,6 +266,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [650, 200],
|
position: [650, 200],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-5',
|
||||||
parameters: {
|
parameters: {
|
||||||
mode: 'passThrough',
|
mode: 'passThrough',
|
||||||
},
|
},
|
||||||
@@ -266,6 +276,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [1150, 500],
|
position: [1150, 500],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-6',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Merge3',
|
name: 'Merge3',
|
||||||
type: 'n8n-nodes-base.merge',
|
type: 'n8n-nodes-base.merge',
|
||||||
@@ -273,6 +284,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [1000, 400],
|
position: [1000, 400],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-7',
|
||||||
parameters: {
|
parameters: {
|
||||||
mode: 'passThrough',
|
mode: 'passThrough',
|
||||||
output: 'input2',
|
output: 'input2',
|
||||||
@@ -283,6 +295,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [700, 400],
|
position: [700, 400],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-8',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Merge1',
|
name: 'Merge1',
|
||||||
type: 'n8n-nodes-base.merge',
|
type: 'n8n-nodes-base.merge',
|
||||||
@@ -290,6 +303,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [500, 300],
|
position: [500, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-9',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -306,6 +320,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [300, 200],
|
position: [300, 200],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-10',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -526,6 +541,7 @@ describe('WorkflowExecute', () => {
|
|||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1',
|
||||||
position: [250, 450],
|
position: [250, 450],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -543,6 +559,7 @@ describe('WorkflowExecute', () => {
|
|||||||
name: 'IF',
|
name: 'IF',
|
||||||
type: 'n8n-nodes-base.if',
|
type: 'n8n-nodes-base.if',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-2',
|
||||||
position: [650, 350],
|
position: [650, 350],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -550,6 +567,7 @@ describe('WorkflowExecute', () => {
|
|||||||
name: 'Merge1',
|
name: 'Merge1',
|
||||||
type: 'n8n-nodes-base.merge',
|
type: 'n8n-nodes-base.merge',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-3',
|
||||||
position: [1150, 450],
|
position: [1150, 450],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -567,6 +585,7 @@ describe('WorkflowExecute', () => {
|
|||||||
name: 'Set1',
|
name: 'Set1',
|
||||||
type: 'n8n-nodes-base.set',
|
type: 'n8n-nodes-base.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-4',
|
||||||
position: [450, 450],
|
position: [450, 450],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -584,6 +603,7 @@ describe('WorkflowExecute', () => {
|
|||||||
name: 'Set2',
|
name: 'Set2',
|
||||||
type: 'n8n-nodes-base.set',
|
type: 'n8n-nodes-base.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1',
|
||||||
position: [800, 250],
|
position: [800, 250],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -672,6 +692,7 @@ describe('WorkflowExecute', () => {
|
|||||||
workflowData: {
|
workflowData: {
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -679,6 +700,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [250, 300],
|
position: [250, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Merge',
|
name: 'Merge',
|
||||||
type: 'n8n-nodes-base.merge',
|
type: 'n8n-nodes-base.merge',
|
||||||
@@ -686,6 +708,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [800, 450],
|
position: [800, 450],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-3',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Merge1',
|
name: 'Merge1',
|
||||||
type: 'n8n-nodes-base.merge',
|
type: 'n8n-nodes-base.merge',
|
||||||
@@ -693,6 +716,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [1000, 300],
|
position: [1000, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-4',
|
||||||
parameters: {
|
parameters: {
|
||||||
conditions: {
|
conditions: {
|
||||||
boolean: [
|
boolean: [
|
||||||
@@ -716,6 +740,7 @@ describe('WorkflowExecute', () => {
|
|||||||
alwaysOutputData: false,
|
alwaysOutputData: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-5',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -738,6 +763,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [450, 300],
|
position: [450, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-6',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -761,6 +787,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [450, 450],
|
position: [450, 450],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-7',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -889,6 +916,7 @@ describe('WorkflowExecute', () => {
|
|||||||
workflowData: {
|
workflowData: {
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -896,6 +924,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [250, 300],
|
position: [250, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: {
|
parameters: {
|
||||||
conditions: {
|
conditions: {
|
||||||
number: [
|
number: [
|
||||||
@@ -913,6 +942,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [650, 300],
|
position: [650, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-3',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
string: [],
|
string: [],
|
||||||
@@ -931,6 +961,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [850, 450],
|
position: [850, 450],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-4',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -948,6 +979,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [450, 300],
|
position: [450, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-5',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Merge',
|
name: 'Merge',
|
||||||
type: 'n8n-nodes-base.merge',
|
type: 'n8n-nodes-base.merge',
|
||||||
@@ -1034,6 +1066,7 @@ describe('WorkflowExecute', () => {
|
|||||||
workflowData: {
|
workflowData: {
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -1041,6 +1074,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [250, 300],
|
position: [250, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: {
|
parameters: {
|
||||||
values: {
|
values: {
|
||||||
number: [
|
number: [
|
||||||
@@ -1057,6 +1091,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [450, 300],
|
position: [450, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-3',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Merge',
|
name: 'Merge',
|
||||||
type: 'n8n-nodes-base.merge',
|
type: 'n8n-nodes-base.merge',
|
||||||
@@ -1064,6 +1099,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [1050, 250],
|
position: [1050, 250],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-4',
|
||||||
parameters: {
|
parameters: {
|
||||||
conditions: {
|
conditions: {
|
||||||
number: [
|
number: [
|
||||||
@@ -1081,6 +1117,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [650, 300],
|
position: [650, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-5',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'NoOpTrue',
|
name: 'NoOpTrue',
|
||||||
type: 'n8n-nodes-base.noOp',
|
type: 'n8n-nodes-base.noOp',
|
||||||
@@ -1088,6 +1125,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [850, 150],
|
position: [850, 150],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-6',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'NoOpFalse',
|
name: 'NoOpFalse',
|
||||||
type: 'n8n-nodes-base.noOp',
|
type: 'n8n-nodes-base.noOp',
|
||||||
@@ -1177,6 +1215,7 @@ describe('WorkflowExecute', () => {
|
|||||||
workflowData: {
|
workflowData: {
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'n8n-nodes-base.start',
|
type: 'n8n-nodes-base.start',
|
||||||
@@ -1184,6 +1223,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [240, 300],
|
position: [240, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-2',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'VersionTest1a',
|
name: 'VersionTest1a',
|
||||||
type: 'n8n-nodes-base.versionTest',
|
type: 'n8n-nodes-base.versionTest',
|
||||||
@@ -1191,6 +1231,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [460, 300],
|
position: [460, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-3',
|
||||||
parameters: {
|
parameters: {
|
||||||
versionTest: 11,
|
versionTest: 11,
|
||||||
},
|
},
|
||||||
@@ -1200,6 +1241,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [680, 300],
|
position: [680, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-4',
|
||||||
parameters: {},
|
parameters: {},
|
||||||
name: 'VersionTest2a',
|
name: 'VersionTest2a',
|
||||||
type: 'n8n-nodes-base.versionTest',
|
type: 'n8n-nodes-base.versionTest',
|
||||||
@@ -1207,6 +1249,7 @@ describe('WorkflowExecute', () => {
|
|||||||
position: [880, 300],
|
position: [880, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'uuid-5',
|
||||||
parameters: {
|
parameters: {
|
||||||
versionTest: 22,
|
versionTest: 22,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -259,6 +259,12 @@ export interface IWorkflowDataUpdate {
|
|||||||
pinData?: IPinData;
|
pinData?: IPinData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IWorkflowToShare extends IWorkflowDataUpdate {
|
||||||
|
meta?: {
|
||||||
|
instanceId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface IWorkflowTemplate {
|
export interface IWorkflowTemplate {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -866,7 +872,6 @@ export interface IRootState {
|
|||||||
workflowExecutionData: IExecutionResponse | null;
|
workflowExecutionData: IExecutionResponse | null;
|
||||||
lastSelectedNode: string | null;
|
lastSelectedNode: string | null;
|
||||||
lastSelectedNodeOutputIndex: number | null;
|
lastSelectedNodeOutputIndex: number | null;
|
||||||
nodeIndex: Array<string | null>;
|
|
||||||
nodeViewOffsetPosition: XYPosition;
|
nodeViewOffsetPosition: XYPosition;
|
||||||
nodeViewMoveInProgress: boolean;
|
nodeViewMoveInProgress: boolean;
|
||||||
selectedNodes: INodeUi[];
|
selectedNodes: INodeUi[];
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export default mixins(showMessage, workflowHelpers).extend({
|
|||||||
|
|
||||||
this.$data.isSaving = true;
|
this.$data.isSaving = true;
|
||||||
|
|
||||||
const saved = await this.saveAsNewWorkflow({name, tags: this.currentTagIds, resetWebhookUrls: true, openInNewWindow: true});
|
const saved = await this.saveAsNewWorkflow({name, tags: this.currentTagIds, resetWebhookUrls: true, openInNewWindow: true, resetNodeIds: true});
|
||||||
|
|
||||||
if (saved) {
|
if (saved) {
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ import {
|
|||||||
IExecutionResponse,
|
IExecutionResponse,
|
||||||
IWorkflowDataUpdate,
|
IWorkflowDataUpdate,
|
||||||
IMenuItem,
|
IMenuItem,
|
||||||
IUser,
|
IWorkflowToShare,
|
||||||
} from '../Interface';
|
} from '../Interface';
|
||||||
|
|
||||||
import ExecutionsList from '@/components/ExecutionsList.vue';
|
import ExecutionsList from '@/components/ExecutionsList.vue';
|
||||||
@@ -442,7 +442,6 @@ export default mixins(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$telemetry.track('User imported workflow', { source: 'file', workflow_id: this.$store.getters.workflowId });
|
|
||||||
this.$root.$emit('importWorkflowData', { data: worflowData });
|
this.$root.$emit('importWorkflowData', { data: worflowData });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -513,8 +512,11 @@ export default mixins(
|
|||||||
data.id = parseInt(data.id, 10);
|
data.id = parseInt(data.id, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportData: IWorkflowDataUpdate = {
|
const exportData: IWorkflowToShare = {
|
||||||
...data,
|
...data,
|
||||||
|
meta: {
|
||||||
|
instanceId: this.$store.getters.instanceId,
|
||||||
|
},
|
||||||
tags: (tags || []).map(tagId => {
|
tags: (tags || []).map(tagId => {
|
||||||
const {usageCount, ...tag} = this.$store.getters["tags/getTagById"](tagId);
|
const {usageCount, ...tag} = this.$store.getters["tags/getTagById"](tagId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="node-wrapper" :style="nodePosition">
|
<div class="node-wrapper" :style="nodePosition" :id="nodeId">
|
||||||
<div class="select-background" v-show="isSelected"></div>
|
<div class="select-background" v-show="isSelected"></div>
|
||||||
<div :class="{'node-default': true, 'touch-active': isTouchActive, 'is-touch-device': isTouchDevice}" :data-name="data.name" :ref="data.name">
|
<div :class="{'node-default': true, 'touch-active': isTouchActive, 'is-touch-device': isTouchDevice}" :data-name="data.name" :ref="data.name">
|
||||||
<div :class="nodeClass" :style="nodeStyle" @dblclick="setNodeActive" @click.left="mouseLeftClick" v-touch:start="touchStart" v-touch:end="touchEnd">
|
<div :class="nodeClass" :style="nodeStyle" @dblclick="setNodeActive" @click.left="mouseLeftClick" v-touch:start="touchStart" v-touch:end="touchEnd">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sticky-wrapper" :style="stickyPosition">
|
<div class="sticky-wrapper" :style="stickyPosition" :id="nodeId">
|
||||||
<div
|
<div
|
||||||
:class="{'sticky-default': true, 'touch-active': isTouchActive, 'is-touch-device': isTouchDevice}"
|
:class="{'sticky-default': true, 'touch-active': isTouchActive, 'is-touch-device': isTouchDevice}"
|
||||||
:style="stickySize"
|
:style="stickySize"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
:height="node.parameters.height"
|
:height="node.parameters.height"
|
||||||
:width="node.parameters.width"
|
:width="node.parameters.width"
|
||||||
:scale="nodeViewScale"
|
:scale="nodeViewScale"
|
||||||
:id="nodeIndex"
|
:id="node.id"
|
||||||
:readOnly="isReadOnly"
|
:readOnly="isReadOnly"
|
||||||
:defaultText="defaultText"
|
:defaultText="defaultText"
|
||||||
:editMode="isActive && !isReadOnly"
|
:editMode="isActive && !isReadOnly"
|
||||||
@@ -165,9 +165,9 @@ export default mixins(externalHooks, nodeBase, nodeHelpers, workflowHelpers).ext
|
|||||||
if (!this.isSelected && this.node) {
|
if (!this.isSelected && this.node) {
|
||||||
this.$emit('nodeSelected', this.node.name, false, true);
|
this.$emit('nodeSelected', this.node.name, false, true);
|
||||||
}
|
}
|
||||||
const nodeIndex = this.$store.getters.getNodeIndex(this.data.name);
|
if (this.node) {
|
||||||
const nodeIdName = `node-${nodeIndex}`;
|
this.instance.destroyDraggable(this.node.id); // todo avoid destroying if possible
|
||||||
this.instance.destroyDraggable(nodeIdName); // todo
|
}
|
||||||
},
|
},
|
||||||
onResize({height, width, dX, dY}: { width: number, height: number, dX: number, dY: number }) {
|
onResize({height, width, dX, dY}: { width: number, height: number, dX: number, dY: number }) {
|
||||||
if (!this.node) {
|
if (!this.node) {
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ import { INodeUi, XYPosition } from '@/Interface';
|
|||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
||||||
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
|
||||||
import { getMousePosition, getRelativePosition } from '@/views/canvasHelpers';
|
import { getMousePosition, getRelativePosition } from '@/views/canvasHelpers';
|
||||||
|
|
||||||
export const mouseSelect = mixins(
|
export const mouseSelect = mixins(
|
||||||
deviceSupportHelpers,
|
deviceSupportHelpers,
|
||||||
nodeIndex,
|
|
||||||
).extend({
|
).extend({
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -171,18 +169,15 @@ export const mouseSelect = mixins(
|
|||||||
|
|
||||||
this.updateSelectBox(e);
|
this.updateSelectBox(e);
|
||||||
},
|
},
|
||||||
|
|
||||||
nodeDeselected (node: INodeUi) {
|
nodeDeselected (node: INodeUi) {
|
||||||
this.$store.commit('removeNodeFromSelection', node);
|
this.$store.commit('removeNodeFromSelection', node);
|
||||||
const nodeElement = `node-${this.getNodeIndex(node.name)}`;
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.instance.removeFromDragSelection(nodeElement);
|
this.instance.removeFromDragSelection(node.id);
|
||||||
},
|
},
|
||||||
nodeSelected (node: INodeUi) {
|
nodeSelected (node: INodeUi) {
|
||||||
this.$store.commit('addSelectedNode', node);
|
this.$store.commit('addSelectedNode', node);
|
||||||
const nodeElement = `node-${this.getNodeIndex(node.name)}`;
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.instance.addToDragSelection(nodeElement);
|
this.instance.addToDragSelection(node.id);
|
||||||
},
|
},
|
||||||
deselectAllNodes () {
|
deselectAllNodes () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import mixins from 'vue-typed-mixins';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import normalizeWheel from 'normalize-wheel';
|
import normalizeWheel from 'normalize-wheel';
|
||||||
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
||||||
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
|
||||||
import { getMousePosition } from '@/views/canvasHelpers';
|
import { getMousePosition } from '@/views/canvasHelpers';
|
||||||
|
|
||||||
export const moveNodeWorkflow = mixins(
|
export const moveNodeWorkflow = mixins(
|
||||||
deviceSupportHelpers,
|
deviceSupportHelpers,
|
||||||
nodeIndex,
|
|
||||||
).extend({
|
).extend({
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import { IEndpointOptions, INodeUi, XYPosition } from '@/Interface';
|
|||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
|
|
||||||
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
import { deviceSupportHelpers } from '@/components/mixins/deviceSupportHelpers';
|
||||||
import { nodeIndex } from '@/components/mixins/nodeIndex';
|
import { NO_OP_NODE_TYPE, STICKY_NODE_TYPE } from '@/constants';
|
||||||
import { NODE_NAME_PREFIX, NO_OP_NODE_TYPE, STICKY_NODE_TYPE } from '@/constants';
|
|
||||||
import * as CanvasHelpers from '@/views/canvasHelpers';
|
import * as CanvasHelpers from '@/views/canvasHelpers';
|
||||||
import { Endpoint } from 'jsplumb';
|
import { Endpoint } from 'jsplumb';
|
||||||
|
|
||||||
@@ -15,7 +14,6 @@ import { getStyleTokenValue } from '../helpers';
|
|||||||
|
|
||||||
export const nodeBase = mixins(
|
export const nodeBase = mixins(
|
||||||
deviceSupportHelpers,
|
deviceSupportHelpers,
|
||||||
nodeIndex,
|
|
||||||
).extend({
|
).extend({
|
||||||
mounted () {
|
mounted () {
|
||||||
// Initialize the node
|
// Initialize the node
|
||||||
@@ -28,10 +26,7 @@ export const nodeBase = mixins(
|
|||||||
return this.$store.getters.getNodeByName(this.name);
|
return this.$store.getters.getNodeByName(this.name);
|
||||||
},
|
},
|
||||||
nodeId (): string {
|
nodeId (): string {
|
||||||
return NODE_NAME_PREFIX + this.nodeIndex;
|
return this.data.id;
|
||||||
},
|
|
||||||
nodeIndex (): string {
|
|
||||||
return this.$store.getters.getNodeIndex(this.data.name).toString();
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
@@ -62,7 +57,7 @@ export const nodeBase = mixins(
|
|||||||
const anchorPosition = CanvasHelpers.ANCHOR_POSITIONS.input[nodeTypeData.inputs.length][index];
|
const anchorPosition = CanvasHelpers.ANCHOR_POSITIONS.input[nodeTypeData.inputs.length][index];
|
||||||
|
|
||||||
const newEndpointData: IEndpointOptions = {
|
const newEndpointData: IEndpointOptions = {
|
||||||
uuid: CanvasHelpers.getInputEndpointUUID(this.nodeIndex, index),
|
uuid: CanvasHelpers.getInputEndpointUUID(this.nodeId, index),
|
||||||
anchor: anchorPosition,
|
anchor: anchorPosition,
|
||||||
maxConnections: -1,
|
maxConnections: -1,
|
||||||
endpoint: 'Rectangle',
|
endpoint: 'Rectangle',
|
||||||
@@ -71,7 +66,7 @@ export const nodeBase = mixins(
|
|||||||
isSource: false,
|
isSource: false,
|
||||||
isTarget: !this.isReadOnly && nodeTypeData.inputs.length > 1, // only enabled for nodes with multiple inputs.. otherwise attachment handled by connectionDrag event in NodeView,
|
isTarget: !this.isReadOnly && nodeTypeData.inputs.length > 1, // only enabled for nodes with multiple inputs.. otherwise attachment handled by connectionDrag event in NodeView,
|
||||||
parameters: {
|
parameters: {
|
||||||
nodeIndex: this.nodeIndex,
|
nodeId: this.nodeId,
|
||||||
type: inputName,
|
type: inputName,
|
||||||
index,
|
index,
|
||||||
},
|
},
|
||||||
@@ -130,7 +125,7 @@ export const nodeBase = mixins(
|
|||||||
const anchorPosition = CanvasHelpers.ANCHOR_POSITIONS.output[nodeTypeData.outputs.length][index];
|
const anchorPosition = CanvasHelpers.ANCHOR_POSITIONS.output[nodeTypeData.outputs.length][index];
|
||||||
|
|
||||||
const newEndpointData: IEndpointOptions = {
|
const newEndpointData: IEndpointOptions = {
|
||||||
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeIndex, index),
|
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeId, index),
|
||||||
anchor: anchorPosition,
|
anchor: anchorPosition,
|
||||||
maxConnections: -1,
|
maxConnections: -1,
|
||||||
endpoint: 'Dot',
|
endpoint: 'Dot',
|
||||||
@@ -140,7 +135,7 @@ export const nodeBase = mixins(
|
|||||||
isTarget: false,
|
isTarget: false,
|
||||||
enabled: !this.isReadOnly,
|
enabled: !this.isReadOnly,
|
||||||
parameters: {
|
parameters: {
|
||||||
nodeIndex: this.nodeIndex,
|
nodeId: this.nodeId,
|
||||||
type: inputName,
|
type: inputName,
|
||||||
index,
|
index,
|
||||||
},
|
},
|
||||||
@@ -166,7 +161,7 @@ export const nodeBase = mixins(
|
|||||||
|
|
||||||
if (!this.isReadOnly) {
|
if (!this.isReadOnly) {
|
||||||
const plusEndpointData: IEndpointOptions = {
|
const plusEndpointData: IEndpointOptions = {
|
||||||
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeIndex, index),
|
uuid: CanvasHelpers.getOutputEndpointUUID(this.nodeId, index),
|
||||||
anchor: anchorPosition,
|
anchor: anchorPosition,
|
||||||
maxConnections: -1,
|
maxConnections: -1,
|
||||||
endpoint: 'N8nPlus',
|
endpoint: 'N8nPlus',
|
||||||
@@ -187,7 +182,7 @@ export const nodeBase = mixins(
|
|||||||
hover: true, // hack to distinguish hover state
|
hover: true, // hack to distinguish hover state
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
nodeIndex: this.nodeIndex,
|
nodeId: this.nodeId,
|
||||||
type: inputName,
|
type: inputName,
|
||||||
index,
|
index,
|
||||||
},
|
},
|
||||||
@@ -258,8 +253,7 @@ export const nodeBase = mixins(
|
|||||||
// create a proper solution
|
// create a proper solution
|
||||||
let newNodePositon: XYPosition;
|
let newNodePositon: XYPosition;
|
||||||
moveNodes.forEach((node: INodeUi) => {
|
moveNodes.forEach((node: INodeUi) => {
|
||||||
const nodeElement = `node-${this.getNodeIndex(node.name)}`;
|
const element = document.getElementById(node.id);
|
||||||
const element = document.getElementById(nodeElement);
|
|
||||||
if (element === null) {
|
if (element === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
export const nodeIndex = Vue.extend({
|
|
||||||
methods: {
|
|
||||||
getNodeIndex (nodeName: string): string {
|
|
||||||
let uniqueId = this.$store.getters.getNodeIndex(nodeName);
|
|
||||||
|
|
||||||
if (uniqueId === -1) {
|
|
||||||
this.$store.commit('addToNodeIndex', nodeName);
|
|
||||||
uniqueId = this.$store.getters.getNodeIndex(nodeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We return as string as draggable and jsplumb seems to make problems
|
|
||||||
// when numbers are given
|
|
||||||
return uniqueId.toString();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -53,7 +53,7 @@ import { showMessage } from '@/components/mixins/showMessage';
|
|||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
export const workflowHelpers = mixins(
|
export const workflowHelpers = mixins(
|
||||||
externalHooks,
|
externalHooks,
|
||||||
@@ -666,7 +666,7 @@ export const workflowHelpers = mixins(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveAsNewWorkflow ({name, tags, resetWebhookUrls, openInNewWindow}: {name?: string, tags?: string[], resetWebhookUrls?: boolean, openInNewWindow?: boolean} = {}, redirect = true): Promise<boolean> {
|
async saveAsNewWorkflow ({name, tags, resetWebhookUrls, resetNodeIds, openInNewWindow}: {name?: string, tags?: string[], resetWebhookUrls?: boolean, openInNewWindow?: boolean, resetNodeIds?: boolean} = {}, redirect = true): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
this.$store.commit('addActiveAction', 'workflowSaving');
|
this.$store.commit('addActiveAction', 'workflowSaving');
|
||||||
|
|
||||||
@@ -674,10 +674,19 @@ export const workflowHelpers = mixins(
|
|||||||
// make sure that the new ones are not active
|
// make sure that the new ones are not active
|
||||||
workflowDataRequest.active = false;
|
workflowDataRequest.active = false;
|
||||||
const changedNodes = {} as IDataObject;
|
const changedNodes = {} as IDataObject;
|
||||||
|
|
||||||
|
if (resetNodeIds) {
|
||||||
|
workflowDataRequest.nodes = workflowDataRequest.nodes!.map(node => {
|
||||||
|
node.id = uuid();
|
||||||
|
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (resetWebhookUrls) {
|
if (resetWebhookUrls) {
|
||||||
workflowDataRequest.nodes = workflowDataRequest.nodes!.map(node => {
|
workflowDataRequest.nodes = workflowDataRequest.nodes!.map(node => {
|
||||||
if (node.webhookId) {
|
if (node.webhookId) {
|
||||||
node.webhookId = uuidv4();
|
node.webhookId = uuid();
|
||||||
changedNodes[node.name] = node.webhookId;
|
changedNodes[node.name] = node.webhookId;
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ export const MAX_WORKFLOW_SIZE = 16777216; // Workflow size limit in bytes
|
|||||||
export const MAX_WORKFLOW_PINNED_DATA_SIZE = 12582912; // Workflow pinned data size limit in bytes
|
export const MAX_WORKFLOW_PINNED_DATA_SIZE = 12582912; // Workflow pinned data size limit in bytes
|
||||||
export const MAX_DISPLAY_DATA_SIZE = 204800;
|
export const MAX_DISPLAY_DATA_SIZE = 204800;
|
||||||
export const MAX_DISPLAY_ITEMS_AUTO_ALL = 250;
|
export const MAX_DISPLAY_ITEMS_AUTO_ALL = 250;
|
||||||
export const NODE_NAME_PREFIX = 'node-';
|
|
||||||
|
|
||||||
export const PLACEHOLDER_FILLED_AT_EXECUTION_TIME = '[filled at execution time]';
|
export const PLACEHOLDER_FILLED_AT_EXECUTION_TIME = '[filled at execution time]';
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ const state: IRootState = {
|
|||||||
workflowExecutionData: null,
|
workflowExecutionData: null,
|
||||||
lastSelectedNode: null,
|
lastSelectedNode: null,
|
||||||
lastSelectedNodeOutputIndex: null,
|
lastSelectedNodeOutputIndex: null,
|
||||||
nodeIndex: [],
|
|
||||||
nodeViewOffsetPosition: [0, 0],
|
nodeViewOffsetPosition: [0, 0],
|
||||||
nodeViewMoveInProgress: false,
|
nodeViewMoveInProgress: false,
|
||||||
selectedNodes: [],
|
selectedNodes: [],
|
||||||
@@ -533,17 +532,6 @@ export const store = new Vuex.Store({
|
|||||||
Vue.set(state.nodeMetadata[node.name], 'parametersLastUpdatedAt', Date.now());
|
Vue.set(state.nodeMetadata[node.name], 'parametersLastUpdatedAt', Date.now());
|
||||||
},
|
},
|
||||||
|
|
||||||
// Node-Index
|
|
||||||
addToNodeIndex(state, nodeName: string) {
|
|
||||||
state.nodeIndex.push(nodeName);
|
|
||||||
},
|
|
||||||
setNodeIndex(state, newData: { index: number, name: string | null }) {
|
|
||||||
state.nodeIndex[newData.index] = newData.name;
|
|
||||||
},
|
|
||||||
resetNodeIndex(state) {
|
|
||||||
Vue.set(state, 'nodeIndex', []);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Node-View
|
// Node-View
|
||||||
setNodeViewMoveInProgress(state, value: boolean) {
|
setNodeViewMoveInProgress(state, value: boolean) {
|
||||||
state.nodeViewMoveInProgress = value;
|
state.nodeViewMoveInProgress = value;
|
||||||
@@ -821,14 +809,6 @@ export const store = new Vuex.Store({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Node-Index
|
|
||||||
getNodeIndex: (state) => (nodeName: string): number => {
|
|
||||||
return state.nodeIndex.indexOf(nodeName);
|
|
||||||
},
|
|
||||||
getNodeNameByIndex: (state) => (index: number): string | null => {
|
|
||||||
return state.nodeIndex[index];
|
|
||||||
},
|
|
||||||
|
|
||||||
getNodeViewOffsetPosition: (state): XYPosition => {
|
getNodeViewOffsetPosition: (state): XYPosition => {
|
||||||
return state.nodeViewOffsetPosition;
|
return state.nodeViewOffsetPosition;
|
||||||
},
|
},
|
||||||
@@ -838,7 +818,16 @@ export const store = new Vuex.Store({
|
|||||||
|
|
||||||
// Selected Nodes
|
// Selected Nodes
|
||||||
getSelectedNodes: (state): INodeUi[] => {
|
getSelectedNodes: (state): INodeUi[] => {
|
||||||
return state.selectedNodes;
|
const seen = new Set();
|
||||||
|
return state.selectedNodes.filter((node: INodeUi) => {
|
||||||
|
// dedupe for instances when same node is selected in different ways
|
||||||
|
if (!seen.has(node.id)) {
|
||||||
|
seen.add(node.id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
isNodeSelected: (state) => (nodeName: string): boolean => {
|
isNodeSelected: (state) => (nodeName: string): boolean => {
|
||||||
let index;
|
let index;
|
||||||
@@ -874,6 +863,9 @@ export const store = new Vuex.Store({
|
|||||||
getNodeByName: (state, getters) => (nodeName: string): INodeUi | null => {
|
getNodeByName: (state, getters) => (nodeName: string): INodeUi | null => {
|
||||||
return getters.nodesByName[nodeName] || null;
|
return getters.nodesByName[nodeName] || null;
|
||||||
},
|
},
|
||||||
|
getNodeById: (state, getters) => (nodeId: string): INodeUi | undefined => {
|
||||||
|
return state.workflow.nodes.find((node: INodeUi) => node.id === nodeId);
|
||||||
|
},
|
||||||
nodesIssuesExist: (state): boolean => {
|
nodesIssuesExist: (state): boolean => {
|
||||||
for (const node of state.workflow.nodes) {
|
for (const node of state.workflow.nodes) {
|
||||||
if (node.issues === undefined || Object.keys(node.issues).length === 0) {
|
if (node.issues === undefined || Object.keys(node.issues).length === 0) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
class="node-view"
|
class="node-view"
|
||||||
:style="workflowStyle"
|
:style="workflowStyle"
|
||||||
>
|
>
|
||||||
<div v-for="nodeData in nodes" :key="getNodeIndex(nodeData.name)">
|
<div v-for="nodeData in nodes" :key="nodeData.id">
|
||||||
<node
|
<node
|
||||||
v-if="nodeData.type !== STICKY_NODE_TYPE"
|
v-if="nodeData.type !== STICKY_NODE_TYPE"
|
||||||
@duplicateNode="duplicateNode"
|
@duplicateNode="duplicateNode"
|
||||||
@@ -32,8 +32,7 @@
|
|||||||
@runWorkflow="onRunNode"
|
@runWorkflow="onRunNode"
|
||||||
@moved="onNodeMoved"
|
@moved="onNodeMoved"
|
||||||
@run="onNodeRun"
|
@run="onNodeRun"
|
||||||
:id="'node-' + getNodeIndex(nodeData.name)"
|
:key="nodeData.id"
|
||||||
:key="getNodeIndex(nodeData.name)"
|
|
||||||
:name="nodeData.name"
|
:name="nodeData.name"
|
||||||
:isReadOnly="isReadOnly"
|
:isReadOnly="isReadOnly"
|
||||||
:instance="instance"
|
:instance="instance"
|
||||||
@@ -46,7 +45,7 @@
|
|||||||
@deselectNode="nodeDeselectedByName"
|
@deselectNode="nodeDeselectedByName"
|
||||||
@nodeSelected="nodeSelectedByName"
|
@nodeSelected="nodeSelectedByName"
|
||||||
@removeNode="removeNode"
|
@removeNode="removeNode"
|
||||||
:id="'node-' + getNodeIndex(nodeData.name)"
|
:key="nodeData.id"
|
||||||
:name="nodeData.name"
|
:name="nodeData.name"
|
||||||
:isReadOnly="isReadOnly"
|
:isReadOnly="isReadOnly"
|
||||||
:instance="instance"
|
:instance="instance"
|
||||||
@@ -161,7 +160,6 @@ import {
|
|||||||
MODAL_CANCEL,
|
MODAL_CANCEL,
|
||||||
MODAL_CLOSE,
|
MODAL_CLOSE,
|
||||||
MODAL_CONFIRMED,
|
MODAL_CONFIRMED,
|
||||||
NODE_NAME_PREFIX,
|
|
||||||
NODE_OUTPUT_DEFAULT_KEY,
|
NODE_OUTPUT_DEFAULT_KEY,
|
||||||
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
|
ONBOARDING_CALL_SIGNUP_MODAL_KEY,
|
||||||
ONBOARDING_PROMPT_TIMEBOX,
|
ONBOARDING_PROMPT_TIMEBOX,
|
||||||
@@ -195,7 +193,7 @@ import Sticky from '@/components/Sticky.vue';
|
|||||||
import * as CanvasHelpers from './canvasHelpers';
|
import * as CanvasHelpers from './canvasHelpers';
|
||||||
|
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { v4 as uuidv4} from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import {
|
import {
|
||||||
IConnection,
|
IConnection,
|
||||||
IConnections,
|
IConnections,
|
||||||
@@ -228,6 +226,7 @@ import {
|
|||||||
ITag,
|
ITag,
|
||||||
IWorkflowTemplate,
|
IWorkflowTemplate,
|
||||||
IExecutionsSummary,
|
IExecutionsSummary,
|
||||||
|
IWorkflowToShare,
|
||||||
} from '../Interface';
|
} from '../Interface';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
@@ -639,6 +638,7 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
this.resetWorkspace();
|
this.resetWorkspace();
|
||||||
data.workflow.nodes = CanvasHelpers.getFixedNodesList(data.workflow.nodes);
|
data.workflow.nodes = CanvasHelpers.getFixedNodesList(data.workflow.nodes);
|
||||||
|
|
||||||
await this.addNodes(data.workflow.nodes, data.workflow.connections);
|
await this.addNodes(data.workflow.nodes, data.workflow.connections);
|
||||||
|
|
||||||
if (data.workflow.pinData) {
|
if (data.workflow.pinData) {
|
||||||
@@ -1094,7 +1094,14 @@ export default mixins(
|
|||||||
|
|
||||||
copySelectedNodes (isCut: boolean) {
|
copySelectedNodes (isCut: boolean) {
|
||||||
this.getSelectedNodesToSave().then((data) => {
|
this.getSelectedNodesToSave().then((data) => {
|
||||||
const nodeData = JSON.stringify(data, null, 2);
|
const workflowToCopy: IWorkflowToShare = {
|
||||||
|
meta: {
|
||||||
|
instanceId: this.$store.getters.instanceId,
|
||||||
|
},
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
|
||||||
|
const nodeData = JSON.stringify(workflowToCopy, null, 2);
|
||||||
this.copyToClipboard(nodeData);
|
this.copyToClipboard(nodeData);
|
||||||
if (data.nodes.length > 0) {
|
if (data.nodes.length > 0) {
|
||||||
if(!isCut){
|
if(!isCut){
|
||||||
@@ -1290,11 +1297,7 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$telemetry.track('User pasted nodes', {
|
return this.importWorkflowData(workflowData!, false, 'paste');
|
||||||
workflow_id: this.$store.getters.workflowId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.importWorkflowData(workflowData!, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns the workflow data from a given URL. If no data gets found or
|
// Returns the workflow data from a given URL. If no data gets found or
|
||||||
@@ -1315,13 +1318,11 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
this.stopLoading();
|
this.stopLoading();
|
||||||
|
|
||||||
this.$telemetry.track('User imported workflow', { source: 'url', workflow_id: this.$store.getters.workflowId });
|
|
||||||
|
|
||||||
return workflowData;
|
return workflowData;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Imports the given workflow data into the current workflow
|
// Imports the given workflow data into the current workflow
|
||||||
async importWorkflowData (workflowData: IWorkflowDataUpdate, importTags = true): Promise<void> {
|
async importWorkflowData (workflowData: IWorkflowToShare, importTags = true, source: string): Promise<void> {
|
||||||
// If it is JSON check if it looks on the first look like data we can use
|
// If it is JSON check if it looks on the first look like data we can use
|
||||||
if (
|
if (
|
||||||
!workflowData.hasOwnProperty('nodes') ||
|
!workflowData.hasOwnProperty('nodes') ||
|
||||||
@@ -1331,6 +1332,40 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const nodeIdMap: {[prev: string]: string} = {};
|
||||||
|
if (workflowData.nodes) {
|
||||||
|
// set all new ids when pasting/importing workflows
|
||||||
|
workflowData.nodes.forEach((node: INode) => {
|
||||||
|
if (node.id) {
|
||||||
|
const newId = uuid();
|
||||||
|
nodeIdMap[newId] = node.id;
|
||||||
|
node.id = newId;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.id = uuid();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const currInstanceId = this.$store.getters.instanceId;
|
||||||
|
|
||||||
|
const nodeGraph = JSON.stringify(
|
||||||
|
TelemetryHelpers.generateNodesGraph(workflowData as IWorkflowBase,
|
||||||
|
this.getNodeTypes(),
|
||||||
|
{
|
||||||
|
nodeIdMap,
|
||||||
|
sourceInstanceId: workflowData.meta && workflowData.meta.instanceId !== currInstanceId? workflowData.meta.instanceId: '',
|
||||||
|
}).nodeGraph,
|
||||||
|
);
|
||||||
|
if (source === 'paste') {
|
||||||
|
this.$telemetry.track('User pasted nodes', {
|
||||||
|
workflow_id: this.$store.getters.workflowId,
|
||||||
|
node_graph_string: nodeGraph,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.$telemetry.track('User imported workflow', { source, workflow_id: this.$store.getters.workflowId, node_graph_string: nodeGraph });
|
||||||
|
}
|
||||||
|
|
||||||
// By default we automatically deselect all the currently
|
// By default we automatically deselect all the currently
|
||||||
// selected nodes and select the new ones
|
// selected nodes and select the new ones
|
||||||
this.deselectAllNodes();
|
this.deselectAllNodes();
|
||||||
@@ -1500,6 +1535,7 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newNodeData: INodeUi = {
|
const newNodeData: INodeUi = {
|
||||||
|
id: uuid(),
|
||||||
name: nodeTypeData.defaults.name as string,
|
name: nodeTypeData.defaults.name as string,
|
||||||
type: nodeTypeData.name,
|
type: nodeTypeData.name,
|
||||||
typeVersion: Array.isArray(nodeTypeData.version)
|
typeVersion: Array.isArray(nodeTypeData.version)
|
||||||
@@ -1564,7 +1600,7 @@ export default mixins(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (nodeTypeData.webhooks && nodeTypeData.webhooks.length) {
|
if (nodeTypeData.webhooks && nodeTypeData.webhooks.length) {
|
||||||
newNodeData.webhookId = uuidv4();
|
newNodeData.webhookId = uuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.addNodes([newNodeData]);
|
await this.addNodes([newNodeData]);
|
||||||
@@ -1676,8 +1712,12 @@ export default mixins(
|
|||||||
// Get the node and set it as active that new nodes
|
// Get the node and set it as active that new nodes
|
||||||
// which get created get automatically connected
|
// which get created get automatically connected
|
||||||
// to it.
|
// to it.
|
||||||
const sourceNodeName = this.$store.getters.getNodeNameByIndex(info.sourceId.slice(NODE_NAME_PREFIX.length));
|
const sourceNode = this.$store.getters.getNodeById(info.sourceId) as INodeUi | null;
|
||||||
this.$store.commit('setLastSelectedNode', sourceNodeName);
|
if (!sourceNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit('setLastSelectedNode', sourceNode.name);
|
||||||
this.$store.commit('setLastSelectedNodeOutputIndex', info.index);
|
this.$store.commit('setLastSelectedNodeOutputIndex', info.index);
|
||||||
this.newNodeInsertPosition = null;
|
this.newNodeInsertPosition = null;
|
||||||
|
|
||||||
@@ -1696,7 +1736,8 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.pullConnActiveNodeName) {
|
if (this.pullConnActiveNodeName) {
|
||||||
const sourceNodeName = this.$store.getters.getNodeNameByIndex(connection.sourceId.slice(NODE_NAME_PREFIX.length));
|
const sourceNode = this.$store.getters.getNodeById(connection.sourceId);
|
||||||
|
const sourceNodeName = sourceNode.name;
|
||||||
const outputIndex = connection.getParameters().index;
|
const outputIndex = connection.getParameters().index;
|
||||||
|
|
||||||
this.connectTwoNodes(sourceNodeName, outputIndex, this.pullConnActiveNodeName, 0);
|
this.connectTwoNodes(sourceNodeName, outputIndex, this.pullConnActiveNodeName, 0);
|
||||||
@@ -1720,8 +1761,8 @@ export default mixins(
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const targetInfo = info.dropEndpoint.getParameters();
|
const targetInfo = info.dropEndpoint.getParameters();
|
||||||
|
|
||||||
const sourceNodeName = this.$store.getters.getNodeNameByIndex(sourceInfo.nodeIndex);
|
const sourceNodeName = this.$store.getters.getNodeById(sourceInfo.nodeId).name;
|
||||||
const targetNodeName = this.$store.getters.getNodeNameByIndex(targetInfo.nodeIndex);
|
const targetNodeName = this.$store.getters.getNodeById(targetInfo.nodeId).name;
|
||||||
|
|
||||||
// check for duplicates
|
// check for duplicates
|
||||||
if (this.getConnection(sourceNodeName, sourceInfo.index, targetNodeName, targetInfo.index)) {
|
if (this.getConnection(sourceNodeName, sourceInfo.index, targetNodeName, targetInfo.index)) {
|
||||||
@@ -1745,8 +1786,8 @@ export default mixins(
|
|||||||
const sourceInfo = info.sourceEndpoint.getParameters();
|
const sourceInfo = info.sourceEndpoint.getParameters();
|
||||||
const targetInfo = info.targetEndpoint.getParameters();
|
const targetInfo = info.targetEndpoint.getParameters();
|
||||||
|
|
||||||
const sourceNodeName = this.$store.getters.getNodeNameByIndex(sourceInfo.nodeIndex);
|
const sourceNodeName = this.$store.getters.getNodeById(sourceInfo.nodeId).name;
|
||||||
const targetNodeName = this.$store.getters.getNodeNameByIndex(targetInfo.nodeIndex);
|
const targetNodeName = this.$store.getters.getNodeById(targetInfo.nodeId).name;
|
||||||
|
|
||||||
info.connection.__meta = {
|
info.connection.__meta = {
|
||||||
sourceNodeName,
|
sourceNodeName,
|
||||||
@@ -1872,12 +1913,12 @@ export default mixins(
|
|||||||
|
|
||||||
const connectionInfo = [
|
const connectionInfo = [
|
||||||
{
|
{
|
||||||
node: this.$store.getters.getNodeNameByIndex(sourceInfo.nodeIndex),
|
node: this.$store.getters.getNodeById(sourceInfo.nodeId).name,
|
||||||
type: sourceInfo.type,
|
type: sourceInfo.type,
|
||||||
index: sourceInfo.index,
|
index: sourceInfo.index,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: this.$store.getters.getNodeNameByIndex(targetInfo.nodeIndex),
|
node: this.$store.getters.getNodeById(targetInfo.nodeId).name,
|
||||||
type: targetInfo.type,
|
type: targetInfo.type,
|
||||||
index: targetInfo.index,
|
index: targetInfo.index,
|
||||||
},
|
},
|
||||||
@@ -1896,7 +1937,8 @@ export default mixins(
|
|||||||
this.__removeConnectionByConnectionInfo(info, false);
|
this.__removeConnectionByConnectionInfo(info, false);
|
||||||
|
|
||||||
if (this.pullConnActiveNodeName) { // establish new connection when dragging connection from one node to another
|
if (this.pullConnActiveNodeName) { // establish new connection when dragging connection from one node to another
|
||||||
const sourceNodeName = this.$store.getters.getNodeNameByIndex(info.connection.sourceId.slice(NODE_NAME_PREFIX.length));
|
const sourceNode = this.$store.getters.getNodeById(info.connection.sourceId);
|
||||||
|
const sourceNodeName = sourceNode.name;
|
||||||
const outputIndex = info.connection.getParameters().index;
|
const outputIndex = info.connection.getParameters().index;
|
||||||
|
|
||||||
this.connectTwoNodes(sourceNodeName, outputIndex, this.pullConnActiveNodeName, 0);
|
this.connectTwoNodes(sourceNodeName, outputIndex, this.pullConnActiveNodeName, 0);
|
||||||
@@ -1940,11 +1982,14 @@ export default mixins(
|
|||||||
const nodeType = this.$store.getters['nodeTypes/getNodeType'](node.type, node.typeVersion) as INodeTypeDescription | null;
|
const nodeType = this.$store.getters['nodeTypes/getNodeType'](node.type, node.typeVersion) as INodeTypeDescription | null;
|
||||||
if (nodeType && nodeType.inputs && nodeType.inputs.length === 1) {
|
if (nodeType && nodeType.inputs && nodeType.inputs.length === 1) {
|
||||||
this.pullConnActiveNodeName = node.name;
|
this.pullConnActiveNodeName = node.name;
|
||||||
const endpoint = this.instance.getEndpoint(this.getInputEndpointUUID(nodeName, 0));
|
const endpointUUID = this.getInputEndpointUUID(nodeName, 0);
|
||||||
|
if (endpointUUID) {
|
||||||
|
const endpoint = this.instance.getEndpoint(endpointUUID);
|
||||||
|
|
||||||
CanvasHelpers.showDropConnectionState(connection, endpoint);
|
CanvasHelpers.showDropConnectionState(connection, endpoint);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1992,7 +2037,10 @@ export default mixins(
|
|||||||
|
|
||||||
this.$store.commit('setStateDirty', false);
|
this.$store.commit('setStateDirty', false);
|
||||||
|
|
||||||
await this.addNodes([{...CanvasHelpers.DEFAULT_START_NODE}]);
|
await this.addNodes([{
|
||||||
|
id: uuid(),
|
||||||
|
...CanvasHelpers.DEFAULT_START_NODE,
|
||||||
|
}]);
|
||||||
|
|
||||||
this.nodeSelectedByName(CanvasHelpers.DEFAULT_START_NODE.name, false);
|
this.nodeSelectedByName(CanvasHelpers.DEFAULT_START_NODE.name, false);
|
||||||
|
|
||||||
@@ -2007,6 +2055,7 @@ export default mixins(
|
|||||||
this.$nextTick(async () => {
|
this.$nextTick(async () => {
|
||||||
await this.addNodes([
|
await this.addNodes([
|
||||||
{
|
{
|
||||||
|
id: uuid(),
|
||||||
...CanvasHelpers.WELCOME_STICKY_NODE,
|
...CanvasHelpers.WELCOME_STICKY_NODE,
|
||||||
parameters: {
|
parameters: {
|
||||||
// Use parameters from the template but add translated content
|
// Use parameters from the template but add translated content
|
||||||
@@ -2108,17 +2157,33 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getOutputEndpointUUID(nodeName: string, index: number) {
|
getOutputEndpointUUID(nodeName: string, index: number): string | null {
|
||||||
return CanvasHelpers.getOutputEndpointUUID(this.getNodeIndex(nodeName), index);
|
const node = this.$store.getters.getNodeByName(nodeName);
|
||||||
|
if (!node) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CanvasHelpers.getOutputEndpointUUID(node.id, index);
|
||||||
},
|
},
|
||||||
getInputEndpointUUID(nodeName: string, index: number) {
|
getInputEndpointUUID(nodeName: string, index: number) {
|
||||||
return CanvasHelpers.getInputEndpointUUID(this.getNodeIndex(nodeName), index);
|
const node = this.$store.getters.getNodeByName(nodeName);
|
||||||
|
if (!node) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CanvasHelpers.getInputEndpointUUID(node.id, index);
|
||||||
},
|
},
|
||||||
__addConnection (connection: [IConnection, IConnection], addVisualConnection = false) {
|
__addConnection (connection: [IConnection, IConnection], addVisualConnection = false) {
|
||||||
if (addVisualConnection === true) {
|
if (addVisualConnection === true) {
|
||||||
|
const outputUuid = this.getOutputEndpointUUID(connection[0].node, connection[0].index);
|
||||||
|
const inputUuid = this.getInputEndpointUUID(connection[1].node, connection[1].index);
|
||||||
|
if (!outputUuid || !inputUuid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const uuid: [string, string] = [
|
const uuid: [string, string] = [
|
||||||
this.getOutputEndpointUUID(connection[0].node, connection[0].index),
|
outputUuid,
|
||||||
this.getInputEndpointUUID(connection[1].node, connection[1].index),
|
inputUuid,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Create connections in DOM
|
// Create connections in DOM
|
||||||
@@ -2140,10 +2205,12 @@ export default mixins(
|
|||||||
},
|
},
|
||||||
__removeConnection (connection: [IConnection, IConnection], removeVisualConnection = false) {
|
__removeConnection (connection: [IConnection, IConnection], removeVisualConnection = false) {
|
||||||
if (removeVisualConnection === true) {
|
if (removeVisualConnection === true) {
|
||||||
|
const sourceId = this.$store.getters.getNodeByName(connection[0].node);
|
||||||
|
const targetId = this.$store.getters.getNodeByName(connection[1].node);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const connections = this.instance.getConnections({
|
const connections = this.instance.getConnections({
|
||||||
source: NODE_NAME_PREFIX + this.getNodeIndex(connection[0].node),
|
source: sourceId,
|
||||||
target: NODE_NAME_PREFIX + this.getNodeIndex(connection[1].node),
|
target: targetId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -2175,12 +2242,12 @@ export default mixins(
|
|||||||
|
|
||||||
const connectionInfo = [
|
const connectionInfo = [
|
||||||
{
|
{
|
||||||
node: this.$store.getters.getNodeNameByIndex(sourceInfo.nodeIndex),
|
node: this.$store.getters.getNodeById(sourceInfo.nodeId).name,
|
||||||
type: sourceInfo.type,
|
type: sourceInfo.type,
|
||||||
index: sourceInfo.index,
|
index: sourceInfo.index,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: this.$store.getters.getNodeNameByIndex(targetInfo.nodeIndex),
|
node: this.$store.getters.getNodeById(targetInfo.nodeId).name,
|
||||||
type: targetInfo.type,
|
type: targetInfo.type,
|
||||||
index: targetInfo.index,
|
index: targetInfo.index,
|
||||||
},
|
},
|
||||||
@@ -2208,6 +2275,7 @@ export default mixins(
|
|||||||
// Deep copy the data so that data on lower levels of the node-properties do
|
// Deep copy the data so that data on lower levels of the node-properties do
|
||||||
// not share objects
|
// not share objects
|
||||||
const newNodeData = JSON.parse(JSON.stringify(this.getNodeDataToSave(node)));
|
const newNodeData = JSON.parse(JSON.stringify(this.getNodeDataToSave(node)));
|
||||||
|
newNodeData.id = uuid();
|
||||||
|
|
||||||
// Check if node-name is unique else find one that is
|
// Check if node-name is unique else find one that is
|
||||||
newNodeData.name = this.getUniqueNodeName({
|
newNodeData.name = this.getUniqueNodeName({
|
||||||
@@ -2223,7 +2291,7 @@ export default mixins(
|
|||||||
|
|
||||||
if (newNodeData.webhookId) {
|
if (newNodeData.webhookId) {
|
||||||
// Make sure that the node gets a new unique webhook-ID
|
// Make sure that the node gets a new unique webhook-ID
|
||||||
newNodeData.webhookId = uuidv4();
|
newNodeData.webhookId = uuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.addNodes([newNodeData]);
|
await this.addNodes([newNodeData]);
|
||||||
@@ -2248,14 +2316,17 @@ export default mixins(
|
|||||||
this.$telemetry.track('User duplicated node', { node_type: node.type, workflow_id: this.$store.getters.workflowId });
|
this.$telemetry.track('User duplicated node', { node_type: node.type, workflow_id: this.$store.getters.workflowId });
|
||||||
},
|
},
|
||||||
getJSPlumbConnection (sourceNodeName: string, sourceOutputIndex: number, targetNodeName: string, targetInputIndex: number): Connection | undefined {
|
getJSPlumbConnection (sourceNodeName: string, sourceOutputIndex: number, targetNodeName: string, targetInputIndex: number): Connection | undefined {
|
||||||
const sourceIndex = this.getNodeIndex(sourceNodeName);
|
const sourceNode = this.$store.getters.getNodeByName(sourceNodeName) as INodeUi;
|
||||||
const sourceId = `${NODE_NAME_PREFIX}${sourceIndex}`;
|
const targetNode = this.$store.getters.getNodeByName(targetNodeName) as INodeUi;
|
||||||
|
if (!sourceNode || !targetNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const targetIndex = this.getNodeIndex(targetNodeName);
|
const sourceId = sourceNode.id;
|
||||||
const targetId = `${NODE_NAME_PREFIX}${targetIndex}`;
|
const targetId = targetNode.id;
|
||||||
|
|
||||||
const sourceEndpoint = CanvasHelpers.getOutputEndpointUUID(sourceIndex, sourceOutputIndex);
|
const sourceEndpoint = CanvasHelpers.getOutputEndpointUUID(sourceId, sourceOutputIndex);
|
||||||
const targetEndpoint = CanvasHelpers.getInputEndpointUUID(targetIndex, targetInputIndex);
|
const targetEndpoint = CanvasHelpers.getInputEndpointUUID(targetId, targetInputIndex);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const connections = this.instance.getConnections({
|
const connections = this.instance.getConnections({
|
||||||
@@ -2269,9 +2340,8 @@ export default mixins(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
getJSPlumbEndpoints (nodeName: string): Endpoint[] {
|
getJSPlumbEndpoints (nodeName: string): Endpoint[] {
|
||||||
const nodeIndex = this.getNodeIndex(nodeName);
|
const node = this.$store.getters.getNodeByName(nodeName);
|
||||||
const nodeId = `${NODE_NAME_PREFIX}${nodeIndex}`;
|
return this.instance.getEndpoints(node.id);
|
||||||
return this.instance.getEndpoints(nodeId);
|
|
||||||
},
|
},
|
||||||
getPlusEndpoint (nodeName: string, outputIndex: number): Endpoint | undefined {
|
getPlusEndpoint (nodeName: string, outputIndex: number): Endpoint | undefined {
|
||||||
const endpoints = this.getJSPlumbEndpoints(nodeName);
|
const endpoints = this.getJSPlumbEndpoints(nodeName);
|
||||||
@@ -2279,15 +2349,15 @@ export default mixins(
|
|||||||
return endpoints.find((endpoint: Endpoint) => endpoint.type === 'N8nPlus' && endpoint.__meta && endpoint.__meta.index === outputIndex);
|
return endpoints.find((endpoint: Endpoint) => endpoint.type === 'N8nPlus' && endpoint.__meta && endpoint.__meta.index === outputIndex);
|
||||||
},
|
},
|
||||||
getIncomingOutgoingConnections(nodeName: string): {incoming: Connection[], outgoing: Connection[]} {
|
getIncomingOutgoingConnections(nodeName: string): {incoming: Connection[], outgoing: Connection[]} {
|
||||||
const name = `${NODE_NAME_PREFIX}${this.$store.getters.getNodeIndex(nodeName)}`;
|
const node = this.$store.getters.getNodeByName(nodeName);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const outgoing = this.instance.getConnections({
|
const outgoing = this.instance.getConnections({
|
||||||
source: name,
|
source: node.id,
|
||||||
}) as Connection[];
|
}) as Connection[];
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const incoming = this.instance.getConnections({
|
const incoming = this.instance.getConnections({
|
||||||
target: name,
|
target: node.id,
|
||||||
}) as Connection[];
|
}) as Connection[];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -2305,8 +2375,8 @@ export default mixins(
|
|||||||
},
|
},
|
||||||
onNodeRun ({name, data, waiting}: {name: string, data: ITaskData[] | null, waiting: boolean}) {
|
onNodeRun ({name, data, waiting}: {name: string, data: ITaskData[] | null, waiting: boolean}) {
|
||||||
const sourceNodeName = name;
|
const sourceNodeName = name;
|
||||||
const sourceIndex = this.$store.getters.getNodeIndex(sourceNodeName);
|
const sourceNode = this.$store.getters.getNodeByName(sourceNodeName);
|
||||||
const sourceId = `${NODE_NAME_PREFIX}${sourceIndex}`;
|
const sourceId = sourceNode.id;
|
||||||
|
|
||||||
if (data === null || data.length === 0 || waiting) {
|
if (data === null || data.length === 0 || waiting) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -2438,18 +2508,15 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const nodeIndex = this.$store.getters.getNodeIndex(nodeName);
|
|
||||||
const nodeIdName = `node-${nodeIndex}`;
|
|
||||||
|
|
||||||
// Suspend drawing
|
// Suspend drawing
|
||||||
this.instance.setSuspendDrawing(true);
|
this.instance.setSuspendDrawing(true);
|
||||||
|
|
||||||
// Remove all endpoints and the connections in jsplumb
|
// Remove all endpoints and the connections in jsplumb
|
||||||
this.instance.removeAllEndpoints(nodeIdName);
|
this.instance.removeAllEndpoints(node.id);
|
||||||
|
|
||||||
// Remove the draggable
|
// Remove the draggable
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.instance.destroyDraggable(nodeIdName);
|
this.instance.destroyDraggable(node.id);
|
||||||
|
|
||||||
// Remove the connections in data
|
// Remove the connections in data
|
||||||
this.$store.commit('removeAllNodeConnection', node);
|
this.$store.commit('removeAllNodeConnection', node);
|
||||||
@@ -2465,10 +2532,6 @@ export default mixins(
|
|||||||
// Remove node from selected index if found in it
|
// Remove node from selected index if found in it
|
||||||
this.$store.commit('removeNodeFromSelection', node);
|
this.$store.commit('removeNodeFromSelection', node);
|
||||||
|
|
||||||
// Remove from node index
|
|
||||||
if (nodeIndex !== -1) {
|
|
||||||
this.$store.commit('setNodeIndex', { index: nodeIndex, name: null });
|
|
||||||
}
|
|
||||||
}, 0); // allow other events to finish like drag stop
|
}, 0); // allow other events to finish like drag stop
|
||||||
},
|
},
|
||||||
valueChanged (parameterData: IUpdateInformation) {
|
valueChanged (parameterData: IUpdateInformation) {
|
||||||
@@ -2557,7 +2620,7 @@ export default mixins(
|
|||||||
try {
|
try {
|
||||||
const nodes = this.$store.getters.allNodes as INodeUi[];
|
const nodes = this.$store.getters.allNodes as INodeUi[];
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
nodes.forEach((node: INodeUi) => this.instance.destroyDraggable(`${NODE_NAME_PREFIX}${this.$store.getters.getNodeIndex(node.name)}`));
|
nodes.forEach((node: INodeUi) => this.instance.destroyDraggable(node.id));
|
||||||
|
|
||||||
this.instance.deleteEveryEndpoint();
|
this.instance.deleteEveryEndpoint();
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
@@ -2621,6 +2684,10 @@ export default mixins(
|
|||||||
let nodeType: INodeTypeDescription | null;
|
let nodeType: INodeTypeDescription | null;
|
||||||
let foundNodeIssues: INodeIssues | null;
|
let foundNodeIssues: INodeIssues | null;
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
|
if (!node.id) {
|
||||||
|
node.id = uuid();
|
||||||
|
}
|
||||||
|
|
||||||
nodeType = this.$store.getters['nodeTypes/getNodeType'](node.type, node.typeVersion) as INodeTypeDescription | null;
|
nodeType = this.$store.getters['nodeTypes/getNodeType'](node.type, node.typeVersion) as INodeTypeDescription | null;
|
||||||
|
|
||||||
// Make sure that some properties always exist
|
// Make sure that some properties always exist
|
||||||
@@ -2919,7 +2986,6 @@ export default mixins(
|
|||||||
this.$store.commit('removeActiveAction', 'workflowRunning');
|
this.$store.commit('removeActiveAction', 'workflowRunning');
|
||||||
this.$store.commit('setExecutionWaitingForWebhook', false);
|
this.$store.commit('setExecutionWaitingForWebhook', false);
|
||||||
|
|
||||||
this.$store.commit('resetNodeIndex');
|
|
||||||
this.$store.commit('resetSelectedNodes');
|
this.$store.commit('resetSelectedNodes');
|
||||||
|
|
||||||
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [0, 0], setStateDirty: false});
|
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [0, 0], setStateDirty: false});
|
||||||
@@ -2983,19 +3049,24 @@ export default mixins(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onImportWorkflowDataEvent(data: IDataObject) {
|
async onImportWorkflowDataEvent(data: IDataObject) {
|
||||||
await this.importWorkflowData(data.data as IWorkflowDataUpdate);
|
await this.importWorkflowData(data.data as IWorkflowDataUpdate, undefined, 'file');
|
||||||
},
|
},
|
||||||
async onImportWorkflowUrlEvent(data: IDataObject) {
|
async onImportWorkflowUrlEvent(data: IDataObject) {
|
||||||
const workflowData = await this.getWorkflowDataFromUrl(data.url as string);
|
const workflowData = await this.getWorkflowDataFromUrl(data.url as string);
|
||||||
if (workflowData !== undefined) {
|
if (workflowData !== undefined) {
|
||||||
await this.importWorkflowData(workflowData);
|
await this.importWorkflowData(workflowData, undefined, 'url');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addPinDataConnections(pinData: IPinData) {
|
addPinDataConnections(pinData: IPinData) {
|
||||||
Object.keys(pinData).forEach((nodeName) => {
|
Object.keys(pinData).forEach((nodeName) => {
|
||||||
|
const node = this.$store.getters.getNodeByName(nodeName);
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const connections = this.instance.getConnections({
|
const connections = this.instance.getConnections({
|
||||||
source: NODE_NAME_PREFIX + this.getNodeIndex(nodeName),
|
source: node.id,
|
||||||
}) as Connection[];
|
}) as Connection[];
|
||||||
|
|
||||||
connections.forEach((connection) => {
|
connections.forEach((connection) => {
|
||||||
@@ -3008,9 +3079,14 @@ export default mixins(
|
|||||||
},
|
},
|
||||||
removePinDataConnections(pinData: IPinData) {
|
removePinDataConnections(pinData: IPinData) {
|
||||||
Object.keys(pinData).forEach((nodeName) => {
|
Object.keys(pinData).forEach((nodeName) => {
|
||||||
|
const node = this.$store.getters.getNodeByName(nodeName);
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const connections = this.instance.getConnections({
|
const connections = this.instance.getConnections({
|
||||||
source: NODE_NAME_PREFIX + this.getNodeIndex(nodeName),
|
source: node.id,
|
||||||
}) as Connection[];
|
}) as Connection[];
|
||||||
|
|
||||||
connections.forEach(CanvasHelpers.resetConnection);
|
connections.forEach(CanvasHelpers.resetConnection);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
NodeInputConnections,
|
NodeInputConnections,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
export const OVERLAY_DROP_NODE_ID = 'drop-add-node';
|
export const OVERLAY_DROP_NODE_ID = 'drop-add-node';
|
||||||
export const OVERLAY_MIDPOINT_ARROW_ID = 'midpoint-arrow';
|
export const OVERLAY_MIDPOINT_ARROW_ID = 'midpoint-arrow';
|
||||||
@@ -705,12 +706,12 @@ export const addConnectionActionsOverlay = (connection: Connection, onDelete: Fu
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getOutputEndpointUUID = (nodeIndex: string, outputIndex: number) => {
|
export const getOutputEndpointUUID = (nodeId: string, outputIndex: number) => {
|
||||||
return `${nodeIndex}${OUTPUT_UUID_KEY}${outputIndex}`;
|
return `${nodeId}${OUTPUT_UUID_KEY}${outputIndex}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInputEndpointUUID = (nodeIndex: string, inputIndex: number) => {
|
export const getInputEndpointUUID = (nodeId: string, inputIndex: number) => {
|
||||||
return `${nodeIndex}${INPUT_UUID_KEY}${inputIndex}`;
|
return `${nodeId}${INPUT_UUID_KEY}${inputIndex}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getFixedNodesList = (workflowNodes: INode[]) => {
|
export const getFixedNodesList = (workflowNodes: INode[]) => {
|
||||||
@@ -728,7 +729,7 @@ export const getFixedNodesList = (workflowNodes: INode[]) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!hasStartNode) {
|
if (!hasStartNode) {
|
||||||
nodes.push({...DEFAULT_START_NODE});
|
nodes.push({...DEFAULT_START_NODE, id: uuid() });
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -823,6 +823,7 @@ export interface INodeCredentials {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface INode {
|
export interface INode {
|
||||||
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
typeVersion: number;
|
typeVersion: number;
|
||||||
type: string;
|
type: string;
|
||||||
@@ -1542,6 +1543,7 @@ export interface INoteGraphItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface INodeGraphItem {
|
export interface INodeGraphItem {
|
||||||
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
resource?: string;
|
resource?: string;
|
||||||
operation?: string;
|
operation?: string;
|
||||||
@@ -1553,6 +1555,8 @@ export interface INodeGraphItem {
|
|||||||
credential_type?: string; // HTTP Request node v2
|
credential_type?: string; // HTTP Request node v2
|
||||||
credential_set?: boolean; // HTTP Request node v2
|
credential_set?: boolean; // HTTP Request node v2
|
||||||
method?: string; // HTTP Request node v2
|
method?: string; // HTTP Request node v2
|
||||||
|
src_node_id?: string;
|
||||||
|
src_instance_id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INodeNameIndex {
|
export interface INodeNameIndex {
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ export function isNumber(value: unknown): value is number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getStickyDimensions(note: INode, stickyType: INodeType | undefined) {
|
function getStickyDimensions(note: INode, stickyType: INodeType | undefined) {
|
||||||
const heightProperty = stickyType?.description.properties.find(
|
const heightProperty = stickyType?.description?.properties.find(
|
||||||
(property) => property.name === 'height',
|
(property) => property.name === 'height',
|
||||||
);
|
);
|
||||||
const widthProperty = stickyType?.description.properties.find(
|
const widthProperty = stickyType?.description?.properties.find(
|
||||||
(property) => property.name === 'width',
|
(property) => property.name === 'width',
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -114,6 +114,10 @@ export function getDomainPath(raw: string, urlParts = URL_PARTS_REGEX): string {
|
|||||||
export function generateNodesGraph(
|
export function generateNodesGraph(
|
||||||
workflow: IWorkflowBase,
|
workflow: IWorkflowBase,
|
||||||
nodeTypes: INodeTypes,
|
nodeTypes: INodeTypes,
|
||||||
|
options?: {
|
||||||
|
sourceInstanceId?: string;
|
||||||
|
nodeIdMap?: { [curr: string]: string };
|
||||||
|
},
|
||||||
): INodesGraphResult {
|
): INodesGraphResult {
|
||||||
const nodesGraph: INodesGraph = {
|
const nodesGraph: INodesGraph = {
|
||||||
node_types: [],
|
node_types: [],
|
||||||
@@ -149,10 +153,19 @@ export function generateNodesGraph(
|
|||||||
otherNodes.forEach((node: INode, index: number) => {
|
otherNodes.forEach((node: INode, index: number) => {
|
||||||
nodesGraph.node_types.push(node.type);
|
nodesGraph.node_types.push(node.type);
|
||||||
const nodeItem: INodeGraphItem = {
|
const nodeItem: INodeGraphItem = {
|
||||||
|
id: node.id,
|
||||||
type: node.type,
|
type: node.type,
|
||||||
position: node.position,
|
position: node.position,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (options?.sourceInstanceId) {
|
||||||
|
nodeItem.src_instance_id = options.sourceInstanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.id && options?.nodeIdMap && options.nodeIdMap[node.id]) {
|
||||||
|
nodeItem.src_node_id = options.nodeIdMap[node.id];
|
||||||
|
}
|
||||||
|
|
||||||
if (node.type === 'n8n-nodes-base.httpRequest' && node.typeVersion === 1) {
|
if (node.type === 'n8n-nodes-base.httpRequest' && node.typeVersion === 1) {
|
||||||
try {
|
try {
|
||||||
nodeItem.domain = new URL(node.parameters.url as string).hostname;
|
nodeItem.domain = new URL(node.parameters.url as string).hostname;
|
||||||
@@ -182,7 +195,7 @@ export function generateNodesGraph(
|
|||||||
} else {
|
} else {
|
||||||
const nodeType = nodeTypes.getByNameAndVersion(node.type);
|
const nodeType = nodeTypes.getByNameAndVersion(node.type);
|
||||||
|
|
||||||
nodeType?.description.properties.forEach((property) => {
|
nodeType?.description?.properties?.forEach((property) => {
|
||||||
if (
|
if (
|
||||||
property.name === 'operation' ||
|
property.name === 'operation' ||
|
||||||
property.name === 'resource' ||
|
property.name === 'resource' ||
|
||||||
@@ -212,7 +225,7 @@ export function generateNodesGraph(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (_) {
|
} catch (e) {
|
||||||
return { nodeGraph: nodesGraph, nameIndices: nodeNameAndIndex, webhookNodeNames };
|
return { nodeGraph: nodesGraph, nameIndices: nodeNameAndIndex, webhookNodeNames };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ describe('Expression', () => {
|
|||||||
name: 'node',
|
name: 'node',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
|
id: 'uuid-1234',
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
parameters: {}
|
parameters: {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -613,6 +613,7 @@ describe('RoutingNode', () => {
|
|||||||
name: 'test',
|
name: 'test',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1234',
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1659,6 +1660,7 @@ describe('RoutingNode', () => {
|
|||||||
name: 'test',
|
name: 'test',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1234',
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1831,6 +1833,7 @@ describe('RoutingNode', () => {
|
|||||||
name: 'test',
|
name: 'test',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1234',
|
||||||
position: [0, 0],
|
position: [0, 0],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -548,6 +548,7 @@ describe('Workflow', () => {
|
|||||||
parameters: stubData.parameters,
|
parameters: stubData.parameters,
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1234',
|
||||||
position: [100, 100],
|
position: [100, 100],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1008,6 +1009,7 @@ describe('Workflow', () => {
|
|||||||
parameters: testData.input.Node1.parameters,
|
parameters: testData.input.Node1.parameters,
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1',
|
||||||
position: [100, 100],
|
position: [100, 100],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1015,6 +1017,7 @@ describe('Workflow', () => {
|
|||||||
parameters: testData.input.Node2.parameters,
|
parameters: testData.input.Node2.parameters,
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-2',
|
||||||
position: [100, 200],
|
position: [100, 200],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1026,6 +1029,7 @@ describe('Workflow', () => {
|
|||||||
: {},
|
: {},
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-3',
|
||||||
position: [100, 300],
|
position: [100, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1037,6 +1041,7 @@ describe('Workflow', () => {
|
|||||||
: {},
|
: {},
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-4',
|
||||||
position: [100, 400],
|
position: [100, 400],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -1219,6 +1224,7 @@ describe('Workflow', () => {
|
|||||||
},
|
},
|
||||||
type: 'test.setMulti',
|
type: 'test.setMulti',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1234',
|
||||||
position: [100, 100],
|
position: [100, 100],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -1296,6 +1302,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1',
|
||||||
position: [240, 300],
|
position: [240, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1305,6 +1312,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Set',
|
name: 'Set',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-2',
|
||||||
position: [460, 300],
|
position: [460, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1314,6 +1322,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Set1',
|
name: 'Set1',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-3',
|
||||||
position: [680, 300],
|
position: [680, 300],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1353,6 +1362,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Switch',
|
name: 'Switch',
|
||||||
type: 'test.switch',
|
type: 'test.switch',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1',
|
||||||
position: [460, 300],
|
position: [460, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1362,6 +1372,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Set',
|
name: 'Set',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-2',
|
||||||
position: [740, 300],
|
position: [740, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1371,6 +1382,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Set1',
|
name: 'Set1',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-3',
|
||||||
position: [780, 100],
|
position: [780, 100],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1380,6 +1392,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Set2',
|
name: 'Set2',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-4',
|
||||||
position: [1040, 260],
|
position: [1040, 260],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -1443,6 +1456,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Switch',
|
name: 'Switch',
|
||||||
type: 'test.switch',
|
type: 'test.switch',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1',
|
||||||
position: [920, 340],
|
position: [920, 340],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1450,6 +1464,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-2',
|
||||||
position: [240, 300],
|
position: [240, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1459,6 +1474,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Set1',
|
name: 'Set1',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-3',
|
||||||
position: [700, 340],
|
position: [700, 340],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1468,6 +1484,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Set',
|
name: 'Set',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-4',
|
||||||
position: [1220, 300],
|
position: [1220, 300],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1475,6 +1492,7 @@ describe('Workflow', () => {
|
|||||||
name: 'Switch',
|
name: 'Switch',
|
||||||
type: 'test.switch',
|
type: 'test.switch',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-5',
|
||||||
position: [920, 340],
|
position: [920, 340],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ describe('WorkflowDataProxy', () => {
|
|||||||
name: 'Start',
|
name: 'Start',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-1',
|
||||||
position: [100, 200],
|
position: [100, 200],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -20,6 +21,7 @@ describe('WorkflowDataProxy', () => {
|
|||||||
name: 'Function',
|
name: 'Function',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-2',
|
||||||
position: [280, 200],
|
position: [280, 200],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -36,6 +38,7 @@ describe('WorkflowDataProxy', () => {
|
|||||||
name: 'Rename',
|
name: 'Rename',
|
||||||
type: 'test.set',
|
type: 'test.set',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
|
id: 'uuid-3',
|
||||||
position: [460, 200],
|
position: [460, 200],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user