diff --git a/packages/browser/src/helpers.ts b/packages/browser/src/helpers.ts index f1f47cddeb65..773c933eb1cf 100644 --- a/packages/browser/src/helpers.ts +++ b/packages/browser/src/helpers.ts @@ -1,4 +1,4 @@ -import { API, captureException, withScope } from '@sentry/core'; +import { captureException, getReportDialogEndpoint, withScope } from '@sentry/core'; import { DsnLike, Event as SentryEvent, Mechanism, Scope, WrappedFunction } from '@sentry/types'; import { addExceptionMechanism, addExceptionTypeValue, getGlobalObject, logger } from '@sentry/utils'; @@ -210,7 +210,7 @@ export function injectReportDialog(options: ReportDialogOptions = {}): void { const script = global.document.createElement('script'); script.async = true; - script.src = new API(options.dsn).getReportDialogEndpoint(options); + script.src = getReportDialogEndpoint(options.dsn, options); if (options.onLoad) { // eslint-disable-next-line @typescript-eslint/unbound-method diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index 980405326352..c5dff08ba653 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -42,9 +42,7 @@ export class API { /** Returns the prefix to construct Sentry ingestion API endpoints. */ public getBaseApiEndpoint(): string { const dsn = this.getDsn(); - const protocol = dsn.protocol ? `${dsn.protocol}:` : ''; - const port = dsn.port ? `:${dsn.port}` : ''; - return `${protocol}//${dsn.host}${port}${dsn.path ? `/${dsn.path}` : ''}/api/`; + return getBaseApiEndpoint(dsn); } /** Returns the store endpoint URL. */ @@ -99,45 +97,6 @@ export class API { }; } - /** Returns the url to the report dialog endpoint. */ - public getReportDialogEndpoint( - dialogOptions: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; - user?: { name?: string; email?: string }; - } = {}, - ): string { - const dsn = this.getDsn(); - const endpoint = `${this.getBaseApiEndpoint()}embed/error-page/`; - - const encodedOptions = []; - encodedOptions.push(`dsn=${dsn.toString()}`); - for (const key in dialogOptions) { - if (key === 'dsn') { - continue; - } - - if (key === 'user') { - if (!dialogOptions.user) { - continue; - } - if (dialogOptions.user.name) { - encodedOptions.push(`name=${encodeURIComponent(dialogOptions.user.name)}`); - } - if (dialogOptions.user.email) { - encodedOptions.push(`email=${encodeURIComponent(dialogOptions.user.email)}`); - } - } else { - encodedOptions.push(`${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key] as string)}`); - } - } - if (encodedOptions.length) { - return `${endpoint}?${encodedOptions.join('&')}`; - } - - return endpoint; - } - /** Returns the envelope endpoint URL. */ private _getEnvelopeEndpoint(): string { return this._getIngestEndpoint('envelope'); @@ -165,3 +124,46 @@ export class API { return urlEncode(auth); } } + +/** Returns the prefix to construct Sentry ingestion API endpoints. */ +function getBaseApiEndpoint(dsn: Dsn): string { + const protocol = dsn.protocol ? `${dsn.protocol}:` : ''; + const port = dsn.port ? `:${dsn.port}` : ''; + return `${protocol}//${dsn.host}${port}${dsn.path ? `/${dsn.path}` : ''}/api/`; +} + +/** Returns the url to the report dialog endpoint. */ +export function getReportDialogEndpoint( + dsnLike: DsnLike, + dialogOptions: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; + user?: { name?: string; email?: string }; + }, +): string { + const dsn = new Dsn(dsnLike); + const endpoint = `${getBaseApiEndpoint(dsn)}embed/error-page/`; + + let encodedOptions = `dsn=${dsn.toString()}`; + for (const key in dialogOptions) { + if (key === 'dsn') { + continue; + } + + if (key === 'user') { + if (!dialogOptions.user) { + continue; + } + if (dialogOptions.user.name) { + encodedOptions += `&name=${encodeURIComponent(dialogOptions.user.name)}`; + } + if (dialogOptions.user.email) { + encodedOptions += `&email=${encodeURIComponent(dialogOptions.user.email)}`; + } + } else { + encodedOptions += `&${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key] as string)}`; + } + } + + return `${endpoint}?${encodedOptions}`; +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5fe8450de3ed..7e7423b11ff8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -14,7 +14,7 @@ export { withScope, } from '@sentry/minimal'; export { addGlobalEventProcessor, getCurrentHub, getHubFromCarrier, Hub, makeMain, Scope } from '@sentry/hub'; -export { API } from './api'; +export { API, getReportDialogEndpoint } from './api'; export { BaseClient } from './baseclient'; export { BackendClass, BaseBackend } from './basebackend'; export { eventToSentryRequest, sessionToSentryRequest } from './request'; diff --git a/packages/core/test/lib/api.test.ts b/packages/core/test/lib/api.test.ts index fa1f2f43dd5e..4ca0b381ed09 100644 --- a/packages/core/test/lib/api.test.ts +++ b/packages/core/test/lib/api.test.ts @@ -1,6 +1,6 @@ import { Dsn } from '@sentry/utils'; -import { API } from '../../src/api'; +import { API, getReportDialogEndpoint } from '../../src/api'; const ingestDsn = 'https://abc@xxxx.ingest.sentry.io:1234/subpath/123'; const dsnPublic = 'https://abc@sentry.io:1234/subpath/123'; @@ -37,44 +37,86 @@ describe('API', () => { }); }); - test('getReportDialogEndpoint', () => { - expect(new API(ingestDsn).getReportDialogEndpoint({})).toEqual( - 'https://xxxx.ingest.sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@xxxx.ingest.sentry.io:1234/subpath/123', - ); - - expect(new API(dsnPublic).getReportDialogEndpoint({})).toEqual( - 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123', - ); - expect( - new API(dsnPublic).getReportDialogEndpoint({ - eventId: 'abc', - testy: '2', - }), - ).toEqual( - 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123&eventId=abc&testy=2', - ); - - expect( - new API(dsnPublic).getReportDialogEndpoint({ - eventId: 'abc', - user: { - email: 'email', - name: 'yo', + describe('getReportDialogEndpoint', () => { + test.each([ + [ + 'with Ingest DSN', + ingestDsn, + {}, + 'https://xxxx.ingest.sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@xxxx.ingest.sentry.io:1234/subpath/123', + ], + [ + 'with Public DSN', + dsnPublic, + {}, + 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123', + ], + [ + 'with Public DSN and dynamic options', + dsnPublic, + { eventId: 'abc', testy: '2' }, + 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123&eventId=abc&testy=2', + ], + [ + 'with Public DSN, dynamic options and user name and email', + dsnPublic, + { + eventId: 'abc', + user: { + email: 'email', + name: 'yo', + }, }, - }), - ).toEqual( - 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123&eventId=abc&name=yo&email=email', - ); - - expect( - new API(dsnPublic).getReportDialogEndpoint({ - eventId: 'abc', - user: undefined, - }), - ).toEqual( - 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123&eventId=abc', + 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123&eventId=abc&name=yo&email=email', + ], + [ + 'with Public DSN and user name', + dsnPublic, + { + user: { + name: 'yo', + }, + }, + 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123&name=yo', + ], + [ + 'with Public DSN and user email', + dsnPublic, + { + user: { + email: 'email', + }, + }, + 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123&email=email', + ], + [ + 'with Public DSN, dynamic options and undefined user', + dsnPublic, + { + eventId: 'abc', + user: undefined, + }, + 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123&eventId=abc', + ], + [ + 'with Public DSN and undefined user', + dsnPublic, + { user: undefined }, + 'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://abc@sentry.io:1234/subpath/123', + ], + ])( + '%s', + ( + _: string, + dsn: Parameters[0], + options: Parameters[1], + output: ReturnType, + ) => { + expect(getReportDialogEndpoint(dsn, options)).toBe(output); + }, ); }); + test('getDsn', () => { expect(new API(dsnPublic).getDsn()).toEqual(new Dsn(dsnPublic)); });