fix: Extend deduplication check to all webhook-based triggers and chat trigger (#18044)

This commit is contained in:
Michael Kret
2025-08-07 13:46:17 +03:00
committed by GitHub
parent 99c2f3715e
commit 847a5d822f
4 changed files with 185 additions and 16 deletions

View File

@@ -1,10 +1,13 @@
import { createTestingPinia } from '@pinia/testing';
import { createComponentRenderer } from '@/__tests__/render';
import WorkflowActivationConflictingWebhookModal from '@/components/WorkflowActivationConflictingWebhookModal.vue';
import { WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY } from '@/constants';
import {
SLACK_TRIGGER_NODE_TYPE,
WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY,
} from '@/constants';
import { waitFor } from '@testing-library/vue';
import { FORM_TRIGGER_NODE_TYPE, WEBHOOK_NODE_TYPE } from 'n8n-workflow';
import { CHAT_TRIGGER_NODE_TYPE, FORM_TRIGGER_NODE_TYPE, WEBHOOK_NODE_TYPE } from 'n8n-workflow';
vi.mock('@/stores/ui.store', () => {
return {
@@ -60,6 +63,9 @@ describe('WorkflowActivationConflictingWebhookModal', () => {
expect(wrapper.getByTestId('conflicting-webhook-callout')).toHaveTextContent(
"A webhook trigger 'Node in workflow' in the workflow 'Test Workflow' uses a conflicting URL path, so this workflow cannot be activated",
);
expect(wrapper.getByTestId('conflicting-webhook-suggestion')).toHaveTextContent(
'and activate this one, or adjust the following URL path in either workflow:',
);
expect(wrapper.getByTestId('conflicting-webhook-path')).toHaveTextContent(
'http://webhook-base/webhook-path',
);
@@ -87,8 +93,71 @@ describe('WorkflowActivationConflictingWebhookModal', () => {
expect(wrapper.getByTestId('conflicting-webhook-callout')).toHaveTextContent(
"A form trigger 'Node in workflow' in the workflow 'Test Form' uses a conflicting URL path, so this workflow cannot be activated",
);
expect(wrapper.getByTestId('conflicting-webhook-suggestion')).toHaveTextContent(
'and activate this one, or adjust the following URL path in either workflow:',
);
expect(wrapper.getByTestId('conflicting-webhook-path')).toHaveTextContent(
'http://webhook-base/form-path',
);
});
it('should render chat conflict modal', async () => {
const props = {
modalName: WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY,
data: {
triggerName: 'Chat in this workflow',
workflowName: 'Test Chat',
triggerType: CHAT_TRIGGER_NODE_TYPE,
workflowId: '123',
webhookPath: '123/chat',
method: 'POST',
node: 'Chat trigger',
},
};
const wrapper = renderComponent({ props });
await waitFor(() => {
expect(wrapper.queryByTestId('conflicting-webhook-callout')).toBeInTheDocument();
});
expect(wrapper.getByTestId('conflicting-webhook-callout')).toHaveTextContent(
"A chat trigger 'Chat trigger' in the workflow 'Test Chat' uses a conflicting URL path, so this workflow cannot be activated",
);
expect(wrapper.getByTestId('conflicting-webhook-suggestion')).toHaveTextContent(
'and activate this one, or insert a new Chat Trigger node in either workflow:',
);
expect(wrapper.getByTestId('conflicting-webhook-path')).toHaveTextContent(
'http://webhook-base/123/chat',
);
});
it('should render trigger conflict modal', async () => {
const props = {
modalName: WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY,
data: {
triggerName: 'Slack in this workflow',
workflowName: 'Test Trigger',
triggerType: SLACK_TRIGGER_NODE_TYPE,
workflowId: '123',
webhookPath: '123/webhook',
method: 'POST',
node: 'Slack trigger',
},
};
const wrapper = renderComponent({ props });
await waitFor(() => {
expect(wrapper.queryByTestId('conflicting-webhook-callout')).toBeInTheDocument();
});
expect(wrapper.getByTestId('conflicting-webhook-callout')).toHaveTextContent(
"A trigger 'Slack trigger' in the workflow 'Test Trigger' uses a conflicting URL path, so this workflow cannot be activated",
);
expect(wrapper.getByTestId('conflicting-webhook-suggestion')).toHaveTextContent(
'and activate this one, or insert a new trigger node of the same type in either workflow:',
);
expect(wrapper.getByTestId('conflicting-webhook-path')).toHaveTextContent(
'http://webhook-base/123/webhook',
);
});
});

View File

@@ -6,7 +6,7 @@ import { useUIStore } from '@/stores/ui.store';
import { useRootStore } from '@n8n/stores/useRootStore';
import { computed } from 'vue';
import { FORM_TRIGGER_NODE_TYPE } from 'n8n-workflow';
import { CHAT_TRIGGER_NODE_TYPE, FORM_TRIGGER_NODE_TYPE, WEBHOOK_NODE_TYPE } from 'n8n-workflow';
const modalBus = createEventBus();
const uiStore = useUIStore();
@@ -28,9 +28,33 @@ const webhookUrl = computed(() => {
return rootStore.webhookUrl;
});
const webhookType = computed(() => {
if (data.triggerType === FORM_TRIGGER_NODE_TYPE) return 'form';
return 'webhook';
const webhookTypeUi = computed((): { title: string; callout: string; suggestion: string } => {
const suggestionBase = 'and activate this one, or ';
if (data.triggerType === FORM_TRIGGER_NODE_TYPE)
return {
title: 'Form',
callout: 'form trigger',
suggestion: suggestionBase + 'adjust the following URL path in either workflow:',
};
if (data.triggerType === CHAT_TRIGGER_NODE_TYPE)
return {
title: 'Chat',
callout: 'chat trigger',
suggestion: suggestionBase + 'insert a new Chat Trigger node in either workflow:',
};
if (data.triggerType === WEBHOOK_NODE_TYPE)
return {
title: 'Webhook',
callout: 'webhook trigger',
suggestion: suggestionBase + 'adjust the following URL path in either workflow:',
};
return {
title: 'Trigger',
callout: 'trigger',
suggestion: suggestionBase + 'insert a new trigger node of the same type in either workflow:',
};
});
const workflowUrl = computed(() => {
@@ -46,21 +70,21 @@ const onClick = async () => {
<Modal
width="540px"
:name="WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY"
:title="`Conflicting ${webhookType === 'form' ? 'Form' : 'Webhook'} Path`"
:title="`Conflicting ${webhookTypeUi.title} Path`"
:event-bus="modalBus"
:center="true"
>
<template #content>
<n8n-callout theme="danger" data-test-id="conflicting-webhook-callout">
A {{ webhookType }} trigger '{{ data.node }}' in the workflow '{{ data.workflowName }}' uses
a conflicting URL path, so this workflow cannot be activated
A {{ webhookTypeUi.callout }} '{{ data.node }}' in the workflow '{{ data.workflowName }}'
uses a conflicting URL path, so this workflow cannot be activated
</n8n-callout>
<div :class="$style.container">
<div>
<n8n-text color="text-base"> You can deactivate </n8n-text>
<n8n-link :to="workflowUrl" :underline="true"> '{{ data.workflowName }}' </n8n-link>
<n8n-text color="text-base">
and activate this one, or adjust the following URL path in either workflow:
<n8n-link :to="workflowUrl" :underline="true"> {{ data.workflowName }} </n8n-link>
<n8n-text color="text-base" data-test-id="conflicting-webhook-suggestion">
{{ webhookTypeUi.suggestion }}
</n8n-text>
</div>
</div>