From bff17695e8cfe311bf373fbdac8703e40dc09bd2 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Tue, 16 Nov 2021 14:27:21 +0100 Subject: [PATCH 1/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20remove=20cookie=20cach?= =?UTF-8?q?e=20use=20from=20old=20cookie=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/domain/session/oldCookiesMigration.ts | 9 +++--- .../core/src/domain/session/sessionStore.ts | 31 ++++++++++++++----- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/packages/core/src/domain/session/oldCookiesMigration.ts b/packages/core/src/domain/session/oldCookiesMigration.ts index 32cfa0d4b0..84094eaf9a 100644 --- a/packages/core/src/domain/session/oldCookiesMigration.ts +++ b/packages/core/src/domain/session/oldCookiesMigration.ts @@ -1,5 +1,5 @@ -import { getCookie, CookieOptions, cacheCookieAccess } from '../../browser/cookie' -import { persistSession, SessionState, SESSION_COOKIE_NAME } from './sessionStore' +import { getCookie, CookieOptions } from '../../browser/cookie' +import { SessionState, SESSION_COOKIE_NAME, persistSession } from './sessionStore' export const OLD_SESSION_COOKIE_NAME = '_dd' export const OLD_RUM_COOKIE_NAME = '_dd_r' @@ -14,8 +14,7 @@ export const LOGS_SESSION_KEY = 'logs' * to allow older sdk versions to be upgraded to newer versions without compatibility issues. */ export function tryOldCookiesMigration(options: CookieOptions) { - const sessionCookie = cacheCookieAccess(SESSION_COOKIE_NAME, options) - const sessionString = sessionCookie.get() + const sessionString = getCookie(SESSION_COOKIE_NAME) const oldSessionId = getCookie(OLD_SESSION_COOKIE_NAME) const oldRumType = getCookie(OLD_RUM_COOKIE_NAME) const oldLogsType = getCookie(OLD_LOGS_COOKIE_NAME) @@ -30,6 +29,6 @@ export function tryOldCookiesMigration(options: CookieOptions) { if (oldRumType && /^[012]$/.test(oldRumType)) { session[RUM_SESSION_KEY] = oldRumType } - persistSession(session, sessionCookie) + persistSession(session, options) } } diff --git a/packages/core/src/domain/session/sessionStore.ts b/packages/core/src/domain/session/sessionStore.ts index 85a4eb033c..621363ced7 100644 --- a/packages/core/src/domain/session/sessionStore.ts +++ b/packages/core/src/domain/session/sessionStore.ts @@ -1,4 +1,4 @@ -import { CookieCache, CookieOptions, cacheCookieAccess, COOKIE_ACCESS_DELAY } from '../../browser/cookie' +import { CookieCache, CookieOptions, cacheCookieAccess, COOKIE_ACCESS_DELAY, setCookie } from '../../browser/cookie' import { Observable } from '../../tools/observable' import * as utils from '../../tools/utils' import { monitor } from '../internalMonitoring' @@ -45,7 +45,7 @@ export function startSessionStore( cookieSession.created = String(Date.now()) } // save changes and expand session duration - persistSession(cookieSession, sessionCookie) + persistSessionFromCache(cookieSession, sessionCookie) // If the session id has changed, notify that the session has been renewed if (isTracked && inMemorySession.id !== cookieSession.id) { @@ -60,7 +60,7 @@ export function startSessionStore( function expandSession() { sessionCookie.clearCache() const session = retrieveActiveSession(sessionCookie) - persistSession(session, sessionCookie) + persistSessionFromCache(session, sessionCookie) } function retrieveSession() { @@ -87,7 +87,7 @@ function retrieveActiveSession(sessionCookie: CookieCache): SessionState { if (isActiveSession(session)) { return session } - clearSession(sessionCookie) + clearSessionFromCache(sessionCookie) return {} } @@ -115,9 +115,9 @@ function retrieveSession(sessionCookie: CookieCache): SessionState { return session } -export function persistSession(session: SessionState, cookie: CookieCache) { +export function persistSessionFromCache(session: SessionState, cookie: CookieCache) { if (utils.isEmptyObject(session)) { - clearSession(cookie) + clearSessionFromCache(cookie) return } session.expire = String(Date.now() + SESSION_EXPIRATION_DELAY) @@ -128,6 +128,23 @@ export function persistSession(session: SessionState, cookie: CookieCache) { cookie.set(cookieString, SESSION_EXPIRATION_DELAY) } -function clearSession(cookie: CookieCache) { +export function persistSession(session: SessionState, options: CookieOptions) { + if (utils.isEmptyObject(session)) { + clearSession(options) + return + } + session.expire = String(Date.now() + SESSION_EXPIRATION_DELAY) + const cookieString = utils + .objectEntries(session) + .map(([key, value]) => `${key}=${value as string}`) + .join(SESSION_ENTRY_SEPARATOR) + setCookie(SESSION_COOKIE_NAME, cookieString, SESSION_EXPIRATION_DELAY, options) +} + +function clearSessionFromCache(cookie: CookieCache) { cookie.set('', 0) } + +function clearSession(options: CookieOptions) { + setCookie(SESSION_COOKIE_NAME, '', 0, options) +} From 6bb5c96e2a63c5bd4be7e696572d0d09f3810c52 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Tue, 16 Nov 2021 14:35:42 +0100 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=94=A5=EF=B8=8F=20remove=20cookie=20c?= =?UTF-8?q?ache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/browser/cookie.ts | 40 ----------------- .../domain/session/sessionManagement.spec.ts | 44 +------------------ .../core/src/domain/session/sessionStore.ts | 44 +++++-------------- 3 files changed, 13 insertions(+), 115 deletions(-) diff --git a/packages/core/src/browser/cookie.ts b/packages/core/src/browser/cookie.ts index fe8cba2d65..b087390a6c 100644 --- a/packages/core/src/browser/cookie.ts +++ b/packages/core/src/browser/cookie.ts @@ -9,46 +9,6 @@ export interface CookieOptions { domain?: string } -export interface CookieCache { - get: () => string | undefined - set: (value: string, expireDelay: number) => void - clearCache: () => void -} - -export function cacheCookieAccess(name: string, options: CookieOptions): CookieCache { - let timeout: number - let cache: string | undefined - let hasCache = false - - const cacheAccess = () => { - hasCache = true - clearTimeout(timeout) - timeout = setTimeout(() => { - hasCache = false - }, COOKIE_ACCESS_DELAY) - } - - return { - get: () => { - if (hasCache) { - return cache - } - cache = getCookie(name) - cacheAccess() - return cache - }, - set: (value: string, expireDelay: number) => { - setCookie(name, value, expireDelay, options) - cache = value - cacheAccess() - }, - clearCache: () => { - clearTimeout(timeout) - hasCache = false - }, - } -} - export function setCookie(name: string, value: string, expireDelay: number, options?: CookieOptions) { const date = new Date() date.setTime(date.getTime() + expireDelay) diff --git a/packages/core/src/domain/session/sessionManagement.spec.ts b/packages/core/src/domain/session/sessionManagement.spec.ts index 9c60ab57c1..ddadff2421 100644 --- a/packages/core/src/domain/session/sessionManagement.spec.ts +++ b/packages/core/src/domain/session/sessionManagement.spec.ts @@ -1,52 +1,10 @@ -import { - cacheCookieAccess, - COOKIE_ACCESS_DELAY, - CookieCache, - CookieOptions, - getCookie, - setCookie, -} from '../../browser/cookie' +import { COOKIE_ACCESS_DELAY, CookieOptions, getCookie, setCookie } from '../../browser/cookie' import { Clock, mockClock, restorePageVisibility, setPageVisibility, createNewEvent } from '../../../test/specHelper' import { ONE_HOUR, DOM_EVENT } from '../../tools/utils' import { isIE } from '../../tools/browserDetection' import { Session, startSessionManagement, stopSessionManagement, VISIBILITY_CHECK_DELAY } from './sessionManagement' import { SESSION_COOKIE_NAME, SESSION_TIME_OUT_DELAY, SESSION_EXPIRATION_DELAY } from './sessionStore' -describe('cacheCookieAccess', () => { - const TEST_COOKIE = 'test' - const TEST_DELAY = 1000 - const options: CookieOptions = {} - const DURATION = 123456 - let cookieCache: CookieCache - let clock: Clock - - beforeEach(() => { - clock = mockClock() - cookieCache = cacheCookieAccess(TEST_COOKIE, options) - }) - - afterEach(() => clock.cleanup()) - - it('should keep cookie value in cache', () => { - setCookie(TEST_COOKIE, 'foo', DURATION) - expect(cookieCache.get()).toEqual('foo') - - setCookie(TEST_COOKIE, '', DURATION) - expect(cookieCache.get()).toEqual('foo') - - clock.tick(TEST_DELAY) - expect(cookieCache.get()).toBeUndefined() - }) - - it('should invalidate cache when updating the cookie', () => { - setCookie(TEST_COOKIE, 'foo', DURATION) - expect(cookieCache.get()).toEqual('foo') - - cookieCache.set('bar', DURATION) - expect(cookieCache.get()).toEqual('bar') - }) -}) - enum FakeTrackingType { NOT_TRACKED = 'not-tracked', TRACKED = 'tracked', diff --git a/packages/core/src/domain/session/sessionStore.ts b/packages/core/src/domain/session/sessionStore.ts index 621363ced7..4cb5d74e1f 100644 --- a/packages/core/src/domain/session/sessionStore.ts +++ b/packages/core/src/domain/session/sessionStore.ts @@ -1,4 +1,4 @@ -import { CookieCache, CookieOptions, cacheCookieAccess, COOKIE_ACCESS_DELAY, setCookie } from '../../browser/cookie' +import { CookieOptions, COOKIE_ACCESS_DELAY, setCookie, getCookie } from '../../browser/cookie' import { Observable } from '../../tools/observable' import * as utils from '../../tools/utils' import { monitor } from '../internalMonitoring' @@ -31,13 +31,11 @@ export function startSessionStore( computeSessionState: (rawTrackingType?: string) => { trackingType: TrackingType; isTracked: boolean } ): SessionStore { const renewObservable = new Observable() - const sessionCookie = cacheCookieAccess(SESSION_COOKIE_NAME, options) - let inMemorySession = retrieveActiveSession(sessionCookie) + let inMemorySession = retrieveActiveSession(options) const { throttled: expandOrRenewSession } = utils.throttle( monitor(() => { - sessionCookie.clearCache() - const cookieSession = retrieveActiveSession(sessionCookie) + const cookieSession = retrieveActiveSession(options) const { trackingType, isTracked } = computeSessionState(cookieSession[productKey]) cookieSession[productKey] = trackingType if (isTracked && !cookieSession.id) { @@ -45,7 +43,7 @@ export function startSessionStore( cookieSession.created = String(Date.now()) } // save changes and expand session duration - persistSessionFromCache(cookieSession, sessionCookie) + persistSession(cookieSession, options) // If the session id has changed, notify that the session has been renewed if (isTracked && inMemorySession.id !== cookieSession.id) { @@ -58,13 +56,12 @@ export function startSessionStore( ) function expandSession() { - sessionCookie.clearCache() - const session = retrieveActiveSession(sessionCookie) - persistSessionFromCache(session, sessionCookie) + const session = retrieveActiveSession(options) + persistSession(session, options) } function retrieveSession() { - return retrieveActiveSession(sessionCookie) + return retrieveActiveSession(options) } return { @@ -82,12 +79,12 @@ function isValidSessionString(sessionString: string | undefined): sessionString ) } -function retrieveActiveSession(sessionCookie: CookieCache): SessionState { - const session = retrieveSession(sessionCookie) +function retrieveActiveSession(options: CookieOptions): SessionState { + const session = retrieveSession() if (isActiveSession(session)) { return session } - clearSessionFromCache(sessionCookie) + clearSession(options) return {} } @@ -100,8 +97,8 @@ function isActiveSession(session: SessionState) { ) } -function retrieveSession(sessionCookie: CookieCache): SessionState { - const sessionString = sessionCookie.get() +function retrieveSession(): SessionState { + const sessionString = getCookie(SESSION_COOKIE_NAME) const session: SessionState = {} if (isValidSessionString(sessionString)) { sessionString.split(SESSION_ENTRY_SEPARATOR).forEach((entry) => { @@ -115,19 +112,6 @@ function retrieveSession(sessionCookie: CookieCache): SessionState { return session } -export function persistSessionFromCache(session: SessionState, cookie: CookieCache) { - if (utils.isEmptyObject(session)) { - clearSessionFromCache(cookie) - return - } - session.expire = String(Date.now() + SESSION_EXPIRATION_DELAY) - const cookieString = utils - .objectEntries(session) - .map(([key, value]) => `${key}=${value as string}`) - .join(SESSION_ENTRY_SEPARATOR) - cookie.set(cookieString, SESSION_EXPIRATION_DELAY) -} - export function persistSession(session: SessionState, options: CookieOptions) { if (utils.isEmptyObject(session)) { clearSession(options) @@ -141,10 +125,6 @@ export function persistSession(session: SessionState, options: CookieOptions) { setCookie(SESSION_COOKIE_NAME, cookieString, SESSION_EXPIRATION_DELAY, options) } -function clearSessionFromCache(cookie: CookieCache) { - cookie.set('', 0) -} - function clearSession(options: CookieOptions) { setCookie(SESSION_COOKIE_NAME, '', 0, options) } From 2aa6388ca6b37e995afda780e13ed6b4bbe6b847 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Tue, 23 Nov 2021 15:01:26 +0100 Subject: [PATCH 3/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20expose=20session=20thr?= =?UTF-8?q?ough=20in=20memory=20cache=20and=20sync=20regularly=20with=20st?= =?UTF-8?q?ore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit behavior changes: - we consider the session expired when the store session is different from the cache session - we don't expand a session that has not been active on the current tab --- .../src/domain/session/sessionManagement.ts | 1 + .../src/domain/session/sessionStore.spec.ts | 275 ++++++++++++++++++ .../core/src/domain/session/sessionStore.ts | 38 ++- 3 files changed, 306 insertions(+), 8 deletions(-) create mode 100644 packages/core/src/domain/session/sessionStore.spec.ts diff --git a/packages/core/src/domain/session/sessionManagement.ts b/packages/core/src/domain/session/sessionManagement.ts index 0076701502..71ae31bcf4 100644 --- a/packages/core/src/domain/session/sessionManagement.ts +++ b/packages/core/src/domain/session/sessionManagement.ts @@ -20,6 +20,7 @@ export function startSessionManagement( ): Session { tryOldCookiesMigration(options) const sessionStore = startSessionStore(options, productKey, computeSessionState) + stopCallbacks.push(() => sessionStore.stop()) sessionStore.expandOrRenewSession() trackActivity(() => sessionStore.expandOrRenewSession()) diff --git a/packages/core/src/domain/session/sessionStore.spec.ts b/packages/core/src/domain/session/sessionStore.spec.ts new file mode 100644 index 0000000000..9639277d0b --- /dev/null +++ b/packages/core/src/domain/session/sessionStore.spec.ts @@ -0,0 +1,275 @@ +import { Clock, mockClock } from '../../../test/specHelper' +import { CookieOptions, getCookie, setCookie, COOKIE_ACCESS_DELAY } from '../../browser/cookie' +import { startSessionStore, SESSION_COOKIE_NAME, SessionStore } from './sessionStore' + +enum FakeTrackingType { + TRACKED = 'tracked', + NOT_TRACKED = 'not-tracked', +} + +const DURATION = 123456 +const PRODUCT_KEY = 'product' +const FIRST_ID = 'first' +const SECOND_ID = 'second' +const COOKIE_OPTIONS: CookieOptions = {} + +function setSessionInStore(id: string, trackingType: FakeTrackingType = FakeTrackingType.TRACKED) { + setCookie(SESSION_COOKIE_NAME, `id=${id}&${PRODUCT_KEY}=${trackingType}`, DURATION) +} + +function expectSessionToBeInStore(id?: string) { + expect(getCookie(SESSION_COOKIE_NAME)).toMatch(new RegExp(`id=${id ? id : '[a-f0-9-]+'}`)) +} + +function expectNotTrackedSessionToBeInStore() { + expect(getCookie(SESSION_COOKIE_NAME)).not.toContain(`id=`) + expect(getCookie(SESSION_COOKIE_NAME)).toContain(`${PRODUCT_KEY}=${FakeTrackingType.NOT_TRACKED}`) +} + +function resetSessionInStore() { + setCookie(SESSION_COOKIE_NAME, '', DURATION) +} + +describe('session store', () => { + let renewSpy: () => void + let sessionStore: SessionStore + let clock: Clock + + function setupSessionStore( + computeSessionState: (rawTrackingType?: string) => { trackingType: FakeTrackingType; isTracked: boolean } = () => ({ + isTracked: true, + trackingType: FakeTrackingType.TRACKED, + }) + ) { + sessionStore = startSessionStore(COOKIE_OPTIONS, PRODUCT_KEY, computeSessionState) + sessionStore.renewObservable.subscribe(renewSpy) + } + + beforeEach(() => { + renewSpy = jasmine.createSpy('renew session') + clock = mockClock() + }) + + afterEach(() => { + resetSessionInStore() + clock.cleanup() + sessionStore.stop() + }) + + describe('expand or renew session', () => { + it( + 'when session not in cache, session not in store and new session tracked, ' + + 'should create new session and trigger renew session ', + () => { + setupSessionStore() + + sessionStore.expandOrRenewSession() + + expect(sessionStore.retrieveSession().id).toBeDefined() + expectSessionToBeInStore() + expect(renewSpy).toHaveBeenCalled() + } + ) + + it( + 'when session not in cache, session not in store and new session not tracked, ' + + 'should store not tracked session', + () => { + setupSessionStore(() => ({ isTracked: false, trackingType: FakeTrackingType.NOT_TRACKED })) + + sessionStore.expandOrRenewSession() + + expect(sessionStore.retrieveSession().id).toBeUndefined() + expectNotTrackedSessionToBeInStore() + expect(renewSpy).not.toHaveBeenCalled() + } + ) + + it('when session not in cache and session in store, should expand session and trigger renew session', () => { + setupSessionStore() + setSessionInStore(FIRST_ID) + + sessionStore.expandOrRenewSession() + + expect(sessionStore.retrieveSession().id).toBe(FIRST_ID) + expectSessionToBeInStore(FIRST_ID) + expect(renewSpy).toHaveBeenCalled() + }) + + it( + 'when session in cache, session not in store and new session tracked, ' + + 'should expire session, create a new one and trigger renew session', + () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + resetSessionInStore() + + sessionStore.expandOrRenewSession() + + const sessionId = sessionStore.retrieveSession().id + expect(sessionId).toBeDefined() + expect(sessionId).not.toBe(FIRST_ID) + expectSessionToBeInStore(sessionId) + expect(renewSpy).toHaveBeenCalled() + } + ) + + it( + 'when session in cache, session not in store and new session not tracked, ' + 'should store not tracked session', + () => { + setSessionInStore(FIRST_ID) + setupSessionStore(() => ({ isTracked: false, trackingType: FakeTrackingType.NOT_TRACKED })) + resetSessionInStore() + + sessionStore.expandOrRenewSession() + + expect(sessionStore.retrieveSession().id).toBeUndefined() + expectNotTrackedSessionToBeInStore() + expect(renewSpy).not.toHaveBeenCalled() + } + ) + + it('when session in cache is same session than in store, should expand session', () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + + sessionStore.expandOrRenewSession() + + expect(sessionStore.retrieveSession().id).toBe(FIRST_ID) + expectSessionToBeInStore(FIRST_ID) + expect(renewSpy).not.toHaveBeenCalled() + }) + + it( + 'when session in cache is different session than in store and store session is tracked, ' + + 'should expire session, expand store session and trigger renew', + () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + setSessionInStore(SECOND_ID) + + sessionStore.expandOrRenewSession() + + expect(sessionStore.retrieveSession().id).toBe(SECOND_ID) + expectSessionToBeInStore(SECOND_ID) + expect(renewSpy).toHaveBeenCalled() + } + ) + + it( + 'when session in cache is different session than in store and store session is not tracked, ' + + 'should expire session, expand store session and trigger renew', + () => { + setSessionInStore(FIRST_ID) + setupSessionStore((rawTrackingType) => ({ + isTracked: rawTrackingType === FakeTrackingType.TRACKED, + trackingType: rawTrackingType as FakeTrackingType, + })) + setSessionInStore('', FakeTrackingType.NOT_TRACKED) + + sessionStore.expandOrRenewSession() + + expect(sessionStore.retrieveSession().id).toBeUndefined() + expectNotTrackedSessionToBeInStore() + expect(renewSpy).not.toHaveBeenCalled() + } + ) + }) + + describe('expand session', () => { + it('when session not in cache and session not in store, should do nothing', () => { + setupSessionStore() + + sessionStore.expandSession() + + expect(sessionStore.retrieveSession().id).toBeUndefined() + }) + + it('when session not in cache and session in store, should do nothing', () => { + setupSessionStore() + setSessionInStore(FIRST_ID) + + sessionStore.expandSession() + + expect(sessionStore.retrieveSession().id).toBeUndefined() + }) + + it('when session in cache and session not in store, should clear session cache', () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + resetSessionInStore() + + sessionStore.expandSession() + + expect(sessionStore.retrieveSession().id).toBeUndefined() + }) + + it('when session in cache is same session than in store, should expand session', () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + + sessionStore.expandSession() + + expect(sessionStore.retrieveSession().id).toBe(FIRST_ID) + }) + + it('when session in cache is different session than in store, should clear session cache', () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + setSessionInStore(SECOND_ID) + + sessionStore.expandSession() + + expect(sessionStore.retrieveSession().id).toBeUndefined() + expectSessionToBeInStore(SECOND_ID) + }) + }) + + describe('regular watch', () => { + it('when session not in cache and session not in store, should do nothing', () => { + setupSessionStore() + + clock.tick(COOKIE_ACCESS_DELAY) + + expect(sessionStore.retrieveSession().id).toBeUndefined() + }) + + it('when session not in cache and session in store, should do nothing', () => { + setupSessionStore() + setSessionInStore(FIRST_ID) + + clock.tick(COOKIE_ACCESS_DELAY) + + expect(sessionStore.retrieveSession().id).toBeUndefined() + }) + + it('when session in cache and session not in store, should clear session cache', () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + resetSessionInStore() + + clock.tick(COOKIE_ACCESS_DELAY) + + expect(sessionStore.retrieveSession().id).toBeUndefined() + }) + + it('when session in cache is same session than in store, should do nothing', () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + + clock.tick(COOKIE_ACCESS_DELAY) + + expect(sessionStore.retrieveSession().id).toBe(FIRST_ID) + }) + + it('when session in cache is different session than in store, should clear session cache', () => { + setSessionInStore(FIRST_ID) + setupSessionStore() + setSessionInStore(SECOND_ID) + + clock.tick(COOKIE_ACCESS_DELAY) + + expect(sessionStore.retrieveSession().id).toBeUndefined() + }) + }) +}) diff --git a/packages/core/src/domain/session/sessionStore.ts b/packages/core/src/domain/session/sessionStore.ts index 4cb5d74e1f..493563a3b4 100644 --- a/packages/core/src/domain/session/sessionStore.ts +++ b/packages/core/src/domain/session/sessionStore.ts @@ -8,6 +8,7 @@ export interface SessionStore { expandSession: () => void retrieveSession: () => SessionState renewObservable: Observable + stop: () => void } export interface SessionState { @@ -31,7 +32,7 @@ export function startSessionStore( computeSessionState: (rawTrackingType?: string) => { trackingType: TrackingType; isTracked: boolean } ): SessionStore { const renewObservable = new Observable() - let inMemorySession = retrieveActiveSession(options) + let sessionCache: SessionState = retrieveActiveSession(options) const { throttled: expandOrRenewSession } = utils.throttle( monitor(() => { @@ -45,23 +46,41 @@ export function startSessionStore( // save changes and expand session duration persistSession(cookieSession, options) - // If the session id has changed, notify that the session has been renewed - if (isTracked && inMemorySession.id !== cookieSession.id) { - inMemorySession = { ...cookieSession } + if (isTracked && sessionCacheOutdated(cookieSession)) { + sessionCache = { ...cookieSession } renewObservable.notify() } - inMemorySession = { ...cookieSession } + sessionCache = { ...cookieSession } }), COOKIE_ACCESS_DELAY ) + const cookieWatch = setInterval(() => { + const cookieSession = retrieveActiveSession(options) + if (sessionCacheOutdated(cookieSession)) { + clearSessionCache() + } + }, COOKIE_ACCESS_DELAY) + function expandSession() { - const session = retrieveActiveSession(options) - persistSession(session, options) + const cookieSession = retrieveActiveSession(options) + if (sessionCacheOutdated(cookieSession)) { + clearSessionCache() + } else { + persistSession(cookieSession, options) + } + } + + function sessionCacheOutdated(cookieSession: SessionState) { + return sessionCache.id !== cookieSession.id + } + + function clearSessionCache() { + sessionCache = {} } function retrieveSession() { - return retrieveActiveSession(options) + return sessionCache } return { @@ -69,6 +88,9 @@ export function startSessionStore( expandSession, retrieveSession, renewObservable, + stop: () => { + clearInterval(cookieWatch) + }, } } From 1597e85b5a974e51a453852b0ee9c988afec6f38 Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Thu, 25 Nov 2021 16:24:29 +0100 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=91=8C=20sessionCacheOutdated=20->=20?= =?UTF-8?q?isSessionCacheOutdated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/domain/session/sessionStore.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/domain/session/sessionStore.ts b/packages/core/src/domain/session/sessionStore.ts index 493563a3b4..81ec9f9dd2 100644 --- a/packages/core/src/domain/session/sessionStore.ts +++ b/packages/core/src/domain/session/sessionStore.ts @@ -46,7 +46,7 @@ export function startSessionStore( // save changes and expand session duration persistSession(cookieSession, options) - if (isTracked && sessionCacheOutdated(cookieSession)) { + if (isTracked && isSessionCacheOutdated(cookieSession)) { sessionCache = { ...cookieSession } renewObservable.notify() } @@ -57,21 +57,21 @@ export function startSessionStore( const cookieWatch = setInterval(() => { const cookieSession = retrieveActiveSession(options) - if (sessionCacheOutdated(cookieSession)) { + if (isSessionCacheOutdated(cookieSession)) { clearSessionCache() } }, COOKIE_ACCESS_DELAY) function expandSession() { const cookieSession = retrieveActiveSession(options) - if (sessionCacheOutdated(cookieSession)) { + if (isSessionCacheOutdated(cookieSession)) { clearSessionCache() } else { persistSession(cookieSession, options) } } - function sessionCacheOutdated(cookieSession: SessionState) { + function isSessionCacheOutdated(cookieSession: SessionState) { return sessionCache.id !== cookieSession.id } From f353f77288e79e523fda7a704af846477886861d Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Thu, 25 Nov 2021 16:27:41 +0100 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=91=8C=20retrieveSession=20->=20getSe?= =?UTF-8?q?ssion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/domain/session/sessionManagement.ts | 4 +-- .../src/domain/session/sessionStore.spec.ts | 36 +++++++++---------- .../core/src/domain/session/sessionStore.ts | 8 ++--- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/packages/core/src/domain/session/sessionManagement.ts b/packages/core/src/domain/session/sessionManagement.ts index 71ae31bcf4..8146b62c7b 100644 --- a/packages/core/src/domain/session/sessionManagement.ts +++ b/packages/core/src/domain/session/sessionManagement.ts @@ -27,8 +27,8 @@ export function startSessionManagement( trackVisibility(() => sessionStore.expandSession()) return { - getId: () => sessionStore.retrieveSession().id, - getTrackingType: () => sessionStore.retrieveSession()[productKey] as TrackingType | undefined, + getId: () => sessionStore.getSession().id, + getTrackingType: () => sessionStore.getSession()[productKey] as TrackingType | undefined, renewObservable: sessionStore.renewObservable, } } diff --git a/packages/core/src/domain/session/sessionStore.spec.ts b/packages/core/src/domain/session/sessionStore.spec.ts index 9639277d0b..0cf3ae2d5a 100644 --- a/packages/core/src/domain/session/sessionStore.spec.ts +++ b/packages/core/src/domain/session/sessionStore.spec.ts @@ -65,7 +65,7 @@ describe('session store', () => { sessionStore.expandOrRenewSession() - expect(sessionStore.retrieveSession().id).toBeDefined() + expect(sessionStore.getSession().id).toBeDefined() expectSessionToBeInStore() expect(renewSpy).toHaveBeenCalled() } @@ -79,7 +79,7 @@ describe('session store', () => { sessionStore.expandOrRenewSession() - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() expectNotTrackedSessionToBeInStore() expect(renewSpy).not.toHaveBeenCalled() } @@ -91,7 +91,7 @@ describe('session store', () => { sessionStore.expandOrRenewSession() - expect(sessionStore.retrieveSession().id).toBe(FIRST_ID) + expect(sessionStore.getSession().id).toBe(FIRST_ID) expectSessionToBeInStore(FIRST_ID) expect(renewSpy).toHaveBeenCalled() }) @@ -106,7 +106,7 @@ describe('session store', () => { sessionStore.expandOrRenewSession() - const sessionId = sessionStore.retrieveSession().id + const sessionId = sessionStore.getSession().id expect(sessionId).toBeDefined() expect(sessionId).not.toBe(FIRST_ID) expectSessionToBeInStore(sessionId) @@ -123,7 +123,7 @@ describe('session store', () => { sessionStore.expandOrRenewSession() - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() expectNotTrackedSessionToBeInStore() expect(renewSpy).not.toHaveBeenCalled() } @@ -135,7 +135,7 @@ describe('session store', () => { sessionStore.expandOrRenewSession() - expect(sessionStore.retrieveSession().id).toBe(FIRST_ID) + expect(sessionStore.getSession().id).toBe(FIRST_ID) expectSessionToBeInStore(FIRST_ID) expect(renewSpy).not.toHaveBeenCalled() }) @@ -150,7 +150,7 @@ describe('session store', () => { sessionStore.expandOrRenewSession() - expect(sessionStore.retrieveSession().id).toBe(SECOND_ID) + expect(sessionStore.getSession().id).toBe(SECOND_ID) expectSessionToBeInStore(SECOND_ID) expect(renewSpy).toHaveBeenCalled() } @@ -169,7 +169,7 @@ describe('session store', () => { sessionStore.expandOrRenewSession() - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() expectNotTrackedSessionToBeInStore() expect(renewSpy).not.toHaveBeenCalled() } @@ -182,7 +182,7 @@ describe('session store', () => { sessionStore.expandSession() - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() }) it('when session not in cache and session in store, should do nothing', () => { @@ -191,7 +191,7 @@ describe('session store', () => { sessionStore.expandSession() - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() }) it('when session in cache and session not in store, should clear session cache', () => { @@ -201,7 +201,7 @@ describe('session store', () => { sessionStore.expandSession() - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() }) it('when session in cache is same session than in store, should expand session', () => { @@ -210,7 +210,7 @@ describe('session store', () => { sessionStore.expandSession() - expect(sessionStore.retrieveSession().id).toBe(FIRST_ID) + expect(sessionStore.getSession().id).toBe(FIRST_ID) }) it('when session in cache is different session than in store, should clear session cache', () => { @@ -220,7 +220,7 @@ describe('session store', () => { sessionStore.expandSession() - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() expectSessionToBeInStore(SECOND_ID) }) }) @@ -231,7 +231,7 @@ describe('session store', () => { clock.tick(COOKIE_ACCESS_DELAY) - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() }) it('when session not in cache and session in store, should do nothing', () => { @@ -240,7 +240,7 @@ describe('session store', () => { clock.tick(COOKIE_ACCESS_DELAY) - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() }) it('when session in cache and session not in store, should clear session cache', () => { @@ -250,7 +250,7 @@ describe('session store', () => { clock.tick(COOKIE_ACCESS_DELAY) - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() }) it('when session in cache is same session than in store, should do nothing', () => { @@ -259,7 +259,7 @@ describe('session store', () => { clock.tick(COOKIE_ACCESS_DELAY) - expect(sessionStore.retrieveSession().id).toBe(FIRST_ID) + expect(sessionStore.getSession().id).toBe(FIRST_ID) }) it('when session in cache is different session than in store, should clear session cache', () => { @@ -269,7 +269,7 @@ describe('session store', () => { clock.tick(COOKIE_ACCESS_DELAY) - expect(sessionStore.retrieveSession().id).toBeUndefined() + expect(sessionStore.getSession().id).toBeUndefined() }) }) }) diff --git a/packages/core/src/domain/session/sessionStore.ts b/packages/core/src/domain/session/sessionStore.ts index 81ec9f9dd2..b3a4993551 100644 --- a/packages/core/src/domain/session/sessionStore.ts +++ b/packages/core/src/domain/session/sessionStore.ts @@ -6,7 +6,7 @@ import { monitor } from '../internalMonitoring' export interface SessionStore { expandOrRenewSession: () => void expandSession: () => void - retrieveSession: () => SessionState + getSession: () => SessionState renewObservable: Observable stop: () => void } @@ -79,14 +79,10 @@ export function startSessionStore( sessionCache = {} } - function retrieveSession() { - return sessionCache - } - return { expandOrRenewSession, expandSession, - retrieveSession, + getSession: () => sessionCache, renewObservable, stop: () => { clearInterval(cookieWatch) From f41458d00e29f69f4db010c5d86980f6e6265c7d Mon Sep 17 00:00:00 2001 From: Bastien Caudan Date: Thu, 25 Nov 2021 16:28:36 +0100 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=91=8C=20make=20stopCallbacks=20more?= =?UTF-8?q?=20visible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/domain/session/sessionManagement.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/domain/session/sessionManagement.ts b/packages/core/src/domain/session/sessionManagement.ts index 8146b62c7b..cf076c93b9 100644 --- a/packages/core/src/domain/session/sessionManagement.ts +++ b/packages/core/src/domain/session/sessionManagement.ts @@ -13,6 +13,8 @@ export interface Session { getTrackingType: () => T | undefined } +let stopCallbacks: Array<() => void> = [] + export function startSessionManagement( options: CookieOptions, productKey: string, @@ -38,8 +40,6 @@ export function stopSessionManagement() { stopCallbacks = [] } -let stopCallbacks: Array<() => void> = [] - function trackActivity(expandOrRenewSession: () => void) { const { stop } = utils.addEventListeners( window,