Skip to content

feat(core): Add client.init() to replace client.setupIntegrations() #10118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 11, 2024
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
4 changes: 4 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ npx @sentry/migr8@latest

This will let you select which updates to run, and automatically update your code. Make sure to still review all code changes!

## 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()`.
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 @@ -167,7 +167,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 @@ -671,7 +671,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 @@ -685,7 +685,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 @@ -706,7 +706,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 @@ -1482,24 +1482,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 @@ -1508,12 +1532,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 @@ -1523,17 +1563,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