Skip to content

Commit fa34914

Browse files
Lms24lobsterkatie
authored andcommitted
ref: Inject Transports into Client (#4921)
Changes the way we initialize Transports. Previously, the Transport was initialized within the Client constructor (by calling setupTransport()). Change this so that the Transport is initialized beforehand and then injected into the client. This is currently (i.e. with this PR) happening via two additional arguments in the Client class constructors. The reason for two arguments is that we're still using both, the old `Transport` classes and the `NewTransport` interface. In the future, `NewTransport` will replace `Transport` and (once the old Transport is removed) the transport is then passed into the Client constructor as a property of the options object. * add the injection logic * initialize the transports in Browser and Node and pass them to the `Browser/NodeClient` initialization * add client-external transport setup functions (extracted from setupTransport()) * add basic tests for these transport setup functions * delete the client-internal setupTransport methods * fixe a bunch of tests that are initializing clients
1 parent 3c7faf9 commit fa34914

30 files changed

+889
-515
lines changed

packages/browser/src/client.ts

+5-42
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core';
2-
import { Event, EventHint, Options, Severity, SeverityLevel, Transport, TransportOptions } from '@sentry/types';
3-
import { getGlobalObject, logger, stackParserFromOptions, supportsFetch } from '@sentry/utils';
1+
import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core';
2+
import { Event, EventHint, Options, Severity, SeverityLevel, Transport } from '@sentry/types';
3+
import { getGlobalObject, logger, stackParserFromOptions } from '@sentry/utils';
44

55
import { eventFromException, eventFromMessage } from './eventbuilder';
66
import { IS_DEBUG_BUILD } from './flags';
77
import { injectReportDialog, ReportDialogOptions } from './helpers';
88
import { Breadcrumbs } from './integrations';
9-
import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTransport } from './transports';
109

1110
/**
1211
* Configuration options for the Sentry Browser SDK.
@@ -40,7 +39,7 @@ export class BrowserClient extends BaseClient<BrowserOptions> {
4039
*
4140
* @param options Configuration options for this SDK.
4241
*/
43-
public constructor(options: BrowserOptions = {}) {
42+
public constructor(options: BrowserOptions = {}, transport: Transport, newTransport?: NewTransport) {
4443
options._metadata = options._metadata || {};
4544
options._metadata.sdk = options._metadata.sdk || {
4645
name: 'sentry.javascript.browser',
@@ -53,7 +52,7 @@ export class BrowserClient extends BaseClient<BrowserOptions> {
5352
version: SDK_VERSION,
5453
};
5554

56-
super(options);
55+
super(options, transport, newTransport);
5756
}
5857

5958
/**
@@ -122,40 +121,4 @@ export class BrowserClient extends BaseClient<BrowserOptions> {
122121
}
123122
super._sendEvent(event);
124123
}
125-
126-
/**
127-
* @inheritDoc
128-
*/
129-
protected _setupTransport(): Transport {
130-
if (!this._options.dsn) {
131-
// We return the noop transport here in case there is no Dsn.
132-
return super._setupTransport();
133-
}
134-
135-
const transportOptions: TransportOptions = {
136-
...this._options.transportOptions,
137-
dsn: this._options.dsn,
138-
tunnel: this._options.tunnel,
139-
sendClientReports: this._options.sendClientReports,
140-
_metadata: this._options._metadata,
141-
};
142-
143-
const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel);
144-
const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel);
145-
146-
if (this._options.transport) {
147-
return new this._options.transport(transportOptions);
148-
}
149-
if (supportsFetch()) {
150-
const requestOptions: RequestInit = { ...transportOptions.fetchParameters };
151-
this._newTransport = makeNewFetchTransport({ requestOptions, url });
152-
return new FetchTransport(transportOptions);
153-
}
154-
155-
this._newTransport = makeNewXHRTransport({
156-
url,
157-
headers: transportOptions.headers,
158-
});
159-
return new XHRTransport(transportOptions);
160-
}
161124
}

packages/browser/src/sdk.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { IS_DEBUG_BUILD } from './flags';
77
import { ReportDialogOptions, wrap as internalWrap } from './helpers';
88
import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations';
99
import { defaultStackParsers } from './stack-parsers';
10+
import { setupBrowserTransport } from './transports/setup';
1011

1112
export const defaultIntegrations = [
1213
new CoreIntegrations.InboundFilters(),
@@ -97,7 +98,8 @@ export function init(options: BrowserOptions = {}): void {
9798
options.stackParser = defaultStackParsers;
9899
}
99100

100-
initAndBind(BrowserClient, options);
101+
const { transport, newTransport } = setupBrowserTransport(options);
102+
initAndBind(BrowserClient, options, transport, newTransport);
101103

102104
if (options.autoSessionTracking) {
103105
startSessionTracking();

packages/browser/src/transports/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ export { XHRTransport } from './xhr';
44

55
export { makeNewFetchTransport } from './new-fetch';
66
export { makeNewXHRTransport } from './new-xhr';
7+
8+
export { setupBrowserTransport } from './setup';
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
BaseTransportOptions,
3+
getEnvelopeEndpointWithUrlEncodedAuth,
4+
initAPIDetails,
5+
NewTransport,
6+
NoopTransport,
7+
} from '@sentry/core';
8+
import { Transport, TransportOptions } from '@sentry/types';
9+
import { supportsFetch } from '@sentry/utils';
10+
11+
import { BrowserOptions } from '../client';
12+
import { FetchTransport } from './fetch';
13+
import { makeNewFetchTransport } from './new-fetch';
14+
import { makeNewXHRTransport } from './new-xhr';
15+
import { XHRTransport } from './xhr';
16+
17+
export interface BrowserTransportOptions extends BaseTransportOptions {
18+
// options to pass into fetch request
19+
fetchParams: Record<string, string>;
20+
headers?: Record<string, string>;
21+
sendClientReports?: boolean;
22+
}
23+
24+
/**
25+
* Sets up Browser transports based on the passed `options`. If available, the returned
26+
* transport will use the fetch API. In case fetch is not supported, an XMLHttpRequest
27+
* based transport is created.
28+
*
29+
* @returns an object currently still containing both, the old `Transport` and
30+
* `NewTransport` which will eventually replace `Transport`. Once this is replaced,
31+
* this function will return a ready to use `NewTransport`.
32+
*/
33+
// TODO(v7): Adjust return value when NewTransport is the default
34+
export function setupBrowserTransport(options: BrowserOptions): { transport: Transport; newTransport?: NewTransport } {
35+
if (!options.dsn) {
36+
// We return the noop transport here in case there is no Dsn.
37+
return { transport: new NoopTransport() };
38+
}
39+
40+
const transportOptions: TransportOptions = {
41+
...options.transportOptions,
42+
dsn: options.dsn,
43+
tunnel: options.tunnel,
44+
sendClientReports: options.sendClientReports,
45+
_metadata: options._metadata,
46+
};
47+
48+
const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel);
49+
const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel);
50+
51+
if (options.transport) {
52+
return { transport: new options.transport(transportOptions) };
53+
}
54+
55+
if (supportsFetch()) {
56+
const requestOptions: RequestInit = { ...transportOptions.fetchParameters };
57+
const newTransport = makeNewFetchTransport({ requestOptions, url });
58+
const fetchTransport = new FetchTransport(transportOptions);
59+
return { transport: fetchTransport, newTransport };
60+
}
61+
62+
const newTransport = makeNewXHRTransport({
63+
url,
64+
headers: transportOptions.headers,
65+
});
66+
const transport = new XHRTransport(transportOptions);
67+
return { transport, newTransport };
68+
}

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

+56-41
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe('SentryBrowser', () => {
7575
describe('showReportDialog', () => {
7676
describe('user', () => {
7777
const EX_USER = { email: '[email protected]' };
78-
const client = new BrowserClient({ dsn });
78+
const client = new BrowserClient({ dsn }, new SimpleTransport({ dsn }));
7979
const reportDialogSpy = jest.spyOn(client, 'showReportDialog');
8080

8181
beforeEach(() => {
@@ -140,42 +140,51 @@ describe('SentryBrowser', () => {
140140

141141
it('should capture a message', done => {
142142
getCurrentHub().bindClient(
143-
new BrowserClient({
144-
beforeSend: (event: Event): Event | null => {
145-
expect(event.message).toBe('test');
146-
expect(event.exception).toBeUndefined();
147-
done();
148-
return event;
143+
new BrowserClient(
144+
{
145+
beforeSend: (event: Event): Event | null => {
146+
expect(event.message).toBe('test');
147+
expect(event.exception).toBeUndefined();
148+
done();
149+
return event;
150+
},
151+
dsn,
149152
},
150-
dsn,
151-
}),
153+
new SimpleTransport({ dsn }),
154+
),
152155
);
153156
captureMessage('test');
154157
});
155158

156159
it('should capture an event', done => {
157160
getCurrentHub().bindClient(
158-
new BrowserClient({
159-
beforeSend: (event: Event): Event | null => {
160-
expect(event.message).toBe('event');
161-
expect(event.exception).toBeUndefined();
162-
done();
163-
return event;
161+
new BrowserClient(
162+
{
163+
beforeSend: (event: Event): Event | null => {
164+
expect(event.message).toBe('event');
165+
expect(event.exception).toBeUndefined();
166+
done();
167+
return event;
168+
},
169+
dsn,
164170
},
165-
dsn,
166-
}),
171+
new SimpleTransport({ dsn }),
172+
),
167173
);
168174
captureEvent({ message: 'event' });
169175
});
170176

171177
it('should not dedupe an event on bound client', async () => {
172178
const localBeforeSend = jest.fn();
173179
getCurrentHub().bindClient(
174-
new BrowserClient({
175-
beforeSend: localBeforeSend,
176-
dsn,
177-
integrations: [],
178-
}),
180+
new BrowserClient(
181+
{
182+
beforeSend: localBeforeSend,
183+
dsn,
184+
integrations: [],
185+
},
186+
new SimpleTransport({ dsn }),
187+
),
179188
);
180189

181190
captureMessage('event222');
@@ -189,11 +198,14 @@ describe('SentryBrowser', () => {
189198
it('should use inboundfilter rules of bound client', async () => {
190199
const localBeforeSend = jest.fn();
191200
getCurrentHub().bindClient(
192-
new BrowserClient({
193-
beforeSend: localBeforeSend,
194-
dsn,
195-
integrations: [new Integrations.InboundFilters({ ignoreErrors: ['capture'] })],
196-
}),
201+
new BrowserClient(
202+
{
203+
beforeSend: localBeforeSend,
204+
dsn,
205+
integrations: [new Integrations.InboundFilters({ ignoreErrors: ['capture'] })],
206+
},
207+
new SimpleTransport({ dsn }),
208+
),
197209
);
198210

199211
captureMessage('capture');
@@ -248,16 +260,16 @@ describe('SentryBrowser initialization', () => {
248260

249261
const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk;
250262

251-
expect(sdkData.name).toBe('sentry.javascript.browser');
252-
expect(sdkData.packages[0].name).toBe('npm:@sentry/browser');
253-
expect(sdkData.packages[0].version).toBe(SDK_VERSION);
254-
expect(sdkData.version).toBe(SDK_VERSION);
263+
expect(sdkData?.name).toBe('sentry.javascript.browser');
264+
expect(sdkData?.packages[0].name).toBe('npm:@sentry/browser');
265+
expect(sdkData?.packages[0].version).toBe(SDK_VERSION);
266+
expect(sdkData?.version).toBe(SDK_VERSION);
255267
});
256268

257269
it('should set SDK data when instantiating a client directly', () => {
258-
const client = new BrowserClient({ dsn });
270+
const client = new BrowserClient({ dsn }, new SimpleTransport({ dsn }));
259271

260-
const sdkData = (client as any).getTransport()._api.metadata?.sdk;
272+
const sdkData = (client.getTransport() as any)._api.metadata?.sdk;
261273

262274
expect(sdkData.name).toBe('sentry.javascript.browser');
263275
expect(sdkData.packages[0].name).toBe('npm:@sentry/browser');
@@ -298,15 +310,18 @@ describe('SentryBrowser initialization', () => {
298310
describe('wrap()', () => {
299311
it('should wrap and call function while capturing error', done => {
300312
getCurrentHub().bindClient(
301-
new BrowserClient({
302-
beforeSend: (event: Event): Event | null => {
303-
expect(event.exception!.values![0].type).toBe('TypeError');
304-
expect(event.exception!.values![0].value).toBe('mkey');
305-
done();
306-
return null;
313+
new BrowserClient(
314+
{
315+
beforeSend: (event: Event): Event | null => {
316+
expect(event.exception!.values![0].type).toBe('TypeError');
317+
expect(event.exception!.values![0].value).toBe('mkey');
318+
done();
319+
return null;
320+
},
321+
dsn,
307322
},
308-
dsn,
309-
}),
323+
new SimpleTransport({ dsn }),
324+
),
310325
);
311326

312327
try {

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { createStackParser } from '@sentry/utils';
44
import { BrowserClient } from '../../../src/client';
55
import * as LinkedErrorsModule from '../../../src/integrations/linkederrors';
66
import { defaultStackParsers } from '../../../src/stack-parsers';
7+
import { setupBrowserTransport } from '../../../src/transports';
78

89
const parser = createStackParser(...defaultStackParsers);
910

@@ -38,7 +39,8 @@ describe('LinkedErrors', () => {
3839
one.cause = two;
3940

4041
const originalException = one;
41-
const client = new BrowserClient({ stackParser: parser });
42+
const options = { stackParser: parser };
43+
const client = new BrowserClient(options, setupBrowserTransport(options).transport);
4244
return client.eventFromException(originalException).then(event => {
4345
const result = LinkedErrorsModule._handler(parser, 'cause', 5, event, {
4446
originalException,
@@ -68,7 +70,8 @@ describe('LinkedErrors', () => {
6870
one.reason = two;
6971

7072
const originalException = one;
71-
const client = new BrowserClient({ stackParser: parser });
73+
const options = { stackParser: parser };
74+
const client = new BrowserClient(options, setupBrowserTransport(options).transport);
7275
return client.eventFromException(originalException).then(event => {
7376
const result = LinkedErrorsModule._handler(parser, 'reason', 5, event, {
7477
originalException,
@@ -94,7 +97,8 @@ describe('LinkedErrors', () => {
9497
one.cause = two;
9598
two.cause = three;
9699

97-
const client = new BrowserClient({ stackParser: parser });
100+
const options = { stackParser: parser };
101+
const client = new BrowserClient(options, setupBrowserTransport(options).transport);
98102
const originalException = one;
99103
return client.eventFromException(originalException).then(event => {
100104
const result = LinkedErrorsModule._handler(parser, 'cause', 2, event, {

0 commit comments

Comments
 (0)