Skip to content

Conversation

GTFalcao
Copy link
Collaborator

@GTFalcao GTFalcao commented Sep 22, 2025

Some HubSpot sources currently ignore the "last timestamp" value if it is undefined (e.g. the first time the source runs).

This PR removes the ts check from the individual methods it was on, and instead expands that behavior to all sources, i.e. to all the instances of calling the isRelevant method. This ensures all sources properly emit the items fetched in their first run.

Summary by CodeRabbit

  • Bug Fixes

    • Emission logic updated: when no "after"/start time is provided, initial runs now emit results for event- and pagination-based HubSpot sources (including form submissions and general event pagination). Property-change triggers (company, contact, custom object, deal, ticket, etc.) now require a defined start time to be considered relevant.
  • Chores

    • Version bumps across many HubSpot sources and the HubSpot package.

Copy link

vercel bot commented Sep 22, 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 Sep 23, 2025 7:56pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Sep 23, 2025 7:56pm

Copy link
Contributor

coderabbitai bot commented Sep 22, 2025

Walkthrough

Shared HubSpot pagination now short-circuits relevance checks when no after/updatedAfter is provided; several property-change sources tighten isRelevant to require a timestamp comparison. Many individual HubSpot sources and the package version received version-only bumps.

Changes

Cohort / File(s) Summary of changes
Common pagination & initial-run emission
components/hubspot/sources/common/common.mjs
Add short-circuit checks so emission bypasses isRelevant when after is unset; updated null-ts handling and comments.
Sources that emit all when no after
components/hubspot/sources/new-form-submission/new-form-submission.mjs, components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs, components/hubspot/sources/...
Use `!after
Tightened property-change relevance
components/hubspot/sources/new-company-property-change/new-company-property-change.mjs, components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs, components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs, components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs, components/hubspot/sources/new-ticket-property-change/new-ticket-property-change.mjs
isRelevant(...) changed from `!updatedAfter
Version bumps (individual sources)
components/hubspot/package.json, components/hubspot/sources/delete-blog-article/delete-blog-article.mjs, components/hubspot/sources/new-contact-added-to-list/new-contact-added-to-list.mjs, components/hubspot/sources/new-email-event/new-email-event.mjs, components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs, components/hubspot/sources/new-engagement/new-engagement.mjs, components/hubspot/sources/new-event/new-event.mjs, components/hubspot/sources/new-note/new-note.mjs, components/hubspot/sources/new-or-updated-*/*, components/hubspot/sources/new-social-media-message/new-social-media-message.mjs, components/hubspot/sources/new-task/new-task.mjs, components/hubspot/sources/new-ticket/new-ticket.mjs
Version strings incremented in many source modules and package.json; no other logic changes.

Sequence Diagram(s)

sequenceDiagram
  participant Source as HubSpot Source
  participant Common as common.mjs
  participant API as HubSpot API
  participant Emitter as Event Emitter

  Source->>Common: paginate/processEvents(after)
  Common->>API: fetch page/items
  loop each item
    alt after is unset
      Note over Common,Emitter: Short-circuit emit (no isRelevant)
      Common->>Emitter: emit(item)
    else after is set
      Common->>Common: isRelevant(item, after)?
      alt relevant
        Common->>Emitter: emit(item)
      else not relevant
        Common-->>Source: skip
      end
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Suggested labels

User submitted

Suggested reviewers

  • michelle0927
  • jcortes

Poem

A rabbit tapped the timeline gate,
If "after" hides, we won't wait—emit!
When timestamps sleep we check the date,
Versions hop, the queues commit.
Tiny paws, a tidy commit. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR body describes the technical change and motivation but does not follow the repository's required description template because the explicit "## WHY" section header from the template is missing. The template expects a top-level "## WHY" section to outline the rationale and impact, and that structure is not present even though the content exists in freeform. Because the required section structure is absent, the description is non-compliant with the repository template. Please add the required "## WHY" heading and place or expand the current explanation under it, clearly stating the rationale, scope (which sources are affected), and expected behavioral changes; also include a brief test plan or link to the reviewer test report and CI results referenced in the PR objectives. After updating the description with that information, request reviewers re-check the PR for completeness and test coverage. This will bring the PR into compliance with the repository template and improve reviewer context.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely describes the primary change: fixing timestamp (ts) comparisons on initial runs for HubSpot sources. It matches the code changes that centralize ts logic and adjust isRelevant calls across multiple HubSpot source files. The phrasing is specific and easy for a teammate scanning history to understand.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hubspot-sources-fix

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

@GTFalcao GTFalcao changed the title Adjusting isRelevant calls to always emit if ts is falsy HubSpot sources - fixing ts comparison on initial run Sep 22, 2025
@GTFalcao GTFalcao moved this to Ready for PR Review in Component (Source and Action) Backlog Sep 22, 2025
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 (10)
components/hubspot/sources/new-or-updated-company/new-or-updated-company.mjs (1)

43-47: Bug: using this.isNew instead of this.newOnly breaks timestamp selection.

this.isNew is undefined; this will always pick updatedAt and can mis-emit. Use this.newOnly for consistency with other sources.

Apply:

 getTs(company) {
-  return this.isNew
+  return this.newOnly
     ? Date.parse(company.createdAt)
     : Date.parse(company.updatedAt);
 }
components/hubspot/sources/delete-blog-article/delete-blog-article.mjs (2)

21-26: Incorrect meta timestamp and id formatting for deletions.

ts should reflect deletion time, and id concatenation lacks a delimiter (risking collisions).

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

28-36: Do not send an undefined deletedAt filter; keep sort as "-updatedAt" (deletedAt is not a valid sort field).

Only add deletedAt__gte when after != null. HubSpot Blog Posts API does not support sorting by deletedAt — valid sort fields: name, createdAt, updatedAt, createdBy, updatedBy.

-    getParams(after) {
-      return {
-        params: {
-          limit: 100,
-          deletedAt__gte: after,
-          sort: "-updatedAt",
-        },
-      };
-    },
+    getParams(after) {
+      const params = { limit: 100, sort: "-updatedAt" };
+      if (after != null) params.deletedAt__gte = after;
+      return { params };
+    },
components/hubspot/sources/new-or-updated-crm-object/new-or-updated-crm-object.mjs (1)

27-35: Dedupe id risks collisions; add a separator.
id: \${id}${ts}`can collide (e.g.,1|234vs12|34). Other sources use id-ts`. Align for safety.

-        id: `${id}${ts}`,
+        id: `${id}-${ts}`,
components/hubspot/sources/new-or-updated-custom-object/new-or-updated-custom-object.mjs (1)

27-35: Dedupe id risks collisions; add a separator.
Align with other sources to prevent accidental id/ts concatenation collisions.

-        id: `${id}${ts}`,
+        id: `${id}-${ts}`,
components/hubspot/sources/common/common.mjs (3)

57-63: Bug: using !after treats 0 as “first run”, causing perpetual re‑emits when ts is missing. Use nullish checks.

If ts is undefined/NaN on first run, maxTs stays 0, _setAfter(0) persists a falsy value, and subsequent runs bypass relevance again. Switch to nullish semantics.

-        if (!after || await this.isRelevant(result, after)) {
+        if (after == null || await this.isRelevant(result, after)) {

Follow‑ups outside this hunk (recommended):

  • Initialize with nullish coalescing: let maxTs = after ?? 0;
  • First‑run guards: if (after == null) return;
  • In getPaginatedItems: while (params.after && after != null && page < maxPages)

83-91: Same nullish bug here; fix the gating condition.

-          if (!after || ts > after) {
-            if (!after || await this.isRelevant(result, after, ts)) {
+          if (after == null || ts > after) {
+            if (after == null || await this.isRelevant(result, after, ts)) {

127-134: Same nullish bug in hasMore pagination; fix gating.

-          if (!after || await this.isRelevant(item, after)) {
+          if (after == null || await this.isRelevant(item, after)) {

Optional hardening: if (after == null && (ts == null || Number.isNaN(ts))) set after to Date.now() once to prevent infinite “first run” behavior when upstream never supplies timestamps.

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

101-114: Use nullish check for after to avoid treating 0 as “first run”.

Align with fixes in common.

-          if (!after || this.isRelevant(ts, after)) {
+          if (after == null || this.isRelevant(ts, after)) {

Optional: ensure ts is numeric

const tsNum = typeof ts === "number" ? ts : Number(ts);
components/hubspot/sources/new-form-submission/new-form-submission.mjs (1)

38-55: Same nullish check for after.

Prevents epoch(0) from being misread as falsy and re-triggering first-run behavior.

-          if (!after || await this.isRelevant(result, after)) {
+          if (after == null || await this.isRelevant(result, after)) {
🧹 Nitpick comments (26)
components/hubspot/sources/new-engagement/new-engagement.mjs (1)

19-19: Typo in user-facing description.

"engagment" → "engagement".

-      description: "Filter results by the type of engagment",
+      description: "Filter results by the type of engagement",
components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs (3)

26-28: Make isRelevant resilient to nullish createdAfter.

Central logic should gate calls, but adding a local nullish guard prevents accidental future regressions.

-  isRelevant(timeline, createdAfter) {
-    return this.getTs(timeline) > createdAfter;
-  },
+  isRelevant(timeline, createdAfter) {
+    if (createdAfter == null) return true;
+    return this.getTs(timeline) > createdAfter;
+  },

29-35: Avoid sending undefined query params.

If after is undefined on first run, this will serialize startTimestamp: undefined. Prefer omitting the key.

-  getParams(after) {
-    return {
-      params: {
-        startTimestamp: after,
-      },
-    };
-  },
+  getParams(after) {
+    const params = {};
+    if (after != null) params.startTimestamp = after;
+    return { params };
+  },

17-24: Dedupe id: add a delimiter to reduce collision risk.

"${id}${ts}" can collide for some concatenations. Add a delimiter.

-      return {
-        id: `${id}${ts}`,
+      return {
+        id: `${id}:${ts}`,
         summary: `New subscription event for recipient ${id}`,
         ts,
       };
components/hubspot/sources/new-or-updated-line-item/new-or-updated-line-item.mjs (1)

43-47: Guard against invalid timestamps.

If createdAt/updatedAt is missing or unparsable, Date.parse returns NaN and relevance comparisons fail. Consider a safe fallback.

Suggested change:

 getTs(lineItem) {
-  return this.newOnly
-    ? Date.parse(lineItem.createdAt)
-    : Date.parse(lineItem.updatedAt);
+  const raw = this.newOnly ? lineItem.createdAt : lineItem.updatedAt;
+  const ts = Date.parse(raw);
+  return Number.isFinite(ts) ? ts : 0;
 }
components/hubspot/sources/new-task/new-task.mjs (1)

17-19: Minor: handle NaN from Date.parse.

Add a fallback to avoid silent drops when createdAt is missing.

 getTs(task) {
-  return Date.parse(task.createdAt);
+  const ts = Date.parse(task.createdAt);
+  return Number.isFinite(ts) ? ts : 0;
 }
components/hubspot/sources/new-ticket/new-ticket.mjs (1)

36-38: Minor: protect against invalid dates.

Guard Date.parse(ticket.createdAt) returning NaN.

 getTs(ticket) {
-  return Date.parse(ticket.createdAt);
+  const ts = Date.parse(ticket.createdAt);
+  return Number.isFinite(ts) ? ts : 0;
 }
components/hubspot/sources/new-or-updated-deal/new-or-updated-deal.mjs (1)

64-68: Minor: invalid timestamp fallback.

Add a fallback when createdAt/updatedAt is missing to avoid NaN comparisons.

 getTs(deal) {
-  return this.newOnly
-    ? Date.parse(deal.createdAt)
-    : Date.parse(deal.updatedAt);
+  const raw = this.newOnly ? deal.createdAt : deal.updatedAt;
+  const ts = Date.parse(raw);
+  return Number.isFinite(ts) ? ts : 0;
 }
components/hubspot/sources/new-note/new-note.mjs (1)

16-18: Minor: handle NaN timestamps.

Guard Date.parse(note.createdAt) result.

 getTs(note) {
-  return Date.parse(note.createdAt);
+  const ts = Date.parse(note.createdAt);
+  return Number.isFinite(ts) ? ts : 0;
 }
components/hubspot/sources/new-or-updated-contact/new-or-updated-contact.mjs (1)

79-98: Boundary check may re‑emit objects sitting exactly at the cursor.
if (this.getTs(contact) < updatedAfter) return false; treats ts === updatedAfter as relevant, unlike other sources that use strict > checks. Recommend consistent, cursor‑safe comparison.

-    async isRelevant(contact, updatedAfter) {
-      if (this.getTs(contact) < updatedAfter) {
+    async isRelevant(contact, updatedAfter) {
+      if (updatedAfter && this.getTs(contact) <= updatedAfter) {
         return false;
       }
components/hubspot/sources/new-social-media-message/new-social-media-message.mjs (2)

24-26: Normalize timestamp to a number to avoid NaN comparisons.
getTs returns message.createdAt directly; if it’s an ISO string, > with createdAfter will fail. Parse or coerce to number.

-    getTs(message) {
-      return message.createdAt;
-    },
+    getTs(message) {
+      return typeof message.createdAt === "number"
+        ? message.createdAt
+        : Date.parse(message.createdAt);
+    },

Also applies to: 38-40


41-47: Avoid sending undefined query params.
since: after when after is falsy may leak undefined into the request depending on the HTTP client. Guard it.

-    getParams(after) {
-      return {
-        params: {
-          withChannelKeys: this.channel,
-          since: after,
-        },
-      };
-    },
+    getParams(after) {
+      const params = { withChannelKeys: this.channel };
+      if (after) params.since = after;
+      return { params };
+    },
components/hubspot/sources/new-or-updated-crm-object/new-or-updated-crm-object.mjs (2)

43-47: Strict equality and naming nit.
Use === and avoid shadowing generic names; minor readability win.

-      const propertyName =
-        object == "contacts"
+      const propertyName =
+        object === "contacts"
           ? "lastmodifieddate"
           : "hs_lastmodifieddate";

60-66: Minor: avoid generic name “object”.
Consider objectName to improve readability.

-    async processResults(after) {
-      const object =
+    async processResults(after) {
+      const objectName =
         this.objectType == "company"
           ? "companies"
           : `${this.objectType}s`;
-      const params = this.getObjectParams(object);
+      const params = this.getObjectParams(objectName);
       await this.searchCRM(params, after);
     },
components/hubspot/sources/new-event/new-event.mjs (2)

49-52: Prefer event occurrence time over “now” for ts.
Using Date.now() skews ordering/analytics. If available, use the API’s occurrence timestamp.

-    getTs() {
-      return Date.now();
-    },
+    getTs(result) {
+      // Prefer explicit occurrence field if present; fall back to now.
+      return result?.occurredAt
+        ? (typeof result.occurredAt === "number" ? result.occurredAt : Date.parse(result.occurredAt))
+        : Date.now();
+    },
@@
-    generateMeta(result) {
+    generateMeta(result) {
@@
-        ts: this.getTs(),
+        ts: this.getTs(result),

Also applies to: 53-61


76-85: Guard occurredAfter param to avoid undefined in requests.
Safer to include only when present.

-    getEventParams(objectId, occurredAfter) {
-      return {
-        params: {
-          limit: DEFAULT_LIMIT,
-          objectType: this.objectType,
-          objectId,
-          occurredAfter,
-        },
-      };
-    },
+    getEventParams(objectId, occurredAfter) {
+      const params = {
+        limit: DEFAULT_LIMIT,
+        objectType: this.objectType,
+        objectId,
+      };
+      if (occurredAfter) params.occurredAfter = occurredAfter;
+      return { params };
+    },
components/hubspot/sources/new-or-updated-blog-article/new-or-updated-blog-article.mjs (1)

46-53: Avoid passing updated__gte: undefined.
Guard the filter param to prevent accidental API behavior.

-    getParams(after) {
-      return {
-        params: {
-          limit: DEFAULT_LIMIT,
-          updated__gte: after,
-          sort: "-updatedAt",
-        },
-      };
-    },
+    getParams(after) {
+      const params = { limit: DEFAULT_LIMIT, sort: "-updatedAt" };
+      if (after) params.updated__gte = after;
+      return { params };
+    },
components/hubspot/sources/new-email-event/new-email-event.mjs (1)

40-51: Guard startTimestamp to avoid undefined in requests.
Match the centralized “emit when no ts” behavior by omitting the filter entirely when after is falsy.

-    getParams(after) {
-      const params = {
-        limit: DEFAULT_LIMIT,
-        startTimestamp: after,
-      };
+    getParams(after) {
+      const params = { limit: DEFAULT_LIMIT };
+      if (after) params.startTimestamp = after;
       if (this.type) {
         params.eventType = this.type;
       }
       return {
         params,
       };
     },
components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs (1)

51-53: LGTM on tightened isRelevant; consider optional ts passthrough to avoid recomputing.

Matches the centralized gating in common. To skip re-reading history when a precomputed ts is available, accept an optional third param:

-    isRelevant(object, updatedAfter) {
-      return this.getTs(object) > updatedAfter;
-    },
+    isRelevant(object, updatedAfter, ts) {
+      const t = ts ?? this.getTs(object);
+      return t > updatedAfter;
+    },
components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs (1)

45-47: LGTM; same optional ts passthrough applies.

Consistent with common’s first-run behavior. Optional micro-optimization below.

-    isRelevant(contact, updatedAfter) {
-      return this.getTs(contact) > updatedAfter;
-    },
+    isRelevant(contact, updatedAfter, ts) {
+      const t = ts ?? this.getTs(contact);
+      return t > updatedAfter;
+    },
components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs (1)

43-45: LGTM; consider optional ts passthrough for parity/perf.

-    isRelevant(deal, updatedAfter) {
-      return this.getTs(deal) > updatedAfter;
-    },
+    isRelevant(deal, updatedAfter, ts) {
+      const t = ts ?? this.getTs(deal);
+      return t > updatedAfter;
+    },
components/hubspot/sources/new-company-property-change/new-company-property-change.mjs (1)

45-47: LGTM; mirrors other sources. Optional: accept precomputed ts.

-    isRelevant(company, updatedAfter) {
-      return this.getTs(company) > updatedAfter;
-    },
+    isRelevant(company, updatedAfter, ts) {
+      const t = ts ?? this.getTs(company);
+      return t > updatedAfter;
+    },
components/hubspot/sources/new-ticket-property-change/new-ticket-property-change.mjs (1)

46-48: LGTM; consistent with centralized gating. Optional ts passthrough for parity.

-    isRelevant(ticket, updatedAfter) {
-      return this.getTs(ticket) > updatedAfter;
-    },
+    isRelevant(ticket, updatedAfter, ts) {
+      const t = ts ?? this.getTs(ticket);
+      return t > updatedAfter;
+    },
components/hubspot/sources/common/common.mjs (1)

55-66: Use nullish semantics for after and add a first‑run fallback

Replace after || 0 with after ?? 0 and change !after checks to explicit nullish checks (e.g. after == null). If after == null and no emitted item yields a finite timestamp, set after to Date.now() at the end of the run to avoid perpetual first‑run behavior. Locations to update:

  • components/hubspot/sources/common/common.mjs — lines 55 (let maxTs), 57 (if !after ...), 69 (let maxTs), 113 (let maxTs)
  • components/hubspot/sources/new-form-submission/new-form-submission.mjs — line 26 (let maxTs)
  • components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs — line 91 (let maxTs)
components/hubspot/sources/new-contact-added-to-list/new-contact-added-to-list.mjs (2)

98-103: Avoid duplicate properties in batched contact fetch

Merging DEFAULT_CONTACT_PROPERTIES with user-specified properties can introduce duplicates. Deduplicate to prevent larger payloads and potential API errors.

-      const allProperties = [
-        ...DEFAULT_CONTACT_PROPERTIES,
-        ...properties,
-      ];
+      const allProperties = Array.from(new Set([
+        ...DEFAULT_CONTACT_PROPERTIES,
+        ...properties,
+      ]));

183-187: Safer timestamp comparison (optional)

Comparing ISO strings works if all values are normalized, but numeric comparison is more defensive.

-            const { membershipTimestamp } = membership;
+            const { membershipTimestamp } = membership;
+            const lastTS = lastMembershipTimestamp ? Date.parse(lastMembershipTimestamp) : null;
+            const mTS = Date.parse(membershipTimestamp);
@@
-            if (!lastMembershipTimestamp || membershipTimestamp > lastMembershipTimestamp) {
+            if (lastTS == null || mTS > lastTS) {
@@
-              if (
-                !latestMembershipTimestamp ||
-                membershipTimestamp > latestMembershipTimestamp
-              ) {
-                latestMembershipTimestamp = membershipTimestamp;
-              }
+              if (!latestMembershipTimestamp || mTS > Date.parse(latestMembershipTimestamp)) {
+                latestMembershipTimestamp = membershipTimestamp;
+              }

Run-time behavior should be identical with normalized inputs; this change guards against any format variance.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78ebe2f and 619582d.

📒 Files selected for processing (27)
  • components/hubspot/package.json (1 hunks)
  • components/hubspot/sources/common/common.mjs (3 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 (2 hunks)
  • components/hubspot/sources/new-contact-added-to-list/new-contact-added-to-list.mjs (1 hunks)
  • components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs (2 hunks)
  • components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs (2 hunks)
  • components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (2 hunks)
  • components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs (2 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 (2 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 (2 hunks)
  • components/hubspot/sources/new-ticket/new-ticket.mjs (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-10T19:18:27.998Z
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.

Applied to files:

  • components/hubspot/sources/common/common.mjs
🧬 Code graph analysis (4)
components/hubspot/sources/new-form-submission/new-form-submission.mjs (1)
components/hubspot/sources/common/common.mjs (1)
  • after (187-187)
components/hubspot/sources/common/common.mjs (15)
components/hubspot/sources/new-form-submission/new-form-submission.mjs (3)
  • result (62-62)
  • ts (47-47)
  • ts (63-63)
components/hubspot/sources/new-company-property-change/new-company-property-change.mjs (1)
  • ts (38-38)
components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs (1)
  • ts (38-38)
components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs (1)
  • ts (44-44)
components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs (1)
  • ts (36-36)
components/hubspot/sources/new-ticket-property-change/new-ticket-property-change.mjs (1)
  • ts (39-39)
components/hubspot/sources/delete-blog-article/delete-blog-article.mjs (1)
  • ts (21-21)
components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (1)
  • ts (102-102)
components/hubspot/sources/new-email-event/new-email-event.mjs (1)
  • ts (33-33)
components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs (1)
  • ts (19-19)
components/hubspot/sources/new-engagement/new-engagement.mjs (1)
  • ts (33-33)
components/hubspot/sources/new-or-updated-blog-article/new-or-updated-blog-article.mjs (1)
  • ts (34-34)
components/hubspot/sources/new-or-updated-company/new-or-updated-company.mjs (1)
  • ts (52-52)
components/hubspot/sources/new-or-updated-contact/new-or-updated-contact.mjs (1)
  • ts (58-58)
components/hubspot/sources/new-or-updated-crm-object/new-or-updated-crm-object.mjs (1)
  • ts (29-29)
components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (1)
components/hubspot/sources/common/common.mjs (4)
  • after (187-187)
  • ts (59-59)
  • ts (82-82)
  • ts (129-129)
components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs (1)
components/hubspot/hubspot.app.mjs (1)
  • object (768-771)
⏰ 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: Lint Code Base
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (18)
components/hubspot/sources/new-engagement/new-engagement.mjs (1)

11-11: Version bump to 0.0.39 — LGTM; verify publish flow.

No functional changes here. Please confirm this version bump will be included in the release/publish for the HubSpot app and aligns with the centralized isRelevant behavior change elsewhere.

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

9-9: Approve version bump — confirm HubSpot dependency bump

components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs shows version "0.0.34" (line 9); grep returned no occurrences of "@pipedream/hubspot" or "hubspot" in package.json, pnpm-lock.yaml, or package-lock.json. If a dependency bump was intended, update the dependency and lockfile(s); otherwise confirm no change needed.


36-43: Pagination gating verified — no action required

paginateUsingHasMore (components/hubspot/sources/common/common.mjs) short‑circuits isRelevant when after is falsy and only calls isRelevant when after is truthy; new-email-subscriptions-timeline passes the stored after into paginateUsingHasMore, so central gating covers this source.

components/hubspot/package.json (1)

3-3: Version bump looks good; confirm publish artifacts.

Please ensure the 1.7.7 tag/changelog and any release notes reflect the centralized isRelevant behavior so downstream users understand the behavior change on first runs.

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

50-52: No action required — first-run emits are already handled.
processEvents in components/hubspot/sources/common/common.mjs uses if (!after || await this.isRelevant(...)) before emitting (see ~lines 54–60, 81–86, 124–130) and the default isRelevant() returns true (lines 165–167).

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

59-61: No change required — common short-circuits isRelevant when after is falsy.

processEvents/searchCRM guard with if (!after || ...), so the override in components/hubspot/sources/new-or-updated-line-item/new-or-updated-line-item.mjs (isRelevant => this.getTs(lineItem) > updatedAfter) is not called when after is falsy. See components/hubspot/sources/common/common.mjs (~lines 54–58, 82–85, 165–167).

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

82-84: No action required — common emits on first run when after is falsy.

common.processEvents and common.paginate short‑circuit on !after (they use if (!after || ...)), so isRelevant is bypassed and items are emitted on first run (components/hubspot/sources/common/common.mjs — processEvents / paginate).

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

26-28: Common first-run emit logic is correct
When after is falsy, processEvents short-circuits the isRelevant check and emits all items on initial run.

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

27-29: First-run bypass confirmed — do not change isRelevant.
common.processEvents uses if (!after || await this.isRelevant(result, after)) (components/hubspot/sources/common/common.mjs:54-63), so when after is falsy the relevance check is bypassed and items are emitted.

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

61-63: Confirmed — common bypasses isRelevant when after is falsy. common.mjs short‑circuits in processEvents and paginate with if (!after || await this.isRelevant(...)), so isRelevant is not invoked on the first run.

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

13-13: Version bump only — OK.
Metadata bump to 0.0.22 looks fine.

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

13-13: Version bump only — OK.
No behavior change here.

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

10-10: Version bump only — OK.
No logic changes in this file.

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

10-10: Version bump only — OK.
No behavioral diffs here.

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

10-10: Version bump only — OK.
Looks good.

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

11-11: Version bump only — OK.
No logic changes here.

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

10-10: Version bump only — OK.
No functional changes.

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

11-11: Version bump only — OK.
All good.

description:
"Emit new event when a contact is added to a HubSpot list. [See the documentation](https://developers.hubspot.com/docs/reference/api/crm/lists#get-%2Fcrm%2Fv3%2Flists%2F%7Blistid%7D%2Fmemberships%2Fjoin-order)",
version: "0.0.5",
version: "0.0.6",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Version bump only, but source still skips first run; align with PR goal to emit when ts is falsy

This source seeds and returns early when no last timestamp (Lines 154–158), which prevents emitting on first run. The PR objective is to always emit when ts is falsy. Recommend removing the early return and making the comparison include the falsy-ts case.

Apply:

@@
-        if (!lastMembershipTimestamp) {
-          const baselineTimestamp = new Date().toISOString();
-          this._setLastMembershipTimestamp(listId, baselineTimestamp);
-          return newMemberships;
-        }
+        // Emit on first run when no prior timestamp exists
+        // (do not seed-and-skip; we'll set the latest seen at the end)
@@
-            if (membershipTimestamp > lastMembershipTimestamp) {
+            if (!lastMembershipTimestamp || membershipTimestamp > lastMembershipTimestamp) {
               newMemberships.push({
                 membership,
                 listInfo,
               });
 
               if (
-                !latestMembershipTimestamp ||
-                membershipTimestamp > latestMembershipTimestamp
+                !latestMembershipTimestamp ||
+                membershipTimestamp > latestMembershipTimestamp
               ) {
                 latestMembershipTimestamp = membershipTimestamp;
               }
             }

Optionally, for robustness, compare numeric timestamps instead of ISO strings to avoid format pitfalls; I can provide that diff if desired. Please confirm this behavior matches your centralized isRelevant semantics.

Also applies to: 154-158, 183-195


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file=$(rg --hidden --files | rg "components/hubspot/sources/new-contact-added-to-list/new-contact-added-to-list.mjs" || true)
if [[ -z "$file" ]]; then
  file=$(rg --hidden --files | rg "new-contact-added-to-list.mjs" || true)
fi
if [[ -z "$file" ]]; then
  echo "FILE_NOT_FOUND"
  exit 0
fi
echo "FILE: $file"
nl -ba "$file"
echo
echo "--- relevant identifier matches ---"
rg -n --hidden "lastMembershipTimestamp|_setLastMembershipTimestamp|membershipTimestamp|baselineTimestamp|latestMembershipTimestamp" "$file" -n -C3 || true

Length of output: 130


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=$(rg --hidden --files | rg "components/hubspot/sources/new-contact-added-to-list/new-contact-added-to-list.mjs" || true)
if [[ -z "$file" ]]; then
  file=$(rg --hidden --files | rg "new-contact-added-to-list.mjs" || true)
fi
if [[ -z "$file" ]]; then
  echo "FILE_NOT_FOUND"
  exit 0
fi

echo "FILE: $file"
echo
echo "----- lines 1-30 -----"
awk 'NR>=1 && NR<=30 {printf "%4d: %s\n", NR, $0}' "$file" || true
echo
echo "----- lines 140-210 -----"
awk 'NR>=140 && NR<=210 {printf "%4d: %s\n", NR, $0}' "$file" || true
echo
echo "----- identifier matches (with context) -----"
rg -n --hidden "lastMembershipTimestamp|_setLastMembershipTimestamp|membershipTimestamp|baselineTimestamp|latestMembershipTimestamp" "$file" -n -C3 || true

Length of output: 6064


Emit on first run: remove seed-and-skip and include falsy ts in comparison

The code currently seeds-and-returns when lastMembershipTimestamp is falsy (lines 154–158), preventing any emits on first run — remove the early return and treat a falsy lastMembershipTimestamp as "emit everything seen" by changing the comparison. Applies to lines 154–158 and 183–195.

Apply:

@@
-        if (!lastMembershipTimestamp) {
-          const baselineTimestamp = new Date().toISOString();
-          this._setLastMembershipTimestamp(listId, baselineTimestamp);
-          return newMemberships;
-        }
+        // Emit on first run when no prior timestamp exists
+        // (do not seed-and-skip; we'll set the latest seen at the end)
@@
-            if (membershipTimestamp > lastMembershipTimestamp) {
+            if (!lastMembershipTimestamp || membershipTimestamp > lastMembershipTimestamp) {
               newMemberships.push({
                 membership,
                 listInfo,
               });
 
               if (
-                !latestMembershipTimestamp ||
-                membershipTimestamp > latestMembershipTimestamp
+                !latestMembershipTimestamp ||
+                membershipTimestamp > latestMembershipTimestamp
               ) {
                 latestMembershipTimestamp = membershipTimestamp;
               }
             }

Optional: compare numeric timestamps (Date.getTime()) instead of ISO strings for robustness. Confirm this matches your centralized isRelevant semantics.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
components/hubspot/sources/new-contact-added-to-list/new-contact-added-to-list.mjs
around lines 154–158 and 183–195, the code currently seeds-and-skips when
lastMembershipTimestamp is falsy which prevents any emits on first run; remove
the early return and change the membership timestamp comparison so that a falsy
lastMembershipTimestamp is treated as "emit everything seen" (i.e., when
lastMembershipTimestamp is falsy, consider all events as newer), and use numeric
timestamps (Date.getTime() or Number(new Date(...))) for comparisons instead of
ISO string comparison to match centralized isRelevant semantics.

lcaresia
lcaresia previously approved these changes Sep 22, 2025
@lcaresia lcaresia moved this from Ready for PR Review to Ready for QA in Component (Source and Action) Backlog Sep 22, 2025
@vunguyenhung vunguyenhung moved this from Ready for QA to In QA in Component (Source and Action) Backlog Sep 23, 2025
@vunguyenhung vunguyenhung moved this from In QA to Changes Required in Component (Source and Action) Backlog Sep 23, 2025
@vunguyenhung
Copy link
Collaborator

Hello everyone, I have tested this PR and there're some test cases failed or needed improvement.

Please check the test report below for more information
https://vunguyenhung.notion.site/HubSpot-sources-fixing-ts-comparison-on-initial-run-276bf548bb5e8114b3b2e0f3a67b66de

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: 0

Caution

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

⚠️ Outside diff range comments (3)
components/hubspot/sources/common/common.mjs (3)

55-66: Avoid persisting after=0 when no valid ts; also await getTs

On first run, if all items lack a valid ts, this writes after as 0, causing repeated “first run” behavior. Also, getTs may be async in some sources; not awaiting it risks comparing a Promise to a number.

Apply:

-    async processEvents(resources, after) {
-      let maxTs = after || 0;
+    async processEvents(resources, after) {
+      let maxTs = Number.isFinite(after) ? after : -Infinity;
       for (const result of resources) {
-        if (!after || await this.isRelevant(result, after)) {
-          this.emitEvent(result);
-          const ts = this.getTs(result);
-          if (ts > maxTs) {
-            maxTs = ts;
-          }
-        }
+        if (!after || await this.isRelevant(result, after)) {
+          this.emitEvent(result);
+          const ts = await this.getTs(result);
+          if (Number.isFinite(ts) && ts > maxTs) {
+            maxTs = ts;
+          }
+        }
       }
-      this._setAfter(maxTs);
+      if (Number.isFinite(maxTs) && (after == null || maxTs > after)) {
+        this._setAfter(maxTs);
+      }
     },

128-136: Align hasMore pagination with async getTs + safe ts checks

Await getTs and guard with Number.isFinite to avoid persisting invalid after.

-          if (!after || await this.isRelevant(item, after)) {
-            this.emitEvent(item);
-            const ts = this.getTs(item);
-            if (ts > maxTs) {
-              maxTs = ts;
-              this._setAfter(ts);
-            }
-          }
+          if (!after || await this.isRelevant(item, after)) {
+            this.emitEvent(item);
+            const ts = await this.getTs(item);
+            if (Number.isFinite(ts) && ts > maxTs) {
+              maxTs = ts;
+              this._setAfter(ts);
+            }
+          }

94-95: Don't early-return on ts check — HubSpot paging isn't guaranteed ordered by timestamp

HubSpot Search/List do not guarantee a global, monotonic sort by created/updated timestamp across pages; the opaque "after" cursor and "sorts" are not reliable for full incremental syncs. The early return at components/hubspot/sources/common/common.mjs:94-95 (return;) can drop newer items on later pages. Remove the early return and instead continue paging + filter/dedupe (use an overlap window) or switch to Lists/changes/webhooks for reliable incremental syncs.

🧹 Nitpick comments (2)
components/hubspot/sources/common/common.mjs (2)

83-87: Comment doesn’t match behavior; proposed logic handles null ts explicitly

The comment claims this handles null ts, but current condition forces a function‑level return when ts is null on first run. The patch above makes null ts a non‑blocking case on first run and a skip on subsequent runs.


57-57: Optional: pass ts to isRelevant in processEvents for parity

For parity and to avoid recomputing in overrides, consider computing ts before isRelevant here and passing it (like in paginate).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 619582d and 636fed9.

📒 Files selected for processing (2)
  • components/hubspot/sources/common/common.mjs (3 hunks)
  • components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-10T19:18:27.998Z
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.

Applied to files:

  • components/hubspot/sources/common/common.mjs
🧬 Code graph analysis (1)
components/hubspot/sources/common/common.mjs (16)
components/hubspot/sources/new-event/new-event.mjs (1)
  • result (54-56)
components/hubspot/sources/new-form-submission/new-form-submission.mjs (3)
  • result (62-62)
  • ts (47-47)
  • ts (63-63)
components/hubspot/sources/new-deal-in-stage/new-deal-in-stage.mjs (1)
  • ts (102-102)
components/hubspot/sources/new-contact-property-change/new-contact-property-change.mjs (1)
  • ts (38-38)
components/hubspot/sources/new-custom-object-property-change/new-custom-object-property-change.mjs (1)
  • ts (44-44)
components/hubspot/sources/new-ticket-property-change/new-ticket-property-change.mjs (1)
  • ts (39-39)
components/hubspot/sources/new-company-property-change/new-company-property-change.mjs (1)
  • ts (38-38)
components/hubspot/sources/delete-blog-article/delete-blog-article.mjs (1)
  • ts (21-21)
components/hubspot/sources/new-deal-property-change/new-deal-property-change.mjs (1)
  • ts (36-36)
components/hubspot/sources/new-email-subscriptions-timeline/new-email-subscriptions-timeline.mjs (1)
  • ts (19-19)
components/hubspot/sources/new-email-event/new-email-event.mjs (1)
  • ts (33-33)
components/hubspot/sources/new-engagement/new-engagement.mjs (1)
  • ts (33-33)
components/hubspot/sources/new-or-updated-blog-article/new-or-updated-blog-article.mjs (1)
  • ts (34-34)
components/hubspot/sources/new-or-updated-company/new-or-updated-company.mjs (1)
  • ts (52-52)
components/hubspot/sources/new-or-updated-crm-object/new-or-updated-crm-object.mjs (1)
  • ts (29-29)
components/hubspot/sources/new-or-updated-contact/new-or-updated-contact.mjs (1)
  • ts (58-58)
⏰ 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: Lint Code Base
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (1)
components/hubspot/sources/common/common.mjs (1)

81-96: Don't return early when ts is null — await getTs and guard comparisons

The current if ((ts && !after) || ts > after) { … } else { return; } short‑circuits when ts is null and blocks emission on first run. Await getTs, emit all items when !after, update after only for finite numeric ts, use Number.isFinite(ts), and prefer continue for invalid/null ts. Keep return only if the API guarantees results are sorted descending by timestamp.

File: components/hubspot/sources/common/common.mjs (≈lines 81–96)

Apply:

         for (const result of results) {
-          const ts = this.getTs(result);
-          // Adding ts && !after to handle the case where ts is null
-          // (e.g. when using deletedAt as the ts field for deleted items)
-          if ((ts && !after) || ts > after) {
-            if (!after || await this.isRelevant(result, after, ts)) {
-              this.emitEvent(result);
-            }
-            if (ts > maxTs) {
-              maxTs = ts;
-              this._setAfter(ts);
-            }
-          } else {
-            return;
-          }
+          const ts = await this.getTs(result);
+          // When no 'after', emit everything; update 'after' only with valid timestamps
+          if (!after) {
+            this.emitEvent(result);
+            if (Number.isFinite(ts) && ts > maxTs) {
+              maxTs = ts;
+              this._setAfter(ts);
+            }
+            continue;
+          }
+
+          // With 'after', emit only newer items. If list is sorted by ts desc,
+          // we can return when we reach <= after; otherwise just continue.
+          if (Number.isFinite(ts) && ts > after) {
+            if (await this.isRelevant(result, after, ts)) {
+              this.emitEvent(result);
+            }
+            if (ts > maxTs) {
+              maxTs = ts;
+              this._setAfter(ts);
+            }
+          } else if (Number.isFinite(ts)) {
+            return; // safe only if API sorts by descending timestamp
+          } else {
+            continue; // skip items without a valid ts on subsequent runs
+          }
         }

My automated search found no matches for async getTs or usages of this.paginate(UsingHasMore) in the repo — manual verification required: confirm getTs implementations are async/await-safe and whether the HubSpot endpoints guarantee descending timestamps; if not, replace the return with continue.

@GTFalcao
Copy link
Collaborator Author

/approve

@GTFalcao GTFalcao merged commit 8855cf9 into master Sep 23, 2025
10 checks passed
@GTFalcao GTFalcao deleted the hubspot-sources-fix branch September 23, 2025 20:19
@github-project-automation github-project-automation bot moved this from Changes Required to Done in Component (Source and Action) Backlog Sep 23, 2025
@GTFalcao GTFalcao moved this from Done to Ready for QA in Component (Source and Action) Backlog Sep 23, 2025
@GTFalcao
Copy link
Collaborator Author

I'm merging this earlier due to the majority of the sources having issues with event emissions currently. The 2 remaining issues reported should've been fixed also, but we can work it out on a subsequent PR if needed.

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.

4 participants