-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Salesloft new components #16954
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Salesloft new components #16954
Changes from 5 commits
0742575
156cb18
5ca3955
3572753
1f30302
adc002d
0bf81a5
c466980
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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: { | ||
|
Check warning on line 12 in components/salesloft/actions/create-person/create-person.mjs
|
||
| 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; | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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,6 +9,9 @@ | |
| ], | ||
| "homepage": "https://pipedream.com/apps/salesloft", | ||
| "author": "Pipedream <[email protected]> (https://pipedream.com/)", | ||
| "dependencies": { | ||
| "@pipedream/platform": "^3.0.3" | ||
| }, | ||
| "publishConfig": { | ||
| "access": "public" | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,172 @@ | ||
| 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, | ||
| })); | ||
|
Comment on lines
+14
to
+23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainVerify the API response structure for list endpoints The propDefinitions assume that list methods return arrays directly, but Salesloft API likely returns paginated responses with a nested structure. This could cause runtime errors when trying to map over the response. Also applies to: 34-43, 63-73, 82-91 🏁 Script executed: #!/bin/bash
# Description: Check if there are any examples or documentation about Salesloft API response structure
# Search for any test files or examples that might show the API response structure
fd -e test.js -e spec.js -e test.mjs -e spec.mjs | xargs rg -A 5 "salesloft|listPeople|listCadences"
# Search for any comments or documentation about the response structure
rg -A 5 "response.*structure|data.*array|pagination.*response" --type jsLength of output: 30910 🌐 Web query: 💡 Result: The Salesloft API v2 provides list endpoints such as 1. This endpoint retrieves multiple person records. The response includes metadata about the request and an array of person objects. Response Schema: {
"metadata": {
"filtering": {
// Filtering criteria applied to the request
},
"paging": {
"per_page": 25,
"current_page": 1,
"next_page": 2,
"prev_page": null,
"total_pages": 10,
"total_count": 250
},
"sorting": {
"sort_by": "updated_at",
"sort_direction": "DESC"
}
},
"data": [
{
"id": 123,
"created_at": "2023-05-15T10:30:00Z",
"updated_at": "2023-05-15T14:45:00Z",
"first_name": "John",
"last_name": "Doe",
"email_address": "[email protected]",
"phone": "+14155551234",
"title": "Software Engineer",
"city": "San Francisco",
"state": "CA",
"country": "US",
"account": {
"id": 456,
"_href": "/v2/accounts/456"
},
"owner": {
"id": 789,
"_href": "/v2/users/789"
},
"counts": {
"emails_sent": 100,
"emails_viewed": 80,
"emails_clicked": 50,
"emails_replied_to": 30,
"calls": 20
}
},
// Additional person objects
]
}Key Elements:
2. This endpoint retrieves multiple cadence records. The response includes metadata about the request and an array of cadence objects. Response Schema: {
"metadata": {
"filtering": {
// Filtering criteria applied to the request
},
"paging": {
"per_page": 25,
"current_page": 1,
"next_page": 2,
"prev_page": null,
"total_pages": 10,
"total_count": 250
},
"sorting": {
"sort_by": "updated_at",
"sort_direction": "DESC"
}
},
"data": [
{
"id": 123,
"created_at": "2023-05-15T10:30:00Z",
"updated_at": "2023-05-15T14:45:00Z",
"name": "Sales Outreach",
"team_cadence": true,
"shared": false,
"remove_bounces_enabled": true,
"remove_replies_enabled": true,
"opt_out_link_included": true,
"draft": false,
"cadence_framework_id": 456,
"cadence_function": "outbound",
"tags": ["sales", "outreach", "q2"],
"creator": {
"id": 789,
"_href": "/v2/users/789"
},
"owner": {
"id": 101,
"_href": "/v2/users/101"
},
"counts": {
"cadence_people": 500,
"people_acted_on_count": 350,
"target_daily_people": 50,
"opportunities_created": 25,
"meetings_booked": 15
}
},
// Additional cadence objects
]
}Key Elements:
For more detailed information, you can refer to the official Salesloft API documentation: Fix mapping over Salesloft API responses to use the The Please update the following in • Lines 14–23 (people) Example diff for lines 14–23: - 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,
- }));
+ const { data: 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,
+ }));Apply the same pattern to cadences and any other 🤖 Prompt for AI Agents
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain@GTFalcao You raise an important point, but I believe there might be a misunderstanding about what The return response.data;This {
"metadata": { "paging": {...}, "sorting": {...} },
"data": [array of items]
}So Could you clarify if you've tested this with actual API calls, or if there's something specific about the Salesloft API response structure that differs from their documented format? 🏁 Script executed: Length of output: 5408 @GTFalcao You're absolutely right, and I apologize for the confusion. Looking at the code more carefully, I can see there's even a If the current implementation is working correctly and returning arrays that can be mapped over, then either:
Since you've clearly tested this implementation and it's working, I'll withdraw my concern about the API response structure. 🏁 Script executed: Length of output: 9734 @GTFalcao Perfect! Now I can see the evidence clearly. The Looking at the other app integrations, I can see different patterns - some APIs (like Firmao) do require accessing Thank you for the clarification and sorry for the confusion! The current implementation is indeed correct for the Salesloft API structure. ✏️ Learnings added
|
||
| }, | ||
| }, | ||
| 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, | ||
| }, | ||
| }); | ||
| console.log(cadences); | ||
GTFalcao marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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; | ||
| }, | ||
GTFalcao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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, | ||
| }); | ||
| }, | ||
| }, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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}`; | ||
| }, | ||
| }, | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.