diff --git a/packages/cli/src/__tests__/workflow-runner.test.ts b/packages/cli/src/__tests__/workflow-runner.test.ts index 1de4ddef6b..0ea0646276 100644 --- a/packages/cli/src/__tests__/workflow-runner.test.ts +++ b/packages/cli/src/__tests__/workflow-runner.test.ts @@ -9,6 +9,7 @@ import type { ITaskData, IWaitingForExecution, IWaitingForExecutionSource, + IWorkflowBase, IWorkflowExecutionDataProcess, StartNodeData, } from 'n8n-workflow'; @@ -19,7 +20,6 @@ import { ActiveExecutions } from '@/active-executions'; import config from '@/config'; import type { ExecutionEntity } from '@/databases/entities/execution-entity'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ExecutionNotFoundError } from '@/errors/execution-not-found-error'; import { Telemetry } from '@/telemetry'; import { PermissionChecker } from '@/user-management/permission-checker'; @@ -53,7 +53,7 @@ beforeEach(async () => { }); describe('processError', () => { - let workflow: WorkflowEntity; + let workflow: IWorkflowBase; let execution: ExecutionEntity; let hooks: core.ExecutionLifecycleHooks; diff --git a/packages/cli/src/active-workflow-manager.ts b/packages/cli/src/active-workflow-manager.ts index 2f47212561..536f9bf268 100644 --- a/packages/cli/src/active-workflow-manager.ts +++ b/packages/cli/src/active-workflow-manager.ts @@ -39,13 +39,11 @@ import { WORKFLOW_REACTIVATE_INITIAL_TIMEOUT, WORKFLOW_REACTIVATE_MAX_TIMEOUT, } from '@/constants'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { OnShutdown } from '@/decorators/on-shutdown'; import { executeErrorWorkflow } from '@/execution-lifecycle/execute-error-workflow'; import { ExecutionService } from '@/executions/execution.service'; import { ExternalHooks } from '@/external-hooks'; -import type { IWorkflowDb } from '@/interfaces'; import { NodeTypes } from '@/node-types'; import { Publisher } from '@/scaling/pubsub/publisher.service'; import { ActiveWorkflowsService } from '@/services/active-workflows.service'; @@ -55,12 +53,13 @@ import { WebhookService } from '@/webhooks/webhook.service'; import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data'; import { WorkflowExecutionService } from '@/workflows/workflow-execution.service'; import { WorkflowStaticDataService } from '@/workflows/workflow-static-data.service'; +import { formatWorkflow } from '@/workflows/workflow.formatter'; interface QueuedActivation { activationMode: WorkflowActivateMode; lastTimeout: number; timeout: NodeJS.Timeout; - workflowData: IWorkflowDb; + workflowData: IWorkflowBase; } @Service() @@ -271,7 +270,7 @@ export class ActiveWorkflowManager { * and overwrites the emit to be able to start it in subprocess */ getExecutePollFunctions( - workflowData: IWorkflowDb, + workflowData: IWorkflowBase, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, @@ -322,7 +321,7 @@ export class ActiveWorkflowManager { * and overwrites the emit to be able to start it in subprocess */ getExecuteTriggerFunctions( - workflowData: IWorkflowDb, + workflowData: IWorkflowBase, additionalData: IWorkflowExecuteAdditionalData, mode: WorkflowExecuteMode, activation: WorkflowActivateMode, @@ -379,7 +378,7 @@ export class ActiveWorkflowManager { ); this.executeErrorWorkflow(activationError, workflowData, mode); - this.addQueuedWorkflowActivation(activation, workflowData as WorkflowEntity); + this.addQueuedWorkflowActivation(activation, workflowData); }; return new TriggerContext(workflow, node, additionalData, mode, activation, emit, emitError); }; @@ -436,7 +435,7 @@ export class ActiveWorkflowManager { } private async activateWorkflow( - dbWorkflow: WorkflowEntity, + dbWorkflow: IWorkflowBase, activationMode: 'init' | 'leadershipChange', ) { try { @@ -444,9 +443,9 @@ export class ActiveWorkflowManager { shouldPublish: false, }); if (wasActivated) { - this.logger.info(` - ${dbWorkflow.display()})`); + this.logger.info(` - ${formatWorkflow(dbWorkflow)})`); this.logger.info(' => Started'); - this.logger.debug(`Successfully started workflow ${dbWorkflow.display()}`, { + this.logger.debug(`Successfully started workflow ${formatWorkflow(dbWorkflow)}`, { workflowName: dbWorkflow.name, workflowId: dbWorkflow.id, }); @@ -454,12 +453,12 @@ export class ActiveWorkflowManager { } catch (error) { this.errorReporter.error(error); this.logger.info( - ` => ERROR: Workflow ${dbWorkflow.display()} could not be activated on first try, keep on trying if not an auth issue`, + ` => ERROR: Workflow ${formatWorkflow(dbWorkflow)} could not be activated on first try, keep on trying if not an auth issue`, ); this.logger.info(` ${error.message}`); this.logger.error( - `Issue on initial workflow activation try of ${dbWorkflow.display()} (startup)`, + `Issue on initial workflow activation try of ${formatWorkflow(dbWorkflow)} (startup)`, { workflowName: dbWorkflow.name, workflowId: dbWorkflow.id, @@ -518,7 +517,7 @@ export class ActiveWorkflowManager { async add( workflowId: string, activationMode: WorkflowActivateMode, - existingWorkflow?: WorkflowEntity, + existingWorkflow?: IWorkflowBase, { shouldPublish } = { shouldPublish: true }, ) { if (this.instanceSettings.isMultiMain && shouldPublish) { @@ -549,7 +548,7 @@ export class ActiveWorkflowManager { } if (shouldDisplayActivationMessage) { - this.logger.debug(`Initializing active workflow ${dbWorkflow.display()} (startup)`, { + this.logger.debug(`Initializing active workflow ${formatWorkflow(dbWorkflow)} (startup)`, { workflowName: dbWorkflow.name, workflowId: dbWorkflow.id, }); @@ -570,7 +569,7 @@ export class ActiveWorkflowManager { if (!canBeActivated) { throw new WorkflowActivationError( - `Workflow ${dbWorkflow.display()} has no node to start the workflow - at least one trigger, poller or webhook node is required`, + `Workflow ${formatWorkflow(dbWorkflow)} has no node to start the workflow - at least one trigger, poller or webhook node is required`, { level: 'warning' }, ); } @@ -673,7 +672,7 @@ export class ActiveWorkflowManager { */ private addQueuedWorkflowActivation( activationMode: WorkflowActivateMode, - workflowData: WorkflowEntity, + workflowData: IWorkflowBase, ) { const workflowId = workflowData.id; const workflowName = workflowData.name; @@ -811,7 +810,7 @@ export class ActiveWorkflowManager { * Register as active in memory a trigger- or poller-based workflow. */ async addTriggersAndPollers( - dbWorkflow: WorkflowEntity, + dbWorkflow: IWorkflowBase, workflow: Workflow, { activationMode, @@ -838,7 +837,7 @@ export class ActiveWorkflowManager { ); if (workflow.getTriggerNodes().length !== 0 || workflow.getPollNodes().length !== 0) { - this.logger.debug(`Adding triggers and pollers for workflow ${dbWorkflow.display()}`); + this.logger.debug(`Adding triggers and pollers for workflow ${formatWorkflow(dbWorkflow)}`); await this.activeWorkflows.add( workflow.id, @@ -850,7 +849,7 @@ export class ActiveWorkflowManager { getPollFunctions, ); - this.logger.debug(`Workflow ${dbWorkflow.display()} activated`, { + this.logger.debug(`Workflow ${formatWorkflow(dbWorkflow)} activated`, { workflowId: dbWorkflow.id, workflowName: dbWorkflow.name, }); diff --git a/packages/cli/src/commands/execute-batch.ts b/packages/cli/src/commands/execute-batch.ts index 883cd7068d..3116186025 100644 --- a/packages/cli/src/commands/execute-batch.ts +++ b/packages/cli/src/commands/execute-batch.ts @@ -4,7 +4,7 @@ import { Flags } from '@oclif/core'; import fs from 'fs'; import { diff } from 'json-diff'; import pick from 'lodash/pick'; -import type { IRun, ITaskData, IWorkflowExecutionDataProcess } from 'n8n-workflow'; +import type { IRun, ITaskData, IWorkflowBase, IWorkflowExecutionDataProcess } from 'n8n-workflow'; import { ApplicationError, jsonParse } from 'n8n-workflow'; import os from 'os'; import { sep } from 'path'; @@ -12,7 +12,6 @@ import { sep } from 'path'; import { ActiveExecutions } from '@/active-executions'; import type { User } from '@/databases/entities/user'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; -import type { IWorkflowDb } from '@/interfaces'; import { OwnershipService } from '@/services/ownership.service'; import { findCliWorkflowStart } from '@/utils'; import { WorkflowRunner } from '@/workflow-runner'; @@ -275,7 +274,7 @@ export class ExecuteBatch extends BaseCommand { query.andWhere('workflows.id not in (:...skipIds)', { skipIds }); } - const allWorkflows = (await query.getMany()) as IWorkflowDb[]; + const allWorkflows = (await query.getMany()) as IWorkflowBase[]; if (ExecuteBatch.debug) { process.stdout.write(`Found ${allWorkflows.length} workflows to execute.\n`); @@ -378,7 +377,7 @@ export class ExecuteBatch extends BaseCommand { }); } - private async runTests(allWorkflows: IWorkflowDb[]): Promise { + private async runTests(allWorkflows: IWorkflowBase[]): Promise { const result: IResult = { totalWorkflows: allWorkflows.length, slackMessage: '', @@ -401,7 +400,7 @@ export class ExecuteBatch extends BaseCommand { const promisesArray = []; for (let i = 0; i < ExecuteBatch.concurrency; i++) { const promise = new Promise(async (resolve) => { - let workflow: IWorkflowDb | undefined; + let workflow: IWorkflowBase | undefined; while (allWorkflows.length > 0) { workflow = allWorkflows.shift(); if (ExecuteBatch.cancelled) { @@ -563,7 +562,7 @@ export class ExecuteBatch extends BaseCommand { } } - async startThread(workflowData: IWorkflowDb): Promise { + async startThread(workflowData: IWorkflowBase): Promise { // This will be the object returned by the promise. // It will be updated according to execution progress below. const executionResult: IExecutionResult = { diff --git a/packages/cli/src/commands/import/workflow.ts b/packages/cli/src/commands/import/workflow.ts index d8813485a1..6d33df6c02 100644 --- a/packages/cli/src/commands/import/workflow.ts +++ b/packages/cli/src/commands/import/workflow.ts @@ -2,6 +2,7 @@ import { Container } from '@n8n/di'; import { Flags } from '@oclif/core'; import glob from 'fast-glob'; import fs from 'fs'; +import type { IWorkflowBase, WorkflowId } from 'n8n-workflow'; import { ApplicationError, jsonParse } from 'n8n-workflow'; import { UM_FIX_INSTRUCTION } from '@/constants'; @@ -102,7 +103,7 @@ export class ImportWorkflowsCommand extends BaseCommand { this.reportSuccess(workflows.length); } - private async checkRelations(workflows: WorkflowEntity[], projectId?: string, userId?: string) { + private async checkRelations(workflows: IWorkflowBase[], projectId?: string, userId?: string) { // The credential is not supposed to be re-owned. if (!userId && !projectId) { return { @@ -112,11 +113,11 @@ export class ImportWorkflowsCommand extends BaseCommand { } for (const workflow of workflows) { - if (!(await this.workflowExists(workflow))) { + if (!(await this.workflowExists(workflow.id))) { continue; } - const { user, project: ownerProject } = await this.getWorkflowOwner(workflow); + const { user, project: ownerProject } = await this.getWorkflowOwner(workflow.id); if (!ownerProject) { continue; @@ -155,9 +156,9 @@ export class ImportWorkflowsCommand extends BaseCommand { this.logger.info(`Successfully imported ${total} ${total === 1 ? 'workflow.' : 'workflows.'}`); } - private async getWorkflowOwner(workflow: WorkflowEntity) { + private async getWorkflowOwner(workflowId: WorkflowId) { const sharing = await Container.get(SharedWorkflowRepository).findOne({ - where: { workflowId: workflow.id, role: 'workflow:owner' }, + where: { workflowId, role: 'workflow:owner' }, relations: { project: true }, }); @@ -175,8 +176,8 @@ export class ImportWorkflowsCommand extends BaseCommand { return {}; } - private async workflowExists(workflow: WorkflowEntity) { - return await Container.get(WorkflowRepository).existsBy({ id: workflow.id }); + private async workflowExists(workflowId: WorkflowId) { + return await Container.get(WorkflowRepository).existsBy({ id: workflowId }); } private async readWorkflows(path: string, separate: boolean): Promise { diff --git a/packages/cli/src/databases/entities/workflow-entity.ts b/packages/cli/src/databases/entities/workflow-entity.ts index d6f0a615fa..177fa4771c 100644 --- a/packages/cli/src/databases/entities/workflow-entity.ts +++ b/packages/cli/src/databases/entities/workflow-entity.ts @@ -104,10 +104,6 @@ export class WorkflowEntity extends WithTimestampsAndStringId implements IWorkfl }) @JoinColumn({ name: 'parentFolderId' }) parentFolder: Folder | null; - - display() { - return `"${this.name}" (ID: ${this.id})`; - } } /** diff --git a/packages/cli/src/databases/repositories/tag.repository.ts b/packages/cli/src/databases/repositories/tag.repository.ts index f58b0e5b68..b6d1400920 100644 --- a/packages/cli/src/databases/repositories/tag.repository.ts +++ b/packages/cli/src/databases/repositories/tag.repository.ts @@ -3,8 +3,9 @@ import type { EntityManager } from '@n8n/typeorm'; import { DataSource, In, Repository } from '@n8n/typeorm'; import intersection from 'lodash/intersection'; +import type { IWorkflowDb } from '@/interfaces'; + import { TagEntity } from '../entities/tag-entity'; -import type { WorkflowEntity } from '../entities/workflow-entity'; @Service() export class TagRepository extends Repository { @@ -23,7 +24,7 @@ export class TagRepository extends Repository { * Set tags on workflow to import while ensuring all tags exist in the database, * either by matching incoming to existing tags or by creating them first. */ - async setTags(tx: EntityManager, dbTags: TagEntity[], workflow: WorkflowEntity) { + async setTags(tx: EntityManager, dbTags: TagEntity[], workflow: IWorkflowDb) { if (!workflow?.tags?.length) return; for (let i = 0; i < workflow.tags.length; i++) { diff --git a/packages/cli/src/environments.ee/source-control/__tests__/source-control-export.service.test.ts b/packages/cli/src/environments.ee/source-control/__tests__/source-control-export.service.test.ts index 96f2f8ecdb..b36faf2475 100644 --- a/packages/cli/src/environments.ee/source-control/__tests__/source-control-export.service.test.ts +++ b/packages/cli/src/environments.ee/source-control/__tests__/source-control-export.service.test.ts @@ -247,14 +247,15 @@ describe('SourceControlExportService', () => { projectRelations: [], }), workflow: mock({ - display: () => 'TestWorkflow', + id: 'test-workflow-id', + name: 'TestWorkflow', }), }), ]); // Act & Assert await expect(service.exportWorkflowsToWorkFolder([mock()])).rejects.toThrow( - 'Workflow TestWorkflow has no owner', + 'Workflow "TestWorkflow" (ID: test-workflow-id) has no owner', ); }); }); diff --git a/packages/cli/src/environments.ee/source-control/source-control-export.service.ee.ts b/packages/cli/src/environments.ee/source-control/source-control-export.service.ee.ts index ec8e4efd22..9505f738a4 100644 --- a/packages/cli/src/environments.ee/source-control/source-control-export.service.ee.ts +++ b/packages/cli/src/environments.ee/source-control/source-control-export.service.ee.ts @@ -6,12 +6,13 @@ import { ApplicationError, type ICredentialDataDecryptedObject } from 'n8n-workf import { writeFile as fsWriteFile, rm as fsRm } from 'node:fs/promises'; import path from 'path'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { SharedCredentialsRepository } from '@/databases/repositories/shared-credentials.repository'; import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository'; import { TagRepository } from '@/databases/repositories/tag.repository'; import { WorkflowTagMappingRepository } from '@/databases/repositories/workflow-tag-mapping.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; +import type { IWorkflowDb } from '@/interfaces'; +import { formatWorkflow } from '@/workflows/workflow.formatter'; import { SOURCE_CONTROL_CREDENTIAL_EXPORT_FOLDER, @@ -84,7 +85,7 @@ export class SourceControlExportService { } private async writeExportableWorkflowsToExportFolder( - workflowsToBeExported: WorkflowEntity[], + workflowsToBeExported: IWorkflowDb[], owners: Record, ) { await Promise.all( @@ -119,7 +120,9 @@ export class SourceControlExportService { const project = sharedWorkflow.project; if (!project) { - throw new ApplicationError(`Workflow ${sharedWorkflow.workflow.display()} has no owner`); + throw new ApplicationError( + `Workflow ${formatWorkflow(sharedWorkflow.workflow)} has no owner`, + ); } if (project.type === 'personal') { @@ -128,7 +131,7 @@ export class SourceControlExportService { ); if (!ownerRelation) { throw new ApplicationError( - `Workflow ${sharedWorkflow.workflow.display()} has no owner`, + `Workflow ${formatWorkflow(sharedWorkflow.workflow)} has no owner`, ); } owners[sharedWorkflow.workflowId] = { diff --git a/packages/cli/src/environments.ee/source-control/types/exportable-workflow.ts b/packages/cli/src/environments.ee/source-control/types/exportable-workflow.ts index f4e281d3d4..9e8924cc47 100644 --- a/packages/cli/src/environments.ee/source-control/types/exportable-workflow.ts +++ b/packages/cli/src/environments.ee/source-control/types/exportable-workflow.ts @@ -9,6 +9,6 @@ export interface ExportableWorkflow { connections: IConnections; settings?: IWorkflowSettings; triggerCount: number; - versionId: string; + versionId?: string; owner: ResourceOwner; } diff --git a/packages/cli/src/errors/workflow-missing-id.error.ts b/packages/cli/src/errors/workflow-missing-id.error.ts index f34a761244..fe9db748a6 100644 --- a/packages/cli/src/errors/workflow-missing-id.error.ts +++ b/packages/cli/src/errors/workflow-missing-id.error.ts @@ -1,10 +1,8 @@ -import type { Workflow } from 'n8n-workflow'; +import type { Workflow, IWorkflowBase } from 'n8n-workflow'; import { ApplicationError } from 'n8n-workflow'; -import type { IWorkflowDb } from '@/interfaces'; - export class WorkflowMissingIdError extends ApplicationError { - constructor(workflow: Workflow | IWorkflowDb) { + constructor(workflow: Workflow | IWorkflowBase) { super('Detected ID-less worklfow', { extra: { workflow } }); } } diff --git a/packages/cli/src/evaluation.ee/test-runner/test-runner.service.ee.ts b/packages/cli/src/evaluation.ee/test-runner/test-runner.service.ee.ts index bb0ffbb295..89d5ca2282 100644 --- a/packages/cli/src/evaluation.ee/test-runner/test-runner.service.ee.ts +++ b/packages/cli/src/evaluation.ee/test-runner/test-runner.service.ee.ts @@ -17,7 +17,6 @@ import type { ExecutionEntity } from '@/databases/entities/execution-entity'; import type { MockedNodeItem, TestDefinition } from '@/databases/entities/test-definition.ee'; import type { TestRun } from '@/databases/entities/test-run.ee'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ExecutionRepository } from '@/databases/repositories/execution.repository'; import { TestCaseExecutionRepository } from '@/databases/repositories/test-case-execution.repository.ee'; import { TestMetricRepository } from '@/databases/repositories/test-metric.repository.ee'; @@ -80,7 +79,7 @@ export class TestRunnerService { * Prepares the start nodes and trigger node data props for the `workflowRunner.run` method input. */ private getStartNodesData( - workflow: WorkflowEntity, + workflow: IWorkflowBase, pastExecutionData: IRunExecutionData, pastExecutionWorkflowData: IWorkflowBase, ): Pick { @@ -140,7 +139,7 @@ export class TestRunnerService { * Waits for the workflow under test to finish execution. */ private async runTestCase( - workflow: WorkflowEntity, + workflow: IWorkflowBase, pastExecutionData: IRunExecutionData, pastExecutionWorkflowData: IWorkflowBase, mockedNodes: MockedNodeItem[], @@ -197,7 +196,7 @@ export class TestRunnerService { * Run the evaluation workflow with the expected and actual run data. */ private async runTestCaseEvaluation( - evaluationWorkflow: WorkflowEntity, + evaluationWorkflow: IWorkflowBase, expectedData: IRunData, actualData: IRunData, abortSignal: AbortSignal, diff --git a/packages/cli/src/evaluation.ee/test-runner/utils.ee.ts b/packages/cli/src/evaluation.ee/test-runner/utils.ee.ts index 3326c0dbe6..25f5ddf820 100644 --- a/packages/cli/src/evaluation.ee/test-runner/utils.ee.ts +++ b/packages/cli/src/evaluation.ee/test-runner/utils.ee.ts @@ -3,7 +3,6 @@ import type { IRunExecutionData, IPinData, IWorkflowBase } from 'n8n-workflow'; import type { TestCaseExecution } from '@/databases/entities/test-case-execution.ee'; import type { MockedNodeItem } from '@/databases/entities/test-definition.ee'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import type { TestRunFinalResult } from '@/databases/repositories/test-run.repository.ee'; import { TestCaseExecutionError } from '@/evaluation.ee/test-runner/errors.ee'; @@ -14,7 +13,7 @@ import { TestCaseExecutionError } from '@/evaluation.ee/test-runner/errors.ee'; * to decide which nodes to pin. */ export function createPinData( - workflow: WorkflowEntity, + workflow: IWorkflowBase, mockedNodes: MockedNodeItem[], executionData: IRunExecutionData, pastWorkflowData?: IWorkflowBase, diff --git a/packages/cli/src/events/maps/pub-sub.event-map.ts b/packages/cli/src/events/maps/pub-sub.event-map.ts index 0d71fcff91..e4e002f779 100644 --- a/packages/cli/src/events/maps/pub-sub.event-map.ts +++ b/packages/cli/src/events/maps/pub-sub.event-map.ts @@ -1,6 +1,5 @@ import type { PushMessage, WorkerStatus } from '@n8n/api-types'; - -import type { IWorkflowDb } from '@/interfaces'; +import type { IWorkflowBase } from 'n8n-workflow'; export type PubSubEventMap = PubSubCommandMap & PubSubWorkerResponseMap; @@ -70,7 +69,7 @@ export type PubSubCommandMap = { 'clear-test-webhooks': { webhookKey: string; - workflowEntity: IWorkflowDb; + workflowEntity: IWorkflowBase; pushRef: string; }; diff --git a/packages/cli/src/executions/__tests__/constants.ts b/packages/cli/src/executions/__tests__/constants.ts index e2ab19bf62..aeba0f1475 100644 --- a/packages/cli/src/executions/__tests__/constants.ts +++ b/packages/cli/src/executions/__tests__/constants.ts @@ -1,11 +1,10 @@ +import type { IWorkflowBase } from 'n8n-workflow'; import { NodeConnectionType } from 'n8n-workflow'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; - /** * Workflow producing an execution whose data will be truncated by an instance crash. */ -export const OOM_WORKFLOW: Partial = { +export const OOM_WORKFLOW: Partial = { nodes: [ { parameters: {}, diff --git a/packages/cli/src/executions/execution.service.ts b/packages/cli/src/executions/execution.service.ts index 7daff214bb..84090bcb0a 100644 --- a/packages/cli/src/executions/execution.service.ts +++ b/packages/cli/src/executions/execution.service.ts @@ -36,7 +36,6 @@ import type { CreateExecutionPayload, IExecutionFlattedResponse, IExecutionResponse, - IWorkflowDb, } from '@/interfaces'; import { License } from '@/license'; import { NodeTypes } from '@/node-types'; @@ -268,7 +267,7 @@ export class ExecutionService { async createErrorExecution( error: ExecutionError, node: INode, - workflowData: IWorkflowDb, + workflowData: IWorkflowBase, workflow: Workflow, mode: WorkflowExecuteMode, ) { diff --git a/packages/cli/src/interfaces.ts b/packages/cli/src/interfaces.ts index c5cf76bd32..b7e51525c1 100644 --- a/packages/cli/src/interfaces.ts +++ b/packages/cli/src/interfaces.ts @@ -90,6 +90,7 @@ export type IAnnotationTagWithCountDb = IAnnotationTagDb & UsageCount; // Almost identical to editor-ui.Interfaces.ts export interface IWorkflowDb extends IWorkflowBase { + triggerCount: number; tags?: TagEntity[]; } diff --git a/packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts index 77a07fea0f..9698c2bb24 100644 --- a/packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/public-api/v1/handlers/workflows/workflows.handler.ts @@ -330,7 +330,7 @@ export = { } // change the status to active in the DB - await setWorkflowAsActive(workflow); + await setWorkflowAsActive(workflow.id); workflow.active = true; @@ -363,7 +363,7 @@ export = { if (workflow.active) { await activeWorkflowManager.remove(workflow.id); - await setWorkflowAsInactive(workflow); + await setWorkflowAsInactive(workflow.id); workflow.active = false; diff --git a/packages/cli/src/public-api/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/public-api/v1/handlers/workflows/workflows.service.ts index 131919d096..534101b3a6 100644 --- a/packages/cli/src/public-api/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/public-api/v1/handlers/workflows/workflows.service.ts @@ -1,6 +1,7 @@ import { GlobalConfig } from '@n8n/config'; import { Container } from '@n8n/di'; import type { Scope } from '@n8n/permissions'; +import type { WorkflowId } from 'n8n-workflow'; import type { Project } from '@/databases/entities/project'; import { SharedWorkflow, type WorkflowSharingRole } from '@/databases/entities/shared-workflow'; @@ -83,15 +84,15 @@ export async function createWorkflow( }); } -export async function setWorkflowAsActive(workflow: WorkflowEntity) { - await Container.get(WorkflowRepository).update(workflow.id, { +export async function setWorkflowAsActive(workflowId: WorkflowId) { + await Container.get(WorkflowRepository).update(workflowId, { active: true, updatedAt: new Date(), }); } -export async function setWorkflowAsInactive(workflow: WorkflowEntity) { - return await Container.get(WorkflowRepository).update(workflow.id, { +export async function setWorkflowAsInactive(workflowId: WorkflowId) { + return await Container.get(WorkflowRepository).update(workflowId, { active: false, updatedAt: new Date(), }); diff --git a/packages/cli/src/scaling/__tests__/pubsub-handler.test.ts b/packages/cli/src/scaling/__tests__/pubsub-handler.test.ts index bc15911913..3c6d00c02d 100644 --- a/packages/cli/src/scaling/__tests__/pubsub-handler.test.ts +++ b/packages/cli/src/scaling/__tests__/pubsub-handler.test.ts @@ -1,14 +1,13 @@ import type { WorkerStatus } from '@n8n/api-types'; import { mock } from 'jest-mock-extended'; import type { InstanceSettings } from 'n8n-core'; -import type { Workflow } from 'n8n-workflow'; +import type { IWorkflowBase, Workflow } from 'n8n-workflow'; import type { ActiveWorkflowManager } from '@/active-workflow-manager'; import type { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import type { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus'; import { EventService } from '@/events/event.service'; import type { ExternalSecretsManager } from '@/external-secrets.ee/external-secrets-manager.ee'; -import type { IWorkflowDb } from '@/interfaces'; import type { License } from '@/license'; import type { Push } from '@/push'; import type { CommunityPackagesService } from '@/services/community-packages.service'; @@ -852,7 +851,7 @@ describe('PubSubHandler', () => { ).init(); const webhookKey = 'test-webhook-key'; - const workflowEntity = mock({ id: 'test-workflow-id' }); + const workflowEntity = mock({ id: 'test-workflow-id' }); const pushRef = 'test-push-ref'; push.hasPushRef.mockReturnValue(true); diff --git a/packages/cli/src/security-audit/risk-reporters/credentials-risk-reporter.ts b/packages/cli/src/security-audit/risk-reporters/credentials-risk-reporter.ts index ca8bf11415..cd40dc76b2 100644 --- a/packages/cli/src/security-audit/risk-reporters/credentials-risk-reporter.ts +++ b/packages/cli/src/security-audit/risk-reporters/credentials-risk-reporter.ts @@ -2,7 +2,6 @@ import { SecurityConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import type { IWorkflowBase } from 'n8n-workflow'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; import { ExecutionDataRepository } from '@/databases/repositories/execution-data.repository'; import { ExecutionRepository } from '@/databases/repositories/execution.repository'; @@ -18,7 +17,7 @@ export class CredentialsRiskReporter implements RiskReporter { private readonly securityConfig: SecurityConfig, ) {} - async report(workflows: WorkflowEntity[]) { + async report(workflows: IWorkflowBase[]) { const days = this.securityConfig.daysAbandonedWorkflow; const allExistingCreds = await this.getAllExistingCreds(); @@ -84,7 +83,7 @@ export class CredentialsRiskReporter implements RiskReporter { return report; } - private async getAllCredsInUse(workflows: WorkflowEntity[]) { + private async getAllCredsInUse(workflows: IWorkflowBase[]) { const credsInAnyUse = new Set(); const credsInActiveUse = new Set(); diff --git a/packages/cli/src/security-audit/risk-reporters/database-risk-reporter.ts b/packages/cli/src/security-audit/risk-reporters/database-risk-reporter.ts index f66ae02282..39071eaa14 100644 --- a/packages/cli/src/security-audit/risk-reporters/database-risk-reporter.ts +++ b/packages/cli/src/security-audit/risk-reporters/database-risk-reporter.ts @@ -1,6 +1,6 @@ import { Service } from '@n8n/di'; +import type { IWorkflowBase } from 'n8n-workflow'; -import type { WorkflowEntity as Workflow } from '@/databases/entities/workflow-entity'; import { SQL_NODE_TYPES, DATABASE_REPORT, @@ -12,7 +12,7 @@ import { toFlaggedNode } from '@/security-audit/utils'; @Service() export class DatabaseRiskReporter implements RiskReporter { - async report(workflows: Workflow[]) { + async report(workflows: IWorkflowBase[]) { const { expressionsInQueries, expressionsInQueryParams, unusedQueryParams } = this.getIssues(workflows); @@ -69,7 +69,7 @@ export class DatabaseRiskReporter implements RiskReporter { return report; } - private getIssues(workflows: Workflow[]) { + private getIssues(workflows: IWorkflowBase[]) { return workflows.reduce<{ [sectionTitle: string]: Risk.NodeLocation[] }>( (acc, workflow) => { workflow.nodes.forEach((node) => { diff --git a/packages/cli/src/security-audit/risk-reporters/filesystem-risk-reporter.ts b/packages/cli/src/security-audit/risk-reporters/filesystem-risk-reporter.ts index a73b152d2e..e9f6899402 100644 --- a/packages/cli/src/security-audit/risk-reporters/filesystem-risk-reporter.ts +++ b/packages/cli/src/security-audit/risk-reporters/filesystem-risk-reporter.ts @@ -1,13 +1,13 @@ import { Service } from '@n8n/di'; +import type { IWorkflowBase } from 'n8n-workflow'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { FILESYSTEM_INTERACTION_NODE_TYPES, FILESYSTEM_REPORT } from '@/security-audit/constants'; import type { RiskReporter, Risk } from '@/security-audit/types'; import { getNodeTypes } from '@/security-audit/utils'; @Service() export class FilesystemRiskReporter implements RiskReporter { - async report(workflows: WorkflowEntity[]) { + async report(workflows: IWorkflowBase[]) { const fsInteractionNodeTypes = getNodeTypes(workflows, (node) => FILESYSTEM_INTERACTION_NODE_TYPES.has(node.type), ); diff --git a/packages/cli/src/security-audit/risk-reporters/instance-risk-reporter.ts b/packages/cli/src/security-audit/risk-reporters/instance-risk-reporter.ts index 8d870b5d76..0f35230be4 100644 --- a/packages/cli/src/security-audit/risk-reporters/instance-risk-reporter.ts +++ b/packages/cli/src/security-audit/risk-reporters/instance-risk-reporter.ts @@ -2,10 +2,10 @@ import { GlobalConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import axios from 'axios'; import { InstanceSettings, Logger } from 'n8n-core'; +import type { IWorkflowBase } from 'n8n-workflow'; import config from '@/config'; import { getN8nPackageJson, inDevelopment } from '@/constants'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { isApiEnabled } from '@/public-api'; import { ENV_VARS_DOCS_URL, @@ -25,7 +25,7 @@ export class InstanceRiskReporter implements RiskReporter { private readonly globalConfig: GlobalConfig, ) {} - async report(workflows: WorkflowEntity[]) { + async report(workflows: IWorkflowBase[]) { const unprotectedWebhooks = this.getUnprotectedWebhookNodes(workflows); const outdatedState = await this.getOutdatedState(); const securitySettings = this.getSecuritySettings(); @@ -115,8 +115,8 @@ export class InstanceRiskReporter implements RiskReporter { node, workflow, }: { - node: WorkflowEntity['nodes'][number]; - workflow: WorkflowEntity; + node: IWorkflowBase['nodes'][number]; + workflow: IWorkflowBase; }) { const childNodeNames = workflow.connections[node.name]?.main[0]?.map((i) => i.node); @@ -127,7 +127,7 @@ export class InstanceRiskReporter implements RiskReporter { ); } - private getUnprotectedWebhookNodes(workflows: WorkflowEntity[]) { + private getUnprotectedWebhookNodes(workflows: IWorkflowBase[]) { return workflows.reduce((acc, workflow) => { if (!workflow.active) return acc; diff --git a/packages/cli/src/security-audit/risk-reporters/nodes-risk-reporter.ts b/packages/cli/src/security-audit/risk-reporters/nodes-risk-reporter.ts index 58f6da5e18..64206061f4 100644 --- a/packages/cli/src/security-audit/risk-reporters/nodes-risk-reporter.ts +++ b/packages/cli/src/security-audit/risk-reporters/nodes-risk-reporter.ts @@ -1,9 +1,9 @@ import { GlobalConfig } from '@n8n/config'; import { Service } from '@n8n/di'; import glob from 'fast-glob'; +import type { IWorkflowBase } from 'n8n-workflow'; import * as path from 'path'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import { OFFICIAL_RISKY_NODE_TYPES, @@ -24,7 +24,7 @@ export class NodesRiskReporter implements RiskReporter { private readonly globalConfig: GlobalConfig, ) {} - async report(workflows: WorkflowEntity[]) { + async report(workflows: IWorkflowBase[]) { const officialRiskyNodes = getNodeTypes(workflows, (node) => OFFICIAL_RISKY_NODE_TYPES.has(node.type), ); diff --git a/packages/cli/src/security-audit/types.ts b/packages/cli/src/security-audit/types.ts index a848ff56c5..593e49abad 100644 --- a/packages/cli/src/security-audit/types.ts +++ b/packages/cli/src/security-audit/types.ts @@ -1,4 +1,4 @@ -import type { WorkflowEntity as Workflow } from '@/databases/entities/workflow-entity'; +import type { IWorkflowBase } from 'n8n-workflow'; export namespace Risk { export type Category = 'database' | 'credentials' | 'nodes' | 'instance' | 'filesystem'; @@ -62,16 +62,16 @@ export namespace Risk { [reportTitle: string]: Report; }; - export type SyncReportFn = (workflows: Workflow[]) => StandardReport | null; + export type SyncReportFn = (workflows: IWorkflowBase[]) => StandardReport | null; - export type AsyncReportFn = (workflows: Workflow[]) => Promise; + export type AsyncReportFn = (workflows: IWorkflowBase[]) => Promise; } export namespace n8n { export type Version = { name: string; nodes: Array< - Workflow['nodes'][number] & { + IWorkflowBase['nodes'][number] & { iconData?: { type: string; fileBuffer: string }; // removed to declutter report } >; @@ -86,5 +86,5 @@ export namespace n8n { } export interface RiskReporter { - report(workflows: Workflow[]): Promise; + report(workflows: IWorkflowBase[]): Promise; } diff --git a/packages/cli/src/security-audit/utils.ts b/packages/cli/src/security-audit/utils.ts index b724114494..9b4817b467 100644 --- a/packages/cli/src/security-audit/utils.ts +++ b/packages/cli/src/security-audit/utils.ts @@ -1,9 +1,10 @@ -import type { WorkflowEntity as Workflow } from '@/databases/entities/workflow-entity'; +import type { IWorkflowBase } from 'n8n-workflow'; + import type { Risk } from '@/security-audit/types'; -type Node = Workflow['nodes'][number]; +type Node = IWorkflowBase['nodes'][number]; -export const toFlaggedNode = ({ node, workflow }: { node: Node; workflow: Workflow }) => ({ +export const toFlaggedNode = ({ node, workflow }: { node: Node; workflow: IWorkflowBase }) => ({ kind: 'node' as const, workflowId: workflow.id, workflowName: workflow.name, @@ -15,7 +16,7 @@ export const toFlaggedNode = ({ node, workflow }: { node: Node; workflow: Workfl export const toReportTitle = (riskCategory: Risk.Category) => riskCategory.charAt(0).toUpperCase() + riskCategory.slice(1) + ' Risk Report'; -export function getNodeTypes(workflows: Workflow[], test: (element: Node) => boolean) { +export function getNodeTypes(workflows: IWorkflowBase[], test: (element: Node) => boolean) { return workflows.reduce((acc, workflow) => { workflow.nodes.forEach((node) => { if (test(node)) acc.push(toFlaggedNode({ node, workflow })); diff --git a/packages/cli/src/services/import.service.ts b/packages/cli/src/services/import.service.ts index dcb7bff0ad..b0b0978058 100644 --- a/packages/cli/src/services/import.service.ts +++ b/packages/cli/src/services/import.service.ts @@ -1,6 +1,6 @@ import { Service } from '@n8n/di'; import { Logger } from 'n8n-core'; -import { type INode, type INodeCredentialsDetails } from 'n8n-workflow'; +import { type INode, type INodeCredentialsDetails, type IWorkflowBase } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { Project } from '@/databases/entities/project'; @@ -11,7 +11,7 @@ import { WorkflowTagMapping } from '@/databases/entities/workflow-tag-mapping'; import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; import { TagRepository } from '@/databases/repositories/tag.repository'; import * as Db from '@/db'; -import type { ICredentialsDb } from '@/interfaces'; +import type { ICredentialsDb, IWorkflowDb } from '@/interfaces'; import { replaceInvalidCredentials } from '@/workflow-helpers'; @Service() @@ -31,7 +31,7 @@ export class ImportService { this.dbTags = await this.tagRepository.find(); } - async importWorkflows(workflows: WorkflowEntity[], projectId: string) { + async importWorkflows(workflows: IWorkflowDb[], projectId: string) { await this.initRecords(); for (const workflow of workflows) { @@ -84,7 +84,7 @@ export class ImportService { }); } - async replaceInvalidCreds(workflow: WorkflowEntity) { + async replaceInvalidCreds(workflow: IWorkflowBase) { try { await replaceInvalidCredentials(workflow); } catch (e) { diff --git a/packages/cli/src/user-management/email/user-management-mailer.ts b/packages/cli/src/user-management/email/user-management-mailer.ts index 53320c61e1..c9ae37e8d7 100644 --- a/packages/cli/src/user-management/email/user-management-mailer.ts +++ b/packages/cli/src/user-management/email/user-management-mailer.ts @@ -4,11 +4,11 @@ import { existsSync } from 'fs'; import { readFile } from 'fs/promises'; import Handlebars from 'handlebars'; import { Logger } from 'n8n-core'; +import type { IWorkflowBase } from 'n8n-workflow'; import { join as pathJoin } from 'path'; import { inTest } from '@/constants'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { UserRepository } from '@/databases/repositories/user.repository'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; import { EventService } from '@/events/event.service'; @@ -81,7 +81,7 @@ export class UserManagementMailer { }: { sharer: User; newShareeIds: string[]; - workflow: WorkflowEntity; + workflow: IWorkflowBase; }): Promise { if (!this.mailer) return { emailSent: false }; diff --git a/packages/cli/src/webhooks/__tests__/test-webhooks.test.ts b/packages/cli/src/webhooks/__tests__/test-webhooks.test.ts index a07d4fc0fd..bde459591b 100644 --- a/packages/cli/src/webhooks/__tests__/test-webhooks.test.ts +++ b/packages/cli/src/webhooks/__tests__/test-webhooks.test.ts @@ -1,6 +1,6 @@ import type * as express from 'express'; import { mock } from 'jest-mock-extended'; -import type { ITaskData } from 'n8n-workflow'; +import type { ITaskData, IWorkflowBase } from 'n8n-workflow'; import { type IWebhookData, type IWorkflowExecuteAdditionalData, @@ -11,7 +11,6 @@ import { v4 as uuid } from 'uuid'; import { generateNanoId } from '@/databases/utils/generators'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error'; -import type { IWorkflowDb } from '@/interfaces'; import type { TestWebhookRegistrationsService, TestWebhookRegistration, @@ -26,7 +25,7 @@ jest.mock('@/workflow-execute-additional-data'); const mockedAdditionalData = AdditionalData as jest.Mocked; -const workflowEntity = mock({ id: generateNanoId(), nodes: [] }); +const workflowEntity = mock({ id: generateNanoId(), nodes: [] }); const httpMethod = 'GET'; const path = uuid(); diff --git a/packages/cli/src/webhooks/test-webhook-registrations.service.ts b/packages/cli/src/webhooks/test-webhook-registrations.service.ts index 565608c583..0f86be48d6 100644 --- a/packages/cli/src/webhooks/test-webhook-registrations.service.ts +++ b/packages/cli/src/webhooks/test-webhook-registrations.service.ts @@ -1,14 +1,13 @@ import { Service } from '@n8n/di'; import { InstanceSettings } from 'n8n-core'; -import type { IWebhookData } from 'n8n-workflow'; +import type { IWebhookData, IWorkflowBase } from 'n8n-workflow'; import { TEST_WEBHOOK_TIMEOUT, TEST_WEBHOOK_TIMEOUT_BUFFER } from '@/constants'; -import type { IWorkflowDb } from '@/interfaces'; import { CacheService } from '@/services/cache/cache.service'; export type TestWebhookRegistration = { pushRef?: string; - workflowEntity: IWorkflowDb; + workflowEntity: IWorkflowBase; destinationNode?: string; webhook: IWebhookData; }; diff --git a/packages/cli/src/webhooks/test-webhooks.ts b/packages/cli/src/webhooks/test-webhooks.ts index 61d29d7b79..f944021e47 100644 --- a/packages/cli/src/webhooks/test-webhooks.ts +++ b/packages/cli/src/webhooks/test-webhooks.ts @@ -7,13 +7,13 @@ import type { IWorkflowExecuteAdditionalData, IHttpRequestMethods, IRunData, + IWorkflowBase, } from 'n8n-workflow'; import { TEST_WEBHOOK_TIMEOUT } from '@/constants'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error'; import { WorkflowMissingIdError } from '@/errors/workflow-missing-id.error'; -import type { IWorkflowDb } from '@/interfaces'; import { NodeTypes } from '@/node-types'; import { Push } from '@/push'; import { Publisher } from '@/scaling/pubsub/publisher.service'; @@ -217,7 +217,7 @@ export class TestWebhooks implements IWebhookManager { */ async needsWebhook(options: { userId: string; - workflowEntity: IWorkflowDb; + workflowEntity: IWorkflowBase; additionalData: IWorkflowExecuteAdditionalData; runData?: IRunData; pushRef?: string; @@ -434,9 +434,10 @@ export class TestWebhooks implements IWebhookManager { } /** - * Convert a `WorkflowEntity` from `typeorm` to a temporary `Workflow` from `n8n-workflow`. + * Convert a `IWorkflowBase` interface (e.g. `WorkflowEntity`) to a temporary + * `Workflow` from `n8n-workflow`. */ - toWorkflow(workflowEntity: IWorkflowDb) { + toWorkflow(workflowEntity: IWorkflowBase) { return new Workflow({ id: workflowEntity.id, name: workflowEntity.name, diff --git a/packages/cli/src/webhooks/waiting-webhooks.ts b/packages/cli/src/webhooks/waiting-webhooks.ts index 4fe89180f1..585a39a18c 100644 --- a/packages/cli/src/webhooks/waiting-webhooks.ts +++ b/packages/cli/src/webhooks/waiting-webhooks.ts @@ -13,7 +13,7 @@ import { import { ExecutionRepository } from '@/databases/repositories/execution.repository'; import { ConflictError } from '@/errors/response-errors/conflict.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; -import type { IExecutionResponse, IWorkflowDb } from '@/interfaces'; +import type { IExecutionResponse } from '@/interfaces'; import { NodeTypes } from '@/node-types'; import * as WebhookHelpers from '@/webhooks/webhook-helpers'; import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data'; @@ -217,7 +217,7 @@ export class WaitingWebhooks implements IWebhookManager { void WebhookHelpers.executeWebhook( workflow, webhookData, - workflowData as IWorkflowDb, + workflowData, workflowStartNode, executionMode, runExecutionData.pushRef, diff --git a/packages/cli/src/webhooks/webhook-helpers.ts b/packages/cli/src/webhooks/webhook-helpers.ts index ca54a14b52..c70976ada9 100644 --- a/packages/cli/src/webhooks/webhook-helpers.ts +++ b/packages/cli/src/webhooks/webhook-helpers.ts @@ -29,6 +29,7 @@ import type { Workflow, WorkflowExecuteMode, IWorkflowExecutionDataProcess, + IWorkflowBase, } from 'n8n-workflow'; import { ApplicationError, @@ -47,7 +48,6 @@ import type { Project } from '@/databases/entities/project'; import { InternalServerError } from '@/errors/response-errors/internal-server.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; -import type { IWorkflowDb } from '@/interfaces'; import { parseBody } from '@/middlewares'; import { OwnershipService } from '@/services/ownership.service'; import { WorkflowStatisticsService } from '@/services/workflow-statistics.service'; @@ -111,7 +111,7 @@ const parseFormData = createMultiFormDataParser(formDataFileSizeMax); export async function executeWebhook( workflow: Workflow, webhookData: IWebhookData, - workflowData: IWorkflowDb, + workflowData: IWorkflowBase, workflowStartNode: INode, executionMode: WorkflowExecuteMode, pushRef: string | undefined, diff --git a/packages/cli/src/workflow-helpers.ts b/packages/cli/src/workflow-helpers.ts index 36946dd77e..3f47b13c82 100644 --- a/packages/cli/src/workflow-helpers.ts +++ b/packages/cli/src/workflow-helpers.ts @@ -9,10 +9,10 @@ import type { WorkflowExecuteMode, WorkflowOperationError, NodeOperationError, + IWorkflowBase, } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; import { VariablesService } from '@/environments.ee/variables/variables.service.ee'; @@ -103,7 +103,7 @@ export function getDataLastExecutedNodeData(inputData: IRun): ITaskData | undefi /** * Set node ids if not already set */ -export function addNodeIds(workflow: WorkflowEntity) { +export function addNodeIds(workflow: IWorkflowBase) { const { nodes } = workflow; if (!nodes) return; @@ -115,7 +115,7 @@ export function addNodeIds(workflow: WorkflowEntity) { } // 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 { +export async function replaceInvalidCredentials(workflow: T): Promise { const { nodes } = workflow; if (!nodes) return workflow; diff --git a/packages/cli/src/workflows/__tests__/workflow-execution.service.test.ts b/packages/cli/src/workflows/__tests__/workflow-execution.service.test.ts index 00a81700e0..0de5e3496a 100644 --- a/packages/cli/src/workflows/__tests__/workflow-execution.service.test.ts +++ b/packages/cli/src/workflows/__tests__/workflow-execution.service.test.ts @@ -1,9 +1,7 @@ import { mock } from 'jest-mock-extended'; -import type { INode, IWorkflowExecuteAdditionalData } from 'n8n-workflow'; +import type { INode, IWorkflowBase, IWorkflowExecuteAdditionalData } from 'n8n-workflow'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; -import type { IWorkflowDb } from '@/interfaces'; import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data'; import type { WorkflowRunner } from '@/workflow-runner'; import { WorkflowExecutionService } from '@/workflows/workflow-execution.service'; @@ -73,7 +71,7 @@ describe('WorkflowExecutionService', () => { describe('runWorkflow()', () => { test('should call `WorkflowRunner.run()`', async () => { const node = mock(); - const workflow = mock({ active: true, nodes: [node] }); + const workflow = mock({ active: true, nodes: [node] }); workflowRunner.run.mockResolvedValue('fake-execution-id'); @@ -300,7 +298,7 @@ describe('WorkflowExecutionService', () => { }); describe('selectPinnedActivatorStarter()', () => { - const workflow = mock({ + const workflow = mock({ nodes: [], }); diff --git a/packages/cli/src/workflows/workflow-execution.service.ts b/packages/cli/src/workflows/workflow-execution.service.ts index 23394493df..6c9bdbc717 100644 --- a/packages/cli/src/workflows/workflow-execution.service.ts +++ b/packages/cli/src/workflows/workflow-execution.service.ts @@ -12,6 +12,7 @@ import type { IWorkflowExecuteAdditionalData, WorkflowExecuteMode, IWorkflowExecutionDataProcess, + IWorkflowBase, } from 'n8n-workflow'; import { SubworkflowOperationError, Workflow } from 'n8n-workflow'; @@ -20,7 +21,7 @@ import type { Project } from '@/databases/entities/project'; import type { User } from '@/databases/entities/user'; import { ExecutionRepository } from '@/databases/repositories/execution.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; -import type { CreateExecutionPayload, IWorkflowDb, IWorkflowErrorData } from '@/interfaces'; +import type { CreateExecutionPayload, IWorkflowErrorData } from '@/interfaces'; import { NodeTypes } from '@/node-types'; import { SubworkflowPolicyChecker } from '@/subworkflows/subworkflow-policy-checker.service'; import { TestWebhooks } from '@/webhooks/test-webhooks'; @@ -44,7 +45,7 @@ export class WorkflowExecutionService { ) {} async runWorkflow( - workflowData: IWorkflowDb, + workflowData: IWorkflowBase, node: INode, data: INodeExecutionData[][], additionalData: IWorkflowExecuteAdditionalData, @@ -346,7 +347,7 @@ export class WorkflowExecutionService { * prioritizing `n8n-nodes-base.webhook` over other activators. If the executed node * has no upstream nodes and is itself is a pinned activator, select it. */ - selectPinnedActivatorStarter(workflow: IWorkflowDb, startNodes?: string[], pinData?: IPinData) { + selectPinnedActivatorStarter(workflow: IWorkflowBase, startNodes?: string[], pinData?: IPinData) { if (!pinData || !startNodes) return null; const allPinnedActivators = this.findAllPinnedActivators(workflow, pinData); @@ -385,7 +386,7 @@ export class WorkflowExecutionService { return allPinnedActivators.find((pa) => pa.name === firstStartNodeName) ?? null; } - private findAllPinnedActivators(workflow: IWorkflowDb, pinData?: IPinData) { + private findAllPinnedActivators(workflow: IWorkflowBase, pinData?: IPinData) { return workflow.nodes .filter( (node) => diff --git a/packages/cli/src/workflows/workflow-history.ee/workflow-history.service.ee.ts b/packages/cli/src/workflows/workflow-history.ee/workflow-history.service.ee.ts index 05256ec7a6..11bcaa8eb1 100644 --- a/packages/cli/src/workflows/workflow-history.ee/workflow-history.service.ee.ts +++ b/packages/cli/src/workflows/workflow-history.ee/workflow-history.service.ee.ts @@ -1,9 +1,9 @@ import { Service } from '@n8n/di'; import { Logger } from 'n8n-core'; +import type { IWorkflowBase } from 'n8n-workflow'; import { ensureError } from 'n8n-workflow'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import type { WorkflowHistory } from '@/databases/entities/workflow-history'; import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository'; import { WorkflowHistoryRepository } from '@/databases/repositories/workflow-history.repository'; @@ -66,7 +66,7 @@ export class WorkflowHistoryService { return hist; } - async saveVersion(user: User, workflow: WorkflowEntity, workflowId: string) { + async saveVersion(user: User, workflow: IWorkflowBase, workflowId: string) { // On some update scenarios, `nodes` and `connections` are missing, such as when // changing workflow settings or renaming. In these cases, we don't want to save // a new version diff --git a/packages/cli/src/workflows/workflow.formatter.ts b/packages/cli/src/workflows/workflow.formatter.ts new file mode 100644 index 0000000000..1d98ca4871 --- /dev/null +++ b/packages/cli/src/workflows/workflow.formatter.ts @@ -0,0 +1,8 @@ +import type { IWorkflowBase } from 'n8n-workflow'; + +/** + * Display a workflow in a user-friendly format + */ +export function formatWorkflow(workflow: IWorkflowBase) { + return `"${workflow.name}" (ID: ${workflow.id})`; +} diff --git a/packages/cli/src/workflows/workflow.request.ts b/packages/cli/src/workflows/workflow.request.ts index 3e44d5b950..f8cb35aee1 100644 --- a/packages/cli/src/workflows/workflow.request.ts +++ b/packages/cli/src/workflows/workflow.request.ts @@ -5,9 +5,9 @@ import type { IRunData, StartNodeData, ITaskData, + IWorkflowBase, } from 'n8n-workflow'; -import type { IWorkflowDb } from '@/interfaces'; import type { AuthenticatedRequest, ListQuery } from '@/requests'; export declare namespace WorkflowRequest { @@ -25,7 +25,7 @@ export declare namespace WorkflowRequest { }>; type ManualRunPayload = { - workflowData: IWorkflowDb; + workflowData: IWorkflowBase; runData: IRunData; startNodes?: StartNodeData[]; destinationNode?: string; diff --git a/packages/cli/src/workflows/workflow.service.ee.ts b/packages/cli/src/workflows/workflow.service.ee.ts index 8122e909c4..2d97e4f66e 100644 --- a/packages/cli/src/workflows/workflow.service.ee.ts +++ b/packages/cli/src/workflows/workflow.service.ee.ts @@ -3,6 +3,7 @@ import { Service } from '@n8n/di'; import { In, type EntityManager } from '@n8n/typeorm'; import omit from 'lodash/omit'; import { Logger } from 'n8n-core'; +import type { IWorkflowBase, WorkflowId } from 'n8n-workflow'; import { ApplicationError, NodeOperationError, WorkflowActivationError } from 'n8n-workflow'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; @@ -11,7 +12,6 @@ import type { CredentialsEntity } from '@/databases/entities/credentials-entity' import { Project } from '@/databases/entities/project'; import { SharedWorkflow } from '@/databases/entities/shared-workflow'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; @@ -40,7 +40,7 @@ export class EnterpriseWorkflowService { ) {} async shareWithProjects( - workflow: WorkflowEntity, + workflowId: WorkflowId, shareWithIds: string[], entityManager: EntityManager, ) { @@ -55,7 +55,7 @@ export class EnterpriseWorkflowService { // always only be one owner. .map((project) => this.sharedWorkflowRepository.create({ - workflowId: workflow.id, + workflowId, role: 'workflow:editor', projectId: project.id, }), @@ -118,7 +118,7 @@ export class EnterpriseWorkflowService { } validateCredentialPermissionsToUser( - workflow: WorkflowEntity, + workflow: IWorkflowBase, allowedCredentials: CredentialsEntity[], ) { workflow.nodes.forEach((node) => { @@ -138,7 +138,7 @@ export class EnterpriseWorkflowService { }); } - async preventTampering(workflow: WorkflowEntity, workflowId: string, user: User) { + async preventTampering(workflow: T, workflowId: string, user: User) { const previousVersion = await this.workflowRepository.get({ id: workflowId }); if (!previousVersion) { @@ -162,9 +162,9 @@ export class EnterpriseWorkflowService { } } - validateWorkflowCredentialUsage( - newWorkflowVersion: WorkflowEntity, - previousWorkflowVersion: WorkflowEntity, + validateWorkflowCredentialUsage( + newWorkflowVersion: T, + previousWorkflowVersion: IWorkflowBase, credentialsUserHasAccessTo: Array<{ id: string }>, ) { /** @@ -232,7 +232,7 @@ export class EnterpriseWorkflowService { } /** Get all nodes in a workflow where the node credential is not accessible to the user. */ - getNodesWithInaccessibleCreds(workflow: WorkflowEntity, userCredIds: string[]) { + getNodesWithInaccessibleCreds(workflow: IWorkflowBase, userCredIds: string[]) { if (!workflow.nodes) { return []; } diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index 76904e5201..07a170cdf8 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -447,7 +447,7 @@ export class WorkflowsController { projectId: In(toUnshare), }); - await this.enterpriseWorkflowService.shareWithProjects(workflow, toShare, trx); + await this.enterpriseWorkflowService.shareWithProjects(workflow.id, toShare, trx); newShareeIds = toShare; }); diff --git a/packages/cli/test/integration/active-workflow-manager.test.ts b/packages/cli/test/integration/active-workflow-manager.test.ts index 8c502bba94..c9df802937 100644 --- a/packages/cli/test/integration/active-workflow-manager.test.ts +++ b/packages/cli/test/integration/active-workflow-manager.test.ts @@ -2,12 +2,11 @@ import { Container } from '@n8n/di'; import { mock } from 'jest-mock-extended'; import { Logger } from 'n8n-core'; import { NodeApiError, Workflow } from 'n8n-workflow'; -import type { IWebhookData, WorkflowActivateMode } from 'n8n-workflow'; +import type { IWebhookData, IWorkflowBase, WorkflowActivateMode } from 'n8n-workflow'; import { ActiveExecutions } from '@/active-executions'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; import type { WebhookEntity } from '@/databases/entities/webhook-entity'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ExecutionService } from '@/executions/execution.service'; import { ExternalHooks } from '@/external-hooks'; import { NodeTypes } from '@/node-types'; @@ -36,8 +35,8 @@ const externalHooks = mockInstance(ExternalHooks); let activeWorkflowManager: ActiveWorkflowManager; -let createActiveWorkflow: () => Promise; -let createInactiveWorkflow: () => Promise; +let createActiveWorkflow: () => Promise; +let createInactiveWorkflow: () => Promise; beforeAll(async () => { await testDb.init(); diff --git a/packages/cli/test/integration/collaboration/collaboration.service.test.ts b/packages/cli/test/integration/collaboration/collaboration.service.test.ts index 33ebb9ee31..76baf55a10 100644 --- a/packages/cli/test/integration/collaboration/collaboration.service.test.ts +++ b/packages/cli/test/integration/collaboration/collaboration.service.test.ts @@ -1,5 +1,6 @@ import { Container } from '@n8n/di'; import { mock } from 'jest-mock-extended'; +import type { IWorkflowBase } from 'n8n-workflow'; import type { WorkflowClosedMessage, @@ -7,7 +8,6 @@ import type { } from '@/collaboration/collaboration.message'; import { CollaborationService } from '@/collaboration/collaboration.service'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { Push } from '@/push'; import { CacheService } from '@/services/cache/cache.service'; import { mockInstance } from '@test/mocking'; @@ -22,7 +22,7 @@ describe('CollaborationService', () => { let owner: User; let memberWithoutAccess: User; let memberWithAccess: User; - let workflow: WorkflowEntity; + let workflow: IWorkflowBase; let cacheService: CacheService; beforeAll(async () => { diff --git a/packages/cli/test/integration/evaluation/metrics.api.test.ts b/packages/cli/test/integration/evaluation/metrics.api.test.ts index 996fd96148..ff04aedf12 100644 --- a/packages/cli/test/integration/evaluation/metrics.api.test.ts +++ b/packages/cli/test/integration/evaluation/metrics.api.test.ts @@ -1,8 +1,8 @@ import { Container } from '@n8n/di'; +import type { IWorkflowBase } from 'n8n-workflow'; import type { TestDefinition } from '@/databases/entities/test-definition.ee'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { TestDefinitionRepository } from '@/databases/repositories/test-definition.repository.ee'; import { TestMetricRepository } from '@/databases/repositories/test-metric.repository.ee'; import { createUserShell } from '@test-integration/db/users'; @@ -12,8 +12,8 @@ import type { SuperAgentTest } from '@test-integration/types'; import * as utils from '@test-integration/utils'; let authOwnerAgent: SuperAgentTest; -let workflowUnderTest: WorkflowEntity; -let otherWorkflow: WorkflowEntity; +let workflowUnderTest: IWorkflowBase; +let otherWorkflow: IWorkflowBase; let testDefinition: TestDefinition; let otherTestDefinition: TestDefinition; let ownerShell: User; diff --git a/packages/cli/test/integration/evaluation/test-definitions.api.test.ts b/packages/cli/test/integration/evaluation/test-definitions.api.test.ts index 52bc72f6f9..255c71b59e 100644 --- a/packages/cli/test/integration/evaluation/test-definitions.api.test.ts +++ b/packages/cli/test/integration/evaluation/test-definitions.api.test.ts @@ -1,9 +1,9 @@ import { Container } from '@n8n/di'; import { mockInstance } from 'n8n-core/test/utils'; +import type { IWorkflowBase } from 'n8n-workflow'; import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { TestDefinitionRepository } from '@/databases/repositories/test-definition.repository.ee'; import { TestRunnerService } from '@/evaluation.ee/test-runner/test-runner.service.ee'; import { createAnnotationTags } from '@test-integration/db/executions'; @@ -17,10 +17,10 @@ import * as utils from './../shared/utils/'; const testRunner = mockInstance(TestRunnerService); let authOwnerAgent: SuperAgentTest; -let workflowUnderTest: WorkflowEntity; -let workflowUnderTest2: WorkflowEntity; -let evaluationWorkflow: WorkflowEntity; -let otherWorkflow: WorkflowEntity; +let workflowUnderTest: IWorkflowBase; +let workflowUnderTest2: IWorkflowBase; +let evaluationWorkflow: IWorkflowBase; +let otherWorkflow: IWorkflowBase; let ownerShell: User; let annotationTag: AnnotationTagEntity; const testServer = utils.setupTestServer({ endpointGroups: ['evaluation'] }); diff --git a/packages/cli/test/integration/evaluation/test-runs.api.test.ts b/packages/cli/test/integration/evaluation/test-runs.api.test.ts index 9690d1b50e..f2f4676ad0 100644 --- a/packages/cli/test/integration/evaluation/test-runs.api.test.ts +++ b/packages/cli/test/integration/evaluation/test-runs.api.test.ts @@ -1,9 +1,9 @@ import { Container } from '@n8n/di'; import { mockInstance } from 'n8n-core/test/utils'; +import type { IWorkflowBase } from 'n8n-workflow'; import type { TestDefinition } from '@/databases/entities/test-definition.ee'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ProjectRepository } from '@/databases/repositories/project.repository'; import { TestDefinitionRepository } from '@/databases/repositories/test-definition.repository.ee'; import { TestRunRepository } from '@/databases/repositories/test-run.repository.ee'; @@ -15,8 +15,8 @@ import type { SuperAgentTest } from '@test-integration/types'; import * as utils from '@test-integration/utils'; let authOwnerAgent: SuperAgentTest; -let workflowUnderTest: WorkflowEntity; -let otherWorkflow: WorkflowEntity; +let workflowUnderTest: IWorkflowBase; +let otherWorkflow: IWorkflowBase; let testDefinition: TestDefinition; let otherTestDefinition: TestDefinition; let ownerShell: User; diff --git a/packages/cli/test/integration/permission-checker.test.ts b/packages/cli/test/integration/permission-checker.test.ts index 71dd77b503..40a31d4236 100644 --- a/packages/cli/test/integration/permission-checker.test.ts +++ b/packages/cli/test/integration/permission-checker.test.ts @@ -1,11 +1,10 @@ import { Container } from '@n8n/di'; -import type { INode } from 'n8n-workflow'; +import type { INode, IWorkflowBase } from 'n8n-workflow'; import { randomInt } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import type { Project } from '@/databases/entities/project'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ProjectRepository } from '@/databases/repositories/project.repository'; import { SharedCredentialsRepository } from '@/databases/repositories/shared-credentials.repository'; import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository'; @@ -26,7 +25,7 @@ import { mockInstance } from '../shared/mocking'; const ownershipService = mockInstance(OwnershipService); -const createWorkflow = async (nodes: INode[], workflowOwner?: User): Promise => { +const createWorkflow = async (nodes: INode[], workflowOwner?: User): Promise => { const workflowDetails = { id: randomInt(1, 10).toString(), name: 'test', diff --git a/packages/cli/test/integration/pruning.service.test.ts b/packages/cli/test/integration/pruning.service.test.ts index 3a97350f79..b2d84ea362 100644 --- a/packages/cli/test/integration/pruning.service.test.ts +++ b/packages/cli/test/integration/pruning.service.test.ts @@ -2,11 +2,10 @@ import { ExecutionsConfig } from '@n8n/config'; import { Container } from '@n8n/di'; import { mock } from 'jest-mock-extended'; import { BinaryDataService, InstanceSettings } from 'n8n-core'; -import type { ExecutionStatus } from 'n8n-workflow'; +import type { ExecutionStatus, IWorkflowBase } from 'n8n-workflow'; import { Time } from '@/constants'; import type { ExecutionEntity } from '@/databases/entities/execution-entity'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ExecutionRepository } from '@/databases/repositories/execution.repository'; import { PruningService } from '@/services/pruning/pruning.service'; @@ -26,7 +25,7 @@ describe('softDeleteOnPruningCycle()', () => { const now = new Date(); const yesterday = new Date(Date.now() - 1 * Time.days.toMilliseconds); - let workflow: WorkflowEntity; + let workflow: IWorkflowBase; let executionsConfig: ExecutionsConfig; beforeAll(async () => { diff --git a/packages/cli/test/integration/shared/db/executions.ts b/packages/cli/test/integration/shared/db/executions.ts index 9af7ad22ed..5a52c22bf7 100644 --- a/packages/cli/test/integration/shared/db/executions.ts +++ b/packages/cli/test/integration/shared/db/executions.ts @@ -1,9 +1,8 @@ import { Container } from '@n8n/di'; -import type { AnnotationVote } from 'n8n-workflow'; +import type { AnnotationVote, IWorkflowBase } from 'n8n-workflow'; import type { ExecutionData } from '@/databases/entities/execution-data'; import type { ExecutionEntity } from '@/databases/entities/execution-entity'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { AnnotationTagRepository } from '@/databases/repositories/annotation-tag.repository.ee'; import { ExecutionDataRepository } from '@/databases/repositories/execution-data.repository'; import { ExecutionMetadataRepository } from '@/databases/repositories/execution-metadata.repository'; @@ -16,8 +15,8 @@ mockInstance(Telemetry); export async function createManyExecutions( amount: number, - workflow: WorkflowEntity, - callback: (workflow: WorkflowEntity) => Promise, + workflow: IWorkflowBase, + callback: (workflow: IWorkflowBase) => Promise, ) { const executionsRequests = [...Array(amount)].map(async (_) => await callback(workflow)); return await Promise.all(executionsRequests); @@ -31,7 +30,7 @@ export async function createExecution( Omit & ExecutionData & { metadata: Array<{ key: string; value: string }> } >, - workflow: WorkflowEntity, + workflow: IWorkflowBase, ) { const { data, finished, mode, startedAt, stoppedAt, waitTill, status, deletedAt, metadata } = attributes; @@ -70,14 +69,14 @@ export async function createExecution( /** * Store a successful execution in the DB and assign it to a workflow. */ -export async function createSuccessfulExecution(workflow: WorkflowEntity) { +export async function createSuccessfulExecution(workflow: IWorkflowBase) { return await createExecution({ finished: true, status: 'success' }, workflow); } /** * Store an error execution in the DB and assign it to a workflow. */ -export async function createErrorExecution(workflow: WorkflowEntity) { +export async function createErrorExecution(workflow: IWorkflowBase) { return await createExecution( { finished: false, stoppedAt: new Date(), status: 'error' }, workflow, @@ -87,7 +86,7 @@ export async function createErrorExecution(workflow: WorkflowEntity) { /** * Store a waiting execution in the DB and assign it to a workflow. */ -export async function createWaitingExecution(workflow: WorkflowEntity) { +export async function createWaitingExecution(workflow: IWorkflowBase) { return await createExecution( { finished: false, waitTill: new Date(), status: 'waiting' }, workflow, diff --git a/packages/cli/test/integration/shared/db/tags.ts b/packages/cli/test/integration/shared/db/tags.ts index af5638f902..1216c9b445 100644 --- a/packages/cli/test/integration/shared/db/tags.ts +++ b/packages/cli/test/integration/shared/db/tags.ts @@ -1,14 +1,14 @@ import { Container } from '@n8n/di'; +import type { IWorkflowBase } from 'n8n-workflow'; import type { TagEntity } from '@/databases/entities/tag-entity'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { TagRepository } from '@/databases/repositories/tag.repository'; import { WorkflowTagMappingRepository } from '@/databases/repositories/workflow-tag-mapping.repository'; import { generateNanoId } from '@/databases/utils/generators'; import { randomName } from '../random'; -export async function createTag(attributes: Partial = {}, workflow?: WorkflowEntity) { +export async function createTag(attributes: Partial = {}, workflow?: IWorkflowBase) { const { name } = attributes; const tag = await Container.get(TagRepository).save({ diff --git a/packages/cli/test/integration/shared/db/workflows.ts b/packages/cli/test/integration/shared/db/workflows.ts index dfd272b396..87d7d21242 100644 --- a/packages/cli/test/integration/shared/db/workflows.ts +++ b/packages/cli/test/integration/shared/db/workflows.ts @@ -1,19 +1,20 @@ import { Container } from '@n8n/di'; import type { DeepPartial } from '@n8n/typeorm'; +import type { IWorkflowBase } from 'n8n-workflow'; import { NodeConnectionType } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { Project } from '@/databases/entities/project'; import type { SharedWorkflow, WorkflowSharingRole } from '@/databases/entities/shared-workflow'; import { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ProjectRepository } from '@/databases/repositories/project.repository'; import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; +import type { IWorkflowDb } from '@/interfaces'; export async function createManyWorkflows( amount: number, - attributes: Partial = {}, + attributes: Partial = {}, user?: User, ) { const workflowRequests = [...Array(amount)].map( @@ -22,7 +23,7 @@ export async function createManyWorkflows( return await Promise.all(workflowRequests); } -export function newWorkflow(attributes: Partial = {}): WorkflowEntity { +export function newWorkflow(attributes: Partial = {}): IWorkflowDb { const { active, name, nodes, connections, versionId } = attributes; const workflowEntity = Container.get(WorkflowRepository).create({ @@ -53,7 +54,7 @@ export function newWorkflow(attributes: Partial = {}): WorkflowE * @param user user to assign the workflow to */ export async function createWorkflow( - attributes: Partial = {}, + attributes: Partial = {}, userOrProject?: User | Project, ) { const workflow = await Container.get(WorkflowRepository).save(newWorkflow(attributes)); @@ -84,7 +85,7 @@ export async function createWorkflow( return workflow; } -export async function shareWorkflowWithUsers(workflow: WorkflowEntity, users: User[]) { +export async function shareWorkflowWithUsers(workflow: IWorkflowBase, users: User[]) { const sharedWorkflows: Array> = await Promise.all( users.map(async (user) => { const project = await Container.get(ProjectRepository).getPersonalProjectForUserOrFail( @@ -101,7 +102,7 @@ export async function shareWorkflowWithUsers(workflow: WorkflowEntity, users: Us } export async function shareWorkflowWithProjects( - workflow: WorkflowEntity, + workflow: IWorkflowBase, projectsWithRole: Array<{ project: Project; role?: WorkflowSharingRole }>, ) { const newSharedWorkflow = await Promise.all( @@ -117,7 +118,7 @@ export async function shareWorkflowWithProjects( return await Container.get(SharedWorkflowRepository).save(newSharedWorkflow); } -export async function getWorkflowSharing(workflow: WorkflowEntity) { +export async function getWorkflowSharing(workflow: IWorkflowBase) { return await Container.get(SharedWorkflowRepository).findBy({ workflowId: workflow.id, }); @@ -128,7 +129,7 @@ export async function getWorkflowSharing(workflow: WorkflowEntity) { * @param user user to assign the workflow to */ export async function createWorkflowWithTrigger( - attributes: Partial = {}, + attributes: Partial = {}, user?: User, ) { const workflow = await createWorkflow( diff --git a/packages/cli/test/integration/webhooks.api.test.ts b/packages/cli/test/integration/webhooks.api.test.ts index f1da8e3ab0..8d1e7168f0 100644 --- a/packages/cli/test/integration/webhooks.api.test.ts +++ b/packages/cli/test/integration/webhooks.api.test.ts @@ -1,4 +1,5 @@ import { readFileSync } from 'fs'; +import type { IWorkflowBase } from 'n8n-workflow'; import { NodeConnectionType, type INodeType, @@ -7,7 +8,6 @@ import { } from 'n8n-workflow'; import { agent as testAgent } from 'supertest'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ExternalHooks } from '@/external-hooks'; import { NodeTypes } from '@/node-types'; import { Push } from '@/push'; @@ -230,7 +230,7 @@ describe('Webhook API', () => { node: WebhookTestingNode, path = 'abcd', httpMethod = 'POST', - ): Partial => ({ + ): Partial => ({ active: true, nodes: [ { diff --git a/packages/cli/test/integration/workflows/workflows.controller.test.ts b/packages/cli/test/integration/workflows/workflows.controller.test.ts index 55c34f633c..2f92b1776c 100644 --- a/packages/cli/test/integration/workflows/workflows.controller.test.ts +++ b/packages/cli/test/integration/workflows/workflows.controller.test.ts @@ -1,11 +1,10 @@ import { Container } from '@n8n/di'; import type { Scope } from '@n8n/permissions'; -import type { INode, IPinData } from 'n8n-workflow'; +import type { INode, IPinData, IWorkflowBase } from 'n8n-workflow'; import { v4 as uuid } from 'uuid'; import { ActiveWorkflowManager } from '@/active-workflow-manager'; import type { User } from '@/databases/entities/user'; -import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; import { ProjectRepository } from '@/databases/repositories/project.repository'; import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository'; import { WorkflowHistoryRepository } from '@/databases/repositories/workflow-history.repository'; @@ -519,7 +518,7 @@ describe('GET /workflows', () => { expect(response.statusCode).toBe(200); expect(response.body.data.length).toBe(2); - const workflows = response.body.data as Array; + const workflows = response.body.data as Array; const wf1 = workflows.find((w) => w.id === savedWorkflow1.id)!; const wf2 = workflows.find((w) => w.id === savedWorkflow2.id)!; @@ -546,7 +545,7 @@ describe('GET /workflows', () => { expect(response.statusCode).toBe(200); expect(response.body.data.length).toBe(2); - const workflows = response.body.data as Array; + const workflows = response.body.data as Array; const wf1 = workflows.find((w) => w.id === savedWorkflow1.id)!; const wf2 = workflows.find((w) => w.id === savedWorkflow2.id)!; @@ -579,7 +578,7 @@ describe('GET /workflows', () => { expect(response.statusCode).toBe(200); expect(response.body.data.length).toBe(2); - const workflows = response.body.data as Array; + const workflows = response.body.data as Array; const wf1 = workflows.find((w) => w.id === savedWorkflow1.id)!; const wf2 = workflows.find((w) => w.id === savedWorkflow2.id)!; @@ -1124,7 +1123,7 @@ describe('PATCH /workflows/:workflowId', () => { describe('POST /workflows/:workflowId/run', () => { let sharingSpy: jest.SpyInstance; let tamperingSpy: jest.SpyInstance; - let workflow: WorkflowEntity; + let workflow: IWorkflowBase; beforeAll(() => { const enterpriseWorkflowService = Container.get(EnterpriseWorkflowService); diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index fa2ee53641..a528ee2aa3 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2196,6 +2196,8 @@ export interface IWaitingForExecutionSource { }; } +export type WorkflowId = IWorkflowBase['id']; + export interface IWorkflowBase { id: string; name: string;