From df4c2ad33352cf1be7e68dae1bf6b8982278ca15 Mon Sep 17 00:00:00 2001 From: HazA Date: Mon, 25 Jun 2018 16:23:29 +0200 Subject: [PATCH 1/2] fix: Fixes the dsn behaviour --- packages/core/src/dsn.ts | 28 ++++++++++++----- packages/core/test/lib/dsn.test.ts | 50 +++++++++++++++++++++++------- packages/types/src/index.ts | 6 ++-- 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/packages/core/src/dsn.ts b/packages/core/src/dsn.ts index 57a534cae603..a70194470885 100644 --- a/packages/core/src/dsn.ts +++ b/packages/core/src/dsn.ts @@ -16,8 +16,10 @@ export class DSN implements DSNComponents { public host!: string; /** Port of the Sentry instance. */ public port!: string; - /** Project path */ + /** Path */ public path!: string; + /** Project ID */ + public projectId!: string; /** Creates a new DSN component */ public constructor(from: DSNLike) { @@ -41,10 +43,12 @@ export class DSN implements DSNComponents { */ public toString(withPassword: boolean = false): string { // tslint:disable-next-line:no-this-assignment - const { host, path, pass, port, protocol, user } = this; + const { host, path, pass, port, projectId, protocol, user } = this; return ( `${protocol}://${user}${withPassword && pass ? `:${pass}` : ''}` + - `@${host}${port ? `:${port}` : ''}/${path}` + `@${host}${port ? `:${port}` : ''}/${ + path ? `${path}/` : path + }${projectId}` ); } @@ -55,8 +59,17 @@ export class DSN implements DSNComponents { throw new SentryError('Invalid DSN'); } - const [protocol, user, pass = '', host, port = '', path] = match.slice(1); - Object.assign(this, { host, pass, path, port, protocol, user }); + const [protocol, user, pass = '', host, port = '', lastPath] = match.slice( + 1, + ); + let path = ''; + let projectId = lastPath; + const split = projectId.split('/'); + if (split.length > 1) { + path = split.slice(0, -1).join('/'); + projectId = split.pop() as string; + } + Object.assign(this, { host, pass, path, projectId, port, protocol, user }); } /** Maps DSN components into this instance. */ @@ -66,12 +79,13 @@ export class DSN implements DSNComponents { this.pass = components.pass || ''; this.host = components.host; this.port = components.port || ''; - this.path = components.path; + this.path = components.path || ''; + this.projectId = components.projectId; } /** Validates this DSN and throws on error. */ private validate(): void { - for (const component of ['protocol', 'user', 'host', 'path']) { + for (const component of ['protocol', 'user', 'host', 'projectId']) { if (!this[component as keyof DSNComponents]) { throw new SentryError(`Invalid DSN: Missing ${component}`); } diff --git a/packages/core/test/lib/dsn.test.ts b/packages/core/test/lib/dsn.test.ts index 40fa3c01f5e8..0d2dc7b7f00a 100644 --- a/packages/core/test/lib/dsn.test.ts +++ b/packages/core/test/lib/dsn.test.ts @@ -7,8 +7,8 @@ describe('DSN', () => { const dsn = new DSN({ host: 'sentry.io', pass: 'xyz', - path: '123', port: '1234', + projectId: '123', protocol: 'https', user: 'abc', }); @@ -17,13 +17,14 @@ describe('DSN', () => { expect(dsn.pass).toBe('xyz'); expect(dsn.host).toBe('sentry.io'); expect(dsn.port).toBe('1234'); - expect(dsn.path).toBe('123'); + expect(dsn.projectId).toBe('123'); + expect(dsn.path).toBe(''); }); test('applies partial components', () => { const dsn = new DSN({ host: 'sentry.io', - path: '123', + projectId: '123', protocol: 'https', user: 'abc', }); @@ -32,7 +33,8 @@ describe('DSN', () => { expect(dsn.pass).toBe(''); expect(dsn.host).toBe('sentry.io'); expect(dsn.port).toBe(''); - expect(dsn.path).toBe('123'); + expect(dsn.projectId).toBe('123'); + expect(dsn.path).toBe(''); }); test('throws for missing components', () => { @@ -40,7 +42,7 @@ describe('DSN', () => { () => new DSN({ host: '', - path: '123', + projectId: '123', protocol: 'https', user: 'abc', }), @@ -49,7 +51,7 @@ describe('DSN', () => { () => new DSN({ host: 'sentry.io', - path: '', + projectId: '', protocol: 'https', user: 'abc', }), @@ -58,7 +60,7 @@ describe('DSN', () => { () => new DSN({ host: 'sentry.io', - path: '123', + projectId: '123', protocol: '' as 'http', // Trick the type checker here user: 'abc', }), @@ -67,7 +69,7 @@ describe('DSN', () => { () => new DSN({ host: 'sentry.io', - path: '123', + projectId: '123', protocol: 'https', user: '', }), @@ -79,7 +81,7 @@ describe('DSN', () => { () => new DSN({ host: 'sentry.io', - path: '123', + projectId: '123', protocol: 'httpx' as 'http', // Trick the type checker here user: 'abc', }), @@ -88,8 +90,8 @@ describe('DSN', () => { () => new DSN({ host: 'sentry.io', - path: '123', port: 'xxx', + projectId: '123', protocol: 'https', user: 'abc', }), @@ -105,17 +107,32 @@ describe('DSN', () => { expect(dsn.pass).toBe('xyz'); expect(dsn.host).toBe('sentry.io'); expect(dsn.port).toBe('1234'); - expect(dsn.path).toBe('123'); + expect(dsn.projectId).toBe('123'); + expect(dsn.path).toBe(''); }); test('parses a valid partial DSN', () => { - const dsn = new DSN('https://abc@sentry.io/123'); + const dsn = new DSN('https://abc@sentry.io/123/321'); expect(dsn.protocol).toBe('https'); expect(dsn.user).toBe('abc'); expect(dsn.pass).toBe(''); expect(dsn.host).toBe('sentry.io'); expect(dsn.port).toBe(''); expect(dsn.path).toBe('123'); + expect(dsn.projectId).toBe('321'); + }); + + test('with a long path', () => { + const dsn = new DSN( + 'https://abc@sentry.io/sentry/custom/installation/321', + ); + expect(dsn.protocol).toBe('https'); + expect(dsn.user).toBe('abc'); + expect(dsn.pass).toBe(''); + expect(dsn.host).toBe('sentry.io'); + expect(dsn.port).toBe(''); + expect(dsn.path).toBe('sentry/custom/installation'); + expect(dsn.projectId).toBe('321'); }); test('throws when provided invalid DSN', () => { @@ -157,5 +174,14 @@ describe('DSN', () => { const dsn = new DSN('https://abc@sentry.io/123'); expect(dsn.toString()).toBe('https://abc@sentry.io/123'); }); + + test('renders the full path correctly', () => { + const dsn = new DSN( + 'https://abc@sentry.io/sentry/custom/installation/321', + ); + expect(dsn.toString()).toBe( + 'https://abc@sentry.io/sentry/custom/installation/321', + ); + }); }); }); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 84e6432f6965..60353d8a82d3 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -13,8 +13,10 @@ export interface DSNComponents { host: string; /** Port of the Sentry instance. */ port?: string; - /** Project path */ - path: string; + /** Sub path/ */ + path?: string; + /** Project ID */ + projectId: string; } /** Anything that can be parsed into a DSN. */ From 3886d4aaeb26a8ba58ee3213c36dbb875aac201c Mon Sep 17 00:00:00 2001 From: HazA Date: Mon, 25 Jun 2018 16:42:34 +0200 Subject: [PATCH 2/2] fix: Browser endpoint --- packages/browser/src/transports/base.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/browser/src/transports/base.ts b/packages/browser/src/transports/base.ts index fae967e2fcd6..d8e6a2730960 100644 --- a/packages/browser/src/transports/base.ts +++ b/packages/browser/src/transports/base.ts @@ -36,7 +36,10 @@ export abstract class BaseTransport implements Transport { const protocol = dsn.protocol ? `${dsn.protocol}:` : ''; const port = dsn.port ? `:${dsn.port}` : ''; - const endpoint = `${protocol}//${dsn.host}${port}/api/${dsn.path}/store/`; + const path = dsn.path ? `/${dsn.path}` : ''; + const endpoint = `${protocol}//${dsn.host}${port}${path}/api/${ + dsn.projectId + }/store/`; // Auth is intentionally sent as part of query string (NOT as custom HTTP header) // to avoid preflight CORS requests