Skip to content

feat: enhance appendEventToGoogleSheets to prevent value duplication …#8584

Merged
mikeallisonJS merged 15 commits intomainfrom
mikeallison/nes-1142-inconsistent-data-between-google-sync-and-standard-data-2
Jan 22, 2026
Merged

feat: enhance appendEventToGoogleSheets to prevent value duplication …#8584
mikeallisonJS merged 15 commits intomainfrom
mikeallison/nes-1142-inconsistent-data-between-google-sync-and-standard-data-2

Conversation

@mikeallisonJS
Copy link
Copy Markdown
Collaborator

@mikeallisonJS mikeallisonJS commented Jan 13, 2026

…and support semicolon-separated appending

Summary by CodeRabbit

  • Bug Fixes

    • Export now uses a single column per block (keeps the first label deterministically), avoids creating duplicate columns when labels differ, routes updates to existing columns, and merges values cleanly (semicolon-separated) without duplication.
  • Tests

    • Added tests for append/update merge behavior, deduplication by block, deterministic header ordering, label-to-column routing, header handling, and avoiding duplicated values.

✏️ Tip: You can customize this high-level summary in your review settings.

@mikeallisonJS mikeallisonJS requested a review from tanflem January 13, 2026 01:28
@mikeallisonJS mikeallisonJS self-assigned this Jan 13, 2026
@linear
Copy link
Copy Markdown

linear Bot commented Jan 13, 2026

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 13, 2026

Walkthrough

appendEventToGoogleSheets and journey export header logic now deduplicate headers by blockId, choose deterministic first labels (ordered by createdAt), resolve target columns per row dynamically, and merge existing/new cell values with semicolon-separated deduplication. Three unit tests were added for merge/dedupe/column-resolution behaviors.

Changes

Cohort / File(s) Change Summary
Tests (event utils)
apis/api-journeys-modern/src/schema/event/utils.spec.ts
Added three unit tests validating append/update semantics: semicolon-merged updates via updateRangeValues, avoiding duplicate appends when value already exists, and reusing an existing column when label differs but blockId matches. Includes Google Sheets read/write and token mocks.
Core logic (events)
apis/api-journeys-modern/src/schema/event/utils.ts
Reworked header construction to key by blockId (one column per block) and order headers by createdAt. Added per-row keyForRow resolution that reuses existing header keys or derives/augments headers for connected blocks. Rebuilds columns after header updates, aligns rows to updatedFinalHeader, recalculates ranges, and merges existing/new cell values with semicolon-separated deduplication and no duplicates.
Journey export / headings
apis/api-journeys-modern/src/schema/journeyVisitor/export/headings.ts, .../journeyVisitor.ts, .../journeyVisitorExportToGoogleSheet.mutation.ts, .../export/headings.spec.ts, .../journeyVisitor.spec.ts
Added deduplicateBlockHeadersByBlockId and changed export header deduplication to keep only the first label per blockId (deterministic via orderBy: { createdAt: 'asc' }). Tests updated/added to assert single-column behavior when multiple labels exist for the same blockId and to verify order preservation.

Sequence Diagram(s)

(Skipped — changes are internal header/merge logic and do not introduce a new multi-component sequential flow.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • JesusFilm/core 8570: Modifies appendEventToGoogleSheets and header/column reconciliation logic — strong overlap with these event util changes.
  • JesusFilm/core 8568: Alters appendEventToGoogleSheets and Google Sheets export flow header/row construction — touches same code paths.
  • JesusFilm/core 8394: Adjusts journey visitor export and header construction, including deduplication-by-blockId and ordering — related to headings changes.

Suggested reviewers

  • tanflem
  • tataihono
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the pull request—enhancing appendEventToGoogleSheets to prevent value duplication and support semicolon-separated appending, which aligns with the changes across multiple test and implementation files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mikeallison/nes-1142-inconsistent-data-between-google-sync-and-standard-data-2

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.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jan 13, 2026

View your CI Pipeline Execution ↗ for commit 1ddd925

Command Status Duration Result
nx affected --target=subgraph-check --base=f7a5... ✅ Succeeded 2s View ↗
nx affected --target=extract-translations --bas... ✅ Succeeded <1s View ↗
nx affected --target=lint --base=f7a5c680ef8f92... ✅ Succeeded 11s View ↗
nx affected --target=type-check --base=f7a5c680... ✅ Succeeded 18s View ↗
nx run-many --target=codegen --all --parallel=3 ✅ Succeeded 2s View ↗
nx run-many --target=prisma-generate --all --pa... ✅ Succeeded 6s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-22 00:41:20 UTC

Copy link
Copy Markdown
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

🧹 Nitpick comments (2)
apis/api-journeys-modern/src/schema/event/utils.spec.ts (2)

610-618: Test assertions don't verify the actual merged value.

The test verifies that updateRangeValues is called with values containing 'visitor-id', but doesn't assert that the merged value actually contains 'Option A; Option B'. This weakens the test's ability to catch regressions in the merge logic.

♻️ Suggested improvement to verify merged values
       // Should call updateRangeValues with merged values (semicolon separated)
       expect(mockUpdateRangeValues).toHaveBeenCalledWith(
         expect.objectContaining({
           values: expect.arrayContaining([
-            expect.arrayContaining(['visitor-id'])
+            expect.arrayContaining(['Option A; Option B'])
           ])
         })
       )

Alternatively, capture the call arguments and assert the specific cell value:

const updateCall = mockUpdateRangeValues.mock.calls.find(
  (call) => call[0].range.includes('A2')
)
expect(updateCall[0].values[0]).toContain('Option A; Option B')

659-661: Test doesn't verify the actual deduplication result.

The test confirms that updateRangeValues is called, but doesn't assert that the value remains 'Option A; Option B' without duplicating 'Option A'. Consider adding an assertion to verify the merged row content.

♻️ Suggested improvement
       // Should call updateRangeValues without duplicating 'Option A'
-      expect(mockUpdateRangeValues).toHaveBeenCalled()
+      expect(mockUpdateRangeValues).toHaveBeenCalledWith(
+        expect.objectContaining({
+          values: [expect.arrayContaining(['Option A; Option B'])]
+        })
+      )
       expect(mockWriteValues).not.toHaveBeenCalled()
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 39d15fd and a912453.

📒 Files selected for processing (2)
  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
  • apis/api-journeys-modern/src/schema/event/utils.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
  • apis/api-journeys-modern/src/schema/event/utils.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

Define a type if possible.

Files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
  • apis/api-journeys-modern/src/schema/event/utils.ts
🧠 Learnings (7)
📚 Learning: 2025-12-19T04:58:24.460Z
Learnt from: CR
Repo: JesusFilm/core PR: 0
File: apps/watch/AGENTS.md:0-0
Timestamp: 2025-12-19T04:58:24.460Z
Learning: Applies to apps/watch/src/**/*.spec.{ts,tsx} : Co-locate React Testing Library specs under `*.spec.ts(x)` and mock network traffic with MSW handlers in `apps/watch/test`.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-12-19T04:58:24.460Z
Learnt from: CR
Repo: JesusFilm/core PR: 0
File: apps/watch/AGENTS.md:0-0
Timestamp: 2025-12-19T04:58:24.460Z
Learning: Applies to apps/watch/src/**/*.spec.{ts,tsx} : Enclose SWR-based hooks in `TestSWRConfig` (`apps/watch/test/TestSWRConfig.tsx`) to isolate cache state between assertions in tests.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-12-19T04:58:24.460Z
Learnt from: CR
Repo: JesusFilm/core PR: 0
File: apps/watch/AGENTS.md:0-0
Timestamp: 2025-12-19T04:58:24.460Z
Learning: Applies to apps/watch/src/**/*.spec.{ts,tsx} : Use the shared Jest setup in `apps/watch/setupTests.tsx` which already boots MSW, Next router mock, and has a longer async timeout.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-09-29T23:03:36.840Z
Learnt from: mikeallisonJS
Repo: JesusFilm/core PR: 7629
File: apis/api-journeys-modern/src/schema/event/utils.ts:43-60
Timestamp: 2025-09-29T23:03:36.840Z
Learning: In the JesusFilm/core repository, do not recommend using Prisma's `upsert` operation for `JourneyVisitor` creation in `apis/api-journeys-modern/src/schema/event/utils.ts` as it is not race condition safe for this use case. The current `findUnique` then `create` pattern is the preferred approach.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-12-19T19:18:43.790Z
Learnt from: CR
Repo: JesusFilm/core PR: 0
File: apps/resources/AGENTS.md:0-0
Timestamp: 2025-12-19T19:18:43.790Z
Learning: Applies to apps/resources/**/*.spec.ts?(x) : Enclose SWR-based hooks in `TestSWRConfig` (`apps/resources/test/TestSWRConfig.tsx`) to isolate cache state between assertions

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-12-19T19:18:43.790Z
Learnt from: CR
Repo: JesusFilm/core PR: 0
File: apps/resources/AGENTS.md:0-0
Timestamp: 2025-12-19T19:18:43.790Z
Learning: Applies to apps/resources/**/*.spec.ts?(x) : Co-locate React Testing Library specs under `*.spec.ts(x)` and mock network traffic with MSW handlers in `apps/resources/test`

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-11-16T21:30:53.412Z
Learnt from: Kneesal
Repo: JesusFilm/core PR: 8309
File: apps/journeys-admin/setupTests.tsx:47-51
Timestamp: 2025-11-16T21:30:53.412Z
Learning: In apps/journeys-admin/setupTests.tsx, the `document.clearImmediate` mock is required for tests involving the mux upload provider, as an underlying library uses clearImmediate for timeout management to prevent race conditions and unnecessary polling calls.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
🧬 Code graph analysis (1)
apis/api-journeys-modern/src/schema/event/utils.spec.ts (1)
apis/api-journeys-modern/src/schema/event/utils.ts (1)
  • appendEventToGoogleSheets (256-499)
⏰ 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). (5)
  • GitHub Check: test (22, 2/3)
  • GitHub Check: test (22, 3/3)
  • GitHub Check: test (22, 1/3)
  • GitHub Check: lint (22)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apis/api-journeys-modern/src/schema/event/utils.ts (1)

457-468: LGTM! Merge logic correctly handles deduplication and semicolon-separated appending.

The implementation properly:

  1. Preserves existing values when new value is empty
  2. Uses new value when existing is empty
  3. Avoids duplicates by checking if the trimmed new value exists in the split parts
  4. Appends with consistent "; " separator

One minor consideration: if a value itself contains "; " as part of its content, it would be incorrectly split. This is likely an acceptable trade-off for this use case since event values typically don't contain semicolons.

Copy link
Copy Markdown
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

🧹 Nitpick comments (2)
apis/api-journeys-modern/src/schema/event/utils.spec.ts (2)

610-618: Weak assertion does not verify semicolon merging behavior.

The test description says it "should append new values with semicolon" but the assertion only checks that values contains 'visitor-id'. This would pass even if the merging logic was broken. Consider capturing the call arguments and asserting the actual merged value contains 'Option A; Option B'.

💡 Suggested stronger assertion
-      // Should call updateRangeValues with merged values (semicolon separated)
-      expect(mockUpdateRangeValues).toHaveBeenCalledWith(
-        expect.objectContaining({
-          values: expect.arrayContaining([
-            expect.arrayContaining(['visitor-id'])
-          ])
-        })
-      )
+      // Should call updateRangeValues with merged values (semicolon separated)
+      expect(mockUpdateRangeValues).toHaveBeenCalled()
+      const updateCall = mockUpdateRangeValues.mock.calls.find(
+        (call) => call[0].range?.includes('A2')
+      )
+      expect(updateCall).toBeDefined()
+      const mergedValues = updateCall?.[0].values[0]
+      expect(mergedValues).toContain('Option A; Option B')

660-664: Assertion does not verify deduplication behavior.

The test intends to verify that duplicates are not added, but only asserts that updateRangeValues was called. To properly validate deduplication, assert that the merged value remains 'Option A; Option B' (not 'Option A; Option B; Option A').

💡 Suggested stronger assertion
-      // Should call updateRangeValues without duplicating 'Option A'
-      expect(mockUpdateRangeValues).toHaveBeenCalled()
-      expect(mockWriteValues).not.toHaveBeenCalled()
+      // Should call updateRangeValues without duplicating 'Option A'
+      expect(mockUpdateRangeValues).toHaveBeenCalled()
+      const updateCall = mockUpdateRangeValues.mock.calls.find(
+        (call) => call[0].range?.includes('A2')
+      )
+      expect(updateCall).toBeDefined()
+      const mergedValues = updateCall?.[0].values[0]
+      // Verify 'Option A' appears only once (no duplication)
+      expect(mergedValues).toContain('Option A; Option B')
+      expect(mergedValues).not.toContain('Option A; Option B; Option A')
+      expect(mockWriteValues).not.toHaveBeenCalled()
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 a912453 and f7266ef.

📒 Files selected for processing (1)
  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/base.mdc)

Define a type if possible.

Files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
🧠 Learnings (4)
📚 Learning: 2025-12-19T04:58:24.460Z
Learnt from: CR
Repo: JesusFilm/core PR: 0
File: apps/watch/AGENTS.md:0-0
Timestamp: 2025-12-19T04:58:24.460Z
Learning: Applies to apps/watch/src/**/*.spec.{ts,tsx} : Enclose SWR-based hooks in `TestSWRConfig` (`apps/watch/test/TestSWRConfig.tsx`) to isolate cache state between assertions in tests.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-12-19T04:58:24.460Z
Learnt from: CR
Repo: JesusFilm/core PR: 0
File: apps/watch/AGENTS.md:0-0
Timestamp: 2025-12-19T04:58:24.460Z
Learning: Applies to apps/watch/src/**/*.spec.{ts,tsx} : Co-locate React Testing Library specs under `*.spec.ts(x)` and mock network traffic with MSW handlers in `apps/watch/test`.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-09-29T23:03:36.840Z
Learnt from: mikeallisonJS
Repo: JesusFilm/core PR: 7629
File: apis/api-journeys-modern/src/schema/event/utils.ts:43-60
Timestamp: 2025-09-29T23:03:36.840Z
Learning: In the JesusFilm/core repository, do not recommend using Prisma's `upsert` operation for `JourneyVisitor` creation in `apis/api-journeys-modern/src/schema/event/utils.ts` as it is not race condition safe for this use case. The current `findUnique` then `create` pattern is the preferred approach.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
📚 Learning: 2025-11-16T21:30:53.412Z
Learnt from: Kneesal
Repo: JesusFilm/core PR: 8309
File: apps/journeys-admin/setupTests.tsx:47-51
Timestamp: 2025-11-16T21:30:53.412Z
Learning: In apps/journeys-admin/setupTests.tsx, the `document.clearImmediate` mock is required for tests involving the mux upload provider, as an underlying library uses clearImmediate for timeout management to prevent race conditions and unnecessary polling calls.

Applied to files:

  • apis/api-journeys-modern/src/schema/event/utils.spec.ts
🧬 Code graph analysis (1)
apis/api-journeys-modern/src/schema/event/utils.spec.ts (1)
apis/api-journeys-modern/src/schema/event/utils.ts (1)
  • appendEventToGoogleSheets (256-499)
⏰ 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: test (22, 3/3)
  • GitHub Check: lint (22)
  • GitHub Check: Analyze (javascript)

@stage-branch-merger
Copy link
Copy Markdown
Contributor

I see you added the "on stage" label, I'll get this merged to the stage branch!

tanflem
tanflem previously approved these changes Jan 13, 2026
…eys based on blockId, preventing incorrect value placements
@stage-branch-merger
Copy link
Copy Markdown
Contributor

Merge conflict attempting to merge this into stage. Please fix manually.

Copy link
Copy Markdown
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

🤖 Fix all issues with AI agents
In `@apis/api-journeys-modern/src/schema/event/utils.ts`:
- Around line 371-390: The extractor extractBlockIdFromKey currently only
detects keys like "blockId-..." and returns null for a key that is exactly a
blockId, causing resolvedBlockId to be null and resolvedColumnKey to fall back
to the raw blockId and be dropped; update extractBlockIdFromKey to also treat an
exact match (key === col.blockId) as a valid match (i.e., check col.blockId !=
null && (key === col.blockId || key.startsWith(`${col.blockId}-`))), so
resolvedBlockId will be set correctly and resolvedColumnKey (which uses
columns.find(col => col.blockId === resolvedBlockId)?.key) will resolve to the
correct column key and preserve the value with finalHeader.
🧹 Nitpick comments (3)
apis/api-journeys-modern/src/schema/event/utils.ts (1)

478-488: Optional: make semicolon splitting more tolerant.

If existing data was written with ";" (no space) or inconsistent spacing, the current split on "; " can miss duplicates. Consider splitting on ";" and trimming to be robust.

♻️ Optional tweak
-          const existingParts = existingValue.split('; ').map((s) => s.trim())
+          const existingParts = existingValue
+            .split(';')
+            .map((s) => s.trim())
+            .filter(Boolean)
apis/api-journeys-modern/src/schema/event/utils.spec.ts (2)

572-619: Consider asserting the actual semicolon-merged value.

Right now the test only checks that updateRangeValues is called (Line 610+). To lock behavior, assert the merged cell contains "Option A; Option B".


621-664: Consider asserting that duplicates are not introduced.

This test could explicitly assert the merged cell remains "Option A; Option B" (and not "Option A; Option B; Option A"), rather than only checking that an update happened.

Comment thread apis/api-journeys-modern/src/schema/event/utils.ts Outdated
@stage-branch-merger
Copy link
Copy Markdown
Contributor

Merge conflict attempting to merge this into stage. Please fix manually.

@stage-branch-merger
Copy link
Copy Markdown
Contributor

Merge conflict attempting to merge this into stage. Please fix manually.

…nsure new blocks are included and prevent duplicate column keys
@stage-branch-merger
Copy link
Copy Markdown
Contributor

Merge conflict attempting to merge this into stage. Please fix manually.

Copy link
Copy Markdown
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

🤖 Fix all issues with AI agents
In `@apis/api-journeys-modern/src/schema/event/utils.ts`:
- Around line 509-519: The dedup logic in the mergedRow computation
(alignedRow.map) only splits existingValue into existingParts so multi-item new
values duplicate; change the merge to split both existingValue and value by ';',
trim parts, create a Set to union them (preserving original order if possible),
then join with '; ' and return that combined string (update the block that
defines existingParts and the final return to perform split-new, merge-unique,
and rejoin); keep the early returns for empty value or empty existingValue and
reference the alignedRow, existingRow, existingParts, and mergedRow identifiers
when making the change.

Comment thread apis/api-journeys-modern/src/schema/event/utils.ts Outdated
mikeallisonJS and others added 5 commits January 16, 2026 20:36
…om existing and new inputs, improving data integrity
…mns by blockId, ensuring only the first label is retained for each block
Copy link
Copy Markdown
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)
apis/api-journeys-modern/src/schema/journeyVisitor/journeyVisitor.spec.ts (1)

376-441: Don’t drop values when labels change for the same blockId.
With blockId-only columns, events with different labels should still land in the same column; the current expectation (“Submit” only) would mask data loss. Please update the expected output (and implementation) to merge both values (e.g., “Submit; Cancel”), or explicitly confirm that dropping is intended.

🛠️ Suggested expectation update
-      '"Date","Button Click"\n"2024-01-01","Submit"\n'
+      '"Date","Button Click"\n"2024-01-01","Submit; Cancel"\n'
apis/api-journeys-modern/src/schema/journeyVisitor/journeyVisitor.ts (1)

332-349: Header dedupe now causes label-changed events to be dropped.
Headers are blockId-only, but row keys are still ${blockId}-${label} in getJourneyVisitors, so values from label changes won’t match any column key and get omitted. Map events to the canonical column key for the blockId (or move to blockId-only keys) so all values land in the single column.

🛠️ One way to align row keys with blockId-only headers
-async function* getJourneyVisitors(
-  journeyId: string,
-  eventWhere: Prisma.EventWhereInput,
-  timezone: string,
-  batchSize: number = 1000
-): AsyncGenerator<JourneyVisitorExportRow> {
+async function* getJourneyVisitors(
+  journeyId: string,
+  eventWhere: Prisma.EventWhereInput,
+  timezone: string,
+  blockIdToKey: Map<string, string>,
+  batchSize: number = 1000
+): AsyncGenerator<JourneyVisitorExportRow> {
@@
-        if (event.blockId) {
+        if (event.blockId) {
           const normalizedLabel = (event.label ?? '')
             .replace(/\s+/g, ' ')
             .trim()
-          const key = `${event.blockId}-${normalizedLabel}`
+          const key =
+            blockIdToKey.get(event.blockId) ??
+            `${event.blockId}-${normalizedLabel}`
@@
-        for await (const row of getJourneyVisitors(
-          journeyId,
-          eventWhere,
-          userTimezone
-        )) {
+        const blockIdToKey = new Map(
+          columns
+            .filter((c) => c.blockId != null)
+            .map((c) => [c.blockId as string, c.key])
+        )
+        for await (const row of getJourneyVisitors(
+          journeyId,
+          eventWhere,
+          userTimezone,
+          blockIdToKey
+        )) {
apis/api-journeys-modern/src/schema/journeyVisitor/journeyVisitorExportToGoogleSheet.mutation.ts (1)

303-322: Same blockId-only header mismatch here (label-changed events dropped).
Headers are deduped by blockId, but rows are still keyed by ${blockId}-${label} in getJourneyVisitors, so events with changed labels won’t land in any column. Please route all events to the canonical column key for their blockId (or switch to blockId-only keys) to avoid data loss.

🛠️ Align row keys with blockId-only headers
-async function* getJourneyVisitors(
-  journeyId: string,
-  eventWhere: Prisma.EventWhereInput,
-  timezone: string,
-  batchSize: number = 1000
-): AsyncGenerator<JourneyVisitorExportRow> {
+async function* getJourneyVisitors(
+  journeyId: string,
+  eventWhere: Prisma.EventWhereInput,
+  timezone: string,
+  blockIdToKey: Map<string, string>,
+  batchSize: number = 1000
+): AsyncGenerator<JourneyVisitorExportRow> {
@@
-        if (event.blockId) {
+        if (event.blockId) {
           const normalizedLabel = (event.label ?? '')
             .replace(/\s+/g, ' ')
             .trim()
-          const key = `${event.blockId}-${normalizedLabel}`
+          const key =
+            blockIdToKey.get(event.blockId) ??
+            `${event.blockId}-${normalizedLabel}`
@@
-      for await (const row of getJourneyVisitors(
-        journeyId,
-        eventWhere,
-        userTimezone
-      )) {
+      const blockIdToKey = new Map(
+        columns
+          .filter((c) => c.blockId != null)
+          .map((c) => [c.blockId as string, c.key])
+      )
+      for await (const row of getJourneyVisitors(
+        journeyId,
+        eventWhere,
+        userTimezone,
+        blockIdToKey
+      )) {

…el retrieval

Updated journeyVisitor and related exports to include an orderBy clause, ensuring labels are consistently retrieved based on creation date. This change enhances data integrity by retaining the first label per blockId across various export functions.
@mikeallisonJS mikeallisonJS merged commit fb98b91 into main Jan 22, 2026
14 checks passed
@mikeallisonJS mikeallisonJS deleted the mikeallison/nes-1142-inconsistent-data-between-google-sync-and-standard-data-2 branch January 22, 2026 00:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants