diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 23a7d00d70df..3c7603b024ff 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -7,6 +7,7 @@ import { EventItem, InternalBaseTransportOptions, Transport, + TransportMakeRequestResponse, TransportRequestExecutor, } from '@sentry/types'; import { @@ -35,13 +36,15 @@ export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30; export function createTransport( options: InternalBaseTransportOptions, makeRequest: TransportRequestExecutor, - buffer: PromiseBuffer = makePromiseBuffer(options.bufferSize || DEFAULT_TRANSPORT_BUFFER_SIZE), + buffer: PromiseBuffer = makePromiseBuffer( + options.bufferSize || DEFAULT_TRANSPORT_BUFFER_SIZE, + ), ): Transport { let rateLimits: RateLimits = {}; const flush = (timeout?: number): PromiseLike => buffer.drain(timeout); - function send(envelope: Envelope): PromiseLike { + function send(envelope: Envelope): PromiseLike { const filteredEnvelopeItems: EnvelopeItem[] = []; // Drop rate limited items from envelope @@ -71,7 +74,7 @@ export function createTransport( }); }; - const requestTask = (): PromiseLike => + const requestTask = (): PromiseLike => makeRequest({ body: serializeEnvelope(filteredEnvelope, options.textEncoder) }).then( response => { // We don't want to throw on NOK responses, but we want to at least log them @@ -80,10 +83,11 @@ export function createTransport( } rateLimits = updateRateLimits(rateLimits, response); + return response; }, error => { - __DEBUG_BUILD__ && logger.error('Failed while sending event:', error); recordEnvelopeLoss('network_error'); + throw error; }, ); diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index 0bf61118b9e9..80ab74b9f673 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -7,6 +7,8 @@ import { createGunzip } from 'zlib'; import { makeNodeTransport } from '../../src/transports'; +const textEncoder = new TextEncoder(); + jest.mock('@sentry/core', () => { const actualCore = jest.requireActual('@sentry/core'); return { @@ -70,22 +72,19 @@ const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, ]); -const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()); +const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE, textEncoder); const ATTACHMENT_ITEM = createAttachmentEnvelopeItem( { filename: 'empty-file.bin', data: new Uint8Array(50_000) }, - new TextEncoder(), + textEncoder, ); const EVENT_ATTACHMENT_ENVELOPE = addItemToEnvelope(EVENT_ENVELOPE, ATTACHMENT_ITEM); -const SERIALIZED_EVENT_ATTACHMENT_ENVELOPE = serializeEnvelope( - EVENT_ATTACHMENT_ENVELOPE, - new TextEncoder(), -) as Uint8Array; +const SERIALIZED_EVENT_ATTACHMENT_ENVELOPE = serializeEnvelope(EVENT_ATTACHMENT_ENVELOPE, textEncoder) as Uint8Array; const defaultOptions = { url: TEST_SERVER_URL, recordDroppedEvent: () => undefined, - textEncoder: new TextEncoder(), + textEncoder, }; describe('makeNewHttpTransport()', () => { @@ -151,7 +150,9 @@ describe('makeNewHttpTransport()', () => { const transport = makeNodeTransport(defaultOptions); - await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); + await expect(transport.send(EVENT_ENVELOPE)).resolves.toEqual( + expect.objectContaining({ statusCode: serverStatusCode }), + ); }, ); @@ -165,20 +166,13 @@ describe('makeNewHttpTransport()', () => { }); const transport = makeNodeTransport(defaultOptions); - await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); - }); - - it('should resolve when server responds with rate limit header and status code 200', async () => { - await setupTestServer({ + await expect(transport.send(EVENT_ENVELOPE)).resolves.toEqual({ statusCode: SUCCESS, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + headers: { + 'retry-after': '2700', + 'x-sentry-rate-limits': '60::organization, 2700::organization', }, }); - - const transport = makeNodeTransport(defaultOptions); - await transport.send(EVENT_ENVELOPE); }); }); @@ -308,7 +302,7 @@ describe('makeNewHttpTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), + body: serializeEnvelope(EVENT_ENVELOPE, textEncoder), category: 'error', }); @@ -328,7 +322,7 @@ describe('makeNewHttpTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), + body: serializeEnvelope(EVENT_ENVELOPE, textEncoder), category: 'error', }); @@ -356,7 +350,7 @@ describe('makeNewHttpTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), + body: serializeEnvelope(EVENT_ENVELOPE, textEncoder), category: 'error', }); @@ -384,7 +378,7 @@ describe('makeNewHttpTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), + body: serializeEnvelope(EVENT_ENVELOPE, textEncoder), category: 'error', }); diff --git a/packages/node/test/transports/https.test.ts b/packages/node/test/transports/https.test.ts index cf7051b54fe4..77286e32cbb6 100644 --- a/packages/node/test/transports/https.test.ts +++ b/packages/node/test/transports/https.test.ts @@ -9,6 +9,8 @@ import { makeNodeTransport } from '../../src/transports'; import { HTTPModule, HTTPModuleRequestIncomingMessage } from '../../src/transports/http-module'; import testServerCerts from './test-server-certs'; +const textEncoder = new TextEncoder(); + jest.mock('@sentry/core', () => { const actualCore = jest.requireActual('@sentry/core'); return { @@ -70,7 +72,7 @@ const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, ]); -const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()); +const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE, textEncoder); const unsafeHttpsModule: HTTPModule = { request: jest @@ -84,7 +86,7 @@ const defaultOptions = { httpModule: unsafeHttpsModule, url: TEST_SERVER_URL, recordDroppedEvent: () => undefined, // noop - textEncoder: new TextEncoder(), + textEncoder, }; describe('makeNewHttpsTransport()', () => { @@ -151,20 +153,13 @@ describe('makeNewHttpsTransport()', () => { }); const transport = makeNodeTransport(defaultOptions); - await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); - }); - - it('should resolve when server responds with rate limit header and status code 200', async () => { - await setupTestServer({ + await expect(transport.send(EVENT_ENVELOPE)).resolves.toEqual({ statusCode: SUCCESS, - responseHeaders: { - 'Retry-After': '2700', - 'X-Sentry-Rate-Limits': '60::organization, 2700::organization', + headers: { + 'retry-after': '2700', + 'x-sentry-rate-limits': '60::organization, 2700::organization', }, }); - - const transport = makeNodeTransport(defaultOptions); - await expect(transport.send(EVENT_ENVELOPE)).resolves.toBeUndefined(); }); it('should use `caCerts` option', async () => { @@ -299,7 +294,7 @@ describe('makeNewHttpsTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), + body: serializeEnvelope(EVENT_ENVELOPE, textEncoder), category: 'error', }); @@ -319,7 +314,7 @@ describe('makeNewHttpsTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), + body: serializeEnvelope(EVENT_ENVELOPE, textEncoder), category: 'error', }); @@ -347,7 +342,7 @@ describe('makeNewHttpsTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), + body: serializeEnvelope(EVENT_ENVELOPE, textEncoder), category: 'error', }); @@ -375,7 +370,7 @@ describe('makeNewHttpsTransport()', () => { const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; const executorResult = registeredRequestExecutor({ - body: serializeEnvelope(EVENT_ENVELOPE, new TextEncoder()), + body: serializeEnvelope(EVENT_ENVELOPE, textEncoder), category: 'error', }); diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 50f34beb2f45..1cbfc40cec94 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ // TODO: We might want to split this file up import { addGlobalEventProcessor, captureException, getCurrentHub, setContext } from '@sentry/core'; -import { Breadcrumb, ReplayEvent } from '@sentry/types'; +import { Breadcrumb, ReplayEvent, TransportMakeRequestResponse } from '@sentry/types'; import { addInstrumentationHandler, logger } from '@sentry/utils'; import { EventType, record } from 'rrweb'; @@ -904,7 +904,7 @@ export class ReplayContainer implements ReplayContainerInterface { segmentId: segment_id, includeReplayStartTimestamp, eventContext, - }: SendReplay): Promise { + }: SendReplay): Promise { const payloadWithSequence = createPayload({ events, headers: { diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index b9beb2148f20..b254edecc6df 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -29,7 +29,8 @@ export interface BaseTransportOptions extends InternalBaseTransportOptions { } export interface Transport { - send(request: Envelope): PromiseLike; + // TODO (v8) Remove void from return as it was only retained to avoid a breaking change + send(request: Envelope): PromiseLike; flush(timeout?: number): PromiseLike; }