From 7bce4cc6fff6850010c5e24b94456385b04e2b3a Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Mon, 10 Jan 2022 23:13:02 -0500 Subject: [PATCH 01/37] IGNORE --- .github/workflows/build.yml | 14 ++++++++------ packages/utils/src/logger.ts | 4 ++++ packages/utils/src/object.ts | 7 ++++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c73ff666216..9476b841e506 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,12 @@ name: 'Build & Test' -on: - push: - branches: - - master - - release/** - pull_request: + +on: [push, pull_request] +#on: +# push: +# branches: +# - master +# - release/** +# pull_request: env: CACHED_DEPENDENCY_PATHS: | diff --git a/packages/utils/src/logger.ts b/packages/utils/src/logger.ts index 9c4d366ecb62..4f764572b523 100644 --- a/packages/utils/src/logger.ts +++ b/packages/utils/src/logger.ts @@ -53,6 +53,10 @@ export function consoleSandbox(callback: () => any): any { return result; } +function when(predicate: boolean, callback: () => any) { + return predicate ? callback() : undefined; +} + /** JSDoc */ class Logger { /** JSDoc */ diff --git a/packages/utils/src/object.ts b/packages/utils/src/object.ts index de4a4d000312..f27ddd24c833 100644 --- a/packages/utils/src/object.ts +++ b/packages/utils/src/object.ts @@ -86,9 +86,10 @@ export function getOriginalFunction(func: WrappedFunction): WrappedFunction | un * @returns string Encoded */ export function urlEncode(object: { [key: string]: any }): string { - return Object.keys(object) - .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`) - .join('&'); + return new URLSearchParams(object).toString(); + // return Object.keys(object) + // .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`) + // .join('&'); } /** From f553fef752b82b56b7f09443f78e87078a110086 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Mon, 10 Jan 2022 23:23:14 -0500 Subject: [PATCH 02/37] asd --- .github/workflows/build.yml | 14 ++++++-------- packages/utils/src/logger.ts | 4 ---- packages/utils/src/object.ts | 7 +++---- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9476b841e506..4c73ff666216 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,12 +1,10 @@ name: 'Build & Test' - -on: [push, pull_request] -#on: -# push: -# branches: -# - master -# - release/** -# pull_request: +on: + push: + branches: + - master + - release/** + pull_request: env: CACHED_DEPENDENCY_PATHS: | diff --git a/packages/utils/src/logger.ts b/packages/utils/src/logger.ts index 4f764572b523..9c4d366ecb62 100644 --- a/packages/utils/src/logger.ts +++ b/packages/utils/src/logger.ts @@ -53,10 +53,6 @@ export function consoleSandbox(callback: () => any): any { return result; } -function when(predicate: boolean, callback: () => any) { - return predicate ? callback() : undefined; -} - /** JSDoc */ class Logger { /** JSDoc */ diff --git a/packages/utils/src/object.ts b/packages/utils/src/object.ts index f27ddd24c833..de4a4d000312 100644 --- a/packages/utils/src/object.ts +++ b/packages/utils/src/object.ts @@ -86,10 +86,9 @@ export function getOriginalFunction(func: WrappedFunction): WrappedFunction | un * @returns string Encoded */ export function urlEncode(object: { [key: string]: any }): string { - return new URLSearchParams(object).toString(); - // return Object.keys(object) - // .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`) - // .join('&'); + return Object.keys(object) + .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`) + .join('&'); } /** From d7a49d762392496c7682f87886b34790f041b74e Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Mon, 10 Jan 2022 23:46:24 -0500 Subject: [PATCH 03/37] feat: move functions out of the class --- packages/hub/src/hub.ts | 64 +++++++++++++++++++++-------------- packages/hub/test/hub.test.ts | 7 ++-- packages/types/src/hub.ts | 10 ------ 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index c7fd4cac85e4..e2cbc56101fc 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -82,12 +82,35 @@ export interface DomainAsCarrier extends Carrier { members: { [key: string]: any }[]; } + +/** + * Returns the topmost scope layer in the order domain > local > process. + * + * @hidden + * */ +export function getStackTop(hub: Hub): Layer { + return hub._stack[hub._stack.length - 1]; +} + +/** + * Checks if this hub's version is older than the given version. + * + * @param hub The hub to check the version on. + * @param version A version number to compare to. + * @return True if the given version is newer; otherwise false. + * + * @hidden + */ +export function isOlderThan(hub: Hub, version: number): boolean { + return hub._version < version; +} + /** * @inheritDoc */ export class Hub implements HubInterface { /** Is a {@link Layer}[] containing the client and scope */ - private readonly _stack: Layer[] = [{}]; + public readonly _stack: Layer[] = [{}]; /** Contains the last event id of a captured event. */ private _lastEventId?: string; @@ -100,25 +123,18 @@ export class Hub implements HubInterface { * @param scope bound to the hub. * @param version number, higher number means higher priority. */ - public constructor(client?: Client, scope: Scope = new Scope(), private readonly _version: number = API_VERSION) { - this.getStackTop().scope = scope; + public constructor(client?: Client, scope: Scope = new Scope(), public readonly _version: number = API_VERSION) { + getStackTop(this).scope = scope; if (client) { this.bindClient(client); } } - /** - * @inheritDoc - */ - public isOlderThan(version: number): boolean { - return this._version < version; - } - /** * @inheritDoc */ public bindClient(client?: Client): void { - const top = this.getStackTop(); + const top = getStackTop(this); top.client = client; if (client && client.setupIntegrations) { client.setupIntegrations(); @@ -162,12 +178,12 @@ export class Hub implements HubInterface { * @inheritDoc */ public getClient(): C | undefined { - return this.getStackTop().client as C; + return getStackTop(this).client as C; } /** Returns the scope of the top stack. */ public getScope(): Scope | undefined { - return this.getStackTop().scope; + return getStackTop(this).scope; } /** Returns the scope stack for domains or the process. */ @@ -175,10 +191,6 @@ export class Hub implements HubInterface { return this._stack; } - /** Returns the topmost scope layer in the order domain > local > process. */ - public getStackTop(): Layer { - return this._stack[this._stack.length - 1]; - } /** * @inheritDoc @@ -270,7 +282,7 @@ export class Hub implements HubInterface { * @inheritDoc */ public addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void { - const { scope, client } = this.getStackTop(); + const { scope, client } = getStackTop(this); if (!scope || !client) return; @@ -344,7 +356,7 @@ export class Hub implements HubInterface { * @inheritDoc */ public configureScope(callback: (scope: Scope) => void): void { - const { scope, client } = this.getStackTop(); + const { scope, client } = getStackTop(this); if (scope && client) { callback(scope); } @@ -414,7 +426,7 @@ export class Hub implements HubInterface { * @inheritDoc */ public endSession(): void { - const layer = this.getStackTop(); + const layer = getStackTop(this); const scope = layer && layer.scope; const session = scope && scope.getSession(); if (session) { @@ -432,7 +444,7 @@ export class Hub implements HubInterface { * @inheritDoc */ public startSession(context?: SessionContext): Session { - const { scope, client } = this.getStackTop(); + const { scope, client } = getStackTop(this); const { release, environment } = (client && client.getOptions()) || {}; // Will fetch userAgent if called from browser sdk @@ -466,7 +478,7 @@ export class Hub implements HubInterface { * Sends the current Session on the scope */ private _sendSessionUpdate(): void { - const { scope, client } = this.getStackTop(); + const { scope, client } = getStackTop(this); if (!scope) return; const session = scope.getSession && scope.getSession(); @@ -485,7 +497,7 @@ export class Hub implements HubInterface { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any private _invokeClient(method: M, ...args: any[]): void { - const { scope, client } = this.getStackTop(); + const { scope, client } = getStackTop(this); if (client && client[method]) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any (client as any)[method](...args, scope); @@ -547,7 +559,7 @@ export function getCurrentHub(): Hub { const registry = getMainCarrier(); // If there's no hub, or its an old API, assign a new one - if (!hasHubOnCarrier(registry) || getHubFromCarrier(registry).isOlderThan(API_VERSION)) { + if (!hasHubOnCarrier(registry) || isOlderThan(getHubFromCarrier(registry), API_VERSION)) { setHubOnCarrier(registry, new Hub()); } @@ -588,8 +600,8 @@ function getHubFromActiveDomain(registry: Carrier): Hub { } // If there's no hub on current domain, or it's an old API, assign a new one - if (!hasHubOnCarrier(activeDomain) || getHubFromCarrier(activeDomain).isOlderThan(API_VERSION)) { - const registryHubTopStack = getHubFromCarrier(registry).getStackTop(); + if (!hasHubOnCarrier(activeDomain) || isOlderThan(getHubFromCarrier(activeDomain), API_VERSION)) { + const registryHubTopStack = getStackTop(getHubFromCarrier(registry)); setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, Scope.clone(registryHubTopStack.scope))); } diff --git a/packages/hub/test/hub.test.ts b/packages/hub/test/hub.test.ts index 4e865fa10412..2cdb27d1cf41 100644 --- a/packages/hub/test/hub.test.ts +++ b/packages/hub/test/hub.test.ts @@ -1,6 +1,7 @@ import { Event } from '@sentry/types'; import { getCurrentHub, Hub, Scope } from '../src'; +import { getStackTop, isOlderThan } from '../src/hub'; const clientFn: any = jest.fn(); @@ -36,7 +37,7 @@ describe('Hub', () => { test('isOlderThan', () => { const hub = new Hub(); - expect(hub.isOlderThan(0)).toBeFalsy(); + expect(isOlderThan(hub, 0)).toBeFalsy(); }); describe('pushScope', () => { @@ -104,7 +105,7 @@ describe('Hub', () => { }); hub.pushScope(); - const pushedScope = hub.getStackTop().scope; + const pushedScope = getStackTop(hub).scope; return pushedScope!.applyToEvent(event).then(final => { expect(final!.dist).toEqual('1'); @@ -172,7 +173,7 @@ describe('Hub', () => { hub.pushScope(); hub.pushScope(); hub.bindClient(testClient); - expect(hub.getStackTop().client).toEqual({ bla: 'a' }); + expect(getStackTop(hub).client).toEqual({ bla: 'a' }); }); describe('configureScope', () => { diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index 6dac7c47e545..c40b7c040b56 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -16,16 +16,6 @@ import { User } from './user'; * working in case we have a version conflict. */ export interface Hub { - /** - * Checks if this hub's version is older than the given version. - * - * @param version A version number to compare to. - * @return True if the given version is newer; otherwise false. - * - * @hidden - */ - isOlderThan(version: number): boolean; - /** * This binds the given client to the current scope. * @param client An SDK client (client) instance. From 2fbffc47c4a30e6ba9d90d9a498d89c1a9215e87 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 00:19:00 -0500 Subject: [PATCH 04/37] chore: move cloneScope to file context instead of being a static function --- packages/core/src/baseclient.ts | 4 +- packages/hub/src/hub.ts | 6 +-- packages/hub/src/index.ts | 2 +- packages/hub/src/scope.ts | 74 ++++++++++++++++----------------- packages/hub/test/scope.test.ts | 12 +++--- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 755cbd741416..ca2133710db0 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { Scope, Session } from '@sentry/hub'; +import { Scope, Session, cloneScope } from '@sentry/hub'; import { Client, DsnComponents, @@ -360,7 +360,7 @@ export abstract class BaseClient implement // This allows us to prevent unnecessary copying of data if `captureContext` is not provided. let finalScope = scope; if (hint && hint.captureContext) { - finalScope = Scope.clone(finalScope).update(hint.captureContext); + finalScope = cloneScope(finalScope).update(hint.captureContext); } // We prepare the result here with a resolved Event. diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index e2cbc56101fc..38d30e311f68 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -22,7 +22,7 @@ import { } from '@sentry/types'; import { consoleSandbox, dateTimestampInSeconds, getGlobalObject, isNodeEnv, logger, uuid4 } from '@sentry/utils'; -import { Scope } from './scope'; +import { cloneScope, Scope } from './scope'; import { Session } from './session'; /** @@ -146,7 +146,7 @@ export class Hub implements HubInterface { */ public pushScope(): Scope { // We want to clone the content of prev scope - const scope = Scope.clone(this.getScope()); + const scope = cloneScope(this.getScope()); this.getStack().push({ client: this.getClient(), scope, @@ -602,7 +602,7 @@ function getHubFromActiveDomain(registry: Carrier): Hub { // If there's no hub on current domain, or it's an old API, assign a new one if (!hasHubOnCarrier(activeDomain) || isOlderThan(getHubFromCarrier(activeDomain), API_VERSION)) { const registryHubTopStack = getStackTop(getHubFromCarrier(registry)); - setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, Scope.clone(registryHubTopStack.scope))); + setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, cloneScope(registryHubTopStack.scope))); } // Return hub that lives on a domain diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 9c0a77625dc2..5c2c4bceab2c 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,4 +1,4 @@ -export { addGlobalEventProcessor, Scope } from './scope'; +export { addGlobalEventProcessor, Scope, cloneScope } from './scope'; export { Session } from './session'; export { SessionFlusher } from './sessionflusher'; export { diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 940d0443c9f7..157fd43639d1 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -28,75 +28,75 @@ import { Session } from './session'; */ const MAX_BREADCRUMBS = 100; +/** + * Inherit values from the parent scope. + * @param scope to clone. + */ +export function cloneScope(scope?: Scope): Scope { + const newScope = new Scope(); + if (scope) { + newScope._breadcrumbs = [...scope._breadcrumbs]; + newScope._tags = { ...scope._tags }; + newScope._extra = { ...scope._extra }; + newScope._contexts = { ...scope._contexts }; + newScope._user = scope._user; + newScope._level = scope._level; + newScope._span = scope._span; + newScope._session = scope._session; + newScope._transactionName = scope._transactionName; + newScope._fingerprint = scope._fingerprint; + newScope._eventProcessors = [...scope._eventProcessors]; + newScope._requestSession = scope._requestSession; + } + return newScope; +} + /** * Holds additional event information. {@link Scope.applyToEvent} will be * called by the client before an event will be sent. */ export class Scope implements ScopeInterface { /** Flag if notifying is happening. */ - protected _notifyingListeners: boolean = false; + public _notifyingListeners: boolean = false; /** Callback for client to receive scope changes. */ - protected _scopeListeners: Array<(scope: Scope) => void> = []; + public _scopeListeners: Array<(scope: Scope) => void> = []; /** Callback list that will be called after {@link applyToEvent}. */ - protected _eventProcessors: EventProcessor[] = []; + public _eventProcessors: EventProcessor[] = []; /** Array of breadcrumbs. */ - protected _breadcrumbs: Breadcrumb[] = []; + public _breadcrumbs: Breadcrumb[] = []; /** User */ - protected _user: User = {}; + public _user: User = {}; /** Tags */ - protected _tags: { [key: string]: Primitive } = {}; + public _tags: { [key: string]: Primitive } = {}; /** Extra */ - protected _extra: Extras = {}; + public _extra: Extras = {}; /** Contexts */ - protected _contexts: Contexts = {}; + public _contexts: Contexts = {}; /** Fingerprint */ - protected _fingerprint?: string[]; + public _fingerprint?: string[]; /** Severity */ - protected _level?: SeverityLevel; + public _level?: SeverityLevel; /** Transaction Name */ - protected _transactionName?: string; + public _transactionName?: string; /** Span */ - protected _span?: Span; + public _span?: Span; /** Session */ - protected _session?: Session; + public _session?: Session; /** Request Mode Session Status */ - protected _requestSession?: RequestSession; - - /** - * Inherit values from the parent scope. - * @param scope to clone. - */ - public static clone(scope?: Scope): Scope { - const newScope = new Scope(); - if (scope) { - newScope._breadcrumbs = [...scope._breadcrumbs]; - newScope._tags = { ...scope._tags }; - newScope._extra = { ...scope._extra }; - newScope._contexts = { ...scope._contexts }; - newScope._user = scope._user; - newScope._level = scope._level; - newScope._span = scope._span; - newScope._session = scope._session; - newScope._transactionName = scope._transactionName; - newScope._fingerprint = scope._fingerprint; - newScope._eventProcessors = [...scope._eventProcessors]; - newScope._requestSession = scope._requestSession; - } - return newScope; - } + public _requestSession?: RequestSession; /** * Add internal on change listener. Used for sub SDKs that need to store the scope. diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index 9b1760dcd11c..e0d6e1508a61 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -1,7 +1,7 @@ import { Event, EventHint } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils'; -import { addGlobalEventProcessor, Scope } from '../src'; +import { addGlobalEventProcessor, cloneScope, Scope } from '../src'; describe('Scope', () => { afterEach(() => { @@ -141,20 +141,20 @@ describe('Scope', () => { test('basic inheritance', () => { const parentScope = new Scope(); parentScope.setExtra('a', 1); - const scope = Scope.clone(parentScope); + const scope = cloneScope(parentScope); expect((parentScope as any)._extra).toEqual((scope as any)._extra); }); test('_requestSession clone', () => { const parentScope = new Scope(); parentScope.setRequestSession({ status: 'errored' }); - const scope = Scope.clone(parentScope); + const scope = cloneScope(parentScope); expect(parentScope.getRequestSession()).toEqual(scope.getRequestSession()); }); test('parent changed inheritance', () => { const parentScope = new Scope(); - const scope = Scope.clone(parentScope); + const scope = cloneScope(parentScope); parentScope.setExtra('a', 2); expect((scope as any)._extra).toEqual({}); expect((parentScope as any)._extra).toEqual({ a: 2 }); @@ -164,7 +164,7 @@ describe('Scope', () => { const parentScope = new Scope(); parentScope.setExtra('a', 1); - const scope = Scope.clone(parentScope); + const scope = cloneScope(parentScope); scope.setExtra('a', 2); expect((parentScope as any)._extra).toEqual({ a: 1 }); expect((scope as any)._extra).toEqual({ a: 2 }); @@ -176,7 +176,7 @@ describe('Scope', () => { const parentScope = new Scope(); parentScope.setRequestSession({ status: 'errored' }); - const scope = Scope.clone(parentScope); + const scope = cloneScope(parentScope); const requestSession = scope.getRequestSession(); if (requestSession) { requestSession.status = 'ok'; From ac90228ce414d398c4995f62511cba5504d201c9 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 00:50:29 -0500 Subject: [PATCH 05/37] chore: move more functions from the object out --- packages/core/src/baseclient.ts | 4 +-- packages/core/src/sdk.ts | 4 +-- packages/hub/src/hub.ts | 53 +++++++++++++++-------------- packages/hub/src/index.ts | 3 +- packages/hub/src/scope.ts | 14 ++++---- packages/nextjs/src/index.server.ts | 4 +-- packages/node/src/handlers.ts | 3 +- packages/node/src/sdk.ts | 4 +-- packages/types/src/hub.ts | 6 ---- packages/types/src/scope.ts | 5 --- 10 files changed, 46 insertions(+), 54 deletions(-) diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index ca2133710db0..cf53ec767f5c 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { Scope, Session, cloneScope } from '@sentry/hub'; +import { Scope, Session, cloneScope, getSession } from '@sentry/hub'; import { Client, DsnComponents, @@ -576,7 +576,7 @@ export abstract class BaseClient implement throw new SentryError('`beforeSend` returned `null`, will not send event.'); } - const session = scope && scope.getSession && scope.getSession(); + const session = getSession(scope); if (!isTransaction && session) { this._updateSessionFromEvent(session, processedEvent); } diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index e6af9a5e2336..a7291e21167c 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/hub'; +import { getCurrentHub, bindClient } from '@sentry/hub'; import { Client, Options } from '@sentry/types'; import { logger } from '@sentry/utils'; @@ -22,5 +22,5 @@ export function initAndBind(clientClass: Cl scope.update(options.initialScope); } const client = new clientClass(options); - hub.bindClient(client); + bindClient(hub, client); } diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 38d30e311f68..e5997463a1be 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -22,7 +22,7 @@ import { } from '@sentry/types'; import { consoleSandbox, dateTimestampInSeconds, getGlobalObject, isNodeEnv, logger, uuid4 } from '@sentry/utils'; -import { cloneScope, Scope } from './scope'; +import { cloneScope, getSession, Scope } from './scope'; import { Session } from './session'; /** @@ -92,6 +92,24 @@ export function getStackTop(hub: Hub): Layer { return hub._stack[hub._stack.length - 1]; } +/** Returns the scope stack for domains or the process. */ +function getStack(hub: Hub): Layer[] { + return hub._stack; +} + +/** + * This binds the given client to the current scope. + * @param hub The Hub instance. + * @param client An SDK client (client) instance. + */ +export function bindClient(hub: Hub, client?: Client): void { + const top = getStackTop(hub); + top.client = client; + if (client && client.setupIntegrations) { + client.setupIntegrations(); + } +} + /** * Checks if this hub's version is older than the given version. * @@ -126,18 +144,7 @@ export class Hub implements HubInterface { public constructor(client?: Client, scope: Scope = new Scope(), public readonly _version: number = API_VERSION) { getStackTop(this).scope = scope; if (client) { - this.bindClient(client); - } - } - - /** - * @inheritDoc - */ - public bindClient(client?: Client): void { - const top = getStackTop(this); - top.client = client; - if (client && client.setupIntegrations) { - client.setupIntegrations(); + bindClient(this, client); } } @@ -147,7 +154,7 @@ export class Hub implements HubInterface { public pushScope(): Scope { // We want to clone the content of prev scope const scope = cloneScope(this.getScope()); - this.getStack().push({ + getStack(this).push({ client: this.getClient(), scope, }); @@ -158,8 +165,8 @@ export class Hub implements HubInterface { * @inheritDoc */ public popScope(): boolean { - if (this.getStack().length <= 1) return false; - return !!this.getStack().pop(); + if (getStack(this).length <= 1) return false; + return !!getStack(this).pop(); } /** @@ -186,12 +193,6 @@ export class Hub implements HubInterface { return getStackTop(this).scope; } - /** Returns the scope stack for domains or the process. */ - public getStack(): Layer[] { - return this._stack; - } - - /** * @inheritDoc */ @@ -288,7 +289,7 @@ export class Hub implements HubInterface { // eslint-disable-next-line @typescript-eslint/unbound-method const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = - (client.getOptions && client.getOptions()) || {}; + (client.getOptions && client.getOptions()) || {}; if (maxBreadcrumbs <= 0) return; @@ -428,7 +429,7 @@ export class Hub implements HubInterface { public endSession(): void { const layer = getStackTop(this); const scope = layer && layer.scope; - const session = scope && scope.getSession(); + const session = getSession(scope); if (session) { session.close(); } @@ -461,7 +462,7 @@ export class Hub implements HubInterface { if (scope) { // End existing session if there's one - const currentSession = scope.getSession && scope.getSession(); + const currentSession = getSession(scope); if (currentSession && currentSession.status === 'ok') { currentSession.update({ status: 'exited' }); } @@ -481,7 +482,7 @@ export class Hub implements HubInterface { const { scope, client } = getStackTop(this); if (!scope) return; - const session = scope.getSession && scope.getSession(); + const session = getSession(scope) if (session) { if (client && client.captureSession) { client.captureSession(session); diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 5c2c4bceab2c..fb451886721c 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,4 +1,4 @@ -export { addGlobalEventProcessor, Scope, cloneScope } from './scope'; +export { addGlobalEventProcessor, cloneScope, getSession, Scope } from './scope'; export { Session } from './session'; export { SessionFlusher } from './sessionflusher'; export { @@ -7,6 +7,7 @@ export { getCurrentHub, getHubFromCarrier, getMainCarrier, + bindClient, Hub, makeMain, setHubOnCarrier, diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 157fd43639d1..a4ecb421ea4f 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -51,6 +51,13 @@ export function cloneScope(scope?: Scope): Scope { return newScope; } +/** + * Returns the `Session` if there is one + */ +export function getSession(scope?: Scope): Session | undefined { + return scope && scope._session; +} + /** * Holds additional event information. {@link Scope.applyToEvent} will be * called by the client before an event will be sent. @@ -290,13 +297,6 @@ export class Scope implements ScopeInterface { return this; } - /** - * @inheritDoc - */ - public getSession(): Session | undefined { - return this._session; - } - /** * @inheritDoc */ diff --git a/packages/nextjs/src/index.server.ts b/packages/nextjs/src/index.server.ts index ac3d4c4bb6ce..4b52348c17b4 100644 --- a/packages/nextjs/src/index.server.ts +++ b/packages/nextjs/src/index.server.ts @@ -1,4 +1,4 @@ -import { Carrier, getHubFromCarrier, getMainCarrier } from '@sentry/hub'; +import { Carrier, getHubFromCarrier, getMainCarrier, bindClient } from '@sentry/hub'; import { RewriteFrames } from '@sentry/integrations'; import { configureScope, getCurrentHub, init as nodeInit, Integrations } from '@sentry/node'; import { Event } from '@sentry/types'; @@ -75,7 +75,7 @@ export function init(options: NextjsOptions): void { const domainHub = getHubFromCarrier(activeDomain); // apply the changes made by `nodeInit` to the domain's hub also - domainHub.bindClient(globalHub.getClient()); + bindClient(domainHub, globalHub.getClient()); domainHub.getScope()?.update(globalHub.getScope()); // `scope.update()` doesn’t copy over event processors, so we have to add it manually domainHub.getScope()?.addEventProcessor(filterTransactions); diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 332d8593010a..87d3a7e1e28b 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -1,6 +1,7 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { captureException, getCurrentHub, startTransaction, withScope } from '@sentry/core'; +import { getSession } from '@sentry/hub'; import { extractTraceparentData, Span } from '@sentry/tracing'; import { Event, ExtractedNodeRequestData, Transaction } from '@sentry/types'; import { isPlainObject, isString, logger, normalize, stripUrlQueryAndFragment } from '@sentry/utils'; @@ -387,7 +388,7 @@ export function requestHandler( // If Scope contains a Single mode Session, it is removed in favor of using Session Aggregates mode const scope = currentHub.getScope(); - if (scope && scope.getSession()) { + if (scope && getSession(scope)) { scope.setSession(); } } diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 52d137277fd8..e4f79c7a5d86 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,5 +1,5 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; -import { getMainCarrier, setHubOnCarrier } from '@sentry/hub'; +import { getMainCarrier, setHubOnCarrier, getSession } from '@sentry/hub'; import { SessionStatus } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; import * as domain from 'domain'; @@ -231,7 +231,7 @@ function startSessionTracking(): void { // such as calling process.exit() or uncaught exceptions. // Ref: https://nodejs.org/api/process.html#process_event_beforeexit process.on('beforeExit', () => { - const session = hub.getScope()?.getSession(); + const session = getSession(hub.getScope()); const terminalStates: SessionStatus[] = ['exited', 'crashed']; // Only call endSession, if the Session exists on Scope and SessionStatus is not a // Terminal Status i.e. Exited or Crashed because diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index c40b7c040b56..6032d2f84e34 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -16,12 +16,6 @@ import { User } from './user'; * working in case we have a version conflict. */ export interface Hub { - /** - * This binds the given client to the current scope. - * @param client An SDK client (client) instance. - */ - bindClient(client?: Client): void; - /** * Create a new scope to store context information. * diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index 00014e0735d8..d65d3a61a38f 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -112,11 +112,6 @@ export interface Scope { */ getTransaction(): Transaction | undefined; - /** - * Returns the `Session` if there is one - */ - getSession(): Session | undefined; - /** * Sets the `Session` on the scope */ From 4ac12dd72497c707ed67677a0224127b7a281e9c Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 00:58:55 -0500 Subject: [PATCH 06/37] chore: move more hub functions out of the prototype --- packages/hub/src/hub.ts | 57 +++++++++++++++++----------- packages/hub/src/index.ts | 2 + packages/serverless/src/awslambda.ts | 5 ++- packages/types/src/hub.ts | 21 ---------- 4 files changed, 39 insertions(+), 46 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index e5997463a1be..89a9e2c5e2ec 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -110,6 +110,38 @@ export function bindClient(hub: Hub, client?: Client): void { } } +/** + * Removes a previously pushed scope from the stack. + * + * This restores the state before the scope was pushed. All breadcrumbs and + * context information added since the last call to {@link pushScope} are + * discarded. + */ +export function popScope(hub: Hub): boolean { + if (getStack(hub).length <= 1) return false; + return !!getStack(hub).pop(); +} + +/** + * Create a new scope to store context information. + * + * The scope will be layered on top of the current one. It is isolated, i.e. all + * breadcrumbs and context information added to this scope will be removed once + * the scope ends. Be sure to always remove this scope with {@link popScope} + * when the operation finishes or throws. + * + * @returns Scope, the new cloned scope + */ +export function pushScope(hub: Hub): Scope { + // We want to clone the content of prev scope + const scope = cloneScope(hub.getScope()); + getStack(hub).push({ + client: hub.getClient(), + scope, + }); + return scope; +} + /** * Checks if this hub's version is older than the given version. * @@ -148,36 +180,15 @@ export class Hub implements HubInterface { } } - /** - * @inheritDoc - */ - public pushScope(): Scope { - // We want to clone the content of prev scope - const scope = cloneScope(this.getScope()); - getStack(this).push({ - client: this.getClient(), - scope, - }); - return scope; - } - - /** - * @inheritDoc - */ - public popScope(): boolean { - if (getStack(this).length <= 1) return false; - return !!getStack(this).pop(); - } - /** * @inheritDoc */ public withScope(callback: (scope: Scope) => void): void { - const scope = this.pushScope(); + const scope = pushScope(this); try { callback(scope); } finally { - this.popScope(); + popScope(this); } } diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index fb451886721c..1b5afb8e32c2 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -8,6 +8,8 @@ export { getHubFromCarrier, getMainCarrier, bindClient, + popScope, + pushScope, Hub, makeMain, setHubOnCarrier, diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 6001613a91de..ebd773acd0bd 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -9,6 +9,7 @@ import { startTransaction, withScope, } from '@sentry/node'; +import { popScope, pushScope } from '@sentry/hub'; import { extractTraceparentData } from '@sentry/tracing'; import { Integration } from '@sentry/types'; import { isString, logger } from '@sentry/utils'; @@ -292,7 +293,7 @@ export function wrapHandler( }); const hub = getCurrentHub(); - const scope = hub.pushScope(); + const scope = pushScope(hub); let rv: TResult | undefined; try { enhanceScopeWithEnvironmentData(scope, context, START_TIME); @@ -315,7 +316,7 @@ export function wrapHandler( } finally { clearTimeout(timeoutWarningTimer); transaction.finish(); - hub.popScope(); + popScope(hub); await flush(options.flushTimeout); } return rv; diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index 6032d2f84e34..f1381efdd2fc 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -16,27 +16,6 @@ import { User } from './user'; * working in case we have a version conflict. */ export interface Hub { - /** - * Create a new scope to store context information. - * - * The scope will be layered on top of the current one. It is isolated, i.e. all - * breadcrumbs and context information added to this scope will be removed once - * the scope ends. Be sure to always remove this scope with {@link this.popScope} - * when the operation finishes or throws. - * - * @returns Scope, the new cloned scope - */ - pushScope(): Scope; - - /** - * Removes a previously pushed scope from the stack. - * - * This restores the state before the scope was pushed. All breadcrumbs and - * context information added since the last call to {@link this.pushScope} are - * discarded. - */ - popScope(): boolean; - /** * Creates a new scope with and executes the given operation within. * The scope is automatically removed once the operation From 3feec27c1bc7b590e2cd0057decfd664bbe92008 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 01:42:36 -0500 Subject: [PATCH 07/37] chore: move fns outside class --- packages/browser/package.json | 1 + .../src/integrations/globalhandlers.ts | 3 +- packages/browser/src/sdk.ts | 7 +- packages/browser/test/unit/index.test.ts | 3 +- .../core/src/integrations/inboundfilters.ts | 4 +- packages/hub/src/hub.ts | 53 ++++++++------ packages/hub/src/index.ts | 2 + packages/hub/test/hub.test.ts | 72 +++++++++---------- packages/integrations/package.json | 1 + packages/integrations/src/angular.ts | 3 +- packages/integrations/src/captureconsole.ts | 3 +- packages/integrations/src/ember.ts | 3 +- .../integrations/src/reportingobserver.ts | 3 +- packages/integrations/src/vue.ts | 3 +- packages/nextjs/src/index.server.ts | 6 +- packages/node/src/handlers.ts | 10 +-- .../src/integrations/onuncaughtexception.ts | 5 +- .../src/integrations/onunhandledrejection.ts | 3 +- .../src/integrations/utils/errorhandling.ts | 3 +- packages/node/src/integrations/utils/http.ts | 5 +- packages/node/src/sdk.ts | 6 +- packages/tracing/src/hubextensions.ts | 6 +- packages/tracing/src/transaction.ts | 4 +- packages/tracing/src/utils.ts | 4 +- packages/types/src/hub.ts | 15 ---- packages/vue/package.json | 1 + packages/vue/src/errorhandler.ts | 3 +- 27 files changed, 121 insertions(+), 111 deletions(-) diff --git a/packages/browser/package.json b/packages/browser/package.json index 2eb895b8421e..b5d3809489f7 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -16,6 +16,7 @@ "access": "public" }, "dependencies": { + "@sentry/hub": "6.17.0-beta.0", "@sentry/core": "6.17.0-beta.0", "@sentry/types": "6.17.0-beta.0", "@sentry/utils": "6.17.0-beta.0", diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index e71962d567d8..535b3e067d4e 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getCurrentHub } from '@sentry/core'; +import { getClient } from '@sentry/hub'; import { Event, EventHint, Hub, Integration, Primitive } from '@sentry/types'; import { addExceptionMechanism, @@ -257,7 +258,7 @@ function addMechanismAndCapture(hub: Hub, error: EventHint['originalException'], function getHubAndAttachStacktrace(): [Hub, boolean | undefined] { const hub = getCurrentHub(); - const client = hub.getClient(); + const client = getClient(hub); const attachStacktrace = client && client.getOptions().attachStacktrace; return [hub, attachStacktrace]; } diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index ed3f0c8ba888..c567d7641584 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -6,6 +6,7 @@ import { BrowserOptions } from './backend'; import { BrowserClient } from './client'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; +import { getClient } from '@sentry/hub'; export const defaultIntegrations = [ new CoreIntegrations.InboundFilters(), @@ -118,7 +119,7 @@ export function showReportDialog(options: ReportDialogOptions = {}): void { if (!options.eventId) { options.eventId = hub.lastEventId(); } - const client = hub.getClient(); + const client = getClient(hub); if (client) { client.showReportDialog(options); } @@ -158,7 +159,7 @@ export function onLoad(callback: () => void): void { * doesn't (or if there's no client defined). */ export function flush(timeout?: number): PromiseLike { - const client = getCurrentHub().getClient(); + const client = getClient(getCurrentHub()); if (client) { return client.flush(timeout); } @@ -177,7 +178,7 @@ export function flush(timeout?: number): PromiseLike { * doesn't (or if there's no client defined). */ export function close(timeout?: number): PromiseLike { - const client = getCurrentHub().getClient(); + const client = getClient(getCurrentHub()); if (client) { return client.close(timeout); } diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index f5f4554c552a..868d411a51b2 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -17,6 +17,7 @@ import { wrap, } from '../../src'; import { SimpleTransport } from './mocks/simpletransport'; +import { getClient } from '@sentry/hub'; const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; @@ -285,7 +286,7 @@ describe('SentryBrowser initialization', () => { }, }); - const sdkData = (getCurrentHub().getClient() as any)._backend._transport._api.metadata?.sdk; + const sdkData = (getClient(getCurrentHub()) as any)._backend._transport._api.metadata?.sdk; expect(sdkData.name).toBe('sentry.javascript.angular'); expect(sdkData.packages[0].name).toBe('npm:@sentry/angular'); diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index f779c3be6266..26c0adce2eca 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -1,4 +1,4 @@ -import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub'; +import { addGlobalEventProcessor, getCurrentHub, getClient } from '@sentry/hub'; import { Event, Integration, StackFrame } from '@sentry/types'; import { getEventDescription, isDebugBuild, isMatchingPattern, logger } from '@sentry/utils'; @@ -44,7 +44,7 @@ export class InboundFilters implements Integration { } const self = hub.getIntegration(InboundFilters); if (self) { - const client = hub.getClient(); + const client = getClient(hub); const clientOptions = client ? client.getOptions() : {}; // This checks prevents most of the occurrences of the bug linked below: // https://github.com/getsentry/sentry-javascript/issues/2622 diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 89a9e2c5e2ec..e1061f03eb28 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -93,7 +93,7 @@ export function getStackTop(hub: Hub): Layer { } /** Returns the scope stack for domains or the process. */ -function getStack(hub: Hub): Layer[] { +export function getStack(hub: Hub): Layer[] { return hub._stack; } @@ -136,7 +136,7 @@ export function pushScope(hub: Hub): Scope { // We want to clone the content of prev scope const scope = cloneScope(hub.getScope()); getStack(hub).push({ - client: hub.getClient(), + client: getClient(hub), scope, }); return scope; @@ -155,6 +155,34 @@ export function isOlderThan(hub: Hub, version: number): boolean { return hub._version < version; } +/** + * Creates a new scope with and executes the given operation within. + * The scope is automatically removed once the operation + * finishes or throws. + * + * This is essentially a convenience function for: + * + * pushScope(); + * callback(); + * popScope(); + * + * @param hub The Hub instance. + * @param callback that will be enclosed into push/popScope. + */ +export function withScope(hub: Hub, callback: (scope: Scope) => void): void { + const scope = pushScope(hub); + try { + callback(scope); + } finally { + popScope(hub); + } +} + +/** Returns the client of the top stack. */ +export function getClient(hub: Hub): C | undefined { + return getStackTop(hub).client as C; +} + /** * @inheritDoc */ @@ -180,25 +208,6 @@ export class Hub implements HubInterface { } } - /** - * @inheritDoc - */ - public withScope(callback: (scope: Scope) => void): void { - const scope = pushScope(this); - try { - callback(scope); - } finally { - popScope(this); - } - } - - /** - * @inheritDoc - */ - public getClient(): C | undefined { - return getStackTop(this).client as C; - } - /** Returns the scope of the top stack. */ public getScope(): Scope | undefined { return getStackTop(this).scope; @@ -390,7 +399,7 @@ export class Hub implements HubInterface { * @inheritDoc */ public getIntegration(integration: IntegrationClass): T | null { - const client = this.getClient(); + const client = getClient(this); if (!client) return null; try { return client.getIntegration(integration); diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 1b5afb8e32c2..5ea4dd3f36ea 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -10,6 +10,8 @@ export { bindClient, popScope, pushScope, + withScope, + getClient, Hub, makeMain, setHubOnCarrier, diff --git a/packages/hub/test/hub.test.ts b/packages/hub/test/hub.test.ts index 2cdb27d1cf41..b785ecf617b3 100644 --- a/packages/hub/test/hub.test.ts +++ b/packages/hub/test/hub.test.ts @@ -1,7 +1,7 @@ import { Event } from '@sentry/types'; -import { getCurrentHub, Hub, Scope } from '../src'; -import { getStackTop, isOlderThan } from '../src/hub'; +import { getCurrentHub, bindClient, pushScope, Hub, Scope } from '../src'; +import { getStackTop, isOlderThan, getStack } from '../src/hub'; const clientFn: any = jest.fn(); @@ -20,12 +20,12 @@ describe('Hub', () => { test('push process into stack', () => { const hub = new Hub(); - expect(hub.getStack()).toHaveLength(1); + expect(getStack(hub)).toHaveLength(1); }); test('pass in filled layer', () => { const hub = new Hub(clientFn); - expect(hub.getStack()).toHaveLength(1); + expect(getStack(hub)).toHaveLength(1); }); test("don't invoke client sync with wrong func", () => { @@ -45,18 +45,18 @@ describe('Hub', () => { const localScope = new Scope(); localScope.setExtra('a', 'b'); const hub = new Hub(undefined, localScope); - hub.pushScope(); - expect(hub.getStack()).toHaveLength(2); - expect(hub.getStack()[1].scope).not.toBe(localScope); - expect(((hub.getStack()[1].scope as Scope) as any)._extra).toEqual({ a: 'b' }); + pushScope(hub); + expect(getStack(hub)).toHaveLength(2); + expect(getStack(hub)[1].scope).not.toBe(localScope); + expect(((getStack(hub)[1].scope as Scope) as any)._extra).toEqual({ a: 'b' }); }); test('inherit client', () => { const testClient: any = { bla: 'a' }; const hub = new Hub(testClient); - hub.pushScope(); - expect(hub.getStack()).toHaveLength(2); - expect(hub.getStack()[1].client).toBe(testClient); + pushScope(hub); + expect(getStack(hub)).toHaveLength(2); + expect(getStack(hub)[1].client).toBe(testClient); }); describe('bindClient', () => { @@ -64,27 +64,27 @@ describe('Hub', () => { const testClient: any = { setupIntegrations: jest.fn() }; const nextClient: any = { setupIntegrations: jest.fn() }; const hub = new Hub(testClient); - hub.bindClient(nextClient); - expect(hub.getStack()).toHaveLength(1); - expect(hub.getStack()[0].client).toBe(nextClient); + bindClient(hub, nextClient); + expect(getStack(hub)).toHaveLength(1); + expect(getStack(hub)[0].client).toBe(nextClient); }); test('should bind client to the top-most layer', () => { const testClient: any = { bla: 'a' }; const nextClient: any = { foo: 'bar' }; const hub = new Hub(testClient); - hub.pushScope(); - hub.bindClient(nextClient); - expect(hub.getStack()).toHaveLength(2); - expect(hub.getStack()[0].client).toBe(testClient); - expect(hub.getStack()[1].client).toBe(nextClient); + pushScope(hub); + bindClient(hub, nextClient); + expect(getStack(hub)).toHaveLength(2); + expect(getStack(hub)[0].client).toBe(testClient); + expect(getStack(hub)[1].client).toBe(nextClient); }); test('should call setupIntegration method of passed client', () => { const testClient: any = { setupIntegrations: jest.fn() }; const nextClient: any = { setupIntegrations: jest.fn() }; const hub = new Hub(testClient); - hub.bindClient(nextClient); + bindClient(hub, nextClient); expect(testClient.setupIntegrations).toHaveBeenCalled(); expect(nextClient.setupIntegrations).toHaveBeenCalled(); }); @@ -104,7 +104,7 @@ describe('Hub', () => { return processedEvent; }); - hub.pushScope(); + pushScope(hub); const pushedScope = getStackTop(hub).scope; return pushedScope!.applyToEvent(event).then(final => { @@ -115,10 +115,10 @@ describe('Hub', () => { test('popScope', () => { const hub = new Hub(); - hub.pushScope(); - expect(hub.getStack()).toHaveLength(2); + pushScope(hub); + expect(getStack(hub)).toHaveLength(2); hub.popScope(); - expect(hub.getStack()).toHaveLength(1); + expect(getStack(hub)).toHaveLength(1); }); describe('withScope', () => { @@ -129,20 +129,20 @@ describe('Hub', () => { }); test('simple', () => { - hub.withScope(() => { - expect(hub.getStack()).toHaveLength(2); + withScope(hub, () => { + expect(getStack(hub)).toHaveLength(2); }); - expect(hub.getStack()).toHaveLength(1); + expect(getStack(hub)).toHaveLength(1); }); test('bindClient', () => { const testClient: any = { bla: 'a' }; - hub.withScope(() => { - hub.bindClient(testClient); - expect(hub.getStack()).toHaveLength(2); - expect(hub.getStack()[1].client).toBe(testClient); + withScope(hub, () => { + bindClient(hub, testClient); + expect(getStack(hub)).toHaveLength(2); + expect(getStack(hub)[1].client).toBe(testClient); }); - expect(hub.getStack()).toHaveLength(1); + expect(getStack(hub)).toHaveLength(1); }); test('should bubble up exceptions', () => { @@ -164,15 +164,15 @@ describe('Hub', () => { test('getStack', () => { const client: any = { a: 'b' }; const hub = new Hub(client); - expect(hub.getStack()[0].client).toBe(client); + expect(getStack(hub)[0].client).toBe(client); }); test('getStackTop', () => { const testClient: any = { bla: 'a' }; const hub = new Hub(); - hub.pushScope(); - hub.pushScope(); - hub.bindClient(testClient); + pushScope(hub); + pushScope(hub); + bindClient(hub, testClient); expect(getStackTop(hub).client).toEqual({ bla: 'a' }); }); diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 98f75c54b0ca..75dc5677979e 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -16,6 +16,7 @@ "module": "esm/index.js", "types": "dist/index.d.ts", "dependencies": { + "@sentry/hub": "6.17.0-beta.0", "@sentry/types": "6.17.0-beta.0", "@sentry/utils": "6.17.0-beta.0", "localforage": "^1.8.1", diff --git a/packages/integrations/src/angular.ts b/packages/integrations/src/angular.ts index f1e5eaccaf8c..0c3f1a765c8e 100644 --- a/packages/integrations/src/angular.ts +++ b/packages/integrations/src/angular.ts @@ -1,3 +1,4 @@ +import { withScope } from '@sentry/hub'; import { Event, EventProcessor, Hub, Integration } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; @@ -91,7 +92,7 @@ export class Angular implements Integration { const hub = this._getCurrentHub && this._getCurrentHub(); if (hub && hub.getIntegration(Angular)) { - hub.withScope(scope => { + withScope(hub, scope => { if (cause) { scope.setExtra('cause', cause); } diff --git a/packages/integrations/src/captureconsole.ts b/packages/integrations/src/captureconsole.ts index bd3a7c055083..9ec9714da033 100644 --- a/packages/integrations/src/captureconsole.ts +++ b/packages/integrations/src/captureconsole.ts @@ -1,5 +1,6 @@ import { EventProcessor, Hub, Integration } from '@sentry/types'; import { fill, getGlobalObject, safeJoin, severityFromString } from '@sentry/utils'; +import { withScope } from '@sentry/hub'; const global = getGlobalObject(); @@ -47,7 +48,7 @@ export class CaptureConsole implements Integration { const hub = getCurrentHub(); if (hub.getIntegration(CaptureConsole)) { - hub.withScope(scope => { + withScope(hub, scope => { scope.setLevel(severityFromString(level)); scope.setExtra('arguments', args); scope.addEventProcessor(event => { diff --git a/packages/integrations/src/ember.ts b/packages/integrations/src/ember.ts index 811e8b4852f8..a8c89ddd3660 100644 --- a/packages/integrations/src/ember.ts +++ b/packages/integrations/src/ember.ts @@ -1,5 +1,6 @@ import { EventProcessor, Hub, Integration } from '@sentry/types'; import { getGlobalObject, isInstanceOf, logger } from '@sentry/utils'; +import { withScope } from '@sentry/hub'; /** JSDoc */ export class Ember implements Integration { @@ -55,7 +56,7 @@ export class Ember implements Integration { // eslint-disable-next-line @typescript-eslint/no-explicit-any this._Ember.RSVP.on('error', (reason: unknown): void => { if (getCurrentHub().getIntegration(Ember)) { - getCurrentHub().withScope(scope => { + withScope(getCurrentHub(), scope => { if (isInstanceOf(reason, Error)) { scope.setExtra('context', 'Unhandled Promise error detected'); getCurrentHub().captureException(reason, { originalException: reason as Error }); diff --git a/packages/integrations/src/reportingobserver.ts b/packages/integrations/src/reportingobserver.ts index 4ae8f0f2d5c1..ce04cf91b0c2 100644 --- a/packages/integrations/src/reportingobserver.ts +++ b/packages/integrations/src/reportingobserver.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { EventProcessor, Hub, Integration } from '@sentry/types'; import { getGlobalObject, supportsReportingObserver } from '@sentry/utils'; +import { withScope } from '@sentry/hub'; /** JSDoc */ interface Report { @@ -108,7 +109,7 @@ export class ReportingObserver implements Integration { return; } for (const report of reports) { - hub.withScope(scope => { + withScope(hub, scope => { scope.setExtra('url', report.url); const label = `ReportingObserver [${report.type}]`; diff --git a/packages/integrations/src/vue.ts b/packages/integrations/src/vue.ts index 0f8d9c20b683..321cee976889 100644 --- a/packages/integrations/src/vue.ts +++ b/packages/integrations/src/vue.ts @@ -1,5 +1,6 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { withScope } from '@sentry/hub'; import { EventProcessor, Hub, Integration, IntegrationClass, Scope, Span, Transaction } from '@sentry/types'; import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/utils'; @@ -415,7 +416,7 @@ export class Vue implements Integration { if (getCurrentHub().getIntegration(Vue)) { // Capture exception in the next event loop, to make sure that all breadcrumbs are recorded in time. setTimeout(() => { - getCurrentHub().withScope(scope => { + withScope(getCurrentHub(), scope => { scope.setContext('vue', metadata); getCurrentHub().captureException(error); }); diff --git a/packages/nextjs/src/index.server.ts b/packages/nextjs/src/index.server.ts index 4b52348c17b4..8eec0e1a6dbe 100644 --- a/packages/nextjs/src/index.server.ts +++ b/packages/nextjs/src/index.server.ts @@ -1,4 +1,4 @@ -import { Carrier, getHubFromCarrier, getMainCarrier, bindClient } from '@sentry/hub'; +import { bindClient, Carrier, getClient,getHubFromCarrier, getMainCarrier } from '@sentry/hub'; import { RewriteFrames } from '@sentry/integrations'; import { configureScope, getCurrentHub, init as nodeInit, Integrations } from '@sentry/node'; import { Event } from '@sentry/types'; @@ -75,7 +75,7 @@ export function init(options: NextjsOptions): void { const domainHub = getHubFromCarrier(activeDomain); // apply the changes made by `nodeInit` to the domain's hub also - bindClient(domainHub, globalHub.getClient()); + bindClient(domainHub, getClient(globalHub)); domainHub.getScope()?.update(globalHub.getScope()); // `scope.update()` doesn’t copy over event processors, so we have to add it manually domainHub.getScope()?.addEventProcessor(filterTransactions); @@ -89,7 +89,7 @@ export function init(options: NextjsOptions): void { function sdkAlreadyInitialized(): boolean { const hub = getCurrentHub(); - return !!hub.getClient(); + return !!getClient(hub); } function addServerIntegrations(options: NextjsOptions): void { diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 87d3a7e1e28b..64aed7f68c34 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { captureException, getCurrentHub, startTransaction, withScope } from '@sentry/core'; -import { getSession } from '@sentry/hub'; +import { getClient, getSession } from '@sentry/hub'; import { extractTraceparentData, Span } from '@sentry/tracing'; import { Event, ExtractedNodeRequestData, Transaction } from '@sentry/types'; import { isPlainObject, isString, logger, normalize, stripUrlQueryAndFragment } from '@sentry/utils'; @@ -380,7 +380,7 @@ export function requestHandler( options?: RequestHandlerOptions, ): (req: http.IncomingMessage, res: http.ServerResponse, next: (error?: any) => void) => void { const currentHub = getCurrentHub(); - const client = currentHub.getClient(); + const client = getClient(currentHub); // Initialise an instance of SessionFlusher on the client when `autoSessionTracking` is enabled and the // `requestHandler` middleware is used indicating that we are running in SessionAggregates mode if (client && isAutoSessionTrackingEnabled(client)) { @@ -420,7 +420,7 @@ export function requestHandler( currentHub.configureScope(scope => { scope.addEventProcessor((event: Event) => parseRequest(event, req, options)); - const client = currentHub.getClient(); + const client = getClient(currentHub); if (isAutoSessionTrackingEnabled(client)) { const scope = currentHub.getScope(); if (scope) { @@ -431,7 +431,7 @@ export function requestHandler( }); res.once('finish', () => { - const client = currentHub.getClient(); + const client = getClient(currentHub); if (isAutoSessionTrackingEnabled(client)) { setImmediate(() => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access @@ -505,7 +505,7 @@ export function errorHandler(options?: { _scope.setSpan(transaction); } - const client = getCurrentHub().getClient(); + const client = getClient(getCurrentHub()); if (client && isAutoSessionTrackingEnabled(client)) { // Check if the `SessionFlusher` is instantiated on the client to go into this branch that marks the // `requestSession.status` as `Crashed`, and this check is necessary because the `SessionFlusher` is only diff --git a/packages/node/src/integrations/onuncaughtexception.ts b/packages/node/src/integrations/onuncaughtexception.ts index b9f1824a2f77..ad1b4e8f208e 100644 --- a/packages/node/src/integrations/onuncaughtexception.ts +++ b/packages/node/src/integrations/onuncaughtexception.ts @@ -1,4 +1,5 @@ import { getCurrentHub, Scope } from '@sentry/core'; +import { getClient, withScope } from '@sentry/hub'; import { Integration } from '@sentry/types'; import { logger } from '@sentry/utils'; @@ -56,7 +57,7 @@ export class OnUncaughtException implements Integration { return (error: Error): void => { let onFatalError: OnFatalErrorHandler = logAndExitProcess; - const client = getCurrentHub().getClient(); + const client = getClient(getCurrentHub()); if (this._options.onFatalError) { // eslint-disable-next-line @typescript-eslint/unbound-method @@ -76,7 +77,7 @@ export class OnUncaughtException implements Integration { caughtFirstError = true; if (hub.getIntegration(OnUncaughtException)) { - hub.withScope((scope: Scope) => { + withScope(hub, (scope: Scope) => { scope.setLevel('fatal'); hub.captureException(error, { originalException: error, diff --git a/packages/node/src/integrations/onunhandledrejection.ts b/packages/node/src/integrations/onunhandledrejection.ts index 19f733b1f908..17cec1886ea5 100644 --- a/packages/node/src/integrations/onunhandledrejection.ts +++ b/packages/node/src/integrations/onunhandledrejection.ts @@ -1,4 +1,5 @@ import { getCurrentHub, Scope } from '@sentry/core'; +import { withScope } from '@sentry/hub'; import { Integration } from '@sentry/types'; import { consoleSandbox } from '@sentry/utils'; @@ -55,7 +56,7 @@ export class OnUnhandledRejection implements Integration { /* eslint-disable @typescript-eslint/no-unsafe-member-access */ const context = (promise.domain && promise.domain.sentryContext) || {}; - hub.withScope((scope: Scope) => { + withScope(hub, (scope: Scope) => { scope.setExtra('unhandledPromiseRejection', true); // Preserve backwards compatibility with raven-node for now diff --git a/packages/node/src/integrations/utils/errorhandling.ts b/packages/node/src/integrations/utils/errorhandling.ts index 4ef901adb061..9780bf7cbc2c 100644 --- a/packages/node/src/integrations/utils/errorhandling.ts +++ b/packages/node/src/integrations/utils/errorhandling.ts @@ -1,4 +1,5 @@ import { getCurrentHub } from '@sentry/core'; +import { getClient } from '@sentry/hub'; import { forget, logger } from '@sentry/utils'; import { NodeClient } from '../../client'; @@ -12,7 +13,7 @@ export function logAndExitProcess(error: Error): void { // eslint-disable-next-line no-console console.error(error && error.stack ? error.stack : error); - const client = getCurrentHub().getClient(); + const client = getClient(getCurrentHub()); if (client === undefined) { logger.warn('No NodeClient was defined, we are exiting the process now.'); diff --git a/packages/node/src/integrations/utils/http.ts b/packages/node/src/integrations/utils/http.ts index bb0e9a76cf00..0416551b4603 100644 --- a/packages/node/src/integrations/utils/http.ts +++ b/packages/node/src/integrations/utils/http.ts @@ -1,4 +1,5 @@ import { getCurrentHub } from '@sentry/core'; +import { getClient } from '@sentry/hub'; import { parseSemver } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; @@ -11,9 +12,7 @@ const NODE_VERSION = parseSemver(process.versions.node); * @param url url to verify */ export function isSentryRequest(url: string): boolean { - const dsn = getCurrentHub() - .getClient() - ?.getDsn(); + const dsn = getClient(getCurrentHub())?.getDsn(); return dsn ? url.includes(dsn.host) : false; } diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index e4f79c7a5d86..d269d8779522 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,5 +1,5 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; -import { getMainCarrier, setHubOnCarrier, getSession } from '@sentry/hub'; +import { getMainCarrier, setHubOnCarrier, getSession, getClient } from '@sentry/hub'; import { SessionStatus } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; import * as domain from 'domain'; @@ -148,7 +148,7 @@ export function lastEventId(): string | undefined { * doesn't (or if there's no client defined). */ export async function flush(timeout?: number): Promise { - const client = getCurrentHub().getClient(); + const client = getClient(getCurrentHub()); if (client) { return client.flush(timeout); } @@ -165,7 +165,7 @@ export async function flush(timeout?: number): Promise { * doesn't (or if there's no client defined). */ export async function close(timeout?: number): Promise { - const client = getCurrentHub().getClient(); + const client = getClient(getCurrentHub()); if (client) { return client.close(timeout); } diff --git a/packages/tracing/src/hubextensions.ts b/packages/tracing/src/hubextensions.ts index 7bc654094ef7..3c0fc526b656 100644 --- a/packages/tracing/src/hubextensions.ts +++ b/packages/tracing/src/hubextensions.ts @@ -1,4 +1,4 @@ -import { getMainCarrier, Hub } from '@sentry/hub'; +import { getClient, getMainCarrier, Hub } from '@sentry/hub'; import { CustomSamplingContext, Integration, @@ -165,7 +165,7 @@ function _startTransaction( transactionContext: TransactionContext, customSamplingContext?: CustomSamplingContext, ): Transaction { - const client = this.getClient(); + const client = getClient(this); const options = (client && client.getOptions()) || {}; let transaction = new Transaction(transactionContext, this); @@ -190,7 +190,7 @@ export function startIdleTransaction( onScope?: boolean, customSamplingContext?: CustomSamplingContext, ): IdleTransaction { - const client = hub.getClient(); + const client = getClient(hub); const options = (client && client.getOptions()) || {}; let transaction = new IdleTransaction(transactionContext, hub, idleTimeout, onScope); diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 5143881ab159..198b294f7cee 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -1,4 +1,4 @@ -import { getCurrentHub, Hub } from '@sentry/hub'; +import { getClient, getCurrentHub, Hub } from '@sentry/hub'; import { Event, Measurements, @@ -103,7 +103,7 @@ export class Transaction extends SpanClass implements TransactionInterface { // At this point if `sampled !== true` we want to discard the transaction. logger.log('[Tracing] Discarding transaction because its trace was not chosen to be sampled.'); - const client = this._hub.getClient(); + const client = getClient(this._hub); const transport = client && client.getTransport && client.getTransport(); if (transport && transport.recordLostEvent) { transport.recordLostEvent('sample_rate', 'transaction'); diff --git a/packages/tracing/src/utils.ts b/packages/tracing/src/utils.ts index 0e2ea9ed694b..93dc5b37202f 100644 --- a/packages/tracing/src/utils.ts +++ b/packages/tracing/src/utils.ts @@ -1,4 +1,4 @@ -import { getCurrentHub, Hub } from '@sentry/hub'; +import { getClient, getCurrentHub, Hub } from '@sentry/hub'; import { Options, TraceparentData, Transaction } from '@sentry/types'; export const TRACEPARENT_REGEXP = new RegExp( @@ -15,7 +15,7 @@ export const TRACEPARENT_REGEXP = new RegExp( * Tracing is enabled when at least one of `tracesSampleRate` and `tracesSampler` is defined in the SDK config. */ export function hasTracingEnabled(maybeOptions?: Options | undefined): boolean { - const client = getCurrentHub().getClient(); + const client = getClient(getCurrentHub()); const options = maybeOptions || (client && client.getOptions()); return !!options && ('tracesSampleRate' in options || 'tracesSampler' in options); } diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index f1381efdd2fc..299dfbc89eca 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -16,21 +16,6 @@ import { User } from './user'; * working in case we have a version conflict. */ export interface Hub { - /** - * Creates a new scope with and executes the given operation within. - * The scope is automatically removed once the operation - * finishes or throws. - * - * This is essentially a convenience function for: - * - * pushScope(); - * callback(); - * popScope(); - * - * @param callback that will be enclosed into push/popScope. - */ - withScope(callback: (scope: Scope) => void): void; - /** Returns the client of the top stack. */ getClient(): Client | undefined; diff --git a/packages/vue/package.json b/packages/vue/package.json index b9bf16d240a7..56b094b60724 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -16,6 +16,7 @@ "access": "public" }, "dependencies": { + "@sentry/hub": "6.17.0-beta.0", "@sentry/browser": "6.17.0-beta.0", "@sentry/core": "6.17.0-beta.0", "@sentry/minimal": "6.17.0-beta.0", diff --git a/packages/vue/src/errorhandler.ts b/packages/vue/src/errorhandler.ts index 2343e351cd72..040a6ae2a290 100644 --- a/packages/vue/src/errorhandler.ts +++ b/packages/vue/src/errorhandler.ts @@ -1,4 +1,5 @@ import { getCurrentHub } from '@sentry/browser'; +import { withScope } from '@sentry/hub'; import { formatComponentName, generateComponentTrace } from './components'; import { Options, ViewModel, Vue } from './types'; @@ -25,7 +26,7 @@ export const attachErrorHandler = (app: Vue, options: Options): void => { // Capture exception in the next event loop, to make sure that all breadcrumbs are recorded in time. setTimeout(() => { - getCurrentHub().withScope(scope => { + withScope(getCurrentHub(), scope => { scope.setContext('vue', metadata); getCurrentHub().captureException(error); }); From cef8a95a4c2a91ef9225f2159517aa0e8ceb969b Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 02:04:07 -0500 Subject: [PATCH 08/37] chore: fix compiler issues --- packages/hub/src/hub.ts | 1 - packages/integrations/src/angular.ts | 4 ++-- packages/integrations/src/captureconsole.ts | 4 ++-- packages/integrations/src/ember.ts | 4 ++-- packages/integrations/src/reportingobserver.ts | 4 ++-- packages/integrations/src/vue.ts | 6 +++--- packages/types/src/hub.ts | 4 ---- 7 files changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index e1061f03eb28..02ce44c57443 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { Breadcrumb, BreadcrumbHint, diff --git a/packages/integrations/src/angular.ts b/packages/integrations/src/angular.ts index 0c3f1a765c8e..f087ccda93bd 100644 --- a/packages/integrations/src/angular.ts +++ b/packages/integrations/src/angular.ts @@ -1,5 +1,5 @@ -import { withScope } from '@sentry/hub'; -import { Event, EventProcessor, Hub, Integration } from '@sentry/types'; +import { Hub, withScope } from '@sentry/hub'; +import { Event, EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; // See https://github.com/angular/angular.js/blob/v1.4.7/src/minErr.js diff --git a/packages/integrations/src/captureconsole.ts b/packages/integrations/src/captureconsole.ts index 9ec9714da033..e9b0f1206e35 100644 --- a/packages/integrations/src/captureconsole.ts +++ b/packages/integrations/src/captureconsole.ts @@ -1,6 +1,6 @@ -import { EventProcessor, Hub, Integration } from '@sentry/types'; +import { Hub, withScope } from '@sentry/hub'; +import { EventProcessor, Integration } from '@sentry/types'; import { fill, getGlobalObject, safeJoin, severityFromString } from '@sentry/utils'; -import { withScope } from '@sentry/hub'; const global = getGlobalObject(); diff --git a/packages/integrations/src/ember.ts b/packages/integrations/src/ember.ts index a8c89ddd3660..0a318094319a 100644 --- a/packages/integrations/src/ember.ts +++ b/packages/integrations/src/ember.ts @@ -1,6 +1,6 @@ -import { EventProcessor, Hub, Integration } from '@sentry/types'; +import { EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, isInstanceOf, logger } from '@sentry/utils'; -import { withScope } from '@sentry/hub'; +import { Hub, withScope } from '@sentry/hub'; /** JSDoc */ export class Ember implements Integration { diff --git a/packages/integrations/src/reportingobserver.ts b/packages/integrations/src/reportingobserver.ts index ce04cf91b0c2..a393ce0e1a04 100644 --- a/packages/integrations/src/reportingobserver.ts +++ b/packages/integrations/src/reportingobserver.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { EventProcessor, Hub, Integration } from '@sentry/types'; +import { EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, supportsReportingObserver } from '@sentry/utils'; -import { withScope } from '@sentry/hub'; +import { withScope, Hub } from '@sentry/hub'; /** JSDoc */ interface Report { diff --git a/packages/integrations/src/vue.ts b/packages/integrations/src/vue.ts index 321cee976889..d0667e2cdd1a 100644 --- a/packages/integrations/src/vue.ts +++ b/packages/integrations/src/vue.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { withScope } from '@sentry/hub'; -import { EventProcessor, Hub, Integration, IntegrationClass, Scope, Span, Transaction } from '@sentry/types'; +import { Hub, withScope } from '@sentry/hub'; +import { EventProcessor, Hub as IHub, Integration, IntegrationClass, Scope, Span, Transaction } from '@sentry/types'; import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/utils'; /** @@ -438,7 +438,7 @@ export class Vue implements Integration { } } -interface HubType extends Hub { +interface HubType extends IHub { getScope?(): Scope | undefined; } diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index 299dfbc89eca..787374014a10 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -1,5 +1,4 @@ import { Breadcrumb, BreadcrumbHint } from './breadcrumb'; -import { Client } from './client'; import { Event, EventHint } from './event'; import { Extra, Extras } from './extra'; import { Integration, IntegrationClass } from './integration'; @@ -16,9 +15,6 @@ import { User } from './user'; * working in case we have a version conflict. */ export interface Hub { - /** Returns the client of the top stack. */ - getClient(): Client | undefined; - /** * Captures an exception event and sends it to Sentry. * From e2da41a774ed0a35c3e2bf7ba8ef7f1b6333007d Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 03:22:10 -0500 Subject: [PATCH 09/37] chore: move all methods from Hub class --- packages/browser/src/sdk.ts | 6 +- packages/core/src/sdk.ts | 4 +- packages/hub/src/hub.ts | 691 ++++++++++-------- packages/hub/src/index.ts | 6 + packages/hub/src/sessionflusher.ts | 4 +- packages/integrations/src/angular.ts | 8 +- packages/integrations/src/captureconsole.ts | 10 +- packages/integrations/src/ember.ts | 12 +- .../integrations/src/reportingobserver.ts | 6 +- packages/integrations/src/vue.ts | 28 +- packages/node/src/sdk.ts | 4 +- packages/types/src/hub.ts | 24 - packages/types/src/integration.ts | 2 +- 13 files changed, 430 insertions(+), 375 deletions(-) diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index c567d7641584..28d4391cba2f 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -1,4 +1,5 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; +import { getClient, lastEventId as hubLastEventId } from '@sentry/hub'; import { Hub } from '@sentry/types'; import { addInstrumentationHandler, getGlobalObject, isDebugBuild, logger, resolvedSyncPromise } from '@sentry/utils'; @@ -6,7 +7,6 @@ import { BrowserOptions } from './backend'; import { BrowserClient } from './client'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; -import { getClient } from '@sentry/hub'; export const defaultIntegrations = [ new CoreIntegrations.InboundFilters(), @@ -117,7 +117,7 @@ export function showReportDialog(options: ReportDialogOptions = {}): void { } if (!options.eventId) { - options.eventId = hub.lastEventId(); + options.eventId = hubLastEventId(hub); } const client = getClient(hub); if (client) { @@ -131,7 +131,7 @@ export function showReportDialog(options: ReportDialogOptions = {}): void { * @returns The last event id of a captured event. */ export function lastEventId(): string | undefined { - return getCurrentHub().lastEventId(); + return hubLastEventId(getCurrentHub()); } /** diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index a7291e21167c..8eaa339a9175 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,4 +1,4 @@ -import { getCurrentHub, bindClient } from '@sentry/hub'; +import { bindClient, getCurrentHub, getScope } from '@sentry/hub'; import { Client, Options } from '@sentry/types'; import { logger } from '@sentry/utils'; @@ -17,7 +17,7 @@ export function initAndBind(clientClass: Cl logger.enable(); } const hub = getCurrentHub(); - const scope = hub.getScope(); + const scope = getScope(hub); if (scope) { scope.update(options.initialScope); } diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 02ce44c57443..45f22a9b6629 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -7,7 +7,6 @@ import { EventHint, Extra, Extras, - Hub as HubInterface, Integration, IntegrationClass, Primitive, @@ -81,6 +80,32 @@ export interface DomainAsCarrier extends Carrier { members: { [key: string]: any }[]; } +/** + * Internal class used to make sure we always have the latest internal functions + * working in case we have a version conflict. + */ +export class Hub { + /** Is a {@link Layer}[] containing the client and scope */ + public readonly stack: Layer[] = [{}]; + + /** Contains the last event id of a captured event. */ + public _lastEventId?: string; + + /** + * Creates a new instance of the hub, will push one {@link Layer} into the + * internal stack on creation. + * + * @param client bound to the hub. + * @param scope bound to the hub. + * @param version number, higher number means higher priority. + */ + public constructor(client?: Client, scope: Scope = new Scope(), public readonly _version: number = API_VERSION) { + getStackTop(this).scope = scope; + if (client) { + bindClient(this, client); + } + } +} /** * Returns the topmost scope layer in the order domain > local > process. @@ -88,12 +113,12 @@ export interface DomainAsCarrier extends Carrier { * @hidden * */ export function getStackTop(hub: Hub): Layer { - return hub._stack[hub._stack.length - 1]; + return hub.stack[hub.stack.length - 1]; } /** Returns the scope stack for domains or the process. */ export function getStack(hub: Hub): Layer[] { - return hub._stack; + return hub.stack; } /** @@ -133,7 +158,7 @@ export function popScope(hub: Hub): boolean { */ export function pushScope(hub: Hub): Scope { // We want to clone the content of prev scope - const scope = cloneScope(hub.getScope()); + const scope = cloneScope(getScope(hub)); getStack(hub).push({ client: getClient(hub), scope, @@ -183,360 +208,406 @@ export function getClient(hub: Hub): C | undefined { } /** - * @inheritDoc + * Updates user context information for future events. + * + * @param hub The Hub instance. + * @param user User context object to be set in the current context. Pass `null` to unset the user. */ -export class Hub implements HubInterface { - /** Is a {@link Layer}[] containing the client and scope */ - public readonly _stack: Layer[] = [{}]; +export function setUser(hub: Hub, user: User | null): void { + const scope = getScope(hub); + if (scope) scope.setUser(user); +} - /** Contains the last event id of a captured event. */ - private _lastEventId?: string; +/** Returns the scope of the top stack. */ +export function getScope(hub: Hub): Scope | undefined { + return getStackTop(hub).scope; +} - /** - * Creates a new instance of the hub, will push one {@link Layer} into the - * internal stack on creation. - * - * @param client bound to the hub. - * @param scope bound to the hub. - * @param version number, higher number means higher priority. - */ - public constructor(client?: Client, scope: Scope = new Scope(), public readonly _version: number = API_VERSION) { - getStackTop(this).scope = scope; - if (client) { - bindClient(this, client); - } - } +/** + * This is the getter for lastEventId. + * + * @returns The last event id of a captured event. + */ +export function lastEventId(hub: Hub): string | undefined { + return hub._lastEventId; +} - /** Returns the scope of the top stack. */ - public getScope(): Scope | undefined { - return getStackTop(this).scope; +/** + * Sends the current session on the scope to Sentry + * @param hub The Hub instance + * @param shouldEndSession If set the session will be marked as exited and removed from the scope + */ +export function captureSession(hub: Hub, shouldEndSession: boolean = false): void { + // both send the update and pull the session from the scope + if (shouldEndSession) { + return endSession(hub); } - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types - public captureException(exception: any, hint?: EventHint): string { - const eventId = (this._lastEventId = uuid4()); - let finalHint = hint; - - // If there's no explicit hint provided, mimic the same thing that would happen - // in the minimal itself to create a consistent behavior. - // We don't do this in the client, as it's the lowest level API, and doing this, - // would prevent user from having full control over direct calls. - if (!hint) { - let syntheticException: Error; - try { - throw new Error('Sentry syntheticException'); - } catch (exception) { - syntheticException = exception as Error; - } - finalHint = { - originalException: exception, - syntheticException, - }; + // only send the update + _sendSessionUpdate(hub); +} + +/** + * Sends the current Session on the scope + */ +function _sendSessionUpdate(hub: Hub): void { + const { scope, client } = getStackTop(hub); + if (!scope) return; + + const session = getSession(scope) + if (session) { + if (client && client.captureSession) { + client.captureSession(session); } + } +} - this._invokeClient('captureException', exception, { - ...finalHint, - event_id: eventId, - }); - return eventId; +/** + * Ends the session that lives on the current scope and sends it to Sentry + */ +function endSession(hub: Hub): void { + const layer = getStackTop(hub); + const scope = layer && layer.scope; + const session = getSession(scope); + if (session) { + session.close(); } - /** - * @inheritDoc - */ - public captureMessage(message: string, level?: SeverityLevel, hint?: EventHint): string { - const eventId = (this._lastEventId = uuid4()); - let finalHint = hint; - - // If there's no explicit hint provided, mimic the same thing that would happen - // in the minimal itself to create a consistent behavior. - // We don't do this in the client, as it's the lowest level API, and doing this, - // would prevent user from having full control over direct calls. - if (!hint) { - let syntheticException: Error; - try { - throw new Error(message); - } catch (exception) { - syntheticException = exception as Error; - } - finalHint = { - originalException: message, - syntheticException, - }; - } + _sendSessionUpdate(hub); - this._invokeClient('captureMessage', message, level, { - ...finalHint, - event_id: eventId, - }); - return eventId; + // the session is over; take it off of the scope + if (scope) { + scope.setSession(); } +} - /** - * @inheritDoc - */ - public captureEvent(event: Event, hint?: EventHint): string { - const eventId = uuid4(); - if (event.type !== 'transaction') { - this._lastEventId = eventId; +/** + * Starts a new `Session`, sets on the current scope and returns it. + * + * To finish a `session`, it has to be passed directly to `client.captureSession`, which is done automatically + * when using `endSession(hub)` for the session currently stored on the scope. + * + * When there's already an existing session on the scope, it'll be automatically ended. + * + * @param hub The Hub instance. + * @param context Optional properties of the new `Session`. + * + * @returns The session which was just started + */ +export function startSession(hub: Hub, context?: SessionContext): Session { + const { scope, client } = getStackTop(hub); + const { release, environment } = (client && client.getOptions()) || {}; + + // Will fetch userAgent if called from browser sdk + const global = getGlobalObject<{ navigator?: { userAgent?: string } }>(); + const { userAgent } = global.navigator || {}; + + const session = new Session({ + release, + environment, + ...(scope && { user: scope.getUser() }), + ...(userAgent && { userAgent }), + ...context, + }); + + if (scope) { + // End existing session if there's one + const currentSession = getSession(scope); + if (currentSession && currentSession.status === 'ok') { + currentSession.update({ status: 'exited' }); } + endSession(hub); - this._invokeClient('captureEvent', event, { - ...hint, - event_id: eventId, - }); - return eventId; + // Afterwards we set the new session on the scope + scope.setSession(session); } - /** - * @inheritDoc - */ - public lastEventId(): string | undefined { - return this._lastEventId; - } - - /** - * @inheritDoc - */ - public addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void { - const { scope, client } = getStackTop(this); - - if (!scope || !client) return; + return session; +} - // eslint-disable-next-line @typescript-eslint/unbound-method - const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = - (client.getOptions && client.getOptions()) || {}; +/** + * Captures an exception event and sends it to Sentry. + * + * @param hub The Hub instance. + * @param exception An exception-like object. + * @param hint May contain additional information about the original exception. + * @returns The generated eventId. + */ +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function captureException(hub: Hub, exception: any, hint?: EventHint): string { + const eventId = (hub._lastEventId = uuid4()); + let finalHint = hint; + + // If there's no explicit hint provided, mimic the same thing that would happen + // in the minimal itself to create a consistent behavior. + // We don't do this in the client, as it's the lowest level API, and doing this, + // would prevent user from having full control over direct calls. + if (!hint) { + let syntheticException: Error; + try { + throw new Error('Sentry syntheticException'); + } catch (exception) { + syntheticException = exception as Error; + } + finalHint = { + originalException: exception, + syntheticException, + }; + } - if (maxBreadcrumbs <= 0) return; + _invokeClient(hub, 'captureException', exception, { + ...finalHint, + event_id: eventId, + }); + return eventId; +} - const timestamp = dateTimestampInSeconds(); - const mergedBreadcrumb = { timestamp, ...breadcrumb }; - const finalBreadcrumb = beforeBreadcrumb - ? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) as Breadcrumb | null) - : mergedBreadcrumb; +/** + * Captures a message event and sends it to Sentry. + * + * @param hub The Hub instance. + * @param message The message to send to Sentry. + * @param level Define the level of the message. + * @param hint May contain additional information about the original exception. + * @returns The generated eventId. + */ +export function captureMessage(hub: Hub, message: string, level?: SeverityLevel, hint?: EventHint): string { + const eventId = (hub._lastEventId = uuid4()); + let finalHint = hint; + + // If there's no explicit hint provided, mimic the same thing that would happen + // in the minimal itself to create a consistent behavior. + // We don't do this in the client, as it's the lowest level API, and doing this, + // would prevent user from having full control over direct calls. + if (!hint) { + let syntheticException: Error; + try { + throw new Error(message); + } catch (exception) { + syntheticException = exception as Error; + } + finalHint = { + originalException: message, + syntheticException, + }; + } - if (finalBreadcrumb === null) return; + _invokeClient(hub, 'captureMessage', message, level, { + ...finalHint, + event_id: eventId, + }); + return eventId; +} - scope.addBreadcrumb(finalBreadcrumb, maxBreadcrumbs); +/** + * Captures a manually created event and sends it to Sentry. + * + * @param hub The Hub instance. + * @param event The event to send to Sentry. + * @param hint May contain additional information about the original exception. + */ +export function captureEvent(hub: Hub, event: Event, hint?: EventHint): string { + const eventId = uuid4(); + if (event.type !== 'transaction') { + hub._lastEventId = eventId; } - /** - * @inheritDoc - */ - public setUser(user: User | null): void { - const scope = this.getScope(); - if (scope) scope.setUser(user); - } + _invokeClient(hub, 'captureEvent', event, { + ...hint, + event_id: eventId, + }); + return eventId; +} - /** - * @inheritDoc - */ - public setTags(tags: { [key: string]: Primitive }): void { - const scope = this.getScope(); - if (scope) scope.setTags(tags); - } +/** + * Records a new breadcrumb which will be attached to future events. + * + * Breadcrumbs will be added to subsequent events to provide more context on + * user's actions prior to an error or crash. + * + * @param hub The Hub instance. + * @param breadcrumb The breadcrumb to record. + * @param hint May contain additional information about the original breadcrumb. + */ +export function addBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void { + const { scope, client } = getStackTop(hub); - /** - * @inheritDoc - */ - public setExtras(extras: Extras): void { - const scope = this.getScope(); - if (scope) scope.setExtras(extras); - } + if (!scope || !client) return; - /** - * @inheritDoc - */ - public setTag(key: string, value: Primitive): void { - const scope = this.getScope(); - if (scope) scope.setTag(key, value); - } +// eslint-disable-next-line @typescript-eslint/unbound-method + const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = + (client.getOptions && client.getOptions()) || {}; - /** - * @inheritDoc - */ - public setExtra(key: string, extra: Extra): void { - const scope = this.getScope(); - if (scope) scope.setExtra(key, extra); - } + if (maxBreadcrumbs <= 0) return; - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public setContext(name: string, context: { [key: string]: any } | null): void { - const scope = this.getScope(); - if (scope) scope.setContext(name, context); - } + const timestamp = dateTimestampInSeconds(); + const mergedBreadcrumb = { timestamp, ...breadcrumb }; + const finalBreadcrumb = beforeBreadcrumb + ? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) as Breadcrumb | null) + : mergedBreadcrumb; - /** - * @inheritDoc - */ - public configureScope(callback: (scope: Scope) => void): void { - const { scope, client } = getStackTop(this); - if (scope && client) { - callback(scope); - } - } + if (finalBreadcrumb === null) return; - /** - * @inheritDoc - */ - public run(callback: (hub: Hub) => void): void { - const oldHub = makeMain(this); - try { - callback(this); - } finally { - makeMain(oldHub); - } - } + scope.addBreadcrumb(finalBreadcrumb, maxBreadcrumbs); +} - /** - * @inheritDoc - */ - public getIntegration(integration: IntegrationClass): T | null { - const client = getClient(this); - if (!client) return null; - try { - return client.getIntegration(integration); - } catch (_oO) { - logger.warn(`Cannot retrieve integration ${integration.id} from the current Hub`); - return null; - } - } +/** + * Set an object that will be merged sent as tags data with the event. + * + * @param hub The Hub instance. + * @param tags Tags context object to merge into current context. + */ +export function setTags(hub: Hub, tags: { [key: string]: Primitive }): void { + const scope = getScope(hub); + if (scope) scope.setTags(tags); +} - /** - * @inheritDoc - */ - public startSpan(context: SpanContext): Span { - return this._callExtensionMethod('startSpan', context); - } +/** + * Set an object that will be merged sent as extra data with the event. + * @param hub The Hub instance. + * @param extras Extras object to merge into current context. + */ +export function setExtras(hub: Hub, extras: Extras): void { + const scope = getScope(hub); + if (scope) scope.setExtras(extras); +} - /** - * @inheritDoc - */ - public startTransaction(context: TransactionContext, customSamplingContext?: CustomSamplingContext): Transaction { - return this._callExtensionMethod('startTransaction', context, customSamplingContext); - } +/** + * Set key:value that will be sent as tags data with the event. + * + * Can also be used to unset a tag, by passing `undefined`. + * + * @param hub The Hub instance. + * @param key String key of tag + * @param value Value of tag + */ +export function setTag(hub: Hub, key: string, value: Primitive): void { + const scope = getScope(hub); + if (scope) scope.setTag(key, value); +} - /** - * @inheritDoc - */ - public traceHeaders(): { [key: string]: string } { - return this._callExtensionMethod<{ [key: string]: string }>('traceHeaders'); - } +/** + * Set key:value that will be sent as extra data with the event. + * @param hub The Hub instance. + * @param key String of extra + * @param extra Any kind of data. This data will be normalized. + */ +export function setExtra(hub: Hub, key: string, extra: Extra): void { + const scope = getScope(hub); + if (scope) scope.setExtra(key, extra); +} - /** - * @inheritDoc - */ - public captureSession(endSession: boolean = false): void { - // both send the update and pull the session from the scope - if (endSession) { - return this.endSession(); - } +/** + * Sets context data with the given name. + * @param hub The Hub instance. + * @param name of the context + * @param context Any kind of data. This data will be normalized. + */ +export function setContext(hub: Hub, name: string, context: { [key: string]: any } | null): void { + const scope = getScope(hub); + if (scope) scope.setContext(name, context); +} - // only send the update - this._sendSessionUpdate(); +/** + * Callback to set context information onto the scope. + * + * @param hub The Hub instance. + * @param callback Callback function that receives Scope. + */ +export function configureScope(hub: Hub, callback: (scope: Scope) => void): void { + const { scope, client } = getStackTop(hub); + if (scope && client) { + callback(scope); } +} - /** - * @inheritDoc - */ - public endSession(): void { - const layer = getStackTop(this); - const scope = layer && layer.scope; - const session = getSession(scope); - if (session) { - session.close(); - } - this._sendSessionUpdate(); +/** + * For the duration of the callback, this hub will be set as the global current Hub. + * This function is useful if you want to run your own client and hook into an already initialized one + * e.g.: Reporting issues to your own sentry when running in your component while still using the users configuration. + */ +export function run(hub: Hub, callback: (hub: Hub) => void): void { + const oldHub = makeMain(hub); + try { + callback(hub); + } finally { + makeMain(oldHub); + } +} - // the session is over; take it off of the scope - if (scope) { - scope.setSession(); - } +/** Returns the integration if installed on the current client. */ +export function getIntegration(hub: Hub, integration: IntegrationClass): T | null { + const client = getClient(hub); + if (!client) return null; + try { + return client.getIntegration(integration); + } catch (_oO) { + logger.warn(`Cannot retrieve integration ${integration.id} from the current Hub`); + return null; } +} - /** - * @inheritDoc - */ - public startSession(context?: SessionContext): Session { - const { scope, client } = getStackTop(this); - const { release, environment } = (client && client.getOptions()) || {}; - - // Will fetch userAgent if called from browser sdk - const global = getGlobalObject<{ navigator?: { userAgent?: string } }>(); - const { userAgent } = global.navigator || {}; - - const session = new Session({ - release, - environment, - ...(scope && { user: scope.getUser() }), - ...(userAgent && { userAgent }), - ...context, - }); - - if (scope) { - // End existing session if there's one - const currentSession = getSession(scope); - if (currentSession && currentSession.status === 'ok') { - currentSession.update({ status: 'exited' }); - } - this.endSession(); - - // Afterwards we set the new session on the scope - scope.setSession(session); - } +/** + * @deprecated No longer does anything. Use use {@link Transaction.startChild} instead. + */ +export function startSpan(hub: Hub, context: SpanContext): Span { + return _callExtensionMethod(hub, 'startSpan', context); +} - return session; - } +/** + * Starts a new `Transaction` and returns it. This is the entry point to manual tracing instrumentation. + * + * A tree structure can be built by adding child spans to the transaction, and child spans to other spans. To start a + * new child span within the transaction or any span, call the respective `.startChild()` method. + * + * Every child span must be finished before the transaction is finished, otherwise the unfinished spans are discarded. + * + * The transaction must be finished with a call to its `.finish()` method, at which point the transaction with all its + * finished child spans will be sent to Sentry. + * + * @param hub The Hub i + * @param context Properties of the new `Transaction`. + * @param customSamplingContext Information given to the transaction sampling function (along with context-dependent + * default values). See {@link Options.tracesSampler}. + * + * @returns The transaction which was just started + */ +export function startTransaction(hub: Hub, context: TransactionContext, customSamplingContext?: CustomSamplingContext): Transaction { + return _callExtensionMethod(hub, 'startTransaction', context, customSamplingContext); +} - /** - * Sends the current Session on the scope - */ - private _sendSessionUpdate(): void { - const { scope, client } = getStackTop(this); - if (!scope) return; - - const session = getSession(scope) - if (session) { - if (client && client.captureSession) { - client.captureSession(session); - } - } - } +/** Returns all trace headers that are currently on the top scope. */ +export function traceHeaders(hub: Hub): { [key: string]: string } { + return _callExtensionMethod<{ [key: string]: string }>(hub, 'traceHeaders'); +} - /** - * Internal helper function to call a method on the top client if it exists. - * - * @param method The method to call on the client. - * @param args Arguments to pass to the client function. - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private _invokeClient(method: M, ...args: any[]): void { - const { scope, client } = getStackTop(this); - if (client && client[method]) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - (client as any)[method](...args, scope); - } +/** + * Internal helper function to call a method on the top client if it exists. + * + * @param method The method to call on the client. + * @param args Arguments to pass to the client function. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function _invokeClient(hub: Hub, method: M, ...args: any[]): void { + const { scope, client } = getStackTop(hub); + if (client && client[method]) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any + (client as any)[method](...args, scope); } +} - /** - * Calls global extension method and binding current instance to the function call - */ - // @ts-ignore Function lacks ending return statement and return type does not include 'undefined'. ts(2366) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private _callExtensionMethod(method: string, ...args: any[]): T { - const carrier = getMainCarrier(); - const sentry = carrier.__SENTRY__; - if (sentry && sentry.extensions && typeof sentry.extensions[method] === 'function') { - return sentry.extensions[method].apply(this, args); - } - logger.warn(`Extension method ${method} couldn't be found, doing nothing.`); +/** + * Calls global extension method and binding current instance to the function call + */ +// @ts-ignore Function lacks ending return statement and return type does not include 'undefined'. ts(2366) +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function _callExtensionMethod(hub: Hub, method: string, ...args: any[]): T { + const carrier = getMainCarrier(); + const sentry = carrier.__SENTRY__; + if (sentry && sentry.extensions && typeof sentry.extensions[method] === 'function') { + return sentry.extensions[method].apply(hub, args); } + logger.warn(`Extension method ${method} couldn't be found, doing nothing.`); } /** diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 5ea4dd3f36ea..ad713e8e8027 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -12,6 +12,12 @@ export { pushScope, withScope, getClient, + getScope, + setUser, + lastEventId, + captureException, + getIntegration, + captureMessage, Hub, makeMain, setHubOnCarrier, diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index 82aa0af8c4c2..674987c6bab5 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -7,7 +7,7 @@ import { } from '@sentry/types'; import { dropUndefinedKeys, logger } from '@sentry/utils'; -import { getCurrentHub } from './hub'; +import { getCurrentHub, getScope } from './hub'; type ReleaseHealthAttributes = { environment?: string; @@ -82,7 +82,7 @@ export class SessionFlusher implements SessionFlusherLike { if (!this._isEnabled) { return; } - const scope = getCurrentHub().getScope(); + const scope = getScope(getCurrentHub()); const requestSession = scope && scope.getRequestSession(); if (requestSession && requestSession.status) { diff --git a/packages/integrations/src/angular.ts b/packages/integrations/src/angular.ts index f087ccda93bd..1ccec934d29e 100644 --- a/packages/integrations/src/angular.ts +++ b/packages/integrations/src/angular.ts @@ -1,4 +1,4 @@ -import { Hub, withScope } from '@sentry/hub'; +import { captureException, getIntegration, Hub, withScope } from '@sentry/hub'; import { Event, EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; @@ -65,7 +65,7 @@ export class Angular implements Integration { /** * @inheritDoc */ - public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { + public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub | any): void { if (!this._module) { return; } @@ -91,7 +91,7 @@ export class Angular implements Integration { return (exception: Error, cause?: string): void => { const hub = this._getCurrentHub && this._getCurrentHub(); - if (hub && hub.getIntegration(Angular)) { + if (hub && getIntegration(hub, Angular)) { withScope(hub, scope => { if (cause) { scope.setExtra('cause', cause); @@ -119,7 +119,7 @@ export class Angular implements Integration { return event; }); - hub.captureException(exception); + captureException(hub, exception); }); } $delegate(exception, cause); diff --git a/packages/integrations/src/captureconsole.ts b/packages/integrations/src/captureconsole.ts index e9b0f1206e35..6633ea9e6a86 100644 --- a/packages/integrations/src/captureconsole.ts +++ b/packages/integrations/src/captureconsole.ts @@ -1,4 +1,4 @@ -import { Hub, withScope } from '@sentry/hub'; +import { captureException, captureMessage, getIntegration, Hub, withScope } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { fill, getGlobalObject, safeJoin, severityFromString } from '@sentry/utils'; @@ -47,7 +47,7 @@ export class CaptureConsole implements Integration { fill(global.console, level, (originalConsoleLevel: () => any) => (...args: any[]): void => { const hub = getCurrentHub(); - if (hub.getIntegration(CaptureConsole)) { + if (getIntegration(hub, CaptureConsole)) { withScope(hub, scope => { scope.setLevel(severityFromString(level)); scope.setExtra('arguments', args); @@ -61,12 +61,12 @@ export class CaptureConsole implements Integration { if (args[0] === false) { message = `Assertion failed: ${safeJoin(args.slice(1), ' ') || 'console.assert'}`; scope.setExtra('arguments', args.slice(1)); - hub.captureMessage(message); + captureMessage(hub, message); } } else if (level === 'error' && args[0] instanceof Error) { - hub.captureException(args[0]); + captureException(hub, args[0]); } else { - hub.captureMessage(message); + captureMessage(hub, message); } }); } diff --git a/packages/integrations/src/ember.ts b/packages/integrations/src/ember.ts index 0a318094319a..08e61946298f 100644 --- a/packages/integrations/src/ember.ts +++ b/packages/integrations/src/ember.ts @@ -1,6 +1,6 @@ +import { captureException, captureMessage, getIntegration, Hub, withScope } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, isInstanceOf, logger } from '@sentry/utils'; -import { Hub, withScope } from '@sentry/hub'; /** JSDoc */ export class Ember implements Integration { @@ -42,8 +42,8 @@ export class Ember implements Integration { const oldOnError = this._Ember.onerror; this._Ember.onerror = (error: Error): void => { - if (getCurrentHub().getIntegration(Ember)) { - getCurrentHub().captureException(error, { originalException: error }); + if (getIntegration(getCurrentHub(), Ember)) { + captureException(getCurrentHub(), error, { originalException: error }); } if (typeof oldOnError === 'function') { @@ -55,14 +55,14 @@ export class Ember implements Integration { // eslint-disable-next-line @typescript-eslint/no-explicit-any this._Ember.RSVP.on('error', (reason: unknown): void => { - if (getCurrentHub().getIntegration(Ember)) { + if (getIntegration(getCurrentHub(), Ember)) { withScope(getCurrentHub(), scope => { if (isInstanceOf(reason, Error)) { scope.setExtra('context', 'Unhandled Promise error detected'); - getCurrentHub().captureException(reason, { originalException: reason as Error }); + captureException(getCurrentHub(), reason, { originalException: reason as Error }); } else { scope.setExtra('reason', reason); - getCurrentHub().captureMessage('Unhandled Promise error detected'); + captureMessage(getCurrentHub(), 'Unhandled Promise error detected'); } }); } diff --git a/packages/integrations/src/reportingobserver.ts b/packages/integrations/src/reportingobserver.ts index a393ce0e1a04..8c627103358f 100644 --- a/packages/integrations/src/reportingobserver.ts +++ b/packages/integrations/src/reportingobserver.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { captureMessage, getIntegration, Hub, withScope } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, supportsReportingObserver } from '@sentry/utils'; -import { withScope, Hub } from '@sentry/hub'; /** JSDoc */ interface Report { @@ -105,7 +105,7 @@ export class ReportingObserver implements Integration { */ public handler(reports: Report[]): void { const hub = this._getCurrentHub && this._getCurrentHub(); - if (!hub || !hub.getIntegration(ReportingObserver)) { + if (!hub || !getIntegration(hub, ReportingObserver)) { return; } for (const report of reports) { @@ -138,7 +138,7 @@ export class ReportingObserver implements Integration { } } - hub.captureMessage(`${label}: ${details}`); + captureMessage(hub, `${label}: ${details}`); }); } } diff --git a/packages/integrations/src/vue.ts b/packages/integrations/src/vue.ts index d0667e2cdd1a..f08b499fe027 100644 --- a/packages/integrations/src/vue.ts +++ b/packages/integrations/src/vue.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Hub, withScope } from '@sentry/hub'; -import { EventProcessor, Hub as IHub, Integration, IntegrationClass, Scope, Span, Transaction } from '@sentry/types'; +import { captureException, getIntegration, getScope, Hub, withScope } from '@sentry/hub'; +import { EventProcessor, Integration, IntegrationClass, Scope, Span, Transaction } from '@sentry/types'; import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/utils'; /** @@ -264,7 +264,7 @@ export class Vue implements Integration { // We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods. /* eslint-disable @typescript-eslint/no-unsafe-member-access */ // eslint-disable-next-line deprecation/deprecation - const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER); + const tracingIntegration = getIntegration(getCurrentHub(), TRACING_GETTER); if (tracingIntegration) { this._tracingActivity = (tracingIntegration as any).constructor.pushActivity('Vue Application Render'); const transaction = (tracingIntegration as any).constructor.getTransaction(); @@ -358,7 +358,7 @@ export class Vue implements Integration { // We do this whole dance with `TRACING_GETTER` to prevent `@sentry/apm` from becoming a peerDependency. // We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods. // eslint-disable-next-line deprecation/deprecation - const tracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER); + const tracingIntegration = getIntegration(getCurrentHub(), TRACING_GETTER); if (tracingIntegration) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access (tracingIntegration as any).constructor.popActivity(this._tracingActivity); @@ -379,7 +379,7 @@ export class Vue implements Integration { this._options.Vue.mixin({ beforeCreate(this: ViewModel): void { // eslint-disable-next-line deprecation/deprecation - if (getCurrentHub().getIntegration(TRACING_GETTER) || getCurrentHub().getIntegration(BROWSER_TRACING_GETTER)) { + if (getIntegration(getCurrentHub(), TRACING_GETTER) || getIntegration(getCurrentHub(), BROWSER_TRACING_GETTER)) { // `this` points to currently rendered component applyTracingHooks(this, getCurrentHub); } else { @@ -413,12 +413,12 @@ export class Vue implements Integration { metadata.lifecycleHook = info; } - if (getCurrentHub().getIntegration(Vue)) { + if (getIntegration(getCurrentHub(), Vue)) { // Capture exception in the next event loop, to make sure that all breadcrumbs are recorded in time. setTimeout(() => { withScope(getCurrentHub(), scope => { scope.setContext('vue', metadata); - getCurrentHub().captureException(error); + captureException(getCurrentHub(), error); }); }); } @@ -438,14 +438,16 @@ export class Vue implements Integration { } } -interface HubType extends IHub { - getScope?(): Scope | undefined; -} +// interface HubType extends IHub { +// getScope?(): Scope | undefined; +// } /** Grabs active transaction off scope */ -export function getActiveTransaction(hub: HubType): T | undefined { - if (hub && hub.getScope) { - const scope = hub.getScope() as Scope; +export function getActiveTransaction(hub: Hub): T | undefined { + // TODO: I am confused about why the HubType and not IHub is used here. + // And why we need to check for `getScope`. So this may be wrong. + if (hub) { + const scope = getScope(hub) as Scope; if (scope) { return scope.getTransaction() as T | undefined; } diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index d269d8779522..3b881501eb35 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,5 +1,5 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; -import { getMainCarrier, setHubOnCarrier, getSession, getClient } from '@sentry/hub'; +import { getClient, getMainCarrier, getSession, lastEventId as hubLastEventId, setHubOnCarrier } from '@sentry/hub'; import { SessionStatus } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; import * as domain from 'domain'; @@ -136,7 +136,7 @@ export function init(options: NodeOptions = {}): void { * @returns The last event id of a captured event. */ export function lastEventId(): string | undefined { - return getCurrentHub().lastEventId(); + return hubLastEventId(getCurrentHub()); } /** diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index 787374014a10..a0eb7544ce07 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -8,22 +8,12 @@ import { Session, SessionContext } from './session'; import { SeverityLevel } from './severity'; import { Span, SpanContext } from './span'; import { CustomSamplingContext, Transaction, TransactionContext } from './transaction'; -import { User } from './user'; /** * Internal class used to make sure we always have the latest internal functions * working in case we have a version conflict. */ export interface Hub { - /** - * Captures an exception event and sends it to Sentry. - * - * @param exception An exception-like object. - * @param hint May contain additional information about the original exception. - * @returns The generated eventId. - */ - captureException(exception: any, hint?: EventHint): string; - /** * Captures a message event and sends it to Sentry. * @@ -42,13 +32,6 @@ export interface Hub { */ captureEvent(event: Event, hint?: EventHint): string; - /** - * This is the getter for lastEventId. - * - * @returns The last event id of a captured event. - */ - lastEventId(): string | undefined; - /** * Records a new breadcrumb which will be attached to future events. * @@ -60,13 +43,6 @@ export interface Hub { */ addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void; - /** - * Updates user context information for future events. - * - * @param user User context object to be set in the current context. Pass `null` to unset the user. - */ - setUser(user: User | null): void; - /** * Set an object that will be merged sent as tags data with the event. * diff --git a/packages/types/src/integration.ts b/packages/types/src/integration.ts index 251c135973fd..a6c4967af09e 100644 --- a/packages/types/src/integration.ts +++ b/packages/types/src/integration.ts @@ -22,5 +22,5 @@ export interface Integration { * Sets the integration up only once. * This takes no options on purpose, options should be passed in the constructor */ - setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void; + setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub | any): void; // TODO: fix any just to make the compiler happy for now } From 8558cad4761cc47d4fbabf1965aa8da109b915d9 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 03:33:23 -0500 Subject: [PATCH 10/37] chore: rename field --- packages/core/src/integrations/inboundfilters.ts | 7 +++++-- packages/hub/src/hub.ts | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index 26c0adce2eca..140a838278a6 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -1,4 +1,5 @@ -import { addGlobalEventProcessor, getCurrentHub, getClient } from '@sentry/hub'; +import { addGlobalEventProcessor, getClient,getCurrentHub } from '@sentry/hub'; +import { getIntegration } from '@sentry/hub/src'; import { Event, Integration, StackFrame } from '@sentry/types'; import { getEventDescription, isDebugBuild, isMatchingPattern, logger } from '@sentry/utils'; @@ -42,7 +43,9 @@ export class InboundFilters implements Integration { if (!hub) { return event; } - const self = hub.getIntegration(InboundFilters); + // TODO: this is really confusing, why would ask back the integration? + // setupOnce() belongs already to `self` (this) so it is confusing to ask for it. No? + const self = getIntegration(hub, InboundFilters); if (self) { const client = getClient(hub); const clientOptions = client ? client.getOptions() : {}; diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 45f22a9b6629..82c483f8e813 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -89,7 +89,7 @@ export class Hub { public readonly stack: Layer[] = [{}]; /** Contains the last event id of a captured event. */ - public _lastEventId?: string; + public lastEventId?: string; /** * Creates a new instance of the hub, will push one {@link Layer} into the @@ -229,7 +229,7 @@ export function getScope(hub: Hub): Scope | undefined { * @returns The last event id of a captured event. */ export function lastEventId(hub: Hub): string | undefined { - return hub._lastEventId; + return hub.lastEventId; } /** @@ -335,7 +335,7 @@ export function startSession(hub: Hub, context?: SessionContext): Session { */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function captureException(hub: Hub, exception: any, hint?: EventHint): string { - const eventId = (hub._lastEventId = uuid4()); + const eventId = (hub.lastEventId = uuid4()); let finalHint = hint; // If there's no explicit hint provided, mimic the same thing that would happen @@ -372,7 +372,7 @@ export function captureException(hub: Hub, exception: any, hint?: EventHint): st * @returns The generated eventId. */ export function captureMessage(hub: Hub, message: string, level?: SeverityLevel, hint?: EventHint): string { - const eventId = (hub._lastEventId = uuid4()); + const eventId = (hub.lastEventId = uuid4()); let finalHint = hint; // If there's no explicit hint provided, mimic the same thing that would happen @@ -409,7 +409,7 @@ export function captureMessage(hub: Hub, message: string, level?: SeverityLevel, export function captureEvent(hub: Hub, event: Event, hint?: EventHint): string { const eventId = uuid4(); if (event.type !== 'transaction') { - hub._lastEventId = eventId; + hub.lastEventId = eventId; } _invokeClient(hub, 'captureEvent', event, { From 482696f240aa88d91341c351ab7a4e52896341ad Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 03:35:08 -0500 Subject: [PATCH 11/37] chore: move declaration to body --- packages/hub/src/hub.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 82c483f8e813..9cfec1b264a3 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -91,6 +91,9 @@ export class Hub { /** Contains the last event id of a captured event. */ public lastEventId?: string; + /** Higher number means higher priority. */ + public readonly version: number; + /** * Creates a new instance of the hub, will push one {@link Layer} into the * internal stack on creation. @@ -99,7 +102,8 @@ export class Hub { * @param scope bound to the hub. * @param version number, higher number means higher priority. */ - public constructor(client?: Client, scope: Scope = new Scope(), public readonly _version: number = API_VERSION) { + public constructor(client?: Client, scope: Scope = new Scope(), version: number = API_VERSION) { + this.version = version; getStackTop(this).scope = scope; if (client) { bindClient(this, client); @@ -176,7 +180,7 @@ export function pushScope(hub: Hub): Scope { * @hidden */ export function isOlderThan(hub: Hub, version: number): boolean { - return hub._version < version; + return hub.version < version; } /** From c25359c982bfebc0464e1a649c9f70619a65461d Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 03:42:12 -0500 Subject: [PATCH 12/37] chore: move functions out --- .../core/src/integrations/inboundfilters.ts | 180 +++++++++--------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index 140a838278a6..f9368da6755f 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -65,8 +65,8 @@ export class InboundFilters implements Integration { } /** JSDoc */ - private _shouldDropEvent(event: Event, options: Partial): boolean { - if (this._isSentryError(event, options)) { + public _shouldDropEvent(event: Event, options: Partial): boolean { + if (isSentryError(event, options)) { if (isDebugBuild()) { logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`); } @@ -85,7 +85,7 @@ export class InboundFilters implements Integration { logger.warn( `Event dropped due to being matched by \`denyUrls\` option.\nEvent: ${getEventDescription( event, - )}.\nUrl: ${this._getEventFilterUrl(event)}`, + )}.\nUrl: ${getEventFilterUrl(event)}`, ); } return true; @@ -95,7 +95,7 @@ export class InboundFilters implements Integration { logger.warn( `Event dropped due to not being matched by \`allowUrls\` option.\nEvent: ${getEventDescription( event, - )}.\nUrl: ${this._getEventFilterUrl(event)}`, + )}.\nUrl: ${getEventFilterUrl(event)}`, ); } return true; @@ -104,56 +104,7 @@ export class InboundFilters implements Integration { } /** JSDoc */ - private _isSentryError(event: Event, options: Partial): boolean { - if (!options.ignoreInternal) { - return false; - } - - try { - // @ts-ignore can't be a sentry error if undefined - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return event.exception.values[0].type === 'SentryError'; - } catch (e) { - // ignore - } - - return false; - } - - /** JSDoc */ - private _isIgnoredError(event: Event, options: Partial): boolean { - if (!options.ignoreErrors || !options.ignoreErrors.length) { - return false; - } - - return this._getPossibleEventMessages(event).some(message => - // Not sure why TypeScript complains here... - (options.ignoreErrors as Array).some(pattern => isMatchingPattern(message, pattern)), - ); - } - - /** JSDoc */ - private _isDeniedUrl(event: Event, options: Partial): boolean { - // TODO: Use Glob instead? - if (!options.denyUrls || !options.denyUrls.length) { - return false; - } - const url = this._getEventFilterUrl(event); - return !url ? false : options.denyUrls.some(pattern => isMatchingPattern(url, pattern)); - } - - /** JSDoc */ - private _isAllowedUrl(event: Event, options: Partial): boolean { - // TODO: Use Glob instead? - if (!options.allowUrls || !options.allowUrls.length) { - return true; - } - const url = this._getEventFilterUrl(event); - return !url ? true : options.allowUrls.some(pattern => isMatchingPattern(url, pattern)); - } - - /** JSDoc */ - private _mergeOptions(clientOptions: Partial = {}): Partial { + public _mergeOptions(clientOptions: Partial = {}): Partial { return { allowUrls: [ // eslint-disable-next-line deprecation/deprecation @@ -181,56 +132,105 @@ export class InboundFilters implements Integration { } /** JSDoc */ - private _getPossibleEventMessages(event: Event): string[] { - if (event.message) { - return [event.message]; - } - if (event.exception) { - try { - const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {}; - return [`${value}`, `${type}: ${value}`]; - } catch (oO) { - if (isDebugBuild()) { - logger.error(`Cannot extract message for event ${getEventDescription(event)}`); - } - return []; - } + private _isIgnoredError(event: Event, options: Partial): boolean { + if (!options.ignoreErrors || !options.ignoreErrors.length) { + return false; } - return []; + + return getPossibleEventMessages(event).some(message => + // Not sure why TypeScript complains here... + (options.ignoreErrors as Array).some(pattern => isMatchingPattern(message, pattern)), + ); } /** JSDoc */ - private _getLastValidUrl(frames: StackFrame[] = []): string | null { - for (let i = frames.length - 1; i >= 0; i--) { - const frame = frames[i]; + private _isDeniedUrl(event: Event, options: Partial): boolean { + // TODO: Use Glob instead? + if (!options.denyUrls || !options.denyUrls.length) { + return false; + } + const url = getEventFilterUrl(event); + return !url ? false : options.denyUrls.some(pattern => isMatchingPattern(url, pattern)); + } - if (frame && frame.filename !== '' && frame.filename !== '[native code]') { - return frame.filename || null; - } + /** JSDoc */ + private _isAllowedUrl(event: Event, options: Partial): boolean { + // TODO: Use Glob instead? + if (!options.allowUrls || !options.allowUrls.length) { + return true; } + const url = getEventFilterUrl(event); + return !url ? true : options.allowUrls.some(pattern => isMatchingPattern(url, pattern)); + } +} +/** JSDoc */ +function getEventFilterUrl(event: Event): string | null { + try { + if (event.stacktrace) { + return getLastValidUrl(event.stacktrace.frames); + } + let frames; + try { + // @ts-ignore we only care about frames if the whole thing here is defined + frames = event.exception.values[0].stacktrace.frames; + } catch (e) { + // ignore + } + return frames ? getLastValidUrl(frames) : null; + } catch (oO) { + if (isDebugBuild()) { + logger.error(`Cannot extract url for event ${getEventDescription(event)}`); + } return null; } +} - /** JSDoc */ - private _getEventFilterUrl(event: Event): string | null { +/** JSDoc */ +function getPossibleEventMessages(event: Event): string[] { + if (event.message) { + return [event.message]; + } + if (event.exception) { try { - if (event.stacktrace) { - return this._getLastValidUrl(event.stacktrace.frames); - } - let frames; - try { - // @ts-ignore we only care about frames if the whole thing here is defined - frames = event.exception.values[0].stacktrace.frames; - } catch (e) { - // ignore - } - return frames ? this._getLastValidUrl(frames) : null; + const { type = '', value = '' } = (event.exception.values && event.exception.values[0]) || {}; + return [`${value}`, `${type}: ${value}`]; } catch (oO) { if (isDebugBuild()) { - logger.error(`Cannot extract url for event ${getEventDescription(event)}`); + logger.error(`Cannot extract message for event ${getEventDescription(event)}`); } - return null; + return []; } } + return []; +} + +/** JSDoc */ +function getLastValidUrl(frames: StackFrame[] = []): string | null { + for (let i = frames.length - 1; i >= 0; i--) { + const frame = frames[i]; + + if (frame && frame.filename !== '' && frame.filename !== '[native code]') { + return frame.filename || null; + } + } + + return null; +} + +/** JSDoc */ +function isSentryError(event: Event, options: Partial): boolean { + if (!options.ignoreInternal) { + return false; + } + + try { + // @ts-ignore can't be a sentry error if undefined + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + return event.exception.values[0].type === 'SentryError'; + } catch (e) { + // ignore + } + + return false; } From 98ebe312e1f4d4c0ea77474c6d754fd434f13ba8 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 04:01:33 -0500 Subject: [PATCH 13/37] fix: compiler issues --- packages/browser/src/sdk.ts | 12 ++++++------ packages/core/src/integrations/inboundfilters.ts | 6 ++++-- packages/hub/src/scope.ts | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 28d4391cba2f..c486039bf2e9 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -1,6 +1,5 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; -import { getClient, lastEventId as hubLastEventId } from '@sentry/hub'; -import { Hub } from '@sentry/types'; +import { getClient, getScope, Hub, lastEventId as hubLastEventId } from '@sentry/hub'; import { addInstrumentationHandler, getGlobalObject, isDebugBuild, logger, resolvedSyncPromise } from '@sentry/utils'; import { BrowserOptions } from './backend'; @@ -108,7 +107,7 @@ export function init(options: BrowserOptions = {}): void { */ export function showReportDialog(options: ReportDialogOptions = {}): void { const hub = getCurrentHub(); - const scope = hub.getScope(); + const scope = getScope(hub); if (scope) { options.user = { ...scope.getUser(), @@ -227,9 +226,10 @@ function startSessionTracking(): void { // https://github.com/getsentry/sentry-javascript/issues/3207 and // https://github.com/getsentry/sentry-javascript/issues/3234 and // https://github.com/getsentry/sentry-javascript/issues/3278. - if (!hub.captureSession) { - return; - } + // TODO: Follow up on this + // if (!hub.captureSession) { + // return; + // } // The session duration for browser sessions does not track a meaningful // concept that can be used as a metric. diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index f9368da6755f..2c5ed2ea5db9 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -1,5 +1,4 @@ -import { addGlobalEventProcessor, getClient,getCurrentHub } from '@sentry/hub'; -import { getIntegration } from '@sentry/hub/src'; +import { addGlobalEventProcessor, getClient,getCurrentHub, getIntegration } from '@sentry/hub'; import { Event, Integration, StackFrame } from '@sentry/types'; import { getEventDescription, isDebugBuild, isMatchingPattern, logger } from '@sentry/utils'; @@ -54,10 +53,13 @@ export class InboundFilters implements Integration { // The bug is caused by multiple SDK instances, where one is minified and one is using non-mangled code. // Unfortunatelly we cannot fix it reliably (thus reserved property in rollup's terser config), // as we cannot force people using multiple instances in their apps to sync SDK versions. + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const options = typeof self._mergeOptions === 'function' ? self._mergeOptions(clientOptions) : {}; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (typeof self._shouldDropEvent !== 'function') { return event; } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access return self._shouldDropEvent(event, options) ? null : event; } return event; diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index a4ecb421ea4f..da80c74ce303 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -447,7 +447,7 @@ export class Scope implements ScopeInterface { /** * This will be called after {@link applyToEvent} is finished. */ - protected _notifyEventProcessors( + public _notifyEventProcessors( processors: EventProcessor[], event: Event | null, hint?: EventHint, From 06042d089c2ff80fbcf028069871261fd52ef917 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 04:31:42 -0500 Subject: [PATCH 14/37] fix: compilation issues --- packages/angular/src/tracing.ts | 3 ++- .../browser/src/integrations/breadcrumbs.ts | 20 ++++++++++++------- .../src/integrations/globalhandlers.ts | 10 +++++----- .../browser/src/integrations/linkederrors.ts | 7 ++++--- .../browser/src/integrations/useragent.ts | 3 ++- packages/browser/src/sdk.ts | 6 +++--- packages/hub/src/index.ts | 5 +++++ packages/node/src/integrations/console.ts | 5 +++-- packages/react/src/profiler.tsx | 5 +++-- packages/tracing/src/hubextensions.ts | 4 ++-- packages/tracing/src/idletransaction.ts | 6 +++--- .../tracing/src/integrations/node/mongo.ts | 4 ++-- .../tracing/src/integrations/node/mysql.ts | 4 ++-- .../tracing/src/integrations/node/postgres.ts | 4 ++-- packages/tracing/src/transaction.ts | 4 ++-- packages/tracing/src/utils.ts | 4 ++-- packages/vue/src/errorhandler.ts | 4 ++-- packages/vue/src/tracing.ts | 5 ++--- 18 files changed, 59 insertions(+), 44 deletions(-) diff --git a/packages/angular/src/tracing.ts b/packages/angular/src/tracing.ts index 991f580a52eb..2ad3b529d290 100644 --- a/packages/angular/src/tracing.ts +++ b/packages/angular/src/tracing.ts @@ -1,6 +1,7 @@ import { AfterViewInit, Directive, Injectable, Input, NgModule, OnDestroy, OnInit } from '@angular/core'; import { Event, NavigationEnd, NavigationStart, Router } from '@angular/router'; import { getCurrentHub } from '@sentry/browser'; +import { getScope } from '@sentry/hub'; import { Span, Transaction, TransactionContext } from '@sentry/types'; import { getGlobalObject, logger, stripUrlQueryAndFragment, timestampWithMs } from '@sentry/utils'; import { Observable, Subscription } from 'rxjs'; @@ -44,7 +45,7 @@ export function getActiveTransaction(): Transaction | undefined { const currentHub = getCurrentHub(); if (currentHub) { - const scope = currentHub.getScope(); + const scope = getScope(currentHub); if (scope) { return scope.getTransaction(); } diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index f946430d9a2b..27a7fa26b3ae 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable max-lines */ import { getCurrentHub } from '@sentry/core'; +import { addBreadcrumb } from '@sentry/hub'; import { Event, Integration } from '@sentry/types'; import { addInstrumentationHandler, @@ -62,7 +63,8 @@ export class Breadcrumbs implements Integration { if (!this._options.sentry) { return; } - getCurrentHub().addBreadcrumb( + addBreadcrumb( + getCurrentHub(), { category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`, event_id: event.event_id, @@ -130,7 +132,8 @@ function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: { [key: s return; } - getCurrentHub().addBreadcrumb( + addBreadcrumb( + getCurrentHub(), { category: `ui.${handlerData.name}`, message: target, @@ -171,7 +174,7 @@ function _consoleBreadcrumb(handlerData: { [key: string]: any }): void { } } - getCurrentHub().addBreadcrumb(breadcrumb, { + addBreadcrumb(getCurrentHub(), breadcrumb, { input: handlerData.args, level: handlerData.level, }); @@ -190,7 +193,8 @@ function _xhrBreadcrumb(handlerData: { [key: string]: any }): void { const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__ || {}; - getCurrentHub().addBreadcrumb( + addBreadcrumb( + getCurrentHub(), { category: 'xhr', data: { @@ -226,7 +230,8 @@ function _fetchBreadcrumb(handlerData: { [key: string]: any }): void { } if (handlerData.error) { - getCurrentHub().addBreadcrumb( + addBreadcrumb( + getCurrentHub(), { category: 'fetch', data: handlerData.fetchData, @@ -239,7 +244,8 @@ function _fetchBreadcrumb(handlerData: { [key: string]: any }): void { }, ); } else { - getCurrentHub().addBreadcrumb( + addBreadcrumb( + getCurrentHub(), { category: 'fetch', data: { @@ -282,7 +288,7 @@ function _historyBreadcrumb(handlerData: { [key: string]: any }): void { from = parsedFrom.relative; } - getCurrentHub().addBreadcrumb({ + addBreadcrumb(getCurrentHub(), { category: 'navigation', data: { from, diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index 535b3e067d4e..df40bc6c7274 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getCurrentHub } from '@sentry/core'; -import { getClient } from '@sentry/hub'; -import { Event, EventHint, Hub, Integration, Primitive } from '@sentry/types'; +import { captureEvent, getClient, getIntegration, Hub } from '@sentry/hub'; +import { Event, EventHint, Integration, Primitive } from '@sentry/types'; import { addExceptionMechanism, addInstrumentationHandler, @@ -81,7 +81,7 @@ function _installGlobalOnErrorHandler(): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any (data: { msg: any; url: any; line: any; column: any; error: any }) => { const [hub, attachStacktrace] = getHubAndAttachStacktrace(); - if (!hub.getIntegration(GlobalHandlers)) { + if (!getIntegration(hub, GlobalHandlers)) { return; } const { msg, url, line, column, error } = data; @@ -114,7 +114,7 @@ function _installGlobalOnUnhandledRejectionHandler(): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any (e: any) => { const [hub, attachStacktrace] = getHubAndAttachStacktrace(); - if (!hub.getIntegration(GlobalHandlers)) { + if (!getIntegration(hub, GlobalHandlers)) { return; } let error = e; @@ -251,7 +251,7 @@ function addMechanismAndCapture(hub: Hub, error: EventHint['originalException'], handled: false, type, }); - hub.captureEvent(event, { + captureEvent(hub, event, { originalException: error, }); } diff --git a/packages/browser/src/integrations/linkederrors.ts b/packages/browser/src/integrations/linkederrors.ts index 8870e689f8ef..5935d88ae9c1 100644 --- a/packages/browser/src/integrations/linkederrors.ts +++ b/packages/browser/src/integrations/linkederrors.ts @@ -1,4 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; +import { getIntegration } from '@sentry/hub'; import { Event, EventHint, Exception, ExtendedError, Integration } from '@sentry/types'; import { isInstanceOf } from '@sentry/utils'; @@ -28,12 +29,12 @@ export class LinkedErrors implements Integration { /** * @inheritDoc */ - private readonly _key: LinkedErrorsOptions['key']; + public readonly _key: LinkedErrorsOptions['key']; /** * @inheritDoc */ - private readonly _limit: LinkedErrorsOptions['limit']; + public readonly _limit: LinkedErrorsOptions['limit']; /** * @inheritDoc @@ -48,7 +49,7 @@ export class LinkedErrors implements Integration { */ public setupOnce(): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getCurrentHub().getIntegration(LinkedErrors); + const self = getIntegration(getCurrentHub(), LinkedErrors); return self ? _handler(self._key, self._limit, event, hint) : event; }); } diff --git a/packages/browser/src/integrations/useragent.ts b/packages/browser/src/integrations/useragent.ts index 1160320f9d93..567d7df444f0 100644 --- a/packages/browser/src/integrations/useragent.ts +++ b/packages/browser/src/integrations/useragent.ts @@ -1,4 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; +import { getIntegration } from '@sentry/hub'; import { Event, Integration } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils'; @@ -21,7 +22,7 @@ export class UserAgent implements Integration { */ public setupOnce(): void { addGlobalEventProcessor((event: Event) => { - if (getCurrentHub().getIntegration(UserAgent)) { + if (getIntegration(getCurrentHub(), UserAgent)) { // if none of the information we want exists, don't bother if (!global.navigator && !global.location && !global.document) { return event; diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index c486039bf2e9..a46add98d45d 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -1,5 +1,5 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; -import { getClient, getScope, Hub, lastEventId as hubLastEventId } from '@sentry/hub'; +import { startSession, getClient, getScope, Hub, lastEventId as hubLastEventId, captureSession } from '@sentry/hub'; import { addInstrumentationHandler, getGlobalObject, isDebugBuild, logger, resolvedSyncPromise } from '@sentry/utils'; import { BrowserOptions } from './backend'; @@ -200,8 +200,8 @@ export function wrap(fn: (...args: any) => any): any { } function startSessionOnHub(hub: Hub): void { - hub.startSession({ ignoreDuration: true }); - hub.captureSession(); + startSession(hub, { ignoreDuration: true }); + captureSession(hub); } /** diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index ad713e8e8027..ed8a0bdab0f7 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -15,9 +15,14 @@ export { getScope, setUser, lastEventId, + captureSession, + startSession, + addBreadcrumb, + captureEvent, captureException, getIntegration, captureMessage, + configureScope, Hub, makeMain, setHubOnCarrier, diff --git a/packages/node/src/integrations/console.ts b/packages/node/src/integrations/console.ts index 0f19f15ffeb7..edead20a390c 100644 --- a/packages/node/src/integrations/console.ts +++ b/packages/node/src/integrations/console.ts @@ -1,4 +1,5 @@ import { getCurrentHub } from '@sentry/core'; +import { addBreadcrumb, getIntegration } from '@sentry/hub'; import { Integration } from '@sentry/types'; import { fill, severityFromString } from '@sentry/utils'; import * as util from 'util'; @@ -34,8 +35,8 @@ function createConsoleWrapper(level: string): (originalConsoleMethod: () => void /* eslint-disable prefer-rest-params */ return function(this: typeof console): void { - if (getCurrentHub().getIntegration(Console)) { - getCurrentHub().addBreadcrumb( + if (getIntegration(getCurrentHub(), Console)) { + addBreadcrumb(getCurrentHub(), { category: 'console', level: sentryLevel, diff --git a/packages/react/src/profiler.tsx b/packages/react/src/profiler.tsx index acf764347c69..ca3281cc9444 100644 --- a/packages/react/src/profiler.tsx +++ b/packages/react/src/profiler.tsx @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { getCurrentHub, Hub } from '@sentry/browser'; +import { getScope, getIntegration } from '@sentry/hub'; import { Integration, IntegrationClass, Span, Transaction } from '@sentry/types'; import { timestampWithMs } from '@sentry/utils'; import hoistNonReactStatics from 'hoist-non-react-statics'; @@ -21,7 +22,7 @@ const getTracingIntegration = (): Integration | null => { return globalTracingIntegration; } - globalTracingIntegration = getCurrentHub().getIntegration(TRACING_GETTER); + globalTracingIntegration = getIntegration(getCurrentHub(), TRACING_GETTER); return globalTracingIntegration; }; @@ -274,7 +275,7 @@ export { withProfiler, Profiler, useProfiler }; /** Grabs active transaction off scope */ export function getActiveTransaction(hub: Hub = getCurrentHub()): T | undefined { if (hub) { - const scope = hub.getScope(); + const scope = getScope(hub); if (scope) { return scope.getTransaction() as T | undefined; } diff --git a/packages/tracing/src/hubextensions.ts b/packages/tracing/src/hubextensions.ts index 3c0fc526b656..4a76c39c8186 100644 --- a/packages/tracing/src/hubextensions.ts +++ b/packages/tracing/src/hubextensions.ts @@ -1,4 +1,4 @@ -import { getClient, getMainCarrier, Hub } from '@sentry/hub'; +import { getClient, getMainCarrier, getScope, Hub } from '@sentry/hub'; import { CustomSamplingContext, Integration, @@ -16,7 +16,7 @@ import { hasTracingEnabled } from './utils'; /** Returns all trace headers that are currently on the top scope. */ function traceHeaders(this: Hub): { [key: string]: string } { - const scope = this.getScope(); + const scope = getScope(this); if (scope) { const span = scope.getSpan(); if (span) { diff --git a/packages/tracing/src/idletransaction.ts b/packages/tracing/src/idletransaction.ts index 07839d6c6616..948752095692 100644 --- a/packages/tracing/src/idletransaction.ts +++ b/packages/tracing/src/idletransaction.ts @@ -1,4 +1,4 @@ -import { Hub } from '@sentry/hub'; +import { getScope, Hub, configureScope } from '@sentry/hub'; import { TransactionContext } from '@sentry/types'; import { logger, timestampWithMs } from '@sentry/utils'; @@ -93,7 +93,7 @@ export class IdleTransaction extends Transaction { // We set the transaction here on the scope so error events pick up the trace // context and attach it to the error. logger.log(`Setting idle transaction on scope. Span ID: ${this.spanId}`); - _idleHub.configureScope(scope => scope.setSpan(this)); + configureScope(_idleHub, scope => scope.setSpan(this)); } this._initTimeout = setTimeout(() => { @@ -276,7 +276,7 @@ export class IdleTransaction extends Transaction { */ function clearActiveTransaction(hub?: Hub): void { if (hub) { - const scope = hub.getScope(); + const scope = getScope(hub); if (scope) { const transaction = scope.getTransaction(); if (transaction) { diff --git a/packages/tracing/src/integrations/node/mongo.ts b/packages/tracing/src/integrations/node/mongo.ts index f19ae2a10429..8768a9f835cf 100644 --- a/packages/tracing/src/integrations/node/mongo.ts +++ b/packages/tracing/src/integrations/node/mongo.ts @@ -1,4 +1,4 @@ -import { Hub } from '@sentry/hub'; +import { getScope, Hub } from '@sentry/hub'; import { EventProcessor, Integration, SpanContext } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; @@ -148,7 +148,7 @@ export class Mongo implements Integration { fill(collection.prototype, operation, function(orig: () => void | Promise) { return function(this: unknown, ...args: unknown[]) { const lastArg = args[args.length - 1]; - const scope = getCurrentHub().getScope(); + const scope = getScope(getCurrentHub()); const parentSpan = scope?.getSpan(); // Check if the operation was passed a callback. (mapReduce requires a different check, as diff --git a/packages/tracing/src/integrations/node/mysql.ts b/packages/tracing/src/integrations/node/mysql.ts index 2d53f7dfc61a..56a664e0544d 100644 --- a/packages/tracing/src/integrations/node/mysql.ts +++ b/packages/tracing/src/integrations/node/mysql.ts @@ -1,4 +1,4 @@ -import { Hub } from '@sentry/hub'; +import { getScope, Hub } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { fill, loadModule, logger } from '@sentry/utils'; @@ -35,7 +35,7 @@ export class Mysql implements Integration { // function (options, values, callback) => void fill(pkg, 'createQuery', function(orig: () => void) { return function(this: unknown, options: unknown, values: unknown, callback: unknown) { - const scope = getCurrentHub().getScope(); + const scope = getScope(getCurrentHub()); const parentSpan = scope?.getSpan(); const span = parentSpan?.startChild({ description: typeof options === 'string' ? options : (options as { sql: string }).sql, diff --git a/packages/tracing/src/integrations/node/postgres.ts b/packages/tracing/src/integrations/node/postgres.ts index b345f8a2876b..3bde0adb9c3d 100644 --- a/packages/tracing/src/integrations/node/postgres.ts +++ b/packages/tracing/src/integrations/node/postgres.ts @@ -1,4 +1,4 @@ -import { Hub } from '@sentry/hub'; +import { getScope, Hub } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; @@ -57,7 +57,7 @@ export class Postgres implements Integration { */ fill(Client.prototype, 'query', function(orig: () => void | Promise) { return function(this: unknown, config: unknown, values: unknown, callback: unknown) { - const scope = getCurrentHub().getScope(); + const scope = getScope(getCurrentHub()); const parentSpan = scope?.getSpan(); const span = parentSpan?.startChild({ description: typeof config === 'string' ? config : (config as { text: string }).text, diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 198b294f7cee..80453c9df2f5 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -1,4 +1,4 @@ -import { getClient, getCurrentHub, Hub } from '@sentry/hub'; +import { captureEvent, getClient, getCurrentHub, Hub } from '@sentry/hub'; import { Event, Measurements, @@ -144,7 +144,7 @@ export class Transaction extends SpanClass implements TransactionInterface { logger.log(`[Tracing] Finishing ${this.op} transaction: ${this.name}.`); - return this._hub.captureEvent(transaction); + return captureEvent(this._hub, transaction); } /** diff --git a/packages/tracing/src/utils.ts b/packages/tracing/src/utils.ts index 93dc5b37202f..f44e2ea75090 100644 --- a/packages/tracing/src/utils.ts +++ b/packages/tracing/src/utils.ts @@ -1,4 +1,4 @@ -import { getClient, getCurrentHub, Hub } from '@sentry/hub'; +import { getClient, getCurrentHub, getScope, Hub } from '@sentry/hub'; import { Options, TraceparentData, Transaction } from '@sentry/types'; export const TRACEPARENT_REGEXP = new RegExp( @@ -48,7 +48,7 @@ export function extractTraceparentData(traceparent: string): TraceparentData | u /** Grabs active transaction off scope, if any */ export function getActiveTransaction(maybeHub?: Hub): T | undefined { const hub = maybeHub || getCurrentHub(); - const scope = hub.getScope(); + const scope = getScope(hub); return scope && (scope.getTransaction() as T | undefined); } diff --git a/packages/vue/src/errorhandler.ts b/packages/vue/src/errorhandler.ts index 040a6ae2a290..20bc43ab8855 100644 --- a/packages/vue/src/errorhandler.ts +++ b/packages/vue/src/errorhandler.ts @@ -1,5 +1,5 @@ import { getCurrentHub } from '@sentry/browser'; -import { withScope } from '@sentry/hub'; +import { captureException, withScope } from '@sentry/hub'; import { formatComponentName, generateComponentTrace } from './components'; import { Options, ViewModel, Vue } from './types'; @@ -28,7 +28,7 @@ export const attachErrorHandler = (app: Vue, options: Options): void => { setTimeout(() => { withScope(getCurrentHub(), scope => { scope.setContext('vue', metadata); - getCurrentHub().captureException(error); + captureException(getCurrentHub(), error); }); }); diff --git a/packages/vue/src/tracing.ts b/packages/vue/src/tracing.ts index deb14eb848f6..53154baa4f4c 100644 --- a/packages/vue/src/tracing.ts +++ b/packages/vue/src/tracing.ts @@ -1,4 +1,5 @@ import { getCurrentHub } from '@sentry/browser'; +import { getScope } from '@sentry/hub'; import { Span, Transaction } from '@sentry/types'; import { logger, timestampInSeconds } from '@sentry/utils'; @@ -30,9 +31,7 @@ const HOOKS: { [key in Operation]: Hook[] } = { /** Grabs active transaction off scope, if any */ function getActiveTransaction(): Transaction | undefined { - return getCurrentHub() - .getScope() - ?.getTransaction(); + return getScope(getCurrentHub())?.getTransaction(); } /** Finish top-level span and activity with a debounce configured using `timeout` option */ From f88b7fda1bd5db6a6f6558efc9dc34b33dfd0a33 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 05:10:11 -0500 Subject: [PATCH 15/37] chore: remove Hub interface funcs --- packages/browser/src/integrations/dedupe.ts | 5 +- packages/core/test/mocks/integration.ts | 4 +- .../sentry-performance.ts | 12 +- packages/integrations/src/debug.ts | 3 +- packages/integrations/src/dedupe.ts | 3 +- packages/integrations/src/extraerrordata.ts | 3 +- packages/integrations/src/offline.ts | 7 +- packages/integrations/src/rewriteframes.ts | 3 +- packages/integrations/src/sessiontiming.ts | 3 +- packages/integrations/src/transaction.ts | 3 +- packages/node/src/eventbuilder.ts | 4 +- packages/node/src/handlers.ts | 6 +- packages/node/src/integrations/http.ts | 6 +- .../node/src/integrations/linkederrors.ts | 3 +- packages/node/src/integrations/modules.ts | 3 +- .../src/integrations/onuncaughtexception.ts | 4 +- .../src/integrations/onunhandledrejection.ts | 4 +- packages/node/src/sdk.ts | 11 +- packages/types/src/hub.ts | 148 +----------------- 19 files changed, 57 insertions(+), 178 deletions(-) diff --git a/packages/browser/src/integrations/dedupe.ts b/packages/browser/src/integrations/dedupe.ts index 641823bbde3c..4698c17b6c1b 100644 --- a/packages/browser/src/integrations/dedupe.ts +++ b/packages/browser/src/integrations/dedupe.ts @@ -1,5 +1,6 @@ -import { Event, EventProcessor, Exception, Hub, Integration, StackFrame } from '@sentry/types'; +import { Event, EventProcessor, Exception, Integration, StackFrame } from '@sentry/types'; import { logger } from '@sentry/utils'; +import { Hub, getIntegration } from '@sentry/hub'; /** Deduplication filter */ export class Dedupe implements Integration { @@ -23,7 +24,7 @@ export class Dedupe implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((currentEvent: Event) => { - const self = getCurrentHub().getIntegration(Dedupe); + const self = getIntegration(getCurrentHub(), Dedupe); if (self) { // Juuust in case something goes wrong try { diff --git a/packages/core/test/mocks/integration.ts b/packages/core/test/mocks/integration.ts index 465fac64576a..1b00c309e514 100644 --- a/packages/core/test/mocks/integration.ts +++ b/packages/core/test/mocks/integration.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/hub'; +import { getCurrentHub, getIntegration } from '@sentry/hub'; import { configureScope } from '@sentry/minimal'; import { Event, Integration } from '@sentry/types'; @@ -10,7 +10,7 @@ export class TestIntegration implements Integration { public setupOnce(): void { configureScope(scope => { scope.addEventProcessor((event: Event) => { - if (!getCurrentHub().getIntegration(TestIntegration)) { + if (!getIntegration(getCurrentHub(), TestIntegration)) { return event; } diff --git a/packages/ember/addon/instance-initializers/sentry-performance.ts b/packages/ember/addon/instance-initializers/sentry-performance.ts index c77e9a80738e..ef03f56bfe37 100644 --- a/packages/ember/addon/instance-initializers/sentry-performance.ts +++ b/packages/ember/addon/instance-initializers/sentry-performance.ts @@ -8,6 +8,7 @@ import { getActiveTransaction } from '..'; import { browserPerformanceTimeOrigin, getGlobalObject, timestampWithMs } from '@sentry/utils'; import { macroCondition, isTesting, getOwnConfig } from '@embroider/macros'; import { EmberSentryConfig, GlobalConfig, OwnConfig } from '../types'; +import { getIntegration } from '@sentry/hub'; function getSentryConfig() { const _global = getGlobalObject(); @@ -266,6 +267,7 @@ function _instrumentComponents(config: EmberSentryConfig) { const beforeComponentDefinitionEntries = {} as RenderEntries; const subscribe = Ember.subscribe; + function _subscribeToRenderEvents() { subscribe('render.component', { before(_name: string, _timestamp: number, payload: Payload) { @@ -288,6 +290,7 @@ function _instrumentComponents(config: EmberSentryConfig) { }); } } + _subscribeToRenderEvents(); } @@ -366,9 +369,12 @@ export async function instrumentForPerformance(appInstance: ApplicationInstance) }), ]; - if (isTesting() && Sentry.getCurrentHub()?.getIntegration(tracing.Integrations.BrowserTracing)) { - // Initializers are called more than once in tests, causing the integrations to not be setup correctly. - return; + if (isTesting()) { + const hub = Sentry.getCurrentHub(); + if (getIntegration(hub, tracing.Integrations.BrowserTracing)) { + // Initializers are called more than once in tests, causing the integrations to not be setup correctly. + return; + } } Sentry.init(sentryConfig); // Call init again to rebind client with new integration list in addition to the defaults diff --git a/packages/integrations/src/debug.ts b/packages/integrations/src/debug.ts index b54e96d2ee4a..39044b5ad794 100644 --- a/packages/integrations/src/debug.ts +++ b/packages/integrations/src/debug.ts @@ -1,5 +1,6 @@ import { Event, EventHint, EventProcessor, Hub, Integration } from '@sentry/types'; import { consoleSandbox } from '@sentry/utils'; +import { getIntegration } from '@sentry/hub'; /** JSDoc */ interface DebugOptions { @@ -38,7 +39,7 @@ export class Debug implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getCurrentHub().getIntegration(Debug); + const self = getIntegration(getCurrentHub(), Debug); if (self) { if (self._options.debugger) { // eslint-disable-next-line no-debugger diff --git a/packages/integrations/src/dedupe.ts b/packages/integrations/src/dedupe.ts index f36e9770b908..efdaa8258caf 100644 --- a/packages/integrations/src/dedupe.ts +++ b/packages/integrations/src/dedupe.ts @@ -1,5 +1,6 @@ import { Event, EventProcessor, Exception, Hub, Integration, StackFrame } from '@sentry/types'; import { logger } from '@sentry/utils'; +import { getIntegration } from '@sentry/hub'; /** Deduplication filter */ export class Dedupe implements Integration { @@ -23,7 +24,7 @@ export class Dedupe implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((currentEvent: Event) => { - const self = getCurrentHub().getIntegration(Dedupe); + const self = getIntegration(getCurrentHub(), Dedupe); if (self) { // Juuust in case something goes wrong try { diff --git a/packages/integrations/src/extraerrordata.ts b/packages/integrations/src/extraerrordata.ts index de01df6605c0..8f1d41b3503b 100644 --- a/packages/integrations/src/extraerrordata.ts +++ b/packages/integrations/src/extraerrordata.ts @@ -1,5 +1,6 @@ import { Event, EventHint, EventProcessor, ExtendedError, Hub, Integration } from '@sentry/types'; import { isError, isPlainObject, logger, normalize } from '@sentry/utils'; +import { getIntegration } from '@sentry/hub'; /** JSDoc */ interface ExtraErrorDataOptions { @@ -28,7 +29,7 @@ export class ExtraErrorData implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getCurrentHub().getIntegration(ExtraErrorData); + const self = getIntegration(getCurrentHub(), ExtraErrorData); if (!self) { return event; } diff --git a/packages/integrations/src/offline.ts b/packages/integrations/src/offline.ts index 5cf1be2cd069..9a83aa9f825d 100644 --- a/packages/integrations/src/offline.ts +++ b/packages/integrations/src/offline.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { Event, EventProcessor, Hub, Integration } from '@sentry/types'; +import { Event, EventProcessor, Integration } from '@sentry/types'; +import { captureEvent, getIntegration, Hub } from '@sentry/hub'; import { getGlobalObject, logger, normalize, uuid4 } from '@sentry/utils'; import localForage from 'localforage'; @@ -76,7 +77,7 @@ export class Offline implements Integration { } addGlobalEventProcessor((event: Event) => { - if (this.hub && this.hub.getIntegration(Offline)) { + if (this.hub && getIntegration(this.hub, Offline)) { // cache if we are positively offline if ('navigator' in this.global && 'onLine' in this.global.navigator && !this.global.navigator.onLine) { void this._cacheEvent(event) @@ -157,7 +158,7 @@ export class Offline implements Integration { private async _sendEvents(): Promise { return this.offlineEventStore.iterate((event: Event, cacheKey: string, _index: number): void => { if (this.hub) { - this.hub.captureEvent(event); + captureEvent(this.hub, event); void this._purgeEvent(cacheKey).catch((_error): void => { logger.warn('could not purge event from cache'); diff --git a/packages/integrations/src/rewriteframes.ts b/packages/integrations/src/rewriteframes.ts index 9eb95e54f6d3..1c93778f13fb 100644 --- a/packages/integrations/src/rewriteframes.ts +++ b/packages/integrations/src/rewriteframes.ts @@ -1,5 +1,6 @@ import { Event, EventProcessor, Hub, Integration, StackFrame, Stacktrace } from '@sentry/types'; import { basename, relative } from '@sentry/utils'; +import { getIntegration } from '@sentry/hub'; type StackFrameIteratee = (frame: StackFrame) => StackFrame; @@ -45,7 +46,7 @@ export class RewriteFrames implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(event => { - const self = getCurrentHub().getIntegration(RewriteFrames); + const self = getIntegration(getCurrentHub(), RewriteFrames); if (self) { return self.process(event); } diff --git a/packages/integrations/src/sessiontiming.ts b/packages/integrations/src/sessiontiming.ts index 0e45588e0f31..06151029c829 100644 --- a/packages/integrations/src/sessiontiming.ts +++ b/packages/integrations/src/sessiontiming.ts @@ -1,4 +1,5 @@ import { Event, EventProcessor, Hub, Integration } from '@sentry/types'; +import { getIntegration } from '@sentry/hub'; /** This function adds duration since Sentry was initialized till the time event was sent */ export class SessionTiming implements Integration { @@ -20,7 +21,7 @@ export class SessionTiming implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(event => { - const self = getCurrentHub().getIntegration(SessionTiming); + const self = getIntegration(getCurrentHub(), SessionTiming); if (self) { return self.process(event); } diff --git a/packages/integrations/src/transaction.ts b/packages/integrations/src/transaction.ts index e6d0ac24f770..fceae5c82bc0 100644 --- a/packages/integrations/src/transaction.ts +++ b/packages/integrations/src/transaction.ts @@ -1,4 +1,5 @@ import { Event, EventProcessor, Hub, Integration, StackFrame } from '@sentry/types'; +import { getIntegration } from '@sentry/hub'; /** Add node transaction to the event */ export class Transaction implements Integration { @@ -17,7 +18,7 @@ export class Transaction implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(event => { - const self = getCurrentHub().getIntegration(Transaction); + const self = getIntegration(getCurrentHub(), Transaction); if (self) { return self.process(event); } diff --git a/packages/node/src/eventbuilder.ts b/packages/node/src/eventbuilder.ts index 782bd7494e20..3c9cf2130b6c 100644 --- a/packages/node/src/eventbuilder.ts +++ b/packages/node/src/eventbuilder.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/hub'; +import { getCurrentHub, configureScope } from '@sentry/hub'; import { Event, EventHint, Mechanism, Options, SeverityLevel } from '@sentry/types'; import { addExceptionMechanism, @@ -32,7 +32,7 @@ export function eventFromException(options: Options, exception: unknown, hint?: // which is much better than creating new group when any key/value change const message = `Non-Error exception captured with keys: ${extractExceptionKeysForMessage(exception)}`; - getCurrentHub().configureScope(scope => { + configureScope(getCurrentHub(), scope => { scope.setExtra('__serialized__', normalizeToSize(exception as Record)); }); diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 64aed7f68c34..2aea777fa062 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { captureException, getCurrentHub, startTransaction, withScope } from '@sentry/core'; -import { getClient, getSession } from '@sentry/hub'; +import { configureScope, getClient, getSession } from '@sentry/hub'; import { extractTraceparentData, Span } from '@sentry/tracing'; import { Event, ExtractedNodeRequestData, Transaction } from '@sentry/types'; import { isPlainObject, isString, logger, normalize, stripUrlQueryAndFragment } from '@sentry/utils'; @@ -71,7 +71,7 @@ export function tracingHandler(): ( ); // We put the transaction on the scope so users can attach children to it - getCurrentHub().configureScope(scope => { + configureScope(getCurrentHub(), scope => { scope.setSpan(transaction); }); @@ -418,7 +418,7 @@ export function requestHandler( local.run(() => { const currentHub = getCurrentHub(); - currentHub.configureScope(scope => { + configureScope(currentHub, scope => { scope.addEventProcessor((event: Event) => parseRequest(event, req, options)); const client = getClient(currentHub); if (isAutoSessionTrackingEnabled(client)) { diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 6307f6008fc1..632af49574b0 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -12,6 +12,7 @@ import { RequestMethod, RequestMethodArgs, } from './utils/http'; +import { addBreadcrumb, getIntegration } from '@sentry/hub'; const NODE_VERSION = parseSemver(process.versions.node); @@ -163,11 +164,12 @@ function _createWrappedRequestMethodFactory( * Captures Breadcrumb based on provided request/response pair */ function addRequestBreadcrumb(event: string, url: string, req: http.ClientRequest, res?: http.IncomingMessage): void { - if (!getCurrentHub().getIntegration(Http)) { + if (!getIntegration(getCurrentHub(), Http)) { return; } - getCurrentHub().addBreadcrumb( + addBreadcrumb( + getCurrentHub(), { category: 'http', data: { diff --git a/packages/node/src/integrations/linkederrors.ts b/packages/node/src/integrations/linkederrors.ts index 0b70aad73360..e969cc923d81 100644 --- a/packages/node/src/integrations/linkederrors.ts +++ b/packages/node/src/integrations/linkederrors.ts @@ -3,6 +3,7 @@ import { Event, EventHint, Exception, ExtendedError, Integration } from '@sentry import { isInstanceOf, resolvedSyncPromise, SyncPromise } from '@sentry/utils'; import { getExceptionFromError } from '../parsers'; +import { getIntegration } from '@sentry/hub'; const DEFAULT_KEY = 'cause'; const DEFAULT_LIMIT = 5; @@ -42,7 +43,7 @@ export class LinkedErrors implements Integration { */ public setupOnce(): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getCurrentHub().getIntegration(LinkedErrors); + const self = getIntegration(getCurrentHub(), LinkedErrors); if (self) { const handler = self._handler && self._handler.bind(self); return typeof handler === 'function' ? handler(event, hint) : event; diff --git a/packages/node/src/integrations/modules.ts b/packages/node/src/integrations/modules.ts index df8bcc16d261..ce0bd1f84063 100644 --- a/packages/node/src/integrations/modules.ts +++ b/packages/node/src/integrations/modules.ts @@ -1,6 +1,7 @@ import { EventProcessor, Hub, Integration } from '@sentry/types'; import { existsSync, readFileSync } from 'fs'; import { dirname, join } from 'path'; +import { getIntegration } from '@sentry/hub'; let moduleCache: { [key: string]: string }; @@ -82,7 +83,7 @@ export class Modules implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(event => { - if (!getCurrentHub().getIntegration(Modules)) { + if (!getIntegration(getCurrentHub(), Modules)) { return event; } return { diff --git a/packages/node/src/integrations/onuncaughtexception.ts b/packages/node/src/integrations/onuncaughtexception.ts index ad1b4e8f208e..ab3a1078d2ee 100644 --- a/packages/node/src/integrations/onuncaughtexception.ts +++ b/packages/node/src/integrations/onuncaughtexception.ts @@ -1,5 +1,5 @@ import { getCurrentHub, Scope } from '@sentry/core'; -import { getClient, withScope } from '@sentry/hub'; +import { getClient, getIntegration, withScope } from '@sentry/hub'; import { Integration } from '@sentry/types'; import { logger } from '@sentry/utils'; @@ -76,7 +76,7 @@ export class OnUncaughtException implements Integration { firstError = error; caughtFirstError = true; - if (hub.getIntegration(OnUncaughtException)) { + if (getIntegration(hub, OnUncaughtException)) { withScope(hub, (scope: Scope) => { scope.setLevel('fatal'); hub.captureException(error, { diff --git a/packages/node/src/integrations/onunhandledrejection.ts b/packages/node/src/integrations/onunhandledrejection.ts index 17cec1886ea5..c2a86125634e 100644 --- a/packages/node/src/integrations/onunhandledrejection.ts +++ b/packages/node/src/integrations/onunhandledrejection.ts @@ -1,5 +1,5 @@ import { getCurrentHub, Scope } from '@sentry/core'; -import { withScope } from '@sentry/hub'; +import { getIntegration, withScope } from '@sentry/hub'; import { Integration } from '@sentry/types'; import { consoleSandbox } from '@sentry/utils'; @@ -48,7 +48,7 @@ export class OnUnhandledRejection implements Integration { public sendUnhandledPromise(reason: any, promise: any): void { const hub = getCurrentHub(); - if (!hub.getIntegration(OnUnhandledRejection)) { + if (!getIntegration(hub, OnUnhandledRejection)) { this._handleRejection(reason); return; } diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 3b881501eb35..168da7b08b32 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,5 +1,12 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; -import { getClient, getMainCarrier, getSession, lastEventId as hubLastEventId, setHubOnCarrier } from '@sentry/hub'; +import { + getClient, + getMainCarrier, + startSession, + getSession, + lastEventId as hubLastEventId, + setHubOnCarrier, +} from '@sentry/hub'; import { SessionStatus } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; import * as domain from 'domain'; @@ -225,7 +232,7 @@ export function getSentryRelease(fallback?: string): string | undefined { */ function startSessionTracking(): void { const hub = getCurrentHub(); - hub.startSession(); + startSession(hub); // Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because // The 'beforeExit' event is not emitted for conditions causing explicit termination, // such as calling process.exit() or uncaught exceptions. diff --git a/packages/types/src/hub.ts b/packages/types/src/hub.ts index a0eb7544ce07..2ce9790fd537 100644 --- a/packages/types/src/hub.ts +++ b/packages/types/src/hub.ts @@ -1,151 +1,5 @@ -import { Breadcrumb, BreadcrumbHint } from './breadcrumb'; -import { Event, EventHint } from './event'; -import { Extra, Extras } from './extra'; -import { Integration, IntegrationClass } from './integration'; -import { Primitive } from './misc'; -import { Scope } from './scope'; -import { Session, SessionContext } from './session'; -import { SeverityLevel } from './severity'; -import { Span, SpanContext } from './span'; -import { CustomSamplingContext, Transaction, TransactionContext } from './transaction'; - /** * Internal class used to make sure we always have the latest internal functions * working in case we have a version conflict. */ -export interface Hub { - /** - * Captures a message event and sends it to Sentry. - * - * @param message The message to send to Sentry. - * @param level Define the level of the message. - * @param hint May contain additional information about the original exception. - * @returns The generated eventId. - */ - captureMessage(message: string, level?: SeverityLevel, hint?: EventHint): string; - - /** - * Captures a manually created event and sends it to Sentry. - * - * @param event The event to send to Sentry. - * @param hint May contain additional information about the original exception. - */ - captureEvent(event: Event, hint?: EventHint): string; - - /** - * Records a new breadcrumb which will be attached to future events. - * - * Breadcrumbs will be added to subsequent events to provide more context on - * user's actions prior to an error or crash. - * - * @param breadcrumb The breadcrumb to record. - * @param hint May contain additional information about the original breadcrumb. - */ - addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void; - - /** - * Set an object that will be merged sent as tags data with the event. - * - * @param tags Tags context object to merge into current context. - */ - setTags(tags: { [key: string]: Primitive }): void; - - /** - * Set key:value that will be sent as tags data with the event. - * - * Can also be used to unset a tag, by passing `undefined`. - * - * @param key String key of tag - * @param value Value of tag - */ - setTag(key: string, value: Primitive): void; - - /** - * Set key:value that will be sent as extra data with the event. - * @param key String of extra - * @param extra Any kind of data. This data will be normalized. - */ - setExtra(key: string, extra: Extra): void; - - /** - * Set an object that will be merged sent as extra data with the event. - * @param extras Extras object to merge into current context. - */ - setExtras(extras: Extras): void; - - /** - * Sets context data with the given name. - * @param name of the context - * @param context Any kind of data. This data will be normalized. - */ - setContext(name: string, context: { [key: string]: any } | null): void; - - /** - * Callback to set context information onto the scope. - * - * @param callback Callback function that receives Scope. - */ - configureScope(callback: (scope: Scope) => void): void; - - /** - * For the duration of the callback, this hub will be set as the global current Hub. - * This function is useful if you want to run your own client and hook into an already initialized one - * e.g.: Reporting issues to your own sentry when running in your component while still using the users configuration. - */ - run(callback: (hub: Hub) => void): void; - - /** Returns the integration if installed on the current client. */ - getIntegration(integration: IntegrationClass): T | null; - - /** Returns all trace headers that are currently on the top scope. */ - traceHeaders(): { [key: string]: string }; - - /** - * @deprecated No longer does anything. Use use {@link Transaction.startChild} instead. - */ - startSpan(context: SpanContext): Span; - - /** - * Starts a new `Transaction` and returns it. This is the entry point to manual tracing instrumentation. - * - * A tree structure can be built by adding child spans to the transaction, and child spans to other spans. To start a - * new child span within the transaction or any span, call the respective `.startChild()` method. - * - * Every child span must be finished before the transaction is finished, otherwise the unfinished spans are discarded. - * - * The transaction must be finished with a call to its `.finish()` method, at which point the transaction with all its - * finished child spans will be sent to Sentry. - * - * @param context Properties of the new `Transaction`. - * @param customSamplingContext Information given to the transaction sampling function (along with context-dependent - * default values). See {@link Options.tracesSampler}. - * - * @returns The transaction which was just started - */ - startTransaction(context: TransactionContext, customSamplingContext?: CustomSamplingContext): Transaction; - - /** - * Starts a new `Session`, sets on the current scope and returns it. - * - * To finish a `session`, it has to be passed directly to `client.captureSession`, which is done automatically - * when using `hub.endSession()` for the session currently stored on the scope. - * - * When there's already an existing session on the scope, it'll be automatically ended. - * - * @param context Optional properties of the new `Session`. - * - * @returns The session which was just started - */ - startSession(context?: SessionContext): Session; - - /** - * Ends the session that lives on the current scope and sends it to Sentry - */ - endSession(): void; - - /** - * Sends the current session on the scope to Sentry - * @param endSession If set the session will be marked as exited and removed from the scope - */ - captureSession(endSession?: boolean): void; -} +export interface Hub {} From 9fc028cd2ad0041203679bb6eab1ebf37eadca90 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 11 Jan 2022 15:38:43 -0500 Subject: [PATCH 16/37] Update packages/integrations/src/angular.ts --- packages/integrations/src/angular.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integrations/src/angular.ts b/packages/integrations/src/angular.ts index 1ccec934d29e..98cd05de5531 100644 --- a/packages/integrations/src/angular.ts +++ b/packages/integrations/src/angular.ts @@ -91,7 +91,7 @@ export class Angular implements Integration { return (exception: Error, cause?: string): void => { const hub = this._getCurrentHub && this._getCurrentHub(); - if (hub && getIntegration(hub, Angular)) { + if (hub && getIntegration(hub, Angular)) { withScope(hub, scope => { if (cause) { scope.setExtra('cause', cause); From 712ebb8570b9b8bc034a7d3e01298d7ff765b5b6 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 23:06:27 -0500 Subject: [PATCH 17/37] chore: make scope functional --- packages/core/src/baseclient.ts | 6 +- packages/hub/src/index.ts | 4 +- packages/hub/src/scope.ts | 740 +++++++++++++++++--------------- packages/hub/src/session.ts | 198 ++++----- packages/types/src/scope.ts | 139 +----- packages/types/src/session.ts | 26 +- 6 files changed, 495 insertions(+), 618 deletions(-) diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index cf53ec767f5c..96a13cf96e6f 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { Scope, Session, cloneScope, getSession } from '@sentry/hub'; +import { Scope, Session, updateSession, cloneScope, getSession } from '@sentry/hub'; import { Client, DsnComponents, @@ -186,7 +186,7 @@ export abstract class BaseClient implement } else { this._sendSession(session); // After sending, we set init false to indicate it's not the first occurrence - session.update({ init: false }); + updateSession(session, { init: false }); } } @@ -278,7 +278,7 @@ export abstract class BaseClient implement const shouldUpdateAndSend = (sessionNonTerminal && session.errors === 0) || (sessionNonTerminal && crashed); if (shouldUpdateAndSend) { - session.update({ + updateSession(session, { ...(crashed && { status: 'crashed' }), errors: session.errors || Number(errored || crashed), }); diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index ed8a0bdab0f7..cb3501654158 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,5 +1,5 @@ -export { addGlobalEventProcessor, cloneScope, getSession, Scope } from './scope'; -export { Session } from './session'; +export { applyToEvent, addGlobalEventProcessor, cloneScope, getSession, Scope } from './scope'; +export { Session, updateSession } from './session'; export { SessionFlusher } from './sessionflusher'; export { // eslint-disable-next-line deprecation/deprecation diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index da80c74ce303..ae8768777b7a 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -11,7 +11,6 @@ import { Extras, Primitive, RequestSession, - Scope as ScopeInterface, ScopeContext, SeverityLevel, Span, @@ -20,7 +19,7 @@ import { } from '@sentry/types'; import { dateTimestampInSeconds, getGlobalObject, isPlainObject, isThenable, SyncPromise } from '@sentry/utils'; -import { Session } from './session'; +import { Session, updateSession } from './session'; /** * Absolute maximum number of breadcrumbs added to an event. @@ -59,10 +58,10 @@ export function getSession(scope?: Scope): Session | undefined { } /** - * Holds additional event information. {@link Scope.applyToEvent} will be + * Holds additional event information. {@link applyToEvent} will be * called by the client before an event will be sent. */ -export class Scope implements ScopeInterface { +export class Scope { /** Flag if notifying is happening. */ public _notifyingListeners: boolean = false; @@ -104,411 +103,442 @@ export class Scope implements ScopeInterface { /** Request Mode Session Status */ public _requestSession?: RequestSession; +} - /** - * Add internal on change listener. Used for sub SDKs that need to store the scope. - * @hidden - */ - public addScopeListener(callback: (scope: Scope) => void): void { - this._scopeListeners.push(callback); - } +/** + * Add internal on change listener. Used for sub SDKs that need to store the scope. + * @hidden + */ +export function addScopeListener(scope: Scope, callback: (scope: Scope) => void): Scope { + scope._scopeListeners.push(callback); + return scope; +} - /** - * @inheritDoc - */ - public addEventProcessor(callback: EventProcessor): this { - this._eventProcessors.push(callback); - return this; - } +/** Add new event processor that will be called after {@link applyToEvent}. */ +export function addEventProcessor(scope: Scope, callback: EventProcessor): Scope { + scope._eventProcessors.push(callback); + return scope; +} - /** - * @inheritDoc - */ - public setUser(user: User | null): this { - this._user = user || {}; - if (this._session) { - this._session.update({ user }); - } - this._notifyScopeListeners(); - return this; - } +/** + * Set key:value that will be sent as tags data with the event. + * + * Can also be used to unset a tag by passing `undefined`. + * + * @param scope + * @param key String key of tag + * @param value Value of tag + */ +export function setTag(scope: Scope, key: string, value: Primitive): Scope { + scope._tags = { ...scope._tags, [key]: value }; + notifyScopeListeners(scope); + return scope; +} - /** - * @inheritDoc - */ - public getUser(): User | undefined { - return this._user; - } +/** + * Set an object that will be merged sent as extra data with the event. + * @param scope + * @param extras Extras object to merge into current context. + */ +export function setExtras(scope: Scope, extras: Extras): Scope { + scope._extra = { + ...scope._extra, + ...extras, + }; + notifyScopeListeners(scope); + return scope; +} - /** - * @inheritDoc - */ - public getRequestSession(): RequestSession | undefined { - return this._requestSession; - } +/** + * Set key:value that will be sent as extra data with the event. + * @param scope + * @param key String of extra + * @param extra Any kind of data. This data will be normalized. + */ +export function setExtra(scope: Scope, key: string, extra: Extra): Scope { + scope._extra = { ...scope._extra, [key]: extra }; + notifyScopeListeners(scope); + return scope; +} - /** - * @inheritDoc - */ - public setRequestSession(requestSession?: RequestSession): this { - this._requestSession = requestSession; - return this; - } +/** + * Sets the fingerprint on the scope to send with the events. + * @param scope + * @param fingerprint string[] to group events in Sentry. + */ +export function setFingerprint(scope: Scope, fingerprint: string[]): Scope { + scope._fingerprint = fingerprint; + notifyScopeListeners(scope); + return scope; +} - /** - * @inheritDoc - */ - public setTags(tags: { [key: string]: Primitive }): this { - this._tags = { - ...this._tags, - ...tags, - }; - this._notifyScopeListeners(); - return this; - } +/** + * Sets the level on the scope for future events. + * @param scope + * @param level string {@link Severity} + */ +export function setLevel(scope: Scope, level: SeverityLevel): Scope { + scope._level = level; + notifyScopeListeners(scope); + return scope; +} - /** - * @inheritDoc - */ - public setTag(key: string, value: Primitive): this { - this._tags = { ...this._tags, [key]: value }; - this._notifyScopeListeners(); - return this; - } +/** + * Sets the transaction name on the scope for future events. + */ +export function setTransactionName(scope: Scope, name?: string): Scope { + scope._transactionName = name; + notifyScopeListeners(scope); + return scope; +} - /** - * @inheritDoc - */ - public setExtras(extras: Extras): this { - this._extra = { - ...this._extra, - ...extras, - }; - this._notifyScopeListeners(); - return this; - } +/** + * Sets the transaction name on the scope for future events. + */ +export function setTransaction(scope: Scope, name?: string): Scope { + return setTransactionName(scope, name); +} - /** - * @inheritDoc - */ - public setExtra(key: string, extra: Extra): this { - this._extra = { ...this._extra, [key]: extra }; - this._notifyScopeListeners(); - return this; +/** + * Sets context data with the given name. + * @param scope + * @param key + * @param context an object containing context data. This data will be normalized. Pass `null` to unset the context. + */ +export function setContext(scope: Scope, key: string, context: Context | null): Scope { + if (context === null) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete scope._contexts[key]; + } else { + scope._contexts = { ...scope._contexts, [key]: context }; } - /** - * @inheritDoc - */ - public setFingerprint(fingerprint: string[]): this { - this._fingerprint = fingerprint; - this._notifyScopeListeners(); - return this; - } + notifyScopeListeners(scope); + return scope; +} - /** - * @inheritDoc - */ - public setLevel(level: SeverityLevel): this { - this._level = level; - this._notifyScopeListeners(); - return this; - } +/** + * Sets the Span on the scope. + * @param scope + * @param span Span + */ +export function setSpan(scope: Scope, span?: Span): Scope { + scope._span = span; + notifyScopeListeners(scope); + return scope; +} + +/** + * @inheritDoc + */ +export function getSpan(scope: Scope): Span | undefined { + return scope._span; +} - /** - * @inheritDoc - */ - public setTransactionName(name?: string): this { - this._transactionName = name; - this._notifyScopeListeners(); - return this; +/** + * Returns the `Transaction` attached to the scope (if there is one) + */ +export function getTransaction(scope: Scope): Transaction | undefined { + // often, this span will be a transaction, but it's not guaranteed to be + const span = getSpan(scope) as undefined | (Span & { spanRecorder: { spans: Span[] } }); + + // try it the new way first + if (span && span.transaction) { + return span.transaction; } - /** - * Can be removed in major version. - * @deprecated in favor of {@link this.setTransactionName} - */ - public setTransaction(name?: string): this { - return this.setTransactionName(name); + // fallback to the old way (known bug: this only finds transactions with sampled = true) + if (span && span.spanRecorder && span.spanRecorder.spans[0]) { + return span.spanRecorder.spans[0] as Transaction; } - /** - * @inheritDoc - */ - public setContext(key: string, context: Context | null): this { - if (context === null) { - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete this._contexts[key]; - } else { - this._contexts = { ...this._contexts, [key]: context }; - } + // neither way found a transaction + return undefined; +} - this._notifyScopeListeners(); - return this; +/** + * Updates user context information for future events. + * + * @param scope + * @param user User context object to be set in the current context. Pass `null` to unset the user. + */ +export function setUser(scope: Scope, user: User | null): Scope { + scope._user = user || {}; + if (scope._session) { + updateSession(scope._session, { user }); } + notifyScopeListeners(scope); + return scope; +} - /** - * @inheritDoc - */ - public setSpan(span?: Span): this { - this._span = span; - this._notifyScopeListeners(); - return this; - } +/** + * Returns the `User` if there is one + */ +export function getUser(scope: Scope): User | undefined { + return scope._user; +} - /** - * @inheritDoc - */ - public getSpan(): Span | undefined { - return this._span; - } +/** + * Returns the `RequestSession` if there is one + */ +export function getRequestSession(scope: Scope): RequestSession | undefined { + return scope._requestSession; +} - /** - * @inheritDoc - */ - public getTransaction(): Transaction | undefined { - // often, this span will be a transaction, but it's not guaranteed to be - const span = this.getSpan() as undefined | (Span & { spanRecorder: { spans: Span[] } }); +/** + * Set an object that will be merged sent as tags data with the event. + * @param scope + * @param tags Tags context object to merge into current context. + */ +export function setTags(scope: Scope, tags: { [key: string]: Primitive }): Scope { + scope._tags = { + ...scope._tags, + ...tags, + }; + notifyScopeListeners(scope); + return scope; +} - // try it the new way first - if (span && span.transaction) { - return span.transaction; - } +/** + * Sets the `RequestSession` on the scope + */ +export function setRequestSession(scope: Scope, requestSession?: RequestSession): Scope { + scope._requestSession = requestSession; + return scope; +} - // fallback to the old way (known bug: this only finds transactions with sampled = true) - if (span && span.spanRecorder && span.spanRecorder.spans[0]) { - return span.spanRecorder.spans[0] as Transaction; - } +/** + * Sets the `Session` on the scope + */ +export function setSession(scope: Scope, session?: Session): Scope { + if (!session) { + delete scope._session; + } else { + scope._session = session; + } + notifyScopeListeners(scope); + return scope; +} - // neither way found a transaction - return undefined; +/** + * Updates the scope with provided data. Can work in three variations: + * - plain object containing updatable attributes + * - Scope instance that'll extract the attributes from + * - callback function that'll receive the current scope as an argument and allow for modifications + * @param scope + * @param captureContext scope modifier to be used + */ +export function update(scope: Scope, captureContext?: CaptureContext): Scope { + if (!captureContext) { + return scope; } - /** - * @inheritDoc - */ - public setSession(session?: Session): this { - if (!session) { - delete this._session; - } else { - this._session = session; - } - this._notifyScopeListeners(); - return this; + if (typeof captureContext === 'function') { + const updatedScope = (captureContext as (scope: T) => T)(scope); + return updatedScope instanceof Scope ? updatedScope : scope; } - /** - * @inheritDoc - */ - public update(captureContext?: CaptureContext): this { - if (!captureContext) { - return this; + if (captureContext instanceof Scope) { + scope._tags = { ...scope._tags, ...captureContext._tags }; + scope._extra = { ...scope._extra, ...captureContext._extra }; + scope._contexts = { ...scope._contexts, ...captureContext._contexts }; + if (captureContext._user && Object.keys(captureContext._user).length) { + scope._user = captureContext._user; } - - if (typeof captureContext === 'function') { - const updatedScope = (captureContext as (scope: T) => T)(this); - return updatedScope instanceof Scope ? updatedScope : this; + if (captureContext._level) { + scope._level = captureContext._level; } - - if (captureContext instanceof Scope) { - this._tags = { ...this._tags, ...captureContext._tags }; - this._extra = { ...this._extra, ...captureContext._extra }; - this._contexts = { ...this._contexts, ...captureContext._contexts }; - if (captureContext._user && Object.keys(captureContext._user).length) { - this._user = captureContext._user; - } - if (captureContext._level) { - this._level = captureContext._level; - } - if (captureContext._fingerprint) { - this._fingerprint = captureContext._fingerprint; - } - if (captureContext._requestSession) { - this._requestSession = captureContext._requestSession; - } - } else if (isPlainObject(captureContext)) { - // eslint-disable-next-line no-param-reassign - captureContext = captureContext as ScopeContext; - this._tags = { ...this._tags, ...captureContext.tags }; - this._extra = { ...this._extra, ...captureContext.extra }; - this._contexts = { ...this._contexts, ...captureContext.contexts }; - if (captureContext.user) { - this._user = captureContext.user; - } - if (captureContext.level) { - this._level = captureContext.level; - } - if (captureContext.fingerprint) { - this._fingerprint = captureContext.fingerprint; - } - if (captureContext.requestSession) { - this._requestSession = captureContext.requestSession; - } + if (captureContext._fingerprint) { + scope._fingerprint = captureContext._fingerprint; + } + if (captureContext._requestSession) { + scope._requestSession = captureContext._requestSession; + } + } else if (isPlainObject(captureContext)) { + // eslint-disable-next-line no-param-reassign + captureContext = captureContext as ScopeContext; + scope._tags = { ...scope._tags, ...captureContext.tags }; + scope._extra = { ...scope._extra, ...captureContext.extra }; + scope._contexts = { ...scope._contexts, ...captureContext.contexts }; + if (captureContext.user) { + scope._user = captureContext.user; + } + if (captureContext.level) { + scope._level = captureContext.level; + } + if (captureContext.fingerprint) { + scope._fingerprint = captureContext.fingerprint; + } + if (captureContext.requestSession) { + scope._requestSession = captureContext.requestSession; } - - return this; } - /** - * @inheritDoc - */ - public clear(): this { - this._breadcrumbs = []; - this._tags = {}; - this._extra = {}; - this._user = {}; - this._contexts = {}; - this._level = undefined; - this._transactionName = undefined; - this._fingerprint = undefined; - this._requestSession = undefined; - this._span = undefined; - this._session = undefined; - this._notifyScopeListeners(); - return this; + return scope; +} + +/** Clears the current scope and resets its properties. */ +export function clear(scope: Scope): Scope { + scope._breadcrumbs = []; + scope._tags = {}; + scope._extra = {}; + scope._user = {}; + scope._contexts = {}; + scope._level = undefined; + scope._transactionName = undefined; + scope._fingerprint = undefined; + scope._requestSession = undefined; + scope._span = undefined; + scope._session = undefined; + notifyScopeListeners(scope); + return scope; +} + +/** + * Sets the breadcrumbs in the scope + * @param scope + * @param breadcrumb + * @param maxBreadcrumbs number of max breadcrumbs to merged into event. + */ +export function addBreadcrumb(scope: Scope, breadcrumb: Breadcrumb, maxBreadcrumbs?: number): Scope { + const maxCrumbs = typeof maxBreadcrumbs === 'number' ? Math.min(maxBreadcrumbs, MAX_BREADCRUMBS) : MAX_BREADCRUMBS; + + // No data has been changed, so don't notify scope listeners + if (maxCrumbs <= 0) { + return scope; } - /** - * @inheritDoc - */ - public addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): this { - const maxCrumbs = typeof maxBreadcrumbs === 'number' ? Math.min(maxBreadcrumbs, MAX_BREADCRUMBS) : MAX_BREADCRUMBS; + const mergedBreadcrumb = { + timestamp: dateTimestampInSeconds(), + ...breadcrumb, + }; + scope._breadcrumbs = [...scope._breadcrumbs, mergedBreadcrumb].slice(-maxCrumbs); + notifyScopeListeners(scope); - // No data has been changed, so don't notify scope listeners - if (maxCrumbs <= 0) { - return this; - } + return scope; +} - const mergedBreadcrumb = { - timestamp: dateTimestampInSeconds(), - ...breadcrumb, - }; - this._breadcrumbs = [...this._breadcrumbs, mergedBreadcrumb].slice(-maxCrumbs); - this._notifyScopeListeners(); +/** + * Clears all currently set Breadcrumbs. + */ +export function clearBreadcrumbs(scope: Scope): Scope { + scope._breadcrumbs = []; + notifyScopeListeners(scope); + return scope; +} - return this; +/** + * Applies the current context and fingerprint to the event. + * Note that breadcrumbs will be added by the client. + * Also if the event has already breadcrumbs on it, we do not merge them. + * @param scope The Scope to apply the event to. + * @param event Event + * @param hint May contain additional information about the original exception. + * @hidden + */ +export function applyToEvent(scope: Scope, event: Event, hint?: EventHint): PromiseLike { + if (scope._extra && Object.keys(scope._extra).length) { + event.extra = { ...scope._extra, ...event.extra }; } - - /** - * @inheritDoc - */ - public clearBreadcrumbs(): this { - this._breadcrumbs = []; - this._notifyScopeListeners(); - return this; + if (scope._tags && Object.keys(scope._tags).length) { + event.tags = { ...scope._tags, ...event.tags }; } - - /** - * Applies the current context and fingerprint to the event. - * Note that breadcrumbs will be added by the client. - * Also if the event has already breadcrumbs on it, we do not merge them. - * @param event Event - * @param hint May contain additional information about the original exception. - * @hidden - */ - public applyToEvent(event: Event, hint?: EventHint): PromiseLike { - if (this._extra && Object.keys(this._extra).length) { - event.extra = { ...this._extra, ...event.extra }; - } - if (this._tags && Object.keys(this._tags).length) { - event.tags = { ...this._tags, ...event.tags }; - } - if (this._user && Object.keys(this._user).length) { - event.user = { ...this._user, ...event.user }; - } - if (this._contexts && Object.keys(this._contexts).length) { - event.contexts = { ...this._contexts, ...event.contexts }; - } - if (this._level) { - event.level = this._level; - } - if (this._transactionName) { - event.transaction = this._transactionName; - } - // We want to set the trace context for normal events only if there isn't already - // a trace context on the event. There is a product feature in place where we link - // errors with transaction and it relies on that. - if (this._span) { - event.contexts = { trace: this._span.getTraceContext(), ...event.contexts }; - const transactionName = this._span.transaction && this._span.transaction.name; - if (transactionName) { - event.tags = { transaction: transactionName, ...event.tags }; - } + if (scope._user && Object.keys(scope._user).length) { + event.user = { ...scope._user, ...event.user }; + } + if (scope._contexts && Object.keys(scope._contexts).length) { + event.contexts = { ...scope._contexts, ...event.contexts }; + } + if (scope._level) { + event.level = scope._level; + } + if (scope._transactionName) { + event.transaction = scope._transactionName; + } + // We want to set the trace context for normal events only if there isn't already + // a trace context on the event. There is a product feature in place where we link + // errors with transaction and it relies on that. + if (scope._span) { + event.contexts = { trace: scope._span.getTraceContext(), ...event.contexts }; + const transactionName = scope._span.transaction && scope._span.transaction.name; + if (transactionName) { + event.tags = { transaction: transactionName, ...event.tags }; } + } - this._applyFingerprint(event); + applyFingerprint(scope, event); - event.breadcrumbs = [...(event.breadcrumbs || []), ...this._breadcrumbs]; - event.breadcrumbs = event.breadcrumbs.length > 0 ? event.breadcrumbs : undefined; + event.breadcrumbs = [...(event.breadcrumbs || []), ...scope._breadcrumbs]; + event.breadcrumbs = event.breadcrumbs.length > 0 ? event.breadcrumbs : undefined; - return this._notifyEventProcessors([...getGlobalEventProcessors(), ...this._eventProcessors], event, hint); - } + return notifyEventProcessors(scope, [...getGlobalEventProcessors(), ...scope._eventProcessors], event, hint); +} - /** - * This will be called after {@link applyToEvent} is finished. - */ - public _notifyEventProcessors( - processors: EventProcessor[], - event: Event | null, - hint?: EventHint, - index: number = 0, - ): PromiseLike { - return new SyncPromise((resolve, reject) => { - const processor = processors[index]; - if (event === null || typeof processor !== 'function') { - resolve(event); +/** + * This will be called after {@link applyToEvent} is finished. + */ +function notifyEventProcessors( + scope: Scope, + processors: EventProcessor[], + event: Event | null, + hint?: EventHint, + index: number = 0, +): PromiseLike { + return new SyncPromise((resolve, reject) => { + const processor = processors[index]; + if (event === null || typeof processor !== 'function') { + resolve(event); + } else { + const result = processor({ ...event }, hint) as Event | null; + if (isThenable(result)) { + void (result as PromiseLike) + .then(final => notifyEventProcessors(scope, processors, final, hint, index + 1).then(resolve)) + .then(null, reject); } else { - const result = processor({ ...event }, hint) as Event | null; - if (isThenable(result)) { - void (result as PromiseLike) - .then(final => this._notifyEventProcessors(processors, final, hint, index + 1).then(resolve)) - .then(null, reject); - } else { - void this._notifyEventProcessors(processors, result, hint, index + 1) - .then(resolve) - .then(null, reject); - } + void notifyEventProcessors(scope, processors, result, hint, index + 1) + .then(resolve) + .then(null, reject); } + } + }); +} + +/** + * This will be called on every set call. + */ +function notifyScopeListeners(scope: Scope): void { + // We need this check for this._notifyingListeners to be able to work on scope during updates + // If this check is not here we'll produce endless recursion when something is done with the scope + // during the callback. + if (!scope._notifyingListeners) { + scope._notifyingListeners = true; + scope._scopeListeners.forEach(callback => { + callback(scope); }); + scope._notifyingListeners = false; } +} - /** - * This will be called on every set call. - */ - protected _notifyScopeListeners(): void { - // We need this check for this._notifyingListeners to be able to work on scope during updates - // If this check is not here we'll produce endless recursion when something is done with the scope - // during the callback. - if (!this._notifyingListeners) { - this._notifyingListeners = true; - this._scopeListeners.forEach(callback => { - callback(this); - }); - this._notifyingListeners = false; - } +/** + * Applies fingerprint from the scope to the event if there's one, + * uses message if there's one instead or get rid of empty fingerprint + */ +function applyFingerprint(scope: Scope, event: Event): void { + // Make sure it's an array first and we actually have something in place + event.fingerprint = event.fingerprint + ? Array.isArray(event.fingerprint) + ? event.fingerprint + : [event.fingerprint] + : []; + + // If we have something on the scope, then merge it with event + if (scope._fingerprint) { + event.fingerprint = event.fingerprint.concat(scope._fingerprint); } - /** - * Applies fingerprint from the scope to the event if there's one, - * uses message if there's one instead or get rid of empty fingerprint - */ - private _applyFingerprint(event: Event): void { - // Make sure it's an array first and we actually have something in place - event.fingerprint = event.fingerprint - ? Array.isArray(event.fingerprint) - ? event.fingerprint - : [event.fingerprint] - : []; - - // If we have something on the scope, then merge it with event - if (this._fingerprint) { - event.fingerprint = event.fingerprint.concat(this._fingerprint); - } - - // If we have no data at all, remove empty array default - if (event.fingerprint && !event.fingerprint.length) { - delete event.fingerprint; - } + // If we have no data at all, remove empty array default + if (event.fingerprint && !event.fingerprint.length) { + delete event.fingerprint; } } diff --git a/packages/hub/src/session.ts b/packages/hub/src/session.ts index 3206ef9306dc..94d6ab10c75d 100644 --- a/packages/hub/src/session.ts +++ b/packages/hub/src/session.ts @@ -2,7 +2,7 @@ import { Session as SessionInterface, SessionContext, SessionStatus } from '@sen import { dropUndefinedKeys, timestampInSeconds, uuid4 } from '@sentry/utils'; /** - * @inheritdoc + * Session Context */ export class Session implements SessionInterface { public userAgent?: string; @@ -25,112 +25,114 @@ export class Session implements SessionInterface { this.timestamp = startingTime; this.started = startingTime; if (context) { - this.update(context); + updateSession(this, context); } } +} - /** JSDoc */ - // eslint-disable-next-line complexity - public update(context: SessionContext = {}): void { - if (context.user) { - if (!this.ipAddress && context.user.ip_address) { - this.ipAddress = context.user.ip_address; - } - - if (!this.did && !context.did) { - this.did = context.user.id || context.user.email || context.user.username; - } +/** JSDoc */ +// eslint-disable-next-line complexity +export function updateSession(session: Session, context: SessionContext = {}): void { + if (context.user) { + if (!session.ipAddress && context.user.ip_address) { + session.ipAddress = context.user.ip_address; } - this.timestamp = context.timestamp || timestampInSeconds(); - if (context.ignoreDuration) { - this.ignoreDuration = context.ignoreDuration; - } - if (context.sid) { - // Good enough uuid validation. — Kamil - this.sid = context.sid.length === 32 ? context.sid : uuid4(); - } - if (context.init !== undefined) { - this.init = context.init; - } - if (!this.did && context.did) { - this.did = `${context.did}`; - } - if (typeof context.started === 'number') { - this.started = context.started; - } - if (this.ignoreDuration) { - this.duration = undefined; - } else if (typeof context.duration === 'number') { - this.duration = context.duration; - } else { - const duration = this.timestamp - this.started; - this.duration = duration >= 0 ? duration : 0; - } - if (context.release) { - this.release = context.release; - } - if (context.environment) { - this.environment = context.environment; - } - if (!this.ipAddress && context.ipAddress) { - this.ipAddress = context.ipAddress; - } - if (!this.userAgent && context.userAgent) { - this.userAgent = context.userAgent; - } - if (typeof context.errors === 'number') { - this.errors = context.errors; - } - if (context.status) { - this.status = context.status; + if (!session.did && !context.did) { + session.did = context.user.id || context.user.email || context.user.username; } } - /** JSDoc */ - public close(status?: Exclude): void { - if (status) { - this.update({ status }); - } else if (this.status === 'ok') { - this.update({ status: 'exited' }); - } else { - this.update(); - } + session.timestamp = context.timestamp || timestampInSeconds(); + if (context.ignoreDuration) { + session.ignoreDuration = context.ignoreDuration; } + if (context.sid) { + // Good enough uuid validation. — Kamil + session.sid = context.sid.length === 32 ? context.sid : uuid4(); + } + if (context.init !== undefined) { + session.init = context.init; + } + if (!session.did && context.did) { + session.did = `${context.did}`; + } + if (typeof context.started === 'number') { + session.started = context.started; + } + if (session.ignoreDuration) { + session.duration = undefined; + } else if (typeof context.duration === 'number') { + session.duration = context.duration; + } else { + const duration = session.timestamp - session.started; + session.duration = duration >= 0 ? duration : 0; + } + if (context.release) { + session.release = context.release; + } + if (context.environment) { + session.environment = context.environment; + } + if (!session.ipAddress && context.ipAddress) { + session.ipAddress = context.ipAddress; + } + if (!session.userAgent && context.userAgent) { + session.userAgent = context.userAgent; + } + if (typeof context.errors === 'number') { + session.errors = context.errors; + } + if (context.status) { + session.status = context.status; + } +} - /** JSDoc */ - public toJSON(): { - init: boolean; - sid: string; - did?: string; - timestamp: string; - started: string; - duration?: number; - status: SessionStatus; - errors: number; - attrs?: { - release?: string; - environment?: string; - user_agent?: string; - ip_address?: string; - }; - } { - return dropUndefinedKeys({ - sid: `${this.sid}`, - init: this.init, - // Make sure that sec is converted to ms for date constructor - started: new Date(this.started * 1000).toISOString(), - timestamp: new Date(this.timestamp * 1000).toISOString(), - status: this.status, - errors: this.errors, - did: typeof this.did === 'number' || typeof this.did === 'string' ? `${this.did}` : undefined, - duration: this.duration, - attrs: { - release: this.release, - environment: this.environment, - ip_address: this.ipAddress, - user_agent: this.userAgent, - }, - }); +/** JSDoc */ +export function closeSession(session: Session, status?: Exclude): void { + if (status) { + updateSession(session, { status }); + } else if (session.status === 'ok') { + updateSession(session, { status: 'exited' }); + } else { + updateSession(session); } } + +/** JSDoc */ +export function sessionAsJSON( + session: Session, +): { + init: boolean; + sid: string; + did?: string; + timestamp: string; + started: string; + duration?: number; + status: SessionStatus; + errors: number; + attrs?: { + release?: string; + environment?: string; + user_agent?: string; + ip_address?: string; + }; +} { + return dropUndefinedKeys({ + sid: `${session.sid}`, + init: session.init, + // Make sure that sec is converted to ms for date constructor + started: new Date(session.started * 1000).toISOString(), + timestamp: new Date(session.timestamp * 1000).toISOString(), + status: session.status, + errors: session.errors, + did: typeof session.did === 'number' || typeof session.did === 'string' ? `${session.did}` : undefined, + duration: session.duration, + attrs: { + release: session.release, + environment: session.environment, + ip_address: session.ipAddress, + user_agent: session.userAgent, + }, + }); +} diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index d65d3a61a38f..d1b53d3b2606 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -1,12 +1,8 @@ -import { Breadcrumb } from './breadcrumb'; -import { Context, Contexts } from './context'; -import { EventProcessor } from './eventprocessor'; -import { Extra, Extras } from './extra'; +import { Contexts } from './context'; +import { Extras } from './extra'; import { Primitive } from './misc'; -import { RequestSession, Session } from './session'; +import { RequestSession } from './session'; import { SeverityLevel } from './severity'; -import { Span } from './span'; -import { Transaction } from './transaction'; import { User } from './user'; /** JSDocs */ @@ -23,131 +19,4 @@ export interface ScopeContext { requestSession: RequestSession; } -/** - * Holds additional event information. {@link Scope.applyToEvent} will be - * called by the client before an event will be sent. - */ -export interface Scope { - /** Add new event processor that will be called after {@link applyToEvent}. */ - addEventProcessor(callback: EventProcessor): this; - - /** - * Updates user context information for future events. - * - * @param user User context object to be set in the current context. Pass `null` to unset the user. - */ - setUser(user: User | null): this; - - /** - * Returns the `User` if there is one - */ - getUser(): User | undefined; - - /** - * Set an object that will be merged sent as tags data with the event. - * @param tags Tags context object to merge into current context. - */ - setTags(tags: { [key: string]: Primitive }): this; - - /** - * Set key:value that will be sent as tags data with the event. - * - * Can also be used to unset a tag by passing `undefined`. - * - * @param key String key of tag - * @param value Value of tag - */ - setTag(key: string, value: Primitive): this; - - /** - * Set an object that will be merged sent as extra data with the event. - * @param extras Extras object to merge into current context. - */ - setExtras(extras: Extras): this; - - /** - * Set key:value that will be sent as extra data with the event. - * @param key String of extra - * @param extra Any kind of data. This data will be normalized. - */ - setExtra(key: string, extra: Extra): this; - - /** - * Sets the fingerprint on the scope to send with the events. - * @param fingerprint string[] to group events in Sentry. - */ - setFingerprint(fingerprint: string[]): this; - - /** - * Sets the level on the scope for future events. - * @param level string {@link Severity} - */ - setLevel(level: SeverityLevel): this; - - /** - * Sets the transaction name on the scope for future events. - */ - setTransactionName(name?: string): this; - - /** - * Sets context data with the given name. - * @param name of the context - * @param context an object containing context data. This data will be normalized. Pass `null` to unset the context. - */ - setContext(name: string, context: Context | null): this; - - /** - * Sets the Span on the scope. - * @param span Span - */ - setSpan(span?: Span): this; - - /** - * Returns the `Span` if there is one - */ - getSpan(): Span | undefined; - - /** - * Returns the `Transaction` attached to the scope (if there is one) - */ - getTransaction(): Transaction | undefined; - - /** - * Sets the `Session` on the scope - */ - setSession(session?: Session): this; - - /** - * Returns the `RequestSession` if there is one - */ - getRequestSession(): RequestSession | undefined; - - /** - * Sets the `RequestSession` on the scope - */ - setRequestSession(requestSession?: RequestSession): this; - - /** - * Updates the scope with provided data. Can work in three variations: - * - plain object containing updatable attributes - * - Scope instance that'll extract the attributes from - * - callback function that'll receive the current scope as an argument and allow for modifications - * @param captureContext scope modifier to be used - */ - update(captureContext?: CaptureContext): this; - - /** Clears the current scope and resets its properties. */ - clear(): this; - - /** - * Sets the breadcrumbs in the scope - * @param breadcrumbs Breadcrumb - * @param maxBreadcrumbs number of max breadcrumbs to merged into event. - */ - addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): this; - - /** - * Clears all currently set Breadcrumbs. - */ - clearBreadcrumbs(): this; -} +export interface Scope {} diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 552dda002531..71c108ab7556 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -3,31 +3,7 @@ import { User } from './user'; /** * @inheritdoc */ -export interface Session extends SessionContext { - /** JSDoc */ - update(context?: SessionContext): void; - - /** JSDoc */ - close(status?: SessionStatus): void; - - /** JSDoc */ - toJSON(): { - init: boolean; - sid: string; - did?: string; - timestamp: string; - started: string; - duration?: number; - status: SessionStatus; - errors: number; - attrs?: { - release?: string; - environment?: string; - user_agent?: string; - ip_address?: string; - }; - }; -} +export interface Session extends SessionContext {} export interface RequestSession { status?: RequestSessionStatus; From 798210a6b529558d34090db4642682fca08861ef Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 23:06:58 -0500 Subject: [PATCH 18/37] chore: reorder code --- packages/hub/src/scope.ts | 60 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index ae8768777b7a..33e86079ed44 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -27,36 +27,6 @@ import { Session, updateSession } from './session'; */ const MAX_BREADCRUMBS = 100; -/** - * Inherit values from the parent scope. - * @param scope to clone. - */ -export function cloneScope(scope?: Scope): Scope { - const newScope = new Scope(); - if (scope) { - newScope._breadcrumbs = [...scope._breadcrumbs]; - newScope._tags = { ...scope._tags }; - newScope._extra = { ...scope._extra }; - newScope._contexts = { ...scope._contexts }; - newScope._user = scope._user; - newScope._level = scope._level; - newScope._span = scope._span; - newScope._session = scope._session; - newScope._transactionName = scope._transactionName; - newScope._fingerprint = scope._fingerprint; - newScope._eventProcessors = [...scope._eventProcessors]; - newScope._requestSession = scope._requestSession; - } - return newScope; -} - -/** - * Returns the `Session` if there is one - */ -export function getSession(scope?: Scope): Session | undefined { - return scope && scope._session; -} - /** * Holds additional event information. {@link applyToEvent} will be * called by the client before an event will be sent. @@ -105,6 +75,36 @@ export class Scope { public _requestSession?: RequestSession; } +/** + * Inherit values from the parent scope. + * @param scope to clone. + */ +export function cloneScope(scope?: Scope): Scope { + const newScope = new Scope(); + if (scope) { + newScope._breadcrumbs = [...scope._breadcrumbs]; + newScope._tags = { ...scope._tags }; + newScope._extra = { ...scope._extra }; + newScope._contexts = { ...scope._contexts }; + newScope._user = scope._user; + newScope._level = scope._level; + newScope._span = scope._span; + newScope._session = scope._session; + newScope._transactionName = scope._transactionName; + newScope._fingerprint = scope._fingerprint; + newScope._eventProcessors = [...scope._eventProcessors]; + newScope._requestSession = scope._requestSession; + } + return newScope; +} + +/** + * Returns the `Session` if there is one + */ +export function getSession(scope?: Scope): Session | undefined { + return scope && scope._session; +} + /** * Add internal on change listener. Used for sub SDKs that need to store the scope. * @hidden From db6dde60bc3df2fb5d772cb5e5f28911d365834c Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 23:25:53 -0500 Subject: [PATCH 19/37] chore: fix compiler --- packages/hub/src/hub.ts | 54 ++++++--- packages/hub/src/scope.ts | 18 +-- packages/hub/src/session.ts | 4 +- packages/hub/src/sessionflusher.ts | 188 +++++++++++++++-------------- packages/types/src/session.ts | 22 ---- 5 files changed, 143 insertions(+), 143 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 9cfec1b264a3..eefc3b887ee5 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -20,8 +20,21 @@ import { } from '@sentry/types'; import { consoleSandbox, dateTimestampInSeconds, getGlobalObject, isNodeEnv, logger, uuid4 } from '@sentry/utils'; -import { cloneScope, getSession, Scope } from './scope'; -import { Session } from './session'; +import { + addBreadcrumbScope, + cloneScope, + getSession, + getUserScope, + Scope, + setContextScope, + setExtraScope, + setExtrasScope, + setSessionScope, + setTagScope, + setTagsScope, + setUserScope, +} from './scope'; +import { closeSession, Session, updateSession } from './session'; /** * API compatibility version of this hub. @@ -219,7 +232,7 @@ export function getClient(hub: Hub): C | undefined { */ export function setUser(hub: Hub, user: User | null): void { const scope = getScope(hub); - if (scope) scope.setUser(user); + if (scope) setUserScope(scope, user); } /** Returns the scope of the top stack. */ @@ -258,7 +271,7 @@ function _sendSessionUpdate(hub: Hub): void { const { scope, client } = getStackTop(hub); if (!scope) return; - const session = getSession(scope) + const session = getSession(scope); if (session) { if (client && client.captureSession) { client.captureSession(session); @@ -274,14 +287,14 @@ function endSession(hub: Hub): void { const scope = layer && layer.scope; const session = getSession(scope); if (session) { - session.close(); + closeSession(session); } _sendSessionUpdate(hub); // the session is over; take it off of the scope if (scope) { - scope.setSession(); + setSessionScope(scope); } } @@ -297,6 +310,7 @@ function endSession(hub: Hub): void { * @param context Optional properties of the new `Session`. * * @returns The session which was just started + * */ export function startSession(hub: Hub, context?: SessionContext): Session { const { scope, client } = getStackTop(hub); @@ -309,7 +323,7 @@ export function startSession(hub: Hub, context?: SessionContext): Session { const session = new Session({ release, environment, - ...(scope && { user: scope.getUser() }), + ...(scope && { user: getUserScope(scope) }), ...(userAgent && { userAgent }), ...context, }); @@ -318,12 +332,12 @@ export function startSession(hub: Hub, context?: SessionContext): Session { // End existing session if there's one const currentSession = getSession(scope); if (currentSession && currentSession.status === 'ok') { - currentSession.update({ status: 'exited' }); + updateSession(currentSession, { status: 'exited' }); } endSession(hub); // Afterwards we set the new session on the scope - scope.setSession(session); + setSessionScope(scope, session); } return session; @@ -438,9 +452,9 @@ export function addBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: Breadcrum if (!scope || !client) return; -// eslint-disable-next-line @typescript-eslint/unbound-method + // eslint-disable-next-line @typescript-eslint/unbound-method const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = - (client.getOptions && client.getOptions()) || {}; + (client.getOptions && client.getOptions()) || {}; if (maxBreadcrumbs <= 0) return; @@ -452,7 +466,7 @@ export function addBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: Breadcrum if (finalBreadcrumb === null) return; - scope.addBreadcrumb(finalBreadcrumb, maxBreadcrumbs); + addBreadcrumbScope(scope, finalBreadcrumb, maxBreadcrumbs); } /** @@ -463,7 +477,7 @@ export function addBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: Breadcrum */ export function setTags(hub: Hub, tags: { [key: string]: Primitive }): void { const scope = getScope(hub); - if (scope) scope.setTags(tags); + if (scope) setTagsScope(scope, tags); } /** @@ -473,7 +487,7 @@ export function setTags(hub: Hub, tags: { [key: string]: Primitive }): void { */ export function setExtras(hub: Hub, extras: Extras): void { const scope = getScope(hub); - if (scope) scope.setExtras(extras); + if (scope) setExtrasScope(scope, extras); } /** @@ -487,7 +501,7 @@ export function setExtras(hub: Hub, extras: Extras): void { */ export function setTag(hub: Hub, key: string, value: Primitive): void { const scope = getScope(hub); - if (scope) scope.setTag(key, value); + if (scope) setTagScope(scope, key, value); } /** @@ -498,7 +512,7 @@ export function setTag(hub: Hub, key: string, value: Primitive): void { */ export function setExtra(hub: Hub, key: string, extra: Extra): void { const scope = getScope(hub); - if (scope) scope.setExtra(key, extra); + if (scope) setExtraScope(scope, key, extra); } /** @@ -509,7 +523,7 @@ export function setExtra(hub: Hub, key: string, extra: Extra): void { */ export function setContext(hub: Hub, name: string, context: { [key: string]: any } | null): void { const scope = getScope(hub); - if (scope) scope.setContext(name, context); + if (scope) setContextScope(scope, name, context); } /** @@ -576,7 +590,11 @@ export function startSpan(hub: Hub, context: SpanContext): Span { * * @returns The transaction which was just started */ -export function startTransaction(hub: Hub, context: TransactionContext, customSamplingContext?: CustomSamplingContext): Transaction { +export function startTransaction( + hub: Hub, + context: TransactionContext, + customSamplingContext?: CustomSamplingContext, +): Transaction { return _callExtensionMethod(hub, 'startTransaction', context, customSamplingContext); } diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 33e86079ed44..d77b05ecb487 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -129,7 +129,7 @@ export function addEventProcessor(scope: Scope, callback: EventProcessor): Scope * @param key String key of tag * @param value Value of tag */ -export function setTag(scope: Scope, key: string, value: Primitive): Scope { +export function setTagScope(scope: Scope, key: string, value: Primitive): Scope { scope._tags = { ...scope._tags, [key]: value }; notifyScopeListeners(scope); return scope; @@ -140,7 +140,7 @@ export function setTag(scope: Scope, key: string, value: Primitive): Scope { * @param scope * @param extras Extras object to merge into current context. */ -export function setExtras(scope: Scope, extras: Extras): Scope { +export function setExtrasScope(scope: Scope, extras: Extras): Scope { scope._extra = { ...scope._extra, ...extras, @@ -155,7 +155,7 @@ export function setExtras(scope: Scope, extras: Extras): Scope { * @param key String of extra * @param extra Any kind of data. This data will be normalized. */ -export function setExtra(scope: Scope, key: string, extra: Extra): Scope { +export function setExtraScope(scope: Scope, key: string, extra: Extra): Scope { scope._extra = { ...scope._extra, [key]: extra }; notifyScopeListeners(scope); return scope; @@ -205,7 +205,7 @@ export function setTransaction(scope: Scope, name?: string): Scope { * @param key * @param context an object containing context data. This data will be normalized. Pass `null` to unset the context. */ -export function setContext(scope: Scope, key: string, context: Context | null): Scope { +export function setContextScope(scope: Scope, key: string, context: Context | null): Scope { if (context === null) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete scope._contexts[key]; @@ -262,7 +262,7 @@ export function getTransaction(scope: Scope): Transaction | undefined { * @param scope * @param user User context object to be set in the current context. Pass `null` to unset the user. */ -export function setUser(scope: Scope, user: User | null): Scope { +export function setUserScope(scope: Scope, user: User | null): Scope { scope._user = user || {}; if (scope._session) { updateSession(scope._session, { user }); @@ -274,7 +274,7 @@ export function setUser(scope: Scope, user: User | null): Scope { /** * Returns the `User` if there is one */ -export function getUser(scope: Scope): User | undefined { +export function getUserScope(scope: Scope): User | undefined { return scope._user; } @@ -290,7 +290,7 @@ export function getRequestSession(scope: Scope): RequestSession | undefined { * @param scope * @param tags Tags context object to merge into current context. */ -export function setTags(scope: Scope, tags: { [key: string]: Primitive }): Scope { +export function setTagsScope(scope: Scope, tags: { [key: string]: Primitive }): Scope { scope._tags = { ...scope._tags, ...tags, @@ -310,7 +310,7 @@ export function setRequestSession(scope: Scope, requestSession?: RequestSession) /** * Sets the `Session` on the scope */ -export function setSession(scope: Scope, session?: Session): Scope { +export function setSessionScope(scope: Scope, session?: Session): Scope { if (!session) { delete scope._session; } else { @@ -400,7 +400,7 @@ export function clear(scope: Scope): Scope { * @param breadcrumb * @param maxBreadcrumbs number of max breadcrumbs to merged into event. */ -export function addBreadcrumb(scope: Scope, breadcrumb: Breadcrumb, maxBreadcrumbs?: number): Scope { +export function addBreadcrumbScope(scope: Scope, breadcrumb: Breadcrumb, maxBreadcrumbs?: number): Scope { const maxCrumbs = typeof maxBreadcrumbs === 'number' ? Math.min(maxBreadcrumbs, MAX_BREADCRUMBS) : MAX_BREADCRUMBS; // No data has been changed, so don't notify scope listeners diff --git a/packages/hub/src/session.ts b/packages/hub/src/session.ts index 94d6ab10c75d..cb1630e7faa6 100644 --- a/packages/hub/src/session.ts +++ b/packages/hub/src/session.ts @@ -1,10 +1,10 @@ -import { Session as SessionInterface, SessionContext, SessionStatus } from '@sentry/types'; +import { SessionContext, SessionStatus } from '@sentry/types'; import { dropUndefinedKeys, timestampInSeconds, uuid4 } from '@sentry/utils'; /** * Session Context */ -export class Session implements SessionInterface { +export class Session { public userAgent?: string; public errors: number = 0; public release?: string; diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index 674987c6bab5..327dabdf56d5 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -1,13 +1,8 @@ -import { - AggregationCounts, - RequestSessionStatus, - SessionAggregates, - SessionFlusherLike, - Transport, -} from '@sentry/types'; +import { AggregationCounts, RequestSessionStatus, SessionAggregates, Transport } from '@sentry/types'; import { dropUndefinedKeys, logger } from '@sentry/utils'; import { getCurrentHub, getScope } from './hub'; +import { getRequestSession, setRequestSession } from './scope'; type ReleaseHealthAttributes = { environment?: string; @@ -17,111 +12,120 @@ type ReleaseHealthAttributes = { /** * @inheritdoc */ -export class SessionFlusher implements SessionFlusherLike { +export class SessionFlusher { public readonly flushTimeout: number = 60; - private _pendingAggregates: Record = {}; - private _sessionAttrs: ReleaseHealthAttributes; - private _intervalId: ReturnType; - private _isEnabled: boolean = true; - private _transport: Transport; + public _pendingAggregates: Record = {}; + public _sessionAttrs: ReleaseHealthAttributes; + public _intervalId: ReturnType; + public _isEnabled: boolean = true; + public _transport: Transport; public constructor(transport: Transport, attrs: ReleaseHealthAttributes) { this._transport = transport; // Call to setInterval, so that flush is called every 60 seconds - this._intervalId = setInterval(() => this.flush(), this.flushTimeout * 1000); + this._intervalId = setInterval(() => flush(this), this.flushTimeout * 1000); this._sessionAttrs = attrs; } +} - /** Sends session aggregates to Transport */ - public sendSessionAggregates(sessionAggregates: SessionAggregates): void { - if (!this._transport.sendSession) { - logger.warn("Dropping session because custom transport doesn't implement sendSession"); - return; - } - void this._transport.sendSession(sessionAggregates).then(null, reason => { - logger.error(`Error while sending session: ${reason}`); - }); +/** Submits the aggregates request mode sessions to Sentry */ +function sendSessionAggregates(sessionflusher: SessionFlusher, sessionAggregates: SessionAggregates): void { + if (!sessionflusher._transport.sendSession) { + logger.warn("Dropping session because custom transport doesn't implement sendSession"); + return; } + void sessionflusher._transport.sendSession(sessionAggregates).then(null, reason => { + logger.error(`Error while sending session: ${reason}`); + }); +} - /** Checks if `pendingAggregates` has entries, and if it does flushes them by calling `sendSessions` */ - public flush(): void { - const sessionAggregates = this.getSessionAggregates(); - if (sessionAggregates.aggregates.length === 0) { - return; - } - this._pendingAggregates = {}; - this.sendSessionAggregates(sessionAggregates); +/** + * Empties Aggregate Buckets and Sends them to Transport Buffer. + * Checks if `pendingAggregates` has entries, and if it does flushes them by calling `sendSessions` + * */ +function flush(sessionFlusher: SessionFlusher): void { + const sessionAggregates = getSessionAggregates(sessionFlusher); + if (sessionAggregates.aggregates.length === 0) { + return; } + sessionFlusher._pendingAggregates = {}; + sendSessionAggregates(sessionFlusher, sessionAggregates); +} - /** Massages the entries in `pendingAggregates` and returns aggregated sessions */ - public getSessionAggregates(): SessionAggregates { - const aggregates: AggregationCounts[] = Object.keys(this._pendingAggregates).map((key: string) => { - return this._pendingAggregates[parseInt(key)]; - }); +/** Massages the entries in `pendingAggregates` and returns aggregated sessions */ +function getSessionAggregates(sessionFlusher: SessionFlusher): SessionAggregates { + const aggregates: AggregationCounts[] = Object.keys(sessionFlusher._pendingAggregates).map((key: string) => { + return sessionFlusher._pendingAggregates[parseInt(key)]; + }); - const sessionAggregates: SessionAggregates = { - attrs: this._sessionAttrs, - aggregates, - }; - return dropUndefinedKeys(sessionAggregates); - } + const sessionAggregates: SessionAggregates = { + attrs: sessionFlusher._sessionAttrs, + aggregates, + }; + return dropUndefinedKeys(sessionAggregates); +} - /** JSDoc */ - public close(): void { - clearInterval(this._intervalId); - this._isEnabled = false; - this.flush(); - } +/** Clears setInterval and calls flush */ +export function close(sessionFlusher: SessionFlusher): void { + clearInterval(sessionFlusher._intervalId); + sessionFlusher._isEnabled = false; + flush(sessionFlusher); +} - /** - * Wrapper function for _incrementSessionStatusCount that checks if the instance of SessionFlusher is enabled then - * fetches the session status of the request from `Scope.getRequestSession().status` on the scope and passes them to - * `_incrementSessionStatusCount` along with the start date - */ - public incrementSessionStatusCount(): void { - if (!this._isEnabled) { - return; - } - const scope = getScope(getCurrentHub()); - const requestSession = scope && scope.getRequestSession(); +/** + * Increments the Session Status bucket in SessionAggregates Object corresponding to the status of the session captured. + * + * Wrapper function for _incrementSessionStatusCount that checks if the instance of SessionFlusher is enabled then + * fetches the session status of the request from `Scope.getRequestSession().status` on the scope and passes them to + * `_incrementSessionStatusCount` along with the start date + */ +export function incrementSessionStatusCount(sessionFlusher: SessionFlusher): void { + if (!sessionFlusher._isEnabled) { + return; + } + const scope = getScope(getCurrentHub()); + const requestSession = scope && getRequestSession(scope); - if (requestSession && requestSession.status) { - this._incrementSessionStatusCount(requestSession.status, new Date()); - // This is not entirely necessarily but is added as a safe guard to indicate the bounds of a request and so in - // case captureRequestSession is called more than once to prevent double count - if (scope) { - scope.setRequestSession(undefined); - } - /* eslint-enable @typescript-eslint/no-unsafe-member-access */ + if (requestSession && requestSession.status) { + _incrementSessionStatusCount(sessionFlusher, requestSession.status, new Date()); + // This is not entirely necessarily but is added as a safe guard to indicate the bounds of a request and so in + // case captureRequestSession is called more than once to prevent double count + if (scope) { + setRequestSession(scope, undefined); } + /* eslint-enable @typescript-eslint/no-unsafe-member-access */ } +} - /** - * Increments status bucket in pendingAggregates buffer (internal state) corresponding to status of - * the session received - */ - private _incrementSessionStatusCount(status: RequestSessionStatus, date: Date): number { - // Truncate minutes and seconds on Session Started attribute to have one minute bucket keys - const sessionStartedTrunc = new Date(date).setSeconds(0, 0); - this._pendingAggregates[sessionStartedTrunc] = this._pendingAggregates[sessionStartedTrunc] || {}; +/** + * Increments status bucket in pendingAggregates buffer (internal state) corresponding to status of + * the session received + */ +function _incrementSessionStatusCount( + sessionFlusher: SessionFlusher, + status: RequestSessionStatus, + date: Date, +): number { + // Truncate minutes and seconds on Session Started attribute to have one minute bucket keys + const sessionStartedTrunc = new Date(date).setSeconds(0, 0); + sessionFlusher._pendingAggregates[sessionStartedTrunc] = sessionFlusher._pendingAggregates[sessionStartedTrunc] || {}; - // corresponds to aggregated sessions in one specific minute bucket - // for example, {"started":"2021-03-16T08:00:00.000Z","exited":4, "errored": 1} - const aggregationCounts: AggregationCounts = this._pendingAggregates[sessionStartedTrunc]; - if (!aggregationCounts.started) { - aggregationCounts.started = new Date(sessionStartedTrunc).toISOString(); - } + // corresponds to aggregated sessions in one specific minute bucket + // for example, {"started":"2021-03-16T08:00:00.000Z","exited":4, "errored": 1} + const aggregationCounts: AggregationCounts = sessionFlusher._pendingAggregates[sessionStartedTrunc]; + if (!aggregationCounts.started) { + aggregationCounts.started = new Date(sessionStartedTrunc).toISOString(); + } - switch (status) { - case 'errored': - aggregationCounts.errored = (aggregationCounts.errored || 0) + 1; - return aggregationCounts.errored; - case 'ok': - aggregationCounts.exited = (aggregationCounts.exited || 0) + 1; - return aggregationCounts.exited; - default: - aggregationCounts.crashed = (aggregationCounts.crashed || 0) + 1; - return aggregationCounts.crashed; - } + switch (status) { + case 'errored': + aggregationCounts.errored = (aggregationCounts.errored || 0) + 1; + return aggregationCounts.errored; + case 'ok': + aggregationCounts.exited = (aggregationCounts.exited || 0) + 1; + return aggregationCounts.exited; + default: + aggregationCounts.crashed = (aggregationCounts.crashed || 0) + 1; + return aggregationCounts.crashed; } } diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 71c108ab7556..c0699417c36b 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -1,10 +1,5 @@ import { User } from './user'; -/** - * @inheritdoc - */ -export interface Session extends SessionContext {} - export interface RequestSession { status?: RequestSessionStatus; } @@ -43,23 +38,6 @@ export interface SessionAggregates { aggregates: Array; } -export interface SessionFlusherLike { - /** - * Increments the Session Status bucket in SessionAggregates Object corresponding to the status of the session - * captured - */ - incrementSessionStatusCount(): void; - - /** Submits the aggregates request mode sessions to Sentry */ - sendSessionAggregates(sessionAggregates: SessionAggregates): void; - - /** Empties Aggregate Buckets and Sends them to Transport Buffer */ - flush(): void; - - /** Clears setInterval and calls flush */ - close(): void; -} - export interface AggregationCounts { started: string; errored?: number; From 6c59dad086f509d0389aaf49a3cceb81b490febf Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Tue, 11 Jan 2022 23:45:04 -0500 Subject: [PATCH 20/37] chore: fixing test cases --- packages/hub/src/sessionflusher.ts | 48 ++++++++++++------------ packages/hub/test/sessionflusher.test.ts | 42 ++++++++++++--------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index 327dabdf56d5..e637bf66b4b4 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -14,27 +14,29 @@ type ReleaseHealthAttributes = { */ export class SessionFlusher { public readonly flushTimeout: number = 60; - public _pendingAggregates: Record = {}; - public _sessionAttrs: ReleaseHealthAttributes; - public _intervalId: ReturnType; - public _isEnabled: boolean = true; - public _transport: Transport; + public pendingAggregates: Record = {}; + public sessionAttrs: ReleaseHealthAttributes; + public intervalId: ReturnType; + public isEnabled: boolean = true; + public transport: Transport; public constructor(transport: Transport, attrs: ReleaseHealthAttributes) { - this._transport = transport; + this.transport = transport; // Call to setInterval, so that flush is called every 60 seconds - this._intervalId = setInterval(() => flush(this), this.flushTimeout * 1000); - this._sessionAttrs = attrs; + this.intervalId = setInterval(() => flush(this), this.flushTimeout * 1000); + this.sessionAttrs = attrs; } } /** Submits the aggregates request mode sessions to Sentry */ -function sendSessionAggregates(sessionflusher: SessionFlusher, sessionAggregates: SessionAggregates): void { - if (!sessionflusher._transport.sendSession) { +function sendSessionAggregates(sessionFlusher: SessionFlusher, sessionAggregates: SessionAggregates): void { + // TODO: But why to take a Transport when it may be the case that doesn't implement the callback? + // The only point of taking `transport` is to call this one function thou. + if (!sessionFlusher.transport.sendSession) { logger.warn("Dropping session because custom transport doesn't implement sendSession"); return; } - void sessionflusher._transport.sendSession(sessionAggregates).then(null, reason => { + void sessionFlusher.transport.sendSession(sessionAggregates).then(null, reason => { logger.error(`Error while sending session: ${reason}`); }); } @@ -48,27 +50,27 @@ function flush(sessionFlusher: SessionFlusher): void { if (sessionAggregates.aggregates.length === 0) { return; } - sessionFlusher._pendingAggregates = {}; + sessionFlusher.pendingAggregates = {}; sendSessionAggregates(sessionFlusher, sessionAggregates); } /** Massages the entries in `pendingAggregates` and returns aggregated sessions */ -function getSessionAggregates(sessionFlusher: SessionFlusher): SessionAggregates { - const aggregates: AggregationCounts[] = Object.keys(sessionFlusher._pendingAggregates).map((key: string) => { - return sessionFlusher._pendingAggregates[parseInt(key)]; +export function getSessionAggregates(sessionFlusher: SessionFlusher): SessionAggregates { + const aggregates: AggregationCounts[] = Object.keys(sessionFlusher.pendingAggregates).map((key: string) => { + return sessionFlusher.pendingAggregates[parseInt(key)]; }); const sessionAggregates: SessionAggregates = { - attrs: sessionFlusher._sessionAttrs, + attrs: sessionFlusher.sessionAttrs, aggregates, }; return dropUndefinedKeys(sessionAggregates); } /** Clears setInterval and calls flush */ -export function close(sessionFlusher: SessionFlusher): void { - clearInterval(sessionFlusher._intervalId); - sessionFlusher._isEnabled = false; +export function closeSessionFlusher(sessionFlusher: SessionFlusher): void { + clearInterval(sessionFlusher.intervalId); + sessionFlusher.isEnabled = false; flush(sessionFlusher); } @@ -80,7 +82,7 @@ export function close(sessionFlusher: SessionFlusher): void { * `_incrementSessionStatusCount` along with the start date */ export function incrementSessionStatusCount(sessionFlusher: SessionFlusher): void { - if (!sessionFlusher._isEnabled) { + if (!sessionFlusher.isEnabled) { return; } const scope = getScope(getCurrentHub()); @@ -101,18 +103,18 @@ export function incrementSessionStatusCount(sessionFlusher: SessionFlusher): voi * Increments status bucket in pendingAggregates buffer (internal state) corresponding to status of * the session received */ -function _incrementSessionStatusCount( +export function _incrementSessionStatusCount( sessionFlusher: SessionFlusher, status: RequestSessionStatus, date: Date, ): number { // Truncate minutes and seconds on Session Started attribute to have one minute bucket keys const sessionStartedTrunc = new Date(date).setSeconds(0, 0); - sessionFlusher._pendingAggregates[sessionStartedTrunc] = sessionFlusher._pendingAggregates[sessionStartedTrunc] || {}; + sessionFlusher.pendingAggregates[sessionStartedTrunc] = sessionFlusher.pendingAggregates[sessionStartedTrunc] || {}; // corresponds to aggregated sessions in one specific minute bucket // for example, {"started":"2021-03-16T08:00:00.000Z","exited":4, "errored": 1} - const aggregationCounts: AggregationCounts = sessionFlusher._pendingAggregates[sessionStartedTrunc]; + const aggregationCounts: AggregationCounts = sessionFlusher.pendingAggregates[sessionStartedTrunc]; if (!aggregationCounts.started) { aggregationCounts.started = new Date(sessionStartedTrunc).toISOString(); } diff --git a/packages/hub/test/sessionflusher.test.ts b/packages/hub/test/sessionflusher.test.ts index 3c7dc9782615..d5d33d64e4c8 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -1,4 +1,5 @@ -import { SessionFlusher } from '../src/sessionflusher'; +import { SessionFlusher } from '../src'; +import { _incrementSessionStatusCount, closeSessionFlusher, getSessionAggregates } from '../src/sessionflusher'; describe('Session Flusher', () => { let sendSession: jest.Mock; @@ -26,33 +27,33 @@ describe('Session Flusher', () => { const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); - let count = (flusher as any)._incrementSessionStatusCount('ok', date); + let count = _incrementSessionStatusCount(flusher, 'ok', date); expect(count).toEqual(1); - count = (flusher as any)._incrementSessionStatusCount('ok', date); + count = _incrementSessionStatusCount(flusher, 'ok', date); expect(count).toEqual(2); - count = (flusher as any)._incrementSessionStatusCount('errored', date); + count = _incrementSessionStatusCount(flusher, 'errored', date); expect(count).toEqual(1); date.setMinutes(date.getMinutes() + 1); - count = (flusher as any)._incrementSessionStatusCount('ok', date); + count = _incrementSessionStatusCount(flusher, 'ok', date); expect(count).toEqual(1); - count = (flusher as any)._incrementSessionStatusCount('errored', date); + count = _incrementSessionStatusCount(flusher, 'errored', date); expect(count).toEqual(1); - expect(flusher.getSessionAggregates().aggregates).toEqual([ + expect(getSessionAggregates(flusher).aggregates).toEqual([ { errored: 1, exited: 2, started: '2021-04-08T12:18:00.000Z' }, { errored: 1, exited: 1, started: '2021-04-08T12:19:00.000Z' }, ]); - expect(flusher.getSessionAggregates().attrs).toEqual({ release: '1.0.0', environment: 'dev' }); + expect(getSessionAggregates(flusher).attrs).toEqual({ release: '1.0.0', environment: 'dev' }); }); test('test undefined attributes are excluded, on incrementSessionStatusCount call', () => { const flusher = new SessionFlusher(transport, { release: '1.0.0' }); const date = new Date('2021-04-08T12:18:23.043Z'); - (flusher as any)._incrementSessionStatusCount('ok', date); - (flusher as any)._incrementSessionStatusCount('errored', date); + _incrementSessionStatusCount(flusher, 'ok', date); + _incrementSessionStatusCount(flusher, 'errored', date); - expect(flusher.getSessionAggregates()).toEqual({ + expect(getSessionAggregates(flusher)).toEqual({ aggregates: [{ errored: 1, exited: 1, started: '2021-04-08T12:18:00.000Z' }], attrs: { release: '1.0.0' }, }); @@ -103,19 +104,26 @@ describe('Session Flusher', () => { test('calling close on SessionFlusher should disable SessionFlusher', () => { const flusher = new SessionFlusher(transport, { release: '1.0.x' }); - flusher.close(); + closeSessionFlusher(flusher); expect((flusher as any)._isEnabled).toEqual(false); }); test('calling close on SessionFlusher will force call flush', () => { + // GIVEN const flusher = new SessionFlusher(transport, { release: '1.0.x' }); - const flusherFlushFunc = jest.spyOn(flusher, 'flush'); const date = new Date('2021-04-08T12:18:23.043Z'); - (flusher as any)._incrementSessionStatusCount('ok', date); - (flusher as any)._incrementSessionStatusCount('ok', date); - flusher.close(); - expect(flusherFlushFunc).toHaveBeenCalledTimes(1); + // TODO: code-smell using internal function + // why can we call the public API instead of the internal one? + _incrementSessionStatusCount(flusher, 'ok', date); + _incrementSessionStatusCount(flusher, 'ok', date); + + // WHEN + closeSessionFlusher(flusher); + + // THEN + expect(flusher.isEnabled).toEqual(false); + expect(flusher.pendingAggregates).toEqual({}); expect(sendSession).toHaveBeenCalledWith( expect.objectContaining({ attrs: { release: '1.0.x' }, From 0bb6089939421a1c93ede20e313000d76d2e6e98 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Wed, 12 Jan 2022 00:15:16 -0500 Subject: [PATCH 21/37] chore: fixing tests --- packages/hub/src/sessionflusher.ts | 38 +++++----- packages/hub/test/sessionflusher.test.ts | 96 ++++++++++++------------ packages/types/src/index.ts | 2 - 3 files changed, 66 insertions(+), 70 deletions(-) diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index e637bf66b4b4..45318fb358f9 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -1,16 +1,27 @@ -import { AggregationCounts, RequestSessionStatus, SessionAggregates, Transport } from '@sentry/types'; +import { AggregationCounts, Event, EventStatus, RequestSessionStatus, SessionAggregates } from '@sentry/types'; +import { EventType } from '@sentry/types/src/event'; import { dropUndefinedKeys, logger } from '@sentry/utils'; import { getCurrentHub, getScope } from './hub'; import { getRequestSession, setRequestSession } from './scope'; +import { Session } from './session'; type ReleaseHealthAttributes = { environment?: string; release: string; }; +export interface Response { + status: EventStatus; + event?: Event | Session; + type?: EventType; + reason?: string; +} + +export type Transporter = (session: Session | SessionAggregates) => PromiseLike; + /** - * @inheritdoc + * ... */ export class SessionFlusher { public readonly flushTimeout: number = 60; @@ -18,29 +29,16 @@ export class SessionFlusher { public sessionAttrs: ReleaseHealthAttributes; public intervalId: ReturnType; public isEnabled: boolean = true; - public transport: Transport; + public transport: Transporter; - public constructor(transport: Transport, attrs: ReleaseHealthAttributes) { + public constructor(transport: Transporter, attrs: ReleaseHealthAttributes) { this.transport = transport; - // Call to setInterval, so that flush is called every 60 seconds + // Call to setInterval, so that flush is called every ~60 seconds this.intervalId = setInterval(() => flush(this), this.flushTimeout * 1000); this.sessionAttrs = attrs; } } -/** Submits the aggregates request mode sessions to Sentry */ -function sendSessionAggregates(sessionFlusher: SessionFlusher, sessionAggregates: SessionAggregates): void { - // TODO: But why to take a Transport when it may be the case that doesn't implement the callback? - // The only point of taking `transport` is to call this one function thou. - if (!sessionFlusher.transport.sendSession) { - logger.warn("Dropping session because custom transport doesn't implement sendSession"); - return; - } - void sessionFlusher.transport.sendSession(sessionAggregates).then(null, reason => { - logger.error(`Error while sending session: ${reason}`); - }); -} - /** * Empties Aggregate Buckets and Sends them to Transport Buffer. * Checks if `pendingAggregates` has entries, and if it does flushes them by calling `sendSessions` @@ -51,7 +49,9 @@ function flush(sessionFlusher: SessionFlusher): void { return; } sessionFlusher.pendingAggregates = {}; - sendSessionAggregates(sessionFlusher, sessionAggregates); + void sessionFlusher.transport(sessionAggregates).then(null, reason => { + logger.error(`Error while sending session: ${reason}`); + }); } /** Massages the entries in `pendingAggregates` and returns aggregated sessions */ diff --git a/packages/hub/test/sessionflusher.test.ts b/packages/hub/test/sessionflusher.test.ts index d5d33d64e4c8..15c3a3127f61 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -1,30 +1,25 @@ -import { SessionFlusher } from '../src'; -import { _incrementSessionStatusCount, closeSessionFlusher, getSessionAggregates } from '../src/sessionflusher'; +import { EventStatus } from '@sentry/types'; -describe('Session Flusher', () => { - let sendSession: jest.Mock; - let transport: { - sendEvent: jest.Mock; - sendSession: jest.Mock; - close: jest.Mock; - }; - - beforeEach(() => { - jest.useFakeTimers(); - sendSession = jest.fn(() => Promise.resolve({ status: 'success' })); - transport = { - sendEvent: jest.fn(), - sendSession, - close: jest.fn(), - }; - }); +import { + _incrementSessionStatusCount, + closeSessionFlusher, + getSessionAggregates, + SessionFlusher, +} from '../src/sessionflusher'; - afterEach(() => { - jest.restoreAllMocks(); - }); +function makeTransporter() { + return jest.fn(() => Promise.resolve({ status: 'success' as EventStatus })); +} + +jest.useFakeTimers(); + +describe('Session Flusher', () => { + beforeEach(() => jest.useFakeTimers()); + afterEach(() => jest.clearAllTimers()); test('test incrementSessionStatusCount updates the internal SessionFlusher state', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); + const transporter = makeTransporter(); + const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); let count = _incrementSessionStatusCount(flusher, 'ok', date); @@ -47,7 +42,8 @@ describe('Session Flusher', () => { }); test('test undefined attributes are excluded, on incrementSessionStatusCount call', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0' }); + const transporter = makeTransporter(); + const flusher = new SessionFlusher(transporter, { release: '1.0.0' }); const date = new Date('2021-04-08T12:18:23.043Z'); _incrementSessionStatusCount(flusher, 'ok', date); @@ -59,32 +55,32 @@ describe('Session Flusher', () => { }); }); - test('flush is called every 60 seconds after initialisation of an instance of SessionFlusher', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); - const flusherFlushFunc = jest.spyOn(flusher, 'flush'); + test('flush is called every ~60 seconds after initialisation of an instance of SessionFlusher', () => { + const transporter = makeTransporter(); + new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); jest.advanceTimersByTime(59000); - expect(flusherFlushFunc).toHaveBeenCalledTimes(0); + expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(2000); - expect(flusherFlushFunc).toHaveBeenCalledTimes(1); + expect(transporter).toHaveBeenCalledTimes(1); jest.advanceTimersByTime(58000); - expect(flusherFlushFunc).toHaveBeenCalledTimes(1); + expect(transporter).toHaveBeenCalledTimes(1); jest.advanceTimersByTime(2000); - expect(flusherFlushFunc).toHaveBeenCalledTimes(2); + expect(transporter).toHaveBeenCalledTimes(2); }); - test('sendSessions is called on flush if sessions were captured', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); - const flusherFlushFunc = jest.spyOn(flusher, 'flush'); + test('transporter is called on flush if sessions were captured', () => { + const transporter = makeTransporter(); + const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); - (flusher as any)._incrementSessionStatusCount('ok', date); - (flusher as any)._incrementSessionStatusCount('ok', date); + _incrementSessionStatusCount(flusher, 'ok', date); + _incrementSessionStatusCount(flusher, 'ok', date); - expect(sendSession).toHaveBeenCalledTimes(0); + expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(61000); - expect(flusherFlushFunc).toHaveBeenCalledTimes(1); - expect(sendSession).toHaveBeenCalledWith( + expect(transporter).toHaveBeenCalledTimes(1); + expect(transporter).toHaveBeenCalledWith( expect.objectContaining({ attrs: { release: '1.0.0', environment: 'dev' }, aggregates: [{ started: '2021-04-08T12:18:00.000Z', exited: 2 }], @@ -92,25 +88,27 @@ describe('Session Flusher', () => { ); }); - test('sendSessions is not called on flush if no sessions were captured', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); - const flusherFlushFunc = jest.spyOn(flusher, 'flush'); + test('transporter is not called on flush if no sessions were captured', () => { + const transporter = makeTransporter(); + new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); - expect(sendSession).toHaveBeenCalledTimes(0); + expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(61000); - expect(flusherFlushFunc).toHaveBeenCalledTimes(1); - expect(sendSession).toHaveBeenCalledTimes(0); + expect(transporter).toHaveBeenCalledTimes(1); + expect(transporter).toHaveBeenCalledTimes(0); }); test('calling close on SessionFlusher should disable SessionFlusher', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.x' }); + const transporter = makeTransporter(); + const flusher = new SessionFlusher(transporter, { release: '1.0.x' }); closeSessionFlusher(flusher); - expect((flusher as any)._isEnabled).toEqual(false); + expect(flusher.isEnabled).toEqual(false); }); test('calling close on SessionFlusher will force call flush', () => { // GIVEN - const flusher = new SessionFlusher(transport, { release: '1.0.x' }); + const transporter = makeTransporter(); + const flusher = new SessionFlusher(transporter, { release: '1.0.x' }); const date = new Date('2021-04-08T12:18:23.043Z'); // TODO: code-smell using internal function @@ -124,7 +122,7 @@ describe('Session Flusher', () => { // THEN expect(flusher.isEnabled).toEqual(false); expect(flusher.pendingAggregates).toEqual({}); - expect(sendSession).toHaveBeenCalledWith( + expect(transporter).toHaveBeenCalledWith( expect.objectContaining({ attrs: { release: '1.0.x' }, aggregates: [{ started: '2021-04-08T12:18:00.000Z', exited: 2 }], diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index a6268e586a8d..76678f6646cb 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -24,12 +24,10 @@ export { SdkMetadata } from './sdkmetadata'; export { SessionAggregates, AggregationCounts, - Session, SessionContext, SessionStatus, RequestSession, RequestSessionStatus, - SessionFlusherLike, } from './session'; /* eslint-disable-next-line deprecation/deprecation */ From 648ca06fbc02aeb9a4fdb90d3e76d015fbf88d75 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 00:15:11 -0500 Subject: [PATCH 22/37] chore: make scope to pass tests --- packages/core/src/sdk.ts | 4 +- packages/hub/src/hub.ts | 24 +- packages/hub/src/index.ts | 2 +- packages/hub/src/scope.ts | 367 +++++++++++---------- packages/hub/src/sessionflusher.ts | 8 +- packages/hub/test/scope.test.ts | 395 ++++++++++++----------- packages/hub/test/sessionflusher.test.ts | 29 +- packages/types/src/scope.ts | 5 +- packages/types/src/session.ts | 2 + 9 files changed, 447 insertions(+), 389 deletions(-) diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index 8eaa339a9175..641188c7b19f 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,4 +1,4 @@ -import { bindClient, getCurrentHub, getScope } from '@sentry/hub'; +import { bindClient, getCurrentHub, getScope, updateScope } from '@sentry/hub'; import { Client, Options } from '@sentry/types'; import { logger } from '@sentry/utils'; @@ -19,7 +19,7 @@ export function initAndBind(clientClass: Cl const hub = getCurrentHub(); const scope = getScope(hub); if (scope) { - scope.update(options.initialScope); + updateScope(scope, options.initialScope); } const client = new clientClass(options); bindClient(hub, client); diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index eefc3b887ee5..afdb14e8309a 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -21,18 +21,18 @@ import { import { consoleSandbox, dateTimestampInSeconds, getGlobalObject, isNodeEnv, logger, uuid4 } from '@sentry/utils'; import { - addBreadcrumbScope, + addScopeBreadcrumb, cloneScope, getSession, getUserScope, Scope, - setContextScope, - setExtraScope, - setExtrasScope, + setScopeContext, + setScopeExtra, + setScopeExtras, setSessionScope, - setTagScope, + setScopeTag, setTagsScope, - setUserScope, + setScopeUser, } from './scope'; import { closeSession, Session, updateSession } from './session'; @@ -232,7 +232,7 @@ export function getClient(hub: Hub): C | undefined { */ export function setUser(hub: Hub, user: User | null): void { const scope = getScope(hub); - if (scope) setUserScope(scope, user); + if (scope) setScopeUser(scope, user); } /** Returns the scope of the top stack. */ @@ -466,7 +466,7 @@ export function addBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: Breadcrum if (finalBreadcrumb === null) return; - addBreadcrumbScope(scope, finalBreadcrumb, maxBreadcrumbs); + addScopeBreadcrumb(scope, finalBreadcrumb, maxBreadcrumbs); } /** @@ -487,7 +487,7 @@ export function setTags(hub: Hub, tags: { [key: string]: Primitive }): void { */ export function setExtras(hub: Hub, extras: Extras): void { const scope = getScope(hub); - if (scope) setExtrasScope(scope, extras); + if (scope) setScopeExtras(scope, extras); } /** @@ -501,7 +501,7 @@ export function setExtras(hub: Hub, extras: Extras): void { */ export function setTag(hub: Hub, key: string, value: Primitive): void { const scope = getScope(hub); - if (scope) setTagScope(scope, key, value); + if (scope) setScopeTag(scope, key, value); } /** @@ -512,7 +512,7 @@ export function setTag(hub: Hub, key: string, value: Primitive): void { */ export function setExtra(hub: Hub, key: string, extra: Extra): void { const scope = getScope(hub); - if (scope) setExtraScope(scope, key, extra); + if (scope) setScopeExtra(scope, key, extra); } /** @@ -523,7 +523,7 @@ export function setExtra(hub: Hub, key: string, extra: Extra): void { */ export function setContext(hub: Hub, name: string, context: { [key: string]: any } | null): void { const scope = getScope(hub); - if (scope) setContextScope(scope, name, context); + if (scope) setScopeContext(scope, name, context); } /** diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index cb3501654158..7cbf5dd2449e 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,4 +1,4 @@ -export { applyToEvent, addGlobalEventProcessor, cloneScope, getSession, Scope } from './scope'; +export { updateScope, applyScopeToEvent, addGlobalEventProcessor, cloneScope, getSession, Scope } from './scope'; export { Session, updateSession } from './session'; export { SessionFlusher } from './sessionflusher'; export { diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index d77b05ecb487..dce93aa98a37 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { Breadcrumb, CaptureContext, @@ -17,10 +16,13 @@ import { Transaction, User, } from '@sentry/types'; +import { CaptureContextCallback } from '@sentry/types/src/scope'; import { dateTimestampInSeconds, getGlobalObject, isPlainObject, isThenable, SyncPromise } from '@sentry/utils'; import { Session, updateSession } from './session'; +type ScopeListener = (scope: Scope) => void; + /** * Absolute maximum number of breadcrumbs added to an event. * The `maxBreadcrumbs` option cannot be higher than this value. @@ -28,51 +30,51 @@ import { Session, updateSession } from './session'; const MAX_BREADCRUMBS = 100; /** - * Holds additional event information. {@link applyToEvent} will be + * Holds additional event information. {@link applyScopeToEvent} will be * called by the client before an event will be sent. */ export class Scope { /** Flag if notifying is happening. */ - public _notifyingListeners: boolean = false; + public notifyingListeners: boolean = false; /** Callback for client to receive scope changes. */ - public _scopeListeners: Array<(scope: Scope) => void> = []; + public scopeListeners: Array<(scope: Scope) => void> = []; - /** Callback list that will be called after {@link applyToEvent}. */ - public _eventProcessors: EventProcessor[] = []; + /** Callback list that will be called after {@link applyScopeToEvent}. */ + public eventProcessors: EventProcessor[] = []; /** Array of breadcrumbs. */ - public _breadcrumbs: Breadcrumb[] = []; + public breadcrumbs: Breadcrumb[] = []; /** User */ - public _user: User = {}; + public user: User = {}; /** Tags */ - public _tags: { [key: string]: Primitive } = {}; + public tags: Record = {}; /** Extra */ - public _extra: Extras = {}; + public extra: Extras = {}; /** Contexts */ - public _contexts: Contexts = {}; + public contexts: Contexts = {}; /** Fingerprint */ - public _fingerprint?: string[]; + public fingerprint?: string[]; /** Severity */ - public _level?: SeverityLevel; + public level?: SeverityLevel; /** Transaction Name */ - public _transactionName?: string; + public transactionName?: string; /** Span */ - public _span?: Span; + public span?: Span; /** Session */ - public _session?: Session; + public session?: Session; /** Request Mode Session Status */ - public _requestSession?: RequestSession; + public requestSession?: RequestSession; } /** @@ -82,18 +84,18 @@ export class Scope { export function cloneScope(scope?: Scope): Scope { const newScope = new Scope(); if (scope) { - newScope._breadcrumbs = [...scope._breadcrumbs]; - newScope._tags = { ...scope._tags }; - newScope._extra = { ...scope._extra }; - newScope._contexts = { ...scope._contexts }; - newScope._user = scope._user; - newScope._level = scope._level; - newScope._span = scope._span; - newScope._session = scope._session; - newScope._transactionName = scope._transactionName; - newScope._fingerprint = scope._fingerprint; - newScope._eventProcessors = [...scope._eventProcessors]; - newScope._requestSession = scope._requestSession; + newScope.breadcrumbs = [...scope.breadcrumbs]; + newScope.tags = { ...scope.tags }; + newScope.extra = { ...scope.extra }; + newScope.contexts = { ...scope.contexts }; + newScope.user = scope.user; + newScope.level = scope.level; + newScope.span = scope.span; + newScope.session = scope.session; + newScope.transactionName = scope.transactionName; + newScope.fingerprint = scope.fingerprint; + newScope.eventProcessors = [...scope.eventProcessors]; + newScope.requestSession = scope.requestSession; } return newScope; } @@ -102,21 +104,21 @@ export function cloneScope(scope?: Scope): Scope { * Returns the `Session` if there is one */ export function getSession(scope?: Scope): Session | undefined { - return scope && scope._session; + return scope && scope.session; } /** * Add internal on change listener. Used for sub SDKs that need to store the scope. * @hidden */ -export function addScopeListener(scope: Scope, callback: (scope: Scope) => void): Scope { - scope._scopeListeners.push(callback); +export function addScopeListener(scope: Scope, callback: ScopeListener): Scope { + scope.scopeListeners.push(callback); return scope; } -/** Add new event processor that will be called after {@link applyToEvent}. */ -export function addEventProcessor(scope: Scope, callback: EventProcessor): Scope { - scope._eventProcessors.push(callback); +/** Add new event processor that will be called after {@link applyScopeToEvent}. */ +export function addScopeEventProcessor(scope: Scope, callback: EventProcessor): Scope { + scope.eventProcessors.push(callback); return scope; } @@ -129,10 +131,9 @@ export function addEventProcessor(scope: Scope, callback: EventProcessor): Scope * @param key String key of tag * @param value Value of tag */ -export function setTagScope(scope: Scope, key: string, value: Primitive): Scope { - scope._tags = { ...scope._tags, [key]: value }; - notifyScopeListeners(scope); - return scope; +export function setScopeTag(scope: Scope, key: string, value: Primitive): Scope { + scope.tags = { ...scope.tags, [key]: value }; + return notifyListeners(scope); } /** @@ -140,13 +141,12 @@ export function setTagScope(scope: Scope, key: string, value: Primitive): Scope * @param scope * @param extras Extras object to merge into current context. */ -export function setExtrasScope(scope: Scope, extras: Extras): Scope { - scope._extra = { - ...scope._extra, +export function setScopeExtras(scope: Scope, extras: Extras): Scope { + scope.extra = { + ...scope.extra, ...extras, }; - notifyScopeListeners(scope); - return scope; + return notifyListeners(scope); } /** @@ -155,10 +155,9 @@ export function setExtrasScope(scope: Scope, extras: Extras): Scope { * @param key String of extra * @param extra Any kind of data. This data will be normalized. */ -export function setExtraScope(scope: Scope, key: string, extra: Extra): Scope { - scope._extra = { ...scope._extra, [key]: extra }; - notifyScopeListeners(scope); - return scope; +export function setScopeExtra(scope: Scope, key: string, extra: Extra): Scope { + scope.extra = { ...scope.extra, [key]: extra }; + return notifyListeners(scope); } /** @@ -166,10 +165,9 @@ export function setExtraScope(scope: Scope, key: string, extra: Extra): Scope { * @param scope * @param fingerprint string[] to group events in Sentry. */ -export function setFingerprint(scope: Scope, fingerprint: string[]): Scope { - scope._fingerprint = fingerprint; - notifyScopeListeners(scope); - return scope; +export function setScopeFingerprint(scope: Scope, fingerprint: string[]): Scope { + scope.fingerprint = fingerprint; + return notifyListeners(scope); } /** @@ -177,19 +175,17 @@ export function setFingerprint(scope: Scope, fingerprint: string[]): Scope { * @param scope * @param level string {@link Severity} */ -export function setLevel(scope: Scope, level: SeverityLevel): Scope { - scope._level = level; - notifyScopeListeners(scope); - return scope; +export function setScopeLevel(scope: Scope, level: SeverityLevel): Scope { + scope.level = level; + return notifyListeners(scope); } /** * Sets the transaction name on the scope for future events. */ export function setTransactionName(scope: Scope, name?: string): Scope { - scope._transactionName = name; - notifyScopeListeners(scope); - return scope; + scope.transactionName = name; + return notifyListeners(scope); } /** @@ -205,16 +201,15 @@ export function setTransaction(scope: Scope, name?: string): Scope { * @param key * @param context an object containing context data. This data will be normalized. Pass `null` to unset the context. */ -export function setContextScope(scope: Scope, key: string, context: Context | null): Scope { +export function setScopeContext(scope: Scope, key: string, context: Context | null): Scope { if (context === null) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete - delete scope._contexts[key]; + delete scope.contexts[key]; } else { - scope._contexts = { ...scope._contexts, [key]: context }; + scope.contexts = { ...scope.contexts, [key]: context }; } - notifyScopeListeners(scope); - return scope; + return notifyListeners(scope); } /** @@ -222,17 +217,16 @@ export function setContextScope(scope: Scope, key: string, context: Context | nu * @param scope * @param span Span */ -export function setSpan(scope: Scope, span?: Span): Scope { - scope._span = span; - notifyScopeListeners(scope); - return scope; +export function setScopeSpan(scope: Scope, span?: Span): Scope { + scope.span = span; + return notifyListeners(scope); } /** * @inheritDoc */ -export function getSpan(scope: Scope): Span | undefined { - return scope._span; +export function getScopeSpan(scope: Scope): Span | undefined { + return scope.span; } /** @@ -240,7 +234,7 @@ export function getSpan(scope: Scope): Span | undefined { */ export function getTransaction(scope: Scope): Transaction | undefined { // often, this span will be a transaction, but it's not guaranteed to be - const span = getSpan(scope) as undefined | (Span & { spanRecorder: { spans: Span[] } }); + const span = getScopeSpan(scope) as undefined | (Span & { spanRecorder: { spans: Span[] } }); // try it the new way first if (span && span.transaction) { @@ -262,27 +256,26 @@ export function getTransaction(scope: Scope): Transaction | undefined { * @param scope * @param user User context object to be set in the current context. Pass `null` to unset the user. */ -export function setUserScope(scope: Scope, user: User | null): Scope { - scope._user = user || {}; - if (scope._session) { - updateSession(scope._session, { user }); +export function setScopeUser(scope: Scope, user: User | null): Scope { + scope.user = user || {}; + if (scope.session) { + updateSession(scope.session, { user }); } - notifyScopeListeners(scope); - return scope; + return notifyListeners(scope); } /** * Returns the `User` if there is one */ export function getUserScope(scope: Scope): User | undefined { - return scope._user; + return scope.user; } /** * Returns the `RequestSession` if there is one */ -export function getRequestSession(scope: Scope): RequestSession | undefined { - return scope._requestSession; +export function getScopeRequestSession(scope: Scope): RequestSession | undefined { + return scope.requestSession; } /** @@ -290,20 +283,19 @@ export function getRequestSession(scope: Scope): RequestSession | undefined { * @param scope * @param tags Tags context object to merge into current context. */ -export function setTagsScope(scope: Scope, tags: { [key: string]: Primitive }): Scope { - scope._tags = { - ...scope._tags, +export function setScopeTags(scope: Scope, tags: { [key: string]: Primitive }): Scope { + scope.tags = { + ...scope.tags, ...tags, }; - notifyScopeListeners(scope); - return scope; + return notifyListeners(scope); } /** * Sets the `RequestSession` on the scope */ -export function setRequestSession(scope: Scope, requestSession?: RequestSession): Scope { - scope._requestSession = requestSession; +export function setScopeRequestSession(scope: Scope, requestSession?: RequestSession): Scope { + scope.requestSession = requestSession; return scope; } @@ -312,12 +304,11 @@ export function setRequestSession(scope: Scope, requestSession?: RequestSession) */ export function setSessionScope(scope: Scope, session?: Session): Scope { if (!session) { - delete scope._session; + delete scope.session; } else { - scope._session = session; + scope.session = session; } - notifyScopeListeners(scope); - return scope; + return notifyListeners(scope); } /** @@ -328,69 +319,91 @@ export function setSessionScope(scope: Scope, session?: Session): Scope { * @param scope * @param captureContext scope modifier to be used */ -export function update(scope: Scope, captureContext?: CaptureContext): Scope { +export function updateScope(scope: Scope, captureContext?: CaptureContext): Scope { if (!captureContext) { return scope; } - if (typeof captureContext === 'function') { - const updatedScope = (captureContext as (scope: T) => T)(scope); + if (isCaptureContextCallback(captureContext)) { + const updatedScope = captureContext(scope); + // TODO: It seems to be defensive programming to check to check, since the + // the type says you need to return a Scope back. return updatedScope instanceof Scope ? updatedScope : scope; } if (captureContext instanceof Scope) { - scope._tags = { ...scope._tags, ...captureContext._tags }; - scope._extra = { ...scope._extra, ...captureContext._extra }; - scope._contexts = { ...scope._contexts, ...captureContext._contexts }; - if (captureContext._user && Object.keys(captureContext._user).length) { - scope._user = captureContext._user; - } - if (captureContext._level) { - scope._level = captureContext._level; - } - if (captureContext._fingerprint) { - scope._fingerprint = captureContext._fingerprint; - } - if (captureContext._requestSession) { - scope._requestSession = captureContext._requestSession; - } - } else if (isPlainObject(captureContext)) { - // eslint-disable-next-line no-param-reassign - captureContext = captureContext as ScopeContext; - scope._tags = { ...scope._tags, ...captureContext.tags }; - scope._extra = { ...scope._extra, ...captureContext.extra }; - scope._contexts = { ...scope._contexts, ...captureContext.contexts }; - if (captureContext.user) { - scope._user = captureContext.user; - } - if (captureContext.level) { - scope._level = captureContext.level; - } - if (captureContext.fingerprint) { - scope._fingerprint = captureContext.fingerprint; - } - if (captureContext.requestSession) { - scope._requestSession = captureContext.requestSession; - } + return mergeScopes(scope, captureContext); + } else if (isScopeContext(captureContext)) { + return mergeScopeContext(scope, captureContext); } return scope; } -/** Clears the current scope and resets its properties. */ -export function clear(scope: Scope): Scope { - scope._breadcrumbs = []; - scope._tags = {}; - scope._extra = {}; - scope._user = {}; - scope._contexts = {}; - scope._level = undefined; - scope._transactionName = undefined; - scope._fingerprint = undefined; - scope._requestSession = undefined; - scope._span = undefined; - scope._session = undefined; - notifyScopeListeners(scope); +function mergeScopeContext(scope: Scope, captureContext: Partial): Scope { + scope.tags = { ...scope.tags, ...captureContext.tags }; + scope.extra = { ...scope.extra, ...captureContext.extra }; + scope.contexts = { ...scope.contexts, ...captureContext.contexts }; + if (captureContext.user) { + scope.user = captureContext.user; + } + if (captureContext.level) { + scope.level = captureContext.level; + } + if (captureContext.fingerprint) { + scope.fingerprint = captureContext.fingerprint; + } + if (captureContext.requestSession) { + scope.requestSession = captureContext.requestSession; + } + + return scope; +} + +function mergeScopes(scope: Scope, newScope: Scope): Scope { + scope.tags = { ...scope.tags, ...newScope.tags }; + scope.extra = { ...scope.extra, ...newScope.extra }; + scope.contexts = { ...scope.contexts, ...newScope.contexts }; + if (newScope.user && Object.keys(newScope.user).length) { + scope.user = newScope.user; + } + if (newScope.level) { + scope.level = newScope.level; + } + if (newScope.fingerprint) { + scope.fingerprint = newScope.fingerprint; + } + if (newScope.requestSession) { + scope.requestSession = newScope.requestSession; + } + + return scope; +} + +function isCaptureContextCallback(val: unknown): val is CaptureContextCallback { + return typeof val === 'function'; +} + +function isScopeContext(val: unknown): val is Partial { + return isPlainObject(val); +} + +/** + * Clears the current scope and resets its properties. + * */ +export function clearScope(scope: Scope): Scope { + scope.breadcrumbs = []; + scope.tags = {}; + scope.extra = {}; + scope.user = {}; + scope.contexts = {}; + scope.level = undefined; + scope.transactionName = undefined; + scope.fingerprint = undefined; + scope.requestSession = undefined; + scope.span = undefined; + scope.session = undefined; + notifyListeners(scope); return scope; } @@ -400,7 +413,7 @@ export function clear(scope: Scope): Scope { * @param breadcrumb * @param maxBreadcrumbs number of max breadcrumbs to merged into event. */ -export function addBreadcrumbScope(scope: Scope, breadcrumb: Breadcrumb, maxBreadcrumbs?: number): Scope { +export function addScopeBreadcrumb(scope: Scope, breadcrumb: Breadcrumb, maxBreadcrumbs?: number): Scope { const maxCrumbs = typeof maxBreadcrumbs === 'number' ? Math.min(maxBreadcrumbs, MAX_BREADCRUMBS) : MAX_BREADCRUMBS; // No data has been changed, so don't notify scope listeners @@ -412,19 +425,17 @@ export function addBreadcrumbScope(scope: Scope, breadcrumb: Breadcrumb, maxBrea timestamp: dateTimestampInSeconds(), ...breadcrumb, }; - scope._breadcrumbs = [...scope._breadcrumbs, mergedBreadcrumb].slice(-maxCrumbs); - notifyScopeListeners(scope); + scope.breadcrumbs = [...scope.breadcrumbs, mergedBreadcrumb].slice(-maxCrumbs); - return scope; + return notifyListeners(scope); } /** * Clears all currently set Breadcrumbs. */ -export function clearBreadcrumbs(scope: Scope): Scope { - scope._breadcrumbs = []; - notifyScopeListeners(scope); - return scope; +export function clearScopeBreadcrumbs(scope: Scope): Scope { + scope.breadcrumbs = []; + return notifyListeners(scope); } /** @@ -436,31 +447,31 @@ export function clearBreadcrumbs(scope: Scope): Scope { * @param hint May contain additional information about the original exception. * @hidden */ -export function applyToEvent(scope: Scope, event: Event, hint?: EventHint): PromiseLike { - if (scope._extra && Object.keys(scope._extra).length) { - event.extra = { ...scope._extra, ...event.extra }; +export function applyScopeToEvent(scope: Scope, event: Event, hint?: EventHint): PromiseLike { + if (scope.extra && Object.keys(scope.extra).length) { + event.extra = { ...scope.extra, ...event.extra }; } - if (scope._tags && Object.keys(scope._tags).length) { - event.tags = { ...scope._tags, ...event.tags }; + if (scope.tags && Object.keys(scope.tags).length) { + event.tags = { ...scope.tags, ...event.tags }; } - if (scope._user && Object.keys(scope._user).length) { - event.user = { ...scope._user, ...event.user }; + if (scope.user && Object.keys(scope.user).length) { + event.user = { ...scope.user, ...event.user }; } - if (scope._contexts && Object.keys(scope._contexts).length) { - event.contexts = { ...scope._contexts, ...event.contexts }; + if (scope.contexts && Object.keys(scope.contexts).length) { + event.contexts = { ...scope.contexts, ...event.contexts }; } - if (scope._level) { - event.level = scope._level; + if (scope.level) { + event.level = scope.level; } - if (scope._transactionName) { - event.transaction = scope._transactionName; + if (scope.transactionName) { + event.transaction = scope.transactionName; } // We want to set the trace context for normal events only if there isn't already // a trace context on the event. There is a product feature in place where we link // errors with transaction and it relies on that. - if (scope._span) { - event.contexts = { trace: scope._span.getTraceContext(), ...event.contexts }; - const transactionName = scope._span.transaction && scope._span.transaction.name; + if (scope.span) { + event.contexts = { trace: scope.span.getTraceContext(), ...event.contexts }; + const transactionName = scope.span.transaction && scope.span.transaction.name; if (transactionName) { event.tags = { transaction: transactionName, ...event.tags }; } @@ -468,14 +479,14 @@ export function applyToEvent(scope: Scope, event: Event, hint?: EventHint): Prom applyFingerprint(scope, event); - event.breadcrumbs = [...(event.breadcrumbs || []), ...scope._breadcrumbs]; + event.breadcrumbs = [...(event.breadcrumbs || []), ...scope.breadcrumbs]; event.breadcrumbs = event.breadcrumbs.length > 0 ? event.breadcrumbs : undefined; - return notifyEventProcessors(scope, [...getGlobalEventProcessors(), ...scope._eventProcessors], event, hint); + return notifyEventProcessors(scope, [...getGlobalEventProcessors(), ...scope.eventProcessors], event, hint); } /** - * This will be called after {@link applyToEvent} is finished. + * This will be called after {@link applyScopeToEvent} is finished. */ function notifyEventProcessors( scope: Scope, @@ -506,17 +517,17 @@ function notifyEventProcessors( /** * This will be called on every set call. */ -function notifyScopeListeners(scope: Scope): void { +function notifyListeners(scope: Scope): Scope { // We need this check for this._notifyingListeners to be able to work on scope during updates // If this check is not here we'll produce endless recursion when something is done with the scope // during the callback. - if (!scope._notifyingListeners) { - scope._notifyingListeners = true; - scope._scopeListeners.forEach(callback => { - callback(scope); - }); - scope._notifyingListeners = false; + if (!scope.notifyingListeners) { + scope.notifyingListeners = true; + scope.scopeListeners.forEach(callback => callback(scope)); + scope.notifyingListeners = false; } + + return scope; } /** @@ -532,8 +543,8 @@ function applyFingerprint(scope: Scope, event: Event): void { : []; // If we have something on the scope, then merge it with event - if (scope._fingerprint) { - event.fingerprint = event.fingerprint.concat(scope._fingerprint); + if (scope.fingerprint) { + event.fingerprint = event.fingerprint.concat(scope.fingerprint); } // If we have no data at all, remove empty array default @@ -550,7 +561,7 @@ function getGlobalEventProcessors(): EventProcessor[] { const global = getGlobalObject(); global.__SENTRY__ = global.__SENTRY__ || {}; global.__SENTRY__.globalEventProcessors = global.__SENTRY__.globalEventProcessors || []; - return global.__SENTRY__.globalEventProcessors; + return global.__SENTRY__.globalEventProcessors ?? []; /* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */ } diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index 45318fb358f9..11e5acebbe51 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -3,7 +3,7 @@ import { EventType } from '@sentry/types/src/event'; import { dropUndefinedKeys, logger } from '@sentry/utils'; import { getCurrentHub, getScope } from './hub'; -import { getRequestSession, setRequestSession } from './scope'; +import { getScopeRequestSession, setScopeRequestSession } from './scope'; import { Session } from './session'; type ReleaseHealthAttributes = { @@ -33,9 +33,9 @@ export class SessionFlusher { public constructor(transport: Transporter, attrs: ReleaseHealthAttributes) { this.transport = transport; + this.sessionAttrs = attrs; // Call to setInterval, so that flush is called every ~60 seconds this.intervalId = setInterval(() => flush(this), this.flushTimeout * 1000); - this.sessionAttrs = attrs; } } @@ -86,14 +86,14 @@ export function incrementSessionStatusCount(sessionFlusher: SessionFlusher): voi return; } const scope = getScope(getCurrentHub()); - const requestSession = scope && getRequestSession(scope); + const requestSession = scope && getScopeRequestSession(scope); if (requestSession && requestSession.status) { _incrementSessionStatusCount(sessionFlusher, requestSession.status, new Date()); // This is not entirely necessarily but is added as a safe guard to indicate the bounds of a request and so in // case captureRequestSession is called more than once to prevent double count if (scope) { - setRequestSession(scope, undefined); + setScopeRequestSession(scope, undefined); } /* eslint-enable @typescript-eslint/no-unsafe-member-access */ } diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index e0d6e1508a61..e15b7e318d01 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -1,189 +1,209 @@ import { Event, EventHint } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils'; -import { addGlobalEventProcessor, cloneScope, Scope } from '../src'; +import { addGlobalEventProcessor, applyScopeToEvent, cloneScope, Scope, updateScope } from '../src'; +import { + addScopeBreadcrumb, + addScopeEventProcessor, + addScopeListener, + clearScope, + clearScopeBreadcrumbs, + getScopeRequestSession, + setScopeContext, + setScopeExtra, + setScopeExtras, + setScopeFingerprint, + setScopeLevel, + setScopeRequestSession, + setScopeSpan, + setScopeTag, + setScopeTags, + setScopeUser, + setTransactionName, +} from '../src/scope'; describe('Scope', () => { afterEach(() => { jest.resetAllMocks(); - jest.useRealTimers(); getGlobalObject().__SENTRY__.globalEventProcessors = undefined; }); describe('attributes modification', () => { test('setFingerprint', () => { const scope = new Scope(); - scope.setFingerprint(['abcd']); - expect((scope as any)._fingerprint).toEqual(['abcd']); + setScopeFingerprint(scope, ['abcd']); + expect(scope.fingerprint).toEqual(['abcd']); }); test('setExtra', () => { const scope = new Scope(); - scope.setExtra('a', 1); - expect((scope as any)._extra).toEqual({ a: 1 }); + setScopeExtra(scope, 'a', 1); + expect(scope.extra).toEqual({ a: 1 }); }); test('setExtras', () => { const scope = new Scope(); - scope.setExtras({ a: 1 }); - expect((scope as any)._extra).toEqual({ a: 1 }); + setScopeExtras(scope, { a: 1 }); + expect(scope.extra).toEqual({ a: 1 }); }); test('setExtras with undefined overrides the value', () => { const scope = new Scope(); - scope.setExtra('a', 1); - scope.setExtras({ a: undefined }); - expect((scope as any)._extra).toEqual({ a: undefined }); + setScopeExtra(scope, 'a', 1); + setScopeExtras(scope, { a: undefined }); + expect(scope.extra).toEqual({ a: undefined }); }); test('setTag', () => { const scope = new Scope(); - scope.setTag('a', 'b'); - expect((scope as any)._tags).toEqual({ a: 'b' }); + setScopeTag(scope, 'a', 'b'); + expect(scope.tags).toEqual({ a: 'b' }); }); test('setTags', () => { const scope = new Scope(); - scope.setTags({ a: 'b' }); - expect((scope as any)._tags).toEqual({ a: 'b' }); + setScopeTags(scope, { a: 'b' }); + expect(scope.tags).toEqual({ a: 'b' }); }); test('setUser', () => { const scope = new Scope(); - scope.setUser({ id: '1' }); - expect((scope as any)._user).toEqual({ id: '1' }); + setScopeUser(scope, { id: '1' }); + expect(scope.user).toEqual({ id: '1' }); }); test('setUser with null unsets the user', () => { const scope = new Scope(); - scope.setUser({ id: '1' }); - scope.setUser(null); - expect((scope as any)._user).toEqual({}); + setScopeUser(scope, { id: '1' }); + setScopeUser(scope, null); + expect(scope.user).toEqual({}); }); test('addBreadcrumb', () => { const scope = new Scope(); - scope.addBreadcrumb({ message: 'test' }); - expect((scope as any)._breadcrumbs[0]).toHaveProperty('message', 'test'); + addScopeBreadcrumb(scope, { message: 'test' }); + expect(scope.breadcrumbs[0]).toHaveProperty('message', 'test'); }); test('addBreadcrumb can be limited to hold up to N breadcrumbs', () => { const scope = new Scope(); for (let i = 0; i < 10; i++) { - scope.addBreadcrumb({ message: 'test' }, 5); + addScopeBreadcrumb(scope, { message: 'test' }, 5); } - expect((scope as any)._breadcrumbs).toHaveLength(5); + expect(scope.breadcrumbs).toHaveLength(5); }); test('addBreadcrumb cannot go over MAX_BREADCRUMBS value', () => { const scope = new Scope(); for (let i = 0; i < 111; i++) { - scope.addBreadcrumb({ message: 'test' }, 111); + addScopeBreadcrumb(scope, { message: 'test' }, 111); } - expect((scope as any)._breadcrumbs).toHaveLength(100); + expect(scope.breadcrumbs).toHaveLength(100); }); test('setLevel', () => { const scope = new Scope(); - scope.setLevel('critical'); - expect((scope as any)._level).toEqual('critical'); + setScopeLevel(scope, 'critical'); + expect(scope.level).toEqual('critical'); }); test('setTransactionName', () => { const scope = new Scope(); - scope.setTransactionName('/abc'); - expect((scope as any)._transactionName).toEqual('/abc'); + setTransactionName(scope, '/abc'); + expect(scope.transactionName).toEqual('/abc'); }); test('setTransactionName with no value unsets it', () => { const scope = new Scope(); - scope.setTransactionName('/abc'); - scope.setTransactionName(); - expect((scope as any)._transactionName).toBeUndefined(); + setTransactionName(scope, '/abc'); + setTransactionName(scope); + expect(scope.transactionName).toBeUndefined(); }); test('setContext', () => { const scope = new Scope(); - scope.setContext('os', { id: '1' }); - expect((scope as any)._contexts.os).toEqual({ id: '1' }); + setScopeContext(scope, 'os', { id: '1' }); + expect(scope.contexts.os).toEqual({ id: '1' }); }); test('setContext with null unsets it', () => { const scope = new Scope(); - scope.setContext('os', { id: '1' }); - scope.setContext('os', null); - expect((scope as any)._user).toEqual({}); + setScopeContext(scope, 'os', { id: '1' }); + setScopeContext(scope, 'os', null); + expect(scope.user).toEqual({}); }); test('setSpan', () => { const scope = new Scope(); const span = { fake: 'span' } as any; - scope.setSpan(span); - expect((scope as any)._span).toEqual(span); + setScopeSpan(scope, span); + expect(scope.span).toEqual(span); }); test('setSpan with no value unsets it', () => { const scope = new Scope(); - scope.setSpan({ fake: 'span' } as any); - scope.setSpan(); - expect((scope as any)._span).toEqual(undefined); + const span = { fake: 'span' } as any; + setScopeSpan(scope, span); + setScopeSpan(scope); + expect(scope.span).toEqual(undefined); }); test('chaining', () => { const scope = new Scope(); - scope.setLevel('critical').setUser({ id: '1' }); - expect((scope as any)._level).toEqual('critical'); - expect((scope as any)._user).toEqual({ id: '1' }); + setScopeLevel(scope, 'critical'); + setScopeUser(scope, { id: '1' }); + expect(scope.level).toEqual('critical'); + expect(scope.user).toEqual({ id: '1' }); }); }); describe('clone', () => { test('basic inheritance', () => { const parentScope = new Scope(); - parentScope.setExtra('a', 1); + setScopeExtra(parentScope, 'a', 1); const scope = cloneScope(parentScope); - expect((parentScope as any)._extra).toEqual((scope as any)._extra); + expect(parentScope.extra).toEqual(scope.extra); }); test('_requestSession clone', () => { const parentScope = new Scope(); - parentScope.setRequestSession({ status: 'errored' }); + setScopeRequestSession(parentScope, { status: 'errored' }); const scope = cloneScope(parentScope); - expect(parentScope.getRequestSession()).toEqual(scope.getRequestSession()); + expect(getScopeRequestSession(parentScope)).toEqual(getScopeRequestSession(scope)); }); test('parent changed inheritance', () => { const parentScope = new Scope(); const scope = cloneScope(parentScope); - parentScope.setExtra('a', 2); - expect((scope as any)._extra).toEqual({}); - expect((parentScope as any)._extra).toEqual({ a: 2 }); + setScopeExtra(parentScope, 'a', 2); + expect(scope.extra).toEqual({}); + expect(parentScope.extra).toEqual({ a: 2 }); }); test('child override inheritance', () => { const parentScope = new Scope(); - parentScope.setExtra('a', 1); + setScopeExtra(parentScope, 'a', 1); const scope = cloneScope(parentScope); - scope.setExtra('a', 2); - expect((parentScope as any)._extra).toEqual({ a: 1 }); - expect((scope as any)._extra).toEqual({ a: 2 }); + setScopeExtra(scope, 'a', 2); + expect(parentScope.extra).toEqual({ a: 1 }); + expect(scope.extra).toEqual({ a: 2 }); }); test('child override should set the value of parent _requestSession', () => { // Test that ensures if the status value of `status` of `_requestSession` is changed in a child scope // that it should also change in parent scope because we are copying the reference to the object const parentScope = new Scope(); - parentScope.setRequestSession({ status: 'errored' }); + setScopeRequestSession(parentScope, { status: 'errored' }); const scope = cloneScope(parentScope); - const requestSession = scope.getRequestSession(); + const requestSession = getScopeRequestSession(scope); if (requestSession) { requestSession.status = 'ok'; } - expect(parentScope.getRequestSession()).toEqual({ status: 'ok' }); - expect(scope.getRequestSession()).toEqual({ status: 'ok' }); + expect(getScopeRequestSession(parentScope)).toEqual({ status: 'ok' }); + expect(getScopeRequestSession(scope)).toEqual({ status: 'ok' }); }); }); @@ -191,16 +211,17 @@ describe('Scope', () => { test('basic usage', () => { expect.assertions(8); const scope = new Scope(); - scope.setExtra('a', 2); - scope.setTag('a', 'b'); - scope.setUser({ id: '1' }); - scope.setFingerprint(['abcd']); - scope.setLevel('warning'); - scope.setTransactionName('/abc'); - scope.addBreadcrumb({ message: 'test' }); - scope.setContext('os', { id: '1' }); + + setScopeExtra(scope, 'a', 2); + setScopeTag(scope, 'a', 'b'); + setScopeUser(scope, { id: '1' }); + setScopeFingerprint(scope, ['abcd']); + setScopeLevel(scope, 'warning'); + setTransactionName(scope, '/abc'); + addScopeBreadcrumb(scope, { message: 'test' }); + setScopeContext(scope, 'os', { id: '1' }); const event: Event = {}; - return scope.applyToEvent(event).then(processedEvent => { + return applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.extra).toEqual({ a: 2 }); expect(processedEvent!.tags).toEqual({ a: 'b' }); expect(processedEvent!.user).toEqual({ id: '1' }); @@ -215,12 +236,12 @@ describe('Scope', () => { test('merge with existing event data', () => { expect.assertions(8); const scope = new Scope(); - scope.setExtra('a', 2); - scope.setTag('a', 'b'); - scope.setUser({ id: '1' }); - scope.setFingerprint(['abcd']); - scope.addBreadcrumb({ message: 'test' }); - scope.setContext('server', { id: '2' }); + setScopeExtra(scope, 'a', 2); + setScopeTag(scope, 'a', 'b'); + setScopeUser(scope, { id: '1' }); + setScopeFingerprint(scope, ['abcd']); + addScopeBreadcrumb(scope, { message: 'test' }); + setScopeContext(scope, 'server', { id: '2' }); const event: Event = { breadcrumbs: [{ message: 'test1' }], contexts: { os: { id: '1' } }, @@ -229,7 +250,7 @@ describe('Scope', () => { tags: { b: 'c' }, user: { id: '3' }, }; - return scope.applyToEvent(event).then(processedEvent => { + return applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.extra).toEqual({ a: 2, b: 3 }); expect(processedEvent!.tags).toEqual({ a: 'b', b: 'c' }); expect(processedEvent!.user).toEqual({ id: '3' }); @@ -250,25 +271,25 @@ describe('Scope', () => { // @ts-ignore we want to be able to assign string value event.fingerprint = 'foo'; - await scope.applyToEvent(event).then(processedEvent => { + await applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.fingerprint).toEqual(['foo']); }); // @ts-ignore we want to be able to assign string value event.fingerprint = 'bar'; - await scope.applyToEvent(event).then(processedEvent => { + await applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.fingerprint).toEqual(['bar']); }); }); test('should merge fingerprint from event and scope', async () => { const scope = new Scope(); - scope.setFingerprint(['foo']); + setScopeFingerprint(scope, ['foo']); const event: Event = { fingerprint: ['bar'], }; - await scope.applyToEvent(event).then(processedEvent => { + await applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.fingerprint).toEqual(['bar', 'foo']); }); }); @@ -276,7 +297,7 @@ describe('Scope', () => { test('should remove default empty fingerprint array if theres no data available', async () => { const scope = new Scope(); const event: Event = {}; - await scope.applyToEvent(event).then(processedEvent => { + await applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.fingerprint).toEqual(undefined); }); }); @@ -284,10 +305,10 @@ describe('Scope', () => { test('scope level should have priority over event level', () => { expect.assertions(1); const scope = new Scope(); - scope.setLevel('warning'); + setScopeLevel(scope, 'warning'); const event: Event = {}; event.level = 'critical'; - return scope.applyToEvent(event).then(processedEvent => { + return applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.level).toEqual('warning'); }); }); @@ -295,10 +316,10 @@ describe('Scope', () => { test('scope transaction should have priority over event transaction', () => { expect.assertions(1); const scope = new Scope(); - scope.setTransactionName('/abc'); + setTransactionName(scope, '/abc'); const event: Event = {}; event.transaction = '/cdf'; - return scope.applyToEvent(event).then(processedEvent => { + return applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.transaction).toEqual('/abc'); }); }); @@ -311,10 +332,10 @@ describe('Scope', () => { fake: 'span', getTraceContext: () => ({ a: 'b' }), } as any; - scope.setSpan(span); + setScopeSpan(scope, span); const event: Event = {}; - return scope.applyToEvent(event).then(processedEvent => { - expect((processedEvent!.contexts!.trace as any).a).toEqual('b'); + return applyScopeToEvent(scope, event).then(processedEvent => { + expect(processedEvent!.contexts!.trace.a).toEqual('b'); }); }); @@ -325,14 +346,14 @@ describe('Scope', () => { fake: 'span', getTraceContext: () => ({ a: 'b' }), } as any; - scope.setSpan(span); + setScopeSpan(scope, span); const event: Event = { contexts: { trace: { a: 'c' }, }, }; - return scope.applyToEvent(event).then(processedEvent => { - expect((processedEvent!.contexts!.trace as any).a).toEqual('c'); + return applyScopeToEvent(scope, event).then(processedEvent => { + expect(processedEvent!.contexts!.trace.a).toEqual('c'); }); }); @@ -345,9 +366,9 @@ describe('Scope', () => { name: 'fake transaction', } as any; transaction.transaction = transaction; // because this is a transaction, its transaction pointer points to itself - scope.setSpan(transaction); + setScopeSpan(scope, transaction); const event: Event = {}; - return scope.applyToEvent(event).then(processedEvent => { + return applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.tags!.transaction).toEqual('fake transaction'); }); }); @@ -361,33 +382,33 @@ describe('Scope', () => { getTraceContext: () => ({ a: 'b' }), transaction, } as any; - scope.setSpan(span); + setScopeSpan(scope, span); const event: Event = {}; - return scope.applyToEvent(event).then(processedEvent => { + return applyScopeToEvent(scope, event).then(processedEvent => { expect(processedEvent!.tags!.transaction).toEqual('fake transaction'); }); }); test('clear', () => { const scope = new Scope(); - scope.setExtra('a', 2); - scope.setTag('a', 'b'); - scope.setUser({ id: '1' }); - scope.setFingerprint(['abcd']); - scope.addBreadcrumb({ message: 'test' }); - scope.setRequestSession({ status: 'ok' }); - expect((scope as any)._extra).toEqual({ a: 2 }); - scope.clear(); - expect((scope as any)._extra).toEqual({}); - expect((scope as any)._requestSession).toEqual(undefined); + setScopeExtra(scope, 'a', 2); + setScopeTag(scope, 'a', 'b'); + setScopeUser(scope, { id: '1' }); + setScopeFingerprint(scope, ['abcd']); + addScopeBreadcrumb(scope, { message: 'test' }); + setScopeRequestSession(scope, { status: 'ok' }); + expect(scope.extra).toEqual({ a: 2 }); + clearScope(scope); + expect(scope.extra).toEqual({}); + expect(scope.requestSession).toEqual(undefined); }); test('clearBreadcrumbs', () => { const scope = new Scope(); - scope.addBreadcrumb({ message: 'test' }); - expect((scope as any)._breadcrumbs).toHaveLength(1); - scope.clearBreadcrumbs(); - expect((scope as any)._breadcrumbs).toHaveLength(0); + addScopeBreadcrumb(scope, { message: 'test' }); + expect(scope.breadcrumbs).toHaveLength(1); + clearScopeBreadcrumbs(scope); + expect(scope.breadcrumbs).toHaveLength(0); }); describe('update', () => { @@ -395,24 +416,24 @@ describe('Scope', () => { beforeEach(() => { scope = new Scope(); - scope.setTags({ foo: '1', bar: '2' }); - scope.setExtras({ foo: '1', bar: '2' }); - scope.setContext('foo', { id: '1' }); - scope.setContext('bar', { id: '2' }); - scope.setUser({ id: '1337' }); - scope.setLevel('info'); - scope.setFingerprint(['foo']); - scope.setRequestSession({ status: 'ok' }); + setScopeTags(scope, { foo: '1', bar: '2' }); + setScopeExtras(scope, { foo: '1', bar: '2' }); + setScopeContext(scope, 'foo', { id: '1' }); + setScopeContext(scope, 'bar', { id: '2' }); + setScopeUser(scope, { id: '1337' }); + setScopeLevel(scope, 'info'); + setScopeFingerprint(scope, ['foo']); + setScopeRequestSession(scope, { status: 'ok' }); }); test('given no data, returns the original scope', () => { - const updatedScope = scope.update(); + const updatedScope = updateScope(scope); expect(updatedScope).toEqual(scope); }); test('given neither function, Scope or plain object, returns original scope', () => { // @ts-ignore we want to be able to update scope with string - const updatedScope = scope.update('wat'); + const updatedScope = updateScope(scope, 'wat'); expect(updatedScope).toEqual(scope); }); @@ -421,79 +442,79 @@ describe('Scope', () => { .fn() .mockImplementationOnce(v => v) .mockImplementationOnce(v => { - v.setTag('foo', 'bar'); + setScopeTag(v, 'foo', 'bar'); return v; }); - let updatedScope = scope.update(cb); + let updatedScope = updateScope(scope, cb); expect(cb).toHaveBeenNthCalledWith(1, scope); expect(updatedScope).toEqual(scope); - updatedScope = scope.update(cb); + updatedScope = updateScope(scope, cb); expect(cb).toHaveBeenNthCalledWith(2, scope); expect(updatedScope).toEqual(scope); }); test('given callback function, when it doesnt return instanceof Scope, ignore it and return original scope', () => { const cb = jest.fn().mockImplementationOnce(_v => 'wat'); - const updatedScope = scope.update(cb); + const updatedScope = updateScope(scope, cb); expect(cb).toHaveBeenCalledWith(scope); expect(updatedScope).toEqual(scope); }); test('given another instance of Scope, it should merge two together, with the passed scope having priority', () => { const localScope = new Scope(); - localScope.setTags({ bar: '3', baz: '4' }); - localScope.setExtras({ bar: '3', baz: '4' }); - localScope.setContext('bar', { id: '3' }); - localScope.setContext('baz', { id: '4' }); - localScope.setUser({ id: '42' }); - localScope.setLevel('warning'); - localScope.setFingerprint(['bar']); - (localScope as any)._requestSession = { status: 'ok' }; - - const updatedScope = scope.update(localScope) as any; - - expect(updatedScope._tags).toEqual({ + setScopeTags(localScope, { bar: '3', baz: '4' }); + setScopeExtras(localScope, { bar: '3', baz: '4' }); + setScopeContext(localScope, 'bar', { id: '3' }); + setScopeContext(localScope, 'baz', { id: '4' }); + setScopeUser(localScope, { id: '42' }); + setScopeLevel(localScope, 'warning'); + setScopeFingerprint(localScope, ['bar']); + localScope.requestSession = { status: 'ok' }; + + const updatedScope = updateScope(scope, localScope); + + expect(updatedScope.tags).toEqual({ bar: '3', baz: '4', foo: '1', }); - expect(updatedScope._extra).toEqual({ + expect(updatedScope.extra).toEqual({ bar: '3', baz: '4', foo: '1', }); - expect(updatedScope._contexts).toEqual({ + expect(updatedScope.contexts).toEqual({ bar: { id: '3' }, baz: { id: '4' }, foo: { id: '1' }, }); - expect(updatedScope._user).toEqual({ id: '42' }); - expect(updatedScope._level).toEqual('warning'); - expect(updatedScope._fingerprint).toEqual(['bar']); - expect(updatedScope._requestSession.status).toEqual('ok'); + expect(updatedScope.user).toEqual({ id: '42' }); + expect(updatedScope.level).toEqual('warning'); + expect(updatedScope.fingerprint).toEqual(['bar']); + expect(updatedScope.requestSession?.status).toEqual('ok'); }); test('given an empty instance of Scope, it should preserve all the original scope data', () => { - const updatedScope = scope.update(new Scope()) as any; + const updatedScope = updateScope(scope, new Scope()); - expect(updatedScope._tags).toEqual({ + expect(updatedScope.tags).toEqual({ bar: '2', foo: '1', }); - expect(updatedScope._extra).toEqual({ + expect(updatedScope.extra).toEqual({ bar: '2', foo: '1', }); - expect(updatedScope._contexts).toEqual({ + expect(updatedScope.contexts).toEqual({ bar: { id: '2' }, foo: { id: '1' }, }); - expect(updatedScope._user).toEqual({ id: '1337' }); - expect(updatedScope._level).toEqual('info'); - expect(updatedScope._fingerprint).toEqual(['foo']); - expect(updatedScope._requestSession.status).toEqual('ok'); + expect(updatedScope.user).toEqual({ id: '1337' }); + expect(updatedScope.level).toEqual('info'); + expect(updatedScope.fingerprint).toEqual(['foo']); + expect(updatedScope.requestSession?.status).toEqual('ok'); }); test('given a plain object, it should merge two together, with the passed object having priority', () => { @@ -506,27 +527,27 @@ describe('Scope', () => { user: { id: '42' }, requestSession: { status: 'errored' }, }; - const updatedScope = scope.update(localAttributes) as any; + const updatedScope = updateScope(scope, localAttributes); - expect(updatedScope._tags).toEqual({ + expect(updatedScope.tags).toEqual({ bar: '3', baz: '4', foo: '1', }); - expect(updatedScope._extra).toEqual({ + expect(updatedScope.extra).toEqual({ bar: '3', baz: '4', foo: '1', }); - expect(updatedScope._contexts).toEqual({ + expect(updatedScope.contexts).toEqual({ bar: { id: '3' }, baz: { id: '4' }, foo: { id: '1' }, }); - expect(updatedScope._user).toEqual({ id: '42' }); - expect(updatedScope._level).toEqual('warning'); - expect(updatedScope._fingerprint).toEqual(['bar']); - expect(updatedScope._requestSession).toEqual({ status: 'errored' }); + expect(updatedScope.user).toEqual({ id: '42' }); + expect(updatedScope.level).toEqual('warning'); + expect(updatedScope.fingerprint).toEqual(['bar']); + expect(updatedScope.requestSession).toEqual({ status: 'errored' }); }); }); @@ -537,21 +558,21 @@ describe('Scope', () => { extra: { b: 3 }, }; const localScope = new Scope(); - localScope.setExtra('a', 'b'); - localScope.addEventProcessor((processedEvent: Event) => { + setScopeExtra(localScope, 'a', 'b'); + addScopeEventProcessor(localScope, (processedEvent: Event) => { expect(processedEvent.extra).toEqual({ a: 'b', b: 3 }); return processedEvent; }); - localScope.addEventProcessor((processedEvent: Event) => { + addScopeEventProcessor(localScope, (processedEvent: Event) => { processedEvent.dist = '1'; return processedEvent; }); - localScope.addEventProcessor((processedEvent: Event) => { + addScopeEventProcessor(localScope, (processedEvent: Event) => { expect(processedEvent.dist).toEqual('1'); return processedEvent; }); - return localScope.applyToEvent(event).then(final => { + return applyScopeToEvent(localScope, event).then(final => { expect(final!.dist).toEqual('1'); }); }); @@ -562,24 +583,24 @@ describe('Scope', () => { extra: { b: 3 }, }; const localScope = new Scope(); - localScope.setExtra('a', 'b'); + setScopeExtra(localScope, 'a', 'b'); addGlobalEventProcessor((processedEvent: Event) => { processedEvent.dist = '1'; return processedEvent; }); - localScope.addEventProcessor((processedEvent: Event) => { + addScopeEventProcessor(localScope, (processedEvent: Event) => { expect(processedEvent.extra).toEqual({ a: 'b', b: 3 }); return processedEvent; }); - localScope.addEventProcessor((processedEvent: Event) => { + addScopeEventProcessor(localScope, (processedEvent: Event) => { expect(processedEvent.dist).toEqual('1'); return processedEvent; }); - return localScope.applyToEvent(event).then(final => { + return applyScopeToEvent(localScope, event).then(final => { expect(final!.dist).toEqual('1'); }); }); @@ -591,14 +612,15 @@ describe('Scope', () => { extra: { b: 3 }, }; const localScope = new Scope(); - localScope.setExtra('a', 'b'); + setScopeExtra(localScope, 'a', 'b'); const callCounter = jest.fn(); - localScope.addEventProcessor((processedEvent: Event) => { + addScopeEventProcessor(localScope, (processedEvent: Event) => { callCounter(1); expect(processedEvent.extra).toEqual({ a: 'b', b: 3 }); return processedEvent; }); - localScope.addEventProcessor( + addScopeEventProcessor( + localScope, async (processedEvent: Event) => new Promise(resolve => { callCounter(2); @@ -610,12 +632,12 @@ describe('Scope', () => { jest.runAllTimers(); }), ); - localScope.addEventProcessor((processedEvent: Event) => { + addScopeEventProcessor(localScope, (processedEvent: Event) => { callCounter(4); return processedEvent; }); - return localScope.applyToEvent(event).then(processedEvent => { + return applyScopeToEvent(localScope, event).then(processedEvent => { expect(callCounter.mock.calls[0][0]).toBe(1); expect(callCounter.mock.calls[1][0]).toBe(2); expect(callCounter.mock.calls[2][0]).toBe(3); @@ -631,14 +653,15 @@ describe('Scope', () => { extra: { b: 3 }, }; const localScope = new Scope(); - localScope.setExtra('a', 'b'); + setScopeExtra(localScope, 'a', 'b'); const callCounter = jest.fn(); - localScope.addEventProcessor((processedEvent: Event) => { + addScopeEventProcessor(localScope, (processedEvent: Event) => { callCounter(1); expect(processedEvent.extra).toEqual({ a: 'b', b: 3 }); return processedEvent; }); - localScope.addEventProcessor( + addScopeEventProcessor( + localScope, async (_processedEvent: Event) => new Promise((_, reject) => { setTimeout(() => { @@ -647,12 +670,12 @@ describe('Scope', () => { jest.runAllTimers(); }), ); - localScope.addEventProcessor((processedEvent: Event) => { + addScopeEventProcessor(localScope, (processedEvent: Event) => { callCounter(4); return processedEvent; }); - return localScope.applyToEvent(event).then(null, reason => { + return applyScopeToEvent(localScope, event).then(null, reason => { expect(reason).toEqual('bla'); }); }); @@ -663,9 +686,9 @@ describe('Scope', () => { extra: { b: 3 }, }; const localScope = new Scope(); - localScope.setExtra('a', 'b'); - localScope.addEventProcessor(async (_: Event) => null); - return localScope.applyToEvent(event).then(processedEvent => { + setScopeExtra(localScope, 'a', 'b'); + addScopeEventProcessor(localScope, async (_: Event) => null); + return applyScopeToEvent(localScope, event).then(processedEvent => { expect(processedEvent).toBeNull(); }); }); @@ -676,13 +699,13 @@ describe('Scope', () => { extra: { b: 3 }, }; const localScope = new Scope(); - localScope.setExtra('a', 'b'); - localScope.addEventProcessor(async (internalEvent: Event, hint?: EventHint) => { + setScopeExtra(localScope, 'a', 'b'); + addScopeEventProcessor(localScope, async (internalEvent: Event, hint?: EventHint) => { expect(hint).toBeTruthy(); expect(hint!.syntheticException).toBeTruthy(); return internalEvent; }); - return localScope.applyToEvent(event, { syntheticException: new Error('what') }).then(processedEvent => { + return applyScopeToEvent(localScope, event, { syntheticException: new Error('what') }).then(processedEvent => { expect(processedEvent).toEqual(event); }); }); @@ -691,11 +714,11 @@ describe('Scope', () => { jest.useFakeTimers(); const scope = new Scope(); const listener = jest.fn(); - scope.addScopeListener(listener); - scope.setExtra('a', 2); + addScopeListener(scope, listener); + setScopeExtra(scope, 'a', 2); jest.runAllTimers(); expect(listener).toHaveBeenCalled(); - expect(listener.mock.calls[0][0]._extra).toEqual({ a: 2 }); + expect(listener.mock.calls[0][0].extra).toEqual({ a: 2 }); }); }); }); diff --git a/packages/hub/test/sessionflusher.test.ts b/packages/hub/test/sessionflusher.test.ts index 15c3a3127f61..11ac9670154f 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -11,17 +11,17 @@ function makeTransporter() { return jest.fn(() => Promise.resolve({ status: 'success' as EventStatus })); } -jest.useFakeTimers(); - describe('Session Flusher', () => { beforeEach(() => jest.useFakeTimers()); afterEach(() => jest.clearAllTimers()); test('test incrementSessionStatusCount updates the internal SessionFlusher state', () => { + // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); - const date = new Date('2021-04-08T12:18:23.043Z'); + + // WHEN let count = _incrementSessionStatusCount(flusher, 'ok', date); expect(count).toEqual(1); count = _incrementSessionStatusCount(flusher, 'ok', date); @@ -42,13 +42,16 @@ describe('Session Flusher', () => { }); test('test undefined attributes are excluded, on incrementSessionStatusCount call', () => { + // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.0' }); - const date = new Date('2021-04-08T12:18:23.043Z'); + + // WHEN _incrementSessionStatusCount(flusher, 'ok', date); _incrementSessionStatusCount(flusher, 'errored', date); + // THEN expect(getSessionAggregates(flusher)).toEqual({ aggregates: [{ errored: 1, exited: 1, started: '2021-04-08T12:18:00.000Z' }], attrs: { release: '1.0.0' }, @@ -56,8 +59,13 @@ describe('Session Flusher', () => { }); test('flush is called every ~60 seconds after initialisation of an instance of SessionFlusher', () => { + // GIVEN const transporter = makeTransporter(); + + // WHEN new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); + + // THEN jest.advanceTimersByTime(59000); expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(2000); @@ -69,12 +77,16 @@ describe('Session Flusher', () => { }); test('transporter is called on flush if sessions were captured', () => { + // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); + + // WHEN _incrementSessionStatusCount(flusher, 'ok', date); _incrementSessionStatusCount(flusher, 'ok', date); + // THEN expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(61000); @@ -89,9 +101,13 @@ describe('Session Flusher', () => { }); test('transporter is not called on flush if no sessions were captured', () => { + // GIVEN const transporter = makeTransporter(); + + // WHEN new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); + // THEN expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(61000); expect(transporter).toHaveBeenCalledTimes(1); @@ -99,9 +115,14 @@ describe('Session Flusher', () => { }); test('calling close on SessionFlusher should disable SessionFlusher', () => { + // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.x' }); + + // WHEN closeSessionFlusher(flusher); + + // THEN expect(flusher.isEnabled).toEqual(false); }); diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index d1b53d3b2606..f20c4d922dfe 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -6,7 +6,8 @@ import { SeverityLevel } from './severity'; import { User } from './user'; /** JSDocs */ -export type CaptureContext = Scope | Partial | ((scope: Scope) => Scope); +export type CaptureContextCallback = (scope: Scope) => Scope; +export type CaptureContext = Scope | Partial | CaptureContextCallback; /** JSDocs */ export interface ScopeContext { @@ -19,4 +20,4 @@ export interface ScopeContext { requestSession: RequestSession; } -export interface Scope {} +export type Scope = any; diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index c0699417c36b..509c3a794dd7 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -44,3 +44,5 @@ export interface AggregationCounts { exited?: number; crashed?: number; } + +export type Session = any; From 7b72534efd4332090cf8f3d07662d4d73c81e802 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 00:34:10 -0500 Subject: [PATCH 23/37] chore: increase test coverage --- packages/hub/src/hub.ts | 22 +++--- packages/hub/src/index.ts | 2 +- packages/hub/src/scope.ts | 128 ++++++++++++++++---------------- packages/hub/test/scope.test.ts | 45 +++++++++-- 4 files changed, 115 insertions(+), 82 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index afdb14e8309a..8a605d01d7e2 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -23,15 +23,15 @@ import { consoleSandbox, dateTimestampInSeconds, getGlobalObject, isNodeEnv, log import { addScopeBreadcrumb, cloneScope, - getSession, - getUserScope, + getScopeUser, + getScopeSession, Scope, setScopeContext, setScopeExtra, setScopeExtras, - setSessionScope, + setScopeSession, setScopeTag, - setTagsScope, + setScopeTags, setScopeUser, } from './scope'; import { closeSession, Session, updateSession } from './session'; @@ -271,7 +271,7 @@ function _sendSessionUpdate(hub: Hub): void { const { scope, client } = getStackTop(hub); if (!scope) return; - const session = getSession(scope); + const session = getScopeSession(scope); if (session) { if (client && client.captureSession) { client.captureSession(session); @@ -285,7 +285,7 @@ function _sendSessionUpdate(hub: Hub): void { function endSession(hub: Hub): void { const layer = getStackTop(hub); const scope = layer && layer.scope; - const session = getSession(scope); + const session = getScopeSession(scope); if (session) { closeSession(session); } @@ -294,7 +294,7 @@ function endSession(hub: Hub): void { // the session is over; take it off of the scope if (scope) { - setSessionScope(scope); + setScopeSession(scope); } } @@ -323,21 +323,21 @@ export function startSession(hub: Hub, context?: SessionContext): Session { const session = new Session({ release, environment, - ...(scope && { user: getUserScope(scope) }), + ...(scope && { user: getScopeUser(scope) }), ...(userAgent && { userAgent }), ...context, }); if (scope) { // End existing session if there's one - const currentSession = getSession(scope); + const currentSession = getScopeSession(scope); if (currentSession && currentSession.status === 'ok') { updateSession(currentSession, { status: 'exited' }); } endSession(hub); // Afterwards we set the new session on the scope - setSessionScope(scope, session); + setScopeSession(scope, session); } return session; @@ -477,7 +477,7 @@ export function addBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: Breadcrum */ export function setTags(hub: Hub, tags: { [key: string]: Primitive }): void { const scope = getScope(hub); - if (scope) setTagsScope(scope, tags); + if (scope) setScopeTags(scope, tags); } /** diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 7cbf5dd2449e..aaec54f37528 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,4 +1,4 @@ -export { updateScope, applyScopeToEvent, addGlobalEventProcessor, cloneScope, getSession, Scope } from './scope'; +export { updateScope, applyScopeToEvent, addGlobalEventProcessor, cloneScope, getScopeSession, Scope } from './scope'; export { Session, updateSession } from './session'; export { SessionFlusher } from './sessionflusher'; export { diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index dce93aa98a37..4c097e48fee1 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -103,7 +103,7 @@ export function cloneScope(scope?: Scope): Scope { /** * Returns the `Session` if there is one */ -export function getSession(scope?: Scope): Session | undefined { +export function getScopeSession(scope?: Scope): Session | undefined { return scope && scope.session; } @@ -183,7 +183,7 @@ export function setScopeLevel(scope: Scope, level: SeverityLevel): Scope { /** * Sets the transaction name on the scope for future events. */ -export function setTransactionName(scope: Scope, name?: string): Scope { +export function setScopeTransactionName(scope: Scope, name?: string): Scope { scope.transactionName = name; return notifyListeners(scope); } @@ -191,8 +191,8 @@ export function setTransactionName(scope: Scope, name?: string): Scope { /** * Sets the transaction name on the scope for future events. */ -export function setTransaction(scope: Scope, name?: string): Scope { - return setTransactionName(scope, name); +export function setScopeTransaction(scope: Scope, name?: string): Scope { + return setScopeTransactionName(scope, name); } /** @@ -232,7 +232,7 @@ export function getScopeSpan(scope: Scope): Span | undefined { /** * Returns the `Transaction` attached to the scope (if there is one) */ -export function getTransaction(scope: Scope): Transaction | undefined { +export function getScopeTransaction(scope: Scope): Transaction | undefined { // often, this span will be a transaction, but it's not guaranteed to be const span = getScopeSpan(scope) as undefined | (Span & { spanRecorder: { spans: Span[] } }); @@ -267,7 +267,7 @@ export function setScopeUser(scope: Scope, user: User | null): Scope { /** * Returns the `User` if there is one */ -export function getUserScope(scope: Scope): User | undefined { +export function getScopeUser(scope: Scope): User | undefined { return scope.user; } @@ -302,7 +302,7 @@ export function setScopeRequestSession(scope: Scope, requestSession?: RequestSes /** * Sets the `Session` on the scope */ -export function setSessionScope(scope: Scope, session?: Session): Scope { +export function setScopeSession(scope: Scope, session?: Session): Scope { if (!session) { delete scope.session; } else { @@ -340,54 +340,6 @@ export function updateScope(scope: Scope, captureContext?: CaptureContext): Scop return scope; } -function mergeScopeContext(scope: Scope, captureContext: Partial): Scope { - scope.tags = { ...scope.tags, ...captureContext.tags }; - scope.extra = { ...scope.extra, ...captureContext.extra }; - scope.contexts = { ...scope.contexts, ...captureContext.contexts }; - if (captureContext.user) { - scope.user = captureContext.user; - } - if (captureContext.level) { - scope.level = captureContext.level; - } - if (captureContext.fingerprint) { - scope.fingerprint = captureContext.fingerprint; - } - if (captureContext.requestSession) { - scope.requestSession = captureContext.requestSession; - } - - return scope; -} - -function mergeScopes(scope: Scope, newScope: Scope): Scope { - scope.tags = { ...scope.tags, ...newScope.tags }; - scope.extra = { ...scope.extra, ...newScope.extra }; - scope.contexts = { ...scope.contexts, ...newScope.contexts }; - if (newScope.user && Object.keys(newScope.user).length) { - scope.user = newScope.user; - } - if (newScope.level) { - scope.level = newScope.level; - } - if (newScope.fingerprint) { - scope.fingerprint = newScope.fingerprint; - } - if (newScope.requestSession) { - scope.requestSession = newScope.requestSession; - } - - return scope; -} - -function isCaptureContextCallback(val: unknown): val is CaptureContextCallback { - return typeof val === 'function'; -} - -function isScopeContext(val: unknown): val is Partial { - return isPlainObject(val); -} - /** * Clears the current scope and resets its properties. * */ @@ -414,6 +366,7 @@ export function clearScope(scope: Scope): Scope { * @param maxBreadcrumbs number of max breadcrumbs to merged into event. */ export function addScopeBreadcrumb(scope: Scope, breadcrumb: Breadcrumb, maxBreadcrumbs?: number): Scope { + // TODO: Defensive programming checking for `number` const maxCrumbs = typeof maxBreadcrumbs === 'number' ? Math.min(maxBreadcrumbs, MAX_BREADCRUMBS) : MAX_BREADCRUMBS; // No data has been changed, so don't notify scope listeners @@ -485,6 +438,62 @@ export function applyScopeToEvent(scope: Scope, event: Event, hint?: EventHint): return notifyEventProcessors(scope, [...getGlobalEventProcessors(), ...scope.eventProcessors], event, hint); } +/** + * Add a EventProcessor to be kept globally. + * @param callback EventProcessor to add + */ +export function addGlobalEventProcessor(callback: EventProcessor): void { + getGlobalEventProcessors().push(callback); +} + +function mergeScopeContext(scope: Scope, captureContext: Partial): Scope { + scope.tags = { ...scope.tags, ...captureContext.tags }; + scope.extra = { ...scope.extra, ...captureContext.extra }; + scope.contexts = { ...scope.contexts, ...captureContext.contexts }; + if (captureContext.user) { + scope.user = captureContext.user; + } + if (captureContext.level) { + scope.level = captureContext.level; + } + if (captureContext.fingerprint) { + scope.fingerprint = captureContext.fingerprint; + } + if (captureContext.requestSession) { + scope.requestSession = captureContext.requestSession; + } + + return scope; +} + +function mergeScopes(scope: Scope, newScope: Scope): Scope { + scope.tags = { ...scope.tags, ...newScope.tags }; + scope.extra = { ...scope.extra, ...newScope.extra }; + scope.contexts = { ...scope.contexts, ...newScope.contexts }; + if (newScope.user && Object.keys(newScope.user).length) { + scope.user = newScope.user; + } + if (newScope.level) { + scope.level = newScope.level; + } + if (newScope.fingerprint) { + scope.fingerprint = newScope.fingerprint; + } + if (newScope.requestSession) { + scope.requestSession = newScope.requestSession; + } + + return scope; +} + +function isCaptureContextCallback(val: unknown): val is CaptureContextCallback { + return typeof val === 'function'; +} + +function isScopeContext(val: unknown): val is Partial { + return isPlainObject(val); +} + /** * This will be called after {@link applyScopeToEvent} is finished. */ @@ -553,6 +562,7 @@ function applyFingerprint(scope: Scope, event: Event): void { } } +// TODO: I would move this out of there and move it to some globals package like `getGlobalObject` is /** * Returns the global event processors. */ @@ -564,11 +574,3 @@ function getGlobalEventProcessors(): EventProcessor[] { return global.__SENTRY__.globalEventProcessors ?? []; /* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access */ } - -/** - * Add a EventProcessor to be kept globally. - * @param callback EventProcessor to add - */ -export function addGlobalEventProcessor(callback: EventProcessor): void { - getGlobalEventProcessors().push(callback); -} diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index e15b7e318d01..e345e73136b6 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -1,7 +1,15 @@ import { Event, EventHint } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils'; -import { addGlobalEventProcessor, applyScopeToEvent, cloneScope, Scope, updateScope } from '../src'; +import { + addGlobalEventProcessor, + applyScopeToEvent, + cloneScope, + getScopeSession, + Scope, + Session, + updateScope, +} from '../src'; import { addScopeBreadcrumb, addScopeEventProcessor, @@ -15,11 +23,12 @@ import { setScopeFingerprint, setScopeLevel, setScopeRequestSession, + setScopeSession, setScopeSpan, setScopeTag, setScopeTags, + setScopeTransactionName, setScopeUser, - setTransactionName, } from '../src/scope'; describe('Scope', () => { @@ -28,6 +37,28 @@ describe('Scope', () => { getGlobalObject().__SENTRY__.globalEventProcessors = undefined; }); + describe('setScopeSession', () => { + test('given an session then set the session to the scope', () => { + // GIVEN + const session = new Session(); + const scope = new Scope(); + // WHEN + setScopeSession(scope, session); + // THEN + expect(getScopeSession(scope)).toEqual(session); + }); + test('given an undefined or null session then removes the existing session', () => { + // GIVEN + const session = new Session(); + const scope = new Scope(); + setScopeSession(scope, session); + // WHEN + setScopeSession(scope, undefined); + // THEN + expect(getScopeSession(scope)).toBeUndefined(); + }); + }); + describe('attributes modification', () => { test('setFingerprint', () => { const scope = new Scope(); @@ -109,14 +140,14 @@ describe('Scope', () => { test('setTransactionName', () => { const scope = new Scope(); - setTransactionName(scope, '/abc'); + setScopeTransactionName(scope, '/abc'); expect(scope.transactionName).toEqual('/abc'); }); test('setTransactionName with no value unsets it', () => { const scope = new Scope(); - setTransactionName(scope, '/abc'); - setTransactionName(scope); + setScopeTransactionName(scope, '/abc'); + setScopeTransactionName(scope); expect(scope.transactionName).toBeUndefined(); }); @@ -217,7 +248,7 @@ describe('Scope', () => { setScopeUser(scope, { id: '1' }); setScopeFingerprint(scope, ['abcd']); setScopeLevel(scope, 'warning'); - setTransactionName(scope, '/abc'); + setScopeTransactionName(scope, '/abc'); addScopeBreadcrumb(scope, { message: 'test' }); setScopeContext(scope, 'os', { id: '1' }); const event: Event = {}; @@ -316,7 +347,7 @@ describe('Scope', () => { test('scope transaction should have priority over event transaction', () => { expect.assertions(1); const scope = new Scope(); - setTransactionName(scope, '/abc'); + setScopeTransactionName(scope, '/abc'); const event: Event = {}; event.transaction = '/cdf'; return applyScopeToEvent(scope, event).then(processedEvent => { From 434acc1ec7114562e734ceed33566d3612f08fc3 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 01:11:08 -0500 Subject: [PATCH 24/37] chore: fix tests --- packages/hub/src/index.ts | 6 +++--- packages/hub/src/session.ts | 2 +- packages/hub/test/scope.test.ts | 14 ++++++-------- packages/hub/test/session.test.ts | 18 +++++++++--------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index aaec54f37528..9c00300ec2e1 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -5,8 +5,6 @@ export { // eslint-disable-next-line deprecation/deprecation getActiveDomain, getCurrentHub, - getHubFromCarrier, - getMainCarrier, bindClient, popScope, pushScope, @@ -25,9 +23,11 @@ export { configureScope, Hub, makeMain, - setHubOnCarrier, Carrier, // eslint-disable-next-line deprecation/deprecation DomainAsCarrier, Layer, + getHubFromCarrier, + setHubOnCarrier, + getMainCarrier, } from './hub'; diff --git a/packages/hub/src/session.ts b/packages/hub/src/session.ts index cb1630e7faa6..23718c5b75e7 100644 --- a/packages/hub/src/session.ts +++ b/packages/hub/src/session.ts @@ -100,7 +100,7 @@ export function closeSession(session: Session, status?: Exclude { afterEach(() => { diff --git a/packages/hub/test/session.test.ts b/packages/hub/test/session.test.ts index f25e5ad4189b..9ead830b211a 100644 --- a/packages/hub/test/session.test.ts +++ b/packages/hub/test/session.test.ts @@ -1,11 +1,11 @@ import { SessionContext } from '@sentry/types'; import { timestampInSeconds } from '@sentry/utils'; -import { Session } from '../src/session'; +import { closeSession, Session, toJSON, updateSession } from '../src/session'; describe('Session', () => { it('initializes with the proper defaults', () => { - const session = new Session().toJSON(); + const session = toJSON(new Session()); // Grab current year to check if we are converting from sec -> ms correctly const currentYear = new Date(timestampInSeconds() * 1000).toISOString().slice(0, 4); @@ -83,10 +83,10 @@ describe('Session', () => { const DEFAULT_OUT = { duration: expect.any(Number), timestamp: expect.any(String) }; const session = new Session(); - const initSessionProps = session.toJSON(); + const initSessionProps = toJSON(session); - session.update(test[1]); - expect(session.toJSON()).toEqual({ ...initSessionProps, ...DEFAULT_OUT, ...test[2] }); + updateSession(session, test[1]); + expect(toJSON(session)).toEqual({ ...initSessionProps, ...DEFAULT_OUT, ...test[2] }); }); }); @@ -94,7 +94,7 @@ describe('Session', () => { it('exits a normal session', () => { const session = new Session(); expect(session.status).toEqual('ok'); - session.close(); + closeSession(session); expect(session.status).toEqual('exited'); }); @@ -102,16 +102,16 @@ describe('Session', () => { const session = new Session(); expect(session.status).toEqual('ok'); - session.close('abnormal'); + closeSession(session, 'abnormal'); expect(session.status).toEqual('abnormal'); }); it('only changes status ok to exited', () => { const session = new Session(); - session.update({ status: 'crashed' }); + updateSession(session, { status: 'crashed' }); expect(session.status).toEqual('crashed'); - session.close(); + closeSession(session); expect(session.status).toEqual('crashed'); }); }); From 581e9e75451112def49b97486dd934ae9e0dc126 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 01:44:16 -0500 Subject: [PATCH 25/37] chore: fix test cases --- packages/hub/src/hub.ts | 5 +- packages/hub/test/hub.test.ts | 208 ++++++++++++++++++---------------- 2 files changed, 116 insertions(+), 97 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 8a605d01d7e2..f014b5ea4b87 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -23,8 +23,8 @@ import { consoleSandbox, dateTimestampInSeconds, getGlobalObject, isNodeEnv, log import { addScopeBreadcrumb, cloneScope, - getScopeUser, getScopeSession, + getScopeUser, Scope, setScopeContext, setScopeExtra, @@ -606,11 +606,12 @@ export function traceHeaders(hub: Hub): { [key: string]: string } { /** * Internal helper function to call a method on the top client if it exists. * + * @param hub * @param method The method to call on the client. * @param args Arguments to pass to the client function. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -function _invokeClient(hub: Hub, method: M, ...args: any[]): void { +export function _invokeClient(hub: Hub, method: M, ...args: any[]): void { const { scope, client } = getStackTop(hub); if (client && client[method]) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any diff --git a/packages/hub/test/hub.test.ts b/packages/hub/test/hub.test.ts index b785ecf617b3..5d3cb9d42367 100644 --- a/packages/hub/test/hub.test.ts +++ b/packages/hub/test/hub.test.ts @@ -1,10 +1,44 @@ import { Event } from '@sentry/types'; -import { getCurrentHub, bindClient, pushScope, Hub, Scope } from '../src'; -import { getStackTop, isOlderThan, getStack } from '../src/hub'; +import { + _invokeClient, + addBreadcrumb, + bindClient, + captureEvent, + captureException, + captureMessage, + configureScope, + getClient, + getCurrentHub, + getScope, + getStack, + getStackTop, + Hub, + isOlderThan, + lastEventId, + popScope, + pushScope, + run, + withScope, +} from '../src/hub'; +import { addScopeBreadcrumb, addScopeEventProcessor, applyScopeToEvent, Scope, setScopeExtra } from '../src/scope'; const clientFn: any = jest.fn(); +function makeClient() { + return { + getOptions: jest.fn(), + captureEvent: jest.fn(), + captureException: jest.fn(), + close: jest.fn(), + flush: jest.fn(), + getDsn: jest.fn(), + getIntegration: jest.fn(), + setupIntegrations: jest.fn(), + captureMessage: jest.fn(), + }; +} + describe('Hub', () => { afterEach(() => { jest.restoreAllMocks(); @@ -12,10 +46,9 @@ describe('Hub', () => { }); test('call bindClient with provided client when constructing new instance', () => { - const testClient: any = { setupIntegrations: jest.fn() }; - const spy = jest.spyOn(Hub.prototype, 'bindClient'); - new Hub(testClient); - expect(spy).toHaveBeenCalledWith(testClient); + const testClient = makeClient(); + const hub = new Hub(testClient); + expect(getStackTop(hub).client).toBe(testClient); }); test('push process into stack', () => { @@ -31,7 +64,7 @@ describe('Hub', () => { test("don't invoke client sync with wrong func", () => { const hub = new Hub(clientFn); // @ts-ignore we want to able to call private method - hub._invokeClient('funca', true); + _invokeClient(hub, 'funca', true); expect(clientFn).not.toHaveBeenCalled(); }); @@ -43,12 +76,12 @@ describe('Hub', () => { describe('pushScope', () => { test('simple', () => { const localScope = new Scope(); - localScope.setExtra('a', 'b'); + setScopeExtra(localScope, 'a', 'b'); const hub = new Hub(undefined, localScope); pushScope(hub); expect(getStack(hub)).toHaveLength(2); expect(getStack(hub)[1].scope).not.toBe(localScope); - expect(((getStack(hub)[1].scope as Scope) as any)._extra).toEqual({ a: 'b' }); + expect((getStack(hub)[1].scope as Scope).extra).toEqual({ a: 'b' }); }); test('inherit client', () => { @@ -61,8 +94,8 @@ describe('Hub', () => { describe('bindClient', () => { test('should override current client', () => { - const testClient: any = { setupIntegrations: jest.fn() }; - const nextClient: any = { setupIntegrations: jest.fn() }; + const testClient = makeClient(); + const nextClient = makeClient(); const hub = new Hub(testClient); bindClient(hub, nextClient); expect(getStack(hub)).toHaveLength(1); @@ -81,8 +114,8 @@ describe('Hub', () => { }); test('should call setupIntegration method of passed client', () => { - const testClient: any = { setupIntegrations: jest.fn() }; - const nextClient: any = { setupIntegrations: jest.fn() }; + const testClient = makeClient(); + const nextClient = makeClient(); const hub = new Hub(testClient); bindClient(hub, nextClient); expect(testClient.setupIntegrations).toHaveBeenCalled(); @@ -96,10 +129,10 @@ describe('Hub', () => { extra: { b: 3 }, }; const localScope = new Scope(); - localScope.setExtra('a', 'b'); + setScopeExtra(localScope, 'a', 'b'); const hub = new Hub({ a: 'b' } as any, localScope); - localScope.addEventProcessor(async (processedEvent: Event) => { + addScopeEventProcessor(localScope, async (processedEvent: Event) => { processedEvent.dist = '1'; return processedEvent; }); @@ -107,7 +140,7 @@ describe('Hub', () => { pushScope(hub); const pushedScope = getStackTop(hub).scope; - return pushedScope!.applyToEvent(event).then(final => { + return applyScopeToEvent(pushedScope!, event).then(final => { expect(final!.dist).toEqual('1'); }); }); @@ -117,7 +150,7 @@ describe('Hub', () => { const hub = new Hub(); pushScope(hub); expect(getStack(hub)).toHaveLength(2); - hub.popScope(); + popScope(hub); expect(getStack(hub)).toHaveLength(1); }); @@ -148,7 +181,7 @@ describe('Hub', () => { test('should bubble up exceptions', () => { const error = new Error('test'); expect(() => { - hub.withScope(() => { + withScope(hub, () => { throw error; }); }).toThrow(error); @@ -158,7 +191,7 @@ describe('Hub', () => { test('getCurrentClient', () => { const testClient: any = { bla: 'a' }; const hub = new Hub(testClient); - expect(hub.getClient()).toBe(testClient); + expect(getClient(hub)).toBe(testClient); }); test('getStack', () => { @@ -179,129 +212,115 @@ describe('Hub', () => { describe('configureScope', () => { test('should have an access to provide scope', () => { const localScope = new Scope(); - localScope.setExtra('a', 'b'); + setScopeExtra(localScope, 'a', 'b'); const hub = new Hub({} as any, localScope); const cb = jest.fn(); - hub.configureScope(cb); + configureScope(hub, cb); expect(cb).toHaveBeenCalledWith(localScope); }); test('should not invoke without client and scope', () => { const hub = new Hub(); const cb = jest.fn(); - hub.configureScope(cb); + configureScope(hub, cb); expect(cb).not.toHaveBeenCalled(); }); }); describe('captureException', () => { test('simple', () => { - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureException('a'); - expect(spy).toHaveBeenCalled(); - expect(spy.mock.calls[0][0]).toBe('captureException'); - expect(spy.mock.calls[0][1]).toBe('a'); + const testClient = makeClient(); + const hub = new Hub(testClient); + captureException(hub, 'a'); + expect(testClient.captureException).toHaveBeenCalled(); + expect(testClient.captureException.mock.calls[0][0]).toBe('a'); }); test('should set event_id in hint', () => { - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureException('a'); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][2].event_id).toBeTruthy(); + const testClient = makeClient(); + const hub = new Hub(testClient); + captureException(hub, 'a'); + expect(testClient.captureException.mock.calls[0][1].event_id).toBeTruthy(); }); test('should generate hint if not provided in the call', () => { - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); + const testClient = makeClient(); + const hub = new Hub(testClient); const ex = new Error('foo'); - hub.captureException(ex); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][2].originalException).toBe(ex); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][2].syntheticException).toBeInstanceOf(Error); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][2].syntheticException.message).toBe('Sentry syntheticException'); + captureException(hub, ex); + expect(testClient.captureException.mock.calls[0][1].originalException).toBe(ex); + expect(testClient.captureException.mock.calls[0][1].syntheticException).toBeInstanceOf(Error); + expect(testClient.captureException.mock.calls[0][1].syntheticException.message).toBe('Sentry syntheticException'); }); }); describe('captureMessage', () => { test('simple', () => { - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureMessage('a'); - expect(spy).toHaveBeenCalled(); - expect(spy.mock.calls[0][0]).toBe('captureMessage'); - expect(spy.mock.calls[0][1]).toBe('a'); + const testClient = makeClient(); + const hub = new Hub(testClient); + captureMessage(hub, 'a'); + expect(testClient.captureMessage).toHaveBeenCalled(); + expect(testClient.captureMessage.mock.calls[0][0]).toBe('a'); }); test('should set event_id in hint', () => { - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureMessage('a'); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][3].event_id).toBeTruthy(); + const testClient = makeClient(); + const hub = new Hub(testClient); + captureMessage(hub, 'a'); + expect(testClient.captureMessage.mock.calls[0][2].event_id).toBeTruthy(); }); test('should generate hint if not provided in the call', () => { - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureMessage('foo'); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][3].originalException).toBe('foo'); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][3].syntheticException).toBeInstanceOf(Error); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][3].syntheticException.message).toBe('foo'); + const testClient = makeClient(); + const hub = new Hub(testClient); + captureMessage(hub, 'foo'); + expect(testClient.captureMessage.mock.calls[0][2].originalException).toBe('foo'); + expect(testClient.captureMessage.mock.calls[0][2].syntheticException).toBeInstanceOf(Error); + expect(testClient.captureMessage.mock.calls[0][2].syntheticException.message).toBe('foo'); }); }); describe('captureEvent', () => { test('simple', () => { + const testClient = makeClient(); const event: Event = { extra: { b: 3 }, }; - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureEvent(event); - expect(spy).toHaveBeenCalled(); - expect(spy.mock.calls[0][0]).toBe('captureEvent'); - expect(spy.mock.calls[0][1]).toBe(event); + const hub = new Hub(testClient); + captureEvent(hub, event); + expect(testClient.captureEvent).toHaveBeenCalled(); + expect(testClient.captureEvent.mock.calls[0][0]).toBe(event); }); test('should set event_id in hint', () => { + const testClient = makeClient(); const event: Event = { extra: { b: 3 }, }; - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureEvent(event); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][2].event_id).toBeTruthy(); + const hub = new Hub(testClient); + captureEvent(hub, event); + expect(testClient.captureEvent.mock.calls[0][1].event_id).toBeTruthy(); }); test('sets lastEventId', () => { + const testClient = makeClient(); const event: Event = { extra: { b: 3 }, }; - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureEvent(event); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][2].event_id).toEqual(hub.lastEventId()); + const hub = new Hub(testClient); + captureEvent(hub, event); + expect(testClient.captureEvent.mock.calls[0][1].event_id).toEqual(lastEventId(hub)); }); test('transactions do not set lastEventId', () => { + const testClient = makeClient(); const event: Event = { extra: { b: 3 }, type: 'transaction', }; - const hub = new Hub(); - const spy = jest.spyOn(hub as any, '_invokeClient'); - hub.captureEvent(event); - // @ts-ignore Says mock object is type unknown - expect(spy.mock.calls[0][2].event_id).not.toEqual(hub.lastEventId()); + const hub = new Hub(testClient); + captureEvent(hub, event); + expect(testClient.captureEvent.mock.calls[0][1].event_id).not.toEqual(lastEventId(hub)); }); }); @@ -310,8 +329,8 @@ describe('Hub', () => { extra: { b: 3 }, }; const hub = new Hub(); - const eventId = hub.captureEvent(event); - expect(eventId).toBe(hub.lastEventId()); + const eventId = captureEvent(hub, event); + expect(eventId).toBe(lastEventId(hub)); }); describe('run', () => { @@ -319,11 +338,11 @@ describe('Hub', () => { const currentHub = getCurrentHub(); const myScope = new Scope(); const myClient: any = { a: 'b' }; - myScope.setExtra('a', 'b'); + setScopeExtra(myScope, 'a', 'b'); const myHub = new Hub(myClient, myScope); - myHub.run(hub => { - expect(hub.getScope()).toBe(myScope); - expect(hub.getClient()).toBe(myClient); + run(myHub, hub => { + expect(getScope(hub)).toBe(myScope); + expect(getClient(hub)).toBe(myClient); expect(hub).toBe(getCurrentHub()); }); expect(currentHub).toBe(getCurrentHub()); @@ -333,7 +352,7 @@ describe('Hub', () => { const hub = new Hub(); const error = new Error('test'); expect(() => { - hub.run(() => { + run(hub, () => { throw error; }); }).toThrow(error); @@ -344,12 +363,11 @@ describe('Hub', () => { test('withScope', () => { expect.assertions(6); const hub = new Hub(clientFn); - hub.addBreadcrumb({ message: 'My Breadcrumb' }); - hub.withScope(scope => { - scope.addBreadcrumb({ message: 'scope breadcrumb' }); + addBreadcrumb(hub, { message: 'My Breadcrumb' }); + withScope(hub, scope => { + addScopeBreadcrumb(scope, { message: 'scope breadcrumb' }); const event: Event = {}; - void scope - .applyToEvent(event) + void applyScopeToEvent(scope, event) .then((appliedEvent: Event | null) => { expect(appliedEvent).toBeTruthy(); expect(appliedEvent!.breadcrumbs).toHaveLength(2); From 07abdbeeb7cfcba0ffee80e9d998663a1aca0949 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 02:15:03 -0500 Subject: [PATCH 26/37] chore: fix issues --- packages/hub/test/sessionflusher.test.ts | 29 +++++++++--------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/packages/hub/test/sessionflusher.test.ts b/packages/hub/test/sessionflusher.test.ts index 11ac9670154f..a358bb0725cc 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -59,34 +59,35 @@ describe('Session Flusher', () => { }); test('flush is called every ~60 seconds after initialisation of an instance of SessionFlusher', () => { - // GIVEN const transporter = makeTransporter(); + const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); - // WHEN - new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); - - // THEN jest.advanceTimersByTime(59000); expect(transporter).toHaveBeenCalledTimes(0); + + _incrementSessionStatusCount(flusher, 'ok', new Date()); jest.advanceTimersByTime(2000); + + _incrementSessionStatusCount(flusher, 'ok', new Date()); expect(transporter).toHaveBeenCalledTimes(1); + + _incrementSessionStatusCount(flusher, 'ok', new Date()); jest.advanceTimersByTime(58000); expect(transporter).toHaveBeenCalledTimes(1); + + _incrementSessionStatusCount(flusher, 'ok', new Date()); jest.advanceTimersByTime(2000); expect(transporter).toHaveBeenCalledTimes(2); }); test('transporter is called on flush if sessions were captured', () => { - // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); - // WHEN _incrementSessionStatusCount(flusher, 'ok', date); _incrementSessionStatusCount(flusher, 'ok', date); - // THEN expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(61000); @@ -101,33 +102,27 @@ describe('Session Flusher', () => { }); test('transporter is not called on flush if no sessions were captured', () => { - // GIVEN const transporter = makeTransporter(); - // WHEN - new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); - // THEN expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(61000); + expect(transporter).toHaveBeenCalledTimes(1); expect(transporter).toHaveBeenCalledTimes(0); }); test('calling close on SessionFlusher should disable SessionFlusher', () => { - // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.x' }); - // WHEN closeSessionFlusher(flusher); - // THEN expect(flusher.isEnabled).toEqual(false); }); test('calling close on SessionFlusher will force call flush', () => { - // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.x' }); const date = new Date('2021-04-08T12:18:23.043Z'); @@ -137,10 +132,8 @@ describe('Session Flusher', () => { _incrementSessionStatusCount(flusher, 'ok', date); _incrementSessionStatusCount(flusher, 'ok', date); - // WHEN closeSessionFlusher(flusher); - // THEN expect(flusher.isEnabled).toEqual(false); expect(flusher.pendingAggregates).toEqual({}); expect(transporter).toHaveBeenCalledWith( From eff9fd4a16f63e2464e7bda76033e6d8d14b719b Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 02:22:02 -0500 Subject: [PATCH 27/37] chore: fix test --- packages/hub/test/sessionflusher.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/hub/test/sessionflusher.test.ts b/packages/hub/test/sessionflusher.test.ts index a358bb0725cc..947cb3502372 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -104,12 +104,10 @@ describe('Session Flusher', () => { test('transporter is not called on flush if no sessions were captured', () => { const transporter = makeTransporter(); - const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); + new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(61000); - - expect(transporter).toHaveBeenCalledTimes(1); expect(transporter).toHaveBeenCalledTimes(0); }); From 51976cb13497932fdddd697f625760d26f12ecff Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 02:38:17 -0500 Subject: [PATCH 28/37] fix node client --- packages/hub/test/sessionflusher.test.ts | 5 ----- packages/node/src/client.ts | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/hub/test/sessionflusher.test.ts b/packages/hub/test/sessionflusher.test.ts index 947cb3502372..be49658e2ebd 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -16,12 +16,10 @@ describe('Session Flusher', () => { afterEach(() => jest.clearAllTimers()); test('test incrementSessionStatusCount updates the internal SessionFlusher state', () => { - // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); - // WHEN let count = _incrementSessionStatusCount(flusher, 'ok', date); expect(count).toEqual(1); count = _incrementSessionStatusCount(flusher, 'ok', date); @@ -42,16 +40,13 @@ describe('Session Flusher', () => { }); test('test undefined attributes are excluded, on incrementSessionStatusCount call', () => { - // GIVEN const transporter = makeTransporter(); const flusher = new SessionFlusher(transporter, { release: '1.0.0' }); const date = new Date('2021-04-08T12:18:23.043Z'); - // WHEN _incrementSessionStatusCount(flusher, 'ok', date); _incrementSessionStatusCount(flusher, 'errored', date); - // THEN expect(getSessionAggregates(flusher)).toEqual({ aggregates: [{ errored: 1, exited: 1, started: '2021-04-08T12:18:00.000Z' }], attrs: { release: '1.0.0' }, diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index c5f19ed611d0..a41cf0f16e18 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -98,7 +98,7 @@ export class NodeClient extends BaseClient { if (!release) { logger.warn('Cannot initialise an instance of SessionFlusher if no release is provided!'); } else { - this._sessionFlusher = new SessionFlusher(this.getTransport(), { + this._sessionFlusher = new SessionFlusher(this.getTransport().sendSession!, { release, environment, }); From 7c7bee2940812455904fe8164025eb9658ed0a1b Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 03:03:13 -0500 Subject: [PATCH 29/37] chore: change session flusher api and hub functions --- packages/hub/src/hub.ts | 246 +++++++++++------------ packages/hub/src/index.ts | 30 +-- packages/hub/src/sessionflusher.ts | 32 +-- packages/hub/test/hub.test.ts | 148 +++++++------- packages/hub/test/sessionflusher.test.ts | 18 +- 5 files changed, 240 insertions(+), 234 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index f014b5ea4b87..5c15767c084b 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -117,9 +117,9 @@ export class Hub { */ public constructor(client?: Client, scope: Scope = new Scope(), version: number = API_VERSION) { this.version = version; - getStackTop(this).scope = scope; + getHubStackTop(this).scope = scope; if (client) { - bindClient(this, client); + bindHubClient(this, client); } } } @@ -129,12 +129,12 @@ export class Hub { * * @hidden * */ -export function getStackTop(hub: Hub): Layer { +export function getHubStackTop(hub: Hub): Layer { return hub.stack[hub.stack.length - 1]; } /** Returns the scope stack for domains or the process. */ -export function getStack(hub: Hub): Layer[] { +export function getHubStack(hub: Hub): Layer[] { return hub.stack; } @@ -143,8 +143,8 @@ export function getStack(hub: Hub): Layer[] { * @param hub The Hub instance. * @param client An SDK client (client) instance. */ -export function bindClient(hub: Hub, client?: Client): void { - const top = getStackTop(hub); +export function bindHubClient(hub: Hub, client?: Client): void { + const top = getHubStackTop(hub); top.client = client; if (client && client.setupIntegrations) { client.setupIntegrations(); @@ -155,12 +155,12 @@ export function bindClient(hub: Hub, client?: Client): void { * Removes a previously pushed scope from the stack. * * This restores the state before the scope was pushed. All breadcrumbs and - * context information added since the last call to {@link pushScope} are + * context information added since the last call to {@link pushHubScope} are * discarded. */ -export function popScope(hub: Hub): boolean { - if (getStack(hub).length <= 1) return false; - return !!getStack(hub).pop(); +export function popHubScope(hub: Hub): boolean { + if (getHubStack(hub).length <= 1) return false; + return !!getHubStack(hub).pop(); } /** @@ -168,16 +168,16 @@ export function popScope(hub: Hub): boolean { * * The scope will be layered on top of the current one. It is isolated, i.e. all * breadcrumbs and context information added to this scope will be removed once - * the scope ends. Be sure to always remove this scope with {@link popScope} + * the scope ends. Be sure to always remove this scope with {@link popHubScope} * when the operation finishes or throws. * * @returns Scope, the new cloned scope */ -export function pushScope(hub: Hub): Scope { +export function pushHubScope(hub: Hub): Scope { // We want to clone the content of prev scope - const scope = cloneScope(getScope(hub)); - getStack(hub).push({ - client: getClient(hub), + const scope = cloneScope(getHubScope(hub)); + getHubStack(hub).push({ + client: getHubClient(hub), scope, }); return scope; @@ -210,18 +210,18 @@ export function isOlderThan(hub: Hub, version: number): boolean { * @param hub The Hub instance. * @param callback that will be enclosed into push/popScope. */ -export function withScope(hub: Hub, callback: (scope: Scope) => void): void { - const scope = pushScope(hub); +export function withHubScope(hub: Hub, callback: (scope: Scope) => void): void { + const scope = pushHubScope(hub); try { callback(scope); } finally { - popScope(hub); + popHubScope(hub); } } /** Returns the client of the top stack. */ -export function getClient(hub: Hub): C | undefined { - return getStackTop(hub).client as C; +export function getHubClient(hub: Hub): C | undefined { + return getHubStackTop(hub).client as C; } /** @@ -230,14 +230,14 @@ export function getClient(hub: Hub): C | undefined { * @param hub The Hub instance. * @param user User context object to be set in the current context. Pass `null` to unset the user. */ -export function setUser(hub: Hub, user: User | null): void { - const scope = getScope(hub); +export function setHubUser(hub: Hub, user: User | null): void { + const scope = getHubScope(hub); if (scope) setScopeUser(scope, user); } /** Returns the scope of the top stack. */ -export function getScope(hub: Hub): Scope | undefined { - return getStackTop(hub).scope; +export function getHubScope(hub: Hub): Scope | undefined { + return getHubStackTop(hub).scope; } /** @@ -245,7 +245,7 @@ export function getScope(hub: Hub): Scope | undefined { * * @returns The last event id of a captured event. */ -export function lastEventId(hub: Hub): string | undefined { +export function getHubLastEventId(hub: Hub): string | undefined { return hub.lastEventId; } @@ -254,55 +254,21 @@ export function lastEventId(hub: Hub): string | undefined { * @param hub The Hub instance * @param shouldEndSession If set the session will be marked as exited and removed from the scope */ -export function captureSession(hub: Hub, shouldEndSession: boolean = false): void { +export function captureHubSession(hub: Hub, shouldEndSession: boolean = false): void { // both send the update and pull the session from the scope if (shouldEndSession) { - return endSession(hub); + return endHubSession(hub); } // only send the update - _sendSessionUpdate(hub); -} - -/** - * Sends the current Session on the scope - */ -function _sendSessionUpdate(hub: Hub): void { - const { scope, client } = getStackTop(hub); - if (!scope) return; - - const session = getScopeSession(scope); - if (session) { - if (client && client.captureSession) { - client.captureSession(session); - } - } -} - -/** - * Ends the session that lives on the current scope and sends it to Sentry - */ -function endSession(hub: Hub): void { - const layer = getStackTop(hub); - const scope = layer && layer.scope; - const session = getScopeSession(scope); - if (session) { - closeSession(session); - } - - _sendSessionUpdate(hub); - - // the session is over; take it off of the scope - if (scope) { - setScopeSession(scope); - } + sendSessionUpdate(hub); } /** * Starts a new `Session`, sets on the current scope and returns it. * * To finish a `session`, it has to be passed directly to `client.captureSession`, which is done automatically - * when using `endSession(hub)` for the session currently stored on the scope. + * when using `endHubSession(hub)` for the session currently stored on the scope. * * When there's already an existing session on the scope, it'll be automatically ended. * @@ -312,8 +278,8 @@ function endSession(hub: Hub): void { * @returns The session which was just started * */ -export function startSession(hub: Hub, context?: SessionContext): Session { - const { scope, client } = getStackTop(hub); +export function startHubSession(hub: Hub, context?: SessionContext): Session { + const { scope, client } = getHubStackTop(hub); const { release, environment } = (client && client.getOptions()) || {}; // Will fetch userAgent if called from browser sdk @@ -334,7 +300,7 @@ export function startSession(hub: Hub, context?: SessionContext): Session { if (currentSession && currentSession.status === 'ok') { updateSession(currentSession, { status: 'exited' }); } - endSession(hub); + endHubSession(hub); // Afterwards we set the new session on the scope setScopeSession(scope, session); @@ -352,7 +318,7 @@ export function startSession(hub: Hub, context?: SessionContext): Session { * @returns The generated eventId. */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export function captureException(hub: Hub, exception: any, hint?: EventHint): string { +export function captureHubException(hub: Hub, exception: any, hint?: EventHint): string { const eventId = (hub.lastEventId = uuid4()); let finalHint = hint; @@ -389,7 +355,7 @@ export function captureException(hub: Hub, exception: any, hint?: EventHint): st * @param hint May contain additional information about the original exception. * @returns The generated eventId. */ -export function captureMessage(hub: Hub, message: string, level?: SeverityLevel, hint?: EventHint): string { +export function captureHubMessage(hub: Hub, message: string, level?: SeverityLevel, hint?: EventHint): string { const eventId = (hub.lastEventId = uuid4()); let finalHint = hint; @@ -424,7 +390,7 @@ export function captureMessage(hub: Hub, message: string, level?: SeverityLevel, * @param event The event to send to Sentry. * @param hint May contain additional information about the original exception. */ -export function captureEvent(hub: Hub, event: Event, hint?: EventHint): string { +export function captureHubEvent(hub: Hub, event: Event, hint?: EventHint): string { const eventId = uuid4(); if (event.type !== 'transaction') { hub.lastEventId = eventId; @@ -447,8 +413,8 @@ export function captureEvent(hub: Hub, event: Event, hint?: EventHint): string { * @param breadcrumb The breadcrumb to record. * @param hint May contain additional information about the original breadcrumb. */ -export function addBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void { - const { scope, client } = getStackTop(hub); +export function addHubBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void { + const { scope, client } = getHubStackTop(hub); if (!scope || !client) return; @@ -475,8 +441,8 @@ export function addBreadcrumb(hub: Hub, breadcrumb: Breadcrumb, hint?: Breadcrum * @param hub The Hub instance. * @param tags Tags context object to merge into current context. */ -export function setTags(hub: Hub, tags: { [key: string]: Primitive }): void { - const scope = getScope(hub); +export function setHubTags(hub: Hub, tags: { [key: string]: Primitive }): void { + const scope = getHubScope(hub); if (scope) setScopeTags(scope, tags); } @@ -485,8 +451,8 @@ export function setTags(hub: Hub, tags: { [key: string]: Primitive }): void { * @param hub The Hub instance. * @param extras Extras object to merge into current context. */ -export function setExtras(hub: Hub, extras: Extras): void { - const scope = getScope(hub); +export function setHubExtras(hub: Hub, extras: Extras): void { + const scope = getHubScope(hub); if (scope) setScopeExtras(scope, extras); } @@ -499,8 +465,8 @@ export function setExtras(hub: Hub, extras: Extras): void { * @param key String key of tag * @param value Value of tag */ -export function setTag(hub: Hub, key: string, value: Primitive): void { - const scope = getScope(hub); +export function setHubTag(hub: Hub, key: string, value: Primitive): void { + const scope = getHubScope(hub); if (scope) setScopeTag(scope, key, value); } @@ -510,8 +476,8 @@ export function setTag(hub: Hub, key: string, value: Primitive): void { * @param key String of extra * @param extra Any kind of data. This data will be normalized. */ -export function setExtra(hub: Hub, key: string, extra: Extra): void { - const scope = getScope(hub); +export function setHubExtra(hub: Hub, key: string, extra: Extra): void { + const scope = getHubScope(hub); if (scope) setScopeExtra(scope, key, extra); } @@ -521,8 +487,8 @@ export function setExtra(hub: Hub, key: string, extra: Extra): void { * @param name of the context * @param context Any kind of data. This data will be normalized. */ -export function setContext(hub: Hub, name: string, context: { [key: string]: any } | null): void { - const scope = getScope(hub); +export function setHubContext(hub: Hub, name: string, context: { [key: string]: any } | null): void { + const scope = getHubScope(hub); if (scope) setScopeContext(scope, name, context); } @@ -532,8 +498,8 @@ export function setContext(hub: Hub, name: string, context: { [key: string]: any * @param hub The Hub instance. * @param callback Callback function that receives Scope. */ -export function configureScope(hub: Hub, callback: (scope: Scope) => void): void { - const { scope, client } = getStackTop(hub); +export function configureHubScope(hub: Hub, callback: (scope: Scope) => void): void { + const { scope, client } = getHubStackTop(hub); if (scope && client) { callback(scope); } @@ -555,7 +521,7 @@ export function run(hub: Hub, callback: (hub: Hub) => void): void { /** Returns the integration if installed on the current client. */ export function getIntegration(hub: Hub, integration: IntegrationClass): T | null { - const client = getClient(hub); + const client = getHubClient(hub); if (!client) return null; try { return client.getIntegration(integration); @@ -568,8 +534,8 @@ export function getIntegration(hub: Hub, integration: Int /** * @deprecated No longer does anything. Use use {@link Transaction.startChild} instead. */ -export function startSpan(hub: Hub, context: SpanContext): Span { - return _callExtensionMethod(hub, 'startSpan', context); +export function startHubSpan(hub: Hub, context: SpanContext): Span { + return callExtensionMethod(hub, 'startSpan', context); } /** @@ -590,17 +556,17 @@ export function startSpan(hub: Hub, context: SpanContext): Span { * * @returns The transaction which was just started */ -export function startTransaction( +export function startHubTransaction( hub: Hub, context: TransactionContext, customSamplingContext?: CustomSamplingContext, ): Transaction { - return _callExtensionMethod(hub, 'startTransaction', context, customSamplingContext); + return callExtensionMethod(hub, 'startTransaction', context, customSamplingContext); } /** Returns all trace headers that are currently on the top scope. */ export function traceHeaders(hub: Hub): { [key: string]: string } { - return _callExtensionMethod<{ [key: string]: string }>(hub, 'traceHeaders'); + return callExtensionMethod<{ [key: string]: string }>(hub, 'traceHeaders'); } /** @@ -612,27 +578,13 @@ export function traceHeaders(hub: Hub): { [key: string]: string } { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function _invokeClient(hub: Hub, method: M, ...args: any[]): void { - const { scope, client } = getStackTop(hub); + const { scope, client } = getHubStackTop(hub); if (client && client[method]) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any (client as any)[method](...args, scope); } } -/** - * Calls global extension method and binding current instance to the function call - */ -// @ts-ignore Function lacks ending return statement and return type does not include 'undefined'. ts(2366) -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function _callExtensionMethod(hub: Hub, method: string, ...args: any[]): T { - const carrier = getMainCarrier(); - const sentry = carrier.__SENTRY__; - if (sentry && sentry.extensions && typeof sentry.extensions[method] === 'function') { - return sentry.extensions[method].apply(hub, args); - } - logger.warn(`Extension method ${method} couldn't be found, doing nothing.`); -} - /** * Returns the global shim registry. * @@ -699,6 +651,32 @@ export function getActiveDomain(): DomainAsCarrier | undefined { return sentry && sentry.extensions && sentry.extensions.domain && sentry.extensions.domain.active; } +/** + * This will create a new {@link Hub} and add to the passed object on + * __SENTRY__.hub. + * @param carrier object + * @hidden + */ +export function getHubFromCarrier(carrier: Carrier): Hub { + if (carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub) return carrier.__SENTRY__.hub; + carrier.__SENTRY__ = carrier.__SENTRY__ || {}; + carrier.__SENTRY__.hub = new Hub(); + return carrier.__SENTRY__.hub; +} + +/** + * This will set passed {@link Hub} on the passed object's __SENTRY__.hub attribute + * @param carrier object + * @param hub Hub + * @returns A boolean indicating success or failure + */ +export function setHubOnCarrier(carrier: Carrier, hub: Hub): boolean { + if (!carrier) return false; + carrier.__SENTRY__ = carrier.__SENTRY__ || {}; + carrier.__SENTRY__.hub = hub; + return true; +} + /** * Try to read the hub from an active domain, and fallback to the registry if one doesn't exist * @returns discovered hub @@ -715,7 +693,7 @@ function getHubFromActiveDomain(registry: Carrier): Hub { // If there's no hub on current domain, or it's an old API, assign a new one if (!hasHubOnCarrier(activeDomain) || isOlderThan(getHubFromCarrier(activeDomain), API_VERSION)) { - const registryHubTopStack = getStackTop(getHubFromCarrier(registry)); + const registryHubTopStack = getHubStackTop(getHubFromCarrier(registry)); setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, cloneScope(registryHubTopStack.scope))); } @@ -736,27 +714,49 @@ function hasHubOnCarrier(carrier: Carrier): boolean { } /** - * This will create a new {@link Hub} and add to the passed object on - * __SENTRY__.hub. - * @param carrier object - * @hidden + * Sends the current Session on the scope */ -export function getHubFromCarrier(carrier: Carrier): Hub { - if (carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub) return carrier.__SENTRY__.hub; - carrier.__SENTRY__ = carrier.__SENTRY__ || {}; - carrier.__SENTRY__.hub = new Hub(); - return carrier.__SENTRY__.hub; +function sendSessionUpdate(hub: Hub): void { + const { scope, client } = getHubStackTop(hub); + if (!scope) return; + + const session = getScopeSession(scope); + if (session) { + if (client && client.captureSession) { + client.captureSession(session); + } + } } /** - * This will set passed {@link Hub} on the passed object's __SENTRY__.hub attribute - * @param carrier object - * @param hub Hub - * @returns A boolean indicating success or failure + * Ends the session that lives on the current scope and sends it to Sentry */ -export function setHubOnCarrier(carrier: Carrier, hub: Hub): boolean { - if (!carrier) return false; - carrier.__SENTRY__ = carrier.__SENTRY__ || {}; - carrier.__SENTRY__.hub = hub; - return true; +function endHubSession(hub: Hub): void { + const layer = getHubStackTop(hub); + const scope = layer && layer.scope; + const session = getScopeSession(scope); + if (session) { + closeSession(session); + } + + sendSessionUpdate(hub); + + // the session is over; take it off of the scope + if (scope) { + setScopeSession(scope); + } +} + +/** + * Calls global extension method and binding current instance to the function call + */ +// @ts-ignore Function lacks ending return statement and return type does not include 'undefined'. ts(2366) +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function callExtensionMethod(hub: Hub, method: string, ...args: any[]): T { + const carrier = getMainCarrier(); + const sentry = carrier.__SENTRY__; + if (sentry && sentry.extensions && typeof sentry.extensions[method] === 'function') { + return sentry.extensions[method].apply(hub, args); + } + logger.warn(`Extension method ${method} couldn't be found, doing nothing.`); } diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 9c00300ec2e1..9e02394fe61e 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -5,22 +5,22 @@ export { // eslint-disable-next-line deprecation/deprecation getActiveDomain, getCurrentHub, - bindClient, - popScope, - pushScope, - withScope, - getClient, - getScope, - setUser, - lastEventId, - captureSession, - startSession, - addBreadcrumb, - captureEvent, - captureException, + bindHubClient, + popHubScope, + pushHubScope, + withHubScope, + getHubClient, + getHubScope, + setHubUser, + getHubLastEventId, + captureHubSession, + startHubSession, + addHubBreadcrumb, + captureHubEvent, + captureHubException, getIntegration, - captureMessage, - configureScope, + captureHubMessage, + configureHubScope, Hub, makeMain, Carrier, diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index 11e5acebbe51..7d2cee2dcc8e 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -2,15 +2,10 @@ import { AggregationCounts, Event, EventStatus, RequestSessionStatus, SessionAgg import { EventType } from '@sentry/types/src/event'; import { dropUndefinedKeys, logger } from '@sentry/utils'; -import { getCurrentHub, getScope } from './hub'; +import { getCurrentHub, getHubScope } from './hub'; import { getScopeRequestSession, setScopeRequestSession } from './scope'; import { Session } from './session'; -type ReleaseHealthAttributes = { - environment?: string; - release: string; -}; - export interface Response { status: EventStatus; event?: Event | Session; @@ -24,18 +19,22 @@ export type Transporter = (session: Session | SessionAggregates) => PromiseLike< * ... */ export class SessionFlusher { - public readonly flushTimeout: number = 60; + /** + * Flush the session every ~60 seconds. + */ + public readonly flushTimeout: number = 60 * 1000; public pendingAggregates: Record = {}; - public sessionAttrs: ReleaseHealthAttributes; public intervalId: ReturnType; public isEnabled: boolean = true; public transport: Transporter; + public environment?: string; + public release: string; - public constructor(transport: Transporter, attrs: ReleaseHealthAttributes) { - this.transport = transport; - this.sessionAttrs = attrs; - // Call to setInterval, so that flush is called every ~60 seconds - this.intervalId = setInterval(() => flush(this), this.flushTimeout * 1000); + public constructor(opts: { environment?: string; release: string; transporter: Transporter }) { + this.transport = opts.transporter; + this.environment = opts.environment; + this.release = opts.release; + this.intervalId = setInterval(() => flush(this), this.flushTimeout); } } @@ -61,7 +60,10 @@ export function getSessionAggregates(sessionFlusher: SessionFlusher): SessionAgg }); const sessionAggregates: SessionAggregates = { - attrs: sessionFlusher.sessionAttrs, + attrs: { + environment: sessionFlusher.environment, + release: sessionFlusher.release, + }, aggregates, }; return dropUndefinedKeys(sessionAggregates); @@ -85,7 +87,7 @@ export function incrementSessionStatusCount(sessionFlusher: SessionFlusher): voi if (!sessionFlusher.isEnabled) { return; } - const scope = getScope(getCurrentHub()); + const scope = getHubScope(getCurrentHub()); const requestSession = scope && getScopeRequestSession(scope); if (requestSession && requestSession.status) { diff --git a/packages/hub/test/hub.test.ts b/packages/hub/test/hub.test.ts index 5d3cb9d42367..71ccbd32fc65 100644 --- a/packages/hub/test/hub.test.ts +++ b/packages/hub/test/hub.test.ts @@ -2,24 +2,24 @@ import { Event } from '@sentry/types'; import { _invokeClient, - addBreadcrumb, - bindClient, - captureEvent, - captureException, - captureMessage, - configureScope, - getClient, + addHubBreadcrumb, + bindHubClient, + captureHubEvent, + captureHubException, + captureHubMessage, + configureHubScope, + getHubClient, getCurrentHub, - getScope, - getStack, - getStackTop, + getHubScope, + getHubStack, + getHubStackTop, Hub, isOlderThan, - lastEventId, - popScope, - pushScope, + getHubLastEventId, + popHubScope, + pushHubScope, run, - withScope, + withHubScope, } from '../src/hub'; import { addScopeBreadcrumb, addScopeEventProcessor, applyScopeToEvent, Scope, setScopeExtra } from '../src/scope'; @@ -48,17 +48,17 @@ describe('Hub', () => { test('call bindClient with provided client when constructing new instance', () => { const testClient = makeClient(); const hub = new Hub(testClient); - expect(getStackTop(hub).client).toBe(testClient); + expect(getHubStackTop(hub).client).toBe(testClient); }); test('push process into stack', () => { const hub = new Hub(); - expect(getStack(hub)).toHaveLength(1); + expect(getHubStack(hub)).toHaveLength(1); }); test('pass in filled layer', () => { const hub = new Hub(clientFn); - expect(getStack(hub)).toHaveLength(1); + expect(getHubStack(hub)).toHaveLength(1); }); test("don't invoke client sync with wrong func", () => { @@ -78,18 +78,18 @@ describe('Hub', () => { const localScope = new Scope(); setScopeExtra(localScope, 'a', 'b'); const hub = new Hub(undefined, localScope); - pushScope(hub); - expect(getStack(hub)).toHaveLength(2); - expect(getStack(hub)[1].scope).not.toBe(localScope); - expect((getStack(hub)[1].scope as Scope).extra).toEqual({ a: 'b' }); + pushHubScope(hub); + expect(getHubStack(hub)).toHaveLength(2); + expect(getHubStack(hub)[1].scope).not.toBe(localScope); + expect((getHubStack(hub)[1].scope as Scope).extra).toEqual({ a: 'b' }); }); test('inherit client', () => { const testClient: any = { bla: 'a' }; const hub = new Hub(testClient); - pushScope(hub); - expect(getStack(hub)).toHaveLength(2); - expect(getStack(hub)[1].client).toBe(testClient); + pushHubScope(hub); + expect(getHubStack(hub)).toHaveLength(2); + expect(getHubStack(hub)[1].client).toBe(testClient); }); describe('bindClient', () => { @@ -97,27 +97,27 @@ describe('Hub', () => { const testClient = makeClient(); const nextClient = makeClient(); const hub = new Hub(testClient); - bindClient(hub, nextClient); - expect(getStack(hub)).toHaveLength(1); - expect(getStack(hub)[0].client).toBe(nextClient); + bindHubClient(hub, nextClient); + expect(getHubStack(hub)).toHaveLength(1); + expect(getHubStack(hub)[0].client).toBe(nextClient); }); test('should bind client to the top-most layer', () => { const testClient: any = { bla: 'a' }; const nextClient: any = { foo: 'bar' }; const hub = new Hub(testClient); - pushScope(hub); - bindClient(hub, nextClient); - expect(getStack(hub)).toHaveLength(2); - expect(getStack(hub)[0].client).toBe(testClient); - expect(getStack(hub)[1].client).toBe(nextClient); + pushHubScope(hub); + bindHubClient(hub, nextClient); + expect(getHubStack(hub)).toHaveLength(2); + expect(getHubStack(hub)[0].client).toBe(testClient); + expect(getHubStack(hub)[1].client).toBe(nextClient); }); test('should call setupIntegration method of passed client', () => { const testClient = makeClient(); const nextClient = makeClient(); const hub = new Hub(testClient); - bindClient(hub, nextClient); + bindHubClient(hub, nextClient); expect(testClient.setupIntegrations).toHaveBeenCalled(); expect(nextClient.setupIntegrations).toHaveBeenCalled(); }); @@ -137,8 +137,8 @@ describe('Hub', () => { return processedEvent; }); - pushScope(hub); - const pushedScope = getStackTop(hub).scope; + pushHubScope(hub); + const pushedScope = getHubStackTop(hub).scope; return applyScopeToEvent(pushedScope!, event).then(final => { expect(final!.dist).toEqual('1'); @@ -148,10 +148,10 @@ describe('Hub', () => { test('popScope', () => { const hub = new Hub(); - pushScope(hub); - expect(getStack(hub)).toHaveLength(2); - popScope(hub); - expect(getStack(hub)).toHaveLength(1); + pushHubScope(hub); + expect(getHubStack(hub)).toHaveLength(2); + popHubScope(hub); + expect(getHubStack(hub)).toHaveLength(1); }); describe('withScope', () => { @@ -162,26 +162,26 @@ describe('Hub', () => { }); test('simple', () => { - withScope(hub, () => { - expect(getStack(hub)).toHaveLength(2); + withHubScope(hub, () => { + expect(getHubStack(hub)).toHaveLength(2); }); - expect(getStack(hub)).toHaveLength(1); + expect(getHubStack(hub)).toHaveLength(1); }); test('bindClient', () => { const testClient: any = { bla: 'a' }; - withScope(hub, () => { - bindClient(hub, testClient); - expect(getStack(hub)).toHaveLength(2); - expect(getStack(hub)[1].client).toBe(testClient); + withHubScope(hub, () => { + bindHubClient(hub, testClient); + expect(getHubStack(hub)).toHaveLength(2); + expect(getHubStack(hub)[1].client).toBe(testClient); }); - expect(getStack(hub)).toHaveLength(1); + expect(getHubStack(hub)).toHaveLength(1); }); test('should bubble up exceptions', () => { const error = new Error('test'); expect(() => { - withScope(hub, () => { + withHubScope(hub, () => { throw error; }); }).toThrow(error); @@ -191,22 +191,22 @@ describe('Hub', () => { test('getCurrentClient', () => { const testClient: any = { bla: 'a' }; const hub = new Hub(testClient); - expect(getClient(hub)).toBe(testClient); + expect(getHubClient(hub)).toBe(testClient); }); test('getStack', () => { const client: any = { a: 'b' }; const hub = new Hub(client); - expect(getStack(hub)[0].client).toBe(client); + expect(getHubStack(hub)[0].client).toBe(client); }); test('getStackTop', () => { const testClient: any = { bla: 'a' }; const hub = new Hub(); - pushScope(hub); - pushScope(hub); - bindClient(hub, testClient); - expect(getStackTop(hub).client).toEqual({ bla: 'a' }); + pushHubScope(hub); + pushHubScope(hub); + bindHubClient(hub, testClient); + expect(getHubStackTop(hub).client).toEqual({ bla: 'a' }); }); describe('configureScope', () => { @@ -215,14 +215,14 @@ describe('Hub', () => { setScopeExtra(localScope, 'a', 'b'); const hub = new Hub({} as any, localScope); const cb = jest.fn(); - configureScope(hub, cb); + configureHubScope(hub, cb); expect(cb).toHaveBeenCalledWith(localScope); }); test('should not invoke without client and scope', () => { const hub = new Hub(); const cb = jest.fn(); - configureScope(hub, cb); + configureHubScope(hub, cb); expect(cb).not.toHaveBeenCalled(); }); }); @@ -231,7 +231,7 @@ describe('Hub', () => { test('simple', () => { const testClient = makeClient(); const hub = new Hub(testClient); - captureException(hub, 'a'); + captureHubException(hub, 'a'); expect(testClient.captureException).toHaveBeenCalled(); expect(testClient.captureException.mock.calls[0][0]).toBe('a'); }); @@ -239,7 +239,7 @@ describe('Hub', () => { test('should set event_id in hint', () => { const testClient = makeClient(); const hub = new Hub(testClient); - captureException(hub, 'a'); + captureHubException(hub, 'a'); expect(testClient.captureException.mock.calls[0][1].event_id).toBeTruthy(); }); @@ -247,7 +247,7 @@ describe('Hub', () => { const testClient = makeClient(); const hub = new Hub(testClient); const ex = new Error('foo'); - captureException(hub, ex); + captureHubException(hub, ex); expect(testClient.captureException.mock.calls[0][1].originalException).toBe(ex); expect(testClient.captureException.mock.calls[0][1].syntheticException).toBeInstanceOf(Error); expect(testClient.captureException.mock.calls[0][1].syntheticException.message).toBe('Sentry syntheticException'); @@ -258,7 +258,7 @@ describe('Hub', () => { test('simple', () => { const testClient = makeClient(); const hub = new Hub(testClient); - captureMessage(hub, 'a'); + captureHubMessage(hub, 'a'); expect(testClient.captureMessage).toHaveBeenCalled(); expect(testClient.captureMessage.mock.calls[0][0]).toBe('a'); }); @@ -266,14 +266,14 @@ describe('Hub', () => { test('should set event_id in hint', () => { const testClient = makeClient(); const hub = new Hub(testClient); - captureMessage(hub, 'a'); + captureHubMessage(hub, 'a'); expect(testClient.captureMessage.mock.calls[0][2].event_id).toBeTruthy(); }); test('should generate hint if not provided in the call', () => { const testClient = makeClient(); const hub = new Hub(testClient); - captureMessage(hub, 'foo'); + captureHubMessage(hub, 'foo'); expect(testClient.captureMessage.mock.calls[0][2].originalException).toBe('foo'); expect(testClient.captureMessage.mock.calls[0][2].syntheticException).toBeInstanceOf(Error); expect(testClient.captureMessage.mock.calls[0][2].syntheticException.message).toBe('foo'); @@ -287,7 +287,7 @@ describe('Hub', () => { extra: { b: 3 }, }; const hub = new Hub(testClient); - captureEvent(hub, event); + captureHubEvent(hub, event); expect(testClient.captureEvent).toHaveBeenCalled(); expect(testClient.captureEvent.mock.calls[0][0]).toBe(event); }); @@ -298,7 +298,7 @@ describe('Hub', () => { extra: { b: 3 }, }; const hub = new Hub(testClient); - captureEvent(hub, event); + captureHubEvent(hub, event); expect(testClient.captureEvent.mock.calls[0][1].event_id).toBeTruthy(); }); @@ -308,8 +308,8 @@ describe('Hub', () => { extra: { b: 3 }, }; const hub = new Hub(testClient); - captureEvent(hub, event); - expect(testClient.captureEvent.mock.calls[0][1].event_id).toEqual(lastEventId(hub)); + captureHubEvent(hub, event); + expect(testClient.captureEvent.mock.calls[0][1].event_id).toEqual(getHubLastEventId(hub)); }); test('transactions do not set lastEventId', () => { @@ -319,8 +319,8 @@ describe('Hub', () => { type: 'transaction', }; const hub = new Hub(testClient); - captureEvent(hub, event); - expect(testClient.captureEvent.mock.calls[0][1].event_id).not.toEqual(lastEventId(hub)); + captureHubEvent(hub, event); + expect(testClient.captureEvent.mock.calls[0][1].event_id).not.toEqual(getHubLastEventId(hub)); }); }); @@ -329,8 +329,8 @@ describe('Hub', () => { extra: { b: 3 }, }; const hub = new Hub(); - const eventId = captureEvent(hub, event); - expect(eventId).toBe(lastEventId(hub)); + const eventId = captureHubEvent(hub, event); + expect(eventId).toBe(getHubLastEventId(hub)); }); describe('run', () => { @@ -341,8 +341,8 @@ describe('Hub', () => { setScopeExtra(myScope, 'a', 'b'); const myHub = new Hub(myClient, myScope); run(myHub, hub => { - expect(getScope(hub)).toBe(myScope); - expect(getClient(hub)).toBe(myClient); + expect(getHubScope(hub)).toBe(myScope); + expect(getHubClient(hub)).toBe(myClient); expect(hub).toBe(getCurrentHub()); }); expect(currentHub).toBe(getCurrentHub()); @@ -363,8 +363,8 @@ describe('Hub', () => { test('withScope', () => { expect.assertions(6); const hub = new Hub(clientFn); - addBreadcrumb(hub, { message: 'My Breadcrumb' }); - withScope(hub, scope => { + addHubBreadcrumb(hub, { message: 'My Breadcrumb' }); + withHubScope(hub, scope => { addScopeBreadcrumb(scope, { message: 'scope breadcrumb' }); const event: Event = {}; void applyScopeToEvent(scope, event) diff --git a/packages/hub/test/sessionflusher.test.ts b/packages/hub/test/sessionflusher.test.ts index be49658e2ebd..c8953e8d51f5 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -17,7 +17,11 @@ describe('Session Flusher', () => { test('test incrementSessionStatusCount updates the internal SessionFlusher state', () => { const transporter = makeTransporter(); - const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher({ + transporter: transporter, + release: '1.0.0', + environment: 'dev', + }); const date = new Date('2021-04-08T12:18:23.043Z'); let count = _incrementSessionStatusCount(flusher, 'ok', date); @@ -41,7 +45,7 @@ describe('Session Flusher', () => { test('test undefined attributes are excluded, on incrementSessionStatusCount call', () => { const transporter = makeTransporter(); - const flusher = new SessionFlusher(transporter, { release: '1.0.0' }); + const flusher = new SessionFlusher({ transporter, release: '1.0.0' }); const date = new Date('2021-04-08T12:18:23.043Z'); _incrementSessionStatusCount(flusher, 'ok', date); @@ -55,7 +59,7 @@ describe('Session Flusher', () => { test('flush is called every ~60 seconds after initialisation of an instance of SessionFlusher', () => { const transporter = makeTransporter(); - const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher({ transporter, release: '1.0.0', environment: 'dev' }); jest.advanceTimersByTime(59000); expect(transporter).toHaveBeenCalledTimes(0); @@ -77,7 +81,7 @@ describe('Session Flusher', () => { test('transporter is called on flush if sessions were captured', () => { const transporter = makeTransporter(); - const flusher = new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher({ transporter, release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); _incrementSessionStatusCount(flusher, 'ok', date); @@ -99,7 +103,7 @@ describe('Session Flusher', () => { test('transporter is not called on flush if no sessions were captured', () => { const transporter = makeTransporter(); - new SessionFlusher(transporter, { release: '1.0.0', environment: 'dev' }); + new SessionFlusher({ transporter, release: '1.0.0', environment: 'dev' }); expect(transporter).toHaveBeenCalledTimes(0); jest.advanceTimersByTime(61000); @@ -108,7 +112,7 @@ describe('Session Flusher', () => { test('calling close on SessionFlusher should disable SessionFlusher', () => { const transporter = makeTransporter(); - const flusher = new SessionFlusher(transporter, { release: '1.0.x' }); + const flusher = new SessionFlusher({ transporter, release: '1.0.x' }); closeSessionFlusher(flusher); @@ -117,7 +121,7 @@ describe('Session Flusher', () => { test('calling close on SessionFlusher will force call flush', () => { const transporter = makeTransporter(); - const flusher = new SessionFlusher(transporter, { release: '1.0.x' }); + const flusher = new SessionFlusher({ transporter, release: '1.0.x' }); const date = new Date('2021-04-08T12:18:23.043Z'); // TODO: code-smell using internal function From 121a455c91e2762be95d5b333f608cb3a49d9c6f Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 03:51:59 -0500 Subject: [PATCH 30/37] fix: node client --- packages/hub/src/hub.ts | 4 +- packages/hub/src/index.ts | 30 +++++++++++-- packages/hub/src/sessionflusher.ts | 9 ++-- packages/node/src/client.ts | 20 ++++++--- packages/node/src/eventbuilder.ts | 6 +-- packages/node/src/handlers.ts | 45 ++++++++++++------- packages/node/src/integrations/console.ts | 7 +-- packages/node/src/integrations/http.ts | 11 ++--- .../node/src/integrations/linkederrors.ts | 4 +- packages/node/src/integrations/modules.ts | 6 +-- .../src/integrations/onuncaughtexception.ts | 13 +++--- .../src/integrations/onunhandledrejection.ts | 24 ++++++---- .../src/integrations/utils/errorhandling.ts | 4 +- packages/node/src/integrations/utils/http.ts | 4 +- packages/node/src/sdk.ts | 22 ++++----- packages/node/test/integrations/http.test.ts | 7 ++- packages/types/src/index.ts | 1 + 17 files changed, 138 insertions(+), 79 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index 5c15767c084b..a5958b94d86d 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -520,7 +520,7 @@ export function run(hub: Hub, callback: (hub: Hub) => void): void { } /** Returns the integration if installed on the current client. */ -export function getIntegration(hub: Hub, integration: IntegrationClass): T | null { +export function getHubIntegration(hub: Hub, integration: IntegrationClass): T | null { const client = getHubClient(hub); if (!client) return null; try { @@ -731,7 +731,7 @@ function sendSessionUpdate(hub: Hub): void { /** * Ends the session that lives on the current scope and sends it to Sentry */ -function endHubSession(hub: Hub): void { +export function endHubSession(hub: Hub): void { const layer = getHubStackTop(hub); const scope = layer && layer.scope; const session = getScopeSession(scope); diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 9e02394fe61e..442bef428661 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -1,12 +1,36 @@ -export { updateScope, applyScopeToEvent, addGlobalEventProcessor, cloneScope, getScopeSession, Scope } from './scope'; +export { + getScopeRequestSession, + setScopeExtra, + setScopeUser, + setScopeTags, + setScopeExtras, + updateScope, + applyScopeToEvent, + setScopeLevel, + addGlobalEventProcessor, + cloneScope, + getScopeSession, + setScopeSpan, + setScopeRequestSession, + addScopeEventProcessor, + setScopeSession, + getScopeSpan, + Scope, +} from './scope'; export { Session, updateSession } from './session'; -export { SessionFlusher } from './sessionflusher'; +export { + SessionFlusher, + closeSessionFlusher, + incrementSessionStatusCount, + SessionFlusherTransporter, +} from './sessionflusher'; export { // eslint-disable-next-line deprecation/deprecation getActiveDomain, getCurrentHub, bindHubClient, popHubScope, + endHubSession, pushHubScope, withHubScope, getHubClient, @@ -18,7 +42,7 @@ export { addHubBreadcrumb, captureHubEvent, captureHubException, - getIntegration, + getHubIntegration, captureHubMessage, configureHubScope, Hub, diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index 7d2cee2dcc8e..0c39edb63d24 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -1,10 +1,9 @@ import { AggregationCounts, Event, EventStatus, RequestSessionStatus, SessionAggregates } from '@sentry/types'; import { EventType } from '@sentry/types/src/event'; import { dropUndefinedKeys, logger } from '@sentry/utils'; - +import { Session } from './session'; import { getCurrentHub, getHubScope } from './hub'; import { getScopeRequestSession, setScopeRequestSession } from './scope'; -import { Session } from './session'; export interface Response { status: EventStatus; @@ -13,7 +12,7 @@ export interface Response { reason?: string; } -export type Transporter = (session: Session | SessionAggregates) => PromiseLike; +export type SessionFlusherTransporter = (session: Session | SessionAggregates) => PromiseLike; /** * ... @@ -26,11 +25,11 @@ export class SessionFlusher { public pendingAggregates: Record = {}; public intervalId: ReturnType; public isEnabled: boolean = true; - public transport: Transporter; + public transport: SessionFlusherTransporter; public environment?: string; public release: string; - public constructor(opts: { environment?: string; release: string; transporter: Transporter }) { + public constructor(opts: { environment?: string; release: string; transporter: SessionFlusherTransporter }) { this.transport = opts.transporter; this.environment = opts.environment; this.release = opts.release; diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index a41cf0f16e18..01d3cf3c2b38 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,10 +1,11 @@ import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; -import { SessionFlusher } from '@sentry/hub'; +import { closeSessionFlusher, getScopeRequestSession, incrementSessionStatusCount, SessionFlusher } from '@sentry/hub'; import { Event, EventHint } from '@sentry/types'; import { logger } from '@sentry/utils'; import { NodeBackend } from './backend'; import { NodeOptions } from './types'; +import { SessionFlusherTransporter } from '@sentry/hub/src'; /** * The Sentry Node SDK Client. @@ -44,7 +45,7 @@ export class NodeClient extends BaseClient { // when the `requestHandler` middleware is used, and hence the expectation is to have SessionAggregates payload // sent to the Server only when the `requestHandler` middleware is used if (this._options.autoSessionTracking && this._sessionFlusher && scope) { - const requestSession = scope.getRequestSession(); + const requestSession = getScopeRequestSession(scope); // Necessary checks to ensure this is code block is executed only within a request // Should override the status only if `requestSession.status` is `Ok`, which is its initial stage @@ -70,7 +71,7 @@ export class NodeClient extends BaseClient { // If the event is of type Exception, then a request session should be captured if (isException) { - const requestSession = scope.getRequestSession(); + const requestSession = getScopeRequestSession(scope); // Ensure that this is happening within the bounds of a request, and make sure not to override // Session Status if Errored / Crashed @@ -88,7 +89,9 @@ export class NodeClient extends BaseClient { * @inheritdoc */ public close(timeout?: number): PromiseLike { - this._sessionFlusher?.close(); + if (this._sessionFlusher) { + closeSessionFlusher(this._sessionFlusher); + } return super.close(timeout); } @@ -98,9 +101,14 @@ export class NodeClient extends BaseClient { if (!release) { logger.warn('Cannot initialise an instance of SessionFlusher if no release is provided!'); } else { - this._sessionFlusher = new SessionFlusher(this.getTransport().sendSession!, { + this._sessionFlusher = new SessionFlusher({ release, environment, + // How to make this a required option? + // The existing transporter could omit implementing this method. + // Would it makesense to have a default implementation that does nothing? + // Or should the session flusher be optional? + transporter: this.getTransport().sendSession || (Function.prototype as SessionFlusherTransporter), }); } } @@ -124,7 +132,7 @@ export class NodeClient extends BaseClient { if (!this._sessionFlusher) { logger.warn('Discarded request mode session because autoSessionTracking option was disabled'); } else { - this._sessionFlusher.incrementSessionStatusCount(); + incrementSessionStatusCount(this._sessionFlusher); } } } diff --git a/packages/node/src/eventbuilder.ts b/packages/node/src/eventbuilder.ts index 3c9cf2130b6c..0ad2cc181e61 100644 --- a/packages/node/src/eventbuilder.ts +++ b/packages/node/src/eventbuilder.ts @@ -1,4 +1,4 @@ -import { getCurrentHub, configureScope } from '@sentry/hub'; +import { getCurrentHub, configureHubScope, setScopeExtra } from '@sentry/hub'; import { Event, EventHint, Mechanism, Options, SeverityLevel } from '@sentry/types'; import { addExceptionMechanism, @@ -32,8 +32,8 @@ export function eventFromException(options: Options, exception: unknown, hint?: // which is much better than creating new group when any key/value change const message = `Non-Error exception captured with keys: ${extractExceptionKeysForMessage(exception)}`; - configureScope(getCurrentHub(), scope => { - scope.setExtra('__serialized__', normalizeToSize(exception as Record)); + configureHubScope(getCurrentHub(), scope => { + setScopeExtra(scope, '__serialized__', normalizeToSize(exception as Record)); }); ex = (hint && hint.syntheticException) || new Error(message); diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 2aea777fa062..87cdad504b00 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -1,7 +1,18 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { captureException, getCurrentHub, startTransaction, withScope } from '@sentry/core'; -import { configureScope, getClient, getSession } from '@sentry/hub'; +import { + getHubClient, + getHubScope, + getScopeRequestSession, + getScopeSession, + getScopeSpan, + setScopeSpan, + setScopeRequestSession, + addScopeEventProcessor, + configureHubScope, + setScopeSession, +} from '@sentry/hub'; import { extractTraceparentData, Span } from '@sentry/tracing'; import { Event, ExtractedNodeRequestData, Transaction } from '@sentry/types'; import { isPlainObject, isString, logger, normalize, stripUrlQueryAndFragment } from '@sentry/utils'; @@ -71,8 +82,8 @@ export function tracingHandler(): ( ); // We put the transaction on the scope so users can attach children to it - configureScope(getCurrentHub(), scope => { - scope.setSpan(transaction); + configureHubScope(getCurrentHub(), scope => { + setScopeSpan(scope, transaction); }); // We also set __sentry_transaction on the response so people can grab the transaction there to add @@ -380,16 +391,16 @@ export function requestHandler( options?: RequestHandlerOptions, ): (req: http.IncomingMessage, res: http.ServerResponse, next: (error?: any) => void) => void { const currentHub = getCurrentHub(); - const client = getClient(currentHub); + const client = getHubClient(currentHub); // Initialise an instance of SessionFlusher on the client when `autoSessionTracking` is enabled and the // `requestHandler` middleware is used indicating that we are running in SessionAggregates mode if (client && isAutoSessionTrackingEnabled(client)) { client.initSessionFlusher(); // If Scope contains a Single mode Session, it is removed in favor of using Session Aggregates mode - const scope = currentHub.getScope(); - if (scope && getSession(scope)) { - scope.setSession(); + const scope = getHubScope(currentHub); + if (scope && getScopeSession(scope)) { + setScopeSession(scope); } } return function sentryRequestMiddleware( @@ -418,20 +429,20 @@ export function requestHandler( local.run(() => { const currentHub = getCurrentHub(); - configureScope(currentHub, scope => { - scope.addEventProcessor((event: Event) => parseRequest(event, req, options)); - const client = getClient(currentHub); + configureHubScope(currentHub, scope => { + addScopeEventProcessor(scope, (event: Event) => parseRequest(event, req, options)); + const client = getHubClient(currentHub); if (isAutoSessionTrackingEnabled(client)) { - const scope = currentHub.getScope(); + const scope = getHubScope(currentHub); if (scope) { // Set `status` of `RequestSession` to Ok, at the beginning of the request - scope.setRequestSession({ status: 'ok' }); + setScopeRequestSession(scope, { status: 'ok' }); } } }); res.once('finish', () => { - const client = getClient(currentHub); + const client = getHubClient(currentHub); if (isAutoSessionTrackingEnabled(client)) { setImmediate(() => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access @@ -501,11 +512,11 @@ export function errorHandler(options?: { // For some reason we need to set the transaction on the scope again // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const transaction = (res as any).__sentry_transaction as Span; - if (transaction && _scope.getSpan() === undefined) { - _scope.setSpan(transaction); + if (transaction && getScopeSpan(_scope) === undefined) { + setScopeSpan(_scope, transaction); } - const client = getClient(getCurrentHub()); + const client = getHubClient(getCurrentHub()); if (client && isAutoSessionTrackingEnabled(client)) { // Check if the `SessionFlusher` is instantiated on the client to go into this branch that marks the // `requestSession.status` as `Crashed`, and this check is necessary because the `SessionFlusher` is only @@ -514,7 +525,7 @@ export function errorHandler(options?: { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const isSessionAggregatesMode = (client as any)._sessionFlusher !== undefined; if (isSessionAggregatesMode) { - const requestSession = _scope.getRequestSession(); + const requestSession = getScopeRequestSession(_scope); // If an error bubbles to the `errorHandler`, then this is an unhandled error, and should be reported as a // Crashed session. The `_requestSession.status` is checked to ensure that this error is happening within // the bounds of a request, and if so the status is updated diff --git a/packages/node/src/integrations/console.ts b/packages/node/src/integrations/console.ts index edead20a390c..9d1859904383 100644 --- a/packages/node/src/integrations/console.ts +++ b/packages/node/src/integrations/console.ts @@ -1,5 +1,5 @@ import { getCurrentHub } from '@sentry/core'; -import { addBreadcrumb, getIntegration } from '@sentry/hub'; +import { addHubBreadcrumb, getHubIntegration } from '@sentry/hub'; import { Integration } from '@sentry/types'; import { fill, severityFromString } from '@sentry/utils'; import * as util from 'util'; @@ -35,8 +35,9 @@ function createConsoleWrapper(level: string): (originalConsoleMethod: () => void /* eslint-disable prefer-rest-params */ return function(this: typeof console): void { - if (getIntegration(getCurrentHub(), Console)) { - addBreadcrumb(getCurrentHub(), + if (getHubIntegration(getCurrentHub(), Console)) { + addHubBreadcrumb( + getCurrentHub(), { category: 'console', level: sentryLevel, diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 632af49574b0..e326316d4abd 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -12,7 +12,8 @@ import { RequestMethod, RequestMethodArgs, } from './utils/http'; -import { addBreadcrumb, getIntegration } from '@sentry/hub'; +import { addHubBreadcrumb, getHubScope, getHubIntegration } from '@sentry/hub'; +import { getScopeSpan } from '@sentry/hub/src/scope'; const NODE_VERSION = parseSemver(process.versions.node); @@ -109,9 +110,9 @@ function _createWrappedRequestMethodFactory( let span: Span | undefined; let parentSpan: Span | undefined; - const scope = getCurrentHub().getScope(); + const scope = getHubScope(getCurrentHub()); if (scope && tracingEnabled) { - parentSpan = scope.getSpan(); + parentSpan = getScopeSpan(scope); if (parentSpan) { span = parentSpan.startChild({ description: `${requestOptions.method || 'GET'} ${requestUrl}`, @@ -164,11 +165,11 @@ function _createWrappedRequestMethodFactory( * Captures Breadcrumb based on provided request/response pair */ function addRequestBreadcrumb(event: string, url: string, req: http.ClientRequest, res?: http.IncomingMessage): void { - if (!getIntegration(getCurrentHub(), Http)) { + if (!getHubIntegration(getCurrentHub(), Http)) { return; } - addBreadcrumb( + addHubBreadcrumb( getCurrentHub(), { category: 'http', diff --git a/packages/node/src/integrations/linkederrors.ts b/packages/node/src/integrations/linkederrors.ts index e969cc923d81..aabb9cc0803c 100644 --- a/packages/node/src/integrations/linkederrors.ts +++ b/packages/node/src/integrations/linkederrors.ts @@ -1,9 +1,9 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; +import { getHubIntegration } from '@sentry/hub'; import { Event, EventHint, Exception, ExtendedError, Integration } from '@sentry/types'; import { isInstanceOf, resolvedSyncPromise, SyncPromise } from '@sentry/utils'; import { getExceptionFromError } from '../parsers'; -import { getIntegration } from '@sentry/hub'; const DEFAULT_KEY = 'cause'; const DEFAULT_LIMIT = 5; @@ -43,7 +43,7 @@ export class LinkedErrors implements Integration { */ public setupOnce(): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getIntegration(getCurrentHub(), LinkedErrors); + const self = getHubIntegration(getCurrentHub(), LinkedErrors); if (self) { const handler = self._handler && self._handler.bind(self); return typeof handler === 'function' ? handler(event, hint) : event; diff --git a/packages/node/src/integrations/modules.ts b/packages/node/src/integrations/modules.ts index ce0bd1f84063..2e4d5f269eca 100644 --- a/packages/node/src/integrations/modules.ts +++ b/packages/node/src/integrations/modules.ts @@ -1,7 +1,7 @@ -import { EventProcessor, Hub, Integration } from '@sentry/types'; +import { getHubIntegration, Hub } from '@sentry/hub'; +import { EventProcessor, Integration } from '@sentry/types'; import { existsSync, readFileSync } from 'fs'; import { dirname, join } from 'path'; -import { getIntegration } from '@sentry/hub'; let moduleCache: { [key: string]: string }; @@ -83,7 +83,7 @@ export class Modules implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(event => { - if (!getIntegration(getCurrentHub(), Modules)) { + if (!getHubIntegration(getCurrentHub(), Modules)) { return event; } return { diff --git a/packages/node/src/integrations/onuncaughtexception.ts b/packages/node/src/integrations/onuncaughtexception.ts index ab3a1078d2ee..ae4f91cf0bcb 100644 --- a/packages/node/src/integrations/onuncaughtexception.ts +++ b/packages/node/src/integrations/onuncaughtexception.ts @@ -1,5 +1,5 @@ import { getCurrentHub, Scope } from '@sentry/core'; -import { getClient, getIntegration, withScope } from '@sentry/hub'; +import { captureHubException, getHubClient, getHubIntegration, setScopeLevel, withHubScope } from '@sentry/hub'; import { Integration } from '@sentry/types'; import { logger } from '@sentry/utils'; @@ -38,6 +38,7 @@ export class OnUncaughtException implements Integration { onFatalError?(firstError: Error, secondError?: Error): void; } = {}, ) {} + /** * @inheritDoc */ @@ -57,7 +58,7 @@ export class OnUncaughtException implements Integration { return (error: Error): void => { let onFatalError: OnFatalErrorHandler = logAndExitProcess; - const client = getClient(getCurrentHub()); + const client = getHubClient(getCurrentHub()); if (this._options.onFatalError) { // eslint-disable-next-line @typescript-eslint/unbound-method @@ -76,10 +77,10 @@ export class OnUncaughtException implements Integration { firstError = error; caughtFirstError = true; - if (getIntegration(hub, OnUncaughtException)) { - withScope(hub, (scope: Scope) => { - scope.setLevel('fatal'); - hub.captureException(error, { + if (getHubIntegration(hub, OnUncaughtException)) { + withHubScope(hub, (scope: Scope) => { + setScopeLevel(scope, 'fatal'); + captureHubException(hub, error, { originalException: error, data: { mechanism: { handled: false, type: 'onuncaughtexception' } }, }); diff --git a/packages/node/src/integrations/onunhandledrejection.ts b/packages/node/src/integrations/onunhandledrejection.ts index c2a86125634e..e352f6174aeb 100644 --- a/packages/node/src/integrations/onunhandledrejection.ts +++ b/packages/node/src/integrations/onunhandledrejection.ts @@ -1,5 +1,13 @@ import { getCurrentHub, Scope } from '@sentry/core'; -import { getIntegration, withScope } from '@sentry/hub'; +import { + captureHubException, + getHubIntegration, + setScopeExtra, + setScopeExtras, + setScopeTags, + setScopeUser, + withHubScope, +} from '@sentry/hub'; import { Integration } from '@sentry/types'; import { consoleSandbox } from '@sentry/utils'; @@ -48,7 +56,7 @@ export class OnUnhandledRejection implements Integration { public sendUnhandledPromise(reason: any, promise: any): void { const hub = getCurrentHub(); - if (!getIntegration(hub, OnUnhandledRejection)) { + if (!getHubIntegration(hub, OnUnhandledRejection)) { this._handleRejection(reason); return; } @@ -56,21 +64,21 @@ export class OnUnhandledRejection implements Integration { /* eslint-disable @typescript-eslint/no-unsafe-member-access */ const context = (promise.domain && promise.domain.sentryContext) || {}; - withScope(hub, (scope: Scope) => { - scope.setExtra('unhandledPromiseRejection', true); + withHubScope(hub, (scope: Scope) => { + setScopeExtra(scope, 'unhandledPromiseRejection', true); // Preserve backwards compatibility with raven-node for now if (context.user) { - scope.setUser(context.user); + setScopeUser(scope, context.user); } if (context.tags) { - scope.setTags(context.tags); + setScopeTags(scope, context.tags); } if (context.extra) { - scope.setExtras(context.extra); + setScopeExtras(scope, context.extra); } - hub.captureException(reason, { + captureHubException(hub, reason, { originalException: promise, data: { mechanism: { handled: false, type: 'onunhandledrejection' } }, }); diff --git a/packages/node/src/integrations/utils/errorhandling.ts b/packages/node/src/integrations/utils/errorhandling.ts index 9780bf7cbc2c..e0c5ae7b7651 100644 --- a/packages/node/src/integrations/utils/errorhandling.ts +++ b/packages/node/src/integrations/utils/errorhandling.ts @@ -1,5 +1,5 @@ import { getCurrentHub } from '@sentry/core'; -import { getClient } from '@sentry/hub'; +import { getHubClient } from '@sentry/hub'; import { forget, logger } from '@sentry/utils'; import { NodeClient } from '../../client'; @@ -13,7 +13,7 @@ export function logAndExitProcess(error: Error): void { // eslint-disable-next-line no-console console.error(error && error.stack ? error.stack : error); - const client = getClient(getCurrentHub()); + const client = getHubClient(getCurrentHub()); if (client === undefined) { logger.warn('No NodeClient was defined, we are exiting the process now.'); diff --git a/packages/node/src/integrations/utils/http.ts b/packages/node/src/integrations/utils/http.ts index 0416551b4603..c4b71e6f3340 100644 --- a/packages/node/src/integrations/utils/http.ts +++ b/packages/node/src/integrations/utils/http.ts @@ -1,5 +1,5 @@ import { getCurrentHub } from '@sentry/core'; -import { getClient } from '@sentry/hub'; +import { getHubClient } from '@sentry/hub'; import { parseSemver } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; @@ -12,7 +12,7 @@ const NODE_VERSION = parseSemver(process.versions.node); * @param url url to verify */ export function isSentryRequest(url: string): boolean { - const dsn = getClient(getCurrentHub())?.getDsn(); + const dsn = getHubClient(getCurrentHub())?.getDsn(); return dsn ? url.includes(dsn.host) : false; } diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 168da7b08b32..f63ef3d3c30d 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,11 +1,13 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; import { - getClient, + getHubClient, + getHubLastEventId, getMainCarrier, - startSession, - getSession, - lastEventId as hubLastEventId, + getScopeSession, setHubOnCarrier, + endHubSession, + getHubScope, + startHubSession, } from '@sentry/hub'; import { SessionStatus } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; @@ -143,7 +145,7 @@ export function init(options: NodeOptions = {}): void { * @returns The last event id of a captured event. */ export function lastEventId(): string | undefined { - return hubLastEventId(getCurrentHub()); + return getHubLastEventId(getCurrentHub()); } /** @@ -155,7 +157,7 @@ export function lastEventId(): string | undefined { * doesn't (or if there's no client defined). */ export async function flush(timeout?: number): Promise { - const client = getClient(getCurrentHub()); + const client = getHubClient(getCurrentHub()); if (client) { return client.flush(timeout); } @@ -172,7 +174,7 @@ export async function flush(timeout?: number): Promise { * doesn't (or if there's no client defined). */ export async function close(timeout?: number): Promise { - const client = getClient(getCurrentHub()); + const client = getHubClient(getCurrentHub()); if (client) { return client.close(timeout); } @@ -232,18 +234,18 @@ export function getSentryRelease(fallback?: string): string | undefined { */ function startSessionTracking(): void { const hub = getCurrentHub(); - startSession(hub); + startHubSession(hub); // Emitted in the case of healthy sessions, error of `mechanism.handled: true` and unhandledrejections because // The 'beforeExit' event is not emitted for conditions causing explicit termination, // such as calling process.exit() or uncaught exceptions. // Ref: https://nodejs.org/api/process.html#process_event_beforeexit process.on('beforeExit', () => { - const session = getSession(hub.getScope()); + const session = getScopeSession(getHubScope(hub)); const terminalStates: SessionStatus[] = ['exited', 'crashed']; // Only call endSession, if the Session exists on Scope and SessionStatus is not a // Terminal Status i.e. Exited or Crashed because // "When a session is moved away from ok it must not be updated anymore." // Ref: https://develop.sentry.dev/sdk/sessions/ - if (session && !terminalStates.includes(session.status)) hub.endSession(); + if (session && !terminalStates.includes(session.status)) endHubSession(hub); }); } diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 36c17ec7cd51..0f2b3b811241 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -11,6 +11,8 @@ import * as nock from 'nock'; import { Breadcrumb } from '../../src'; import { NodeClient } from '../../src/client'; import { Http as HttpIntegration } from '../../src/integrations/http'; +import { bindHubClient } from '@sentry/hub/src'; +import { startHubTransaction } from '@sentry/hub/src/hub'; const NODE_VERSION = parseSemver(process.versions.node); @@ -30,7 +32,7 @@ describe('tracing', () => { jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); jest.spyOn(hubModule, 'getCurrentHub').mockReturnValue(hub); - const transaction = hub.startTransaction({ name: 'dogpark' }); + const transaction = startHubTransaction(hub, { name: 'dogpark' }); hub.getScope()?.setSpan(transaction); return transaction; @@ -107,7 +109,8 @@ describe('default protocols', () => { const p = new Promise(r => { resolve = r; }); - hub.bindClient( + bindHubClient( + hub, new NodeClient({ dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', integrations: [new HttpIntegration({ breadcrumbs: true })], diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 76678f6646cb..0a44c33d8755 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -22,6 +22,7 @@ export { CaptureContext, Scope, ScopeContext } from './scope'; export { SdkInfo } from './sdkinfo'; export { SdkMetadata } from './sdkmetadata'; export { + Session, SessionAggregates, AggregationCounts, SessionContext, From 8bb839182b6b56ab04f88d39b893f7edf9481604 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 04:13:13 -0500 Subject: [PATCH 31/37] fix: minimal package --- packages/hub/src/hub.ts | 8 ++--- packages/hub/src/index.ts | 8 +++++ packages/hub/test/hub.test.ts | 4 +-- packages/minimal/src/index.ts | 59 +++++++++++++++++++++++------------ 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index a5958b94d86d..4e426bf59570 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -339,7 +339,7 @@ export function captureHubException(hub: Hub, exception: any, hint?: EventHint): }; } - _invokeClient(hub, 'captureException', exception, { + _invokeHubClient(hub, 'captureException', exception, { ...finalHint, event_id: eventId, }); @@ -376,7 +376,7 @@ export function captureHubMessage(hub: Hub, message: string, level?: SeverityLev }; } - _invokeClient(hub, 'captureMessage', message, level, { + _invokeHubClient(hub, 'captureMessage', message, level, { ...finalHint, event_id: eventId, }); @@ -396,7 +396,7 @@ export function captureHubEvent(hub: Hub, event: Event, hint?: EventHint): strin hub.lastEventId = eventId; } - _invokeClient(hub, 'captureEvent', event, { + _invokeHubClient(hub, 'captureEvent', event, { ...hint, event_id: eventId, }); @@ -577,7 +577,7 @@ export function traceHeaders(hub: Hub): { [key: string]: string } { * @param args Arguments to pass to the client function. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function _invokeClient(hub: Hub, method: M, ...args: any[]): void { +export function _invokeHubClient(hub: Hub, method: M, ...args: any[]): void { const { scope, client } = getHubStackTop(hub); if (client && client[method]) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 442bef428661..58070ac825d8 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -45,13 +45,21 @@ export { getHubIntegration, captureHubMessage, configureHubScope, + startHubTransaction, Hub, + setHubTags, + setHubTag, + setHubExtras, + setHubExtra, makeMain, Carrier, // eslint-disable-next-line deprecation/deprecation DomainAsCarrier, Layer, getHubFromCarrier, + setHubContext, setHubOnCarrier, getMainCarrier, + // TODO: This is being used from outside ... weird + _invokeHubClient, } from './hub'; diff --git a/packages/hub/test/hub.test.ts b/packages/hub/test/hub.test.ts index 71ccbd32fc65..22ab2ff12a1f 100644 --- a/packages/hub/test/hub.test.ts +++ b/packages/hub/test/hub.test.ts @@ -1,7 +1,7 @@ import { Event } from '@sentry/types'; import { - _invokeClient, + _invokeHubClient, addHubBreadcrumb, bindHubClient, captureHubEvent, @@ -64,7 +64,7 @@ describe('Hub', () => { test("don't invoke client sync with wrong func", () => { const hub = new Hub(clientFn); // @ts-ignore we want to able to call private method - _invokeClient(hub, 'funca', true); + _invokeHubClient(hub, 'funca', true); expect(clientFn).not.toHaveBeenCalled(); }); diff --git a/packages/minimal/src/index.ts b/packages/minimal/src/index.ts index 15fcf7e3eaab..10ee78103e44 100644 --- a/packages/minimal/src/index.ts +++ b/packages/minimal/src/index.ts @@ -1,4 +1,21 @@ -import { getCurrentHub, Hub, Scope } from '@sentry/hub'; +import { + _invokeHubClient, + addHubBreadcrumb, + captureHubEvent, + captureHubException, + captureHubMessage, + configureHubScope, + setHubUser, + withHubScope, + getCurrentHub, + setHubContext, + setHubExtra, + setHubExtras, + setHubTag, + setHubTags, + startHubTransaction, + Scope, +} from '@sentry/hub'; import { Breadcrumb, CaptureContext, @@ -13,19 +30,21 @@ import { User, } from '@sentry/types'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyFunction = (...args: any[]) => any; + /** * This calls a function on the current hub. * @param method function to call on hub. * @param args to pass to function. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -function callOnHub(method: string, ...args: any[]): T { +function callOnHub(method: TMethod, ...args: any[]): ReturnType { const hub = getCurrentHub(); - if (hub && hub[method as keyof Hub]) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (hub[method as keyof Hub] as any)(...args); + if (hub) { + return method(hub, ...args); } - throw new Error(`No hub defined or ${method} was not found on the hub, please open a bug report.`); + throw new Error(`No hub defined please open a bug report.`); } /** @@ -42,7 +61,7 @@ export function captureException(exception: any, captureContext?: CaptureContext } catch (exception) { syntheticException = exception as Error; } - return callOnHub('captureException', exception, { + return callOnHub(captureHubException, exception, { captureContext, originalException: exception, syntheticException, @@ -69,7 +88,7 @@ export function captureMessage(message: string, captureContext?: CaptureContext const level = typeof captureContext === 'string' ? captureContext : undefined; const context = typeof captureContext !== 'string' ? { captureContext } : undefined; - return callOnHub('captureMessage', message, level, { + return callOnHub(captureHubMessage, message, level, { originalException: message, syntheticException, ...context, @@ -83,7 +102,7 @@ export function captureMessage(message: string, captureContext?: CaptureContext * @returns The generated eventId. */ export function captureEvent(event: Event): string { - return callOnHub('captureEvent', event); + return callOnHub(captureHubEvent, event); } /** @@ -91,7 +110,7 @@ export function captureEvent(event: Event): string { * @param callback Callback function that receives Scope. */ export function configureScope(callback: (scope: Scope) => void): void { - callOnHub('configureScope', callback); + callOnHub(configureHubScope, callback); } /** @@ -103,7 +122,7 @@ export function configureScope(callback: (scope: Scope) => void): void { * @param breadcrumb The breadcrumb to record. */ export function addBreadcrumb(breadcrumb: Breadcrumb): void { - callOnHub('addBreadcrumb', breadcrumb); + callOnHub(addHubBreadcrumb, breadcrumb); } /** @@ -113,7 +132,7 @@ export function addBreadcrumb(breadcrumb: Breadcrumb): void { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function setContext(name: string, context: { [key: string]: any } | null): void { - callOnHub('setContext', name, context); + callOnHub(setHubContext, name, context); } /** @@ -121,7 +140,7 @@ export function setContext(name: string, context: { [key: string]: any } | null) * @param extras Extras object to merge into current context. */ export function setExtras(extras: Extras): void { - callOnHub('setExtras', extras); + callOnHub(setHubExtras, extras); } /** @@ -129,7 +148,7 @@ export function setExtras(extras: Extras): void { * @param tags Tags context object to merge into current context. */ export function setTags(tags: { [key: string]: Primitive }): void { - callOnHub('setTags', tags); + callOnHub(setHubTags, tags); } /** @@ -138,7 +157,7 @@ export function setTags(tags: { [key: string]: Primitive }): void { * @param extra Any kind of data. This data will be normalized. */ export function setExtra(key: string, extra: Extra): void { - callOnHub('setExtra', key, extra); + callOnHub(setHubExtra, key, extra); } /** @@ -150,7 +169,7 @@ export function setExtra(key: string, extra: Extra): void { * @param value Value of tag */ export function setTag(key: string, value: Primitive): void { - callOnHub('setTag', key, value); + callOnHub(setHubTag, key, value); } /** @@ -159,7 +178,7 @@ export function setTag(key: string, value: Primitive): void { * @param user User context object to be set in the current context. Pass `null` to unset the user. */ export function setUser(user: User | null): void { - callOnHub('setUser', user); + callOnHub(setHubUser, user); } /** @@ -176,7 +195,7 @@ export function setUser(user: User | null): void { * @param callback that will be enclosed into push/popScope. */ export function withScope(callback: (scope: Scope) => void): void { - callOnHub('withScope', callback); + callOnHub(withHubScope, callback); } /** @@ -191,7 +210,7 @@ export function withScope(callback: (scope: Scope) => void): void { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function _callOnClient(method: string, ...args: any[]): void { - callOnHub('_invokeClient', method, ...args); + callOnHub(_invokeHubClient, method, ...args); } /** @@ -215,5 +234,5 @@ export function startTransaction( context: TransactionContext, customSamplingContext?: CustomSamplingContext, ): Transaction { - return callOnHub('startTransaction', { ...context }, customSamplingContext); + return callOnHub(startHubTransaction, { ...context }, customSamplingContext); } From ee258de2e519be47c6924a8fb5961757a880e3c1 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 04:21:19 -0500 Subject: [PATCH 32/37] fix: core --- packages/core/src/baseclient.ts | 16 ++++++++++++---- packages/core/src/integrations/inboundfilters.ts | 6 +++--- packages/core/src/sdk.ts | 6 +++--- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 96a13cf96e6f..656a1b36fe6d 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -1,5 +1,13 @@ /* eslint-disable max-lines */ -import { Scope, Session, updateSession, cloneScope, getSession } from '@sentry/hub'; +import { + applyScopeToEvent, + cloneScope, + getScopeSession, + Scope, + Session, + updateScope, + updateSession, +} from '@sentry/hub'; import { Client, DsnComponents, @@ -360,7 +368,7 @@ export abstract class BaseClient implement // This allows us to prevent unnecessary copying of data if `captureContext` is not provided. let finalScope = scope; if (hint && hint.captureContext) { - finalScope = cloneScope(finalScope).update(hint.captureContext); + finalScope = updateScope(cloneScope(finalScope), hint.captureContext); } // We prepare the result here with a resolved Event. @@ -370,7 +378,7 @@ export abstract class BaseClient implement // {@link Hub.addEventProcessor} gets the finished prepared event. if (finalScope) { // In case we have a hub we reassign it. - result = finalScope.applyToEvent(prepared, hint); + result = applyScopeToEvent(finalScope, prepared, hint); } return result.then(evt => { @@ -576,7 +584,7 @@ export abstract class BaseClient implement throw new SentryError('`beforeSend` returned `null`, will not send event.'); } - const session = getSession(scope); + const session = getScopeSession(scope); if (!isTransaction && session) { this._updateSessionFromEvent(session, processedEvent); } diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts index 2c5ed2ea5db9..c28e23ab34da 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/inboundfilters.ts @@ -1,4 +1,4 @@ -import { addGlobalEventProcessor, getClient,getCurrentHub, getIntegration } from '@sentry/hub'; +import { addGlobalEventProcessor, getHubClient, getCurrentHub, getHubIntegration } from '@sentry/hub'; import { Event, Integration, StackFrame } from '@sentry/types'; import { getEventDescription, isDebugBuild, isMatchingPattern, logger } from '@sentry/utils'; @@ -44,9 +44,9 @@ export class InboundFilters implements Integration { } // TODO: this is really confusing, why would ask back the integration? // setupOnce() belongs already to `self` (this) so it is confusing to ask for it. No? - const self = getIntegration(hub, InboundFilters); + const self = getHubIntegration(hub, InboundFilters); if (self) { - const client = getClient(hub); + const client = getHubClient(hub); const clientOptions = client ? client.getOptions() : {}; // This checks prevents most of the occurrences of the bug linked below: // https://github.com/getsentry/sentry-javascript/issues/2622 diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index 641188c7b19f..fe39b27ba143 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,4 +1,4 @@ -import { bindClient, getCurrentHub, getScope, updateScope } from '@sentry/hub'; +import { bindHubClient, getCurrentHub, getHubScope, updateScope } from '@sentry/hub'; import { Client, Options } from '@sentry/types'; import { logger } from '@sentry/utils'; @@ -17,10 +17,10 @@ export function initAndBind(clientClass: Cl logger.enable(); } const hub = getCurrentHub(); - const scope = getScope(hub); + const scope = getHubScope(hub); if (scope) { updateScope(scope, options.initialScope); } const client = new clientClass(options); - bindClient(hub, client); + bindHubClient(hub, client); } From 64e9d6b7295995f2d3f165d91600d9e6293bde20 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 04:27:59 -0500 Subject: [PATCH 33/37] fix: browser --- .../browser/src/integrations/breadcrumbs.ts | 16 +++++------ packages/browser/src/integrations/dedupe.ts | 6 ++-- .../src/integrations/globalhandlers.ts | 10 +++---- .../browser/src/integrations/linkederrors.ts | 4 +-- .../browser/src/integrations/useragent.ts | 4 +-- packages/browser/src/sdk.ts | 28 ++++++++++++------- packages/hub/src/index.ts | 1 + 7 files changed, 39 insertions(+), 30 deletions(-) diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index 27a7fa26b3ae..3931ca6536cc 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable max-lines */ import { getCurrentHub } from '@sentry/core'; -import { addBreadcrumb } from '@sentry/hub'; +import { addHubBreadcrumb } from '@sentry/hub'; import { Event, Integration } from '@sentry/types'; import { addInstrumentationHandler, @@ -63,7 +63,7 @@ export class Breadcrumbs implements Integration { if (!this._options.sentry) { return; } - addBreadcrumb( + addHubBreadcrumb( getCurrentHub(), { category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`, @@ -132,7 +132,7 @@ function _domBreadcrumb(dom: BreadcrumbsOptions['dom']): (handlerData: { [key: s return; } - addBreadcrumb( + addHubBreadcrumb( getCurrentHub(), { category: `ui.${handlerData.name}`, @@ -174,7 +174,7 @@ function _consoleBreadcrumb(handlerData: { [key: string]: any }): void { } } - addBreadcrumb(getCurrentHub(), breadcrumb, { + addHubBreadcrumb(getCurrentHub(), breadcrumb, { input: handlerData.args, level: handlerData.level, }); @@ -193,7 +193,7 @@ function _xhrBreadcrumb(handlerData: { [key: string]: any }): void { const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__ || {}; - addBreadcrumb( + addHubBreadcrumb( getCurrentHub(), { category: 'xhr', @@ -230,7 +230,7 @@ function _fetchBreadcrumb(handlerData: { [key: string]: any }): void { } if (handlerData.error) { - addBreadcrumb( + addHubBreadcrumb( getCurrentHub(), { category: 'fetch', @@ -244,7 +244,7 @@ function _fetchBreadcrumb(handlerData: { [key: string]: any }): void { }, ); } else { - addBreadcrumb( + addHubBreadcrumb( getCurrentHub(), { category: 'fetch', @@ -288,7 +288,7 @@ function _historyBreadcrumb(handlerData: { [key: string]: any }): void { from = parsedFrom.relative; } - addBreadcrumb(getCurrentHub(), { + addHubBreadcrumb(getCurrentHub(), { category: 'navigation', data: { from, diff --git a/packages/browser/src/integrations/dedupe.ts b/packages/browser/src/integrations/dedupe.ts index 4698c17b6c1b..51e14c1e4465 100644 --- a/packages/browser/src/integrations/dedupe.ts +++ b/packages/browser/src/integrations/dedupe.ts @@ -1,6 +1,6 @@ import { Event, EventProcessor, Exception, Integration, StackFrame } from '@sentry/types'; import { logger } from '@sentry/utils'; -import { Hub, getIntegration } from '@sentry/hub'; +import { Hub, getHubIntegration } from '@sentry/hub'; /** Deduplication filter */ export class Dedupe implements Integration { @@ -17,14 +17,14 @@ export class Dedupe implements Integration { /** * @inheritDoc */ - private _previousEvent?: Event; + public _previousEvent?: Event; /** * @inheritDoc */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((currentEvent: Event) => { - const self = getIntegration(getCurrentHub(), Dedupe); + const self = getHubIntegration(getCurrentHub(), Dedupe); if (self) { // Juuust in case something goes wrong try { diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index df40bc6c7274..d18c82ff4603 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getCurrentHub } from '@sentry/core'; -import { captureEvent, getClient, getIntegration, Hub } from '@sentry/hub'; +import { captureHubEvent, getHubClient, getHubIntegration, Hub } from '@sentry/hub'; import { Event, EventHint, Integration, Primitive } from '@sentry/types'; import { addExceptionMechanism, @@ -81,7 +81,7 @@ function _installGlobalOnErrorHandler(): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any (data: { msg: any; url: any; line: any; column: any; error: any }) => { const [hub, attachStacktrace] = getHubAndAttachStacktrace(); - if (!getIntegration(hub, GlobalHandlers)) { + if (!getHubIntegration(hub, GlobalHandlers)) { return; } const { msg, url, line, column, error } = data; @@ -114,7 +114,7 @@ function _installGlobalOnUnhandledRejectionHandler(): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any (e: any) => { const [hub, attachStacktrace] = getHubAndAttachStacktrace(); - if (!getIntegration(hub, GlobalHandlers)) { + if (!getHubIntegration(hub, GlobalHandlers)) { return; } let error = e; @@ -251,14 +251,14 @@ function addMechanismAndCapture(hub: Hub, error: EventHint['originalException'], handled: false, type, }); - captureEvent(hub, event, { + captureHubEvent(hub, event, { originalException: error, }); } function getHubAndAttachStacktrace(): [Hub, boolean | undefined] { const hub = getCurrentHub(); - const client = getClient(hub); + const client = getHubClient(hub); const attachStacktrace = client && client.getOptions().attachStacktrace; return [hub, attachStacktrace]; } diff --git a/packages/browser/src/integrations/linkederrors.ts b/packages/browser/src/integrations/linkederrors.ts index 5935d88ae9c1..d28d5df3b7bd 100644 --- a/packages/browser/src/integrations/linkederrors.ts +++ b/packages/browser/src/integrations/linkederrors.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import { getIntegration } from '@sentry/hub'; +import { getHubIntegration } from '@sentry/hub'; import { Event, EventHint, Exception, ExtendedError, Integration } from '@sentry/types'; import { isInstanceOf } from '@sentry/utils'; @@ -49,7 +49,7 @@ export class LinkedErrors implements Integration { */ public setupOnce(): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getIntegration(getCurrentHub(), LinkedErrors); + const self = getHubIntegration(getCurrentHub(), LinkedErrors); return self ? _handler(self._key, self._limit, event, hint) : event; }); } diff --git a/packages/browser/src/integrations/useragent.ts b/packages/browser/src/integrations/useragent.ts index 567d7df444f0..ebc7f2e35c19 100644 --- a/packages/browser/src/integrations/useragent.ts +++ b/packages/browser/src/integrations/useragent.ts @@ -1,5 +1,5 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; -import { getIntegration } from '@sentry/hub'; +import { getHubIntegration } from '@sentry/hub'; import { Event, Integration } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils'; @@ -22,7 +22,7 @@ export class UserAgent implements Integration { */ public setupOnce(): void { addGlobalEventProcessor((event: Event) => { - if (getIntegration(getCurrentHub(), UserAgent)) { + if (getHubIntegration(getCurrentHub(), UserAgent)) { // if none of the information we want exists, don't bother if (!global.navigator && !global.location && !global.document) { return event; diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index a46add98d45d..c9ec6a8e78ea 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -1,5 +1,13 @@ import { getCurrentHub, initAndBind, Integrations as CoreIntegrations } from '@sentry/core'; -import { startSession, getClient, getScope, Hub, lastEventId as hubLastEventId, captureSession } from '@sentry/hub'; +import { + getScopeUser, + captureHubSession, + getHubClient, + getHubLastEventId, + getHubScope, + Hub, + startHubSession, +} from '@sentry/hub'; import { addInstrumentationHandler, getGlobalObject, isDebugBuild, logger, resolvedSyncPromise } from '@sentry/utils'; import { BrowserOptions } from './backend'; @@ -107,18 +115,18 @@ export function init(options: BrowserOptions = {}): void { */ export function showReportDialog(options: ReportDialogOptions = {}): void { const hub = getCurrentHub(); - const scope = getScope(hub); + const scope = getHubScope(hub); if (scope) { options.user = { - ...scope.getUser(), + ...getScopeUser(scope), ...options.user, }; } if (!options.eventId) { - options.eventId = hubLastEventId(hub); + options.eventId = getHubLastEventId(hub); } - const client = getClient(hub); + const client = getHubClient(hub); if (client) { client.showReportDialog(options); } @@ -130,7 +138,7 @@ export function showReportDialog(options: ReportDialogOptions = {}): void { * @returns The last event id of a captured event. */ export function lastEventId(): string | undefined { - return hubLastEventId(getCurrentHub()); + return getHubLastEventId(getCurrentHub()); } /** @@ -158,7 +166,7 @@ export function onLoad(callback: () => void): void { * doesn't (or if there's no client defined). */ export function flush(timeout?: number): PromiseLike { - const client = getClient(getCurrentHub()); + const client = getHubClient(getCurrentHub()); if (client) { return client.flush(timeout); } @@ -177,7 +185,7 @@ export function flush(timeout?: number): PromiseLike { * doesn't (or if there's no client defined). */ export function close(timeout?: number): PromiseLike { - const client = getClient(getCurrentHub()); + const client = getHubClient(getCurrentHub()); if (client) { return client.close(timeout); } @@ -200,8 +208,8 @@ export function wrap(fn: (...args: any) => any): any { } function startSessionOnHub(hub: Hub): void { - startSession(hub, { ignoreDuration: true }); - captureSession(hub); + startHubSession(hub, { ignoreDuration: true }); + captureHubSession(hub); } /** diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 58070ac825d8..4f57f5299230 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -15,6 +15,7 @@ export { addScopeEventProcessor, setScopeSession, getScopeSpan, + getScopeUser, Scope, } from './scope'; export { Session, updateSession } from './session'; From a9204ed12734c966a93000ccf470547422ac4e92 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 04:30:37 -0500 Subject: [PATCH 34/37] fix: react --- packages/hub/src/index.ts | 2 ++ packages/react/src/profiler.tsx | 22 ++++++++++------------ packages/react/src/redux.ts | 8 +++++--- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index 4f57f5299230..c87f807bd6f9 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -16,6 +16,8 @@ export { setScopeSession, getScopeSpan, getScopeUser, + setScopeContext, + getScopeTransaction, Scope, } from './scope'; export { Session, updateSession } from './session'; diff --git a/packages/react/src/profiler.tsx b/packages/react/src/profiler.tsx index ca3281cc9444..5fe0523d4a7a 100644 --- a/packages/react/src/profiler.tsx +++ b/packages/react/src/profiler.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { getCurrentHub, Hub } from '@sentry/browser'; -import { getScope, getIntegration } from '@sentry/hub'; +import { getHubIntegration, getHubScope, getScopeTransaction } from '@sentry/hub'; import { Integration, IntegrationClass, Span, Transaction } from '@sentry/types'; import { timestampWithMs } from '@sentry/utils'; import hoistNonReactStatics from 'hoist-non-react-statics'; @@ -22,7 +22,7 @@ const getTracingIntegration = (): Integration | null => { return globalTracingIntegration; } - globalTracingIntegration = getIntegration(getCurrentHub(), TRACING_GETTER); + globalTracingIntegration = getHubIntegration(getCurrentHub(), TRACING_GETTER); return globalTracingIntegration; }; @@ -90,22 +90,20 @@ export type ProfilerProps = { * spans based on component lifecycles. */ class Profiler extends React.Component { + // eslint-disable-next-line @typescript-eslint/member-ordering + public static defaultProps: Partial = { + disabled: false, + includeRender: true, + includeUpdates: true, + }; /** * The span of the mount activity * Made protected for the React Native SDK to access */ protected _mountSpan: Span | undefined = undefined; - // The activity representing how long it takes to mount a component. private _mountActivity: number | null = null; - // eslint-disable-next-line @typescript-eslint/member-ordering - public static defaultProps: Partial = { - disabled: false, - includeRender: true, - includeUpdates: true, - }; - public constructor(props: ProfilerProps) { super(props); const { name, disabled = false } = this.props; @@ -275,9 +273,9 @@ export { withProfiler, Profiler, useProfiler }; /** Grabs active transaction off scope */ export function getActiveTransaction(hub: Hub = getCurrentHub()): T | undefined { if (hub) { - const scope = getScope(hub); + const scope = getHubScope(hub); if (scope) { - return scope.getTransaction() as T | undefined; + return getScopeTransaction(scope) as T | undefined; } } diff --git a/packages/react/src/redux.ts b/packages/react/src/redux.ts index ad4b4fe7b5e7..0c745ffb87dd 100644 --- a/packages/react/src/redux.ts +++ b/packages/react/src/redux.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { configureScope } from '@sentry/minimal'; import { Scope } from '@sentry/types'; +import { setScopeContext } from '@sentry/hub'; +import { addScopeBreadcrumb } from '@sentry/hub/src/scope'; interface Action { type: T; @@ -98,7 +100,7 @@ function createReduxEnhancer(enhancerOptions?: Partial): /* Action breadcrumbs */ const transformedAction = options.actionTransformer(action); if (typeof transformedAction !== 'undefined' && transformedAction !== null) { - scope.addBreadcrumb({ + addScopeBreadcrumb(scope, { category: ACTION_BREADCRUMB_CATEGORY, data: transformedAction, type: ACTION_BREADCRUMB_TYPE, @@ -108,9 +110,9 @@ function createReduxEnhancer(enhancerOptions?: Partial): /* Set latest state to scope */ const transformedState = options.stateTransformer(newState); if (typeof transformedState !== 'undefined' && transformedState !== null) { - scope.setContext(STATE_CONTEXT_KEY, transformedState); + setScopeContext(scope, STATE_CONTEXT_KEY, transformedState); } else { - scope.setContext(STATE_CONTEXT_KEY, null); + setScopeContext(scope, STATE_CONTEXT_KEY, null); } /* Allow user to configure scope with latest state */ From 31c6711e15244a40fe4b8db55a33f5a9c6b66ce0 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 04:39:23 -0500 Subject: [PATCH 35/37] fix: nextjs --- packages/nextjs/src/index.client.ts | 8 +++-- packages/nextjs/src/index.server.ts | 34 ++++++++++++++----- packages/nextjs/src/utils/instrumentServer.ts | 9 ++--- packages/nextjs/src/utils/withSentry.ts | 9 ++--- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/packages/nextjs/src/index.client.ts b/packages/nextjs/src/index.client.ts index 61a1b27da7f3..8ec88021cc9f 100644 --- a/packages/nextjs/src/index.client.ts +++ b/packages/nextjs/src/index.client.ts @@ -5,6 +5,8 @@ import { nextRouterInstrumentation } from './performance/client'; import { buildMetadata } from './utils/metadata'; import { NextjsOptions } from './utils/nextjsOptions'; import { addIntegration, UserIntegrations } from './utils/userIntegrations'; +import { addScopeEventProcessor } from '@sentry/hub'; +import { setScopeTag } from '@sentry/hub/dist/scope'; export * from '@sentry/react'; export { nextRouterInstrumentation } from './performance/client'; @@ -27,8 +29,10 @@ export function init(options: NextjsOptions): void { integrations, }); configureScope(scope => { - scope.setTag('runtime', 'browser'); - scope.addEventProcessor(event => (event.type === 'transaction' && event.transaction === '/404' ? null : event)); + setScopeTag(scope, 'runtime', 'browser'); + addScopeEventProcessor(scope, event => + event.type === 'transaction' && event.transaction === '/404' ? null : event, + ); }); } diff --git a/packages/nextjs/src/index.server.ts b/packages/nextjs/src/index.server.ts index 8eec0e1a6dbe..15425cdfa893 100644 --- a/packages/nextjs/src/index.server.ts +++ b/packages/nextjs/src/index.server.ts @@ -1,4 +1,13 @@ -import { bindClient, Carrier, getClient,getHubFromCarrier, getMainCarrier } from '@sentry/hub'; +import { + addScopeEventProcessor, + bindHubClient, + Carrier, + getHubClient, + getHubFromCarrier, + getHubScope, + getMainCarrier, + updateScope, +} from '@sentry/hub'; import { RewriteFrames } from '@sentry/integrations'; import { configureScope, getCurrentHub, init as nodeInit, Integrations } from '@sentry/node'; import { Event } from '@sentry/types'; @@ -9,6 +18,7 @@ import * as path from 'path'; import { buildMetadata } from './utils/metadata'; import { NextjsOptions } from './utils/nextjsOptions'; import { addIntegration } from './utils/userIntegrations'; +import { setScopeTag } from '@sentry/hub/dist/scope'; export * from '@sentry/node'; @@ -62,12 +72,12 @@ export function init(options: NextjsOptions): void { nodeInit(options); configureScope(scope => { - scope.setTag('runtime', 'node'); + setScopeTag(scope, 'runtime', 'node'); if (isVercel) { - scope.setTag('vercel', true); + setScopeTag(scope, 'vercel', true); } - scope.addEventProcessor(filterTransactions); + addScopeEventProcessor(scope, filterTransactions); }); if (activeDomain) { @@ -75,10 +85,18 @@ export function init(options: NextjsOptions): void { const domainHub = getHubFromCarrier(activeDomain); // apply the changes made by `nodeInit` to the domain's hub also - bindClient(domainHub, getClient(globalHub)); - domainHub.getScope()?.update(globalHub.getScope()); + bindHubClient(domainHub, getHubClient(globalHub)); + const domainScope = getHubScope(domainHub); + const globalScope = getHubScope(globalHub); + if (domainScope) { + updateScope(domainScope, globalScope); + } + // `scope.update()` doesn’t copy over event processors, so we have to add it manually - domainHub.getScope()?.addEventProcessor(filterTransactions); + const scope = getHubScope(domainHub); + if (scope) { + addScopeEventProcessor(scope, filterTransactions); + } // restore the domain hub as the current one domain.active = activeDomain; @@ -89,7 +107,7 @@ export function init(options: NextjsOptions): void { function sdkAlreadyInitialized(): boolean { const hub = getCurrentHub(); - return !!getClient(hub); + return !!getHubClient(hub); } function addServerIntegrations(options: NextjsOptions): void { diff --git a/packages/nextjs/src/utils/instrumentServer.ts b/packages/nextjs/src/utils/instrumentServer.ts index c05fa9cac226..d7bbae194aaf 100644 --- a/packages/nextjs/src/utils/instrumentServer.ts +++ b/packages/nextjs/src/utils/instrumentServer.ts @@ -14,6 +14,7 @@ import * as http from 'http'; import { default as createNextServer } from 'next'; import * as querystring from 'querystring'; import * as url from 'url'; +import { addScopeEventProcessor, getHubScope, setScopeSpan } from '@sentry/hub'; const { parseRequest } = Handlers; @@ -159,7 +160,7 @@ function makeWrappedErrorLogger(origErrorLogger: ErrorLogger): WrappedErrorLogge // gets its own scope. (`configureScope` has the advantage of not creating a clone of the current scope before // modifying it, which in this case is unnecessary.) configureScope(scope => { - scope.addEventProcessor(event => { + addScopeEventProcessor(scope, event => { addExceptionMechanism(event, { type: 'instrument', handled: true, @@ -217,10 +218,10 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler { // local.on('error', Sentry.captureException); local.run(() => { - const currentScope = getCurrentHub().getScope(); + const currentScope = getHubScope(getCurrentHub()); if (currentScope) { - currentScope.addEventProcessor(event => parseRequest(event, req)); + addScopeEventProcessor(currentScope, event => parseRequest(event, req)); // We only want to record page and API requests if (hasTracingEnabled() && shouldTraceRequest(req.url, publicDirFiles)) { @@ -249,7 +250,7 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler { { request: req }, ); - currentScope.setSpan(transaction); + setScopeSpan(currentScope, transaction); res.once('finish', () => { const transaction = getActiveTransaction(); diff --git a/packages/nextjs/src/utils/withSentry.ts b/packages/nextjs/src/utils/withSentry.ts index 6c0f1d26a764..86bed8048bec 100644 --- a/packages/nextjs/src/utils/withSentry.ts +++ b/packages/nextjs/src/utils/withSentry.ts @@ -1,3 +1,4 @@ +import { addScopeEventProcessor, getHubScope, setScopeSpan } from '@sentry/hub'; import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node'; import { extractTraceparentData, hasTracingEnabled } from '@sentry/tracing'; import { Transaction } from '@sentry/types'; @@ -33,10 +34,10 @@ export const withSentry = (origHandler: NextApiHandler): WrappedNextApiHandler = // return a value. In our case, all any of the codepaths return is a promise of `void`, but nextjs still counts on // getting that before it will finish the response. const boundHandler = local.bind(async () => { - const currentScope = getCurrentHub().getScope(); + const currentScope = getHubScope(getCurrentHub()); if (currentScope) { - currentScope.addEventProcessor(event => parseRequest(event, req)); + addScopeEventProcessor(currentScope, event => parseRequest(event, req)); if (hasTracingEnabled()) { // If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision) @@ -68,7 +69,7 @@ export const withSentry = (origHandler: NextApiHandler): WrappedNextApiHandler = // extra context passed to the `tracesSampler` { request: req }, ); - currentScope.setSpan(transaction); + setScopeSpan(currentScope, transaction); // save a link to the transaction on the response, so that even if there's an error (landing us outside of // the domain), we can still finish it (albeit possibly missing some scope data) @@ -136,7 +137,7 @@ export const withSentry = (origHandler: NextApiHandler): WrappedNextApiHandler = const objectifiedErr = objectify(e); if (currentScope) { - currentScope.addEventProcessor(event => { + addScopeEventProcessor(currentScope, event => { addExceptionMechanism(event, { type: 'instrument', handled: true, From d1370e7bc530031bc2ffe9f949e550f814eba695 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 04:46:39 -0500 Subject: [PATCH 36/37] fix: tracing --- packages/tracing/src/hubextensions.ts | 10 +++++----- packages/tracing/src/idletransaction.ts | 10 +++++----- packages/tracing/src/integrations/node/mongo.ts | 6 +++--- packages/tracing/src/integrations/node/mysql.ts | 6 +++--- packages/tracing/src/integrations/node/postgres.ts | 6 +++--- packages/tracing/src/transaction.ts | 6 +++--- packages/tracing/src/utils.ts | 8 ++++---- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/tracing/src/hubextensions.ts b/packages/tracing/src/hubextensions.ts index 4a76c39c8186..b8a47382fa2e 100644 --- a/packages/tracing/src/hubextensions.ts +++ b/packages/tracing/src/hubextensions.ts @@ -1,4 +1,4 @@ -import { getClient, getMainCarrier, getScope, Hub } from '@sentry/hub'; +import { getHubClient, getMainCarrier, getHubScope, Hub, getScopeSpan } from '@sentry/hub'; import { CustomSamplingContext, Integration, @@ -16,9 +16,9 @@ import { hasTracingEnabled } from './utils'; /** Returns all trace headers that are currently on the top scope. */ function traceHeaders(this: Hub): { [key: string]: string } { - const scope = getScope(this); + const scope = getHubScope(this); if (scope) { - const span = scope.getSpan(); + const span = getScopeSpan(scope); if (span) { return { 'sentry-trace': span.toTraceparent(), @@ -165,7 +165,7 @@ function _startTransaction( transactionContext: TransactionContext, customSamplingContext?: CustomSamplingContext, ): Transaction { - const client = getClient(this); + const client = getHubClient(this); const options = (client && client.getOptions()) || {}; let transaction = new Transaction(transactionContext, this); @@ -190,7 +190,7 @@ export function startIdleTransaction( onScope?: boolean, customSamplingContext?: CustomSamplingContext, ): IdleTransaction { - const client = getClient(hub); + const client = getHubClient(hub); const options = (client && client.getOptions()) || {}; let transaction = new IdleTransaction(transactionContext, hub, idleTimeout, onScope); diff --git a/packages/tracing/src/idletransaction.ts b/packages/tracing/src/idletransaction.ts index 948752095692..c522281ae7bc 100644 --- a/packages/tracing/src/idletransaction.ts +++ b/packages/tracing/src/idletransaction.ts @@ -1,4 +1,4 @@ -import { getScope, Hub, configureScope } from '@sentry/hub'; +import { getHubScope, Hub, configureHubScope, setScopeSpan, getScopeTransaction } from '@sentry/hub'; import { TransactionContext } from '@sentry/types'; import { logger, timestampWithMs } from '@sentry/utils'; @@ -93,7 +93,7 @@ export class IdleTransaction extends Transaction { // We set the transaction here on the scope so error events pick up the trace // context and attach it to the error. logger.log(`Setting idle transaction on scope. Span ID: ${this.spanId}`); - configureScope(_idleHub, scope => scope.setSpan(this)); + configureHubScope(_idleHub, scope => setScopeSpan(scope, this)); } this._initTimeout = setTimeout(() => { @@ -276,11 +276,11 @@ export class IdleTransaction extends Transaction { */ function clearActiveTransaction(hub?: Hub): void { if (hub) { - const scope = getScope(hub); + const scope = getHubScope(hub); if (scope) { - const transaction = scope.getTransaction(); + const transaction = getScopeTransaction(scope); if (transaction) { - scope.setSpan(undefined); + setScopeSpan(scope, undefined); } } } diff --git a/packages/tracing/src/integrations/node/mongo.ts b/packages/tracing/src/integrations/node/mongo.ts index 8768a9f835cf..6b3261eb481c 100644 --- a/packages/tracing/src/integrations/node/mongo.ts +++ b/packages/tracing/src/integrations/node/mongo.ts @@ -1,4 +1,4 @@ -import { getScope, Hub } from '@sentry/hub'; +import { getHubScope, getScopeSpan, Hub } from '@sentry/hub'; import { EventProcessor, Integration, SpanContext } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; @@ -148,8 +148,8 @@ export class Mongo implements Integration { fill(collection.prototype, operation, function(orig: () => void | Promise) { return function(this: unknown, ...args: unknown[]) { const lastArg = args[args.length - 1]; - const scope = getScope(getCurrentHub()); - const parentSpan = scope?.getSpan(); + const scope = getHubScope(getCurrentHub()); + const parentSpan = scope && getScopeSpan(scope); // Check if the operation was passed a callback. (mapReduce requires a different check, as // its (non-callback) arguments can also be functions.) diff --git a/packages/tracing/src/integrations/node/mysql.ts b/packages/tracing/src/integrations/node/mysql.ts index 56a664e0544d..635ead77a1fe 100644 --- a/packages/tracing/src/integrations/node/mysql.ts +++ b/packages/tracing/src/integrations/node/mysql.ts @@ -1,4 +1,4 @@ -import { getScope, Hub } from '@sentry/hub'; +import { getHubScope, getScopeSpan, Hub } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { fill, loadModule, logger } from '@sentry/utils'; @@ -35,8 +35,8 @@ export class Mysql implements Integration { // function (options, values, callback) => void fill(pkg, 'createQuery', function(orig: () => void) { return function(this: unknown, options: unknown, values: unknown, callback: unknown) { - const scope = getScope(getCurrentHub()); - const parentSpan = scope?.getSpan(); + const scope = getHubScope(getCurrentHub()); + const parentSpan = scope && getScopeSpan(scope); const span = parentSpan?.startChild({ description: typeof options === 'string' ? options : (options as { sql: string }).sql, op: `db`, diff --git a/packages/tracing/src/integrations/node/postgres.ts b/packages/tracing/src/integrations/node/postgres.ts index 3bde0adb9c3d..c5a9a6a12f33 100644 --- a/packages/tracing/src/integrations/node/postgres.ts +++ b/packages/tracing/src/integrations/node/postgres.ts @@ -1,4 +1,4 @@ -import { getScope, Hub } from '@sentry/hub'; +import { getHubScope, getScopeSpan, Hub } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { fill, isThenable, loadModule, logger } from '@sentry/utils'; @@ -57,8 +57,8 @@ export class Postgres implements Integration { */ fill(Client.prototype, 'query', function(orig: () => void | Promise) { return function(this: unknown, config: unknown, values: unknown, callback: unknown) { - const scope = getScope(getCurrentHub()); - const parentSpan = scope?.getSpan(); + const scope = getHubScope(getCurrentHub()); + const parentSpan = scope && getScopeSpan(scope); const span = parentSpan?.startChild({ description: typeof config === 'string' ? config : (config as { text: string }).text, op: `db`, diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 80453c9df2f5..648a9956a9e9 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -1,4 +1,4 @@ -import { captureEvent, getClient, getCurrentHub, Hub } from '@sentry/hub'; +import { captureHubEvent, getCurrentHub, getHubClient, Hub } from '@sentry/hub'; import { Event, Measurements, @@ -103,7 +103,7 @@ export class Transaction extends SpanClass implements TransactionInterface { // At this point if `sampled !== true` we want to discard the transaction. logger.log('[Tracing] Discarding transaction because its trace was not chosen to be sampled.'); - const client = getClient(this._hub); + const client = getHubClient(this._hub); const transport = client && client.getTransport && client.getTransport(); if (transport && transport.recordLostEvent) { transport.recordLostEvent('sample_rate', 'transaction'); @@ -144,7 +144,7 @@ export class Transaction extends SpanClass implements TransactionInterface { logger.log(`[Tracing] Finishing ${this.op} transaction: ${this.name}.`); - return captureEvent(this._hub, transaction); + return captureHubEvent(this._hub, transaction); } /** diff --git a/packages/tracing/src/utils.ts b/packages/tracing/src/utils.ts index f44e2ea75090..abf85a74f310 100644 --- a/packages/tracing/src/utils.ts +++ b/packages/tracing/src/utils.ts @@ -1,4 +1,4 @@ -import { getClient, getCurrentHub, getScope, Hub } from '@sentry/hub'; +import { getCurrentHub, getHubClient, getHubScope, getScopeTransaction, Hub } from '@sentry/hub'; import { Options, TraceparentData, Transaction } from '@sentry/types'; export const TRACEPARENT_REGEXP = new RegExp( @@ -15,7 +15,7 @@ export const TRACEPARENT_REGEXP = new RegExp( * Tracing is enabled when at least one of `tracesSampleRate` and `tracesSampler` is defined in the SDK config. */ export function hasTracingEnabled(maybeOptions?: Options | undefined): boolean { - const client = getClient(getCurrentHub()); + const client = getHubClient(getCurrentHub()); const options = maybeOptions || (client && client.getOptions()); return !!options && ('tracesSampleRate' in options || 'tracesSampler' in options); } @@ -48,8 +48,8 @@ export function extractTraceparentData(traceparent: string): TraceparentData | u /** Grabs active transaction off scope, if any */ export function getActiveTransaction(maybeHub?: Hub): T | undefined { const hub = maybeHub || getCurrentHub(); - const scope = getScope(hub); - return scope && (scope.getTransaction() as T | undefined); + const scope = getHubScope(hub); + return scope && (getScopeTransaction(scope) as T | undefined); } /** From 8929a9f2c71ad5c6a60b3f9882a37da673395da1 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 13 Jan 2022 05:25:05 -0500 Subject: [PATCH 37/37] fix: compilers --- packages/angular/src/tracing.ts | 6 ++-- packages/hub/src/index.ts | 1 + packages/integrations/src/angular.ts | 19 ++++++---- packages/integrations/src/captureconsole.ts | 29 +++++++++------ packages/integrations/src/debug.ts | 6 ++-- packages/integrations/src/dedupe.ts | 6 ++-- packages/integrations/src/ember.ts | 26 +++++++++----- packages/integrations/src/extraerrordata.ts | 6 ++-- packages/integrations/src/offline.ts | 6 ++-- .../integrations/src/reportingobserver.ts | 12 +++---- packages/integrations/src/rewriteframes.ts | 6 ++-- packages/integrations/src/sessiontiming.ts | 6 ++-- packages/integrations/src/transaction.ts | 6 ++-- packages/integrations/src/vue.ts | 35 +++++++++++++------ packages/node/src/client.ts | 9 +++-- packages/node/src/integrations/http.ts | 3 +- packages/node/test/integrations/http.test.ts | 3 +- packages/react/src/redux.ts | 3 +- packages/vue/src/errorhandler.ts | 8 ++--- packages/vue/src/tracing.ts | 5 +-- 20 files changed, 122 insertions(+), 79 deletions(-) diff --git a/packages/angular/src/tracing.ts b/packages/angular/src/tracing.ts index 2ad3b529d290..8e0564cff2d9 100644 --- a/packages/angular/src/tracing.ts +++ b/packages/angular/src/tracing.ts @@ -1,7 +1,7 @@ import { AfterViewInit, Directive, Injectable, Input, NgModule, OnDestroy, OnInit } from '@angular/core'; import { Event, NavigationEnd, NavigationStart, Router } from '@angular/router'; import { getCurrentHub } from '@sentry/browser'; -import { getScope } from '@sentry/hub'; +import { getScopeTransaction, getHubScope } from '@sentry/hub'; import { Span, Transaction, TransactionContext } from '@sentry/types'; import { getGlobalObject, logger, stripUrlQueryAndFragment, timestampWithMs } from '@sentry/utils'; import { Observable, Subscription } from 'rxjs'; @@ -45,9 +45,9 @@ export function getActiveTransaction(): Transaction | undefined { const currentHub = getCurrentHub(); if (currentHub) { - const scope = getScope(currentHub); + const scope = getHubScope(currentHub); if (scope) { - return scope.getTransaction(); + return getScopeTransaction(scope); } } diff --git a/packages/hub/src/index.ts b/packages/hub/src/index.ts index c87f807bd6f9..095c275f53a9 100644 --- a/packages/hub/src/index.ts +++ b/packages/hub/src/index.ts @@ -18,6 +18,7 @@ export { getScopeUser, setScopeContext, getScopeTransaction, + addScopeBreadcrumb, Scope, } from './scope'; export { Session, updateSession } from './session'; diff --git a/packages/integrations/src/angular.ts b/packages/integrations/src/angular.ts index 98cd05de5531..66664e6c83de 100644 --- a/packages/integrations/src/angular.ts +++ b/packages/integrations/src/angular.ts @@ -1,4 +1,11 @@ -import { captureException, getIntegration, Hub, withScope } from '@sentry/hub'; +import { + addScopeEventProcessor, + captureHubException, + getHubIntegration, + Hub, + setScopeExtra, + withHubScope, +} from '@sentry/hub'; import { Event, EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; @@ -91,13 +98,13 @@ export class Angular implements Integration { return (exception: Error, cause?: string): void => { const hub = this._getCurrentHub && this._getCurrentHub(); - if (hub && getIntegration(hub, Angular)) { - withScope(hub, scope => { + if (hub && getHubIntegration(hub, Angular)) { + withHubScope(hub, scope => { if (cause) { - scope.setExtra('cause', cause); + setScopeExtra(scope, 'cause', cause); } - scope.addEventProcessor((event: Event) => { + addScopeEventProcessor(scope, (event: Event) => { const ex = event.exception && event.exception.values && event.exception.values[0]; if (ex) { @@ -119,7 +126,7 @@ export class Angular implements Integration { return event; }); - captureException(hub, exception); + captureHubException(hub, exception); }); } $delegate(exception, cause); diff --git a/packages/integrations/src/captureconsole.ts b/packages/integrations/src/captureconsole.ts index 6633ea9e6a86..3a95c8c0d979 100644 --- a/packages/integrations/src/captureconsole.ts +++ b/packages/integrations/src/captureconsole.ts @@ -1,4 +1,13 @@ -import { captureException, captureMessage, getIntegration, Hub, withScope } from '@sentry/hub'; +import { + addScopeEventProcessor, + captureHubException, + captureHubMessage, + getHubIntegration, + Hub, + setScopeExtra, + setScopeLevel, + withHubScope, +} from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { fill, getGlobalObject, safeJoin, severityFromString } from '@sentry/utils'; @@ -47,11 +56,11 @@ export class CaptureConsole implements Integration { fill(global.console, level, (originalConsoleLevel: () => any) => (...args: any[]): void => { const hub = getCurrentHub(); - if (getIntegration(hub, CaptureConsole)) { - withScope(hub, scope => { - scope.setLevel(severityFromString(level)); - scope.setExtra('arguments', args); - scope.addEventProcessor(event => { + if (getHubIntegration(hub, CaptureConsole)) { + withHubScope(hub, scope => { + setScopeLevel(scope, severityFromString(level)); + setScopeExtra(scope, 'arguments', args); + addScopeEventProcessor(scope, event => { event.logger = 'console'; return event; }); @@ -60,13 +69,13 @@ export class CaptureConsole implements Integration { if (level === 'assert') { if (args[0] === false) { message = `Assertion failed: ${safeJoin(args.slice(1), ' ') || 'console.assert'}`; - scope.setExtra('arguments', args.slice(1)); - captureMessage(hub, message); + setScopeExtra(scope, 'arguments', args.slice(1)); + captureHubMessage(hub, message); } } else if (level === 'error' && args[0] instanceof Error) { - captureException(hub, args[0]); + captureHubException(hub, args[0]); } else { - captureMessage(hub, message); + captureHubMessage(hub, message); } }); } diff --git a/packages/integrations/src/debug.ts b/packages/integrations/src/debug.ts index 39044b5ad794..f27a6b06bc66 100644 --- a/packages/integrations/src/debug.ts +++ b/packages/integrations/src/debug.ts @@ -1,6 +1,6 @@ -import { Event, EventHint, EventProcessor, Hub, Integration } from '@sentry/types'; +import { getHubIntegration, Hub } from '@sentry/hub'; +import { Event, EventHint, EventProcessor, Integration } from '@sentry/types'; import { consoleSandbox } from '@sentry/utils'; -import { getIntegration } from '@sentry/hub'; /** JSDoc */ interface DebugOptions { @@ -39,7 +39,7 @@ export class Debug implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getIntegration(getCurrentHub(), Debug); + const self = getHubIntegration(getCurrentHub(), Debug); if (self) { if (self._options.debugger) { // eslint-disable-next-line no-debugger diff --git a/packages/integrations/src/dedupe.ts b/packages/integrations/src/dedupe.ts index efdaa8258caf..2a143d483de0 100644 --- a/packages/integrations/src/dedupe.ts +++ b/packages/integrations/src/dedupe.ts @@ -1,6 +1,6 @@ -import { Event, EventProcessor, Exception, Hub, Integration, StackFrame } from '@sentry/types'; +import { getHubIntegration, Hub } from '@sentry/hub'; +import { Event, EventProcessor, Exception, Integration, StackFrame } from '@sentry/types'; import { logger } from '@sentry/utils'; -import { getIntegration } from '@sentry/hub'; /** Deduplication filter */ export class Dedupe implements Integration { @@ -24,7 +24,7 @@ export class Dedupe implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((currentEvent: Event) => { - const self = getIntegration(getCurrentHub(), Dedupe); + const self = getHubIntegration(getCurrentHub(), Dedupe); if (self) { // Juuust in case something goes wrong try { diff --git a/packages/integrations/src/ember.ts b/packages/integrations/src/ember.ts index 08e61946298f..34daaf493601 100644 --- a/packages/integrations/src/ember.ts +++ b/packages/integrations/src/ember.ts @@ -1,4 +1,11 @@ -import { captureException, captureMessage, getIntegration, Hub, withScope } from '@sentry/hub'; +import { + captureHubException, + captureHubMessage, + getHubIntegration, + Hub, + setScopeExtra, + withHubScope, +} from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, isInstanceOf, logger } from '@sentry/utils'; @@ -42,8 +49,8 @@ export class Ember implements Integration { const oldOnError = this._Ember.onerror; this._Ember.onerror = (error: Error): void => { - if (getIntegration(getCurrentHub(), Ember)) { - captureException(getCurrentHub(), error, { originalException: error }); + if (getHubIntegration(getCurrentHub(), Ember)) { + captureHubException(getCurrentHub(), error, { originalException: error }); } if (typeof oldOnError === 'function') { @@ -55,18 +62,19 @@ export class Ember implements Integration { // eslint-disable-next-line @typescript-eslint/no-explicit-any this._Ember.RSVP.on('error', (reason: unknown): void => { - if (getIntegration(getCurrentHub(), Ember)) { - withScope(getCurrentHub(), scope => { + if (getHubIntegration(getCurrentHub(), Ember)) { + withHubScope(getCurrentHub(), scope => { if (isInstanceOf(reason, Error)) { - scope.setExtra('context', 'Unhandled Promise error detected'); - captureException(getCurrentHub(), reason, { originalException: reason as Error }); + setScopeExtra(scope, 'context', 'Unhandled Promise error detected'); + captureHubException(getCurrentHub(), reason, { originalException: reason as Error }); } else { - scope.setExtra('reason', reason); - captureMessage(getCurrentHub(), 'Unhandled Promise error detected'); + setScopeExtra(scope, 'reason', reason); + captureHubMessage(getCurrentHub(), 'Unhandled Promise error detected'); } }); } }); } + /* eslint-enable @typescript-eslint/no-unsafe-member-access */ } diff --git a/packages/integrations/src/extraerrordata.ts b/packages/integrations/src/extraerrordata.ts index 8f1d41b3503b..fec5f6e81e7b 100644 --- a/packages/integrations/src/extraerrordata.ts +++ b/packages/integrations/src/extraerrordata.ts @@ -1,6 +1,6 @@ -import { Event, EventHint, EventProcessor, ExtendedError, Hub, Integration } from '@sentry/types'; +import { getHubIntegration, Hub } from '@sentry/hub'; +import { Event, EventHint, EventProcessor, ExtendedError, Integration } from '@sentry/types'; import { isError, isPlainObject, logger, normalize } from '@sentry/utils'; -import { getIntegration } from '@sentry/hub'; /** JSDoc */ interface ExtraErrorDataOptions { @@ -29,7 +29,7 @@ export class ExtraErrorData implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor((event: Event, hint?: EventHint) => { - const self = getIntegration(getCurrentHub(), ExtraErrorData); + const self = getHubIntegration(getCurrentHub(), ExtraErrorData); if (!self) { return event; } diff --git a/packages/integrations/src/offline.ts b/packages/integrations/src/offline.ts index 9a83aa9f825d..9a6493f2a75d 100644 --- a/packages/integrations/src/offline.ts +++ b/packages/integrations/src/offline.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { captureHubEvent, getHubIntegration, Hub } from '@sentry/hub'; import { Event, EventProcessor, Integration } from '@sentry/types'; -import { captureEvent, getIntegration, Hub } from '@sentry/hub'; import { getGlobalObject, logger, normalize, uuid4 } from '@sentry/utils'; import localForage from 'localforage'; @@ -77,7 +77,7 @@ export class Offline implements Integration { } addGlobalEventProcessor((event: Event) => { - if (this.hub && getIntegration(this.hub, Offline)) { + if (this.hub && getHubIntegration(this.hub, Offline)) { // cache if we are positively offline if ('navigator' in this.global && 'onLine' in this.global.navigator && !this.global.navigator.onLine) { void this._cacheEvent(event) @@ -158,7 +158,7 @@ export class Offline implements Integration { private async _sendEvents(): Promise { return this.offlineEventStore.iterate((event: Event, cacheKey: string, _index: number): void => { if (this.hub) { - captureEvent(this.hub, event); + captureHubEvent(this.hub, event); void this._purgeEvent(cacheKey).catch((_error): void => { logger.warn('could not purge event from cache'); diff --git a/packages/integrations/src/reportingobserver.ts b/packages/integrations/src/reportingobserver.ts index 8c627103358f..5908ea9b18b4 100644 --- a/packages/integrations/src/reportingobserver.ts +++ b/packages/integrations/src/reportingobserver.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { captureMessage, getIntegration, Hub, withScope } from '@sentry/hub'; +import { captureHubMessage, getHubIntegration, Hub, setScopeExtra, withHubScope } from '@sentry/hub'; import { EventProcessor, Integration } from '@sentry/types'; import { getGlobalObject, supportsReportingObserver } from '@sentry/utils'; @@ -105,12 +105,12 @@ export class ReportingObserver implements Integration { */ public handler(reports: Report[]): void { const hub = this._getCurrentHub && this._getCurrentHub(); - if (!hub || !getIntegration(hub, ReportingObserver)) { + if (!hub || !getHubIntegration(hub, ReportingObserver)) { return; } for (const report of reports) { - withScope(hub, scope => { - scope.setExtra('url', report.url); + withHubScope(hub, scope => { + setScopeExtra(scope, 'url', report.url); const label = `ReportingObserver [${report.type}]`; let details = 'No details available'; @@ -126,7 +126,7 @@ export class ReportingObserver implements Integration { plainBody[prop] = report.body[prop]; } - scope.setExtra('body', plainBody); + setScopeExtra(scope, 'body', plainBody); if (report.type === ReportTypes.Crash) { const body = report.body as CrashReportBody; @@ -138,7 +138,7 @@ export class ReportingObserver implements Integration { } } - captureMessage(hub, `${label}: ${details}`); + captureHubMessage(hub, `${label}: ${details}`); }); } } diff --git a/packages/integrations/src/rewriteframes.ts b/packages/integrations/src/rewriteframes.ts index 1c93778f13fb..ef8cd0773143 100644 --- a/packages/integrations/src/rewriteframes.ts +++ b/packages/integrations/src/rewriteframes.ts @@ -1,6 +1,6 @@ -import { Event, EventProcessor, Hub, Integration, StackFrame, Stacktrace } from '@sentry/types'; +import { getHubIntegration, Hub } from '@sentry/hub'; +import { Event, EventProcessor, Integration, StackFrame, Stacktrace } from '@sentry/types'; import { basename, relative } from '@sentry/utils'; -import { getIntegration } from '@sentry/hub'; type StackFrameIteratee = (frame: StackFrame) => StackFrame; @@ -46,7 +46,7 @@ export class RewriteFrames implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(event => { - const self = getIntegration(getCurrentHub(), RewriteFrames); + const self = getHubIntegration(getCurrentHub(), RewriteFrames); if (self) { return self.process(event); } diff --git a/packages/integrations/src/sessiontiming.ts b/packages/integrations/src/sessiontiming.ts index 06151029c829..2dd8051825e0 100644 --- a/packages/integrations/src/sessiontiming.ts +++ b/packages/integrations/src/sessiontiming.ts @@ -1,5 +1,5 @@ -import { Event, EventProcessor, Hub, Integration } from '@sentry/types'; -import { getIntegration } from '@sentry/hub'; +import { getHubIntegration, Hub } from '@sentry/hub'; +import { Event, EventProcessor, Integration } from '@sentry/types'; /** This function adds duration since Sentry was initialized till the time event was sent */ export class SessionTiming implements Integration { @@ -21,7 +21,7 @@ export class SessionTiming implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(event => { - const self = getIntegration(getCurrentHub(), SessionTiming); + const self = getHubIntegration(getCurrentHub(), SessionTiming); if (self) { return self.process(event); } diff --git a/packages/integrations/src/transaction.ts b/packages/integrations/src/transaction.ts index fceae5c82bc0..b4dac0847432 100644 --- a/packages/integrations/src/transaction.ts +++ b/packages/integrations/src/transaction.ts @@ -1,5 +1,5 @@ -import { Event, EventProcessor, Hub, Integration, StackFrame } from '@sentry/types'; -import { getIntegration } from '@sentry/hub'; +import { getHubIntegration, Hub } from '@sentry/hub'; +import { Event, EventProcessor, Integration, StackFrame } from '@sentry/types'; /** Add node transaction to the event */ export class Transaction implements Integration { @@ -18,7 +18,7 @@ export class Transaction implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(event => { - const self = getIntegration(getCurrentHub(), Transaction); + const self = getHubIntegration(getCurrentHub(), Transaction); if (self) { return self.process(event); } diff --git a/packages/integrations/src/vue.ts b/packages/integrations/src/vue.ts index f08b499fe027..b2568bb3fae0 100644 --- a/packages/integrations/src/vue.ts +++ b/packages/integrations/src/vue.ts @@ -1,6 +1,14 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { captureException, getIntegration, getScope, Hub, withScope } from '@sentry/hub'; +import { + captureHubException, + getHubIntegration, + getHubScope, + getScopeTransaction, + Hub, + setScopeContext, + withHubScope, +} from '@sentry/hub'; import { EventProcessor, Integration, IntegrationClass, Scope, Span, Transaction } from '@sentry/types'; import { basename, getGlobalObject, logger, timestampWithMs } from '@sentry/utils'; @@ -30,12 +38,14 @@ interface VueInstance { util?: { warn(...input: any): void; }; + mixin(hooks: { [key: string]: () => void }): void; } /** Representation of Vue component internals */ interface ViewModel { [key: string]: any; + // eslint-disable-next-line @typescript-eslint/ban-types $root: object; $options: { @@ -46,6 +56,7 @@ interface ViewModel { __file?: string; $_sentryPerfHook?: boolean; }; + $once(hook: string, cb: () => void): void; } @@ -96,6 +107,7 @@ interface TracingOptions { /** Optional metadata attached to Sentry Event */ interface Metadata { [key: string]: any; + componentName?: string; propsData?: { [key: string]: any }; lifecycleHook?: string; @@ -264,7 +276,7 @@ export class Vue implements Integration { // We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods. /* eslint-disable @typescript-eslint/no-unsafe-member-access */ // eslint-disable-next-line deprecation/deprecation - const tracingIntegration = getIntegration(getCurrentHub(), TRACING_GETTER); + const tracingIntegration = getHubIntegration(getCurrentHub(), TRACING_GETTER); if (tracingIntegration) { this._tracingActivity = (tracingIntegration as any).constructor.pushActivity('Vue Application Render'); const transaction = (tracingIntegration as any).constructor.getTransaction(); @@ -358,7 +370,7 @@ export class Vue implements Integration { // We do this whole dance with `TRACING_GETTER` to prevent `@sentry/apm` from becoming a peerDependency. // We also need to ask for the `.constructor`, as `pushActivity` and `popActivity` are static, not instance methods. // eslint-disable-next-line deprecation/deprecation - const tracingIntegration = getIntegration(getCurrentHub(), TRACING_GETTER); + const tracingIntegration = getHubIntegration(getCurrentHub(), TRACING_GETTER); if (tracingIntegration) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access (tracingIntegration as any).constructor.popActivity(this._tracingActivity); @@ -379,7 +391,10 @@ export class Vue implements Integration { this._options.Vue.mixin({ beforeCreate(this: ViewModel): void { // eslint-disable-next-line deprecation/deprecation - if (getIntegration(getCurrentHub(), TRACING_GETTER) || getIntegration(getCurrentHub(), BROWSER_TRACING_GETTER)) { + if ( + getHubIntegration(getCurrentHub(), TRACING_GETTER) || + getHubIntegration(getCurrentHub(), BROWSER_TRACING_GETTER) + ) { // `this` points to currently rendered component applyTracingHooks(this, getCurrentHub); } else { @@ -413,12 +428,12 @@ export class Vue implements Integration { metadata.lifecycleHook = info; } - if (getIntegration(getCurrentHub(), Vue)) { + if (getHubIntegration(getCurrentHub(), Vue)) { // Capture exception in the next event loop, to make sure that all breadcrumbs are recorded in time. setTimeout(() => { - withScope(getCurrentHub(), scope => { - scope.setContext('vue', metadata); - captureException(getCurrentHub(), error); + withHubScope(getCurrentHub(), scope => { + setScopeContext(scope, 'vue', metadata); + captureHubException(getCurrentHub(), error); }); }); } @@ -447,9 +462,9 @@ export function getActiveTransaction(hub: Hub): T | undef // TODO: I am confused about why the HubType and not IHub is used here. // And why we need to check for `getScope`. So this may be wrong. if (hub) { - const scope = getScope(hub) as Scope; + const scope = getHubScope(hub) as Scope; if (scope) { - return scope.getTransaction() as T | undefined; + return getScopeTransaction(scope) as T | undefined; } } diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 01d3cf3c2b38..b9a561de3d92 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,11 +1,16 @@ import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; -import { closeSessionFlusher, getScopeRequestSession, incrementSessionStatusCount, SessionFlusher } from '@sentry/hub'; +import { + SessionFlusherTransporter, + closeSessionFlusher, + getScopeRequestSession, + incrementSessionStatusCount, + SessionFlusher, +} from '@sentry/hub'; import { Event, EventHint } from '@sentry/types'; import { logger } from '@sentry/utils'; import { NodeBackend } from './backend'; import { NodeOptions } from './types'; -import { SessionFlusherTransporter } from '@sentry/hub/src'; /** * The Sentry Node SDK Client. diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index e326316d4abd..0e0ef4823247 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -12,8 +12,7 @@ import { RequestMethod, RequestMethodArgs, } from './utils/http'; -import { addHubBreadcrumb, getHubScope, getHubIntegration } from '@sentry/hub'; -import { getScopeSpan } from '@sentry/hub/src/scope'; +import { getScopeSpan, addHubBreadcrumb, getHubScope, getHubIntegration } from '@sentry/hub'; const NODE_VERSION = parseSemver(process.versions.node); diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 0f2b3b811241..8067f7839974 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -11,8 +11,7 @@ import * as nock from 'nock'; import { Breadcrumb } from '../../src'; import { NodeClient } from '../../src/client'; import { Http as HttpIntegration } from '../../src/integrations/http'; -import { bindHubClient } from '@sentry/hub/src'; -import { startHubTransaction } from '@sentry/hub/src/hub'; +import { startHubTransaction, bindHubClient } from '@sentry/hub'; const NODE_VERSION = parseSemver(process.versions.node); diff --git a/packages/react/src/redux.ts b/packages/react/src/redux.ts index 0c745ffb87dd..472039ac2ba8 100644 --- a/packages/react/src/redux.ts +++ b/packages/react/src/redux.ts @@ -1,8 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { configureScope } from '@sentry/minimal'; import { Scope } from '@sentry/types'; -import { setScopeContext } from '@sentry/hub'; -import { addScopeBreadcrumb } from '@sentry/hub/src/scope'; +import { addScopeBreadcrumb, setScopeContext } from '@sentry/hub'; interface Action { type: T; diff --git a/packages/vue/src/errorhandler.ts b/packages/vue/src/errorhandler.ts index 20bc43ab8855..08f9250b3094 100644 --- a/packages/vue/src/errorhandler.ts +++ b/packages/vue/src/errorhandler.ts @@ -1,5 +1,5 @@ import { getCurrentHub } from '@sentry/browser'; -import { captureException, withScope } from '@sentry/hub'; +import { captureHubException, setScopeContext, withHubScope } from '@sentry/hub'; import { formatComponentName, generateComponentTrace } from './components'; import { Options, ViewModel, Vue } from './types'; @@ -26,9 +26,9 @@ export const attachErrorHandler = (app: Vue, options: Options): void => { // Capture exception in the next event loop, to make sure that all breadcrumbs are recorded in time. setTimeout(() => { - withScope(getCurrentHub(), scope => { - scope.setContext('vue', metadata); - captureException(getCurrentHub(), error); + withHubScope(getCurrentHub(), scope => { + setScopeContext(scope, 'vue', metadata); + captureHubException(getCurrentHub(), error); }); }); diff --git a/packages/vue/src/tracing.ts b/packages/vue/src/tracing.ts index 53154baa4f4c..685c8caacccf 100644 --- a/packages/vue/src/tracing.ts +++ b/packages/vue/src/tracing.ts @@ -1,5 +1,5 @@ import { getCurrentHub } from '@sentry/browser'; -import { getScope } from '@sentry/hub'; +import { getHubScope, getScopeTransaction } from '@sentry/hub'; import { Span, Transaction } from '@sentry/types'; import { logger, timestampInSeconds } from '@sentry/utils'; @@ -31,7 +31,8 @@ const HOOKS: { [key in Operation]: Hook[] } = { /** Grabs active transaction off scope, if any */ function getActiveTransaction(): Transaction | undefined { - return getScope(getCurrentHub())?.getTransaction(); + const scope = getHubScope(getCurrentHub()); + return scope ? getScopeTransaction(scope) : undefined; } /** Finish top-level span and activity with a debounce configured using `timeout` option */