Skip to content

Commit

Permalink
ref: Port functionality from Backend to Client (#4911)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Lms24 authored and lobsterkatie committed Apr 26, 2022
1 parent a11aa63 commit 3f3f526
Show file tree
Hide file tree
Showing 16 changed files with 394 additions and 84 deletions.
2 changes: 2 additions & 0 deletions packages/browser/src/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTranspor
/**
* Configuration options for the Sentry Browser SDK.
* @see BrowserClient for more information.
*
* TODO(v7): move to client
*/
export interface BrowserOptions extends Options {
/**
Expand Down
60 changes: 57 additions & 3 deletions packages/browser/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { BaseClient, Scope, SDK_VERSION } from '@sentry/core';
import { Event, EventHint } from '@sentry/types';
import { getGlobalObject, logger } from '@sentry/utils';
import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, Scope, SDK_VERSION } from '@sentry/core';
import { Event, EventHint, Severity, Transport, TransportOptions } from '@sentry/types';
import { getGlobalObject, logger, supportsFetch } from '@sentry/utils';

import { BrowserBackend, BrowserOptions } from './backend';
import { eventFromException, eventFromMessage } from './eventbuilder';
import { IS_DEBUG_BUILD } from './flags';
import { injectReportDialog, ReportDialogOptions } from './helpers';
import { Breadcrumbs } from './integrations';
import { FetchTransport, makeNewFetchTransport, makeNewXHRTransport, XHRTransport } from './transports';

/**
* The Sentry Browser SDK Client.
*
* @see BrowserOptions for documentation on configuration options.
* @see SentryClient for usage documentation.
* TODO(v7): remove BrowserBackend
*/
export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
/**
Expand All @@ -32,6 +35,7 @@ export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
version: SDK_VERSION,
};

// TODO(v7): remove BrowserBackend param
super(BrowserBackend, options);
}

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

/**
* @inheritDoc
*/
public eventFromException(exception: unknown, hint?: EventHint): PromiseLike<Event> {
return eventFromException(exception, hint, this._options.attachStacktrace);
}

/**
* @inheritDoc
*/
public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: EventHint): PromiseLike<Event> {
return eventFromMessage(message, level, hint, this._options.attachStacktrace);
}

/**
* @inheritDoc
*/
Expand All @@ -76,4 +94,40 @@ export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
}
super._sendEvent(event);
}

/**
* @inheritDoc
*/
protected _setupTransport(): Transport {
if (!this._options.dsn) {
// We return the noop transport here in case there is no Dsn.
return super._setupTransport();
}

const transportOptions: TransportOptions = {
...this._options.transportOptions,
dsn: this._options.dsn,
tunnel: this._options.tunnel,
sendClientReports: this._options.sendClientReports,
_metadata: this._options._metadata,
};

const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel);
const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel);

if (this._options.transport) {
return new this._options.transport(transportOptions);
}
if (supportsFetch()) {
const requestOptions: RequestInit = { ...transportOptions.fetchParameters };
this._newTransport = makeNewFetchTransport({ requestOptions, url });
return new FetchTransport(transportOptions);
}

this._newTransport = makeNewXHRTransport({
url,
headers: transportOptions.headers,
});
return new XHRTransport(transportOptions);
}
}
1 change: 1 addition & 0 deletions packages/browser/src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export {
withScope,
} from '@sentry/core';

// TODO(v7): refactor to use client here!
export { BrowserOptions } from './backend';
export { BrowserClient } from './client';
export { injectReportDialog, ReportDialogOptions } from './helpers';
Expand Down
2 changes: 2 additions & 0 deletions packages/browser/test/unit/backend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { BrowserBackend } from '../../src/backend';

let backend: BrowserBackend;

// TODO(v7): remove when deleting Backend

describe('BrowserBackend', () => {
describe('sendEvent()', () => {
it('should use NoopTransport', () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/browser/test/unit/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ describe('SentryBrowser initialization', () => {
it('should set SDK data when Sentry.init() is called', () => {
init({ dsn });

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

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

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

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

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

expect(sdkData.name).toBe('sentry.javascript.angular');
expect(sdkData.packages[0].name).toBe('npm:@sentry/angular');
Expand Down
3 changes: 3 additions & 0 deletions packages/browser/test/unit/integrations/linkederrors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('LinkedErrors', () => {
one.cause = two;

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

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

const backend = new BrowserBackend({});
const originalException = one;
// TODO(v7): refactor to use client here!
return backend.eventFromException(originalException).then(event => {
const result = LinkedErrorsModule._handler('cause', 2, event, {
originalException,
Expand Down
106 changes: 98 additions & 8 deletions packages/core/src/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,21 @@ import {
uuid4,
} from '@sentry/utils';

import { initAPIDetails } from './api';
import { Backend, BackendClass } from './basebackend';
import { IS_DEBUG_BUILD } from './flags';
import { IntegrationIndex, setupIntegrations } from './integration';
import { createEventEnvelope, createSessionEnvelope } from './request';
import { NewTransport } from './transports/base';
import { NoopTransport } from './transports/noop';

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

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

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

/** Cached transport used internally. */
protected _transport: Transport;

/** New v7 Transport that is initialized alongside the old one */
protected _newTransport?: NewTransport;

/**
* Initializes this client instance.
*
* @param backendClass A constructor function to create the backend.
* @param options Options for the client.
*/
protected constructor(backendClass: BackendClass<B, O>, options: O) {
// TODO(v7): Delete
this._backend = new backendClass(options);
this._options = options;

if (options.dsn) {
this._dsn = makeDsn(options.dsn);
} else {
IS_DEBUG_BUILD && logger.warn('No DSN provided, client will not do anything.');
}

this._transport = this._setupTransport();
}

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

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

const promisedEvent = isPrimitive(message)
? this._getBackend().eventFromMessage(String(message), level, hint)
: this._getBackend().eventFromException(message, hint);
? this.eventFromMessage(String(message), level, hint)
: this.eventFromException(message, hint);

this._process(
promisedEvent
Expand Down Expand Up @@ -204,7 +221,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
* @inheritDoc
*/
public getTransport(): Transport {
return this._getBackend().getTransport();
return this._transport;
}

/**
Expand Down Expand Up @@ -249,6 +266,57 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
}
}

/**
* @inheritDoc
*/
public sendEvent(event: Event): void {
// TODO(v7): Remove the if-else
if (
this._newTransport &&
this._options.dsn &&
this._options._experiments &&
this._options._experiments.newTransport
) {
const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel);
const env = createEventEnvelope(event, api);
void this._newTransport.send(env).then(null, reason => {
IS_DEBUG_BUILD && logger.error('Error while sending event:', reason);
});
} else {
void this._transport.sendEvent(event).then(null, reason => {
IS_DEBUG_BUILD && logger.error('Error while sending event:', reason);
});
}
}

/**
* @inheritDoc
*/
public sendSession(session: Session): void {
if (!this._transport.sendSession) {
IS_DEBUG_BUILD && logger.warn("Dropping session because custom transport doesn't implement sendSession");
return;
}

// TODO(v7): Remove the if-else
if (
this._newTransport &&
this._options.dsn &&
this._options._experiments &&
this._options._experiments.newTransport
) {
const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel);
const [env] = createSessionEnvelope(session, api);
void this._newTransport.send(env).then(null, reason => {
IS_DEBUG_BUILD && logger.error('Error while sending session:', reason);
});
} else {
void this._transport.sendSession(session).then(null, reason => {
IS_DEBUG_BUILD && logger.error('Error while sending session:', reason);
});
}
}

/** Updates existing session based on the provided event */
protected _updateSessionFromEvent(session: Session, event: Event): void {
let crashed = false;
Expand Down Expand Up @@ -283,8 +351,9 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
}

/** Deliver captured session to Sentry */
// TODO(v7): should this be deleted?
protected _sendSession(session: Session): void {
this._getBackend().sendSession(session);
this.sendSession(session);
}

/**
Expand Down Expand Up @@ -317,7 +386,9 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
});
}

/** Returns the current backend. */
/** Returns the current backend.
* TODO(v7): DELETE
*/
protected _getBackend(): B {
return this._backend;
}
Expand Down Expand Up @@ -490,8 +561,9 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
* Tells the backend to send this event
* @param event The Sentry event to send
*/
// TODO(v7): refactor: get rid of method?
protected _sendEvent(event: Event): void {
this._getBackend().sendEvent(event);
this.sendEvent(event);
}

/**
Expand Down Expand Up @@ -618,6 +690,24 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
},
);
}

/**
* Sets up the transport so it can be used later to send requests.
*/
protected _setupTransport(): Transport {
return new NoopTransport();
}

/**
* @inheritDoc
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
public abstract eventFromException(_exception: any, _hint?: EventHint): PromiseLike<Event>;

/**
* @inheritDoc
*/
public abstract eventFromMessage(_message: string, _level?: Severity, _hint?: EventHint): PromiseLike<Event>;
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export {
getReportDialogEndpoint,
} from './api';
export { BaseClient } from './baseclient';
// TODO(v7): Delete!
export { BackendClass, BaseBackend } from './basebackend';
export { eventToSentryRequest, sessionToSentryRequest } from './request';
export { initAndBind, ClientClass } from './sdk';
Expand Down
Loading

0 comments on commit 3f3f526

Please sign in to comment.