diff --git a/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts b/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts index cdd7a9064d..2a98bfcdc6 100644 --- a/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts +++ b/packages/nodes-base/nodes/Pipedrive/PipedriveTrigger.node.ts @@ -29,14 +29,60 @@ function authorizationError(resp: Response, realm: string, responseCode: number, noWebhookResponse: true, }; } - +const entityOptions = [ + { + name: 'Activity', + value: 'activity', + }, + { + name: 'Activity Type', + value: 'activityType', + }, + { + name: 'All', + value: '*', + }, + { + name: 'Deal', + value: 'deal', + }, + { + name: 'Note', + value: 'note', + }, + { + name: 'Organization', + value: 'organization', + }, + { + name: 'Person', + value: 'person', + }, + { + name: 'Pipeline', + value: 'pipeline', + }, + { + name: 'Product', + value: 'product', + }, + { + name: 'Stage', + value: 'stage', + }, + { + name: 'User', + value: 'user', + }, +]; export class PipedriveTrigger implements INodeType { description: INodeTypeDescription = { displayName: 'Pipedrive Trigger', name: 'pipedriveTrigger', icon: 'file:pipedrive.svg', group: ['trigger'], - version: 1, + version: [1, 1.1], + defaultVersion: 1.1, description: 'Starts the workflow when Pipedrive events occur', defaults: { name: 'Pipedrive Trigger', @@ -118,6 +164,11 @@ export class PipedriveTrigger implements INodeType { displayName: 'Action', name: 'action', type: 'options', + displayOptions: { + hide: { + '@version': [{ _cnd: { gte: 1.1 } }], + }, + }, options: [ { name: 'Added', @@ -154,57 +205,68 @@ export class PipedriveTrigger implements INodeType { description: 'Type of action to receive notifications about', }, { - displayName: 'Object', - name: 'object', + displayName: 'Action', + name: 'action', type: 'options', + displayOptions: { + hide: { + '@version': [{ _cnd: { lte: 1 } }], + }, + }, options: [ - { - name: 'Activity', - value: 'activity', - }, - { - name: 'Activity Type', - value: 'activityType', - }, { name: 'All', value: '*', + description: 'Any change', + action: 'Any change', }, { - name: 'Deal', - value: 'deal', + name: 'Create', + value: 'create', + description: 'Data got added', + action: 'Data was added', }, { - name: 'Note', - value: 'note', + name: 'Delete', + value: 'delete', + description: 'Data got deleted', + action: 'Data was deleted', }, { - name: 'Organization', - value: 'organization', - }, - { - name: 'Person', - value: 'person', - }, - { - name: 'Pipeline', - value: 'pipeline', - }, - { - name: 'Product', - value: 'product', - }, - { - name: 'Stage', - value: 'stage', - }, - { - name: 'User', - value: 'user', + name: 'Change', + value: 'change', + description: 'Data got changed', + action: 'Data was changed', }, ], default: '*', + description: 'Type of action to receive notifications about', + }, + { + displayName: 'Entity', + name: 'entity', + type: 'options', + options: entityOptions, + default: '*', description: 'Type of object to receive notifications about', + displayOptions: { + hide: { + '@version': [{ _cnd: { lte: 1 } }], + }, + }, + }, + { + displayName: 'Object', + name: 'object', + type: 'options', + options: entityOptions, + default: '*', + description: 'Type of object to receive notifications about', + displayOptions: { + hide: { + '@version': [{ _cnd: { gte: 1.1 } }], + }, + }, }, ], }; @@ -218,7 +280,9 @@ export class PipedriveTrigger implements INodeType { const eventAction = this.getNodeParameter('action') as string; - const eventObject = this.getNodeParameter('object') as string; + const nodeVersion = this.getNode().typeVersion; + const entityParamName = nodeVersion === 1 ? 'object' : 'entity'; + const eventObject = this.getNodeParameter(entityParamName) as string; // Webhook got created before so check if it still exists const endpoint = '/webhooks'; @@ -247,7 +311,9 @@ export class PipedriveTrigger implements INodeType { const webhookUrl = this.getNodeWebhookUrl('default'); const incomingAuthentication = this.getNodeParameter('incomingAuthentication', 0) as string; const eventAction = this.getNodeParameter('action') as string; - const eventObject = this.getNodeParameter('object') as string; + const nodeVersion = this.getNode().typeVersion; + const entityParamName = nodeVersion === 1 ? 'object' : 'entity'; + const eventObject = this.getNodeParameter(entityParamName) as string; const endpoint = '/webhooks'; diff --git a/packages/nodes-base/nodes/Pipedrive/test/PipedriveTrigger.node.test.ts b/packages/nodes-base/nodes/Pipedrive/test/PipedriveTrigger.node.test.ts new file mode 100644 index 0000000000..d6531433f1 --- /dev/null +++ b/packages/nodes-base/nodes/Pipedrive/test/PipedriveTrigger.node.test.ts @@ -0,0 +1,109 @@ +import type { IHookFunctions } from 'n8n-workflow'; + +import { pipedriveApiRequest } from '../GenericFunctions'; +import { PipedriveTrigger } from '../PipedriveTrigger.node'; + +jest.mock('basic-auth'); +jest.mock('../GenericFunctions'); + +describe('PipedriveTrigger', () => { + let pipedriveTrigger: PipedriveTrigger; + let mockHookFunctions: jest.Mocked; + + beforeEach(() => { + pipedriveTrigger = new PipedriveTrigger(); + + mockHookFunctions = { + getNodeWebhookUrl: jest.fn(), + getWorkflowStaticData: jest.fn(), + getNodeParameter: jest.fn(), + getNode: jest.fn().mockReturnValue({ typeVersion: 1.1 }), + } as unknown as jest.Mocked; + }); + + describe('Webhook Methods', () => { + describe('checkExists', () => { + it('should return true if webhook already exists', async () => { + mockHookFunctions.getNodeWebhookUrl.mockReturnValue('http://test-webhook-url'); + mockHookFunctions.getWorkflowStaticData.mockReturnValue({}); + mockHookFunctions.getNodeParameter.mockImplementation((param) => { + if (param === 'action') return '*'; + if (param === 'entity') return 'deal'; + return null; + }); + + (pipedriveApiRequest as jest.Mock).mockResolvedValue({ + data: [ + { + id: '123', + subscription_url: 'http://test-webhook-url', + event_action: '*', + event_object: 'deal', + }, + ], + }); + + const result = + await pipedriveTrigger.webhookMethods.default.checkExists.call(mockHookFunctions); + + expect(result).toBe(true); + expect(mockHookFunctions.getWorkflowStaticData('node').webhookId).toBe('123'); + }); + + it('should return false if no matching webhook exists', async () => { + mockHookFunctions.getNodeWebhookUrl.mockReturnValue('http://test-webhook-url'); + mockHookFunctions.getWorkflowStaticData.mockReturnValue({}); + mockHookFunctions.getNodeParameter.mockImplementation((param) => { + if (param === 'action') return '*'; + if (param === 'entity') return 'deal'; + return null; + }); + + (pipedriveApiRequest as jest.Mock).mockResolvedValue({ + data: [ + { + subscription_url: 'http://different-url', + event_action: 'create', + event_object: 'person', + }, + ], + }); + + const result = + await pipedriveTrigger.webhookMethods.default.checkExists.call(mockHookFunctions); + + expect(result).toBe(false); + }); + }); + + describe('create', () => { + it('should create a webhook successfully', async () => { + mockHookFunctions.getNodeWebhookUrl.mockReturnValue('http://test-webhook-url'); + mockHookFunctions.getNodeParameter.mockImplementation((param) => { + if (param === 'incomingAuthentication') return 'none'; + if (param === 'action') return '*'; + if (param === 'entity') return 'deal'; + return null; + }); + mockHookFunctions.getWorkflowStaticData.mockReturnValue({}); + + (pipedriveApiRequest as jest.Mock).mockResolvedValue({ + data: { id: '123' }, + }); + + const result = await pipedriveTrigger.webhookMethods.default.create.call(mockHookFunctions); + + expect(result).toBe(true); + expect(pipedriveApiRequest).toHaveBeenCalledWith( + 'POST', + '/webhooks', + expect.objectContaining({ + event_action: '*', + event_object: 'deal', + subscription_url: 'http://test-webhook-url', + }), + ); + }); + }); + }); +});