Skip to content

Commit

Permalink
Merge pull request #96 from PostHog/session-id-generation
Browse files Browse the repository at this point in the history
Send/create $session_id on $snapshot events
  • Loading branch information
macobo committed Oct 21, 2020
2 parents b261829 + 30cde30 commit 55d1b31
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 6 deletions.
43 changes: 43 additions & 0 deletions src/__tests__/extensions/sessionid.js
@@ -0,0 +1,43 @@
import sessionIdGenerator from '../../extensions/sessionid'
import { SESSION_ID } from '../../posthog-persistence'
import { _ } from '../../utils'

jest.mock('../../utils')

describe('Session ID generation', () => {
given('subject', () => sessionIdGenerator(given.persistence, given.timestamp))

given('timestamp', () => 1603107479471)

given('persistence', () => ({
props: { [SESSION_ID]: given.recordedData },
register: jest.fn(),
}))

beforeEach(() => {
_.UUID.mockReturnValue('newSessionId')
})

describe('no stored session data', () => {
it('generates a new session id, saves it', () => {
expect(given.subject).toEqual('newSessionId')
expect(given.persistence.register).toHaveBeenCalledWith({ [SESSION_ID]: [given.timestamp, 'newSessionId'] })
})
})

describe('stored session data', () => {
it('reuses old session data', () => {
given('recordedData', () => [1603107460000, 'oldSessionId'])

expect(given.subject).toEqual('oldSessionId')
expect(given.persistence.register).toHaveBeenCalledWith({ [SESSION_ID]: [given.timestamp, 'oldSessionId'] })
})

it('generates a new session id, saves it when too long since last event', () => {
given('recordedData', () => [1603007460000, 'oldSessionId'])

expect(given.subject).toEqual('newSessionId')
expect(given.persistence.register).toHaveBeenCalledWith({ [SESSION_ID]: [given.timestamp, 'newSessionId'] })
})
})
})
13 changes: 11 additions & 2 deletions src/__tests__/extensions/sessionrecording.js
@@ -1,8 +1,10 @@
import { loadScript } from '../../autocapture-utils'
import { SessionRecording } from '../../extensions/sessionrecording'
import { SESSION_RECORDING_ENABLED } from '../../posthog-persistence'
import sessionIdGenerator from '../../extensions/sessionid'

jest.mock('../../autocapture-utils')
jest.mock('../../extensions/sessionid')

describe('SessionRecording', () => {
let _emit
Expand Down Expand Up @@ -54,6 +56,7 @@ describe('SessionRecording', () => {
}

loadScript.mockImplementation((path, callback) => callback())
sessionIdGenerator.mockReturnValue('sid')
})

it('records events emitted before and after starting recording', () => {
Expand All @@ -67,8 +70,14 @@ describe('SessionRecording', () => {
_emit({ event: 2 })

expect(given.posthog.capture).toHaveBeenCalledTimes(2)
expect(given.posthog.capture).toHaveBeenCalledWith('$snapshot', { $snapshot_data: { event: 1 } })
expect(given.posthog.capture).toHaveBeenCalledWith('$snapshot', { $snapshot_data: { event: 2 } })
expect(given.posthog.capture).toHaveBeenCalledWith('$snapshot', {
$session_id: 'sid',
$snapshot_data: { event: 1 },
})
expect(given.posthog.capture).toHaveBeenCalledWith('$snapshot', {
$session_id: 'sid',
$snapshot_data: { event: 2 },
})
})

it('loads recording script from right place', () => {
Expand Down
15 changes: 15 additions & 0 deletions src/extensions/sessionid.js
@@ -0,0 +1,15 @@
import { SESSION_ID } from '../posthog-persistence'
import { _ } from '../utils'

const SESSION_CHANGE_THRESHOLD = 30 * 60 * 1000 // 30 mins

export default (persistence, timestamp) => {
let [lastTimestamp, sessionId] = persistence['props'][SESSION_ID] || [0, null]

if (Math.abs(timestamp - lastTimestamp) > SESSION_CHANGE_THRESHOLD) {
sessionId = _.UUID()
}

persistence.register({ [SESSION_ID]: [timestamp, sessionId] })
return sessionId
}
14 changes: 10 additions & 4 deletions src/extensions/sessionrecording.js
@@ -1,6 +1,7 @@
import { loadScript } from '../autocapture-utils'
import { _ } from '../utils'
import { SESSION_RECORDING_ENABLED } from '../posthog-persistence'
import sessionIdGenerator from './sessionid'

export class SessionRecording {
constructor(instance) {
Expand Down Expand Up @@ -29,8 +30,8 @@ export class SessionRecording {
submitRecordings() {
this.emit = true
this._startCapture()
this.snapshots.forEach((data) => {
this.instance.capture('$snapshot', { $snapshot_data: data })
this.snapshots.forEach((properties) => {
this.instance.capture('$snapshot', properties)
})
}

Expand All @@ -44,10 +45,15 @@ export class SessionRecording {
_onScriptLoaded() {
window.rrweb.record({
emit: (data) => {
const properties = {
$snapshot_data: data,
$session_id: sessionIdGenerator(this.instance.persistence, data.timestamp),
}

if (this.emit) {
this.instance.capture('$snapshot', { $snapshot_data: data })
this.instance.capture('$snapshot', properties)
} else {
this.snapshots.push(data)
this.snapshots.push(properties)
}
},
})
Expand Down
3 changes: 3 additions & 0 deletions src/posthog-persistence.js
Expand Up @@ -20,6 +20,7 @@ import { _, console } from './utils'
/** @const */ var CAMPAIGN_IDS_KEY = '__cmpns'
/** @const */ var EVENT_TIMERS_KEY = '__timers'
/** @const */ var SESSION_RECORDING_ENABLED = '$session_recording_enabled'
/** @const */ var SESSION_ID = '$sesid'
/** @const */ var RESERVED_PROPERTIES = [
SET_QUEUE_KEY,
SET_ONCE_QUEUE_KEY,
Expand All @@ -33,6 +34,7 @@ import { _, console } from './utils'
CAMPAIGN_IDS_KEY,
EVENT_TIMERS_KEY,
SESSION_RECORDING_ENABLED,
SESSION_ID,
]

/**
Expand Down Expand Up @@ -412,4 +414,5 @@ export {
CAMPAIGN_IDS_KEY,
EVENT_TIMERS_KEY,
SESSION_RECORDING_ENABLED,
SESSION_ID,
}

0 comments on commit 55d1b31

Please sign in to comment.