diff --git a/src/features/session_replay/aggregate/index.js b/src/features/session_replay/aggregate/index.js index 6e6b16d6a..1178c54be 100644 --- a/src/features/session_replay/aggregate/index.js +++ b/src/features/session_replay/aggregate/index.js @@ -28,6 +28,7 @@ import { deregisterDrain } from '../../../common/drain/drain' import { now } from '../../../common/timing/now' import { buildNRMetaNode } from '../shared/utils' import { MAX_PAYLOAD_SIZE } from '../../../common/constants/agent-constants' +import { cleanURL } from '../../../common/url/clean-url' export class Aggregate extends AggregateBase { static featureName = FEATURE_NAME @@ -362,7 +363,8 @@ export class Aggregate extends AggregateBase { 'rrweb.version': RRWEB_VERSION, 'payload.type': recorderEvents.type, // customer-defined data should go last so that if it exceeds the query param padding limit it will be truncated instead of important attrs - ...(endUserId && { 'enduser.id': this.obfuscator.obfuscateString(endUserId) }) + ...(endUserId && { 'enduser.id': this.obfuscator.obfuscateString(endUserId) }), + currentUrl: this.obfuscator.obfuscateString(cleanURL('' + location)) // The Query Param is being arbitrarily limited in length here. It is also applied when estimating the size of the payload in getPayloadSize() }, QUERY_PARAM_PADDING).substring(1) // remove the leading '&' }, diff --git a/src/features/session_trace/aggregate/index.js b/src/features/session_trace/aggregate/index.js index c034f0e74..8df797cfe 100644 --- a/src/features/session_trace/aggregate/index.js +++ b/src/features/session_trace/aggregate/index.js @@ -11,6 +11,7 @@ import { deregisterDrain } from '../../../common/drain/drain' import { globalScope } from '../../../common/constants/runtime' import { MODE, SESSION_EVENTS } from '../../../common/session/constants' import { applyFnToProps } from '../../../common/util/traverse' +import { cleanURL } from '../../../common/url/clean-url' const ERROR_MODE_SECONDS_WINDOW = 30 * 1000 // sliding window of nodes to track when simply monitoring (but not harvesting) in error mode /** Reserved room for query param attrs */ @@ -172,7 +173,8 @@ export class Aggregate extends AggregateBase { ptid: `${this.ptid}`, session: `${this.sessionId}`, // customer-defined data should go last so that if it exceeds the query param padding limit it will be truncated instead of important attrs - ...(endUserId && { 'enduser.id': this.obfuscator.obfuscateString(endUserId) }) + ...(endUserId && { 'enduser.id': this.obfuscator.obfuscateString(endUserId) }), + currentUrl: this.obfuscator.obfuscateString(cleanURL('' + location)) // The Query Param is being arbitrarily limited in length here. It is also applied when estimating the size of the payload in getPayloadSize() }, QUERY_PARAM_PADDING).substring(1) // remove the leading '&' }, diff --git a/tests/specs/session-replay/payload.e2e.js b/tests/specs/session-replay/payload.e2e.js index 09e594ac1..f8b2ea7a5 100644 --- a/tests/specs/session-replay/payload.e2e.js +++ b/tests/specs/session-replay/payload.e2e.js @@ -82,7 +82,15 @@ describe('Session Replay Payload Validation', () => { .then(() => browser.getAgentSessionInfo()) ]) - testExpectedReplay({ data: sessionReplayHarvest.request, session, hasError: false, hasMeta: true, hasSnapshot: true, isFirstChunk: true }) + testExpectedReplay({ + data: sessionReplayHarvest.request, + session, + hasError: false, + hasMeta: true, + hasSnapshot: true, + isFirstChunk: true, + currentUrl: sessionReplayHarvest.request.headers.origin + '/tests/assets/rrweb-instrumented.html' + }) }) it('should match expected payload - error', async () => { diff --git a/tests/specs/session-trace/payload-metadata.e2e.js b/tests/specs/session-trace/payload-metadata.e2e.js index a8dde1c63..704a05524 100644 --- a/tests/specs/session-trace/payload-metadata.e2e.js +++ b/tests/specs/session-trace/payload-metadata.e2e.js @@ -32,6 +32,13 @@ describe('STN Payload metadata checks', () => { const firstTimestampOffset = request.body.reduce((acc, next) => (next.s < acc) ? next.s : acc, Infinity) const lastTimestampOffset = request.body.reduce((acc, next) => (next.e > acc) ? next.e : acc, 0) // first session harvest is not reported if session is disabled - testExpectedTrace({ data: request, nodeCount: request.body.length, firstTimestampOffset, lastTimestampOffset, firstSessionHarvest: true }) + testExpectedTrace({ + data: request, + nodeCount: request.body.length, + firstTimestampOffset, + lastTimestampOffset, + firstSessionHarvest: true, + currentUrl: request.headers.origin + '/tests/assets/instrumented.html' + }) }) }) diff --git a/tests/specs/util/helpers.js b/tests/specs/util/helpers.js index a40722d5d..0a54bc20d 100644 --- a/tests/specs/util/helpers.js +++ b/tests/specs/util/helpers.js @@ -14,7 +14,7 @@ export const RRWEB_EVENT_TYPES = { Custom: 5 } -export function testExpectedReplay ({ data, session, hasMeta, hasSnapshot, hasError, isFirstChunk, contentEncoding, decompressedBytes, appId, entityGuid, harvestId }) { +export function testExpectedReplay ({ data, session, hasMeta, hasSnapshot, hasError, isFirstChunk, contentEncoding, decompressedBytes, appId, entityGuid, harvestId, currentUrl }) { expect(data.query).toMatchObject({ browser_monitoring_key: expect.any(String), type: 'SessionReplay', @@ -41,7 +41,8 @@ export function testExpectedReplay ({ data, session, hasMeta, hasSnapshot, hasEr isFirstChunk: isFirstChunk || expect.any(Boolean), decompressedBytes: decompressedBytes || expect.any(Number), 'rrweb.version': expect.any(String), - inlinedAllStylesheets: expect.any(Boolean) + inlinedAllStylesheets: expect.any(Boolean), + ...(currentUrl && { currentUrl }) }) expect(data.body).toEqual(expect.any(Array)) @@ -57,7 +58,8 @@ export function testExpectedTrace ({ session, ptid, harvestId, - entityGuid + entityGuid, + currentUrl }) { expect(data.query).toMatchObject({ browser_monitoring_key: expect.any(String), @@ -77,6 +79,7 @@ export function testExpectedTrace ({ 'trace.nodes': nodeCount || expect.any(Number), ptid: ptid || expect.anything(), session: session || expect.any(String), + ...(currentUrl && { currentUrl }), // optional attrs here ...(firstSessionHarvest && { firstSessionHarvest }), ...(hasReplay && { hasReplay })