refactor(core): Move initial files to @n8n/db (#14953)

This commit is contained in:
Iván Ovejero
2025-04-29 18:14:52 +02:00
committed by GitHub
parent 8613521aab
commit 20115a8fa1
95 changed files with 423 additions and 369 deletions

View File

@@ -102,7 +102,7 @@
"@n8n/permissions": "workspace:*",
"@n8n/task-runner": "workspace:*",
"@n8n/ai-workflow-builder": "workspace:*",
"@n8n/typeorm": "0.3.20-12",
"@n8n/typeorm": "catalog:",
"@n8n_io/ai-assistant-sdk": "catalog:",
"@n8n_io/license-sdk": "2.20.0",
"@oclif/core": "4.0.7",

View File

@@ -13,16 +13,12 @@ import type PCancelable from 'p-cancelable';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { ExecutionNotFoundError } from '@/errors/execution-not-found-error';
import type {
CreateExecutionPayload,
IExecutingWorkflowData,
IExecutionDb,
IExecutionsCurrentSummary,
} from '@/interfaces';
import type { IExecutingWorkflowData, IExecutionsCurrentSummary } from '@/interfaces';
import { isWorkflowIdValid } from '@/utils';
import { ConcurrencyControlService } from './concurrency/concurrency-control.service';
import config from './config';
import type { CreateExecutionPayload, IExecutionDb } from './types-db';
@Service()
export class ActiveExecutions {

View File

@@ -1,3 +1,4 @@
import { generateNanoId } from '@n8n/db';
import { Container } from '@n8n/di';
import { Flags } from '@oclif/core';
import glob from 'fast-glob';
@@ -11,7 +12,6 @@ import { ProjectRepository } from '@/databases/repositories/project.repository';
import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository';
import { UserRepository } from '@/databases/repositories/user.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { generateNanoId } from '@/databases/utils/generators';
import type { IWorkflowToImport } from '@/interfaces';
import { ImportService } from '@/services/import.service';

View File

@@ -21,9 +21,9 @@ import { FolderNotFoundError } from '@/errors/folder-not-found.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { ListQuery } from '@/requests';
import { AuthenticatedRequest } from '@/requests';
import { FolderService } from '@/services/folder.service';
import type { ListQuery } from '@/types-db';
@RestController('/projects/:projectId/folders')
export class ProjectController {

View File

@@ -27,10 +27,11 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks';
import { listQueryMiddleware } from '@/middlewares';
import { AuthenticatedRequest, ListQuery, UserRequest } from '@/requests';
import { AuthenticatedRequest, UserRequest } from '@/requests';
import { FolderService } from '@/services/folder.service';
import { ProjectService } from '@/services/project.service.ee';
import { UserService } from '@/services/user.service';
import { ListQuery } from '@/types-db';
import type { PublicUser } from '@/types-db';
import { WorkflowService } from '@/workflows/workflow.service';

View File

@@ -50,6 +50,7 @@ describe('CredentialsController', () => {
id: newCredentialsPayload.projectId,
});
// @ts-ignore
credentialsService.createUnmanagedCredential.mockResolvedValue(createdCredentials);
sharedCredentialsRepository.findCredentialOwningProject.mockResolvedValue(

View File

@@ -33,13 +33,12 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { ExternalHooks } from '@/external-hooks';
import { validateEntity } from '@/generic-helpers';
import { userHasScopes } from '@/permissions.ee/check-access';
import type { CredentialRequest, ListQuery } from '@/requests';
import type { CredentialRequest } from '@/requests';
import { CredentialsTester } from '@/services/credentials-tester.service';
import { OwnershipService } from '@/services/ownership.service';
import { ProjectService } from '@/services/project.service.ee';
import type { ScopesField } from '@/services/role.service';
import { RoleService } from '@/services/role.service';
import type { ICredentialsDb } from '@/types-db';
import type { ICredentialsDb, ListQuery, ScopesField } from '@/types-db';
import { CredentialsFinderService } from './credentials-finder.service';

View File

@@ -1,68 +0,0 @@
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import type { ColumnOptions } from '@n8n/typeorm';
import {
BeforeInsert,
BeforeUpdate,
CreateDateColumn,
PrimaryColumn,
UpdateDateColumn,
} from '@n8n/typeorm';
import type { Class } from 'n8n-core';
import { generateNanoId } from '../utils/generators';
export const { type: dbType } = Container.get(GlobalConfig).database;
const timestampSyntax = {
sqlite: "STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')",
postgresdb: 'CURRENT_TIMESTAMP(3)',
mysqldb: 'CURRENT_TIMESTAMP(3)',
mariadb: 'CURRENT_TIMESTAMP(3)',
}[dbType];
export const jsonColumnType = dbType === 'sqlite' ? 'simple-json' : 'json';
export const datetimeColumnType = dbType === 'postgresdb' ? 'timestamptz' : 'datetime';
const tsColumnOptions: ColumnOptions = {
precision: 3,
default: () => timestampSyntax,
type: datetimeColumnType,
};
function mixinStringId<T extends Class<{}, any[]>>(base: T) {
class Derived extends base {
@PrimaryColumn('varchar')
id: string;
@BeforeInsert()
generateId() {
if (!this.id) {
this.id = generateNanoId();
}
}
}
return Derived;
}
function mixinTimestamps<T extends Class<{}, any[]>>(base: T) {
class Derived extends base {
@CreateDateColumn(tsColumnOptions)
createdAt: Date;
@UpdateDateColumn(tsColumnOptions)
updatedAt: Date;
@BeforeUpdate()
setUpdateDate(): void {
this.updatedAt = new Date();
}
}
return Derived;
}
class BaseEntity {}
export const WithStringId = mixinStringId(BaseEntity);
export const WithTimestamps = mixinTimestamps(BaseEntity);
export const WithTimestampsAndStringId = mixinStringId(WithTimestamps);

View File

@@ -1,11 +1,10 @@
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';
import { WithTimestampsAndStringId } from './abstract-entity';
@Entity()
export class AnnotationTagEntity extends WithTimestampsAndStringId {
@Column({ length: 24 })

View File

@@ -1,7 +1,7 @@
import { JsonColumn, WithTimestampsAndStringId } from '@n8n/db';
import type { ApiKeyScope } from '@n8n/permissions';
import { Column, Entity, Index, ManyToOne, Unique } from '@n8n/typeorm';
import { jsonColumnType, WithTimestampsAndStringId } from './abstract-entity';
import { User } from './user';
@Entity('user_api_keys')
@@ -20,7 +20,7 @@ export class ApiKey extends WithTimestampsAndStringId {
@Column({ type: String })
label: string;
@Column({ type: jsonColumnType, nullable: false })
@JsonColumn({ nullable: false })
scopes: ApiKeyScope[];
@Index({ unique: true })

View File

@@ -1,6 +1,6 @@
import { WithTimestamps } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn, Unique } from '@n8n/typeorm';
import { WithTimestamps } from './abstract-entity';
import { User } from './user';
export type AuthProviderType = 'ldap' | 'email' | 'saml'; // | 'google';

View File

@@ -1,6 +1,6 @@
import { DateTimeColumn } from '@n8n/db';
import { Column, Entity, PrimaryGeneratedColumn } from '@n8n/typeorm';
import { datetimeColumnType } from './abstract-entity';
import { AuthProviderType } from './auth-identity';
export type RunningMode = 'dry' | 'live';
@@ -20,10 +20,10 @@ export class AuthProviderSyncHistory {
@Column('text')
status: SyncStatus;
@Column(datetimeColumnType)
@DateTimeColumn()
startedAt: Date;
@Column(datetimeColumnType)
@DateTimeColumn()
endedAt: Date;
@Column()

View File

@@ -1,9 +1,9 @@
import { WithTimestampsAndStringId } from '@n8n/db';
import { Column, Entity, Index, OneToMany } from '@n8n/typeorm';
import { IsObject, IsString, Length } from 'class-validator';
import type { ICredentialsDb } from '@/types-db';
import { WithTimestampsAndStringId } from './abstract-entity';
import type { SharedCredentials } from './shared-credentials';
@Entity()

View File

@@ -1,13 +1,12 @@
import { Column, Entity, PrimaryColumn } from '@n8n/typeorm';
import { JsonColumn, WithTimestamps } from '@n8n/db';
import { Entity, PrimaryColumn } from '@n8n/typeorm';
import { MessageEventBusDestinationOptions } from 'n8n-workflow';
import { WithTimestamps, jsonColumnType } from './abstract-entity';
@Entity({ name: 'event_destinations' })
export class EventDestinations extends WithTimestamps {
@PrimaryColumn('uuid')
id: string;
@Column(jsonColumnType)
@JsonColumn()
destination: MessageEventBusDestinationOptions;
}

View File

@@ -1,7 +1,7 @@
import { JsonColumn } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { IWorkflowBase } from 'n8n-workflow';
import { jsonColumnType } from './abstract-entity';
import { ExecutionEntity } from './execution-entity';
import { idStringifier } from '../utils/transformers';
@@ -15,7 +15,7 @@ export class ExecutionData {
// 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.
@Column(jsonColumnType)
@JsonColumn()
workflowData: IWorkflowBase;
@PrimaryColumn({ transformer: idStringifier })

View File

@@ -1,3 +1,4 @@
import { DateTimeColumn, datetimeColumnType } from '@n8n/db';
import {
Column,
Entity,
@@ -10,11 +11,11 @@ import {
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 { datetimeColumnType } from './abstract-entity';
import type { ExecutionData } from './execution-data';
import type { ExecutionMetadata } from './execution-metadata';
import { WorkflowEntity } from './workflow-entity';
@@ -58,20 +59,23 @@ export class ExecutionEntity {
* 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, nullable: true })
@Column({
type: datetimeColumnType as SimpleColumnType,
nullable: true,
})
startedAt: Date | null;
@Index()
@Column({ type: datetimeColumnType, nullable: true })
@DateTimeColumn({ nullable: true })
stoppedAt: Date;
@DeleteDateColumn({ type: datetimeColumnType, nullable: true })
@DeleteDateColumn({ type: datetimeColumnType as SimpleColumnType, nullable: true })
deletedAt: Date;
@Column({ nullable: true })
workflowId: string;
@Column({ type: datetimeColumnType, nullable: true })
@DateTimeColumn({ nullable: true })
waitTill: Date | null;
@OneToMany('ExecutionMetadata', 'execution')

View File

@@ -1,3 +1,4 @@
import { WithTimestampsAndStringId } from '@n8n/db';
import {
Column,
Entity,
@@ -8,7 +9,6 @@ import {
OneToMany,
} from '@n8n/typeorm';
import { WithTimestampsAndStringId } from './abstract-entity';
import { Project } from './project';
import { TagEntity } from './tag-entity';
import { type WorkflowEntity } from './workflow-entity';

View File

@@ -1,6 +1,6 @@
import { WithTimestamps } from '@n8n/db';
import { Column, Entity, JoinColumn, OneToMany, PrimaryColumn } from '@n8n/typeorm';
import { WithTimestamps } from './abstract-entity';
import type { InstalledNodes } from './installed-nodes';
@Entity()

View File

@@ -1,12 +1,11 @@
import { Column, Entity, PrimaryColumn } from '@n8n/typeorm';
import { datetimeColumnType } from './abstract-entity';
import { DateTimeColumn } from '@n8n/db';
import { Entity, PrimaryColumn } from '@n8n/typeorm';
@Entity()
export class InvalidAuthToken {
@PrimaryColumn()
token: string;
@Column(datetimeColumnType)
@DateTimeColumn()
expiresAt: Date;
}

View File

@@ -1,8 +1,8 @@
import { Column, Entity, PrimaryColumn } from '@n8n/typeorm';
import { JsonColumn, WithTimestamps } from '@n8n/db';
import { Entity, PrimaryColumn } from '@n8n/typeorm';
import type { IProcessedDataEntries, IProcessedDataLatest } from '@/interfaces';
import type { IProcessedDataEntries, IProcessedDataLatest } from '@/types-db';
import { jsonColumnType, WithTimestamps } from './abstract-entity';
import { objectRetriever } from '../utils/transformers';
@Entity()
@@ -13,8 +13,7 @@ export class ProcessedData extends WithTimestamps {
@PrimaryColumn()
workflowId: string;
@Column({
type: jsonColumnType,
@JsonColumn({
nullable: true,
transformer: objectRetriever,
})

View File

@@ -1,7 +1,7 @@
import { ProjectRole } from '@n8n/api-types';
import { WithTimestamps } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { WithTimestamps } from './abstract-entity';
import { Project } from './project';
import { User } from './user';

View File

@@ -1,7 +1,7 @@
import { ProjectIcon, ProjectType } from '@n8n/api-types';
import { WithTimestampsAndStringId } from '@n8n/db';
import { Column, Entity, OneToMany } from '@n8n/typeorm';
import { WithTimestampsAndStringId } from './abstract-entity';
import type { ProjectRelation } from './project-relation';
import type { SharedCredentials } from './shared-credentials';
import type { SharedWorkflow } from './shared-workflow';

View File

@@ -1,6 +1,6 @@
import { WithTimestamps } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { WithTimestamps } from './abstract-entity';
import { CredentialsEntity } from './credentials-entity';
import { Project } from './project';

View File

@@ -1,6 +1,6 @@
import { WithTimestamps } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { WithTimestamps } from './abstract-entity';
import { Project } from './project';
import { WorkflowEntity } from './workflow-entity';

View File

@@ -1,7 +1,7 @@
import { WithTimestampsAndStringId } from '@n8n/db';
import { Column, Entity, Index, ManyToMany, OneToMany } from '@n8n/typeorm';
import { IsString, Length } from 'class-validator';
import { WithTimestampsAndStringId } from './abstract-entity';
import type { FolderTagMapping } from './folder-tag-mapping';
import type { WorkflowEntity } from './workflow-entity';
import type { WorkflowTagMapping } from './workflow-tag-mapping';

View File

@@ -1,11 +1,7 @@
import { DateTimeColumn, JsonColumn, WithStringId } from '@n8n/db';
import { Column, Entity, ManyToOne, OneToOne } from '@n8n/typeorm';
import type { IDataObject } from 'n8n-workflow';
import {
datetimeColumnType,
jsonColumnType,
WithStringId,
} from '@/databases/entities/abstract-entity';
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';
@@ -62,18 +58,18 @@ export class TestCaseExecution extends WithStringId {
@Column()
status: TestCaseExecutionStatus;
@Column({ type: datetimeColumnType, nullable: true })
@DateTimeColumn({ nullable: true })
runAt: Date | null;
@Column({ type: datetimeColumnType, nullable: true })
@DateTimeColumn({ nullable: true })
completedAt: Date | null;
@Column('varchar', { nullable: true })
errorCode: TestCaseExecutionErrorCode | null;
@Column(jsonColumnType, { nullable: true })
@JsonColumn({ nullable: true })
errorDetails: IDataObject | null;
@Column(jsonColumnType, { nullable: true })
@JsonColumn({ nullable: true })
metrics: TestCaseRunMetrics;
}

View File

@@ -1,3 +1,4 @@
import { JsonColumn, WithTimestampsAndStringId } from '@n8n/db';
import { Column, Entity, Index, ManyToOne, OneToMany, RelationId } from '@n8n/typeorm';
import { Length } from 'class-validator';
@@ -5,8 +6,6 @@ import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.
import type { TestMetric } from '@/databases/entities/test-metric.ee';
import { WorkflowEntity } from '@/databases/entities/workflow-entity';
import { jsonColumnType, WithTimestampsAndStringId } from './abstract-entity';
// Entity representing a node in a workflow under test, for which data should be mocked during test execution
export type MockedNodeItem = {
name?: string;
@@ -33,7 +32,7 @@ export class TestDefinition extends WithTimestampsAndStringId {
@Column('text')
description: string;
@Column(jsonColumnType, { default: '[]' })
@JsonColumn({ default: '[]' })
mockedNodes: MockedNodeItem[];
/**

View File

@@ -1,7 +1,7 @@
import { WithTimestampsAndStringId } from '@n8n/db';
import { Column, Entity, Index, ManyToOne } from '@n8n/typeorm';
import { Length } from 'class-validator';
import { WithTimestampsAndStringId } from '@/databases/entities/abstract-entity';
import { TestDefinition } from '@/databases/entities/test-definition.ee';
/**

View File

@@ -1,11 +1,7 @@
import { WithTimestampsAndStringId, JsonColumn, DateTimeColumn } from '@n8n/db';
import { Column, Entity, Index, ManyToOne, OneToMany, RelationId } from '@n8n/typeorm';
import type { IDataObject } from 'n8n-workflow';
import {
datetimeColumnType,
jsonColumnType,
WithTimestampsAndStringId,
} from '@/databases/entities/abstract-entity';
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';
@@ -31,13 +27,13 @@ export class TestRun extends WithTimestampsAndStringId {
@Column('varchar')
status: TestRunStatus;
@Column({ type: datetimeColumnType, nullable: true })
@DateTimeColumn({ nullable: true })
runAt: Date | null;
@Column({ type: datetimeColumnType, nullable: true })
@DateTimeColumn({ nullable: true })
completedAt: Date | null;
@Column(jsonColumnType, { nullable: true })
@JsonColumn({ nullable: true })
metrics: AggregatedTestRunMetrics;
/**
@@ -69,7 +65,7 @@ export class TestRun extends WithTimestampsAndStringId {
/**
* Optional details about the error that happened during the test run
*/
@Column(jsonColumnType, { nullable: true })
@JsonColumn({ nullable: true })
errorDetails: IDataObject | null;
@OneToMany('TestCaseExecution', 'testRun')

View File

@@ -1,3 +1,4 @@
import { JsonColumn, WithTimestamps } from '@n8n/db';
import { hasScope, type ScopeOptions, type Scope, GlobalRole } from '@n8n/permissions';
import {
AfterLoad,
@@ -22,7 +23,6 @@ import type { IPersonalizationSurveyAnswers } from '@/types-db';
import { NoUrl } from '@/validators/no-url.validator';
import { NoXss } from '@/validators/no-xss.validator';
import { WithTimestamps, jsonColumnType } from './abstract-entity';
import type { ApiKey } from './api-key';
import type { AuthIdentity } from './auth-identity';
import type { ProjectRelation } from './project-relation';
@@ -68,17 +68,13 @@ export class User extends WithTimestamps implements IUser {
@IsString({ message: 'Password must be of type string.' })
password: string;
@Column({
type: jsonColumnType,
@JsonColumn({
nullable: true,
transformer: objectRetriever,
})
personalizationAnswers: IPersonalizationSurveyAnswers | null;
@Column({
type: jsonColumnType,
nullable: true,
})
@JsonColumn({ nullable: true })
settings: IUserSettings | null;
@Column({ type: String })

View File

@@ -1,7 +1,6 @@
import { WithStringId } from '@n8n/db';
import { Column, Entity } from '@n8n/typeorm';
import { WithStringId } from './abstract-entity';
@Entity()
export class Variables extends WithStringId {
@Column('text')

View File

@@ -1,3 +1,4 @@
import { JsonColumn, WithTimestampsAndStringId, dbType } from '@n8n/db';
import {
Column,
Entity,
@@ -14,7 +15,6 @@ import type { IBinaryKeyData, INode, IPairedItemData } from 'n8n-workflow';
import type { IWorkflowDb } from '@/types-db';
import { WithTimestampsAndStringId, dbType, jsonColumnType } from './abstract-entity';
import { type Folder } from './folder';
import type { SharedWorkflow } from './shared-workflow';
import type { TagEntity } from './tag-entity';
@@ -35,27 +35,22 @@ export class WorkflowEntity extends WithTimestampsAndStringId implements IWorkfl
@Column()
active: boolean;
@Column(jsonColumnType)
@JsonColumn()
nodes: INode[];
@Column(jsonColumnType)
@JsonColumn()
connections: IConnections;
@Column({
type: jsonColumnType,
nullable: true,
})
@JsonColumn({ nullable: true })
settings?: IWorkflowSettings;
@Column({
type: jsonColumnType,
@JsonColumn({
nullable: true,
transformer: objectRetriever,
})
staticData?: IDataObject;
@Column({
type: jsonColumnType,
@JsonColumn({
nullable: true,
transformer: objectRetriever,
})

View File

@@ -1,8 +1,8 @@
import { JsonColumn, WithTimestamps } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { IConnections } from 'n8n-workflow';
import type { INode } from 'n8n-workflow';
import { WithTimestamps, jsonColumnType } from './abstract-entity';
import { WorkflowEntity } from './workflow-entity';
@Entity()
@@ -13,10 +13,10 @@ export class WorkflowHistory extends WithTimestamps {
@Column()
workflowId: string;
@Column(jsonColumnType)
@JsonColumn()
nodes: INode[];
@Column(jsonColumnType)
@JsonColumn()
connections: IConnections;
@Column()

View File

@@ -1,6 +1,6 @@
import { DateTimeColumn } from '@n8n/db';
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { datetimeColumnType } from './abstract-entity';
import { WorkflowEntity } from './workflow-entity';
export const enum StatisticsNames {
@@ -19,7 +19,7 @@ export class WorkflowStatistics {
@Column()
rootCount: number;
@Column(datetimeColumnType)
@DateTimeColumn()
latestEvent: Date;
@PrimaryColumn({ length: 128 })

View File

@@ -1,10 +1,10 @@
import type { ProjectRole } from '@n8n/api-types';
import { generateNanoId } from '@n8n/db';
import { UserError } from 'n8n-workflow';
import { nanoid } from 'nanoid';
import type { User } from '@/databases/entities/user';
import type { MigrationContext, ReversibleMigration } from '@/databases/types';
import { generateNanoId } from '@/databases/utils/generators';
const projectAdminRole: ProjectRole = 'project:personalOwner';

View File

@@ -1,6 +1,7 @@
import { generateNanoId } from '@n8n/db';
import type { ApiKey } from '@/databases/entities/api-key';
import type { MigrationContext, ReversibleMigration } from '@/databases/types';
import { generateNanoId } from '@/databases/utils/generators';
export class AddApiKeysTable1724951148974 implements ReversibleMigration {
async up({

View File

@@ -1,6 +1,7 @@
import { generateNanoId } from '@n8n/db';
import type { ApiKey } from '@/databases/entities/api-key';
import type { MigrationContext, ReversibleMigration } from '@/databases/types';
import { generateNanoId } from '@/databases/utils/generators';
export class AddApiKeysTable1724951148974 implements ReversibleMigration {
transaction = false as const;

View File

@@ -2,7 +2,7 @@ import { Service } from '@n8n/di';
import { DataSource, In, Repository, Like } from '@n8n/typeorm';
import type { FindManyOptions } from '@n8n/typeorm';
import type { ListQuery } from '@/requests';
import type { ListQuery } from '@/types-db';
import { CredentialsEntity } from '../entities/credentials-entity';

View File

@@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { separate } from '@n8n/db';
import { Service } from '@n8n/di';
import type {
FindManyOptions,
@@ -35,10 +36,13 @@ import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.
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 type { ExecutionSummaries } from '@/executions/execution.types';
import type { CreateExecutionPayload, IExecutionFlattedDb } from '@/interfaces';
import type { IExecutionBase, IExecutionResponse } from '@/types-db';
import { separate } from '@/utils';
import type {
CreateExecutionPayload,
IExecutionFlattedDb,
IExecutionBase,
IExecutionResponse,
ExecutionSummaries,
} from '@/types-db';
import { ExecutionDataRepository } from './execution-data.repository';
import { ExecutionData } from '../entities/execution-data';

View File

@@ -3,7 +3,7 @@ import type { EntityManager, SelectQueryBuilder } from '@n8n/typeorm';
import { DataSource, Repository } from '@n8n/typeorm';
import { PROJECT_ROOT } from 'n8n-workflow';
import type { ListQuery } from '@/requests';
import type { ListQuery } from '@/types-db';
import type { FolderWithWorkflowAndSubFolderCount } from '../entities/folder';
import { Folder } from '../entities/folder';

View File

@@ -4,7 +4,7 @@ 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';
import type { ListQuery } from '@/types-db';
@Service()
export class TestDefinitionRepository extends Repository<TestDefinition> {

View File

@@ -8,7 +8,7 @@ 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';
import type { ListQuery } from '@/types-db';
export type TestRunFinalResult = 'success' | 'error' | 'warning';

View File

@@ -3,7 +3,7 @@ import type { GlobalRole } from '@n8n/permissions';
import type { DeepPartial, EntityManager, FindManyOptions } from '@n8n/typeorm';
import { DataSource, In, IsNull, Not, Repository } from '@n8n/typeorm';
import type { ListQuery } from '@/requests';
import type { ListQuery } from '@/types-db';
import { Project } from '../entities/project';
import { ProjectRelation } from '../entities/project-relation';

View File

@@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { isStringArray } from '@n8n/db';
import { Service } from '@n8n/di';
import { DataSource, Repository, In, Like } from '@n8n/typeorm';
import type {
@@ -12,8 +13,7 @@ import type {
} from '@n8n/typeorm';
import { PROJECT_ROOT } from 'n8n-workflow';
import type { ListQuery } from '@/requests';
import { isStringArray } from '@/utils';
import type { ListQuery } from '@/types-db';
import { FolderRepository } from './folder.repository';
import type { Folder, FolderWithWorkflowAndSubFolderCount } from '../entities/folder';

View File

@@ -1,13 +0,0 @@
import type { InstanceType } from 'n8n-core';
import { ALPHABET } from 'n8n-workflow';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet(ALPHABET, 16);
export function generateNanoId() {
return nanoid();
}
export function generateHostInstanceId(instanceType: InstanceType) {
return `${instanceType}-${nanoid()}`;
}

View File

@@ -15,7 +15,7 @@ import * as assert from 'node:assert/strict';
import type { ProcessedData } from '@/databases/entities/processed-data';
import { ProcessedDataRepository } from '@/databases/repositories/processed-data.repository';
import { DeduplicationError } from '@/errors/deduplication.error';
import type { IProcessedDataEntries, IProcessedDataLatest } from '@/interfaces';
import type { IProcessedDataEntries, IProcessedDataLatest } from '@/types-db';
export class DeduplicationHelper implements IDataDeduplicator {
private static sortEntries(

View File

@@ -1,8 +1,8 @@
import { generateNanoId } from '@n8n/db';
import { Service } from '@n8n/di';
import type { Variables } from '@/databases/entities/variables';
import { VariablesRepository } from '@/databases/repositories/variables.repository';
import { generateNanoId } from '@/databases/utils/generators';
import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error';
import { VariableValidationError } from '@/errors/variable-validation.error';
import { EventService } from '@/events/event.service';

View File

@@ -6,8 +6,8 @@ import { TestDefinitionRepository } from '@/databases/repositories/test-definiti
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { validateEntity } from '@/generic-helpers';
import type { ListQuery } from '@/requests';
import { Telemetry } from '@/telemetry';
import type { ListQuery } from '@/types-db';
type TestDefinitionLike = Omit<
Partial<TestDefinition>,

View File

@@ -1,5 +1,6 @@
import type { MockedNodeItem } from '@/databases/entities/test-definition.ee';
import type { AuthenticatedRequest, ListQuery } from '@/requests';
import type { AuthenticatedRequest } from '@/requests';
import type { ListQuery } from '@/types-db';
// ----------------------------------
// /test-definitions

View File

@@ -4,8 +4,9 @@ import { Logger } from 'n8n-core';
import { ensureError, type ExecutionStatus, type IRun, type IWorkflowBase } from 'n8n-workflow';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import type { IExecutionDb, UpdateExecutionPayload } from '@/interfaces';
import type { UpdateExecutionPayload } from '@/interfaces';
import { ExecutionMetadataService } from '@/services/execution-metadata.service';
import type { IExecutionDb } from '@/types-db';
import { isWorkflowIdValid } from '@/utils';
export function determineFinalExecutionStatus(runData: IRun): ExecutionStatus {

View File

@@ -3,8 +3,9 @@ import { mock } from 'jest-mock-extended';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { ExecutionService } from '@/executions/execution.service';
import type { ExecutionRequest, ExecutionSummaries } from '@/executions/execution.types';
import type { ExecutionRequest } from '@/executions/execution.types';
import { ExecutionsController } from '@/executions/executions.controller';
import type { ExecutionSummaries } from '@/types-db';
import type { WorkflowSharingService } from '@/workflows/workflow-sharing.service';
describe('ExecutionsController', () => {

View File

@@ -33,15 +33,15 @@ import { MissingExecutionStopError } from '@/errors/missing-execution-stop.error
import { QueuedExecutionRetryError } from '@/errors/queued-execution-retry.error';
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { CreateExecutionPayload, IExecutionFlattedResponse } from '@/interfaces';
import type { IExecutionFlattedResponse } from '@/interfaces';
import { License } from '@/license';
import { NodeTypes } from '@/node-types';
import type { IExecutionResponse } from '@/types-db';
import type { CreateExecutionPayload, ExecutionSummaries, IExecutionResponse } from '@/types-db';
import { WaitTracker } from '@/wait-tracker';
import { WorkflowRunner } from '@/workflow-runner';
import { WorkflowSharingService } from '@/workflows/workflow-sharing.service';
import type { ExecutionRequest, ExecutionSummaries, StopResult } from './execution.types';
import type { ExecutionRequest, StopResult } from './execution.types';
export const schemaGetExecutionsQueryFilter = {
$id: '/IGetExecutionsQueryFilter',

View File

@@ -1,14 +1,13 @@
import type { Scope } from '@n8n/permissions';
import type {
AnnotationVote,
ExecutionStatus,
ExecutionSummary,
IDataObject,
WorkflowExecuteMode,
} from 'n8n-workflow';
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
import type { AuthenticatedRequest } from '@/requests';
import type { ExecutionSummaries } from '@/types-db';
export declare namespace ExecutionRequest {
namespace QueryParams {
@@ -56,55 +55,6 @@ export declare namespace ExecutionRequest {
type Update = AuthenticatedRequest<RouteParams.ExecutionId, {}, ExecutionUpdatePayload, {}>;
}
export namespace ExecutionSummaries {
export type Query = RangeQuery | CountQuery;
export type RangeQuery = { kind: 'range' } & FilterFields &
AccessFields &
RangeFields &
OrderFields;
export type CountQuery = { kind: 'count' } & FilterFields & AccessFields;
type FilterFields = Partial<{
id: string;
finished: boolean;
mode: string;
retryOf: string;
retrySuccessId: string;
status: ExecutionStatus[];
workflowId: string;
waitTill: boolean;
metadata: Array<{ key: string; value: string }>;
startedAfter: string;
startedBefore: string;
annotationTags: string[]; // tag IDs
vote: AnnotationVote;
projectId: string;
}>;
type AccessFields = {
accessibleWorkflowIds?: string[];
};
type RangeFields = {
range: {
limit: number;
firstId?: string;
lastId?: string;
};
};
type OrderFields = {
order?: {
top?: ExecutionStatus;
startedAt?: 'DESC';
};
};
export type ExecutionSummaryWithScopes = ExecutionSummary & { scopes: Scope[] };
}
export type StopResult = {
mode: WorkflowExecuteMode;
startedAt: Date;

View File

@@ -5,12 +5,13 @@ import type { User } from '@/databases/entities/user';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { License } from '@/license';
import type { ExecutionSummaries } from '@/types-db';
import { isPositiveInteger } from '@/utils';
import { WorkflowSharingService } from '@/workflows/workflow-sharing.service';
import { ExecutionService } from './execution.service';
import { EnterpriseExecutionsService } from './execution.service.ee';
import { ExecutionRequest, type ExecutionSummaries } from './execution.types';
import { ExecutionRequest } from './execution.types';
import { parseRangeQuery } from './parse-range-query.middleware';
import { validateExecutionUpdatePayload } from './validation';

View File

@@ -7,7 +7,6 @@ import type {
IDeferredPromise,
IExecuteResponsePromiseData,
IRun,
IRunExecutionData,
ITelemetryTrackProperties,
IWorkflowBase,
CredentialLoadingDetails,
@@ -15,14 +14,12 @@ import type {
ExecutionStatus,
ExecutionSummary,
IWorkflowExecutionDataProcess,
DeduplicationMode,
DeduplicationItemTypes,
} from 'n8n-workflow';
import type PCancelable from 'p-cancelable';
import type { ActiveWorkflowManager } from '@/active-workflow-manager';
import type { ExternalHooks } from '@/external-hooks';
import type { ICredentialsBase, IExecutionBase, ITagBase } from '@/types-db';
import type { ICredentialsBase, IExecutionBase, IExecutionDb, ITagBase } from '@/types-db';
export interface ICredentialsTypeData {
[key: string]: CredentialLoadingDetails;
@@ -32,20 +29,6 @@ export interface ICredentialsOverwrite {
[key: string]: ICredentialDataDecryptedObject;
}
// ----------------------------------
// ProcessedData
// ----------------------------------
export interface IProcessedDataLatest {
mode: DeduplicationMode;
data: DeduplicationItemTypes;
}
export interface IProcessedDataEntries {
mode: DeduplicationMode;
data: DeduplicationItemTypes[];
}
// ----------------------------------
// tags
// ----------------------------------
@@ -82,15 +65,6 @@ export type ICredentialsDecryptedResponse = ICredentialsDecryptedDb;
export type SaveExecutionDataType = 'all' | 'none';
// Data in regular format with references
export interface IExecutionDb extends IExecutionBase {
data: IRunExecutionData;
workflowData: IWorkflowBase;
}
/** Payload for creating an execution. */
export type CreateExecutionPayload = Omit<IExecutionDb, 'id' | 'createdAt' | 'startedAt'>;
/** Payload for updating an execution. */
export type UpdateExecutionPayload = Omit<IExecutionDb, 'id' | 'createdAt'>;
@@ -101,13 +75,6 @@ export interface IExecutionFlatted extends IExecutionBase {
workflowData: IWorkflowBase;
}
export interface IExecutionFlattedDb extends IExecutionBase {
id: string;
data: string;
workflowData: Omit<IWorkflowBase, 'pinData'>;
customData: Record<string, string>;
}
export interface IExecutionFlattedResponse extends IExecutionFlatted {
id: string;
retryOf?: string;

View File

@@ -1,7 +1,8 @@
import { generateNanoId } from '@n8n/db';
import { AuthIdentity } from '@/databases/entities/auth-identity';
import { User } from '@/databases/entities/user';
import { UserRepository } from '@/databases/repositories/user.repository';
import { generateNanoId } from '@/databases/utils/generators';
import * as helpers from '@/ldap.ee/helpers.ee';
import { mockInstance } from '@test/mocking';

View File

@@ -3,8 +3,8 @@ import type { Response, NextFunction } from 'express';
import { filterListQueryMiddleware } from '@/middlewares/list-query/filter';
import { paginationListQueryMiddleware } from '@/middlewares/list-query/pagination';
import { selectListQueryMiddleware } from '@/middlewares/list-query/select';
import type { ListQuery } from '@/requests';
import * as ResponseHelper from '@/response-helper';
import type { ListQuery } from '@/types-db';
import { sortByQueryMiddleware } from '../sort-by';

View File

@@ -1,7 +1,6 @@
import { isStringArray } from '@n8n/db';
import { jsonParse, UnexpectedError } from 'n8n-workflow';
import { isStringArray } from '@/utils';
export class BaseSelect {
static selectableFields: Set<string>;

View File

@@ -1,7 +1,7 @@
import type { NextFunction, Response } from 'express';
import type { ListQuery } from '@/requests';
import * as ResponseHelper from '@/response-helper';
import type { ListQuery } from '@/types-db';
import { toError } from '@/utils';
import { CredentialsFilter } from './dtos/credentials.filter.dto';

View File

@@ -1,6 +1,6 @@
import { type NextFunction, type Response } from 'express';
import type { ListQuery } from '@/requests';
import type { ListQuery } from '@/types-db';
import { filterListQueryMiddleware } from './filter';
import { paginationListQueryMiddleware } from './pagination';

View File

@@ -1,8 +1,8 @@
import type { RequestHandler } from 'express';
import { UnexpectedError } from 'n8n-workflow';
import type { ListQuery } from '@/requests';
import * as ResponseHelper from '@/response-helper';
import type { ListQuery } from '@/types-db';
import { toError } from '@/utils';
import { Pagination } from './dtos/pagination.dto';

View File

@@ -1,7 +1,7 @@
import type { RequestHandler } from 'express';
import type { ListQuery } from '@/requests';
import * as ResponseHelper from '@/response-helper';
import type { ListQuery } from '@/types-db';
import { toError } from '@/utils';
import { CredentialsSelect } from './dtos/credentials.select.dto';

View File

@@ -3,8 +3,8 @@ import { validateSync } from 'class-validator';
import type { RequestHandler } from 'express';
import { UnexpectedError } from 'n8n-workflow';
import type { ListQuery } from '@/requests';
import * as ResponseHelper from '@/response-helper';
import type { ListQuery } from '@/types-db';
import { toError } from '@/utils';
import { WorkflowSorting } from './dtos/workflow.sort-by.dto';

View File

@@ -1,3 +1,4 @@
import { DateTimeColumn } from '@n8n/db';
import {
BaseEntity,
Column,
@@ -8,8 +9,6 @@ import {
} from '@n8n/typeorm';
import { UnexpectedError } from 'n8n-workflow';
import { datetimeColumnType } from '@/databases/entities/abstract-entity';
import { InsightsMetadata } from './insights-metadata';
import type { PeriodUnit } from './insights-shared';
import {
@@ -70,6 +69,6 @@ export class InsightsByPeriod extends BaseEntity {
this.periodUnit_ = PeriodUnitToNumber[value];
}
@Column({ type: datetimeColumnType })
@DateTimeColumn()
periodStart: Date;
}

View File

@@ -1,10 +1,9 @@
import { GlobalConfig } from '@n8n/config';
import { DateTimeColumn } from '@n8n/db';
import { Container } from '@n8n/di';
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from '@n8n/typeorm';
import { UnexpectedError } from 'n8n-workflow';
import { datetimeColumnType } from '@/databases/entities/abstract-entity';
import { isValidTypeNumber, NumberToType, TypeToNumber } from './insights-shared';
export const { type: dbType } = Container.get(GlobalConfig).database;
@@ -42,9 +41,6 @@ export class InsightsRaw extends BaseEntity {
@Column()
value: number;
@Column({
name: 'timestamp',
type: datetimeColumnType,
})
@DateTimeColumn({ name: 'timestamp' })
timestamp: Date;
}

View File

@@ -5,17 +5,13 @@ import type {
ICredentialDataDecryptedObject,
INodeCredentialTestRequest,
IPersonalizationSurveyAnswersV4,
IUser,
} from 'n8n-workflow';
import type { CredentialsEntity } from '@/databases/entities/credentials-entity';
import type { Project } from '@/databases/entities/project';
import type { User } from '@/databases/entities/user';
import type { Variables } from '@/databases/entities/variables';
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
import type { WorkflowHistory } from '@/databases/entities/workflow-history';
import type { ScopesField } from '@/services/role.service';
import type { SlimProject } from '@/types-db';
import type { ListQuery } from '@/types-db';
export type APIRequest<
RouteParams = {},
@@ -50,80 +46,6 @@ export type AuthenticatedRequest<
// list query
// ----------------------------------
export namespace ListQuery {
export type Request = AuthenticatedRequest<{}, {}, {}, Params> & {
listQueryOptions?: Options;
};
export type Params = {
filter?: string;
skip?: string;
take?: string;
select?: string;
sortBy?: string;
};
export type Options = {
filter?: Record<string, unknown>;
select?: Record<string, true>;
skip?: number;
take?: number;
sortBy?: string;
};
/**
* Slim workflow returned from a list query operation.
*/
export namespace Workflow {
type OptionalBaseFields = 'name' | 'active' | 'versionId' | 'createdAt' | 'updatedAt' | 'tags';
type BaseFields = Pick<WorkflowEntity, 'id'> &
Partial<Pick<WorkflowEntity, OptionalBaseFields>>;
type SharedField = Partial<Pick<WorkflowEntity, 'shared'>>;
type SortingField = 'createdAt' | 'updatedAt' | 'name';
export type SortOrder = `${SortingField}:asc` | `${SortingField}:desc`;
type OwnedByField = { ownedBy: SlimUser | null; homeProject: SlimProject | null };
export type Plain = BaseFields;
export type WithSharing = BaseFields & SharedField;
export type WithOwnership = BaseFields & OwnedByField;
type SharedWithField = { sharedWith: SlimUser[]; sharedWithProjects: SlimProject[] };
export type WithOwnedByAndSharedWith = BaseFields &
OwnedByField &
SharedWithField &
SharedField;
export type WithScopes = BaseFields & ScopesField & SharedField;
}
export namespace Credentials {
type OwnedByField = { homeProject: SlimProject | null };
type SharedField = Partial<Pick<CredentialsEntity, 'shared'>>;
type SharedWithField = { sharedWithProjects: SlimProject[] };
export type WithSharing = CredentialsEntity & SharedField;
export type WithOwnedByAndSharedWith = CredentialsEntity &
OwnedByField &
SharedWithField &
SharedField;
export type WithScopes = CredentialsEntity & ScopesField & SharedField;
}
}
type SlimUser = Pick<IUser, 'id' | 'email' | 'firstName' | 'lastName'>;
export function hasSharing(
workflows: ListQuery.Workflow.Plain[] | ListQuery.Workflow.WithSharing[],
): workflows is ListQuery.Workflow.WithSharing[] {

View File

@@ -1,4 +1,5 @@
import { GlobalConfig } from '@n8n/config';
import { separate } from '@n8n/db';
import { Service } from '@n8n/di';
import axios from 'axios';
import { InstanceSettings, Logger } from 'n8n-core';
@@ -15,7 +16,6 @@ import {
} from '@/security-audit/constants';
import type { RiskReporter, Risk, n8n } from '@/security-audit/types';
import { toFlaggedNode } from '@/security-audit/utils';
import { separate } from '@/utils';
@Service()
export class InstanceRiskReporter implements RiskReporter {

View File

@@ -9,7 +9,7 @@ import { FolderTagMappingRepository } from '@/databases/repositories/folder-tag-
import { FolderRepository } from '@/databases/repositories/folder.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { FolderNotFoundError } from '@/errors/folder-not-found.error';
import type { ListQuery } from '@/requests';
import type { ListQuery } from '@/types-db';
export interface SimpleFolderNode {
id: string;

View File

@@ -6,8 +6,8 @@ import { ProjectRelationRepository } from '@/databases/repositories/project-rela
import { ProjectRepository } from '@/databases/repositories/project.repository';
import { SharedWorkflowRepository } from '@/databases/repositories/shared-workflow.repository';
import { UserRepository } from '@/databases/repositories/user.repository';
import type { ListQuery } from '@/requests';
import { CacheService } from '@/services/cache/cache.service';
import type { ListQuery } from '@/types-db';
@Service()
export class OwnershipService {

View File

@@ -30,7 +30,7 @@ import {
WORKFLOW_SHARING_EDITOR_SCOPES,
WORKFLOW_SHARING_OWNER_SCOPES,
} from '@/permissions.ee/resource-roles';
import type { ListQuery } from '@/requests';
import type { ListQuery, ScopesField } from '@/types-db';
export type RoleNamespace = 'global' | 'project' | 'credential' | 'workflow';
@@ -100,7 +100,7 @@ const ROLE_NAMES: Record<
'workflow:editor': 'Workflow Editor',
};
export type ScopesField = { scopes: Scope[] };
// export type ScopesField = { scopes: Scope[] };
@Service()
export class RoleService {
@@ -189,7 +189,10 @@ export class RoleService {
| ListQuery.Workflow.WithScopes
| ListQuery.Credentials.WithScopes {
const shared = rawEntity.shared;
const entity = rawEntity as ListQuery.Workflow.WithScopes | ListQuery.Credentials.WithScopes;
const entity = rawEntity as
| (CredentialsEntity & ScopesField)
| ListQuery.Workflow.WithScopes
| ListQuery.Credentials.WithScopes;
Object.assign(entity, {
scopes: [],

View File

@@ -1,8 +1,9 @@
import { generateNanoId } from '@n8n/db';
import type { AuthIdentity } from '@/databases/entities/auth-identity';
import { User } from '@/databases/entities/user';
import { AuthIdentityRepository } from '@/databases/repositories/auth-identity.repository';
import { UserRepository } from '@/databases/repositories/user.repository';
import { generateNanoId } from '@/databases/utils/generators';
import * as helpers from '@/sso.ee/saml/saml-helpers';
import type { SamlUserAttributes } from '@/sso.ee/saml/types';
import { mockInstance } from '@test/mocking';

View File

@@ -1,4 +1,5 @@
import type { GlobalRole, Scope } from '@n8n/permissions';
import type express from 'express';
import type {
ICredentialsEncrypted,
IRunExecutionData,
@@ -7,10 +8,16 @@ import type {
ExecutionStatus,
FeatureFlags,
IUserSettings,
DeduplicationMode,
DeduplicationItemTypes,
AnnotationVote,
ExecutionSummary,
IUser,
} from 'n8n-workflow';
import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee';
import type { AuthProviderType } from '@/databases/entities/auth-identity';
import type { CredentialsEntity } from '@/databases/entities/credentials-entity';
import type { Folder } from '@/databases/entities/folder';
import type { Project } from '@/databases/entities/project';
import type { SharedCredentials } from '@/databases/entities/shared-credentials';
@@ -140,3 +147,176 @@ export interface WorkflowWithSharingsMetaDataAndCredentials extends Omit<Workflo
sharedWithProjects: SlimProject[];
usedCredentials?: CredentialUsedByWorkflow[];
}
export interface IProcessedDataLatest {
mode: DeduplicationMode;
data: DeduplicationItemTypes;
}
export interface IProcessedDataEntries {
mode: DeduplicationMode;
data: DeduplicationItemTypes[];
}
/** Payload for creating an execution. */
export type CreateExecutionPayload = Omit<IExecutionDb, 'id' | 'createdAt' | 'startedAt'>;
// Data in regular format with references
export interface IExecutionDb extends IExecutionBase {
data: IRunExecutionData;
workflowData: IWorkflowBase;
}
export interface IExecutionFlattedDb extends IExecutionBase {
id: string;
data: string;
workflowData: Omit<IWorkflowBase, 'pinData'>;
customData: Record<string, string>;
}
export namespace ExecutionSummaries {
export type Query = RangeQuery | CountQuery;
export type RangeQuery = { kind: 'range' } & FilterFields &
AccessFields &
RangeFields &
OrderFields;
export type CountQuery = { kind: 'count' } & FilterFields & AccessFields;
type FilterFields = Partial<{
id: string;
finished: boolean;
mode: string;
retryOf: string;
retrySuccessId: string;
status: ExecutionStatus[];
workflowId: string;
waitTill: boolean;
metadata: Array<{ key: string; value: string }>;
startedAfter: string;
startedBefore: string;
annotationTags: string[]; // tag IDs
vote: AnnotationVote;
projectId: string;
}>;
type AccessFields = {
accessibleWorkflowIds?: string[];
};
type RangeFields = {
range: {
limit: number;
firstId?: string;
lastId?: string;
};
};
type OrderFields = {
order?: {
top?: ExecutionStatus;
startedAt?: 'DESC';
};
};
export type ExecutionSummaryWithScopes = ExecutionSummary & { scopes: Scope[] };
}
export type APIRequest<
RouteParams = {},
ResponseBody = {},
RequestBody = {},
RequestQuery = {},
> = express.Request<RouteParams, ResponseBody, RequestBody, RequestQuery> & {
browserId?: string;
};
export type AuthenticatedRequest<
RouteParams = {},
ResponseBody = {},
RequestBody = {},
RequestQuery = {},
> = Omit<APIRequest<RouteParams, ResponseBody, RequestBody, RequestQuery>, 'user' | 'cookies'> & {
user: User;
cookies: Record<string, string | undefined>;
headers: express.Request['headers'] & {
'push-ref': string;
};
};
export namespace ListQuery {
export type Request = AuthenticatedRequest<{}, {}, {}, Params> & {
listQueryOptions?: Options;
};
export type Params = {
filter?: string;
skip?: string;
take?: string;
select?: string;
sortBy?: string;
};
export type Options = {
filter?: Record<string, unknown>;
select?: Record<string, true>;
skip?: number;
take?: number;
sortBy?: string;
};
/**
* Slim workflow returned from a list query operation.
*/
export namespace Workflow {
type OptionalBaseFields = 'name' | 'active' | 'versionId' | 'createdAt' | 'updatedAt' | 'tags';
type BaseFields = Pick<WorkflowEntity, 'id'> &
Partial<Pick<WorkflowEntity, OptionalBaseFields>>;
type SharedField = Partial<Pick<WorkflowEntity, 'shared'>>;
type SortingField = 'createdAt' | 'updatedAt' | 'name';
export type SortOrder = `${SortingField}:asc` | `${SortingField}:desc`;
type OwnedByField = { ownedBy: SlimUser | null; homeProject: SlimProject | null };
export type Plain = BaseFields;
export type WithSharing = BaseFields & SharedField;
export type WithOwnership = BaseFields & OwnedByField;
type SharedWithField = { sharedWith: SlimUser[]; sharedWithProjects: SlimProject[] };
export type WithOwnedByAndSharedWith = BaseFields &
OwnedByField &
SharedWithField &
SharedField;
export type WithScopes = BaseFields & ScopesField & SharedField;
}
export namespace Credentials {
type OwnedByField = { homeProject: SlimProject | null };
type SharedField = Partial<Pick<CredentialsEntity, 'shared'>>;
type SharedWithField = { sharedWithProjects: SlimProject[] };
export type WithSharing = CredentialsEntity & SharedField;
export type WithOwnedByAndSharedWith = CredentialsEntity &
OwnedByField &
SharedWithField &
SharedField;
export type WithScopes = CredentialsEntity & ScopesField & SharedField;
}
}
type SlimUser = Pick<IUser, 'id' | 'email' | 'firstName' | 'lastName'>;
export type ScopesField = { scopes: Scope[] };

View File

@@ -39,23 +39,10 @@ export const findSubworkflowStart = findWorkflowStart('integrated');
export const findCliWorkflowStart = findWorkflowStart('cli');
export const separate = <T>(array: T[], test: (element: T) => boolean) => {
const pass: T[] = [];
const fail: T[] = [];
array.forEach((i) => (test(i) ? pass : fail).push(i));
return [pass, fail];
};
export const toError = (maybeError: unknown) =>
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
maybeError instanceof Error ? maybeError : new Error(`${maybeError}`);
export function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every((item) => typeof item === 'string');
}
export const isIntegerString = (value: string) => /^\d+$/.test(value);
export function removeTrailingSlash(path: string) {

View File

@@ -1,3 +1,4 @@
import { generateNanoId } from '@n8n/db';
import type * as express from 'express';
import { mock } from 'jest-mock-extended';
import type { ITaskData, IWorkflowBase } from 'n8n-workflow';
@@ -8,7 +9,6 @@ import {
} from 'n8n-workflow';
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 {

View File

@@ -23,8 +23,9 @@ import { ExecutionRepository } from '@/databases/repositories/execution.reposito
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { ExecutionDataService } from '@/executions/execution-data.service';
import { SubworkflowPolicyChecker } from '@/executions/pre-execution-checks';
import type { CreateExecutionPayload, IWorkflowErrorData } from '@/interfaces';
import type { IWorkflowErrorData } from '@/interfaces';
import { NodeTypes } from '@/node-types';
import type { CreateExecutionPayload } from '@/types-db';
import { TestWebhooks } from '@/webhooks/test-webhooks';
import * as WorkflowExecuteAdditionalData from '@/workflow-execute-additional-data';
import { WorkflowRunner } from '@/workflow-runner';

View File

@@ -8,7 +8,8 @@ import type {
IWorkflowBase,
} from 'n8n-workflow';
import type { AuthenticatedRequest, ListQuery } from '@/requests';
import type { AuthenticatedRequest } from '@/requests';
import type { ListQuery } from '@/types-db';
export declare namespace WorkflowRequest {
type CreateUpdatePayload = Partial<{

View File

@@ -27,13 +27,14 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error';
import { EventService } from '@/events/event.service';
import { ExternalHooks } from '@/external-hooks';
import { validateEntity } from '@/generic-helpers';
import { hasSharing, type ListQuery } from '@/requests';
import { hasSharing } from '@/requests';
import { FolderService } from '@/services/folder.service';
import { OrchestrationService } from '@/services/orchestration.service';
import { OwnershipService } from '@/services/ownership.service';
import { ProjectService } from '@/services/project.service.ee';
import { RoleService } from '@/services/role.service';
import { TagService } from '@/services/tag.service';
import type { ListQuery } from '@/types-db';
import * as WorkflowHelpers from '@/workflow-helpers';
import { WorkflowFinderService } from './workflow-finder.service';

View File

@@ -8,8 +8,8 @@ import type { Project } from '@/databases/entities/project';
import type { User } from '@/databases/entities/user';
import { ProjectRepository } from '@/databases/repositories/project.repository';
import { SharedCredentialsRepository } from '@/databases/repositories/shared-credentials.repository';
import type { ListQuery } from '@/requests';
import { ProjectService } from '@/services/project.service.ee';
import type { ListQuery } from '@/types-db';
import { UserManagementMailer } from '@/user-management/email';
import { createWorkflow, shareWorkflowWithUsers } from '@test-integration/db/workflows';

View File

@@ -14,8 +14,8 @@ import type { User } from '@/databases/entities/user';
import { CredentialsRepository } from '@/databases/repositories/credentials.repository';
import { ProjectRepository } from '@/databases/repositories/project.repository';
import { SharedCredentialsRepository } from '@/databases/repositories/shared-credentials.repository';
import type { ListQuery } from '@/requests';
import { CredentialsTester } from '@/services/credentials-tester.service';
import type { ListQuery } from '@/types-db';
import {
decryptCredentialData,

View File

@@ -1,10 +1,10 @@
import { generateNanoId } from '@n8n/db';
import { Container } from '@n8n/di';
import { InstanceSettings } from 'n8n-core';
import { ActiveWorkflowManager } from '@/active-workflow-manager';
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { generateNanoId } from '@/databases/utils/generators';
import { MultiMainSetup } from '@/scaling/multi-main-setup.ee';
import { createOwner } from './shared/db/users';

View File

@@ -5,7 +5,7 @@ import { ExecutionMetadataRepository } from '@/databases/repositories/execution-
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { ExecutionService } from '@/executions/execution.service';
import type { ExecutionSummaries } from '@/executions/execution.types';
import type { ExecutionSummaries } from '@/types-db';
import { createTeamProject } from '@test-integration/db/projects';
import { annotateExecution, createAnnotationTags, createExecution } from './shared/db/executions';

View File

@@ -1,4 +1,5 @@
import type { SecurityConfig } from '@n8n/config';
import { generateNanoId } from '@n8n/db';
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { v4 as uuid } from 'uuid';
@@ -7,7 +8,6 @@ import { CredentialsRepository } from '@/databases/repositories/credentials.repo
import { ExecutionDataRepository } from '@/databases/repositories/execution-data.repository';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { generateNanoId } from '@/databases/utils/generators';
import { CREDENTIALS_REPORT } from '@/security-audit/constants';
import { SecurityAuditService } from '@/security-audit/security-audit.service';

View File

@@ -1,9 +1,9 @@
import { generateNanoId } from '@n8n/db';
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { v4 as uuid } from 'uuid';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { generateNanoId } from '@/databases/utils/generators';
import {
DATABASE_REPORT,
SQL_NODE_TYPES,

View File

@@ -1,11 +1,11 @@
import { GlobalConfig } from '@n8n/config';
import { generateNanoId } from '@n8n/db';
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
import { NodeConnectionTypes } from 'n8n-workflow';
import { v4 as uuid } from 'uuid';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { generateNanoId } from '@/databases/utils/generators';
import { INSTANCE_REPORT, WEBHOOK_VALIDATOR_NODE_TYPES } from '@/security-audit/constants';
import { SecurityAuditService } from '@/security-audit/security-audit.service';
import { toReportTitle } from '@/security-audit/utils';

View File

@@ -1,3 +1,4 @@
import { generateNanoId } from '@n8n/db';
import { Container } from '@n8n/di';
import type { IWorkflowBase } from 'n8n-workflow';
@@ -5,7 +6,6 @@ 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';

View File

@@ -1,8 +1,8 @@
import { generateNanoId } from '@n8n/db';
import { Container } from '@n8n/di';
import { randomString } from 'n8n-workflow';
import { VariablesRepository } from '@/databases/repositories/variables.repository';
import { generateNanoId } from '@/databases/utils/generators';
export async function createVariable(key = randomString(5), value = randomString(5)) {
return await Container.get(VariablesRepository).save({ id: generateNanoId(), key, value });

View File

@@ -1,8 +1,8 @@
import { generateNanoId } from '@n8n/db';
import { Container } from '@n8n/di';
import type { Variables } from '@/databases/entities/variables';
import { VariablesRepository } from '@/databases/repositories/variables.repository';
import { generateNanoId } from '@/databases/utils/generators';
import { VariablesService } from '@/environments.ee/variables/variables.service.ee';
import { CacheService } from '@/services/cache/cache.service';

View File

@@ -12,8 +12,8 @@ import { WorkflowHistoryRepository } from '@/databases/repositories/workflow-his
import type { WorkflowFolderUnionFull } from '@/databases/repositories/workflow.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { License } from '@/license';
import type { ListQuery } from '@/requests';
import { ProjectService } from '@/services/project.service.ee';
import type { ListQuery } from '@/types-db';
import { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee';
import { createFolder } from '@test-integration/db/folders';