From d27ef2df4d1e1f3113d0469710287c90d3e295dd 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 | 22 +++++++++ 9 files changed, 123 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..716d6a976 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 === 'post' ? 'post2' : 'get'}`; + } + /* * 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..6c0608ccc 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?: 'post' | 'get' | undefined) => 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..86015a0ed 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?: "post" | "get" | undefined) => 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..38181a1dd 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,24 @@ describe('Activity tracker behaviour', () => { expect(secondPageId).toBe(extractPageId(ppl)); }); }); + +describe('API', () => { + it('getQueueName 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(); + }); +});