Skip to content

Commit a9cc619

Browse files
Lms24lobsterkatie
authored andcommitted
ref: Port functionality from Backend to Client (#4911)
port all the functionality from the Backend classes to the Client classes. This includes: * Backend (interface) * BaseBackend * BrowserBackend * NodeBackend * TestBackend Additionally, fix the unit and integration tests in Core to spy on TestClient instead of TestBackend.
1 parent 05292b9 commit a9cc619

File tree

16 files changed

+394
-84
lines changed

16 files changed

+394
-84
lines changed

Diff for: packages/browser/src/backend.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTranspor
88
/**
99
* Configuration options for the Sentry Browser SDK.
1010
* @see BrowserClient for more information.
11+
*
12+
* TODO(v7): move to client
1113
*/
1214
export interface BrowserOptions extends Options {
1315
/**

Diff for: packages/browser/src/client.ts

+57-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
import { BaseClient, Scope, SDK_VERSION } from '@sentry/core';
2-
import { Event, EventHint } from '@sentry/types';
3-
import { getGlobalObject, logger } from '@sentry/utils';
1+
import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core';
2+
import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types';
3+
import { getGlobalObject, logger, supportsFetch } from '@sentry/utils';
44

55
import { BrowserBackend, BrowserOptions } from './backend';
6+
import { eventFromException, eventFromMessage } from './eventbuilder';
67
import { IS_DEBUG_BUILD } from './flags';
78
import { injectReportDialog, ReportDialogOptions } from './helpers';
89
import { Breadcrumbs } from './integrations';
10+
import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTransport } from './transports';
911

1012
/**
1113
* The Sentry Browser SDK Client.
1214
*
1315
* @see BrowserOptions for documentation on configuration options.
1416
* @see SentryClient for usage documentation.
17+
* TODO(v7): remove BrowserBackend
1518
*/
1619
export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
1720
/**
@@ -32,6 +35,7 @@ export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
3235
version: SDK_VERSION,
3336
};
3437

38+
// TODO(v7): remove BrowserBackend param
3539
super(BrowserBackend, options);
3640
}
3741

@@ -58,6 +62,20 @@ export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
5862
});
5963
}
6064

65+
/**
66+
* @inheritDoc
67+
*/
68+
public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {
69+
return eventFromException(exception, hint, this._options.attachStacktrace);
70+
}
71+
72+
/**
73+
* @inheritDoc
74+
*/
75+
public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike<Event> {
76+
return eventFromMessage(message, level, hint, this._options.attachStacktrace);
77+
}
78+
6179
/**
6280
* @inheritDoc
6381
*/
@@ -76,4 +94,40 @@ export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
7694
}
7795
super._sendEvent(event);
7896
}
97+
98+
/**
99+
* @inheritDoc
100+
*/
101+
protected _setupTransport(): Transport {
102+
if (!this._options.dsn) {
103+
// We return the noop transport here in case there is no Dsn.
104+
return super._setupTransport();
105+
}
106+
107+
const transportOptions: TransportOptions = {
108+
...this._options.transportOptions,
109+
dsn: this._options.dsn,
110+
tunnel: this._options.tunnel,
111+
sendClientReports: this._options.sendClientReports,
112+
_metadata: this._options._metadata,
113+
};
114+
115+
const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel);
116+
const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel);
117+
118+
if (this._options.transport) {
119+
return new this._options.transport(transportOptions);
120+
}
121+
if (supportsFetch()) {
122+
const requestOptions: RequestInit = { ...transportOptions.fetchParameters };
123+
this._newTransport = makeNewFetchTransport({ requestOptions, url });
124+
return new FetchTransport(transportOptions);
125+
}
126+
127+
this._newTransport = makeNewXHRTransport({
128+
url,
129+
headers: transportOptions.headers,
130+
});
131+
return new XHRTransport(transportOptions);
132+
}
79133
}

Diff for: packages/browser/src/exports.ts

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export {
4141
withScope,
4242
} from '@sentry/core';
4343

44+
// TODO(v7): refactor to use client here!
4445
export { BrowserOptions } from './backend';
4546
export { BrowserClient } from './client';
4647
export { injectReportDialog, ReportDialogOptions } from './helpers';

Diff for: packages/browser/test/unit/backend.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { BrowserBackend } from '../../src/backend';
22

33
let backend: BrowserBackend;
44

5+
// TODO(v7): remove when deleting Backend
6+
57
describe('BrowserBackend', () => {
68
describe('sendEvent()', () => {
79
it('should use NoopTransport', () => {

Diff for: packages/browser/test/unit/index.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ describe('SentryBrowser initialization', () => {
246246
it('should set SDK data when Sentry.init() is called', () => {
247247
init({ dsn });
248248

249-
const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk;
249+
const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk;
250250

251251
expect(sdkData.name).toBe('sentry.javascript.browser');
252252
expect(sdkData.packages[0].name).toBe('npm:@sentry/browser');
@@ -257,7 +257,7 @@ describe('SentryBrowser initialization', () => {
257257
it('should set SDK data when instantiating a client directly', () => {
258258
const client = new BrowserClient({ dsn });
259259

260-
const sdkData = (client as any)._backend._transport._api.metadata?.sdk;
260+
const sdkData = (client as any).getTransport()._api.metadata?.sdk;
261261

262262
expect(sdkData.name).toBe('sentry.javascript.browser');
263263
expect(sdkData.packages[0].name).toBe('npm:@sentry/browser');
@@ -285,7 +285,7 @@ describe('SentryBrowser initialization', () => {
285285
},
286286
});
287287

288-
const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk;
288+
const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk;
289289

290290
expect(sdkData.name).toBe('sentry.javascript.angular');
291291
expect(sdkData.packages[0].name).toBe('npm:@sentry/angular');

Diff for: packages/browser/test/unit/integrations/linkederrors.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe('LinkedErrors', () => {
3434
one.cause = two;
3535

3636
const originalException = one;
37+
// TODO(v7): refactor to use client here!
3738
const backend = new BrowserBackend({});
3839
return backend.eventFromException(originalException).then(event => {
3940
const result = LinkedErrorsModule._handler('cause', 5, event, {
@@ -65,6 +66,7 @@ describe('LinkedErrors', () => {
6566

6667
const originalException = one;
6768
const backend = new BrowserBackend({});
69+
// TODO(v7): refactor to use client here!
6870
return backend.eventFromException(originalException).then(event => {
6971
const result = LinkedErrorsModule._handler('reason', 5, event, {
7072
originalException,
@@ -92,6 +94,7 @@ describe('LinkedErrors', () => {
9294

9395
const backend = new BrowserBackend({});
9496
const originalException = one;
97+
// TODO(v7): refactor to use client here!
9598
return backend.eventFromException(originalException).then(event => {
9699
const result = LinkedErrorsModule._handler('cause', 2, event, {
97100
originalException,

Diff for: packages/core/src/baseclient.ts

+98-8
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,21 @@ import {
2828
uuid4,
2929
} from '@sentry/utils';
3030

31+
import { initAPIDetails } from './api';
3132
import { Backend, BackendClass } from './basebackend';
3233
import { IS_DEBUG_BUILD } from './flags';
3334
import { IntegrationIndex, setupIntegrations } from './integration';
35+
import { createEventEnvelope, createSessionEnvelope } from './request';
36+
import { NewTransport } from './transports/base';
37+
import { NoopTransport } from './transports/noop';
3438

3539
const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured.";
3640

3741
/**
3842
* Base implementation for all JavaScript SDK clients.
3943
*
44+
* TODO(v7): refactor doc w.r.t. Backend
45+
*
4046
* Call the constructor with the corresponding backend constructor and options
4147
* specific to the client subclass. To access these options later, use
4248
* {@link Client.getOptions}. Also, the Backend instance is available via
@@ -71,6 +77,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
7177
* The backend used to physically interact in the environment. Usually, this
7278
* will correspond to the client. When composing SDKs, however, the Backend
7379
* from the root SDK will be used.
80+
* TODO(v7): DELETE
7481
*/
7582
protected readonly _backend: B;
7683

@@ -86,19 +93,30 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
8693
/** Number of calls being processed */
8794
protected _numProcessing: number = 0;
8895

96+
/** Cached transport used internally. */
97+
protected _transport: Transport;
98+
99+
/** New v7 Transport that is initialized alongside the old one */
100+
protected _newTransport?: NewTransport;
101+
89102
/**
90103
* Initializes this client instance.
91104
*
92105
* @param backendClass A constructor function to create the backend.
93106
* @param options Options for the client.
94107
*/
95108
protected constructor(backendClass: BackendClass<B, O>, options: O) {
109+
// TODO(v7): Delete
96110
this._backend = new backendClass(options);
97111
this._options = options;
98112

99113
if (options.dsn) {
100114
this._dsn = makeDsn(options.dsn);
115+
} else {
116+
IS_DEBUG_BUILD && logger.warn('No DSN provided, client will not do anything.');
101117
}
118+
119+
this._transport = this._setupTransport();
102120
}
103121

104122
/**
@@ -115,8 +133,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
115133
let eventId: string | undefined = hint && hint.event_id;
116134

117135
this._process(
118-
this._getBackend()
119-
.eventFromException(exception, hint)
136+
this.eventFromException(exception, hint)
120137
.then(event => this._captureEvent(event, hint, scope))
121138
.then(result => {
122139
eventId = result;
@@ -133,8 +150,8 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
133150
let eventId: string | undefined = hint && hint.event_id;
134151

135152
const promisedEvent = isPrimitive(message)
136-
? this._getBackend().eventFromMessage(String(message), level, hint)
137-
: this._getBackend().eventFromException(message, hint);
153+
? this.eventFromMessage(String(message), level, hint)
154+
: this.eventFromException(message, hint);
138155

139156
this._process(
140157
promisedEvent
@@ -204,7 +221,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
204221
* @inheritDoc
205222
*/
206223
public getTransport(): Transport {
207-
return this._getBackend().getTransport();
224+
return this._transport;
208225
}
209226

210227
/**
@@ -249,6 +266,57 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
249266
}
250267
}
251268

269+
/**
270+
* @inheritDoc
271+
*/
272+
public sendEvent(event: Event): void {
273+
// TODO(v7): Remove the if-else
274+
if (
275+
this._newTransport &&
276+
this._options.dsn &&
277+
this._options._experiments &&
278+
this._options._experiments.newTransport
279+
) {
280+
const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel);
281+
const env = createEventEnvelope(event, api);
282+
void this._newTransport.send(env).then(null, reason => {
283+
IS_DEBUG_BUILD && logger.error('Error while sending event:', reason);
284+
});
285+
} else {
286+
void this._transport.sendEvent(event).then(null, reason => {
287+
IS_DEBUG_BUILD && logger.error('Error while sending event:', reason);
288+
});
289+
}
290+
}
291+
292+
/**
293+
* @inheritDoc
294+
*/
295+
public sendSession(session: Session): void {
296+
if (!this._transport.sendSession) {
297+
IS_DEBUG_BUILD && logger.warn("Dropping session because custom transport doesn't implement sendSession");
298+
return;
299+
}
300+
301+
// TODO(v7): Remove the if-else
302+
if (
303+
this._newTransport &&
304+
this._options.dsn &&
305+
this._options._experiments &&
306+
this._options._experiments.newTransport
307+
) {
308+
const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel);
309+
const [env] = createSessionEnvelope(session, api);
310+
void this._newTransport.send(env).then(null, reason => {
311+
IS_DEBUG_BUILD && logger.error('Error while sending session:', reason);
312+
});
313+
} else {
314+
void this._transport.sendSession(session).then(null, reason => {
315+
IS_DEBUG_BUILD && logger.error('Error while sending session:', reason);
316+
});
317+
}
318+
}
319+
252320
/** Updates existing session based on the provided event */
253321
protected _updateSessionFromEvent(session: Session, event: Event): void {
254322
let crashed = false;
@@ -283,8 +351,9 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
283351
}
284352

285353
/** Deliver captured session to Sentry */
354+
// TODO(v7): should this be deleted?
286355
protected _sendSession(session: Session): void {
287-
this._getBackend().sendSession(session);
356+
this.sendSession(session);
288357
}
289358

290359
/**
@@ -317,7 +386,9 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
317386
});
318387
}
319388

320-
/** Returns the current backend. */
389+
/** Returns the current backend.
390+
* TODO(v7): DELETE
391+
*/
321392
protected _getBackend(): B {
322393
return this._backend;
323394
}
@@ -490,8 +561,9 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
490561
* Tells the backend to send this event
491562
* @param event The Sentry event to send
492563
*/
564+
// TODO(v7): refactor: get rid of method?
493565
protected _sendEvent(event: Event): void {
494-
this._getBackend().sendEvent(event);
566+
this.sendEvent(event);
495567
}
496568

497569
/**
@@ -618,6 +690,24 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
618690
},
619691
);
620692
}
693+
694+
/**
695+
* Sets up the transport so it can be used later to send requests.
696+
*/
697+
protected _setupTransport(): Transport {
698+
return new NoopTransport();
699+
}
700+
701+
/**
702+
* @inheritDoc
703+
*/
704+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
705+
public abstract eventFromException(_exception: any, _hint?: EventHint): PromiseLike<Event>;
706+
707+
/**
708+
* @inheritDoc
709+
*/
710+
public abstract eventFromMessage(_message: string, _level?: Severity, _hint?: EventHint): PromiseLike<Event>;
621711
}
622712

623713
/**

Diff for: packages/core/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export {
2323
getReportDialogEndpoint,
2424
} from './api';
2525
export { BaseClient } from './baseclient';
26+
// TODO(v7): Delete!
2627
export { BackendClass, BaseBackend } from './basebackend';
2728
export { eventToSentryRequest, sessionToSentryRequest } from './request';
2829
export { initAndBind, ClientClass } from './sdk';

0 commit comments

Comments
 (0)