diff --git a/components/salesloft/actions/add-person-to-cadence/add-person-to-cadence.mjs b/components/salesloft/actions/add-person-to-cadence/add-person-to-cadence.mjs new file mode 100644 index 0000000000000..f0ac1dc185947 --- /dev/null +++ b/components/salesloft/actions/add-person-to-cadence/add-person-to-cadence.mjs @@ -0,0 +1,44 @@ +import salesloft from "../../salesloft.app.mjs"; + +export default { + key: "salesloft-add-person-to-cadence", + name: "Add Person to Cadence", + description: "Adds a person to a cadence in Salesloft. [See the documentation](https://developers.salesloft.com/docs/api/cadence-memberships-create/)", + version: "0.0.1", + type: "action", + props: { + salesloft, + personId: { + propDefinition: [ + salesloft, + "personId", + ], + }, + cadenceId: { + propDefinition: [ + salesloft, + "cadenceId", + ], + }, + userId: { + propDefinition: [ + salesloft, + "userId", + ], + }, + }, + async run({ $ }) { + const response = await this.salesloft.addPersonToCadence({ + $, + data: { + person_id: this.personId, + cadence_id: this.cadenceId, + user_id: this.userId, + }, + }); + + $.export("$summary", `Successfully added person ${this.personId} to cadence ${this.cadenceId}`); + + return response; + }, +}; diff --git a/components/salesloft/actions/create-note/create-note.mjs b/components/salesloft/actions/create-note/create-note.mjs new file mode 100644 index 0000000000000..a021c4985b761 --- /dev/null +++ b/components/salesloft/actions/create-note/create-note.mjs @@ -0,0 +1,57 @@ +import salesloft from "../../salesloft.app.mjs"; + +export default { + key: "salesloft-create-note", + name: "Create Note", + description: "Creates a new note in Salesloft. [See the documentation](https://developers.salesloft.com/docs/api/notes-create/)", + version: "0.0.1", + type: "action", + props: { + salesloft, + content: { + type: "string", + label: "Content", + description: "The content of the note", + }, + associatedWithType: { + type: "string", + label: "Associated Record Type", + description: "The type of record this note is associated with", + options: [ + "person", + "account", + ], + }, + associatedWithId: { + propDefinition: [ + salesloft, + "recordId", + ({ associatedWithType }) => ({ + recordType: associatedWithType, + }), + ], + }, + skipActivities: { + type: "boolean", + label: "Skip Activities", + description: "If true, no activities will be created for this note", + optional: true, + default: false, + }, + }, + async run({ $ }) { + const response = await this.salesloft.createNote({ + $, + data: { + content: this.content, + associated_with_id: this.associatedWithId, + associated_with_type: this.associatedWithType, + skip_activities: this.skipActivities, + }, + }); + + $.export("$summary", `Successfully created note for ${this.associatedWithType.toLowerCase()} ${this.associatedWithId}`); + + return response; + }, +}; diff --git a/components/salesloft/actions/create-person/create-person.mjs b/components/salesloft/actions/create-person/create-person.mjs new file mode 100644 index 0000000000000..75d71734d4c84 --- /dev/null +++ b/components/salesloft/actions/create-person/create-person.mjs @@ -0,0 +1,75 @@ +import salesloft from "../../salesloft.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + key: "salesloft-create-person", + name: "Create Person", + description: "Creates a new person in Salesloft. [See the documentation](https://developers.salesloft.com/docs/api/people-create/)", + version: "0.0.1", + type: "action", + props: { + salesloft, + info: { + type: "alert", + alertType: "info", + content: "Either `Email Address` or both `Phone` and `Last Name` must be provided.", + }, + email: { + type: "string", + label: "Email", + description: "The email address of the person", + optional: true, + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the person", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the person", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the person", + optional: true, + }, + title: { + type: "string", + label: "Title", + description: "The job title of the person", + optional: true, + }, + company: { + type: "string", + label: "Company", + description: "The company name of the person", + optional: true, + }, + }, + async run({ $ }) { + if (!this.email && !(this.phone && this.lastName)) { + throw new ConfigurationError("Either `Email Address` or both `Phone` and `Last Name` must be provided"); + } + + const response = await this.salesloft.createPerson({ + $, + data: { + email_address: this.email, + first_name: this.firstName, + last_name: this.lastName, + phone: this.phone, + title: this.title, + company_name: this.company, + }, + }); + + $.export("$summary", `Successfully created person (ID: ${response.id})`); + + return response; + }, +}; diff --git a/components/salesloft/package.json b/components/salesloft/package.json index d4b512f6de825..0e2f391b8f406 100644 --- a/components/salesloft/package.json +++ b/components/salesloft/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/salesloft", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Salesloft Components", "main": "salesloft.app.mjs", "keywords": [ @@ -9,7 +9,10 @@ ], "homepage": "https://pipedream.com/apps/salesloft", "author": "Pipedream (https://pipedream.com/)", + "dependencies": { + "@pipedream/platform": "^3.0.3" + }, "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/salesloft/salesloft.app.mjs b/components/salesloft/salesloft.app.mjs index bd22c7044fdc8..028ecabe4e883 100644 --- a/components/salesloft/salesloft.app.mjs +++ b/components/salesloft/salesloft.app.mjs @@ -1,11 +1,171 @@ +import { + axios, ConfigurationError, +} from "@pipedream/platform"; + export default { type: "app", app: "salesloft", - propDefinitions: {}, + propDefinitions: { + personId: { + type: "string", + label: "Person ID", + description: "Select a person to add to the cadence or provide a person ID", + async options({ page }) { + const people = await this.listPeople({ + params: { + per_page: 100, + page: page + 1, + }, + }); + return people?.map((person) => ({ + label: person.full_email_address ?? person.email_address ?? person.display_name, + value: person.id, + })); + }, + }, + recordId: { + type: "string", + label: "Associated Record ID", + description: "Select a record (a person or account) to associate this note with", + async options({ + page, recordType, + }) { + if (recordType === "person") { + const people = await this.listPeople({ + params: { + per_page: 100, + page: page + 1, + }, + }); + return people?.map((person) => ({ + label: person.full_email_address ?? person.email_address ?? person.display_name, + value: person.id, + })); + } else if (recordType === "account") { + const accounts = await this.listAccounts({ + params: { + per_page: 100, + page: page + 1, + }, + }); + return accounts?.map((account) => ({ + label: account.name, + value: account.id, + })); + } else throw new ConfigurationError("Invalid record type, select either `person` or `account`"); + }, + }, + cadenceId: { + type: "string", + label: "Cadence ID", + description: "Select a cadence or provide a cadence ID", + async options({ page }) { + const cadences = await this.listCadences({ + params: { + per_page: 100, + page: page + 1, + }, + }); + return cadences?.map((cadence) => ({ + label: cadence.name, + value: cadence.id, + })); + }, + }, + userId: { + type: "string", + label: "User ID", + description: "Select a user who will own this cadence membership or provide a user ID. Defaults to the authenticated user if not provided.", + optional: true, + async options({ page }) { + const users = await this.listUsers({ + params: { + per_page: 100, + page: page + 1, + }, + }); + return users?.map((user) => ({ + label: user.name ?? user.email, + value: user.id, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _apiUrl() { + return "https://api.salesloft.com/v2"; + }, + async _makeRequest({ + $ = this, + path, + ...args + }) { + const response = await axios($, { + url: `${this._apiUrl()}${path}`, + headers: { + Authorization: `Bearer ${this.$auth.api_key}`, + }, + ...args, + }); + return response.data; + }, + async createPerson(args = {}) { + return this._makeRequest({ + path: "/people", + method: "POST", + ...args, + }); + }, + async addPersonToCadence(args = {}) { + return this._makeRequest({ + path: "/cadence_memberships", + method: "POST", + ...args, + }); + }, + async createNote(args = {}) { + return this._makeRequest({ + path: "/notes", + method: "POST", + ...args, + }); + }, + async createWebhookSubscription(args = {}) { + return this._makeRequest({ + path: "/webhook_subscriptions", + method: "POST", + ...args, + }); + }, + async deleteWebhookSubscription(subscriptionId) { + return this._makeRequest({ + path: `/webhook_subscriptions/${subscriptionId}`, + method: "DELETE", + }); + }, + async listPeople(args = {}) { + return this._makeRequest({ + path: "/people", + ...args, + }); + }, + async listCadences(args = {}) { + return this._makeRequest({ + path: "/cadences", + ...args, + }); + }, + async listUsers(args = {}) { + return this._makeRequest({ + path: "/users", + ...args, + }); + }, + async listAccounts(args = {}) { + return this._makeRequest({ + path: "/accounts", + ...args, + }); }, }, }; diff --git a/components/salesloft/sources/cadence-updated-instant/cadence-updated-instant.mjs b/components/salesloft/sources/cadence-updated-instant/cadence-updated-instant.mjs new file mode 100644 index 0000000000000..ff4a22e5d09f2 --- /dev/null +++ b/components/salesloft/sources/cadence-updated-instant/cadence-updated-instant.mjs @@ -0,0 +1,20 @@ +import common from "../common/webhook.mjs"; + +export default { + ...common, + key: "salesloft-cadence-updated-instant", + name: "Cadence Updated (Instant)", + description: "Emit new event when a cadence is updated in Salesloft. [See the documentation](https://developers.salesloft.com/docs/api/webhook-subscriptions/)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "cadence_updated"; + }, + getSummary(data) { + return `Cadence updated: ${data.name || data.id}`; + }, + }, +}; diff --git a/components/salesloft/sources/common/webhook.mjs b/components/salesloft/sources/common/webhook.mjs new file mode 100644 index 0000000000000..b0d2636c92ac2 --- /dev/null +++ b/components/salesloft/sources/common/webhook.mjs @@ -0,0 +1,51 @@ +import { v4 as uuid } from "uuid"; +import salesloft from "../../salesloft.app.mjs"; + +export default { + props: { + salesloft, + db: "$.service.db", + http: "$.interface.http", + }, + methods: { + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(webhookId) { + this.db.set("webhookId", webhookId); + }, + generateMeta(data) { + const { id } = data; + return { + id: id || uuid(), + summary: this.getSummary(data), + ts: Date.now(), + }; + }, + }, + hooks: { + async activate() { + const response = await this.salesloft.createWebhookSubscription({ + data: { + callback_url: this.http.endpoint, + callback_token: `pd_${Date.now()}`, + event_type: this.getEventType(), + }, + }); + this._setWebhookId(response.id); + }, + async deactivate() { + const webhookId = this._getWebhookId(); + if (webhookId) { + await this.salesloft.deleteWebhookSubscription(webhookId); + } + }, + }, + async run(event) { + const { body } = event; + if (body.id) { + const meta = this.generateMeta(body); + this.$emit(body, meta); + } + }, +}; diff --git a/components/salesloft/sources/new-email-activity-instant/new-email-activity-instant.mjs b/components/salesloft/sources/new-email-activity-instant/new-email-activity-instant.mjs new file mode 100644 index 0000000000000..77038ce76899f --- /dev/null +++ b/components/salesloft/sources/new-email-activity-instant/new-email-activity-instant.mjs @@ -0,0 +1,20 @@ +import common from "../common/webhook.mjs"; + +export default { + ...common, + key: "salesloft-new-email-activity-instant", + name: "New Email Activity (Instant)", + description: "Emit new event when an email is updated in Salesloft. [See the documentation](https://developers.salesloft.com/docs/api/webhook-subscriptions/)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "email_updated"; + }, + getSummary(data) { + return `Email updated: ${data.subject || data.id}`; + }, + }, +}; diff --git a/components/salesloft/sources/new-person-instant/new-person-instant.mjs b/components/salesloft/sources/new-person-instant/new-person-instant.mjs new file mode 100644 index 0000000000000..6b735a5526a40 --- /dev/null +++ b/components/salesloft/sources/new-person-instant/new-person-instant.mjs @@ -0,0 +1,20 @@ +import common from "../common/webhook.mjs"; + +export default { + ...common, + key: "salesloft-new-person-instant", + name: "New Person (Instant)", + description: "Emit new event when a person is created in Salesloft. [See the documentation](https://developers.salesloft.com/docs/api/webhook-subscriptions/)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "person_created"; + }, + getSummary(data) { + return `New person created: ${data.display_name || data.email_address || data.id}`; + }, + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b8f3ed1afc0b..dd93f97dd2bb1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -367,8 +367,7 @@ importers: specifier: ^1.2.1 version: 1.6.6 - components/adtraction: - specifiers: {} + components/adtraction: {} components/adversus: dependencies: @@ -3739,8 +3738,7 @@ importers: components/dokan: {} - components/dolibarr: - specifiers: {} + components/dolibarr: {} components/domain_group: dependencies: @@ -7373,8 +7371,7 @@ importers: specifier: ^3.0.0 version: 3.0.3 - components/limoexpress: - specifiers: {} + components/limoexpress: {} components/line: dependencies: @@ -9884,8 +9881,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/pinghome: - specifiers: {} + components/pinghome: {} components/pingone: {} @@ -10380,8 +10376,7 @@ importers: components/procore_sandbox: {} - components/prodatakey: - specifiers: {} + components/prodatakey: {} components/prodpad: dependencies: @@ -11429,7 +11424,11 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/salesloft: {} + components/salesloft: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/salesmate: dependencies: @@ -15050,8 +15049,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/zenedu: - specifiers: {} + components/zenedu: {} components/zenkit: dependencies: