Skip to content

Commit

Permalink
move generateFailedExecutionFromError to ExecutionDataService
Browse files Browse the repository at this point in the history
  • Loading branch information
netroy committed Mar 11, 2025
1 parent 0300696 commit 9a9462b
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 111 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { mock } from 'jest-mock-extended';
import { NodeOperationError } from 'n8n-workflow';
import type { INode, WorkflowExecuteMode } from 'n8n-workflow';

import { ExecutionDataService } from '../execution-data.service';

describe('ExecutionDataService', () => {
const service = new ExecutionDataService();

describe('generateFailedExecutionFromError', () => {
const mode: WorkflowExecuteMode = 'manual';
const node = mock<INode>({ name: 'Test Node' });
const error = new NodeOperationError(node, 'Test error message');

it('should generate a failed execution with error details', () => {
const startTime = Date.now();

const result = service.generateFailedExecutionFromError(mode, error, node, startTime);

expect(result.mode).toBe(mode);
expect(result.status).toBe('error');
expect(result.startedAt).toBeInstanceOf(Date);
expect(result.stoppedAt).toBeInstanceOf(Date);
expect(result.data.resultData.error?.message).toBe(error.message);

const taskData = result.data.resultData.runData[node.name][0];
expect(taskData.error?.message).toBe(error.message);
expect(taskData.startTime).toBe(startTime);
expect(taskData.executionStatus).toBe('error');
expect(result.data.resultData.lastNodeExecuted).toBe(node.name);
expect(result.data.executionData?.nodeExecutionStack[0].node).toEqual(node);
});

it('should generate a failed execution without node details if node is undefined', () => {
const result = service.generateFailedExecutionFromError(mode, error, undefined);

expect(result.mode).toBe(mode);
expect(result.status).toBe('error');
expect(result.startedAt).toBeInstanceOf(Date);
expect(result.stoppedAt).toBeInstanceOf(Date);
expect(result.data.resultData.error?.message).toBe(error.message);
expect(result.data.resultData.runData).toEqual({});
expect(result.data.resultData.lastNodeExecuted).toBeUndefined();
expect(result.data.executionData).toBeUndefined();
});
});
});
46 changes: 1 addition & 45 deletions packages/cli/src/executions/__tests__/execution.service.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { mock } from 'jest-mock-extended';
import type { INode, WorkflowExecuteMode } from 'n8n-workflow';
import { NodeOperationError, WorkflowOperationError } from 'n8n-workflow';
import { WorkflowOperationError } from 'n8n-workflow';

import type { ActiveExecutions } from '@/active-executions';
import type { ConcurrencyControlService } from '@/concurrency/concurrency-control.service';
Expand Down Expand Up @@ -278,47 +277,4 @@ describe('ExecutionService', () => {
});
});
});

describe('generateFailedExecutionFromError', () => {
const mode: WorkflowExecuteMode = 'manual';
const node = mock<INode>({ name: 'Test Node' });
const error = new NodeOperationError(node, 'Test error message');

it('should generate a failed execution with error details', () => {
const startTime = Date.now();

const result = executionService.generateFailedExecutionFromError(
mode,
error,
node,
startTime,
);

expect(result.mode).toBe(mode);
expect(result.status).toBe('error');
expect(result.startedAt).toBeInstanceOf(Date);
expect(result.stoppedAt).toBeInstanceOf(Date);
expect(result.data.resultData.error?.message).toBe(error.message);

const taskData = result.data.resultData.runData[node.name][0];
expect(taskData.error?.message).toBe(error.message);
expect(taskData.startTime).toBe(startTime);
expect(taskData.executionStatus).toBe('error');
expect(result.data.resultData.lastNodeExecuted).toBe(node.name);
expect(result.data.executionData?.nodeExecutionStack[0].node).toEqual(node);
});

it('should generate a failed execution without node details if node is undefined', () => {
const result = executionService.generateFailedExecutionFromError(mode, error, undefined);

expect(result.mode).toBe(mode);
expect(result.status).toBe('error');
expect(result.startedAt).toBeInstanceOf(Date);
expect(result.stoppedAt).toBeInstanceOf(Date);
expect(result.data.resultData.error?.message).toBe(error.message);
expect(result.data.resultData.runData).toEqual({});
expect(result.data.resultData.lastNodeExecuted).toBeUndefined();
expect(result.data.executionData).toBeUndefined();
});
});
});
62 changes: 62 additions & 0 deletions packages/cli/src/executions/execution-data.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Service } from '@n8n/di';
import type { ExecutionError, INode, IRun, WorkflowExecuteMode } from 'n8n-workflow';

@Service()
export class ExecutionDataService {
generateFailedExecutionFromError(
mode: WorkflowExecuteMode,
error: ExecutionError,
node: INode | undefined,
startTime = Date.now(),
): IRun {
const executionError = {
...error,
message: error.message,
stack: error.stack,
};
const returnData: IRun = {
data: {
resultData: {
error: executionError,
runData: {},
},
},
finished: false,
mode,
startedAt: new Date(),
stoppedAt: new Date(),
status: 'error',
};

if (node) {
returnData.data.startData = {
destinationNode: node.name,
runNodeFilter: [node.name],
};
returnData.data.resultData.lastNodeExecuted = node.name;
returnData.data.resultData.runData[node.name] = [
{
startTime,
executionTime: 0,
executionStatus: 'error',
error: executionError,
source: [],
},
];
returnData.data.executionData = {
contextData: {},
metadata: {},
waitingExecution: {},
waitingExecutionSource: {},
nodeExecutionStack: [
{
node,
data: {},
source: null,
},
],
};
}
return returnData;
}
}
58 changes: 0 additions & 58 deletions packages/cli/src/executions/execution.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import type {
IWorkflowBase,
WorkflowExecuteMode,
IWorkflowExecutionDataProcess,
IRun,
} from 'n8n-workflow';
import {
ExecutionStatusList,
Expand Down Expand Up @@ -525,61 +524,4 @@ export class ExecutionService {
await this.annotationTagMappingRepository.overwriteTags(annotation.id, updateData.tags);
}
}

generateFailedExecutionFromError(
mode: WorkflowExecuteMode,
error: ExecutionError,
node: INode | undefined,
startTime = Date.now(),
): IRun {
const executionError = {
...error,
message: error.message,
stack: error.stack,
};
const returnData: IRun = {
data: {
resultData: {
error: executionError,
runData: {},
},
},
finished: false,
mode,
startedAt: new Date(),
stoppedAt: new Date(),
status: 'error',
};

if (node) {
returnData.data.startData = {
destinationNode: node.name,
runNodeFilter: [node.name],
};
returnData.data.resultData.lastNodeExecuted = node.name;
returnData.data.resultData.runData[node.name] = [
{
startTime,
executionTime: 0,
executionStatus: 'error',
error: executionError,
source: [],
},
];
returnData.data.executionData = {
contextData: {},
metadata: {},
waitingExecution: {},
waitingExecutionSource: {},
nodeExecutionStack: [
{
node,
data: {},
source: null,
},
],
};
}
return returnData;
}
}
4 changes: 2 additions & 2 deletions packages/cli/src/workflow-execute-additional-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { WorkflowRepository } from '@/databases/repositories/workflow.repository
import { EventService } from '@/events/event.service';
import type { AiEventMap, AiEventPayload } from '@/events/maps/ai.event-map';
import { getLifecycleHooksForSubExecutions } from '@/execution-lifecycle/execution-lifecycle-hooks';
import { ExecutionService } from '@/executions/execution.service';
import { ExecutionDataService } from '@/executions/execution-data.service';
import {
CredentialsPermissionChecker,
SubworkflowPolicyChecker,
Expand Down Expand Up @@ -262,7 +262,7 @@ async function startExecution(
data = await execution;
} catch (error) {
const executionError = error as ExecutionError;
const fullRunData = Container.get(ExecutionService).generateFailedExecutionFromError(
const fullRunData = Container.get(ExecutionDataService).generateFailedExecutionFromError(
runData.executionMode,
executionError,
'node' in executionError ? executionError.node : undefined,
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/workflow-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
getLifecycleHooksForScalingWorker,
getLifecycleHooksForScalingMain,
} from '@/execution-lifecycle/execution-lifecycle-hooks';
import { ExecutionService } from '@/executions/execution.service';
import { ExecutionDataService } from '@/executions/execution-data.service';
import { CredentialsPermissionChecker } from '@/executions/pre-execution-checks';
import { ManualExecutionService } from '@/manual-execution.service';
import { NodeTypes } from '@/node-types';
Expand All @@ -52,7 +52,7 @@ export class WorkflowRunner {
private readonly credentialsPermissionChecker: CredentialsPermissionChecker,
private readonly instanceSettings: InstanceSettings,
private readonly manualExecutionService: ManualExecutionService,
private readonly executionService: ExecutionService,
private readonly executionDataService: ExecutionDataService,
) {}

/** The process did error */
Expand Down Expand Up @@ -137,7 +137,7 @@ export class WorkflowRunner {
await this.credentialsPermissionChecker.check(workflowId, nodes);
} catch (error) {
// Create a failed execution with the data for the node, save it and abort execution
const runData = this.executionService.generateFailedExecutionFromError(
const runData = this.executionDataService.generateFailedExecutionFromError(
data.executionMode,
error,
error.node,
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/workflows/workflow-execution.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { Project } from '@/databases/entities/project';
import type { User } from '@/databases/entities/user';
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { ExecutionService } from '@/executions/execution.service';
import { ExecutionDataService } from '@/executions/execution-data.service';
import { SubworkflowPolicyChecker } from '@/executions/pre-execution-checks';
import type { CreateExecutionPayload, IWorkflowErrorData } from '@/interfaces';
import { NodeTypes } from '@/node-types';
Expand All @@ -42,7 +42,7 @@ export class WorkflowExecutionService {
private readonly workflowRunner: WorkflowRunner,
private readonly globalConfig: GlobalConfig,
private readonly subworkflowPolicyChecker: SubworkflowPolicyChecker,
private readonly executionService: ExecutionService,
private readonly executionDataService: ExecutionDataService,
) {}

async runWorkflow(
Expand Down Expand Up @@ -274,7 +274,7 @@ export class WorkflowExecutionService {
);

// Create a fake execution and save it to DB.
const fakeExecution = this.executionService.generateFailedExecutionFromError(
const fakeExecution = this.executionDataService.generateFailedExecutionFromError(
'error',
errorWorkflowPermissionError,
initialNode,
Expand Down

0 comments on commit 9a9462b

Please sign in to comment.