feat(core): Database migration to add workflow dependency index table (#20723)

This commit is contained in:
mfsiega 2025-10-15 14:47:28 +02:00 committed by GitHub
parent 87d143bdd7
commit ed3b19a3de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 139 additions and 6 deletions

View File

@ -61,11 +61,8 @@ function mixinStringId<T extends Class<{}, any[]>>(base: T) {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mixinTimestamps<T extends Class<{}, any[]>>(base: T) {
function mixinUpdatedAt<T extends Class<{}, any[]>>(base: T) {
class Derived extends base {
@CreateDateColumn(tsColumnOptions)
createdAt: Date;
@UpdateDateColumn(tsColumnOptions)
updatedAt: Date;
@ -77,8 +74,19 @@ function mixinTimestamps<T extends Class<{}, any[]>>(base: T) {
return Derived;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mixinCreatedAt<T extends Class<{}, any[]>>(base: T) {
class Derived extends base {
@CreateDateColumn(tsColumnOptions)
createdAt: Date;
}
return Derived;
}
class BaseEntity {}
export const WithStringId = mixinStringId(BaseEntity);
export const WithTimestamps = mixinTimestamps(BaseEntity);
export const WithCreatedAt = mixinCreatedAt(BaseEntity);
export const WithUpdatedAt = mixinUpdatedAt(BaseEntity);
export const WithTimestamps = mixinCreatedAt(mixinUpdatedAt(BaseEntity));
export const WithTimestampsAndStringId = mixinStringId(WithTimestamps);

View File

@ -26,6 +26,7 @@ import { TestRun } from './test-run.ee';
import { User } from './user';
import { Variables } from './variables';
import { WebhookEntity } from './webhook-entity';
import { WorkflowDependency } from './workflow-dependency-entity';
import { WorkflowEntity } from './workflow-entity';
import { WorkflowHistory } from './workflow-history';
import { WorkflowStatistics } from './workflow-statistics';
@ -50,6 +51,7 @@ export {
SharedWorkflow,
TagEntity,
User,
WorkflowDependency,
WorkflowEntity,
WorkflowStatistics,
WorkflowTagMapping,
@ -84,6 +86,7 @@ export const entities = {
SharedWorkflow,
TagEntity,
User,
WorkflowDependency,
WorkflowEntity,
WorkflowStatistics,
WorkflowTagMapping,

View File

@ -0,0 +1,69 @@
import {
Column,
Entity,
Index,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
Relation,
} from '@n8n/typeorm';
import { WithCreatedAt } from './abstract-entity';
import type { WorkflowEntity } from './workflow-entity';
export type DependencyType = 'credential' | 'nodeType' | 'webhookPath' | 'workflowCall';
@Entity({ name: 'workflow_dependency' })
export class WorkflowDependency extends WithCreatedAt {
@PrimaryGeneratedColumn()
id: number;
/**
* The ID of the workflow the dependency belongs to.
*/
@Column({ length: 36 })
@Index()
workflowId: string;
/**
* The version ID of the workflow the dependency belongs to.
* Used to ensure consistency between the workflow and dependency tables.
*/
@Column({ type: 'int' })
workflowVersionId: number;
/**
* The type of the dependency.
* credential | nodeType | webhookPath | workflowCall
*/
@Column({ length: 32 })
@Index()
dependencyType: DependencyType;
/**
* The ID of the dependency, interpreted based on the dependency type.
* E.g., for 'credential' it would be the credential ID, for 'nodeType' the node type name, etc.
*/
@Column({ length: 255 })
@Index()
dependencyKey: string;
/**
* Additional information about the dependency, interpreted based on the type.
* E.g., for 'nodeType' it could be the node ID, for 'webhookPath' the webhook ID.
*/
@Column({ type: 'varchar', length: 255, nullable: true })
dependencyInfo: string | null;
/**
* The version of the index structure. Used for migrations and updates.
*/
@Column({ type: 'smallint', default: 1 })
indexVersionId: number;
@ManyToOne('WorkflowEntity', {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'workflowId' })
workflow: Relation<WorkflowEntity>;
}

View File

@ -0,0 +1,36 @@
import type { MigrationContext, ReversibleMigration } from '../migration-types';
export class CreateWorkflowDependencyTable1760314000000 implements ReversibleMigration {
async up({ schemaBuilder: { createTable, column } }: MigrationContext) {
await createTable('workflow_dependency')
.withColumns(
column('id').int.primary.autoGenerate2,
column('workflowId').varchar(36).notNull,
column('workflowVersionId').int.notNull.comment('Version of the workflow'),
column('dependencyType')
.varchar(32)
.notNull.comment(
'Type of dependency: "credential", "nodeType", "webhookPath", or "workflowCall"',
),
column('dependencyKey').varchar(255).notNull.comment('ID or name of the dependency'),
column('dependencyInfo')
.varchar(255)
.comment('Additional info about the dependency, interpreted based on type'),
column('indexVersionId')
.smallint.notNull.default(1)
.comment('Version of the index structure'),
)
.withForeignKey('workflowId', {
tableName: 'workflow_entity',
columnName: 'id',
onDelete: 'CASCADE',
})
.withIndexOn(['workflowId'])
.withIndexOn(['dependencyType'])
.withIndexOn(['dependencyKey']).withCreatedAt;
}
async down({ schemaBuilder: { dropTable } }: MigrationContext) {
await dropTable('workflow_dependency');
}
}

View File

@ -11,7 +11,8 @@ export class Column {
| 'timestamp'
| 'uuid'
| 'double'
| 'bigint';
| 'bigint'
| 'smallint';
private isGenerated = false;
@ -46,6 +47,11 @@ export class Column {
return this;
}
get smallint() {
this.type = 'smallint';
return this;
}
get double() {
this.type = 'double';
return this;

View File

@ -41,6 +41,11 @@ export class CreateTable extends TableOperation {
return this;
}
get withCreatedAt() {
this.columns.push(new Column('createdAt').timestampTimezone().notNull.default('NOW()'));
return this;
}
withIndexOn(columnName: string | string[], isUnique = false) {
const columnNames = Array.isArray(columnName) ? columnName : [columnName];
this.indices.add({ columnNames, isUnique });

View File

@ -1,4 +1,5 @@
import { AddAudienceColumnToApiKeys1758731786132 } from '../common/1758731786132-AddAudienceColumnToApiKey';
import { CreateWorkflowDependencyTable1760314000000 } from '../common/1760314000000-CreateWorkflowDependencyTable';
import { AddMfaColumns1690000000030 } from './../common/1690000000040-AddMfaColumns';
import { LinkRoleToProjectRelationTable1753953244168 } from './../common/1753953244168-LinkRoleToProjectRelationTable';
import { InitialMigration1588157391238 } from './1588157391238-InitialMigration';
@ -211,4 +212,5 @@ export const mysqlMigrations: Migration[] = [
ChangeValueTypesForInsights1759399811000,
CreateChatHubTables1760019379982,
UniqueRoleNames1760020838000,
CreateWorkflowDependencyTable1760314000000,
];

View File

@ -102,6 +102,7 @@ import { AddAudienceColumnToApiKeys1758731786132 } from '../common/1758731786132
import { ChangeValueTypesForInsights1759399811000 } from '../common/1759399811000-ChangeValueTypesForInsights';
import { CreateChatHubTables1760019379982 } from '../common/1760019379982-CreateChatHubTables';
import { UniqueRoleNames1760020838000 } from '../common/1760020838000-UniqueRoleNames';
import { CreateWorkflowDependencyTable1760314000000 } from '../common/1760314000000-CreateWorkflowDependencyTable';
import type { Migration } from '../migration-types';
export const postgresMigrations: Migration[] = [
@ -209,4 +210,5 @@ export const postgresMigrations: Migration[] = [
ChangeValueTypesForInsights1759399811000,
CreateChatHubTables1760019379982,
UniqueRoleNames1760020838000,
CreateWorkflowDependencyTable1760314000000,
];

View File

@ -100,6 +100,7 @@ import type { Migration } from '../migration-types';
import { LinkRoleToProjectRelationTable1753953244168 } from './../common/1753953244168-LinkRoleToProjectRelationTable';
import { AddProjectIdToVariableTable1758794506893 } from './1758794506893-AddProjectIdToVariableTable';
import { CreateChatHubTables1760019379982 } from '../common/1760019379982-CreateChatHubTables';
import { CreateWorkflowDependencyTable1760314000000 } from '../common/1760314000000-CreateWorkflowDependencyTable';
const sqliteMigrations: Migration[] = [
InitialMigration1588102412422,
@ -203,6 +204,7 @@ const sqliteMigrations: Migration[] = [
ChangeValueTypesForInsights1759399811000,
CreateChatHubTables1760019379982,
UniqueRoleNames1760020838000,
CreateWorkflowDependencyTable1760314000000,
];
export { sqliteMigrations };