From bcb9a2078186ff80e03ca3b8532d3585c307d86b Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:48:56 +0200 Subject: [PATCH] fix: Prevent workflow to run if active and single webhook service (#11752) --- .../src/composables/useRunWorkflow.test.ts | 34 +++++++++++++++++++ .../src/composables/useRunWorkflow.ts | 31 ++++++++++++----- packages/editor-ui/src/constants.ts | 9 +++++ .../src/plugins/i18n/locales/en.json | 2 ++ 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/packages/editor-ui/src/composables/useRunWorkflow.test.ts b/packages/editor-ui/src/composables/useRunWorkflow.test.ts index bbdcbb81c0ade..7a29fcd0988fe 100644 --- a/packages/editor-ui/src/composables/useRunWorkflow.test.ts +++ b/packages/editor-ui/src/composables/useRunWorkflow.test.ts @@ -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()', () => { diff --git a/packages/editor-ui/src/composables/useRunWorkflow.ts b/packages/editor-ui/src/composables/useRunWorkflow.ts index 034fa74e7d14c..f527a2340bd61 100644 --- a/packages/editor-ui/src/composables/useRunWorkflow.ts +++ b/packages/editor-ui/src/composables/useRunWorkflow.ts @@ -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 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 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 + 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