Skip to content

Commit

Permalink
remove remaining throws
Browse files Browse the repository at this point in the history
  • Loading branch information
Lms24 committed May 16, 2023
1 parent f5c2107 commit 78468e3
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 28 deletions.
12 changes: 9 additions & 3 deletions packages/core/src/transports/multiplexed.ts
Expand Up @@ -51,9 +51,13 @@ export function makeMultiplexedTransport<TO extends BaseTransportOptions>(
const fallbackTransport = createTransport(options);
const otherTransports: Record<string, Transport> = {};

function getTransport(dsn: string): Transport {
function getTransport(dsn: string): Transport | undefined {
if (!otherTransports[dsn]) {
const url = getEnvelopeEndpointWithUrlEncodedAuth(dsnFromString(dsn));
const validatedDsn = dsnFromString(dsn);
if (!validatedDsn) {
return undefined;
}
const url = getEnvelopeEndpointWithUrlEncodedAuth(validatedDsn);
otherTransports[dsn] = createTransport({ ...options, url });
}

Expand All @@ -66,7 +70,9 @@ export function makeMultiplexedTransport<TO extends BaseTransportOptions>(
return eventFromEnvelope(envelope, eventTypes);
}

const transports = matcher({ envelope, getEvent }).map(dsn => getTransport(dsn));
const transports = matcher({ envelope, getEvent })
.map(dsn => getTransport(dsn))
.filter((t): t is Transport => !!t);

// If we have no transports to send to, use the fallback transport
if (transports.length === 0) {
Expand Down
18 changes: 16 additions & 2 deletions packages/core/test/lib/transports/multiplexed.test.ts
Expand Up @@ -12,10 +12,10 @@ import { TextEncoder } from 'util';
import { createTransport, getEnvelopeEndpointWithUrlEncodedAuth, makeMultiplexedTransport } from '../../../src';

const DSN1 = 'https://1234@5678.ingest.sentry.io/4321';
const DSN1_URL = getEnvelopeEndpointWithUrlEncodedAuth(dsnFromString(DSN1));
const DSN1_URL = getEnvelopeEndpointWithUrlEncodedAuth(dsnFromString(DSN1)!);

const DSN2 = 'https://5678@1234.ingest.sentry.io/8765';
const DSN2_URL = getEnvelopeEndpointWithUrlEncodedAuth(dsnFromString(DSN2));
const DSN2_URL = getEnvelopeEndpointWithUrlEncodedAuth(dsnFromString(DSN2)!);

const ERROR_EVENT = { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' };
const ERROR_ENVELOPE = createEnvelope<EventEnvelope>({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [
Expand Down Expand Up @@ -83,6 +83,20 @@ describe('makeMultiplexedTransport', () => {
await transport.send(ERROR_ENVELOPE);
});

it('Falls back to options DSN when a matched DSN is invalid', async () => {
expect.assertions(1);

const makeTransport = makeMultiplexedTransport(
createTestTransport(url => {
expect(url).toBe(DSN1_URL);
}),
() => ['invalidDsn'],
);

const transport = makeTransport({ url: DSN1_URL, ...transportOptions });
await transport.send(ERROR_ENVELOPE);
});

it('DSN can be overridden via match callback', async () => {
expect.assertions(1);

Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/lib/transports/offline.test.ts
Expand Up @@ -38,7 +38,7 @@ const REPLAY_EVENT: ReplayEvent = {
replay_type: 'buffer',
};

const DSN = dsnFromString('https://public@dsn.ingest.sentry.io/1337');
const DSN = dsnFromString('https://public@dsn.ingest.sentry.io/1337')!;

const DATA = 'nothing';

Expand Down
3 changes: 3 additions & 0 deletions packages/nextjs/src/client/tunnelRoute.ts
Expand Up @@ -12,6 +12,9 @@ export function applyTunnelRouteOption(options: BrowserOptions): void {
const tunnelRouteOption = globalWithInjectedValues.__sentryRewritesTunnelPath__;
if (tunnelRouteOption && options.dsn) {
const dsnComponents = dsnFromString(options.dsn);
if (!dsnComponents) {
return;
}
const sentrySaasDsnMatch = dsnComponents.host.match(/^o(\d+)\.ingest\.sentry\.io$/);
if (sentrySaasDsnMatch) {
const orgId = sentrySaasDsnMatch[1];
Expand Down
19 changes: 15 additions & 4 deletions packages/nextjs/test/utils/tunnelRoute.test.ts
Expand Up @@ -11,7 +11,7 @@ beforeEach(() => {
});

describe('applyTunnelRouteOption()', () => {
it('should correctly apply `tunnelRoute` option when conditions are met', () => {
it('Correctly applies `tunnelRoute` option when conditions are met', () => {
globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
const options: any = {
dsn: 'https://11111111111111111111111111111111@o2222222.ingest.sentry.io/3333333',
Expand All @@ -22,7 +22,7 @@ describe('applyTunnelRouteOption()', () => {
expect(options.tunnel).toBe('/my-error-monitoring-route?o=2222222&p=3333333');
});

it('should not apply `tunnelRoute` when DSN is missing', () => {
it("Doesn't apply `tunnelRoute` when DSN is missing", () => {
globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
const options: any = {
// no dsn
Expand All @@ -33,7 +33,18 @@ describe('applyTunnelRouteOption()', () => {
expect(options.tunnel).toBeUndefined();
});

it("should not apply `tunnelRoute` option when `tunnelRoute` option wasn't injected", () => {
it("Doesn't apply `tunnelRoute` when DSN is invalid", () => {
globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
const options: any = {
dsn: 'invalidDsn',
} as BrowserOptions;

applyTunnelRouteOption(options);

expect(options.tunnel).toBeUndefined();
});

it("Doesn't apply `tunnelRoute` option when `tunnelRoute` option wasn't injected", () => {
const options: any = {
dsn: 'https://11111111111111111111111111111111@o2222222.ingest.sentry.io/3333333',
} as BrowserOptions;
Expand All @@ -43,7 +54,7 @@ describe('applyTunnelRouteOption()', () => {
expect(options.tunnel).toBeUndefined();
});

it('should not apply `tunnelRoute` option when DSN is not a SaaS DSN', () => {
it("Doesn't `tunnelRoute` option when DSN is not a SaaS DSN", () => {
globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
const options: any = {
dsn: 'https://11111111111111111111111111111111@example.com/3333333',
Expand Down
41 changes: 23 additions & 18 deletions packages/utils/src/dsn.ts
Expand Up @@ -31,13 +31,14 @@ export function dsnToString(dsn: DsnComponents, withPassword: boolean = false):
* Parses a Dsn from a given string.
*
* @param str A Dsn as string
* @returns Dsn as DsnComponents
* @returns Dsn as DsnComponents or undefined if @param str is not a valid DSN string
*/
export function dsnFromString(str: string): DsnComponents {
export function dsnFromString(str: string): DsnComponents | undefined {
const match = DSN_REGEX.exec(str);

if (!match) {
throw new SentryError(`Invalid Sentry Dsn: ${str}`);
logger.error(`Invalid Sentry Dsn: ${str}`);
return undefined;
}

const [protocol, publicKey, pass = '', host, port = '', lastPath] = match.slice(1);
Expand Down Expand Up @@ -72,30 +73,39 @@ function dsnFromComponents(components: DsnComponents): DsnComponents {
};
}

function validateDsn(dsn: DsnComponents): boolean | void {
function validateDsn(dsn: DsnComponents): boolean {
if (!__DEBUG_BUILD__) {
return;
return true;
}

const { port, projectId, protocol } = dsn;

const requiredComponents: ReadonlyArray<keyof DsnComponents> = ['protocol', 'publicKey', 'host', 'projectId'];
requiredComponents.forEach(component => {
const hasMissingRequiredComponent = requiredComponents.find(component => {
if (!dsn[component]) {
throw new SentryError(`Invalid Sentry Dsn: ${component} missing`);
logger.error(`Invalid Sentry Dsn: ${component} missing`);
return true;
}
return false;
});

if (hasMissingRequiredComponent) {
return false;
}

if (!projectId.match(/^\d+$/)) {
throw new SentryError(`Invalid Sentry Dsn: Invalid projectId ${projectId}`);
logger.error(`Invalid Sentry Dsn: Invalid projectId ${projectId}`);
return false;
}

if (!isValidProtocol(protocol)) {
throw new SentryError(`Invalid Sentry Dsn: Invalid protocol ${protocol}`);
logger.error(`Invalid Sentry Dsn: Invalid protocol ${protocol}`);
return false;
}

if (port && isNaN(parseInt(port, 10))) {
throw new SentryError(`Invalid Sentry Dsn: Invalid port ${port}`);
logger.error(`Invalid Sentry Dsn: Invalid port ${port}`);
return false;
}

return true;
Expand All @@ -106,14 +116,9 @@ function validateDsn(dsn: DsnComponents): boolean | void {
* @returns a valid DsnComponents object or `undefined` if @param from is an invalid DSN source
*/
export function makeDsn(from: DsnLike): DsnComponents | undefined {
try {
const components = typeof from === 'string' ? dsnFromString(from) : dsnFromComponents(from);
validateDsn(components);
return components;
} catch (e) {
if (e instanceof SentryError) {
logger.error(e.message);
}
const components = typeof from === 'string' ? dsnFromString(from) : dsnFromComponents(from);
if (!components || !validateDsn(components)) {
return undefined;
}
return components;
}

0 comments on commit 78468e3

Please sign in to comment.