Skip to content

Commit 494418a

Browse files
committed
Add feature operation step vital apis
1 parent 20b55ab commit 494418a

File tree

10 files changed

+263
-45
lines changed

10 files changed

+263
-45
lines changed

packages/core/src/tools/experimentalFeatures.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export enum ExperimentalFeature {
1919
EARLY_REQUEST_COLLECTION = 'early_request_collection',
2020
WATCH_COOKIE_WITHOUT_LOCK = 'watch_cookie_without_lock',
2121
USE_TREE_WALKER_FOR_ACTION_NAME = 'use_tree_walker_for_action_name',
22+
FEATURE_OPERATION_VITAL = 'feature_operation_vital',
2223
}
2324

2425
const enabledExperimentalFeatures: Set<ExperimentalFeature> = new Set()

packages/rum-core/src/boot/preStartRum.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,17 @@ describe('preStartRum', () => {
742742
strategy.init(DEFAULT_INIT_CONFIGURATION, PUBLIC_API)
743743
expect(addDurationVitalSpy).toHaveBeenCalledOnceWith(vitalAdd)
744744
})
745+
746+
it('addOperationStepVital', () => {
747+
const addOperationStepVitalSpy = jasmine.createSpy()
748+
doStartRumSpy.and.returnValue({
749+
addOperationStepVital: addOperationStepVitalSpy,
750+
} as unknown as StartRumResult)
751+
752+
strategy.addOperationStepVital('foo', 'start')
753+
strategy.init(DEFAULT_INIT_CONFIGURATION, PUBLIC_API)
754+
expect(addOperationStepVitalSpy).toHaveBeenCalledOnceWith('foo', 'start', undefined, undefined, undefined)
755+
})
745756
})
746757

747758
describe('tracking consent', () => {

packages/rum-core/src/boot/preStartRum.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
serializeRumConfiguration,
2626
} from '../domain/configuration'
2727
import type { ViewOptions } from '../domain/view/trackViews'
28-
import type { DurationVital, CustomVitalsState } from '../domain/vital/vitalCollection'
28+
import type { DurationVital, CustomVitalsState, VitalOptions } from '../domain/vital/vitalCollection'
2929
import { startDurationVital, stopDurationVital } from '../domain/vital/vitalCollection'
3030
import { callPluginsMethod } from '../domain/plugins'
3131
import type { StartRumResult } from './startRum'
@@ -148,6 +148,18 @@ export function createPreStartStrategy(
148148
bufferApiCalls.add((startRumResult) => startRumResult.addDurationVital(vital))
149149
}
150150

151+
const addOperationStepVital = (
152+
name: string,
153+
stepType: 'start' | 'end',
154+
operationKey?: string,
155+
failureReason?: string,
156+
options?: VitalOptions
157+
) => {
158+
bufferApiCalls.add((startRumResult) =>
159+
startRumResult.addOperationStepVital(name, stepType, operationKey, failureReason, options)
160+
)
161+
}
162+
151163
const strategy: Strategy = {
152164
init(initConfiguration, publicApi) {
153165
if (!initConfiguration) {
@@ -248,6 +260,7 @@ export function createPreStartStrategy(
248260
},
249261

250262
addDurationVital,
263+
addOperationStepVital,
251264
}
252265

253266
return strategy

packages/rum-core/src/boot/rumPublicApi.spec.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const noopStartRum = (): ReturnType<StartRum> => ({
3434
accountContext: {} as any,
3535
hooks: {} as any,
3636
telemetry: {} as any,
37+
addOperationStepVital: () => undefined,
3738
})
3839
const DEFAULT_INIT_CONFIGURATION = { applicationId: 'xxx', clientToken: 'xxx' }
3940
const FAKE_WORKER = {} as DeflateWorker
@@ -784,6 +785,66 @@ describe('rum public api', () => {
784785
})
785786
})
786787

788+
describe('startFeatureOperation', () => {
789+
it('should call addOperationStepVital on the startRum result with start status', () => {
790+
const addOperationStepVitalSpy = jasmine.createSpy()
791+
const rumPublicApi = makeRumPublicApi(
792+
() => ({ ...noopStartRum(), addOperationStepVital: addOperationStepVitalSpy }),
793+
noopRecorderApi,
794+
noopProfilerApi
795+
)
796+
rumPublicApi.init(DEFAULT_INIT_CONFIGURATION)
797+
rumPublicApi.startFeatureOperation('foo', '00000000-0000-0000-0000-000000000000')
798+
expect(addOperationStepVitalSpy).toHaveBeenCalledWith(
799+
'foo',
800+
'start',
801+
'00000000-0000-0000-0000-000000000000',
802+
undefined,
803+
undefined
804+
)
805+
})
806+
})
807+
808+
describe('succeedFeatureOperation', () => {
809+
it('should call addOperationStepVital on the startRum result with end status', () => {
810+
const addOperationStepVitalSpy = jasmine.createSpy()
811+
const rumPublicApi = makeRumPublicApi(
812+
() => ({ ...noopStartRum(), addOperationStepVital: addOperationStepVitalSpy }),
813+
noopRecorderApi,
814+
noopProfilerApi
815+
)
816+
rumPublicApi.init(DEFAULT_INIT_CONFIGURATION)
817+
rumPublicApi.succeedFeatureOperation('foo', '00000000-0000-0000-0000-000000000000')
818+
expect(addOperationStepVitalSpy).toHaveBeenCalledWith(
819+
'foo',
820+
'end',
821+
'00000000-0000-0000-0000-000000000000',
822+
undefined,
823+
undefined
824+
)
825+
})
826+
})
827+
828+
describe('failFeatureOperation', () => {
829+
it('should call addOperationStepVital on the startRum result with end status and failure reason', () => {
830+
const addOperationStepVitalSpy = jasmine.createSpy()
831+
const rumPublicApi = makeRumPublicApi(
832+
() => ({ ...noopStartRum(), addOperationStepVital: addOperationStepVitalSpy }),
833+
noopRecorderApi,
834+
noopProfilerApi
835+
)
836+
rumPublicApi.init(DEFAULT_INIT_CONFIGURATION)
837+
rumPublicApi.failFeatureOperation('foo', '00000000-0000-0000-0000-000000000000', 'page not found')
838+
expect(addOperationStepVitalSpy).toHaveBeenCalledWith(
839+
'foo',
840+
'end',
841+
'00000000-0000-0000-0000-000000000000',
842+
'page not found',
843+
undefined
844+
)
845+
})
846+
})
847+
787848
it('should provide sdk version', () => {
788849
const rumPublicApi = makeRumPublicApi(noopStartRum, noopRecorderApi, noopProfilerApi)
789850
expect(rumPublicApi.version).toBe('test')

packages/rum-core/src/boot/rumPublicApi.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ import type { RumConfiguration, RumInitConfiguration } from '../domain/configura
4242
import type { ViewOptions } from '../domain/view/trackViews'
4343
import type {
4444
AddDurationVitalOptions,
45-
DurationVitalOptions,
4645
DurationVitalReference,
46+
DurationVitalOptions,
47+
VitalOptions,
4748
} from '../domain/vital/vitalCollection'
4849
import { createCustomVitalsState } from '../domain/vital/vitalCollection'
4950
import { callPluginsMethod } from '../domain/plugins'
@@ -427,6 +428,17 @@ export interface RumPublicApi extends PublicApi {
427428
* @param options - Options for the custom vital (context, description)
428429
*/
429430
stopDurationVital: (nameOrRef: string | DurationVitalReference, options?: DurationVitalOptions) => void
431+
432+
/**
433+
* Add an operation step vital
434+
*
435+
* @category Vital
436+
* @param name - Name of the operation step
437+
* @param options - Options for the operation step (context, description)
438+
*/
439+
startFeatureOperation: (name: string, operationKey?: string, options?: VitalOptions) => void
440+
succeedFeatureOperation: (name: string, operationKey?: string, options?: VitalOptions) => void
441+
failFeatureOperation: (name: string, operationKey?: string, failureReason?: string, options?: VitalOptions) => void
430442
}
431443

432444
export interface RecorderApi {
@@ -494,6 +506,7 @@ export interface Strategy {
494506
startDurationVital: StartRumResult['startDurationVital']
495507
stopDurationVital: StartRumResult['stopDurationVital']
496508
addDurationVital: StartRumResult['addDurationVital']
509+
addOperationStepVital: StartRumResult['addOperationStepVital']
497510
}
498511

499512
export function makeRumPublicApi(
@@ -771,6 +784,21 @@ export function makeRumPublicApi(
771784
description: sanitize(options && options.description) as string | undefined,
772785
})
773786
}),
787+
788+
startFeatureOperation: monitor((name, operationKey, options) => {
789+
addTelemetryUsage({ feature: 'add-operation-step-vital', action_type: 'start' })
790+
strategy.addOperationStepVital(name, 'start', operationKey, undefined, options)
791+
}),
792+
793+
succeedFeatureOperation: monitor((name, operationKey, options) => {
794+
addTelemetryUsage({ feature: 'add-operation-step-vital', action_type: 'succeed' })
795+
strategy.addOperationStepVital(name, 'end', operationKey, undefined, options)
796+
}),
797+
798+
failFeatureOperation: monitor((name, operationKey, failureReason, options) => {
799+
addTelemetryUsage({ feature: 'add-operation-step-vital', action_type: 'fail' })
800+
strategy.addOperationStepVital(name, 'end', operationKey, failureReason, options)
801+
}),
774802
})
775803

776804
return rumPublicApi

packages/rum-core/src/boot/startRum.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ export function startRum(
236236
startDurationVital: vitalCollection.startDurationVital,
237237
stopDurationVital: vitalCollection.stopDurationVital,
238238
addDurationVital: vitalCollection.addDurationVital,
239+
addOperationStepVital: vitalCollection.addOperationStepVital,
239240
globalContext,
240241
userContext,
241242
accountContext,

packages/rum-core/src/domain/vital/vitalCollection.spec.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Duration } from '@datadog/browser-core'
2-
import { mockClock, type Clock } from '@datadog/browser-core/test'
3-
import { clocksNow } from '@datadog/browser-core'
2+
import { mockClock, mockExperimentalFeatures, type Clock } from '@datadog/browser-core/test'
3+
import { clocksNow, ExperimentalFeature } from '@datadog/browser-core'
44
import { collectAndValidateRawRumEvents, mockPageStateHistory } from '../../../test'
55
import type { RawRumEvent, RawRumVitalEvent } from '../../rawRumEvent.types'
66
import { VitalType, RumEventType } from '../../rawRumEvent.types'
@@ -229,7 +229,34 @@ describe('vitalCollection', () => {
229229
expect(rawRumEvents[0].domainContext).toEqual({})
230230
})
231231

232-
it('should create a vital from add API', () => {
232+
it('should collect raw rum event from operation step vital', () => {
233+
mockExperimentalFeatures([ExperimentalFeature.FEATURE_OPERATION_VITAL])
234+
vitalCollection.addOperationStepVital('foo', 'start')
235+
236+
expect(rawRumEvents[0].startTime).toEqual(jasmine.any(Number))
237+
expect(rawRumEvents[0].rawRumEvent).toEqual({
238+
date: jasmine.any(Number),
239+
vital: {
240+
id: jasmine.any(String),
241+
type: VitalType.OPERATION_STEP,
242+
name: 'foo',
243+
step_type: 'start',
244+
operation_key: undefined,
245+
failure_reason: undefined,
246+
description: undefined,
247+
},
248+
context: undefined,
249+
type: RumEventType.VITAL,
250+
_dd: {
251+
vital: {
252+
computed_value: true,
253+
},
254+
},
255+
})
256+
expect(rawRumEvents[0].domainContext).toEqual({})
257+
})
258+
259+
it('should create a duration vital from add API', () => {
233260
vitalCollection.addDurationVital({
234261
name: 'foo',
235262
type: VitalType.DURATION,
@@ -244,6 +271,23 @@ describe('vitalCollection', () => {
244271
expect((rawRumEvents[0].rawRumEvent as RawRumVitalEvent).vital.description).toBe('baz')
245272
expect((rawRumEvents[0].rawRumEvent as RawRumVitalEvent).context).toEqual({ foo: 'bar' })
246273
})
274+
275+
it('should create a operation step vital from add API', () => {
276+
mockExperimentalFeatures([ExperimentalFeature.FEATURE_OPERATION_VITAL])
277+
vitalCollection.addOperationStepVital('foo', 'end', '00000000-0000-0000-0000-000000000000', 'error', {
278+
context: { foo: 'bar' },
279+
description: 'baz',
280+
})
281+
282+
expect(rawRumEvents.length).toBe(1)
283+
expect((rawRumEvents[0].rawRumEvent as RawRumVitalEvent).vital.step_type).toBe('end')
284+
expect((rawRumEvents[0].rawRumEvent as RawRumVitalEvent).vital.operation_key).toBe(
285+
'00000000-0000-0000-0000-000000000000'
286+
)
287+
expect((rawRumEvents[0].rawRumEvent as RawRumVitalEvent).vital.failure_reason).toBe('error')
288+
expect((rawRumEvents[0].rawRumEvent as RawRumVitalEvent).vital.description).toBe('baz')
289+
expect((rawRumEvents[0].rawRumEvent as RawRumVitalEvent).context).toEqual({ foo: 'bar' })
290+
})
247291
})
248292
})
249293
})

0 commit comments

Comments
 (0)