mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +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,
|
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 {
|
export class PipedriveTrigger implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Pipedrive Trigger',
|
displayName: 'Pipedrive Trigger',
|
||||||
name: 'pipedriveTrigger',
|
name: 'pipedriveTrigger',
|
||||||
icon: 'file:pipedrive.svg',
|
icon: 'file:pipedrive.svg',
|
||||||
group: ['trigger'],
|
group: ['trigger'],
|
||||||
version: 1,
|
version: [1, 1.1],
|
||||||
|
defaultVersion: 1.1,
|
||||||
description: 'Starts the workflow when Pipedrive events occur',
|
description: 'Starts the workflow when Pipedrive events occur',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: 'Pipedrive Trigger',
|
name: 'Pipedrive Trigger',
|
||||||
@@ -118,6 +164,11 @@ export class PipedriveTrigger implements INodeType {
|
|||||||
displayName: 'Action',
|
displayName: 'Action',
|
||||||
name: 'action',
|
name: 'action',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
hide: {
|
||||||
|
'@version': [{ _cnd: { gte: 1.1 } }],
|
||||||
|
},
|
||||||
|
},
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Added',
|
name: 'Added',
|
||||||
@@ -154,57 +205,68 @@ export class PipedriveTrigger implements INodeType {
|
|||||||
description: 'Type of action to receive notifications about',
|
description: 'Type of action to receive notifications about',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Object',
|
displayName: 'Action',
|
||||||
name: 'object',
|
name: 'action',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
hide: {
|
||||||
|
'@version': [{ _cnd: { lte: 1 } }],
|
||||||
|
},
|
||||||
|
},
|
||||||
options: [
|
options: [
|
||||||
{
|
|
||||||
name: 'Activity',
|
|
||||||
value: 'activity',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Activity Type',
|
|
||||||
value: 'activityType',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'All',
|
name: 'All',
|
||||||
value: '*',
|
value: '*',
|
||||||
|
description: 'Any change',
|
||||||
|
action: 'Any change',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Deal',
|
name: 'Create',
|
||||||
value: 'deal',
|
value: 'create',
|
||||||
|
description: 'Data got added',
|
||||||
|
action: 'Data was added',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Note',
|
name: 'Delete',
|
||||||
value: 'note',
|
value: 'delete',
|
||||||
|
description: 'Data got deleted',
|
||||||
|
action: 'Data was deleted',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Organization',
|
name: 'Change',
|
||||||
value: 'organization',
|
value: 'change',
|
||||||
},
|
description: 'Data got changed',
|
||||||
{
|
action: 'Data was changed',
|
||||||
name: 'Person',
|
|
||||||
value: 'person',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Pipeline',
|
|
||||||
value: 'pipeline',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Product',
|
|
||||||
value: 'product',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Stage',
|
|
||||||
value: 'stage',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'User',
|
|
||||||
value: 'user',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default: '*',
|
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',
|
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 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
|
// Webhook got created before so check if it still exists
|
||||||
const endpoint = '/webhooks';
|
const endpoint = '/webhooks';
|
||||||
@@ -247,7 +311,9 @@ export class PipedriveTrigger implements INodeType {
|
|||||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||||
const incomingAuthentication = this.getNodeParameter('incomingAuthentication', 0) as string;
|
const incomingAuthentication = this.getNodeParameter('incomingAuthentication', 0) as string;
|
||||||
const eventAction = this.getNodeParameter('action') 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';
|
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