Skip to content

Commit 692e9c6

Browse files
authored
feat(core): Add top level getClient() method (#9638)
This can be used instead of `getCurrentHub().getClient()`. This prepares us also for a post-hub time, but makes sense generally IMHO, also simplifies user-facing interaction with the client (e.g. to lazy-add integrations etc). This is a 1-1 replacement, so should be pretty straightforward.
1 parent 92c9fbb commit 692e9c6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+150
-123
lines changed

packages/angular/src/errorhandler.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class SentryErrorHandler implements AngularErrorHandler {
115115

116116
// Optionally show user dialog to provide details on what happened.
117117
if (this._options.showDialog) {
118-
const client = Sentry.getCurrentHub().getClient();
118+
const client = Sentry.getClient();
119119

120120
if (client && client.on && !this._registeredAfterSendEventHandler) {
121121
client.on('afterSendEvent', (event: Event) => {

packages/angular/test/errorhandler.test.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { HttpErrorResponse } from '@angular/common/http';
22
import * as SentryBrowser from '@sentry/browser';
3-
import type { Event } from '@sentry/types';
3+
import type { Client, Event } from '@sentry/types';
44

55
import { createErrorHandler, SentryErrorHandler } from '../src/errorhandler';
66

@@ -505,10 +505,7 @@ describe('SentryErrorHandler', () => {
505505
}),
506506
};
507507

508-
// @ts-expect-error this is a minmal hub, we're missing a few props but that's ok
509-
jest.spyOn(SentryBrowser, 'getCurrentHub').mockImplementationOnce(() => {
510-
return { getClient: () => client };
511-
});
508+
jest.spyOn(SentryBrowser, 'getClient').mockImplementationOnce(() => client as unknown as Client);
512509

513510
const showReportDialogSpy = jest.spyOn(SentryBrowser, 'showReportDialog');
514511

packages/astro/src/index.server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export {
2121
getActiveTransaction,
2222
getHubFromCarrier,
2323
getCurrentHub,
24+
getClient,
2425
Hub,
2526
makeMain,
2627
Scope,

packages/astro/test/client/sdk.test.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { BrowserClient } from '@sentry/browser';
22
import * as SentryBrowser from '@sentry/browser';
3-
import { BrowserTracing, getCurrentHub, SDK_VERSION, WINDOW } from '@sentry/browser';
3+
import { BrowserTracing, getClient, getCurrentHub, SDK_VERSION, WINDOW } from '@sentry/browser';
44
import { vi } from 'vitest';
55

66
import { init } from '../../../astro/src/client/sdk';
@@ -60,7 +60,7 @@ describe('Sentry client SDK', () => {
6060
});
6161

6262
const integrationsToInit = browserInit.mock.calls[0][0]?.integrations;
63-
const browserTracing = (getCurrentHub().getClient() as BrowserClient)?.getIntegrationById('BrowserTracing');
63+
const browserTracing = getClient<BrowserClient>()?.getIntegrationById('BrowserTracing');
6464

6565
expect(integrationsToInit).toContainEqual(expect.objectContaining({ name: 'BrowserTracing' }));
6666
expect(browserTracing).toBeDefined();
@@ -76,7 +76,7 @@ describe('Sentry client SDK', () => {
7676
});
7777

7878
const integrationsToInit = browserInit.mock.calls[0][0]?.integrations;
79-
const browserTracing = (getCurrentHub().getClient() as BrowserClient)?.getIntegrationById('BrowserTracing');
79+
const browserTracing = getClient<BrowserClient>()?.getIntegrationById('BrowserTracing');
8080

8181
expect(integrationsToInit).not.toContainEqual(expect.objectContaining({ name: 'BrowserTracing' }));
8282
expect(browserTracing).toBeUndefined();
@@ -91,7 +91,7 @@ describe('Sentry client SDK', () => {
9191
});
9292

9393
const integrationsToInit = browserInit.mock.calls[0][0]?.integrations;
94-
const browserTracing = (getCurrentHub().getClient() as BrowserClient)?.getIntegrationById('BrowserTracing');
94+
const browserTracing = getClient<BrowserClient>()?.getIntegrationById('BrowserTracing');
9595

9696
expect(integrationsToInit).not.toContainEqual(expect.objectContaining({ name: 'BrowserTracing' }));
9797
expect(browserTracing).toBeUndefined();
@@ -108,9 +108,7 @@ describe('Sentry client SDK', () => {
108108

109109
const integrationsToInit = browserInit.mock.calls[0][0]?.integrations;
110110

111-
const browserTracing = (getCurrentHub().getClient() as BrowserClient)?.getIntegrationById(
112-
'BrowserTracing',
113-
) as BrowserTracing;
111+
const browserTracing = getClient<BrowserClient>()?.getIntegrationById('BrowserTracing') as BrowserTracing;
114112
const options = browserTracing.options;
115113

116114
expect(integrationsToInit).toContainEqual(expect.objectContaining({ name: 'BrowserTracing' }));

packages/browser/src/exports.ts

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export {
3333
flush,
3434
getHubFromCarrier,
3535
getCurrentHub,
36+
getClient,
3637
Hub,
3738
lastEventId,
3839
makeMain,

packages/browser/src/integrations/breadcrumbs.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
severityLevelFromString,
2020
} from '@sentry/utils';
2121

22+
import { getClient } from '../exports';
2223
import { WINDOW } from '../helpers';
2324

2425
type HandlerData = Record<string, unknown>;
@@ -103,7 +104,7 @@ export class Breadcrumbs implements Integration {
103104
addInstrumentationHandler('history', _historyBreadcrumb);
104105
}
105106
if (this.options.sentry) {
106-
const client = getCurrentHub().getClient();
107+
const client = getClient();
107108
client && client.on && client.on('beforeSendEvent', addSentryBreadcrumb);
108109
}
109110
}

packages/browser/src/profiling/utils.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { DebugImage, Envelope, Event, StackFrame, StackParser, Transaction
55
import type { Profile, ThreadCpuProfile } from '@sentry/types/src/profiling';
66
import { browserPerformanceTimeOrigin, forEachEnvelopeItem, GLOBAL_OBJ, logger, uuid4 } from '@sentry/utils';
77

8+
import { getClient } from '../exports';
89
import { WINDOW } from '../helpers';
910
import type { JSSelfProfile, JSSelfProfiler, JSSelfProfilerConstructor, JSSelfProfileStack } from './jsSelfProfiling';
1011

@@ -532,7 +533,7 @@ export function shouldProfileTransaction(transaction: Transaction): boolean {
532533
return false;
533534
}
534535

535-
const client = getCurrentHub().getClient();
536+
const client = getClient();
536537
const options = client && client.getOptions();
537538
if (!options) {
538539
__DEBUG_BUILD__ && logger.log('[Profiling] Profiling disabled, no options found.');

packages/browser/src/sdk.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Hub } from '@sentry/core';
22
import {
3+
getClient,
34
getCurrentHub,
45
getIntegrationsToSetup,
56
getReportDialogEndpoint,
@@ -252,7 +253,7 @@ function startSessionTracking(): void {
252253
* Captures user feedback and sends it to Sentry.
253254
*/
254255
export function captureUserFeedback(feedback: UserFeedback): void {
255-
const client = getCurrentHub().getClient<BrowserClient>();
256+
const client = getClient<BrowserClient>();
256257
if (client) {
257258
client.captureUserFeedback(feedback);
258259
}

packages/browser/test/unit/eventbuilder.test.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
import type { Client } from '@sentry/types';
2-
31
import { defaultStackParser } from '../../src';
42
import { eventFromPlainObject } from '../../src/eventbuilder';
53

64
jest.mock('@sentry/core', () => {
75
const original = jest.requireActual('@sentry/core');
86
return {
97
...original,
10-
getCurrentHub(): {
11-
getClient(): Client;
12-
} {
8+
getClient() {
9+
return {
10+
getOptions(): any {
11+
return { normalizeDepth: 6 };
12+
},
13+
};
14+
},
15+
getCurrentHub() {
1316
return {
1417
getClient(): any {
1518
return {

packages/browser/test/unit/index.test.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
captureMessage,
1212
configureScope,
1313
flush,
14+
getClient,
1415
getCurrentHub,
1516
init,
1617
Integrations,
@@ -271,31 +272,31 @@ describe('SentryBrowser initialization', () => {
271272
it('should set SDK data when Sentry.init() is called', () => {
272273
init({ dsn });
273274

274-
const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk;
275+
const sdkData = getClient()?.getOptions()._metadata?.sdk || {};
275276

276277
expect(sdkData?.name).toBe('sentry.javascript.browser');
277-
expect(sdkData?.packages[0].name).toBe('npm:@sentry/browser');
278-
expect(sdkData?.packages[0].version).toBe(SDK_VERSION);
278+
expect(sdkData?.packages?.[0].name).toBe('npm:@sentry/browser');
279+
expect(sdkData?.packages?.[0].version).toBe(SDK_VERSION);
279280
expect(sdkData?.version).toBe(SDK_VERSION);
280281
});
281282

282283
it('uses SDK source from window for package name', () => {
283284
global.SENTRY_SDK_SOURCE = 'loader';
284285
init({ dsn });
285286

286-
const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk;
287+
const sdkData = getClient()?.getOptions()._metadata?.sdk || {};
287288

288-
expect(sdkData?.packages[0].name).toBe('loader:@sentry/browser');
289+
expect(sdkData.packages?.[0].name).toBe('loader:@sentry/browser');
289290
delete global.SENTRY_SDK_SOURCE;
290291
});
291292

292293
it('uses SDK source from global for package name', () => {
293294
const spy = jest.spyOn(utils, 'getSDKSource').mockReturnValue('cdn');
294295
init({ dsn });
295296

296-
const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk;
297+
const sdkData = getClient()?.getOptions()._metadata?.sdk || {};
297298

298-
expect(sdkData?.packages[0].name).toBe('cdn:@sentry/browser');
299+
expect(sdkData.packages?.[0].name).toBe('cdn:@sentry/browser');
299300
expect(utils.getSDKSource).toBeCalledTimes(1);
300301
spy.mockRestore();
301302
});
@@ -332,11 +333,11 @@ describe('SentryBrowser initialization', () => {
332333
},
333334
});
334335

335-
const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata?.sdk;
336+
const sdkData = getClient()?.getOptions()._metadata?.sdk || {};
336337

337338
expect(sdkData.name).toBe('sentry.javascript.angular');
338-
expect(sdkData.packages[0].name).toBe('npm:@sentry/angular');
339-
expect(sdkData.packages[0].version).toBe(SDK_VERSION);
339+
expect(sdkData.packages?.[0].name).toBe('npm:@sentry/angular');
340+
expect(sdkData.packages?.[0].version).toBe(SDK_VERSION);
340341
expect(sdkData.version).toBe(SDK_VERSION);
341342
});
342343
});

packages/browser/test/unit/integrations/breadcrumbs.test.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import { getCurrentHub } from '@sentry/core';
2+
import type { Client } from '@sentry/types';
23

34
import { Breadcrumbs, BrowserClient, flush, Hub } from '../../../src';
45
import { getDefaultBrowserClientOptions } from '../helper/browser-client-options';
56

67
const hub = new Hub();
8+
let client: Client | undefined;
79

810
jest.mock('@sentry/core', () => {
911
const original = jest.requireActual('@sentry/core');
1012
return {
1113
...original,
1214
getCurrentHub: () => hub,
15+
getClient: () => client,
1316
};
1417
});
1518

@@ -18,7 +21,7 @@ describe('Breadcrumbs', () => {
1821
const addBreadcrumb = jest.fn();
1922
hub.addBreadcrumb = addBreadcrumb;
2023

21-
const client = new BrowserClient({
24+
client = new BrowserClient({
2225
...getDefaultBrowserClientOptions(),
2326
dsn: 'https://username@domain/123',
2427
integrations: [new Breadcrumbs()],

packages/browser/test/unit/profiling/integration.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ describe('BrowserProfilingIntegration', () => {
4848
integrations: [new Sentry.BrowserTracing(), new BrowserProfilingIntegration()],
4949
});
5050

51-
const client = Sentry.getCurrentHub().getClient() as BrowserClient;
51+
const client = Sentry.getClient<BrowserClient>();
5252

5353
const currentTransaction = Sentry.getCurrentHub().getScope().getTransaction();
5454
expect(currentTransaction?.op).toBe('pageload');
5555
currentTransaction?.finish();
56-
await client.flush(1000);
56+
await client?.flush(1000);
5757

5858
expect(send).toHaveBeenCalledTimes(1);
5959

packages/bun/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export {
3939
getActiveTransaction,
4040
getHubFromCarrier,
4141
getCurrentHub,
42+
getClient,
4243
Hub,
4344
lastEventId,
4445
makeMain,

packages/core/src/baseclient.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
EventDropReason,
1414
EventHint,
1515
EventProcessor,
16+
FeedbackEvent,
1617
Integration,
1718
IntegrationClass,
1819
Outcome,
@@ -27,7 +28,6 @@ import type {
2728
Transport,
2829
TransportMakeRequestResponse,
2930
} from '@sentry/types';
30-
import type { FeedbackEvent } from '@sentry/types';
3131
import {
3232
addItemToEnvelope,
3333
checkOrSetAlreadyCaught,

packages/core/src/exports.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
Breadcrumb,
33
CaptureContext,
44
CheckIn,
5+
Client,
56
CustomSamplingContext,
67
Event,
78
EventHint,
@@ -266,7 +267,7 @@ export function withMonitor<T>(
266267
* doesn't (or if there's no client defined).
267268
*/
268269
export async function flush(timeout?: number): Promise<boolean> {
269-
const client = getCurrentHub().getClient();
270+
const client = getClient();
270271
if (client) {
271272
return client.flush(timeout);
272273
}
@@ -283,7 +284,7 @@ export async function flush(timeout?: number): Promise<boolean> {
283284
* doesn't (or if there's no client defined).
284285
*/
285286
export async function close(timeout?: number): Promise<boolean> {
286-
const client = getCurrentHub().getClient();
287+
const client = getClient();
287288
if (client) {
288289
return client.close(timeout);
289290
}
@@ -299,3 +300,10 @@ export async function close(timeout?: number): Promise<boolean> {
299300
export function lastEventId(): string | undefined {
300301
return getCurrentHub().lastEventId();
301302
}
303+
304+
/**
305+
* Get the currently active client.
306+
*/
307+
export function getClient<C extends Client>(): C | undefined {
308+
return getCurrentHub().getClient<C>();
309+
}

packages/core/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export {
2525
setTags,
2626
setUser,
2727
withScope,
28+
getClient,
2829
} from './exports';
2930
export {
3031
getCurrentHub,

packages/core/src/integration.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Client, Event, EventHint, Integration, Options } from '@sentry/typ
22
import { arrayify, logger } from '@sentry/utils';
33

44
import { addGlobalEventProcessor } from './eventProcessors';
5+
import { getClient } from './exports';
56
import { getCurrentHub } from './hub';
67

78
declare module '@sentry/types' {
@@ -132,7 +133,7 @@ export function setupIntegration(client: Client, integration: Integration, integ
132133

133134
/** Add an integration to the current hub's client. */
134135
export function addIntegration(integration: Integration): void {
135-
const client = getCurrentHub().getClient();
136+
const client = getClient();
136137

137138
if (!client || !client.addIntegration) {
138139
__DEBUG_BUILD__ && logger.warn(`Cannot add integration "${integration.name}" because no SDK Client is available.`);

packages/core/src/utils/hasTracingEnabled.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Options } from '@sentry/types';
22

3-
import { getCurrentHub } from '../hub';
3+
import { getClient } from '../exports';
44

55
// Treeshakable guard to remove all code related to tracing
66
declare const __SENTRY_TRACING__: boolean | undefined;
@@ -17,7 +17,7 @@ export function hasTracingEnabled(
1717
return false;
1818
}
1919

20-
const client = getCurrentHub().getClient();
20+
const client = getClient();
2121
const options = maybeOptions || (client && client.getOptions());
2222
return !!options && (options.enableTracing || 'tracesSampleRate' in options || 'tracesSampler' in options);
2323
}

packages/deno/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export {
3838
getActiveTransaction,
3939
getHubFromCarrier,
4040
getCurrentHub,
41+
getClient,
4142
Hub,
4243
lastEventId,
4344
makeMain,

packages/feedback/test/sendFeedback.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { getCurrentHub } from '@sentry/core';
1+
import { getClient } from '@sentry/core';
22

33
import { sendFeedback } from '../src/sendFeedback';
44
import { mockSdk } from './utils/mockSdk';
55

66
describe('sendFeedback', () => {
77
it('sends feedback', async () => {
88
mockSdk();
9-
const mockTransport = jest.spyOn(getCurrentHub().getClient()!.getTransport()!, 'send');
9+
const mockTransport = jest.spyOn(getClient()!.getTransport()!, 'send');
1010

1111
await sendFeedback({
1212
name: 'doe',

0 commit comments

Comments
 (0)