Skip to content
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

ref(backend): Port functionality from Backend to Client #4911

Merged
merged 13 commits into from
Apr 12, 2022
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();
Lms24 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @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