Skip to content

Commit 39e2d35

Browse files
MarcLdespairbluenetroy
authored
fix(core): Return correct trigger count for nodes with multiple webhooks (#14300)
Co-authored-by: Danny Martini <[email protected]> Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <[email protected]>
1 parent 480b44d commit 39e2d35

File tree

4 files changed

+82
-14
lines changed

4 files changed

+82
-14
lines changed

packages/cli/src/active-workflow-manager.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,10 +673,23 @@ export class ActiveWorkflowManager {
673673
const triggerFilter = (nodeType: INodeType) =>
674674
!!nodeType.trigger && !nodeType.description.name.includes('manualTrigger');
675675

676+
// Retrieve unique webhooks as some nodes have multiple webhooks
677+
const workflowWebhooks = WebhookHelpers.getWorkflowWebhooks(
678+
workflow,
679+
additionalData,
680+
undefined,
681+
true,
682+
);
683+
684+
const uniqueWebhooks = workflowWebhooks.reduce<Set<string>>((acc, webhook) => {
685+
acc.add(webhook.node);
686+
return acc;
687+
}, new Set());
688+
676689
return (
677690
workflow.queryNodes(triggerFilter).length +
678691
workflow.getPollNodes().length +
679-
WebhookHelpers.getWorkflowWebhooks(workflow, additionalData, undefined, true).length
692+
uniqueWebhooks.size
680693
);
681694
}
682695

packages/cli/test/integration/active-workflow-manager.test.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import { Container } from '@n8n/di';
22
import { mock } from 'jest-mock-extended';
33
import { Logger } from 'n8n-core';
4+
import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node';
5+
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
46
import { NodeApiError, Workflow } from 'n8n-workflow';
5-
import type { IWebhookData, IWorkflowBase, WorkflowActivateMode } from 'n8n-workflow';
7+
import type {
8+
IWebhookData,
9+
IWorkflowBase,
10+
WorkflowActivateMode,
11+
INodeTypeData,
12+
} from 'n8n-workflow';
613

714
import { ActiveExecutions } from '@/active-executions';
815
import { ActiveWorkflowManager } from '@/active-workflow-manager';
916
import type { WebhookEntity } from '@/databases/entities/webhook-entity';
17+
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
1018
import { ExecutionService } from '@/executions/execution.service';
1119
import { ExternalHooks } from '@/external-hooks';
1220
import { NodeTypes } from '@/node-types';
@@ -43,7 +51,18 @@ beforeAll(async () => {
4351

4452
activeWorkflowManager = Container.get(ActiveWorkflowManager);
4553

46-
await utils.initNodeTypes();
54+
const nodes: INodeTypeData = {
55+
'n8n-nodes-base.scheduleTrigger': {
56+
type: new ScheduleTrigger(),
57+
sourcePath: '',
58+
},
59+
'n8n-nodes-base.formTrigger': {
60+
type: new FormTrigger(),
61+
sourcePath: '',
62+
},
63+
};
64+
65+
await utils.initNodeTypes(nodes);
4766

4867
const owner = await createOwner();
4968
createActiveWorkflow = async () => await createWorkflow({ active: true }, owner);
@@ -136,6 +155,43 @@ describe('add()', () => {
136155
},
137156
);
138157
});
158+
159+
test('should count workflow triggers correctly when node has multiple webhooks', async () => {
160+
const workflowRepositoryInstance = Container.get(WorkflowRepository);
161+
const updateWorkflowTriggerCountSpy = jest.spyOn(
162+
workflowRepositoryInstance,
163+
'updateWorkflowTriggerCount',
164+
);
165+
await activeWorkflowManager.init();
166+
167+
// Mock all of the webhooks
168+
const mockWebhooks: IWebhookData[] = [
169+
mock<IWebhookData>({ node: 'Form Trigger', httpMethod: 'GET', path: '/webhook-path' }),
170+
mock<IWebhookData>({ node: 'Form Trigger', httpMethod: 'POST', path: '/webhook-path' }),
171+
];
172+
webhookService.getNodeWebhooks.mockReturnValue(mockWebhooks);
173+
webhookService.createWebhook.mockReturnValue(
174+
mock<WebhookEntity>({ webhookPath: '/webhook-path' }),
175+
);
176+
177+
// Create a workflow which has a form trigger
178+
const dbWorkflow = await createWorkflow({
179+
nodes: [
180+
{
181+
id: 'uuid-1',
182+
parameters: { path: 'test-webhook-path', options: {} },
183+
name: 'Form Trigger',
184+
type: 'n8n-nodes-base.formTrigger',
185+
typeVersion: 1,
186+
position: [500, 300],
187+
},
188+
],
189+
});
190+
191+
await activeWorkflowManager.add(dbWorkflow.id, 'activate');
192+
193+
expect(updateWorkflowTriggerCountSpy).toHaveBeenCalledWith(dbWorkflow.id, 1);
194+
});
139195
});
140196

141197
describe('removeAll()', () => {

packages/cli/test/integration/shared/utils/index.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { Ftp } from 'n8n-nodes-base/credentials/Ftp.credentials';
1010
import { GithubApi } from 'n8n-nodes-base/credentials/GithubApi.credentials';
1111
import { Cron } from 'n8n-nodes-base/nodes/Cron/Cron.node';
12+
import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node';
1213
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
1314
import { Set } from 'n8n-nodes-base/nodes/Set/Set.node';
1415
import { Start } from 'n8n-nodes-base/nodes/Start/Start.node';
@@ -67,9 +68,8 @@ export async function initCredentialsTypes(): Promise<void> {
6768
/**
6869
* Initialize node types.
6970
*/
70-
export async function initNodeTypes() {
71-
ScheduleTrigger.prototype.trigger = async () => ({});
72-
const nodes: INodeTypeData = {
71+
export async function initNodeTypes(customNodes?: INodeTypeData) {
72+
const defaultNodes: INodeTypeData = {
7373
'n8n-nodes-base.start': {
7474
type: new Start(),
7575
sourcePath: '',
@@ -86,7 +86,14 @@ export async function initNodeTypes() {
8686
type: new ScheduleTrigger(),
8787
sourcePath: '',
8888
},
89+
'n8n-nodes-base.formTrigger': {
90+
type: new FormTrigger(),
91+
sourcePath: '',
92+
},
8993
};
94+
95+
ScheduleTrigger.prototype.trigger = async () => ({});
96+
const nodes = customNodes ?? defaultNodes;
9097
const loader = mock<DirectoryLoader>();
9198
loader.getNode.mockImplementation((nodeType) => {
9299
const node = nodes[`n8n-nodes-base.${nodeType}`];

packages/nodes-base/test/nodes/TriggerHelpers.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,6 @@ function getNodeVersion(Trigger: new () => VersionedNodeType, version?: number)
4747
return instance.nodeVersions[version ?? instance.currentVersion];
4848
}
4949

50-
export async function testVersionedTriggerNode(
51-
Trigger: new () => VersionedNodeType,
52-
version?: number,
53-
options: TestTriggerNodeOptions = {},
54-
) {
55-
return await testTriggerNode(getNodeVersion(Trigger, version), options);
56-
}
57-
5850
export async function testTriggerNode(
5951
Trigger: (new () => INodeType) | INodeType,
6052
options: TestTriggerNodeOptions = {},

0 commit comments

Comments
 (0)