diff --git a/MIGRATION.md b/MIGRATION.md index 77f1c21e7aca..59f7d8bbae9c 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -115,12 +115,6 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p [#4919](https://github.com/getsentry/sentry-javascript/pull/4919)). `Backend` was an unnecessary abstraction which is not present in other Sentry SDKs. For the sake of reducing complexity, increasing consistency with other Sentry SDKs and decreasing bundle-size, `Backend` was removed. - - -- Inject transport into client instead of initializing it in the client in `setupTransport` (see - [#4921](https://github.com/getsentry/sentry-javascript/pull/4921/)). If you are creating your own `Client` or - calling `initAndBind`, you will have to supply your desired transport. Either provide a custom one or call - `setupBrowserTransport` or `setupNodeTransport` for default transports, depending on your requirements. - Remove support for Opera browser pre v15 ## Sentry Angular SDK Changes diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 0679413da1c4..d57b35c02d31 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,5 +1,5 @@ -import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; -import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; +import { ClientOptions, Event, EventHint, Options, Severity, SeverityLevel } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; @@ -47,7 +47,7 @@ export class BrowserClient extends BaseClient { * * @param options Configuration options for this SDK. */ - public constructor(options: BrowserClientOptions, transport: Transport, newTransport?: NewTransport) { + public constructor(options: BrowserClientOptions) { options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || { name: 'sentry.javascript.browser', @@ -59,7 +59,7 @@ export class BrowserClient extends BaseClient { ], version: SDK_VERSION, }; - super(options, transport, newTransport); + super(options); } /** diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index 1af8a7d8aaf5..0777d8124579 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -27,6 +27,7 @@ export { captureEvent, captureMessage, configureScope, + createTransport, getHubFromCarrier, getCurrentHub, Hub, diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index f8f233a1c230..9b138864515a 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -14,8 +14,7 @@ import { IS_DEBUG_BUILD } from './flags'; import { ReportDialogOptions, wrap as internalWrap } from './helpers'; import { Breadcrumbs, Dedupe, GlobalHandlers, LinkedErrors, TryCatch, UserAgent } from './integrations'; import { defaultStackParsers } from './stack-parsers'; -import { FetchTransport, XHRTransport } from './transports'; -import { setupBrowserTransport } from './transports/setup'; +import { makeNewFetchTransport, makeNewXHRTransport } from './transports'; export const defaultIntegrations = [ new CoreIntegrations.InboundFilters(), @@ -102,17 +101,15 @@ export function init(options: BrowserOptions = {}): void { if (options.sendClientReports === undefined) { options.sendClientReports = true; } - const { transport, newTransport } = setupBrowserTransport(options); const clientOptions: BrowserClientOptions = { ...options, stackParser: stackParserFromOptions(options.stackParser || defaultStackParsers), integrations: getIntegrationsToSetup(options), - // TODO(v7): get rid of transport being passed down below - transport: options.transport || (supportsFetch() ? FetchTransport : XHRTransport), + transport: options.transport || (supportsFetch() ? makeNewFetchTransport : makeNewXHRTransport), }; - initAndBind(BrowserClient, clientOptions, transport, newTransport); + initAndBind(BrowserClient, clientOptions); if (options.autoSessionTracking) { startSessionTracking(); diff --git a/packages/browser/src/transports/index.ts b/packages/browser/src/transports/index.ts index 31871a76d01c..287e14e0ac50 100644 --- a/packages/browser/src/transports/index.ts +++ b/packages/browser/src/transports/index.ts @@ -4,5 +4,3 @@ export { XHRTransport } from './xhr'; export { makeNewFetchTransport } from './new-fetch'; export { makeNewXHRTransport } from './new-xhr'; - -export { setupBrowserTransport } from './setup'; diff --git a/packages/browser/src/transports/new-fetch.ts b/packages/browser/src/transports/new-fetch.ts index 47a85b517e77..9a9d7b14ae19 100644 --- a/packages/browser/src/transports/new-fetch.ts +++ b/packages/browser/src/transports/new-fetch.ts @@ -1,10 +1,5 @@ -import { - BaseTransportOptions, - createTransport, - NewTransport, - TransportMakeRequestResponse, - TransportRequest, -} from '@sentry/core'; +import { createTransport } from '@sentry/core'; +import { BaseTransportOptions, NewTransport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; import { FetchImpl, getNativeFetchImplementation } from './utils'; diff --git a/packages/browser/src/transports/new-xhr.ts b/packages/browser/src/transports/new-xhr.ts index cd19b1de0cd4..d45a0019914c 100644 --- a/packages/browser/src/transports/new-xhr.ts +++ b/packages/browser/src/transports/new-xhr.ts @@ -1,10 +1,5 @@ -import { - BaseTransportOptions, - createTransport, - NewTransport, - TransportMakeRequestResponse, - TransportRequest, -} from '@sentry/core'; +import { createTransport } from '@sentry/core'; +import { BaseTransportOptions, NewTransport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; import { SyncPromise } from '@sentry/utils'; /** diff --git a/packages/browser/src/transports/setup.ts b/packages/browser/src/transports/setup.ts deleted file mode 100644 index f72365e7dc94..000000000000 --- a/packages/browser/src/transports/setup.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { - BaseTransportOptions, - getEnvelopeEndpointWithUrlEncodedAuth, - initAPIDetails, - NewTransport, - NoopTransport, -} from '@sentry/core'; -import { Transport, TransportOptions } from '@sentry/types'; -import { supportsFetch } from '@sentry/utils'; - -import { BrowserOptions } from '../client'; -import { FetchTransport } from './fetch'; -import { makeNewFetchTransport } from './new-fetch'; -import { makeNewXHRTransport } from './new-xhr'; -import { XHRTransport } from './xhr'; - -export interface BrowserTransportOptions extends BaseTransportOptions { - // options to pass into fetch request - fetchParams: Record; - headers?: Record; - sendClientReports?: boolean; -} - -/** - * Sets up Browser transports based on the passed `options`. If available, the returned - * transport will use the fetch API. In case fetch is not supported, an XMLHttpRequest - * based transport is created. - * - * @returns an object currently still containing both, the old `Transport` and - * `NewTransport` which will eventually replace `Transport`. Once this is replaced, - * this function will return a ready to use `NewTransport`. - */ -// TODO(v7): Adjust return value when NewTransport is the default -export function setupBrowserTransport(options: BrowserOptions): { - transport: Transport; - newTransport?: NewTransport; -} { - if (!options.dsn) { - // We return the noop transport here in case there is no Dsn. - return { transport: new NoopTransport() }; - } - - const transportOptions: TransportOptions = { - ...options.transportOptions, - dsn: options.dsn, - tunnel: options.tunnel, - sendClientReports: options.sendClientReports, - _metadata: options._metadata, - }; - - const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); - const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); - - if (options.transport) { - return { transport: new options.transport(transportOptions) }; - } - - if (supportsFetch()) { - const requestOptions: RequestInit = { ...transportOptions.fetchParameters }; - const newTransport = makeNewFetchTransport({ requestOptions, url }); - const fetchTransport = new FetchTransport(transportOptions); - return { transport: fetchTransport, newTransport }; - } - - const newTransport = makeNewXHRTransport({ - url, - headers: transportOptions.headers, - }); - const transport = new XHRTransport(transportOptions); - return { transport, newTransport }; -} diff --git a/packages/browser/test/unit/helper/browser-client-options.ts b/packages/browser/test/unit/helper/browser-client-options.ts index aa763a5de06b..19d4a4cb8c05 100644 --- a/packages/browser/test/unit/helper/browser-client-options.ts +++ b/packages/browser/test/unit/helper/browser-client-options.ts @@ -1,11 +1,12 @@ -import { NoopTransport } from '@sentry/core'; +import { createTransport } from '@sentry/core'; +import { resolvedSyncPromise } from '@sentry/utils'; import { BrowserClientOptions } from '../../../src/client'; export function getDefaultBrowserClientOptions(options: Partial = {}): BrowserClientOptions { return { integrations: [], - transport: NoopTransport, + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), stackParser: () => [], ...options, }; diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index 434dea98977a..c8d1df23bac9 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -17,7 +17,7 @@ import { wrap, } from '../../src'; import { getDefaultBrowserClientOptions } from './helper/browser-client-options'; -import { SimpleTransport } from './mocks/simpletransport'; +import { makeSimpleTransport } from './mocks/simpletransport'; const dsn = 'https://53039209a22b4ec1bcc296a3c9fdecd6@sentry.io/4291'; @@ -31,7 +31,7 @@ describe('SentryBrowser', () => { init({ beforeSend, dsn, - transport: SimpleTransport, + transport: makeSimpleTransport, }); }); @@ -77,7 +77,7 @@ describe('SentryBrowser', () => { describe('user', () => { const EX_USER = { email: 'test@example.com' }; const options = getDefaultBrowserClientOptions({ dsn }); - const client = new BrowserClient(options, new SimpleTransport({ dsn })); + const client = new BrowserClient(options); const reportDialogSpy = jest.spyOn(client, 'showReportDialog'); beforeEach(() => { @@ -150,7 +150,7 @@ describe('SentryBrowser', () => { }, dsn, }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); captureMessage('test'); }); @@ -164,7 +164,7 @@ describe('SentryBrowser', () => { }, dsn, }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); captureEvent({ message: 'event' }); }); @@ -175,7 +175,7 @@ describe('SentryBrowser', () => { dsn, integrations: [], }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); captureMessage('event222'); captureMessage('event222'); @@ -192,7 +192,7 @@ describe('SentryBrowser', () => { dsn, integrations: [new Integrations.InboundFilters({ ignoreErrors: ['capture'] })], }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); captureMessage('capture'); @@ -244,7 +244,7 @@ describe('SentryBrowser initialization', () => { it('should set SDK data when Sentry.init() is called', () => { init({ dsn }); - const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk; expect(sdkData?.name).toBe('sentry.javascript.browser'); expect(sdkData?.packages[0].name).toBe('npm:@sentry/browser'); @@ -254,9 +254,9 @@ describe('SentryBrowser initialization', () => { it('should set SDK data when instantiating a client directly', () => { const options = getDefaultBrowserClientOptions({ dsn }); - const client = new BrowserClient(options, new SimpleTransport({ dsn })); + const client = new BrowserClient(options); - const sdkData = (client.getTransport() as any)._api.metadata?.sdk; + const sdkData = client.getOptions()._metadata?.sdk as any; expect(sdkData.name).toBe('sentry.javascript.browser'); expect(sdkData.packages[0].name).toBe('npm:@sentry/browser'); @@ -284,7 +284,7 @@ describe('SentryBrowser initialization', () => { }, }); - const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata?.sdk; expect(sdkData.name).toBe('sentry.javascript.angular'); expect(sdkData.packages[0].name).toBe('npm:@sentry/angular'); @@ -305,7 +305,7 @@ describe('wrap()', () => { }, dsn, }); - getCurrentHub().bindClient(new BrowserClient(options, new SimpleTransport({ dsn }))); + getCurrentHub().bindClient(new BrowserClient(options)); try { wrap(() => { diff --git a/packages/browser/test/unit/integrations/linkederrors.test.ts b/packages/browser/test/unit/integrations/linkederrors.test.ts index 4b862705bccc..445a4fde8699 100644 --- a/packages/browser/test/unit/integrations/linkederrors.test.ts +++ b/packages/browser/test/unit/integrations/linkederrors.test.ts @@ -4,7 +4,6 @@ import { createStackParser } from '@sentry/utils'; import { BrowserClient } from '../../../src/client'; import * as LinkedErrorsModule from '../../../src/integrations/linkederrors'; import { defaultStackParsers } from '../../../src/stack-parsers'; -import { setupBrowserTransport } from '../../../src/transports'; import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; const parser = createStackParser(...defaultStackParsers); @@ -47,7 +46,7 @@ describe('LinkedErrors', () => { const originalException = one; const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options, setupBrowserTransport(options).transport); + const client = new BrowserClient(options); return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 5, event, { originalException, @@ -78,7 +77,7 @@ describe('LinkedErrors', () => { const originalException = one; const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options, setupBrowserTransport(options).transport); + const client = new BrowserClient(options); return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'reason', 5, event, { originalException, @@ -105,7 +104,7 @@ describe('LinkedErrors', () => { two.cause = three; const options = getDefaultBrowserClientOptions({ stackParser: parser }); - const client = new BrowserClient(options, setupBrowserTransport(options).transport); + const client = new BrowserClient(options); const originalException = one; return client.eventFromException(originalException).then(event => { const result = LinkedErrorsModule._handler(parser, 'cause', 2, event, { diff --git a/packages/browser/test/unit/mocks/simpletransport.ts b/packages/browser/test/unit/mocks/simpletransport.ts index 398feb1a3e6a..59a21aed4ac3 100644 --- a/packages/browser/test/unit/mocks/simpletransport.ts +++ b/packages/browser/test/unit/mocks/simpletransport.ts @@ -1,15 +1,6 @@ -import { eventStatusFromHttpCode, resolvedSyncPromise } from '@sentry/utils'; +import { createTransport } from '@sentry/core'; +import { resolvedSyncPromise } from '@sentry/utils'; -import { Event, Response } from '../../../src'; -import { BaseTransport } from '../../../src/transports'; - -// @ts-ignore It's okay that we're not implementing the `_sendRequest()` method because we don't use it in our tests -export class SimpleTransport extends BaseTransport { - public sendEvent(_: Event): PromiseLike { - return this._buffer.add(() => - resolvedSyncPromise({ - status: eventStatusFromHttpCode(200), - }), - ); - } +export function makeSimpleTransport() { + return createTransport({}, () => resolvedSyncPromise({ statusCode: 200 })); } diff --git a/packages/browser/test/unit/sdk.test.ts b/packages/browser/test/unit/sdk.test.ts index 8814329f16e9..85fa04b21612 100644 --- a/packages/browser/test/unit/sdk.test.ts +++ b/packages/browser/test/unit/sdk.test.ts @@ -1,7 +1,9 @@ /* eslint-disable @typescript-eslint/unbound-method */ -import { NoopTransport, Scope } from '@sentry/core'; +import { Scope } from '@sentry/core'; +import { createTransport } from '@sentry/core'; import { MockIntegration } from '@sentry/core/test/lib/sdk.test'; import { Client, Integration } from '@sentry/types'; +import { resolvedSyncPromise } from '@sentry/utils'; import { BrowserOptions } from '../../src'; import { init } from '../../src/sdk'; @@ -13,7 +15,7 @@ const PUBLIC_DSN = 'https://username@domain/123'; function getDefaultBrowserOptions(options: Partial = {}): BrowserOptions { return { integrations: [], - transport: NoopTransport, + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), stackParser: () => [], ...options, }; diff --git a/packages/browser/test/unit/transports/base.test.ts b/packages/browser/test/unit/transports/base.test.ts index 9df498352c1e..75894049c1ca 100644 --- a/packages/browser/test/unit/transports/base.test.ts +++ b/packages/browser/test/unit/transports/base.test.ts @@ -7,7 +7,8 @@ const envelopeEndpoint = 'https://sentry.io/api/42/envelope/?sentry_key=123&sent // assert on what the class provides and what it leaves to the concrete class to implement class SimpleTransport extends BaseTransport {} -describe('BaseTransport', () => { +// TODO(v7): Re-enable these tests with client reports +describe.skip('BaseTransport', () => { describe('Client Reports', () => { const sendBeaconSpy = jest.fn(); let visibilityState: string; diff --git a/packages/browser/test/unit/transports/setup.test.ts b/packages/browser/test/unit/transports/setup.test.ts deleted file mode 100644 index 41b361d684d5..000000000000 --- a/packages/browser/test/unit/transports/setup.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { NoopTransport } from '@sentry/core'; - -import { - FetchTransport, - makeNewFetchTransport, - makeNewXHRTransport, - setupBrowserTransport, - XHRTransport, -} from '../../../src/transports'; -import { getDefaultBrowserClientOptions } from '../helper/browser-client-options'; -import { SimpleTransport } from '../mocks/simpletransport'; - -const DSN = 'https://username@domain/123'; - -let fetchSupported = true; - -jest.mock('@sentry/utils', () => { - const original = jest.requireActual('@sentry/utils'); - return { - ...original, - supportsFetch(): boolean { - return fetchSupported; - }, - getGlobalObject(): any { - return { - fetch: () => {}, - }; - }, - }; -}); - -jest.mock('../../../src/transports/new-fetch', () => { - const original = jest.requireActual('../../../src/transports/new-fetch'); - return { - ...original, - makeNewFetchTransport: jest.fn(() => ({ - send: () => Promise.resolve({ status: 'success' }), - flush: () => Promise.resolve(true), - })), - }; -}); - -jest.mock('../../../src/transports/new-xhr', () => { - const original = jest.requireActual('../../../src/transports/new-xhr'); - return { - ...original, - makeNewXHRTransport: jest.fn(() => ({ - send: () => Promise.resolve({ status: 'success' }), - flush: () => Promise.resolve(true), - })), - }; -}); - -describe('setupBrowserTransport', () => { - afterEach(() => jest.clearAllMocks()); - - afterAll(() => jest.resetAllMocks()); - - it('returns NoopTransport if no dsn is passed', () => { - const { transport, newTransport } = setupBrowserTransport({}); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(NoopTransport); - expect(newTransport).toBeUndefined(); - }); - - it('returns the instantiated transport passed via the options', () => { - const options = getDefaultBrowserClientOptions({ dsn: DSN, transport: SimpleTransport }); - const { transport, newTransport } = setupBrowserTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(SimpleTransport); - expect(newTransport).toBeUndefined(); - }); - - it('returns fetchTransports if fetch is supported', () => { - const options = getDefaultBrowserClientOptions({ dsn: DSN }); - delete options.transport; - const { transport, newTransport } = setupBrowserTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(FetchTransport); - expect(newTransport).toBeDefined(); - expect(makeNewFetchTransport).toHaveBeenCalledTimes(1); - expect(makeNewXHRTransport).toHaveBeenCalledTimes(0); - }); - - it('returns xhrTransports if fetch is not supported', () => { - fetchSupported = false; - - const options = getDefaultBrowserClientOptions({ dsn: DSN }); - delete options.transport; - const { transport, newTransport } = setupBrowserTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(XHRTransport); - expect(newTransport).toBeDefined(); - expect(makeNewFetchTransport).toHaveBeenCalledTimes(0); - expect(makeNewXHRTransport).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 314d3d6216a0..e2502289742a 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -4,10 +4,13 @@ import { Client, ClientOptions, DsnComponents, + Envelope, Event, EventHint, Integration, IntegrationClass, + NewTransport, + SessionAggregates, Severity, SeverityLevel, Transport, @@ -29,11 +32,10 @@ import { uuid4, } from '@sentry/utils'; -import { APIDetails, initAPIDetails } from './api'; +import { getEnvelopeEndpointWithUrlEncodedAuth } from './api'; import { IS_DEBUG_BUILD } from './flags'; import { IntegrationIndex, setupIntegrations } from './integration'; import { createEventEnvelope, createSessionEnvelope } from './request'; -import { NewTransport } from './transports/base'; const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured."; @@ -75,6 +77,8 @@ export abstract class BaseClient implements Client { /** The client Dsn, if specified in options. Without this Dsn, the SDK will be disabled. */ protected readonly _dsn?: DsnComponents; + protected readonly _transport?: NewTransport; + /** Array of set up integrations. */ protected _integrations: IntegrationIndex = {}; @@ -84,12 +88,6 @@ export abstract class BaseClient implements Client { /** Number of calls being processed */ protected _numProcessing: number = 0; - /** Cached transport used internally. */ - protected _transport: Transport; - - /** New v7 Transport that is initialized alongside the old one */ - protected _newTransport?: NewTransport; - /** * Initializes this client instance. * @@ -97,25 +95,15 @@ export abstract class BaseClient implements Client { * @param transport The (old) Transport instance for the client to use (TODO(v7): remove) * @param newTransport The NewTransport instance for the client to use */ - protected constructor(options: O, transport: Transport, newTransport?: NewTransport) { + protected constructor(options: O) { this._options = options; - if (options.dsn) { this._dsn = makeDsn(options.dsn); + const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, options.tunnel); + this._transport = options.transport({ ...options.transportOptions, url }); } else { IS_DEBUG_BUILD && logger.warn('No DSN provided, client will not do anything.'); } - - // TODO(v7): remove old transport - this._transport = transport; - this._newTransport = newTransport; - - // TODO(v7): refactor this to keep metadata/api outside of transport. This hack is used to - // satisfy tests until we move to NewTransport where we have to revisit this. - (this._transport as unknown as { _api: Partial })._api = { - ...((this._transport as unknown as { _api: Partial })._api || {}), - metadata: options._metadata || {}, - }; } /** @@ -202,7 +190,7 @@ export abstract class BaseClient implements Client { if (!(typeof session.release === 'string')) { IS_DEBUG_BUILD && logger.warn('Discarded session because of missing or non-string release'); } else { - this._sendSession(session); + this.sendSession(session); // After sending, we set init false to indicate it's not the first occurrence session.update({ init: false }); } @@ -225,7 +213,7 @@ export abstract class BaseClient implements Client { /** * @inheritDoc */ - public getTransport(): Transport { + public getTransport(): NewTransport | undefined { return this._transport; } @@ -233,11 +221,14 @@ export abstract class BaseClient implements Client { * @inheritDoc */ public flush(timeout?: number): PromiseLike { - return this._isClientDoneProcessing(timeout).then(clientFinished => { - return this.getTransport() - .close(timeout) - .then(transportFlushed => clientFinished && transportFlushed); - }); + const transport = this._transport; + if (transport) { + return this._isClientDoneProcessing(timeout).then(clientFinished => { + return transport.flush(timeout).then(transportFlushed => clientFinished && transportFlushed); + }); + } else { + return resolvedSyncPromise(true); + } } /** @@ -276,50 +267,32 @@ export abstract class BaseClient implements Client { * @inheritDoc */ public sendEvent(event: Event): void { - // TODO(v7): Remove the if-else - if ( - this._newTransport && - this._options.dsn && - this._options._experiments && - this._options._experiments.newTransport - ) { - const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel); - const env = createEventEnvelope(event, api); - void this._newTransport.send(env).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); - }); - } else { - void this._transport.sendEvent(event).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); - }); + if (this._dsn) { + const env = createEventEnvelope(event, this._dsn, this._options._metadata, this._options.tunnel); + this.sendEnvelope(env); } } /** * @inheritDoc */ - public sendSession(session: Session): void { - if (!this._transport.sendSession) { - IS_DEBUG_BUILD && logger.warn("Dropping session because custom transport doesn't implement sendSession"); - return; + public sendSession(session: Session | SessionAggregates): void { + if (this._dsn) { + const [env] = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel); + this.sendEnvelope(env); } + } - // TODO(v7): Remove the if-else - if ( - this._newTransport && - this._options.dsn && - this._options._experiments && - this._options._experiments.newTransport - ) { - const api = initAPIDetails(this._options.dsn, this._options._metadata, this._options.tunnel); - const [env] = createSessionEnvelope(session, api); - void this._newTransport.send(env).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); + /** + * @inheritdoc + */ + public sendEnvelope(env: Envelope): void { + if (this._transport && this._dsn) { + this._transport.send(env).then(null, reason => { + IS_DEBUG_BUILD && logger.error('Error while sending event:', reason); }); } else { - void this._transport.sendSession(session).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); - }); + IS_DEBUG_BUILD && logger.error('Transport disabled'); } } @@ -356,12 +329,6 @@ export abstract class BaseClient implements Client { } } - /** Deliver captured session to Sentry */ - // TODO(v7): should this be deleted? - protected _sendSession(session: Session): void { - this.sendSession(session); - } - /** * Determine if the client is finished processing. Returns a promise because it will wait `timeout` ms before saying * "no" (resolving to `false`) in order to give the client a chance to potentially finish first. @@ -599,15 +566,16 @@ export abstract class BaseClient implements Client { protected _processEvent(event: Event, hint?: EventHint, scope?: Scope): PromiseLike { // eslint-disable-next-line @typescript-eslint/unbound-method const { beforeSend, sampleRate } = this.getOptions(); - const transport = this.getTransport(); type RecordLostEvent = NonNullable; type RecordLostEventParams = Parameters; - function recordLostEvent(outcome: RecordLostEventParams[0], category: RecordLostEventParams[1]): void { - if (transport.recordLostEvent) { - transport.recordLostEvent(outcome, category); - } + function recordLostEvent(_outcome: RecordLostEventParams[0], _category: RecordLostEventParams[1]): void { + // no-op as new transports don't have client outcomes + // TODO(v7): Re-add this functionality + // if (transport.recordLostEvent) { + // transport.recordLostEvent(outcome, category); + // } } if (!this._isEnabled()) { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index da68917c936e..f5932bdbeaa4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,12 +1,5 @@ export type { APIDetails } from './api'; export type { ClientClass } from './sdk'; -export type { - BaseTransportOptions, - NewTransport, - TransportMakeRequestResponse, - TransportRequest, - TransportRequestExecutor, -} from './transports/base'; export { addBreadcrumb, diff --git a/packages/core/src/request.ts b/packages/core/src/request.ts index 2d65006272dd..9e58710af719 100644 --- a/packages/core/src/request.ts +++ b/packages/core/src/request.ts @@ -1,8 +1,10 @@ import { + DsnComponents, Event, EventEnvelope, EventItem, SdkInfo, + SdkMetadata, SentryRequest, SentryRequestType, Session, @@ -15,11 +17,11 @@ import { createEnvelope, dsnToString, normalize, serializeEnvelope } from '@sent import { APIDetails, getEnvelopeEndpointWithUrlEncodedAuth, getStoreEndpointWithUrlEncodedAuth } from './api'; /** Extract sdk info from from the API metadata */ -function getSdkMetadataForEnvelopeHeader(api: APIDetails): SdkInfo | undefined { - if (!api.metadata || !api.metadata.sdk) { +function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined { + if (!metadata || !metadata.sdk) { return; } - const { name, version } = api.metadata.sdk; + const { name, version } = metadata.sdk; return { name, version }; } @@ -42,13 +44,15 @@ function enhanceEventWithSdkInfo(event: Event, sdkInfo?: SdkInfo): Event { /** Creates an envelope from a Session */ export function createSessionEnvelope( session: Session | SessionAggregates, - api: APIDetails, + dsn: DsnComponents, + metadata?: SdkMetadata, + tunnel?: string, ): [SessionEnvelope, SentryRequestType] { - const sdkInfo = getSdkMetadataForEnvelopeHeader(api); + const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata); const envelopeHeaders = { sent_at: new Date().toISOString(), ...(sdkInfo && { sdk: sdkInfo }), - ...(!!api.tunnel && { dsn: dsnToString(api.dsn) }), + ...(!!tunnel && { dsn: dsnToString(dsn) }), }; // I know this is hacky but we don't want to add `sessions` to request type since it's never rate limited @@ -63,7 +67,7 @@ export function createSessionEnvelope( /** Creates a SentryRequest from a Session. */ export function sessionToSentryRequest(session: Session | SessionAggregates, api: APIDetails): SentryRequest { - const [envelope, type] = createSessionEnvelope(session, api); + const [envelope, type] = createSessionEnvelope(session, api.dsn, api.metadata, api.tunnel); return { body: serializeEnvelope(envelope), type, @@ -75,8 +79,13 @@ export function sessionToSentryRequest(session: Session | SessionAggregates, api * Create an Envelope from an event. Note that this is duplicated from below, * but on purpose as this will be refactored in v7. */ -export function createEventEnvelope(event: Event, api: APIDetails): EventEnvelope { - const sdkInfo = getSdkMetadataForEnvelopeHeader(api); +export function createEventEnvelope( + event: Event, + dsn: DsnComponents, + metadata?: SdkMetadata, + tunnel?: string, +): EventEnvelope { + const sdkInfo = getSdkMetadataForEnvelopeHeader(metadata); const eventType = event.type || 'event'; const { transactionSampling } = event.sdkProcessingMetadata || {}; @@ -96,7 +105,7 @@ export function createEventEnvelope(event: Event, api: APIDetails): EventEnvelop // 2. Restore the original version of the request body, which is commented out // 3. Search for either of the PR URLs above and pull out the companion hacks in the browser playwright tests and the // baseClient tests in this package - enhanceEventWithSdkInfo(event, api.metadata.sdk); + enhanceEventWithSdkInfo(event, metadata && metadata.sdk); event.tags = event.tags || {}; event.extra = event.extra || {}; @@ -115,7 +124,7 @@ export function createEventEnvelope(event: Event, api: APIDetails): EventEnvelop event_id: event.event_id as string, sent_at: new Date().toISOString(), ...(sdkInfo && { sdk: sdkInfo }), - ...(!!api.tunnel && { dsn: dsnToString(api.dsn) }), + ...(!!tunnel && { dsn: dsnToString(dsn) }), }; const eventItem: EventItem = [ { @@ -129,7 +138,7 @@ export function createEventEnvelope(event: Event, api: APIDetails): EventEnvelop /** Creates a SentryRequest from an event. */ export function eventToSentryRequest(event: Event, api: APIDetails): SentryRequest { - const sdkInfo = getSdkMetadataForEnvelopeHeader(api); + const sdkInfo = getSdkMetadataForEnvelopeHeader(api.metadata); const eventType = event.type || 'event'; const useEnvelope = eventType === 'transaction' || !!api.tunnel; diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index c7f7bb4916a3..b549163a1ecc 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -1,16 +1,11 @@ import { getCurrentHub } from '@sentry/hub'; -import { Client, ClientOptions, Transport } from '@sentry/types'; +import { Client, ClientOptions } from '@sentry/types'; import { logger } from '@sentry/utils'; import { IS_DEBUG_BUILD } from './flags'; -import { NewTransport } from './transports/base'; /** A class object that can instantiate Client objects. */ -export type ClientClass = new ( - options: O, - transport: Transport, - newTransport?: NewTransport, -) => F; +export type ClientClass = new (options: O) => F; /** * Internal function to create a new SDK client instance. The client is @@ -22,8 +17,6 @@ export type ClientClass = new ( export function initAndBind( clientClass: ClientClass, options: O, - transport: Transport, - newTransport?: NewTransport, ): void { if (options.debug === true) { if (IS_DEBUG_BUILD) { @@ -40,6 +33,6 @@ export function initAndBind( scope.update(options.initialScope); } - const client = new clientClass(options, transport, newTransport); + const client = new clientClass(options); hub.bindClient(client); } diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 787e175b9985..8c6cfe373bfe 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -1,4 +1,12 @@ -import { Envelope, EventStatus } from '@sentry/types'; +import { + Envelope, + InternalBaseTransportOptions, + NewTransport, + TransportCategory, + TransportRequest, + TransportRequestExecutor, + TransportResponse, +} from '@sentry/types'; import { disabledUntil, eventStatusFromHttpCode, @@ -13,58 +21,6 @@ import { updateRateLimits, } from '@sentry/utils'; -export const ERROR_TRANSPORT_CATEGORY = 'error'; - -export const TRANSACTION_TRANSPORT_CATEGORY = 'transaction'; - -export const ATTACHMENT_TRANSPORT_CATEGORY = 'attachment'; - -export const SESSION_TRANSPORT_CATEGORY = 'session'; - -type TransportCategory = - | typeof ERROR_TRANSPORT_CATEGORY - | typeof TRANSACTION_TRANSPORT_CATEGORY - | typeof ATTACHMENT_TRANSPORT_CATEGORY - | typeof SESSION_TRANSPORT_CATEGORY; - -export type TransportRequest = { - body: string; - category: TransportCategory; -}; - -export type TransportMakeRequestResponse = { - body?: string; - headers?: { - [key: string]: string | null; - 'x-sentry-rate-limits': string | null; - 'retry-after': string | null; - }; - reason?: string; - statusCode: number; -}; - -export type TransportResponse = { - status: EventStatus; - reason?: string; -}; - -interface InternalBaseTransportOptions { - bufferSize?: number; -} -export interface BaseTransportOptions extends InternalBaseTransportOptions { - // url to send the event - // transport does not care about dsn specific - client should take care of - // parsing and figuring that out - url: string; -} - -export interface NewTransport { - send(request: Envelope): PromiseLike; - flush(timeout?: number): PromiseLike; -} - -export type TransportRequestExecutor = (request: TransportRequest) => PromiseLike; - export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30; /** diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 47ec8ae70266..feb18390c3e8 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -1,12 +1,11 @@ import { Hub, Scope, Session } from '@sentry/hub'; -import { Event, Span, Transport } from '@sentry/types'; +import { Event, Span } from '@sentry/types'; import { dsnToString, logger, SentryError, SyncPromise } from '@sentry/utils'; import * as integrationModule from '../../src/integration'; -import { NoopTransport } from '../../src/transports/noop'; -import { getDefaultTestClientOptions, setupTestTransport, TestClient } from '../mocks/client'; +import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; import { TestIntegration } from '../mocks/integration'; -import { FakeTransport } from '../mocks/transport'; +import { makeFakeTransport } from '../mocks/transport'; const PUBLIC_DSN = 'https://username@domain/123'; // eslint-disable-next-line no-var @@ -68,7 +67,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); expect(dsnToString(client.getDsn()!)).toBe(PUBLIC_DSN); }); @@ -76,7 +75,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions(); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); expect(client.getDsn()).toBeUndefined(); }); @@ -85,7 +84,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: 'abc' }); - expect(() => new TestClient(options, setupTestTransport(options).transport)).toThrow(SentryError); + expect(() => new TestClient(options)).toThrow(SentryError); }); }); @@ -94,31 +93,20 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, test: true }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); expect(client.getOptions()).toEqual(options); }); }); describe('getTransport()', () => { - test('returns the transport from client', () => { - expect.assertions(2); - - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, transport: FakeTransport }); - const client = new TestClient(options, new FakeTransport()); - - expect(client.getTransport()).toBeInstanceOf(FakeTransport); - expect(TestClient.instance!.getTransport()).toBe(client.getTransport()); - }); - - test('retruns NoopTransport when no transport is passed', () => { - expect.assertions(2); + test('returns undefined when no dsn is set', () => { + expect.assertions(1); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const options = getDefaultTestClientOptions({}); + const client = new TestClient(options); - expect(client.getTransport()).toBeInstanceOf(NoopTransport); - expect(TestClient.instance!.getTransport()).toBe(client.getTransport()); + expect(client.getTransport()).toBeUndefined(); }); }); @@ -127,7 +115,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({}); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -141,7 +129,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({}); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -155,7 +143,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ maxBreadcrumbs: 1 }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -170,7 +158,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({}); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -185,7 +173,7 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -199,7 +187,7 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(() => ({ message: 'changed' })); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -213,7 +201,7 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn(() => null); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -227,7 +215,7 @@ describe('BaseClient', () => { const beforeBreadcrumb = jest.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data })); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); @@ -241,7 +229,7 @@ describe('BaseClient', () => { describe('captureException', () => { test('captures and sends exceptions', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureException(new Error('test exception')); @@ -264,7 +252,7 @@ describe('BaseClient', () => { test('allows for providing explicit scope', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -292,7 +280,7 @@ describe('BaseClient', () => { test('allows for clearing data from existing scope if explicit one does so in a callback function', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -327,7 +315,7 @@ describe('BaseClient', () => { // is encountered, so this test doesn't apply. ])("doesn't capture the same exception twice - %s", (_name: string, thrown: any) => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); expect(thrown.__sentry_captured__).toBeUndefined(); @@ -346,7 +334,7 @@ describe('BaseClient', () => { describe('captureMessage', () => { test('captures and sends messages', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureMessage('test message'); @@ -363,7 +351,7 @@ describe('BaseClient', () => { test('should call eventFromException if input to captureMessage is not a primitive', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const spy = jest.spyOn(TestClient.instance!, 'eventFromException'); client.captureMessage('foo'); @@ -382,7 +370,7 @@ describe('BaseClient', () => { test('allows for providing explicit scope', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setExtra('foo', 'wat'); @@ -416,7 +404,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ enabled: false, dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({}, undefined, scope); @@ -428,7 +416,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({}); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({}, undefined, scope); @@ -449,7 +437,7 @@ describe('BaseClient', () => { // Note: this is the same test as in `describe(captureException)`, except with the exception already wrapped in a // hint and accompanying an event. Duplicated here because some methods skip `captureException` and go straight to // `captureEvent`. - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const event = { exception: { values: [{ type: 'Error', message: 'Will I get caught twice?' }] } }; const hint = { originalException: thrown }; @@ -470,7 +458,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -490,7 +478,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message', timestamp: 1234 }, undefined, scope); @@ -510,7 +498,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, { event_id: 'wat' }, scope); @@ -529,7 +517,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -548,7 +536,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ environment: 'env', dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -567,7 +555,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, environment: undefined }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -586,7 +574,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, release: 'v1.0.0' }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); client.captureEvent({ message: 'message' }, undefined, scope); @@ -606,7 +594,7 @@ describe('BaseClient', () => { expect.assertions(4); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.addBreadcrumb({ message: 'breadcrumb' }, 100); @@ -622,7 +610,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, maxBreadcrumbs: 1 }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); const hub = new Hub(client, scope); hub.addBreadcrumb({ message: '1' }); @@ -638,7 +626,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setExtra('b', 'b'); scope.setTag('a', 'a'); @@ -663,7 +651,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const scope = new Scope(); scope.setFingerprint(['abcd']); @@ -682,7 +670,7 @@ describe('BaseClient', () => { test('adds installed integrations to sdk info', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.setupIntegrations(); client.captureEvent({ message: 'message' }); @@ -696,7 +684,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const fourLevelsObject = { a: { b: { @@ -748,7 +736,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, normalizeDepth: 2 }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const fourLevelsObject = { a: { b: { @@ -797,7 +785,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, normalizeDepth: 0 }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const fourLevelsObject = { a: { b: { @@ -851,7 +839,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const transaction: Event = { contexts: { trace: { @@ -926,7 +914,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn(event => event); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); @@ -938,7 +926,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn(() => ({ message: 'changed1' })); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); @@ -950,7 +938,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn(() => null); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); @@ -969,7 +957,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn(() => val); // @ts-ignore we need to test regular-js behavior const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const loggerErrorSpy = jest.spyOn(logger, 'error'); client.captureEvent({ message: 'hello' }); @@ -994,7 +982,7 @@ describe('BaseClient', () => { }), ); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); @@ -1023,7 +1011,7 @@ describe('BaseClient', () => { }), ); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); jest.runOnlyPendingTimers(); @@ -1052,7 +1040,7 @@ describe('BaseClient', () => { }), ); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }); jest.runAllTimers(); @@ -1065,7 +1053,7 @@ describe('BaseClient', () => { const beforeSend = jest.fn((event, hint) => ({ ...event, data: hint.data })); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.captureEvent({ message: 'hello' }, { data: 'someRandomThing' }); @@ -1073,38 +1061,35 @@ describe('BaseClient', () => { expect((TestClient.instance!.event! as any).data).toBe('someRandomThing'); }); - test('beforeSend records dropped events', () => { - expect.assertions(1); + // TODO(v7): Add back test with client reports + // test('beforeSend records dropped events', () => { + // expect.assertions(1); - const client = new TestClient( - getDefaultTestClientOptions({ - dsn: PUBLIC_DSN, - beforeSend() { - return null; - }, - }), - setupTestTransport(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })).transport, - ); - const recordLostEventSpy = jest.fn(); - jest.spyOn(client, 'getTransport').mockImplementationOnce( - () => - ({ - recordLostEvent: recordLostEventSpy, - } as any as Transport), - ); + // const client = new TestClient( + // getDefaultTestClientOptions({ + // dsn: PUBLIC_DSN, + // beforeSend() { + // return null; + // }, + // }), + // ); + // const recordLostEventSpy = jest.fn(); + // jest.spyOn(client, 'getTransport').mockImplementationOnce( + // () => + // ({ + // recordLostEvent: recordLostEventSpy, + // } as any as Transport), + // ); - client.captureEvent({ message: 'hello' }, {}); + // client.captureEvent({ message: 'hello' }, {}); - expect(recordLostEventSpy).toHaveBeenCalledWith('before_send', 'event'); - }); + // expect(recordLostEventSpy).toHaveBeenCalledWith('before_send', 'event'); + // }); test('eventProcessor can drop the even when it returns null', () => { expect.assertions(3); - const client = new TestClient( - getDefaultTestClientOptions({ dsn: PUBLIC_DSN }), - setupTestTransport(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })).transport, - ); + const client = new TestClient(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); const scope = new Scope(); @@ -1117,33 +1102,34 @@ describe('BaseClient', () => { expect(loggerErrorSpy).toBeCalledWith(new SentryError('An event processor returned null, will not send event.')); }); - test('eventProcessor records dropped events', () => { - expect.assertions(1); + // TODO(v7): Add back tests with client reports + // test('eventProcessor records dropped events', () => { + // expect.assertions(1); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); - - const recordLostEventSpy = jest.fn(); - jest.spyOn(client, 'getTransport').mockImplementationOnce( - () => - ({ - recordLostEvent: recordLostEventSpy, - } as any as Transport), - ); + // const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); + // const client = new TestClient(options); - const scope = new Scope(); - scope.addEventProcessor(() => null); + // const recordLostEventSpy = jest.fn(); + // jest.spyOn(client, 'getTransport').mockImplementationOnce( + // () => + // ({ + // recordLostEvent: recordLostEventSpy, + // } as any as Transport), + // ); - client.captureEvent({ message: 'hello' }, {}, scope); + // const scope = new Scope(); + // scope.addEventProcessor(() => null); - expect(recordLostEventSpy).toHaveBeenCalledWith('event_processor', 'event'); - }); + // client.captureEvent({ message: 'hello' }, {}, scope); + + // expect(recordLostEventSpy).toHaveBeenCalledWith('event_processor', 'event'); + // }); test('eventProcessor sends an event and logs when it crashes', () => { expect.assertions(3); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const captureExceptionSpy = jest.spyOn(client, 'captureException'); const loggerErrorSpy = jest.spyOn(logger, 'error'); const scope = new Scope(); @@ -1168,23 +1154,24 @@ describe('BaseClient', () => { ); }); - test('records events dropped due to sampleRate', () => { - expect.assertions(1); + // TODO(v7): Add back test with client reports + // test('records events dropped due to sampleRate', () => { + // expect.assertions(1); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, sampleRate: 0 }); - const client = new TestClient(options, setupTestTransport(options).transport); + // const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, sampleRate: 0 }); + // const client = new TestClient(options); - const recordLostEventSpy = jest.fn(); - jest.spyOn(client, 'getTransport').mockImplementationOnce( - () => - ({ - recordLostEvent: recordLostEventSpy, - } as any as Transport), - ); + // const recordLostEventSpy = jest.fn(); + // jest.spyOn(client, 'getTransport').mockImplementationOnce( + // () => + // ({ + // recordLostEvent: recordLostEventSpy, + // } as any as Transport), + // ); - client.captureEvent({ message: 'hello' }, {}); - expect(recordLostEventSpy).toHaveBeenCalledWith('sample_rate', 'event'); - }); + // client.captureEvent({ message: 'hello' }, {}); + // expect(recordLostEventSpy).toHaveBeenCalledWith('sample_rate', 'event'); + // }); }); describe('integrations', () => { @@ -1196,7 +1183,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(1); @@ -1207,7 +1194,7 @@ describe('BaseClient', () => { expect.assertions(2); const options = getDefaultTestClientOptions({ integrations: [new TestIntegration()] }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(0); @@ -1222,7 +1209,7 @@ describe('BaseClient', () => { enabled: false, integrations: [new TestIntegration()], }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); client.setupIntegrations(); expect(Object.keys((client as any)._integrations).length).toBe(0); @@ -1233,7 +1220,7 @@ describe('BaseClient', () => { expect.assertions(4); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); // note: not the `Client` method `setupIntegrations`, but the free-standing function which that method calls const setupIntegrationsHelper = jest.spyOn(integrationModule, 'setupIntegrations'); @@ -1254,47 +1241,43 @@ describe('BaseClient', () => { describe('flush/close', () => { test('flush', async () => { jest.useRealTimers(); - expect.assertions(5); + expect.assertions(4); + + const { makeTransport, getSendCalled, getSentCount, delay } = makeFakeTransport(1); const client = new TestClient( getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, - transport: FakeTransport, + transport: makeTransport, }), - new FakeTransport(), ); - const delay = 1; - const transportInstance = client.getTransport() as FakeTransport; - transportInstance.delay = delay; - client.captureMessage('test'); - expect(transportInstance).toBeInstanceOf(FakeTransport); - expect(transportInstance.sendCalled).toEqual(1); - expect(transportInstance.sentCount).toEqual(0); + expect(getSendCalled()).toEqual(1); + expect(getSentCount()).toEqual(0); await client.flush(delay); - expect(transportInstance.sentCount).toEqual(1); - expect(transportInstance.sendCalled).toEqual(1); + expect(getSentCount()).toEqual(1); + expect(getSendCalled()).toEqual(1); }); test('flush with some events being processed async', async () => { jest.useRealTimers(); - expect.assertions(5); + expect.assertions(4); + + const { makeTransport, getSendCalled, getSentCount, delay } = makeFakeTransport(300); const client = new TestClient( getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, - transport: FakeTransport, + transport: makeTransport, }), - new FakeTransport(), ); - const delay = 300; const spy = jest.spyOn(TestClient.instance!, 'eventFromMessage'); spy.mockImplementationOnce( (message, level) => @@ -1302,20 +1285,16 @@ describe('BaseClient', () => { setTimeout(() => resolve({ message, level }), 150); }), ); - const transportInstance = client.getTransport() as FakeTransport; - transportInstance.delay = delay; - client.captureMessage('test async'); client.captureMessage('test non-async'); - expect(transportInstance).toBeInstanceOf(FakeTransport); - expect(transportInstance.sendCalled).toEqual(1); - expect(transportInstance.sentCount).toEqual(0); + expect(getSendCalled()).toEqual(1); + expect(getSentCount()).toEqual(0); await client.flush(delay); - expect(transportInstance.sentCount).toEqual(2); - expect(transportInstance.sendCalled).toEqual(2); + expect(getSentCount()).toEqual(2); + expect(getSendCalled()).toEqual(2); spy.mockRestore(); }); @@ -1324,19 +1303,15 @@ describe('BaseClient', () => { jest.useRealTimers(); expect.assertions(2); + const { makeTransport, delay } = makeFakeTransport(300); + const client = new TestClient( getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableSend: true, - transport: FakeTransport, + transport: makeTransport, }), - new FakeTransport(), ); - - const delay = 1; - const transportInstance = client.getTransport() as FakeTransport; - transportInstance.delay = delay; - expect(client.captureMessage('test')).toBeTruthy(); await client.close(delay); @@ -1350,7 +1325,7 @@ describe('BaseClient', () => { expect.assertions(3); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); return Promise.all([ client.flush(1).then(() => { @@ -1371,7 +1346,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const session = new Session({ release: 'test' }); client.captureSession(session); @@ -1383,7 +1358,7 @@ describe('BaseClient', () => { expect.assertions(1); const options = getDefaultTestClientOptions({ enabled: false, dsn: PUBLIC_DSN }); - const client = new TestClient(options, setupTestTransport(options).transport); + const client = new TestClient(options); const session = new Session({ release: 'test' }); client.captureSession(session); diff --git a/packages/core/test/lib/sdk.test.ts b/packages/core/test/lib/sdk.test.ts index 7dd3229c5c7e..d2857a2d83bd 100644 --- a/packages/core/test/lib/sdk.test.ts +++ b/packages/core/test/lib/sdk.test.ts @@ -3,7 +3,7 @@ import { Client, Integration } from '@sentry/types'; import { installedIntegrations } from '../../src/integration'; import { initAndBind } from '../../src/sdk'; -import { getDefaultTestClientOptions, setupTestTransport, TestClient } from '../mocks/client'; +import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; // eslint-disable-next-line no-var declare var global: any; @@ -56,7 +56,7 @@ describe('SDK', () => { new MockIntegration('MockIntegration 2'), ]; const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations }); - initAndBind(TestClient, options, setupTestTransport(options).transport); + initAndBind(TestClient, options); expect((integrations[0].setupOnce as jest.Mock).mock.calls.length).toBe(1); expect((integrations[1].setupOnce as jest.Mock).mock.calls.length).toBe(1); }); diff --git a/packages/core/test/lib/transports/base.test.ts b/packages/core/test/lib/transports/base.test.ts index 2257a67165b1..78b40d0a4f7c 100644 --- a/packages/core/test/lib/transports/base.test.ts +++ b/packages/core/test/lib/transports/base.test.ts @@ -1,14 +1,11 @@ -import { EventEnvelope, EventItem } from '@sentry/types'; +import { EventEnvelope, EventItem, NewTransport, TransportMakeRequestResponse, TransportResponse } from '@sentry/types'; import { createEnvelope, PromiseBuffer, resolvedSyncPromise, serializeEnvelope } from '@sentry/utils'; -import { - createTransport, - ERROR_TRANSPORT_CATEGORY, - NewTransport, - TRANSACTION_TRANSPORT_CATEGORY, - TransportMakeRequestResponse, - TransportResponse, -} from '../../../src/transports/base'; +import { createTransport } from '../../../src/transports/base'; + +const ERROR_TRANSPORT_CATEGORY = 'error'; + +const TRANSACTION_TRANSPORT_CATEGORY = 'transaction'; const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [ [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, diff --git a/packages/core/test/mocks/client.ts b/packages/core/test/mocks/client.ts index 5778dcf5e193..9f02e5b6540d 100644 --- a/packages/core/test/mocks/client.ts +++ b/packages/core/test/mocks/client.ts @@ -1,16 +1,15 @@ import { Session } from '@sentry/hub'; -import { ClientOptions, Event, Integration, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { ClientOptions, Event, Integration, Severity, SeverityLevel } from '@sentry/types'; import { resolvedSyncPromise } from '@sentry/utils'; import { BaseClient } from '../../src/baseclient'; import { initAndBind } from '../../src/sdk'; -import { NewTransport } from '../../src/transports/base'; -import { NoopTransport } from '../../src/transports/noop'; +import { createTransport } from '../../src/transports/base'; export function getDefaultTestClientOptions(options: Partial = {}): TestClientOptions { return { integrations: [], - transport: NoopTransport, + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), stackParser: () => [], ...options, }; @@ -30,8 +29,8 @@ export class TestClient extends BaseClient { public event?: Event; public session?: Session; - public constructor(options: TestClientOptions, transport: Transport, newTransport?: NewTransport) { - super(options, transport, newTransport); + public constructor(options: TestClientOptions) { + super(options); TestClient.instance = this; } @@ -74,23 +73,6 @@ export class TestClient extends BaseClient { } } -export function init(options: TestClientOptions, transport: Transport, newTransport?: NewTransport): void { - initAndBind(TestClient, options, transport, newTransport); -} - -export function setupTestTransport(options: TestClientOptions): { transport: Transport; newTransport?: NewTransport } { - const noop = { transport: new NoopTransport() }; - - if (!options.dsn) { - // We return the noop transport here in case there is no Dsn. - return noop; - } - - const transportOptions = options.transportOptions ? options.transportOptions : { dsn: options.dsn }; - - if (options.transport) { - return { transport: new options.transport(transportOptions) }; - } - - return noop; +export function init(options: TestClientOptions): void { + initAndBind(TestClient, options); } diff --git a/packages/core/test/mocks/transport.ts b/packages/core/test/mocks/transport.ts index 1037fada987d..c7fdb02e558a 100644 --- a/packages/core/test/mocks/transport.ts +++ b/packages/core/test/mocks/transport.ts @@ -1,31 +1,22 @@ -import { Event, Response, Transport } from '@sentry/types'; -import { makePromiseBuffer, PromiseBuffer, SyncPromise } from '@sentry/utils'; +import { SyncPromise } from '@sentry/utils'; + +import { createTransport } from '../../src/transports/base'; async function sleep(delay: number): Promise { return new SyncPromise(resolve => setTimeout(resolve, delay)); } -export class FakeTransport implements Transport { - public sendCalled: number = 0; - public sentCount: number = 0; - public delay: number = 2000; - - /** A simple buffer holding all requests. */ - protected readonly _buffer: PromiseBuffer = makePromiseBuffer(9999); - - public sendEvent(_event: Event): PromiseLike { - this.sendCalled += 1; - return this._buffer.add( - () => - new SyncPromise(async res => { - await sleep(this.delay); - this.sentCount += 1; - res({ status: 'success' }); - }), - ); - } - - public close(timeout?: number): PromiseLike { - return this._buffer.drain(timeout); - } +export function makeFakeTransport(delay: number = 2000) { + let sendCalled = 0; + let sentCount = 0; + const makeTransport = () => + createTransport({}, () => { + sendCalled += 1; + return new SyncPromise(async res => { + await sleep(delay); + sentCount += 1; + res({ statusCode: 200 }); + }); + }); + return { makeTransport, getSendCalled: () => sendCalled, getSentCount: () => sentCount, delay }; } diff --git a/packages/ember/tests/dummy/app/app.js b/packages/ember/tests/dummy/app/app.js index 25a843863e69..3479bd41f315 100644 --- a/packages/ember/tests/dummy/app/app.js +++ b/packages/ember/tests/dummy/app/app.js @@ -4,25 +4,7 @@ import loadInitializers from 'ember-load-initializers'; import config from './config/environment'; import * as Sentry from '@sentry/ember'; -import { Transports } from '@sentry/browser'; -import Ember from 'ember'; - -class TestFetchTransport extends Transports.FetchTransport { - sendEvent(event) { - if (Ember.testing) { - if (!window._sentryTestEvents) { - window._sentryTestEvents = []; - } - window._sentryTestEvents.push(event); - return Promise.resolve(); - } - return super.sendEvent(event); - } -} - -Sentry.init({ - transport: TestFetchTransport, -}); +Sentry.init(); export default class App extends Application { modulePrefix = config.modulePrefix; diff --git a/packages/ember/tests/test-helper.js b/packages/ember/tests/test-helper.js index eae0acb0b24e..da7c45929c8f 100644 --- a/packages/ember/tests/test-helper.js +++ b/packages/ember/tests/test-helper.js @@ -10,30 +10,17 @@ import Application from '../app'; import config from '../config/environment'; import { setApplication } from '@ember/test-helpers'; import { start } from 'ember-qunit'; -import { Transports } from '@sentry/browser'; import Ember from 'ember'; -import { getConfig } from '@embroider/macros'; - -function getSentryConfig() { - return getConfig('@sentry/ember').sentryConfig; -} - -export class TestFetchTransport extends Transports.FetchTransport { - sendEvent(event) { - if (Ember.testing) { - if (!window._sentryTestEvents) { - window._sentryTestEvents = []; - } - window._sentryTestEvents.push(event); - return Promise.resolve(); +Sentry.addGlobalEventProcessor((event) => { + if (Ember.testing) { + if (!window._sentryTestEvents) { + window._sentryTestEvents = []; } - return super.sendEvent(event); + window._sentryTestEvents.push(event); } -} - -const sentryConfig = getSentryConfig(); -sentryConfig.sentry['transport'] = TestFetchTransport; + return event; +}); setApplication(Application.create(config.APP)); diff --git a/packages/hub/src/sessionflusher.ts b/packages/hub/src/sessionflusher.ts index fcc4386c70d8..7b2bda98c4da 100644 --- a/packages/hub/src/sessionflusher.ts +++ b/packages/hub/src/sessionflusher.ts @@ -1,13 +1,6 @@ -import { - AggregationCounts, - RequestSessionStatus, - SessionAggregates, - SessionFlusherLike, - Transport, -} from '@sentry/types'; -import { dropUndefinedKeys, logger } from '@sentry/utils'; +import { AggregationCounts, Client, RequestSessionStatus, SessionAggregates, SessionFlusherLike } from '@sentry/types'; +import { dropUndefinedKeys } from '@sentry/utils'; -import { IS_DEBUG_BUILD } from './flags'; import { getCurrentHub } from './hub'; type ReleaseHealthAttributes = { @@ -24,34 +17,23 @@ export class SessionFlusher implements SessionFlusherLike { private _sessionAttrs: ReleaseHealthAttributes; private _intervalId: ReturnType; private _isEnabled: boolean = true; - private _transport: Transport; + private _client: Client; - public constructor(transport: Transport, attrs: ReleaseHealthAttributes) { - this._transport = transport; + public constructor(client: Client, attrs: ReleaseHealthAttributes) { + this._client = client; // Call to setInterval, so that flush is called every 60 seconds this._intervalId = setInterval(() => this.flush(), this.flushTimeout * 1000); this._sessionAttrs = attrs; } - /** Sends session aggregates to Transport */ - public sendSessionAggregates(sessionAggregates: SessionAggregates): void { - if (!this._transport.sendSession) { - IS_DEBUG_BUILD && logger.warn("Dropping session because custom transport doesn't implement sendSession"); - return; - } - void this._transport.sendSession(sessionAggregates).then(null, reason => { - IS_DEBUG_BUILD && logger.error('Error while sending session:', reason); - }); - } - - /** Checks if `pendingAggregates` has entries, and if it does flushes them by calling `sendSessions` */ + /** Checks if `pendingAggregates` has entries, and if it does flushes them by calling `sendSession` */ public flush(): void { const sessionAggregates = this.getSessionAggregates(); if (sessionAggregates.aggregates.length === 0) { return; } this._pendingAggregates = {}; - this.sendSessionAggregates(sessionAggregates); + this._client.sendSession(sessionAggregates); } /** 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 3c7dc9782615..58ce3ee374ce 100644 --- a/packages/hub/test/sessionflusher.test.ts +++ b/packages/hub/test/sessionflusher.test.ts @@ -1,21 +1,17 @@ +import { Client } from '@sentry/types'; + import { SessionFlusher } from '../src/sessionflusher'; describe('Session Flusher', () => { let sendSession: jest.Mock; - let transport: { - sendEvent: jest.Mock; - sendSession: jest.Mock; - close: jest.Mock; - }; + let mockClient: Client; beforeEach(() => { jest.useFakeTimers(); sendSession = jest.fn(() => Promise.resolve({ status: 'success' })); - transport = { - sendEvent: jest.fn(), + mockClient = { sendSession, - close: jest.fn(), - }; + } as unknown as Client; }); afterEach(() => { @@ -23,7 +19,7 @@ describe('Session Flusher', () => { }); test('test incrementSessionStatusCount updates the internal SessionFlusher state', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.0', environment: 'dev' }); const date = new Date('2021-04-08T12:18:23.043Z'); let count = (flusher as any)._incrementSessionStatusCount('ok', date); @@ -46,7 +42,7 @@ describe('Session Flusher', () => { }); test('test undefined attributes are excluded, on incrementSessionStatusCount call', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.0' }); const date = new Date('2021-04-08T12:18:23.043Z'); (flusher as any)._incrementSessionStatusCount('ok', date); @@ -59,7 +55,7 @@ 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 flusher = new SessionFlusher(mockClient, { release: '1.0.0', environment: 'dev' }); const flusherFlushFunc = jest.spyOn(flusher, 'flush'); jest.advanceTimersByTime(59000); expect(flusherFlushFunc).toHaveBeenCalledTimes(0); @@ -72,7 +68,7 @@ describe('Session Flusher', () => { }); test('sendSessions is called on flush if sessions were captured', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.0', environment: 'dev' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.0', environment: 'dev' }); const flusherFlushFunc = jest.spyOn(flusher, 'flush'); const date = new Date('2021-04-08T12:18:23.043Z'); (flusher as any)._incrementSessionStatusCount('ok', date); @@ -92,7 +88,7 @@ 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 flusher = new SessionFlusher(mockClient, { release: '1.0.0', environment: 'dev' }); const flusherFlushFunc = jest.spyOn(flusher, 'flush'); expect(sendSession).toHaveBeenCalledTimes(0); @@ -102,13 +98,13 @@ describe('Session Flusher', () => { }); test('calling close on SessionFlusher should disable SessionFlusher', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.x' }); + const flusher = new SessionFlusher(mockClient, { release: '1.0.x' }); flusher.close(); expect((flusher as any)._isEnabled).toEqual(false); }); test('calling close on SessionFlusher will force call flush', () => { - const flusher = new SessionFlusher(transport, { release: '1.0.x' }); + const flusher = new SessionFlusher(mockClient, { 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); diff --git a/packages/integration-tests/suites/new-transports/fetch-captureException/subject.js b/packages/integration-tests/suites/new-transports/fetch-captureException/subject.js deleted file mode 100644 index 9cc217bdb087..000000000000 --- a/packages/integration-tests/suites/new-transports/fetch-captureException/subject.js +++ /dev/null @@ -1 +0,0 @@ -Sentry.captureException(new Error('this is an error')); diff --git a/packages/integration-tests/suites/new-transports/fetch-captureException/test.ts b/packages/integration-tests/suites/new-transports/fetch-captureException/test.ts deleted file mode 100644 index cb92e50e2dc5..000000000000 --- a/packages/integration-tests/suites/new-transports/fetch-captureException/test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; - -import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; - -sentryTest('should capture an error with the new fetch transport', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.exception?.values).toHaveLength(1); - expect(eventData.exception?.values?.[0]).toMatchObject({ - type: 'Error', - value: 'this is an error', - mechanism: { - type: 'generic', - handled: true, - }, - }); -}); diff --git a/packages/integration-tests/suites/new-transports/fetch-startTransaction/subject.js b/packages/integration-tests/suites/new-transports/fetch-startTransaction/subject.js deleted file mode 100644 index 78c7c33c654c..000000000000 --- a/packages/integration-tests/suites/new-transports/fetch-startTransaction/subject.js +++ /dev/null @@ -1,2 +0,0 @@ -const transaction = Sentry.startTransaction({ name: 'test_transaction_1' }); -transaction.finish(); diff --git a/packages/integration-tests/suites/new-transports/fetch-startTransaction/test.ts b/packages/integration-tests/suites/new-transports/fetch-startTransaction/test.ts deleted file mode 100644 index 8daef2e06b54..000000000000 --- a/packages/integration-tests/suites/new-transports/fetch-startTransaction/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; - -import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; - -sentryTest('should report a transaction with the new fetch transport', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - const transaction = await getFirstSentryEnvelopeRequest(page, url); - - expect(transaction.transaction).toBe('test_transaction_1'); - expect(transaction.spans).toBeDefined(); -}); diff --git a/packages/integration-tests/suites/new-transports/init.js b/packages/integration-tests/suites/new-transports/init.js deleted file mode 100644 index 6cc8110c0475..000000000000 --- a/packages/integration-tests/suites/new-transports/init.js +++ /dev/null @@ -1,13 +0,0 @@ -import * as Sentry from '@sentry/browser'; -// eslint-disable-next-line no-unused-vars -import * as _ from '@sentry/tracing'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - _experiments: { - newTransport: true, - }, - tracesSampleRate: 1.0, -}); diff --git a/packages/integration-tests/suites/new-transports/xhr-captureException/subject.js b/packages/integration-tests/suites/new-transports/xhr-captureException/subject.js deleted file mode 100644 index e42102004dad..000000000000 --- a/packages/integration-tests/suites/new-transports/xhr-captureException/subject.js +++ /dev/null @@ -1,4 +0,0 @@ -// deactivate fetch s.t. the SDK falls back to XHR transport -window.fetch = undefined; - -Sentry.captureException(new Error('this is an error')); diff --git a/packages/integration-tests/suites/new-transports/xhr-captureException/test.ts b/packages/integration-tests/suites/new-transports/xhr-captureException/test.ts deleted file mode 100644 index cb92e50e2dc5..000000000000 --- a/packages/integration-tests/suites/new-transports/xhr-captureException/test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; - -import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; - -sentryTest('should capture an error with the new fetch transport', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.exception?.values).toHaveLength(1); - expect(eventData.exception?.values?.[0]).toMatchObject({ - type: 'Error', - value: 'this is an error', - mechanism: { - type: 'generic', - handled: true, - }, - }); -}); diff --git a/packages/integration-tests/suites/new-transports/xhr-startTransaction/subject.js b/packages/integration-tests/suites/new-transports/xhr-startTransaction/subject.js deleted file mode 100644 index 444e095ed3a1..000000000000 --- a/packages/integration-tests/suites/new-transports/xhr-startTransaction/subject.js +++ /dev/null @@ -1,5 +0,0 @@ -// deactivate fetch s.t. the SDK falls back to XHR transport -window.fetch = undefined; - -const transaction = Sentry.startTransaction({ name: 'test_transaction_1' }); -transaction.finish(); diff --git a/packages/integration-tests/suites/new-transports/xhr-startTransaction/test.ts b/packages/integration-tests/suites/new-transports/xhr-startTransaction/test.ts deleted file mode 100644 index 59ddfb00c6a1..000000000000 --- a/packages/integration-tests/suites/new-transports/xhr-startTransaction/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { expect } from '@playwright/test'; -import { Event } from '@sentry/types'; - -import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; - -sentryTest('should report a transaction with the new XHR transport', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - const transaction = await getFirstSentryEnvelopeRequest(page, url); - - expect(transaction.transaction).toBe('test_transaction_1'); - expect(transaction.spans).toBeDefined(); -}); diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts b/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts index e6e2b9a8f4dd..3fea4283b71e 100644 --- a/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts +++ b/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts @@ -1,14 +1,15 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest( 'should add an empty breadcrumb initialized with a timestamp, when an empty object is given', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.breadcrumbs).toHaveLength(1); expect(eventData.breadcrumbs?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts b/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts index c7bca64aafd5..d864be4f9073 100644 --- a/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts +++ b/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should add multiple breadcrumbs', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.breadcrumbs).toHaveLength(2); expect(eventData.breadcrumbs?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts b/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts index 98e36a254076..224d4dba0932 100644 --- a/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts +++ b/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should add a simple breadcrumb', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.breadcrumbs).toHaveLength(1); expect(eventData.breadcrumbs?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts b/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts index b41f527c58ed..5e5ec669a7dc 100644 --- a/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts +++ b/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts @@ -1,14 +1,15 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest( 'should add an empty breadcrumb initialized with a timestamp, when no argument is given', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.breadcrumbs).toHaveLength(1); expect(eventData.breadcrumbs?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts b/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts index 2606b2984d08..a41fdcc6a6e1 100644 --- a/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts +++ b/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should capture an empty object', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.exception?.values).toHaveLength(1); expect(eventData.exception?.values?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts b/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts index f52e951c20c6..49627e826726 100644 --- a/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts +++ b/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should capture a simple error with message', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.exception?.values).toHaveLength(1); expect(eventData.exception?.values?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts b/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts index 021af6f922f3..52e2ef5c21f8 100644 --- a/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts +++ b/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should capture an undefined error when no arguments are provided', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.exception?.values).toHaveLength(1); expect(eventData.exception?.values?.[0]).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts b/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts index 7b4b68f228d6..cfd5580653ac 100644 --- a/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts +++ b/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should capture a simple message string', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('foo'); expect(eventData.level).toBe('info'); diff --git a/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts b/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts index ba8bb18d729a..45eaeca66161 100644 --- a/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts +++ b/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryRequests } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should capture with different severity levels', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const events = await getMultipleSentryRequests(page, 7, url); + const events = await getMultipleSentryEnvelopeRequests(page, 7, { url }); expect(events[0].message).toBe('debug_message'); expect(events[0].level).toBe('debug'); diff --git a/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts b/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts index 4bbb0faf4c56..8626cf1adf66 100644 --- a/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts +++ b/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should clear previously set properties of a scope', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); // TODO: This is to compensate for a temporary debugging hack which adds data the tests aren't anticipating to the // event. The code can be restored to its original form (the commented-out line below) once that hack is diff --git a/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts b/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts index d4001c317d05..992dd7c31043 100644 --- a/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts +++ b/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set different properties of a scope', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('configured_scope'); expect(eventData.user).toMatchObject({ id: 'baz' }); diff --git a/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts b/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts index 96eecdd38662..6d00519cbad4 100644 --- a/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts +++ b/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should record multiple contexts', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('multiple_contexts'); expect(eventData.contexts).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts b/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts index fe67bdaff3e7..54fea2c68908 100644 --- a/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts +++ b/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should normalize non-serializable context', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.contexts?.non_serializable).toMatchObject({}); expect(eventData.message).toBe('non_serializable'); diff --git a/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts b/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts index 05f534888796..a39f838f5b18 100644 --- a/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts +++ b/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set a simple context', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('simple_context_object'); expect(eventData.contexts).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts b/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts index 13d8aa83d9c4..82a2b4ce21e5 100644 --- a/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts +++ b/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should record multiple extras of different types', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('multiple_extras'); expect(eventData.extra).toMatchObject({ extra_1: { foo: 'bar', baz: { qux: 'quux' } }, extra_2: false }); diff --git a/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts b/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts index eaa9d342e4e8..168bfc88e2c5 100644 --- a/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts +++ b/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should normalize non-serializable extra', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('non_serializable'); expect(eventData.extra).toMatchObject({}); diff --git a/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts b/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts index 352b01191e6e..95a6184e95a9 100644 --- a/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts +++ b/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should record a simple extra object', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('simple_extra'); expect(eventData.extra).toMatchObject({ simple_extra: { foo: 'bar', baz: { qux: 'quux' } } }); diff --git a/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts b/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts index 319afc32255b..641325affa34 100644 --- a/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts +++ b/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set extras from multiple consecutive calls', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('consecutive_calls'); expect(eventData.extra).toMatchObject({ extra: [], Infinity: 2, null: null, obj: { foo: ['bar', 'baz', 1] } }); diff --git a/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts b/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts index 07a43458e94b..1e238739d8a1 100644 --- a/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts +++ b/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should record an extras object', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('multiple_extras'); expect(eventData.extra).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts b/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts index 56843d8f6652..e4a1f9b19bd4 100644 --- a/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts +++ b/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should not accept non-primitive tags', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('non_primitives'); expect(eventData.tags).toMatchObject({}); diff --git a/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts b/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts index b3c24a8cd2c9..ba2b648ad913 100644 --- a/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts +++ b/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set primitive tags', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('primitive_tags'); expect(eventData.tags).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts b/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts index 56843d8f6652..e4a1f9b19bd4 100644 --- a/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts +++ b/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should not accept non-primitive tags', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('non_primitives'); expect(eventData.tags).toMatchObject({}); diff --git a/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts b/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts index b3c24a8cd2c9..ba2b648ad913 100644 --- a/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts +++ b/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getSentryRequest } from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; sentryTest('should set primitive tags', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getSentryRequest(page, url); + const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('primitive_tags'); expect(eventData.tags).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts b/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts index 045b79597b72..2aed7beb60aa 100644 --- a/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts +++ b/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryRequests } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should unset user', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getMultipleSentryRequests(page, 3, url); + const eventData = await getMultipleSentryEnvelopeRequests(page, 3, { url }); expect(eventData[0].message).toBe('no_user'); expect(eventData[0].user).toBeUndefined(); diff --git a/packages/integration-tests/suites/public-api/setUser/update_user/test.ts b/packages/integration-tests/suites/public-api/setUser/update_user/test.ts index 1520655c8363..fa846f0221c2 100644 --- a/packages/integration-tests/suites/public-api/setUser/update_user/test.ts +++ b/packages/integration-tests/suites/public-api/setUser/update_user/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryRequests } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should update user', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getMultipleSentryRequests(page, 2, url); + const eventData = await getMultipleSentryEnvelopeRequests(page, 2, { url }); expect(eventData[0].message).toBe('first_user'); expect(eventData[0].user).toMatchObject({ diff --git a/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts b/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts index 7175eb10ae52..1cc024e799fc 100644 --- a/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts +++ b/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts @@ -1,12 +1,13 @@ import { expect } from '@playwright/test'; +import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryRequests } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should allow nested scoping', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getMultipleSentryRequests(page, 5, url); + const eventData = await getMultipleSentryEnvelopeRequests(page, 5, { url }); expect(eventData[0].message).toBe('root_before'); expect(eventData[0].user).toMatchObject({ id: 'qux' }); diff --git a/packages/integration-tests/suites/tracing/request/fetch/test.ts b/packages/integration-tests/suites/tracing/request/fetch/test.ts index 77db6f8c18aa..88bbeaf2c00d 100644 --- a/packages/integration-tests/suites/tracing/request/fetch/test.ts +++ b/packages/integration-tests/suites/tracing/request/fetch/test.ts @@ -2,24 +2,36 @@ import { expect, Request } from '@playwright/test'; import { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; sentryTest('should create spans for multiple fetch requests', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const requestSpans = eventData.spans?.filter(({ op }) => op === 'http.client'); + // Because we fetch from http://example.com, fetch will throw a CORS error in firefox and webkit. + // Chromium does not throw for cors errors. + // This means that we will intercept a dynamic amount of envelopes here. + + // We will wait 500ms for all envelopes to be sent. Generally, in all browsers, the last sent + // envelope contains tracing data. + + // If we are on FF or webkit: + // 1st envelope contains CORS error + // 2nd envelope contains the tracing data we want to check here + const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url, timeout: 10000 }); + const tracingEvent = envelopes[envelopes.length - 1]; // last envelope contains tracing data on all browsers + + const requestSpans = tracingEvent.spans?.filter(({ op }) => op === 'http.client'); expect(requestSpans).toHaveLength(3); requestSpans?.forEach((span, index) => expect(span).toMatchObject({ description: `GET http://example.com/${index}`, - parent_span_id: eventData.contexts?.trace.span_id, + parent_span_id: tracingEvent.contexts?.trace.span_id, span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), - trace_id: eventData.contexts?.trace.trace_id, + trace_id: tracingEvent.contexts?.trace.trace_id, }), ); }); diff --git a/packages/integration-tests/utils/helpers.ts b/packages/integration-tests/utils/helpers.ts index 5218dc02c27c..8f6e06b97d5a 100644 --- a/packages/integration-tests/utils/helpers.ts +++ b/packages/integration-tests/utils/helpers.ts @@ -1,10 +1,8 @@ import { Page, Request } from '@playwright/test'; import { Event } from '@sentry/types'; -const storeUrlRegex = /\.sentry\.io\/api\/\d+\/store\//; const envelopeUrlRegex = /\.sentry\.io\/api\/\d+\/envelope\//; -const storeRequestParser = (request: Request | null): Event => JSON.parse((request && request.postData()) || ''); const envelopeRequestParser = (request: Request | null): Event => { // https://develop.sentry.dev/sdk/envelopes/ const envelope = request?.postData() || ''; @@ -24,17 +22,6 @@ async function runScriptInSandbox(page: Page, path: string): Promise { await page.addScriptTag({ path }); } -/** - * Wait and get Sentry's request sending the event at the given URL, or the current page - * - * @param {Page} page - * @param {string} [url] - * @return {*} {Promise} - */ -async function getSentryRequest(page: Page, url?: string): Promise { - return (await getMultipleSentryRequests(page, 1, url))[0]; -} - /** * Get Sentry events at the given URL, or the current page. * @@ -52,27 +39,26 @@ async function getSentryEvents(page: Page, url?: string): Promise> } /** - * Wait and get multiple requests matching urlRgx at the given URL, or the current page - * - * @param {Page} page - * @param {number} count - * @param {RegExp} urlRgx - * @param {(req: Request) => Event} requestParser - * @param {string} [url] - * @return {*} {Promise} + * Waits until a number of requests matching urlRgx at the given URL arrive. + * If the timout option is configured, this function will abort waiting, even if it hasn't reveived the configured + * amount of requests, and returns all the events recieved up to that point in time. */ async function getMultipleRequests( page: Page, count: number, urlRgx: RegExp, requestParser: (req: Request) => Event, - url?: string, + options?: { + url?: string; + timeout?: number; + }, ): Promise { const requests: Promise = new Promise((resolve, reject) => { let reqCount = count; const requestData: Event[] = []; + let timeoutId: NodeJS.Timeout | undefined = undefined; - page.on('request', request => { + function requestHandler(request: Request): void { if (urlRgx.test(request.url())) { try { reqCount -= 1; @@ -98,47 +84,48 @@ async function getMultipleRequests( // requestData.push(requestParser(request)); if (reqCount === 0) { + if (timeoutId) { + clearTimeout(timeoutId); + } + page.off('request', requestHandler); resolve(requestData); } } catch (err) { reject(err); } } - }); + } + + page.on('request', requestHandler); + + if (options?.timeout) { + timeoutId = setTimeout(() => { + resolve(requestData); + }, options.timeout); + } }); - if (url) { - await page.goto(url); + if (options?.url) { + await page.goto(options.url); } return requests; } -/** - * Wait and get multiple event requests at the given URL, or the current page - * - * @param {Page} page - * @param {number} count - * @param {string} [url] - * @return {*} {Promise} - */ -async function getMultipleSentryRequests(page: Page, count: number, url?: string): Promise { - return getMultipleRequests(page, count, storeUrlRegex, storeRequestParser, url); -} - /** * Wait and get multiple envelope requests at the given URL, or the current page - * - * @template T - * @param {Page} page - * @param {number} count - * @param {string} [url] - * @return {*} {Promise} */ -async function getMultipleSentryEnvelopeRequests(page: Page, count: number, url?: string): Promise { +async function getMultipleSentryEnvelopeRequests( + page: Page, + count: number, + options?: { + url?: string; + timeout?: number; + }, +): Promise { // TODO: This is not currently checking the type of envelope, just casting for now. // We can update this to include optional type-guarding when we have types for Envelope. - return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, url) as Promise; + return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, options) as Promise; } /** @@ -150,7 +137,7 @@ async function getMultipleSentryEnvelopeRequests(page: Page, count: number, u * @return {*} {Promise} */ async function getFirstSentryEnvelopeRequest(page: Page, url?: string): Promise { - return (await getMultipleSentryEnvelopeRequests(page, 1, url))[0]; + return (await getMultipleSentryEnvelopeRequests(page, 1, { url }))[0]; } /** @@ -172,10 +159,8 @@ async function injectScriptAndGetEvents(page: Page, url: string, scriptPath: str export { runScriptInSandbox, - getMultipleSentryRequests, getMultipleSentryEnvelopeRequests, getFirstSentryEnvelopeRequest, - getSentryRequest, getSentryEvents, injectScriptAndGetEvents, }; diff --git a/packages/nextjs/test/index.client.test.ts b/packages/nextjs/test/index.client.test.ts index 44edf997ef50..5d7053c00def 100644 --- a/packages/nextjs/test/index.client.test.ts +++ b/packages/nextjs/test/index.client.test.ts @@ -68,12 +68,12 @@ describe('Client init()', () => { tracesSampleRate: 1.0, }); const hub = getCurrentHub(); - const sendEvent = jest.spyOn(hub.getClient()!.getTransport!(), 'sendEvent'); + const transportSend = jest.spyOn(hub.getClient()!.getTransport()!, 'send'); const transaction = hub.startTransaction({ name: '/404' }); transaction.finish(); - expect(sendEvent).not.toHaveBeenCalled(); + expect(transportSend).not.toHaveBeenCalled(); expect(captureEvent.mock.results[0].value).toBeUndefined(); expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.')); }); diff --git a/packages/nextjs/test/index.server.test.ts b/packages/nextjs/test/index.server.test.ts index e4e359a10624..477ad252cd29 100644 --- a/packages/nextjs/test/index.server.test.ts +++ b/packages/nextjs/test/index.server.test.ts @@ -95,7 +95,7 @@ describe('Server init()', () => { tracesSampleRate: 1.0, }); const hub = getCurrentHub(); - const sendEvent = jest.spyOn(hub.getClient()!.getTransport!(), 'sendEvent'); + const transportSend = jest.spyOn(hub.getClient()!.getTransport()!, 'send'); const transaction = hub.startTransaction({ name: '/404' }); transaction.finish(); @@ -103,7 +103,7 @@ describe('Server init()', () => { // We need to flush because the event processor pipeline is async whereas transaction.finish() is sync. await SentryNode.flush(); - expect(sendEvent).not.toHaveBeenCalled(); + expect(transportSend).not.toHaveBeenCalled(); expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.')); }); diff --git a/packages/nextjs/test/integration/test/utils/client.js b/packages/nextjs/test/integration/test/utils/client.js index d2c11837db3d..76d88832c863 100644 --- a/packages/nextjs/test/integration/test/utils/client.js +++ b/packages/nextjs/test/integration/test/utils/client.js @@ -20,7 +20,7 @@ const createRequestInterceptor = env => { } if (isEventRequest(request)) { - logIf(process.env.LOG_REQUESTS, 'Intercepted Event', extractEventFromRequest(request), env.argv.depth); + logIf(process.env.LOG_REQUESTS, 'Intercepted Event', extractEnvelopeFromRequest(request), env.argv.depth); env.requests.events.push(request); } else if (isSessionRequest(request)) { logIf(process.env.LOG_REQUESTS, 'Intercepted Session', extractEnvelopeFromRequest(request), env.argv.depth); @@ -38,14 +38,14 @@ const isSentryRequest = request => { return /sentry.io\/api/.test(request.url()); }; -const isEventRequest = request => { - return /sentry.io\/api\/\d+\/store/.test(request.url()); -}; - const isEnvelopeRequest = request => { return /sentry.io\/api\/\d+\/envelope/.test(request.url()); }; +const isEventRequest = request => { + return isEnvelopeRequest(request) && extractEnvelopeFromRequest(request).itemHeader.type === 'event'; +}; + const isSessionRequest = request => { return isEnvelopeRequest(request) && extractEnvelopeFromRequest(request).itemHeader.type === 'session'; }; @@ -54,21 +54,25 @@ const isTransactionRequest = request => { return isEnvelopeRequest(request) && extractEnvelopeFromRequest(request).itemHeader.type === 'transaction'; }; -const expectEvent = (request, expectedEvent) => { +const expectEvent = (request, expectedItem) => { if (!request) throw new Error('Event missing'); - return assertObjectMatches(extractEventFromRequest(request), expectedEvent); + const { itemHeader, item } = extractEnvelopeFromRequest(request); + strictEqual(itemHeader.type, 'event'); + assertObjectMatches(item, expectedItem); }; const expectSession = (request, expectedItem) => { if (!request) throw new Error('Session missing'); const { itemHeader, item } = extractEnvelopeFromRequest(request); - return itemHeader.type === 'session' && assertObjectMatches(item, expectedItem); + strictEqual(itemHeader.type, 'session'); + assertObjectMatches(item, expectedItem); }; const expectTransaction = (request, expectedItem) => { if (!request) throw new Error('Transaction missing'); const { itemHeader, item } = extractEnvelopeFromRequest(request); - return itemHeader.type === 'transaction' && assertObjectMatches(item, expectedItem); + strictEqual(itemHeader.type, 'transaction'); + assertObjectMatches(item, expectedItem); }; const expectRequestCount = (requests, expectedCount, timeout = 100) => { @@ -89,10 +93,6 @@ const expectRequestCount = (requests, expectedCount, timeout = 100) => { }); }; -const extractEventFromRequest = request => { - return JSON.parse(request.postData()); -}; - const extractEnvelopeFromRequest = request => { return parseEnvelope(request.postData()); }; @@ -111,8 +111,6 @@ const assertObjectMatches = (actual, expected) => { strictEqual(actual[key], expectedValue); } } - - return true; }; module.exports = { @@ -122,7 +120,6 @@ module.exports = { expectSession, expectTransaction, extractEnvelopeFromRequest, - extractEventFromRequest, isEnvelopeRequest, isEventRequest, isSentryRequest, diff --git a/packages/nextjs/test/integration/test/utils/server.js b/packages/nextjs/test/integration/test/utils/server.js index 8844c8cc1799..8c38446e3f41 100644 --- a/packages/nextjs/test/integration/test/utils/server.js +++ b/packages/nextjs/test/integration/test/utils/server.js @@ -35,15 +35,17 @@ const getAsync = (url, rewrap = false) => { const interceptEventRequest = (expectedEvent, argv, testName = '') => { return nock('https://dsn.ingest.sentry.io') - .post('/api/1337/store/', body => { + .post('/api/1337/envelope/', body => { + const { envelopeHeader, itemHeader, item } = parseEnvelope(body); logIf( process.env.LOG_REQUESTS, '\nIntercepted Event' + (testName.length ? ` (from test \`${testName}\`)` : ''), - body, + { envelopeHeader, itemHeader, item }, argv.depth, ); - return objectMatches(body, expectedEvent); + return itemHeader.type === 'event' && objectMatches(item, expectedEvent); }) + .query(true) // accept any query params - used for sentry_key param used by the envelope endpoint .reply(200); }; @@ -59,6 +61,7 @@ const interceptSessionRequest = (expectedItem, argv, testName = '') => { ); return itemHeader.type === 'session' && objectMatches(item, expectedItem); }) + .query(true) // accept any query params - used for sentry_key param used by the envelope endpoint .reply(200); }; @@ -74,6 +77,7 @@ const interceptTracingRequest = (expectedItem, argv, testName = '') => { ); return itemHeader.type === 'transaction' && objectMatches(item, expectedItem); }) + .query(true) // accept any query params - used for sentry_key param used by the envelope endpoint .reply(200); }; diff --git a/packages/node-integration-tests/suites/express/handle-error/test.ts b/packages/node-integration-tests/suites/express/handle-error/test.ts index 66cc1ec2d9ae..9ae4586f6510 100644 --- a/packages/node-integration-tests/suites/express/handle-error/test.ts +++ b/packages/node-integration-tests/suites/express/handle-error/test.ts @@ -1,12 +1,12 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../utils/index'; +import { assertSentryEvent, getEnvelopeRequest, runServer } from '../../../utils/index'; test('should capture and send Express controller error.', async () => { const url = await runServer(__dirname, `${__dirname}/server.ts`); - const event = await getEventRequest(`${url}/express`); + const event = await getEnvelopeRequest(`${url}/express`); - expect((event as any).exception.values[0].stacktrace.frames.length).toBeGreaterThan(0); + expect((event[2] as any).exception.values[0].stacktrace.frames.length).toBeGreaterThan(0); - assertSentryEvent(event, { + assertSentryEvent(event[2] as any, { exception: { values: [ { diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts index dd9a8ca4fe87..be9c0b6c15b6 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts @@ -1,10 +1,13 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should add an empty breadcrumb, when an empty object is given', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + expect(errorEnvelope).toHaveLength(3); + + assertSentryEvent(errorEnvelope[2], { message: 'test-empty-obj', }); }); diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts index b643a187bc6f..c8b894a62102 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts @@ -1,10 +1,10 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should add multiple breadcrumbs', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); - assertSentryEvent(requestBody, { + assertSentryEvent(envelopes[1][2], { message: 'test_multi_breadcrumbs', breadcrumbs: [ { diff --git a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts index 8bc0ee26fea2..a56f0711e086 100644 --- a/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts +++ b/packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts @@ -1,10 +1,10 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should add a simple breadcrumb', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); - assertSentryEvent(requestBody, { + assertSentryEvent(envelopes[1][2], { message: 'test_simple', breadcrumbs: [ { diff --git a/packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts b/packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts index f6fee1d8b819..2070199f77d9 100644 --- a/packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should work inside catch block', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { exception: { values: [ { diff --git a/packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts b/packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts index fb6ca293a27f..0df21996f08c 100644 --- a/packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should capture an empty object', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { exception: { values: [ { diff --git a/packages/node-integration-tests/suites/public-api/captureException/new-transport/scenario.ts b/packages/node-integration-tests/suites/public-api/captureException/new-transport/scenario.ts deleted file mode 100644 index a03dea7cbce5..000000000000 --- a/packages/node-integration-tests/suites/public-api/captureException/new-transport/scenario.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - _experiments: { - newTransport: true, // use new transport - }, -}); - -Sentry.captureException(new Error('test_simple_error')); diff --git a/packages/node-integration-tests/suites/public-api/captureException/new-transport/test.ts b/packages/node-integration-tests/suites/public-api/captureException/new-transport/test.ts deleted file mode 100644 index 0424ac121dcd..000000000000 --- a/packages/node-integration-tests/suites/public-api/captureException/new-transport/test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { getMultipleEnvelopeRequest, runServer } from '../../../../utils'; - -test('should correctly send envelope', async () => { - const url = await runServer(__dirname); - const envelopes = await getMultipleEnvelopeRequest(url, 2); - - const errorEnvelope = envelopes[1]; - - expect(errorEnvelope).toHaveLength(3); - expect(errorEnvelope[2]).toMatchObject({ - exception: { - values: [ - { - type: 'Error', - value: 'test_simple_error', - }, - ], - }, - release: '1.0', - event_id: expect.any(String), - timestamp: expect.any(Number), - }); -}); diff --git a/packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts b/packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts index 8553cb1be0d7..66ca0410377a 100644 --- a/packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should capture a simple error with message', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { exception: { values: [ { diff --git a/packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts b/packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts index 69fa8c27764b..2ecc0e86720d 100644 --- a/packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should capture a simple message string', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'Message', level: 'info', }); diff --git a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts index e43c13db3711..5db1ef646168 100644 --- a/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts +++ b/packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts @@ -1,40 +1,40 @@ -import { assertSentryEvent, getMultipleEventRequests, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should capture with different severity levels', async () => { const url = await runServer(__dirname); - const events = await getMultipleEventRequests(url, 7); + const envelopes = await getMultipleEnvelopeRequest(url, 14); - assertSentryEvent(events[0], { + assertSentryEvent(envelopes[1][2], { message: 'debug_message', level: 'debug', }); - assertSentryEvent(events[1], { + assertSentryEvent(envelopes[3][2], { message: 'info_message', level: 'info', }); - assertSentryEvent(events[2], { + assertSentryEvent(envelopes[5][2], { message: 'warning_message', level: 'warning', }); - assertSentryEvent(events[3], { + assertSentryEvent(envelopes[7][2], { message: 'error_message', level: 'error', }); - assertSentryEvent(events[4], { + assertSentryEvent(envelopes[9][2], { message: 'fatal_message', level: 'fatal', }); - assertSentryEvent(events[5], { + assertSentryEvent(envelopes[11][2], { message: 'critical_message', level: 'critical', }); - assertSentryEvent(events[6], { + assertSentryEvent(envelopes[13][2], { message: 'log_message', level: 'log', }); diff --git a/packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts b/packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts index 97437a4e7e90..e1cf213e2e4c 100644 --- a/packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts +++ b/packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts @@ -1,16 +1,16 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getEnvelopeRequest, runServer } from '../../../../utils'; test('should clear previously set properties of a scope', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelope = await getEnvelopeRequest(url); - assertSentryEvent(requestBody, { + assertSentryEvent(envelope[2], { message: 'cleared_scope', tags: {}, extra: {}, }); - expect((requestBody as Event).user).not.toBeDefined(); + expect((envelope[2] as Event).user).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts b/packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts index 6e482197470c..17d7c9b8df42 100644 --- a/packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts +++ b/packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set different properties of a scope', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'configured_scope', tags: { foo: 'bar', diff --git a/packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts b/packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts index 46d3de6bc2bf..f1c8981fad9a 100644 --- a/packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts +++ b/packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts @@ -1,12 +1,13 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should record multiple contexts', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'multiple_contexts', contexts: { context_1: { @@ -17,5 +18,5 @@ test('should record multiple contexts', async () => { }, }); - expect((requestBody as Event).contexts?.context_3).not.toBeDefined(); + expect((errorEnvelope[2] as Event).contexts?.context_3).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts b/packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts index 2ea859de480c..26b5fe8c7025 100644 --- a/packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts +++ b/packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts @@ -1,15 +1,16 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should normalize non-serializable context', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'non_serializable', contexts: {}, }); - expect((requestBody as Event).contexts?.context_3).not.toBeDefined(); + expect((errorEnvelope[2] as Event).contexts?.context_3).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts b/packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts index cdabe34d8680..362afb9e55e4 100644 --- a/packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts +++ b/packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts @@ -1,12 +1,13 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set a simple context', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'simple_context_object', contexts: { foo: { @@ -15,5 +16,5 @@ test('should set a simple context', async () => { }, }); - expect((requestBody as Event).contexts?.context_3).not.toBeDefined(); + expect((errorEnvelope[2] as Event).contexts?.context_3).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts b/packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts index 0bc530b13cef..428ebd7f45c4 100644 --- a/packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should record multiple extras of different types', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'multiple_extras', extra: { extra_1: { foo: 'bar', baz: { qux: 'quux' } }, diff --git a/packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts b/packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts index 24005031781b..3cd2ca078eeb 100644 --- a/packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should normalize non-serializable extra', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'non_serializable', extra: {}, }); diff --git a/packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts b/packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts index 23c0cbc6ad42..33bfe641bfa3 100644 --- a/packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set a simple extra', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'simple_extra', extra: { foo: { diff --git a/packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts b/packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts index 5126cde1a58f..464324c97fdf 100644 --- a/packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set extras from multiple consecutive calls', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'consecutive_calls', extra: { extra: [], Infinity: 2, null: 0, obj: { foo: ['bar', 'baz', 1] } }, }); diff --git a/packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts b/packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts index f1c653307e45..d23c8e815a06 100644 --- a/packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts +++ b/packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should record an extras object', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const errorEnvelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(errorEnvelope[2], { message: 'multiple_extras', extra: { extra_1: [1, ['foo'], 'bar'], diff --git a/packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts b/packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts index c6b94aca64d1..88d78b70c655 100644 --- a/packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts +++ b/packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set primitive tags', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const envelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(envelope[2], { message: 'primitive_tags', tags: { tag_1: 'foo', diff --git a/packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts b/packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts index c6b94aca64d1..88d78b70c655 100644 --- a/packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts +++ b/packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts @@ -1,10 +1,11 @@ -import { assertSentryEvent, getEventRequest, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should set primitive tags', async () => { const url = await runServer(__dirname); - const requestBody = await getEventRequest(url); + const envelopes = await getMultipleEnvelopeRequest(url, 2); + const envelope = envelopes[1]; - assertSentryEvent(requestBody, { + assertSentryEvent(envelope[2], { message: 'primitive_tags', tags: { tag_1: 'foo', diff --git a/packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts b/packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts index 8c804f3864b8..736ed1fdf38c 100644 --- a/packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts +++ b/packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts @@ -1,18 +1,18 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getMultipleEventRequests, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should unset user', async () => { const url = await runServer(__dirname); - const events = await getMultipleEventRequests(url, 3); + const envelopes = await getMultipleEnvelopeRequest(url, 6); - assertSentryEvent(events[0], { + assertSentryEvent(envelopes[1][2], { message: 'no_user', }); - expect((events[0] as Event).user).not.toBeDefined(); + expect((envelopes[0][2] as Event).user).not.toBeDefined(); - assertSentryEvent(events[1], { + assertSentryEvent(envelopes[3][2], { message: 'user', user: { id: 'foo', @@ -21,9 +21,9 @@ test('should unset user', async () => { }, }); - assertSentryEvent(events[2], { + assertSentryEvent(envelopes[5][2], { message: 'unset_user', }); - expect((events[2] as Event).user).not.toBeDefined(); + expect((envelopes[2][2] as Event).user).not.toBeDefined(); }); diff --git a/packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts b/packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts index 3f84a390cd05..27c7de89fe45 100644 --- a/packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts +++ b/packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts @@ -1,10 +1,10 @@ -import { assertSentryEvent, getMultipleEventRequests, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should update user', async () => { const url = await runServer(__dirname); - const events = await getMultipleEventRequests(url, 2); + const envelopes = await getMultipleEnvelopeRequest(url, 4); - assertSentryEvent(events[0], { + assertSentryEvent(envelopes[1][2], { message: 'first_user', user: { id: 'foo', @@ -12,7 +12,7 @@ test('should update user', async () => { }, }); - assertSentryEvent(events[1], { + assertSentryEvent(envelopes[3][2], { message: 'second_user', user: { id: 'baz', diff --git a/packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts b/packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts index 03a219470b0f..57047696cf0c 100644 --- a/packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts +++ b/packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts @@ -1,12 +1,12 @@ import { Event } from '@sentry/node'; -import { assertSentryEvent, getMultipleEventRequests, runServer } from '../../../../utils'; +import { assertSentryEvent, getMultipleEnvelopeRequest, runServer } from '../../../../utils'; test('should allow nested scoping', async () => { const url = await runServer(__dirname); - const events = await getMultipleEventRequests(url, 5); + const envelopes = await getMultipleEnvelopeRequest(url, 10); - assertSentryEvent(events[0], { + assertSentryEvent(envelopes[1][2], { message: 'root_before', user: { id: 'qux', @@ -14,7 +14,7 @@ test('should allow nested scoping', async () => { tags: {}, }); - assertSentryEvent(events[1], { + assertSentryEvent(envelopes[3][2], { message: 'outer_before', user: { id: 'qux', @@ -24,7 +24,7 @@ test('should allow nested scoping', async () => { }, }); - assertSentryEvent(events[2], { + assertSentryEvent(envelopes[5][2], { message: 'inner', tags: { foo: false, @@ -32,9 +32,9 @@ test('should allow nested scoping', async () => { }, }); - expect((events[2] as Event).user).toBeUndefined(); + expect((envelopes[4][2] as Event).user).toBeUndefined(); - assertSentryEvent(events[3], { + assertSentryEvent(envelopes[7][2], { message: 'outer_after', user: { id: 'baz', @@ -44,7 +44,7 @@ test('should allow nested scoping', async () => { }, }); - assertSentryEvent(events[4], { + assertSentryEvent(envelopes[9][2], { message: 'root_after', user: { id: 'qux', diff --git a/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts b/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts index 28ce0ba822c0..13051fd9c47a 100644 --- a/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts +++ b/packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts @@ -1,17 +1,17 @@ import path from 'path'; -import { getEnvelopeRequest, runServer } from '../../../utils'; +import { getMultipleEnvelopeRequest, runServer } from '../../../utils'; test('should aggregate successful and crashed sessions', async () => { const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); - const envelope = await Promise.race([ - getEnvelopeRequest(`${url}/success`), - getEnvelopeRequest(`${url}/error_unhandled`), - getEnvelopeRequest(`${url}/success_next`), + const envelopes = await Promise.race([ + getMultipleEnvelopeRequest(`${url}/success`, 2), + getMultipleEnvelopeRequest(`${url}/error_unhandled`, 2), + getMultipleEnvelopeRequest(`${url}/success_next`, 2), ]); + const envelope = envelopes[1]; - expect(envelope).toHaveLength(3); expect(envelope[0]).toMatchObject({ sent_at: expect.any(String), sdk: { diff --git a/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts b/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts index ad1cf46f2704..0f3130cd9b09 100644 --- a/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts +++ b/packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts @@ -1,18 +1,19 @@ import path from 'path'; -import { getEnvelopeRequest, runServer } from '../../../utils'; +import { getMultipleEnvelopeRequest, runServer } from '../../../utils'; test('should aggregate successful, crashed and erroneous sessions', async () => { const url = await runServer(__dirname, `${path.resolve(__dirname, '..')}/server.ts`); - const envelope = await Promise.race([ - getEnvelopeRequest(`${url}/success`), - getEnvelopeRequest(`${url}/error_handled`), - getEnvelopeRequest(`${url}/error_unhandled`), + const envelopes = await Promise.race([ + getMultipleEnvelopeRequest(`${url}/success`, 3), + getMultipleEnvelopeRequest(`${url}/error_handled`, 3), + getMultipleEnvelopeRequest(`${url}/error_unhandled`, 3), ]); - expect(envelope).toHaveLength(3); - expect(envelope[0]).toMatchObject({ + expect(envelopes).toHaveLength(3); + const aggregateSessionEnvelope = envelopes[2]; + expect(aggregateSessionEnvelope[0]).toMatchObject({ sent_at: expect.any(String), sdk: { name: 'sentry.javascript.node', @@ -20,11 +21,11 @@ test('should aggregate successful, crashed and erroneous sessions', async () => }, }); - expect(envelope[1]).toMatchObject({ + expect(aggregateSessionEnvelope[1]).toMatchObject({ type: 'sessions', }); - expect(envelope[2]).toMatchObject({ + expect(aggregateSessionEnvelope[2]).toMatchObject({ aggregates: [ { started: expect.any(String), diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index d652f63185b0..fba8bb7d52de 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -65,42 +65,6 @@ export const parseEnvelope = (body: string): Array> => { return body.split('\n').map(e => JSON.parse(e)); }; -/** - * Intercepts and extracts multiple requests containing a Sentry Event - * - * @param {string} url - * @param {number} count - * @return {*} {Promise>>} - */ -export const getMultipleEventRequests = async (url: string, count: number): Promise>> => { - const events: Record[] = []; - - return new Promise(resolve => { - nock('https://dsn.ingest.sentry.io') - .post('/api/1337/store/', body => { - events.push(body); - - if (events.length === count) { - resolve(events); - } - return true; - }) - .times(7) - .reply(200); - http.get(url); - }); -}; - -/** - * Intercepts and extracts a single request containing a Sentry Event - * - * @param {string} url - * @return {*} {Promise>} - */ -export const getEventRequest = async (url: string): Promise> => { - return (await getMultipleEventRequests(url, 1))[0]; -}; - /** * Intercepts and extracts up to a number of requests containing Sentry envelopes. * diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 4ba91d0db9a2..e6a0fc7a43a0 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,6 +1,6 @@ -import { BaseClient, NewTransport, Scope, SDK_VERSION } from '@sentry/core'; +import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; -import { Event, EventHint, Severity, SeverityLevel, Transport } from '@sentry/types'; +import { Event, EventHint, Severity, SeverityLevel } from '@sentry/types'; import { logger, resolvedSyncPromise } from '@sentry/utils'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; @@ -20,7 +20,7 @@ export class NodeClient extends BaseClient { * Creates a new Node SDK instance. * @param options Configuration options for this SDK. */ - public constructor(options: NodeClientOptions, transport: Transport, newTransport?: NewTransport) { + public constructor(options: NodeClientOptions) { options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || { name: 'sentry.javascript.node', @@ -33,7 +33,7 @@ export class NodeClient extends BaseClient { version: SDK_VERSION, }; - super(options, transport, newTransport); + super(options); } /** @@ -99,7 +99,7 @@ export class NodeClient extends BaseClient { if (!release) { IS_DEBUG_BUILD && 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, { release, environment, }); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 0e8febd8a92a..ec659b543bff 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -26,6 +26,7 @@ export { captureEvent, captureMessage, configureScope, + createTransport, getHubFromCarrier, getCurrentHub, Hub, diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 4b1e503e851a..5658257776b1 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -8,7 +8,7 @@ import { NodeClient } from './client'; import { IS_DEBUG_BUILD } from './flags'; import { Console, ContextLines, Http, LinkedErrors, OnUncaughtException, OnUnhandledRejection } from './integrations'; import { nodeStackParser } from './stack-parser'; -import { HTTPSTransport, HTTPTransport, setupNodeTransport } from './transports'; +import { makeNodeTransport } from './transports'; import { NodeClientOptions, NodeOptions } from './types'; export const defaultIntegrations = [ @@ -127,18 +127,15 @@ export function init(options: NodeOptions = {}): void { setHubOnCarrier(carrier, getCurrentHub()); } - const { transport, newTransport } = setupNodeTransport(options); - // TODO(v7): Refactor this to reduce the logic above const clientOptions: NodeClientOptions = { ...options, stackParser: stackParserFromOptions(options.stackParser || [nodeStackParser]), integrations: getIntegrationsToSetup(options), - // TODO(v7): Fix me when we switch to new transports entirely. - transport: options.transport || (transport instanceof HTTPTransport ? HTTPTransport : HTTPSTransport), + transport: options.transport || makeNodeTransport, }; - initAndBind(NodeClient, clientOptions, transport, newTransport); + initAndBind(NodeClient, clientOptions); if (options.autoSessionTracking) { startSessionTracking(); diff --git a/packages/node/src/transports/index.ts b/packages/node/src/transports/index.ts index 01877029269e..958562933321 100644 --- a/packages/node/src/transports/index.ts +++ b/packages/node/src/transports/index.ts @@ -4,4 +4,3 @@ export { BaseTransport } from './base'; export { HTTPTransport } from './http'; export { HTTPSTransport } from './https'; export { makeNodeTransport } from './new'; -export { setupNodeTransport } from './setup'; diff --git a/packages/node/src/transports/new.ts b/packages/node/src/transports/new.ts index b30dc3ffe36f..e31a58b03b7b 100644 --- a/packages/node/src/transports/new.ts +++ b/packages/node/src/transports/new.ts @@ -1,11 +1,11 @@ +import { createTransport } from '@sentry/core'; import { BaseTransportOptions, - createTransport, NewTransport, TransportMakeRequestResponse, TransportRequest, TransportRequestExecutor, -} from '@sentry/core'; +} from '@sentry/types'; import { eventStatusFromHttpCode } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; @@ -91,7 +91,6 @@ function createRequestExecutor( agent: http.Agent, ): TransportRequestExecutor { const { hostname, pathname, port, protocol, search } = new URL(options.url); - return function makeRequest(request: TransportRequest): Promise { return new Promise((resolve, reject) => { const req = httpModule.request( diff --git a/packages/node/src/transports/setup.ts b/packages/node/src/transports/setup.ts deleted file mode 100644 index 2cffd2d43ecd..000000000000 --- a/packages/node/src/transports/setup.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { getEnvelopeEndpointWithUrlEncodedAuth, initAPIDetails, NewTransport, NoopTransport } from '@sentry/core'; -import { Transport, TransportOptions } from '@sentry/types'; -import { makeDsn } from '@sentry/utils'; - -import { NodeOptions } from '../types'; -import { HTTPSTransport, HTTPTransport, makeNodeTransport } from '.'; - -/** - * Sets up Node transport based on the passed `options`. - * - * @returns an object currently still containing both, the old `Transport` and - * `NewTransport` which will eventually replace `Transport`. Once this is replaced, - * this function will return a ready to use `NewTransport`. - */ -// TODO(v7): Adjust return value when NewTransport is the default -export function setupNodeTransport(options: NodeOptions): { transport: Transport; newTransport?: NewTransport } { - if (!options.dsn) { - // We return the noop transport here in case there is no Dsn. - return { transport: new NoopTransport() }; - } - - const dsn = makeDsn(options.dsn); - - const transportOptions: TransportOptions = { - ...options.transportOptions, - ...(options.httpProxy && { httpProxy: options.httpProxy }), - ...(options.httpsProxy && { httpsProxy: options.httpsProxy }), - ...(options.caCerts && { caCerts: options.caCerts }), - dsn: options.dsn, - tunnel: options.tunnel, - _metadata: options._metadata, - }; - - if (options.transport) { - return { transport: new options.transport(transportOptions) }; - } - - const api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel); - const url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel); - - const newTransport = makeNodeTransport({ - url, - headers: transportOptions.headers, - proxy: transportOptions.httpProxy, - caCerts: transportOptions.caCerts, - }); - - if (dsn.protocol === 'http') { - return { transport: new HTTPTransport(transportOptions), newTransport }; - } - return { transport: new HTTPSTransport(transportOptions), newTransport }; -} diff --git a/packages/node/test/client.test.ts b/packages/node/test/client.test.ts index bb3d13ca4122..2aafb8a2544e 100644 --- a/packages/node/test/client.test.ts +++ b/packages/node/test/client.test.ts @@ -1,7 +1,6 @@ import { Scope, SessionFlusher } from '@sentry/hub'; import { NodeClient } from '../src'; -import { setupNodeTransport } from '../src/transports'; import { getDefaultNodeClientOptions } from './helper/node-client-options'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -17,7 +16,7 @@ describe('NodeClient', () => { describe('captureException', () => { test('when autoSessionTracking is enabled, and requestHandler is not used -> requestStatus should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const scope = new Scope(); scope.setRequestSession({ status: 'ok' }); @@ -28,7 +27,7 @@ describe('NodeClient', () => { }); test('when autoSessionTracking is disabled -> requestStatus should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -43,7 +42,7 @@ describe('NodeClient', () => { }); test('when autoSessionTracking is enabled + requestSession status is Crashed -> requestStatus should not be overridden', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -58,7 +57,7 @@ describe('NodeClient', () => { }); test('when autoSessionTracking is enabled + error occurs within request bounds -> requestStatus should be set to Errored', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -73,7 +72,7 @@ describe('NodeClient', () => { }); test('when autoSessionTracking is enabled + error occurs outside of request bounds -> requestStatus should not be set to Errored', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -90,7 +89,7 @@ describe('NodeClient', () => { describe('captureEvent()', () => { test('If autoSessionTracking is disabled, requestSession status should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: false, release: '1.4' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -109,7 +108,7 @@ describe('NodeClient', () => { test('When captureEvent is called with an exception, requestSession status should be set to Errored', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -125,7 +124,7 @@ describe('NodeClient', () => { test('When captureEvent is called without an exception, requestSession status should not be set to Errored', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -141,7 +140,7 @@ describe('NodeClient', () => { test('When captureEvent is called with an exception but outside of a request, then requestStatus should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '2.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -159,7 +158,7 @@ describe('NodeClient', () => { test('When captureEvent is called with a transaction, then requestSession status should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -174,7 +173,7 @@ describe('NodeClient', () => { test('When captureEvent is called with an exception but requestHandler is not used, then requestSession status should not be set', () => { const options = getDefaultNodeClientOptions({ dsn: PUBLIC_DSN, autoSessionTracking: true, release: '1.3' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const scope = new Scope(); scope.setRequestSession({ status: 'ok' }); @@ -198,7 +197,7 @@ describe('flush/close', () => { autoSessionTracking: true, release: '1.1', }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); client.initSessionFlusher(); // Clearing interval is important here to ensure that the flush function later on is called by the `client.close()` // not due to the interval running every 60s diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index b857e1b74bf5..a423a92aa3d0 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -18,7 +18,6 @@ import { tracingHandler, } from '../src/handlers'; import * as SDK from '../src/sdk'; -import { setupNodeTransport } from '../src/transports'; import { getDefaultNodeClientOptions } from './helper/node-client-options'; describe('parseRequest', () => { @@ -226,7 +225,7 @@ describe('requestHandler', () => { it('autoSessionTracking is enabled, sets requestSession status to ok, when handling a request', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -239,7 +238,7 @@ describe('requestHandler', () => { it('autoSessionTracking is disabled, does not set requestSession, when handling a request', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '1.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -252,7 +251,7 @@ describe('requestHandler', () => { it('autoSessionTracking is enabled, calls _captureRequestSession, on response finish', done => { const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -273,7 +272,7 @@ describe('requestHandler', () => { it('autoSessionTracking is disabled, does not call _captureRequestSession, on response finish', done => { const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '1.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const hub = new Hub(client); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -374,7 +373,7 @@ describe('tracingHandler', () => { it('extracts request data for sampling context', () => { const tracesSampler = jest.fn(); const options = getDefaultNodeClientOptions({ tracesSampler }); - const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); + const hub = new Hub(new NodeClient(options)); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on // `@sentry/hub`, and mocking breaks the link between the two jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -397,7 +396,7 @@ describe('tracingHandler', () => { it('puts its transaction on the scope', () => { const options = getDefaultNodeClientOptions({ tracesSampleRate: 1.0 }); - const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); + const hub = new Hub(new NodeClient(options)); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on // `@sentry/hub`, and mocking breaks the link between the two jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); @@ -729,7 +728,7 @@ describe('errorHandler()', () => { }); it('when autoSessionTracking is disabled, does not set requestSession status on Crash', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '3.3' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -749,7 +748,7 @@ describe('errorHandler()', () => { it('autoSessionTracking is enabled + requestHandler is not used -> does not set requestSession status on Crash', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: false, release: '3.3' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); const scope = sentryCore.getCurrentHub().getScope(); const hub = new Hub(client); @@ -766,7 +765,7 @@ describe('errorHandler()', () => { it('when autoSessionTracking is enabled, should set requestSession status to Crashed when an unhandled error occurs within the bounds of a request', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '1.1' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); @@ -785,7 +784,7 @@ describe('errorHandler()', () => { it('when autoSessionTracking is enabled, should not set requestSession status on Crash when it occurs outside the bounds of a request', () => { const options = getDefaultNodeClientOptions({ autoSessionTracking: true, release: '2.2' }); - client = new NodeClient(options, setupNodeTransport(options).transport); + client = new NodeClient(options); // It is required to initialise SessionFlusher to capture Session Aggregates (it is usually initialised // by the`requestHandler`) client.initSessionFlusher(); diff --git a/packages/node/test/helper/node-client-options.ts b/packages/node/test/helper/node-client-options.ts index c2bb1d42a871..a010c18f5811 100644 --- a/packages/node/test/helper/node-client-options.ts +++ b/packages/node/test/helper/node-client-options.ts @@ -1,11 +1,12 @@ -import { NoopTransport } from '@sentry/core'; +import { createTransport } from '@sentry/core'; +import { resolvedSyncPromise } from '@sentry/utils'; import { NodeClientOptions } from '../../src/types'; export function getDefaultNodeClientOptions(options: Partial = {}): NodeClientOptions { return { integrations: [], - transport: NoopTransport, + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), stackParser: () => [], ...options, }; diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 2bdf8497097c..502542a25491 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -18,7 +18,6 @@ import { } from '../src'; import { ContextLines, LinkedErrors } from '../src/integrations'; import { nodeStackParser } from '../src/stack-parser'; -import { setupNodeTransport } from '../src/transports'; import { getDefaultNodeClientOptions } from './helper/node-client-options'; const stackParser = createStackParser(nodeStackParser); @@ -102,7 +101,7 @@ describe('SentryNode', () => { dsn, stackParser, }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); getCurrentHub().bindClient(client); addBreadcrumb({ message: 'test1' }); addBreadcrumb({ message: 'test2' }); @@ -137,7 +136,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -164,7 +163,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -195,7 +194,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); configureScope((scope: Scope) => { scope.setTag('test', '1'); }); @@ -233,7 +232,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); try { throw new Error('test'); } catch (e) { @@ -258,7 +257,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); captureMessage('test'); }); @@ -274,7 +273,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); captureEvent({ message: 'test event' }); }); @@ -291,7 +290,7 @@ describe('SentryNode', () => { }, dsn, }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); d.run(() => { getCurrentHub().bindClient(client); @@ -314,7 +313,7 @@ describe('SentryNode', () => { }, dsn, }); - getCurrentHub().bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + getCurrentHub().bindClient(new NodeClient(options)); try { // @ts-ignore allow function declarations in strict mode // eslint-disable-next-line no-inner-declarations @@ -368,7 +367,7 @@ describe('SentryNode initialization', () => { init({ dsn }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk; expect(sdkData.name).toEqual('sentry.javascript.node'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/node'); @@ -378,10 +377,10 @@ describe('SentryNode initialization', () => { it('should set SDK data when instantiating a client directly', () => { const options = getDefaultNodeClientOptions({ dsn }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (client as any).getTransport()._api.metadata?.sdk; + const sdkData = (client as any).getOptions()._metadata.sdk; expect(sdkData.name).toEqual('sentry.javascript.node'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/node'); @@ -410,7 +409,7 @@ describe('SentryNode initialization', () => { }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const sdkData = (getCurrentHub().getClient() as any).getTransport()._api.metadata?.sdk; + const sdkData = (getCurrentHub().getClient() as any).getOptions()._metadata.sdk; expect(sdkData.name).toEqual('sentry.javascript.serverless'); expect(sdkData.packages[0].name).toEqual('npm:@sentry/serverless'); diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 30a923524a3e..ff0af8b65acf 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -11,7 +11,6 @@ import * as nock from 'nock'; import { Breadcrumb } from '../../src'; import { NodeClient } from '../../src/client'; import { Http as HttpIntegration } from '../../src/integrations/http'; -import { setupNodeTransport } from '../../src/transports'; import { getDefaultNodeClientOptions } from '../helper/node-client-options'; const NODE_VERSION = parseSemver(process.versions.node); @@ -23,7 +22,7 @@ describe('tracing', () => { tracesSampleRate: 1.0, integrations: [new HttpIntegration({ tracing: true })], }); - const hub = new Hub(new NodeClient(options, setupNodeTransport(options).transport)); + const hub = new Hub(new NodeClient(options)); addExtensionMethods(); // we need to mock both of these because the tracing handler relies on `@sentry/core` while the sampler relies on @@ -108,7 +107,7 @@ describe('default protocols', () => { return b; }, }); - hub.bindClient(new NodeClient(options, setupNodeTransport(options).transport)); + hub.bindClient(new NodeClient(options)); return p; } diff --git a/packages/node/test/integrations/linkederrors.test.ts b/packages/node/test/integrations/linkederrors.test.ts index 4d18707be904..6b01faa11614 100644 --- a/packages/node/test/integrations/linkederrors.test.ts +++ b/packages/node/test/integrations/linkederrors.test.ts @@ -4,7 +4,6 @@ import { createStackParser } from '@sentry/utils'; import { Event, NodeClient } from '../../src'; import { LinkedErrors } from '../../src/integrations/linkederrors'; import { nodeStackParser } from '../../src/stack-parser'; -import { setupNodeTransport } from '../../src/transports'; import { getDefaultNodeClientOptions } from '../helper/node-client-options'; const stackParser = createStackParser(nodeStackParser); @@ -34,7 +33,7 @@ describe('LinkedErrors', () => { const spy = jest.spyOn(linkedErrors, '_walkErrorTree'); const one = new Error('originalException'); const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); let event: Event | undefined; return client .eventFromException(one) @@ -58,7 +57,7 @@ describe('LinkedErrors', () => { ); const one = new Error('originalException'); const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -79,7 +78,7 @@ describe('LinkedErrors', () => { two.cause = three; const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -113,7 +112,7 @@ describe('LinkedErrors', () => { two.reason = three; const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { @@ -147,7 +146,7 @@ describe('LinkedErrors', () => { two.cause = three; const options = getDefaultNodeClientOptions({ stackParser }); - const client = new NodeClient(options, setupNodeTransport(options).transport); + const client = new NodeClient(options); return client.eventFromException(one).then(event => linkedErrors ._handler(stackParser, event, { diff --git a/packages/node/test/manual/express-scope-separation/start.js b/packages/node/test/manual/express-scope-separation/start.js index 9146c3ab2eba..689c9ef358e8 100644 --- a/packages/node/test/manual/express-scope-separation/start.js +++ b/packages/node/test/manual/express-scope-separation/start.js @@ -12,8 +12,8 @@ function assertTags(actual, expected) { let remaining = 3; -class DummyTransport { - sendEvent(event) { +function makeDummyTransport() { + return Sentry.createTransport({}, req => { --remaining; if (!remaining) { @@ -23,14 +23,14 @@ class DummyTransport { } return Promise.resolve({ - status: 'success', + statusCode: 200, }); - } + }) } Sentry.init({ dsn: 'http://test@example.com/1337', - transport: DummyTransport, + transport: makeDummyTransport, beforeSend(event) { if (event.transaction === 'GET /foo') { assertTags(event.tags, { diff --git a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js index 0c536e0accae..f02788174ea0 100644 --- a/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js +++ b/packages/node/test/manual/release-health/session-aggregates/aggregates-disable-single-session.js @@ -2,7 +2,7 @@ const http = require('http'); const express = require('express'); const app = express(); const Sentry = require('../../../../build/cjs'); -const { assertSessions, BaseDummyTransport } = require('../test-utils'); +const { assertSessions } = require('../test-utils'); function cleanUpAndExitSuccessfully() { server.close(); @@ -27,9 +27,10 @@ function assertSessionAggregates(session, expected) { assertSessions(session, expected); } -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - assertSessionAggregates(session, { +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + assertSessionAggregates(sessionEnv[2], { attrs: { release: '1.1' }, aggregates: [{ crashed: 2, errored: 1, exited: 1 }], }); @@ -37,15 +38,15 @@ class DummyTransport extends BaseDummyTransport { cleanUpAndExitSuccessfully(); return Promise.resolve({ - status: 'success', + statusCode: 200, }); - } + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); /** diff --git a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js index db052b2bc508..8f5634163f25 100644 --- a/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js +++ b/packages/node/test/manual/release-health/single-session/caught-exception-errored-session.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,36 +12,41 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - sessionCounts.sessionCounter++; - - if (sessionCounts.sessionCounter === 1) { - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'ok', - errors: 1, - release: '1.1', - }); - } +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + + if (sessionCounts.sessionCounter === 1) { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'ok', + errors: 1, + release: '1.1', + }); + } - if (sessionCounts.sessionCounter === 2) { - assertSessions(constructStrippedSessionObject(session), { - init: false, - status: 'exited', - errors: 1, - release: '1.1', - }); + if (sessionCounts.sessionCounter === 2) { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: false, + status: 'exited', + errors: 1, + release: '1.1', + }); + } } - return super.sendSession(session); - } + return Promise.resolve({ + statusCode: 200, + }); + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js index 983f10e9b294..6d230292d180 100644 --- a/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js +++ b/packages/node/test/manual/release-health/single-session/errors-in-session-capped-to-one.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,36 +12,41 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - sessionCounts.sessionCounter++; +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); - if (sessionCounts.sessionCounter === 1) { - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'ok', - errors: 1, - release: '1.1', - }); - } + if (sessionCounts.sessionCounter === 1) { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'ok', + errors: 1, + release: '1.1', + }); + } - if (sessionCounts.sessionCounter === 2) { - assertSessions(constructStrippedSessionObject(session), { - init: false, - status: 'exited', - errors: 1, - release: '1.1', - }); + if (sessionCounts.sessionCounter === 2) { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: false, + status: 'exited', + errors: 1, + release: '1.1', + }); + } } - return super.sendSession(session); - } + return Promise.resolve({ + statusCode: 200, + }); + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); /** diff --git a/packages/node/test/manual/release-health/single-session/healthy-session.js b/packages/node/test/manual/release-health/single-session/healthy-session.js index 1906b8196bf5..cfc3909449ef 100644 --- a/packages/node/test/manual/release-health/single-session/healthy-session.js +++ b/packages/node/test/manual/release-health/single-session/healthy-session.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,25 +12,28 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { +function makeDummyTransport() { + return Sentry.createTransport({}, req => { sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); - assertSessions(constructStrippedSessionObject(session), { + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { init: true, status: 'exited', errors: 0, - release: '1.1', + release: '1.1' }); - return super.sendSession(session); - } + return Promise.resolve({ + statusCode: 200, + }); + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js index b2692957aa70..d0b8f72f4af4 100644 --- a/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js +++ b/packages/node/test/manual/release-health/single-session/terminal-state-sessions-sent-once.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,25 +12,30 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - sessionCounts.sessionCounter++; - - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'crashed', - errors: 1, - release: '1.1', +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'crashed', + errors: 1, + release: '1.1', + }); + } + + return Promise.resolve({ + statusCode: 200, }); - - return super.sendSession(session); - } + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js index eaf247aa080a..5cee4449c23a 100644 --- a/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/uncaught-exception-crashed-session.js @@ -1,24 +1,29 @@ const Sentry = require('../../../../build/cjs'); -const { assertSessions, constructStrippedSessionObject, BaseDummyTransport } = require('../test-utils'); +const { assertSessions, constructStrippedSessionObject } = require('../test-utils'); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'crashed', - errors: 1, - release: '1.1', - }); +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); + + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'crashed', + errors: 1, + release: '1.1', + }); + } // We need to explicitly exit process early here to allow for 0 exit code process.exit(0); - } + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js index c5a1874024ba..d94b6add0c00 100644 --- a/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js +++ b/packages/node/test/manual/release-health/single-session/unhandled-rejection-crashed-session.js @@ -2,7 +2,6 @@ const Sentry = require('../../../../build/cjs'); const { assertSessions, constructStrippedSessionObject, - BaseDummyTransport, validateSessionCountFunction, } = require('../test-utils'); @@ -13,25 +12,30 @@ const sessionCounts = { validateSessionCountFunction(sessionCounts); -class DummyTransport extends BaseDummyTransport { - sendSession(session) { - sessionCounts.sessionCounter++; +function makeDummyTransport() { + return Sentry.createTransport({}, req => { + if (req.category === 'session') { + sessionCounts.sessionCounter++; + const sessionEnv = req.body.split('\n').map(e => JSON.parse(e)); - assertSessions(constructStrippedSessionObject(session), { - init: true, - status: 'crashed', - errors: 1, - release: '1.1', - }); + assertSessions(constructStrippedSessionObject(sessionEnv[2]), { + init: true, + status: 'crashed', + errors: 1, + release: '1.1', + }); + } - return super.sendSession(session); - } + return Promise.resolve({ + statusCode: 200, + }); + }) } Sentry.init({ dsn: 'http://test@example.com/1337', release: '1.1', - transport: DummyTransport, + transport: makeDummyTransport, autoSessionTracking: true, }); diff --git a/packages/node/test/manual/release-health/test-utils.js b/packages/node/test/manual/release-health/test-utils.js index 668923e74dd3..2b3f88a440b9 100644 --- a/packages/node/test/manual/release-health/test-utils.js +++ b/packages/node/test/manual/release-health/test-utils.js @@ -8,26 +8,10 @@ function assertSessions(actual, expected) { } function constructStrippedSessionObject(actual) { - const { init, status, errors, release, did } = actual; + const { init, status, errors, attrs: { release }, did } = actual; return { init, status, errors, release, did }; } -class BaseDummyTransport { - sendEvent(event) { - return Promise.resolve({ - status: 'success', - }); - } - sendSession(session) { - return Promise.resolve({ - status: 'success', - }); - } - close(timeout) { - return Promise.resolve(true); - } -} - function validateSessionCountFunction(sessionCounts) { process.on('exit', () => { const { sessionCounter, expectedSessions } = sessionCounts; @@ -38,4 +22,4 @@ function validateSessionCountFunction(sessionCounts) { }); } -module.exports = { assertSessions, constructStrippedSessionObject, BaseDummyTransport, validateSessionCountFunction }; +module.exports = { assertSessions, constructStrippedSessionObject, validateSessionCountFunction }; diff --git a/packages/node/test/manual/webpack-domain/index.js b/packages/node/test/manual/webpack-domain/index.js index 521699a6349a..b970c95d2a98 100644 --- a/packages/node/test/manual/webpack-domain/index.js +++ b/packages/node/test/manual/webpack-domain/index.js @@ -2,8 +2,8 @@ const Sentry = require('../../../build/cjs'); let remaining = 2; -class DummyTransport { - sendEvent(event) { +function makeDummyTransport() { + return Sentry.createTransport({}, req => { --remaining; if (!remaining) { @@ -14,12 +14,12 @@ class DummyTransport { return Promise.resolve({ status: 'success', }); - } + }) } Sentry.init({ dsn: 'https://a@example.com/1', - transport: DummyTransport, + transport: makeDummyTransport, beforeSend(event) { if (event.message === 'inside') { if (event.tags.a !== 'x' && event.tags.b !== 'c') { diff --git a/packages/node/test/transports/setup.test.ts b/packages/node/test/transports/setup.test.ts deleted file mode 100644 index 38f99a4c95f3..000000000000 --- a/packages/node/test/transports/setup.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { NoopTransport } from '@sentry/core'; -import { FakeTransport } from '@sentry/core/test/mocks/transport'; -import { HTTPSTransport, HTTPTransport, setupNodeTransport } from '@sentry/node/src/transports'; - -import { makeNodeTransport } from '../../src/transports/new'; -import { getDefaultNodeClientOptions } from '../helper/node-client-options'; - -jest.mock('../../src/transports/new', () => { - const original = jest.requireActual('../../src/transports/new'); - return { - ...original, - makeNodeTransport: jest.fn(() => ({ - send: () => Promise.resolve({ status: 'success' }), - flush: () => Promise.resolve(true), - })), - }; -}); - -const DSN = 'https://username@domain/123'; - -describe('setupNodeTransport', () => { - afterEach(() => jest.clearAllMocks()); - - afterAll(() => jest.resetAllMocks()); - - it('returns NoopTransport if no dsn is passed', () => { - const { transport, newTransport } = setupNodeTransport({}); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(NoopTransport); - expect(newTransport).toBeUndefined(); - }); - - it('returns the instantiated transport passed via the options', () => { - const options = getDefaultNodeClientOptions({ dsn: DSN, transport: FakeTransport }); - const { transport, newTransport } = setupNodeTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(FakeTransport); - expect(newTransport).toBeUndefined(); - }); - - it('returns HTTPS transport as a default', () => { - const options = { dsn: DSN }; - const { transport, newTransport } = setupNodeTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(HTTPSTransport); - expect(newTransport).toBeDefined(); - expect(makeNodeTransport).toHaveBeenCalledTimes(1); - }); - - it('returns HTTP transport if specified in the dsn', () => { - const options = { dsn: 'http://username@domain/123' }; - const { transport, newTransport } = setupNodeTransport(options); - - expect(transport).toBeDefined(); - expect(transport).toBeInstanceOf(HTTPTransport); - expect(newTransport).toBeDefined(); - expect(makeNodeTransport).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 91fa4d3c435b..3e3c148fd5d3 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -103,11 +103,12 @@ export class Transaction extends SpanClass implements TransactionInterface { // At this point if `sampled !== true` we want to discard the transaction. IS_DEBUG_BUILD && logger.log('[Tracing] Discarding transaction because its trace was not chosen to be sampled.'); - const client = this._hub.getClient(); - const transport = client && client.getTransport && client.getTransport(); - if (transport && transport.recordLostEvent) { - transport.recordLostEvent('sample_rate', 'transaction'); - } + // TODO(v7): Add back client reports functionality + // const client = this._hub.getClient(); + // const transport = client && client.getTransport && client.getTransport(); + // if (transport && transport.recordLostEvent) { + // transport.recordLostEvent('sample_rate', 'transaction'); + // } return undefined; } diff --git a/packages/tracing/test/browser/backgroundtab.test.ts b/packages/tracing/test/browser/backgroundtab.test.ts index 29612b410322..3e332033d74d 100644 --- a/packages/tracing/test/browser/backgroundtab.test.ts +++ b/packages/tracing/test/browser/backgroundtab.test.ts @@ -1,11 +1,10 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import { JSDOM } from 'jsdom'; import { registerBackgroundTabDetection } from '../../src/browser/backgroundtab'; import { addExtensionMethods } from '../../src/hubextensions'; +import { getDefaultBrowserClientOptions } from '../testutils'; describe('registerBackgroundTabDetection', () => { let events: Record = {}; @@ -16,7 +15,7 @@ describe('registerBackgroundTabDetection', () => { global.document = dom.window.document; const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub = new Hub(new BrowserClient(options)); makeMain(hub); // If we do not add extension methods, invoking hub.startTransaction returns undefined diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index 76ccaf947271..5cb144cfd207 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -1,6 +1,4 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import { getGlobalObject, InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { JSDOM } from 'jsdom'; @@ -18,6 +16,7 @@ import { instrumentRoutingWithDefaults } from '../../src/browser/router'; import * as hubExtensions from '../../src/hubextensions'; import { DEFAULT_IDLE_TIMEOUT, IdleTransaction } from '../../src/idletransaction'; import { getActiveTransaction, secToMs } from '../../src/utils'; +import { getDefaultBrowserClientOptions } from '../testutils'; let mockChangeHistory: ({ to, from }: { to: string; from?: string }) => void = () => undefined; @@ -54,7 +53,7 @@ describe('BrowserTracing', () => { beforeEach(() => { jest.useFakeTimers(); const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub = new Hub(new BrowserClient(options)); makeMain(hub); document.head.innerHTML = ''; @@ -476,7 +475,7 @@ describe('BrowserTracing', () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - hub.bindClient(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub.bindClient(new BrowserClient(options)); // setting up the BrowserTracing integration automatically starts a pageload transaction createBrowserTracing(true); @@ -493,7 +492,7 @@ describe('BrowserTracing', () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - hub.bindClient(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub.bindClient(new BrowserClient(options)); // setting up the BrowserTracing integration normally automatically starts a pageload transaction, but that's not // what we're testing here createBrowserTracing(true, { startTransactionOnPageLoad: false }); diff --git a/packages/tracing/test/browser/request.test.ts b/packages/tracing/test/browser/request.test.ts index 4ead6fe5bb9b..e77a0ae15c36 100644 --- a/packages/tracing/test/browser/request.test.ts +++ b/packages/tracing/test/browser/request.test.ts @@ -1,6 +1,4 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import * as utils from '@sentry/utils'; @@ -8,6 +6,7 @@ import { Span, spanStatusfromHttpCode, Transaction } from '../../src'; import { fetchCallback, FetchData, instrumentOutgoingRequests, xhrCallback } from '../../src/browser/request'; import { addExtensionMethods } from '../../src/hubextensions'; import * as tracingUtils from '../../src/utils'; +import { getDefaultBrowserClientOptions } from '../testutils'; beforeAll(() => { addExtensionMethods(); @@ -75,7 +74,7 @@ describe('callbacks', () => { beforeAll(() => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + hub = new Hub(new BrowserClient(options)); makeMain(hub); }); diff --git a/packages/tracing/test/errors.test.ts b/packages/tracing/test/errors.test.ts index 8dbcb2454539..6d3a3fa50768 100644 --- a/packages/tracing/test/errors.test.ts +++ b/packages/tracing/test/errors.test.ts @@ -1,11 +1,10 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { NoopTransport } from '@sentry/core/src/transports/noop'; import { Hub, makeMain } from '@sentry/hub'; import { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { registerErrorInstrumentation } from '../src/errors'; import { _addTracingExtensions } from '../src/hubextensions'; +import { getDefaultBrowserClientOptions } from './testutils'; const mockAddInstrumentationHandler = jest.fn(); let mockErrorCallback: InstrumentHandlerCallback = () => undefined; @@ -36,8 +35,8 @@ describe('registerErrorHandlers()', () => { let hub: Hub; beforeEach(() => { mockAddInstrumentationHandler.mockClear(); - const options = { tracesSampleRate: 1, transport: NoopTransport, integrations: [], stackParser: () => [] }; - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const options = getDefaultBrowserClientOptions(); + hub = new Hub(new BrowserClient(options)); makeMain(hub); }); diff --git a/packages/tracing/test/hub.test.ts b/packages/tracing/test/hub.test.ts index 6df8bad175c1..5fe7d84066ab 100644 --- a/packages/tracing/test/hub.test.ts +++ b/packages/tracing/test/hub.test.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method */ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain } from '@sentry/hub'; import * as utilsModule from '@sentry/utils'; // for mocking import { logger } from '@sentry/utils'; @@ -10,7 +8,12 @@ import { BrowserTracing } from '../src/browser/browsertracing'; import { addExtensionMethods } from '../src/hubextensions'; import { Transaction } from '../src/transaction'; import { extractTraceparentData, TRACEPARENT_REGEXP } from '../src/utils'; -import { addDOMPropertiesToGlobal, getSymbolObjectKeyByName, testOnlyIfNodeVersionAtLeast } from './testutils'; +import { + addDOMPropertiesToGlobal, + getDefaultBrowserClientOptions, + getSymbolObjectKeyByName, + testOnlyIfNodeVersionAtLeast, +} from './testutils'; addExtensionMethods(); @@ -35,7 +38,7 @@ describe('Hub', () => { describe('getTransaction()', () => { it('should find a transaction which has been set on the scope if sampled = true', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); transaction.sampled = true; @@ -49,7 +52,7 @@ describe('Hub', () => { it('should find a transaction which has been set on the scope if sampled = false', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -62,7 +65,7 @@ describe('Hub', () => { it("should not find an open transaction if it's not on the scope", () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -75,7 +78,7 @@ describe('Hub', () => { it('should add transaction context data to default sample context', () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transactionContext = { @@ -92,7 +95,7 @@ describe('Hub', () => { it("should add parent's sampling decision to default sample context", () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const parentSamplingDecsion = false; @@ -112,7 +115,7 @@ describe('Hub', () => { it('should set sampled = false when tracing is disabled', () => { const options = getDefaultBrowserClientOptions({}); // neither tracesSampleRate nor tracesSampler is defined -> tracing disabled - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -121,7 +124,7 @@ describe('Hub', () => { it('should set sampled = false if tracesSampleRate is 0', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -130,7 +133,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampleRate is 1', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -139,7 +142,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampleRate is 1 (without global hub)', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); const transaction = hub.startTransaction({ name: 'dogpark' }); expect(transaction.sampled).toBe(true); @@ -148,7 +151,7 @@ describe('Hub', () => { it("should call tracesSampler if it's defined", () => { const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -158,7 +161,7 @@ describe('Hub', () => { it('should set sampled = false if tracesSampler returns 0', () => { const tracesSampler = jest.fn().mockReturnValue(0); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -169,7 +172,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampler returns 1', () => { const tracesSampler = jest.fn().mockReturnValue(1); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -180,7 +183,7 @@ describe('Hub', () => { it('should set sampled = true if tracesSampler returns 1 (without global hub)', () => { const tracesSampler = jest.fn().mockReturnValue(1); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); const transaction = hub.startTransaction({ name: 'dogpark' }); expect(tracesSampler).toHaveBeenCalled(); @@ -191,7 +194,7 @@ describe('Hub', () => { // so that the decision otherwise would be false const tracesSampler = jest.fn().mockReturnValue(0); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: true }); @@ -202,7 +205,7 @@ describe('Hub', () => { // so that the decision otherwise would be true const tracesSampler = jest.fn().mockReturnValue(1); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -213,7 +216,7 @@ describe('Hub', () => { // make the two options do opposite things to prove precedence const tracesSampler = jest.fn().mockReturnValue(true); const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0, tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -224,7 +227,7 @@ describe('Hub', () => { it('should tolerate tracesSampler returning a boolean', () => { const tracesSampler = jest.fn().mockReturnValue(true); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -235,7 +238,7 @@ describe('Hub', () => { it('should record sampling method when sampling decision is explicitly set', () => { const tracesSampler = jest.fn().mockReturnValue(0.1121); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark', sampled: true }); @@ -247,7 +250,7 @@ describe('Hub', () => { it('should record sampling method and rate when sampling decision comes from tracesSampler', () => { const tracesSampler = jest.fn().mockReturnValue(0.1121); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -258,7 +261,7 @@ describe('Hub', () => { it('should record sampling method when sampling decision is inherited', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.1121 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark', parentSampled: true }); @@ -269,7 +272,7 @@ describe('Hub', () => { it('should record sampling method and rate when sampling decision comes from traceSampleRate', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.1121 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -282,7 +285,7 @@ describe('Hub', () => { describe('isValidSampleRate()', () => { it("should reject tracesSampleRates which aren't numbers or booleans", () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 'dogs!' as any }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -291,7 +294,7 @@ describe('Hub', () => { it('should reject tracesSampleRates which are NaN', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 'dogs!' as any }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -301,7 +304,7 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampleRates less than 0', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: -26 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -311,7 +314,7 @@ describe('Hub', () => { // the rate might be a boolean, but for our purposes, false is equivalent to 0 and true is equivalent to 1 it('should reject tracesSampleRates greater than 1', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 26 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -321,7 +324,7 @@ describe('Hub', () => { it("should reject tracesSampler return values which aren't numbers or booleans", () => { const tracesSampler = jest.fn().mockReturnValue('dogs!'); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -331,7 +334,7 @@ describe('Hub', () => { it('should reject tracesSampler return values which are NaN', () => { const tracesSampler = jest.fn().mockReturnValue(NaN); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -342,7 +345,7 @@ describe('Hub', () => { it('should reject tracesSampler return values less than 0', () => { const tracesSampler = jest.fn().mockReturnValue(-12); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -353,7 +356,7 @@ describe('Hub', () => { it('should reject tracesSampler return values greater than 1', () => { const tracesSampler = jest.fn().mockReturnValue(31); const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); hub.startTransaction({ name: 'dogpark' }); @@ -363,7 +366,7 @@ describe('Hub', () => { it('should drop transactions with sampled = false', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0 }); - const client = new BrowserClient(options, setupBrowserTransport(options).transport); + const client = new BrowserClient(options); jest.spyOn(client, 'captureEvent'); const hub = new Hub(client); @@ -381,7 +384,7 @@ describe('Hub', () => { describe('sampling inheritance', () => { it('should propagate sampling decision to child spans', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: Math.random() }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); const child = transaction.startChild({ op: 'ball.chase' }); @@ -398,7 +401,7 @@ describe('Hub', () => { tracesSampleRate: 1, integrations: [new BrowserTracing()], }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark' }); @@ -439,7 +442,7 @@ describe('Hub', () => { tracesSampleRate: 1, integrations: [new BrowserTracing()], }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ name: 'dogpark', sampled: false }); @@ -485,7 +488,7 @@ describe('Hub', () => { // tracesSampleRate mathRandom.mockReturnValueOnce(1); const options = getDefaultBrowserClientOptions({ tracesSampleRate: 0.5 }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const parentSamplingDecsion = true; @@ -502,7 +505,7 @@ describe('Hub', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); // tracesSampleRate = 1 means every transaction should end up with sampled = true, so make parent's decision the // opposite to prove that inheritance takes precedence over tracesSampleRate - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const parentSamplingDecsion = false; @@ -522,7 +525,7 @@ describe('Hub', () => { const parentSamplingDecsion = false; const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ @@ -541,7 +544,7 @@ describe('Hub', () => { const parentSamplingDecsion = true; const options = getDefaultBrowserClientOptions({ tracesSampler }); - const hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const hub = new Hub(new BrowserClient(options)); makeMain(hub); const transaction = hub.startTransaction({ diff --git a/packages/tracing/test/idletransaction.test.ts b/packages/tracing/test/idletransaction.test.ts index b040f4adbd9b..e6035ad491a1 100644 --- a/packages/tracing/test/idletransaction.test.ts +++ b/packages/tracing/test/idletransaction.test.ts @@ -1,5 +1,4 @@ -import { BrowserClient, Transports } from '@sentry/browser'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; +import { BrowserClient } from '@sentry/browser'; import { Hub } from '@sentry/hub'; import { @@ -9,16 +8,13 @@ import { IdleTransactionSpanRecorder, } from '../src/idletransaction'; import { Span } from '../src/span'; - -// @ts-ignore It's okay that we're not implementing the methods of the abstract `BaseTransport` class, because it's not -// what we're testing here -class SimpleTransport extends Transports.BaseTransport {} +import { getDefaultBrowserClientOptions } from './testutils'; const dsn = 'https://123@sentry.io/42'; let hub: Hub; beforeEach(() => { - const options = getDefaultBrowserClientOptions({ dsn, tracesSampleRate: 1, transport: SimpleTransport }); - hub = new Hub(new BrowserClient(options, new SimpleTransport({ dsn }))); + const options = getDefaultBrowserClientOptions({ dsn, tracesSampleRate: 1 }); + hub = new Hub(new BrowserClient(options)); }); describe('IdleTransaction', () => { @@ -167,18 +163,19 @@ describe('IdleTransaction', () => { } }); - it('should record dropped transactions', async () => { - const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234, sampled: false }, hub, 1000); + // TODO(v7): Add with client reports + // it('should record dropped transactions', async () => { + // const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234, sampled: false }, hub, 1000); - const transport = hub.getClient()!.getTransport!(); + // const transport = hub.getClient()!.getTransport!(); - const spy = jest.spyOn(transport, 'recordLostEvent'); + // const spy = jest.spyOn(transport, 'recordLostEvent'); - transaction.initSpanRecorder(10); - transaction.finish(transaction.startTimestamp + 10); + // transaction.initSpanRecorder(10); + // transaction.finish(transaction.startTimestamp + 10); - expect(spy).toHaveBeenCalledWith('sample_rate', 'transaction'); - }); + // expect(spy).toHaveBeenCalledWith('sample_rate', 'transaction'); + // }); describe('_initTimeout', () => { it('finishes if no activities are added to the transaction', () => { diff --git a/packages/tracing/test/span.test.ts b/packages/tracing/test/span.test.ts index 13f47c51ae7b..27df400ffc9e 100644 --- a/packages/tracing/test/span.test.ts +++ b/packages/tracing/test/span.test.ts @@ -1,10 +1,9 @@ import { BrowserClient } from '@sentry/browser'; -import { setupBrowserTransport } from '@sentry/browser/src/transports'; -import { getDefaultBrowserClientOptions } from '@sentry/browser/test/unit/helper/browser-client-options'; import { Hub, makeMain, Scope } from '@sentry/hub'; import { Span, Transaction } from '../src'; import { TRACEPARENT_REGEXP } from '../src/utils'; +import { getDefaultBrowserClientOptions } from './testutils'; describe('Span', () => { let hub: Hub; @@ -12,7 +11,7 @@ describe('Span', () => { beforeEach(() => { const myScope = new Scope(); const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1 }); - hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport), myScope); + hub = new Hub(new BrowserClient(options), myScope); makeMain(hub); }); @@ -223,7 +222,7 @@ describe('Span', () => { _experiments: { maxSpans: 3 }, tracesSampleRate: 1, }); - const _hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const _hub = new Hub(new BrowserClient(options)); const spy = jest.spyOn(_hub as any, 'captureEvent') as any; const transaction = _hub.startTransaction({ name: 'test' }); for (let i = 0; i < 10; i++) { @@ -238,7 +237,7 @@ describe('Span', () => { const options = getDefaultBrowserClientOptions({ tracesSampleRate: 1, }); - const _hub = new Hub(new BrowserClient(options, setupBrowserTransport(options).transport)); + const _hub = new Hub(new BrowserClient(options)); const spy = jest.spyOn(_hub as any, 'captureEvent') as any; const transaction = _hub.startTransaction({ name: 'test', sampled: false }); for (let i = 0; i < 10; i++) { diff --git a/packages/tracing/test/testutils.ts b/packages/tracing/test/testutils.ts index a8f42f84b9b8..2c0aef189b57 100644 --- a/packages/tracing/test/testutils.ts +++ b/packages/tracing/test/testutils.ts @@ -1,4 +1,6 @@ -import { getGlobalObject } from '@sentry/utils'; +import { createTransport } from '@sentry/browser'; +import { ClientOptions } from '@sentry/types'; +import { getGlobalObject, resolvedSyncPromise } from '@sentry/utils'; import { JSDOM } from 'jsdom'; /** @@ -56,3 +58,12 @@ export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { return it; }; + +export function getDefaultBrowserClientOptions(options: Partial = {}): ClientOptions { + return { + integrations: [], + transport: () => createTransport({}, _ => resolvedSyncPromise({ statusCode: 200 })), + stackParser: () => [], + ...options, + }; +} diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index f0cdc8b3a988..b1cadf971197 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -3,9 +3,9 @@ import { Event, EventHint } from './event'; import { Integration, IntegrationClass } from './integration'; import { ClientOptions } from './options'; import { Scope } from './scope'; -import { Session } from './session'; +import { Session, SessionAggregates } from './session'; import { Severity, SeverityLevel } from './severity'; -import { Transport } from './transport'; +import { NewTransport } from './transport'; /** * User-Facing Sentry SDK Client. @@ -72,7 +72,7 @@ export interface Client { * * @returns The transport. */ - getTransport?(): Transport; + getTransport(): NewTransport | undefined; /** * Flush the event queue and set the client to `enabled = false`. See {@link Client.flush}. @@ -116,5 +116,5 @@ export interface Client { sendEvent(event: Event): void; /** Submits the session to Sentry */ - sendSession(session: Session): void; + sendSession(session: Session | SessionAggregates): void; } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 5fe4ced6375b..3c066e3cc0bf 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -62,6 +62,18 @@ export type { TransactionSamplingMethod, } from './transaction'; export type { Thread } from './thread'; -export type { Outcome, Transport, TransportOptions, TransportClass } from './transport'; +export type { + Outcome, + Transport, + TransportOptions, + TransportCategory, + TransportRequest, + TransportMakeRequestResponse, + TransportResponse, + InternalBaseTransportOptions, + BaseTransportOptions, + NewTransport, + TransportRequestExecutor, +} from './transport'; export type { User, UserFeedback } from './user'; export type { WrappedFunction } from './wrappedfunction'; diff --git a/packages/types/src/options.ts b/packages/types/src/options.ts index 8bd273752700..61188d6c099f 100644 --- a/packages/types/src/options.ts +++ b/packages/types/src/options.ts @@ -5,9 +5,9 @@ import { CaptureContext } from './scope'; import { SdkMetadata } from './sdkmetadata'; import { StackLineParser, StackParser } from './stacktrace'; import { SamplingContext } from './transaction'; -import { Transport, TransportClass, TransportOptions } from './transport'; +import { BaseTransportOptions, NewTransport } from './transport'; -export interface ClientOptions { +export interface ClientOptions { /** * Enable debug functionality in the SDK itself */ @@ -61,7 +61,7 @@ export interface ClientOptions { /** * Transport object that should be used to send events to Sentry */ - transport: TransportClass; + transport: (transportOptions: TO) => NewTransport; /** * A stack parser implementation @@ -72,7 +72,7 @@ export interface ClientOptions { /** * Options for the default transport that the SDK uses. */ - transportOptions?: TransportOptions; + transportOptions?: Partial; /** * Sample rate to determine trace sampling. @@ -202,7 +202,8 @@ export interface ClientOptions { } /** Base configuration options for every SDK. */ -export interface Options extends Omit, 'integrations' | 'transport' | 'stackParser'> { +export interface Options + extends Omit>, 'integrations' | 'transport' | 'stackParser'> { /** * If this is set to false, default integrations will not be added, otherwise this will internally be set to the * recommended default integrations. @@ -220,7 +221,7 @@ export interface Options extends Omit, 'integrations' | ' /** * Transport object that should be used to send events to Sentry */ - transport?: TransportClass; + transport?: (transportOptions: TO) => NewTransport; /** * A stack parser implementation or an array of stack line parsers diff --git a/packages/types/src/session.ts b/packages/types/src/session.ts index 552dda002531..237e4c41808d 100644 --- a/packages/types/src/session.ts +++ b/packages/types/src/session.ts @@ -74,9 +74,6 @@ export interface SessionFlusherLike { */ 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; diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index a1ba2983b1a3..1250d4cb01d5 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -1,5 +1,7 @@ import { DsnLike } from './dsn'; +import { Envelope } from './envelope'; import { Event } from './event'; +import { EventStatus } from './eventstatus'; import { SentryRequestType } from './request'; import { Response } from './response'; import { SdkMetadata } from './sdkmetadata'; @@ -13,6 +15,46 @@ export type Outcome = | 'ratelimit_backoff' | 'sample_rate'; +export type TransportCategory = 'error' | 'transaction' | 'attachment' | 'session'; + +export type TransportRequest = { + body: string; + category: TransportCategory; +}; + +export type TransportMakeRequestResponse = { + body?: string; + headers?: { + [key: string]: string | null; + 'x-sentry-rate-limits': string | null; + 'retry-after': string | null; + }; + reason?: string; + statusCode: number; +}; + +export type TransportResponse = { + status: EventStatus; + reason?: string; +}; + +export interface InternalBaseTransportOptions { + bufferSize?: number; +} +export interface BaseTransportOptions extends InternalBaseTransportOptions { + // url to send the event + // transport does not care about dsn specific - client should take care of + // parsing and figuring that out + url: string; +} + +export interface NewTransport { + send(request: Envelope): PromiseLike; + flush(timeout?: number): PromiseLike; +} + +export type TransportRequestExecutor = (request: TransportRequest) => PromiseLike; + /** Transport used sending data to Sentry */ export interface Transport { /**