mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
fix(core): Restore log event n8n.workflow.failed
(#10253)
This commit is contained in:
parent
efee25ddaa
commit
3e96b29332
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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': {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue