diff --git a/packages/core/src/envelope.ts b/packages/core/src/envelope.ts index 1c148015ae63..1a2f44fd82c6 100644 --- a/packages/core/src/envelope.ts +++ b/packages/core/src/envelope.ts @@ -2,7 +2,6 @@ import { DsnComponents, Event, EventEnvelope, - EventEnvelopeHeaders, EventItem, SdkInfo, SdkMetadata, @@ -11,7 +10,12 @@ import { SessionEnvelope, SessionItem, } from '@sentry/types'; -import { createEnvelope, dropUndefinedKeys, dsnToString, getSdkMetadataForEnvelopeHeader } from '@sentry/utils'; +import { + createEnvelope, + createEventEnvelopeHeaders, + dsnToString, + getSdkMetadataForEnvelopeHeader, +} from '@sentry/utils'; /** * Apply SdkInfo (name, version, packages, integrations) to the corresponding event key. @@ -74,23 +78,3 @@ export function createEventEnvelope( const eventItem: EventItem = [{ type: eventType }, event]; return createEnvelope(envelopeHeaders, [eventItem]); } - -function createEventEnvelopeHeaders( - event: Event, - sdkInfo: SdkInfo | undefined, - tunnel: string | undefined, - dsn: DsnComponents, -): EventEnvelopeHeaders { - const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata.dynamicSamplingContext; - - return { - event_id: event.event_id as string, - sent_at: new Date().toISOString(), - ...(sdkInfo && { sdk: sdkInfo }), - ...(!!tunnel && { dsn: dsnToString(dsn) }), - ...(event.type === 'transaction' && - dynamicSamplingContext && { - trace: dropUndefinedKeys({ ...dynamicSamplingContext }), - }), - }; -} diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 5a2e7f285f57..f83ed70b109b 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -921,8 +921,9 @@ export class ReplayContainer implements ReplayContainerInterface { const client = hub.getClient(); const scope = hub.getScope(); const transport = client && client.getTransport(); + const dsn = client?.getDsn(); - if (!client || !scope || !transport) { + if (!client || !scope || !transport || !dsn) { return; } @@ -982,7 +983,7 @@ export class ReplayContainer implements ReplayContainerInterface { } */ - const envelope = createReplayEnvelope(replayId, replayEvent, payloadWithSequence); + const envelope = createReplayEnvelope(replayEvent, payloadWithSequence, dsn, client.getOptions().tunnel); try { return transport.send(envelope); diff --git a/packages/replay/src/util/createReplayEnvelope.ts b/packages/replay/src/util/createReplayEnvelope.ts index c42ff1fa8d5d..8850974ec18b 100644 --- a/packages/replay/src/util/createReplayEnvelope.ts +++ b/packages/replay/src/util/createReplayEnvelope.ts @@ -1,17 +1,14 @@ -import { Envelope, Event } from '@sentry/types'; -import { createEnvelope, getSdkMetadataForEnvelopeHeader } from '@sentry/utils'; +import { DsnComponents, Envelope, Event } from '@sentry/types'; +import { createEnvelope, createEventEnvelopeHeaders, getSdkMetadataForEnvelopeHeader } from '@sentry/utils'; export function createReplayEnvelope( - replayId: string, replayEvent: Event, payloadWithSequence: string | Uint8Array, + dsn: DsnComponents, + tunnel?: string, ): Envelope { return createEnvelope( - { - event_id: replayId, - sent_at: new Date().toISOString(), - sdk: getSdkMetadataForEnvelopeHeader(replayEvent), - }, + createEventEnvelopeHeaders(replayEvent, getSdkMetadataForEnvelopeHeader(replayEvent), tunnel, dsn), [ // @ts-ignore New types [{ type: 'replay_event' }, replayEvent], diff --git a/packages/replay/test/unit/util/createReplayEnvelope.test.ts b/packages/replay/test/unit/util/createReplayEnvelope.test.ts index a3564f0c34da..76fd3348cecb 100644 --- a/packages/replay/test/unit/util/createReplayEnvelope.test.ts +++ b/packages/replay/test/unit/util/createReplayEnvelope.test.ts @@ -1,41 +1,86 @@ import { Event } from '@sentry/types'; +import { makeDsn } from '@sentry/utils'; import { createReplayEnvelope } from '../../../src/util/createReplayEnvelope'; describe('createReplayEnvelope', () => { + const REPLAY_ID = 'MY_REPLAY_ID'; + + const replayEvent = { + type: 'replay_event', + timestamp: 1670837008.634, + error_ids: ['errorId'], + trace_ids: ['traceId'], + urls: ['https://example.com'], + replay_id: REPLAY_ID, + segment_id: 3, + platform: 'javascript', + event_id: REPLAY_ID, + environment: 'production', + sdk: { + integrations: ['BrowserTracing', 'Replay'], + name: 'sentry.javascript.browser', + version: '7.25.0', + }, + tags: { + sessionSampleRate: 1, + errorSampleRate: 0, + replayType: 'error', + }, + }; + + const payloadWithSequence = 'payload'; + + const dsn = makeDsn({ + host: 'sentry.io', + pass: 'xyz', + port: '1234', + projectId: '123', + protocol: 'https', + publicKey: 'abc', + }); + it('creates an envelope for a given Replay event', () => { - const replayId = '1234'; - const replayEvent = { - type: 'replay_event', - timestamp: 1670837008.634, - error_ids: ['errorId'], - trace_ids: ['traceId'], - urls: ['https://example.com'], - replay_id: 'eventId', - segment_id: 3, - platform: 'javascript', - event_id: 'eventId', - environment: 'production', - sdk: { - integrations: ['BrowserTracing', 'Replay'], - name: 'sentry.javascript.browser', - version: '7.25.0', - }, - tags: { - sessionSampleRate: 1, - errorSampleRate: 0, - replayType: 'error', + const envelope = createReplayEnvelope(replayEvent as Event, payloadWithSequence, dsn); + + expect(envelope).toEqual([ + { + event_id: REPLAY_ID, + sdk: { name: 'sentry.javascript.browser', version: '7.25.0' }, + sent_at: expect.any(String), }, - }; - const payloadWithSequence = 'payload'; + [ + [ + { type: 'replay_event' }, + { + environment: 'production', + error_ids: ['errorId'], + event_id: REPLAY_ID, + platform: 'javascript', + replay_id: REPLAY_ID, + sdk: { integrations: ['BrowserTracing', 'Replay'], name: 'sentry.javascript.browser', version: '7.25.0' }, + segment_id: 3, + tags: { errorSampleRate: 0, replayType: 'error', sessionSampleRate: 1 }, + timestamp: 1670837008.634, + trace_ids: ['traceId'], + type: 'replay_event', + urls: ['https://example.com'], + }, + ], + [{ length: 7, type: 'replay_recording' }, 'payload'], + ], + ]); + }); - const envelope = createReplayEnvelope(replayId, replayEvent as Event, payloadWithSequence); + it('creates an envelope with the `dsn` key in the header if `tunnel` is specified', () => { + const envelope = createReplayEnvelope(replayEvent as Event, payloadWithSequence, dsn, '/my-tunnel-endpoint'); expect(envelope).toEqual([ { - event_id: '1234', + event_id: REPLAY_ID, sdk: { name: 'sentry.javascript.browser', version: '7.25.0' }, sent_at: expect.any(String), + dsn: 'https://abc@sentry.io:1234/123', }, [ [ @@ -43,9 +88,9 @@ describe('createReplayEnvelope', () => { { environment: 'production', error_ids: ['errorId'], - event_id: 'eventId', + event_id: REPLAY_ID, platform: 'javascript', - replay_id: 'eventId', + replay_id: REPLAY_ID, sdk: { integrations: ['BrowserTracing', 'Replay'], name: 'sentry.javascript.browser', version: '7.25.0' }, segment_id: 3, tags: { errorSampleRate: 0, replayType: 'error', sessionSampleRate: 1 }, diff --git a/packages/utils/src/envelope.ts b/packages/utils/src/envelope.ts index 3d6dbd6706cd..92037d18ddfe 100644 --- a/packages/utils/src/envelope.ts +++ b/packages/utils/src/envelope.ts @@ -2,15 +2,18 @@ import { Attachment, AttachmentItem, DataCategory, + DsnComponents, Envelope, EnvelopeItem, EnvelopeItemType, Event, + EventEnvelopeHeaders, SdkInfo, SdkMetadata, TextEncoderInternal, } from '@sentry/types'; +import { dsnToString } from './dsn'; import { normalize } from './normalize'; import { dropUndefinedKeys } from './object'; @@ -154,3 +157,27 @@ export function getSdkMetadataForEnvelopeHeader(metadataOrEvent?: SdkMetadata | const { name, version } = metadataOrEvent.sdk; return { name, version }; } + +/** + * Creates event envelope headers, based on event, sdk info and tunnel + * Note: This function was extracted from the core package to make it available in Replay + */ +export function createEventEnvelopeHeaders( + event: Event, + sdkInfo: SdkInfo | undefined, + tunnel: string | undefined, + dsn: DsnComponents, +): EventEnvelopeHeaders { + const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata.dynamicSamplingContext; + + return { + event_id: event.event_id as string, + sent_at: new Date().toISOString(), + ...(sdkInfo && { sdk: sdkInfo }), + ...(!!tunnel && { dsn: dsnToString(dsn) }), + ...(event.type === 'transaction' && + dynamicSamplingContext && { + trace: dropUndefinedKeys({ ...dynamicSamplingContext }), + }), + }; +}