diff --git a/components/leaddyno/actions/create-affiliate/create-affiliate.mjs b/components/leaddyno/actions/create-affiliate/create-affiliate.mjs new file mode 100644 index 0000000000000..6a01c9799d90d --- /dev/null +++ b/components/leaddyno/actions/create-affiliate/create-affiliate.mjs @@ -0,0 +1,78 @@ +import leaddyno from "../../leaddyno.app.mjs"; + +export default { + key: "leaddyno-create-affiliate", + name: "Create Affiliate", + description: "Creates a new affiliate in LeadDyno. [See the documentation](https://app.theneo.io/leaddyno/leaddyno-rest-api/affiliates/post-affiliates)", + version: "0.0.1", + type: "action", + props: { + leaddyno, + email: { + type: "string", + label: "Email", + description: "The email address of the affiliate", + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the affiliate", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the affiliate", + optional: true, + }, + affiliateCode: { + type: "string", + label: "Affiliate Code", + description: "A custom affiliate code for the new affiliate", + optional: true, + }, + paypalEmail: { + type: "string", + label: "PayPal Email", + description: "The PayPal email address for affiliate payouts", + optional: true, + }, + unsubscribed: { + type: "boolean", + label: "Unsubscribed", + description: "Indicates whether the affiliate is unsubscribed from communications", + optional: true, + }, + affiliateType: { + type: "string", + label: "Affiliate Type", + description: "The code for the affiliate's group", + optional: true, + }, + overrideApproval: { + type: "boolean", + label: "Override Approval", + description: "If set to true, the affiliate will not require approval", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.leaddyno.createAffiliate({ + $, + data: { + email: this.email, + first_name: this.firstName, + last_name: this.lastName, + affiliate_code: this.affiliateCode, + paypal_email: this.paypalEmail, + unsubscribed: this.unsubscribed, + affiliate_type: this.affiliateType, + override_approval: this.overrideApproval, + }, + }); + + $.export("$summary", `Successfully created affiliate with ID ${response.id}`); + + return response; + }, +}; diff --git a/components/leaddyno/actions/create-lead/create-lead.mjs b/components/leaddyno/actions/create-lead/create-lead.mjs new file mode 100644 index 0000000000000..aa04e9ac49178 --- /dev/null +++ b/components/leaddyno/actions/create-lead/create-lead.mjs @@ -0,0 +1,108 @@ +import leaddyno from "../../leaddyno.app.mjs"; + +export default { + key: "leaddyno-create-lead", + name: "Create Lead", + description: "Creates a new lead in LeadDyno. [See the documentation](https://app.theneo.io/leaddyno/leaddyno-rest-api/leaddyno-api#POSTCreate-a-lead)", + version: "0.0.1", + type: "action", + props: { + leaddyno, + email: { + type: "string", + label: "Email", + description: "The email address of the lead", + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the lead", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the lead", + optional: true, + }, + address1: { + type: "string", + label: "Address 1", + description: "The first line of the address of the lead", + optional: true, + }, + address2: { + type: "string", + label: "Address 2", + description: "The second line of the address of the lead", + optional: true, + }, + city: { + type: "string", + label: "City", + description: "The city of the lead", + optional: true, + }, + state: { + type: "string", + label: "State", + description: "The state of the lead", + optional: true, + }, + zipcode: { + type: "string", + label: "Zipcode", + description: "The zipcode of the lead", + optional: true, + }, + country: { + type: "string", + label: "Country", + description: "The country of the lead", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the lead", + optional: true, + }, + affiliate: { + propDefinition: [ + leaddyno, + "affiliate", + ], + optional: true, + }, + customStatus: { + type: "string", + label: "Custom Status", + description: "The custom status of the lead", + optional: true, + }, + }, + async run({ $ }) { + + const response = await this.leaddyno.createLead({ + $, + data: { + email: this.email, + first_name: this.firstName, + last_name: this.lastName, + address1: this.address1, + address2: this.address2, + city: this.city, + state: this.state, + zipcode: this.zipcode, + country: this.country, + phone: this.phone, + affiliate: this.affiliate, + custom_status: this.customStatus, + }, + }); + + $.export("$summary", `Successfully created lead with email ${this.email}`); + + return response; + }, +}; diff --git a/components/leaddyno/actions/create-purchase/create-purchase.mjs b/components/leaddyno/actions/create-purchase/create-purchase.mjs new file mode 100644 index 0000000000000..feee37191e9bd --- /dev/null +++ b/components/leaddyno/actions/create-purchase/create-purchase.mjs @@ -0,0 +1,88 @@ +import { parseObject } from "../../common/utils.mjs"; +import leaddyno from "../../leaddyno.app.mjs"; + +export default { + key: "leaddyno-create-purchase", + name: "Create Purchase", + description: "Creates a new purchase in LeadDyno. [See the documentation](https://app.theneo.io/leaddyno/leaddyno-rest-api/purchases/post-purchases)", + version: "0.0.1", + type: "action", + props: { + leaddyno, + email: { + propDefinition: [ + leaddyno, + "leadEmail", + ], + }, + purchaseCode: { + type: "string", + label: "Purchase Code", + description: "A unique identifier for this purchase. If not provided, a unique ID will be generated", + optional: true, + }, + purchaseAmount: { + type: "string", + label: "Purchase Amount", + description: "The total amount of the purchase, used for percentage commission calculations", + optional: true, + }, + planCode: { + type: "string", + label: "Plan Code", + description: "The code of the reward structure used for calculating affiliate commissions", + optional: true, + }, + affiliateCode: { + propDefinition: [ + leaddyno, + "affiliateCode", + ], + optional: true, + }, + commissionAmount: { + type: "string", + label: "Commission Amount Override", + description: "An overriding commission amount that will replace any predefined plan and provide an immediate fixed-amount commission. This value should be a decimal", + optional: true, + }, + description: { + type: "string", + label: "Description", + description: "Text description of the purchase", + optional: true, + }, + reassignAffiliate: { + type: "boolean", + label: "Reassign Affiliate", + description: "If set to false, the original affiliate of the lead will be retained.", + optional: true, + }, + lineItems: { + type: "string[]", + label: "Line Items", + description: "A list of JSON object containing the line items associated with the purchase. **Format: [{\"sku\": \"string\", \"description\": \"string\", \"quantity\": \"string\", \"amount\": \"string\"}]**", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.leaddyno.createPurchase({ + $, + data: { + email: this.email, + purchase_code: this.purchaseCode, + purchase_amount: this.purchaseAmount && parseFloat(this.purchaseAmount), + plan_code: this.planCode, + code: this.affiliateCode, + commission_amount_override: this.commissionAmount && parseFloat(this.commissionAmount), + description: this.description, + reassign_affiliate: this.reassignAffiliate, + line_items: parseObject(this.lineItems), + }, + }); + + $.export("$summary", `Successfully created purchase with ID ${response.id}`); + + return response; + }, +}; diff --git a/components/leaddyno/actions/retrieve-lead/retrieve-lead.mjs b/components/leaddyno/actions/retrieve-lead/retrieve-lead.mjs new file mode 100644 index 0000000000000..6d17b8da07f07 --- /dev/null +++ b/components/leaddyno/actions/retrieve-lead/retrieve-lead.mjs @@ -0,0 +1,28 @@ +import leaddyno from "../../leaddyno.app.mjs"; + +export default { + key: "leaddyno-retrieve-lead", + name: "Retrieve Lead", + description: "Retrieves information about a lead from LeadDyno. [See the documentation](https://app.theneo.io/leaddyno/leaddyno-rest-api/leads/retrieve-a-lead-by-id)", + version: "0.0.1", + type: "action", + props: { + leaddyno, + leadId: { + propDefinition: [ + leaddyno, + "leadId", + ], + description: "The ID of the lead to retrieve", + }, + }, + async run({ $ }) { + const response = await this.leaddyno.getLead({ + $, + leadId: this.leadId, + }); + + $.export("$summary", `Successfully retrieved lead with ID ${this.leadId}`); + return response; + }, +}; diff --git a/components/leaddyno/common/utils.mjs b/components/leaddyno/common/utils.mjs new file mode 100644 index 0000000000000..dcc9cc61f6f41 --- /dev/null +++ b/components/leaddyno/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/leaddyno/leaddyno.app.mjs b/components/leaddyno/leaddyno.app.mjs index 65c90c8f99158..34513ad012056 100644 --- a/components/leaddyno/leaddyno.app.mjs +++ b/components/leaddyno/leaddyno.app.mjs @@ -1,11 +1,162 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "leaddyno", - propDefinitions: {}, + propDefinitions: { + leadId: { + type: "string", + label: "Lead ID", + description: "The ID of the lead to retrieve", + async options({ page }) { + const data = await this.listLeads({ + params: { + page: page + 1, + }, + }); + + return data.map(({ + id: value, email: label, + }) => ({ + label, + value, + })); + }, + }, + leadEmail: { + type: "string", + label: "Lead Email", + description: "The email of the customer", + async options({ page }) { + const data = await this.listLeads({ + params: { + page: page + 1, + }, + }); + + return data.map(({ email }) => email); + }, + }, + affiliate: { + type: "string", + label: "Affiliate", + description: "The affiliate which the lead belongs to", + async options({ page }) { + const data = await this.listAffiliates({ + params: { + page: page + 1, + }, + }); + return data.map(({ email }) => email); + }, + }, + affiliateCode: { + type: "string", + label: "Affiliate Code", + description: "The affiliate code to which the purchase should be assigned. Its usage depends on the 'first source wins' or 'first affiliate wins' settings", + async options({ page }) { + const data = await this.listAffiliates({ + params: { + page: page + 1, + }, + }); + return data.map((item) => ({ + label: `${item.first_name} ${item.last_name} (${item.email})`, + value: item.affiliate_code, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.leaddyno.com/v1"; + }, + _params(params = {}) { + return { + key: `${this.$auth.api_key}`, + ...params, + }; + }, + _makeRequest({ + $ = this, path, params, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + params: this._params(params), + ...opts, + }); + }, + listLeads(opts = {}) { + return this._makeRequest({ + path: "/leads", + ...opts, + }); + }, + getLead({ + leadId, ...opts + }) { + return this._makeRequest({ + path: `/leads/${leadId}`, + ...opts, + }); + }, + listAffiliates(opts = {}) { + return this._makeRequest({ + path: "/affiliates", + ...opts, + }); + }, + createAffiliate(opts = {}) { + return this._makeRequest({ + path: "/affiliates", + method: "POST", + ...opts, + }); + }, + createPurchase(opts = {}) { + return this._makeRequest({ + path: "/purchases", + method: "POST", + ...opts, + }); + }, + listPurchases(opts = {}) { + return this._makeRequest({ + path: "/purchases", + ...opts, + }); + }, + createLead(opts = {}) { + return this._makeRequest({ + path: "/leads", + method: "POST", + ...opts, + }); + }, + async *paginate({ + fn, params = {}, maxResults = null, ...opts + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params.page = ++page; + const data = await fn({ + params, + ...opts, + }); + for (const d of data) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = data.length; + + } while (hasMore); }, }, -}; \ No newline at end of file +}; diff --git a/components/leaddyno/package.json b/components/leaddyno/package.json index 2d96c0ae41386..76d44cac35445 100644 --- a/components/leaddyno/package.json +++ b/components/leaddyno/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/leaddyno", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream LeadDyno Components", "main": "leaddyno.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/leaddyno/sources/common/base.mjs b/components/leaddyno/sources/common/base.mjs new file mode 100644 index 0000000000000..79ac7c59c569d --- /dev/null +++ b/components/leaddyno/sources/common/base.mjs @@ -0,0 +1,59 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import leaddyno from "../../leaddyno.app.mjs"; + +export default { + props: { + leaddyno, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastId() { + return this.db.get("lastId") || 0; + }, + _setLastId(lastId) { + this.db.set("lastId", lastId); + }, + async emitEvent(maxResults = false) { + const lastId = this._getLastId(); + + const response = this.leaddyno.paginate({ + fn: this.getFunction(), + }); + + let responseArray = []; + for await (const item of response) { + if (item.id <= lastId) break; + responseArray.push(item); + } + + if (responseArray.length) { + if (maxResults && (responseArray.length > maxResults)) { + responseArray.length = maxResults; + } + this._setLastId(responseArray[0].id); + } + + for (const item of responseArray.reverse()) { + this.$emit(item, { + id: item.id, + summary: this.getSummary(item), + ts: Date.parse(item.created || new Date()), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/leaddyno/sources/new-affiliates/new-affiliates.mjs b/components/leaddyno/sources/new-affiliates/new-affiliates.mjs new file mode 100644 index 0000000000000..fa2e02823cdb9 --- /dev/null +++ b/components/leaddyno/sources/new-affiliates/new-affiliates.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "leaddyno-new-affiliates", + name: "New Affiliates", + description: "Emit new event when a new affiliate is created in LeadDyno. [See the documentation](https://app.theneo.io/leaddyno/leaddyno-rest-api/affiliates/get-affiliates)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFunction() { + return this.leaddyno.listAffiliates; + }, + getSummary(item) { + return `New Affiliate: ${item.email}`; + }, + }, + sampleEmit, +}; diff --git a/components/leaddyno/sources/new-affiliates/test-event.mjs b/components/leaddyno/sources/new-affiliates/test-event.mjs new file mode 100644 index 0000000000000..ff649d4def43a --- /dev/null +++ b/components/leaddyno/sources/new-affiliates/test-event.mjs @@ -0,0 +1,26 @@ +export default { + "id": 17175, + "email": "affiliate@email.com", + "first_name": "Johnny", + "last_name": "Doe", + "affiliate_code": "1h", + "referring_affiliate_id": null, + "created_at": "2024-08-05T13:40:01Z", + "updated_at": "2024-10-16T17:10:59Z", + "status": "Active", + "paypal_email": "affiliate_payment@email.com", + "unsubscribed": false, + "archived": false, + "pending_approval": false, + "affiliate_url": "http://www.affiliate.dashboard.com/discount/1h?redirect=%2F2025%3Fafmc%3D1h", + "affiliate_dashboard_url": "https://affiliate.dashboard.com/p/845cd40ce6da84415af84a7f5ff8c92f9a2c23ee", + "compensation_tier_code": "V.I.P.", + "custom_fields": { + "custom": "fields" + }, + "referring_affiliate": null, + "referrer_domain": "https://referrer.example.com/", + "total_leads": 2, + "total_visitors": null, + "total_purchases": 1 +}; \ No newline at end of file diff --git a/components/leaddyno/sources/new-leads/new-leads.mjs b/components/leaddyno/sources/new-leads/new-leads.mjs new file mode 100644 index 0000000000000..1c21bdcbc7ba4 --- /dev/null +++ b/components/leaddyno/sources/new-leads/new-leads.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "leaddyno-new-leads", + name: "New Leads", + description: "Emit new event when a new lead is created in LeadDyno. [See the documentation](https://app.theneo.io/leaddyno/leaddyno-rest-api/leads/list-all-leads)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFunction() { + return this.leaddyno.listLeads; + }, + getSummary(item) { + return `New Lead: ${item.email}`; + }, + }, + sampleEmit, +}; diff --git a/components/leaddyno/sources/new-leads/test-event.mjs b/components/leaddyno/sources/new-leads/test-event.mjs new file mode 100644 index 0000000000000..3535afc272eac --- /dev/null +++ b/components/leaddyno/sources/new-leads/test-event.mjs @@ -0,0 +1,27 @@ +export default { + "id": 2150, + "email": "lead-2024@example.com", + "created_at": "2024-09-24T03:01:12Z", + "updated_at": "2024-09-24T03:01:12Z", + "status": "Registered", + "custom_status": "", + "first_name": "Firstname", + "last_name": "Lastname", + "address1": null, + "address2": null, + "city": null, + "state": null, + "zipcode": null, + "country": null, + "latest_visitor": null, + "url": null, + "referrer": null, + "search_term": null, + "affiliate": { + "id": 17171, + "email": "affiliate@example.com" + }, + "campaign": null, + "tracking_code": null, + "crm_data": {} +}; \ No newline at end of file diff --git a/components/leaddyno/sources/new-purchases/new-purchases.mjs b/components/leaddyno/sources/new-purchases/new-purchases.mjs new file mode 100644 index 0000000000000..1f59f0da903d2 --- /dev/null +++ b/components/leaddyno/sources/new-purchases/new-purchases.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "leaddyno-new-purchases", + name: "New Purchases", + description: "Emit new event when a new purchase is created in LeadDyno. [See the documentation](https://app.theneo.io/leaddyno/leaddyno-rest-api/purchases/get-purchases)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFunction() { + return this.leaddyno.listPurchases; + }, + getSummary(item) { + return `New Purchase: ${item.purchase_code} - $${item.purchase_amount}`; + }, + }, + sampleEmit, +}; diff --git a/components/leaddyno/sources/new-purchases/test-event.mjs b/components/leaddyno/sources/new-purchases/test-event.mjs new file mode 100644 index 0000000000000..f4aa24659c85b --- /dev/null +++ b/components/leaddyno/sources/new-purchases/test-event.mjs @@ -0,0 +1,40 @@ +export default { + "id": 794, + "created_at": "2024-10-09T19:30:00Z", + "updated_at": "2024-10-09T19:30:00Z", + "cancelled": false, + "currency": "USD", + "purchase_code": "04540cce-a364-4751-93a5-abcdefghijkl", + "note": null, + "purchase_amount": 100, + "commission_amount_override": null, + "cancellation": { + "id": 30, + "cancellation_code": "abc", + "effective_date": "2024-10-09T19:58:18Z", + "source": null + }, + "lead": { + "id": 1417, + "email": "joe@foo.com" + }, + "affiliate": null, + "plan": { + "id": 1, + "code": "Default" + }, + "line_items": [ + { + "sku": "SKU-184", + "description": "Monthly Subscription.", + "quantity": "7.0", + "amount": "80.97" + }, + { + "sku": "SKU-369", + "description": "Add-Ons.", + "quantity": "2.0", + "amount": "9.52" + } + ] +}; \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e0b85eb1fdfc9..74df143d11f07 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7298,7 +7298,11 @@ importers: components/leadboxer: {} - components/leaddyno: {} + components/leaddyno: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/leadfeeder: dependencies: