mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(core): Add tables for reporting dashboard (no-changelog) (#13336)
This commit is contained in:
parent
35c00d0c84
commit
f8a7fb38cc
|
@ -1,10 +1,20 @@
|
||||||
import type { Driver, TableColumnOptions } from '@n8n/typeorm';
|
import type { Driver, TableColumnOptions } from '@n8n/typeorm';
|
||||||
|
|
||||||
export class Column {
|
export class Column {
|
||||||
private type: 'int' | 'boolean' | 'varchar' | 'text' | 'json' | 'timestamp' | 'uuid';
|
private type:
|
||||||
|
| 'int'
|
||||||
|
| 'boolean'
|
||||||
|
| 'varchar'
|
||||||
|
| 'text'
|
||||||
|
| 'json'
|
||||||
|
| 'timestamptz'
|
||||||
|
| 'timestamp'
|
||||||
|
| 'uuid';
|
||||||
|
|
||||||
private isGenerated = false;
|
private isGenerated = false;
|
||||||
|
|
||||||
|
private isGenerated2 = false;
|
||||||
|
|
||||||
private isNullable = true;
|
private isNullable = true;
|
||||||
|
|
||||||
private isPrimary = false;
|
private isPrimary = false;
|
||||||
|
@ -15,6 +25,8 @@ export class Column {
|
||||||
|
|
||||||
private primaryKeyConstraintName: string | undefined;
|
private primaryKeyConstraintName: string | undefined;
|
||||||
|
|
||||||
|
private commentValue: string | undefined;
|
||||||
|
|
||||||
constructor(private name: string) {}
|
constructor(private name: string) {}
|
||||||
|
|
||||||
get bool() {
|
get bool() {
|
||||||
|
@ -43,7 +55,22 @@ export class Column {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use `timestampTimezone` instead
|
||||||
|
**/
|
||||||
timestamp(msPrecision = 3) {
|
timestamp(msPrecision = 3) {
|
||||||
|
this.type = 'timestamptz';
|
||||||
|
this.length = msPrecision ?? 'auto';
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
timestampTimezone(msPrecision = 3) {
|
||||||
|
this.type = 'timestamptz';
|
||||||
|
this.length = msPrecision ?? 'auto';
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
timestampNoTimezone(msPrecision = 3) {
|
||||||
this.type = 'timestamp';
|
this.type = 'timestamp';
|
||||||
this.length = msPrecision ?? 'auto';
|
this.length = msPrecision ?? 'auto';
|
||||||
return this;
|
return this;
|
||||||
|
@ -75,15 +102,40 @@ export class Column {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated, use autoGenerate2 instead
|
||||||
|
**/
|
||||||
get autoGenerate() {
|
get autoGenerate() {
|
||||||
this.isGenerated = true;
|
this.isGenerated = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefers `identity` over `increment` (which turns to `serial` for pg)
|
||||||
|
* See https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_serial
|
||||||
|
**/
|
||||||
|
get autoGenerate2() {
|
||||||
|
this.isGenerated2 = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
comment(comment: string) {
|
||||||
|
this.commentValue = comment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line complexity
|
// eslint-disable-next-line complexity
|
||||||
toOptions(driver: Driver): TableColumnOptions {
|
toOptions(driver: Driver): TableColumnOptions {
|
||||||
const { name, type, isNullable, isPrimary, isGenerated, length, primaryKeyConstraintName } =
|
const {
|
||||||
this;
|
name,
|
||||||
|
type,
|
||||||
|
isNullable,
|
||||||
|
isPrimary,
|
||||||
|
isGenerated,
|
||||||
|
isGenerated2,
|
||||||
|
length,
|
||||||
|
primaryKeyConstraintName,
|
||||||
|
} = this;
|
||||||
const isMysql = 'mysql' in driver;
|
const isMysql = 'mysql' in driver;
|
||||||
const isPostgres = 'postgres' in driver;
|
const isPostgres = 'postgres' in driver;
|
||||||
const isSqlite = 'sqlite' in driver;
|
const isSqlite = 'sqlite' in driver;
|
||||||
|
@ -100,8 +152,10 @@ export class Column {
|
||||||
options.type = 'integer';
|
options.type = 'integer';
|
||||||
} else if (type === 'boolean' && isMysql) {
|
} else if (type === 'boolean' && isMysql) {
|
||||||
options.type = 'tinyint(1)';
|
options.type = 'tinyint(1)';
|
||||||
} else if (type === 'timestamp') {
|
} else if (type === 'timestamptz') {
|
||||||
options.type = isPostgres ? 'timestamptz' : 'datetime';
|
options.type = isPostgres ? 'timestamptz' : 'datetime';
|
||||||
|
} else if (type === 'timestamp') {
|
||||||
|
options.type = isPostgres ? 'timestamp' : 'datetime';
|
||||||
} else if (type === 'json' && isSqlite) {
|
} else if (type === 'json' && isSqlite) {
|
||||||
options.type = 'text';
|
options.type = 'text';
|
||||||
} else if (type === 'uuid') {
|
} else if (type === 'uuid') {
|
||||||
|
@ -111,7 +165,10 @@ export class Column {
|
||||||
if (isSqlite) options.type = 'varchar';
|
if (isSqlite) options.type = 'varchar';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type === 'varchar' || type === 'timestamp') && length !== 'auto') {
|
if (
|
||||||
|
(type === 'varchar' || type === 'timestamptz' || type === 'timestamp') &&
|
||||||
|
length !== 'auto'
|
||||||
|
) {
|
||||||
options.type = `${options.type}(${length})`;
|
options.type = `${options.type}(${length})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,12 +177,17 @@ export class Column {
|
||||||
options.generationStrategy = type === 'uuid' ? 'uuid' : 'increment';
|
options.generationStrategy = type === 'uuid' ? 'uuid' : 'increment';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPrimary || isGenerated) {
|
if (isGenerated2) {
|
||||||
|
options.isGenerated = true;
|
||||||
|
options.generationStrategy = type === 'uuid' ? 'uuid' : 'identity';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPrimary || isGenerated || isGenerated2) {
|
||||||
options.isNullable = false;
|
options.isNullable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.defaultValue !== undefined) {
|
if (this.defaultValue !== undefined) {
|
||||||
if (type === 'timestamp' && this.defaultValue === 'NOW()') {
|
if ((type === 'timestamptz' || type === 'timestamp') && this.defaultValue === 'NOW()') {
|
||||||
options.default = isSqlite
|
options.default = isSqlite
|
||||||
? "STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')"
|
? "STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')"
|
||||||
: 'CURRENT_TIMESTAMP(3)';
|
: 'CURRENT_TIMESTAMP(3)';
|
||||||
|
@ -134,6 +196,10 @@ export class Column {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.commentValue) {
|
||||||
|
options.comment = this.commentValue;
|
||||||
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
import type { MigrationContext, ReversibleMigration } from '@/databases/types';
|
||||||
|
|
||||||
|
const names = {
|
||||||
|
// table names
|
||||||
|
t: {
|
||||||
|
analyticsMetadata: 'analytics_metadata',
|
||||||
|
analyticsRaw: 'analytics_raw',
|
||||||
|
analyticsByPeriod: 'analytics_by_period',
|
||||||
|
workflowEntity: 'workflow_entity',
|
||||||
|
project: 'project',
|
||||||
|
},
|
||||||
|
// column names by table
|
||||||
|
c: {
|
||||||
|
analyticsMetadata: {
|
||||||
|
metaId: 'metaId',
|
||||||
|
projectId: 'projectId',
|
||||||
|
workflowId: 'workflowId',
|
||||||
|
},
|
||||||
|
analyticsRaw: {
|
||||||
|
metaId: 'metaId',
|
||||||
|
},
|
||||||
|
analyticsByPeriod: {
|
||||||
|
metaId: 'metaId',
|
||||||
|
type: 'type',
|
||||||
|
periodUnit: 'periodUnit',
|
||||||
|
periodStart: 'periodStart',
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
id: 'id',
|
||||||
|
},
|
||||||
|
workflowEntity: {
|
||||||
|
id: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export class CreateAnalyticsTables1739549398681 implements ReversibleMigration {
|
||||||
|
async up({ schemaBuilder: { createTable, column } }: MigrationContext) {
|
||||||
|
await createTable(names.t.analyticsMetadata)
|
||||||
|
.withColumns(
|
||||||
|
column(names.c.analyticsMetadata.metaId).int.primary.autoGenerate2,
|
||||||
|
column(names.c.analyticsMetadata.workflowId).varchar(16),
|
||||||
|
column(names.c.analyticsMetadata.projectId).varchar(36),
|
||||||
|
column('workflowName').varchar(128).notNull,
|
||||||
|
column('projectName').varchar(255).notNull,
|
||||||
|
)
|
||||||
|
.withForeignKey(names.c.analyticsMetadata.workflowId, {
|
||||||
|
tableName: names.t.workflowEntity,
|
||||||
|
columnName: names.c.workflowEntity.id,
|
||||||
|
onDelete: 'SET NULL',
|
||||||
|
})
|
||||||
|
.withForeignKey(names.c.analyticsMetadata.projectId, {
|
||||||
|
tableName: names.t.project,
|
||||||
|
columnName: names.c.project.id,
|
||||||
|
onDelete: 'SET NULL',
|
||||||
|
});
|
||||||
|
|
||||||
|
const typeComment = '0: time_saved_minutes, 1: runtime_milliseconds, 2: success, 3: failure';
|
||||||
|
|
||||||
|
await createTable(names.t.analyticsRaw)
|
||||||
|
.withColumns(
|
||||||
|
column('id').int.primary.autoGenerate2,
|
||||||
|
column(names.c.analyticsRaw.metaId).int.notNull,
|
||||||
|
column('type').int.notNull.comment(typeComment),
|
||||||
|
column('value').int.notNull,
|
||||||
|
column('timestamp').timestampNoTimezone(0).default('CURRENT_TIMESTAMP').notNull,
|
||||||
|
)
|
||||||
|
.withForeignKey(names.c.analyticsRaw.metaId, {
|
||||||
|
tableName: names.t.analyticsMetadata,
|
||||||
|
columnName: names.c.analyticsMetadata.metaId,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
});
|
||||||
|
|
||||||
|
await createTable(names.t.analyticsByPeriod)
|
||||||
|
.withColumns(
|
||||||
|
column('id').int.primary.autoGenerate2,
|
||||||
|
column(names.c.analyticsByPeriod.metaId).int.notNull,
|
||||||
|
column(names.c.analyticsByPeriod.type).int.notNull.comment(typeComment),
|
||||||
|
column('value').int.notNull,
|
||||||
|
column(names.c.analyticsByPeriod.periodUnit).int.notNull.comment(
|
||||||
|
'0: hour, 1: day, 2: week',
|
||||||
|
),
|
||||||
|
column(names.c.analyticsByPeriod.periodStart).timestampNoTimezone(0),
|
||||||
|
)
|
||||||
|
.withForeignKey(names.c.analyticsByPeriod.metaId, {
|
||||||
|
tableName: names.t.analyticsMetadata,
|
||||||
|
columnName: names.c.analyticsMetadata.metaId,
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
.withIndexOn(
|
||||||
|
[
|
||||||
|
names.c.analyticsByPeriod.periodStart,
|
||||||
|
names.c.analyticsByPeriod.type,
|
||||||
|
names.c.analyticsByPeriod.periodUnit,
|
||||||
|
names.c.analyticsByPeriod.metaId,
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down({ schemaBuilder: { dropTable } }: MigrationContext) {
|
||||||
|
await dropTable(names.t.analyticsMetadata);
|
||||||
|
await dropTable(names.t.analyticsRaw);
|
||||||
|
await dropTable(names.t.analyticsByPeriod);
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,6 +81,7 @@ import { AddManagedColumnToCredentialsTable1734479635324 } from '../common/17344
|
||||||
import { CreateTestCaseExecutionTable1736947513045 } from '../common/1736947513045-CreateTestCaseExecutionTable';
|
import { CreateTestCaseExecutionTable1736947513045 } from '../common/1736947513045-CreateTestCaseExecutionTable';
|
||||||
import { AddErrorColumnsToTestRuns1737715421462 } from '../common/1737715421462-AddErrorColumnsToTestRuns';
|
import { AddErrorColumnsToTestRuns1737715421462 } from '../common/1737715421462-AddErrorColumnsToTestRuns';
|
||||||
import { CreateFolderTable1738709609940 } from '../common/1738709609940-CreateFolderTable';
|
import { CreateFolderTable1738709609940 } from '../common/1738709609940-CreateFolderTable';
|
||||||
|
import { CreateAnalyticsTables1739549398681 } from '../common/1739549398681-CreateAnalyticsTables';
|
||||||
|
|
||||||
export const mysqlMigrations: Migration[] = [
|
export const mysqlMigrations: Migration[] = [
|
||||||
InitialMigration1588157391238,
|
InitialMigration1588157391238,
|
||||||
|
@ -164,4 +165,5 @@ export const mysqlMigrations: Migration[] = [
|
||||||
AddErrorColumnsToTestRuns1737715421462,
|
AddErrorColumnsToTestRuns1737715421462,
|
||||||
CreateFolderTable1738709609940,
|
CreateFolderTable1738709609940,
|
||||||
FixTestDefinitionPrimaryKey1739873751194,
|
FixTestDefinitionPrimaryKey1739873751194,
|
||||||
|
CreateAnalyticsTables1739549398681,
|
||||||
];
|
];
|
||||||
|
|
|
@ -80,6 +80,7 @@ import { AddStatsColumnsToTestRun1736172058779 } from '../common/1736172058779-A
|
||||||
import { CreateTestCaseExecutionTable1736947513045 } from '../common/1736947513045-CreateTestCaseExecutionTable';
|
import { CreateTestCaseExecutionTable1736947513045 } from '../common/1736947513045-CreateTestCaseExecutionTable';
|
||||||
import { AddErrorColumnsToTestRuns1737715421462 } from '../common/1737715421462-AddErrorColumnsToTestRuns';
|
import { AddErrorColumnsToTestRuns1737715421462 } from '../common/1737715421462-AddErrorColumnsToTestRuns';
|
||||||
import { CreateFolderTable1738709609940 } from '../common/1738709609940-CreateFolderTable';
|
import { CreateFolderTable1738709609940 } from '../common/1738709609940-CreateFolderTable';
|
||||||
|
import { CreateAnalyticsTables1739549398681 } from '../common/1739549398681-CreateAnalyticsTables';
|
||||||
|
|
||||||
export const postgresMigrations: Migration[] = [
|
export const postgresMigrations: Migration[] = [
|
||||||
InitialMigration1587669153312,
|
InitialMigration1587669153312,
|
||||||
|
@ -162,4 +163,5 @@ export const postgresMigrations: Migration[] = [
|
||||||
CreateTestCaseExecutionTable1736947513045,
|
CreateTestCaseExecutionTable1736947513045,
|
||||||
AddErrorColumnsToTestRuns1737715421462,
|
AddErrorColumnsToTestRuns1737715421462,
|
||||||
CreateFolderTable1738709609940,
|
CreateFolderTable1738709609940,
|
||||||
|
CreateAnalyticsTables1739549398681,
|
||||||
];
|
];
|
||||||
|
|
|
@ -77,6 +77,7 @@ import { AddManagedColumnToCredentialsTable1734479635324 } from '../common/17344
|
||||||
import { AddStatsColumnsToTestRun1736172058779 } from '../common/1736172058779-AddStatsColumnsToTestRun';
|
import { AddStatsColumnsToTestRun1736172058779 } from '../common/1736172058779-AddStatsColumnsToTestRun';
|
||||||
import { CreateTestCaseExecutionTable1736947513045 } from '../common/1736947513045-CreateTestCaseExecutionTable';
|
import { CreateTestCaseExecutionTable1736947513045 } from '../common/1736947513045-CreateTestCaseExecutionTable';
|
||||||
import { AddErrorColumnsToTestRuns1737715421462 } from '../common/1737715421462-AddErrorColumnsToTestRuns';
|
import { AddErrorColumnsToTestRuns1737715421462 } from '../common/1737715421462-AddErrorColumnsToTestRuns';
|
||||||
|
import { CreateAnalyticsTables1739549398681 } from '../common/1739549398681-CreateAnalyticsTables';
|
||||||
|
|
||||||
const sqliteMigrations: Migration[] = [
|
const sqliteMigrations: Migration[] = [
|
||||||
InitialMigration1588102412422,
|
InitialMigration1588102412422,
|
||||||
|
@ -156,6 +157,7 @@ const sqliteMigrations: Migration[] = [
|
||||||
CreateTestCaseExecutionTable1736947513045,
|
CreateTestCaseExecutionTable1736947513045,
|
||||||
AddErrorColumnsToTestRuns1737715421462,
|
AddErrorColumnsToTestRuns1737715421462,
|
||||||
CreateFolderTable1738709609940,
|
CreateFolderTable1738709609940,
|
||||||
|
CreateAnalyticsTables1739549398681,
|
||||||
];
|
];
|
||||||
|
|
||||||
export { sqliteMigrations };
|
export { sqliteMigrations };
|
||||||
|
|
Loading…
Reference in a new issue