diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index 7910ef38e..d1ea87ffa 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -225,6 +225,7 @@ export interface IExecutionsStopData { mode: WorkflowExecuteMode; startedAt: Date; stoppedAt?: Date; + status: ExecutionStatus; } export interface IExecutionsCurrentSummary { diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index aed725c7a..efeabb4f4 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -145,6 +145,7 @@ import { eventBus } from './eventbus'; import { isSamlEnabled } from './Saml/helpers'; import { Container } from 'typedi'; import { InternalHooks } from './InternalHooks'; +import { getStatusUsingPreviousExecutionStatusMethod } from './executions/executionHelpers'; const exec = promisify(callbackExec); @@ -986,6 +987,9 @@ class Server extends AbstractServer { if (!executions.length) return []; return executions.map((execution) => { + if (!execution.status) { + execution.status = getStatusUsingPreviousExecutionStatusMethod(execution); + } return { id: execution.id, workflowId: execution.workflowId, @@ -1020,6 +1024,7 @@ class Server extends AbstractServer { mode: data.mode, retryOf: data.retryOf, startedAt: new Date(data.startedAt), + status: data.status, }); } @@ -1072,6 +1077,7 @@ class Server extends AbstractServer { startedAt: new Date(result.startedAt), stoppedAt: result.stoppedAt ? new Date(result.stoppedAt) : undefined, finished: result.finished, + status: result.status, } as IExecutionsStopData; } @@ -1098,6 +1104,7 @@ class Server extends AbstractServer { ? new Date(fullExecutionData.stoppedAt) : undefined, finished: fullExecutionData.finished, + status: fullExecutionData.status, }; return returnData; @@ -1116,6 +1123,7 @@ class Server extends AbstractServer { startedAt: new Date(result.startedAt), stoppedAt: result.stoppedAt ? new Date(result.stoppedAt) : undefined, finished: result.finished, + status: result.status, }; } diff --git a/packages/cli/src/WaitTracker.ts b/packages/cli/src/WaitTracker.ts index 435f4e9aa..b36d49856 100644 --- a/packages/cli/src/WaitTracker.ts +++ b/packages/cli/src/WaitTracker.ts @@ -144,6 +144,7 @@ export class WaitTracker { startedAt: new Date(fullExecutionData.startedAt), stoppedAt: fullExecutionData.stoppedAt ? new Date(fullExecutionData.stoppedAt) : undefined, finished: fullExecutionData.finished, + status: fullExecutionData.status, }; } diff --git a/packages/cli/src/databases/migrations/mysqldb/1676996103000-MigrateExecutionStatus.ts b/packages/cli/src/databases/migrations/mysqldb/1676996103000-MigrateExecutionStatus.ts new file mode 100644 index 000000000..6c196148f --- /dev/null +++ b/packages/cli/src/databases/migrations/mysqldb/1676996103000-MigrateExecutionStatus.ts @@ -0,0 +1,28 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers'; +import config from '@/config'; + +export class MigrateExecutionStatus1676996103000 implements MigrationInterface { + name = 'MigrateExecutionStatus1676996103000'; + public async up(queryRunner: QueryRunner): Promise { + logMigrationStart(this.name); + const tablePrefix = config.getEnv('database.tablePrefix'); + + await queryRunner.query( + `UPDATE \`${tablePrefix}execution_entity\` SET status='waiting' WHERE status IS NULL AND \`waitTill\` IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE \`${tablePrefix}execution_entity\` SET status='failed' WHERE status IS NULL AND finished=0 AND \`stoppedAt\` IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE \`${tablePrefix}execution_entity\` SET status='success' WHERE status IS NULL AND finished=1 AND \`stoppedAt\` IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE \`${tablePrefix}execution_entity\` SET status='crashed' WHERE status IS NULL;`, + ); + + logMigrationEnd(this.name); + } + + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/packages/cli/src/databases/migrations/mysqldb/index.ts b/packages/cli/src/databases/migrations/mysqldb/index.ts index af0240b4d..db7ab0ff4 100644 --- a/packages/cli/src/databases/migrations/mysqldb/index.ts +++ b/packages/cli/src/databases/migrations/mysqldb/index.ts @@ -32,6 +32,7 @@ import { DeleteExecutionsWithWorkflows1673268682475 } from './1673268682475-Dele import { CreateLdapEntities1674509946020 } from './1674509946020-CreateLdapEntities'; import { PurgeInvalidWorkflowConnections1675940580449 } from './1675940580449-PurgeInvalidWorkflowConnections'; import { AddStatusToExecutions1674138566000 } from './1674138566000-AddStatusToExecutions'; +import { MigrateExecutionStatus1676996103000 } from './1676996103000-MigrateExecutionStatus'; export const mysqlMigrations = [ InitialMigration1588157391238, @@ -68,4 +69,5 @@ export const mysqlMigrations = [ CreateLdapEntities1674509946020, PurgeInvalidWorkflowConnections1675940580449, AddStatusToExecutions1674138566000, + MigrateExecutionStatus1676996103000, ]; diff --git a/packages/cli/src/databases/migrations/postgresdb/1676996103000-MigrateExecutionStatus.ts b/packages/cli/src/databases/migrations/postgresdb/1676996103000-MigrateExecutionStatus.ts new file mode 100644 index 000000000..fdfec47d7 --- /dev/null +++ b/packages/cli/src/databases/migrations/postgresdb/1676996103000-MigrateExecutionStatus.ts @@ -0,0 +1,28 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers'; +import config from '@/config'; + +export class MigrateExecutionStatus1676996103000 implements MigrationInterface { + name = 'MigrateExecutionStatus1676996103000'; + public async up(queryRunner: QueryRunner): Promise { + logMigrationStart(this.name); + const tablePrefix = config.getEnv('database.tablePrefix'); + + await queryRunner.query( + `UPDATE "${tablePrefix}execution_entity" SET "status" = 'waiting' WHERE "status" IS NULL AND "waitTill" IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE "${tablePrefix}execution_entity" SET "status" = 'failed' WHERE "status" IS NULL AND "finished"=false AND "stoppedAt" IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE "${tablePrefix}execution_entity" SET "status" = 'success' WHERE "status" IS NULL AND "finished"=true AND "stoppedAt" IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE "${tablePrefix}execution_entity" SET "status" = 'crashed' WHERE "status" IS NULL;`, + ); + + logMigrationEnd(this.name); + } + + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/packages/cli/src/databases/migrations/postgresdb/index.ts b/packages/cli/src/databases/migrations/postgresdb/index.ts index 1c19fa86b..32b48f626 100644 --- a/packages/cli/src/databases/migrations/postgresdb/index.ts +++ b/packages/cli/src/databases/migrations/postgresdb/index.ts @@ -30,6 +30,7 @@ import { DeleteExecutionsWithWorkflows1673268682475 } from './1673268682475-Dele import { CreateLdapEntities1674509946020 } from './1674509946020-CreateLdapEntities'; import { PurgeInvalidWorkflowConnections1675940580449 } from './1675940580449-PurgeInvalidWorkflowConnections'; import { AddStatusToExecutions1674138566000 } from './1674138566000-AddStatusToExecutions'; +import { MigrateExecutionStatus1676996103000 } from './1676996103000-MigrateExecutionStatus'; export const postgresMigrations = [ InitialMigration1587669153312, @@ -64,4 +65,5 @@ export const postgresMigrations = [ CreateLdapEntities1674509946020, PurgeInvalidWorkflowConnections1675940580449, AddStatusToExecutions1674138566000, + MigrateExecutionStatus1676996103000, ]; diff --git a/packages/cli/src/databases/migrations/sqlite/1676996103000-MigrateExecutionStatus.ts b/packages/cli/src/databases/migrations/sqlite/1676996103000-MigrateExecutionStatus.ts new file mode 100644 index 000000000..78334751e --- /dev/null +++ b/packages/cli/src/databases/migrations/sqlite/1676996103000-MigrateExecutionStatus.ts @@ -0,0 +1,28 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers'; +import config from '@/config'; + +export class MigrateExecutionStatus1676996103000 implements MigrationInterface { + name = 'MigrateExecutionStatus1676996103000'; + public async up(queryRunner: QueryRunner): Promise { + logMigrationStart(this.name); + const tablePrefix = config.getEnv('database.tablePrefix'); + + await queryRunner.query( + `UPDATE "${tablePrefix}execution_entity" SET "status" = 'waiting' WHERE "status" IS NULL AND "waitTill" IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE "${tablePrefix}execution_entity" SET "status" = 'failed' WHERE "status" IS NULL AND "finished"=0 AND "stoppedAt" IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE "${tablePrefix}execution_entity" SET "status" = 'success' WHERE "status" IS NULL AND "finished"=1 AND "stoppedAt" IS NOT NULL;`, + ); + await queryRunner.query( + `UPDATE "${tablePrefix}execution_entity" SET "status" = 'crashed' WHERE "status" IS NULL;`, + ); + + logMigrationEnd(this.name); + } + + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/packages/cli/src/databases/migrations/sqlite/index.ts b/packages/cli/src/databases/migrations/sqlite/index.ts index 01cd22f22..eadb90eff 100644 --- a/packages/cli/src/databases/migrations/sqlite/index.ts +++ b/packages/cli/src/databases/migrations/sqlite/index.ts @@ -29,6 +29,7 @@ import { DeleteExecutionsWithWorkflows1673268682475 } from './1673268682475-Dele import { CreateLdapEntities1674509946020 } from './1674509946020-CreateLdapEntities'; import { PurgeInvalidWorkflowConnections1675940580449 } from './1675940580449-PurgeInvalidWorkflowConnections'; import { AddStatusToExecutions1674138566000 } from './1674138566000-AddStatusToExecutions'; +import { MigrateExecutionStatus1676996103000 } from './1676996103000-MigrateExecutionStatus'; const sqliteMigrations = [ InitialMigration1588102412422, @@ -62,6 +63,7 @@ const sqliteMigrations = [ CreateLdapEntities1674509946020, PurgeInvalidWorkflowConnections1675940580449, AddStatusToExecutions1674138566000, + MigrateExecutionStatus1676996103000, ]; export { sqliteMigrations }; diff --git a/packages/cli/src/executions/executionHelpers.ts b/packages/cli/src/executions/executionHelpers.ts new file mode 100644 index 000000000..21ee792b0 --- /dev/null +++ b/packages/cli/src/executions/executionHelpers.ts @@ -0,0 +1,18 @@ +import type { IExecutionFlattedDb } from '../Interfaces'; +import type { ExecutionStatus } from 'n8n-workflow'; + +export function getStatusUsingPreviousExecutionStatusMethod( + execution: IExecutionFlattedDb, +): ExecutionStatus { + if (execution.waitTill) { + return 'waiting'; + } else if (execution.stoppedAt === undefined) { + return 'running'; + } else if (execution.finished) { + return 'success'; + } else if (execution.stoppedAt !== null) { + return 'failed'; + } else { + return 'unknown'; + } +} diff --git a/packages/cli/src/executions/executions.service.ts b/packages/cli/src/executions/executions.service.ts index 9aa23a079..a4f9d05dc 100644 --- a/packages/cli/src/executions/executions.service.ts +++ b/packages/cli/src/executions/executions.service.ts @@ -35,6 +35,7 @@ import * as Db from '@/Db'; import * as GenericHelpers from '@/GenericHelpers'; import { parse } from 'flatted'; import { Container } from 'typedi'; +import { getStatusUsingPreviousExecutionStatusMethod } from './executionHelpers'; interface IGetExecutionsQueryFilter { id?: FindOperator; @@ -213,7 +214,6 @@ export class ExecutionsService { }; if (filter?.status) { Object.assign(findWhere, { status: In(filter.status) }); - delete filter.status; // remove status from filter so it does not get applied twice } if (filter?.finished) { Object.assign(findWhere, { finished: filter.finished }); @@ -259,12 +259,18 @@ export class ExecutionsService { 'execution.startedAt', 'execution.stoppedAt', 'execution.workflowData', + 'execution.status', ]) .orderBy('id', 'DESC') .take(limit) .where(findWhere); const countFilter = deepCopy(filter ?? {}); + // deepcopy breaks the In operator so we need to reapply it + if (filter?.status) { + Object.assign(filter, { status: In(filter.status) }); + Object.assign(countFilter, { status: In(filter.status) }); + } if (filter) { this.massageFilters(filter as IDataObject); @@ -286,6 +292,10 @@ export class ExecutionsService { const nodeExecutionStatus = {}; let lastNodeExecuted; let executionError; + // fill execution status for old executions that will return null + if (!execution.status) { + execution.status = getStatusUsingPreviousExecutionStatusMethod(execution); + } try { const data = parse(execution.data) as IRunExecutionData; lastNodeExecuted = data?.resultData?.lastNodeExecuted ?? ''; @@ -363,6 +373,10 @@ export class ExecutionsService { return undefined; } + if (!execution.status) { + execution.status = getStatusUsingPreviousExecutionStatusMethod(execution); + } + if (req.query.unflattedResponse === 'true') { return ResponseHelper.unflattenExecutionData(execution); } diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index dffc17b4c..2d3bb9951 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -31,6 +31,7 @@ import { IExecutionsSummary, IAbstractEventMessage, FeatureFlags, + ExecutionStatus, } from 'n8n-workflow'; import { SignInType } from './constants'; import { FAKE_DOOR_FEATURES, TRIGGER_NODE_FILTER, REGULAR_NODE_FILTER } from './constants'; @@ -383,6 +384,7 @@ export interface IExecutionsStopData { mode: WorkflowExecuteMode; startedAt: Date; stoppedAt: Date; + status: ExecutionStatus; } export interface IExecutionDeleteFilter {