Skip to content

Commit

Permalink
fix: Prevent workflow to run if active and single webhook service (#1…
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-radency authored Nov 20, 2024
1 parent 285534e commit bcb9a20
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 9 deletions.
34 changes: 34 additions & 0 deletions packages/editor-ui/src/composables/useRunWorkflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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()', () => {
Expand Down
31 changes: 22 additions & 9 deletions packages/editor-ui/src/composables/useRunWorkflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -96,8 +96,6 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
return;
}

workflowHelpers.setDocumentTitle(workflow.name as string, 'EXECUTING');

toast.clearAllStickyNotifications();

try {
Expand Down Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ?? {};

Expand Down
9 changes: 9 additions & 0 deletions packages/editor-ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down
2 changes: 2 additions & 0 deletions packages/editor-ui/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit bcb9a20

Please sign in to comment.