From f286ece656c98788695e10f0d3ed6324c5c9bb14 Mon Sep 17 00:00:00 2001
From: Peter Perlepes 
Date: Thu, 29 Jun 2023 13:04:25 +0300
Subject: [PATCH] Add tracker.getQueueName method (close #1215)
---
 ...15-expose-queue-name_2023-06-29-15-53.json | 10 ++++
 ...15-expose-queue-name_2023-06-29-15-54.json | 10 ++++
 .../browser-tracker-core/src/tracker/index.ts |  2 +
 .../src/tracker/out_queue.ts                  | 26 +++++++---
 .../browser-tracker-core/src/tracker/types.ts |  5 ++
 .../test/out_queue.test.ts                    | 48 +++++++++++++++++++
 .../docs/browser-tracker.api.md               |  1 +
 .../browser-tracker/test/helpers/index.ts     |  6 +++
 trackers/browser-tracker/test/tracker.test.ts | 24 ++++++++++
 9 files changed, 125 insertions(+), 7 deletions(-)
 create mode 100644 common/changes/@snowplow/browser-tracker-core/feature-1215-expose-queue-name_2023-06-29-15-53.json
 create mode 100644 common/changes/@snowplow/browser-tracker/feature-1215-expose-queue-name_2023-06-29-15-54.json
 create mode 100644 trackers/browser-tracker/test/helpers/index.ts
diff --git a/common/changes/@snowplow/browser-tracker-core/feature-1215-expose-queue-name_2023-06-29-15-53.json b/common/changes/@snowplow/browser-tracker-core/feature-1215-expose-queue-name_2023-06-29-15-53.json
new file mode 100644
index 000000000..e4a7a4161
--- /dev/null
+++ b/common/changes/@snowplow/browser-tracker-core/feature-1215-expose-queue-name_2023-06-29-15-53.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@snowplow/browser-tracker-core",
+      "comment": "Expose getQueueName method",
+      "type": "none"
+    }
+  ],
+  "packageName": "@snowplow/browser-tracker-core"
+}
\ No newline at end of file
diff --git a/common/changes/@snowplow/browser-tracker/feature-1215-expose-queue-name_2023-06-29-15-54.json b/common/changes/@snowplow/browser-tracker/feature-1215-expose-queue-name_2023-06-29-15-54.json
new file mode 100644
index 000000000..f85d4a1eb
--- /dev/null
+++ b/common/changes/@snowplow/browser-tracker/feature-1215-expose-queue-name_2023-06-29-15-54.json
@@ -0,0 +1,10 @@
+{
+  "changes": [
+    {
+      "packageName": "@snowplow/browser-tracker",
+      "comment": "Add getQueueName tracker method",
+      "type": "none"
+    }
+  ],
+  "packageName": "@snowplow/browser-tracker"
+}
\ No newline at end of file
diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts
index 302dcb58e..807cd6936 100755
--- a/libraries/browser-tracker-core/src/tracker/index.ts
+++ b/libraries/browser-tracker-core/src/tracker/index.ts
@@ -1283,6 +1283,8 @@ export function Tracker(
       },
 
       clearUserData: clearUserDataAndCookies,
+
+      getQueueName: outQueue.getName,
     };
 
     return {
diff --git a/libraries/browser-tracker-core/src/tracker/out_queue.ts b/libraries/browser-tracker-core/src/tracker/out_queue.ts
index b838ba436..01fb87e8c 100644
--- a/libraries/browser-tracker-core/src/tracker/out_queue.ts
+++ b/libraries/browser-tracker-core/src/tracker/out_queue.ts
@@ -33,6 +33,7 @@ import { SharedState } from '../state';
 import { localStorageAccessible } from '../detectors';
 import { LOG, Payload } from '@snowplow/tracker-core';
 import { PAYLOAD_DATA_SCHEMA } from './schemata';
+import { EventMethod } from './types';
 
 export interface OutQueue {
   enqueueRequest: (request: Payload, url: string) => void;
@@ -40,7 +41,11 @@ export interface OutQueue {
   setUseLocalStorage: (localStorage: boolean) => void;
   setAnonymousTracking: (anonymous: boolean) => void;
   setCollectorUrl: (url: string) => void;
-  setBufferSize: (bufferSize: number) => void;
+  setBufferSize: (newBufferSize: number) => void;
+  /**
+   * Returns the currently used queue name or if the `eventMethod` argument is provided, the queue name for the eventMethod.
+   */
+  getName: (eventMethod?: Exclude) => string;
 }
 
 /**
@@ -48,7 +53,7 @@ export interface OutQueue {
  * Instantiated once per tracker instance.
  *
  * @param id - The Snowplow function name (used to generate the localStorage key)
- * @param sharedSate - Stores reference to the outbound queue so it can unload the page when all queues are empty
+ * @param sharedState - Stores reference to the outbound queue so it can unload the page when all queues are empty
  * @param useLocalStorage - Whether to use localStorage at all
  * @param eventMethod - if null will use 'beacon' otherwise can be set to 'post', 'get', or 'beacon' to force.
  * @param postPath - The path where events are to be posted
@@ -67,7 +72,7 @@ export interface OutQueue {
  */
 export function OutQueueManager(
   id: string,
-  sharedSate: SharedState,
+  sharedState: SharedState,
   useLocalStorage: boolean,
   eventMethod: string | boolean,
   postPath: string,
@@ -114,7 +119,7 @@ export function OutQueueManager(
     // Resolve all options and capabilities and decide path
     path = usePost ? postPath : '/i',
     // Different queue names for GET and POST since they are stored differently
-    queueName = `snowplowOutQueue_${id}_${usePost ? 'post2' : 'get'}`;
+    queueName = getQueueName(id, usePost ? 'post' : 'get');
 
   // Ensure we don't set headers when beacon is the requested eventMethod as we might fallback to POST
   // and end up sending them in older browsers which don't support beacon leading to inconsistencies
@@ -128,7 +133,9 @@ export function OutQueueManager(
     try {
       const localStorageQueue = window.localStorage.getItem(queueName);
       outQueue = localStorageQueue ? JSON.parse(localStorageQueue) : [];
-    } catch (e) {}
+    } catch (e) {
+      LOG.error('Failed to access window.localStorage queue.');
+    }
   }
 
   // Initialize to and empty array if we didn't get anything out of localStorage
@@ -137,16 +144,20 @@ export function OutQueueManager(
   }
 
   // Used by pageUnloadGuard
-  sharedSate.outQueues.push(outQueue);
+  sharedState.outQueues.push(outQueue);
 
   if (useXhr && bufferSize > 1) {
-    sharedSate.bufferFlushers.push(function (sync) {
+    sharedState.bufferFlushers.push(function (sync) {
       if (!executingQueue) {
         executeQueue(sync);
       }
     });
   }
 
+  function getQueueName(id: string, method: Exclude) {
+    return `snowplowOutQueue_${id}_${method === 'get' ? 'get' : 'post2'}`;
+  }
+
   /*
    * Convert a dictionary to a querystring
    * The context field is the last in the querystring
@@ -535,6 +546,7 @@ export function OutQueueManager(
     setBufferSize: (newBufferSize: number) => {
       bufferSize = newBufferSize;
     },
+    getName: (method?: Exclude) => (method ? getQueueName(id, method) : queueName),
   };
 
   function hasWebKitBeaconBug(useragent: string) {
diff --git a/libraries/browser-tracker-core/src/tracker/types.ts b/libraries/browser-tracker-core/src/tracker/types.ts
index b45f5f8fe..287b378e5 100755
--- a/libraries/browser-tracker-core/src/tracker/types.ts
+++ b/libraries/browser-tracker-core/src/tracker/types.ts
@@ -580,6 +580,11 @@ export interface BrowserTracker {
    */
   clearUserData: (configuration?: ClearUserDataConfiguration) => void;
 
+  /**
+   * Returns the currently used queue name or if the `eventMethod` argument is provided, the queue name for the eventMethod.
+   */
+  getQueueName: (eventMethod?: Exclude) => string;
+
   /**
    * Add a plugin into the plugin collection after Tracker has already been initialised
    * @param configuration - The plugin to add
diff --git a/libraries/browser-tracker-core/test/out_queue.test.ts b/libraries/browser-tracker-core/test/out_queue.test.ts
index c51b71194..d6a794120 100644
--- a/libraries/browser-tracker-core/test/out_queue.test.ts
+++ b/libraries/browser-tracker-core/test/out_queue.test.ts
@@ -57,6 +57,54 @@ describe('OutQueueManager', () => {
     (xhrMock as any).onreadystatechange();
   };
 
+  describe('API', () => {
+    it('returns the correct queue name', () => {
+      const postOutQueue = OutQueueManager(
+        'sp',
+        new SharedState(),
+        true,
+        'post',
+        '/com.snowplowanalytics.snowplow/tp2',
+        1,
+        40000,
+        0, // maxGetBytes – 0 for no limit
+        false,
+        maxQueueSize,
+        5000,
+        false,
+        {},
+        true,
+        [401], // retry status codes - override don't retry ones
+        [401, 505] // don't retry status codes
+      );
+
+      expect(postOutQueue.getName()).toBe('snowplowOutQueue_sp_post2');
+      expect(postOutQueue.getName('get')).toBe('snowplowOutQueue_sp_get');
+
+      const getOutQueue = OutQueueManager(
+        'sp',
+        new SharedState(),
+        true,
+        'get',
+        '/com.snowplowanalytics.snowplow/tp2',
+        1,
+        40000,
+        0,
+        false,
+        maxQueueSize,
+        5000,
+        false,
+        {},
+        true,
+        [],
+        []
+      );
+
+      expect(getOutQueue.getName()).toBe('snowplowOutQueue_sp_get');
+      expect(getOutQueue.getName('post')).toBe('snowplowOutQueue_sp_post2');
+    });
+  });
+
   describe('POST requests', () => {
     var outQueue: OutQueue;
 
diff --git a/trackers/browser-tracker/docs/browser-tracker.api.md b/trackers/browser-tracker/docs/browser-tracker.api.md
index 3d7214028..bb74c9c23 100644
--- a/trackers/browser-tracker/docs/browser-tracker.api.md
+++ b/trackers/browser-tracker/docs/browser-tracker.api.md
@@ -75,6 +75,7 @@ export interface BrowserTracker {
     getDomainUserId: () => void;
     getDomainUserInfo: () => void;
     getPageViewId: () => string;
+    getQueueName: (eventMethod?: Exclude) => string;
     getTabId: () => string | null;
     getUserId: () => void;
     id: string;
diff --git a/trackers/browser-tracker/test/helpers/index.ts b/trackers/browser-tracker/test/helpers/index.ts
new file mode 100644
index 000000000..8745733a0
--- /dev/null
+++ b/trackers/browser-tracker/test/helpers/index.ts
@@ -0,0 +1,6 @@
+import { SharedState, TrackerConfiguration, addTracker } from '@snowplow/browser-tracker-core';
+
+export function createTracker(configuration?: TrackerConfiguration) {
+  const id = 'sp-' + Math.random();
+  return addTracker(id, id, '', '', new SharedState(), configuration);
+}
diff --git a/trackers/browser-tracker/test/tracker.test.ts b/trackers/browser-tracker/test/tracker.test.ts
index b26982afd..5ba5c3c37 100644
--- a/trackers/browser-tracker/test/tracker.test.ts
+++ b/trackers/browser-tracker/test/tracker.test.ts
@@ -30,6 +30,7 @@
 
 import { SharedState, addTracker } from '@snowplow/browser-tracker-core';
 import F from 'lodash/fp';
+import { createTracker } from './helpers';
 
 jest.useFakeTimers('modern');
 
@@ -276,3 +277,26 @@ describe('Activity tracker behaviour', () => {
     expect(secondPageId).toBe(extractPageId(ppl));
   });
 });
+
+describe('API', () => {
+  describe('getQueueName', () => {
+    it('does return the queue name', () => {
+      let rand = Math.random();
+      const mathSpy = jest.spyOn(global.Math, 'random');
+      mathSpy.mockReturnValueOnce(rand);
+
+      const postTracker = createTracker();
+      expect(postTracker?.getQueueName()).toBe(`snowplowOutQueue_sp-${rand}_post2`);
+      expect(postTracker?.getQueueName('get')).toBe(`snowplowOutQueue_sp-${rand}_get`);
+
+      rand = Math.random();
+      mathSpy.mockReturnValueOnce(rand);
+
+      const getTracker = createTracker({ eventMethod: 'get' });
+      expect(getTracker?.getQueueName()).toBe(`snowplowOutQueue_sp-${rand}_get`);
+      expect(getTracker?.getQueueName('post')).toBe(`snowplowOutQueue_sp-${rand}_post2`);
+
+      mathSpy.mockRestore();
+    });
+  });
+});