Skip to content

Commit

Permalink
Replay event bridge POC
Browse files Browse the repository at this point in the history
  • Loading branch information
amortemousque committed Oct 18, 2023
1 parent 56d7640 commit f80261a
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Expand Up @@ -252,7 +252,7 @@ deploy-feature:
stage: deploy
when: manual
variables:
SUFFIX: 'my-feature' #/datadog-[product]-${SUFFIX}.js
SUFFIX: 'webview' #/datadog-[product]-${SUFFIX}.js
extends:
- .base-configuration
- .feature-branches
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/transport/eventBridge.ts
Expand Up @@ -6,6 +6,8 @@ export interface BrowserWindowWithEventBridge extends Window {
}

export interface DatadogEventBridge {
getWebViewId?(): string
getParentRecordPrivacyLevel?(): string
getAllowedWebViewHosts(): string
send(msg: string): void
}
Expand All @@ -18,6 +20,12 @@ export function getEventBridge<T, E>() {
}

return {
getWebViewId() {
return eventBridgeGlobal.getWebViewId?.()
},
getParentRecordPrivacyLevel() {
return eventBridgeGlobal.getParentRecordPrivacyLevel?.()
},
getAllowedWebViewHosts() {
return JSON.parse(eventBridgeGlobal.getAllowedWebViewHosts()) as string[]
},
Expand Down
1 change: 1 addition & 0 deletions packages/core/test/emulate/eventBridge.ts
Expand Up @@ -2,6 +2,7 @@ import type { BrowserWindowWithEventBridge } from '../../src/transport'

export function initEventBridgeStub(allowedWebViewHosts: string[] = [window.location.hostname]) {
const eventBridgeStub = {
getNestedEnvId: () => '1',
send: (_msg: string) => undefined,
getAllowedWebViewHosts: () => JSON.stringify(allowedWebViewHosts),
}
Expand Down
2 changes: 2 additions & 0 deletions packages/rum-core/src/boot/rumPublicApi.ts
Expand Up @@ -20,6 +20,7 @@ import {
sanitize,
createStoredContextManager,
combine,
getEventBridge,
} from '@datadog/browser-core'
import type { LifeCycle } from '../domain/lifeCycle'
import type { ViewContexts } from '../domain/contexts/viewContexts'
Expand Down Expand Up @@ -308,6 +309,7 @@ export function makeRumPublicApi(
applicationId: '00000000-aaaa-0000-aaaa-000000000000',
clientToken: 'empty',
sessionSampleRate: 100,
defaultPrivacyLevel: getEventBridge()?.getParentRecordPrivacyLevel(),
})
}
}
2 changes: 1 addition & 1 deletion packages/rum-core/src/domain/rumSessionManager.ts
Expand Up @@ -58,7 +58,7 @@ export function startRumSessionManager(configuration: RumConfiguration, lifeCycl
export function startRumSessionManagerStub(): RumSessionManager {
const session: RumSession = {
id: '00000000-aaaa-0000-aaaa-000000000000',
sessionReplayAllowed: false,
sessionReplayAllowed: true,
}
return {
findTrackedSession: () => session,
Expand Down
4 changes: 2 additions & 2 deletions packages/rum/src/boot/recorderApi.ts
@@ -1,4 +1,4 @@
import { canUseEventBridge, noop, runOnReadyState } from '@datadog/browser-core'
import { noop, runOnReadyState } from '@datadog/browser-core'
import type {
LifeCycle,
ViewContexts,
Expand Down Expand Up @@ -53,7 +53,7 @@ export function makeRecorderApi(
startRecordingImpl: StartRecording,
createDeflateWorkerImpl?: CreateDeflateWorker
): RecorderApi {
if (canUseEventBridge() || !isBrowserSupported()) {
if (!isBrowserSupported()) {
return {
start: noop,
stop: noop,
Expand Down
26 changes: 25 additions & 1 deletion packages/rum/src/boot/startRecording.spec.ts
Expand Up @@ -3,7 +3,13 @@ import { PageExitReason, DefaultPrivacyLevel, noop, isIE, timeStampNow } from '@
import type { LifeCycle, ViewCreatedEvent, RumConfiguration } from '@datadog/browser-rum-core'
import { LifeCycleEventType } from '@datadog/browser-rum-core'
import type { Clock } from '@datadog/browser-core/test'
import { collectAsyncCalls, createNewEvent, mockClock } from '@datadog/browser-core/test'
import {
collectAsyncCalls,
createNewEvent,
deleteEventBridgeStub,
initEventBridgeStub,
mockClock,
} from '@datadog/browser-core/test'
import type { RumSessionManagerMock, TestSetupBuilder } from '../../../rum-core/test'
import { appendElement, createRumSessionManagerMock, setup } from '../../../rum-core/test'

Expand All @@ -16,6 +22,7 @@ import {
resetDeflateWorkerState,
} from '../domain/deflate'

import type { BrowserRecord } from '../types'
import { RecordType } from '../types'
import { resetReplayStats } from '../domain/replayStats'
import { startRecording } from './startRecording'
Expand Down Expand Up @@ -256,6 +263,23 @@ describe('startRecording', () => {
})
})

describe('when the event bridge is enable', () => {
it('should send records through the bridge', () => {
const eventBridgeStub = initEventBridgeStub()
const sendSpy = spyOn(eventBridgeStub, 'send')
setupBuilder.build()
document.body.dispatchEvent(createNewEvent('click', { clientX: 1, clientY: 2 }))

const lastBridgeMessage = JSON.parse(sendSpy.calls.mostRecent().args[0]) as {
eventType: 'replay'
event: BrowserRecord
}

expect(lastBridgeMessage.event.type).toBe(RecordType.IncrementalSnapshot)
deleteEventBridgeStub()
})
})

function changeView(lifeCycle: LifeCycle) {
lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, {} as any)
viewId = 'view-id-2'
Expand Down
27 changes: 18 additions & 9 deletions packages/rum/src/boot/startRecording.ts
@@ -1,5 +1,5 @@
import type { RawError, HttpRequest } from '@datadog/browser-core'
import { timeStampNow, createHttpRequest, addTelemetryDebug } from '@datadog/browser-core'
import { timeStampNow, createHttpRequest, addTelemetryDebug, canUseEventBridge, noop } from '@datadog/browser-core'
import type {
LifeCycle,
ViewContexts,
Expand All @@ -12,7 +12,9 @@ import { LifeCycleEventType } from '@datadog/browser-rum-core'
import { record } from '../domain/record'
import type { DeflateEncoder } from '../domain/deflate'
import { startSegmentCollection, SEGMENT_BYTES_LIMIT } from '../domain/segmentCollection'
import type { BrowserRecord } from '../types'
import { RecordType } from '../types'
import { startRecordBridge } from '../domain/startRecordBridge'

export function startRecording(
lifeCycle: LifeCycle,
Expand All @@ -31,14 +33,21 @@ export function startRecording(
httpRequest ||
createHttpRequest(configuration, configuration.sessionReplayEndpointBuilder, SEGMENT_BYTES_LIMIT, reportError)

const { addRecord, stop: stopSegmentCollection } = startSegmentCollection(
lifeCycle,
configuration,
sessionManager,
viewContexts,
replayRequest,
encoder
)
let addRecord = (_record: BrowserRecord) => {}
let stopSegmentCollection = noop

if (!canUseEventBridge()) {
;({ addRecord, stop: stopSegmentCollection } = startSegmentCollection(
lifeCycle,
configuration,
sessionManager,
viewContexts,
replayRequest,
encoder
))
} else {
;({ addRecord } = startRecordBridge())
}

const {
stop: stopRecording,
Expand Down
12 changes: 12 additions & 0 deletions packages/rum/src/domain/startRecordBridge.ts
@@ -0,0 +1,12 @@
import { assign, getEventBridge } from '@datadog/browser-core'
import type { BrowserRecord } from '../types'

export function startRecordBridge() {
const bridge = getEventBridge<'replay', BrowserRecord>()!

return {
addRecord: (record: BrowserRecord) => {
bridge.send('replay', assign(record, { nestedEnvId: bridge.getWebViewId() }))
},
}
}
23 changes: 16 additions & 7 deletions packages/rum/src/types/sessionReplay.ts
Expand Up @@ -48,13 +48,22 @@ export type BrowserRecord =
/**
* Browser-specific. Schema of a Record type which contains the full snapshot of a screen.
*/
export type BrowserFullSnapshotRecord = CommonRecordSchema & {
export type BrowserFullSnapshotRecord = WebviewSupportedCommonRecordSchema & {
/**
* The type of this Record.
*/
readonly type: 2
data: BrowserNode
}
/**
* Schema of common properties for a Record event type that is supported by webviews.
*/
export type WebviewSupportedCommonRecordSchema = CommonRecordSchema & {
/**
* Defines the unique ID of the nested replay environment that generated this record.
*/
readonly nestedEnvId?: number
}
/**
* Serialized node contained by this Record.
*/
Expand All @@ -68,7 +77,7 @@ export type SerializedNode = DocumentNode | DocumentFragmentNode | DocumentTypeN
/**
* Browser-specific. Schema of a Record type which contains mutations of a screen.
*/
export type BrowserIncrementalSnapshotRecord = CommonRecordSchema & {
export type BrowserIncrementalSnapshotRecord = WebviewSupportedCommonRecordSchema & {
/**
* The type of this Record.
*/
Expand Down Expand Up @@ -237,7 +246,7 @@ export type PointerInteractionData = {
/**
* Schema of a Record which contains the screen properties.
*/
export type MetaRecord = CommonRecordSchema & {
export type MetaRecord = WebviewSupportedCommonRecordSchema & {
/**
* The type of this Record.
*/
Expand All @@ -263,7 +272,7 @@ export type MetaRecord = CommonRecordSchema & {
/**
* Schema of a Record type which contains focus information.
*/
export type FocusRecord = CommonRecordSchema & {
export type FocusRecord = WebviewSupportedCommonRecordSchema & {
/**
* The type of this Record.
*/
Expand All @@ -278,7 +287,7 @@ export type FocusRecord = CommonRecordSchema & {
/**
* Schema of a Record which signifies that view lifecycle ended.
*/
export type ViewEndRecord = CommonRecordSchema & {
export type ViewEndRecord = WebviewSupportedCommonRecordSchema & {
/**
* The type of this Record.
*/
Expand All @@ -287,7 +296,7 @@ export type ViewEndRecord = CommonRecordSchema & {
/**
* Schema of a Record which signifies that the viewport properties have changed.
*/
export type VisualViewportRecord = CommonRecordSchema & {
export type VisualViewportRecord = WebviewSupportedCommonRecordSchema & {
data: {
height: number
offsetLeft: number
Expand All @@ -305,7 +314,7 @@ export type VisualViewportRecord = CommonRecordSchema & {
/**
* Schema of a Record which signifies a collection of frustration signals.
*/
export type FrustrationRecord = CommonRecordSchema & {
export type FrustrationRecord = WebviewSupportedCommonRecordSchema & {
/**
* The type of this Record.
*/
Expand Down
2 changes: 1 addition & 1 deletion rum-events-format

0 comments on commit f80261a

Please sign in to comment.