Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions packages/rum-core/src/domain/assembly.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,69 @@ describe('rum assembly', () => {
})
})
})

describe('STREAM event processing', () => {
it('should convert STREAM events to VIEW events', () => {
const { lifeCycle, serverRumEvents } = setupAssemblyTestWithDefaults({})

const streamData = {
id: 'stream-id-123',
document_version: 42,
time_spent: 5000000000, // 5 seconds in nanoseconds
}

notifyRawRumEvent(lifeCycle, {
rawRumEvent: createRawRumEvent(RumEventType.STREAM, {
stream: streamData,
view: { id: 'original-view-id', url: '/test' },
}),
})

expect(serverRumEvents.length).toBe(1)
const resultEvent = serverRumEvents[0]

expect(resultEvent.type).toBe('view')
})

it('should map stream properties correctly in converted VIEW event', () => {
const { lifeCycle, serverRumEvents } = setupAssemblyTestWithDefaults({})

const streamData = {
id: 'stream-id-456',
document_version: 25,
time_spent: 3000000000, // 3 seconds in nanoseconds
}

notifyRawRumEvent(lifeCycle, {
rawRumEvent: createRawRumEvent(RumEventType.STREAM, {
stream: streamData,
view: { id: 'original-view-id', url: '/test-page' },
}),
})

expect(serverRumEvents.length).toBe(1)
const resultEvent = serverRumEvents[0] as any

expect(resultEvent.stream).toBeDefined()

// Check _dd.document_version is set from stream.document_version
expect(resultEvent._dd.document_version).toBe(25)

// Check view.id is set from stream.id
expect(resultEvent.view.id).toBe('stream-id-456')

// Check view.time_spent is set from stream.time_spent
expect(resultEvent.view.time_spent).toBe(3000000000)

// Check stream.time_spent is undefined in the stream object
expect(resultEvent.stream.time_spent).toBeUndefined()

// Check action/error/resource counts are set to 0
expect(resultEvent.view.action.count).toBe(0)
expect(resultEvent.view.error.count).toBe(0)
expect(resultEvent.view.resource.count).toBe(0)
})
})
})

function notifyRawRumEvent<E extends RawRumEvent>(
Expand Down
48 changes: 46 additions & 2 deletions packages/rum-core/src/domain/assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import type { RumEventDomainContext } from '../domainContext.types'
import type { AssembledRumEvent } from '../rawRumEvent.types'
import { RumEventType } from '../rawRumEvent.types'
import type { RumViewEvent } from '../rumEvent.types'
import type { LifeCycle } from './lifeCycle'
import { LifeCycleEventType } from './lifeCycle'
import type { RumConfiguration } from './configuration'
Expand Down Expand Up @@ -86,6 +87,16 @@ export function startRumAssembly(
...VIEW_MODIFIABLE_FIELD_PATHS,
...ROOT_MODIFIABLE_FIELD_PATHS,
},
[RumEventType.STREAM]: {
...USER_CUSTOMIZABLE_FIELD_PATHS,
...VIEW_MODIFIABLE_FIELD_PATHS,
...ROOT_MODIFIABLE_FIELD_PATHS,
},
[RumEventType.TRANSITION]: {
...USER_CUSTOMIZABLE_FIELD_PATHS,
...VIEW_MODIFIABLE_FIELD_PATHS,
...ROOT_MODIFIABLE_FIELD_PATHS,
},
}
const eventRateLimiters = {
[RumEventType.ERROR]: createEventRateLimiter(
Expand All @@ -109,7 +120,7 @@ export function startRumAssembly(
LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
({ startTime, duration, rawRumEvent, domainContext }) => {
const defaultRumEventAttributes = hooks.triggerHook(HookNames.Assemble, {
eventType: rawRumEvent.type,
eventType: rawRumEvent.type === 'stream' ? 'view' : rawRumEvent.type,
startTime,
duration,
})!
Expand All @@ -126,7 +137,40 @@ export function startRumAssembly(
if (isEmptyObject(serverRumEvent.context!)) {
delete serverRumEvent.context
}
lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, serverRumEvent)

if (rawRumEvent.type === 'stream') {
const streamEvent = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: this doesn't look like production ready code..

Copy link
Contributor

@BeltranBulbarellaDD BeltranBulbarellaDD Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My knowledge is that the idea is to make it work, and have it tested next week with customers and if no one want's it, to be dropped. That's why I think maybe the integration is not production ready.

But I will move this to a helper function, use the types and unit test it to make it prod ready.

...(serverRumEvent as RumViewEvent),
_dd: {
...serverRumEvent._dd,
document_version: serverRumEvent.stream?.document_version,
},
stream: {
...serverRumEvent.stream,
time_spent: undefined,
},
view: {
...serverRumEvent.view,
id: serverRumEvent.stream?.id,
is_active: true,
action: {
count: 0,
},
error: {
count: 0,
},
resource: {
count: 0,
},
time_spent: serverRumEvent.stream?.time_spent,
},
type: 'view',
}

lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, streamEvent as AssembledRumEvent)
} else {
lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, serverRumEvent)
}
}
}
)
Expand Down
5 changes: 5 additions & 0 deletions packages/rum-core/src/domain/event/eventCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
RawRumLongTaskEvent,
RawRumResourceEvent,
RawRumVitalEvent,
RawRumStreamEvent,
} from '../../rawRumEvent.types'
import { RumEventType } from '../../rawRumEvent.types'

Expand All @@ -18,6 +19,9 @@ const allowedEventTypes = [
RumEventType.ERROR,
RumEventType.LONG_TASK,
RumEventType.RESOURCE,
RumEventType.STREAM,
RumEventType.STREAM,
RumEventType.TRANSITION,
RumEventType.VITAL,
] as const

Expand All @@ -28,6 +32,7 @@ export type AllowedRawRumEvent = (
| RawRumLongAnimationFrameEvent
| RawRumActionEvent
| RawRumVitalEvent
| RawRumStreamEvent
) & { context?: Context }

export function startEventCollection(lifeCycle: LifeCycle) {
Expand Down
8 changes: 7 additions & 1 deletion packages/rum-core/src/domain/trackEventCounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ export function trackEventCounts({
}

const subscription = lifeCycle.subscribe(LifeCycleEventType.RUM_EVENT_COLLECTED, (event): void => {
if (event.type === 'view' || event.type === 'vital' || !isChildEvent(event)) {
if (
event.type === 'view' ||
event.type === 'vital' ||
event.type === 'transition' ||
!isChildEvent(event) ||
['stream'].includes(event.type)
) {
return
}
switch (event.type) {
Expand Down
7 changes: 6 additions & 1 deletion packages/rum-core/src/domainContext.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export type RumEventDomainContext<T extends RumEventType = any> = T extends type
? RumLongTaskEventDomainContext
: T extends typeof RumEventType.VITAL
? RumVitalEventDomainContext
: never
: T extends typeof RumEventType.STREAM
? RumStreamEventDomainContext
: never

export interface RumViewEventDomainContext {
location: Readonly<Location>
Expand Down Expand Up @@ -59,3 +61,6 @@ export interface RumLongTaskEventDomainContext {

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface RumVitalEventDomainContext {}

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface RumStreamEventDomainContext {}
43 changes: 43 additions & 0 deletions packages/rum-core/src/rawRumEvent.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
RumErrorEvent,
RumLongTaskEvent,
RumResourceEvent,
RumTransitionEvent,
RumViewEvent,
RumVitalEvent,
} from './rumEvent.types'
Expand All @@ -28,6 +29,8 @@ export const RumEventType = {
VIEW: 'view',
RESOURCE: 'resource',
VITAL: 'vital',
STREAM: 'stream',
TRANSITION: 'transition',
} as const

export type RumEventType = (typeof RumEventType)[keyof typeof RumEventType]
Expand All @@ -39,6 +42,7 @@ export type AssembledRumEvent = (
| RumErrorEvent
| RumVitalEvent
| RumLongTaskEvent
| RumTransitionEvent
) &
Context

Expand Down Expand Up @@ -331,6 +335,9 @@ export interface RawRumActionEvent {
pointer_up_delay?: Duration
}
}
stream?: {
id: string
}
context?: Context
}

Expand Down Expand Up @@ -377,6 +384,40 @@ export const VitalType = {

export type VitalType = (typeof VitalType)[keyof typeof VitalType]

export interface RawRumStreamEvent {
date: TimeStamp
type: typeof RumEventType.STREAM
stream: {
id: string
bitrate?: number
document_version: number
duration?: number
format?: string
fps?: number
resolution?: string
time_spent: number
timestamp?: number
watch_time?: number
}
}

export interface RawRumTransitionEvent {
date: TimeStamp
type: typeof RumEventType.TRANSITION
stream: {
id: string
}
transition: {
type: string
id?: string
timestamp?: number
buffer_starvation_duration?: number
media_start_delay?: number
error_code?: number
duration?: number
}
}

export type RawRumEvent =
| RawRumErrorEvent
| RawRumResourceEvent
Expand All @@ -385,3 +426,5 @@ export type RawRumEvent =
| RawRumLongAnimationFrameEvent
| RawRumActionEvent
| RawRumVitalEvent
| RawRumStreamEvent
| RawRumTransitionEvent
39 changes: 39 additions & 0 deletions packages/rum-core/test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,45 @@ export function createRawRumEvent(type: RumEventType, overrides?: Context): RawR
},
overrides
)
case RumEventType.STREAM:
return combine(
{
type,
_dd: {
document_version: 0,
},
date: 0 as TimeStamp,
view: {
id: generateUUID(),
action: { count: 0 },
error: { count: 0 },
long_task: { count: 0 },
resource: { count: 0 },
time_spent: 0 as ServerDuration,
},
stream: {
id: generateUUID(),
document_version: 0,
time_spent: 0 as ServerDuration,
},
},
overrides
)
case RumEventType.TRANSITION:
return combine(
{
type,
date: 0 as TimeStamp,
stream: {
id: generateUUID(),
},
transition: {
id: generateUUID(),
type: 'MEDIA_PLAYER_PLAY',
},
},
overrides
)
}
}

Expand Down