Skip to content

Commit

Permalink
ref(browser): Extract getReportDialogEndpoint from API class (#4274)
Browse files Browse the repository at this point in the history
Although this is a public API, we can extract it from the API class as
it should only be used by browser.
  • Loading branch information
AbhiPrasad authored and onurtemizkan committed Dec 19, 2021
1 parent 6d9b7aa commit eae2f9a
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 81 deletions.
4 changes: 2 additions & 2 deletions packages/browser/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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
Expand Down
86 changes: 44 additions & 42 deletions packages/core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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}`;
}
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
114 changes: 78 additions & 36 deletions packages/core/test/lib/api.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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<typeof getReportDialogEndpoint>[0],
options: Parameters<typeof getReportDialogEndpoint>[1],
output: ReturnType<typeof getReportDialogEndpoint>,
) => {
expect(getReportDialogEndpoint(dsn, options)).toBe(output);
},
);
});

test('getDsn', () => {
expect(new API(dsnPublic).getDsn()).toEqual(new Dsn(dsnPublic));
});
Expand Down

0 comments on commit eae2f9a

Please sign in to comment.