From b611d5ef7f462a6b494db4782e22a8fb44f7b5d6 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 22 Jul 2025 18:51:33 -0300 Subject: [PATCH 1/4] [Components] Pingback #17674 Actions - Create Subscriber - Get Subscriber - Update Subscriber - Add Subscriber To Segmentation Lists - Remove Subscriber From Segmentation List --- .../add-subscriber-to-segmentation-lists.mjs | 38 +++++++ .../create-subscriber/create-subscriber.mjs | 71 ++++++++++++ .../actions/get-subscriber/get-subscriber.mjs | 32 ++++++ ...move-subscriber-from-segmentation-list.mjs | 36 ++++++ .../update-subscriber/update-subscriber.mjs | 65 +++++++++++ components/pingback/common/constants.mjs | 14 +++ components/pingback/common/utils.mjs | 24 ++++ components/pingback/package.json | 7 +- components/pingback/pingback.app.mjs | 103 +++++++++++++++++- 9 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 components/pingback/actions/add-subscriber-to-segmentation-lists/add-subscriber-to-segmentation-lists.mjs create mode 100644 components/pingback/actions/create-subscriber/create-subscriber.mjs create mode 100644 components/pingback/actions/get-subscriber/get-subscriber.mjs create mode 100644 components/pingback/actions/remove-subscriber-from-segmentation-list/remove-subscriber-from-segmentation-list.mjs create mode 100644 components/pingback/actions/update-subscriber/update-subscriber.mjs create mode 100644 components/pingback/common/constants.mjs create mode 100644 components/pingback/common/utils.mjs diff --git a/components/pingback/actions/add-subscriber-to-segmentation-lists/add-subscriber-to-segmentation-lists.mjs b/components/pingback/actions/add-subscriber-to-segmentation-lists/add-subscriber-to-segmentation-lists.mjs new file mode 100644 index 0000000000000..331f778d81bea --- /dev/null +++ b/components/pingback/actions/add-subscriber-to-segmentation-lists/add-subscriber-to-segmentation-lists.mjs @@ -0,0 +1,38 @@ +import { defineAction } from "@pipedream/types"; +import { parseObject } from "../../common/utils.mjs"; +import pingback from "../../pingback.app.mjs"; + +export default defineAction({ + name: "Add Subscriber To Segmentation Lists", + description: "Add a subscriber to segmentation lists by email [See the documentation](https://developer.pingback.com/docs/api/add-subscriber-to-segment)", + key: "pingback-add-subscriber-to-segmentation-lists", + version: "0.0.1", + type: "action", + props: { + pingback, + email: { + propDefinition: [ + pingback, + "email", + ], + }, + segmentationLists: { + propDefinition: [ + pingback, + "segmentationLists", + ], + }, + }, + async run({ $ }) { + const response = await this.pingback.addSubscriberToSegmentationLists({ + $, + email: this.email, + data: { + segmentationLists: parseObject(this.segmentationLists), + }, + }); + + $.export("$summary", `Subscriber ${this.email} added to segmentation list(s) successfully`); + return response; + }, +}); diff --git a/components/pingback/actions/create-subscriber/create-subscriber.mjs b/components/pingback/actions/create-subscriber/create-subscriber.mjs new file mode 100644 index 0000000000000..56a29efb183c1 --- /dev/null +++ b/components/pingback/actions/create-subscriber/create-subscriber.mjs @@ -0,0 +1,71 @@ +import { parseObject } from "../../common/utils.mjs"; +import pingback from "../../pingback.app.mjs"; + +export default { + name: "Create Subscriber", + description: "Create a new subscriber [See the documentation](https://developer.pingback.com/docs/api/create-subscriber)", + key: "pingback-create-subscriber", + version: "0.0.1", + type: "action", + props: { + pingback, + email: { + propDefinition: [ + pingback, + "email", + ], + }, + phone: { + propDefinition: [ + pingback, + "phone", + ], + }, + name: { + propDefinition: [ + pingback, + "name", + ], + }, + status: { + propDefinition: [ + pingback, + "status", + ], + }, + customFields: { + propDefinition: [ + pingback, + "customFields", + ], + }, + segmentationLists: { + propDefinition: [ + pingback, + "segmentationLists", + ], + }, + }, + async run({ $ }) { + const response = await this.pingback.createSubscriber({ + $, + data: { + email: this.email, + phone: this.phone, + name: this.name, + status: this.status, + customFields: Object.entries(parseObject(this.customFields))?.map(([ + key, + value, + ]) => ({ + label: key, + value, + })), + segmentationLists: parseObject(this.segmentationLists), + }, + }); + + $.export("$summary", `Subscriber created successfully with ID: ${response.data.data.id}`); + return response; + }, +}; diff --git a/components/pingback/actions/get-subscriber/get-subscriber.mjs b/components/pingback/actions/get-subscriber/get-subscriber.mjs new file mode 100644 index 0000000000000..b7bf2083be23a --- /dev/null +++ b/components/pingback/actions/get-subscriber/get-subscriber.mjs @@ -0,0 +1,32 @@ +import { ConfigurationError } from "@pipedream/platform"; +import pingback from "../../pingback.app.mjs"; + +export default { + name: "Get Subscriber", + description: "Get a subscriber by email [See the documentation](https://developer.pingback.com/docs/api/get-subscriber)", + key: "pingback-get-subscriber", + version: "0.0.1", + type: "action", + props: { + pingback, + email: { + propDefinition: [ + pingback, + "email", + ], + }, + }, + async run({ $ }) { + try { + const response = await this.pingback.getSubscriber({ + $, + email: this.email, + }); + + $.export("$summary", `Subscriber retrieved successfully with email: ${response.data.data.email}`); + return response; + } catch ({ response }) { + throw new ConfigurationError(response.data.error); + } + }, +}; diff --git a/components/pingback/actions/remove-subscriber-from-segmentation-list/remove-subscriber-from-segmentation-list.mjs b/components/pingback/actions/remove-subscriber-from-segmentation-list/remove-subscriber-from-segmentation-list.mjs new file mode 100644 index 0000000000000..d6386d98acf71 --- /dev/null +++ b/components/pingback/actions/remove-subscriber-from-segmentation-list/remove-subscriber-from-segmentation-list.mjs @@ -0,0 +1,36 @@ +import { defineAction } from "@pipedream/types"; +import pingback from "../../pingback.app.mjs"; + +export default defineAction({ + name: "Remove Subscriber From Segmentation List", + description: "Remove a subscriber from a segmentation list by email [See the documentation](https://developer.pingback.com/docs/api/remove-subscriber-from-segment)", + key: "pingback-remove-subscriber-from-segmentation-list", + version: "0.0.1", + type: "action", + props: { + pingback, + email: { + propDefinition: [ + pingback, + "email", + ], + }, + segmentationListId: { + propDefinition: [ + pingback, + "segmentationListId", + ], + type: "string", + }, + }, + async run({ $ }) { + const response = await this.pingback.removeSubscriberFromSegmentationList({ + $, + email: this.email, + segmentationListId: this.segmentationListId, + }); + + $.export("$summary", `Subscriber ${this.email} removed from segmentation list ${this.segmentationListId} successfully`); + return response; + }, +}); diff --git a/components/pingback/actions/update-subscriber/update-subscriber.mjs b/components/pingback/actions/update-subscriber/update-subscriber.mjs new file mode 100644 index 0000000000000..8b55f5f0537fd --- /dev/null +++ b/components/pingback/actions/update-subscriber/update-subscriber.mjs @@ -0,0 +1,65 @@ +import { defineAction } from "@pipedream/types"; +import { parseObject } from "../../common/utils.mjs"; +import pingback from "../../pingback.app.mjs"; + +export default defineAction({ + name: "Update Subscriber", + description: "Update a specific subscriber by email [See the documentation](https://developer.pingback.com/docs/api/update-subscriber)", + key: "pingback-update-subscriber", + version: "0.0.1", + type: "action", + props: { + pingback, + email: { + propDefinition: [ + pingback, + "email", + ], + }, + phone: { + propDefinition: [ + pingback, + "phone", + ], + }, + name: { + propDefinition: [ + pingback, + "name", + ], + }, + status: { + propDefinition: [ + pingback, + "status", + ], + }, + customFields: { + propDefinition: [ + pingback, + "customFields", + ], + }, + }, + async run({ $ }) { + const response = await this.pingback.updateSubscriber({ + $, + email: this.email, + data: { + phone: this.phone, + name: this.name, + status: this.status, + customFields: Object.entries(parseObject(this.customFields))?.map(([ + key, + value, + ]) => ({ + label: key, + value, + })), + }, + }); + + $.export("$summary", `Subscriber updated successfully with ID: ${response.data.data.id}`); + return response; + }, +}); diff --git a/components/pingback/common/constants.mjs b/components/pingback/common/constants.mjs new file mode 100644 index 0000000000000..3531e58d57972 --- /dev/null +++ b/components/pingback/common/constants.mjs @@ -0,0 +1,14 @@ +export const STATUS_OPTIONS = [ + { + label: "Free Subscriber", + value: "free_subscriber", + }, + { + label: "Paid Subscriber", + value: "paid_subscriber", + }, + { + label: "Unsubscribed Subscriber", + value: "unsubscribed_subscriber", + }, +]; diff --git a/components/pingback/common/utils.mjs b/components/pingback/common/utils.mjs new file mode 100644 index 0000000000000..dcc9cc61f6f41 --- /dev/null +++ b/components/pingback/common/utils.mjs @@ -0,0 +1,24 @@ +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; diff --git a/components/pingback/package.json b/components/pingback/package.json index 77806c463f007..19c2fc4e095bf 100644 --- a/components/pingback/package.json +++ b/components/pingback/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/pingback", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Pingback Components", "main": "pingback.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.0" } -} \ No newline at end of file +} diff --git a/components/pingback/pingback.app.mjs b/components/pingback/pingback.app.mjs index 812a7e5b65d6f..798f9380fb320 100644 --- a/components/pingback/pingback.app.mjs +++ b/components/pingback/pingback.app.mjs @@ -1,11 +1,106 @@ +import { axios } from "@pipedream/platform"; +import { STATUS_OPTIONS } from "./common/constants.mjs"; + export default { type: "app", app: "pingback", - propDefinitions: {}, + propDefinitions: { + email: { + type: "string", + label: "Email", + description: "The email address of the subscriber", + }, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the subscriber. Must be in E.164 format. E.g. +1234567890", + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "The name of the subscriber", + optional: true, + }, + status: { + type: "string", + label: "Status", + description: "The status of the subscriber", + options: STATUS_OPTIONS, + optional: true, + }, + customFields: { + type: "object", + label: "Custom Fields", + description: "Custom fields for the subscriber", + optional: true, + }, + segmentationLists: { + type: "string[]", + label: "Segmentation Lists", + description: "Segmentation lists to add the subscriber to. You can get the ID by clicking audience and lists at Pingback dashboard.", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://connect.pingback.com/v1"; + }, + _headers() { + return { + "x-api-key": `${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(), + ...opts, + }); + }, + createSubscriber(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/subscriber", + ...opts, + }); + }, + getSubscriber({ + email, ...opts + }) { + return this._makeRequest({ + path: `/subscriber/${email}`, + ...opts, + }); + }, + updateSubscriber({ + email, ...opts + }) { + return this._makeRequest({ + method: "PUT", + path: `/subscriber/${email}`, + ...opts, + }); + }, + addSubscriberToSegmentationLists({ + email, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/segmentation/add-subscriber/${email}`, + ...opts, + }); + }, + removeSubscriberFromSegmentationList({ + email, segmentationListId, ...opts + }) { + return this._makeRequest({ + method: "DELETE", + path: `/segmentation/${segmentationListId}/remove-subscriber/${email}`, + ...opts, + }); }, }, }; From 78ff6055ddb1057fcbc1c58f047dc0db0bd975bc Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 22 Jul 2025 18:55:47 -0300 Subject: [PATCH 2/4] pnpm update --- pnpm-lock.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e47a0ac47b05..4db9264af834b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10199,7 +10199,11 @@ importers: specifier: ^6.11.1 version: 6.13.1 - components/pingback: {} + components/pingback: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/pingbell: dependencies: @@ -37295,6 +37299,8 @@ snapshots: '@putout/operator-filesystem': 5.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3)) '@putout/operator-json': 2.2.0 putout: 36.13.1(eslint@8.57.1)(typescript@5.6.3) + transitivePeerDependencies: + - supports-color '@putout/operator-regexp@1.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3))': dependencies: From 500010149ee95f68880662dd5e075ca9ad7bbe13 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 22 Jul 2025 19:01:17 -0300 Subject: [PATCH 3/4] pnpm update --- pnpm-lock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4db9264af834b..0c9b9385116b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16148,7 +16148,7 @@ importers: version: 3.1.7 ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2) tsup: specifier: ^8.3.6 version: 8.3.6(@microsoft/api-extractor@7.47.12(@types/node@20.17.30))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.4)(typescript@5.7.2)(yaml@2.6.1) @@ -16191,7 +16191,7 @@ importers: version: 3.1.0 jest: specifier: ^29.1.2 - version: 29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0) + version: 29.7.0(@types/node@20.17.6)(babel-plugin-macros@3.1.0) type-fest: specifier: ^4.15.0 version: 4.27.0 @@ -51382,7 +51382,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2): + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.30)(babel-plugin-macros@3.1.0))(typescript@5.7.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -51396,10 +51396,10 @@ snapshots: typescript: 5.7.2 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 8.0.0-alpha.13 + '@babel/core': 7.26.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@8.0.0-alpha.13) + babel-jest: 29.7.0(@babel/core@7.26.0) ts-jest@29.2.5(@babel/core@8.0.0-alpha.13)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@8.0.0-alpha.13))(jest@29.7.0(@types/node@20.17.6)(babel-plugin-macros@3.1.0))(typescript@5.6.3): dependencies: From 0a7fe7c840f723d1df68fc320f3597b6d9d9836d Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 23 Jul 2025 12:37:38 -0300 Subject: [PATCH 4/4] Refactor Pingback actions to simplify data handling and improve code consistency. Updated Add, Create, Remove, and Update Subscriber actions to use a unified data structure. Introduced parseCustomFields utility for better custom fields management. --- .../add-subscriber-to-segmentation-lists.mjs | 5 ++-- .../create-subscriber/create-subscriber.mjs | 30 +++++++++---------- ...move-subscriber-from-segmentation-list.mjs | 11 +++---- .../update-subscriber/update-subscriber.mjs | 28 ++++++++--------- components/pingback/common/utils.mjs | 13 ++++++++ 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/components/pingback/actions/add-subscriber-to-segmentation-lists/add-subscriber-to-segmentation-lists.mjs b/components/pingback/actions/add-subscriber-to-segmentation-lists/add-subscriber-to-segmentation-lists.mjs index 331f778d81bea..451e38cafbad7 100644 --- a/components/pingback/actions/add-subscriber-to-segmentation-lists/add-subscriber-to-segmentation-lists.mjs +++ b/components/pingback/actions/add-subscriber-to-segmentation-lists/add-subscriber-to-segmentation-lists.mjs @@ -1,8 +1,7 @@ -import { defineAction } from "@pipedream/types"; import { parseObject } from "../../common/utils.mjs"; import pingback from "../../pingback.app.mjs"; -export default defineAction({ +export default { name: "Add Subscriber To Segmentation Lists", description: "Add a subscriber to segmentation lists by email [See the documentation](https://developer.pingback.com/docs/api/add-subscriber-to-segment)", key: "pingback-add-subscriber-to-segmentation-lists", @@ -35,4 +34,4 @@ export default defineAction({ $.export("$summary", `Subscriber ${this.email} added to segmentation list(s) successfully`); return response; }, -}); +}; diff --git a/components/pingback/actions/create-subscriber/create-subscriber.mjs b/components/pingback/actions/create-subscriber/create-subscriber.mjs index 56a29efb183c1..4ea178158bf1f 100644 --- a/components/pingback/actions/create-subscriber/create-subscriber.mjs +++ b/components/pingback/actions/create-subscriber/create-subscriber.mjs @@ -1,4 +1,7 @@ -import { parseObject } from "../../common/utils.mjs"; +import { + parseCustomFields, + parseObject, +} from "../../common/utils.mjs"; import pingback from "../../pingback.app.mjs"; export default { @@ -47,22 +50,19 @@ export default { }, }, async run({ $ }) { + const data = { + email: this.email, + phone: this.phone, + name: this.name, + status: this.status, + segmentationLists: parseObject(this.segmentationLists), + }; + + data.customFields = parseCustomFields(this.customFields); + const response = await this.pingback.createSubscriber({ $, - data: { - email: this.email, - phone: this.phone, - name: this.name, - status: this.status, - customFields: Object.entries(parseObject(this.customFields))?.map(([ - key, - value, - ]) => ({ - label: key, - value, - })), - segmentationLists: parseObject(this.segmentationLists), - }, + data, }); $.export("$summary", `Subscriber created successfully with ID: ${response.data.data.id}`); diff --git a/components/pingback/actions/remove-subscriber-from-segmentation-list/remove-subscriber-from-segmentation-list.mjs b/components/pingback/actions/remove-subscriber-from-segmentation-list/remove-subscriber-from-segmentation-list.mjs index d6386d98acf71..c7adaf2d34a21 100644 --- a/components/pingback/actions/remove-subscriber-from-segmentation-list/remove-subscriber-from-segmentation-list.mjs +++ b/components/pingback/actions/remove-subscriber-from-segmentation-list/remove-subscriber-from-segmentation-list.mjs @@ -1,7 +1,6 @@ -import { defineAction } from "@pipedream/types"; import pingback from "../../pingback.app.mjs"; -export default defineAction({ +export default { name: "Remove Subscriber From Segmentation List", description: "Remove a subscriber from a segmentation list by email [See the documentation](https://developer.pingback.com/docs/api/remove-subscriber-from-segment)", key: "pingback-remove-subscriber-from-segmentation-list", @@ -18,9 +17,11 @@ export default defineAction({ segmentationListId: { propDefinition: [ pingback, - "segmentationListId", + "segmentationLists", ], - type: "string", + type: "string[]", + label: "Segmentation Lists", + description: "Segmentation list ID to remove the subscriber from. You can get the ID by clicking audience and lists at Pingback dashboard.", }, }, async run({ $ }) { @@ -33,4 +34,4 @@ export default defineAction({ $.export("$summary", `Subscriber ${this.email} removed from segmentation list ${this.segmentationListId} successfully`); return response; }, -}); +}; diff --git a/components/pingback/actions/update-subscriber/update-subscriber.mjs b/components/pingback/actions/update-subscriber/update-subscriber.mjs index 8b55f5f0537fd..0bcec71c63e22 100644 --- a/components/pingback/actions/update-subscriber/update-subscriber.mjs +++ b/components/pingback/actions/update-subscriber/update-subscriber.mjs @@ -1,8 +1,7 @@ -import { defineAction } from "@pipedream/types"; -import { parseObject } from "../../common/utils.mjs"; +import { parseCustomFields } from "../../common/utils.mjs"; import pingback from "../../pingback.app.mjs"; -export default defineAction({ +export default { name: "Update Subscriber", description: "Update a specific subscriber by email [See the documentation](https://developer.pingback.com/docs/api/update-subscriber)", key: "pingback-update-subscriber", @@ -42,24 +41,21 @@ export default defineAction({ }, }, async run({ $ }) { + const data = { + phone: this.phone, + name: this.name, + status: this.status, + }; + + data.customFields = parseCustomFields(this.customFields); + const response = await this.pingback.updateSubscriber({ $, email: this.email, - data: { - phone: this.phone, - name: this.name, - status: this.status, - customFields: Object.entries(parseObject(this.customFields))?.map(([ - key, - value, - ]) => ({ - label: key, - value, - })), - }, + data, }); $.export("$summary", `Subscriber updated successfully with ID: ${response.data.data.id}`); return response; }, -}); +}; diff --git a/components/pingback/common/utils.mjs b/components/pingback/common/utils.mjs index dcc9cc61f6f41..04b1bed4e2330 100644 --- a/components/pingback/common/utils.mjs +++ b/components/pingback/common/utils.mjs @@ -22,3 +22,16 @@ export const parseObject = (obj) => { } return obj; }; + +export const parseCustomFields = (customFields) => { + const parsedCustomFields = Object.entries(parseObject(customFields) || {}); + if (parsedCustomFields.length) { + return parsedCustomFields.map(([ + key, + value, + ]) => ({ + label: key, + value, + })); + } +};