diff --git a/components/pipedrive/actions/add-activity/add-activity.mjs b/components/pipedrive/actions/add-activity/add-activity.mjs
index d38b160d1c405..2c96278c47155 100644
--- a/components/pipedrive/actions/add-activity/add-activity.mjs
+++ b/components/pipedrive/actions/add-activity/add-activity.mjs
@@ -7,7 +7,7 @@ export default {
key: "pipedrive-add-activity",
name: "Add Activity",
description: "Adds a new activity. Includes `more_activities_scheduled_in_context` property in response's `additional_data` which indicates whether there are more undone activities scheduled with the same deal, person or organization (depending on the supplied data). See the Pipedrive API docs for Activities [here](https://developers.pipedrive.com/docs/api/v1/#!/Activities). For info on [adding an activity in Pipedrive](https://developers.pipedrive.com/docs/api/v1/Activities#addActivity)",
- version: "0.1.9",
+ version: "0.1.10",
type: "action",
props: {
pipedriveApp,
diff --git a/components/pipedrive/actions/add-deal/add-deal.mjs b/components/pipedrive/actions/add-deal/add-deal.mjs
index f7bee4947d332..fe13f92baa689 100644
--- a/components/pipedrive/actions/add-deal/add-deal.mjs
+++ b/components/pipedrive/actions/add-deal/add-deal.mjs
@@ -5,7 +5,7 @@ export default {
key: "pipedrive-add-deal",
name: "Add Deal",
description: "Adds a new deal. See the Pipedrive API docs for Deals [here](https://developers.pipedrive.com/docs/api/v1/Deals#addDeal)",
- version: "0.1.9",
+ version: "0.1.10",
type: "action",
props: {
pipedriveApp,
diff --git a/components/pipedrive/actions/add-lead/add-lead.mjs b/components/pipedrive/actions/add-lead/add-lead.mjs
index fc2c15086c0b1..850ed90947eee 100644
--- a/components/pipedrive/actions/add-lead/add-lead.mjs
+++ b/components/pipedrive/actions/add-lead/add-lead.mjs
@@ -6,7 +6,7 @@ export default {
key: "pipedrive-add-lead",
name: "Add Lead",
description: "Create a new lead in Pipedrive. [See the documentation](https://developers.pipedrive.com/docs/api/v1/Leads#addLead)",
- version: "0.0.3",
+ version: "0.0.4",
type: "action",
props: {
pipedrive,
diff --git a/components/pipedrive/actions/add-note/add-note.mjs b/components/pipedrive/actions/add-note/add-note.mjs
index bf14955954144..2f33e6e7384d3 100644
--- a/components/pipedrive/actions/add-note/add-note.mjs
+++ b/components/pipedrive/actions/add-note/add-note.mjs
@@ -5,7 +5,7 @@ export default {
key: "pipedrive-add-note",
name: "Add Note",
description: "Adds a new note. For info on [adding an note in Pipedrive](https://developers.pipedrive.com/docs/api/v1/Notes#addNote)",
- version: "0.0.7",
+ version: "0.0.8",
type: "action",
props: {
pipedriveApp,
diff --git a/components/pipedrive/actions/add-organization/add-organization.mjs b/components/pipedrive/actions/add-organization/add-organization.mjs
index 8db9b9f9f9e1e..55d5f0ba5e9f1 100644
--- a/components/pipedrive/actions/add-organization/add-organization.mjs
+++ b/components/pipedrive/actions/add-organization/add-organization.mjs
@@ -5,7 +5,7 @@ export default {
key: "pipedrive-add-organization",
name: "Add Organization",
description: "Adds a new organization. See the Pipedrive API docs for Organizations [here](https://developers.pipedrive.com/docs/api/v1/Organizations#addOrganization)",
- version: "0.1.9",
+ version: "0.1.10",
type: "action",
props: {
pipedriveApp,
diff --git a/components/pipedrive/actions/add-person/add-person.mjs b/components/pipedrive/actions/add-person/add-person.mjs
index 2c1b102620aeb..1d3bbc901ee71 100644
--- a/components/pipedrive/actions/add-person/add-person.mjs
+++ b/components/pipedrive/actions/add-person/add-person.mjs
@@ -6,7 +6,7 @@ export default {
key: "pipedrive-add-person",
name: "Add Person",
description: "Adds a new person. See the Pipedrive API docs for People [here](https://developers.pipedrive.com/docs/api/v1/Persons#addPerson)",
- version: "0.1.9",
+ version: "0.1.10",
type: "action",
props: {
pipedriveApp,
diff --git a/components/pipedrive/actions/remove-duplicate-notes/remove-duplicate-notes.mjs b/components/pipedrive/actions/remove-duplicate-notes/remove-duplicate-notes.mjs
new file mode 100644
index 0000000000000..c8114ffe86ace
--- /dev/null
+++ b/components/pipedrive/actions/remove-duplicate-notes/remove-duplicate-notes.mjs
@@ -0,0 +1,148 @@
+import pipedriveApp from "../../pipedrive.app.mjs";
+import { decode } from "html-entities";
+
+export default {
+ key: "pipedrive-remove-duplicate-notes",
+ name: "Remove Duplicate Notes",
+ description: "Remove duplicate notes from an object in Pipedrive. See the documentation for [getting notes](https://developers.pipedrive.com/docs/api/v1/Notes#getNotes) and [deleting notes](https://developers.pipedrive.com/docs/api/v1/Notes#deleteNote)",
+ version: "0.0.1",
+ type: "action",
+ props: {
+ pipedriveApp,
+ leadId: {
+ propDefinition: [
+ pipedriveApp,
+ "leadId",
+ ],
+ description: "The ID of the lead that the notes are attached to",
+ },
+ dealId: {
+ propDefinition: [
+ pipedriveApp,
+ "dealId",
+ ],
+ description: "The ID of the deal that the notes are attached to",
+ },
+ personId: {
+ propDefinition: [
+ pipedriveApp,
+ "personId",
+ ],
+ description: "The ID of the person that the notes are attached to",
+ },
+ organizationId: {
+ propDefinition: [
+ pipedriveApp,
+ "organizationId",
+ ],
+ description: "The ID of the organization that the notes are attached to",
+ },
+ userId: {
+ propDefinition: [
+ pipedriveApp,
+ "userId",
+ ],
+ description: "The ID of the user that the notes are attached to",
+ },
+ projectId: {
+ propDefinition: [
+ pipedriveApp,
+ "projectId",
+ ],
+ description: "The ID of the project that the notes are attached to",
+ },
+ keyword: {
+ type: "string",
+ label: "Keyword",
+ description: "Only remove duplicate notes that contain the specified keyword(s)",
+ optional: true,
+ },
+ },
+ methods: {
+ getDuplicateNotes(notes) {
+ const seenContent = new Map();
+ const uniqueNotes = [];
+ const duplicates = [];
+
+ // Sort notes by add_time (ascending) to keep the oldest duplicate
+ const sortedNotes = notes.sort((a, b) => {
+ const dateA = new Date(a.add_time);
+ const dateB = new Date(b.add_time);
+ return dateA - dateB;
+ });
+
+ for (const note of sortedNotes) {
+ // Normalize content by removing extra whitespace and converting to lowercase
+ const decodedContent = decode(note.content || "");
+ const normalizedContent = decodedContent?.replace(/^\s*
|
\s*$/gi, "").trim()
+ .toLowerCase();
+
+ if (!normalizedContent) {
+ // Skip notes with empty content
+ continue;
+ }
+
+ if (seenContent.has(normalizedContent)) {
+ // This is a duplicate
+ duplicates.push({
+ duplicate: note,
+ original: seenContent.get(normalizedContent),
+ });
+ } else {
+ // This is the first occurrence
+ seenContent.set(normalizedContent, note);
+ uniqueNotes.push(note);
+ }
+ }
+
+ return {
+ uniqueNotes,
+ duplicates,
+ duplicateCount: duplicates.length,
+ };
+ },
+ },
+ async run({ $ }) {
+ let notes = await this.pipedriveApp.getPaginatedResources({
+ fn: this.pipedriveApp.getNotes,
+ params: {
+ user_id: this.userId,
+ lead_id: this.leadId,
+ deal_id: this.dealId,
+ person_id: this.personId,
+ org_id: this.organizationId,
+ project_id: this.projectId,
+ },
+ });
+
+ if (this.keyword) {
+ notes = notes.filter((note) =>
+ note.content?.toLowerCase().includes(this.keyword.toLowerCase()));
+ }
+
+ let result = {
+ notes,
+ totalNotes: notes.length,
+ };
+
+ const {
+ uniqueNotes, duplicates, duplicateCount,
+ } = this.getDuplicateNotes(notes);
+
+ for (const note of duplicates) {
+ await this.pipedriveApp.deleteNote(note.duplicate.id);
+ }
+
+ result = {
+ notes: uniqueNotes,
+ totalNotes: uniqueNotes.length,
+ duplicatesFound: duplicateCount,
+ duplicates: duplicates,
+ originalCount: notes.length,
+ };
+
+ $.export("$summary", `Found ${notes.length} total note(s), removed ${duplicateCount} duplicate(s), returning ${uniqueNotes.length} unique note(s)`);
+
+ return result;
+ },
+};
diff --git a/components/pipedrive/actions/search-notes/search-notes.mjs b/components/pipedrive/actions/search-notes/search-notes.mjs
new file mode 100644
index 0000000000000..9f76f363c3773
--- /dev/null
+++ b/components/pipedrive/actions/search-notes/search-notes.mjs
@@ -0,0 +1,179 @@
+import pipedriveApp from "../../pipedrive.app.mjs";
+
+export default {
+ key: "pipedrive-search-notes",
+ name: "Search Notes",
+ description: "Search for notes in Pipedrive. [See the documentation](https://developers.pipedrive.com/docs/api/v1/Notes#getNotes)",
+ version: "0.0.1",
+ type: "action",
+ props: {
+ pipedriveApp,
+ searchTerm: {
+ type: "string",
+ label: "Search Term",
+ description: "The term to search for in the note content",
+ optional: true,
+ },
+ leadId: {
+ propDefinition: [
+ pipedriveApp,
+ "leadId",
+ ],
+ description: "The ID of the lead that the note is attached to",
+ },
+ dealId: {
+ propDefinition: [
+ pipedriveApp,
+ "dealId",
+ ],
+ description: "The ID of the deal that the note is attached to",
+ },
+ personId: {
+ propDefinition: [
+ pipedriveApp,
+ "personId",
+ ],
+ description: "The ID of the person that the note is attached to",
+ },
+ organizationId: {
+ propDefinition: [
+ pipedriveApp,
+ "organizationId",
+ ],
+ description: "The ID of the organization that the note is attached to",
+ },
+ userId: {
+ propDefinition: [
+ pipedriveApp,
+ "userId",
+ ],
+ description: "The ID of the user that the note is attached to",
+ },
+ projectId: {
+ propDefinition: [
+ pipedriveApp,
+ "projectId",
+ ],
+ description: "The ID of the project that the note is attached to",
+ },
+ sortField: {
+ type: "string",
+ label: "Sort Field",
+ description: "The field name to sort by",
+ options: [
+ "id",
+ "user_id",
+ "deal_id",
+ "org_id",
+ "person_id",
+ "content",
+ "add_time",
+ "update_time",
+ ],
+ optional: true,
+ },
+ sortDirection: {
+ type: "string",
+ label: "Sort Direction",
+ description: "The direction to sort the results in",
+ options: [
+ "ASC",
+ "DESC",
+ ],
+ default: "DESC",
+ optional: true,
+ },
+ startDate: {
+ type: "string",
+ label: "Start Date",
+ description: "The date in format of YYYY-MM-DD from which notes to fetch",
+ optional: true,
+ },
+ endDate: {
+ type: "string",
+ label: "End Date",
+ description: "The date in format of YYYY-MM-DD until which notes to fetch to",
+ optional: true,
+ },
+ pinnedToLeadFlag: {
+ type: "boolean",
+ label: "Pinned to Lead Flag",
+ description: "If `true`, the results are filtered by note to lead pinning state",
+ optional: true,
+ },
+ pinnedToDealFlag: {
+ type: "boolean",
+ label: "Pinned to Deal Flag",
+ description: "If `true`, the results are filtered by note to deal pinning state",
+ optional: true,
+ },
+ pinnedToOrganizationFlag: {
+ type: "boolean",
+ label: "Pinned to Organization Flag",
+ description: "If `true`, the results are filtered by note to organization pinning state",
+ optional: true,
+ },
+ pinnedToPersonFlag: {
+ type: "boolean",
+ label: "Pinned to Person Flag",
+ description: "If `true`, the results are filtered by note to person pinning state",
+ optional: true,
+ },
+ pinnedToProjectFlag: {
+ type: "boolean",
+ label: "Pinned to Project Flag",
+ description: "If `true`, the results are filtered by note to project pinning state",
+ optional: true,
+ },
+ maxResults: {
+ type: "integer",
+ label: "Max Results",
+ description: "The maximum number of results to return",
+ optional: true,
+ },
+ },
+ async run({ $ }) {
+ let notes = await this.pipedriveApp.getPaginatedResources({
+ fn: this.pipedriveApp.getNotes,
+ params: {
+ user_id: this.userId,
+ lead_id: this.leadId,
+ deal_id: this.dealId,
+ person_id: this.personId,
+ org_id: this.organizationId,
+ project_id: this.projectId,
+ sort: this.sortField
+ ? `${this.sortField} ${this.sortDirection}`
+ : undefined,
+ pinned_to_lead_flag: this.pinnedToLeadFlag === true
+ ? 1
+ : undefined,
+ pinned_to_deal_flag: this.pinnedToDealFlag === true
+ ? 1
+ : undefined,
+ pinned_to_organization_flag: this.pinnedToOrganizationFlag === true
+ ? 1
+ : undefined,
+ pinned_to_person_flag: this.pinnedToPersonFlag === true
+ ? 1
+ : undefined,
+ pinned_to_project_flag: this.pinnedToProjectFlag === true
+ ? 1
+ : undefined,
+ start_date: this.startDate,
+ end_date: this.endDate,
+ },
+ max: this.maxResults,
+ });
+
+ if (this.searchTerm) {
+ notes = notes.filter((note) =>
+ note.content?.toLowerCase().includes(this.searchTerm.toLowerCase()));
+ }
+
+ $.export("$summary", `Successfully found ${notes.length} note${notes.length === 1
+ ? ""
+ : "s"}`);
+ return notes;
+ },
+};
diff --git a/components/pipedrive/actions/search-persons/search-persons.mjs b/components/pipedrive/actions/search-persons/search-persons.mjs
index a47ddb53a0108..c1b47f7700785 100644
--- a/components/pipedrive/actions/search-persons/search-persons.mjs
+++ b/components/pipedrive/actions/search-persons/search-persons.mjs
@@ -7,7 +7,7 @@ export default {
key: "pipedrive-search-persons",
name: "Search persons",
description: "Searches all Persons by `name`, `email`, `phone`, `notes` and/or custom fields. This endpoint is a wrapper of `/v1/itemSearch` with a narrower OAuth scope. Found Persons can be filtered by Organization ID. See the Pipedrive API docs [here](https://developers.pipedrive.com/docs/api/v1/Persons#searchPersons)",
- version: "0.1.9",
+ version: "0.1.10",
type: "action",
props: {
pipedriveApp,
diff --git a/components/pipedrive/actions/update-deal/update-deal.mjs b/components/pipedrive/actions/update-deal/update-deal.mjs
index cbebcbde1b339..e1ef1d03977af 100644
--- a/components/pipedrive/actions/update-deal/update-deal.mjs
+++ b/components/pipedrive/actions/update-deal/update-deal.mjs
@@ -5,7 +5,7 @@ export default {
key: "pipedrive-update-deal",
name: "Update Deal",
description: "Updates the properties of a deal. See the Pipedrive API docs for Deals [here](https://developers.pipedrive.com/docs/api/v1/Deals#updateDeal)",
- version: "0.1.11",
+ version: "0.1.12",
type: "action",
props: {
pipedriveApp,
diff --git a/components/pipedrive/actions/update-person/update-person.mjs b/components/pipedrive/actions/update-person/update-person.mjs
index 39608512fb703..13f447d3030ce 100644
--- a/components/pipedrive/actions/update-person/update-person.mjs
+++ b/components/pipedrive/actions/update-person/update-person.mjs
@@ -6,7 +6,7 @@ export default {
key: "pipedrive-update-person",
name: "Update Person",
description: "Updates an existing person in Pipedrive. [See the documentation](https://developers.pipedrive.com/docs/api/v1/Persons#updatePerson)",
- version: "0.0.1",
+ version: "0.0.2",
type: "action",
props: {
pipedriveApp,
diff --git a/components/pipedrive/package.json b/components/pipedrive/package.json
index 193b0be48bae4..79cbd3091f598 100644
--- a/components/pipedrive/package.json
+++ b/components/pipedrive/package.json
@@ -1,6 +1,6 @@
{
"name": "@pipedream/pipedrive",
- "version": "0.5.1",
+ "version": "0.6.0",
"description": "Pipedream Pipedrive Components",
"main": "pipedrive.app.mjs",
"keywords": [
@@ -15,6 +15,7 @@
},
"dependencies": {
"@pipedream/platform": "^3.0.3",
+ "html-entities": "^2.6.0",
"pipedrive": "^24.1.1"
}
}
diff --git a/components/pipedrive/pipedrive.app.mjs b/components/pipedrive/pipedrive.app.mjs
index d90fafbe6f780..8c985902defd0 100644
--- a/components/pipedrive/pipedrive.app.mjs
+++ b/components/pipedrive/pipedrive.app.mjs
@@ -355,6 +355,10 @@ export default {
const leadLabelsApi = this.api("LeadLabelsApi");
return leadLabelsApi.getLeadLabels(opts);
},
+ getNotes(opts = {}) {
+ const notesApi = this.api("NotesApi");
+ return notesApi.getNotes(opts);
+ },
addActivity(opts = {}) {
const activityApi = this.api("ActivitiesApi", "v2");
return activityApi.addActivity({
@@ -425,5 +429,45 @@ export default {
UpdatePersonRequest: opts,
});
},
+ deleteNote(noteId) {
+ const notesApi = this.api("NotesApi");
+ return notesApi.deleteNote({
+ id: noteId,
+ });
+ },
+ async *paginate({
+ fn, params, max,
+ }) {
+ params = {
+ ...params,
+ start: 0,
+ limit: 100,
+ };
+ let hasMore, count = 0;
+ do {
+ const {
+ data, additional_data: additionalData,
+ } = await fn(params);
+ if (!data?.length) {
+ return;
+ }
+ for (const item of data) {
+ yield item;
+ if (max && ++count >= max) {
+ return;
+ }
+ }
+ params.start += params.limit;
+ hasMore = additionalData.pagination.more_items_in_collection;
+ } while (hasMore);
+ },
+ async getPaginatedResources(opts) {
+ const results = [];
+ const resources = this.paginate(opts);
+ for await (const resource of resources) {
+ results.push(resource);
+ }
+ return results;
+ },
},
};
diff --git a/components/pipedrive/sources/new-deal-instant/new-deal-instant.mjs b/components/pipedrive/sources/new-deal-instant/new-deal-instant.mjs
index 437f92de339e1..881bda6ffdd18 100644
--- a/components/pipedrive/sources/new-deal-instant/new-deal-instant.mjs
+++ b/components/pipedrive/sources/new-deal-instant/new-deal-instant.mjs
@@ -6,7 +6,7 @@ export default {
key: "pipedrive-new-deal-instant",
name: "New Deal (Instant)",
description: "Emit new event when a new deal is created.",
- version: "0.0.5",
+ version: "0.0.6",
type: "source",
dedupe: "unique",
methods: {
diff --git a/components/pipedrive/sources/new-person-instant/new-person-instant.mjs b/components/pipedrive/sources/new-person-instant/new-person-instant.mjs
index ddbb174757caa..210aac876d859 100644
--- a/components/pipedrive/sources/new-person-instant/new-person-instant.mjs
+++ b/components/pipedrive/sources/new-person-instant/new-person-instant.mjs
@@ -6,7 +6,7 @@ export default {
key: "pipedrive-new-person-instant",
name: "New Person (Instant)",
description: "Emit new event when a new person is created.",
- version: "0.0.5",
+ version: "0.0.6",
type: "source",
dedupe: "unique",
methods: {
diff --git a/components/pipedrive/sources/updated-deal-instant/updated-deal-instant.mjs b/components/pipedrive/sources/updated-deal-instant/updated-deal-instant.mjs
index 89aa2b8eb8bf7..69b221e2e9168 100644
--- a/components/pipedrive/sources/updated-deal-instant/updated-deal-instant.mjs
+++ b/components/pipedrive/sources/updated-deal-instant/updated-deal-instant.mjs
@@ -6,7 +6,7 @@ export default {
key: "pipedrive-updated-deal-instant",
name: "New Deal Update (Instant)",
description: "Emit new event when a deal is updated.",
- version: "0.0.5",
+ version: "0.0.6",
type: "source",
dedupe: "unique",
methods: {
diff --git a/components/pipedrive/sources/updated-person-instant/updated-person-instant.mjs b/components/pipedrive/sources/updated-person-instant/updated-person-instant.mjs
index 6b808b8d12f63..cb09964ece5a7 100644
--- a/components/pipedrive/sources/updated-person-instant/updated-person-instant.mjs
+++ b/components/pipedrive/sources/updated-person-instant/updated-person-instant.mjs
@@ -6,7 +6,7 @@ export default {
key: "pipedrive-updated-person-instant",
name: "Updated Person (Instant)",
description: "Emit new event when a person is updated.",
- version: "0.0.5",
+ version: "0.0.6",
type: "source",
dedupe: "unique",
methods: {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 57e31305818d9..523e19a1d572d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10030,6 +10030,9 @@ importers:
'@pipedream/platform':
specifier: ^3.0.3
version: 3.0.3
+ html-entities:
+ specifier: ^2.6.0
+ version: 2.6.0
pipedrive:
specifier: ^24.1.1
version: 24.1.1
@@ -15639,14 +15642,6 @@ importers:
specifier: ^6.0.0
version: 6.2.0
- modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/cjs: {}
-
- modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/esm: {}
-
- modelcontextprotocol/node_modules2/zod-to-json-schema/dist/cjs: {}
-
- modelcontextprotocol/node_modules2/zod-to-json-schema/dist/esm: {}
-
packages/ai:
dependencies:
'@pipedream/sdk':
@@ -24609,8 +24604,8 @@ packages:
html-entities@1.4.0:
resolution: {integrity: sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==}
- html-entities@2.5.2:
- resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==}
+ html-entities@2.6.0:
+ resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==}
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
@@ -34332,7 +34327,7 @@ snapshots:
fast-xml-parser: 4.5.0
gaxios: 6.7.1
google-auth-library: 9.15.0
- html-entities: 2.5.2
+ html-entities: 2.6.0
mime: 3.0.0
p-limit: 3.1.0
retry-request: 7.0.2
@@ -43012,8 +43007,7 @@ snapshots:
html-entities@1.4.0: {}
- html-entities@2.5.2:
- optional: true
+ html-entities@2.6.0: {}
html-escaper@2.0.2: {}