Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow disabling activity tracking actions (close #1161) #1224

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@snowplow/browser-tracker-core",
"comment": "Allow disabling activity tracking actions with disableActivityTracking and disableActivityTrackingCallback APIs",
"type": "none"
}
],
"packageName": "@snowplow/browser-tracker-core"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@snowplow/browser-tracker",
"comment": "Allow disabling activity tracking actions with disableActivityTracking and disableActivityTrackingCallback APIs",
"type": "none"
}
],
"packageName": "@snowplow/browser-tracker"
}
33 changes: 26 additions & 7 deletions libraries/browser-tracker-core/src/tracker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1048,20 +1048,20 @@ export function Tracker(
//Clear page ping heartbeat on new page view
window.clearInterval(config.activityInterval);

activityInterval(config, context, contextCallback);
scheduleActivityInterval(config, context, contextCallback);
}
}
}
}

function activityInterval(
function scheduleActivityInterval(
config: ActivityConfig,
context?: Array<SelfDescribingJson> | null,
contextCallback?: (() => Array<SelfDescribingJson>) | null
) {
const executePagePing = (cb: ActivityCallback, c: Array<SelfDescribingJson>) => {
const executePagePing = (cb: ActivityCallback, context: Array<SelfDescribingJson>) => {
refreshUrl();
cb({ context: c, pageViewId: getPageViewId(), minXOffset, minYOffset, maxXOffset, maxYOffset });
cb({ context, pageViewId: getPageViewId(), minXOffset, minYOffset, maxXOffset, maxYOffset });
resetMaxScrolls();
};

Expand All @@ -1087,10 +1087,10 @@ export function Tracker(
}
};

if (config.configMinimumVisitLength != 0) {
config.activityInterval = window.setTimeout(timeout, config.configMinimumVisitLength);
} else {
if (config.configMinimumVisitLength === 0) {
config.activityInterval = window.setInterval(heartbeat, config.configHeartBeatTimer);
} else {
config.activityInterval = window.setTimeout(timeout, config.configMinimumVisitLength);
}
}

Expand Down Expand Up @@ -1137,6 +1137,17 @@ export function Tracker(
);
}

function disableActivityTrackingAction(actionKey: keyof ActivityConfigurations) {
const callbackConfiguration = activityTrackingConfig.configurations[actionKey];
if (callbackConfiguration?.configMinimumVisitLength === 0) {
window.clearTimeout(callbackConfiguration?.activityInterval);
} else {
window.clearInterval(callbackConfiguration?.activityInterval);
}

activityTrackingConfig.configurations[actionKey] = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering, should we also set activityTrackingConfig.enabled to false in case there are no more configurations left? From what I can tell it'd be more like a cosmetic change and not have much effect on anything (in which case do we really need the flag?), but wondering if you can see some reason to or considered it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enabled flag seems to be mostly used for the activity tracking at the start and between pageviews. It does not have control over the interval/timeouts of the actual pinging.

It was the first thing I tried 😄

}

const apiMethods = {
getDomainSessionIndex: function () {
return memorizedVisitCount;
Expand Down Expand Up @@ -1219,6 +1230,14 @@ export function Tracker(
}
},

disableActivityTracking: function () {
disableActivityTrackingAction('pagePing');
},

disableActivityTrackingCallback: function () {
disableActivityTrackingAction('callback');
},

updatePageActivity: function () {
activityHandler();
},
Expand Down
10 changes: 10 additions & 0 deletions libraries/browser-tracker-core/src/tracker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,16 @@ export interface BrowserTracker {
configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback
) => void;

/**
* Disables page activity tracking.
*/
disableActivityTracking: () => void;

/**
* Disables page activity tracking callback.
*/
disableActivityTrackingCallback: () => void;

/**
* Triggers the activityHandler manually to allow external user defined
* activity. i.e. While watching a video
Expand Down
8 changes: 8 additions & 0 deletions trackers/browser-tracker/docs/browser-tracker.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export interface BrowserTracker {
// Warning: (ae-forgotten-export) The symbol "TrackerCore" needs to be exported by the entry point index.module.d.ts
core: TrackerCore;
crossDomainLinker: (crossDomainLinkerCriterion: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean) => void;
disableActivityTracking: () => void;
disableActivityTrackingCallback: () => void;
disableAnonymousTracking: (configuration?: DisableAnonymousTrackingConfiguration) => void;
discardBrace: (enableFilter: boolean) => void;
discardHashTag: (enableFilter: boolean) => void;
Expand Down Expand Up @@ -172,6 +174,12 @@ export type CookieSameSite = "None" | "Lax" | "Strict";
// @public
export function crossDomainLinker(crossDomainLinkerCriterion: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean, trackers?: Array<string>): void;

// @public
export function disableActivityTracking(trackers?: Array<string>): void;

// @public
export function disableActivityTrackingCallback(trackers?: Array<string>): void;

// @public
export function disableAnonymousTracking(configuration?: DisableAnonymousTrackingConfiguration, trackers?: Array<string>): void;

Expand Down
22 changes: 22 additions & 0 deletions trackers/browser-tracker/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,28 @@ export function enableActivityTrackingCallback(
});
}

/**
* Disables page activity tracking.
*
* @param trackers - The tracker identifiers the activity tracking will be disabled.
*/
export function disableActivityTracking(trackers?: Array<string>) {
dispatchToTrackers(trackers, (t) => {
t.disableActivityTracking();
});
}

/**
* Disables page activity tracking callback.
*
* @param trackers - The tracker identifiers the activity tracking callback will be disabled.
*/
export function disableActivityTrackingCallback(trackers?: Array<string>) {
dispatchToTrackers(trackers, (t) => {
t.disableActivityTrackingCallback();
});
}

/**
* Triggers the activityHandler manually to allow external user defined activity. i.e. While watching a video
*
Expand Down
63 changes: 63 additions & 0 deletions trackers/browser-tracker/test/tracker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,67 @@ describe('Activity tracker behaviour', () => {
expect(firstPageId).toBe(extractPageId(pph));
expect(secondPageId).toBe(extractPageId(ppl));
});

it('disables activity tracking', () => {
const state = new SharedState();
const t =
addTracker('sp6', 'sp6', '', '', state, { stateStorageStrategy: 'cookie' }) ?? fail('Failed to create tracker');
t.enableActivityTracking({ minimumVisitLength: 5, heartbeatDelay: 5 });
t.trackPageView();

jest.advanceTimersByTime(100);
t.updatePageActivity();
jest.advanceTimersByTime(4900);

expect(F.size(getPPEvents(state.outQueues))).toBe(1);

// page ping timer starts tracking
jest.advanceTimersByTime(100);
t.updatePageActivity();
jest.advanceTimersByTime(4900);

expect(F.size(getPPEvents(state.outQueues))).toBe(2);

/* Disabling activity tracking and callback is expected to not allow more activity actions */
t.disableActivityTracking();
t.disableActivityTrackingCallback();

jest.advanceTimersByTime(100);
t.updatePageActivity();
jest.advanceTimersByTime(4900);

expect(F.size(getPPEvents(state.outQueues))).toBe(2);
});

it('disables activity tracking callback', () => {
let callbacks = 0;
const state = new SharedState();
const t =
addTracker('sp7', 'sp7', '', '', state, { stateStorageStrategy: 'cookie' }) ?? fail('Failed to create tracker');
t.enableActivityTrackingCallback({ minimumVisitLength: 5, heartbeatDelay: 5, callback: () => callbacks++ });
t.trackPageView();

jest.advanceTimersByTime(100);
t.updatePageActivity();
jest.advanceTimersByTime(4900);

expect(callbacks).toBe(1);

// page ping timer starts tracking
jest.advanceTimersByTime(100);
t.updatePageActivity();
jest.advanceTimersByTime(4900);

expect(callbacks).toBe(2);

/* Disabling activity tracking and callback is expected to not allow more activity actions */
t.disableActivityTracking();
t.disableActivityTrackingCallback();

jest.advanceTimersByTime(100);
t.updatePageActivity();
jest.advanceTimersByTime(4900);

expect(callbacks).toBe(2);
});
});
Loading