diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 647d98b61f63..09200e30c503 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -435,6 +435,10 @@ export abstract class BaseClient implements Client { */ protected _prepareEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike { const options = this.getOptions(); + const integrations = Object.keys(this._integrations); + if (!hint.integrations && integrations.length > 0) { + hint.integrations = integrations; + } return prepareEvent(options, event, hint, scope); } diff --git a/packages/core/src/utils/prepareEvent.ts b/packages/core/src/utils/prepareEvent.ts index 3aa0d86df3f6..73a71e138db2 100644 --- a/packages/core/src/utils/prepareEvent.ts +++ b/packages/core/src/utils/prepareEvent.ts @@ -32,12 +32,10 @@ export function prepareEvent( event_id: event.event_id || hint.event_id || uuid4(), timestamp: event.timestamp || dateTimestampInSeconds(), }; + const integrations = hint.integrations || options.integrations.map(i => i.name); applyClientOptions(prepared, options); - applyIntegrationsMetadata( - prepared, - options.integrations.map(i => i.name), - ); + applyIntegrationsMetadata(prepared, integrations); // If we have scope given to us, use it as the base for further modifications. // This allows us to prevent unnecessary copying of data if `captureContext` is not provided. diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index c82014515067..2925c6e213c7 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -4,7 +4,7 @@ import { dsnToString, logger, SentryError, SyncPromise } from '@sentry/utils'; import { Hub, makeSession, Scope } from '../../src'; import * as integrationModule from '../../src/integration'; import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; -import { TestIntegration } from '../mocks/integration'; +import { AdHocIntegration, TestIntegration } from '../mocks/integration'; import { makeFakeTransport } from '../mocks/transport'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -678,6 +678,25 @@ describe('BaseClient', () => { }); }); + test('send all installed integrations in event sdk metadata', () => { + expect.assertions(1); + + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); + const client = new TestClient(options); + client.setupIntegrations(); + client.addIntegration(new AdHocIntegration()); + + client.captureException(new Error('test exception')); + + expect(TestClient.instance!.event).toEqual( + expect.objectContaining({ + sdk: expect.objectContaining({ + integrations: expect.arrayContaining(['TestIntegration', 'AdHockIntegration']), + }), + }), + ); + }); + test('normalizes event with default depth of 3', () => { expect.assertions(1); diff --git a/packages/core/test/mocks/integration.ts b/packages/core/test/mocks/integration.ts index ff01158b0632..ce95d04520a7 100644 --- a/packages/core/test/mocks/integration.ts +++ b/packages/core/test/mocks/integration.ts @@ -36,3 +36,13 @@ export class AddAttachmentTestIntegration implements Integration { }); } } + +export class AdHocIntegration implements Integration { + public static id: string = 'AdHockIntegration'; + + public name: string = 'AdHockIntegration'; + + public setupOnce(): void { + // Noop + } +} diff --git a/packages/replay/src/util/prepareReplayEvent.ts b/packages/replay/src/util/prepareReplayEvent.ts index c2b645e92bdc..522e7e7689bc 100644 --- a/packages/replay/src/util/prepareReplayEvent.ts +++ b/packages/replay/src/util/prepareReplayEvent.ts @@ -1,5 +1,6 @@ import type { Scope } from '@sentry/core'; import { prepareEvent } from '@sentry/core'; +import type { IntegrationIndex } from '@sentry/core/build/types/integration'; import type { Client, ReplayEvent } from '@sentry/types'; /** @@ -11,12 +12,21 @@ export async function prepareReplayEvent({ replayId: event_id, event, }: { - client: Client; + client: Client & { _integrations?: IntegrationIndex }; scope: Scope; replayId: string; event: ReplayEvent; }): Promise { - const preparedEvent = (await prepareEvent(client.getOptions(), event, { event_id }, scope)) as ReplayEvent | null; + const integrations = + typeof client._integrations === 'object' && client._integrations !== null && !Array.isArray(client._integrations) + ? Object.keys(client._integrations) + : undefined; + const preparedEvent = (await prepareEvent( + client.getOptions(), + event, + { event_id, integrations }, + scope, + )) as ReplayEvent | null; // If e.g. a global event processor returned null if (!preparedEvent) { diff --git a/packages/types/src/event.ts b/packages/types/src/event.ts index 86280fc7e65b..7c00bcef4718 100644 --- a/packages/types/src/event.ts +++ b/packages/types/src/event.ts @@ -80,4 +80,5 @@ export interface EventHint { originalException?: Error | string | null; attachments?: Attachment[]; data?: any; + integrations?: string[]; } diff --git a/rollup/plugins/bundlePlugins.js b/rollup/plugins/bundlePlugins.js index 5e483dfddc46..0fb662b7e3e4 100644 --- a/rollup/plugins/bundlePlugins.js +++ b/rollup/plugins/bundlePlugins.js @@ -119,6 +119,8 @@ export function makeTerserPlugin() { // We want to keept he _replay and _isEnabled variable unmangled to enable integration tests to access it '_replay', '_isEnabled', + // We want to keep the _integrations variable unmangled to send all installed integrations from replay + '_integrations', ], }, },