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, executionId,
success: runData.status === 'success', success: runData.status === 'success',
isManual: runData.mode === 'manual', isManual: runData.mode === 'manual',
runData,
}); });
}, },
async function (this: WorkflowHooks, fullRunData: IRun) { async function (this: WorkflowHooks, fullRunData: IRun) {
@ -940,6 +941,7 @@ async function executeWorkflow(
success: data.status === 'success', success: data.status === 'success',
isManual: data.mode === 'manual', isManual: data.mode === 'manual',
userId: additionalData.userId, userId: additionalData.userId,
runData: data,
}); });
// subworkflow either finished, or is in status waiting due to a wait node, both cases are considered successes here // 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', success: executionData?.status === 'success',
isManual: data.executionMode === 'manual', isManual: data.executionMode === 'manual',
userId: data.userId, userId: data.userId,
runData: executionData,
}); });
if (this.externalHooks.exists('workflow.postExecute')) { if (this.externalHooks.exists('workflow.postExecute')) {
try { try {

View file

@ -2,12 +2,14 @@ import { mock } from 'jest-mock-extended';
import { AuditEventRelay } from '../audit-event-relay.service'; import { AuditEventRelay } from '../audit-event-relay.service';
import type { MessageEventBus } from '../MessageEventBus/MessageEventBus'; import type { MessageEventBus } from '../MessageEventBus/MessageEventBus';
import type { Event } from '../event.types'; 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', () => { describe('AuditorService', () => {
const eventBus = mock<MessageEventBus>(); const eventBus = mock<MessageEventBus>();
const eventService = mock<EventService>(); const eventService = new EventService();
const auditor = new AuditEventRelay(eventService, eventBus); const auditor = new AuditEventRelay(eventService, eventBus);
auditor.init();
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); 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']) { 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({ void this.eventBus.sendWorkflowEvent({
eventName: 'n8n.workflow.success', eventName: 'n8n.workflow.failed',
payload: event, 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 { IWorkflowExecutionDataProcess } from '@/Interfaces';
import type { ProjectRole } from '@/databases/entities/ProjectRelation'; import type { ProjectRole } from '@/databases/entities/ProjectRelation';
import type { GlobalRole } from '@/databases/entities/User'; import type { GlobalRole } from '@/databases/entities/User';
@ -46,6 +46,7 @@ export type Event = {
isManual: boolean; isManual: boolean;
workflowName: string; workflowName: string;
metadata?: Record<string, string>; metadata?: Record<string, string>;
runData?: IRun;
}; };
'node-pre-execute': { 'node-pre-execute': {

View file

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