Skip to content

Commit

Permalink
feat(core): Add client.init() to replace `client.setupIntegrations(…
Browse files Browse the repository at this point in the history
…)` (#10118)

This adds a new `client.init()` method to be called instead of
`client.setupIntegrations()`.

Note that this method simply initializes the integrations always, and
depends on the check that we don't add integrations multiple times.

This also has a bit of a different semantic, dropping the `force`
argument in favor of just calling `addIntegration()` again - this
depends on #10116 to
really work.
  • Loading branch information
mydea committed Jan 11, 2024
1 parent 324e5bf commit 7ab0fe8
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 27 deletions.
4 changes: 4 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ If you are using the `Hub` right now, see the following table on how to migrate
| endSession() | `Sentry.endSession()` |
| shouldSendDefaultPii() | REMOVED - The closest equivalent is `Sentry.getClient().getOptions().sendDefaultPii` |

## Deprecate `client.setupIntegrations()`

Instead, use the new `client.init()` method. You should probably not use this directly and instead use `Sentry.init()`, which calls this under the hood. But if you have a special use case that requires that, you can call `client.init()` instead now.

## Deprecate `scope.getSpan()` and `scope.setSpan()`

Instead, you can get the currently active span via `Sentry.getActiveSpan()`. Setting a span on the scope happens
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/test/unit/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jest.mock('@sentry/core', () => {
return new Scope();
},
bindClient(client: Client): boolean {
client.setupIntegrations();
client.init!();
return true;
},
};
Expand Down
20 changes: 17 additions & 3 deletions packages/core/src/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,19 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
}

/**
* Sets up the integrations
* This is an internal function to setup all integrations that should run on the client.
* @deprecated Use `client.init()` instead.
*/
public setupIntegrations(forceInitialize?: boolean): void {
if ((forceInitialize && !this._integrationsInitialized) || (this._isEnabled() && !this._integrationsInitialized)) {
this._integrations = setupIntegrations(this, this._options.integrations);
this._integrationsInitialized = true;
this._setupIntegrations();
}
}

/** @inheritdoc */
public init(): void {
if (this._isEnabled()) {
this._setupIntegrations();
}
}

Expand Down Expand Up @@ -512,6 +519,13 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {

/* eslint-enable @typescript-eslint/unified-signatures */

/** Setup integrations for this client. */
protected _setupIntegrations(): void {
this._integrations = setupIntegrations(this, this._options.integrations);
// TODO v8: We don't need this flag anymore
this._integrationsInitialized = true;
}

/** Updates existing session based on the provided event */
protected _updateSessionFromEvent(session: Session, event: Event): void {
let crashed = false;
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ export class Hub implements HubInterface {
const top = this.getStackTop();
top.client = client;
top.scope.setClient(client);
// eslint-disable-next-line deprecation/deprecation
if (client && client.setupIntegrations) {
// eslint-disable-next-line deprecation/deprecation
client.setupIntegrations();
}
}
Expand Down
74 changes: 69 additions & 5 deletions packages/core/test/lib/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ describe('BaseClient', () => {
test('adds installed integrations to sdk info', () => {
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] });
const client = new TestClient(options);
client.setupIntegrations();
client.init();

client.captureEvent({ message: 'message' });

Expand All @@ -695,7 +695,7 @@ describe('BaseClient', () => {

const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] });
const client = new TestClient(options);
client.setupIntegrations();
client.init();
client.addIntegration(new AdHocIntegration());

client.captureException(new Error('test exception'));
Expand All @@ -716,7 +716,7 @@ describe('BaseClient', () => {
integrations: [new TestIntegration(), null, undefined],
});
const client = new TestClient(options);
client.setupIntegrations();
client.init();

client.captureEvent({ message: 'message' });

Expand Down Expand Up @@ -1492,24 +1492,48 @@ describe('BaseClient', () => {

const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] });
const client = new TestClient(options);
// eslint-disable-next-line deprecation/deprecation
client.setupIntegrations();

expect(Object.keys((client as any)._integrations).length).toEqual(1);
expect(client.getIntegration(TestIntegration)).toBeTruthy();
});

test('skips installation if DSN is not provided', () => {
test('sets up each integration on `init` call', () => {
expect.assertions(2);

const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] });
const client = new TestClient(options);
client.init();

expect(Object.keys((client as any)._integrations).length).toEqual(1);
expect(client.getIntegration(TestIntegration)).toBeTruthy();
});

test('skips installation for `setupIntegrations()` if DSN is not provided', () => {
expect.assertions(2);

const options = getDefaultTestClientOptions({ integrations: [new TestIntegration()] });
const client = new TestClient(options);
// eslint-disable-next-line deprecation/deprecation
client.setupIntegrations();

expect(Object.keys((client as any)._integrations).length).toEqual(0);
expect(client.getIntegration(TestIntegration)).toBeFalsy();
});

test('skips installation if `enabled` is set to `false`', () => {
test('skips installation for `init()` if DSN is not provided', () => {
expect.assertions(2);

const options = getDefaultTestClientOptions({ integrations: [new TestIntegration()] });
const client = new TestClient(options);
client.init();

expect(Object.keys((client as any)._integrations).length).toEqual(0);
expect(client.getIntegration(TestIntegration)).toBeFalsy();
});

test('skips installation for `setupIntegrations()` if `enabled` is set to `false`', () => {
expect.assertions(2);

const options = getDefaultTestClientOptions({
Expand All @@ -1518,12 +1542,28 @@ describe('BaseClient', () => {
integrations: [new TestIntegration()],
});
const client = new TestClient(options);
// eslint-disable-next-line deprecation/deprecation
client.setupIntegrations();

expect(Object.keys((client as any)._integrations).length).toEqual(0);
expect(client.getIntegration(TestIntegration)).toBeFalsy();
});

test('skips installation for `init()` if `enabled` is set to `false`', () => {
expect.assertions(2);

const options = getDefaultTestClientOptions({
dsn: PUBLIC_DSN,
enabled: false,
integrations: [new TestIntegration()],
});
const client = new TestClient(options);
client.init();

expect(Object.keys((client as any)._integrations).length).toEqual(0);
expect(client.getIntegration(TestIntegration)).toBeFalsy();
});

test('skips installation if integrations are already installed', () => {
expect.assertions(4);

Expand All @@ -1533,17 +1573,41 @@ describe('BaseClient', () => {
const setupIntegrationsHelper = jest.spyOn(integrationModule, 'setupIntegrations');

// it should install the first time, because integrations aren't yet installed...
// eslint-disable-next-line deprecation/deprecation
client.setupIntegrations();

expect(Object.keys((client as any)._integrations).length).toEqual(1);
expect(client.getIntegration(TestIntegration)).toBeTruthy();
expect(setupIntegrationsHelper).toHaveBeenCalledTimes(1);

// ...but it shouldn't try to install a second time
// eslint-disable-next-line deprecation/deprecation
client.setupIntegrations();

expect(setupIntegrationsHelper).toHaveBeenCalledTimes(1);
});

test('does not add integrations twice when calling `init` multiple times', () => {
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] });
const client = new TestClient(options);
// 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.init();

expect(Object.keys((client as any)._integrations).length).toEqual(1);
expect(client.getIntegration(TestIntegration)).toBeTruthy();
expect(setupIntegrationsHelper).toHaveBeenCalledTimes(1);

client.init();

// is called again...
expect(setupIntegrationsHelper).toHaveBeenCalledTimes(2);

// but integrations are only added once anyhow!
expect(client['_integrations']).toEqual({ TestIntegration: expect.any(TestIntegration) });
});
});

describe('flush/close', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/lib/integrations/inboundfilters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function createInboundFiltersEventProcessor(
}),
);

client.setupIntegrations();
client.init();

const eventProcessors = client['_eventProcessors'];
const eventProcessor = eventProcessors.find(processor => processor.id === 'InboundFilters');
Expand Down
16 changes: 13 additions & 3 deletions packages/node-experimental/src/sdk/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
getSentryRelease,
makeNodeTransport,
} from '@sentry/node';
import type { Integration } from '@sentry/types';
import type { Client, Integration } from '@sentry/types';
import {
consoleSandbox,
dropUndefinedKeys,
Expand Down Expand Up @@ -67,7 +67,9 @@ export function init(options: NodeExperimentalOptions | undefined = {}): void {
// unless somebody specifically sets a different one on a scope/isolations cope
getGlobalScope().setClient(client);

client.setupIntegrations();
if (isEnabled(client)) {
client.init();
}

if (options.autoSessionTracking) {
startSessionTracking();
Expand All @@ -79,7 +81,11 @@ export function init(options: NodeExperimentalOptions | undefined = {}): void {
const client = getClient();
if (client.addIntegration) {
// force integrations to be setup even if no DSN was set
client.setupIntegrations(true);
// If they have already been added before, they will be ignored anyhow
const integrations = client.getOptions().integrations;
for (const integration of integrations) {
client.addIntegration(integration);
}
client.addIntegration(
new Integrations.Spotlight({
sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined,
Expand Down Expand Up @@ -213,3 +219,7 @@ function startSessionTracking(): void {
}
});
}

function isEnabled(client: Client): boolean {
return client.getOptions().enabled !== false && client.getTransport() !== undefined;
}
6 changes: 5 additions & 1 deletion packages/node/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ export function init(options: NodeOptions = {}): void {
const client = getClient();
if (client && client.addIntegration) {
// force integrations to be setup even if no DSN was set
client.setupIntegrations(true);
// If they have already been added before, they will be ignored anyhow
const integrations = client.getOptions().integrations;
for (const integration of integrations) {
client.addIntegration(integration);
}
client.addIntegration(
new Spotlight({ sidecarUrl: typeof options.spotlight === 'string' ? options.spotlight : undefined }),
);
Expand Down
24 changes: 12 additions & 12 deletions packages/node/test/integrations/localvariables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ describeIf(NODE_VERSION.major >= 18)('LocalVariables', () => {
const options = getDefaultNodeClientOptions({
stackParser: defaultStackParser,
includeLocalVariables: true,
integrations: [localVariables],
integrations: [],
});

const client = new NodeClient(options);
client.setupIntegrations(true);
client.addIntegration(localVariables);

const eventProcessors = client['_eventProcessors'];
const eventProcessor = eventProcessors.find(processor => processor.id === 'LocalVariables');
Expand Down Expand Up @@ -253,11 +253,11 @@ describeIf(NODE_VERSION.major >= 18)('LocalVariables', () => {
const options = getDefaultNodeClientOptions({
stackParser: defaultStackParser,
includeLocalVariables: true,
integrations: [localVariables],
integrations: [],
});

const client = new NodeClient(options);
client.setupIntegrations(true);
client.addIntegration(localVariables);

await session.runPause(exceptionEvent100Frames);

Expand All @@ -278,11 +278,11 @@ describeIf(NODE_VERSION.major >= 18)('LocalVariables', () => {
const options = getDefaultNodeClientOptions({
stackParser: defaultStackParser,
includeLocalVariables: true,
integrations: [localVariables],
integrations: [],
});

const client = new NodeClient(options);
client.setupIntegrations(true);
client.addIntegration(localVariables);

const nonExceptionEvent = {
method: exceptionEvent.method,
Expand All @@ -299,11 +299,11 @@ describeIf(NODE_VERSION.major >= 18)('LocalVariables', () => {
const localVariables = new LocalVariablesSync({}, session);
const options = getDefaultNodeClientOptions({
stackParser: defaultStackParser,
integrations: [localVariables],
integrations: [],
});

const client = new NodeClient(options);
client.setupIntegrations(true);
client.addIntegration(localVariables);

const eventProcessors = client['_eventProcessors'];
const eventProcessor = eventProcessors.find(processor => processor.id === 'LocalVariables');
Expand All @@ -315,11 +315,11 @@ describeIf(NODE_VERSION.major >= 18)('LocalVariables', () => {
const localVariables = new LocalVariablesSync({}, undefined);
const options = getDefaultNodeClientOptions({
stackParser: defaultStackParser,
integrations: [localVariables],
integrations: [],
});

const client = new NodeClient(options);
client.setupIntegrations(true);
client.addIntegration(localVariables);

const eventProcessors = client['_eventProcessors'];
const eventProcessor = eventProcessors.find(processor => processor.id === 'LocalVariables');
Expand All @@ -336,11 +336,11 @@ describeIf(NODE_VERSION.major >= 18)('LocalVariables', () => {
const options = getDefaultNodeClientOptions({
stackParser: defaultStackParser,
includeLocalVariables: true,
integrations: [localVariables],
integrations: [],
});

const client = new NodeClient(options);
client.setupIntegrations(true);
client.addIntegration(localVariables);

await session.runPause(exceptionEvent);
await session.runPause(exceptionEvent);
Expand Down
11 changes: 10 additions & 1 deletion packages/types/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,18 @@ export interface Client<O extends ClientOptions = ClientOptions> {
* */
addIntegration?(integration: Integration): void;

/** This is an internal function to setup all integrations that should run on the client */
/**
* This is an internal function to setup all integrations that should run on the client.
* @deprecated Use `client.init()` instead.
*/
setupIntegrations(forceInitialize?: boolean): void;

/**
* Initialize this client.
* Call this after the client was set on a scope.
*/
init?(): void;

/** Creates an {@link Event} from all inputs to `captureException` and non-primitive inputs to `captureMessage`. */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
eventFromException(exception: any, hint?: EventHint): PromiseLike<Event>;
Expand Down

0 comments on commit 7ab0fe8

Please sign in to comment.