Add Activepieces integration for workflow automation
- Add Activepieces fork with SmoothSchedule custom piece - Create integrations app with Activepieces service layer - Add embed token endpoint for iframe integration - Create Automations page with embedded workflow builder - Add sidebar visibility fix for embed mode - Add list inactive customers endpoint to Public API - Include SmoothSchedule triggers: event created/updated/cancelled - Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import { pipedriveAuth } from '../../index';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { filterIdProp } from '../common/props';
|
||||
import { pipedriveApiCall, pipedrivePaginatedV2ApiCall } from '../common';
|
||||
import { LeadListResponse } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
|
||||
export const activityMatchingFilterTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'activity-matching-filter',
|
||||
displayName: 'Activity Matching Filter',
|
||||
description: 'Trigges when an activity newly matches a Pipedrive filter for the first time.',
|
||||
type: TriggerStrategy.POLLING,
|
||||
props: {
|
||||
filterId: filterIdProp('activity', true),
|
||||
},
|
||||
async onEnable(context) {
|
||||
const ids: number[] = [];
|
||||
|
||||
const response = await pipedrivePaginatedV2ApiCall<{ id: number }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/activities',
|
||||
query: { filter_id: context.propsValue.filterId },
|
||||
});
|
||||
|
||||
if (!isNil(response)) {
|
||||
response.forEach((activity) => {
|
||||
ids.push(activity.id);
|
||||
});
|
||||
}
|
||||
|
||||
await context.store.put('activities', JSON.stringify(ids));
|
||||
},
|
||||
async onDisable(context) {
|
||||
await context.store.delete('activities');
|
||||
},
|
||||
async test(context) {
|
||||
const activities = [];
|
||||
|
||||
const response = await pipedriveApiCall<LeadListResponse>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/activities',
|
||||
query: {
|
||||
limit: 10,
|
||||
filter_id: context.propsValue.filterId,
|
||||
sort_by:'update_time',
|
||||
sort_direction:'desc',
|
||||
include_fields:'attendees'
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const activity of response.data) {
|
||||
activities.push(activity);
|
||||
}
|
||||
|
||||
return activities;
|
||||
},
|
||||
async run(context) {
|
||||
const existingIds = (await context.store.get<string>('activities')) ?? '[]';
|
||||
const parsedExistingIds = JSON.parse(existingIds) as number[];
|
||||
|
||||
const response = await pipedrivePaginatedV2ApiCall<{ id: number }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/activities',
|
||||
query: { filter_id: context.propsValue.filterId,include_fields:'attendees'},
|
||||
});
|
||||
|
||||
if (isNil(response) || response.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Filter valid activities
|
||||
const newActivities = response.filter((activity) => !parsedExistingIds.includes(activity.id));
|
||||
|
||||
const newIds = newActivities.map((activity) => activity.id);
|
||||
|
||||
if (newIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Store new IDs
|
||||
await context.store.put('activities', JSON.stringify([...newIds, ...parsedExistingIds]));
|
||||
|
||||
return newActivities;
|
||||
},
|
||||
sampleData: {
|
||||
id: 8,
|
||||
company_id: 22122,
|
||||
user_id: 1234,
|
||||
done: false,
|
||||
type: 'deadline',
|
||||
reference_type: 'scheduler-service',
|
||||
reference_id: 7,
|
||||
conference_meeting_client: '871b8bc88d3a1202',
|
||||
conference_meeting_url: 'https://pipedrive.zoom.us/link',
|
||||
conference_meeting_id: '01758746701',
|
||||
due_date: '2020-06-09',
|
||||
due_time: '10:00',
|
||||
duration: '01:00',
|
||||
busy_flag: true,
|
||||
add_time: '2020-06-08 12:37:56',
|
||||
marked_as_done_time: '2020-08-08 08:08:38',
|
||||
last_notification_time: '2020-08-08 12:37:56',
|
||||
last_notification_user_id: 7655,
|
||||
notification_language_id: 1,
|
||||
subject: 'Deadline',
|
||||
public_description: 'This is a description',
|
||||
calendar_sync_include_context: '',
|
||||
location: 'Mustamäe tee 3, Tallinn, Estonia',
|
||||
org_id: 5,
|
||||
person_id: 1101,
|
||||
deal_id: 300,
|
||||
lead_id: '46c3b0e1-db35-59ca-1828-4817378dff71',
|
||||
active_flag: true,
|
||||
update_time: '2020-08-08 12:37:56',
|
||||
update_user_id: 5596,
|
||||
gcal_event_id: '',
|
||||
google_calendar_id: '',
|
||||
google_calendar_etag: '',
|
||||
source_timezone: '',
|
||||
rec_rule: 'RRULE:FREQ=WEEKLY;BYDAY=WE',
|
||||
rec_rule_extension: '',
|
||||
rec_master_activity_id: 1,
|
||||
series: [],
|
||||
note: 'A note for the activity',
|
||||
created_by_user_id: 1234,
|
||||
location_subpremise: '',
|
||||
location_street_number: '3',
|
||||
location_route: 'Mustamäe tee',
|
||||
location_sublocality: 'Kristiine',
|
||||
location_locality: 'Tallinn',
|
||||
location_admin_area_level_1: 'Harju maakond',
|
||||
location_admin_area_level_2: '',
|
||||
location_country: 'Estonia',
|
||||
location_postal_code: '10616',
|
||||
location_formatted_address: 'Mustamäe tee 3, 10616 Tallinn, Estonia',
|
||||
attendees: [
|
||||
{
|
||||
email_address: 'attendee@pipedrivemail.com',
|
||||
is_organizer: 0,
|
||||
name: 'Attendee',
|
||||
person_id: 25312,
|
||||
status: 'noreply',
|
||||
user_id: null,
|
||||
},
|
||||
],
|
||||
participants: [
|
||||
{
|
||||
person_id: 17985,
|
||||
primary_flag: false,
|
||||
},
|
||||
{
|
||||
person_id: 1101,
|
||||
primary_flag: true,
|
||||
},
|
||||
],
|
||||
org_name: 'Organization',
|
||||
person_name: 'Person',
|
||||
deal_title: 'Deal',
|
||||
owner_name: 'Creator',
|
||||
person_dropbox_bcc: 'company@pipedrivemail.com',
|
||||
deal_dropbox_bcc: 'company+deal300@pipedrivemail.com',
|
||||
assigned_to_user_id: 1235,
|
||||
file: {
|
||||
id: '376892,',
|
||||
clean_name: 'Audio 10:55:07.m4a',
|
||||
url: 'https://pipedrive-files.s3-eu-west-1.amazonaws.com/Audio-recording.m4a',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,257 @@
|
||||
import { pipedriveAuth } from '../../index';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { createTrigger, Property, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { filterIdProp } from '../common/props';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedrivePaginatedV2ApiCall,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { GetField, LeadListResponse } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { DEAL_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
export const dealMatchingFilterTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'deal-matching-filter',
|
||||
displayName: 'Deal Matching Filter',
|
||||
description: 'Trigges when a deal newly matches a Pipedrive filter for the first time.',
|
||||
type: TriggerStrategy.POLLING,
|
||||
props: {
|
||||
filterId: filterIdProp('deals', true),
|
||||
status: Property.StaticDropdown({
|
||||
displayName: 'Status',
|
||||
required: false,
|
||||
defaultValue: 'all_not_deleted',
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{
|
||||
label: 'Open',
|
||||
value: 'open',
|
||||
},
|
||||
{
|
||||
label: 'Won',
|
||||
value: 'won',
|
||||
},
|
||||
{
|
||||
label: 'Lost',
|
||||
value: 'lost',
|
||||
},
|
||||
{
|
||||
label: 'Deleted',
|
||||
value: 'deleted',
|
||||
},
|
||||
{
|
||||
label: 'All(Not Deleted)',
|
||||
value: 'all_not_deleted',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
async onEnable(context) {
|
||||
const ids: number[] = [];
|
||||
|
||||
const response = await pipedrivePaginatedV2ApiCall<{ id: number }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/deals',
|
||||
query: {
|
||||
filter_id: context.propsValue.filterId,
|
||||
status: context.propsValue.status,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isNil(response)) {
|
||||
response.forEach((deal) => {
|
||||
ids.push(deal.id);
|
||||
});
|
||||
}
|
||||
|
||||
await context.store.put('deals', JSON.stringify(ids));
|
||||
},
|
||||
async onDisable(context) {
|
||||
await context.store.delete('deals');
|
||||
},
|
||||
async test(context) {
|
||||
const deals = [];
|
||||
|
||||
const response = await pipedriveApiCall<LeadListResponse>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/deals',
|
||||
query: {
|
||||
limit: 10,
|
||||
filter_id: context.propsValue.filterId,
|
||||
status: context.propsValue.status,
|
||||
include_fields: DEAL_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const deal of response.data) {
|
||||
deals.push(deal);
|
||||
}
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const deal of deals) {
|
||||
const updatedDealProperties = pipedriveTransformCustomFields(customFieldsResponse, deal);
|
||||
result.push(updatedDealProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const existingIds = (await context.store.get<string>('deals')) ?? '[]';
|
||||
const parsedExistingIds = JSON.parse(existingIds) as number[];
|
||||
|
||||
const response = await pipedrivePaginatedV2ApiCall<{ id: number }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/deals',
|
||||
query: {
|
||||
filter_id: context.propsValue.filterId,
|
||||
status: context.propsValue.status,
|
||||
include_fields: DEAL_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response) || response.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Filter valid deals
|
||||
const newDeals = response.filter((deal) => !parsedExistingIds.includes(deal.id));
|
||||
|
||||
const newIds = newDeals.map((deal) => deal.id);
|
||||
|
||||
if (newIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Store new IDs
|
||||
await context.store.put('deals', JSON.stringify([...newIds, ...parsedExistingIds]));
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
// Transform valid deal fields
|
||||
for (const deal of newDeals) {
|
||||
const updatedDealProperties = pipedriveTransformCustomFields(customFieldsResponse, deal);
|
||||
result.push(updatedDealProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
creator_user_id:22701301,
|
||||
person_id: 1101,
|
||||
org_id: 5,
|
||||
stage_id: 2,
|
||||
title: 'Deal One',
|
||||
value: 5000,
|
||||
currency: 'EUR',
|
||||
add_time: '2019-05-29 04:21:51',
|
||||
update_time: '2019-11-28 16:19:50',
|
||||
stage_change_time: '2019-11-28 15:41:22',
|
||||
active: true,
|
||||
deleted: false,
|
||||
status: 'open',
|
||||
probability: null,
|
||||
next_activity_date: '2019-11-29',
|
||||
next_activity_time: '11:30:00',
|
||||
next_activity_id: 128,
|
||||
last_activity_id: null,
|
||||
last_activity_date: null,
|
||||
lost_reason: null,
|
||||
visible_to: '1',
|
||||
close_time: null,
|
||||
pipeline_id: 1,
|
||||
won_time: '2019-11-27 11:40:36',
|
||||
first_won_time: '2019-11-27 11:40:36',
|
||||
lost_time: '',
|
||||
products_count: 0,
|
||||
files_count: 0,
|
||||
notes_count: 2,
|
||||
followers_count: 0,
|
||||
email_messages_count: 4,
|
||||
activities_count: 1,
|
||||
done_activities_count: 0,
|
||||
undone_activities_count: 1,
|
||||
participants_count: 1,
|
||||
expected_close_date: '2019-06-29',
|
||||
last_incoming_mail_time: '2019-05-29 18:21:42',
|
||||
last_outgoing_mail_time: '2019-05-30 03:45:35',
|
||||
label: 11,
|
||||
stage_order_nr: 2,
|
||||
person_name: 'Person',
|
||||
org_name: 'Organization',
|
||||
next_activity_subject: 'Call',
|
||||
next_activity_type: 'call',
|
||||
next_activity_duration: '00:30:00',
|
||||
next_activity_note: 'Note content',
|
||||
formatted_value: '€5,000',
|
||||
weighted_value: 5000,
|
||||
formatted_weighted_value: '€5,000',
|
||||
weighted_value_currency: 'EUR',
|
||||
rotten_time: null,
|
||||
owner_name: 'Creator',
|
||||
cc_email: 'company+deal1@pipedrivemail.com',
|
||||
org_hidden: false,
|
||||
person_hidden: false,
|
||||
average_time_to_won: {
|
||||
y: 0,
|
||||
m: 0,
|
||||
d: 0,
|
||||
h: 0,
|
||||
i: 20,
|
||||
s: 49,
|
||||
total_seconds: 1249,
|
||||
},
|
||||
average_stage_progress: 4.99,
|
||||
age: {
|
||||
y: 0,
|
||||
m: 6,
|
||||
d: 14,
|
||||
h: 8,
|
||||
i: 57,
|
||||
s: 26,
|
||||
total_seconds: 17139446,
|
||||
},
|
||||
stay_in_pipeline_stages: {
|
||||
times_in_stages: {
|
||||
'1': 15721267,
|
||||
'2': 1288449,
|
||||
'3': 4368,
|
||||
'4': 3315,
|
||||
'5': 26460,
|
||||
},
|
||||
order_of_stages: [1, 2, 3, 4, 5],
|
||||
},
|
||||
last_activity: null,
|
||||
next_activity: null,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,204 @@
|
||||
import { createTrigger } from '@activepieces/pieces-framework';
|
||||
import { TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { pipedriveApiCall, pipedriveCommon } from '../common';
|
||||
import { pipedriveAuth } from '../..';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { LeadListResponse } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
|
||||
interface PipedriveActivityV2 {
|
||||
id: number;
|
||||
subject: string;
|
||||
owner_id: number;
|
||||
type: string;
|
||||
is_deleted: boolean;
|
||||
done: boolean;
|
||||
conference_meeting_client: string | null;
|
||||
conference_meeting_url: string | null;
|
||||
conference_meeting_id: string | null;
|
||||
due_date: string;
|
||||
due_time: string;
|
||||
duration: string;
|
||||
busy: boolean;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
marked_as_done_time: string | null;
|
||||
public_description: string | null;
|
||||
location: {
|
||||
value: string | null;
|
||||
street_number: string | null;
|
||||
route: string | null;
|
||||
sublocality: string | null;
|
||||
locality: string | null;
|
||||
admin_area_level_1: string | null;
|
||||
admin_area_level_2: string | null;
|
||||
country: string | null;
|
||||
postal_code: string | null;
|
||||
formatted_address: string | null;
|
||||
} | null;
|
||||
org_id: number | null;
|
||||
person_id: number | null;
|
||||
deal_id: number | null;
|
||||
lead_id: string | null;
|
||||
project_id: number | null;
|
||||
private: boolean;
|
||||
priority: number;
|
||||
note: string | null;
|
||||
creator_user_id: number;
|
||||
attendees?: {
|
||||
email_address: string;
|
||||
name: string;
|
||||
status: string;
|
||||
is_organizer: number;
|
||||
person_id: number | null;
|
||||
user_id: number | null;
|
||||
}[];
|
||||
participants?: {
|
||||
person_id: number;
|
||||
primary: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface ListActivitiesResponse {
|
||||
data: PipedriveActivityV2[];
|
||||
}
|
||||
|
||||
export const newActivity = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'new_activity',
|
||||
displayName: 'New Activity',
|
||||
description: 'Triggers when a new activity is added',
|
||||
props: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const webhook = await pipedriveCommon.subscribeWebhook(
|
||||
'activity',
|
||||
'create',
|
||||
context.webhookUrl!,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
await context.store?.put<WebhookInformation>('_new_activity_trigger', {
|
||||
webhookId: webhook.data.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const response = await context.store?.get<WebhookInformation>('_new_activity_trigger');
|
||||
if (response !== null && response !== undefined) {
|
||||
await pipedriveCommon.unsubscribeWebhook(
|
||||
response.webhookId,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const activities = [];
|
||||
|
||||
const response = await pipedriveApiCall<LeadListResponse>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/activities',
|
||||
query: {
|
||||
limit: 10,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
include_fields: 'attendees',
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const activity of response.data) {
|
||||
activities.push(activity);
|
||||
}
|
||||
|
||||
return activities;
|
||||
},
|
||||
async run(context) {
|
||||
const payloadBody = context.payload.body as PayloadBody;
|
||||
|
||||
const response = await pipedriveApiCall<{ data: PipedriveActivityV2 }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/activities/${payloadBody.data.id}`,
|
||||
query: {
|
||||
include_fields: 'attendees',
|
||||
},
|
||||
});
|
||||
|
||||
return [response.data];
|
||||
},
|
||||
sampleData: {
|
||||
id: 8,
|
||||
owner_id: 1234,
|
||||
done: false,
|
||||
type: 'deadline',
|
||||
due_date: '2020-06-09',
|
||||
due_time: '10:00',
|
||||
duration: '01:00',
|
||||
busy: true,
|
||||
add_time: '2020-06-08T12:37:56Z',
|
||||
marked_as_done_time: '2020-08-08T08:08:38Z',
|
||||
subject: 'Deadline',
|
||||
public_description: 'This is a description',
|
||||
location: {
|
||||
// Nested object
|
||||
value: 'Mustamäe tee 3, Tallinn, Estonia',
|
||||
street_number: '3',
|
||||
route: 'Mustamäe tee',
|
||||
sublocality: 'Kristiine',
|
||||
locality: 'Tallinn',
|
||||
admin_area_level_1: 'Harju maakond',
|
||||
admin_area_level_2: null,
|
||||
country: 'Estonia',
|
||||
postal_code: '10616',
|
||||
formatted_address: 'Mustamäe tee 3, 10616 Tallinn, Estonia',
|
||||
},
|
||||
org_id: 5,
|
||||
person_id: 1101,
|
||||
deal_id: 300,
|
||||
lead_id: '46c3b0e1-db35-59ca-1828-4817378dff71',
|
||||
is_deleted: false,
|
||||
update_time: '2020-08-08T12:37:56Z',
|
||||
note: 'A note for the activity',
|
||||
creator_user_id: 1234,
|
||||
attendees: [
|
||||
{
|
||||
email_address: 'attendee@pipedrivemail.com',
|
||||
is_organizer: 0,
|
||||
name: 'Attendee',
|
||||
person_id: 25312,
|
||||
status: 'noreply',
|
||||
user_id: null,
|
||||
},
|
||||
],
|
||||
participants: [
|
||||
{
|
||||
person_id: 17985,
|
||||
primary: false,
|
||||
},
|
||||
{
|
||||
person_id: 1101,
|
||||
primary: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
interface WebhookInformation {
|
||||
webhookId: string;
|
||||
}
|
||||
|
||||
type PayloadBody = {
|
||||
data: PipedriveActivityV2;
|
||||
previous: PipedriveActivityV2;
|
||||
meta: {
|
||||
action: string;
|
||||
entity: string;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,220 @@
|
||||
import { createTrigger } from '@activepieces/pieces-framework';
|
||||
import { TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedriveCommon,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { pipedriveAuth } from '../..';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { GetField } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { DEAL_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedriveDealV2 {
|
||||
id: number;
|
||||
title: string;
|
||||
creator_user_id: number;
|
||||
owner_id: number;
|
||||
person_id: number | null;
|
||||
org_id: number | null;
|
||||
stage_id: number;
|
||||
pipeline_id: number;
|
||||
value: number;
|
||||
currency: string;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
stage_change_time: string;
|
||||
is_deleted: boolean;
|
||||
status: 'open' | 'won' | 'lost';
|
||||
probability: number | null;
|
||||
lost_reason: string | null;
|
||||
visible_to: number;
|
||||
close_time: string | null;
|
||||
won_time: string | null;
|
||||
first_won_time?: string;
|
||||
lost_time: string | null;
|
||||
products_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
participants_count?: number;
|
||||
expected_close_date: string | null;
|
||||
last_incoming_mail_time?: string;
|
||||
last_outgoing_mail_time?: string;
|
||||
label_ids: number[];
|
||||
rotten_time: string | null;
|
||||
smart_bcc_email?: string;
|
||||
acv?: number;
|
||||
arr?: number;
|
||||
mrr?: number;
|
||||
custom_fields: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ListDealsResponseV2 {
|
||||
data: PipedriveDealV2[];
|
||||
additional_data?: {
|
||||
next_cursor?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetDealResponseV2 {
|
||||
data: PipedriveDealV2;
|
||||
}
|
||||
|
||||
export const newDeal = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'new_deal',
|
||||
displayName: 'New Deal',
|
||||
description: 'Triggers when a new deal is created.',
|
||||
props: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const webhook = await pipedriveCommon.subscribeWebhook(
|
||||
'deal',
|
||||
'create',
|
||||
context.webhookUrl!,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
await context.store?.put<WebhookInformation>('_new_deal_trigger', {
|
||||
webhookId: webhook.data.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const response = await context.store?.get<WebhookInformation>('_new_deal_trigger');
|
||||
if (response !== null && response !== undefined) {
|
||||
await pipedriveCommon.unsubscribeWebhook(
|
||||
response.webhookId,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const dealsResponse = await pipedriveApiCall<ListDealsResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/deals',
|
||||
query: {
|
||||
limit: 5,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
include_fields: DEAL_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
if (isNil(dealsResponse.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const deal of dealsResponse.data) {
|
||||
const updatedDealProperties = pipedriveTransformCustomFields(customFieldsResponse, deal);
|
||||
result.push(updatedDealProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const payloadBody = context.payload.body as PayloadBody;
|
||||
|
||||
const dealResponse = await pipedriveApiCall<GetDealResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/deals/${payloadBody.data.id}`,
|
||||
query: {
|
||||
include_fields: DEAL_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const updatedDealProperties = pipedriveTransformCustomFields(
|
||||
customFieldsResponse,
|
||||
dealResponse.data,
|
||||
);
|
||||
|
||||
return [updatedDealProperties];
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
creator_user_id: 8877,
|
||||
owner_id: 8877,
|
||||
person_id: 1101,
|
||||
org_id: 5,
|
||||
stage_id: 2,
|
||||
title: 'Deal One',
|
||||
value: 5000,
|
||||
currency: 'EUR',
|
||||
add_time: '2019-05-29T04:21:51Z',
|
||||
update_time: '2019-11-28T16:19:50Z',
|
||||
stage_change_time: '2019-11-28T15:41:22Z',
|
||||
is_deleted: false,
|
||||
status: 'open',
|
||||
probability: null,
|
||||
next_activity_id: 128,
|
||||
last_activity_id: null,
|
||||
lost_reason: null,
|
||||
visible_to: 1,
|
||||
close_time: null,
|
||||
pipeline_id: 1,
|
||||
won_time: '2019-11-27T11:40:36Z',
|
||||
first_won_time: '2019-11-27T11:40:36Z',
|
||||
lost_time: null,
|
||||
products_count: 0,
|
||||
files_count: 0,
|
||||
notes_count: 2,
|
||||
followers_count: 0,
|
||||
email_messages_count: 4,
|
||||
activities_count: 1,
|
||||
done_activities_count: 0,
|
||||
undone_activities_count: 1,
|
||||
participants_count: 1,
|
||||
expected_close_date: '2019-06-29',
|
||||
last_incoming_mail_time: '2019-05-29T18:21:42Z',
|
||||
last_outgoing_mail_time: '2019-05-30T03:45:35Z',
|
||||
label_ids: [11],
|
||||
rotten_time: null,
|
||||
smart_bcc_email: 'company+deal1@pipedrivemail.com',
|
||||
custom_fields: {
|
||||
d4de1c1518b4531717c676029a45911c340390a6: {
|
||||
value: 2300,
|
||||
currency: 'EUR',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
interface WebhookInformation {
|
||||
webhookId: string;
|
||||
}
|
||||
|
||||
type PayloadBody = {
|
||||
data: PipedriveDealV2;
|
||||
previous: PipedriveDealV2;
|
||||
meta: {
|
||||
action: string;
|
||||
entity: string;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
import { pipedriveAuth } from '../../';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { AuthenticationType, httpClient, HttpMethod } from '@activepieces/pieces-common';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
pipedriveTransformV1CustomFields,
|
||||
} from '../common';
|
||||
import { GetField } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
|
||||
interface PipedriveLeadV2 {
|
||||
id: string;
|
||||
title: string;
|
||||
owner_id: number;
|
||||
creator_id: number;
|
||||
label_ids: string[];
|
||||
value: number | null;
|
||||
expected_close_date: string | null;
|
||||
person_id: number | null;
|
||||
organization_id: number | null;
|
||||
is_archived: boolean;
|
||||
source_name: string;
|
||||
origin: string;
|
||||
origin_id: string | null;
|
||||
channel: number | null;
|
||||
channel_id: string | null;
|
||||
was_seen: boolean;
|
||||
next_activity_id: number | null;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
visible_to: number;
|
||||
custom_fields?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface LeadListResponseV2 {
|
||||
data: PipedriveLeadV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetLeadResponseV2 {
|
||||
data: PipedriveLeadV2;
|
||||
}
|
||||
|
||||
export const newLeadTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'new-lead',
|
||||
displayName: 'New Lead',
|
||||
description: 'Triggers when a new lead is created.',
|
||||
props: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await httpClient.sendRequest<{ data: { id: string } }>({
|
||||
method: HttpMethod.POST,
|
||||
url: `${context.auth.data['api_domain']}/api/v1/webhooks`,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token: context.auth.access_token,
|
||||
},
|
||||
body: {
|
||||
event_object: 'lead',
|
||||
event_action: 'create',
|
||||
subscription_url: context.webhookUrl,
|
||||
version: '2.0',
|
||||
},
|
||||
});
|
||||
|
||||
await context.store?.put<{
|
||||
webhookId: string;
|
||||
}>('_new_lead_trigger', {
|
||||
webhookId: response.body.data.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const response = await context.store?.get<{
|
||||
webhookId: string;
|
||||
}>('_new_lead_trigger');
|
||||
if (response !== null && !isNil(response.webhookId)) {
|
||||
await httpClient.sendRequest({
|
||||
method: HttpMethod.DELETE,
|
||||
url: `${context.auth.data['api_domain']}/api/v1/webhooks/${response.webhookId}`,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token: context.auth.access_token,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const response = await pipedriveApiCall<LeadListResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/leads',
|
||||
query: {
|
||||
limit: 10,
|
||||
sort: 'update_time DESC',
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const lead of response.data) {
|
||||
const updatedLeadProperties = pipedriveTransformV1CustomFields(customFieldsResponse, lead);
|
||||
result.push(updatedLeadProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const payloadBody = context.payload.body as {
|
||||
data: PipedriveLeadV2;
|
||||
previous: PipedriveLeadV2;
|
||||
meta: {
|
||||
action: string;
|
||||
entity: string;
|
||||
};
|
||||
};
|
||||
|
||||
const leadResponse = await pipedriveApiCall<GetLeadResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v1/leads/${payloadBody.data.id}`,
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const updatedLeadProperties = pipedriveTransformV1CustomFields(
|
||||
customFieldsResponse,
|
||||
leadResponse.data,
|
||||
);
|
||||
|
||||
return [updatedLeadProperties];
|
||||
},
|
||||
sampleData: {
|
||||
id: 'f3c23480-c9b1-11ef-bc83-2b8218e028ef',
|
||||
title: 'Test lead',
|
||||
owner_id: 22701301,
|
||||
creator_id: 22701301,
|
||||
label_ids: ['a0e5f330-d2a7-4181-a6e3-a44d634b7bf7', '8a0e6918-1eee-4e56-a615-c81d712a6a77'],
|
||||
value: null,
|
||||
expected_close_date: null,
|
||||
person_id: 2,
|
||||
organization_id: 1,
|
||||
is_archived: false,
|
||||
source_name: 'Manually created',
|
||||
origin: 'ManuallyCreated',
|
||||
origin_id: null,
|
||||
channel: 1,
|
||||
channel_id: null,
|
||||
was_seen: true,
|
||||
next_activity_id: null,
|
||||
add_time: '2025-01-03T09:06:00.776Z',
|
||||
update_time: '2025-01-03T09:06:00.776Z',
|
||||
visible_to: 3,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
import { pipedriveAuth } from '../../';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedriveCommon,
|
||||
} from '../common';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
|
||||
interface PipedriveNoteV2 {
|
||||
id: number;
|
||||
user_id: number;
|
||||
deal_id: number | null;
|
||||
person_id: number | null;
|
||||
org_id: number | null;
|
||||
lead_id: string | null;
|
||||
content: string;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
is_deleted: boolean;
|
||||
last_update_user_id: number | null;
|
||||
}
|
||||
|
||||
interface NoteListResponseV2 {
|
||||
data: PipedriveNoteV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetNoteResponseV2 {
|
||||
data: PipedriveNoteV2;
|
||||
}
|
||||
|
||||
export const newNoteTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'new-note',
|
||||
displayName: 'New Note',
|
||||
description: 'Triggers when a new note is created.',
|
||||
props: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const webhook = await pipedriveCommon.subscribeWebhook(
|
||||
'note',
|
||||
'create',
|
||||
context.webhookUrl!,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
await context.store?.put<{
|
||||
webhookId: string;
|
||||
}>('_new_note_trigger', {
|
||||
webhookId: webhook.data.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const response = await context.store?.get<{
|
||||
webhookId: string;
|
||||
}>('_new_note_trigger');
|
||||
if (response !== null && response !== undefined) {
|
||||
await pipedriveCommon.unsubscribeWebhook(
|
||||
response.webhookId,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const response = await pipedriveApiCall<NoteListResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/notes',
|
||||
query: {
|
||||
limit: 10,
|
||||
sort: 'update_time DESC',
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
async run(context) {
|
||||
const payloadBody = context.payload.body as {
|
||||
data: PipedriveNoteV2;
|
||||
previous: PipedriveNoteV2;
|
||||
meta: {
|
||||
action: string;
|
||||
entity: string;
|
||||
};
|
||||
};
|
||||
|
||||
const noteResponse = await pipedriveApiCall<GetNoteResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v1/notes/${payloadBody.data.id}`,
|
||||
});
|
||||
|
||||
return [noteResponse.data];
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
user_id: 22701301,
|
||||
deal_id: null,
|
||||
person_id: 1,
|
||||
org_id: 1,
|
||||
lead_id: null,
|
||||
content: 'Note content for v2 API.',
|
||||
add_time: '2024-12-04T06:48:26Z',
|
||||
update_time: '2024-12-04T06:48:26Z',
|
||||
is_deleted: false,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,202 @@
|
||||
import { pipedriveAuth } from '../../';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedriveCommon,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { GetField } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { ORGANIZATION_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedriveOrganizationV2 {
|
||||
id: number;
|
||||
name: string;
|
||||
owner_id: number;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
is_deleted: boolean;
|
||||
visible_to: number;
|
||||
picture_id: number | null;
|
||||
label_ids: number[];
|
||||
address: {
|
||||
value: string | null;
|
||||
street_number: string | null;
|
||||
route: string | null;
|
||||
sublocality: string | null;
|
||||
locality: string | null;
|
||||
admin_area_level_1: string | null;
|
||||
admin_area_level_2: string | null;
|
||||
country: string | null;
|
||||
postal_code: string | null;
|
||||
formatted_address: string | null;
|
||||
} | null;
|
||||
custom_fields: Record<string, unknown>;
|
||||
next_activity_id?: number | null;
|
||||
last_activity_id?: number | null;
|
||||
open_deals_count?: number;
|
||||
related_open_deals_count?: number;
|
||||
closed_deals_count?: number;
|
||||
related_closed_deals_count?: number;
|
||||
participant_open_deals_count?: number;
|
||||
participant_closed_deals_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
won_deals_count?: number;
|
||||
related_won_deals_count?: number;
|
||||
lost_deals_count?: number;
|
||||
related_lost_deals_count?: number;
|
||||
last_incoming_mail_time?: string | null;
|
||||
last_outgoing_mail_time?: string | null;
|
||||
marketing_status?: string;
|
||||
doi_status?: string;
|
||||
}
|
||||
|
||||
interface OrganizationListResponseV2 {
|
||||
data: PipedriveOrganizationV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetOrganizationResponseV2 {
|
||||
data: PipedriveOrganizationV2;
|
||||
}
|
||||
|
||||
export const newOrganizationTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'new-organization',
|
||||
displayName: 'New Organization',
|
||||
description: 'Triggers when a new organization is created.',
|
||||
props: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const webhook = await pipedriveCommon.subscribeWebhook(
|
||||
'organization',
|
||||
'create',
|
||||
context.webhookUrl!,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
await context.store?.put<{
|
||||
webhookId: string;
|
||||
}>('_new_organization_trigger', {
|
||||
webhookId: webhook.data.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const response = await context.store?.get<{
|
||||
webhookId: string;
|
||||
}>('_new_organization_trigger');
|
||||
if (response !== null && response !== undefined) {
|
||||
await pipedriveCommon.unsubscribeWebhook(
|
||||
response.webhookId,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const response = await pipedriveApiCall<OrganizationListResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/organizations',
|
||||
query: {
|
||||
limit: 10,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
include_fields: ORGANIZATION_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/organizationFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const org of response.data) {
|
||||
const updatedOrgProperties = pipedriveTransformCustomFields(customFieldsResponse, org);
|
||||
result.push(updatedOrgProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const payloadBody = context.payload.body as {
|
||||
data: PipedriveOrganizationV2;
|
||||
previous: PipedriveOrganizationV2;
|
||||
meta: {
|
||||
action: string;
|
||||
entity: string;
|
||||
};
|
||||
};
|
||||
|
||||
const orgResponse = await pipedriveApiCall<GetOrganizationResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/organizations/${payloadBody.data.id}`,
|
||||
query: {
|
||||
include_fields: ORGANIZATION_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/organizationFields',
|
||||
});
|
||||
|
||||
const updatedOrgProperties = pipedriveTransformCustomFields(
|
||||
customFieldsResponse,
|
||||
orgResponse.data,
|
||||
);
|
||||
|
||||
return [updatedOrgProperties];
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
owner_id: 22701301,
|
||||
name: 'Pipedrive Sample Org',
|
||||
add_time: '2024-12-04T03:49:06Z',
|
||||
update_time: '2024-12-14T11:03:19Z',
|
||||
is_deleted: false,
|
||||
visible_to: 3,
|
||||
picture_id: null,
|
||||
label_ids: [],
|
||||
address: {
|
||||
value: 'Mustamäe tee 3, Tallinn, Estonia',
|
||||
street_number: '3',
|
||||
route: 'Mustamäe tee',
|
||||
sublocality: 'Kristiine',
|
||||
locality: 'Tallinn',
|
||||
admin_area_level_1: 'Harju maakond',
|
||||
admin_area_level_2: null,
|
||||
country: 'Estonia',
|
||||
postal_code: '10616',
|
||||
formatted_address: 'Mustamäe tee 3, 10616 Tallinn, Estonia',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,215 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { pipedriveAuth } from '../../';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedriveCommon,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { GetField } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { PERSON_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedrivePersonV2 {
|
||||
id: number;
|
||||
name: string;
|
||||
first_name: string | null;
|
||||
last_name: string | null;
|
||||
owner_id: number;
|
||||
org_id: number | null;
|
||||
picture_id: number | null;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
is_deleted: boolean;
|
||||
visible_to: number;
|
||||
phones: {
|
||||
value: string;
|
||||
primary: boolean;
|
||||
label: string;
|
||||
}[];
|
||||
emails: {
|
||||
value: string;
|
||||
primary: boolean;
|
||||
label: string;
|
||||
}[];
|
||||
label_ids: number[];
|
||||
custom_fields: Record<string, unknown>;
|
||||
next_activity_id?: number | null;
|
||||
last_activity_id?: number | null;
|
||||
open_deals_count?: number;
|
||||
related_open_deals_count?: number;
|
||||
closed_deals_count?: number;
|
||||
related_closed_deals_count?: number;
|
||||
participant_open_deals_count?: number;
|
||||
participant_closed_deals_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
won_deals_count?: number;
|
||||
related_won_deals_count?: number;
|
||||
lost_deals_count?: number;
|
||||
related_lost_deals_count?: number;
|
||||
last_incoming_mail_time?: string | null;
|
||||
last_outgoing_mail_time?: string | null;
|
||||
marketing_status?: string;
|
||||
doi_status?: string;
|
||||
}
|
||||
|
||||
interface PersonListResponseV2 {
|
||||
data: PipedrivePersonV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetPersonResponseV2 {
|
||||
data: PipedrivePersonV2;
|
||||
}
|
||||
|
||||
export const newPerson = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'new_person',
|
||||
displayName: 'New Person',
|
||||
description: 'Triggers when a new person is created',
|
||||
props: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const webhook = await pipedriveCommon.subscribeWebhook(
|
||||
'person',
|
||||
'create',
|
||||
context.webhookUrl!,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
await context.store?.put<WebhookInformation>('_new_person_trigger', {
|
||||
webhookId: webhook.data.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const response = await context.store?.get<WebhookInformation>('_new_person_trigger');
|
||||
if (response !== null && response !== undefined) {
|
||||
await pipedriveCommon.unsubscribeWebhook(
|
||||
response.webhookId,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const personsResponse = await pipedriveApiCall<PersonListResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/persons',
|
||||
query: {
|
||||
limit: 5,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
include_fields: PERSON_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/personFields',
|
||||
});
|
||||
|
||||
if (isNil(personsResponse.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const person of personsResponse.data) {
|
||||
const updatedPersonProperties = pipedriveTransformCustomFields(customFieldsResponse, person);
|
||||
result.push(updatedPersonProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const payloadBody = context.payload.body as PayloadBody;
|
||||
|
||||
const personResponse = await pipedriveApiCall<GetPersonResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/persons/${payloadBody.data.id}`,
|
||||
query: {
|
||||
include_fields: PERSON_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/personFields',
|
||||
});
|
||||
|
||||
const updatedPersonProperties = pipedriveTransformCustomFields(
|
||||
customFieldsResponse,
|
||||
personResponse.data,
|
||||
);
|
||||
|
||||
return [updatedPersonProperties];
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
owner_id: 123,
|
||||
org_id: 1234,
|
||||
name: 'Will Smith',
|
||||
first_name: 'Will',
|
||||
last_name: 'Smith',
|
||||
is_deleted: false,
|
||||
phones: [
|
||||
{
|
||||
value: '12345',
|
||||
primary: true,
|
||||
label: 'work',
|
||||
},
|
||||
],
|
||||
emails: [
|
||||
{
|
||||
value: 'will.smith@example.com',
|
||||
primary: true,
|
||||
label: 'work',
|
||||
},
|
||||
],
|
||||
add_time: '2017-10-18T13:23:07Z',
|
||||
update_time: '2020-05-08T05:30:20Z',
|
||||
visible_to: 3,
|
||||
picture_id: 4,
|
||||
next_activity_id: 128,
|
||||
last_activity_id: 34,
|
||||
last_incoming_mail_time: '2019-05-29T18:21:42Z',
|
||||
last_outgoing_mail_time: '2019-05-30T03:45:35Z',
|
||||
label_ids: [1],
|
||||
marketing_status: 'no_consent',
|
||||
},
|
||||
});
|
||||
|
||||
interface WebhookInformation {
|
||||
webhookId: string;
|
||||
}
|
||||
|
||||
type PayloadBody = {
|
||||
data: PipedrivePersonV2;
|
||||
previous: PipedrivePersonV2;
|
||||
meta: {
|
||||
action: string;
|
||||
entity: string;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,221 @@
|
||||
import { pipedriveAuth } from '../../index';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { filterIdProp } from '../common/props';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedrivePaginatedV2ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { GetField } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { ORGANIZATION_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedriveOrganizationV2 {
|
||||
id: number;
|
||||
name: string;
|
||||
owner_id: number;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
is_deleted: boolean;
|
||||
visible_to: number;
|
||||
picture_id: number | null;
|
||||
label_ids: number[];
|
||||
address: {
|
||||
value: string | null;
|
||||
street_number: string | null;
|
||||
route: string | null;
|
||||
sublocality: string | null;
|
||||
locality: string | null;
|
||||
admin_area_level_1: string | null;
|
||||
admin_area_level_2: string | null;
|
||||
country: string | null;
|
||||
postal_code: string | null;
|
||||
formatted_address: string | null;
|
||||
} | null;
|
||||
custom_fields: Record<string, unknown>;
|
||||
next_activity_id?: number | null;
|
||||
last_activity_id?: number | null;
|
||||
open_deals_count?: number;
|
||||
related_open_deals_count?: number;
|
||||
closed_deals_count?: number;
|
||||
related_closed_deals_count?: number;
|
||||
participant_open_deals_count?: number;
|
||||
participant_closed_deals_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
won_deals_count?: number;
|
||||
related_won_deals_count?: number;
|
||||
lost_deals_count?: number;
|
||||
related_lost_deals_count?: number;
|
||||
last_incoming_mail_time?: string | null;
|
||||
last_outgoing_mail_time?: string | null;
|
||||
marketing_status?: string;
|
||||
doi_status?: string;
|
||||
}
|
||||
|
||||
interface OrganizationListResponseV2 {
|
||||
data: PipedriveOrganizationV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const organizationMatchingFilterTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'organization-matching-filter',
|
||||
displayName: 'Organization Matching Filter',
|
||||
description: 'Triggers when an organization newly matches a Pipedrive filter for the first time.',
|
||||
type: TriggerStrategy.POLLING,
|
||||
props: {
|
||||
filterId: filterIdProp('org', true),
|
||||
},
|
||||
async onEnable(context) {
|
||||
const ids: number[] = [];
|
||||
const response = await pipedrivePaginatedV2ApiCall<{ id: number }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/organizations',
|
||||
query: {
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
filter_id: context.propsValue.filterId,
|
||||
},
|
||||
});
|
||||
if (!isNil(response)) {
|
||||
response.forEach((organization) => {
|
||||
ids.push(organization.id);
|
||||
});
|
||||
}
|
||||
await context.store.put('organizations', JSON.stringify(ids));
|
||||
},
|
||||
async onDisable(context) {
|
||||
await context.store.delete('organizations');
|
||||
},
|
||||
async test(context) {
|
||||
const organizations = [];
|
||||
const response = await pipedriveApiCall<OrganizationListResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/organizations',
|
||||
query: {
|
||||
limit: 10,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
filter_id: context.propsValue.filterId,
|
||||
include_fields: ORGANIZATION_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const org of response.data) {
|
||||
organizations.push(org);
|
||||
}
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/organizationFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const org of organizations) {
|
||||
const updatedOrgProperties = pipedriveTransformCustomFields(customFieldsResponse, org);
|
||||
result.push(updatedOrgProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const existingIds = (await context.store.get<string>('organizations')) ?? '[]';
|
||||
const parsedExistingIds = JSON.parse(existingIds) as number[];
|
||||
const response = await pipedrivePaginatedV2ApiCall<{ id: number }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/organizations',
|
||||
query: {
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
filter_id: context.propsValue.filterId,
|
||||
include_fields: ORGANIZATION_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response) || response.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const newOrganizations = response.filter(
|
||||
(organization) => !parsedExistingIds.includes(organization.id),
|
||||
);
|
||||
|
||||
const newIds = newOrganizations.map((organization) => organization.id);
|
||||
|
||||
if (newIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
await context.store.put('organizations', JSON.stringify([...newIds, ...parsedExistingIds]));
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/organizationFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const org of newOrganizations) {
|
||||
const updatedOrgProperties = pipedriveTransformCustomFields(customFieldsResponse, org);
|
||||
result.push(updatedOrgProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
owner_id: 22701301,
|
||||
name: 'Pipedrive Sample Org',
|
||||
add_time: '2024-12-04T03:49:06Z',
|
||||
update_time: '2024-12-14T11:03:19Z',
|
||||
is_deleted: false,
|
||||
visible_to: 3,
|
||||
picture_id: null,
|
||||
label_ids: [],
|
||||
address: {
|
||||
value: 'Mustamäe tee 3, Tallinn, Estonia',
|
||||
street_number: '3',
|
||||
route: 'Mustamäe tee',
|
||||
sublocality: 'Kristiine',
|
||||
locality: 'Tallinn',
|
||||
admin_area_level_1: 'Harju maakond',
|
||||
admin_area_level_2: null,
|
||||
country: 'Estonia',
|
||||
postal_code: '10616',
|
||||
formatted_address: 'Mustamäe tee 3, 10616 Tallinn, Estonia',
|
||||
},
|
||||
custom_fields: {
|
||||
your_custom_field_key: 'your_custom_field_value',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,219 @@
|
||||
import { pipedriveAuth } from '../../index';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { filterIdProp } from '../common/props';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedrivePaginatedV2ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { GetField } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { PERSON_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedrivePersonV2 {
|
||||
id: number;
|
||||
name: string;
|
||||
first_name: string | null;
|
||||
last_name: string | null;
|
||||
owner_id: number;
|
||||
org_id: number | null;
|
||||
picture_id: number | null;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
is_deleted: boolean;
|
||||
visible_to: number;
|
||||
phones: {
|
||||
value: string;
|
||||
primary: boolean;
|
||||
label: string;
|
||||
}[];
|
||||
emails: {
|
||||
value: string;
|
||||
primary: boolean;
|
||||
label: string;
|
||||
}[];
|
||||
label_ids: number[];
|
||||
custom_fields: Record<string, unknown>;
|
||||
next_activity_id?: number | null;
|
||||
last_activity_id?: number | null;
|
||||
open_deals_count?: number;
|
||||
related_open_deals_count?: number;
|
||||
closed_deals_count?: number;
|
||||
related_closed_deals_count?: number;
|
||||
participant_open_deals_count?: number;
|
||||
participant_closed_deals_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
won_deals_count?: number;
|
||||
related_won_deals_count?: number;
|
||||
lost_deals_count?: number;
|
||||
related_lost_deals_count?: number;
|
||||
last_incoming_mail_time?: string | null;
|
||||
last_outgoing_mail_time?: string | null;
|
||||
marketing_status?: string;
|
||||
doi_status?: string;
|
||||
}
|
||||
|
||||
interface PersonListResponseV2 {
|
||||
data: PipedrivePersonV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetPersonResponseV2 {
|
||||
data: PipedrivePersonV2;
|
||||
}
|
||||
|
||||
export const personMatchingFilterTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'person-matching-filter',
|
||||
displayName: 'Person Matching Filter',
|
||||
description: 'Triggers when a person newly matches a Pipedrive filter for the first time.',
|
||||
type: TriggerStrategy.POLLING,
|
||||
props: {
|
||||
filterId: filterIdProp('people', true),
|
||||
},
|
||||
async onEnable(context) {
|
||||
const ids: number[] = [];
|
||||
const response = await pipedrivePaginatedV2ApiCall<{ id: number }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/persons',
|
||||
query: {
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
filter_id: context.propsValue.filterId,
|
||||
},
|
||||
});
|
||||
if (!isNil(response)) {
|
||||
response.forEach((person) => {
|
||||
ids.push(person.id);
|
||||
});
|
||||
}
|
||||
await context.store.put('persons', JSON.stringify(ids));
|
||||
},
|
||||
async onDisable(context) {
|
||||
await context.store.delete('persons');
|
||||
},
|
||||
async test(context) {
|
||||
const persons = [];
|
||||
const response = await pipedriveApiCall<PersonListResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/persons',
|
||||
query: {
|
||||
limit: 10,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
filter_id: context.propsValue.filterId,
|
||||
include_fields: PERSON_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
for (const person of response.data) {
|
||||
persons.push(person);
|
||||
}
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/personFields',
|
||||
});
|
||||
const result = [];
|
||||
for (const person of persons) {
|
||||
const updatedPersonProperties = pipedriveTransformCustomFields(customFieldsResponse, person);
|
||||
result.push(updatedPersonProperties);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const existingIds = (await context.store.get<string>('persons')) ?? '[]';
|
||||
const parsedExistingIds = JSON.parse(existingIds) as number[];
|
||||
|
||||
const response = await pipedrivePaginatedV2ApiCall<{ id: number }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/persons',
|
||||
query: {
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
filter_id: context.propsValue.filterId,
|
||||
include_fields: PERSON_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
if (isNil(response) || response.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const newPersons = response.filter((person) => !parsedExistingIds.includes(person.id));
|
||||
const newIds = newPersons.map((person) => person.id);
|
||||
|
||||
if (newIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
await context.store.put('persons', JSON.stringify([...newIds, ...parsedExistingIds]));
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/personFields',
|
||||
});
|
||||
const result = [];
|
||||
for (const person of newPersons) {
|
||||
const updatedPersonProperties = pipedriveTransformCustomFields(customFieldsResponse, person);
|
||||
result.push(updatedPersonProperties);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
owner_id: 123,
|
||||
org_id: 1234,
|
||||
name: 'Will Smith',
|
||||
first_name: 'Will',
|
||||
last_name: 'Smith',
|
||||
is_deleted: false,
|
||||
phones: [
|
||||
{
|
||||
value: '12345',
|
||||
primary: true,
|
||||
label: 'work',
|
||||
},
|
||||
],
|
||||
emails: [
|
||||
{
|
||||
value: 'will.smith@example.com',
|
||||
primary: true,
|
||||
label: 'work',
|
||||
},
|
||||
],
|
||||
add_time: '2017-10-18T13:23:07Z',
|
||||
update_time: '2020-05-08T05:30:20Z',
|
||||
visible_to: 3,
|
||||
picture_id: 4,
|
||||
next_activity_id: 128,
|
||||
last_activity_id: 34,
|
||||
last_incoming_mail_time: '2019-05-29T18:21:42Z',
|
||||
last_outgoing_mail_time: '2019-05-30T03:45:35Z',
|
||||
label_ids: [1],
|
||||
marketing_status: 'no_consent',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,331 @@
|
||||
import { pipedriveAuth } from '../../index';
|
||||
import {
|
||||
createTrigger,
|
||||
DropdownOption,
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
TriggerStrategy,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedrivePaginatedV2ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { GetField, RequestParams, WebhookCreateResponse } from '../common/types';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { DEAL_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedriveDealV2 {
|
||||
id: number;
|
||||
title: string;
|
||||
creator_user_id: number;
|
||||
owner_id: number;
|
||||
person_id: number | null;
|
||||
org_id: number | null;
|
||||
stage_id: number;
|
||||
pipeline_id: number;
|
||||
value: number;
|
||||
currency: string;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
stage_change_time: string;
|
||||
is_deleted: boolean;
|
||||
status: 'open' | 'won' | 'lost';
|
||||
probability: number | null;
|
||||
lost_reason: string | null;
|
||||
visible_to: number;
|
||||
close_time: string | null;
|
||||
won_time: string | null;
|
||||
first_won_time?: string;
|
||||
lost_time: string | null;
|
||||
products_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
participants_count?: number;
|
||||
expected_close_date: string | null;
|
||||
last_incoming_mail_time?: string;
|
||||
last_outgoing_mail_time?: string;
|
||||
label_ids: number[];
|
||||
rotten_time: string | null;
|
||||
smart_bcc_email?: string;
|
||||
acv?: number;
|
||||
arr?: number;
|
||||
mrr?: number;
|
||||
custom_fields: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface PipedriveStageV2 {
|
||||
id: number;
|
||||
order_nr: number;
|
||||
name: string;
|
||||
is_deleted: boolean;
|
||||
deal_probability: number;
|
||||
pipeline_id: number;
|
||||
is_deal_rot_enabled: boolean;
|
||||
days_to_rotten: number | null;
|
||||
add_time: string;
|
||||
update_time: string | null;
|
||||
}
|
||||
|
||||
interface ListDealsResponseV2 {
|
||||
data: PipedriveDealV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetDealResponseV2 {
|
||||
data: PipedriveDealV2;
|
||||
}
|
||||
|
||||
export const updatedDealStageTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'updated-deal-stage',
|
||||
displayName: 'Updated Deal Stage',
|
||||
description: "Triggers when a deal's stage is updated.",
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
props: {
|
||||
stage_id: Property.Dropdown({
|
||||
auth: pipedriveAuth,
|
||||
displayName: 'Stage in Pipeline',
|
||||
required: false,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
placeholder: 'please connect your account.',
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const authValue = auth as PiecePropValueSchema<typeof pipedriveAuth>;
|
||||
const response = await pipedrivePaginatedV2ApiCall<PipedriveStageV2>({
|
||||
accessToken: authValue.access_token,
|
||||
apiDomain: authValue.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/stages',
|
||||
});
|
||||
|
||||
const options: DropdownOption<number>[] = [];
|
||||
for (const stage of response) {
|
||||
options.push({
|
||||
label: `${stage.name}`,
|
||||
value: stage.id,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}),
|
||||
},
|
||||
async onEnable(context) {
|
||||
const response = await pipedriveApiCall<WebhookCreateResponse>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.POST,
|
||||
resourceUri: '/v1/webhooks',
|
||||
body: {
|
||||
subscription_url: context.webhookUrl,
|
||||
event_object: 'deal',
|
||||
event_action: 'change',
|
||||
version: '2.0',
|
||||
},
|
||||
});
|
||||
|
||||
await context.store.put<string>('updated-deal-stage-trigger', response.data.id);
|
||||
},
|
||||
async onDisable(context) {
|
||||
const webhook = await context.store.get<string>('updated-deal-stage-trigger');
|
||||
if (webhook) {
|
||||
await pipedriveApiCall<WebhookCreateResponse>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.DELETE,
|
||||
resourceUri: `/v1/webhooks/${webhook}`,
|
||||
});
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const stageId = context.propsValue.stage_id;
|
||||
|
||||
const qs: RequestParams = {
|
||||
limit: 10,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
include_fields: DEAL_OPTIONAL_FIELDS.join(','),
|
||||
};
|
||||
|
||||
if (stageId) {
|
||||
qs['stage_id'] = stageId.toString();
|
||||
}
|
||||
|
||||
const dealsResponse = await pipedriveApiCall<ListDealsResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/deals',
|
||||
query: qs,
|
||||
});
|
||||
|
||||
if (isNil(dealsResponse.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const deal of dealsResponse.data) {
|
||||
const updatedDealProperties = pipedriveTransformCustomFields(customFieldsResponse, deal);
|
||||
|
||||
const stageResponse = await pipedriveApiCall<{ data: PipedriveStageV2 }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/stages/${updatedDealProperties.stage_id}`,
|
||||
});
|
||||
|
||||
updatedDealProperties['stage'] = stageResponse.data;
|
||||
result.push(updatedDealProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const stageId = context.propsValue.stage_id;
|
||||
|
||||
const payloadBody = context.payload.body as PayloadBody;
|
||||
const currentDealData = payloadBody.data;
|
||||
const previousDealData = payloadBody.previous;
|
||||
|
||||
// Check if previous data exists and has stage_id
|
||||
if (!previousDealData || isNil(previousDealData.stage_id)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Only trigger if stage_id actually changed
|
||||
if (currentDealData.stage_id !== previousDealData.stage_id) {
|
||||
// If stage filter is set, only trigger if new stage matches the filter
|
||||
if (stageId && currentDealData.stage_id !== stageId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dealResponse = await pipedriveApiCall<GetDealResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/deals/${payloadBody.data.id}`,
|
||||
query: {
|
||||
include_fields: DEAL_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const updatedDealProperties = pipedriveTransformCustomFields(
|
||||
customFieldsResponse,
|
||||
dealResponse.data,
|
||||
);
|
||||
|
||||
const stageResponse = await pipedriveApiCall<{ data: PipedriveStageV2 }>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/stages/${currentDealData.stage_id}`,
|
||||
});
|
||||
|
||||
updatedDealProperties['stage'] = stageResponse.data;
|
||||
|
||||
return [updatedDealProperties];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
creator_user_id: 8877,
|
||||
owner_id: 8877,
|
||||
person_id: 1101,
|
||||
org_id: 5,
|
||||
stage_id: 2,
|
||||
title: 'Deal One',
|
||||
value: 5000,
|
||||
currency: 'EUR',
|
||||
add_time: '2019-05-29T04:21:51Z',
|
||||
update_time: '2019-11-28T16:19:50Z',
|
||||
stage_change_time: '2019-11-28T15:41:22Z',
|
||||
is_deleted: false,
|
||||
status: 'open',
|
||||
probability: null,
|
||||
next_activity_id: 128,
|
||||
last_activity_id: null,
|
||||
lost_reason: null,
|
||||
visible_to: 1,
|
||||
close_time: null,
|
||||
pipeline_id: 1,
|
||||
won_time: '2019-11-27T11:40:36Z',
|
||||
first_won_time: '2019-11-27T11:40:36Z',
|
||||
lost_time: null,
|
||||
products_count: 0,
|
||||
files_count: 0,
|
||||
notes_count: 2,
|
||||
followers_count: 0,
|
||||
email_messages_count: 4,
|
||||
activities_count: 1,
|
||||
done_activities_count: 0,
|
||||
undone_activities_count: 1,
|
||||
participants_count: 1,
|
||||
expected_close_date: '2019-06-29',
|
||||
last_incoming_mail_time: '2019-05-29T18:21:42Z',
|
||||
last_outgoing_mail_time: '2019-05-30T03:45:35Z',
|
||||
label_ids: [11],
|
||||
rotten_time: null,
|
||||
smart_bcc_email: 'company+deal1@pipedrivemail.com',
|
||||
stage: {
|
||||
id: 2,
|
||||
order_nr: 1,
|
||||
name: 'Qualification',
|
||||
is_deleted: false,
|
||||
deal_probability: false,
|
||||
pipeline_id: 1,
|
||||
is_deal_rot_enabled: false,
|
||||
days_to_rotten: null,
|
||||
add_time: '2018-09-04T06:24:59Z',
|
||||
update_time: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type PayloadBody = {
|
||||
data: PipedriveDealV2;
|
||||
previous: PipedriveDealV2;
|
||||
meta: {
|
||||
action: string;
|
||||
entity: string;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,448 @@
|
||||
import {
|
||||
createTrigger,
|
||||
DropdownOption,
|
||||
DynamicPropsValue,
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
TriggerStrategy,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedriveCommon,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { pipedriveAuth } from '../..';
|
||||
import { AuthenticationType, httpClient, HttpMethod } from '@activepieces/pieces-common';
|
||||
import { FieldsResponse, GetField, RequestParams } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { DEAL_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedriveDealV2 {
|
||||
id: number;
|
||||
title: string;
|
||||
creator_user_id: number;
|
||||
owner_id: number;
|
||||
person_id: number | null;
|
||||
org_id: number | null;
|
||||
stage_id: number;
|
||||
pipeline_id: number;
|
||||
value: number;
|
||||
currency: string;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
stage_change_time: string;
|
||||
is_deleted: boolean;
|
||||
status: 'open' | 'won' | 'lost';
|
||||
probability: number | null;
|
||||
lost_reason: string | null;
|
||||
visible_to: number;
|
||||
close_time: string | null;
|
||||
won_time: string | null;
|
||||
first_won_time?: string;
|
||||
lost_time: string | null;
|
||||
products_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
participants_count?: number;
|
||||
expected_close_date: string | null;
|
||||
last_incoming_mail_time?: string;
|
||||
last_outgoing_mail_time?: string;
|
||||
label_ids: number[];
|
||||
rotten_time: string | null;
|
||||
smart_bcc_email?: string;
|
||||
acv?: number;
|
||||
arr?: number;
|
||||
mrr?: number;
|
||||
custom_fields: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface PipedriveStageV2 {
|
||||
id: number;
|
||||
order_nr: number;
|
||||
name: string;
|
||||
is_deleted: boolean;
|
||||
deal_probability: number;
|
||||
pipeline_id: number;
|
||||
is_deal_rot_enabled: boolean;
|
||||
days_to_rotten: number | null;
|
||||
add_time: string;
|
||||
update_time: string | null;
|
||||
}
|
||||
|
||||
interface ListDealsResponseV2 {
|
||||
data: PipedriveDealV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetDealResponseV2 {
|
||||
data: PipedriveDealV2;
|
||||
}
|
||||
|
||||
export const updatedDeal = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'updated_deal',
|
||||
displayName: 'Updated Deal',
|
||||
description: 'Triggers when a deal is updated.',
|
||||
props: {
|
||||
filter_by: Property.StaticDropdown({
|
||||
displayName: 'Filter by',
|
||||
required: false,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{
|
||||
label: 'Deal Status',
|
||||
value: 'status',
|
||||
},
|
||||
{
|
||||
label: 'Stage in Pipeline',
|
||||
value: 'stage_id',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
filter_by_field_value: Property.DynamicProperties({
|
||||
auth: pipedriveAuth,
|
||||
displayName: 'Field Values',
|
||||
required: false,
|
||||
refreshers: ['filter_by'],
|
||||
props: async ({ auth, filter_by }) => {
|
||||
if (!auth || !filter_by) return {};
|
||||
|
||||
const props: DynamicPropsValue = {};
|
||||
const authValue = auth as PiecePropValueSchema<typeof pipedriveAuth>;
|
||||
const filterBy = filter_by as unknown as string;
|
||||
|
||||
if (filterBy === 'status') {
|
||||
props['field_value'] = Property.StaticDropdown({
|
||||
displayName: 'Deal Status',
|
||||
required: true,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'Open', value: 'open' },
|
||||
{ label: 'Won', value: 'won' },
|
||||
{ label: 'Lost', value: 'lost' },
|
||||
{ label: 'Deleted', value: 'deleted' },
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
if (filterBy === 'stage_id') {
|
||||
const response = await httpClient.sendRequest<{
|
||||
data: PipedriveStageV2[];
|
||||
}>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${authValue.data['api_domain']}/api/v2/stages`,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token: authValue.access_token,
|
||||
},
|
||||
});
|
||||
props['field_value'] = Property.StaticDropdown({
|
||||
displayName: 'Stage in Pipeline',
|
||||
required: true,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: response.body.data.map((stage) => {
|
||||
return {
|
||||
label: stage.name,
|
||||
value: stage.id,
|
||||
};
|
||||
}),
|
||||
},
|
||||
});
|
||||
}
|
||||
return props;
|
||||
},
|
||||
}),
|
||||
field_to_watch: Property.Dropdown({
|
||||
auth: pipedriveAuth,
|
||||
displayName: 'Field to watch for Changes On',
|
||||
required: false,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
placeholder: 'Connect your account',
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const authValue = auth as PiecePropValueSchema<typeof pipedriveAuth>;
|
||||
const response = await httpClient.sendRequest<FieldsResponse>({
|
||||
method: HttpMethod.GET,
|
||||
url: `${authValue.data['api_domain']}/api/v1/dealFields`,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token: authValue.access_token,
|
||||
},
|
||||
});
|
||||
|
||||
const options: DropdownOption<string>[] = [];
|
||||
|
||||
for (const field of response.body.data) {
|
||||
options.push({
|
||||
label: field.name,
|
||||
value: field.key,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}),
|
||||
},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const webhook = await pipedriveCommon.subscribeWebhook(
|
||||
'deal',
|
||||
'change',
|
||||
context.webhookUrl!,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
await context.store?.put<string>('_updated_deal_trigger', webhook.data.id);
|
||||
},
|
||||
async onDisable(context) {
|
||||
const webhookId = await context.store.get<string>('_updated_deal_trigger');
|
||||
if (webhookId) {
|
||||
await pipedriveCommon.unsubscribeWebhook(
|
||||
webhookId,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const filterBy = context.propsValue.filter_by;
|
||||
const filterByValue = context.propsValue.filter_by_field_value!['field_value'];
|
||||
|
||||
const qs: RequestParams = {
|
||||
limit: 10,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
include_fields: DEAL_OPTIONAL_FIELDS.join(','),
|
||||
};
|
||||
|
||||
if (filterBy && filterByValue) {
|
||||
qs[filterBy] = filterByValue;
|
||||
}
|
||||
|
||||
const dealsResponse = await pipedriveApiCall<ListDealsResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/deals',
|
||||
query: qs,
|
||||
});
|
||||
|
||||
if (isNil(dealsResponse.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const deal of dealsResponse.data) {
|
||||
const updatedDealProperties = pipedriveTransformCustomFields(customFieldsResponse, deal);
|
||||
result.push(updatedDealProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const filterBy = context.propsValue.filter_by;
|
||||
const filterByValue = context.propsValue.filter_by_field_value!['field_value'];
|
||||
const fieldToWatch =
|
||||
context.propsValue.field_to_watch === 'label'
|
||||
? 'label_ids'
|
||||
: context.propsValue.field_to_watch;
|
||||
|
||||
const payloadBody = context.payload.body as {
|
||||
data: Record<string, any>;
|
||||
previous: Record<string, any>;
|
||||
meta: {
|
||||
action: string;
|
||||
entity: string;
|
||||
};
|
||||
};
|
||||
const currentDealData = flattenCustomFields(payloadBody.data);
|
||||
const previousDealData = flattenCustomFields(payloadBody.previous);
|
||||
|
||||
const noFilterAndNoField = !filterBy && !fieldToWatch;
|
||||
const isFieldChanged =
|
||||
fieldToWatch &&
|
||||
fieldToWatch in previousDealData && // The previous object now only contains fields whose values have changed
|
||||
currentDealData[fieldToWatch] !== previousDealData[fieldToWatch];
|
||||
const isFilterMatched = filterBy && currentDealData[filterBy] === filterByValue;
|
||||
|
||||
if (
|
||||
noFilterAndNoField ||
|
||||
(!filterBy && isFieldChanged) ||
|
||||
(isFilterMatched && (!fieldToWatch || isFieldChanged))
|
||||
) {
|
||||
const dealResponse = await pipedriveApiCall<GetDealResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/deals/${payloadBody.data.id}`,
|
||||
query: {
|
||||
include_fields: DEAL_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/dealFields',
|
||||
});
|
||||
|
||||
const updatedDealProperties = pipedriveTransformCustomFields(
|
||||
customFieldsResponse,
|
||||
dealResponse.data,
|
||||
);
|
||||
|
||||
return [updatedDealProperties];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
creator_user_id: 8877,
|
||||
owner_id: 8877,
|
||||
person_id: 1101,
|
||||
org_id: 5,
|
||||
stage_id: 2,
|
||||
title: 'Deal One',
|
||||
value: 5000,
|
||||
currency: 'EUR',
|
||||
add_time: '2019-05-29T04:21:51Z',
|
||||
update_time: '2019-11-28T16:19:50Z',
|
||||
stage_change_time: '2019-11-28T15:41:22Z',
|
||||
is_deleted: false,
|
||||
status: 'open',
|
||||
probability: null,
|
||||
next_activity_id: 128,
|
||||
last_activity_id: null,
|
||||
lost_reason: null,
|
||||
visible_to: 1,
|
||||
close_time: null,
|
||||
pipeline_id: 1,
|
||||
won_time: '2019-11-27T11:40:36Z',
|
||||
first_won_time: '2019-11-27T11:40:36Z',
|
||||
lost_time: null,
|
||||
products_count: 0,
|
||||
files_count: 0,
|
||||
notes_count: 2,
|
||||
followers_count: 0,
|
||||
email_messages_count: 4,
|
||||
activities_count: 1,
|
||||
done_activities_count: 0,
|
||||
undone_activities_count: 1,
|
||||
participants_count: 1,
|
||||
expected_close_date: '2019-06-29',
|
||||
last_incoming_mail_time: '2019-05-29T18:21:42Z',
|
||||
last_outgoing_mail_time: '2019-05-30T03:45:35Z',
|
||||
label_ids: [11],
|
||||
rotten_time: null,
|
||||
smart_bcc_email: 'company+deal1@pipedrivemail.com',
|
||||
},
|
||||
});
|
||||
|
||||
function flattenCustomFields(deal: Record<string, any>): Record<string, any> {
|
||||
const { custom_fields, ...rest } = deal;
|
||||
|
||||
if (!custom_fields) return rest;
|
||||
|
||||
const flatCustomFields: Record<string, any> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(custom_fields as Record<string, any>)) {
|
||||
if (isNil(value)) {
|
||||
flatCustomFields[key] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
const type = value['type'] as string;
|
||||
|
||||
switch (type) {
|
||||
case 'varchar':
|
||||
case 'text':
|
||||
case 'varchar_auto':
|
||||
case 'double':
|
||||
case 'phone':
|
||||
case 'date':
|
||||
flatCustomFields[key] = value?.value;
|
||||
break;
|
||||
case 'set':
|
||||
flatCustomFields[key] = value?.values?.length
|
||||
? value.values.map((v: any) => v.id).join(',')
|
||||
: '';
|
||||
break;
|
||||
case 'enum':
|
||||
case 'user':
|
||||
case 'org':
|
||||
case 'people':
|
||||
flatCustomFields[key] = value?.id;
|
||||
break;
|
||||
case 'monetary':
|
||||
flatCustomFields[key] = value?.value;
|
||||
flatCustomFields[`${key}_currency`] = value?.currency;
|
||||
break;
|
||||
case 'time':
|
||||
flatCustomFields[key] = value?.value;
|
||||
flatCustomFields[`${key}_timezone_id`] = value?.timezone_id;
|
||||
break;
|
||||
case 'timerange':
|
||||
flatCustomFields[key] = value?.from;
|
||||
flatCustomFields[`${key}_timezone_id`] = value?.timezone_id;
|
||||
flatCustomFields[`${key}_until`] = value?.until;
|
||||
break;
|
||||
case 'daterange':
|
||||
flatCustomFields[key] = value?.from;
|
||||
flatCustomFields[`${key}_until`] = value?.until;
|
||||
break;
|
||||
case 'address':
|
||||
flatCustomFields[key] = value?.value;
|
||||
flatCustomFields[`${key}_subpremise`] = value?.subpremise;
|
||||
flatCustomFields[`${key}_street_number`] = value?.street_number;
|
||||
flatCustomFields[`${key}_route`] = value?.route;
|
||||
flatCustomFields[`${key}_sublocality`] = value?.sublocality;
|
||||
flatCustomFields[`${key}_locality`] = value?.locality;
|
||||
flatCustomFields[`${key}_admin_area_level_1`] = value?.admin_area_level_1;
|
||||
flatCustomFields[`${key}_admin_area_level_2`] = value?.admin_area_level_2;
|
||||
flatCustomFields[`${key}_country`] = value?.country;
|
||||
flatCustomFields[`${key}_postal_code`] = value?.postal_code;
|
||||
flatCustomFields[`${key}_formatted_address`] = value?.formatted_address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...rest,
|
||||
...flatCustomFields,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
import { pipedriveAuth } from '../../';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedriveCommon,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { GetField } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { ORGANIZATION_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedriveOrganizationV2 {
|
||||
id: number;
|
||||
name: string;
|
||||
owner_id: number;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
is_deleted: boolean;
|
||||
visible_to: number;
|
||||
picture_id: number | null;
|
||||
label_ids: number[];
|
||||
address: {
|
||||
value: string | null;
|
||||
street_number: string | null;
|
||||
route: string | null;
|
||||
sublocality: string | null;
|
||||
locality: string | null;
|
||||
admin_area_level_1: string | null;
|
||||
admin_area_level_2: string | null;
|
||||
country: string | null;
|
||||
postal_code: string | null;
|
||||
formatted_address: string | null;
|
||||
} | null;
|
||||
custom_fields: Record<string, unknown>;
|
||||
next_activity_id?: number | null;
|
||||
last_activity_id?: number | null;
|
||||
open_deals_count?: number;
|
||||
related_open_deals_count?: number;
|
||||
closed_deals_count?: number;
|
||||
related_closed_deals_count?: number;
|
||||
participant_open_deals_count?: number;
|
||||
participant_closed_deals_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
won_deals_count?: number;
|
||||
related_won_deals_count?: number;
|
||||
lost_deals_count?: number;
|
||||
related_lost_deals_count?: number;
|
||||
last_incoming_mail_time?: string | null;
|
||||
last_outgoing_mail_time?: string | null;
|
||||
marketing_status?: string;
|
||||
doi_status?: string;
|
||||
}
|
||||
|
||||
interface OrganizationListResponseV2 {
|
||||
data: PipedriveOrganizationV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetOrganizationResponseV2 {
|
||||
data: PipedriveOrganizationV2;
|
||||
}
|
||||
|
||||
export const updatedOrganizationTrigger = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'updated-organization',
|
||||
displayName: 'Updated Organization',
|
||||
description: 'Triggers when an existing organization is updated.',
|
||||
props: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const webhook = await pipedriveCommon.subscribeWebhook(
|
||||
'organization',
|
||||
'change',
|
||||
context.webhookUrl!,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
await context.store?.put<string>('_updated_organization_trigger', webhook.data.id);
|
||||
},
|
||||
async onDisable(context) {
|
||||
const webhookId = await context.store.get<string>('_updated_organization_trigger');
|
||||
if (webhookId !== null && webhookId !== undefined) {
|
||||
await pipedriveCommon.unsubscribeWebhook(
|
||||
webhookId,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const response = await pipedriveApiCall<OrganizationListResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/organizations',
|
||||
query: {
|
||||
limit: 10,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
include_fields: ORGANIZATION_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
if (isNil(response.data)) {
|
||||
return [];
|
||||
}
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/organizationFields',
|
||||
});
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const org of response.data) {
|
||||
const updatedOrgProperties = pipedriveTransformCustomFields(customFieldsResponse, org);
|
||||
result.push(updatedOrgProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const payloadBody = context.payload.body as {
|
||||
data: Record<string, any>;
|
||||
previous: Record<string, any>;
|
||||
};
|
||||
|
||||
const orgResponse = await pipedriveApiCall<GetOrganizationResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/organizations/${payloadBody.data.id}`,
|
||||
query: {
|
||||
include_fields: ORGANIZATION_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/organizationFields',
|
||||
});
|
||||
|
||||
const updatedOrgProperties = pipedriveTransformCustomFields(
|
||||
customFieldsResponse,
|
||||
orgResponse.data,
|
||||
);
|
||||
|
||||
return [updatedOrgProperties];
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
owner_id: 22701301,
|
||||
name: 'Pipedrive Sample Org',
|
||||
add_time: '2024-12-04T03:49:06Z',
|
||||
update_time: '2024-12-14T11:03:19Z',
|
||||
is_deleted: false,
|
||||
visible_to: 3,
|
||||
picture_id: null,
|
||||
label_ids: [],
|
||||
address: {
|
||||
value: 'Mustamäe tee 3, Tallinn, Estonia',
|
||||
street_number: '3',
|
||||
route: 'Mustamäe tee',
|
||||
sublocality: 'Kristiine',
|
||||
locality: 'Tallinn',
|
||||
admin_area_level_1: 'Harju maakond',
|
||||
admin_area_level_2: null,
|
||||
country: 'Estonia',
|
||||
postal_code: '10616',
|
||||
formatted_address: 'Mustamäe tee 3, 10616 Tallinn, Estonia',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,204 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { pipedriveAuth } from '../../';
|
||||
import {
|
||||
pipedriveApiCall,
|
||||
pipedriveCommon,
|
||||
pipedrivePaginatedV1ApiCall,
|
||||
pipedriveTransformCustomFields,
|
||||
} from '../common';
|
||||
import { GetField } from '../common/types';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
import { PERSON_OPTIONAL_FIELDS } from '../common/constants';
|
||||
|
||||
interface PipedrivePersonV2 {
|
||||
id: number;
|
||||
name: string;
|
||||
first_name: string | null;
|
||||
last_name: string | null;
|
||||
owner_id: number;
|
||||
org_id: number | null;
|
||||
picture_id: number | null;
|
||||
add_time: string;
|
||||
update_time: string;
|
||||
is_deleted: boolean;
|
||||
visible_to: number;
|
||||
phones: {
|
||||
value: string;
|
||||
primary: boolean;
|
||||
label: string;
|
||||
}[];
|
||||
emails: {
|
||||
value: string;
|
||||
primary: boolean;
|
||||
label: string;
|
||||
}[];
|
||||
label_ids: number[];
|
||||
custom_fields: Record<string, unknown>;
|
||||
next_activity_id?: number | null;
|
||||
last_activity_id?: number | null;
|
||||
open_deals_count?: number;
|
||||
related_open_deals_count?: number;
|
||||
closed_deals_count?: number;
|
||||
related_closed_deals_count?: number;
|
||||
participant_open_deals_count?: number;
|
||||
participant_closed_deals_count?: number;
|
||||
email_messages_count?: number;
|
||||
activities_count?: number;
|
||||
done_activities_count?: number;
|
||||
undone_activities_count?: number;
|
||||
files_count?: number;
|
||||
notes_count?: number;
|
||||
followers_count?: number;
|
||||
won_deals_count?: number;
|
||||
related_won_deals_count?: number;
|
||||
lost_deals_count?: number;
|
||||
related_lost_deals_count?: number;
|
||||
last_incoming_mail_time?: string | null;
|
||||
last_outgoing_mail_time?: string | null;
|
||||
marketing_status?: string;
|
||||
doi_status?: string;
|
||||
}
|
||||
|
||||
interface PersonListResponseV2 {
|
||||
data: PipedrivePersonV2[];
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
start: number;
|
||||
limit: number;
|
||||
more_items_in_collection: boolean;
|
||||
next_cursor?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface GetPersonResponseV2 {
|
||||
data: PipedrivePersonV2;
|
||||
}
|
||||
|
||||
export const updatedPerson = createTrigger({
|
||||
auth: pipedriveAuth,
|
||||
name: 'updated_person',
|
||||
displayName: 'Updated Person',
|
||||
description: 'Triggers when a person is updated.',
|
||||
props: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const webhook = await pipedriveCommon.subscribeWebhook(
|
||||
'person',
|
||||
'change',
|
||||
context.webhookUrl!,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
await context.store?.put<string>('_updated_person_trigger', webhook.data.id);
|
||||
},
|
||||
|
||||
async onDisable(context) {
|
||||
const webhookId = await context.store.get<string>('_updated_person_trigger');
|
||||
if (webhookId !== null && webhookId !== undefined) {
|
||||
await pipedriveCommon.unsubscribeWebhook(
|
||||
webhookId,
|
||||
context.auth.data['api_domain'],
|
||||
context.auth.access_token,
|
||||
);
|
||||
}
|
||||
},
|
||||
async test(context) {
|
||||
const personsResponse = await pipedriveApiCall<PersonListResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v2/persons',
|
||||
query: {
|
||||
limit: 5,
|
||||
sort_by: 'update_time',
|
||||
sort_direction: 'desc',
|
||||
include_fields: PERSON_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/personFields',
|
||||
});
|
||||
|
||||
if (isNil(personsResponse.data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const person of personsResponse.data) {
|
||||
const updatedPersonProperties = pipedriveTransformCustomFields(customFieldsResponse, person);
|
||||
result.push(updatedPersonProperties);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async run(context) {
|
||||
const payloadBody = context.payload.body as {
|
||||
data: Record<string, any>;
|
||||
previous: Record<string, any>;
|
||||
};
|
||||
|
||||
const personResponse = await pipedriveApiCall<GetPersonResponseV2>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: `/v2/persons/${payloadBody.data.id}`,
|
||||
query: {
|
||||
include_fields: PERSON_OPTIONAL_FIELDS.join(','),
|
||||
},
|
||||
});
|
||||
|
||||
const customFieldsResponse = await pipedrivePaginatedV1ApiCall<GetField>({
|
||||
accessToken: context.auth.access_token,
|
||||
apiDomain: context.auth.data['api_domain'],
|
||||
method: HttpMethod.GET,
|
||||
resourceUri: '/v1/personFields',
|
||||
});
|
||||
|
||||
const updatedPersonProperties = pipedriveTransformCustomFields(
|
||||
customFieldsResponse,
|
||||
personResponse.data,
|
||||
);
|
||||
|
||||
return [updatedPersonProperties];
|
||||
},
|
||||
sampleData: {
|
||||
id: 1,
|
||||
owner_id: 123,
|
||||
org_id: 1234,
|
||||
name: 'Will Smith',
|
||||
first_name: 'Will',
|
||||
last_name: 'Smith',
|
||||
is_deleted: false,
|
||||
phones: [
|
||||
{
|
||||
value: '12345',
|
||||
primary: true,
|
||||
label: 'work',
|
||||
},
|
||||
],
|
||||
emails: [
|
||||
{
|
||||
value: 'will.smith@example.com',
|
||||
primary: true,
|
||||
label: 'work',
|
||||
},
|
||||
],
|
||||
add_time: '2017-10-18T13:23:07Z',
|
||||
update_time: '2020-05-08T05:30:20Z',
|
||||
visible_to: 3,
|
||||
picture_id: 4,
|
||||
next_activity_id: 128,
|
||||
last_activity_id: 34,
|
||||
last_incoming_mail_time: '2019-05-29T18:21:42Z',
|
||||
last_outgoing_mail_time: '2019-05-30T03:45:35Z',
|
||||
label_ids: [1],
|
||||
marketing_status: 'no_consent',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user