-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Add HubSpot conversation threads with internal notes support #17607
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
base: master
Are you sure you want to change the base?
Add HubSpot conversation threads with internal notes support #17607
Conversation
- Add conversation API methods to HubSpot app with JSDoc documentation - Add threadId prop definition for conversation threads - Create send-conversation-message action for regular messages with validation - Create add-conversation-comment action for internal notes (COMMENT type) - Add webhook sources for conversation messages and internal comments with proper polling - Update package version to 1.2.5 - Support HubSpot Conversations API v3 endpoints - Add input validation for empty text fields
@seynadio is attempting to deploy a commit to the Pipedreamers Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughNew HubSpot integrations were introduced, including actions for sending messages and adding internal comments to conversation threads, along with sources to emit events for new messages and comments. The HubSpot app module was extended with new methods for conversation thread operations, and the package version was incremented. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Action (Send/Add)
participant HubSpot App
participant HubSpot API
User->>Action (Send/Add): Trigger send message or add comment
Action (Send/Add)->>HubSpot App: Call sendConversationMessage/addConversationComment
HubSpot App->>HubSpot API: POST /conversations/v3/conversations/threads/{threadId}/messages
HubSpot API-->>HubSpot App: API Response
HubSpot App-->>Action (Send/Add): Return result
Action (Send/Add)-->>User: Output summary and response
sequenceDiagram
participant HubSpot API
participant Source (New Msg/Comment)
participant User
HubSpot API-->>Source (New Msg/Comment): New message or comment detected
Source (New Msg/Comment)-->>User: Emit event with metadata
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
components/hubspot/actions/add-conversation-comment/add-conversation-comment.mjsOops! Something went wrong! :( ESLint: 8.57.1 Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs components/hubspot/sources/new-conversation-comment/new-conversation-comment.mjsOops! Something went wrong! :( ESLint: 8.57.1 Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs components/hubspot/sources/new-conversation-message/new-conversation-message.mjsOops! Something went wrong! :( ESLint: 8.57.1 Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 1 Skipped Deployment
|
Thank you so much for submitting this! We've added it to our backlog to review, and our team has been notified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
components/hubspot/sources/new-conversation-message/new-conversation-message.mjs (1)
85-89
: Consider improving the no-threadId behaviorWhen no
threadId
is provided, the source only logs a message but doesn't process any events. This might be confusing for users who expect the source to work without specifying a thread ID.Consider either:
- Adding a more prominent warning in the description about requiring threadId
- Implementing a fallback mechanism if the HubSpot API supports it
- Throwing an error during configuration if threadId is not provided
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
components/hubspot/actions/add-conversation-comment/add-conversation-comment.mjs
(1 hunks)components/hubspot/actions/send-conversation-message/send-conversation-message.mjs
(1 hunks)components/hubspot/hubspot.app.mjs
(2 hunks)components/hubspot/package.json
(1 hunks)components/hubspot/sources/new-conversation-comment/new-conversation-comment.mjs
(1 hunks)components/hubspot/sources/new-conversation-message/new-conversation-message.mjs
(1 hunks)
🧰 Additional context used
🧠 Learnings (5)
components/hubspot/package.json (1)
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14935
File: components/sailpoint/package.json:15-18
Timestamp: 2024-12-12T19:23:09.039Z
Learning: When developing Pipedream components, do not add built-in Node.js modules like `fs` to `package.json` dependencies, as they are native modules provided by the Node.js runtime.
components/hubspot/actions/send-conversation-message/send-conversation-message.mjs (2)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-10-08T15:33:38.240Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-07-04T18:11:59.822Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
components/hubspot/sources/new-conversation-message/new-conversation-message.mjs (3)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#15376
File: components/monday/sources/name-updated/name-updated.mjs:6-6
Timestamp: 2025-01-23T03:55:15.166Z
Learning: Source names in Monday.com components don't need to start with "New" if they emit events for updated items (e.g., "Name Updated", "Column Value Updated") rather than new items. This follows the component guidelines exception where the "New" prefix is only required when emits are limited to new items.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-07-24T02:06:47.016Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-10-08T15:33:38.240Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
components/hubspot/sources/new-conversation-comment/new-conversation-comment.mjs (4)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#15376
File: components/monday/sources/name-updated/name-updated.mjs:6-6
Timestamp: 2025-01-23T03:55:15.166Z
Learning: Source names in Monday.com components don't need to start with "New" if they emit events for updated items (e.g., "Name Updated", "Column Value Updated") rather than new items. This follows the component guidelines exception where the "New" prefix is only required when emits are limited to new items.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14265
File: components/the_magic_drip/sources/common.mjs:35-43
Timestamp: 2024-10-10T19:18:27.998Z
Learning: In `components/the_magic_drip/sources/common.mjs`, when processing items in `getAndProcessData`, `savedIds` is intentionally updated with IDs of both emitted and non-emitted items to avoid emitting retroactive events upon first deployment and ensure only new events are emitted as they occur.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-10-08T15:33:38.240Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-07-24T02:06:47.016Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
components/hubspot/hubspot.app.mjs (2)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-07-24T02:06:47.016Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-10-08T15:33:38.240Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
🔇 Additional comments (10)
components/hubspot/package.json (1)
3-3
: LGTM: Appropriate version bump for new featuresThe version increment from 1.2.4 to 1.2.5 correctly follows semantic versioning for adding new conversation thread functionality.
components/hubspot/actions/send-conversation-message/send-conversation-message.mjs (1)
45-63
: LGTM: Well-structured action with proper validationThe implementation follows Pipedream best practices with:
- Proper input validation for empty text
- Correct use of prop definitions
- Appropriate error handling and summary export
- Sensible defaults (richText falls back to text, direction defaults to "OUTGOING")
The hard-coded
type: "MESSAGE"
is appropriate for this specific action.components/hubspot/actions/add-conversation-comment/add-conversation-comment.mjs (1)
29-46
: LGTM: Consistent implementation with proper validationThe implementation follows the same solid patterns as the message action:
- Proper input validation for empty comment text
- Correct use of the
addConversationComment
method- Appropriate type specification (
"COMMENT"
for internal notes)- Good error handling and summary export
The consistency between this action and the message action is beneficial for maintainability.
components/hubspot/sources/new-conversation-message/new-conversation-message.mjs (1)
43-92
: LGTM: Well-structured source with good filtering capabilitiesThe source implementation follows solid patterns:
- Proper extension of common source
- Good filtering logic for threadId and messageType
- Appropriate deduplication strategy ("unique")
- Proper timestamp parsing and metadata generation
- Correct use of the
getConversationMessages
methodThe filtering logic in
isRelevant
is well-implemented and handles all the filter criteria correctly.components/hubspot/hubspot.app.mjs (2)
127-131
: LGTM: Well-defined threadId propertyThe new
threadId
prop definition is correctly structured and provides a clear description for the HubSpot conversation thread ID.
1150-1207
: Approve the JSDoc documentation and method structureThe JSDoc documentation for all four new methods is excellent:
- Clear parameter descriptions
- Proper return type documentation
- Consistent formatting with existing methods
The method parameter destructuring and use of the
makeRequest
helper follows the established patterns in the codebase.components/hubspot/sources/new-conversation-comment/new-conversation-comment.mjs (4)
1-21
: Component setup follows Pipedream conventions well.The component structure, naming, and prop definitions are well-implemented. The optional
threadId
prop with proper prop definition reference is a good design choice that allows for both thread-specific and general monitoring.
22-33
: Helper methods are implemented correctly.The
getTs()
andgenerateMeta()
methods properly handle timestamp parsing and event metadata generation. The summary format ingenerateMeta()
provides helpful context by including the comment text when available.
34-40
: Filtering logic is well-designed.The
isRelevant()
method correctly implements the three key filtering criteria:
- Timestamp filtering for new comments
- Thread-specific filtering when threadId is provided
- Comment type filtering to ensure only internal notes (type "COMMENT") are processed
41-47
: Parameter configuration is appropriate.The 100-item limit is reasonable for conversation message pagination and aligns with typical API best practices.
/** | ||
* Get conversation thread details | ||
* @param {string} threadId - The ID of the conversation thread | ||
* @param {object} opts - Additional options to pass to the request | ||
* @returns {Promise<object>} The conversation thread object | ||
*/ | ||
getConversationThread({ | ||
threadId, ...opts | ||
}) { | ||
return this.makeRequest({ | ||
endpoint: `/conversations/v3/conversations/threads/${threadId}`, | ||
...opts, | ||
}); | ||
}, | ||
/** | ||
* Get messages from a conversation thread | ||
* @param {string} threadId - The ID of the conversation thread | ||
* @param {object} opts - Additional options to pass to the request | ||
* @returns {Promise<object>} The messages in the conversation thread | ||
*/ | ||
getConversationMessages({ | ||
threadId, ...opts | ||
}) { | ||
return this.makeRequest({ | ||
endpoint: `/conversations/v3/conversations/threads/${threadId}/messages`, | ||
...opts, | ||
}); | ||
}, | ||
/** | ||
* Send a message to a conversation thread | ||
* @param {string} threadId - The ID of the conversation thread | ||
* @param {object} opts - Message data and request options | ||
* @returns {Promise<object>} The sent message object | ||
*/ | ||
sendConversationMessage({ | ||
threadId, ...opts | ||
}) { | ||
return this.makeRequest({ | ||
endpoint: `/conversations/v3/conversations/threads/${threadId}/messages`, | ||
method: "POST", | ||
...opts, | ||
}); | ||
}, | ||
/** | ||
* Add an internal comment to a conversation thread | ||
* @param {string} threadId - The ID of the conversation thread | ||
* @param {object} opts - Comment data and request options | ||
* @returns {Promise<object>} The added comment object | ||
*/ | ||
addConversationComment({ | ||
threadId, ...opts | ||
}) { | ||
return this.makeRequest({ | ||
endpoint: `/conversations/v3/conversations/threads/${threadId}/messages`, | ||
method: "POST", | ||
...opts, | ||
}); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Critical issue: Missing API parameter in conversation methods
All the new conversation methods are missing the api
parameter when calling makeRequest
. Looking at the existing pattern in this file, other methods specify an API path like API_PATH.CRMV3
.
Without the api
parameter, the makeRequest
method will construct URLs like ${BASE_URL}undefined${endpoint}
, which is incorrect.
You need to add the appropriate API path parameter to all four conversation methods. For example:
getConversationThread({
threadId, ...opts
}) {
return this.makeRequest({
+ api: API_PATH.CONVERSATIONS, // or appropriate API path constant
endpoint: `/conversations/v3/conversations/threads/${threadId}`,
...opts,
});
},
Run this script to verify the API path constant that should be used:
🏁 Script executed:
#!/bin/bash
# Check for existing API path constants in the constants file
rg "API_PATH" --type js -A 5 -B 5
# Look for any conversation-related API constants
rg "conversation" --type js -i
Length of output: 64
To identify the correct API path constant and how it’s used in this file, let’s inspect imports, existing makeRequest
calls, and any conversation-related constants in hubspot.app.mjs
:
🏁 Script executed:
#!/bin/bash
# 1. See if API_PATH is imported
rg "import .*API_PATH" -n components/hubspot/hubspot.app.mjs
# 2. Find any references to API_PATH in the file
rg "API_PATH" -n components/hubspot/hubspot.app.mjs
# 3. Show existing makeRequest calls (to see how api: is used elsewhere)
rg "makeRequest" -n -A 3 -B 3 components/hubspot/hubspot.app.mjs
# 4. Check for any conversation-related constants in this file
rg "CONVERSATION" -n components/hubspot/hubspot.app.mjs
Length of output: 14117
To confirm which API paths are defined, let’s locate and inspect the file that defines API_PATH
:
🏁 Script executed:
#!/bin/bash
# Find where API_PATH is defined
rg "API_PATH\s*=" -n .
# Display the first definition file to view available constants
FILE=$(rg -l "API_PATH\s*=" . | head -n1)
sed -n '1,200p' "$FILE"
Length of output: 5109
Let’s pull in the HubSpot API_PATH definitions to see which constant to use:
🏁 Script executed:
#!/bin/bash
# Show the API_PATH block in the HubSpot constants file
rg "const API_PATH" -n -A50 components/hubspot/common/constants.mjs
Length of output: 1354
Add api
parameter and new CONVERSATIONS
path constant for conversation methods
The four conversation methods currently call
this.makeRequest({
endpoint: `/conversations/v3/conversations/threads/${threadId}`,
…opts,
});
without an api
field, so makeRequest
will prepend undefined
to the URL. There is no existing API_PATH.CONVERSATIONS
constant—so:
• In components/hubspot/common/constants.mjs
, add to the API_PATH
object:
CONVERSATIONS: "/conversations/v3",
• In components/hubspot/hubspot.app.mjs
, update each method to include api: API_PATH.CONVERSATIONS
and remove the duplicated version prefix in the endpoint. For example:
getConversationThread({ threadId, ...opts }) {
- return this.makeRequest({
- endpoint: `/conversations/v3/conversations/threads/${threadId}`,
- ...opts,
- });
+ return this.makeRequest({
+ api: API_PATH.CONVERSATIONS,
+ endpoint: `/conversations/threads/${threadId}`,
+ ...opts,
+ });
},
Repeat for getConversationMessages
, sendConversationMessage
, and addConversationComment
.
This ensures URLs are constructed as:
<BASE_URL>/conversations/v3/conversations/threads/{threadId}
.
🤖 Prompt for AI Agents
In components/hubspot/hubspot.app.mjs lines 1150 to 1207, the conversation
methods lack the 'api' parameter, causing makeRequest to prepend 'undefined' to
URLs, and the conversation path prefix is duplicated in endpoints. Fix this by
adding a new constant CONVERSATIONS: "/conversations/v3" to the API_PATH object
in components/hubspot/common/constants.mjs, then update each conversation method
to include api: API_PATH.CONVERSATIONS and remove the '/conversations/v3' prefix
from the endpoint strings, ensuring URLs are correctly constructed as
<BASE_URL>/conversations/v3/conversations/threads/{threadId}.
async processResults(after, params) { | ||
const createdAfter = after || this.getLastCreatedAt(); | ||
|
||
if (this.threadId) { | ||
// If specific thread is provided, get messages from that thread | ||
const messages = await this.hubspot.getConversationMessages({ | ||
threadId: this.threadId, | ||
...params, | ||
}); | ||
|
||
const comments = messages.results?.filter(msg => | ||
this.isRelevant(msg, createdAfter) | ||
) || []; | ||
|
||
this.processEvents(comments); | ||
} else { | ||
// Note: HubSpot Conversations API doesn't provide a direct way to list all threads | ||
// This would require HubSpot webhooks or a different approach | ||
console.log("Thread-specific monitoring recommended - provide threadId prop for best results"); | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Incomplete implementation for non-thread-specific monitoring.
The processResults
method handles the thread-specific case well, but the else branch (lines 63-67) only logs a message without actually processing any events. This means the component won't emit any events when no threadId
is provided, which may not be the intended behavior.
Consider implementing a fallback mechanism or providing clearer documentation about the threadId requirement:
} else {
- // Note: HubSpot Conversations API doesn't provide a direct way to list all threads
- // This would require HubSpot webhooks or a different approach
- console.log("Thread-specific monitoring recommended - provide threadId prop for best results");
+ // Note: HubSpot Conversations API doesn't provide a direct way to list all threads
+ // This would require HubSpot webhooks or a different approach
+ throw new Error("threadId is required for conversation comment monitoring due to HubSpot API limitations");
}
Alternatively, if the component is designed to work only with threadId, consider making it a required prop instead of optional.
Let me verify how other similar HubSpot components handle this scenario:
🏁 Script executed:
#!/bin/bash
# Description: Check how other HubSpot conversation components handle optional threadId
# Expected: Find similar patterns or required threadId usage
# Search for similar threadId usage patterns in HubSpot components
rg -A 10 -B 5 "threadId.*optional" components/hubspot/
# Search for other conversation-related components
fd -e mjs -p "conversation" components/hubspot/
Length of output: 430
I’ll gather how the sibling source handles threadId
and its fallback behavior.
🏁 Script executed:
#!/bin/bash
# 1. Inspect threadId prop definition in new-conversation-comment
rg -A 5 -B 5 "threadId" components/hubspot/sources/new-conversation-comment/new-conversation-comment.mjs
# 2. Show processResults in the sibling new-conversation-message source
rg -A 20 -B 5 "async processResults" components/hubspot/sources/new-conversation-message/new-conversation-message.mjs
Length of output: 2651
Consistent threadId requirement across HubSpot conversation sources
Both new-conversation-comment
and its sibling new-conversation-message
log a recommendation and emit no events when threadId
is omitted. Because the HubSpot Conversations API doesn’t support listing all threads, this behavior is by design.
Please choose one of the following to make the contract clearer:
• Make threadId
a required prop in common.props.hubspot.threadId
(optional: false
) so consumers see a compile-time error if they omit it.
• Enhance the component documentation (in the README or prop descriptions) to explicitly state that threadId
is required and that omitting it results in no events.
Locations to update:
- components/hubspot/sources/new-conversation-comment/new-conversation-comment.mjs (
processResults
else-branch) - components/hubspot/sources/new-conversation-message/new-conversation-message.mjs (
processResults
else-branch) - common prop definition:
common.props.hubspot.threadId
🤖 Prompt for AI Agents
In
components/hubspot/sources/new-conversation-comment/new-conversation-comment.mjs
lines 48-68 and similarly in new-conversation-message.mjs, the processResults
method logs a message and emits no events if threadId is omitted, which is by
design due to API limitations. To clarify this contract, update the common prop
definition common.props.hubspot.threadId to set optional: false, making threadId
a required prop and causing compile-time errors if omitted. Alternatively,
enhance the component documentation to explicitly state that threadId is
required and omitting it results in no events. Apply these changes consistently
across the mentioned files and the common prop definition.
Summary
Changes
Test plan
API Usage
Uses HubSpot's Conversations API v3 endpoints:
/conversations/v3/conversations/threads/{threadId}
/conversations/v3/conversations/threads/{threadId}/messages
/conversations/v3/conversations/threads/{threadId}/messages
Notes
type: "COMMENT"
vs regular messages usetype: "MESSAGE"
Summary by CodeRabbit
New Features
Improvements
Chores