diff --git a/packages/core/src/tools/utils/polyfills.ts b/packages/core/src/tools/utils/polyfills.ts index 3428836312..3b97c3a808 100644 --- a/packages/core/src/tools/utils/polyfills.ts +++ b/packages/core/src/tools/utils/polyfills.ts @@ -80,6 +80,7 @@ interface Assignable { export function assign(target: T, source: U): T & U export function assign(target: T, source1: U, source2: V): T & U & V export function assign(target: T, source1: U, source2: V, source3: W): T & U & V & W +export function assign(target: T, source1: U, source2: V, source3: W, source4: X): T & U & V & W & X export function assign(target: Assignable, ...toAssign: Assignable[]) { toAssign.forEach((source: Assignable) => { for (const key in source) { diff --git a/packages/rum-core/src/domain/assembly.spec.ts b/packages/rum-core/src/domain/assembly.spec.ts index 5755df7c35..b99dcf9bfe 100644 --- a/packages/rum-core/src/domain/assembly.spec.ts +++ b/packages/rum-core/src/domain/assembly.spec.ts @@ -603,6 +603,50 @@ describe('rum assembly', () => { expect(serverRumEvents[0].service).toEqual('new service') expect(serverRumEvents[0].version).toEqual('new version') }) + + describe('fields service and version', () => { + it('by default, it should not be modifiable', () => { + const { lifeCycle } = setupBuilder + .withConfiguration({ + beforeSend: (event) => { + event.service = 'bar' + event.version = '0.2.0' + + return true + }, + }) + .build() + + notifyRawRumEvent(lifeCycle, { + rawRumEvent: createRawRumEvent(RumEventType.RESOURCE, { resource: { url: '/path?foo=bar' } }), + }) + + expect((serverRumEvents[0] as RumResourceEvent).service).toBe('default service') + expect((serverRumEvents[0] as RumResourceEvent).version).toBe('default version') + }) + + it('when the micro_frontend experimental flag is set, it should be modifiable', () => { + mockExperimentalFeatures([ExperimentalFeature.MICRO_FRONTEND]) + + const { lifeCycle } = setupBuilder + .withConfiguration({ + beforeSend: (event) => { + event.service = 'bar' + event.version = '0.2.0' + + return true + }, + }) + .build() + + notifyRawRumEvent(lifeCycle, { + rawRumEvent: createRawRumEvent(RumEventType.RESOURCE, { resource: { url: '/path?foo=bar' } }), + }) + + expect((serverRumEvents[0] as RumResourceEvent).service).toBe('bar') + expect((serverRumEvents[0] as RumResourceEvent).version).toBe('0.2.0') + }) + }) }) describe('url context', () => { diff --git a/packages/rum-core/src/domain/assembly.ts b/packages/rum-core/src/domain/assembly.ts index 729cf42c28..8fd4653698 100644 --- a/packages/rum-core/src/domain/assembly.ts +++ b/packages/rum-core/src/domain/assembly.ts @@ -72,6 +72,16 @@ export function startRumAssembly( getCommonContext: () => CommonContext, reportError: (error: RawError) => void ) { + // TODO: lift this declaration to module level once feature flag is removed + const ROOT_MODIFIABLE_FIELD_PATHS: ModifiableFieldPaths = isExperimentalFeatureEnabled( + ExperimentalFeature.MICRO_FRONTEND + ) + ? { + service: 'string', + version: 'string', + } + : {} + modifiableFieldPathsByEvent = { [RumEventType.VIEW]: VIEW_MODIFIABLE_FIELD_PATHS, [RumEventType.ERROR]: assign( @@ -82,24 +92,29 @@ export function startRumAssembly( 'error.fingerprint': 'string', }, USER_CUSTOMIZABLE_FIELD_PATHS, - VIEW_MODIFIABLE_FIELD_PATHS + VIEW_MODIFIABLE_FIELD_PATHS, + ROOT_MODIFIABLE_FIELD_PATHS ), [RumEventType.RESOURCE]: assign( { 'resource.url': 'string', }, - isExperimentalFeatureEnabled(ExperimentalFeature.WRITABLE_RESOURCE_GRAPHQL) && { - 'resource.graphql': 'object', - }, + isExperimentalFeatureEnabled(ExperimentalFeature.WRITABLE_RESOURCE_GRAPHQL) + ? { + 'resource.graphql': 'object', + } + : {}, USER_CUSTOMIZABLE_FIELD_PATHS, - VIEW_MODIFIABLE_FIELD_PATHS + VIEW_MODIFIABLE_FIELD_PATHS, + ROOT_MODIFIABLE_FIELD_PATHS ), [RumEventType.ACTION]: assign( { 'action.target.name': 'string', }, USER_CUSTOMIZABLE_FIELD_PATHS, - VIEW_MODIFIABLE_FIELD_PATHS + VIEW_MODIFIABLE_FIELD_PATHS, + ROOT_MODIFIABLE_FIELD_PATHS ), [RumEventType.LONG_TASK]: assign({}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS), [RumEventType.VITAL]: assign({}, USER_CUSTOMIZABLE_FIELD_PATHS, VIEW_MODIFIABLE_FIELD_PATHS), diff --git a/packages/rum-core/src/rumEvent.types.ts b/packages/rum-core/src/rumEvent.types.ts index 85710e6991..fa01660b57 100644 --- a/packages/rum-core/src/rumEvent.types.ts +++ b/packages/rum-core/src/rumEvent.types.ts @@ -1116,11 +1116,11 @@ export interface CommonProperties { /** * The service name for this application */ - readonly service?: string + service?: string /** * The version for this application */ - readonly version?: string + version?: string /** * The build version for this application */ diff --git a/rum-events-format b/rum-events-format index dcd62e5856..97908ae7f4 160000 --- a/rum-events-format +++ b/rum-events-format @@ -1 +1 @@ -Subproject commit dcd62e58566b9d158c404f3588edc62c041262dd +Subproject commit 97908ae7f45d27f7eaa3ebdf094acf92c9061d89 diff --git a/test/e2e/scenario/microfrontend.scenario.ts b/test/e2e/scenario/microfrontend.scenario.ts index 8ba5e47796..02874cf10c 100644 --- a/test/e2e/scenario/microfrontend.scenario.ts +++ b/test/e2e/scenario/microfrontend.scenario.ts @@ -5,6 +5,8 @@ import { flushEvents, createTest } from '../lib/framework' const HANDLING_STACK_REGEX = /^Error: \n\s+at testHandlingStack @/ const CONFIG: Partial = { + service: 'main-service', + version: '1.0.0', enableExperimentalFeatures: ['micro_frontend'], beforeSend: (event: RumEvent, domainContext: RumEventDomainContext) => { if ('handlingStack' in domainContext) { @@ -123,4 +125,34 @@ describe('microfrontend', () => { expect(event).toBeTruthy() expect(event?.context?.handlingStack).toMatch(HANDLING_STACK_REGEX) }) + + createTest('allow to modify service and version') + .withRum(CONFIG) + .withRumInit((configuration) => { + window.DD_RUM!.init({ + ...configuration, + beforeSend: (event: RumEvent) => { + if (event.type === 'resource') { + event.service = 'mf-service' + event.version = '0.1.0' + } + + return true + }, + }) + }) + .run(async ({ intakeRegistry }) => { + await flushEvents() + + const viewEvent = intakeRegistry.rumViewEvents[0] + const resourceEvent = intakeRegistry.rumResourceEvents[0] + + expect(viewEvent).toBeTruthy() + expect(viewEvent.service).toBe('main-service') + expect(viewEvent.version).toBe('1.0.0') + + expect(resourceEvent).toBeTruthy() + expect(resourceEvent.service).toBe('mf-service') + expect(resourceEvent.version).toBe('0.1.0') + }) })