Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/src/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
* Sets up the integrations
*/
public setupIntegrations(): void {
if (this._isEnabled()) {
if (this._isEnabled() && !this._integrations.initialized) {
this._integrations = setupIntegrations(this._options);
}
}
Expand Down
8 changes: 6 additions & 2 deletions packages/core/src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { logger } from '@sentry/utils';
export const installedIntegrations: string[] = [];

/** Map of integrations assigned to a client */
export interface IntegrationIndex {
export type IntegrationIndex = {
[key: string]: Integration;
}
} & { initialized?: boolean };

/**
* @private
Expand Down Expand Up @@ -74,5 +74,9 @@ export function setupIntegrations<O extends Options>(options: O): IntegrationInd
integrations[integration.name] = integration;
setupIntegration(integration);
});
// set the `initialized` flag so we don't run through the process again unecessarily; use `Object.defineProperty`
// because by default it creates a property which is nonenumerable, which we want since `initialized` shouldn't be
// considered a member of the index the way the actual integrations are
Object.defineProperty(integrations, 'initialized', { value: true });
return integrations;
}
21 changes: 21 additions & 0 deletions packages/core/test/lib/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Hub, Scope, Session } from '@sentry/hub';
import { Event, Severity, Span } from '@sentry/types';
import { logger, SentryError, SyncPromise } from '@sentry/utils';

import * as integrationModule from '../../src/integration';
import { TestBackend } from '../mocks/backend';
import { TestClient } from '../mocks/client';
import { TestIntegration } from '../mocks/integration';
Expand Down Expand Up @@ -872,6 +873,26 @@ describe('BaseClient', () => {
expect(Object.keys((client as any)._integrations).length).toBe(0);
expect(client.getIntegration(TestIntegration)).toBeFalsy();
});

test('skips installation if integrations are already installed', () => {
expect.assertions(4);
const client = new TestClient({
dsn: PUBLIC_DSN,
integrations: [new TestIntegration()],
});
// note: not the `Client` method `setupIntegrations`, but the free-standing function which that method calls
const setupIntegrationsHelper = jest.spyOn(integrationModule, 'setupIntegrations');

// it should install the first time, because integrations aren't yet installed...
client.setupIntegrations();
expect(Object.keys((client as any)._integrations).length).toBe(1);
expect(client.getIntegration(TestIntegration)).toBeTruthy();
expect(setupIntegrationsHelper).toHaveBeenCalledTimes(1);

// ...but it shouldn't try to install a second time
client.setupIntegrations();
expect(setupIntegrationsHelper).toHaveBeenCalledTimes(1);
});
});

describe('flush/close', () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/hub/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ export class Hub implements HubInterface {
*/
public constructor(client?: Client, scope: Scope = new Scope(), private readonly _version: number = API_VERSION) {
this.getStackTop().scope = scope;
this.bindClient(client);
if (client) {
this.bindClient(client);
}
}

/**
Expand Down