refactor(core): Move final batch of entities to @n8n/db (#15061)

This commit is contained in:
Iván Ovejero
2025-05-02 17:46:05 +02:00
committed by GitHub
parent f3e29d25ed
commit bd258be052
63 changed files with 201 additions and 420 deletions
@@ -1,4 +1,5 @@
import type { User } from '@n8n/db';
import type { ExecutionEntity } from '@n8n/db';
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { DirectedGraph, WorkflowExecute } from 'n8n-core';
@@ -20,7 +21,6 @@ import PCancelable from 'p-cancelable';
import { ActiveExecutions } from '@/active-executions';
import config from '@/config';
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
import { ExecutionNotFoundError } from '@/errors/execution-not-found-error';
import { CredentialsPermissionChecker } from '@/executions/pre-execution-checks';
import { ManualExecutionService } from '@/manual-execution.service';
@@ -1,9 +1,8 @@
import { NoXss } from '@n8n/db';
import { Expose } from 'class-transformer';
import { IsString, IsArray, IsOptional, IsEmail, IsEnum } from 'class-validator';
import type { IPersonalizationSurveyAnswersV4 } from 'n8n-workflow';
import { NoXss } from '@/validators/no-xss.validator';
export class PersonalizationSurveyAnswersV4 implements IPersonalizationSurveyAnswersV4 {
@NoXss()
@Expose()
+6 -2
View File
@@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { entities } from '@n8n/db';
import { Container } from '@n8n/di';
import type { DataSourceOptions, LoggerOptions } from '@n8n/typeorm';
import type { MysqlConnectionOptions } from '@n8n/typeorm/driver/mysql/MysqlConnectionOptions';
@@ -10,7 +11,10 @@ import { UserError } from 'n8n-workflow';
import path from 'path';
import type { TlsOptions } from 'tls';
import { entities } from './entities';
import { InsightsByPeriod } from '@/modules/insights/database/entities/insights-by-period';
import { InsightsMetadata } from '@/modules/insights/database/entities/insights-metadata';
import { InsightsRaw } from '@/modules/insights/database/entities/insights-raw';
import { mysqlMigrations } from './migrations/mysqldb';
import { postgresMigrations } from './migrations/postgresdb';
import { sqliteMigrations } from './migrations/sqlite';
@@ -32,7 +36,7 @@ const getCommonOptions = () => {
return {
entityPrefix,
entities: Object.values(entities),
entities: [...Object.values(entities), InsightsRaw, InsightsByPeriod, InsightsMetadata],
subscribers: Object.values(subscribers),
migrationsTableName: `${entityPrefix}migrations`,
migrationsRun: false,
@@ -1,35 +0,0 @@
import { User } from '@n8n/db';
describe('User Entity', () => {
describe('JSON.stringify', () => {
it('should not serialize sensitive data', () => {
const user = Object.assign(new User(), {
email: 'test@example.com',
firstName: 'Don',
lastName: 'Joe',
password: '123456789',
});
expect(JSON.stringify(user)).toEqual(
'{"email":"test@example.com","firstName":"Don","lastName":"Joe"}',
);
});
});
describe('createPersonalProjectName', () => {
test.each([
['Nathan', 'Nathaniel', 'nathan@nathaniel.n8n', 'Nathan Nathaniel <nathan@nathaniel.n8n>'],
[undefined, 'Nathaniel', 'nathan@nathaniel.n8n', '<nathan@nathaniel.n8n>'],
['Nathan', undefined, 'nathan@nathaniel.n8n', '<nathan@nathaniel.n8n>'],
[undefined, undefined, 'nathan@nathaniel.n8n', '<nathan@nathaniel.n8n>'],
[undefined, undefined, undefined, 'Unnamed Project'],
['Nathan', 'Nathaniel', undefined, 'Unnamed Project'],
])(
'given fistName: %s, lastName: %s and email: %s this gives the projectName: "%s"',
async (firstName, lastName, email, projectName) => {
const user = new User();
Object.assign(user, { firstName, lastName, email });
expect(user.createPersonalProjectName()).toBe(projectName);
},
);
});
});
@@ -1,21 +0,0 @@
import { WithTimestampsAndStringId } from '@n8n/db';
import { Column, Entity, Index, ManyToMany, OneToMany } from '@n8n/typeorm';
import { IsString, Length } from 'class-validator';
import type { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping.ee';
import type { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee';
@Entity()
export class AnnotationTagEntity extends WithTimestampsAndStringId {
@Column({ length: 24 })
@Index({ unique: true })
@IsString({ message: 'Tag name must be of type string.' })
@Length(1, 24, { message: 'Tag name must be $constraint1 to $constraint2 characters long.' })
name: string;
@ManyToMany('ExecutionAnnotation', 'tags')
annotations: ExecutionAnnotation[];
@OneToMany('AnnotationTagMapping', 'tags')
annotationMappings: AnnotationTagMapping[];
}
@@ -1,24 +0,0 @@
import { Entity, JoinColumn, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import type { AnnotationTagEntity } from './annotation-tag-entity.ee';
import type { ExecutionAnnotation } from './execution-annotation.ee';
/**
* This entity represents the junction table between the execution annotations and the tags
*/
@Entity({ name: 'execution_annotation_tags' })
export class AnnotationTagMapping {
@PrimaryColumn()
annotationId: number;
@ManyToOne('ExecutionAnnotation', 'tagMappings')
@JoinColumn({ name: 'annotationId' })
annotations: ExecutionAnnotation[];
@PrimaryColumn()
tagId: string;
@ManyToOne('AnnotationTagEntity', 'annotationMappings')
@JoinColumn({ name: 'tagId' })
tags: AnnotationTagEntity[];
}
@@ -1,41 +0,0 @@
import { DateTimeColumn, AuthProviderType } from '@n8n/db';
import { Column, Entity, PrimaryGeneratedColumn } from '@n8n/typeorm';
export type RunningMode = 'dry' | 'live';
export type SyncStatus = 'success' | 'error';
@Entity()
export class AuthProviderSyncHistory {
@PrimaryGeneratedColumn()
id: number;
@Column('text')
providerType: AuthProviderType;
@Column('text')
runMode: RunningMode;
@Column('text')
status: SyncStatus;
@DateTimeColumn()
startedAt: Date;
@DateTimeColumn()
endedAt: Date;
@Column()
scanned: number;
@Column()
created: number;
@Column()
updated: number;
@Column()
disabled: number;
@Column()
error: string;
}
@@ -1,11 +0,0 @@
import { User } from '@n8n/db';
import { Column, Entity } from '@n8n/typeorm';
@Entity({ name: 'user' })
export class AuthUser extends User {
@Column({ type: String, nullable: true })
mfaSecret?: string | null;
@Column({ type: 'simple-array', default: '' })
mfaRecoveryCodes: string[];
}
@@ -1,62 +0,0 @@
import {
Column,
Entity,
Index,
JoinColumn,
JoinTable,
ManyToMany,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
RelationId,
} from '@n8n/typeorm';
import type { AnnotationVote } from 'n8n-workflow';
import type { AnnotationTagEntity } from './annotation-tag-entity.ee';
import type { AnnotationTagMapping } from './annotation-tag-mapping.ee';
import { ExecutionEntity } from './execution-entity';
@Entity({ name: 'execution_annotations' })
export class ExecutionAnnotation {
@PrimaryGeneratedColumn()
id: number;
/**
* This field stores the up- or down-vote of the execution by user.
*/
@Column({ type: 'varchar', nullable: true })
vote: AnnotationVote | null;
/**
* Custom text note added to the execution by user.
*/
@Column({ type: 'varchar', nullable: true })
note: string | null;
@RelationId((annotation: ExecutionAnnotation) => annotation.execution)
executionId: string;
@Index({ unique: true })
@OneToOne('ExecutionEntity', 'annotation', {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'executionId' })
execution: ExecutionEntity;
@ManyToMany('AnnotationTagEntity', 'annotations')
@JoinTable({
name: 'execution_annotation_tags', // table name for the junction table of this relation
joinColumn: {
name: 'annotationId',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'tagId',
referencedColumnName: 'id',
},
})
tags?: AnnotationTagEntity[];
@OneToMany('AnnotationTagMapping', 'annotations')
tagMappings: AnnotationTagMapping[];
}
@@ -1,27 +0,0 @@
import { JsonColumn, idStringifier } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { IWorkflowBase } from 'n8n-workflow';
import { ExecutionEntity } from './execution-entity';
@Entity()
export class ExecutionData {
@Column('text')
data: string;
// WARNING: the workflowData column has been changed from IWorkflowDb to IWorkflowBase
// when ExecutionData was introduced as a separate entity.
// This is because manual executions of unsaved workflows have no workflow id
// and IWorkflowDb has it as a mandatory field. IWorkflowBase reflects the correct
// data structure for this entity.
@JsonColumn()
workflowData: IWorkflowBase;
@PrimaryColumn({ transformer: idStringifier })
executionId: string;
@ManyToOne('ExecutionEntity', 'data', {
onDelete: 'CASCADE',
})
execution: ExecutionEntity;
}
@@ -1,90 +0,0 @@
import { DateTimeColumn, datetimeColumnType, idStringifier, WorkflowEntity } from '@n8n/db';
import {
Column,
Entity,
Generated,
Index,
ManyToOne,
OneToMany,
OneToOne,
PrimaryColumn,
Relation,
DeleteDateColumn,
} from '@n8n/typeorm';
import type { SimpleColumnType } from '@n8n/typeorm/driver/types/ColumnTypes';
import { ExecutionStatus, WorkflowExecuteMode } from 'n8n-workflow';
import type { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee';
import type { ExecutionData } from './execution-data';
import type { ExecutionMetadata } from './execution-metadata';
@Entity()
@Index(['workflowId', 'id'])
@Index(['waitTill', 'id'])
@Index(['finished', 'id'])
@Index(['workflowId', 'finished', 'id'])
@Index(['workflowId', 'waitTill', 'id'])
export class ExecutionEntity {
@Generated()
@PrimaryColumn({ transformer: idStringifier })
id: string;
/**
* Whether the execution finished sucessfully.
*
* @deprecated Use `status` instead
*/
@Column()
finished: boolean;
@Column('varchar')
mode: WorkflowExecuteMode;
@Column({ nullable: true })
retryOf: string;
@Column({ nullable: true })
retrySuccessId: string;
@Column('varchar')
status: ExecutionStatus;
@Column(datetimeColumnType)
createdAt: Date;
/**
* Time when the processing of the execution actually started. This column
* is `null` when an execution is enqueued but has not started yet.
*/
@Column({
type: datetimeColumnType as SimpleColumnType,
nullable: true,
})
startedAt: Date | null;
@Index()
@DateTimeColumn({ nullable: true })
stoppedAt: Date;
@DeleteDateColumn({ type: datetimeColumnType as SimpleColumnType, nullable: true })
deletedAt: Date;
@Column({ nullable: true })
workflowId: string;
@DateTimeColumn({ nullable: true })
waitTill: Date | null;
@OneToMany('ExecutionMetadata', 'execution')
metadata: ExecutionMetadata[];
@OneToOne('ExecutionData', 'execution')
executionData: Relation<ExecutionData>;
@OneToOne('ExecutionAnnotation', 'execution')
annotation?: Relation<ExecutionAnnotation>;
@ManyToOne('WorkflowEntity')
workflow: WorkflowEntity;
}
@@ -1,23 +0,0 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from '@n8n/typeorm';
import { ExecutionEntity } from './execution-entity';
@Entity()
export class ExecutionMetadata {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne('ExecutionEntity', 'metadata', {
onDelete: 'CASCADE',
})
execution: ExecutionEntity;
@Column()
executionId: string;
@Column('text')
key: string;
@Column('text')
value: string;
}
@@ -1,82 +0,0 @@
import {
EventDestinations,
InstalledNodes,
InstalledPackages,
InvalidAuthToken,
ProcessedData,
Settings,
Variables,
WebhookEntity,
AuthIdentity,
User,
WorkflowEntity,
CredentialsEntity,
ApiKey,
Folder,
FolderTagMapping,
Project,
ProjectRelation,
SharedCredentials,
SharedWorkflow,
TagEntity,
WorkflowStatistics,
WorkflowTagMapping,
} from '@n8n/db';
import { AnnotationTagEntity } from './annotation-tag-entity.ee';
import { AnnotationTagMapping } from './annotation-tag-mapping.ee';
import { AuthProviderSyncHistory } from './auth-provider-sync-history';
import { AuthUser } from './auth-user';
import { ExecutionAnnotation } from './execution-annotation.ee';
import { ExecutionData } from './execution-data';
import { ExecutionEntity } from './execution-entity';
import { ExecutionMetadata } from './execution-metadata';
import { TestCaseExecution } from './test-case-execution.ee';
import { TestDefinition } from './test-definition.ee';
import { TestMetric } from './test-metric.ee';
import { TestRun } from './test-run.ee';
import { WorkflowHistory } from './workflow-history';
import { InsightsByPeriod } from '../../modules/insights/database/entities/insights-by-period';
import { InsightsMetadata } from '../../modules/insights/database/entities/insights-metadata';
import { InsightsRaw } from '../../modules/insights/database/entities/insights-raw';
export const entities = {
AnnotationTagEntity,
AnnotationTagMapping,
AuthIdentity,
AuthProviderSyncHistory,
AuthUser,
CredentialsEntity,
EventDestinations,
ExecutionAnnotation,
ExecutionEntity,
InstalledNodes,
InstalledPackages,
InvalidAuthToken,
Settings,
SharedCredentials,
SharedWorkflow,
TagEntity,
User,
Variables,
WebhookEntity,
WorkflowEntity,
WorkflowTagMapping,
WorkflowStatistics,
ExecutionMetadata,
ExecutionData,
WorkflowHistory,
Project,
ProjectRelation,
ApiKey,
ProcessedData,
TestDefinition,
TestMetric,
TestRun,
TestCaseExecution,
Folder,
FolderTagMapping,
InsightsRaw,
InsightsMetadata,
InsightsByPeriod,
};
@@ -1,75 +0,0 @@
import { DateTimeColumn, JsonColumn, WithStringId } from '@n8n/db';
import { Column, Entity, ManyToOne, OneToOne } from '@n8n/typeorm';
import type { IDataObject } from 'n8n-workflow';
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
import { TestRun } from '@/databases/entities/test-run.ee';
import type { TestCaseExecutionErrorCode } from '@/evaluation.ee/test-runner/errors.ee';
export type TestCaseRunMetrics = Record<string, number | boolean>;
export type TestCaseExecutionStatus =
| 'new' // Test case execution was created and added to the test run, but has not been started yet
| 'running' // Workflow under test is running
| 'evaluation_running' // Evaluation workflow is running
| 'success' // Both workflows have completed successfully
| 'error' // An error occurred during the execution of workflow under test or evaluation workflow
| 'warning' // There were warnings during the execution of workflow under test or evaluation workflow. Used only to signal possible issues to user, not to indicate a failure.
| 'cancelled';
/**
* This entity represents the linking between the test runs and individual executions.
* It stores status, links to past, new and evaluation executions, and metrics produced by individual evaluation wf executions
* Entries in this table are meant to outlive the execution entities, which might be pruned over time.
* This allows us to keep track of the details of test runs' status and metrics even after the executions are deleted.
*/
@Entity({ name: 'test_case_execution' })
export class TestCaseExecution extends WithStringId {
@ManyToOne('TestRun')
testRun: TestRun;
@ManyToOne('ExecutionEntity', {
onDelete: 'SET NULL',
nullable: true,
})
pastExecution: ExecutionEntity | null;
@Column({ type: 'varchar', nullable: true })
pastExecutionId: string | null;
@OneToOne('ExecutionEntity', {
onDelete: 'SET NULL',
nullable: true,
})
execution: ExecutionEntity | null;
@Column({ type: 'varchar', nullable: true })
executionId: string | null;
@OneToOne('ExecutionEntity', {
onDelete: 'SET NULL',
nullable: true,
})
evaluationExecution: ExecutionEntity | null;
@Column({ type: 'varchar', nullable: true })
evaluationExecutionId: string | null;
@Column()
status: TestCaseExecutionStatus;
@DateTimeColumn({ nullable: true })
runAt: Date | null;
@DateTimeColumn({ nullable: true })
completedAt: Date | null;
@Column('varchar', { nullable: true })
errorCode: TestCaseExecutionErrorCode | null;
@JsonColumn({ nullable: true })
errorDetails: IDataObject | null;
@JsonColumn({ nullable: true })
metrics: TestCaseRunMetrics;
}
@@ -1,67 +0,0 @@
import { JsonColumn, WithTimestampsAndStringId, WorkflowEntity } from '@n8n/db';
import { Column, Entity, Index, ManyToOne, OneToMany, RelationId } from '@n8n/typeorm';
import { Length } from 'class-validator';
import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';
import type { TestMetric } from '@/databases/entities/test-metric.ee';
// Entity representing a node in a workflow under test, for which data should be mocked during test execution
export type MockedNodeItem = {
name?: string;
id: string;
};
/**
* Entity representing a Test Definition
* It combines:
* - the workflow under test
* - the workflow used to evaluate the results of test execution
* - the filter used to select test cases from previous executions of the workflow under test - annotation tag
*/
@Entity()
@Index(['workflow'])
@Index(['evaluationWorkflow'])
export class TestDefinition extends WithTimestampsAndStringId {
@Column({ length: 255 })
@Length(1, 255, {
message: 'Test definition name must be $constraint1 to $constraint2 characters long.',
})
name: string;
@Column('text')
description: string;
@JsonColumn({ default: '[]' })
mockedNodes: MockedNodeItem[];
/**
* Relation to the workflow under test
*/
@ManyToOne('WorkflowEntity', 'tests')
workflow: WorkflowEntity;
@RelationId((test: TestDefinition) => test.workflow)
workflowId: string;
/**
* Relation to the workflow used to evaluate the results of test execution
*/
@ManyToOne('WorkflowEntity', 'evaluationTests')
evaluationWorkflow: WorkflowEntity;
@RelationId((test: TestDefinition) => test.evaluationWorkflow)
evaluationWorkflowId: string;
/**
* Relation to the annotation tag associated with the test
* This tag will be used to select the test cases to run from previous executions
*/
@ManyToOne('AnnotationTagEntity', 'test')
annotationTag: AnnotationTagEntity;
@RelationId((test: TestDefinition) => test.annotationTag)
annotationTagId: string;
@OneToMany('TestMetric', 'testDefinition')
metrics: TestMetric[];
}
@@ -1,29 +0,0 @@
import { WithTimestampsAndStringId } from '@n8n/db';
import { Column, Entity, Index, ManyToOne } from '@n8n/typeorm';
import { Length } from 'class-validator';
import { TestDefinition } from '@/databases/entities/test-definition.ee';
/**
* Entity representing a Test Metric
* It represents a single metric that can be retrieved from evaluation workflow execution result
*/
@Entity()
@Index(['testDefinition'])
export class TestMetric extends WithTimestampsAndStringId {
/**
* Name of the metric.
* This will be used as a property name to extract metric value from the evaluation workflow execution result object
*/
@Column({ length: 255 })
@Length(1, 255, {
message: 'Metric name must be $constraint1 to $constraint2 characters long.',
})
name: string;
/**
* Relation to test definition
*/
@ManyToOne('TestDefinition', 'metrics')
testDefinition: TestDefinition;
}
@@ -1,79 +0,0 @@
import { WithTimestampsAndStringId, JsonColumn, DateTimeColumn } from '@n8n/db';
import { Column, Entity, Index, ManyToOne, OneToMany, RelationId } from '@n8n/typeorm';
import type { IDataObject } from 'n8n-workflow';
import type { TestCaseExecution } from '@/databases/entities/test-case-execution.ee';
import { TestDefinition } from '@/databases/entities/test-definition.ee';
import type { TestRunFinalResult } from '@/databases/repositories/test-run.repository.ee';
import type { TestRunErrorCode } from '@/evaluation.ee/test-runner/errors.ee';
export type TestRunStatus = 'new' | 'running' | 'completed' | 'error' | 'cancelled';
export type AggregatedTestRunMetrics = Record<string, number | boolean>;
/**
* Entity representing a Test Run.
* It stores info about a specific run of a test, combining the test definition with the status and collected metrics
*/
@Entity()
@Index(['testDefinition'])
export class TestRun extends WithTimestampsAndStringId {
@ManyToOne('TestDefinition', 'runs')
testDefinition: TestDefinition;
@RelationId((testRun: TestRun) => testRun.testDefinition)
testDefinitionId: string;
@Column('varchar')
status: TestRunStatus;
@DateTimeColumn({ nullable: true })
runAt: Date | null;
@DateTimeColumn({ nullable: true })
completedAt: Date | null;
@JsonColumn({ nullable: true })
metrics: AggregatedTestRunMetrics;
/**
* Total number of the test cases, matching the filter condition of the test definition (specified annotationTag)
*/
@Column('integer', { nullable: true })
totalCases: number;
/**
* Number of test cases that passed (evaluation workflow was executed successfully)
*/
@Column('integer', { nullable: true })
passedCases: number;
/**
* Number of failed test cases
* (any unexpected exception happened during the execution or evaluation workflow ended with an error)
*/
@Column('integer', { nullable: true })
failedCases: number;
/**
* This will contain the error code if the test run failed.
* This is used for test run level errors, not for individual test case errors.
*/
@Column('varchar', { nullable: true, length: 255 })
errorCode: TestRunErrorCode | null;
/**
* Optional details about the error that happened during the test run
*/
@JsonColumn({ nullable: true })
errorDetails: IDataObject | null;
@OneToMany('TestCaseExecution', 'testRun')
testCaseExecutions: TestCaseExecution[];
/**
* Calculated property to determine the final result of the test run
* depending on the statuses of test case executions
*/
finalResult?: TestRunFinalResult | null;
}
@@ -1,27 +0,0 @@
import { JsonColumn, WithTimestamps, WorkflowEntity } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { IConnections } from 'n8n-workflow';
import type { INode } from 'n8n-workflow';
@Entity()
export class WorkflowHistory extends WithTimestamps {
@PrimaryColumn()
versionId: string;
@Column()
workflowId: string;
@JsonColumn()
nodes: INode[];
@JsonColumn()
connections: IConnections;
@Column()
authors: string;
@ManyToOne('WorkflowEntity', {
onDelete: 'CASCADE',
})
workflow: WorkflowEntity;
}
@@ -1,6 +1,7 @@
import { GlobalConfig } from '@n8n/config';
import type { SqliteConfig } from '@n8n/config/src/configs/database.config';
import type { IExecutionResponse } from '@n8n/db';
import { ExecutionEntity } from '@n8n/db';
import { Container } from '@n8n/di';
import type { SelectQueryBuilder } from '@n8n/typeorm';
import { Not, LessThanOrEqual } from '@n8n/typeorm';
@@ -9,7 +10,6 @@ import { BinaryDataService } from 'n8n-core';
import type { IRunExecutionData, IWorkflowBase } from 'n8n-workflow';
import { nanoid } from 'nanoid';
import { ExecutionEntity } from '@/databases/entities/execution-entity';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { mockInstance, mockEntityManager } from '@test/mocking';
@@ -1,8 +1,7 @@
import { AnnotationTagMapping } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping.ee';
@Service()
export class AnnotationTagMappingRepository extends Repository<AnnotationTagMapping> {
constructor(dataSource: DataSource) {
@@ -1,8 +1,7 @@
import { AnnotationTagEntity } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';
@Service()
export class AnnotationTagRepository extends Repository<AnnotationTagEntity> {
constructor(dataSource: DataSource) {
@@ -1,8 +1,7 @@
import { AuthProviderSyncHistory } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { AuthProviderSyncHistory } from '../entities/auth-provider-sync-history';
@Service()
export class AuthProviderSyncHistoryRepository extends Repository<AuthProviderSyncHistory> {
constructor(dataSource: DataSource) {
@@ -1,8 +1,7 @@
import { AuthUser } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { AuthUser } from '../entities/auth-user';
@Service()
export class AuthUserRepository extends Repository<AuthUser> {
constructor(dataSource: DataSource) {
@@ -1,8 +1,7 @@
import { ExecutionAnnotation } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee';
@Service()
export class ExecutionAnnotationRepository extends Repository<ExecutionAnnotation> {
constructor(dataSource: DataSource) {
@@ -1,10 +1,9 @@
import { ExecutionData } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, In, Repository } from '@n8n/typeorm';
import type { EntityManager } from '@n8n/typeorm';
import type { QueryDeepPartialEntity } from '@n8n/typeorm/query-builder/QueryPartialEntity';
import { ExecutionData } from '../entities/execution-data';
@Service()
export class ExecutionDataRepository extends Repository<ExecutionData> {
constructor(dataSource: DataSource) {
@@ -1,8 +1,7 @@
import { ExecutionMetadata } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { ExecutionMetadata } from '../entities/execution-metadata';
@Service()
export class ExecutionMetadataRepository extends Repository<ExecutionMetadata> {
constructor(dataSource: DataSource) {
@@ -6,7 +6,17 @@ import type {
IExecutionResponse,
ExecutionSummaries,
} from '@n8n/db';
import { separate, SharedWorkflow, WorkflowEntity } from '@n8n/db';
import {
separate,
SharedWorkflow,
WorkflowEntity,
AnnotationTagEntity,
AnnotationTagMapping,
ExecutionData,
ExecutionEntity,
ExecutionAnnotation,
ExecutionMetadata,
} from '@n8n/db';
import { Service } from '@n8n/di';
import type {
FindManyOptions,
@@ -39,15 +49,9 @@ import type {
IRunExecutionData,
} from 'n8n-workflow';
import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';
import { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping.ee';
import { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee';
import { PostgresLiveRowsRetrievalError } from '@/errors/postgres-live-rows-retrieval.error';
import { ExecutionDataRepository } from './execution-data.repository';
import { ExecutionData } from '../entities/execution-data';
import { ExecutionEntity } from '../entities/execution-entity';
import { ExecutionMetadata } from '../entities/execution-metadata';
export interface IGetExecutionsQueryFilter {
id?: FindOperator<string> | string;
@@ -1,12 +1,11 @@
import { TestCaseExecution } from '@n8n/db';
import type { TestCaseExecutionErrorCode } from '@n8n/db';
import { Service } from '@n8n/di';
import type { EntityManager } from '@n8n/typeorm';
import { DataSource, In, Not, Repository } from '@n8n/typeorm';
import type { DeepPartial } from '@n8n/typeorm/common/DeepPartial';
import type { IDataObject } from 'n8n-workflow';
import { TestCaseExecution } from '@/databases/entities/test-case-execution.ee';
import type { TestCaseExecutionErrorCode } from '@/evaluation.ee/test-runner/errors.ee';
type StatusUpdateOptions = {
testRunId: string;
pastExecutionId: string;
@@ -1,8 +1,8 @@
import { TestDefinition } from '@n8n/db';
import { Service } from '@n8n/di';
import type { FindManyOptions, FindOptionsWhere } from '@n8n/typeorm';
import { DataSource, In, Repository } from '@n8n/typeorm';
import { TestDefinition } from '@/databases/entities/test-definition.ee';
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
import type { ListQuery } from '@/requests';
@@ -1,8 +1,7 @@
import { TestMetric } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, Repository } from '@n8n/typeorm';
import { TestMetric } from '@/databases/entities/test-metric.ee';
@Service()
export class TestMetricRepository extends Repository<TestMetric> {
constructor(dataSource: DataSource) {
@@ -1,17 +1,14 @@
import type { AggregatedTestRunMetrics, TestRunErrorCode, TestRunFinalResult } from '@n8n/db';
import { TestRun } from '@n8n/db';
import { Service } from '@n8n/di';
import type { EntityManager, FindManyOptions } from '@n8n/typeorm';
import { DataSource, In, Repository } from '@n8n/typeorm';
import type { IDataObject } from 'n8n-workflow';
import type { AggregatedTestRunMetrics } from '@/databases/entities/test-run.ee';
import { TestRun } from '@/databases/entities/test-run.ee';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { TestRunErrorCode } from '@/evaluation.ee/test-runner/errors.ee';
import { getTestRunFinalResult } from '@/evaluation.ee/test-runner/utils.ee';
import type { ListQuery } from '@/requests';
export type TestRunFinalResult = 'success' | 'error' | 'warning';
export type TestRunSummary = TestRun & {
finalResult: TestRunFinalResult | null;
};
@@ -1,8 +1,7 @@
import { WorkflowHistory } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, LessThan, Repository } from '@n8n/typeorm';
import { WorkflowHistory } from '../entities/workflow-history';
@Service()
export class WorkflowHistoryRepository extends Repository<WorkflowHistory> {
constructor(dataSource: DataSource) {
@@ -1,6 +1,6 @@
import type { MockedNodeItem, TestDefinition } from '@n8n/db';
import { Service } from '@n8n/di';
import type { MockedNodeItem, TestDefinition } from '@/databases/entities/test-definition.ee';
import { AnnotationTagRepository } from '@/databases/repositories/annotation-tag.repository.ee';
import { TestDefinitionRepository } from '@/databases/repositories/test-definition.repository.ee';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@@ -1,4 +1,5 @@
import type { MockedNodeItem } from '@/databases/entities/test-definition.ee';
import type { MockedNodeItem } from '@n8n/db';
import type { AuthenticatedRequest, ListQuery } from '@/requests';
// ----------------------------------
@@ -1,6 +1,6 @@
import type { TestCaseExecution } from '@n8n/db';
import { mock } from 'jest-mock-extended';
import type { TestCaseExecution } from '@/databases/entities/test-case-execution.ee';
import { getTestRunFinalResult } from '@/evaluation.ee/test-runner/utils.ee';
function mockTestCaseExecutions(statuses: Array<TestCaseExecution['status']>) {
@@ -1,19 +1,19 @@
import type { User } from '@n8n/db';
import type { ExecutionEntity } from '@n8n/db';
import type { TestDefinition } from '@n8n/db';
import type { TestMetric } from '@n8n/db';
import type { TestRun } from '@n8n/db';
import type { SelectQueryBuilder } from '@n8n/typeorm';
import { stringify } from 'flatted';
import { readFileSync } from 'fs';
import { mock, mockDeep } from 'jest-mock-extended';
import type { ErrorReporter } from 'n8n-core';
import type { ExecutionError, GenericValue, IRun } from 'n8n-workflow';
import type { ITaskData } from 'n8n-workflow';
import type { ExecutionError, GenericValue, IRun } from 'n8n-workflow';
import path from 'path';
import type { ActiveExecutions } from '@/active-executions';
import config from '@/config';
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
import type { TestDefinition } from '@/databases/entities/test-definition.ee';
import type { TestMetric } from '@/databases/entities/test-metric.ee';
import type { TestRun } from '@/databases/entities/test-run.ee';
import type { ExecutionRepository } from '@/databases/repositories/execution.repository';
import type { TestCaseExecutionRepository } from '@/databases/repositories/test-case-execution.repository.ee';
import type { TestMetricRepository } from '@/databases/repositories/test-metric.repository.ee';
@@ -1,15 +1,6 @@
import type { TestCaseExecutionErrorCode, TestRunErrorCode } from '@n8n/db';
import { UnexpectedError } from 'n8n-workflow';
export type TestCaseExecutionErrorCode =
| 'MOCKED_NODE_DOES_NOT_EXIST'
| 'TRIGGER_NO_LONGER_EXISTS'
| 'FAILED_TO_EXECUTE_WORKFLOW'
| 'EVALUATION_WORKFLOW_DOES_NOT_EXIST'
| 'FAILED_TO_EXECUTE_EVALUATION_WORKFLOW'
| 'INVALID_METRICS'
| 'PAYLOAD_LIMIT_EXCEEDED'
| 'UNKNOWN_ERROR';
export class TestCaseExecutionError extends UnexpectedError {
readonly code: TestCaseExecutionErrorCode;
@@ -20,12 +11,6 @@ export class TestCaseExecutionError extends UnexpectedError {
}
}
export type TestRunErrorCode =
| 'PAST_EXECUTIONS_NOT_FOUND'
| 'EVALUATION_WORKFLOW_NOT_FOUND'
| 'INTERRUPTED'
| 'UNKNOWN_ERROR';
export class TestRunError extends UnexpectedError {
readonly code: TestRunErrorCode;
@@ -1,4 +1,4 @@
import type { User } from '@n8n/db';
import type { User, ExecutionEntity, MockedNodeItem, TestDefinition, TestRun } from '@n8n/db';
import { Service } from '@n8n/di';
import { parse } from 'flatted';
import difference from 'lodash/difference';
@@ -17,9 +17,6 @@ import assert from 'node:assert';
import { ActiveExecutions } from '@/active-executions';
import config from '@/config';
import { EVALUATION_METRICS_NODE } from '@/constants';
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 { 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';
@@ -1,3 +1,4 @@
import type { TestCaseExecution, MockedNodeItem, TestRunFinalResult } from '@n8n/db';
import assert from 'assert';
import { mapValues, pick } from 'lodash';
import type {
@@ -9,9 +10,6 @@ import type {
INode,
} from 'n8n-workflow';
import type { TestCaseExecution } from '@/databases/entities/test-case-execution.ee';
import type { MockedNodeItem } from '@/databases/entities/test-definition.ee';
import type { TestRunFinalResult } from '@/databases/repositories/test-run.repository.ee';
import { TestCaseExecutionError } from '@/evaluation.ee/test-runner/errors.ee';
import type { TestCaseRunMetadata } from '@/evaluation.ee/test-runner/test-runner.service.ee';
@@ -1,4 +1,4 @@
import type { ExecutionSummaries } from '@n8n/db';
import type { ExecutionSummaries, ExecutionEntity } from '@n8n/db';
import type {
AnnotationVote,
ExecutionStatus,
@@ -6,7 +6,6 @@ import type {
WorkflowExecuteMode,
} from 'n8n-workflow';
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
import type { AuthenticatedRequest } from '@/requests';
export declare namespace ExecutionRequest {
+8 -4
View File
@@ -1,9 +1,13 @@
import type { CredentialsEntity, User, WorkflowEntity, TagEntity } from '@n8n/db';
import type {
CredentialsEntity,
User,
WorkflowEntity,
TagEntity,
AnnotationTagEntity,
TestDefinition,
} from '@n8n/db';
import { validate } from 'class-validator';
import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';
import type { TestDefinition } from '@/databases/entities/test-definition.ee';
import type { PersonalizationSurveyAnswersV4 } from './controllers/survey-answers.dto';
import { BadRequestError } from './errors/response-errors/bad-request.error';
+1 -1
View File
@@ -1,4 +1,5 @@
import { AuthIdentity, User } from '@n8n/db';
import type { AuthProviderSyncHistory } from '@n8n/db';
import { Container } from '@n8n/di';
import { validate } from 'jsonschema';
import type { Entry as LdapUser } from 'ldapts';
@@ -6,7 +7,6 @@ import { Filter } from 'ldapts/filters/Filter';
import { randomString } from 'n8n-workflow';
import config from '@/config';
import type { AuthProviderSyncHistory } from '@/databases/entities/auth-provider-sync-history';
import { AuthIdentityRepository } from '@/databases/repositories/auth-identity.repository';
import { AuthProviderSyncHistoryRepository } from '@/databases/repositories/auth-provider-sync-history.repository';
import { UserRepository } from '@/databases/repositories/user.repository';
+1 -2
View File
@@ -1,4 +1,4 @@
import type { User } from '@n8n/db';
import type { User, RunningMode, SyncStatus } from '@n8n/db';
import { Service } from '@n8n/di';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import { QueryFailedError } from '@n8n/typeorm';
@@ -9,7 +9,6 @@ import { jsonParse, UnexpectedError } from 'n8n-workflow';
import type { ConnectionOptions } from 'tls';
import config from '@/config';
import type { RunningMode, SyncStatus } from '@/databases/entities/auth-provider-sync-history';
import { SettingsRepository } from '@/databases/repositories/settings.repository';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
+2 -1
View File
@@ -1,4 +1,5 @@
import type { RunningMode } from '@/databases/entities/auth-provider-sync-history';
import type { RunningMode } from '@n8n/db';
import type { AuthenticatedRequest } from '@/requests';
export type ConnectionSecurity = 'none' | 'tls' | 'startTls';
+1 -3
View File
@@ -1,5 +1,5 @@
import type { ProjectIcon, ProjectRole, ProjectType } from '@n8n/api-types';
import type { Variables, Project, User, ListQueryDb } from '@n8n/db';
import type { Variables, Project, User, ListQueryDb, WorkflowHistory } from '@n8n/db';
import type { AssignableRole, GlobalRole, Scope } from '@n8n/permissions';
import type express from 'express';
import type {
@@ -8,8 +8,6 @@ import type {
IPersonalizationSurveyAnswersV4,
} from 'n8n-workflow';
import type { WorkflowHistory } from '@/databases/entities/workflow-history';
export type APIRequest<
RouteParams = {},
ResponseBody = {},
@@ -1,9 +1,9 @@
import type { AuthUser } from '@n8n/db';
import RudderStack from '@rudderstack/rudder-sdk-node';
import type { Response } from 'express';
import { mock } from 'jest-mock-extended';
import type { AuthService } from '@/auth/auth.service';
import type { AuthUser } from '@/databases/entities/auth-user';
import type { AuthUserRepository } from '@/databases/repositories/auth-user.repository';
import type { CredentialsRepository } from '@/databases/repositories/credentials.repository';
import type { SettingsRepository } from '@/databases/repositories/settings.repository';
@@ -1,6 +1,6 @@
import type { AnnotationTagEntity } from '@n8n/db';
import { Service } from '@n8n/di';
import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';
import { AnnotationTagRepository } from '@/databases/repositories/annotation-tag.repository.ee';
import { validateEntity } from '@/generic-helpers';
@@ -1,6 +1,6 @@
import type { ExecutionMetadata } from '@n8n/db';
import { Service } from '@n8n/di';
import type { ExecutionMetadata } from '@/databases/entities/execution-metadata';
import { ExecutionMetadataRepository } from '@/databases/repositories/execution-metadata.repository';
@Service()
+1 -2
View File
@@ -1,4 +1,4 @@
import type { Settings, CredentialsEntity, User, WorkflowEntity } from '@n8n/db';
import type { Settings, CredentialsEntity, User, WorkflowEntity, AuthUser } from '@n8n/db';
import { Service } from '@n8n/di';
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
import type { FindManyOptions, FindOneOptions, FindOptionsWhere } from '@n8n/typeorm';
@@ -7,7 +7,6 @@ import RudderStack, { type constructorOptions } from '@rudderstack/rudder-sdk-no
import type { NextFunction, Response } from 'express';
import { AuthService } from '@/auth/auth.service';
import type { AuthUser } from '@/databases/entities/auth-user';
import { AuthUserRepository } from '@/databases/repositories/auth-user.repository';
import { CredentialsRepository } from '@/databases/repositories/credentials.repository';
import { SettingsRepository } from '@/databases/repositories/settings.repository';
@@ -1,27 +0,0 @@
import { validate } from 'class-validator';
import { NoUrl } from '../no-url.validator';
describe('NoUrl', () => {
class Entity {
@NoUrl()
name = '';
}
const entity = new Entity();
describe('URLs', () => {
const URLS = ['http://google.com', 'www.domain.tld', 'n8n.io'];
for (const str of URLS) {
test(`should block ${str}`, async () => {
entity.name = str;
const errors = await validate(entity);
expect(errors).toHaveLength(1);
const [error] = errors;
expect(error.property).toEqual('name');
expect(error.constraints).toEqual({ NoUrl: 'Potentially malicious string' });
});
}
});
});
@@ -1,118 +0,0 @@
import { validate } from 'class-validator';
import { NoXss } from '../no-xss.validator';
describe('NoXss', () => {
class Entity {
@NoXss()
name = '';
@NoXss()
timestamp = '';
@NoXss()
version = '';
@NoXss({ each: true })
categories: string[] = [];
}
const entity = new Entity();
describe('Scripts', () => {
// eslint-disable-next-line n8n-local-rules/no-unneeded-backticks
const XSS_STRINGS = ['<script src/>', "<script>alert('xss')</script>", `<a href="#">Jack</a>`];
for (const str of XSS_STRINGS) {
test(`should block ${str}`, async () => {
entity.name = str;
const errors = await validate(entity);
expect(errors).toHaveLength(1);
const [error] = errors;
expect(error.property).toEqual('name');
expect(error.constraints).toEqual({ NoXss: 'Potentially malicious string' });
});
}
});
describe('Names', () => {
const VALID_NAMES = [
'Johann Strauß',
'Вагиф Сәмәдоғлу',
'René Magritte',
'সুকুমার রায়',
'མགོན་པོ་རྡོ་རྗེ།',
'عبدالحليم حافظ',
];
for (const name of VALID_NAMES) {
test(`should allow ${name}`, async () => {
entity.name = name;
expect(await validate(entity)).toBeEmptyArray();
});
}
});
describe('ISO-8601 timestamps', () => {
const VALID_TIMESTAMPS = ['2022-01-01T00:00:00.000Z', '2022-01-01T00:00:00.000+02:00'];
for (const timestamp of VALID_TIMESTAMPS) {
test(`should allow ${timestamp}`, async () => {
entity.timestamp = timestamp;
await expect(validate(entity)).resolves.toBeEmptyArray();
});
}
});
describe('Semver versions', () => {
const VALID_VERSIONS = ['1.0.0', '1.0.0-alpha.1'];
for (const version of VALID_VERSIONS) {
test(`should allow ${version}`, async () => {
entity.version = version;
await expect(validate(entity)).resolves.toBeEmptyArray();
});
}
});
describe('Miscellaneous strings', () => {
const VALID_MISCELLANEOUS_STRINGS = ['CI/CD'];
for (const str of VALID_MISCELLANEOUS_STRINGS) {
test(`should allow ${str}`, async () => {
entity.name = str;
await expect(validate(entity)).resolves.toBeEmptyArray();
});
}
});
describe('Array of strings', () => {
const VALID_STRING_ARRAYS = [
['cloud-infrastructure-orchestration', 'ci-cd', 'reporting'],
['automationGoalDevops', 'cloudComputing', 'containerization'],
];
for (const arr of VALID_STRING_ARRAYS) {
test(`should allow array: ${JSON.stringify(arr)}`, async () => {
entity.categories = arr;
await expect(validate(entity)).resolves.toBeEmptyArray();
});
}
const INVALID_STRING_ARRAYS = [
['valid-string', '<script>alert("xss")</script>', 'another-valid-string'],
['<img src="x" onerror="alert(\'XSS\')">', 'valid-string'],
];
for (const arr of INVALID_STRING_ARRAYS) {
test(`should reject array containing invalid string: ${JSON.stringify(arr)}`, async () => {
entity.categories = arr;
const errors = await validate(entity);
expect(errors).toHaveLength(1);
const [error] = errors;
expect(error.property).toEqual('categories');
expect(error.constraints).toEqual({ NoXss: 'Potentially malicious string' });
});
}
});
});
@@ -1,27 +0,0 @@
import type { ValidationOptions, ValidatorConstraintInterface } from 'class-validator';
import { registerDecorator, ValidatorConstraint } from 'class-validator';
const URL_REGEX = /^(https?:\/\/|www\.)|(\.[\p{L}\d-]+)/iu;
@ValidatorConstraint({ name: 'NoUrl', async: false })
class NoUrlConstraint implements ValidatorConstraintInterface {
validate(value: string) {
return !URL_REGEX.test(value);
}
defaultMessage() {
return 'Potentially malicious string';
}
}
export function NoUrl(options?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
name: 'NoUrl',
target: object.constructor,
propertyName,
options,
validator: NoUrlConstraint,
});
};
}
@@ -1,33 +0,0 @@
import type { ValidationOptions, ValidatorConstraintInterface } from 'class-validator';
import { registerDecorator, ValidatorConstraint } from 'class-validator';
import xss from 'xss';
@ValidatorConstraint({ name: 'NoXss', async: false })
class NoXssConstraint implements ValidatorConstraintInterface {
validate(value: unknown) {
if (typeof value !== 'string') return false;
return (
value ===
xss(value, {
whiteList: {}, // no tags are allowed
})
);
}
defaultMessage() {
return 'Potentially malicious string';
}
}
export function NoXss(options?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
name: 'NoXss',
target: object.constructor,
propertyName,
options,
validator: NoXssConstraint,
});
};
}
@@ -1,10 +1,9 @@
import type { User } from '@n8n/db';
import type { User, WorkflowHistory } from '@n8n/db';
import { Service } from '@n8n/di';
import { Logger } from 'n8n-core';
import type { IWorkflowBase } from 'n8n-workflow';
import { ensureError } from 'n8n-workflow';
import type { WorkflowHistory } from '@/databases/entities/workflow-history';
import { WorkflowHistoryRepository } from '@/databases/repositories/workflow-history.repository';
import { SharedWorkflowNotFoundError } from '@/errors/shared-workflow-not-found.error';
import { WorkflowHistoryVersionNotFoundError } from '@/errors/workflow-history-version-not-found.error';
@@ -1,9 +1,9 @@
import type { User } from '@n8n/db';
import type { AnnotationTagEntity } from '@n8n/db';
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 { 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';
@@ -1,9 +1,9 @@
import type { User } from '@n8n/db';
import type { TestDefinition } from '@n8n/db';
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 { ProjectRepository } from '@/databases/repositories/project.repository';
import { TestDefinitionRepository } from '@/databases/repositories/test-definition.repository.ee';
import { TestRunRepository } from '@/databases/repositories/test-run.repository.ee';
@@ -1,10 +1,10 @@
import { ExecutionsConfig } from '@n8n/config';
import type { ExecutionEntity } from '@n8n/db';
import { Container } from '@n8n/di';
import { BinaryDataService, InstanceSettings } from 'n8n-core';
import type { ExecutionStatus, IWorkflowBase } from 'n8n-workflow';
import { Time } from '@/constants';
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { PruningService } from '@/services/pruning/pruning.service';
@@ -1,7 +1,7 @@
import type { User } from '@n8n/db';
import type { ExecutionEntity } from '@n8n/db';
import type { ActiveWorkflowManager } from '@/active-workflow-manager';
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
import { Telemetry } from '@/telemetry';
import { mockInstance } from '@test/mocking';
import { createTeamProject } from '@test-integration/db/projects';
@@ -1,8 +1,8 @@
import type { ExecutionEntity } from '@n8n/db';
import type { ExecutionData } from '@n8n/db';
import { Container } from '@n8n/di';
import type { AnnotationVote, IWorkflowBase } from 'n8n-workflow';
import type { ExecutionData } from '@/databases/entities/execution-data';
import type { ExecutionEntity } from '@/databases/entities/execution-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';
@@ -1,7 +1,7 @@
import type { WorkflowHistory } from '@n8n/db';
import { Container } from '@n8n/di';
import { v4 as uuid } from 'uuid';
import type { WorkflowHistory } from '@/databases/entities/workflow-history';
import { WorkflowHistoryRepository } from '@/databases/repositories/workflow-history.repository';
export async function createWorkflowHistoryItem(