fix(core): Restore log event n8n.workflow.failed (#10253)

This commit is contained in:
Iván Ovejero 2024-07-31 12:22:52 +02:00 committed by GitHub
parent efee25ddaa
commit 3e96b29332
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 94 additions and 5 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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<MessageEventBus>();
const eventService = mock<EventService>();
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<Event['workflow-post-execute']>({
executionId: 'some-id',
success: true,
userId: 'some-id',
workflowId: 'some-id',
isManual: true,
workflowName: 'some-name',
metadata: {},
runData: mock<IRun>({ 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<IRun>({
data: {
resultData: {
lastNodeExecuted: 'some-node',
// @ts-expect-error Partial mock
error: {
node: mock<INode>({ 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',
},
});
});
});

View file

@ -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(),
},
});
}

View file

@ -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<string, string>;
runData?: IRun;
};
'node-pre-execute': {

View file

@ -296,6 +296,7 @@ export class ExecutionRecoveryService {
executionId: execution.id,
success: execution.status === 'success',
isManual: execution.mode === 'manual',
runData: execution,
});
const externalHooks = getWorkflowHooksMain(