Skip to content

Commit

Permalink
Add feature detection
Browse files Browse the repository at this point in the history
  • Loading branch information
amortemousque committed Feb 24, 2024
1 parent f77b6b4 commit 681baac
Show file tree
Hide file tree
Showing 10 changed files with 55 additions and 21 deletions.
3 changes: 2 additions & 1 deletion packages/core/src/index.ts
Expand Up @@ -56,8 +56,9 @@ export {
Payload,
createHttpRequest,
canUseEventBridge,
isBridgeForRecordsSupported,
getEventBridge,
bridgeSupports,
BridgeCapability,
startBatchWithReplica,
createFlushController,
FlushEvent,
Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/transport/eventBridge.spec.ts
@@ -1,7 +1,7 @@
import { initEventBridgeStub } from '../../test'
import { DefaultPrivacyLevel } from '../domain/configuration'
import type { DatadogEventBridge } from './eventBridge'
import { getEventBridge, canUseEventBridge } from './eventBridge'
import { getEventBridge, canUseEventBridge, BridgeCapability, bridgeSupports } from './eventBridge'

describe('canUseEventBridge', () => {
const allowedWebViewHosts = ['foo.bar']
Expand Down Expand Up @@ -76,4 +76,16 @@ describe('event bridge getPrivacyLevel', () => {

expect(eventBridge.getPrivacyLevel()).toBeUndefined()
})

describe('bridgeSupports', () => {
it('should returns true when the bridge supports a capability', () => {
initEventBridgeStub({ capabilities: [BridgeCapability.RECORDS] })
expect(bridgeSupports(BridgeCapability.RECORDS)).toBeTrue()
})

it('should returns false when the bridge does not support a capability', () => {
initEventBridgeStub({ capabilities: [] })
expect(bridgeSupports(BridgeCapability.RECORDS)).toBeFalse()
})
})
})
16 changes: 12 additions & 4 deletions packages/core/src/transport/eventBridge.ts
@@ -1,16 +1,21 @@
import { endsWith } from '../tools/utils/polyfills'
import { endsWith, includes } from '../tools/utils/polyfills'
import { getGlobalObject } from '../tools/getGlobalObject'

export interface BrowserWindowWithEventBridge extends Window {
DatadogEventBridge?: DatadogEventBridge
}

export interface DatadogEventBridge {
getCapabilities?(): string
getPrivacyLevel?(): string
getAllowedWebViewHosts(): string
send(msg: string): void
}

export const enum BridgeCapability {
RECORDS = 'records',
}

export function getEventBridge<T, E>() {
const eventBridgeGlobal = getEventBridgeGlobal()

Expand All @@ -19,6 +24,9 @@ export function getEventBridge<T, E>() {
}

return {
getCapabilities() {
return JSON.parse(eventBridgeGlobal.getCapabilities?.() || '[]') as BridgeCapability[]
},
getPrivacyLevel() {
return eventBridgeGlobal.getPrivacyLevel?.()
},
Expand All @@ -32,9 +40,9 @@ export function getEventBridge<T, E>() {
}
}

export function isBridgeForRecordsSupported(): boolean {
const bridge = getEventBridgeGlobal()
return !!bridge && 'getPrivacyLevel' in bridge
export function bridgeSupports(capability: BridgeCapability): boolean {
const bridge = getEventBridge()
return !!bridge && includes(bridge.getCapabilities(), capability)
}

export function canUseEventBridge(currentHost = getGlobalObject<Window>().location?.hostname): boolean {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/transport/index.ts
@@ -1,8 +1,9 @@
export { HttpRequest, createHttpRequest, Payload, RetryInfo } from './httpRequest'
export {
canUseEventBridge,
isBridgeForRecordsSupported,
bridgeSupports,
getEventBridge,
BridgeCapability,
BrowserWindowWithEventBridge,
DatadogEventBridge,
} from './eventBridge'
Expand Down
10 changes: 5 additions & 5 deletions packages/core/test/emulate/eventBridge.ts
@@ -1,18 +1,18 @@
import { DefaultPrivacyLevel } from '../../src/domain/configuration'
import { BridgeCapability } from '../../src/transport'
import type { BrowserWindowWithEventBridge, DatadogEventBridge } from '../../src/transport'
import { registerCleanupTask } from '../registerCleanupTask'

export function initEventBridgeStub({
allowedWebViewHosts = [window.location.hostname],
privacyLevel = DefaultPrivacyLevel.MASK,
bridgeForRecordsSupported = true,
}: { allowedWebViewHosts?: string[]; privacyLevel?: DefaultPrivacyLevel; bridgeForRecordsSupported?: boolean } = {}) {
capabilities = [BridgeCapability.RECORDS],
}: { allowedWebViewHosts?: string[]; privacyLevel?: DefaultPrivacyLevel; capabilities?: BridgeCapability[] } = {}) {
const eventBridgeStub: DatadogEventBridge = {
send: (_msg: string) => undefined,
getAllowedWebViewHosts: () => JSON.stringify(allowedWebViewHosts),
}
if (bridgeForRecordsSupported) {
eventBridgeStub.getPrivacyLevel = () => privacyLevel
getCapabilities: () => JSON.stringify(capabilities),
getPrivacyLevel: () => privacyLevel,
}

;(window as BrowserWindowWithEventBridge).DatadogEventBridge = eventBridgeStub
Expand Down
5 changes: 3 additions & 2 deletions packages/rum-core/src/domain/rumSessionManager.spec.ts
Expand Up @@ -10,6 +10,7 @@ import {
DOM_EVENT,
createTrackingConsentState,
TrackingConsent,
BridgeCapability,
} from '@datadog/browser-core'
import type { Clock } from '@datadog/browser-core/test'
import { createNewEvent, initEventBridgeStub, mockClock } from '@datadog/browser-core/test'
Expand Down Expand Up @@ -221,12 +222,12 @@ describe('rum session manager', () => {

describe('rum session manager stub', () => {
it('should return a tracked session with replay allowed when the event bridge support records', () => {
initEventBridgeStub({ bridgeForRecordsSupported: true })
initEventBridgeStub({ capabilities: [BridgeCapability.RECORDS] })
expect(startRumSessionManagerStub().findTrackedSession()!.sessionReplayAllowed).toEqual(true)
})

it('should return a tracked session without replay allowed when the event bridge support records', () => {
initEventBridgeStub({ bridgeForRecordsSupported: false })
initEventBridgeStub({ capabilities: [] })
expect(startRumSessionManagerStub().findTrackedSession()!.sessionReplayAllowed).toEqual(false)
})
})
11 changes: 9 additions & 2 deletions packages/rum-core/src/domain/rumSessionManager.ts
@@ -1,5 +1,12 @@
import type { RelativeTime, TrackingConsentState } from '@datadog/browser-core'
import { Observable, isBridgeForRecordsSupported, noop, performDraw, startSessionManager } from '@datadog/browser-core'
import {
BridgeCapability,
Observable,
bridgeSupports,
noop,
performDraw,
startSessionManager,
} from '@datadog/browser-core'
import type { RumConfiguration } from './configuration'
import type { LifeCycle } from './lifeCycle'
import { LifeCycleEventType } from './lifeCycle'
Expand Down Expand Up @@ -65,7 +72,7 @@ export function startRumSessionManager(
export function startRumSessionManagerStub(): RumSessionManager {
const session: RumSession = {
id: '00000000-aaaa-0000-aaaa-000000000000',
sessionReplayAllowed: isBridgeForRecordsSupported(),
sessionReplayAllowed: bridgeSupports(BridgeCapability.RECORDS),
}
return {
findTrackedSession: () => session,
Expand Down
6 changes: 3 additions & 3 deletions packages/rum/src/boot/recorderApi.spec.ts
@@ -1,5 +1,5 @@
import type { DeflateEncoder, DeflateWorker, DeflateWorkerAction } from '@datadog/browser-core'
import { PageExitReason, display, isIE } from '@datadog/browser-core'
import { BridgeCapability, PageExitReason, display, isIE } from '@datadog/browser-core'
import type { RecorderApi, ViewContexts, LifeCycle, RumConfiguration } from '@datadog/browser-rum-core'
import { LifeCycleEventType } from '@datadog/browser-rum-core'
import { initEventBridgeStub, createNewEvent } from '@datadog/browser-core/test'
Expand Down Expand Up @@ -184,7 +184,7 @@ describe('makeRecorderApi', () => {

describe('if event bridge present', () => {
it('should start recording when the bridge supports records', () => {
initEventBridgeStub({ bridgeForRecordsSupported: true })
initEventBridgeStub({ capabilities: [BridgeCapability.RECORDS] })

setupBuilder.build()
rumInit()
Expand All @@ -193,7 +193,7 @@ describe('makeRecorderApi', () => {
})

it('should not start recording when the bridge does not support records', () => {
initEventBridgeStub({ bridgeForRecordsSupported: false })
initEventBridgeStub({ capabilities: [] })

setupBuilder.build()
rumInit()
Expand Down
5 changes: 3 additions & 2 deletions packages/rum/src/boot/recorderApi.ts
Expand Up @@ -2,10 +2,11 @@ import type { DeflateEncoder } from '@datadog/browser-core'
import {
DeflateEncoderStreamId,
canUseEventBridge,
isBridgeForRecordsSupported,
noop,
runOnReadyState,
PageExitReason,
BridgeCapability,
bridgeSupports,
} from '@datadog/browser-core'
import type {
LifeCycle,
Expand Down Expand Up @@ -60,7 +61,7 @@ export function makeRecorderApi(
startRecordingImpl: StartRecording,
createDeflateWorkerImpl?: CreateDeflateWorker
): RecorderApi {
if ((canUseEventBridge() && !isBridgeForRecordsSupported()) || !isBrowserSupported()) {
if ((canUseEventBridge() && !bridgeSupports(BridgeCapability.RECORDS)) || !isBrowserSupported()) {
return {
start: noop,
stop: noop,
Expand Down
3 changes: 3 additions & 0 deletions test/e2e/lib/framework/pageSetups.ts
Expand Up @@ -206,6 +206,9 @@ function setupEventBridge(servers: Servers) {
return html`
<script type="text/javascript">
window.DatadogEventBridge = {
getCapabilities() {
return '["records"]'
},
getPrivacyLevel() {
return 'mask'
},
Expand Down

0 comments on commit 681baac

Please sign in to comment.