feat(apollo_io_oauth): add all actions to match API key component#20506
feat(apollo_io_oauth): add all actions to match API key component#20506volcano303 wants to merge 2 commits intoPipedreamHQ:masterfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 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. |
|
Thanks for submitting this PR! When we review PRs, we follow the Pipedream component guidelines. If you're not familiar, here's a quick checklist:
|
WalkthroughAdds 15 Apollo.io OAuth action modules plus app-level API wrappers, utilities, and constants. New actions cover contacts, accounts, opportunities, sequences, and people enrichment; the app module gains propDefinitions, many API helper methods, and a pagination mechanism. Package version and dependency updated. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjs`:
- Around line 49-53: The flag declared as sequenceFinishedInOtherCampaigns is
being read with a typo (this.sequencFinishedInOtherCampaigns) so it is always
undefined; update all usages in add-contacts-to-sequence (where the payload is
built) to reference this.sequenceFinishedInOtherCampaigns and wire it into the
outgoing payload under the exact property name sequenceFinishedInOtherCampaigns
(search for the misspelled symbol and replace it). Ensure both occurrences
mentioned around the payload construction use the corrected identifier so the
option is sent correctly.
In `@components/apollo_io_oauth/actions/create-contact/create-contact.mjs`:
- Around line 79-94: The payload passed to this.app.createContact in the async
run method currently includes fields that may be undefined; to match other
actions (e.g., update-account.mjs, update-contact.mjs) wrap the data object with
utils.cleanObject so undefined/null values are filtered out before sending.
Import utils.cleanObject at the top of the module, then replace the direct data
literal passed to createContact inside run with cleanObject({...}) using the
same keys (email, first_name, last_name, title, account_id, website_url,
label_names, contact_stage_id, present_raw_address, direct_phone) so the API
only receives populated fields.
In
`@components/apollo_io_oauth/actions/create-update-contact/create-update-contact.mjs`:
- Around line 96-104: The code assumes this.app.searchContacts returns a valid
array and does contacts.length directly; add a defensive null-safe check so
accessing length and contacts[0] cannot throw. Change the flow around the
searchContacts result (referencing this.app.searchContacts and the contacts
variable where action = "updated" and contact = contacts[0]) to verify
Array.isArray(contacts) (or default contacts = []) before checking .length and
indexing; if contacts is falsy or not an array, treat it as no matches and
proceed to the create branch or handle the empty case.
In `@components/apollo_io_oauth/actions/update-contact/update-contact.mjs`:
- Around line 91-93: Remove the unreachable typeof check around labelNames in
the update payload: always pass this.labelNames (already a string[] per
propDefinitions) to label_names instead of attempting JSON.parse; update the
code at the label_names assignment (the branch using typeof this.labelNames ===
"string" and JSON.parse) to simply set label_names: this.labelNames so there’s
no dead branch.
In
`@components/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs`:
- Line 6: Update the action description string in update-opportunity.mjs to
correctly reflect that this is an update operation: replace "Updates a new
opportunity in Apollo.io." with "Updates an existing opportunity in Apollo.io."
(the description property in the action metadata) so the documentation text for
the update-opportunity action is accurate.
- Around line 64-76: The run method calls this.app.updateOpportunity with a data
object that may contain undefined values; mirror
update-account.mjs/update-contact.mjs by importing utils.cleanObject and passing
the cleaned payload to this.app.updateOpportunity. Specifically, add an import
for utils.cleanObject at the top, and replace the raw data object in the
this.app.updateOpportunity call (the object built from
owner_id/name/amount/opportunity_stage_id/closed_date/account_id and using
this.opportunityId/this.ownerId/etc.) with utils.cleanObject(...) so undefined
fields are removed before sending to the API.
In `@components/apollo_io_oauth/apollo_io_oauth.app.mjs`:
- Around line 115-122: The opportunity dropdown is not paginating because page
is passed at the top level instead of inside params for makeRequest; update
calls to listOpportunities to pass the page inside a params object (e.g., change
this.listOpportunities({ page: page + 1 }) to this.listOpportunities({ params: {
page: page + 1 } })) inside the opportunityId field's async options and make the
same change for the other occurrences (lines around 223-236 and 289-293) so
makeRequest will forward the page as a query string.
In `@components/apollo_io_oauth/common/utils.mjs`:
- Around line 9-16: The cleanObject function returns nullish when passed
undefined/null; update cleanObject to handle nullish inputs by returning an
empty object instead of propagating null/undefined — e.g., at the start of
cleanObject check if o == null then return {}; otherwise proceed to iterate (or
assign o = o || {}), keep using delete on existing properties and return the
sanitized object; reference the cleanObject function name to locate and modify
this logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 45c32d58-84f3-40d1-8d90-16e5633d3a3a
📒 Files selected for processing (19)
components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjscomponents/apollo_io_oauth/actions/create-account/create-account.mjscomponents/apollo_io_oauth/actions/create-contact/create-contact.mjscomponents/apollo_io_oauth/actions/create-opportunity/create-opportunity.mjscomponents/apollo_io_oauth/actions/create-update-contact/create-update-contact.mjscomponents/apollo_io_oauth/actions/get-opportunity/get-opportunity.mjscomponents/apollo_io_oauth/actions/people-enrichment/people-enrichment.mjscomponents/apollo_io_oauth/actions/search-accounts/search-accounts.mjscomponents/apollo_io_oauth/actions/search-contacts/search-contacts.mjscomponents/apollo_io_oauth/actions/search-sequences/search-sequences.mjscomponents/apollo_io_oauth/actions/update-account-stage/update-account-stage.mjscomponents/apollo_io_oauth/actions/update-account/update-account.mjscomponents/apollo_io_oauth/actions/update-contact-stage/update-contact-stage.mjscomponents/apollo_io_oauth/actions/update-contact/update-contact.mjscomponents/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjscomponents/apollo_io_oauth/apollo_io_oauth.app.mjscomponents/apollo_io_oauth/common/constants.mjscomponents/apollo_io_oauth/common/utils.mjscomponents/apollo_io_oauth/package.json
| const { contacts } = await this.app.searchContacts({ | ||
| params: { | ||
| q_keywords: this.email, | ||
| }, | ||
| }); | ||
|
|
||
| if (contacts.length) { | ||
| action = "updated"; | ||
| contact = contacts[0]; |
There was a problem hiding this comment.
Add defensive check for contacts response.
If the API returns an unexpected response where contacts is undefined or null, the contacts.length access on line 102 will throw a TypeError. Add a defensive check.
🛡️ Add null-safe check
const { contacts } = await this.app.searchContacts({
params: {
q_keywords: this.email,
},
});
- if (contacts.length) {
+ if (contacts?.length) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { contacts } = await this.app.searchContacts({ | |
| params: { | |
| q_keywords: this.email, | |
| }, | |
| }); | |
| if (contacts.length) { | |
| action = "updated"; | |
| contact = contacts[0]; | |
| const { contacts } = await this.app.searchContacts({ | |
| params: { | |
| q_keywords: this.email, | |
| }, | |
| }); | |
| if (contacts?.length) { | |
| action = "updated"; | |
| contact = contacts[0]; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@components/apollo_io_oauth/actions/create-update-contact/create-update-contact.mjs`
around lines 96 - 104, The code assumes this.app.searchContacts returns a valid
array and does contacts.length directly; add a defensive null-safe check so
accessing length and contacts[0] cannot throw. Change the flow around the
searchContacts result (referencing this.app.searchContacts and the contacts
variable where action = "updated" and contact = contacts[0]) to verify
Array.isArray(contacts) (or default contacts = []) before checking .length and
indexing; if contacts is falsy or not an array, treat it as no matches and
proceed to the create branch or handle the empty case.
| label_names: typeof this.labelNames === "string" | ||
| ? JSON.parse(this.labelNames) | ||
| : this.labelNames, |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Dead code: typeof check is unreachable.
The labelNames prop is declared as type: "string[]" in the app's propDefinitions (see apollo_io_oauth.app.mjs lines 174-182), so it will always be an array at runtime. The typeof this.labelNames === "string" branch will never execute.
♻️ Simplify by removing the dead branch
label_names: typeof this.labelNames === "string"
- ? JSON.parse(this.labelNames)
- : this.labelNames,
+ label_names: this.labelNames,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/apollo_io_oauth/actions/update-contact/update-contact.mjs` around
lines 91 - 93, Remove the unreachable typeof check around labelNames in the
update payload: always pass this.labelNames (already a string[] per
propDefinitions) to label_names instead of attempting JSON.parse; update the
code at the label_names assignment (the branch using typeof this.labelNames ===
"string" and JSON.parse) to simply set label_names: this.labelNames so there’s
no dead branch.
| async run({ $ }) { | ||
| const { opportunity } = await this.app.updateOpportunity({ | ||
| $, | ||
| opportunityId: this.opportunityId, | ||
| data: { | ||
| owner_id: this.ownerId, | ||
| name: this.name, | ||
| amount: this.amount, | ||
| opportunity_stage_id: this.opportunityStageId, | ||
| closed_date: this.closedDate, | ||
| account_id: this.accountId, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Missing utils.cleanObject for data payload.
Unlike update-account.mjs and update-contact.mjs which use utils.cleanObject to remove undefined values before sending to the API, this action sends the raw object. This inconsistency could cause issues if the Apollo API doesn't handle undefined values gracefully (they may serialize as null or cause validation errors).
♻️ Add utils.cleanObject for consistency
Add the import at the top of the file:
import app from "../../apollo_io_oauth.app.mjs";
+import utils from "../../common/utils.mjs";Then wrap the data payload:
const { opportunity } = await this.app.updateOpportunity({
$,
opportunityId: this.opportunityId,
- data: {
+ data: utils.cleanObject({
owner_id: this.ownerId,
name: this.name,
amount: this.amount,
opportunity_stage_id: this.opportunityStageId,
closed_date: this.closedDate,
account_id: this.accountId,
- },
+ }),
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async run({ $ }) { | |
| const { opportunity } = await this.app.updateOpportunity({ | |
| $, | |
| opportunityId: this.opportunityId, | |
| data: { | |
| owner_id: this.ownerId, | |
| name: this.name, | |
| amount: this.amount, | |
| opportunity_stage_id: this.opportunityStageId, | |
| closed_date: this.closedDate, | |
| account_id: this.accountId, | |
| }, | |
| }); | |
| import app from "../../apollo_io_oauth.app.mjs"; | |
| import utils from "../../common/utils.mjs"; | |
| // ... rest of file ... | |
| async run({ $ }) { | |
| const { opportunity } = await this.app.updateOpportunity({ | |
| $, | |
| opportunityId: this.opportunityId, | |
| data: utils.cleanObject({ | |
| owner_id: this.ownerId, | |
| name: this.name, | |
| amount: this.amount, | |
| opportunity_stage_id: this.opportunityStageId, | |
| closed_date: this.closedDate, | |
| account_id: this.accountId, | |
| }), | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs`
around lines 64 - 76, The run method calls this.app.updateOpportunity with a
data object that may contain undefined values; mirror
update-account.mjs/update-contact.mjs by importing utils.cleanObject and passing
the cleaned payload to this.app.updateOpportunity. Specifically, add an import
for utils.cleanObject at the top, and replace the raw data object in the
this.app.updateOpportunity call (the object built from
owner_id/name/amount/opportunity_stage_id/closed_date/account_id and using
this.opportunityId/this.ownerId/etc.) with utils.cleanObject(...) so undefined
fields are removed before sending to the API.
| opportunityId: { | ||
| type: "string", | ||
| label: "Opportunity ID", | ||
| description: "The ID of the opportunity.", | ||
| async options({ page }) { | ||
| const { opportunities } = await this.listOpportunities({ | ||
| page: page + 1, | ||
| }); |
There was a problem hiding this comment.
Pass the opportunity page through params.
Line 121 is sent at the top level, but makeRequest() only forwards query-string values from params. The opportunity dropdown will keep loading the default page, so later opportunities never appear for larger workspaces.
🩹 Proposed fix
async options({ page }) {
const { opportunities } = await this.listOpportunities({
- page: page + 1,
+ params: {
+ page: page + 1,
+ },
});
return opportunities?.map(({
id: value, name: label,Also applies to: 223-236, 289-293
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/apollo_io_oauth/apollo_io_oauth.app.mjs` around lines 115 - 122,
The opportunity dropdown is not paginating because page is passed at the top
level instead of inside params for makeRequest; update calls to
listOpportunities to pass the page inside a params object (e.g., change
this.listOpportunities({ page: page + 1 }) to this.listOpportunities({ params: {
page: page + 1 } })) inside the opportunityId field's async options and make the
same change for the other occurrences (lines around 223-236 and 289-293) so
makeRequest will forward the page as a query string.
| function cleanObject(o) { | ||
| for (var k in o || {}) { | ||
| if (typeof o[k] === "undefined") { | ||
| delete o[k]; | ||
| } | ||
| } | ||
| return o; | ||
| } |
There was a problem hiding this comment.
Return value from cleanObject is unsafe for nullish inputs.
If o is undefined or null, Line 15 returns that nullish value instead of an object, which can break downstream object usage.
💡 Proposed fix
-function cleanObject(o) {
- for (var k in o || {}) {
- if (typeof o[k] === "undefined") {
- delete o[k];
- }
- }
- return o;
+function cleanObject(o) {
+ const obj = o || {};
+ for (const k in obj) {
+ if (typeof obj[k] === "undefined") {
+ delete obj[k];
+ }
+ }
+ return obj;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/apollo_io_oauth/common/utils.mjs` around lines 9 - 16, The
cleanObject function returns nullish when passed undefined/null; update
cleanObject to handle nullish inputs by returning an empty object instead of
propagating null/undefined — e.g., at the start of cleanObject check if o ==
null then return {}; otherwise proceed to iterate (or assign o = o || {}), keep
using delete on existing properties and return the sanitized object; reference
the cleanObject function name to locate and modify this logic.
|
@michelle0927 @luancazarine Could you please review this PR and share any feedback? |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
components/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs (1)
64-76:⚠️ Potential issue | 🟠 MajorClean the update payload before sending it.
On Line 68, the raw
dataobject includes optional fields that may beundefined. Please wrap this payload withutils.cleanObject(...)for consistency with sibling update actions and to avoid sending empty fields.♻️ Proposed fix
import app from "../../apollo_io_oauth.app.mjs"; +import utils from "../../common/utils.mjs"; @@ const { opportunity } = await this.app.updateOpportunity({ $, opportunityId: this.opportunityId, - data: { + data: utils.cleanObject({ owner_id: this.ownerId, name: this.name, amount: this.amount, opportunity_stage_id: this.opportunityStageId, closed_date: this.closedDate, account_id: this.accountId, - }, + }), });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs` around lines 64 - 76, The update payload currently passed to this.app.updateOpportunity in async run(...) includes optional fields that may be undefined; wrap the data object with utils.cleanObject(...) (i.e., call utils.cleanObject({ owner_id: this.ownerId, name: this.name, amount: this.amount, opportunity_stage_id: this.opportunityStageId, closed_date: this.closedDate, account_id: this.accountId })) before sending so empty fields are removed, keeping the call to this.app.updateOpportunity and preserving the same keys and this.opportunityId/ $ parameters.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjs`:
- Around line 57-75: The response may omit contacts, so guard against undefined
before using contacts.length and before returning; after calling
this.app.addContactsToSequence in the add-contacts-to-sequence action, coerce or
default the returned contacts to an empty array (e.g., const contacts =
resp.contacts || []) and then use that contacts variable in the
$.export("$summary", ...) and in the return to avoid runtime errors when
contacts is missing.
---
Duplicate comments:
In
`@components/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs`:
- Around line 64-76: The update payload currently passed to
this.app.updateOpportunity in async run(...) includes optional fields that may
be undefined; wrap the data object with utils.cleanObject(...) (i.e., call
utils.cleanObject({ owner_id: this.ownerId, name: this.name, amount:
this.amount, opportunity_stage_id: this.opportunityStageId, closed_date:
this.closedDate, account_id: this.accountId })) before sending so empty fields
are removed, keeping the call to this.app.updateOpportunity and preserving the
same keys and this.opportunityId/ $ parameters.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8bed637a-5fb9-4cbd-935b-9ce5a9c8fd75
📒 Files selected for processing (2)
components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjscomponents/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs
| const { contacts } = await this.app.addContactsToSequence({ | ||
| $, | ||
| sequenceId: this.sequenceId, | ||
| data: { | ||
| contact_ids: this.contactIds, | ||
| emailer_campaign_id: this.sequenceId, | ||
| send_email_from_email_account_id: this.emailAccountId, | ||
| sequence_no_email: this.sequenceNoEmail, | ||
| sequence_active_in_other_campaigns: this.sequenceActiveInOtherCampaigns, | ||
| sequence_finished_in_other_campaigns: this.sequenceFinishedInOtherCampaigns, | ||
| }, | ||
| }); | ||
|
|
||
| $.export("$summary", `Successfully added ${contacts.length} contact${contacts.length === 1 | ||
| ? "" | ||
| : "s"} to sequence.`); | ||
|
|
||
| return contacts; | ||
| }, |
There was a problem hiding this comment.
Guard summary construction against missing contacts.
If the API response shape changes or omits contacts, this will throw at runtime when reading contacts.length. Default to an empty array before exporting $summary and returning data.
Suggested fix
- const { contacts } = await this.app.addContactsToSequence({
+ const { contacts = [] } = await this.app.addContactsToSequence({
$,
sequenceId: this.sequenceId,
data: {
contact_ids: this.contactIds,
emailer_campaign_id: this.sequenceId,
send_email_from_email_account_id: this.emailAccountId,
sequence_no_email: this.sequenceNoEmail,
sequence_active_in_other_campaigns: this.sequenceActiveInOtherCampaigns,
sequence_finished_in_other_campaigns: this.sequenceFinishedInOtherCampaigns,
},
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { contacts } = await this.app.addContactsToSequence({ | |
| $, | |
| sequenceId: this.sequenceId, | |
| data: { | |
| contact_ids: this.contactIds, | |
| emailer_campaign_id: this.sequenceId, | |
| send_email_from_email_account_id: this.emailAccountId, | |
| sequence_no_email: this.sequenceNoEmail, | |
| sequence_active_in_other_campaigns: this.sequenceActiveInOtherCampaigns, | |
| sequence_finished_in_other_campaigns: this.sequenceFinishedInOtherCampaigns, | |
| }, | |
| }); | |
| $.export("$summary", `Successfully added ${contacts.length} contact${contacts.length === 1 | |
| ? "" | |
| : "s"} to sequence.`); | |
| return contacts; | |
| }, | |
| const { contacts = [] } = await this.app.addContactsToSequence({ | |
| $, | |
| sequenceId: this.sequenceId, | |
| data: { | |
| contact_ids: this.contactIds, | |
| emailer_campaign_id: this.sequenceId, | |
| send_email_from_email_account_id: this.emailAccountId, | |
| sequence_no_email: this.sequenceNoEmail, | |
| sequence_active_in_other_campaigns: this.sequenceActiveInOtherCampaigns, | |
| sequence_finished_in_other_campaigns: this.sequenceFinishedInOtherCampaigns, | |
| }, | |
| }); | |
| $.export("$summary", `Successfully added ${contacts.length} contact${contacts.length === 1 | |
| ? "" | |
| : "s"} to sequence.`); | |
| return contacts; | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjs`
around lines 57 - 75, The response may omit contacts, so guard against undefined
before using contacts.length and before returning; after calling
this.app.addContactsToSequence in the add-contacts-to-sequence action, coerce or
default the returned contacts to an empty array (e.g., const contacts =
resp.contacts || []) and then use that contacts variable in the
$.export("$summary", ...) and in the return to avoid runtime errors when
contacts is missing.
|
I didn't see this PR but I already shipped a set of tools for Apollo: #20517 |
Summary
apollo_io_oauth) to achieve feature parity with the existing API key version (apollo_io)Verified
apollo_io_oauth-key prefixesBearertoken (this.$auth.oauth_access_token) instead of API keyconstants.mjs,utils.mjs) are identical to the API key versionCloses: #20386
Summary by CodeRabbit
New Features
Chores