Skip to content
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": "Add client_session context entity if anonymous tracking with session tracking is enabled (#1124)",
"type": "none"
}
],
"packageName": "@snowplow/browser-tracker-core"
}
12 changes: 9 additions & 3 deletions libraries/browser-tracker-core/src/tracker/id_cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,20 @@ export function serializeIdCookie(idCookie: ParsedIdCookie) {
* @param configStateStorageStrategy Cookie storage strategy
* @returns Client session context entity
*/
export function clientSessionFromIdCookie(idCookie: ParsedIdCookie, configStateStorageStrategy: string) {
export function clientSessionFromIdCookie(
idCookie: ParsedIdCookie,
configStateStorageStrategy: string,
configAnonymousTracking: boolean
) {
const firstEventTsInMs = idCookie[firstEventTsInMsIndex];
const clientSession: ClientSession = {
userId: idCookie[domainUserIdIndex],
userId: configAnonymousTracking
? '00000000-0000-0000-0000-000000000000' // TODO: use uuid.NIL when we upgrade to uuid v8.3
: idCookie[domainUserIdIndex],
sessionId: idCookie[sessionIdIndex],
eventIndex: idCookie[eventIndexIndex],
sessionIndex: idCookie[visitCountIndex],
previousSessionId: idCookie[previousSessionIdIndex] || null,
previousSessionId: configAnonymousTracking ? null : idCookie[previousSessionIdIndex] || null,
storageMechanism: configStateStorageStrategy == 'localStorage' ? 'LOCAL_STORAGE' : 'COOKIE_1',
firstEventId: idCookie[firstEventIdIndex] || null,
firstEventTimestamp: firstEventTsInMs ? new Date(firstEventTsInMs).toISOString() : null,
Expand Down
7 changes: 5 additions & 2 deletions libraries/browser-tracker-core/src/tracker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,8 +787,11 @@ export function Tracker(
// Add the page URL last as it may take us over the IE limit (and we don't always need it)
payloadBuilder.add('url', purify(configCustomUrl || locationHrefAlias));

if (configSessionContext && !configAnonymousSessionTracking && !configAnonymousTracking) {
addSessionContextToPayload(payloadBuilder, clientSessionFromIdCookie(idCookie, configStateStorageStrategy));
if (configSessionContext && (!configAnonymousTracking || configAnonymousSessionTracking)) {
addSessionContextToPayload(
payloadBuilder,
clientSessionFromIdCookie(idCookie, configStateStorageStrategy, configAnonymousTracking)
);
}

// Update cookies
Expand Down
16 changes: 15 additions & 1 deletion libraries/browser-tracker-core/test/id_cookie.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ describe('serializeIdCookie', () => {
describe('clientSessionFromIdCookie', () => {
it('Correctly fills out the properties', () => {
let idCookie = parseIdCookie('def.1653632272.10.1653632282.1653632262.ses.previous.fid.1653638673483.9', '', '', 0);
let clientSession = clientSessionFromIdCookie(idCookie, 'cookieAndLocalStorage');
let clientSession = clientSessionFromIdCookie(idCookie, 'cookieAndLocalStorage', false);

expect(clientSession.userId).toBe('def');
expect(clientSession.sessionId).toBe('ses');
Expand All @@ -273,4 +273,18 @@ describe('clientSessionFromIdCookie', () => {
expect(clientSession.firstEventId).toBe('fid');
expect(clientSession.firstEventTimestamp).toBe('2022-05-27T08:04:33.483Z');
});

it('Anonymises userId and previousSessionId when anonymous tracking', () => {
let idCookie = parseIdCookie('def.1653632272.10.1653632282.1653632262.ses.previous.fid.1653638673483.9', '', '', 0);
let clientSession = clientSessionFromIdCookie(idCookie, 'cookieAndLocalStorage', true);

expect(clientSession.userId).toBe('00000000-0000-0000-0000-000000000000');
expect(clientSession.sessionId).toBe('ses');
expect(clientSession.previousSessionId).toBeNull;
expect(clientSession.eventIndex).toBe(9);
expect(clientSession.sessionIndex).toBe(10);
expect(clientSession.storageMechanism).toBe('COOKIE_1');
expect(clientSession.firstEventId).toBe('fid');
expect(clientSession.firstEventTimestamp).toBe('2022-05-27T08:04:33.483Z');
});
});
78 changes: 71 additions & 7 deletions libraries/browser-tracker-core/test/tracker/session_data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import { TrackerConfiguration } from '../../dist/index.module';
import { addTracker, SharedState } from '../../src';
import { createTestIdCookie, createTestSessionIdCookie } from '../helpers';

Expand All @@ -54,45 +55,45 @@ describe('Tracker API: ', () => {
});

it('Sets initial domain session index on first session', () => {
const tracker = addTracker('sp1', 'sp1', '', '', new SharedState());
const tracker = createTracker();

expect(tracker?.getDomainSessionIndex()).toEqual(1);
});

it('Sets correct domain session index on new session', () => {
const initialSessionIndex = 1;
document.cookie = createTestIdCookie({ visitCount: initialSessionIndex });
const tracker = addTracker('sp2', 'sp2', '', '', new SharedState());
const tracker = createTracker();

expect(tracker?.getDomainSessionIndex()).toEqual(initialSessionIndex + 1);
});

it('Sets correct domain session index on existing session', () => {
const initialSessionIndex = 2;
document.cookie = createTestIdCookie({ visitCount: initialSessionIndex }) + ' ' + createTestSessionIdCookie();
const tracker = addTracker('sp3', 'sp3', '', '', new SharedState());
const tracker = createTracker();

expect(tracker?.getDomainSessionIndex()).toEqual(initialSessionIndex);
});

it('Sets correct domain session index (1) after clearUserData() on existing session', () => {
const initialSessionIndex = 2;
document.cookie = createTestIdCookie({ visitCount: initialSessionIndex }) + ' ' + createTestSessionIdCookie();
const tracker = addTracker('sp4', 'sp4', '', '', new SharedState());
const tracker = createTracker();
expect(tracker?.getDomainSessionIndex()).toEqual(initialSessionIndex);

tracker?.clearUserData();
expect(tracker?.getDomainSessionIndex()).toEqual(1);
});

it('Sets correct domain session index anonymous track', () => {
const tracker = addTracker('sp5', 'sp5', '', '', new SharedState(), { anonymousTracking: true });
const tracker = createTracker({ anonymousTracking: true });
expect(tracker?.getDomainSessionIndex()).toEqual(1);
});

it('Retains correct domain session index on opt-out cookie present', () => {
const optOutCookieName = 'optOut';
const tracker = addTracker('sp6', 'sp6', '', '', new SharedState());
const tracker = createTracker();
tracker?.setOptOutCookie(optOutCookieName);
document.cookie = `${optOutCookieName}=1;`;

Expand All @@ -102,10 +103,73 @@ describe('Tracker API: ', () => {

it('Sets correct domain session index after session expiration', () => {
// Session timeout is in seconds
const tracker = addTracker('sp7', 'sp7', '', '', new SharedState(), { sessionCookieTimeout: 1 });
const tracker = createTracker({ sessionCookieTimeout: 1 });
// Advance timer by more than one second
jest.advanceTimersByTime(1001);
tracker?.trackPageView({ title: 'my page' });
expect(tracker?.getDomainSessionIndex()).toEqual(2);
});

it('Adds the client session context entity when enabled', (done) => {
const tracker = createTracker({
contexts: { session: true },
encodeBase64: false,
plugins: [
{
afterTrack: (payload) => {
let context = payload.co as string;
expect(context).toContain('client_session');
done();
},
},
],
});

tracker?.trackPageView();
});

it('Adds the client session context entity when anonymous session tracking', (done) => {
const tracker = createTracker({
contexts: { session: true },
encodeBase64: false,
anonymousTracking: { withSessionTracking: true },
plugins: [
{
afterTrack: (payload) => {
let context = payload.co as string;
expect(context).toContain('client_session');
Comment thread
matus-tomlein marked this conversation as resolved.
expect(context).toContain('"userId":"00000000-0000-0000-0000-000000000000"');
expect(context).toContain('"previousSessionId":null');
done();
},
},
],
});

tracker?.trackPageView();
});

it("Doesn't add the client session context entity when anonymous tracking without session tracking", (done) => {
const tracker = createTracker({
contexts: { session: true },
encodeBase64: false,
anonymousTracking: true,
plugins: [
{
afterTrack: (payload) => {
let context = payload.co as string;
expect(context).not.toContain('client_session');
done();
},
},
],
});

tracker?.trackPageView();
});
});

function createTracker(configuration?: TrackerConfiguration) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍

let id = 'sp-' + Math.random();
return addTracker(id, id, '', '', new SharedState(), configuration);
}