mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
fix(core): Return correct trigger count for nodes with multiple webhooks (#14300)
Co-authored-by: Danny Martini <danny@n8n.io> Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
@@ -673,10 +673,23 @@ export class ActiveWorkflowManager {
|
|||||||
const triggerFilter = (nodeType: INodeType) =>
|
const triggerFilter = (nodeType: INodeType) =>
|
||||||
!!nodeType.trigger && !nodeType.description.name.includes('manualTrigger');
|
!!nodeType.trigger && !nodeType.description.name.includes('manualTrigger');
|
||||||
|
|
||||||
|
// Retrieve unique webhooks as some nodes have multiple webhooks
|
||||||
|
const workflowWebhooks = WebhookHelpers.getWorkflowWebhooks(
|
||||||
|
workflow,
|
||||||
|
additionalData,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const uniqueWebhooks = workflowWebhooks.reduce<Set<string>>((acc, webhook) => {
|
||||||
|
acc.add(webhook.node);
|
||||||
|
return acc;
|
||||||
|
}, new Set());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
workflow.queryNodes(triggerFilter).length +
|
workflow.queryNodes(triggerFilter).length +
|
||||||
workflow.getPollNodes().length +
|
workflow.getPollNodes().length +
|
||||||
WebhookHelpers.getWorkflowWebhooks(workflow, additionalData, undefined, true).length
|
uniqueWebhooks.size
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import { Container } from '@n8n/di';
|
import { Container } from '@n8n/di';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import { Logger } from 'n8n-core';
|
import { Logger } from 'n8n-core';
|
||||||
|
import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node';
|
||||||
|
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
|
||||||
import { NodeApiError, Workflow } from 'n8n-workflow';
|
import { NodeApiError, Workflow } from 'n8n-workflow';
|
||||||
import type { IWebhookData, IWorkflowBase, WorkflowActivateMode } from 'n8n-workflow';
|
import type {
|
||||||
|
IWebhookData,
|
||||||
|
IWorkflowBase,
|
||||||
|
WorkflowActivateMode,
|
||||||
|
INodeTypeData,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { ActiveExecutions } from '@/active-executions';
|
import { ActiveExecutions } from '@/active-executions';
|
||||||
import { ActiveWorkflowManager } from '@/active-workflow-manager';
|
import { ActiveWorkflowManager } from '@/active-workflow-manager';
|
||||||
import type { WebhookEntity } from '@/databases/entities/webhook-entity';
|
import type { WebhookEntity } from '@/databases/entities/webhook-entity';
|
||||||
|
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
|
||||||
import { ExecutionService } from '@/executions/execution.service';
|
import { ExecutionService } from '@/executions/execution.service';
|
||||||
import { ExternalHooks } from '@/external-hooks';
|
import { ExternalHooks } from '@/external-hooks';
|
||||||
import { NodeTypes } from '@/node-types';
|
import { NodeTypes } from '@/node-types';
|
||||||
@@ -43,7 +51,18 @@ beforeAll(async () => {
|
|||||||
|
|
||||||
activeWorkflowManager = Container.get(ActiveWorkflowManager);
|
activeWorkflowManager = Container.get(ActiveWorkflowManager);
|
||||||
|
|
||||||
await utils.initNodeTypes();
|
const nodes: INodeTypeData = {
|
||||||
|
'n8n-nodes-base.scheduleTrigger': {
|
||||||
|
type: new ScheduleTrigger(),
|
||||||
|
sourcePath: '',
|
||||||
|
},
|
||||||
|
'n8n-nodes-base.formTrigger': {
|
||||||
|
type: new FormTrigger(),
|
||||||
|
sourcePath: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await utils.initNodeTypes(nodes);
|
||||||
|
|
||||||
const owner = await createOwner();
|
const owner = await createOwner();
|
||||||
createActiveWorkflow = async () => await createWorkflow({ active: true }, owner);
|
createActiveWorkflow = async () => await createWorkflow({ active: true }, owner);
|
||||||
@@ -136,6 +155,43 @@ describe('add()', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should count workflow triggers correctly when node has multiple webhooks', async () => {
|
||||||
|
const workflowRepositoryInstance = Container.get(WorkflowRepository);
|
||||||
|
const updateWorkflowTriggerCountSpy = jest.spyOn(
|
||||||
|
workflowRepositoryInstance,
|
||||||
|
'updateWorkflowTriggerCount',
|
||||||
|
);
|
||||||
|
await activeWorkflowManager.init();
|
||||||
|
|
||||||
|
// Mock all of the webhooks
|
||||||
|
const mockWebhooks: IWebhookData[] = [
|
||||||
|
mock<IWebhookData>({ node: 'Form Trigger', httpMethod: 'GET', path: '/webhook-path' }),
|
||||||
|
mock<IWebhookData>({ node: 'Form Trigger', httpMethod: 'POST', path: '/webhook-path' }),
|
||||||
|
];
|
||||||
|
webhookService.getNodeWebhooks.mockReturnValue(mockWebhooks);
|
||||||
|
webhookService.createWebhook.mockReturnValue(
|
||||||
|
mock<WebhookEntity>({ webhookPath: '/webhook-path' }),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a workflow which has a form trigger
|
||||||
|
const dbWorkflow = await createWorkflow({
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'uuid-1',
|
||||||
|
parameters: { path: 'test-webhook-path', options: {} },
|
||||||
|
name: 'Form Trigger',
|
||||||
|
type: 'n8n-nodes-base.formTrigger',
|
||||||
|
typeVersion: 1,
|
||||||
|
position: [500, 300],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await activeWorkflowManager.add(dbWorkflow.id, 'activate');
|
||||||
|
|
||||||
|
expect(updateWorkflowTriggerCountSpy).toHaveBeenCalledWith(dbWorkflow.id, 1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('removeAll()', () => {
|
describe('removeAll()', () => {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
import { Ftp } from 'n8n-nodes-base/credentials/Ftp.credentials';
|
import { Ftp } from 'n8n-nodes-base/credentials/Ftp.credentials';
|
||||||
import { GithubApi } from 'n8n-nodes-base/credentials/GithubApi.credentials';
|
import { GithubApi } from 'n8n-nodes-base/credentials/GithubApi.credentials';
|
||||||
import { Cron } from 'n8n-nodes-base/nodes/Cron/Cron.node';
|
import { Cron } from 'n8n-nodes-base/nodes/Cron/Cron.node';
|
||||||
|
import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node';
|
||||||
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
|
import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node';
|
||||||
import { Set } from 'n8n-nodes-base/nodes/Set/Set.node';
|
import { Set } from 'n8n-nodes-base/nodes/Set/Set.node';
|
||||||
import { Start } from 'n8n-nodes-base/nodes/Start/Start.node';
|
import { Start } from 'n8n-nodes-base/nodes/Start/Start.node';
|
||||||
@@ -67,9 +68,8 @@ export async function initCredentialsTypes(): Promise<void> {
|
|||||||
/**
|
/**
|
||||||
* Initialize node types.
|
* Initialize node types.
|
||||||
*/
|
*/
|
||||||
export async function initNodeTypes() {
|
export async function initNodeTypes(customNodes?: INodeTypeData) {
|
||||||
ScheduleTrigger.prototype.trigger = async () => ({});
|
const defaultNodes: INodeTypeData = {
|
||||||
const nodes: INodeTypeData = {
|
|
||||||
'n8n-nodes-base.start': {
|
'n8n-nodes-base.start': {
|
||||||
type: new Start(),
|
type: new Start(),
|
||||||
sourcePath: '',
|
sourcePath: '',
|
||||||
@@ -86,7 +86,14 @@ export async function initNodeTypes() {
|
|||||||
type: new ScheduleTrigger(),
|
type: new ScheduleTrigger(),
|
||||||
sourcePath: '',
|
sourcePath: '',
|
||||||
},
|
},
|
||||||
|
'n8n-nodes-base.formTrigger': {
|
||||||
|
type: new FormTrigger(),
|
||||||
|
sourcePath: '',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ScheduleTrigger.prototype.trigger = async () => ({});
|
||||||
|
const nodes = customNodes ?? defaultNodes;
|
||||||
const loader = mock<DirectoryLoader>();
|
const loader = mock<DirectoryLoader>();
|
||||||
loader.getNode.mockImplementation((nodeType) => {
|
loader.getNode.mockImplementation((nodeType) => {
|
||||||
const node = nodes[`n8n-nodes-base.${nodeType}`];
|
const node = nodes[`n8n-nodes-base.${nodeType}`];
|
||||||
|
|||||||
@@ -47,14 +47,6 @@ function getNodeVersion(Trigger: new () => VersionedNodeType, version?: number)
|
|||||||
return instance.nodeVersions[version ?? instance.currentVersion];
|
return instance.nodeVersions[version ?? instance.currentVersion];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function testVersionedTriggerNode(
|
|
||||||
Trigger: new () => VersionedNodeType,
|
|
||||||
version?: number,
|
|
||||||
options: TestTriggerNodeOptions = {},
|
|
||||||
) {
|
|
||||||
return await testTriggerNode(getNodeVersion(Trigger, version), options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function testTriggerNode(
|
export async function testTriggerNode(
|
||||||
Trigger: (new () => INodeType) | INodeType,
|
Trigger: (new () => INodeType) | INodeType,
|
||||||
options: TestTriggerNodeOptions = {},
|
options: TestTriggerNodeOptions = {},
|
||||||
|
|||||||
Reference in New Issue
Block a user