mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
fix(Pipedrive Trigger Node): Add support for webhooks v2 (#14220)
Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
@@ -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';
|
||||
|
||||
|
||||
@@ -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<IHookFunctions>;
|
||||
|
||||
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<IHookFunctions>;
|
||||
});
|
||||
|
||||
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',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user