diff --git a/packages/browser/README.md b/packages/browser/README.md index 3ccb791b4580..64478e5b97ee 100644 --- a/packages/browser/README.md +++ b/packages/browser/README.md @@ -67,9 +67,9 @@ If you don't want to use a global static instance of Sentry, you can create one yourself: ```javascript -import { BrowserFrontend } from '@sentry/browser'; +import { BrowserClient } from '@sentry/browser'; -const client = new BrowserFrontend({ +const client = new BrowserClient({ dsn: '__DSN__', // ... }); diff --git a/packages/browser/src/backend.ts b/packages/browser/src/backend.ts index ca614e295b66..0dce7ff2534d 100644 --- a/packages/browser/src/backend.ts +++ b/packages/browser/src/backend.ts @@ -1,4 +1,4 @@ -import { Backend, Frontend, Options, SentryError } from '@sentry/core'; +import { Backend, Options, SentryError } from '@sentry/core'; import { addBreadcrumb, captureEvent, @@ -11,12 +11,13 @@ import { Raven, SendMethod } from './raven'; const sendRavenEvent = Raven._sendProcessedPayload.bind(Raven) as SendMethod; /** Normalizes the event so it is consistent with our domain interface. */ -function normalizeRavenEvent(event: SentryEvent): SentryEvent { - const ex = (event.exception || {}) as { values?: SentryException[] }; - if (ex && ex.values) { +function normalizeRavenEvent(event?: SentryEvent): SentryEvent | undefined { + const ex = ((event && event.exception) || {}) as { + values?: SentryException[]; + }; + if (event && ex && ex.values) { event.exception = ex.values; } - return event; } @@ -32,7 +33,7 @@ function prepareEventForRaven(event: SentryEvent): SentryEvent { /** * Configuration options for the Sentry Browser SDK. - * @see BrowserFrontend for more information. + * @see BrowserClient for more information. */ export interface BrowserOptions extends Options { /** @@ -65,32 +66,27 @@ export interface BrowserOptions extends Options { /** The Sentry Browser SDK Backend. */ export class BrowserBackend implements Backend { - /** Handle to the SDK frontend for callbacks. */ - private readonly frontend: Frontend; - /** Creates a new browser backend instance. */ - public constructor(frontend: Frontend) { - this.frontend = frontend; - } + public constructor(private readonly options: BrowserOptions) {} /** * @inheritDoc */ public install(): boolean { - // We are only called by the frontend if the SDK is enabled and a valid DSN + // We are only called by the client if the SDK is enabled and a valid DSN // has been configured. If no DSN is present, this indicates a programming // error. - const dsn = this.frontend.getDSN(); + const dsn = this.options.dsn; if (!dsn) { throw new SentryError( 'Invariant exception: install() must not be called when disabled', ); } - Raven.config(dsn.toString(), this.frontend.getOptions()).install(); + Raven.config(dsn, this.options).install(); // Hook into Raven's breadcrumb mechanism. This allows us to intercept both - // breadcrumbs created internally by Raven and pass them to the Frontend + // breadcrumbs created internally by Raven and pass them to the Client // first, before actually capturing them. Raven.setBreadcrumbCallback(breadcrumb => { addBreadcrumb(breadcrumb); @@ -98,10 +94,13 @@ export class BrowserBackend implements Backend { }); // Hook into Raven's internal event sending mechanism. This allows us to - // pass events to the frontend, before they will be sent back here for + // pass events to the client, before they will be sent back here for // actual submission. Raven._sendProcessedPayload = event => { - captureEvent(normalizeRavenEvent(event)); + const normalizedEvent = normalizeRavenEvent(event); + if (normalizedEvent) { + captureEvent(normalizedEvent); + } }; return true; @@ -117,9 +116,12 @@ export class BrowserBackend implements Backend { Raven._sendProcessedPayload = evt => { event = evt; }; - Raven.captureException(exception); - return normalizeRavenEvent(event); + const normalizedEvent = normalizeRavenEvent(event); + if (normalizedEvent) { + return normalizedEvent; + } + throw new SentryError('Event was undefined when it should be an event'); } finally { Raven._sendProcessedPayload = originalSend; } @@ -135,9 +137,12 @@ export class BrowserBackend implements Backend { Raven._sendProcessedPayload = evt => { event = evt; }; - Raven.captureMessage(message); - return normalizeRavenEvent(event); + const normalizedEvent = normalizeRavenEvent(event); + if (normalizedEvent) { + return normalizedEvent; + } + throw new SentryError('Event was undefined when it should be an event'); } finally { Raven._sendProcessedPayload = originalSend; } diff --git a/packages/browser/src/frontend.ts b/packages/browser/src/client.ts similarity index 84% rename from packages/browser/src/frontend.ts rename to packages/browser/src/client.ts index c246f9f5a593..3a25fd62d48e 100644 --- a/packages/browser/src/frontend.ts +++ b/packages/browser/src/client.ts @@ -1,18 +1,15 @@ -import { FrontendBase } from '@sentry/core'; +import { BaseClient } from '@sentry/core'; import { SdkInfo } from '@sentry/shim'; import { BrowserBackend, BrowserOptions } from './backend'; import { Raven } from './raven'; /** - * The Sentry Browser SDK Frontend. + * The Sentry Browser SDK Client. * * @see BrowserOptions for documentation on configuration options. * @see SentryClient for usage documentation. */ -export class BrowserFrontend extends FrontendBase< - BrowserBackend, - BrowserOptions -> { +export class BrowserClient extends BaseClient { /** * Creates a new Browser SDK instance. * diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 6c2ed09493e4..74e68096fb06 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -27,5 +27,5 @@ export { } from '@sentry/shim'; export { BrowserBackend, BrowserOptions } from './backend'; -export { BrowserFrontend } from './frontend'; -export { init, getCurrentFrontend } from './sdk'; +export { BrowserClient } from './client'; +export { init, getCurrentClient } from './sdk'; diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 705ff400dbce..7cb236c466b4 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -1,7 +1,7 @@ import { initAndBind } from '@sentry/core'; -import { getCurrentClient } from '@sentry/shim'; +import { getCurrentClient as shimGetCurrentClient } from '@sentry/shim'; import { BrowserOptions } from './backend'; -import { BrowserFrontend } from './frontend'; +import { BrowserClient } from './client'; /** * The Sentry Browser SDK Client. @@ -47,10 +47,10 @@ import { BrowserFrontend } from './frontend'; * @see BrowserOptions for documentation on configuration options. */ export function init(options: BrowserOptions): void { - initAndBind(BrowserFrontend, options); + initAndBind(BrowserClient, options); } -/** Returns the current BrowserFrontend, if any. */ -export function getCurrentFrontend(): BrowserFrontend { - return getCurrentClient() as BrowserFrontend; +/** Returns the current BrowserClient, if any. */ +export function getCurrentClient(): BrowserClient { + return shimGetCurrentClient() as BrowserClient; } diff --git a/packages/browser/test/index.test.ts b/packages/browser/test/index.test.ts index 6a5df763a255..00c40d3db105 100644 --- a/packages/browser/test/index.test.ts +++ b/packages/browser/test/index.test.ts @@ -4,7 +4,7 @@ import { spy, stub } from 'sinon'; import { addBreadcrumb, BrowserBackend, - BrowserFrontend, + BrowserClient, captureEvent, captureException, captureMessage, @@ -29,7 +29,7 @@ describe('SentryBrowser', () => { let s: sinon.SinonSpy; beforeEach(() => { - s = spy(BrowserFrontend.prototype, 'setContext'); + s = spy(BrowserClient.prototype, 'setContext'); }); afterEach(() => { @@ -70,7 +70,7 @@ describe('SentryBrowser', () => { it('should record auto breadcrumbs', done => { pushScope( - new BrowserFrontend({ + new BrowserClient({ afterSend: (event: SentryEvent) => { expect(event.breadcrumbs!).to.have.lengthOf(3); done(); @@ -110,7 +110,7 @@ describe('SentryBrowser', () => { it('should capture an exception', done => { pushScope( - new BrowserFrontend({ + new BrowserClient({ afterSend: (event: SentryEvent) => { expect(event.exception).to.not.be.undefined; expect(event.exception![0]).to.not.be.undefined; @@ -132,7 +132,7 @@ describe('SentryBrowser', () => { it('should capture a message', done => { pushScope( - new BrowserFrontend({ + new BrowserClient({ afterSend: (event: SentryEvent) => { expect(event.message).to.equal('test'); expect(event.exception).to.be.undefined; @@ -147,7 +147,7 @@ describe('SentryBrowser', () => { it('should capture an event', done => { pushScope( - new BrowserFrontend({ + new BrowserClient({ afterSend: (event: SentryEvent) => { expect(event.message).to.equal('test'); expect(event.exception).to.be.undefined; diff --git a/packages/core/README.md b/packages/core/README.md index d9e6fcefe181..43266a946a7a 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -18,7 +18,7 @@ building Sentry JavaScript SDKs, like `@sentry/node` or `@sentry/browser`. TODO -## Specializing the Frontend +## Specializing the Client TODO diff --git a/packages/core/src/base.ts b/packages/core/src/base.ts index c766d0db8752..c89d65523b38 100644 --- a/packages/core/src/base.ts +++ b/packages/core/src/base.ts @@ -1,6 +1,6 @@ import { Breadcrumb, Context, SdkInfo, SentryEvent } from '@sentry/shim'; import { DSN } from './dsn'; -import { Backend, Frontend, Options, Scope } from './interfaces'; +import { Backend, Client, Options, Scope } from './interfaces'; import { SendStatus } from './status'; /** @@ -17,37 +17,37 @@ const MAX_BREADCRUMBS = 100; /** A class object that can instanciate Backend objects. */ export interface BackendClass { - new (frontend: Frontend): B; + new (options: O): B; } /** - * Base implementation for all JavaScript SDK frontends. + * Base implementation for all JavaScript SDK clients. * * Call the constructor with the corresponding backend constructor and options - * specific to the frontend subclass. To access these options later, use - * {@link Frontend.getOptions}. Also, the Backend instance is available via - * {@link Frontend.getBackend}. + * specific to the client subclass. To access these options later, use + * {@link Client.getOptions}. Also, the Backend instance is available via + * {@link Client.getBackend}. * * Subclasses must implement one abstract method: {@link getSdkInfo}. It must * return the unique name and the version of the SDK. * * If a DSN is specified in the options, it will be parsed and stored. Use - * {@link Frontend.getDSN} to retrieve the DSN at any moment. In case the DSN is + * {@link Client.getDSN} to retrieve the DSN at any moment. In case the DSN is * invalid, the constructor will throw a {@link SentryException}. Note that * without a valid DSN, the SDK will not send any events to Sentry. * * Before sending an event via the backend, it is passed through - * {@link FrontendBase.prepareEvent} to add SDK information and scope data + * {@link BaseClient.prepareEvent} to add SDK information and scope data * (breadcrumbs and context). To add more custom information, override this * method and extend the resulting prepared event. * * To issue automatically created events (e.g. via instrumentation), use - * {@link Frontend.captureEvent}. It will prepare the event and pass it through + * {@link Client.captureEvent}. It will prepare the event and pass it through * the callback lifecycle. To issue auto-breadcrumbs, use - * {@link Frontend.addBreadcrumb}. + * {@link Client.addBreadcrumb}. * * @example - * class NodeFrontend extends FrontendBase { + * class NodeClient extends BaseClient { * public constructor(options: NodeOptions) { * super(NodeBackend, options); * } @@ -55,11 +55,11 @@ export interface BackendClass { * // ... * } */ -export abstract class FrontendBase - implements Frontend { +export abstract class BaseClient + implements Client { /** * The backend used to physically interact in the enviornment. Usually, this - * will correspond to the frontend. When composing SDKs, however, the Backend + * will correspond to the client. When composing SDKs, however, the Backend * from the root SDK will be used. */ private readonly backend: B; @@ -76,7 +76,7 @@ export abstract class FrontendBase /** * A scope instance containing breadcrumbs and context, used if none is * specified to the public methods. This is specifically used in standalone - * mode, when the Frontend is directly instanciated by the user. + * mode, when the Client is directly instanciated by the user. */ private readonly internalScope: Scope; @@ -87,13 +87,13 @@ export abstract class FrontendBase private installed?: boolean; /** - * Initializes this frontend instance. + * Initializes this client instance. * * @param backendClass A constructor function to create the backend. - * @param options Options for the frontend. + * @param options Options for the client. */ protected constructor(backendClass: BackendClass, options: O) { - this.backend = new backendClass(this); + this.backend = new backendClass(options); this.options = options; if (options.dsn) { @@ -259,7 +259,7 @@ export abstract class FrontendBase * Adds common information to events. * * The information includes release and environment from `options`, SDK - * information returned by {@link FrontendBase.getSdkInfo}, as well as + * information returned by {@link BaseClient.getSdkInfo}, as well as * breadcrumbs and context (extra, tags and user) from the scope. * * Information that is already present in the event is never overwritten. For diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d19765bdb390..96622cb554b2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,5 +1,5 @@ -export { BackendClass, FrontendBase } from './base'; +export { BackendClass, BaseClient } from './base'; export { DSN, DSNComponents, DSNLike, DSNProtocol } from './dsn'; export { SentryError } from './error'; -export { Backend, Frontend, LogLevel, Options, Scope } from './interfaces'; -export { initAndBind, FrontendClass } from './sdk'; +export { Backend, Client, LogLevel, Options, Scope } from './interfaces'; +export { initAndBind, ClientClass } from './sdk'; diff --git a/packages/core/src/interfaces.ts b/packages/core/src/interfaces.ts index a7e506018b59..2d04d0e7b58b 100644 --- a/packages/core/src/interfaces.ts +++ b/packages/core/src/interfaces.ts @@ -6,9 +6,9 @@ import { SendStatus } from './status'; * An exchangable object containing event metadata that will be merged into the * event payload before sending it to Sentry. * - * Each Frontend creates an implicit scope when used directly. When using the + * Each Client creates an implicit scope when used directly. When using the * top-level exported functions, however, the shim maintains a stack of scopes - * and injects them into the frontend. + * and injects them into the client. */ export interface Scope { breadcrumbs: Breadcrumb[]; @@ -131,7 +131,7 @@ export interface Options { } /** - * User-Facing Sentry SDK Client Frontend. + * User-Facing Sentry SDK Client Client. * * This interface contains all methods to interface with the SDK once it has * been installed. It allows to send events to Sentry, record breadcrumbs and @@ -139,7 +139,7 @@ export interface Options { * there will only be one instance during runtime. To retrieve that instance, * use {@link Client.getInstance}. * - * Note that the call to {@link Frontend.install} should occur as early as + * Note that the call to {@link Client.install} should occur as early as * possible so that even errors during startup can be recorded reliably: * * @example @@ -150,7 +150,7 @@ export interface Options { * import { captureMessage } from '@sentry/node'; * captureMessage('Custom message'); */ -export interface Frontend { +export interface Client { /** * Installs the SDK if it hasn't been installed already. * @@ -222,22 +222,22 @@ export interface Frontend { /** * Internal platform-dependent Sentry SDK Backend. * - * While {@link Frontend} contains business logic specific to an SDK, the + * While {@link Client} contains business logic specific to an SDK, the * Backend offers platform specific implementations for low-level operations. * These are persisting and loading information, sending events, and hooking * into the environment. * - * Backends receive a handle to the Frontend in their constructor. When a + * Backends receive a handle to the Client in their constructor. When a * Backend automatically generates events or breadcrumbs, it must pass them to - * the Frontend for validation and processing first. + * the Client for validation and processing first. * - * Usually, the Frontend will be of corresponding type, e.g. NodeBackend - * receives NodeFrontend. However, higher-level SDKs can choose to instanciate + * Usually, the Client will be of corresponding type, e.g. NodeBackend + * receives NodeClient. However, higher-level SDKs can choose to instanciate * multiple Backends and delegate tasks between them. In this case, an event * generated by one backend might very well be sent by another one. * - * The frontend also provides access to options via {@link Frontend.getOptions} - * and context via {@link Frontend.getContext}. Note that the user might update + * The client also provides access to options via {@link Client.getOptions} + * and context via {@link Client.getContext}. Note that the user might update * these any time and they should not be cached. */ export interface Backend { @@ -256,7 +256,7 @@ export interface Backend { /** * Receives a breadcrumb and stores it in a platform-dependent way. * - * This function is invoked by the frontend before merging the breadcrumb into + * This function is invoked by the client before merging the breadcrumb into * the scope. Return `false` to prevent this breadcrumb from being merged. * This should be done for custom breadcrumb management in the backend. * @@ -264,8 +264,8 @@ export interface Backend { * simply return `true`. It can either be synchronous or asynchronous. * * @param breadcrumb The breadcrumb to store. - * @param scope The scope instance currently managed by the frontend. - * @returns True if the breadcrumb should be merged by the frontend. + * @param scope The scope instance currently managed by the client. + * @returns True if the breadcrumb should be merged by the client. */ storeBreadcrumb( breadcrumb: Breadcrumb, @@ -275,7 +275,7 @@ export interface Backend { /** * Receives a context and merges it in a platform-dependent way. * - * This function is invoked by the frontend before merging the context into + * This function is invoked by the client before merging the context into * the scope. Return `false` to prevent this context from being merged. This * should be done for custom context management in the backend. * @@ -283,8 +283,8 @@ export interface Backend { * simply return `true`. It can either be synchronous or asynchronous. * * @param context The context to store. - * @param scope The scope instance currently managed by the frontend. - * @returns True if the breadcrumb should be merged by the frontend. + * @param scope The scope instance currently managed by the client. + * @returns True if the breadcrumb should be merged by the client. */ storeContext(context: Context, scope: Scope): boolean | Promise; } diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index 0963b0db9ccb..a4bf95cc5395 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,5 +1,5 @@ import * as Shim from '@sentry/shim'; -import { Frontend, Options } from './interfaces'; +import { Client, Options } from './interfaces'; export { captureException, @@ -11,28 +11,28 @@ export { setTagsContext, } from '@sentry/shim'; -/** A class object that can instanciate Frontend objects. */ -export interface FrontendClass { +/** A class object that can instanciate Client objects. */ +export interface ClientClass { new (options: O): F; } /** - * Internal function to create a new SDK frontend instance. The frontend is + * Internal function to create a new SDK client instance. The client is * installed and then bound to the current scope. * - * @param frontendClass The frontend class to instanciate. - * @param options Options to pass to the frontend. - * @returns The installed and bound frontend instance. + * @param clientClass The client class to instanciate. + * @param options Options to pass to the client. + * @returns The installed and bound client instance. */ -export function initAndBind( - frontendClass: FrontendClass, +export function initAndBind( + clientClass: ClientClass, options: O, ): void { if (Shim.getCurrentClient()) { return; } - const frontend = new frontendClass(options); - frontend.install(); - Shim.bindClient(frontend); + const client = new clientClass(options); + client.install(); + Shim.bindClient(client); } diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 0ebb3abcb046..1945e6c60f2c 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -4,31 +4,31 @@ import { spy } from 'sinon'; import { SentryError } from '../../src/error'; import { Scope } from '../../src/interfaces'; import { TestBackend, TestOptions } from '../mocks/backend'; -import { TEST_SDK, TestFrontend } from '../mocks/frontend'; +import { TEST_SDK, TestClient } from '../mocks/client'; const PUBLIC_DSN = 'https://username@domain/path'; -describe('FrontendBase', () => { +describe('BaseClient', () => { describe('constructor() / getDSN()', () => { it('returns the DSN', () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); - expect(frontend.getDSN()!.toString()).to.equal(PUBLIC_DSN); + const client = new TestClient({ dsn: PUBLIC_DSN }); + expect(client.getDSN()!.toString()).to.equal(PUBLIC_DSN); }); it('allows missing DSN', () => { - const frontend = new TestFrontend({}); - expect(frontend.getDSN()).to.be.undefined; + const client = new TestClient({}); + expect(client.getDSN()).to.be.undefined; }); it('throws with invalid DSN', () => { - expect(() => new TestFrontend({ dsn: 'abc' })).to.throw(SentryError); + expect(() => new TestClient({ dsn: 'abc' })).to.throw(SentryError); }); it('initializes the internal scope', () => { const options = { dsn: PUBLIC_DSN }; const scope = { breadcrumbs: [], context: { extra: { custom: true } } }; - class TempFrontend extends TestFrontend { + class TempClient extends TestClient { public constructor(opts: TestOptions) { super(opts); expect(this.getInternalScope()).to.equal(scope); @@ -42,39 +42,39 @@ describe('FrontendBase', () => { } } - new TempFrontend(options); + new TempClient(options); }); }); describe('install()', () => { it('calls install() on Backend', async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); - frontend.install(); + const client = new TestClient({ dsn: PUBLIC_DSN }); + client.install(); expect(TestBackend.instance!.installed).to.equal(1); }); it('calls install() only once', async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); - frontend.install(); - frontend.install(); + const client = new TestClient({ dsn: PUBLIC_DSN }); + client.install(); + client.install(); expect(TestBackend.instance!.installed).to.equal(1); }); it('resolves the result of install()', async () => { - const frontend = new TestFrontend({ mockInstallFailure: true }); - const installed = frontend.install(); + const client = new TestClient({ mockInstallFailure: true }); + const installed = client.install(); expect(installed).to.be.false; }); it('does not install() when disabled', async () => { - const frontend = new TestFrontend({ enabled: false, dsn: PUBLIC_DSN }); - frontend.install(); + const client = new TestClient({ enabled: false, dsn: PUBLIC_DSN }); + client.install(); expect(TestBackend.instance!.installed).to.equal(0); }); it('does not install() without DSN', async () => { - const frontend = new TestFrontend({}); - frontend.install(); + const client = new TestClient({}); + client.install(); expect(TestBackend.instance!.installed).to.equal(0); }); }); @@ -82,53 +82,53 @@ describe('FrontendBase', () => { describe('getOptions()', () => { it('returns the options', () => { const options = { dsn: PUBLIC_DSN, test: true }; - const frontend = new TestFrontend(options); - expect(frontend.getOptions()).to.deep.equal(options); + const client = new TestClient(options); + expect(client.getOptions()).to.deep.equal(options); }); }); describe('getContext() / setContext()', () => { it('stores the context on the scope', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const context = { extra: { updated: true } }; const scope = { breadcrumbs: [], context: {} }; - await frontend.setContext(context, scope); + await client.setContext(context, scope); expect(scope.context).to.deep.equal(context); }); it('merges extra into context', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const scope = { breadcrumbs: [], context: { extra: { a: 'a' } } }; - await frontend.setContext({ extra: { b: 'b' } }, scope); + await client.setContext({ extra: { b: 'b' } }, scope); expect(scope.context).to.deep.equal({ extra: { a: 'a', b: 'b' }, }); }); it('merges tags into context', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const scope = { breadcrumbs: [], context: { tags: { a: 'a' } } }; - await frontend.setContext({ tags: { b: 'b' } }, scope); + await client.setContext({ tags: { b: 'b' } }, scope); expect(scope.context).to.deep.equal({ tags: { a: 'a', b: 'b' }, }); }); it('merges user into context', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const scope = { breadcrumbs: [], context: { user: { id: 'a' } } }; - await frontend.setContext({ user: { email: 'b' } }, scope); + await client.setContext({ user: { email: 'b' } }, scope); expect(scope.context).to.deep.equal({ user: { id: 'a', email: 'b' }, }); }); it('allows concurrent updates', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const scope = { breadcrumbs: [], context: {} }; await Promise.all([ - frontend.setContext({ user: { email: 'a' } }, scope), - frontend.setContext({ user: { id: 'b' } }, scope), + client.setContext({ user: { email: 'a' } }, scope), + client.setContext({ user: { id: 'b' } }, scope), ]); expect(scope.context).to.deep.equal({ user: { @@ -141,77 +141,77 @@ describe('FrontendBase', () => { describe('getBreadcrumbs() / addBreadcrumb()', () => { it('adds a breadcrumb', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const scope = { breadcrumbs: [{ message: 'hello' }], context: {} }; - await frontend.addBreadcrumb({ message: 'world' }, scope); + await client.addBreadcrumb({ message: 'world' }, scope); expect(scope.breadcrumbs[1].message).to.equal('world'); }); it('adds a timestamp to new breadcrumbs', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const scope = { breadcrumbs: [{ message: 'hello' }], context: {} }; - await frontend.addBreadcrumb({ message: 'world' }, scope); + await client.addBreadcrumb({ message: 'world' }, scope); expect((scope.breadcrumbs[1] as Breadcrumb).timestamp).to.be.a('number'); }); it('discards breadcrumbs beyond maxBreadcrumbs', async () => { - const frontend = new TestFrontend({ maxBreadcrumbs: 1 }); + const client = new TestClient({ maxBreadcrumbs: 1 }); const scope = { breadcrumbs: [{ message: 'hello' }], context: {} }; - await frontend.addBreadcrumb({ message: 'world' }, scope); + await client.addBreadcrumb({ message: 'world' }, scope); expect(scope.breadcrumbs.length).to.equal(1); expect(scope.breadcrumbs[0].message).to.equal('world'); }); it('exits early when breadcrumbs are deactivated', async () => { const shouldAddBreadcrumb = spy(); - const frontend = new TestFrontend({ + const client = new TestClient({ maxBreadcrumbs: 0, shouldAddBreadcrumb, }); const scope = { breadcrumbs: [], context: {} }; - await frontend.addBreadcrumb({ message: 'hello' }, scope); + await client.addBreadcrumb({ message: 'hello' }, scope); expect(shouldAddBreadcrumb.callCount).to.equal(0); }); it('calls shouldAddBreadcrumb and adds the breadcrumb', async () => { const shouldAddBreadcrumb = spy(() => true); - const frontend = new TestFrontend({ shouldAddBreadcrumb }); + const client = new TestClient({ shouldAddBreadcrumb }); const scope = { breadcrumbs: [], context: {} }; - await frontend.addBreadcrumb({ message: 'hello' }, scope); + await client.addBreadcrumb({ message: 'hello' }, scope); expect(scope.breadcrumbs.length).to.equal(1); }); it('calls shouldAddBreadcrumb and discards the breadcrumb', async () => { const shouldAddBreadcrumb = spy(() => false); - const frontend = new TestFrontend({ shouldAddBreadcrumb }); + const client = new TestClient({ shouldAddBreadcrumb }); const scope = { breadcrumbs: [], context: {} }; - await frontend.addBreadcrumb({ message: 'hello' }, scope); + await client.addBreadcrumb({ message: 'hello' }, scope); expect(scope.breadcrumbs.length).to.equal(0); }); it('calls beforeBreadcrumb and uses the new one', async () => { const beforeBreadcrumb = spy(() => ({ message: 'changed' })); - const frontend = new TestFrontend({ beforeBreadcrumb }); + const client = new TestClient({ beforeBreadcrumb }); const scope = { breadcrumbs: [], context: {} }; - await frontend.addBreadcrumb({ message: 'hello' }, scope); + await client.addBreadcrumb({ message: 'hello' }, scope); expect((scope.breadcrumbs[0] as Breadcrumb).message).to.equal('changed'); }); it('calls afterBreadcrumb', async () => { const afterBreadcrumb = spy(); - const frontend = new TestFrontend({ afterBreadcrumb }); + const client = new TestClient({ afterBreadcrumb }); const scope = { breadcrumbs: [], context: {} }; - await frontend.addBreadcrumb({ message: 'hello' }, scope); + await client.addBreadcrumb({ message: 'hello' }, scope); const breadcrumb = afterBreadcrumb.getCall(0).args[0] as Breadcrumb; expect(breadcrumb.message).to.equal('hello'); }); it('allows concurrent updates', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const scope = { breadcrumbs: [], context: {} }; await Promise.all([ - frontend.addBreadcrumb({ message: 'hello' }, scope), - frontend.addBreadcrumb({ message: 'world' }, scope), + client.addBreadcrumb({ message: 'hello' }, scope), + client.addBreadcrumb({ message: 'world' }, scope), ]); expect(scope.breadcrumbs).to.have.lengthOf(2); }); @@ -219,9 +219,9 @@ describe('FrontendBase', () => { describe('captures', () => { it('captures and sends exceptions', async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); + const client = new TestClient({ dsn: PUBLIC_DSN }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureException(new Error('test exception'), scope); + await client.captureException(new Error('test exception'), scope); expect(TestBackend.instance!.event).to.deep.equal({ exception: [ { @@ -235,9 +235,9 @@ describe('FrontendBase', () => { }); it('captures and sends messages', async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); + const client = new TestClient({ dsn: PUBLIC_DSN }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureMessage('test message', scope); + await client.captureMessage('test message', scope); expect(TestBackend.instance!.event).to.deep.equal({ message: 'test message', sdk: TEST_SDK, @@ -247,23 +247,23 @@ describe('FrontendBase', () => { describe('captureEvent() / prepareEvent()', () => { it('skips when disabled', async () => { - const frontend = new TestFrontend({ enabled: false, dsn: PUBLIC_DSN }); + const client = new TestClient({ enabled: false, dsn: PUBLIC_DSN }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({}, scope); + await client.captureEvent({}, scope); expect(TestBackend.instance!.event).to.be.undefined; }); it('skips without a DSN', async () => { - const frontend = new TestFrontend({}); + const client = new TestClient({}); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({}, scope); + await client.captureEvent({}, scope); expect(TestBackend.instance!.event).to.be.undefined; }); it('sends an event', async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); + const client = new TestClient({ dsn: PUBLIC_DSN }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({ message: 'message' }, scope); + await client.captureEvent({ message: 'message' }, scope); expect(TestBackend.instance!.event!.message).to.equal('message'); expect(TestBackend.instance!.event).to.deep.equal({ message: 'message', @@ -272,12 +272,12 @@ describe('FrontendBase', () => { }); it('adds the configured environment', async () => { - const frontend = new TestFrontend({ + const client = new TestClient({ dsn: PUBLIC_DSN, environment: 'env', }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({ message: 'message' }, scope); + await client.captureEvent({ message: 'message' }, scope); expect(TestBackend.instance!.event!).to.deep.equal({ environment: 'env', message: 'message', @@ -286,12 +286,12 @@ describe('FrontendBase', () => { }); it('adds the configured release', async () => { - const frontend = new TestFrontend({ + const client = new TestClient({ dsn: PUBLIC_DSN, release: 'v1.0.0', }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({ message: 'message' }, scope); + await client.captureEvent({ message: 'message' }, scope); expect(TestBackend.instance!.event!).to.deep.equal({ message: 'message', release: 'v1.0.0', @@ -300,9 +300,9 @@ describe('FrontendBase', () => { }); it('adds breadcrumbs', async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); + const client = new TestClient({ dsn: PUBLIC_DSN }); const scope = { breadcrumbs: [{ message: 'breadcrumb' }], context: {} }; - await frontend.captureEvent({ message: 'message' }, scope); + await client.captureEvent({ message: 'message' }, scope); expect(TestBackend.instance!.event!).to.deep.equal({ breadcrumbs: [{ message: 'breadcrumb' }], message: 'message', @@ -311,12 +311,12 @@ describe('FrontendBase', () => { }); it('limits previously saved breadcrumbs', async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }); + const client = new TestClient({ dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }); const scope = { breadcrumbs: [{ message: '1' }, { message: '2' }], context: {}, }; - await frontend.captureEvent({ message: 'message' }, scope); + await client.captureEvent({ message: 'message' }, scope); expect(TestBackend.instance!.event!).to.deep.equal({ breadcrumbs: [{ message: '2' }], message: 'message', @@ -325,7 +325,7 @@ describe('FrontendBase', () => { }); it('adds context data', async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); + const client = new TestClient({ dsn: PUBLIC_DSN }); const scope = { breadcrumbs: [], context: { @@ -334,7 +334,7 @@ describe('FrontendBase', () => { user: { id: 'user' }, }, }; - await frontend.captureEvent({ message: 'message' }, scope); + await client.captureEvent({ message: 'message' }, scope); expect(TestBackend.instance!.event!).to.deep.equal({ extra: { a: 'a' }, message: 'message', @@ -346,9 +346,9 @@ describe('FrontendBase', () => { it('calls shouldSend and adds the event', async () => { const shouldSend = spy(() => true); - const frontend = new TestFrontend({ dsn: PUBLIC_DSN, shouldSend }); + const client = new TestClient({ dsn: PUBLIC_DSN, shouldSend }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({ message: 'hello' }, scope); + await client.captureEvent({ message: 'hello' }, scope); expect(TestBackend.instance!.event).to.deep.equal({ message: 'hello', sdk: TEST_SDK, @@ -357,34 +357,34 @@ describe('FrontendBase', () => { it('calls shouldSend and discards the event', async () => { const shouldSend = spy(() => false); - const frontend = new TestFrontend({ dsn: PUBLIC_DSN, shouldSend }); + const client = new TestClient({ dsn: PUBLIC_DSN, shouldSend }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({ message: 'hello' }, scope); + await client.captureEvent({ message: 'hello' }, scope); expect(TestBackend.instance!.event).to.be.undefined; }); it('calls beforeSend and uses the new one', async () => { const beforeSend = spy(() => ({ message: 'changed' })); - const frontend = new TestFrontend({ dsn: PUBLIC_DSN, beforeSend }); + const client = new TestClient({ dsn: PUBLIC_DSN, beforeSend }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({ message: 'hello' }, scope); + await client.captureEvent({ message: 'hello' }, scope); expect(TestBackend.instance!.event!.message).to.equal('changed'); }); it('calls afterSend', async () => { const afterSend = spy(); - const frontend = new TestFrontend({ dsn: PUBLIC_DSN, afterSend }); + const client = new TestClient({ dsn: PUBLIC_DSN, afterSend }); const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({ message: 'hello' }, scope); + await client.captureEvent({ message: 'hello' }, scope); const breadcrumb = afterSend.getCall(0).args[0] as SentryEvent; expect(breadcrumb.message).to.equal('hello'); }); it("doesn't do anything with rate limits yet", async () => { - const frontend = new TestFrontend({ dsn: PUBLIC_DSN }); + const client = new TestClient({ dsn: PUBLIC_DSN }); TestBackend.instance!.sendEvent = async () => 429; const scope = { breadcrumbs: [], context: {} }; - await frontend.captureEvent({}, scope); + await client.captureEvent({}, scope); // TODO: Test rate limiting queues here }); }); diff --git a/packages/core/test/mocks/backend.ts b/packages/core/test/mocks/backend.ts index 8372f814c334..dff2c6480118 100644 --- a/packages/core/test/mocks/backend.ts +++ b/packages/core/test/mocks/backend.ts @@ -1,5 +1,5 @@ import { Breadcrumb, Context, SentryEvent } from '@sentry/shim'; -import { Backend, Frontend, Options, Scope } from '../../src/interfaces'; +import { Backend, Client, Options, Scope } from '../../src/interfaces'; export interface TestOptions extends Options { test?: boolean; @@ -12,14 +12,14 @@ export class TestBackend implements Backend { public installed: number; public event?: SentryEvent; - public constructor(private readonly frontend: Frontend) { + public constructor(private readonly options: TestOptions) { TestBackend.instance = this; this.installed = 0; } public install(): boolean { this.installed += 1; - return !this.frontend.getOptions().mockInstallFailure; + return !this.options.mockInstallFailure; } public async eventFromException(exception: any): Promise { diff --git a/packages/core/test/mocks/frontend.ts b/packages/core/test/mocks/client.ts similarity index 63% rename from packages/core/test/mocks/frontend.ts rename to packages/core/test/mocks/client.ts index 3fb664333615..06a8fd3aa84b 100644 --- a/packages/core/test/mocks/frontend.ts +++ b/packages/core/test/mocks/client.ts @@ -1,5 +1,5 @@ import { SdkInfo } from '@sentry/shim'; -import { FrontendBase } from '../../src/base'; +import { BaseClient } from '../../src/base'; import { initAndBind } from '../../src/sdk'; import { TestBackend, TestOptions } from './backend'; @@ -8,12 +8,12 @@ export const TEST_SDK = { version: '0.0.0-dev', }; -export class TestFrontend extends FrontendBase { - public static instance?: TestFrontend; +export class TestClient extends BaseClient { + public static instance?: TestClient; public constructor(options: TestOptions) { super(TestBackend, options); - TestFrontend.instance = this; + TestClient.instance = this; } public getSdkInfo(): SdkInfo { @@ -22,5 +22,5 @@ export class TestFrontend extends FrontendBase { } export function init(options: TestOptions): void { - initAndBind(TestFrontend, options); + initAndBind(TestClient, options); } diff --git a/packages/node/README.md b/packages/node/README.md index 813e55308182..777dec4dfd21 100644 --- a/packages/node/README.md +++ b/packages/node/README.md @@ -65,9 +65,9 @@ If you don't want to use a global static instance of Sentry, you can create one yourself: ```javascript -const { NodeFrontend } = require('@sentry/node'); +const { NodeClient } = require('@sentry/node'); -const client = new NodeFrontend({ +const client = new NodeClient({ dsn: '__DSN__', // ... }); diff --git a/packages/node/src/backend.ts b/packages/node/src/backend.ts index 5a4fe82aa683..87e5a738a826 100644 --- a/packages/node/src/backend.ts +++ b/packages/node/src/backend.ts @@ -1,4 +1,4 @@ -import { Backend, Frontend, Options, SentryError } from '@sentry/core'; +import { Backend, DSN, Options, SentryError } from '@sentry/core'; import { addBreadcrumb, captureEvent, SentryEvent } from '@sentry/shim'; import { HTTPSTransport, @@ -18,7 +18,7 @@ interface FunctionExt extends Function { /** * Configuration options for the Sentry Node SDK. - * @see NodeFrontend for more information. + * @see NodeClient for more information. */ export interface NodeOptions extends Options { /** @@ -44,42 +44,35 @@ export interface NodeOptions extends Options { /** The Sentry Node SDK Backend. */ export class NodeBackend implements Backend { - /** Handle to the SDK frontend for callbacks. */ - private readonly frontend: Frontend; - /** Creates a new Node backend instance. */ - public constructor(frontend: Frontend) { - this.frontend = frontend; - } + public constructor(private readonly options: NodeOptions) {} /** * @inheritDoc */ public install(): boolean { - // We are only called by the frontend if the SDK is enabled and a valid DSN + // We are only called by the client if the SDK is enabled and a valid DSN // has been configured. If no DSN is present, this indicates a programming // error. - const dsn = this.frontend.getDSN(); + const dsn = this.options.dsn; if (!dsn) { throw new SentryError( 'Invariant exception: install() must not be called when disabled', ); } - const { onFatalError } = this.frontend.getOptions(); - Raven.config(dsn.toString(true), this.frontend.getOptions()).install( - onFatalError, - ); + const { onFatalError } = this.options; + Raven.config(dsn, this.options).install(onFatalError); // Hook into Raven's breadcrumb mechanism. This allows us to intercept both - // breadcrumbs created internally by Raven and pass them to the Frontend + // breadcrumbs created internally by Raven and pass them to the Client // first, before actually capturing them. Raven.captureBreadcrumb = breadcrumb => { addBreadcrumb(breadcrumb); }; // Hook into Raven's internal event sending mechanism. This allows us to - // pass events to the frontend, before they will be sent back here for + // pass events to the client, before they will be sent back here for // actual submission. Raven.send = (event, callback) => { if (callback && (callback as FunctionExt).__SENTRY_CAPTURE__) { @@ -147,13 +140,14 @@ export class NodeBackend implements Backend { * @param transport The transport to use for submitting events. */ public setTransport(transport: Transport): void { - const dsn = this.frontend.getDSN(); + const dsn = this.options.dsn; if (!dsn) { return; } + const dsnObject = new DSN(dsn); Raven.transport = - dsn.protocol === 'http' + dsnObject.protocol === 'http' ? new HTTPTransport({ transport }) : new HTTPSTransport({ transport }); } diff --git a/packages/node/src/frontend.ts b/packages/node/src/client.ts similarity index 79% rename from packages/node/src/frontend.ts rename to packages/node/src/client.ts index 142ee6e616e4..98eb479bcabe 100644 --- a/packages/node/src/frontend.ts +++ b/packages/node/src/client.ts @@ -1,15 +1,15 @@ -import { FrontendBase } from '@sentry/core'; +import { BaseClient } from '@sentry/core'; import { SdkInfo } from '@sentry/shim'; import { NodeBackend, NodeOptions } from './backend'; import { Raven } from './raven'; /** - * The Sentry Node SDK Frontend. + * The Sentry Node SDK Client. * * @see NodeOptions for documentation on configuration options. * @see SentryClient for usage documentation. */ -export class NodeFrontend extends FrontendBase { +export class NodeClient extends BaseClient { /** * Creates a new Node SDK instance. * @param options Configuration options for this SDK. diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 8452ee901470..218d1b1c8a38 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -27,5 +27,5 @@ export { } from '@sentry/shim'; export { NodeBackend, NodeOptions } from './backend'; -export { NodeFrontend } from './frontend'; -export { init, getCurrentFrontend } from './sdk'; +export { NodeClient } from './client'; +export { init, getCurrentClient } from './sdk'; diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 684595dc6844..e54132ed9380 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,7 +1,7 @@ import { initAndBind } from '@sentry/core'; -import { getCurrentClient } from '@sentry/shim'; +import { getCurrentClient as shimGetCurrentClient } from '@sentry/shim'; import { NodeOptions } from './backend'; -import { NodeFrontend } from './frontend'; +import { NodeClient } from './client'; /** * The Sentry Node SDK Client. @@ -47,10 +47,10 @@ import { NodeFrontend } from './frontend'; * @see NodeOptions for documentation on configuration options. */ export function init(options: NodeOptions): void { - initAndBind(NodeFrontend, options); + initAndBind(NodeClient, options); } -/** Returns the current NodeFrontend, if any. */ -export function getCurrentFrontend(): NodeFrontend { - return getCurrentClient() as NodeFrontend; +/** Returns the current NodeClient, if any. */ +export function getCurrentClient(): NodeClient { + return shimGetCurrentClient() as NodeClient; } diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 516fe3085d4e..dcd268f36391 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -18,7 +18,7 @@ import { Context, init, NodeBackend, - NodeFrontend, + NodeClient, popScope, pushScope, SentryEvent, @@ -38,7 +38,7 @@ describe('SentryNode', () => { let s: sinon.SinonSpy; beforeEach(() => { - s = spy(NodeFrontend.prototype, 'setContext'); + s = spy(NodeClient.prototype, 'setContext'); }); afterEach(() => { @@ -79,7 +79,7 @@ describe('SentryNode', () => { it('should record auto breadcrumbs', done => { pushScope( - new NodeFrontend({ + new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.breadcrumbs!).to.have.lengthOf(3); done(); @@ -118,7 +118,7 @@ describe('SentryNode', () => { it('should capture an exception', done => { pushScope( - new NodeFrontend({ + new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.exception).to.not.be.undefined; expect(event.exception![0]).to.not.be.undefined; @@ -140,7 +140,7 @@ describe('SentryNode', () => { it('should capture a message', done => { pushScope( - new NodeFrontend({ + new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.message).to.equal('test'); expect(event.exception).to.be.undefined; @@ -155,7 +155,7 @@ describe('SentryNode', () => { it('should capture an event', done => { pushScope( - new NodeFrontend({ + new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.message).to.equal('test'); expect(event.exception).to.be.undefined; @@ -173,7 +173,7 @@ describe('SentryNode', () => { const d = domain.create(); d.run(() => { pushScope( - new NodeFrontend({ + new NodeClient({ afterSend: (event: SentryEvent) => { expect(event.message).to.equal('test'); expect(event.exception).to.be.undefined; diff --git a/packages/shim/src/sdk.ts b/packages/shim/src/sdk.ts index c06bd7151b27..180cc7a549e3 100644 --- a/packages/shim/src/sdk.ts +++ b/packages/shim/src/sdk.ts @@ -12,7 +12,7 @@ function logError(e?: any): void { /** * Internal helper function to call a method on the top client if it exists. * - * @param method The method to call on the client/frontend. + * @param method The method to call on the client/client. * @param args Arguments to pass to the client/fontend. */ function invokeClient(method: string, ...args: any[]): void { @@ -26,7 +26,7 @@ function invokeClient(method: string, ...args: any[]): void { * Internal helper function to call an async method on the top client if it * exists. * - * @param method The method to call on the client/frontend. + * @param method The method to call on the client/client. * @param callback A callback called with the error or success return value. * @param args Arguments to pass to the client/fontend. */ @@ -136,7 +136,7 @@ export function getCurrentClient(): any | undefined { /** * This binds the given client to the current scope. - * @param client An SDK client (frontend) instance. + * @param client An SDK client (client) instance. */ export function bindClient(client: any): void { const shim = getOrCreateShim(); @@ -226,7 +226,7 @@ export function setExtraContext(extra: object): void { * the shim. It is not guaranteed that the client actually implements the * function. * - * @param method The method to call on the client/frontend. + * @param method The method to call on the client/client. * @param args Arguments to pass to the client/fontend. */ export function _callOnClient(method: string, ...args: any[]): void {