From 8c00f4638e9d48c92b42de02a6fb59509000bf4c Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 14 Jul 2025 16:50:43 -0300 Subject: [PATCH 1/5] [Components] indiefunnels #17406 Sources - New Booking Session (Instant) - New Form Submission (Instant) - New Order (Instant) - Contact Updated (Instant) Actions - Create Contact - Update Contact - Create Member - Update Member --- .../actions/create-contact/create-contact.mjs | 138 +++++++ .../actions/create-member/create-member.mjs | 211 +++++++++++ .../actions/update-contact/update-contact.mjs | 157 ++++++++ .../actions/update-member/update-member.mjs | 211 +++++++++++ components/indiefunnels/common/constants.mjs | 1 + components/indiefunnels/common/utils.mjs | 43 +++ components/indiefunnels/indiefunnels.app.mjs | 337 +++++++++++++++++- components/indiefunnels/package.json | 7 +- .../indiefunnels/sources/common/base.mjs | 34 ++ .../contact-updated-instant.mjs | 23 ++ .../contact-updated-instant/test-event.mjs | 23 ++ .../new-booking-session-instant.mjs | 23 ++ .../test-event.mjs | 45 +++ .../new-form-submission-instant.mjs | 23 ++ .../test-event.mjs | 45 +++ .../new-order-instant/new-order-instant.mjs | 23 ++ .../sources/new-order-instant/test-event.mjs | 74 ++++ 17 files changed, 1411 insertions(+), 7 deletions(-) create mode 100644 components/indiefunnels/actions/create-contact/create-contact.mjs create mode 100644 components/indiefunnels/actions/create-member/create-member.mjs create mode 100644 components/indiefunnels/actions/update-contact/update-contact.mjs create mode 100644 components/indiefunnels/actions/update-member/update-member.mjs create mode 100644 components/indiefunnels/common/constants.mjs create mode 100644 components/indiefunnels/common/utils.mjs create mode 100644 components/indiefunnels/sources/common/base.mjs create mode 100644 components/indiefunnels/sources/contact-updated-instant/contact-updated-instant.mjs create mode 100644 components/indiefunnels/sources/contact-updated-instant/test-event.mjs create mode 100644 components/indiefunnels/sources/new-booking-session-instant/new-booking-session-instant.mjs create mode 100644 components/indiefunnels/sources/new-booking-session-instant/test-event.mjs create mode 100644 components/indiefunnels/sources/new-form-submission-instant/new-form-submission-instant.mjs create mode 100644 components/indiefunnels/sources/new-form-submission-instant/test-event.mjs create mode 100644 components/indiefunnels/sources/new-order-instant/new-order-instant.mjs create mode 100644 components/indiefunnels/sources/new-order-instant/test-event.mjs diff --git a/components/indiefunnels/actions/create-contact/create-contact.mjs b/components/indiefunnels/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..14500915ce312 --- /dev/null +++ b/components/indiefunnels/actions/create-contact/create-contact.mjs @@ -0,0 +1,138 @@ +import { parseObject } from "../../common/utils.mjs"; +import indiefunnels from "../../indiefunnels.app.mjs"; + +export default { + key: "indiefunnels-create-contact", + name: "Create Contact", + description: "Creates a new Contact on your IndieFunnel website. [See the documentation](https://websitebuilder.docs.apiary.io/#reference/contacts/list-and-create/create-new-contact)", + version: "0.0.1", + type: "action", + props: { + indiefunnels, + name: { + propDefinition: [ + indiefunnels, + "name", + ], + optional: true, + }, + email: { + propDefinition: [ + indiefunnels, + "email", + ], + }, + phone: { + propDefinition: [ + indiefunnels, + "phone", + ], + optional: true, + }, + note: { + propDefinition: [ + indiefunnels, + "note", + ], + optional: true, + }, + address: { + propDefinition: [ + indiefunnels, + "address", + ], + optional: true, + }, + city: { + propDefinition: [ + indiefunnels, + "city", + ], + optional: true, + }, + state: { + propDefinition: [ + indiefunnels, + "state", + ], + optional: true, + }, + zip: { + propDefinition: [ + indiefunnels, + "zip", + ], + optional: true, + }, + country: { + propDefinition: [ + indiefunnels, + "country", + ], + optional: true, + }, + companyName: { + propDefinition: [ + indiefunnels, + "companyName", + ], + optional: true, + }, + properties: { + propDefinition: [ + indiefunnels, + "properties", + ], + optional: true, + }, + tags: { + propDefinition: [ + indiefunnels, + "tags", + ], + optional: true, + }, + subscribed: { + propDefinition: [ + indiefunnels, + "subscribed", + ], + optional: true, + }, + subscriberLists: { + propDefinition: [ + indiefunnels, + "subscriberLists", + ], + optional: true, + }, + }, + async run({ $ }) { + const { + indiefunnels, + properties, + tags, + subscriberLists, + ...data + } = this; + + const response = await indiefunnels.createContact({ + $, + data: { + properties: Object.entries(parseObject(properties) || {})?.map(([ + key, + value, + ]) => ({ + name: key, + value, + })), + tags: parseObject(tags), + subscriberLists: parseObject(subscriberLists), + ...data, + }, + }); + + $.export("$summary", `Successfully created contact with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/indiefunnels/actions/create-member/create-member.mjs b/components/indiefunnels/actions/create-member/create-member.mjs new file mode 100644 index 0000000000000..f6f9b8d26cf7a --- /dev/null +++ b/components/indiefunnels/actions/create-member/create-member.mjs @@ -0,0 +1,211 @@ +import { cleanObj } from "../../common/utils.mjs"; +import indiefunnels from "../../indiefunnels.app.mjs"; + +export default { + key: "indiefunnels-create-member", + name: "Create Member", + description: "Creates a new Member on your IndieFunnel website. [See the documentation](https://websitebuilder.docs.apiary.io/#reference/members/list-and-create/create-new-member)", + version: "0.0.1", + type: "action", + props: { + indiefunnels, + name: { + propDefinition: [ + indiefunnels, + "name", + ], + description: "The name of the member", + }, + email: { + propDefinition: [ + indiefunnels, + "email", + ], + description: "The email address of the member", + }, + groups: { + propDefinition: [ + indiefunnels, + "groups", + ], + optional: true, + }, + approved: { + propDefinition: [ + indiefunnels, + "approved", + ], + optional: true, + }, + contactId: { + propDefinition: [ + indiefunnels, + "contactId", + ], + optional: true, + }, + billingPhone: { + propDefinition: [ + indiefunnels, + "billingPhone", + ], + optional: true, + }, + billingCompanyName: { + propDefinition: [ + indiefunnels, + "billingCompanyName", + ], + optional: true, + }, + billingCompanyId: { + propDefinition: [ + indiefunnels, + "billingCompanyId", + ], + optional: true, + }, + billingCountry: { + propDefinition: [ + indiefunnels, + "billingCountry", + ], + optional: true, + }, + billingState: { + propDefinition: [ + indiefunnels, + "billingState", + ], + optional: true, + }, + billingCity: { + propDefinition: [ + indiefunnels, + "billingCity", + ], + optional: true, + }, + billingZipCode: { + propDefinition: [ + indiefunnels, + "billingZipCode", + ], + optional: true, + }, + billingAddress: { + propDefinition: [ + indiefunnels, + "billingAddress", + ], + optional: true, + }, + billingAddress2: { + propDefinition: [ + indiefunnels, + "billingAddress2", + ], + optional: true, + }, + shippingPhone: { + propDefinition: [ + indiefunnels, + "shippingPhone", + ], + optional: true, + }, + shippingCompanyName: { + propDefinition: [ + indiefunnels, + "shippingCompanyName", + ], + optional: true, + }, + shippingCompanyId: { + propDefinition: [ + indiefunnels, + "shippingCompanyId", + ], + optional: true, + }, + shippingCountry: { + propDefinition: [ + indiefunnels, + "shippingCountry", + ], + optional: true, + }, + shippingState: { + propDefinition: [ + indiefunnels, + "shippingState", + ], + optional: true, + }, + shippingCity: { + propDefinition: [ + indiefunnels, + "shippingCity", + ], + optional: true, + }, + shippingZipCode: { + propDefinition: [ + indiefunnels, + "shippingZipCode", + ], + optional: true, + }, + shippingAddress: { + propDefinition: [ + indiefunnels, + "shippingAddress", + ], + optional: true, + }, + shippingAddress2: { + propDefinition: [ + indiefunnels, + "shippingAddress2", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.indiefunnels.createMember({ + $, + data: cleanObj({ + name: this.name, + email: this.email, + groups: this.groups, + approved: this.approved, + contactId: this.contactId, + billingAddress: { + phone: this.billingPhone, + companyName: this.billingCompanyName, + companyId: this.billingCompanyId, + country: this.billingCountry, + state: this.billingState, + city: this.billingCity, + zipCode: this.billingZipCode, + address: this.billingAddress, + address2: this.billingAddress2, + }, + shippingAddress: { + phone: this.shippingPhone, + companyName: this.shippingCompanyName, + companyId: this.shippingCompanyId, + country: this.shippingCountry, + state: this.shippingState, + city: this.shippingCity, + zipCode: this.shippingZipCode, + address: this.shippingAddress, + address2: this.shippingAddress2, + }, + }), + }); + + $.export("$summary", `Successfully created member with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/indiefunnels/actions/update-contact/update-contact.mjs b/components/indiefunnels/actions/update-contact/update-contact.mjs new file mode 100644 index 0000000000000..e312aba2fb108 --- /dev/null +++ b/components/indiefunnels/actions/update-contact/update-contact.mjs @@ -0,0 +1,157 @@ +import { + cleanObj, parseObject, +} from "../../common/utils.mjs"; +import indiefunnels from "../../indiefunnels.app.mjs"; + +export default { + key: "indiefunnels-update-contact", + name: "Update Contact", + description: "Updates a Contact specified by ID. [See the documentation](https://websitebuilder.docs.apiary.io/#reference/contacts/search-contacts/update-contact)", + version: "0.0.1", + type: "action", + props: { + indiefunnels, + contactId: { + propDefinition: [ + indiefunnels, + "contactId", + ], + description: "The ID of the contact to update", + withLabel: true, + reloadProps: true, + }, + name: { + propDefinition: [ + indiefunnels, + "name", + ], + optional: true, + }, + email: { + propDefinition: [ + indiefunnels, + "email", + ], + }, + phone: { + propDefinition: [ + indiefunnels, + "phone", + ], + optional: true, + }, + note: { + propDefinition: [ + indiefunnels, + "note", + ], + optional: true, + }, + address: { + propDefinition: [ + indiefunnels, + "address", + ], + optional: true, + }, + city: { + propDefinition: [ + indiefunnels, + "city", + ], + optional: true, + }, + state: { + propDefinition: [ + indiefunnels, + "state", + ], + optional: true, + }, + zip: { + propDefinition: [ + indiefunnels, + "zip", + ], + optional: true, + }, + country: { + propDefinition: [ + indiefunnels, + "country", + ], + optional: true, + }, + companyName: { + propDefinition: [ + indiefunnels, + "companyName", + ], + optional: true, + }, + properties: { + propDefinition: [ + indiefunnels, + "properties", + ], + optional: true, + }, + tags: { + propDefinition: [ + indiefunnels, + "tags", + ], + optional: true, + }, + subscribed: { + propDefinition: [ + indiefunnels, + "subscribed", + ], + optional: true, + }, + subscriberLists: { + propDefinition: [ + indiefunnels, + "subscriberLists", + ], + optional: true, + }, + }, + async additionalProps(props) { + if (this.contactId) { + props.email.default = this.contactId.label; + } + return {}; + }, + async run({ $ }) { + const { + indiefunnels, + contactId, + properties, + tags, + subscriberLists, + ...data + } = this; + + const response = await indiefunnels.updateContact({ + $, + contactId: contactId.value, + data: cleanObj({ + properties: Object.entries(parseObject(properties) || {})?.map(([ + key, + value, + ]) => ({ + name: key, + value, + })), + tags: parseObject(tags), + subscriberLists: parseObject(subscriberLists), + ...data, + }), + }); + + $.export("$summary", `Successfully updated contact with ID: ${contactId.value}`); + return response; + }, +}; diff --git a/components/indiefunnels/actions/update-member/update-member.mjs b/components/indiefunnels/actions/update-member/update-member.mjs new file mode 100644 index 0000000000000..951638231a7c9 --- /dev/null +++ b/components/indiefunnels/actions/update-member/update-member.mjs @@ -0,0 +1,211 @@ +import { cleanObj } from "../../common/utils.mjs"; +import indiefunnels from "../../indiefunnels.app.mjs"; + +export default { + key: "indiefunnels-update-member", + name: "Update Member", + description: "Updates a Member specified by ID. [See the documentation](https://websitebuilder.docs.apiary.io/#reference/members/single-member/update-member)", + version: "0.0.1", + type: "action", + props: { + indiefunnels, + memberId: { + propDefinition: [ + indiefunnels, + "memberId", + ], + }, + name: { + propDefinition: [ + indiefunnels, + "name", + ], + description: "The name of the member", + optional: true, + }, + groups: { + propDefinition: [ + indiefunnels, + "groups", + ], + optional: true, + }, + approved: { + propDefinition: [ + indiefunnels, + "approved", + ], + optional: true, + }, + contactId: { + propDefinition: [ + indiefunnels, + "contactId", + ], + optional: true, + }, + billingPhone: { + propDefinition: [ + indiefunnels, + "billingPhone", + ], + optional: true, + }, + billingCompanyName: { + propDefinition: [ + indiefunnels, + "billingCompanyName", + ], + optional: true, + }, + billingCompanyId: { + propDefinition: [ + indiefunnels, + "billingCompanyId", + ], + optional: true, + }, + billingCountry: { + propDefinition: [ + indiefunnels, + "billingCountry", + ], + optional: true, + }, + billingState: { + propDefinition: [ + indiefunnels, + "billingState", + ], + optional: true, + }, + billingCity: { + propDefinition: [ + indiefunnels, + "billingCity", + ], + optional: true, + }, + billingZipCode: { + propDefinition: [ + indiefunnels, + "billingZipCode", + ], + optional: true, + }, + billingAddress: { + propDefinition: [ + indiefunnels, + "billingAddress", + ], + optional: true, + }, + billingAddress2: { + propDefinition: [ + indiefunnels, + "billingAddress2", + ], + optional: true, + }, + shippingPhone: { + propDefinition: [ + indiefunnels, + "shippingPhone", + ], + optional: true, + }, + shippingCompanyName: { + propDefinition: [ + indiefunnels, + "shippingCompanyName", + ], + optional: true, + }, + shippingCompanyId: { + propDefinition: [ + indiefunnels, + "shippingCompanyId", + ], + optional: true, + }, + shippingCountry: { + propDefinition: [ + indiefunnels, + "shippingCountry", + ], + optional: true, + }, + shippingState: { + propDefinition: [ + indiefunnels, + "shippingState", + ], + optional: true, + }, + shippingCity: { + propDefinition: [ + indiefunnels, + "shippingCity", + ], + optional: true, + }, + shippingZipCode: { + propDefinition: [ + indiefunnels, + "shippingZipCode", + ], + optional: true, + }, + shippingAddress: { + propDefinition: [ + indiefunnels, + "shippingAddress", + ], + optional: true, + }, + shippingAddress2: { + propDefinition: [ + indiefunnels, + "shippingAddress2", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.indiefunnels.updateMember({ + $, + memberId: this.memberId, + data: cleanObj({ + name: this.name, + groups: this.groups, + approved: this.approved, + contactId: this.contactId, + billingAddress: { + phone: this.billingPhone, + companyName: this.billingCompanyName, + companyId: this.billingCompanyId, + country: this.billingCountry, + state: this.billingState, + city: this.billingCity, + zipCode: this.billingZipCode, + address: this.billingAddress, + address2: this.billingAddress2, + }, + shippingAddress: { + phone: this.shippingPhone, + companyName: this.shippingCompanyName, + companyId: this.shippingCompanyId, + country: this.shippingCountry, + state: this.shippingState, + city: this.shippingCity, + zipCode: this.shippingZipCode, + address: this.shippingAddress, + address2: this.shippingAddress2, + }, + }), + }); + + $.export("$summary", `Successfully updated member with ID: ${this.memberId}`); + return response; + }, +}; diff --git a/components/indiefunnels/common/constants.mjs b/components/indiefunnels/common/constants.mjs new file mode 100644 index 0000000000000..9975390fe8151 --- /dev/null +++ b/components/indiefunnels/common/constants.mjs @@ -0,0 +1 @@ +export const LIMIT = 50; diff --git a/components/indiefunnels/common/utils.mjs b/components/indiefunnels/common/utils.mjs new file mode 100644 index 0000000000000..7abe5f31b2141 --- /dev/null +++ b/components/indiefunnels/common/utils.mjs @@ -0,0 +1,43 @@ +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; +}; + +export const cleanObj = (obj) => { + return Object.entries(obj) + .filter(([ + // eslint-disable-next-line no-unused-vars + _, + v, + ]) => (v != null && v != "" && JSON.stringify(v) != "{}")) + .reduce((acc, [ + k, + v, + ]) => ({ + ...acc, + [k]: (!Array.isArray(v) && v === Object(v)) + ? cleanObj(v) + : v, + }), {}); +}; + diff --git a/components/indiefunnels/indiefunnels.app.mjs b/components/indiefunnels/indiefunnels.app.mjs index 6dbeb22a485a6..359ff460e25f9 100644 --- a/components/indiefunnels/indiefunnels.app.mjs +++ b/components/indiefunnels/indiefunnels.app.mjs @@ -1,11 +1,338 @@ +import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/constants.mjs"; + export default { type: "app", app: "indiefunnels", - propDefinitions: {}, + propDefinitions: { + memberId: { + type: "string", + label: "Member ID", + description: "The Id of the member to update", + async options({ page }) { + const { items } = await this.listMembers({ + params: { + page, + }, + }); + + return items.map(({ + id: value, email: label, + }) => ({ + label, + value, + })); + }, + }, + contactId: { + type: "string", + label: "Contact ID", + description: "Contact id of the member", + async options({ page }) { + const { items } = await this.listContacts({ + params: { + limit: LIMIT, + skip: LIMIT * page, + }, + }); + + return items.map(({ + id: value, email: label, + }) => ({ + label, + value, + })); + }, + }, + groups: { + type: "string[]", + label: "Member Groups", + description: "Member group id(s) to which the member belong", + async options() { + const items = await this.listMemberGroups(); + + return items.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + subscriberLists: { + type: "string[]", + label: "Subscriber Lists", + description: "Array of subscriber list IDs (email marketing lists)", + async options() { + const items = await this.listSubscriberLists(); + + return items.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + name: { + type: "string", + label: "Name", + description: "The name of the contact", + }, + email: { + type: "string", + label: "Email", + description: "The email address of the contact", + }, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the contact", + }, + note: { + type: "string", + label: "Note", + description: "Private note for the contact", + }, + address: { + type: "string", + label: "Address", + description: "The address of the contact", + }, + city: { + type: "string", + label: "City", + description: "The city of the contact", + }, + state: { + type: "string", + label: "State", + description: "The state of the contact", + }, + zip: { + type: "string", + label: "Zip", + description: "The zip code of the contact", + }, + country: { + type: "string", + label: "Country", + description: "The country of the contact. 2-letter ISO code", + }, + companyName: { + type: "string", + label: "Company Name", + description: "The name of the company the contact belongs to", + }, + properties: { + type: "object", + label: "Properties", + description: "An object of custom properties, as defined in the CRM section in your website", + }, + tags: { + type: "string[]", + label: "Tags", + description: "An array of tags to assign to the contact", + }, + subscribed: { + type: "boolean", + label: "Subscribed", + description: "Whether the contact is a subscriber or not (for email marketing purposes).", + }, + approved: { + type: "boolean", + label: "Approved", + description: "Member approval status", + }, + billingPhone: { + type: "string", + label: "Billing Address Phone", + description: "Billing address phone number", + }, + billingCompanyName: { + type: "string", + label: "Billing Address Company", + description: "Billing address company name", + }, + billingCompanyId: { + type: "string", + label: "Billing Address Company ID", + description: "Billing address company ID", + }, + billingCountry: { + type: "string", + label: "Billing Address Country", + description: "Billing address country. 2-letter ISO 3166 code", + }, + billingState: { + type: "string", + label: "Billing Address State", + description: "Billing address state", + }, + billingCity: { + type: "string", + label: "Billing Address City", + description: "Billing address city", + }, + billingZipCode: { + type: "string", + label: "Billing Address Zip Code", + description: "Billing address zip code", + }, + billingAddress: { + type: "string", + label: "Billing Address Line 1", + description: "Billing address line 1", + }, + billingAddress2: { + type: "string", + label: "Billing Address Line 2", + description: "Billing address line 2", + }, + shippingPhone: { + type: "string", + label: "Shipping Address Phone", + description: "Shipping address phone number", + }, + shippingCompanyName: { + type: "string", + label: "Shipping Address Company", + description: "Shipping address company name", + }, + shippingCompanyId: { + type: "string", + label: "Shipping Address Company ID", + description: "Shipping address company ID", + }, + shippingCountry: { + type: "string", + label: "Shipping Address Country", + description: "Shipping address country. 2-letter ISO 3166 code", + }, + shippingState: { + type: "string", + label: "Shipping Address State", + description: "Shipping address state", + }, + shippingCity: { + type: "string", + label: "Shipping Address City", + description: "Shipping address city", + }, + shippingZipCode: { + type: "string", + label: "Shipping Address Zip Code", + description: "Shipping address zip code", + }, + shippingAddress: { + type: "string", + label: "Shipping Address Line 1", + description: "Shipping address line 1", + }, + shippingAddress2: { + type: "string", + label: "Shipping Address Line 2", + description: "Shipping address line 2", + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return `${this.$auth.api_url}/api/site`; + }, + _headers() { + return { + "Authorization": `Bearer ${this.$auth.api_key}`, + "user-agent ": "@PipedreamHQ/pipedream v0.1", + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(), + ...opts, + }); + }, + listContacts(opts = {}) { + return this._makeRequest({ + path: "/contacts", + ...opts, + }); + }, + getContact({ + contactId, + ...opts + }) { + return this._makeRequest({ + path: `/contacts/${contactId}`, + ...opts, + }); + }, + listMemberGroups(opts = {}) { + return this._makeRequest({ + path: "/member-groups", + ...opts, + }); + }, + listMembers(opts = {}) { + return this._makeRequest({ + path: "/members", + ...opts, + }); + }, + listSubscriberLists(opts = {}) { + return this._makeRequest({ + path: "/subscriber-lists", + ...opts, + }); + }, + createContact(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/contacts", + ...opts, + }); + }, + createMember(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/members", + ...opts, + }); + }, + updateContact({ + contactId, + ...opts + }) { + return this._makeRequest({ + method: "PUT", + path: `/contacts/${contactId}`, + ...opts, + }); + }, + updateMember({ + memberId, + ...opts + }) { + return this._makeRequest({ + method: "PUT", + path: `/members/${memberId}`, + ...opts, + }); + }, + createHook(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/webhooks", + ...opts, + }); + }, + deleteHook(webhookId) { + return this._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/indiefunnels/package.json b/components/indiefunnels/package.json index f0487c400d445..1d55acd6bdb0f 100644 --- a/components/indiefunnels/package.json +++ b/components/indiefunnels/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/indiefunnels", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream INDIEFUNNELS Components", "main": "indiefunnels.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/indiefunnels/sources/common/base.mjs b/components/indiefunnels/sources/common/base.mjs new file mode 100644 index 0000000000000..4fdc374142d23 --- /dev/null +++ b/components/indiefunnels/sources/common/base.mjs @@ -0,0 +1,34 @@ +import crypto from "crypto"; +import indiefunnels from "../../indiefunnels.app.mjs"; + +export default { + props: { + indiefunnels, + db: "$.service.db", + http: "$.interface.http", + }, + hooks: { + async activate() { + const response = await this.indiefunnels.createHook({ + data: { + target: this.http.endpoint, + secret: crypto.randomUUID(), + events: this.getEvent(), + }, + }); + this.db.set("webhookId", response.id); + }, + async deactivate() { + const webhookId = this.db.get("webhookId"); + await this.indiefunnels.deleteHook(webhookId); + }, + }, + async run({ body }) { + const ts = body.created || Date.now(); + this.$emit(body, { + id: `${body.id}-${ts}`, + summary: this.getSummary(body), + ts, + }); + }, +}; diff --git a/components/indiefunnels/sources/contact-updated-instant/contact-updated-instant.mjs b/components/indiefunnels/sources/contact-updated-instant/contact-updated-instant.mjs new file mode 100644 index 0000000000000..8ac8bbba6b81c --- /dev/null +++ b/components/indiefunnels/sources/contact-updated-instant/contact-updated-instant.mjs @@ -0,0 +1,23 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "indiefunnels-contact-updated-instant", + name: "Contact Updated (Instant)", + description: "Emit new event when a contact is updated.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + getEvent() { + return [ + "contact_updated", + ]; + }, + getSummary(details) { + return `Contact with ID ${details.id} updated`; + }, + }, + sampleEmit, +}; diff --git a/components/indiefunnels/sources/contact-updated-instant/test-event.mjs b/components/indiefunnels/sources/contact-updated-instant/test-event.mjs new file mode 100644 index 0000000000000..cb29406556bf0 --- /dev/null +++ b/components/indiefunnels/sources/contact-updated-instant/test-event.mjs @@ -0,0 +1,23 @@ +export default { + "id": 5, + "name": "Contact Name", + "email": "contact@email.com", + "phone": "1234567890", + "note": "Note content", + "address": "address", + "city": "city", + "state": "ST", + "zip": "12345", + "country": "US", + "companyName": "company name", + "createdOn": 1752263442, + "properties": [], + "tags": [ + "tag01", + "tag02" + ], + "subscribed": true, + "subscriberLists": [ + 0 + ] +} \ No newline at end of file diff --git a/components/indiefunnels/sources/new-booking-session-instant/new-booking-session-instant.mjs b/components/indiefunnels/sources/new-booking-session-instant/new-booking-session-instant.mjs new file mode 100644 index 0000000000000..fb7e5a91dadbd --- /dev/null +++ b/components/indiefunnels/sources/new-booking-session-instant/new-booking-session-instant.mjs @@ -0,0 +1,23 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "indiefunnels-new-booking-session-instant", + name: "New Booking Session (Instant)", + description: "Emit new event when a new session is booked from a client.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + getEvent() { + return [ + "booking_created", + ]; + }, + getSummary(details) { + return `New booking session with ID: ${details.id}`; + }, + }, + sampleEmit, +}; diff --git a/components/indiefunnels/sources/new-booking-session-instant/test-event.mjs b/components/indiefunnels/sources/new-booking-session-instant/test-event.mjs new file mode 100644 index 0000000000000..6665f4a5c23b8 --- /dev/null +++ b/components/indiefunnels/sources/new-booking-session-instant/test-event.mjs @@ -0,0 +1,45 @@ +export default { + "id": 1, + "eventId": 1087, + "start": 1752521400, + "end": 1752522300, + "eventData": { + "name": "Event Name" + }, + "operator": { + "id": 13081, + "name": "Operator Name", + "email": "admin@domain.com" + }, + "fromAnswers": [ + { + "value": "booking customer", + "field": "Name" + }, + { + "value": "customer@booking.com", + "field": "E-Mail" + } + ], + "contact": { + "id": 9, + "name": "booking customer", + "email": "customer@booking.com", + "phone": null, + "note": null, + "address": null, + "city": null, + "state": null, + "zip": null, + "country": null, + "companyName": null, + "createdOn": 1752521184, + "properties": [], + "tags": [ + "booking" + ], + "memberId": 5, + "subscribed": false, + "subscriberLists": [] + } +} \ No newline at end of file diff --git a/components/indiefunnels/sources/new-form-submission-instant/new-form-submission-instant.mjs b/components/indiefunnels/sources/new-form-submission-instant/new-form-submission-instant.mjs new file mode 100644 index 0000000000000..686c0774eadd1 --- /dev/null +++ b/components/indiefunnels/sources/new-form-submission-instant/new-form-submission-instant.mjs @@ -0,0 +1,23 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "indiefunnels-new-form-submission-instant", + name: "New Form Submission (Instant)", + description: "Emit new event when a form is submitted.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + getEvent() { + return [ + "form_submitted", + ]; + }, + getSummary({ website }) { + return `New form submission from website: ${website.systemDomain}`; + }, + }, + sampleEmit, +}; diff --git a/components/indiefunnels/sources/new-form-submission-instant/test-event.mjs b/components/indiefunnels/sources/new-form-submission-instant/test-event.mjs new file mode 100644 index 0000000000000..067151a201e9a --- /dev/null +++ b/components/indiefunnels/sources/new-form-submission-instant/test-event.mjs @@ -0,0 +1,45 @@ +export default { + "website": { + "id": 1467854, + "subdomain": "subdomain", + "systemDomain": "subdomain.indiefunnels.eu", + "domain": null + }, + "contact": { + "id": 11, + "name": "Contact Name", + "email": "contact@email.com", + "phone": null, + "note": null, + "address": null, + "city": null, + "state": null, + "zip": null, + "country": null, + "companyName": null, + "createdOn": 1752521431, + "properties": [], + "tags": [], + "subscribed": false, + "subscriberLists": [] + }, + "formName": "Form", + "formValues": [ + { + "value": "Contact Name", + "field": "Name" + }, + { + "value": "contact@email.com", + "field": "E-Mail" + }, + { + "value": "Subject content", + "field": "Subject" + }, + { + "value": "Message content", + "field": "Message" + } + ] +} \ No newline at end of file diff --git a/components/indiefunnels/sources/new-order-instant/new-order-instant.mjs b/components/indiefunnels/sources/new-order-instant/new-order-instant.mjs new file mode 100644 index 0000000000000..fd326348a3170 --- /dev/null +++ b/components/indiefunnels/sources/new-order-instant/new-order-instant.mjs @@ -0,0 +1,23 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "indiefunnels-new-order-instant", + name: "New Order (Instant)", + description: "Emit new event when a new order is created.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + getEvent() { + return [ + "order_created", + ]; + }, + getSummary({ id }) { + return `New order with ID: ${id}`; + }, + }, + sampleEmit, +}; diff --git a/components/indiefunnels/sources/new-order-instant/test-event.mjs b/components/indiefunnels/sources/new-order-instant/test-event.mjs new file mode 100644 index 0000000000000..d4225774e9e08 --- /dev/null +++ b/components/indiefunnels/sources/new-order-instant/test-event.mjs @@ -0,0 +1,74 @@ +export default { + "id": 2, + "contactId": 11, + "memberId": 0, + "token": "c8c7bb62-a014-4e74-a1fd-96bd7c718a94", + "invoiceNo": 1, + "created": 1752522276, + "createdTimeFormatted": "14/07/2025 16:44", + "customerName": "Customer Name", + "customerEmail": "customer@email.com", + "billingAddress": { + "name": "", + "phone": "", + "companyName": "", + "companyId": "", + "vatId": "", + "country": "DE", + "state": "", + "city": "", + "zipCode": "", + "address": "", + "address2": "" + }, + "shippingAddress": { + "name": "", + "phone": "", + "companyName": "", + "companyId": "", + "vatId": "", + "country": "DE", + "state": "", + "city": "", + "zipCode": "", + "address": "", + "address2": "" + }, + "shippingRequired": false, + "weight": 0, + "weightUnit": "kg", + "subTotal": 0, + "total": 0, + "discountCode": null, + "discountType": null, + "transactionId": "", + "shippingAmount": 0, + "shippingName": "", + "paid": true, + "paymentMethod": "no-payment", + "status": "completed", + "discountAmount": 0, + "taxes": [], + "items": [ + { + "id": 2, + "name": "Product Name", + "url": "product-name", + "productId": 20, + "sku": "", + "quantity": 1, + "weight": 0, + "total": 0, + "type": "service", + "shippingRequired": false, + "images": [], + "variation": [], + "additions": [] + } + ], + "tags": [], + "additionalFields": null, + "currency": "EUR", + "fulfillment": null, + "notes": [] +} \ No newline at end of file From cee0992a2c41348f9294d8b507dd3550dc6d73a5 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 14 Jul 2025 16:51:59 -0300 Subject: [PATCH 2/5] pnpm update --- pnpm-lock.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a5fbd3e91db8..1ae6795533794 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6652,7 +6652,11 @@ importers: specifier: ^3.0.0 version: 3.0.3 - components/indiefunnels: {} + components/indiefunnels: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/inferable: {} @@ -14577,8 +14581,7 @@ importers: specifier: ^1.2.5 version: 1.2.7 - components/viewdns_info: - specifiers: {} + components/viewdns_info: {} components/vimeo: dependencies: @@ -37186,6 +37189,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 dfd7b79991ef1df2b4754b0f97800513a17bf350 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 15 Jul 2025 11:01:15 -0300 Subject: [PATCH 3/5] Fix typos in descriptions and improve webhook ID handling in indiefunnels component --- components/indiefunnels/indiefunnels.app.mjs | 8 ++++---- components/indiefunnels/sources/common/base.mjs | 12 ++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/components/indiefunnels/indiefunnels.app.mjs b/components/indiefunnels/indiefunnels.app.mjs index 359ff460e25f9..021a001da9c37 100644 --- a/components/indiefunnels/indiefunnels.app.mjs +++ b/components/indiefunnels/indiefunnels.app.mjs @@ -8,7 +8,7 @@ export default { memberId: { type: "string", label: "Member ID", - description: "The Id of the member to update", + description: "The ID of the member to update", async options({ page }) { const { items } = await this.listMembers({ params: { @@ -27,7 +27,7 @@ export default { contactId: { type: "string", label: "Contact ID", - description: "Contact id of the member", + description: "Contact ID of the member", async options({ page }) { const { items } = await this.listContacts({ params: { @@ -47,7 +47,7 @@ export default { groups: { type: "string[]", label: "Member Groups", - description: "Member group id(s) to which the member belong", + description: "Member group ID(s) to which the member belongs", async options() { const items = await this.listMemberGroups(); @@ -242,7 +242,7 @@ export default { _headers() { return { "Authorization": `Bearer ${this.$auth.api_key}`, - "user-agent ": "@PipedreamHQ/pipedream v0.1", + "user-agent": "@PipedreamHQ/pipedream v0.1", }; }, _makeRequest({ diff --git a/components/indiefunnels/sources/common/base.mjs b/components/indiefunnels/sources/common/base.mjs index 4fdc374142d23..44f5fbd6020c8 100644 --- a/components/indiefunnels/sources/common/base.mjs +++ b/components/indiefunnels/sources/common/base.mjs @@ -7,6 +7,14 @@ export default { db: "$.service.db", http: "$.interface.http", }, + methods: { + _setWebhookId(id) { + this.db.set("webhookId", id); + }, + _getWebhookId() { + return this.db.get("webhookId"); + }, + }, hooks: { async activate() { const response = await this.indiefunnels.createHook({ @@ -16,10 +24,10 @@ export default { events: this.getEvent(), }, }); - this.db.set("webhookId", response.id); + this._setWebhookId(response.id); }, async deactivate() { - const webhookId = this.db.get("webhookId"); + const webhookId = this._getWebhookId(); await this.indiefunnels.deleteHook(webhookId); }, }, From 31fa1cb4bf0fc55c38f47d958b4d6200d267aa58 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 17 Jul 2025 16:30:09 -0300 Subject: [PATCH 4/5] Add common methods to indiefunnels sources for consistency --- .../sources/contact-updated-instant/contact-updated-instant.mjs | 1 + .../new-booking-session-instant/new-booking-session-instant.mjs | 1 + .../new-form-submission-instant/new-form-submission-instant.mjs | 1 + .../indiefunnels/sources/new-order-instant/new-order-instant.mjs | 1 + 4 files changed, 4 insertions(+) diff --git a/components/indiefunnels/sources/contact-updated-instant/contact-updated-instant.mjs b/components/indiefunnels/sources/contact-updated-instant/contact-updated-instant.mjs index 8ac8bbba6b81c..d0e592c6796f8 100644 --- a/components/indiefunnels/sources/contact-updated-instant/contact-updated-instant.mjs +++ b/components/indiefunnels/sources/contact-updated-instant/contact-updated-instant.mjs @@ -10,6 +10,7 @@ export default { type: "source", dedupe: "unique", methods: { + ...common.methods, getEvent() { return [ "contact_updated", diff --git a/components/indiefunnels/sources/new-booking-session-instant/new-booking-session-instant.mjs b/components/indiefunnels/sources/new-booking-session-instant/new-booking-session-instant.mjs index fb7e5a91dadbd..613dadc067807 100644 --- a/components/indiefunnels/sources/new-booking-session-instant/new-booking-session-instant.mjs +++ b/components/indiefunnels/sources/new-booking-session-instant/new-booking-session-instant.mjs @@ -10,6 +10,7 @@ export default { type: "source", dedupe: "unique", methods: { + ...common.methods, getEvent() { return [ "booking_created", diff --git a/components/indiefunnels/sources/new-form-submission-instant/new-form-submission-instant.mjs b/components/indiefunnels/sources/new-form-submission-instant/new-form-submission-instant.mjs index 686c0774eadd1..c31af535712cc 100644 --- a/components/indiefunnels/sources/new-form-submission-instant/new-form-submission-instant.mjs +++ b/components/indiefunnels/sources/new-form-submission-instant/new-form-submission-instant.mjs @@ -10,6 +10,7 @@ export default { type: "source", dedupe: "unique", methods: { + ...common.methods, getEvent() { return [ "form_submitted", diff --git a/components/indiefunnels/sources/new-order-instant/new-order-instant.mjs b/components/indiefunnels/sources/new-order-instant/new-order-instant.mjs index fd326348a3170..dae8d39aed02c 100644 --- a/components/indiefunnels/sources/new-order-instant/new-order-instant.mjs +++ b/components/indiefunnels/sources/new-order-instant/new-order-instant.mjs @@ -10,6 +10,7 @@ export default { type: "source", dedupe: "unique", methods: { + ...common.methods, getEvent() { return [ "order_created", From f5cfecabfe49031e9bdae796c8b015c05345c91d Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 17 Jul 2025 16:36:54 -0300 Subject: [PATCH 5/5] pnpm update --- pnpm-lock.yaml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e6785fd05830..4ccff1511a26c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -715,8 +715,7 @@ importers: components/alt_text_generator_ai: {} - components/alt_text_lab: - specifiers: {} + components/alt_text_lab: {} components/alteryx_analytics_cloud: {} @@ -10166,8 +10165,7 @@ importers: specifier: ^6.11.1 version: 6.13.1 - components/pingback: - specifiers: {} + components/pingback: {} components/pingbell: dependencies: @@ -29927,22 +29925,22 @@ packages: superagent@3.8.1: resolution: {integrity: sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==} engines: {node: '>= 4.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@4.1.0: resolution: {integrity: sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==} engines: {node: '>= 6.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@5.3.1: resolution: {integrity: sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==} engines: {node: '>= 7.0.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@7.1.6: resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please downgrade to v7.1.5 if you need IE/ActiveXObject support OR upgrade to v8.0.0 as we no longer support IE and published an incorrect patch version (see https://github.com/visionmedia/superagent/issues/1731) + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}