diff --git a/CHANGELOG.md b/CHANGELOG.md index f3c3641b6d..4551e7c76f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ - Message event current stack trace moved from exception to threads ([#2694](https://github.com/getsentry/sentry-react-native/pull/2694)) +### Fixes + +- Unreachable fallback to fetch transport if native is not available ([#2695](https://github.com/getsentry/sentry-react-native/pull/2695)) + ## 4.12.0 ### Features diff --git a/src/js/client.ts b/src/js/client.ts index f124b7aa05..5a3c47c0bd 100644 --- a/src/js/client.ts +++ b/src/js/client.ts @@ -1,5 +1,4 @@ -import { eventFromException, eventFromMessage,makeFetchTransport } from '@sentry/browser'; -import { FetchImpl } from '@sentry/browser/types/transports/utils'; +import { eventFromException, eventFromMessage } from '@sentry/browser'; import { BaseClient } from '@sentry/core'; import { ClientReportEnvelope, @@ -11,18 +10,16 @@ import { Outcome, SeverityLevel, Thread, - Transport, UserFeedback, } from '@sentry/types'; import { dateTimestampInSeconds, logger, SentryError } from '@sentry/utils'; -// @ts-ignore LogBox introduced in RN 0.63 -import { Alert, LogBox, YellowBox } from 'react-native'; +import { Alert } from 'react-native'; import { Screenshot } from './integrations/screenshot'; import { defaultSdkInfo } from './integrations/sdkinfo'; -import { ReactNativeClientOptions, ReactNativeTransportOptions } from './options'; -import { makeReactNativeTransport } from './transports/native'; +import { ReactNativeClientOptions } from './options'; import { createUserFeedbackEnvelope, items } from './utils/envelope'; +import { ignoreRequireCycleLogs } from './utils/ignorerequirecyclelogs'; import { mergeOutcomes } from './utils/outcome'; import { NATIVE } from './wrapper'; @@ -41,34 +38,15 @@ export class ReactNativeClient extends BaseClient { * @param options Configuration options for this SDK. */ public constructor(options: ReactNativeClientOptions) { - if (!options.transport) { - options.transport = (options: ReactNativeTransportOptions, nativeFetch?: FetchImpl): Transport => { - if (NATIVE.isNativeTransportAvailable()) { - return makeReactNativeTransport(options); - } - return makeFetchTransport(options, nativeFetch); - }; - } + ignoreRequireCycleLogs(); options._metadata = options._metadata || {}; options._metadata.sdk = options._metadata.sdk || defaultSdkInfo; super(options); this._outcomesBuffer = []; - - // This is a workaround for now using fetch on RN, this is a known issue in react-native and only generates a warning - // YellowBox deprecated and replaced with with LogBox in RN 0.63 - if (LogBox) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - LogBox.ignoreLogs(['Require cycle:']); - } else { - // eslint-disable-next-line deprecation/deprecation - YellowBox.ignoreWarnings(['Require cycle:']); - } - void this._initNativeSdk(); } - /** * @inheritDoc */ diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index a69d305e41..5705f29140 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -4,6 +4,7 @@ import { defaultIntegrations as reactDefaultIntegrations, defaultStackParser, getCurrentHub, + makeFetchTransport, } from '@sentry/react'; import { Integration, StackFrame, UserFeedback } from '@sentry/types'; import { logger, stackParserFromStackParserOptions } from '@sentry/utils'; @@ -25,7 +26,7 @@ import { ReactNativeClientOptions, ReactNativeOptions, ReactNativeWrapperOptions import { ReactNativeScope } from './scope'; import { TouchEventBoundary } from './touchevents'; import { ReactNativeProfiler, ReactNativeTracing } from './tracing'; -import { DEFAULT_BUFFER_SIZE, makeReactNativeTransport } from './transports/native'; +import { DEFAULT_BUFFER_SIZE, makeNativeTransportFactory } from './transports/native'; import { makeUtf8TextEncoder } from './transports/TextEncoder'; import { safeFactory, safeTracesSampler } from './utils/safe'; @@ -64,7 +65,7 @@ export function init(passedOptions: ReactNativeOptions): void { ...DEFAULT_OPTIONS, ...passedOptions, // If custom transport factory fails the SDK won't initialize - transport: passedOptions.transport || makeReactNativeTransport, + transport: passedOptions.transport || makeNativeTransportFactory() || makeFetchTransport, transportOptions: { ...DEFAULT_OPTIONS.transportOptions, ...(passedOptions.transportOptions ?? {}), diff --git a/src/js/transports/native.ts b/src/js/transports/native.ts index 9eee06764f..9bc2e26e88 100644 --- a/src/js/transports/native.ts +++ b/src/js/transports/native.ts @@ -45,6 +45,16 @@ export class NativeTransport implements Transport { /** * Creates a Native Transport. */ -export function makeReactNativeTransport(options: BaseNativeTransportOptions = {}): NativeTransport { +export function makeNativeTransport(options: BaseNativeTransportOptions = {}): NativeTransport { return new NativeTransport(options); } + +/** + * Creates a Native Transport factory if the native transport is available. + */ +export function makeNativeTransportFactory(): typeof makeNativeTransport | null { + if (NATIVE.isNativeTransportAvailable()) { + return makeNativeTransport; + } + return null; +} diff --git a/src/js/utils/ignorerequirecyclelogs.ts b/src/js/utils/ignorerequirecyclelogs.ts new file mode 100644 index 0000000000..5fdc9120be --- /dev/null +++ b/src/js/utils/ignorerequirecyclelogs.ts @@ -0,0 +1,14 @@ +/* eslint-disable deprecation/deprecation */ +import { LogBox, YellowBox } from 'react-native'; + +/** + * This is a workaround for now using fetch on RN, this is a known issue in react-native and only generates a warning + * YellowBox deprecated and replaced with with LogBox in RN 0.63 + */ +export function ignoreRequireCycleLogs(): void { + if (LogBox) { + LogBox.ignoreLogs(['Require cycle:']); + } else { + YellowBox.ignoreWarnings(['Require cycle:']); + } +} diff --git a/test/sdk.test.ts b/test/sdk.test.ts index cb4fc3d3cd..e3cd17f2a1 100644 --- a/test/sdk.test.ts +++ b/test/sdk.test.ts @@ -3,6 +3,8 @@ */ import { logger } from '@sentry/utils'; +import { NATIVE } from '../src/js/wrapper'; + interface MockedClient { flush: jest.Mock; } @@ -61,18 +63,24 @@ jest.mock('../src/js/client', () => { }; }); +jest.mock('../src/js/wrapper'); + jest.spyOn(logger, 'error'); import { initAndBind } from '@sentry/core'; -import { getCurrentHub } from '@sentry/react'; +import { getCurrentHub, makeFetchTransport } from '@sentry/react'; import { BaseTransportOptions,ClientOptions, Integration, Scope } from '@sentry/types'; import { ReactNativeClientOptions } from '../src/js/options'; import { configureScope,flush, init, withScope } from '../src/js/sdk'; import { ReactNativeTracing, ReactNavigationInstrumentation } from '../src/js/tracing'; +import { makeNativeTransport } from '../src/js/transports/native'; import { firstArg, secondArg } from './testutils'; const mockedInitAndBind = initAndBind as jest.MockedFunction; +const usedOptions = (): ClientOptions | undefined => { + return mockedInitAndBind.mock.calls[0]?.[1]; +} afterEach(() => { jest.clearAllMocks(); @@ -184,10 +192,6 @@ describe('Tests the SDK functionality', () => { }); describe('transport options buffer size', () => { - const usedOptions = (): ClientOptions | undefined => { - return mockedInitAndBind.mock.calls[0]?.[1]; - } - it('uses default transport options buffer size', () => { init({ tracesSampleRate: 0.5, @@ -214,6 +218,28 @@ describe('Tests the SDK functionality', () => { }); }); + describe('transport initialization', () => { + it('uses transport from the options', () => { + const mockTransport = jest.fn(); + init({ + transport: mockTransport, + }); + expect(usedOptions()?.transport).toEqual(mockTransport); + }); + + it('uses native transport', () => { + (NATIVE.isNativeTransportAvailable as jest.Mock).mockImplementation(() => true); + init({}); + expect(usedOptions()?.transport).toEqual(makeNativeTransport); + }); + + it('uses fallback fetch transport', () => { + (NATIVE.isNativeTransportAvailable as jest.Mock).mockImplementation(() => false); + init({}); + expect(usedOptions()?.transport).toEqual(makeFetchTransport); + }); + }); + describe('initIsSafe', () => { test('initialScope callback is safe after init', () => { const mockInitialScope = jest.fn(() => { throw 'Test error' });