diff --git a/packages/cli/commands/import/workflow.ts b/packages/cli/commands/import/workflow.ts index 2f53feae64..34a5c97b8c 100644 --- a/packages/cli/commands/import/workflow.ts +++ b/packages/cli/commands/import/workflow.ts @@ -17,6 +17,7 @@ import fs from 'fs'; import glob from 'fast-glob'; import { UserSettings } from 'n8n-core'; import { EntityManager, getConnection } from 'typeorm'; +import { v4 as uuid } from 'uuid'; import { getLogger } from '../../src/Logger'; import { Db, ICredentialsDb, IWorkflowToImport } from '../../src'; import { SharedWorkflow } from '../../src/databases/entities/SharedWorkflow'; @@ -129,6 +130,11 @@ export class ImportWorkflowsCommand extends Command { if (credentials.length > 0) { workflow.nodes.forEach((node: INode) => { 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) { workflow.nodes.forEach((node: INode) => { this.transformCredentials(node, credentials); + + if (!node.id) { + // eslint-disable-next-line no-param-reassign + node.id = uuid(); + } }); } diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index f5423ae09f..2bcf2566b0 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -586,6 +586,7 @@ export class CredentialsHelper extends ICredentialsHelper { } const node: INode = { + id: 'temp', parameters: {}, name: 'Temp-Node', type: nodeType.description.name, diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/spec/schemas/node.yml b/packages/cli/src/PublicApi/v1/handlers/workflows/spec/schemas/node.yml index 10d1a06808..c57b7febfd 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/spec/schemas/node.yml +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/spec/schemas/node.yml @@ -1,6 +1,9 @@ type: object additionalProperties: false properties: + id: + type: string + example: 0f5532f9-36ba-4bef-86c7-30d607400b15 name: type: string example: Jira diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts index d45a33eeaf..dd1509c57e 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts @@ -7,7 +7,7 @@ import config = require('../../../../../config'); import { WorkflowEntity } from '../../../../databases/entities/WorkflowEntity'; import { InternalHooksManager } from '../../../../InternalHooksManager'; import { externalHooks } from '../../../../Server'; -import { replaceInvalidCredentials } from '../../../../WorkflowHelpers'; +import { addNodeIds, replaceInvalidCredentials } from '../../../../WorkflowHelpers'; import { WorkflowRequest } from '../../../types'; import { authorize, validCursor } from '../../shared/middlewares/global.middleware'; import { encodeNextCursor } from '../../shared/services/pagination.service'; @@ -42,6 +42,8 @@ export = { await replaceInvalidCredentials(workflow); + addNodeIds(workflow); + const role = await getWorkflowOwnerRole(); const createdWorkflow = await createWorkflow(workflow, req.user, role); @@ -186,6 +188,7 @@ export = { } await replaceInvalidCredentials(updateData); + addNodeIds(updateData); const workflowRunner = ActiveWorkflowRunner.getInstance(); diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts index 494e7389bf..bb4760d797 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -1,6 +1,7 @@ import { FindManyOptions, In, UpdateResult } from 'typeorm'; import intersection from 'lodash.intersection'; import type { INode } from 'n8n-workflow'; +import { v4 as uuid } from 'uuid'; import { Db } from '../../../..'; import { User } from '../../../../databases/entities/User'; @@ -133,6 +134,7 @@ export function hasStartNode(workflow: WorkflowEntity): boolean { export function getStartNode(): INode { return { + id: uuid(), parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index b9698f44f1..379fbb3130 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -924,6 +924,8 @@ class App { // check credentials for old format await WorkflowHelpers.replaceInvalidCredentials(updateData); + WorkflowHelpers.addNodeIds(updateData); + await this.externalHooks.run('workflow.update', [updateData]); if (shared.workflow.active) { diff --git a/packages/cli/src/WorkflowHelpers.ts b/packages/cli/src/WorkflowHelpers.ts index e7af8c1b03..8e0996cd5d 100644 --- a/packages/cli/src/WorkflowHelpers.ts +++ b/packages/cli/src/WorkflowHelpers.ts @@ -21,6 +21,7 @@ import { LoggerProxy as Logger, Workflow, } from 'n8n-workflow'; +import { v4 as uuid } from 'uuid'; // eslint-disable-next-line import/no-cycle import { CredentialTypes, @@ -474,6 +475,22 @@ export async function getStaticDataById(workflowId: string | number) { 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 export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promise { const { nodes } = workflow; diff --git a/packages/cli/src/api/workflows.api.ts b/packages/cli/src/api/workflows.api.ts index 076715751d..16c5151963 100644 --- a/packages/cli/src/api/workflows.api.ts +++ b/packages/cli/src/api/workflows.api.ts @@ -43,6 +43,8 @@ workflowsController.post( await WorkflowHelpers.replaceInvalidCredentials(newWorkflow); + WorkflowHelpers.addNodeIds(newWorkflow); + let savedWorkflow: undefined | WorkflowEntity; await Db.transaction(async (transactionManager) => { diff --git a/packages/cli/src/databases/migrations/mysqldb/1658932910559-AddNodeIds.ts b/packages/cli/src/databases/migrations/mysqldb/1658932910559-AddNodeIds.ts new file mode 100644 index 0000000000..6dbc16623a --- /dev/null +++ b/packages/cli/src/databases/migrations/mysqldb/1658932910559-AddNodeIds.ts @@ -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 { + 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 { + 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); + }); + }); + } +} diff --git a/packages/cli/src/databases/migrations/mysqldb/index.ts b/packages/cli/src/databases/migrations/mysqldb/index.ts index bb241524f7..d4c13c4e6c 100644 --- a/packages/cli/src/databases/migrations/mysqldb/index.ts +++ b/packages/cli/src/databases/migrations/mysqldb/index.ts @@ -17,6 +17,7 @@ import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings'; import { CommunityNodes1652254514003 } from './1652254514003-CommunityNodes'; import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn'; import { IntroducePinData1654090101303 } from './1654090101303-IntroducePinData'; +import { AddNodeIds1658932910559 } from './1658932910559-AddNodeIds'; export const mysqlMigrations = [ InitialMigration1588157391238, @@ -38,4 +39,5 @@ export const mysqlMigrations = [ CommunityNodes1652254514003, AddAPIKeyColumn1652905585850, IntroducePinData1654090101303, + AddNodeIds1658932910559, ]; diff --git a/packages/cli/src/databases/migrations/postgresdb/1658932090381-AddNodeIds.ts b/packages/cli/src/databases/migrations/postgresdb/1658932090381-AddNodeIds.ts new file mode 100644 index 0000000000..3571693fa4 --- /dev/null +++ b/packages/cli/src/databases/migrations/postgresdb/1658932090381-AddNodeIds.ts @@ -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 { + 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 { + 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); + }); + }); + } +} diff --git a/packages/cli/src/databases/migrations/postgresdb/index.ts b/packages/cli/src/databases/migrations/postgresdb/index.ts index ebd0a9df3f..39a56af669 100644 --- a/packages/cli/src/databases/migrations/postgresdb/index.ts +++ b/packages/cli/src/databases/migrations/postgresdb/index.ts @@ -15,6 +15,7 @@ import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings'; import { CommunityNodes1652254514002 } from './1652254514002-CommunityNodes'; import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn'; import { IntroducePinData1654090467022 } from './1654090467022-IntroducePinData'; +import { AddNodeIds1658932090381 } from './1658932090381-AddNodeIds'; export const postgresMigrations = [ InitialMigration1587669153312, @@ -34,4 +35,5 @@ export const postgresMigrations = [ CommunityNodes1652254514002, AddAPIKeyColumn1652905585850, IntroducePinData1654090467022, + AddNodeIds1658932090381, ]; diff --git a/packages/cli/src/databases/migrations/sqlite/1658930531669-AddNodeIds.ts b/packages/cli/src/databases/migrations/sqlite/1658930531669-AddNodeIds.ts new file mode 100644 index 0000000000..d772d5c47b --- /dev/null +++ b/packages/cli/src/databases/migrations/sqlite/1658930531669-AddNodeIds.ts @@ -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 { + 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 { + 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); + }); + }); + } +} diff --git a/packages/cli/src/databases/migrations/sqlite/index.ts b/packages/cli/src/databases/migrations/sqlite/index.ts index 83b938b96b..41efea5c74 100644 --- a/packages/cli/src/databases/migrations/sqlite/index.ts +++ b/packages/cli/src/databases/migrations/sqlite/index.ts @@ -14,6 +14,7 @@ import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings'; import { CommunityNodes1652254514001 } from './1652254514001-CommunityNodes' import { AddAPIKeyColumn1652905585850 } from './1652905585850-AddAPIKeyColumn'; import { IntroducePinData1654089251344 } from './1654089251344-IntroducePinData'; +import { AddNodeIds1658930531669 } from './1658930531669-AddNodeIds'; const sqliteMigrations = [ InitialMigration1588102412422, @@ -32,6 +33,7 @@ const sqliteMigrations = [ CommunityNodes1652254514001, AddAPIKeyColumn1652905585850, IntroducePinData1654089251344, + AddNodeIds1658930531669, ]; export { sqliteMigrations }; diff --git a/packages/cli/test/integration/publicApi/workflows.test.ts b/packages/cli/test/integration/publicApi/workflows.test.ts index 9d01a927f2..7f24459444 100644 --- a/packages/cli/test/integration/publicApi/workflows.test.ts +++ b/packages/cli/test/integration/publicApi/workflows.test.ts @@ -951,6 +951,7 @@ test('POST /workflows should create workflow', async () => { name: 'testing', nodes: [ { + id: 'uuid-1234', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -1047,6 +1048,7 @@ test('PUT /workflows/:id should fail due to non-existing workflow', async () => name: 'testing', nodes: [ { + id: 'uuid-1234', parameters: {}, name: '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({ nodes: [ { + id: 'uuid-1234', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -1120,6 +1123,7 @@ test('PUT /workflows/:id should update workflow', async () => { name: 'name updated', nodes: [ { + id: 'uuid-1234', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -1127,6 +1131,7 @@ test('PUT /workflows/:id should update workflow', async () => { position: [240, 300], }, { + id: 'uuid-1234', parameters: {}, name: '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', nodes: [ { + id: 'uuid-1', parameters: {}, name: '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], }, { + id: 'uuid-2', parameters: {}, name: 'Cron', type: 'n8n-nodes-base.cron', diff --git a/packages/cli/test/integration/shared/testDb.ts b/packages/cli/test/integration/shared/testDb.ts index 309e0bebd6..be3886b118 100644 --- a/packages/cli/test/integration/shared/testDb.ts +++ b/packages/cli/test/integration/shared/testDb.ts @@ -522,6 +522,7 @@ export async function createWorkflow(attributes: Partial = {}, u name: name ?? 'test workflow', nodes: nodes ?? [ { + id: 'uuid-1234', name: 'Start', parameters: {}, position: [-20, 260], @@ -555,6 +556,7 @@ export async function createWorkflowWithTrigger( { nodes: [ { + id: 'uuid-1', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -562,6 +564,7 @@ export async function createWorkflowWithTrigger( position: [240, 300], }, { + id: 'uuid-2', parameters: { triggerTimes: { item: [{ mode: 'everyMinute' }] } }, name: 'Cron', type: 'n8n-nodes-base.cron', @@ -569,6 +572,7 @@ export async function createWorkflowWithTrigger( position: [500, 300], }, { + id: 'uuid-3', parameters: { options: {} }, name: 'Set', type: 'n8n-nodes-base.set', diff --git a/packages/cli/test/integration/workflows.api.test.ts b/packages/cli/test/integration/workflows.api.test.ts index ac49f0107e..8dbf2863b5 100644 --- a/packages/cli/test/integration/workflows.api.test.ts +++ b/packages/cli/test/integration/workflows.api.test.ts @@ -91,6 +91,7 @@ function makeWorkflow({ withPinData }: { withPinData: boolean }) { workflow.connections = {}; workflow.nodes = [ { + id: 'uuid-1234', name: 'Spotify', type: 'n8n-nodes-base.spotify', parameters: { resource: 'track', operation: 'get', id: '123' }, diff --git a/packages/cli/test/unit/CredentialsHelper.test.ts b/packages/cli/test/unit/CredentialsHelper.test.ts index afc4a1e2e4..b766804000 100644 --- a/packages/cli/test/unit/CredentialsHelper.test.ts +++ b/packages/cli/test/unit/CredentialsHelper.test.ts @@ -194,6 +194,7 @@ describe('CredentialsHelper', () => { ]; const node: INode = { + id: 'uuid-1', parameters: {}, name: 'test', type: 'test.set', diff --git a/packages/core/src/LoadNodeParameterOptions.ts b/packages/core/src/LoadNodeParameterOptions.ts index c38983c7f9..bdf6e09c04 100644 --- a/packages/core/src/LoadNodeParameterOptions.ts +++ b/packages/core/src/LoadNodeParameterOptions.ts @@ -58,6 +58,7 @@ export class LoadNodeParameterOptions { const nodeData: INode = { parameters: currentNodeParameters, + id: 'uuid-1234', name: TEMP_NODE_NAME, type: nodeTypeNameAndVersion.name, typeVersion: nodeTypeNameAndVersion.version, diff --git a/packages/core/test/WorkflowExecute.test.ts b/packages/core/test/WorkflowExecute.test.ts index f249ebe52f..54873ca8ba 100644 --- a/packages/core/test/WorkflowExecute.test.ts +++ b/packages/core/test/WorkflowExecute.test.ts @@ -37,6 +37,7 @@ describe('WorkflowExecute', () => { workflowData: { nodes: [ { + id: 'uuid-1', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -44,6 +45,7 @@ describe('WorkflowExecute', () => { position: [100, 300], }, { + id: 'uuid-2', parameters: { values: { number: [ @@ -96,6 +98,7 @@ describe('WorkflowExecute', () => { workflowData: { nodes: [ { + id: 'uuid-1', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -103,6 +106,7 @@ describe('WorkflowExecute', () => { position: [100, 300], }, { + id: 'uuid-2', parameters: { values: { number: [ @@ -119,6 +123,7 @@ describe('WorkflowExecute', () => { position: [300, 250], }, { + id: 'uuid-3', parameters: { values: { number: [ @@ -200,6 +205,7 @@ describe('WorkflowExecute', () => { workflowData: { nodes: [ { + id: 'uuid-1', parameters: { mode: 'passThrough', }, @@ -209,6 +215,7 @@ describe('WorkflowExecute', () => { position: [1150, 500], }, { + id: 'uuid-2', parameters: { values: { number: [ @@ -225,6 +232,7 @@ describe('WorkflowExecute', () => { position: [290, 400], }, { + id: 'uuid-3', parameters: { values: { number: [ @@ -241,6 +249,7 @@ describe('WorkflowExecute', () => { position: [850, 200], }, { + id: 'uuid-4', parameters: { values: { number: [ @@ -257,6 +266,7 @@ describe('WorkflowExecute', () => { position: [650, 200], }, { + id: 'uuid-5', parameters: { mode: 'passThrough', }, @@ -266,6 +276,7 @@ describe('WorkflowExecute', () => { position: [1150, 500], }, { + id: 'uuid-6', parameters: {}, name: 'Merge3', type: 'n8n-nodes-base.merge', @@ -273,6 +284,7 @@ describe('WorkflowExecute', () => { position: [1000, 400], }, { + id: 'uuid-7', parameters: { mode: 'passThrough', output: 'input2', @@ -283,6 +295,7 @@ describe('WorkflowExecute', () => { position: [700, 400], }, { + id: 'uuid-8', parameters: {}, name: 'Merge1', type: 'n8n-nodes-base.merge', @@ -290,6 +303,7 @@ describe('WorkflowExecute', () => { position: [500, 300], }, { + id: 'uuid-9', parameters: { values: { number: [ @@ -306,6 +320,7 @@ describe('WorkflowExecute', () => { position: [300, 200], }, { + id: 'uuid-10', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -526,6 +541,7 @@ describe('WorkflowExecute', () => { name: 'Start', type: 'n8n-nodes-base.start', typeVersion: 1, + id: 'uuid-1', position: [250, 450], }, { @@ -543,6 +559,7 @@ describe('WorkflowExecute', () => { name: 'IF', type: 'n8n-nodes-base.if', typeVersion: 1, + id: 'uuid-2', position: [650, 350], }, { @@ -550,6 +567,7 @@ describe('WorkflowExecute', () => { name: 'Merge1', type: 'n8n-nodes-base.merge', typeVersion: 1, + id: 'uuid-3', position: [1150, 450], }, { @@ -567,6 +585,7 @@ describe('WorkflowExecute', () => { name: 'Set1', type: 'n8n-nodes-base.set', typeVersion: 1, + id: 'uuid-4', position: [450, 450], }, { @@ -584,6 +603,7 @@ describe('WorkflowExecute', () => { name: 'Set2', type: 'n8n-nodes-base.set', typeVersion: 1, + id: 'uuid-1', position: [800, 250], }, ], @@ -672,6 +692,7 @@ describe('WorkflowExecute', () => { workflowData: { nodes: [ { + id: 'uuid-1', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -679,6 +700,7 @@ describe('WorkflowExecute', () => { position: [250, 300], }, { + id: 'uuid-2', parameters: {}, name: 'Merge', type: 'n8n-nodes-base.merge', @@ -686,6 +708,7 @@ describe('WorkflowExecute', () => { position: [800, 450], }, { + id: 'uuid-3', parameters: {}, name: 'Merge1', type: 'n8n-nodes-base.merge', @@ -693,6 +716,7 @@ describe('WorkflowExecute', () => { position: [1000, 300], }, { + id: 'uuid-4', parameters: { conditions: { boolean: [ @@ -716,6 +740,7 @@ describe('WorkflowExecute', () => { alwaysOutputData: false, }, { + id: 'uuid-5', parameters: { values: { number: [ @@ -738,6 +763,7 @@ describe('WorkflowExecute', () => { position: [450, 300], }, { + id: 'uuid-6', parameters: { values: { number: [ @@ -761,6 +787,7 @@ describe('WorkflowExecute', () => { position: [450, 450], }, { + id: 'uuid-7', parameters: { values: { number: [ @@ -889,6 +916,7 @@ describe('WorkflowExecute', () => { workflowData: { nodes: [ { + id: 'uuid-1', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -896,6 +924,7 @@ describe('WorkflowExecute', () => { position: [250, 300], }, { + id: 'uuid-2', parameters: { conditions: { number: [ @@ -913,6 +942,7 @@ describe('WorkflowExecute', () => { position: [650, 300], }, { + id: 'uuid-3', parameters: { values: { string: [], @@ -931,6 +961,7 @@ describe('WorkflowExecute', () => { position: [850, 450], }, { + id: 'uuid-4', parameters: { values: { number: [ @@ -948,6 +979,7 @@ describe('WorkflowExecute', () => { position: [450, 300], }, { + id: 'uuid-5', parameters: {}, name: 'Merge', type: 'n8n-nodes-base.merge', @@ -1034,6 +1066,7 @@ describe('WorkflowExecute', () => { workflowData: { nodes: [ { + id: 'uuid-1', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -1041,6 +1074,7 @@ describe('WorkflowExecute', () => { position: [250, 300], }, { + id: 'uuid-2', parameters: { values: { number: [ @@ -1057,6 +1091,7 @@ describe('WorkflowExecute', () => { position: [450, 300], }, { + id: 'uuid-3', parameters: {}, name: 'Merge', type: 'n8n-nodes-base.merge', @@ -1064,6 +1099,7 @@ describe('WorkflowExecute', () => { position: [1050, 250], }, { + id: 'uuid-4', parameters: { conditions: { number: [ @@ -1081,6 +1117,7 @@ describe('WorkflowExecute', () => { position: [650, 300], }, { + id: 'uuid-5', parameters: {}, name: 'NoOpTrue', type: 'n8n-nodes-base.noOp', @@ -1088,6 +1125,7 @@ describe('WorkflowExecute', () => { position: [850, 150], }, { + id: 'uuid-6', parameters: {}, name: 'NoOpFalse', type: 'n8n-nodes-base.noOp', @@ -1177,6 +1215,7 @@ describe('WorkflowExecute', () => { workflowData: { nodes: [ { + id: 'uuid-1', parameters: {}, name: 'Start', type: 'n8n-nodes-base.start', @@ -1184,6 +1223,7 @@ describe('WorkflowExecute', () => { position: [240, 300], }, { + id: 'uuid-2', parameters: {}, name: 'VersionTest1a', type: 'n8n-nodes-base.versionTest', @@ -1191,6 +1231,7 @@ describe('WorkflowExecute', () => { position: [460, 300], }, { + id: 'uuid-3', parameters: { versionTest: 11, }, @@ -1200,6 +1241,7 @@ describe('WorkflowExecute', () => { position: [680, 300], }, { + id: 'uuid-4', parameters: {}, name: 'VersionTest2a', type: 'n8n-nodes-base.versionTest', @@ -1207,6 +1249,7 @@ describe('WorkflowExecute', () => { position: [880, 300], }, { + id: 'uuid-5', parameters: { versionTest: 22, }, diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 7f9d0c914f..024123b56b 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -259,6 +259,12 @@ export interface IWorkflowDataUpdate { pinData?: IPinData; } +export interface IWorkflowToShare extends IWorkflowDataUpdate { + meta?: { + instanceId: string; + }; +} + export interface IWorkflowTemplate { id: number; name: string; @@ -866,7 +872,6 @@ export interface IRootState { workflowExecutionData: IExecutionResponse | null; lastSelectedNode: string | null; lastSelectedNodeOutputIndex: number | null; - nodeIndex: Array; nodeViewOffsetPosition: XYPosition; nodeViewMoveInProgress: boolean; selectedNodes: INodeUi[]; diff --git a/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue b/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue index b64c93b535..f6678371f3 100644 --- a/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue +++ b/packages/editor-ui/src/components/DuplicateWorkflowDialog.vue @@ -117,7 +117,7 @@ export default mixins(showMessage, workflowHelpers).extend({ 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) { this.closeDialog(); diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index 886aa265ce..fcf8ce29d1 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -183,7 +183,7 @@ import { IExecutionResponse, IWorkflowDataUpdate, IMenuItem, - IUser, + IWorkflowToShare, } from '../Interface'; import ExecutionsList from '@/components/ExecutionsList.vue'; @@ -442,7 +442,6 @@ export default mixins( return; } - this.$telemetry.track('User imported workflow', { source: 'file', workflow_id: this.$store.getters.workflowId }); this.$root.$emit('importWorkflowData', { data: worflowData }); }; @@ -513,8 +512,11 @@ export default mixins( data.id = parseInt(data.id, 10); } - const exportData: IWorkflowDataUpdate = { + const exportData: IWorkflowToShare = { ...data, + meta: { + instanceId: this.$store.getters.instanceId, + }, tags: (tags || []).map(tagId => { const {usageCount, ...tag} = this.$store.getters["tags/getTagById"](tagId); diff --git a/packages/editor-ui/src/components/Node.vue b/packages/editor-ui/src/components/Node.vue index 6626662d33..2a336bb7d0 100644 --- a/packages/editor-ui/src/components/Node.vue +++ b/packages/editor-ui/src/components/Node.vue @@ -1,5 +1,5 @@