diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f720ed17d..9a32a4502e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Fixes - Use proper SDK name for Session Replay tags ([#4428](https://github.com/getsentry/sentry-react-native/pull/4428)) +- Use `makeDsn` from `core` to extract the URL from DSN avoiding unimplemented `URL.protocol` errors ([#4395]https://github.com/getsentry/sentry-react-native/pull/4395) ### Changes diff --git a/packages/core/src/js/sdk.tsx b/packages/core/src/js/sdk.tsx index 0d37fb215f..3c6fdff90c 100644 --- a/packages/core/src/js/sdk.tsx +++ b/packages/core/src/js/sdk.tsx @@ -1,6 +1,6 @@ /* eslint-disable complexity */ import type { Breadcrumb, BreadcrumbHint, Integration, Scope, SendFeedbackParams, UserFeedback } from '@sentry/core'; -import { captureFeedback, getClient, getGlobalScope, getIntegrationsToSetup, getIsolationScope, initAndBind, logger, stackParserFromStackParserOptions, withScope as coreWithScope } from '@sentry/core'; +import { captureFeedback, getClient, getGlobalScope, getIntegrationsToSetup, getIsolationScope, initAndBind, logger, makeDsn, stackParserFromStackParserOptions, withScope as coreWithScope } from '@sentry/core'; import { defaultStackParser, makeFetchTransport, @@ -66,13 +66,13 @@ export function init(passedOptions: ReactNativeOptions): void { if (!dsn) { return undefined; } - try { - const url = new URL(dsn); - return `${url.protocol}//${url.host}`; - } catch (e) { - logger.error('Failed to extract url from DSN', e); + const dsnComponents = makeDsn(dsn); + if (!dsnComponents) { + logger.error('Failed to extract url from DSN: ', dsn); return undefined; } + const port = dsnComponents.port ? `:${dsnComponents.port}` : ''; + return `${dsnComponents.protocol}://${dsnComponents.host}${port}`; }; const userBeforeBreadcrumb = safeFactory(passedOptions.beforeBreadcrumb, { loggerMessage: 'The beforeBreadcrumb threw an error' }); diff --git a/packages/core/test/sdk.test.ts b/packages/core/test/sdk.test.ts index 386bb66dbd..afd6137c8a 100644 --- a/packages/core/test/sdk.test.ts +++ b/packages/core/test/sdk.test.ts @@ -321,7 +321,7 @@ describe('Tests the SDK functionality', () => { expect(result).toBeNull(); }); - it('should filters out dsn breadcrumbs', () => { + it('should filter out dsn breadcrumbs', () => { (getDevServer as jest.Mock).mockReturnValue({ url: 'http://localhost:8081' }); const mockBeforeBreadcrumb = (breadcrumb: Breadcrumb, _hint?: BreadcrumbHint) => { @@ -345,21 +345,39 @@ describe('Tests the SDK functionality', () => { expect(result).toBeNull(); }); - it('should keep breadcrumbs matching dsn if the url parsing fails for dsn', () => { + it('should filter out dsn breadcrumbs that the ports match', () => { (getDevServer as jest.Mock).mockReturnValue({ url: 'http://localhost:8081' }); const mockBeforeBreadcrumb = (breadcrumb: Breadcrumb, _hint?: BreadcrumbHint) => { return breadcrumb; }; - // Mock the URL constructor to throw an exception for this test case - const originalURL = (global as any).URL; - jest.spyOn(global as any, 'URL').mockImplementationOnce(() => { - throw new Error('Failed to parse DSN URL'); - }); + const passedOptions = { + dsn: 'https://sentry@selfhosted.app.server:8181/1234567', + beforeBreadcrumb: mockBeforeBreadcrumb, + }; + + init(passedOptions); + + const breadcrumb: Breadcrumb = { + type: 'http', + data: { url: 'https://selfhosted.app.server:8181/api' }, + }; + + const result = usedOptions()?.beforeBreadcrumb!(breadcrumb); + + expect(result).toBeNull(); + }); + + it('should keep breadcrumbs if the ports do not match', () => { + (getDevServer as jest.Mock).mockReturnValue({ url: 'http://localhost:8081' }); + + const mockBeforeBreadcrumb = (breadcrumb: Breadcrumb, _hint?: BreadcrumbHint) => { + return breadcrumb; + }; const passedOptions = { - dsn: 'https://abc@def.ingest.sentry.io/1234567', + dsn: 'https://sentry@selfhosted.app.server:8181/1234567', beforeBreadcrumb: mockBeforeBreadcrumb, }; @@ -367,15 +385,36 @@ describe('Tests the SDK functionality', () => { const breadcrumb: Breadcrumb = { type: 'http', - data: { url: 'https://def.ingest.sentry.io/1234567' }, + data: { url: 'https://selfhosted.app.server:8080/api' }, }; const result = usedOptions()?.beforeBreadcrumb!(breadcrumb); expect(result).toEqual(breadcrumb); + }); + + it('should keep breadcrumbs if the url parsing fails for dsn', () => { + (getDevServer as jest.Mock).mockReturnValue({ url: 'http://localhost:8081' }); + + const mockBeforeBreadcrumb = (breadcrumb: Breadcrumb, _hint?: BreadcrumbHint) => { + return breadcrumb; + }; + + const passedOptions = { + dsn: 'invalid-dsn', + beforeBreadcrumb: mockBeforeBreadcrumb, + }; + + init(passedOptions); - // Restore the original URL constructor - (global as any).URL = originalURL; + const breadcrumb: Breadcrumb = { + type: 'http', + data: { url: 'https://def.ingest.sentry.io/1234567' }, + }; + + const result = usedOptions()?.beforeBreadcrumb!(breadcrumb); + + expect(result).toEqual(breadcrumb); }); it('should keep non dev server or dsn breadcrumbs', () => {