From dc5d12ebe3e34670cf8eb77ff593bff9db545a09 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Wed, 14 May 2025 13:10:08 -0400 Subject: [PATCH 1/2] new components --- .../actions/create-company/create-company.mjs | 120 +++++++++++++++ .../actions/create-contact/create-contact.mjs | 132 +++++++++++++++++ .../actions/create-deal/create-deal.mjs | 72 +++++++++ components/companyhub/companyhub.app.mjs | 137 +++++++++++++++++- components/companyhub/package.json | 2 +- components/companyhub/sources/common/base.mjs | 83 +++++++++++ .../new-company-created.mjs | 22 +++ .../new-company-created/test-event.mjs | 36 +++++ .../new-deal-created/new-deal-created.mjs | 22 +++ .../sources/new-deal-created/test-event.mjs | 35 +++++ .../new-record-created/new-record-created.mjs | 33 +++++ 11 files changed, 689 insertions(+), 5 deletions(-) create mode 100644 components/companyhub/actions/create-company/create-company.mjs create mode 100644 components/companyhub/actions/create-contact/create-contact.mjs create mode 100644 components/companyhub/actions/create-deal/create-deal.mjs create mode 100644 components/companyhub/sources/common/base.mjs create mode 100644 components/companyhub/sources/new-company-created/new-company-created.mjs create mode 100644 components/companyhub/sources/new-company-created/test-event.mjs create mode 100644 components/companyhub/sources/new-deal-created/new-deal-created.mjs create mode 100644 components/companyhub/sources/new-deal-created/test-event.mjs create mode 100644 components/companyhub/sources/new-record-created/new-record-created.mjs diff --git a/components/companyhub/actions/create-company/create-company.mjs b/components/companyhub/actions/create-company/create-company.mjs new file mode 100644 index 0000000000000..b22342b0078b7 --- /dev/null +++ b/components/companyhub/actions/create-company/create-company.mjs @@ -0,0 +1,120 @@ +import companyhub from "../../companyhub.app.mjs"; + +export default { + key: "companyhub-create-company", + name: "Create Company", + description: "Creates a new company. [See the documentation](https://companyhub.com/docs/api-documentation)", + version: "0.0.1", + type: "action", + props: { + companyhub, + name: { + type: "string", + label: "Name", + description: "The name of the company", + }, + website: { + type: "string", + label: "Website", + description: "The website URL of the company", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the company", + optional: true, + }, + description: { + type: "string", + label: "Description", + description: "A description of the company", + optional: true, + }, + billingStreet: { + type: "string", + label: "Billing Street", + description: "The billing street address", + optional: true, + }, + billingCity: { + type: "string", + label: "Billing City", + description: "The billing city", + optional: true, + }, + billingState: { + type: "string", + label: "Billing State", + description: "The billing state/province", + optional: true, + }, + billingCountry: { + type: "string", + label: "Billing Country", + description: "The billing country", + optional: true, + }, + billingPostalCode: { + type: "string", + label: "Billing Postal Code", + description: "The billing postal/zip code", + optional: true, + }, + shippingStreet: { + type: "string", + label: "Shipping Street", + description: "The shipping street address", + optional: true, + }, + shippingCity: { + type: "string", + label: "Shipping City", + description: "The shipping city", + optional: true, + }, + shippingState: { + type: "string", + label: "Shipping State", + description: "The shipping state/province", + optional: true, + }, + shippingCountry: { + type: "string", + label: "Shipping Country", + description: "The shipping country", + optional: true, + }, + shippingPostalCode: { + type: "string", + label: "Shipping Postal Code", + description: "The shipping postal/zip code", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.companyhub.createCompany({ + $, + data: { + Name: this.name, + Website: this.website, + Phone: this.phone, + Description: this.description, + BillingStreet: this.billingStreet, + BillingCity: this.billingCity, + BillingState: this.billingState, + BillingCountry: this.billingCountry, + BillingPostalCode: this.billingPostalCode, + ShippingStreet: this.shippingStreet, + ShippingCity: this.shippingCity, + ShippingState: this.shippingState, + ShippingCountry: this.shippingCountry, + ShippingPostalCode: this.shippingPostalCode, + }, + }); + if (response.Success) { + $.export("$summary", `Successfully created company with ID: ${response.Id}`); + } + return response; + }, +}; diff --git a/components/companyhub/actions/create-contact/create-contact.mjs b/components/companyhub/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..d13cb4cebec8f --- /dev/null +++ b/components/companyhub/actions/create-contact/create-contact.mjs @@ -0,0 +1,132 @@ +import companyhub from "../../companyhub.app.mjs"; + +export default { + key: "companyhub-create-contact", + name: "Create Contact", + description: "Creates a new contact. [See the documentation](https://companyhub.com/docs/api-documentation)", + version: "0.0.1", + type: "action", + props: { + companyhub, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the contact", + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the contact", + }, + email: { + type: "string", + label: "Email", + description: "The email address of the contact", + optional: true, + }, + companyId: { + propDefinition: [ + companyhub, + "companyId", + ], + }, + phone: { + type: "string", + label: "Phone Number", + description: "The phone number of the contact", + optional: true, + }, + designation: { + type: "string", + label: "Designation", + description: "The designation of the contact", + optional: true, + }, + description: { + type: "string", + label: "Description", + description: "The description of the contact", + optional: true, + }, + source: { + type: "string", + label: "Source", + description: "The source of the contact", + optional: true, + options: [ + "Web", + "Call", + "Referral", + "Other", + ], + }, + nextFollowUpDate: { + type: "string", + label: "Next Follow Up Date", + description: "The next follow up date with the contact. E.g. `2025-03-14T00:00:00`", + optional: true, + }, + hasOptedOutOfEmails: { + type: "boolean", + label: "Has Opted Out of Emails", + description: "Whether the contact has opted out of emails", + optional: true, + }, + street: { + type: "string", + label: "Street Address", + description: "The street address of the contact", + optional: true, + }, + city: { + type: "string", + label: "City", + description: "The city where the contact is located", + optional: true, + }, + state: { + type: "string", + label: "State/Province", + description: "The state or province where the contact is located", + optional: true, + }, + country: { + type: "string", + label: "Country", + description: "The country where the contact is located", + optional: true, + }, + postalCode: { + type: "string", + label: "Postal Code", + description: "The postal code of the contact's address", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.companyhub.createContact({ + $, + data: { + FirstName: this.firstName, + LastName: this.lastName, + Email: this.email, + Company: this.companyId, + Phone: this.phone, + Designation: this.designation, + Description: this.description, + Source: this.source, + NextFollowUpDate: this.nextFollowUpDate, + HasOptedOutOfEmails: this.hasOptedOutOfEmails, + Street: this.street, + City: this.city, + State: this.state, + Country: this.country, + PostalCode: this.postalCode, + }, + }); + if (response.Success) { + $.export("$summary", `Successfully created contact with ID: ${response.Id}`); + } + return response; + }, +}; diff --git a/components/companyhub/actions/create-deal/create-deal.mjs b/components/companyhub/actions/create-deal/create-deal.mjs new file mode 100644 index 0000000000000..84fdf86b2f52f --- /dev/null +++ b/components/companyhub/actions/create-deal/create-deal.mjs @@ -0,0 +1,72 @@ +import companyhub from "../../companyhub.app.mjs"; + +export default { + key: "companyhub-create-deal", + name: "Create Deal", + description: "Creates a new deal. [See the documentation](https://companyhub.com/docs/api-documentation)", + version: "0.0.1", + type: "action", + props: { + companyhub, + name: { + type: "string", + label: "Deal Name", + description: "The name of the deal", + }, + stage: { + type: "string", + label: "Deal Stage", + description: "The stage of the deal", + options: [ + "Prospecting", + "Qualification", + "Discussion", + "Proposal", + "Review", + "Closed Won", + "Closed Lost", + ], + }, + companyId: { + propDefinition: [ + companyhub, + "companyId", + ], + }, + contactId: { + propDefinition: [ + companyhub, + "contactId", + ], + }, + amount: { + type: "string", + label: "Amount", + description: "The amount of the deal", + optional: true, + }, + closeDate: { + type: "string", + label: "Expected Close Date", + description: "The expected close date of the deal in ISO-8601 format. E.g. `2025-03-14T00:00:00`", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.companyhub.createDeal({ + $, + data: { + Name: this.name, + Company: this.companyId, + Contact: this.contactId, + Stage: this.stage, + Amount: this.amount, + CloseDate: this.closeDate, + }, + }); + if (response.Success) { + $.export("$summary", `Successfully created company with ID: ${response.Id}`); + } + return response; + }, +}; diff --git a/components/companyhub/companyhub.app.mjs b/components/companyhub/companyhub.app.mjs index ad5d0a30ce1bd..cc4f2d676d2c2 100644 --- a/components/companyhub/companyhub.app.mjs +++ b/components/companyhub/companyhub.app.mjs @@ -1,11 +1,140 @@ +import { + axios, ConfigurationError, +} from "@pipedream/platform"; + export default { type: "app", app: "companyhub", - propDefinitions: {}, + propDefinitions: { + companyId: { + type: "string", + label: "Company ID", + description: "The ID of the company", + optional: true, + async options({ page }) { + const { Data: companies } = await this.listCompanies({ + params: { + start: page, + }, + }); + return companies?.map(({ + ID: value, Name: label, + }) => ({ + label, + value, + })) || []; + }, + }, + contactId: { + type: "string", + label: "Contact ID", + description: "The ID of the contact", + optional: true, + async options({ page }) { + const { Data: contacts } = await this.listContacts({ + params: { + start: page, + }, + }); + return contacts?.map(({ + ID: value, Name: label, + }) => ({ + label, + value, + })) || []; + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.companyhub.com/v1"; + }, + async _makeRequest({ + $ = this, path, ...otherOpts + }) { + const response = await axios($, { + url: `${this._baseUrl()}${path}`, + headers: { + "Authorization": `${this.$auth.subdomain} ${this.$auth.api_key}`, + "Content-Type": "application/json", + }, + ...otherOpts, + }); + if (response.Errors && Object.keys(response.Errors).length > 0) { + throw new ConfigurationError(JSON.stringify(response.Errors)); + } + return response; + }, + listCompanies(opts = {}) { + return this._makeRequest({ + path: "/tables/Company", + ...opts, + }); + }, + listContacts(opts = {}) { + return this._makeRequest({ + path: "/tables/Contact", + ...opts, + }); + }, + listDeals(opts = {}) { + return this._makeRequest({ + path: "/tables/Deal", + ...opts, + }); + }, + listRecords({ + table, ...opts + }) { + return this._makeRequest({ + path: `/tables/${table}`, + ...opts, + }); + }, + createCompany(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/tables/Company", + ...opts, + }); + }, + createDeal(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/tables/Deal", + ...opts, + }); + }, + createContact(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/tables/Contact", + ...opts, + }); + }, + async *paginate({ + fn, args, max, + }) { + args = { + ...args, + params: { + ...args?.params, + start: 1, + limit: 100, + }, + }; + let hasMore, count = 0; + do { + const response = await fn(args); + for (const item of response.Data) { + yield item; + if (max && ++count >= max) { + return; + } + } + args.params.start += 1; + hasMore = response.HasMore; + } while (hasMore); }, }, }; diff --git a/components/companyhub/package.json b/components/companyhub/package.json index 0e86fc5c7b619..b99b9ccc3fe14 100644 --- a/components/companyhub/package.json +++ b/components/companyhub/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/companyhub", - "version": "0.6.0", + "version": "0.7.0", "description": "Pipedream companyhub Components", "main": "companyhub.app.mjs", "keywords": [ diff --git a/components/companyhub/sources/common/base.mjs b/components/companyhub/sources/common/base.mjs new file mode 100644 index 0000000000000..e0c35a17377d9 --- /dev/null +++ b/components/companyhub/sources/common/base.mjs @@ -0,0 +1,83 @@ +import companyhub from "../../companyhub.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + companyhub, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(ts) { + this.db.set("lastTs", ts); + }, + getArgs() { + return {}; + }, + getTsField() { + return "CreatedOn"; + }, + generateMeta(item) { + return { + id: item.ID, + summary: this.getSummary(item), + ts: Date.parse(item[this.getTsField()]), + }; + }, + async processEvent(max) { + const lastTs = this._getLastTs(); + const fn = this.getResourceFn(); + const args = this.getArgs(); + const tsField = this.getTsField(); + + const results = await this.companyhub.paginate({ + fn, + args, + max, + }); + + const items = []; + for await (const item of results) { + const ts = Date.parse(item[tsField]); + if (ts >= lastTs) { + items.push(item); + } else { + break; + } + } + + if (!items.length) { + return; + } + + this._setLastTs(Date.parse(items[0][tsField])); + + for (const item of items.reverse()) { + const meta = this.generateMeta(item); + this.$emit(item, meta); + } + }, + getResourceFn() { + throw new Error("getResourceFn must be implemented"); + }, + getSummary() { + throw new Error("getSummary must be implemented"); + }, + }, + hooks: { + async deploy() { + await this.processEvent(25); + }, + }, + async run() { + await this.processEvent(); + }, +}; diff --git a/components/companyhub/sources/new-company-created/new-company-created.mjs b/components/companyhub/sources/new-company-created/new-company-created.mjs new file mode 100644 index 0000000000000..41a45d511e1d5 --- /dev/null +++ b/components/companyhub/sources/new-company-created/new-company-created.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "companyhub-new-company-created", + name: "New Company Created", + description: "Emit new event when a new company is created. [See the documentation](https://companyhub.com/docs/api-documentation)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.companyhub.listCompanies; + }, + getSummary(item) { + return `New Company: ${item.Name}`; + }, + }, + sampleEmit, +}; diff --git a/components/companyhub/sources/new-company-created/test-event.mjs b/components/companyhub/sources/new-company-created/test-event.mjs new file mode 100644 index 0000000000000..8bde37694266d --- /dev/null +++ b/components/companyhub/sources/new-company-created/test-event.mjs @@ -0,0 +1,36 @@ +export default { + "Name": "New Company", + "Website": null, + "Phone": "", + "Description": "", + "Owner": { + "ID": 1, + "Name": "" + }, + "BillingStreet": "", + "BillingCity": "", + "BillingState": "", + "BillingCountry": "", + "BillingPostalCode": "", + "ShippingStreet": "", + "ShippingCity": "", + "ShippingState": "", + "ShippingCountry": "", + "ShippingPostalCode": "", + "CreatedOn": "2025-05-14T17:00:46", + "CreatedBy": { + "ID": 1, + "Name": "" + }, + "ModifiedOn": "2025-05-14T17:00:46", + "ModifiedBy": { + "ID": 1, + "Name": "" + }, + "ID": 5, + "DeletedOn": null, + "DeletedBy": { + "ID": 0, + "Name": "" + } + } \ No newline at end of file diff --git a/components/companyhub/sources/new-deal-created/new-deal-created.mjs b/components/companyhub/sources/new-deal-created/new-deal-created.mjs new file mode 100644 index 0000000000000..395ff43da200e --- /dev/null +++ b/components/companyhub/sources/new-deal-created/new-deal-created.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "companyhub-new-deal-created", + name: "New Deal Created", + description: "Emit new event when a new deal is created. [See the documentation](https://companyhub.com/docs/api-documentation)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.companyhub.listDeals; + }, + getSummary(item) { + return `New Deal: ${item.Name}`; + }, + }, + sampleEmit, +}; diff --git a/components/companyhub/sources/new-deal-created/test-event.mjs b/components/companyhub/sources/new-deal-created/test-event.mjs new file mode 100644 index 0000000000000..325d1af15ca69 --- /dev/null +++ b/components/companyhub/sources/new-deal-created/test-event.mjs @@ -0,0 +1,35 @@ +export default { + "Name": "CompanyHub Website", + "Company": { + "ID": 1, + "Name": "CompanyHub" + }, + "Contact": { + "ID": 0, + "Name": "" + }, + "Stage": "Closed Won", + "Amount": 10000, + "CloseDate": "2025-02-12T00:00:00", + "Owner": { + "ID": 1, + "Name": "" + }, + "CreatedOn": "2025-05-13T19:48:33", + "CreatedBy": { + "ID": 1, + "Name": "" + }, + "ModifiedOn": "2025-05-13T19:48:33", + "ModifiedBy": { + "ID": 1, + "Name": "" + }, + "ID": 1, + "DeletedOn": null, + "DeletedBy": { + "ID": 0, + "Name": "" + }, + "CurrencySymbol": "USD" + } \ No newline at end of file diff --git a/components/companyhub/sources/new-record-created/new-record-created.mjs b/components/companyhub/sources/new-record-created/new-record-created.mjs new file mode 100644 index 0000000000000..fe47c17edd191 --- /dev/null +++ b/components/companyhub/sources/new-record-created/new-record-created.mjs @@ -0,0 +1,33 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "companyhub-new-record-created", + name: "New Record Created", + description: "Emit new event when a new record is created in a specified custom table. [See the documentation](https://companyhub.com/docs/api-documentation)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + table: { + type: "string", + label: "Table Name", + description: "The name of the table to monitor for new records", + }, + }, + methods: { + ...common.methods, + getResourceFn() { + return this.companyhub.listRecords; + }, + getArgs() { + return { + table: this.table, + }; + }, + getSummary(item) { + return `New Record with ID: ${item.ID}`; + }, + }, +}; From 45b546531d9a599765bb0782a0da9c4cf47ae22c Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Wed, 14 May 2025 13:20:56 -0400 Subject: [PATCH 2/2] fix summary --- components/companyhub/actions/create-deal/create-deal.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/companyhub/actions/create-deal/create-deal.mjs b/components/companyhub/actions/create-deal/create-deal.mjs index 84fdf86b2f52f..0cb7acce5ffec 100644 --- a/components/companyhub/actions/create-deal/create-deal.mjs +++ b/components/companyhub/actions/create-deal/create-deal.mjs @@ -65,7 +65,7 @@ export default { }, }); if (response.Success) { - $.export("$summary", `Successfully created company with ID: ${response.Id}`); + $.export("$summary", `Successfully created deal with ID: ${response.Id}`); } return response; },