fix: Prevent workflow to run if active and single webhook service (#11752)

This commit is contained in:
Michael Kret 2024-11-20 15:48:56 +02:00 committed by GitHub
parent 285534e6d0
commit bcb9a20781
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 9 deletions

View file

@ -10,6 +10,8 @@ import type { IStartRunData, IWorkflowData } from '@/Interface';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useUIStore } from '@/stores/ui.store';
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useToast } from './useToast';
import { useI18n } from '@/composables/useI18n';
vi.mock('@/stores/workflows.store', () => ({
useWorkflowsStore: vi.fn().mockReturnValue({
@ -163,6 +165,38 @@ describe('useRunWorkflow({ router })', () => {
expect(response).toEqual(mockResponse);
expect(workflowsStore.executionWaitingForWebhook).toBe(true);
});
it('should prevent execution and show error message when workflow is active with single webhook trigger', async () => {
const pinia = createTestingPinia({ stubActions: false });
setActivePinia(pinia);
const router = useRouter();
const workflowsStore = useWorkflowsStore();
const toast = useToast();
const i18n = useI18n();
const { runWorkflow } = useRunWorkflow({ router });
vi.mocked(workflowsStore).isWorkflowActive = true;
vi.mocked(useWorkflowHelpers({ router })).getWorkflowDataToSave.mockResolvedValue({
nodes: [
{
name: 'Slack',
type: 'n8n-nodes-base.slackTrigger',
disabled: false,
},
],
} as unknown as IWorkflowData);
const result = await runWorkflow({});
expect(result).toBeUndefined();
expect(toast.showMessage).toHaveBeenCalledWith({
title: i18n.baseText('workflowRun.showError.deactivate'),
message: i18n.baseText('workflowRun.showError.productionActive', {
interpolate: { nodeName: 'Webhook' },
}),
type: 'error',
});
});
});
describe('runWorkflow()', () => {

View file

@ -22,7 +22,7 @@ import { NodeConnectionType } from 'n8n-workflow';
import { useToast } from '@/composables/useToast';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { CHAT_TRIGGER_NODE_TYPE } from '@/constants';
import { CHAT_TRIGGER_NODE_TYPE, SINGLE_WEBHOOK_TRIGGERS } from '@/constants';
import { useRootStore } from '@/stores/root.store';
import { useUIStore } from '@/stores/ui.store';
@ -96,8 +96,6 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
return;
}
workflowHelpers.setDocumentTitle(workflow.name as string, 'EXECUTING');
toast.clearAllStickyNotifications();
try {
@ -172,6 +170,10 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
}
}
const triggers = workflowData.nodes.filter(
(node) => node.type.toLowerCase().includes('trigger') && !node.disabled,
);
//if no destination node is specified
//and execution is not triggered from chat
//and there are other triggers in the workflow
@ -181,12 +183,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
options.source !== 'RunData.ManualChatMessage' &&
workflowData.nodes.some((node) => node.type === CHAT_TRIGGER_NODE_TYPE)
) {
const otherTriggers = workflowData.nodes.filter(
(node) =>
node.type !== CHAT_TRIGGER_NODE_TYPE &&
node.type.toLowerCase().includes('trigger') &&
!node.disabled,
);
const otherTriggers = triggers.filter((node) => node.type !== CHAT_TRIGGER_NODE_TYPE);
if (otherTriggers.length) {
const chatTriggerNode = workflowData.nodes.find(
@ -217,6 +214,21 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
};
});
const singleWebhookTrigger = triggers.find((node) =>
SINGLE_WEBHOOK_TRIGGERS.includes(node.type),
);
if (singleWebhookTrigger && workflowsStore.isWorkflowActive) {
toast.showMessage({
title: i18n.baseText('workflowRun.showError.deactivate'),
message: i18n.baseText('workflowRun.showError.productionActive', {
interpolate: { nodeName: singleWebhookTrigger.name },
}),
type: 'error',
});
return undefined;
}
// -1 means the backend chooses the default
// 0 is the old flow
// 1 is the new flow
@ -263,6 +275,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
workflowsStore.setWorkflowExecutionData(executionData);
nodeHelpers.updateNodesExecutionIssues();
workflowHelpers.setDocumentTitle(workflow.name as string, 'EXECUTING');
const runWorkflowApiResponse = await runWorkflowApi(startRunData);
const pinData = workflowData.pinData ?? {};

View file

@ -197,6 +197,9 @@ export const SIMULATE_NODE_TYPE = 'n8n-nodes-base.simulate';
export const SIMULATE_TRIGGER_NODE_TYPE = 'n8n-nodes-base.simulateTrigger';
export const AI_TRANSFORM_NODE_TYPE = 'n8n-nodes-base.aiTransform';
export const FORM_NODE_TYPE = 'n8n-nodes-base.form';
export const SLACK_TRIGGER_NODE_TYPE = 'n8n-nodes-base.slackTrigger';
export const TELEGRAM_TRIGGER_NODE_TYPE = 'n8n-nodes-base.telegramTrigger';
export const FACEBOOK_LEAD_ADS_TRIGGER_NODE_TYPE = 'n8n-nodes-base.facebookLeadAdsTrigger';
export const CREDENTIAL_ONLY_NODE_PREFIX = 'n8n-creds-base';
export const CREDENTIAL_ONLY_HTTP_NODE_VERSION = 4.1;
@ -232,6 +235,12 @@ export const OPEN_URL_PANEL_TRIGGER_NODE_TYPES = [
CHAT_TRIGGER_NODE_TYPE,
];
export const SINGLE_WEBHOOK_TRIGGERS = [
TELEGRAM_TRIGGER_NODE_TYPE,
SLACK_TRIGGER_NODE_TYPE,
FACEBOOK_LEAD_ADS_TRIGGER_NODE_TYPE,
];
export const LIST_LIKE_NODE_OPERATIONS = ['getAll', 'getMany', 'read', 'search'];
export const PRODUCTION_ONLY_TRIGGER_NODE_TYPES = [CHAT_TRIGGER_NODE_TYPE];

View file

@ -2146,6 +2146,8 @@
"workflowPreview.executionMode.showError.previewError.message": "Unable to preview workflow execution",
"workflowPreview.showError.previewError.title": "Preview error",
"workflowRun.noActiveConnectionToTheServer": "Lost connection to the server",
"workflowRun.showError.deactivate": "Deactivate workflow to execute",
"workflowRun.showError.productionActive": "Because of limitations in {nodeName}, n8n can't listen for test executions at the same time as listening for production ones",
"workflowRun.showError.title": "Problem running workflow",
"workflowRun.showError.payloadTooLarge": "Please execute the whole workflow, rather than just the node. (Existing execution data is too large.)",
"workflowRun.showError.resolveOutstandingIssues": "Please resolve outstanding issues before you activate it",