From 3e96b293329525c9d4b2fcef87b3803e458c8e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 31 Jul 2024 12:22:52 +0200 Subject: [PATCH] fix(core): Restore log event `n8n.workflow.failed` (#10253) --- .../cli/src/WorkflowExecuteAdditionalData.ts | 2 + packages/cli/src/WorkflowRunner.ts | 1 + .../audit-event-relay.service.test.ts | 69 ++++++++++++++++++- .../src/eventbus/audit-event-relay.service.ts | 23 ++++++- packages/cli/src/eventbus/event.types.ts | 3 +- .../executions/execution-recovery.service.ts | 1 + 6 files changed, 94 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 754cfea693c3d..98ad9acde2936 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -651,6 +651,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { executionId, success: runData.status === 'success', isManual: runData.mode === 'manual', + runData, }); }, async function (this: WorkflowHooks, fullRunData: IRun) { @@ -940,6 +941,7 @@ async function executeWorkflow( success: data.status === 'success', isManual: data.mode === 'manual', userId: additionalData.userId, + runData: data, }); // subworkflow either finished, or is in status waiting due to a wait node, both cases are considered successes here diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index f8faf55fbe49d..3318dd283cd60 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -173,6 +173,7 @@ export class WorkflowRunner { success: executionData?.status === 'success', isManual: data.executionMode === 'manual', userId: data.userId, + runData: executionData, }); if (this.externalHooks.exists('workflow.postExecute')) { try { diff --git a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts index 80392206079b7..145bf2929f07a 100644 --- a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts +++ b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts @@ -2,12 +2,14 @@ import { mock } from 'jest-mock-extended'; import { AuditEventRelay } from '../audit-event-relay.service'; import type { MessageEventBus } from '../MessageEventBus/MessageEventBus'; import type { Event } from '../event.types'; -import type { EventService } from '../event.service'; +import { EventService } from '../event.service'; +import type { INode, IRun } from 'n8n-workflow'; describe('AuditorService', () => { const eventBus = mock(); - const eventService = mock(); + const eventService = new EventService(); const auditor = new AuditEventRelay(eventService, eventBus); + auditor.init(); afterEach(() => { jest.clearAllMocks(); @@ -80,4 +82,67 @@ describe('AuditorService', () => { }, }); }); + + it('should log on `workflow-post-execute` for successful execution', () => { + const payload = mock({ + executionId: 'some-id', + success: true, + userId: 'some-id', + workflowId: 'some-id', + isManual: true, + workflowName: 'some-name', + metadata: {}, + runData: mock({ data: { resultData: {} } }), + }); + + eventService.emit('workflow-post-execute', payload); + + const { runData: _, ...rest } = payload; + + expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ + eventName: 'n8n.workflow.success', + payload: rest, + }); + }); + + it('should handle `workflow-post-execute` event for unsuccessful execution', () => { + const runData = mock({ + data: { + resultData: { + lastNodeExecuted: 'some-node', + // @ts-expect-error Partial mock + error: { + node: mock({ type: 'some-type' }), + message: 'some-message', + }, + errorMessage: 'some-message', + }, + }, + }) as unknown as IRun; + + const event = { + executionId: 'some-id', + success: false, + userId: 'some-id', + workflowId: 'some-id', + isManual: true, + workflowName: 'some-name', + metadata: {}, + runData, + }; + + eventService.emit('workflow-post-execute', event); + + const { runData: _, ...rest } = event; + + expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ + eventName: 'n8n.workflow.failed', + payload: { + ...rest, + lastNodeExecuted: 'some-node', + errorNodeType: 'some-type', + errorMessage: 'some-message', + }, + }); + }); }); diff --git a/packages/cli/src/eventbus/audit-event-relay.service.ts b/packages/cli/src/eventbus/audit-event-relay.service.ts index 9c73494520dbe..56f4dd95cd1e6 100644 --- a/packages/cli/src/eventbus/audit-event-relay.service.ts +++ b/packages/cli/src/eventbus/audit-event-relay.service.ts @@ -122,9 +122,28 @@ export class AuditEventRelay { } private workflowPostExecute(event: Event['workflow-post-execute']) { + const { runData, ...rest } = event; + + if (event.success) { + void this.eventBus.sendWorkflowEvent({ + eventName: 'n8n.workflow.success', + payload: rest, + }); + + return; + } + void this.eventBus.sendWorkflowEvent({ - eventName: 'n8n.workflow.success', - payload: event, + eventName: 'n8n.workflow.failed', + payload: { + ...rest, + lastNodeExecuted: runData?.data.resultData.lastNodeExecuted, + errorNodeType: + runData?.data.resultData.error && 'node' in runData?.data.resultData.error + ? runData?.data.resultData.error.node?.type + : undefined, + errorMessage: runData?.data.resultData.error?.message.toString(), + }, }); } diff --git a/packages/cli/src/eventbus/event.types.ts b/packages/cli/src/eventbus/event.types.ts index 225f9aca8c873..513c659d3e2a3 100644 --- a/packages/cli/src/eventbus/event.types.ts +++ b/packages/cli/src/eventbus/event.types.ts @@ -1,4 +1,4 @@ -import type { AuthenticationMethod, IWorkflowBase } from 'n8n-workflow'; +import type { AuthenticationMethod, IRun, IWorkflowBase } from 'n8n-workflow'; import type { IWorkflowExecutionDataProcess } from '@/Interfaces'; import type { ProjectRole } from '@/databases/entities/ProjectRelation'; import type { GlobalRole } from '@/databases/entities/User'; @@ -46,6 +46,7 @@ export type Event = { isManual: boolean; workflowName: string; metadata?: Record; + runData?: IRun; }; 'node-pre-execute': { diff --git a/packages/cli/src/executions/execution-recovery.service.ts b/packages/cli/src/executions/execution-recovery.service.ts index 05431a6d9c9d5..b72fc490dddbb 100644 --- a/packages/cli/src/executions/execution-recovery.service.ts +++ b/packages/cli/src/executions/execution-recovery.service.ts @@ -296,6 +296,7 @@ export class ExecutionRecoveryService { executionId: execution.id, success: execution.status === 'success', isManual: execution.mode === 'manual', + runData: execution, }); const externalHooks = getWorkflowHooksMain(