mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
refactor(core): Use IWorkflowBase over WorkflowEntity in most places (#13225)
This commit is contained in:
parent
11cf1cd23a
commit
f001edb2a2
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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<IResult> {
|
||||
private async runTests(allWorkflows: IWorkflowBase[]): Promise<IResult> {
|
||||
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<IExecutionResult> {
|
||||
async startThread(workflowData: IWorkflowBase): Promise<IExecutionResult> {
|
||||
// This will be the object returned by the promise.
|
||||
// It will be updated according to execution progress below.
|
||||
const executionResult: IExecutionResult = {
|
||||
|
|
|
@ -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<WorkflowEntity[]> {
|
||||
|
|
|
@ -104,10 +104,6 @@ export class WorkflowEntity extends WithTimestampsAndStringId implements IWorkfl
|
|||
})
|
||||
@JoinColumn({ name: 'parentFolderId' })
|
||||
parentFolder: Folder | null;
|
||||
|
||||
display() {
|
||||
return `"${this.name}" (ID: ${this.id})`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<TagEntity> {
|
||||
|
@ -23,7 +24,7 @@ export class TagRepository extends Repository<TagEntity> {
|
|||
* 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++) {
|
||||
|
|
|
@ -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',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<string, ResourceOwner>,
|
||||
) {
|
||||
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] = {
|
||||
|
|
|
@ -9,6 +9,6 @@ export interface ExportableWorkflow {
|
|||
connections: IConnections;
|
||||
settings?: IWorkflowSettings;
|
||||
triggerCount: number;
|
||||
versionId: string;
|
||||
versionId?: string;
|
||||
owner: ResourceOwner;
|
||||
}
|
||||
|
|
|
@ -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 } });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<IWorkflowExecutionDataProcess, 'startNodes' | 'triggerToStartFrom'> {
|
||||
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<WorkflowEntity> = {
|
||||
export const OOM_WORKFLOW: Partial<IWorkflowBase> = {
|
||||
nodes: [
|
||||
{
|
||||
parameters: {},
|
||||
|
|
|
@ -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,
|
||||
) {
|
||||
|
|
|
@ -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[];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
|
|
|
@ -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<IWorkflowDb>({ id: 'test-workflow-id' });
|
||||
const workflowEntity = mock<IWorkflowBase>({ id: 'test-workflow-id' });
|
||||
const pushRef = 'test-push-ref';
|
||||
|
||||
push.hasPushRef.mockReturnValue(true);
|
||||
|
|
|
@ -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<string>();
|
||||
const credsInActiveUse = new Set<string>();
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
|
|
|
@ -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<Risk.NodeLocation[]>((acc, workflow) => {
|
||||
if (!workflow.active) return acc;
|
||||
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
|
|
|
@ -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<Report | null>;
|
||||
export type AsyncReportFn = (workflows: IWorkflowBase[]) => Promise<Report | null>;
|
||||
}
|
||||
|
||||
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<Risk.Report | null>;
|
||||
report(workflows: IWorkflowBase[]): Promise<Risk.Report | null>;
|
||||
}
|
||||
|
|
|
@ -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<Risk.NodeLocation[]>((acc, workflow) => {
|
||||
workflow.nodes.forEach((node) => {
|
||||
if (test(node)) acc.push(toFlaggedNode({ node, workflow }));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<SendEmailResult> {
|
||||
if (!this.mailer) return { emailSent: false };
|
||||
|
||||
|
|
|
@ -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<typeof AdditionalData>;
|
||||
|
||||
const workflowEntity = mock<IWorkflowDb>({ id: generateNanoId(), nodes: [] });
|
||||
const workflowEntity = mock<IWorkflowBase>({ id: generateNanoId(), nodes: [] });
|
||||
|
||||
const httpMethod = 'GET';
|
||||
const path = uuid();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<WorkflowEntity> {
|
||||
export async function replaceInvalidCredentials<T extends IWorkflowBase>(workflow: T): Promise<T> {
|
||||
const { nodes } = workflow;
|
||||
if (!nodes) return workflow;
|
||||
|
||||
|
|
|
@ -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<INode>();
|
||||
const workflow = mock<WorkflowEntity>({ active: true, nodes: [node] });
|
||||
const workflow = mock<IWorkflowBase>({ active: true, nodes: [node] });
|
||||
|
||||
workflowRunner.run.mockResolvedValue('fake-execution-id');
|
||||
|
||||
|
@ -300,7 +298,7 @@ describe('WorkflowExecutionService', () => {
|
|||
});
|
||||
|
||||
describe('selectPinnedActivatorStarter()', () => {
|
||||
const workflow = mock<IWorkflowDb>({
|
||||
const workflow = mock<IWorkflowBase>({
|
||||
nodes: [],
|
||||
});
|
||||
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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
|
||||
|
|
8
packages/cli/src/workflows/workflow.formatter.ts
Normal file
8
packages/cli/src/workflows/workflow.formatter.ts
Normal file
|
@ -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})`;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<T extends IWorkflowBase>(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<T extends IWorkflowBase>(
|
||||
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 [];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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<WorkflowEntity>;
|
||||
let createInactiveWorkflow: () => Promise<WorkflowEntity>;
|
||||
let createActiveWorkflow: () => Promise<IWorkflowBase>;
|
||||
let createInactiveWorkflow: () => Promise<IWorkflowBase>;
|
||||
|
||||
beforeAll(async () => {
|
||||
await testDb.init();
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'] });
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<WorkflowEntity> => {
|
||||
const createWorkflow = async (nodes: INode[], workflowOwner?: User): Promise<IWorkflowBase> => {
|
||||
const workflowDetails = {
|
||||
id: randomInt(1, 10).toString(),
|
||||
name: 'test',
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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<ExecutionEntity>,
|
||||
workflow: IWorkflowBase,
|
||||
callback: (workflow: IWorkflowBase) => Promise<ExecutionEntity>,
|
||||
) {
|
||||
const executionsRequests = [...Array(amount)].map(async (_) => await callback(workflow));
|
||||
return await Promise.all(executionsRequests);
|
||||
|
@ -31,7 +30,7 @@ export async function createExecution(
|
|||
Omit<ExecutionEntity, 'metadata'> &
|
||||
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,
|
||||
|
|
|
@ -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<TagEntity> = {}, workflow?: WorkflowEntity) {
|
||||
export async function createTag(attributes: Partial<TagEntity> = {}, workflow?: IWorkflowBase) {
|
||||
const { name } = attributes;
|
||||
|
||||
const tag = await Container.get(TagRepository).save({
|
||||
|
|
|
@ -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<WorkflowEntity> = {},
|
||||
attributes: Partial<IWorkflowDb> = {},
|
||||
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> = {}): WorkflowEntity {
|
||||
export function newWorkflow(attributes: Partial<IWorkflowDb> = {}): IWorkflowDb {
|
||||
const { active, name, nodes, connections, versionId } = attributes;
|
||||
|
||||
const workflowEntity = Container.get(WorkflowRepository).create({
|
||||
|
@ -53,7 +54,7 @@ export function newWorkflow(attributes: Partial<WorkflowEntity> = {}): WorkflowE
|
|||
* @param user user to assign the workflow to
|
||||
*/
|
||||
export async function createWorkflow(
|
||||
attributes: Partial<WorkflowEntity> = {},
|
||||
attributes: Partial<IWorkflowDb> = {},
|
||||
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<DeepPartial<SharedWorkflow>> = 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<WorkflowEntity> = {},
|
||||
attributes: Partial<IWorkflowDb> = {},
|
||||
user?: User,
|
||||
) {
|
||||
const workflow = await createWorkflow(
|
||||
|
|
|
@ -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<WorkflowEntity> => ({
|
||||
): Partial<IWorkflowBase> => ({
|
||||
active: true,
|
||||
nodes: [
|
||||
{
|
||||
|
|
|
@ -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<WorkflowEntity & { scopes: Scope[] }>;
|
||||
const workflows = response.body.data as Array<IWorkflowBase & { scopes: Scope[] }>;
|
||||
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<WorkflowEntity & { scopes: Scope[] }>;
|
||||
const workflows = response.body.data as Array<IWorkflowBase & { scopes: Scope[] }>;
|
||||
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<WorkflowEntity & { scopes: Scope[] }>;
|
||||
const workflows = response.body.data as Array<IWorkflowBase & { scopes: Scope[] }>;
|
||||
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);
|
||||
|
|
|
@ -2196,6 +2196,8 @@ export interface IWaitingForExecutionSource {
|
|||
};
|
||||
}
|
||||
|
||||
export type WorkflowId = IWorkflowBase['id'];
|
||||
|
||||
export interface IWorkflowBase {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
Loading…
Reference in a new issue