Skip to content

Commit

Permalink
Add an option to deactivate tracker in tracker core and a getTracker …
Browse files Browse the repository at this point in the history
…and removeTracker functions in the React Native tracker
  • Loading branch information
matus-tomlein committed Nov 13, 2024
1 parent 9094cb3 commit c8e742b
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 3 deletions.
20 changes: 20 additions & 0 deletions libraries/tracker-core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ export interface TrackerCore {
*/
addPayloadPair: (key: string, value: unknown) => void;

/**
* Deactivate tracker core including all plugins.
* This is useful for cleaning up resources or listeners that have been created.
* Once deactivated, the tracker won't be able to track any events.
*/
deactivate(): void;

/**
* Get current base64 encoding state
*/
Expand Down Expand Up @@ -387,6 +394,11 @@ export function trackerCore(configuration: CoreConfiguration = {}): TrackerCore
context?: Array<SelfDescribingJson<C>> | null,
timestamp?: Timestamp | null
): Payload | undefined {
if (!active) {
LOG.error('Track called on deactivated tracker');
return undefined;
}

pb.withJsonProcessor(payloadJsonProcessor(encodeBase64));
pb.add('eid', uuid());
pb.addDict(payloadPairs);
Expand Down Expand Up @@ -539,6 +551,7 @@ export function trackerCore(configuration: CoreConfiguration = {}): TrackerCore
return core;
}

let active = true;
const { base64, corePlugins, callback } = configuration,
plugins = corePlugins ?? [],
partialCore = newCore(base64 ?? true, plugins, callback),
Expand All @@ -550,6 +563,13 @@ export function trackerCore(configuration: CoreConfiguration = {}): TrackerCore
plugin.logger?.(LOG);
plugin.activateCorePlugin?.(core);
},
deactivate: () => {
plugins.forEach((plugin) => {
plugin.deactivatePlugin?.(core);
});
plugins.length = 0;
active = false;
}
};

plugins?.forEach((plugin) => {
Expand Down
5 changes: 5 additions & 0 deletions libraries/tracker-core/src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export interface CorePlugin {
* Use to capture the specific core instance for each instance of a core plugin
*/
activateCorePlugin?: (core: TrackerCore) => void;
/**
* Called when the tracker is being destroyed.
* Should be used to clean up any resources or listeners that the plugin has created.
*/
deactivatePlugin?: (core: TrackerCore) => void;
/**
* Called before the `filter` method is called and before the trackerCore callback fires (if the filter passes)
* @param payloadBuilder - The payloadBuilder which will be sent to the callback, can be modified
Expand Down
48 changes: 48 additions & 0 deletions libraries/tracker-core/test/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ function compare(result: Payload, expected: Payload, t: ExecutionContext) {
t.deepEqual(result, expected);
}

test.before(() => {
console.error = () => {}; // Silence console.error globally
});

test('tracker.track API should return the eid attribute', (t) => {
const pageUrl = 'http://www.example.com';
const pageTitle = 'title page';
Expand Down Expand Up @@ -1034,3 +1038,47 @@ test('filter is passed full payload including dynamic context', (t) => {

t.assert(countTracked === 1);
});

test('doesnt track any events on deactivated tracker', (t) => {
let countTracked = 0;
const tracker = trackerCore({
corePlugins: [
{
afterTrack: () => {
countTracked += 1;
},
},
],
});

tracker.deactivate();

t.falsy(
tracker.track(
buildPageView({
pageUrl: 'http://www.example.com',
pageTitle: 'title page',
referrer: 'https://www.google.com',
})
)
);

t.assert(countTracked === 0);
});

test('deactivates plugins on deactivated tracker', (t) => {
let pluginDeactivated = false;
const tracker = trackerCore({
corePlugins: [
{
deactivatePlugin: () => {
pluginDeactivated = true;
}
},
],
});

tracker.deactivate();

t.assert(pluginDeactivated);
});
38 changes: 36 additions & 2 deletions trackers/react-native-tracker/src/tracker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { trackerCore, PayloadBuilder, version, EmitterConfiguration } from '@snowplow/tracker-core';
import { trackerCore, PayloadBuilder, version, EmitterConfiguration, TrackerCore } from '@snowplow/tracker-core';

import { newEmitter } from '@snowplow/tracker-core';
import { newReactNativeEventStore } from './event_store';
Expand All @@ -13,6 +13,8 @@ import {
TrackerConfiguration,
} from './types';

const initializedTrackers: Record<string, { tracker: ReactNativeTracker; core: TrackerCore }> = {};

/**
* Creates a new tracker instance with the given configuration
* @param configuration - Configuration for the tracker
Expand Down Expand Up @@ -45,7 +47,7 @@ export async function newTracker(
core.setAppId(appId);
}

return {
const tracker = {
...newTrackEventFunctions(core),
...subject.properties,
setAppId: core.setAppId,
Expand All @@ -56,4 +58,36 @@ export async function newTracker(
clearGlobalContexts: core.clearGlobalContexts,
addPlugin: core.addPlugin,
};
initializedTrackers[namespace] = { tracker, core };
return tracker;
}

/**
* Retrieves an initialized tracker given its namespace
* @param trackerNamespace Tracker namespace
* @returns Tracker instance if exists
*/
export function getTracker(trackerNamespace: string): ReactNativeTracker | undefined {
return initializedTrackers[trackerNamespace]?.tracker;
}

/**
* Removes a tracker given its namespace
*
* @param trackerNamespace {string}
*/
export function removeTracker(trackerNamespace: string): void {
if (initializedTrackers[trackerNamespace]) {
initializedTrackers[trackerNamespace]?.core.deactivate();
delete initializedTrackers[trackerNamespace];
}
}

/**
* Removes all initialized trackers
*
* @returns - A boolean promise
*/
export function removeAllTrackers(): void {
Object.keys(initializedTrackers).forEach(removeTracker);
}
22 changes: 21 additions & 1 deletion trackers/react-native-tracker/test/tracker.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { newTracker } from '../src';
import { getTracker, newTracker, removeAllTrackers, removeTracker } from '../src';

function createMockFetch(status: number, requests: Request[]) {
return async (input: Request) => {
Expand All @@ -21,6 +21,26 @@ describe('Tracker', () => {
expect(await newTracker({ namespace: 'test', endpoint: 'http://localhost:9090' })).toBeDefined();
});

it('retrieves an existing tracker', async () => {
const tracker = await newTracker({ namespace: 'test', endpoint: 'http://localhost:9090' });
expect(getTracker('test')).toBe(tracker);
expect(getTracker('non-existent')).toBeUndefined();
});

it('removes a tracker', async () => {
await newTracker({ namespace: 'test', endpoint: 'http://localhost:9090' });
expect(getTracker('test')).toBeDefined();
removeTracker('test');
expect(getTracker('test')).toBeUndefined();
});

it('removes all trackers', async () => {
await newTracker({ namespace: 'test', endpoint: 'http://localhost:9090' });
expect(getTracker('test')).toBeDefined();
removeAllTrackers();
expect(getTracker('test')).toBeUndefined();
});

it('tracks a page view event with tracker properties', async () => {
const tracker = await newTracker({
namespace: 'test',
Expand Down

0 comments on commit c8e742b

Please sign in to comment.