Skip to content

Conversation

@GTFalcao
Copy link
Collaborator

@GTFalcao GTFalcao commented Aug 28, 2025

The issue is the limit of 5 requests per second for the search endpoint specifically. We theoretically already enforce this per component, but each user can be running multiple components simultaneously (many HubSpot actions and sources need to use the search endpoint).

On top of reducing the max requests per second per component from 4 to 2, I'm implementing retrying up to 5 times on a 429 response (specifically for the search endpoint). I've added a bit of exponential backoff, but total delay between requests only goes up to 17 seconds to avoid component runs taking too long.

Summary by CodeRabbit

  • New Features

    • CRM search now retries on rate-limit responses with exponential backoff and jitter (up to 5 attempts), improving success rates under throttling.
  • Chores

    • Global HubSpot request rate spaced further to reduce throttling.
    • Package and many HubSpot action/source versions bumped; no behavioral changes.

@vercel
Copy link

vercel bot commented Aug 28, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
pipedream-docs Ignored Ignored Aug 28, 2025 9:59pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Aug 28, 2025 9:59pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 28, 2025


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9b34721 and 4de5401.

📒 Files selected for processing (1)
  • components/hubspot/package.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • components/hubspot/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Lint Code Base
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hubspot-search-rate-limits

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (27)
components/hubspot/actions/list-marketing-events/list-marketing-events.mjs (2)

20-21: Critical: variable shadowing and mutating the iterated array

results is declared as the accumulator (Line 20) and then shadowed by the API response destructure (Line 28). Inside the loop you push into the same array you're iterating, which can lead to incorrect iteration and potential unbounded loops. Also the summary and return reference the wrong array.

Apply:

-    const results = [];
+    const items = [];
@@
-      const {
-        paging, results,
-      } = await this.hubspot.listMarketingEvents({
+      const {
+        paging, results: pageResults,
+      } = await this.hubspot.listMarketingEvents({
@@
-      if (!results?.length) {
+      if (!pageResults?.length) {
         break;
       }
-      for (const item of results) {
-        results.push(item);
+      for (const item of pageResults) {
+        items.push(item);
         count++;
         if (count >= this.maxResults) {
           break;
         }
       }
@@
-    $.export("$summary", `Found ${results.length} event${results.length === 1
+    $.export("$summary", `Found ${items.length} event${items.length === 1
       ? ""
       : "s"}`);
-    return results;
+    return items;

Also applies to: 27-38, 47-50


22-23: Safer paging + limit handling

  • Access paging.next.after defensively.
  • Track the cursor separately from the boolean.
  • Reduce over-fetch by limiting to remaining results.
-    const params = {
-      limit: 100,
-    };
-    let hasMore, count = 0;
+    const params = {};
+    let next, count = 0;
@@
-      hasMore = paging?.next.after;
-      params.after = paging?.next.after;
-    } while (hasMore && count < this.maxResults);
+      next = paging?.next?.after;
+      params.after = next;
+      params.limit = Math.min(100, this.maxResults - count);
+    } while (next && count < this.maxResults);

Also applies to: 24-25, 43-45

components/hubspot/sources/new-note/new-note.mjs (1)

36-41: Bug: possible TypeError when spreading customObjects

When listSchemas() returns no results, custom is undefined, so customObjects becomes undefined. Spreading ...customObjects will throw. Default to [].

-      const { results: custom } = await this.hubspot.listSchemas();
-      const customObjects = custom?.map(({ fullyQualifiedName }) => fullyQualifiedName);
+      const { results: custom } = await this.hubspot.listSchemas();
+      const customObjects = (custom || []).map(({ fullyQualifiedName }) => fullyQualifiedName);
       const associations = [
         ...objectTypes,
         ...customObjects,
       ];
components/hubspot/actions/list-marketing-emails/list-marketing-emails.mjs (2)

82-107: results is shadowed; you're pushing into the page array, not the accumulator (risk of runaway iteration) and returning an empty list.

Use a distinct accumulator and rename the destructured page results.

Apply:

-    const results = [];
+    const items = [];

-      const {
-        paging, results,
-      } = await this.hubspot.listMarketingEmails({
+      const {
+        paging, results: pageResults,
+      } = await this.hubspot.listMarketingEmails({

-      if (!results?.length) {
+      if (!pageResults?.length) {
         break;
       }
-      for (const item of results) {
-        results.push(item);
+      for (const item of pageResults) {
+        items.push(item);
         count++;
         if (count >= this.maxResults) {
           break;
         }
       }
...
-    $.export("$summary", `Found ${results.length} email${results.length === 1
+    $.export("$summary", `Found ${items.length} email${items.length === 1
       ? ""
       : "s"}`);
-    return results;
+    return items;

Also applies to: 117-121


113-115: Optional chaining bug on paging.next.after.

This will throw when paging.next is undefined; chain both accesses.

-      hasMore = paging?.next.after;
-      params.after = paging?.next.after;
+      const nextAfter = paging?.next?.after;
+      hasMore = Boolean(nextAfter);
+      params.after = nextAfter;
components/hubspot/actions/create-meeting/create-meeting.mjs (1)

87-93: Validate JSON input and required properties before calling the API.

Currently JSON.parse errors surface as generic exceptions and required props aren’t enforced.

-    const properties = objectProperties
-      ? typeof objectProperties === "string"
-        ? JSON.parse(objectProperties)
-        : objectProperties
-      : otherProperties;
+    let properties = otherProperties;
+    if (objectProperties) {
+      if (typeof objectProperties === "string") {
+        try {
+          properties = JSON.parse(objectProperties);
+        } catch (e) {
+          throw new ConfigurationError("Invalid JSON in `Meeting Properties`.");
+        }
+      } else {
+        properties = objectProperties;
+      }
+    }
+    const required = ["hs_meeting_title", "hs_meeting_body", "hs_meeting_start_time", "hs_meeting_end_time"];
+    for (const key of required) {
+      if (!properties?.[key]) {
+        throw new ConfigurationError(`Missing required meeting property: ${key}`);
+      }
+    }

Also applies to: 109-113

components/hubspot/actions/list-forms/list-forms.mjs (2)

26-49: Same accumulator/shadowing bug as emails action; fixes required.

-    const results = [];
+    const items = [];
...
-      const {
-        paging, results,
+      const {
+        paging, results: pageResults,
       } = await this.hubspot.listMarketingForms({
...
-      if (!results?.length) {
+      if (!pageResults?.length) {
         break;
       }
-      for (const item of results) {
-        results.push(item);
+      for (const item of pageResults) {
+        items.push(item);
         count++;
         if (count >= this.maxResults) {
           break;
         }
       }
...
-    $.export("$summary", `Found ${results.length} form${results.length === 1
+    $.export("$summary", `Found ${items.length} form${items.length === 1
       ? ""
       : "s"}`);
-    return results;
+    return items;

Also applies to: 54-58


50-52: Optional chaining bug on paging.next.after.

Chain both accesses.

-      hasMore = paging?.next.after;
-      params.after = paging?.next.after;
+      const nextAfter = paging?.next?.after;
+      hasMore = Boolean(nextAfter);
+      params.after = nextAfter;
components/hubspot/actions/batch-create-or-update-contact/batch-create-or-update-contact.mjs (4)

29-33: Likely incorrect HubSpot object name ("contact" → "contacts").

HubSpot search endpoint path is /crm/v3/objects/contacts/search (plural). Inconsistent with other modules (e.g., companies uses plural). This will 404 or fail routing in the app client.

-        object: "contact",
+        object: "contacts",

27-44: Guard against undefined results and filter invalid emails.

If search returns no results, results is undefined, making updateEmails.includes(...) throw. Also, emails may include falsy values.

-      const emails = contacts.map(({ email }) => email);
-      const { results } = await this.hubspot.searchCRM({
+      const emails = contacts.map(({ email }) => email).filter(Boolean);
+      const { results = [] } = await this.hubspot.searchCRM({
         $,
-        object: "contact",
+        object: "contacts",
         data: {
           filters: [
             {
               propertyName: "email",
               operator: "IN",
               values: emails,
             },
           ],
         },
       });
-      const updateEmails = results?.map(({ properties }) => properties.email);
-      const insertProperties = contacts.filter(({ email }) => !updateEmails.includes(email))
+      const updateEmails = results.map(({ properties }) => properties.email).filter(Boolean);
+      const insertProperties = contacts
+        .filter(({ email }) => email && !updateEmails.includes(email))
         .map((properties) => ({
           properties,
         }));

47-53: Avoid passing id inside properties on update.

contacts.find(...) returns the full contact object. If it contains id, you’ll send id both at top-level and inside properties. Strip it to prevent validation errors.

-      for (const contact of results) {
-        updateProperties.push({
-          id: contact.id,
-          properties: contacts.find(({ email }) => contact.properties.email === email),
-        });
-      }
+      for (const contact of results) {
+        const found = contacts.find(({ email }) => contact.properties.email === email) || {};
+        const { id: _discard, ...props } = found;
+        updateProperties.push({
+          id: contact.id,
+          properties: props,
+        });
+      }

67-77: Duplicate updates possible — dedupe by id before calling batch update.

A contact present in search results and also provided with explicit id will be pushed twice. HubSpot batch update may reject duplicate IDs.

-    if (updatePropertiesWithId?.length) {
-      updateProperties.push(...updatePropertiesWithId);
-    }
+    if (updatePropertiesWithId?.length) {
+      updateProperties.push(...updatePropertiesWithId);
+    }
+    // Dedupe updates by id
+    const byId = new Map();
+    for (const u of updateProperties) {
+      const prev = byId.get(u.id);
+      byId.set(u.id, prev
+        ? { id: u.id, properties: { ...(prev.properties || {}), ...(u.properties || {}) } }
+        : u);
+    }
+    const uniqueUpdateProperties = Array.from(byId.values());

And use uniqueUpdateProperties below.

components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs (1)

99-111: Bind the searchCRM method to preserve its context in getPaginatedItems. Without binding, this inside searchCRM will be undefined, leading to runtime errors.

-      const updatedDeals = await this.getPaginatedItems(this.hubspot.searchCRM, params);
+      const updatedDeals = await this.getPaginatedItems(
+        this.hubspot.searchCRM.bind(this.hubspot),
+        params,
+      );
components/hubspot/actions/list-campaigns/list-campaigns.mjs (1)

41-60: Fix variable shadowing and self-extending iteration causing wrong results and potential infinite loop

  • The destructured results from the API shadows the outer results array.
  • You then iterate the API results and push into the same identifier, growing the array during iteration. This can cause iteration over newly appended items and, combined with shadowing, the outer array remains empty, leading to an incorrect summary/return.

Apply this refactor to use distinct names and accumulate into a dedicated array:

-  const results = [];
+  const items = [];
   let hasMore, count = 0;

   const params = {
     sort: this.sort,
   };

   do {
     const {
-        paging, results,
+        paging, results: pageResults,
       } = await this.hubspot.listCampaigns({
         $,
         params,
       });
-      if (!results?.length) {
+      if (!pageResults?.length) {
         break;
       }
-      for (const item of results) {
-        results.push(item);
+      for (const item of pageResults) {
+        items.push(item);
         count++;
         if (count >= this.maxResults) {
           break;
         }
       }
       hasMore = paging?.next.after;
       params.after = paging?.next.after;
-    } while (hasMore && count < this.maxResults);
+    } while (hasMore && count < this.maxResults);

-    $.export("$summary", `Found ${results.length} campaign${results.length === 1
+    $.export("$summary", `Found ${items.length} campaign${items.length === 1
       ? ""
       : "s"}`);
-    return results;
+    return items;

Also applies to: 34-35, 62-66, 37-39

components/hubspot/sources/delete-blog-article/delete-blog-article.mjs (1)

22-27: Use deleted timestamp consistently for event metadata

getTs returns deletedAt, but generateMeta sets ts from created. This can mis-order events and confuse users.

-      const ts = Date.parse(blogpost.created);
+      const ts = this.getTs(blogpost);
       return {
         id: `${id}${this.getTs(blogpost)}`,
         summary,
         ts,
       };

Also applies to: 14-16

components/hubspot/actions/get-file-public-url/get-file-public-url.mjs (2)

30-35: Fix possible TypeError when file isn’t found.

Accessing file.id before verifying file can be undefined.

Apply:

-    const { results: files } = await this.hubspot.searchFiles();
-    const file = files.find(({ url }) => url === fileUrl );
-    const fileId = file.id;
-    if (!fileId) {
-      throw new Error(`File not found at ${fileUrl}`);
-    }
+    const { results: files } = await this.hubspot.searchFiles();
+    const file = files.find(({ url }) => url === fileUrl);
+    if (!file?.id) {
+      throw new Error(`File not found at ${fileUrl}`);
+    }
+    const fileId = file.id;

18-23: Clamp expirationSeconds to API limits (1–21600).

Prevents 400s and enforces documented bounds.

-    const {
-      fileUrl,
-      expirationSeconds,
-    } = this;
+    const { fileUrl, expirationSeconds } = this;
+    const exp = Math.min(Math.max(Number(expirationSeconds ?? 3600), 1), 21600);
@@
-      params: {
-        expirationSeconds,
-      },
+      params: { expirationSeconds: exp },

Also applies to: 37-42

components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (1)

36-43: Guard against missing version history.

properties.dealstage?.versions[0] will throw if versions is undefined.

-      return properties.dealstage?.versions[0].timestamp;
+      return properties.dealstage?.versions?.[0]?.timestamp ?? 0;
components/hubspot/actions/batch-create-companies/batch-create-companies.mjs (1)

43-45: Harden error parsing; current logic can throw and hide root cause.

Avoid double JSON.parse and regex splits on error strings.

-      const message = JSON.parse((JSON.parse(error.message).message).split(/:(.+)/)[1])[0].message;
-      throw new ConfigurationError(message.split(/:(.+)/)[0]);
+      const message =
+        error?.response?.data?.errors?.[0]?.message ||
+        error?.response?.data?.message ||
+        error?.message;
+      throw new ConfigurationError(message);
components/hubspot/sources/new-or-updated-line-item/new-or-updated-line-item.mjs (1)

80-82: Update searchCRM invocations to new object-style signature

  • Replace every await this.searchCRM(params, after) with await this.searchCRM({ ...params, after }) in all source files (e.g. new-ticket, new-or-updated-*, etc.) and adjust the wrapper in components/hubspot/sources/common/common.mjs accordingly.
    Use rg -nP '\bsearchCRM\s*\(' -g '*.mjs' to locate all call-sites.
components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs (1)

102-103: Bind or wrap searchCRM to preserve its this context
Without binding, calling this.getPaginatedItems(this.hubspot.searchCRM, params) will lose the HubSpot app instance, breaking makeRequest inside searchCRM. Replace with, for example:

- const updatedContacts = await this.getPaginatedItems(this.hubspot.searchCRM, params);
+ const updatedContacts = await this.getPaginatedItems(
+   opts => this.hubspot.searchCRM(opts),
+   params,
+ );

Or:

const updatedContacts = await this.getPaginatedItems(
  this.hubspot.searchCRM.bind(this.hubspot),
  params,
);
components/hubspot/sources/new-or-updated-product/new-or-updated-product.mjs (1)

83-85: Update all processResults calls to match the new searchCRM signature
Replace every instance of

await this.searchCRM(params, after);

with

await this.searchCRM({ ...params, after });

in the following files under components/hubspot/sources/:

  • new-ticket/new-ticket.mjs
  • new-or-updated-line-item/new-or-updated-line-item.mjs
  • new-or-updated-custom-object/new-or-updated-custom-object.mjs
  • new-or-updated-product/new-or-updated-product.mjs
  • new-or-updated-crm-object/new-or-updated-crm-object.mjs
  • new-or-updated-contact/new-or-updated-contact.mjs
  • new-or-updated-company/new-or-updated-company.mjs
  • new-or-updated-deal/new-or-updated-deal.mjs

This ensures the after cursor is passed as part of the options object instead of a positional argument.

components/hubspot/actions/list-pages/list-pages.mjs (1)

75-107: Bug: results variable shadowed; items pushed into the wrong array.

Inner destructuring defines results and you then push into that local array instead of the outer accumulator, so you return an empty list. Also guard paging?.next?.after.

Apply:

   async run({ $ }) {
-    const results = [];
+    const results = [];
     let hasMore, count = 0;
@@
-    do {
-      const {
-        paging, results,
-      } = await this.hubspot.listPages({
+    do {
+      const {
+        paging, results: pageResults,
+      } = await this.hubspot.listPages({
         $,
         params,
       });
-      if (!results?.length) {
+      if (!pageResults?.length) {
         break;
       }
-      for (const item of results) {
-        results.push(item);
+      for (const item of pageResults) {
+        results.push(item);
         count++;
         if (count >= this.maxResults) {
           break;
         }
       }
-      hasMore = paging?.next.after;
-      params.after = paging?.next.after;
+      hasMore = paging?.next?.after;
+      params.after = hasMore;
     } while (hasMore && count < this.maxResults);
@@
-    $.export("$summary", `Found ${results.length} page${results.length === 1
+    $.export("$summary", `Found ${results.length} page${results.length === 1
       ? ""
       : "s"}`);
     return results;

Also applies to: 110-114

components/hubspot/actions/list-blog-posts/list-blog-posts.mjs (1)

75-107: Bug: same results-shadowing issue as list-pages.

Local results from the API shadows the accumulator, so nothing is returned.

Apply:

   async run({ $ }) {
-    const results = [];
+    const results = [];
     let hasMore, count = 0;
@@
-    do {
-      const {
-        paging, results,
-      } = await this.hubspot.getBlogPosts({
+    do {
+      const {
+        paging, results: pageResults,
+      } = await this.hubspot.getBlogPosts({
         $,
         params,
       });
-      if (!results?.length) {
+      if (!pageResults?.length) {
         break;
       }
-      for (const item of results) {
-        results.push(item);
+      for (const item of pageResults) {
+        results.push(item);
         count++;
         if (count >= this.maxResults) {
           break;
         }
       }
-      hasMore = paging?.next.after;
-      params.after = paging?.next.after;
+      hasMore = paging?.next?.after;
+      params.after = hasMore;
     } while (hasMore && count < this.maxResults);

Also applies to: 110-114

components/hubspot/actions/create-communication/create-communication.mjs (1)

76-81: Harden JSON parsing for objectProperties.

Invalid JSON will throw a generic error. Wrap in try/catch and surface a ConfigurationError with a clear message.

-    const properties = objectProperties
-      ? typeof objectProperties === "string"
-        ? JSON.parse(objectProperties)
-        : objectProperties
-      : otherProperties;
+    let properties = otherProperties;
+    if (objectProperties) {
+      if (typeof objectProperties === "string") {
+        try {
+          properties = JSON.parse(objectProperties);
+        } catch (err) {
+          throw new ConfigurationError("Invalid JSON in `objectProperties`");
+        }
+      } else {
+        properties = objectProperties;
+      }
+    }
components/hubspot/actions/get-associated-emails/get-associated-emails.mjs (2)

71-74: Bug: incorrect emptiness check due to operator precedence.

if (!results?.length > 0) always evaluates unexpectedly. Use a direct length check.

-    if (!results?.length > 0) {
+    if (!results?.length) {
       $.export("$summary", "No emails found with this association");
       return [];
     }

80-95: Robustness: default to empty array and simplify sorting.

Avoid emails being undefined and remove the || [] no-op.

-    const { results: emails } = await this.hubspot.batchGetObjects({
+    const { results: emails = [] } = await this.hubspot.batchGetObjects({
       $,
       objectType: "emails",
       data: {
         properties,
         inputs: emailIds,
       },
     });
 
-    // Sort emails by timestamp in descending order (most recent first)
-    emails?.sort((a, b) => {
+    // Sort emails by timestamp in descending order (most recent first)
+    emails.sort((a, b) => {
       const timestampA = new Date(a.properties?.hs_timestamp || 0).getTime();
       const timestampB = new Date(b.properties?.hs_timestamp || 0).getTime();
       return timestampB - timestampA;
-    }) || [];
+    });
 
     const summary = `Successfully retrieved ${emails.length} email(s)`;
 
     $.export("$summary", summary);
 
     return emails;

Also applies to: 96-101

🧹 Nitpick comments (15)
components/hubspot/sources/new-note/new-note.mjs (1)

16-18: Optional: guard against missing/invalid timestamps

Date.parse(note.createdAt) can return NaN. Consider a fallback (e.g., updatedAt) or filter out invalid notes.

-    getTs(note) {
-      return Date.parse(note.createdAt);
-    },
+    getTs(note) {
+      const ts = Date.parse(note.createdAt);
+      return Number.isNaN(ts) ? 0 : ts;
+    },

Also applies to: 26-28

components/hubspot/sources/new-or-updated-crm-object/new-or-updated-crm-object.mjs (1)

43-45: Use strict equality; avoid coercion.

Minor, but improves correctness/readability.

-      const propertyName = (object == "contacts")
+      const propertyName = (object === "contacts")
         ? "lastmodifieddate"
         : "hs_lastmodifieddate";
...
-      const object = (this.objectType == "company")
+      const object = (this.objectType === "company")
         ? "companies"
         : `${this.objectType}s`;

Also applies to: 60-63

components/hubspot/actions/batch-create-or-update-contact/batch-create-or-update-contact.mjs (3)

79-92: Skip empty batch calls and use deduped arrays.

Avoid issuing API calls with empty inputs; also ensure you use the deduped updates.

-    response.created = await this.hubspot.batchCreateContacts({
-      $,
-      data: {
-        inputs: insertProperties,
-      },
-    });
-    response.updated = await this.hubspot.batchUpdateContacts({
-      $,
-      data: {
-        inputs: updateProperties,
-      },
-    });
+    response.created = insertProperties.length
+      ? await this.hubspot.batchCreateContacts({ $, data: { inputs: insertProperties } })
+      : { results: [] };
+    response.updated = uniqueUpdateProperties?.length
+      ? await this.hubspot.batchUpdateContacts({ $, data: { inputs: uniqueUpdateProperties } })
+      : { results: [] };

93-95: Fix summary counts after dedupe and conditional calls.

Use the arrays actually sent to the API.

-    $.export("$summary", `Successfully created ${insertProperties.length} and updated ${updateProperties.length} contacts`);
+    $.export("$summary", `Successfully created ${insertProperties.length} and updated ${uniqueUpdateProperties?.length || 0} contacts`);

18-26: Input validation for contacts schema (optional).

Consider validating required fields (e.g., email for create; id or email for update) and max batch sizes to reduce 429s even with the new retry/backoff.

Do you want a zod/Yup-based validator and chunking helper (e.g., size 100) to align with HubSpot limits?

components/hubspot/actions/batch-update-companies/batch-update-companies.mjs (1)

43-45: Harden error parsing to avoid double-parse crashes.

JSON.parse on nested messages can throw and mask original errors. Guard parsing and fall back to raw message.

-      const message = JSON.parse((JSON.parse(error.message).message).split(/:(.+)/)[1])[0].message;
-      throw new ConfigurationError(message.split(/:(.+)/)[0]);
+      let msg = error?.message;
+      try {
+        const outer = JSON.parse(msg);
+        const inner = typeof outer?.message === "string" ? outer.message : "";
+        const sliced = inner.split(/:(.+)/)[1];
+        const arr = JSON.parse(sliced || "[]");
+        msg = (arr?.[0]?.message || inner || msg).split(/:(.+)/)[0];
+      } catch (_) {
+        /* keep original msg */
+      }
+      throw new ConfigurationError(msg);
components/hubspot/sources/new-event/new-event.mjs (1)

10-12: Version bump OK; fix brand capitalization in description.

Only metadata changed. Please update “Hubspot” → “HubSpot” in the user-facing description.

-  description: "Emit new event for each new Hubspot event. Note: Only available for Marketing Hub Enterprise, Sales Hub Enterprise, Service Hub Enterprise, or CMS Hub Enterprise accounts",
+  description: "Emit new event for each new HubSpot event. Note: Only available for Marketing Hub Enterprise, Sales Hub Enterprise, Service Hub Enterprise, or CMS Hub Enterprise accounts",
components/hubspot/actions/create-deal/create-deal.mjs (1)

8-10: Version bump OK; fix brand capitalization in description.

Update “Hubspot” → “HubSpot”.

-  description: "Create a deal in Hubspot. [See the documentation](https://developers.hubspot.com/docs/api/crm/deals#endpoint?spec=POST-/crm/v3/objects/deals)",
+  description: "Create a deal in HubSpot. [See the documentation](https://developers.hubspot.com/docs/api/crm/deals#endpoint?spec=POST-/crm/v3/objects/deals)",
components/hubspot/actions/list-campaigns/list-campaigns.mjs (2)

37-39: Avoid sending undefined query params

Only include sort when provided to keep requests clean and reduce chance of API-side validation noise.

-  const params = {
-    sort: this.sort,
-  };
+  const params = {};
+  if (this.sort) params.sort = this.sort;

7-7: Version bump looks good

Metadata bump to 0.0.2 is fine. Please include a short changelog entry referencing the HubSpot rate-limit adjustments in hubspot.app.mjs for traceability.

components/hubspot/sources/delete-blog-article/delete-blog-article.mjs (1)

29-36: Consider sorting by deleted time

For a deleted-posts source, sorting by -deletedAt likely matches user expectations better than -updatedAt. Review API support and adjust if available.

components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (1)

100-106: Avoid repeated owner lookups; memoize.

This loop fetches the same owner multiple times.

-            if (deal.properties.hubspot_owner_id) {
-              deal.properties.owner = await this.getOwner(deal.properties.hubspot_owner_id);
-            }
+            if (deal.properties.hubspot_owner_id) {
+              deal.properties.owner = await this.getOwnerCached(deal.properties.hubspot_owner_id);
+            }

Add once under methods:

ownerCache: new Map(),
async getOwnerCached(ownerId) {
  if (!this.ownerCache.has(ownerId)) {
    this.ownerCache.set(ownerId, await this.getOwner(ownerId));
  }
  return this.ownerCache.get(ownerId);
}
components/hubspot/hubspot.app.mjs (3)

18-18: Global rate limit halved to 2 rps — verify impact.
This affects all HubSpot endpoints, not just search. Confirm no noticeable throughput regressions for non-search actions/sources. Consider keeping minTime at 250 ms and applying a dedicated limiter just for the search endpoint if needed.


697-725: Add minimal observability for retries.
Log attempt count and delay (when available) to aid support/debugging under rate limit pressure. Use $ from opts if present; otherwise skip.

Example (inline within the retry branch after computing delay):

+            const $ = opts?.$;
+            if ($ && typeof $.log === "function") {
+              $.log(`HubSpot searchCRM 429: retrying attempt ${attempt + 1}/${MAX_ATTEMPTS} in ${delay}ms`);
+            }

18-18: Architecture: consider endpoint-scoped limiter.
If non-search endpoints suffer from the stricter global rate, introduce a separate Bottleneck instance just for /search requests (e.g., 500 ms) and keep the global limiter at 250 ms for others.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 24f033c and 04a99d3.

📒 Files selected for processing (65)
  • components/hubspot/actions/add-contact-to-list/add-contact-to-list.mjs (1 hunks)
  • components/hubspot/actions/batch-create-companies/batch-create-companies.mjs (1 hunks)
  • components/hubspot/actions/batch-create-or-update-contact/batch-create-or-update-contact.mjs (1 hunks)
  • components/hubspot/actions/batch-update-companies/batch-update-companies.mjs (1 hunks)
  • components/hubspot/actions/batch-upsert-companies/batch-upsert-companies.mjs (1 hunks)
  • components/hubspot/actions/create-associations/create-associations.mjs (1 hunks)
  • components/hubspot/actions/create-communication/create-communication.mjs (1 hunks)
  • components/hubspot/actions/create-company/create-company.mjs (1 hunks)
  • components/hubspot/actions/create-custom-object/create-custom-object.mjs (1 hunks)
  • components/hubspot/actions/create-deal/create-deal.mjs (1 hunks)
  • components/hubspot/actions/create-engagement/create-engagement.mjs (1 hunks)
  • components/hubspot/actions/create-lead/create-lead.mjs (1 hunks)
  • components/hubspot/actions/create-meeting/create-meeting.mjs (1 hunks)
  • components/hubspot/actions/create-note/create-note.mjs (1 hunks)
  • components/hubspot/actions/create-or-update-contact/create-or-update-contact.mjs (1 hunks)
  • components/hubspot/actions/create-task/create-task.mjs (1 hunks)
  • components/hubspot/actions/create-ticket/create-ticket.mjs (1 hunks)
  • components/hubspot/actions/enroll-contact-into-workflow/enroll-contact-into-workflow.mjs (1 hunks)
  • components/hubspot/actions/get-associated-emails/get-associated-emails.mjs (1 hunks)
  • components/hubspot/actions/get-associated-meetings/get-associated-meetings.mjs (1 hunks)
  • components/hubspot/actions/get-company/get-company.mjs (1 hunks)
  • components/hubspot/actions/get-contact/get-contact.mjs (1 hunks)
  • components/hubspot/actions/get-deal/get-deal.mjs (1 hunks)
  • components/hubspot/actions/get-file-public-url/get-file-public-url.mjs (1 hunks)
  • components/hubspot/actions/get-meeting/get-meeting.mjs (1 hunks)
  • components/hubspot/actions/get-subscription-preferences/get-subscription-preferences.mjs (1 hunks)
  • components/hubspot/actions/list-blog-posts/list-blog-posts.mjs (1 hunks)
  • components/hubspot/actions/list-campaigns/list-campaigns.mjs (1 hunks)
  • components/hubspot/actions/list-forms/list-forms.mjs (1 hunks)
  • components/hubspot/actions/list-marketing-emails/list-marketing-emails.mjs (1 hunks)
  • components/hubspot/actions/list-marketing-events/list-marketing-events.mjs (1 hunks)
  • components/hubspot/actions/list-pages/list-pages.mjs (1 hunks)
  • components/hubspot/actions/list-templates/list-templates.mjs (1 hunks)
  • components/hubspot/actions/search-crm/search-crm.mjs (1 hunks)
  • components/hubspot/actions/update-company/update-company.mjs (1 hunks)
  • components/hubspot/actions/update-contact/update-contact.mjs (1 hunks)
  • components/hubspot/actions/update-custom-object/update-custom-object.mjs (1 hunks)
  • components/hubspot/actions/update-deal/update-deal.mjs (1 hunks)
  • components/hubspot/actions/update-lead/update-lead.mjs (1 hunks)
  • components/hubspot/hubspot.app.mjs (2 hunks)
  • components/hubspot/package.json (1 hunks)
  • components/hubspot/sources/delete-blog-article/delete-blog-article.mjs (1 hunks)
  • components/hubspot/sources/new-company-property-change/new-company-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (1 hunks)
  • components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-email-event/new-email-event.mjs (1 hunks)
  • components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs (1 hunks)
  • components/hubspot/sources/new-engagement/new-engagement.mjs (1 hunks)
  • components/hubspot/sources/new-event/new-event.mjs (1 hunks)
  • components/hubspot/sources/new-form-submission/new-form-submission.mjs (1 hunks)
  • components/hubspot/sources/new-note/new-note.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-blog-article/new-or-updated-blog-article.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-company/new-or-updated-company.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-contact/new-or-updated-contact.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-crm-object/new-or-updated-crm-object.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-custom-object/new-or-updated-custom-object.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-deal/new-or-updated-deal.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-line-item/new-or-updated-line-item.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-product/new-or-updated-product.mjs (1 hunks)
  • components/hubspot/sources/new-social-media-message/new-social-media-message.mjs (1 hunks)
  • components/hubspot/sources/new-task/new-task.mjs (1 hunks)
  • components/hubspot/sources/new-ticket-property-change/new-ticket-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-ticket/new-ticket.mjs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
components/hubspot/hubspot.app.mjs (1)
components/hubspot/common/constants.mjs (1)
  • API_PATH (9-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
🔇 Additional comments (62)
components/hubspot/sources/new-form-submission/new-form-submission.mjs (1)

9-9: Version bump only — LGTM.

No functional impact detected here.

components/hubspot/actions/update-custom-object/update-custom-object.mjs (1)

9-9: Version bump only — LGTM.

No behavior change in this action.

components/hubspot/sources/new-task/new-task.mjs (1)

11-11: Version bump only — LGTM.

No runtime changes observed.

components/hubspot/actions/create-associations/create-associations.mjs (1)

8-8: Version bump only — LGTM.

Action logic unchanged.

components/hubspot/actions/get-company/get-company.mjs (1)

9-9: Version bump only — LGTM.

No functional differences.

components/hubspot/actions/list-marketing-events/list-marketing-events.mjs (1)

7-7: Version bump only — OK

Metadata update looks fine.

components/hubspot/sources/new-note/new-note.mjs (1)

11-11: Version bump only — OK

No functional changes in this file.

components/hubspot/actions/update-deal/update-deal.mjs (1)

10-10: Version bump only — OK

No logic changes detected.

components/hubspot/actions/create-ticket/create-ticket.mjs (1)

9-9: Version bump only — OK

No functional modifications.

components/hubspot/actions/get-deal/get-deal.mjs (1)

9-9: Version bump only — OK

Looks good.

components/hubspot/actions/list-marketing-emails/list-marketing-emails.mjs (1)

7-7: Version bump only — OK.

components/hubspot/actions/create-meeting/create-meeting.mjs (1)

13-13: Version bump only — OK.

components/hubspot/actions/get-contact/get-contact.mjs (1)

9-9: Version bump only — OK.

components/hubspot/actions/list-forms/list-forms.mjs (1)

7-7: Version bump only — OK.

components/hubspot/actions/create-custom-object/create-custom-object.mjs (1)

9-9: Version bump only — OK.

No behavioral changes. Safe to merge.

components/hubspot/sources/new-or-updated-company/new-or-updated-company.mjs (2)

12-12: Version bump only — OK.

No logic changes in this source.


84-85: All searchCRM invocations are properly awaited. Only the method definitions themselves (in hubspot.app.mjs and common.mjs) appear without await.

components/hubspot/sources/new-email-event/new-email-event.mjs (1)

11-11: Version bump only — OK.

No runtime changes.

components/hubspot/actions/batch-create-or-update-contact/batch-create-or-update-contact.mjs (1)

29-41: searchCRM is properly rate-limited and retry/backoff meets 2 RPS and ~17 s budget. The Bottleneck limiter (minTime = 500 ms) wraps all makeRequest calls, and searchCRM’s exponential backoff (5 attempts, delays 1–1.5 s, 2–2.5 s, 4–4.5 s, 8–8.5 s) caps at ~17 s total.

components/hubspot/actions/update-contact/update-contact.mjs (1)

10-10: Version bump only — OK.

No behavior change.

components/hubspot/actions/batch-update-companies/batch-update-companies.mjs (1)

10-10: Version bump is fine.

Metadata-only change; no runtime impact here.

components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs (1)

10-10: Version bump looks good.

components/hubspot/actions/update-company/update-company.mjs (1)

10-10: OK to ship.

Patch version bump only.

components/hubspot/actions/add-contact-to-list/add-contact-to-list.mjs (1)

7-7: LGTM on version bump.

components/hubspot/actions/create-engagement/create-engagement.mjs (1)

12-12: Version increment acknowledged.

components/hubspot/sources/new-social-media-message/new-social-media-message.mjs (1)

9-10: LGTM on version bump.

No functional changes in this file.

components/hubspot/sources/new-or-updated-deal/new-or-updated-deal.mjs (1)

11-13: Verify searchCRM signature & fix HubSpot branding

  • Confirm this.searchCRM(params, after) matches the updated retry signature and that retries don’t break dedupe/order.
  • Honor any Retry-After header on 429 responses, within the 17 s cap.
  • Rename “Hubspot” → “HubSpot” in the description.
-  description: "Emit new event for each new or updated deal in Hubspot",
+  description: "Emit new event for each new or updated deal in HubSpot",
components/hubspot/sources/new-ticket/new-ticket.mjs (2)

72-75: Confirm searchCRM signature remains (params, after) post-retry changes

You’re awaiting this.searchCRM(params, after) which is good given the new async + retry behavior. Please verify the method’s parameter order didn’t change in hubspot.app.mjs during the retry work.


12-12: Version bump acknowledged

No functional changes detected in this source; version 0.0.26 is fine.

components/hubspot/sources/delete-blog-article/delete-blog-article.mjs (1)

9-9: Version bump looks fine

0.0.26 acknowledged; no other behavioral changes here.

components/hubspot/actions/create-lead/create-lead.mjs (1)

12-12: Version bump only — OK

No runtime or API-surface changes detected; 0.0.12 metadata bump is fine.

components/hubspot/actions/create-task/create-task.mjs (1)

10-10: Version bump only — OK

No logic changes; 0.0.5 acknowledged.

components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs (1)

9-9: Version bump LGTM; searchCRM remains compatible with getPaginatedItems
No changes required—hubspot.app.searchCRM still returns { results, paging } and accepts a single params object (which includes object), so getPaginatedItems(this.hubspot.searchCRM, params) continues to work as before.

components/hubspot/sources/new-ticket-property-change/new-ticket-property-change.mjs (1)

10-10: Confirm 429 retry on searchCRM preserves getPaginatedItems pagination semantics

This source calls getPaginatedItems(this.hubspot.searchCRM, params) (new-ticket-property-change.mjs line 103). Manually verify that the added 429-retry logic doesn’t change the returned item array or paging loop.

components/hubspot/actions/enroll-contact-into-workflow/enroll-contact-into-workflow.mjs (1)

7-7: LGTM on version bump.

No functional changes; action path unaffected by search retry updates.

components/hubspot/actions/get-meeting/get-meeting.mjs (1)

9-9: LGTM on version bump.

No behavior changes detected.

components/hubspot/sources/new-or-updated-blog-article/new-or-updated-blog-article.mjs (1)

10-10: LGTM on version bump.

No logic changes; source should be unaffected by the global limiter tweak.

components/hubspot/actions/create-note/create-note.mjs (1)

10-10: LGTM: metadata-only bump.

No runtime changes here; nothing further from the retry/RPS adjustments impacts this action.

components/hubspot/actions/create-company/create-company.mjs (1)

9-9: LGTM: version bump only.

No coupling to searchCRM; safe.

components/hubspot/actions/get-associated-meetings/get-associated-meetings.mjs (1)

9-9: Version bump verified; searchCRM signature unchanged
All hubspot.searchCRM calls—including in get-associated-meetings—accept the same { object, data } shape used elsewhere. The retry/backoff update adds no new required options.

components/hubspot/actions/batch-upsert-companies/batch-upsert-companies.mjs (1)

10-10: LGTM: version bump only.

No interaction with the updated rate limiter or search retries.

components/hubspot/actions/get-file-public-url/get-file-public-url.mjs (1)

7-7: LGTM – version bump only.

components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (2)

12-12: LGTM – version bump only.


92-99: All .searchCRM invocations are properly awaited. Verified via code search that every call to searchCRM( is preceded by await.

components/hubspot/actions/batch-create-companies/batch-create-companies.mjs (1)

10-10: LGTM – version bump only.

components/hubspot/actions/get-subscription-preferences/get-subscription-preferences.mjs (1)

7-7: LGTM – version bump only.

components/hubspot/actions/update-lead/update-lead.mjs (1)

10-10: LGTM – version bump only.

components/hubspot/sources/new-or-updated-line-item/new-or-updated-line-item.mjs (1)

12-12: Version bump aligns with HubSpot search retry rollout

No issues with the metadata change.

components/hubspot/actions/list-templates/list-templates.mjs (1)

7-7: Version bump only — looks good

No functional changes detected.

components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs (1)

10-10: Version bump matches package patch update

No functional impact here.

components/hubspot/actions/create-or-update-contact/create-or-update-contact.mjs (1)

9-9: Version bump only — OK

No action needed.

components/hubspot/sources/new-or-updated-product/new-or-updated-product.mjs (1)

12-12: Version bump consistent with module set

Looks good.

components/hubspot/actions/list-pages/list-pages.mjs (1)

7-7: Patch version bump looks good.

Consistent with package patch bump and non-functional changes here.

components/hubspot/actions/list-blog-posts/list-blog-posts.mjs (1)

7-7: Patch version bump is appropriate.

No behavior change in this file; aligns with app-level limiter/retry updates.

components/hubspot/package.json (1)

3-3: Package patch bump matches the scope of changes.

1.6.1 is appropriate for limiter + retry tweaks without breaking APIs.

components/hubspot/actions/create-communication/create-communication.mjs (1)

11-11: Version bump OK.

components/hubspot/actions/get-associated-emails/get-associated-emails.mjs (1)

8-8: Patch version bump looks fine.

components/hubspot/sources/new-engagement/new-engagement.mjs (1)

11-11: Version bump only — LGTM.

components/hubspot/sources/new-or-updated-custom-object/new-or-updated-custom-object.mjs (1)

10-10: Version bump only — LGTM.

components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs (1)

9-9: Version bump only — LGTM.

components/hubspot/actions/search-crm/search-crm.mjs (1)

20-20: Version bump only — LGTM.

components/hubspot/hubspot.app.mjs (1)

697-725: searchCRM call sites align with new signature – all invocations use either this.searchCRM(params, after) (wrapper) or direct this.hubspot.searchCRM({ object, … }); no string/legacy signatures remain. Manually confirm that the params object in new-deal-in-stage includes the required object field.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/hubspot/actions/list-campaigns/list-campaigns.mjs (1)

34-61: Fix variable shadowing and self-append bug (returns empty array, may iterate indefinitely)

  • Outer results accumulator is shadowed by { results } from the API response.
  • Inside the loop you push into the same array you’re iterating (for ... of results + results.push(item)), which can extend iteration and never populate the accumulator.
  • Summary/return then use the (never-filled) outer results.

Apply this patch:

-    const results = [];
-    let hasMore, count = 0;
+    const items = [];
+    let after;
+    let count = 0;
@@
-    const params = {
-      sort: this.sort,
-    };
+    const params = {};
+    if (this.sort) params.sort = this.sort;
@@
-      const {
-        paging, results,
-      } = await this.hubspot.listCampaigns({
+      if (after) params.after = after;
+      else delete params.after;
+      const {
+        paging, results: pageResults,
+      } = await this.hubspot.listCampaigns({
         $,
         params,
       });
-      if (!results?.length) {
+      if (!pageResults?.length) {
         break;
       }
-      for (const item of results) {
-        results.push(item);
+      for (const item of pageResults) {
+        items.push(item);
         count++;
         if (count >= this.maxResults) {
           break;
         }
       }
-      hasMore = paging?.next.after;
-      params.after = paging?.next.after;
-    } while (hasMore && count < this.maxResults);
+      after = paging?.next?.after;
+    } while (after && count < this.maxResults);
@@
-    $.export("$summary", `Found ${results.length} campaign${results.length === 1
+    $.export("$summary", `Found ${items.length} campaign${items.length === 1
       ? ""
       : "s"}`);
-    return results;
+    return items;
🧹 Nitpick comments (1)
components/hubspot/actions/list-campaigns/list-campaigns.mjs (1)

45-47: Optional: request larger page sizes to reduce calls

If the HubSpot endpoint supports a limit/pageSize parameter, pass min(remaining, maxSupported) per request to cut round-trips under rate limits.

Can you confirm the correct page-size param name for marketing/v3/campaigns and the max allowed value? If supported, I can provide a follow-up patch.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c40e66a and 9b34721.

📒 Files selected for processing (63)
  • components/hubspot/actions/add-contact-to-list/add-contact-to-list.mjs (1 hunks)
  • components/hubspot/actions/batch-create-companies/batch-create-companies.mjs (1 hunks)
  • components/hubspot/actions/batch-create-or-update-contact/batch-create-or-update-contact.mjs (1 hunks)
  • components/hubspot/actions/batch-update-companies/batch-update-companies.mjs (1 hunks)
  • components/hubspot/actions/batch-upsert-companies/batch-upsert-companies.mjs (1 hunks)
  • components/hubspot/actions/create-associations/create-associations.mjs (1 hunks)
  • components/hubspot/actions/create-communication/create-communication.mjs (1 hunks)
  • components/hubspot/actions/create-company/create-company.mjs (1 hunks)
  • components/hubspot/actions/create-custom-object/create-custom-object.mjs (1 hunks)
  • components/hubspot/actions/create-deal/create-deal.mjs (1 hunks)
  • components/hubspot/actions/create-engagement/create-engagement.mjs (1 hunks)
  • components/hubspot/actions/create-lead/create-lead.mjs (1 hunks)
  • components/hubspot/actions/create-meeting/create-meeting.mjs (1 hunks)
  • components/hubspot/actions/create-note/create-note.mjs (1 hunks)
  • components/hubspot/actions/create-or-update-contact/create-or-update-contact.mjs (1 hunks)
  • components/hubspot/actions/create-task/create-task.mjs (1 hunks)
  • components/hubspot/actions/create-ticket/create-ticket.mjs (1 hunks)
  • components/hubspot/actions/enroll-contact-into-workflow/enroll-contact-into-workflow.mjs (1 hunks)
  • components/hubspot/actions/get-associated-emails/get-associated-emails.mjs (1 hunks)
  • components/hubspot/actions/get-associated-meetings/get-associated-meetings.mjs (1 hunks)
  • components/hubspot/actions/get-company/get-company.mjs (1 hunks)
  • components/hubspot/actions/get-contact/get-contact.mjs (1 hunks)
  • components/hubspot/actions/get-deal/get-deal.mjs (1 hunks)
  • components/hubspot/actions/get-file-public-url/get-file-public-url.mjs (1 hunks)
  • components/hubspot/actions/get-meeting/get-meeting.mjs (1 hunks)
  • components/hubspot/actions/get-subscription-preferences/get-subscription-preferences.mjs (1 hunks)
  • components/hubspot/actions/list-blog-posts/list-blog-posts.mjs (1 hunks)
  • components/hubspot/actions/list-campaigns/list-campaigns.mjs (1 hunks)
  • components/hubspot/actions/list-forms/list-forms.mjs (1 hunks)
  • components/hubspot/actions/list-marketing-emails/list-marketing-emails.mjs (1 hunks)
  • components/hubspot/actions/list-marketing-events/list-marketing-events.mjs (1 hunks)
  • components/hubspot/actions/list-pages/list-pages.mjs (1 hunks)
  • components/hubspot/actions/list-templates/list-templates.mjs (1 hunks)
  • components/hubspot/actions/search-crm/search-crm.mjs (1 hunks)
  • components/hubspot/actions/update-company/update-company.mjs (1 hunks)
  • components/hubspot/actions/update-contact/update-contact.mjs (1 hunks)
  • components/hubspot/actions/update-custom-object/update-custom-object.mjs (1 hunks)
  • components/hubspot/actions/update-deal/update-deal.mjs (1 hunks)
  • components/hubspot/actions/update-lead/update-lead.mjs (1 hunks)
  • components/hubspot/sources/delete-blog-article/delete-blog-article.mjs (1 hunks)
  • components/hubspot/sources/new-company-property-change/new-company-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (1 hunks)
  • components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-email-event/new-email-event.mjs (1 hunks)
  • components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs (1 hunks)
  • components/hubspot/sources/new-engagement/new-engagement.mjs (1 hunks)
  • components/hubspot/sources/new-event/new-event.mjs (1 hunks)
  • components/hubspot/sources/new-form-submission/new-form-submission.mjs (1 hunks)
  • components/hubspot/sources/new-note/new-note.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-blog-article/new-or-updated-blog-article.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-company/new-or-updated-company.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-contact/new-or-updated-contact.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-crm-object/new-or-updated-crm-object.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-custom-object/new-or-updated-custom-object.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-deal/new-or-updated-deal.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-line-item/new-or-updated-line-item.mjs (1 hunks)
  • components/hubspot/sources/new-or-updated-product/new-or-updated-product.mjs (1 hunks)
  • components/hubspot/sources/new-social-media-message/new-social-media-message.mjs (1 hunks)
  • components/hubspot/sources/new-task/new-task.mjs (1 hunks)
  • components/hubspot/sources/new-ticket-property-change/new-ticket-property-change.mjs (1 hunks)
  • components/hubspot/sources/new-ticket/new-ticket.mjs (1 hunks)
✅ Files skipped from review due to trivial changes (60)
  • components/hubspot/sources/new-or-updated-blog-article/new-or-updated-blog-article.mjs
  • components/hubspot/actions/create-note/create-note.mjs
  • components/hubspot/sources/new-company-property-change/new-company-property-change.mjs
  • components/hubspot/sources/new-or-updated-product/new-or-updated-product.mjs
  • components/hubspot/actions/create-deal/create-deal.mjs
  • components/hubspot/actions/batch-create-or-update-contact/batch-create-or-update-contact.mjs
  • components/hubspot/actions/get-file-public-url/get-file-public-url.mjs
  • components/hubspot/actions/search-crm/search-crm.mjs
  • components/hubspot/actions/list-marketing-events/list-marketing-events.mjs
  • components/hubspot/actions/create-engagement/create-engagement.mjs
  • components/hubspot/sources/new-event/new-event.mjs
  • components/hubspot/sources/new-note/new-note.mjs
  • components/hubspot/sources/new-or-updated-crm-object/new-or-updated-crm-object.mjs
  • components/hubspot/actions/update-contact/update-contact.mjs
  • components/hubspot/actions/get-associated-emails/get-associated-emails.mjs
  • components/hubspot/sources/new-social-media-message/new-social-media-message.mjs
  • components/hubspot/actions/create-company/create-company.mjs
  • components/hubspot/actions/batch-create-companies/batch-create-companies.mjs
  • components/hubspot/actions/create-lead/create-lead.mjs
  • components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs
  • components/hubspot/actions/batch-update-companies/batch-update-companies.mjs
  • components/hubspot/actions/list-pages/list-pages.mjs
  • components/hubspot/actions/get-contact/get-contact.mjs
  • components/hubspot/actions/list-blog-posts/list-blog-posts.mjs
  • components/hubspot/actions/get-company/get-company.mjs
  • components/hubspot/actions/add-contact-to-list/add-contact-to-list.mjs
  • components/hubspot/sources/delete-blog-article/delete-blog-article.mjs
  • components/hubspot/sources/new-or-updated-deal/new-or-updated-deal.mjs
  • components/hubspot/actions/enroll-contact-into-workflow/enroll-contact-into-workflow.mjs
  • components/hubspot/actions/get-associated-meetings/get-associated-meetings.mjs
  • components/hubspot/actions/update-custom-object/update-custom-object.mjs
  • components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs
  • components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs
  • components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs
  • components/hubspot/sources/new-or-updated-custom-object/new-or-updated-custom-object.mjs
  • components/hubspot/sources/new-email-event/new-email-event.mjs
  • components/hubspot/actions/list-templates/list-templates.mjs
  • components/hubspot/actions/list-forms/list-forms.mjs
  • components/hubspot/sources/new-or-updated-line-item/new-or-updated-line-item.mjs
  • components/hubspot/actions/update-lead/update-lead.mjs
  • components/hubspot/actions/get-subscription-preferences/get-subscription-preferences.mjs
  • components/hubspot/sources/new-engagement/new-engagement.mjs
  • components/hubspot/actions/create-task/create-task.mjs
  • components/hubspot/sources/new-or-updated-company/new-or-updated-company.mjs
  • components/hubspot/actions/update-deal/update-deal.mjs
  • components/hubspot/actions/batch-upsert-companies/batch-upsert-companies.mjs
  • components/hubspot/sources/new-or-updated-contact/new-or-updated-contact.mjs
  • components/hubspot/actions/create-ticket/create-ticket.mjs
  • components/hubspot/actions/create-or-update-contact/create-or-update-contact.mjs
  • components/hubspot/actions/get-meeting/get-meeting.mjs
  • components/hubspot/sources/new-task/new-task.mjs
  • components/hubspot/actions/create-associations/create-associations.mjs
  • components/hubspot/actions/create-communication/create-communication.mjs
  • components/hubspot/sources/new-form-submission/new-form-submission.mjs
  • components/hubspot/actions/get-deal/get-deal.mjs
  • components/hubspot/sources/new-ticket-property-change/new-ticket-property-change.mjs
  • components/hubspot/actions/update-company/update-company.mjs
  • components/hubspot/actions/list-marketing-emails/list-marketing-emails.mjs
  • components/hubspot/actions/create-meeting/create-meeting.mjs
  • components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
🔇 Additional comments (3)
components/hubspot/actions/list-campaigns/list-campaigns.mjs (1)

7-7: Version bump looks good

No functional change here; matches PR intent to rev the action version.

components/hubspot/sources/new-ticket/new-ticket.mjs (1)

12-12: LGTM: patch version bump only — new-ticket source invokes this.searchCRM, so it inherits the global 2 rps limiter (minTime: 500) and 429 retry/backoff logic.

components/hubspot/actions/create-custom-object/create-custom-object.mjs (1)

9-9: Patch version bump OK

Looks consistent with PR scope; no functional change here.

Copy link
Collaborator

@jcortes jcortes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @GTFalcao lgtm! Ready for QA!

@jcortes jcortes moved this from Ready for PR Review to Ready for QA in Component (Source and Action) Backlog Aug 28, 2025
@vunguyenhung vunguyenhung moved this from Ready for QA to In QA in Component (Source and Action) Backlog Aug 29, 2025
@vunguyenhung vunguyenhung moved this from In QA to Ready for Release in Component (Source and Action) Backlog Aug 29, 2025
@vunguyenhung
Copy link
Collaborator

Hi everyone, all test cases are passed! Ready for release!

Test report
https://vunguyenhung.notion.site/HubSpot-adding-retry-to-search-endpoint-25dbf548bb5e811d81c6e99fb1afee4f

@GTFalcao GTFalcao merged commit 36f3c89 into master Aug 29, 2025
10 checks passed
@GTFalcao GTFalcao deleted the hubspot-search-rate-limits branch August 29, 2025 02:26
@github-project-automation github-project-automation bot moved this from Ready for Release to Done in Component (Source and Action) Backlog Aug 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants