diff --git a/packages/core/src/domain/oldCookiesMigration.spec.ts b/packages/core/src/domain/session/oldCookiesMigration.spec.ts similarity index 78% rename from packages/core/src/domain/oldCookiesMigration.spec.ts rename to packages/core/src/domain/session/oldCookiesMigration.spec.ts index 3be6a83021..3525ae2bfb 100644 --- a/packages/core/src/domain/oldCookiesMigration.spec.ts +++ b/packages/core/src/domain/session/oldCookiesMigration.spec.ts @@ -1,11 +1,11 @@ -import { cacheCookieAccess, CookieOptions, getCookie, setCookie } from '../browser/cookie' +import { CookieOptions, getCookie, setCookie } from '../../browser/cookie' import { OLD_LOGS_COOKIE_NAME, OLD_RUM_COOKIE_NAME, OLD_SESSION_COOKIE_NAME, tryOldCookiesMigration, } from './oldCookiesMigration' -import { SESSION_COOKIE_NAME, SESSION_EXPIRATION_DELAY } from './sessionManagement' +import { SESSION_COOKIE_NAME, SESSION_EXPIRATION_DELAY } from './sessionStore' describe('old cookies migration', () => { const options: CookieOptions = {} @@ -13,7 +13,7 @@ describe('old cookies migration', () => { it('should not touch current cookie', () => { setCookie(SESSION_COOKIE_NAME, 'id=abcde&rum=0&logs=1', SESSION_EXPIRATION_DELAY) - tryOldCookiesMigration(cacheCookieAccess(SESSION_COOKIE_NAME, options)) + tryOldCookiesMigration(options) expect(getCookie(SESSION_COOKIE_NAME)).toBe('id=abcde&rum=0&logs=1') }) @@ -23,7 +23,7 @@ describe('old cookies migration', () => { setCookie(OLD_LOGS_COOKIE_NAME, '1', SESSION_EXPIRATION_DELAY) setCookie(OLD_RUM_COOKIE_NAME, '0', SESSION_EXPIRATION_DELAY) - tryOldCookiesMigration(cacheCookieAccess(SESSION_COOKIE_NAME, options)) + tryOldCookiesMigration(options) expect(getCookie(SESSION_COOKIE_NAME)).toContain('id=abcde') expect(getCookie(SESSION_COOKIE_NAME)).toContain('rum=0') @@ -33,7 +33,7 @@ describe('old cookies migration', () => { it('should create new cookie from a single old cookie', () => { setCookie(OLD_RUM_COOKIE_NAME, '0', SESSION_EXPIRATION_DELAY) - tryOldCookiesMigration(cacheCookieAccess(SESSION_COOKIE_NAME, options)) + tryOldCookiesMigration(options) expect(getCookie(SESSION_COOKIE_NAME)).not.toContain('id=') expect(getCookie(SESSION_COOKIE_NAME)).toContain('rum=0') diff --git a/packages/core/src/domain/oldCookiesMigration.ts b/packages/core/src/domain/session/oldCookiesMigration.ts similarity index 77% rename from packages/core/src/domain/oldCookiesMigration.ts rename to packages/core/src/domain/session/oldCookiesMigration.ts index a6e2792469..32cfa0d4b0 100644 --- a/packages/core/src/domain/oldCookiesMigration.ts +++ b/packages/core/src/domain/session/oldCookiesMigration.ts @@ -1,5 +1,5 @@ -import { CookieCache, getCookie } from '../browser/cookie' -import { persistSession, SessionState } from './sessionManagement' +import { getCookie, CookieOptions, cacheCookieAccess } from '../../browser/cookie' +import { persistSession, SessionState, SESSION_COOKIE_NAME } from './sessionStore' export const OLD_SESSION_COOKIE_NAME = '_dd' export const OLD_RUM_COOKIE_NAME = '_dd_r' @@ -13,7 +13,8 @@ export const LOGS_SESSION_KEY = 'logs' * This migration should remain in the codebase as long as older versions are available/live * to allow older sdk versions to be upgraded to newer versions without compatibility issues. */ -export function tryOldCookiesMigration(sessionCookie: CookieCache) { +export function tryOldCookiesMigration(options: CookieOptions) { + const sessionCookie = cacheCookieAccess(SESSION_COOKIE_NAME, options) const sessionString = sessionCookie.get() const oldSessionId = getCookie(OLD_SESSION_COOKIE_NAME) const oldRumType = getCookie(OLD_RUM_COOKIE_NAME) diff --git a/packages/core/src/domain/sessionManagement.spec.ts b/packages/core/src/domain/session/sessionManagement.spec.ts similarity index 97% rename from packages/core/src/domain/sessionManagement.spec.ts rename to packages/core/src/domain/session/sessionManagement.spec.ts index 09c0a46c77..9c60ab57c1 100644 --- a/packages/core/src/domain/sessionManagement.spec.ts +++ b/packages/core/src/domain/session/sessionManagement.spec.ts @@ -5,19 +5,12 @@ import { 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, - SESSION_COOKIE_NAME, - SESSION_EXPIRATION_DELAY, - SESSION_TIME_OUT_DELAY, - startSessionManagement, - stopSessionManagement, - VISIBILITY_CHECK_DELAY, -} from './sessionManagement' +} 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' diff --git a/packages/core/src/domain/session/sessionManagement.ts b/packages/core/src/domain/session/sessionManagement.ts new file mode 100644 index 0000000000..0076701502 --- /dev/null +++ b/packages/core/src/domain/session/sessionManagement.ts @@ -0,0 +1,66 @@ +import { CookieOptions } from '../../browser/cookie' +import { Observable } from '../../tools/observable' +import * as utils from '../../tools/utils' +import { monitor } from '../internalMonitoring' +import { tryOldCookiesMigration } from './oldCookiesMigration' +import { startSessionStore } from './sessionStore' + +export const VISIBILITY_CHECK_DELAY = utils.ONE_MINUTE + +export interface Session { + renewObservable: Observable + getId: () => string | undefined + getTrackingType: () => T | undefined +} + +export function startSessionManagement( + options: CookieOptions, + productKey: string, + computeSessionState: (rawTrackingType?: string) => { trackingType: TrackingType; isTracked: boolean } +): Session { + tryOldCookiesMigration(options) + const sessionStore = startSessionStore(options, productKey, computeSessionState) + + sessionStore.expandOrRenewSession() + trackActivity(() => sessionStore.expandOrRenewSession()) + trackVisibility(() => sessionStore.expandSession()) + + return { + getId: () => sessionStore.retrieveSession().id, + getTrackingType: () => sessionStore.retrieveSession()[productKey] as TrackingType | undefined, + renewObservable: sessionStore.renewObservable, + } +} + +export function stopSessionManagement() { + stopCallbacks.forEach((e) => e()) + stopCallbacks = [] +} + +let stopCallbacks: Array<() => void> = [] + +function trackActivity(expandOrRenewSession: () => void) { + const { stop } = utils.addEventListeners( + window, + [utils.DOM_EVENT.CLICK, utils.DOM_EVENT.TOUCH_START, utils.DOM_EVENT.KEY_DOWN, utils.DOM_EVENT.SCROLL], + expandOrRenewSession, + { capture: true, passive: true } + ) + stopCallbacks.push(stop) +} + +function trackVisibility(expandSession: () => void) { + const expandSessionWhenVisible = monitor(() => { + if (document.visibilityState === 'visible') { + expandSession() + } + }) + + const { stop } = utils.addEventListener(document, utils.DOM_EVENT.VISIBILITY_CHANGE, expandSessionWhenVisible) + stopCallbacks.push(stop) + + const visibilityCheckInterval = setInterval(expandSessionWhenVisible, VISIBILITY_CHECK_DELAY) + stopCallbacks.push(() => { + clearInterval(visibilityCheckInterval) + }) +} diff --git a/packages/core/src/domain/sessionManagement.ts b/packages/core/src/domain/session/sessionStore.ts similarity index 62% rename from packages/core/src/domain/sessionManagement.ts rename to packages/core/src/domain/session/sessionStore.ts index f37b39c0e7..85a4eb033c 100644 --- a/packages/core/src/domain/sessionManagement.ts +++ b/packages/core/src/domain/session/sessionStore.ts @@ -1,39 +1,37 @@ -import { cacheCookieAccess, COOKIE_ACCESS_DELAY, CookieCache, CookieOptions } from '../browser/cookie' -import { Observable } from '../tools/observable' -import * as utils from '../tools/utils' -import { monitor } from './internalMonitoring' -import { tryOldCookiesMigration } from './oldCookiesMigration' - -export const SESSION_COOKIE_NAME = '_dd_s' -export const SESSION_EXPIRATION_DELAY = 15 * utils.ONE_MINUTE -export const SESSION_TIME_OUT_DELAY = 4 * utils.ONE_HOUR -export const VISIBILITY_CHECK_DELAY = utils.ONE_MINUTE - -export interface Session { +import { CookieCache, CookieOptions, cacheCookieAccess, COOKIE_ACCESS_DELAY } from '../../browser/cookie' +import { Observable } from '../../tools/observable' +import * as utils from '../../tools/utils' +import { monitor } from '../internalMonitoring' + +export interface SessionStore { + expandOrRenewSession: () => void + expandSession: () => void + retrieveSession: () => SessionState renewObservable: Observable - getId: () => string | undefined - getTrackingType: () => T | undefined - getInMemoryTrackingType: () => T | undefined } export interface SessionState { id?: string created?: string expire?: string + [key: string]: string | undefined } -/** - * Limit access to cookie to avoid performance issues - */ -export function startSessionManagement( +export const SESSION_COOKIE_NAME = '_dd_s' +export const SESSION_EXPIRATION_DELAY = 15 * utils.ONE_MINUTE +export const SESSION_TIME_OUT_DELAY = 4 * utils.ONE_HOUR + +const SESSION_ENTRY_REGEXP = /^([a-z]+)=([a-z0-9-]+)$/ +const SESSION_ENTRY_SEPARATOR = '&' + +export function startSessionStore( options: CookieOptions, productKey: string, computeSessionState: (rawTrackingType?: string) => { trackingType: TrackingType; isTracked: boolean } -): Session { - const sessionCookie = cacheCookieAccess(SESSION_COOKIE_NAME, options) - tryOldCookiesMigration(sessionCookie) +): SessionStore { const renewObservable = new Observable() + const sessionCookie = cacheCookieAccess(SESSION_COOKIE_NAME, options) let inMemorySession = retrieveActiveSession(sessionCookie) const { throttled: expandOrRenewSession } = utils.throttle( @@ -59,29 +57,25 @@ export function startSessionManagement( COOKIE_ACCESS_DELAY ) - const expandSession = () => { + function expandSession() { sessionCookie.clearCache() const session = retrieveActiveSession(sessionCookie) persistSession(session, sessionCookie) } - expandOrRenewSession() - trackActivity(expandOrRenewSession) - trackVisibility(expandSession) + function retrieveSession() { + return retrieveActiveSession(sessionCookie) + } return { - getId: () => retrieveActiveSession(sessionCookie).id, - getTrackingType: () => retrieveActiveSession(sessionCookie)[productKey] as TrackingType | undefined, - getInMemoryTrackingType: () => inMemorySession[productKey] as TrackingType | undefined, + expandOrRenewSession, + expandSession, + retrieveSession, renewObservable, } } -const SESSION_ENTRY_REGEXP = /^([a-z]+)=([a-z0-9-]+)$/ - -const SESSION_ENTRY_SEPARATOR = '&' - -export function isValidSessionString(sessionString: string | undefined): sessionString is string { +function isValidSessionString(sessionString: string | undefined): sessionString is string { return ( sessionString !== undefined && (sessionString.indexOf(SESSION_ENTRY_SEPARATOR) !== -1 || SESSION_ENTRY_REGEXP.test(sessionString)) @@ -137,36 +131,3 @@ export function persistSession(session: SessionState, cookie: CookieCache) { function clearSession(cookie: CookieCache) { cookie.set('', 0) } - -export function stopSessionManagement() { - stopCallbacks.forEach((e) => e()) - stopCallbacks = [] -} - -let stopCallbacks: Array<() => void> = [] - -export function trackActivity(expandOrRenewSession: () => void) { - const { stop } = utils.addEventListeners( - window, - [utils.DOM_EVENT.CLICK, utils.DOM_EVENT.TOUCH_START, utils.DOM_EVENT.KEY_DOWN, utils.DOM_EVENT.SCROLL], - expandOrRenewSession, - { capture: true, passive: true } - ) - stopCallbacks.push(stop) -} - -function trackVisibility(expandSession: () => void) { - const expandSessionWhenVisible = monitor(() => { - if (document.visibilityState === 'visible') { - expandSession() - } - }) - - const { stop } = utils.addEventListener(document, utils.DOM_EVENT.VISIBILITY_CHANGE, expandSessionWhenVisible) - stopCallbacks.push(stop) - - const visibilityCheckInterval = setInterval(expandSessionWhenVisible, VISIBILITY_CHECK_DELAY) - stopCallbacks.push(() => { - clearInterval(visibilityCheckInterval) - }) -} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b9e2fe592c..24180d753c 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -31,11 +31,14 @@ export { Observable, Subscription } from './tools/observable' export { startSessionManagement, Session, + // Exposed for tests + stopSessionManagement, +} from './domain/session/sessionManagement' +export { SESSION_TIME_OUT_DELAY, // Exposed for tests SESSION_COOKIE_NAME, - stopSessionManagement, -} from './domain/sessionManagement' +} from './domain/session/sessionStore' export { HttpRequest, Batch, canUseEventBridge, getEventBridge } from './transport' export * from './tools/display' export * from './tools/urlPolyfill'