mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 05:29:44 +02:00
feat(core): Allow a sending status on instance monitoring reports (no-changelog) (#39923)
This commit is contained in:
parent
5576c785ce
commit
45edf565df
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| CHK_instance_monitoring_report_status | CHECK | CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'delivered'::character varying, 'skipped_after_max_retries'::character varying])::text[]))) |
|
||||
| CHK_instance_monitoring_report_status | CHECK | CHECK (((status)::text = ANY ((ARRAY['pending'::character varying, 'delivered'::character varying, 'skipped_after_max_retries'::character varying, 'sending'::character varying])::text[]))) |
|
||||
| PK_4a5cb8aa51c67e4f5a7eb0f9509 | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
| instance_monitoring_report_attempts_not_null | n | NOT NULL attempts |
|
||||
| instance_monitoring_report_createdAt_not_null | n | NOT NULL "createdAt" |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<summary><strong>Table Definition</strong></summary>
|
||||
|
||||
```sql
|
||||
CREATE TABLE "instance_monitoring_report" ("id" varchar PRIMARY KEY NOT NULL, "dataPoints" text NOT NULL, "status" varchar(64) NOT NULL DEFAULT ('pending'), "deliveredAt" datetime(3), "attempts" integer NOT NULL DEFAULT (0), "lastAttemptAt" datetime(3), "lastError" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "reportDate" varchar(10), CONSTRAINT "CHK_instance_monitoring_report_status" CHECK (("status" IN ('pending', 'delivered', 'skipped_after_max_retries'))))
|
||||
CREATE TABLE "instance_monitoring_report" ("id" varchar PRIMARY KEY NOT NULL, "dataPoints" text NOT NULL, "status" varchar(64) NOT NULL DEFAULT ('pending'), "deliveredAt" datetime(3), "attempts" integer NOT NULL DEFAULT (0), "lastAttemptAt" datetime(3), "lastError" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "reportDate" varchar(10), CONSTRAINT "CHK_instance_monitoring_report_status" CHECK ("status" IN ('pending', 'delivered', 'skipped_after_max_retries', 'sending')))
|
||||
```
|
||||
|
||||
</details>
|
||||
|
|
@ -30,7 +30,7 @@ CREATE TABLE "instance_monitoring_report" ("id" varchar PRIMARY KEY NOT NULL, "d
|
|||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| - | CHECK | CHECK (("status" IN ('pending', 'delivered', 'skipped_after_max_retries'))) |
|
||||
| - | CHECK | CHECK ("status" IN ('pending', 'delivered', 'skipped_after_max_retries', 'sending')) |
|
||||
| id | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
| sqlite_autoindex_instance_monitoring_report_1 | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import type { MigrationContext, ReversibleMigration } from '../migration-types';
|
||||
|
||||
const TABLE = 'instance_monitoring_report';
|
||||
const COLUMN = 'status';
|
||||
const VALUES_BEFORE = ['pending', 'delivered', 'skipped_after_max_retries'];
|
||||
const VALUES_AFTER = [...VALUES_BEFORE, 'sending'];
|
||||
|
||||
export class AllowSendingInstanceReportStatus1790774580617 implements ReversibleMigration {
|
||||
async up({ schemaBuilder }: MigrationContext) {
|
||||
await schemaBuilder.dropEnumCheck(TABLE, COLUMN, { recreatesOnSqlite: true });
|
||||
await schemaBuilder.addEnumCheck(TABLE, COLUMN, VALUES_AFTER, { recreatesOnSqlite: true });
|
||||
}
|
||||
|
||||
async down({ escape, runQuery, schemaBuilder }: MigrationContext) {
|
||||
await runQuery(
|
||||
`UPDATE ${escape.tableName(TABLE)} SET ${escape.columnName(COLUMN)} = 'pending' WHERE ${escape.columnName(COLUMN)} = 'sending'`,
|
||||
);
|
||||
await schemaBuilder.dropEnumCheck(TABLE, COLUMN, { recreatesOnSqlite: true });
|
||||
await schemaBuilder.addEnumCheck(TABLE, COLUMN, VALUES_BEFORE, { recreatesOnSqlite: true });
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
import {
|
||||
createTestMigrationContext,
|
||||
initDbUpToMigration,
|
||||
runSingleMigration,
|
||||
undoLastSingleMigration,
|
||||
type TestMigrationContext,
|
||||
} from '@n8n/backend-test-utils';
|
||||
import { DbConnection } from '@n8n/db';
|
||||
import { Container } from '@n8n/di';
|
||||
import { DataSource } from '@n8n/typeorm';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
const MIGRATION_NAME = 'AllowSendingInstanceReportStatus1790774580617';
|
||||
const TABLE = 'instance_monitoring_report';
|
||||
|
||||
describe('AllowSendingInstanceReportStatus Migration', () => {
|
||||
let dataSource: DataSource;
|
||||
|
||||
beforeAll(async () => {
|
||||
const dbConnection = Container.get(DbConnection);
|
||||
await dbConnection.init();
|
||||
dataSource = Container.get(DataSource);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await context.queryRunner.clearDatabase();
|
||||
await context.queryRunner.release();
|
||||
await initDbUpToMigration(MIGRATION_NAME);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const dbConnection = Container.get(DbConnection);
|
||||
await dbConnection.close();
|
||||
});
|
||||
|
||||
async function withContext<T>(run: (context: TestMigrationContext) => Promise<T>): Promise<T> {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
try {
|
||||
return await run(context);
|
||||
} finally {
|
||||
await context.queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
async function insertReport(context: TestMigrationContext, status: string): Promise<string> {
|
||||
const id = randomUUID();
|
||||
await context.runQuery(
|
||||
`INSERT INTO ${context.escape.tableName(TABLE)}
|
||||
("id", "dataPoints", "status", "createdAt", "updatedAt")
|
||||
VALUES (:id, '[]', :status, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
|
||||
{ id, status },
|
||||
);
|
||||
return id;
|
||||
}
|
||||
|
||||
async function statuses(context: TestMigrationContext): Promise<Record<string, string>> {
|
||||
const rows = (await context.queryRunner.query(
|
||||
`SELECT "id", "status" FROM ${context.escape.tableName(TABLE)}`,
|
||||
)) as Array<{ id: string; status: string }>;
|
||||
return Object.fromEntries(rows.map((row) => [row.id, row.status]));
|
||||
}
|
||||
|
||||
describe('up', () => {
|
||||
it('accepts a sending report after the migration and still rejects an unknown status', async () => {
|
||||
await withContext(async (context) => {
|
||||
await expect(insertReport(context, 'sending')).rejects.toThrow();
|
||||
});
|
||||
|
||||
await runSingleMigration(MIGRATION_NAME);
|
||||
|
||||
await withContext(async (context) => {
|
||||
await expect(insertReport(context, 'sending')).resolves.toBeDefined();
|
||||
await expect(insertReport(context, 'unknown')).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('down', () => {
|
||||
it('resets sending reports to pending and rejects sending again', async () => {
|
||||
await runSingleMigration(MIGRATION_NAME);
|
||||
const ids = await withContext(async (context) => ({
|
||||
sending: await insertReport(context, 'sending'),
|
||||
pending: await insertReport(context, 'pending'),
|
||||
delivered: await insertReport(context, 'delivered'),
|
||||
skipped: await insertReport(context, 'skipped_after_max_retries'),
|
||||
}));
|
||||
|
||||
await undoLastSingleMigration();
|
||||
|
||||
await withContext(async (context) => {
|
||||
expect(await statuses(context)).toEqual({
|
||||
[ids.sending]: 'pending',
|
||||
[ids.pending]: 'pending',
|
||||
[ids.delivered]: 'delivered',
|
||||
[ids.skipped]: 'skipped_after_max_retries',
|
||||
});
|
||||
await expect(insertReport(context, 'sending')).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user