From b1584fc1dc531b312dc6020fadc1c14bd153a557 Mon Sep 17 00:00:00 2001
From: Oscar Bazaldua <511911+oscb@users.noreply.github.com>
Date: Fri, 18 Aug 2023 09:14:55 -0700
Subject: [PATCH] fix: sentAt set at batch upload time (#932)

`sentAt` is not set at batch upload time once per the whole batch. Individual event `sentAt` property is stripped when doing batch uploading.
---
 .changeset/odd-nails-collect.md               |  5 +++
 .../__tests__/batched-dispatcher.test.ts      | 43 ++++++++++++++++---
 .../plugins/segmentio/batched-dispatcher.ts   | 12 +++++-
 3 files changed, 54 insertions(+), 6 deletions(-)
 create mode 100644 .changeset/odd-nails-collect.md

diff --git a/.changeset/odd-nails-collect.md b/.changeset/odd-nails-collect.md
new file mode 100644
index 000000000..583621d79
--- /dev/null
+++ b/.changeset/odd-nails-collect.md
@@ -0,0 +1,5 @@
+---
+'@segment/analytics-next': patch
+---
+
+`sentAt` is not set at batch upload time once per the whole batch. Individual event `sentAt` property is stripped when doing batch uploading.
diff --git a/packages/browser/src/plugins/segmentio/__tests__/batched-dispatcher.test.ts b/packages/browser/src/plugins/segmentio/__tests__/batched-dispatcher.test.ts
index 11bc67e9b..56e2e2a67 100644
--- a/packages/browser/src/plugins/segmentio/__tests__/batched-dispatcher.test.ts
+++ b/packages/browser/src/plugins/segmentio/__tests__/batched-dispatcher.test.ts
@@ -49,7 +49,9 @@ describe('Batching', () => {
   beforeEach(() => {
     jest.resetAllMocks()
     jest.restoreAllMocks()
-    jest.useFakeTimers()
+    jest.useFakeTimers({
+      now: new Date('9 Jun 1993 00:00:00Z').getTime(),
+    })
   })
 
   afterEach(() => {
@@ -92,7 +94,7 @@ describe('Batching', () => {
       Array [
         "https://https://api.segment.io/b",
         Object {
-          "body": "{\\"batch\\":[{\\"event\\":\\"first\\"},{\\"event\\":\\"second\\"},{\\"event\\":\\"third\\"}]}",
+          "body": "{\\"batch\\":[{\\"event\\":\\"first\\"},{\\"event\\":\\"second\\"},{\\"event\\":\\"third\\"}],\\"sentAt\\":\\"1993-06-09T00:00:00.000Z\\"}",
           "headers": Object {
             "Content-Type": "text/plain",
           },
@@ -150,7 +152,7 @@ describe('Batching', () => {
       Array [
         "https://https://api.segment.io/b",
         Object {
-          "body": "{\\"batch\\":[{\\"event\\":\\"first\\"},{\\"event\\":\\"second\\"}]}",
+          "body": "{\\"batch\\":[{\\"event\\":\\"first\\"},{\\"event\\":\\"second\\"}],\\"sentAt\\":\\"1993-06-09T00:00:10.000Z\\"}",
           "headers": Object {
             "Content-Type": "text/plain",
           },
@@ -185,7 +187,7 @@ describe('Batching', () => {
       Array [
         "https://https://api.segment.io/b",
         Object {
-          "body": "{\\"batch\\":[{\\"event\\":\\"first\\"}]}",
+          "body": "{\\"batch\\":[{\\"event\\":\\"first\\"}],\\"sentAt\\":\\"1993-06-09T00:00:10.000Z\\"}",
           "headers": Object {
             "Content-Type": "text/plain",
           },
@@ -199,7 +201,38 @@ describe('Batching', () => {
       Array [
         "https://https://api.segment.io/b",
         Object {
-          "body": "{\\"batch\\":[{\\"event\\":\\"second\\"}]}",
+          "body": "{\\"batch\\":[{\\"event\\":\\"second\\"}],\\"sentAt\\":\\"1993-06-09T00:00:21.000Z\\"}",
+          "headers": Object {
+            "Content-Type": "text/plain",
+          },
+          "keepalive": false,
+          "method": "post",
+        },
+      ]
+    `)
+  })
+
+  it('removes sentAt from individual events', async () => {
+    const { dispatch } = batch(`https://api.segment.io`, {
+      size: 2,
+    })
+
+    await dispatch(`https://api.segment.io/v1/t`, {
+      event: 'first',
+      sentAt: new Date('11 Jun 1993 00:01:00Z'),
+    })
+
+    await dispatch(`https://api.segment.io/v1/t`, {
+      event: 'second',
+      sentAt: new Date('11 Jun 1993 00:02:00Z'),
+    })
+
+    expect(fetch).toHaveBeenCalledTimes(1)
+    expect(fetch.mock.calls[0]).toMatchInlineSnapshot(`
+      Array [
+        "https://https://api.segment.io/b",
+        Object {
+          "body": "{\\"batch\\":[{\\"event\\":\\"first\\"},{\\"event\\":\\"second\\"}],\\"sentAt\\":\\"1993-06-09T00:00:00.000Z\\"}",
           "headers": Object {
             "Content-Type": "text/plain",
           },
diff --git a/packages/browser/src/plugins/segmentio/batched-dispatcher.ts b/packages/browser/src/plugins/segmentio/batched-dispatcher.ts
index 35995d115..1fb172d6a 100644
--- a/packages/browser/src/plugins/segmentio/batched-dispatcher.ts
+++ b/packages/browser/src/plugins/segmentio/batched-dispatcher.ts
@@ -60,13 +60,23 @@ export default function batch(
 
     const writeKey = (batch[0] as SegmentEvent)?.writeKey
 
+    // Remove sentAt from every event as batching only needs a single timestamp
+    const updatedBatch = batch.map((event) => {
+      const { sentAt, ...newEvent } = event as SegmentEvent
+      return newEvent
+    })
+
     return fetch(`https://${apiHost}/b`, {
       keepalive: pageUnloaded,
       headers: {
         'Content-Type': 'text/plain',
       },
       method: 'post',
-      body: JSON.stringify({ batch, writeKey }),
+      body: JSON.stringify({
+        writeKey,
+        batch: updatedBatch,
+        sentAt: new Date().toISOString(),
+      }),
     })
   }