mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-12 16:10:30 +02:00
fix(core): Use editor base URL for workflow and execution links (#23630)
Signed-off-by: majiayu000 <majiayu000@gmail.com> Signed-off-by: majiayu000 <1835304752@qq.com> Co-authored-by: Matsu <huhta.matias@gmail.com>
This commit is contained in:
parent
83250c1710
commit
896461bee3
|
|
@ -682,8 +682,10 @@ describe('WorkflowExecuteAdditionalData', () => {
|
|||
});
|
||||
|
||||
describe('getBase', () => {
|
||||
const mockWebhookBaseUrl = 'webhook-base-url.com';
|
||||
const mockWebhookBaseUrl = 'https://webhook.example.com/';
|
||||
const mockInstanceBaseUrl = 'https://editor.example.com';
|
||||
jest.spyOn(urlService, 'getWebhookBaseUrl').mockReturnValue(mockWebhookBaseUrl);
|
||||
jest.spyOn(urlService, 'getInstanceBaseUrl').mockReturnValue(mockInstanceBaseUrl);
|
||||
|
||||
const globalConfig = mockInstance(GlobalConfig);
|
||||
Container.set(GlobalConfig, globalConfig);
|
||||
|
|
@ -706,7 +708,7 @@ describe('WorkflowExecuteAdditionalData', () => {
|
|||
credentialsHelper,
|
||||
executeWorkflow: expect.any(Function),
|
||||
restApiUrl: `${mockWebhookBaseUrl}/rest/`,
|
||||
instanceBaseUrl: mockWebhookBaseUrl,
|
||||
instanceBaseUrl: `${mockInstanceBaseUrl}/`,
|
||||
formWaitingBaseUrl: `${mockWebhookBaseUrl}/form-waiting/`,
|
||||
webhookBaseUrl: `${mockWebhookBaseUrl}/webhook/`,
|
||||
webhookWaitingBaseUrl: `${mockWebhookBaseUrl}/webhook-waiting/`,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
import { Logger } from '@n8n/backend-common';
|
||||
import { mockInstance } from '@n8n/backend-test-utils';
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { Container } from '@n8n/di';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { ErrorReporter } from 'n8n-core';
|
||||
import type { INode, IRun, IWorkflowBase } from 'n8n-workflow';
|
||||
import { createRunExecutionData, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { OwnershipService } from '@/services/ownership.service';
|
||||
import { UrlService } from '@/services/url.service';
|
||||
import { WorkflowExecutionService } from '@/workflows/workflow-execution.service';
|
||||
|
||||
import { executeErrorWorkflow } from '../execute-error-workflow';
|
||||
|
||||
describe('executeErrorWorkflow', () => {
|
||||
mockInstance(Logger);
|
||||
mockInstance(ErrorReporter);
|
||||
const globalConfig = mockInstance(GlobalConfig);
|
||||
const urlService = mockInstance(UrlService);
|
||||
const ownershipService = mockInstance(OwnershipService);
|
||||
const workflowExecutionService = mockInstance(WorkflowExecutionService);
|
||||
|
||||
Container.set(GlobalConfig, globalConfig);
|
||||
Container.set(UrlService, urlService);
|
||||
Container.set(OwnershipService, ownershipService);
|
||||
Container.set(WorkflowExecutionService, workflowExecutionService);
|
||||
|
||||
const mockNode = mock<INode>({
|
||||
name: 'TestNode',
|
||||
type: 'n8n-nodes-base.set',
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
globalConfig.nodes = mock<GlobalConfig['nodes']>({
|
||||
errorTriggerType: 'n8n-nodes-base.errorTrigger',
|
||||
});
|
||||
});
|
||||
|
||||
describe('pastExecutionUrl', () => {
|
||||
it('should use getInstanceBaseUrl for pastExecutionUrl', () => {
|
||||
const mockInstanceBaseUrl = 'https://editor.example.com';
|
||||
urlService.getInstanceBaseUrl.mockReturnValue(mockInstanceBaseUrl);
|
||||
|
||||
const workflowData = mock<IWorkflowBase>({
|
||||
id: 'workflow-123',
|
||||
name: 'Test Workflow',
|
||||
settings: { errorWorkflow: 'error-workflow-456' },
|
||||
nodes: [],
|
||||
});
|
||||
|
||||
const testError = new NodeOperationError(mockNode, 'Test error');
|
||||
const fullRunData: IRun = {
|
||||
data: createRunExecutionData({
|
||||
resultData: {
|
||||
error: testError,
|
||||
lastNodeExecuted: 'TestNode',
|
||||
runData: {},
|
||||
},
|
||||
}),
|
||||
mode: 'trigger',
|
||||
startedAt: new Date(),
|
||||
storedAt: 'db',
|
||||
status: 'error',
|
||||
};
|
||||
|
||||
const mockProject = { id: 'project-123' };
|
||||
ownershipService.getWorkflowProjectCached.mockResolvedValue(mockProject as never);
|
||||
workflowExecutionService.executeErrorWorkflow.mockResolvedValue(undefined);
|
||||
|
||||
executeErrorWorkflow(workflowData, fullRunData, 'trigger', 'execution-789');
|
||||
|
||||
expect(urlService.getInstanceBaseUrl).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should construct correct pastExecutionUrl format with instanceBaseUrl', async () => {
|
||||
const mockInstanceBaseUrl = 'https://editor.example.com';
|
||||
urlService.getInstanceBaseUrl.mockReturnValue(mockInstanceBaseUrl);
|
||||
|
||||
const workflowData = mock<IWorkflowBase>({
|
||||
id: 'workflow-123',
|
||||
name: 'Test Workflow',
|
||||
settings: { errorWorkflow: 'error-workflow-456' },
|
||||
nodes: [],
|
||||
});
|
||||
|
||||
const testError = new NodeOperationError(mockNode, 'Test error');
|
||||
const fullRunData: IRun = {
|
||||
data: createRunExecutionData({
|
||||
resultData: {
|
||||
error: testError,
|
||||
lastNodeExecuted: 'TestNode',
|
||||
runData: {},
|
||||
},
|
||||
}),
|
||||
mode: 'trigger',
|
||||
startedAt: new Date(),
|
||||
storedAt: 'db',
|
||||
status: 'error',
|
||||
};
|
||||
|
||||
const mockProject = { id: 'project-123' };
|
||||
ownershipService.getWorkflowProjectCached.mockResolvedValue(mockProject as never);
|
||||
|
||||
let capturedWorkflowErrorData: unknown;
|
||||
workflowExecutionService.executeErrorWorkflow.mockImplementation(
|
||||
async (_errorWorkflow, workflowErrorData) => {
|
||||
capturedWorkflowErrorData = workflowErrorData;
|
||||
},
|
||||
);
|
||||
|
||||
executeErrorWorkflow(workflowData, fullRunData, 'trigger', 'execution-789');
|
||||
|
||||
// Wait for async operations
|
||||
await new Promise(process.nextTick);
|
||||
|
||||
expect(capturedWorkflowErrorData).toMatchObject({
|
||||
execution: {
|
||||
id: 'execution-789',
|
||||
url: 'https://editor.example.com/workflow/workflow-123/executions/execution-789',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -31,7 +31,7 @@ export function executeErrorWorkflow(
|
|||
// Check if there was an error and if so if an errorWorkflow or a trigger is set
|
||||
let pastExecutionUrl: string | undefined;
|
||||
if (executionId !== undefined) {
|
||||
pastExecutionUrl = `${Container.get(UrlService).getWebhookBaseUrl()}workflow/${
|
||||
pastExecutionUrl = `${Container.get(UrlService).getInstanceBaseUrl()}/workflow/${
|
||||
workflowData.id
|
||||
}/executions/${executionId}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -471,7 +471,9 @@ export async function getBase({
|
|||
executionTimeoutTimestamp?: number;
|
||||
workflowSettings?: IWorkflowSettings;
|
||||
} = {}): Promise<IWorkflowExecuteAdditionalData> {
|
||||
const urlBaseWebhook = Container.get(UrlService).getWebhookBaseUrl();
|
||||
const urlService = Container.get(UrlService);
|
||||
const urlBaseWebhook = urlService.getWebhookBaseUrl();
|
||||
const instanceBaseUrl = urlService.getInstanceBaseUrl();
|
||||
|
||||
const globalConfig = Container.get(GlobalConfig);
|
||||
|
||||
|
|
@ -484,7 +486,7 @@ export async function getBase({
|
|||
credentialsHelper: Container.get(CredentialsHelper),
|
||||
executeWorkflow,
|
||||
restApiUrl: urlBaseWebhook + globalConfig.endpoints.rest,
|
||||
instanceBaseUrl: urlBaseWebhook,
|
||||
instanceBaseUrl: `${instanceBaseUrl}/`,
|
||||
formWaitingBaseUrl: urlBaseWebhook + globalConfig.endpoints.formWaiting,
|
||||
webhookBaseUrl: urlBaseWebhook + globalConfig.endpoints.webhook,
|
||||
webhookWaitingBaseUrl: urlBaseWebhook + globalConfig.endpoints.webhookWaiting,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user