From 112cb4c6eddbe8d16108d58548ded2321a53f5fd Mon Sep 17 00:00:00 2001 From: HazA Date: Fri, 3 Aug 2018 14:13:40 +0200 Subject: [PATCH 1/4] feat: Add API class to help with endpoints --- packages/browser/src/backend.ts | 13 ++- packages/browser/src/client.ts | 29 +------ packages/browser/src/transports/base.ts | 33 +------- packages/core/src/api.ts | 105 ++++++++++++++++++++++++ packages/core/src/index.ts | 1 + packages/core/test/lib/api.test.ts | 64 +++++++++++++++ packages/node/src/transports/base.ts | 31 ++----- 7 files changed, 188 insertions(+), 88 deletions(-) create mode 100644 packages/core/src/api.ts create mode 100644 packages/core/test/lib/api.test.ts diff --git a/packages/browser/src/backend.ts b/packages/browser/src/backend.ts index e9f3cd2e3d52..479be73a2bb5 100644 --- a/packages/browser/src/backend.ts +++ b/packages/browser/src/backend.ts @@ -1,5 +1,5 @@ import { Backend, DSN, Options, SentryError } from '@sentry/core'; -import { SentryEvent, SentryResponse } from '@sentry/types'; +import { SentryEvent, SentryResponse, Status } from '@sentry/types'; import { isDOMError, isDOMException, isError, isErrorEvent, isPlainObject } from '@sentry/utils/is'; import { supportsFetch } from '@sentry/utils/supports'; import { eventFromStacktrace, getEventOptionsFromPlainObject, prepareFramesForEvent } from './parsers'; @@ -132,18 +132,15 @@ export class BrowserBackend implements Backend { * @inheritDoc */ public async sendEvent(event: SentryEvent): Promise { - let dsn: DSN; - if (!this.options.dsn) { - throw new SentryError('Cannot sendEvent without a valid DSN'); - } else { - dsn = new DSN(this.options.dsn); + // We do nothing in case there is no DSN + return { status: Status.Skipped }; } - const transportOptions = this.options.transportOptions ? this.options.transportOptions : { dsn }; + const transportOptions = this.options.transportOptions ? this.options.transportOptions : { dsn: this.options.dsn }; const transport = this.options.transport - ? new this.options.transport({ dsn }) + ? new this.options.transport({ dsn: this.options.dsn }) : supportsFetch() ? new FetchTransport(transportOptions) : new XHRTransport(transportOptions); diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index c0f46f15cfc3..1ab4c4a4aa4c 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,4 +1,4 @@ -import { BaseClient, DSN, SentryError } from '@sentry/core'; +import { API, BaseClient, DSN, SentryError } from '@sentry/core'; import { DSNLike } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils/misc'; import { BrowserBackend, BrowserOptions } from './backend'; @@ -57,34 +57,9 @@ export class BrowserClient extends BaseClient { throw new SentryError('Missing `DSN` option in showReportDialog call'); } - const encodedOptions = []; - for (const key in options) { - if (key === 'user') { - const user = options.user; - if (!user) { - continue; - } - - if (user.name) { - encodedOptions.push(`name=${encodeURIComponent(user.name)}`); - } - if (user.email) { - encodedOptions.push(`email=${encodeURIComponent(user.email)}`); - } - } else { - encodedOptions.push(`${encodeURIComponent(key)}=${encodeURIComponent(options[key] as string)}`); - } - } - - const parsedDSN = new DSN(dsn); - const protocol = parsedDSN.protocol ? `${parsedDSN.protocol}:` : ''; - const port = parsedDSN.port ? `:${parsedDSN.port}` : ''; - const path = parsedDSN.path ? `/${parsedDSN.path}` : ''; - const src = `${protocol}//${parsedDSN.host}${port}${path}/api/embed/error-page/?${encodedOptions.join('&')}`; - const script = document.createElement('script'); script.async = true; - script.src = src; + script.src = new API(dsn).getReportDialogEndpoint(options); (document.head || document.body).appendChild(script); } } diff --git a/packages/browser/src/transports/base.ts b/packages/browser/src/transports/base.ts index 96f94e9ceb57..fed01577eb74 100644 --- a/packages/browser/src/transports/base.ts +++ b/packages/browser/src/transports/base.ts @@ -1,6 +1,5 @@ -import { DSN, SentryError } from '@sentry/core'; -import { DSNComponents, SentryEvent, SentryResponse, Transport, TransportOptions } from '@sentry/types'; -import { urlEncode } from '@sentry/utils/object'; +import { API, SentryError } from '@sentry/core'; +import { SentryEvent, SentryResponse, Transport, TransportOptions } from '@sentry/types'; /** Base Transport class implementation */ export abstract class BaseTransport implements Transport { @@ -10,33 +9,7 @@ export abstract class BaseTransport implements Transport { public url: string; public constructor(public options: TransportOptions) { - this.url = this.composeUrl(new DSN(options.dsn)); - } - - /** - * @inheritDoc - */ - public composeUrl(dsn: DSNComponents): string { - const auth = { - sentry_key: dsn.user, - sentry_secret: '', - sentry_version: '7', - }; - - if (dsn.pass) { - auth.sentry_secret = dsn.pass; - } else { - delete auth.sentry_secret; - } - - const protocol = dsn.protocol ? `${dsn.protocol}:` : ''; - const port = dsn.port ? `:${dsn.port}` : ''; - 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 - return `${endpoint}?${urlEncode(auth)}`; + this.url = new API(this.options.dsn).getStoreEndpoint(); } /** diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts new file mode 100644 index 000000000000..af022548d2f0 --- /dev/null +++ b/packages/core/src/api.ts @@ -0,0 +1,105 @@ +import { DSNLike } from '@sentry/types'; +import { urlEncode } from '@sentry/utils/object'; +import { DSN } from './dsn'; + +const SENTRY_API_VERSION = '7'; + +/** Helper class to provide urls to different Sentry endpoints. */ +export class API { + /** The internally used DSN object. */ + private dsnObject: DSN; + /** Create a new instance of API */ + public constructor(public dsn: DSNLike) { + this.dsnObject = new DSN(dsn); + } + + /** Returns the DSN object. */ + public getDSN(): DSN { + return this.dsnObject; + } + + /** Returns a string with auth headers in the url to the store endpoint. */ + public getStoreEndpoint(urlEncodedHeader: boolean = false): string { + const dsn = this.dsnObject; + const auth = { + sentry_key: dsn.user, + sentry_secret: '', + sentry_version: SENTRY_API_VERSION, + }; + + if (dsn.pass) { + auth.sentry_secret = dsn.pass; + } else { + delete auth.sentry_secret; + } + + const endpoint = `${this.getBaseUrl()}${this.getStoreEndpointPath()}`; + + // Auth is intentionally sent as part of query string (NOT as custom HTTP header) + // to avoid preflight CORS requests + if (urlEncodedHeader) { + return `${endpoint}?${urlEncode(auth)}`; + } + return endpoint; + } + + /** Returns the base path of the url including the port. */ + private getBaseUrl(): string { + const dsn = this.dsnObject; + const protocol = dsn.protocol ? `${dsn.protocol}:` : ''; + const port = dsn.port ? `:${dsn.port}` : ''; + return `${protocol}//${dsn.host}${port}`; + } + + /** Returns only the path component for the store endpoint. */ + public getStoreEndpointPath(): string { + const dsn = this.dsnObject; + return `${dsn.path ? `/${dsn.path}` : ''}/api/${dsn.projectId}/store/`; + } + + /** Returns an object that can be used in request headers. */ + public getRequestHeaders(clientName: string, clientVersion: string): { [key: string]: string } { + const dsn = this.dsnObject; + const header = [`Sentry sentry_version=${SENTRY_API_VERSION}`]; + header.push(`sentry_timestamp=${new Date().getTime()}`); + header.push(`sentry_client=${clientName}/${clientVersion}`); + header.push(`sentry_key=${dsn.user}`); + if (dsn.pass) { + header.push(`sentry_secret=${dsn.pass}`); + } + return { + 'Content-Type': 'application/json', + 'X-Sentry-Auth': header.join(', '), + }; + } + + /** Returns the url to the report dialog endpoint. */ + public getReportDialogEndpoint(dialogOptions: { [key: string]: any }): string { + const dsn = this.dsnObject; + const endpoint = `${this.getBaseUrl()}${dsn.path ? `/${dsn.path}` : ''}/api/embed/error-page/`; + if (dialogOptions) { + const encodedOptions = []; + for (const key in dialogOptions) { + if (key === 'user') { + const user = dialogOptions.user; + if (!user) { + continue; + } + + if (user.name) { + encodedOptions.push(`name=${encodeURIComponent(user.name)}`); + } + if (user.email) { + encodedOptions.push(`email=${encodeURIComponent(user.email)}`); + } + } else { + encodedOptions.push(`${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key] as string)}`); + } + } + if (encodedOptions.length) { + return `${endpoint}?${encodedOptions.join('&')}`; + } + } + return endpoint; + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c9186c092546..861576bbc369 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,7 @@ export { logger } from './logger'; export { captureException, captureMessage, configureScope } from '@sentry/minimal'; export { Hub, Scope } from '@sentry/hub'; +export { API } from './api'; export { BackendClass, BaseClient } from './base'; export { DSN } from './dsn'; export { SentryError } from './error'; diff --git a/packages/core/test/lib/api.test.ts b/packages/core/test/lib/api.test.ts new file mode 100644 index 000000000000..7f82bf9e59b3 --- /dev/null +++ b/packages/core/test/lib/api.test.ts @@ -0,0 +1,64 @@ +import { API } from '../../src/api'; +import { DSN } from '../../src/dsn'; + +const dsnPublic = 'https://abc@sentry.io:1234/subpath/123'; +const dsnWithSecret = 'https://abc:xyz@sentry.io:1234/subpath/123'; + +describe('API', () => { + test('getStoreEndpoint', () => { + expect(new API(dsnPublic).getStoreEndpoint(true)).toEqual( + 'https://sentry.io:1234/subpath/api/123/store/?sentry_key=abc&sentry_version=7', + ); + expect(new API(dsnWithSecret).getStoreEndpoint(true)).toEqual( + 'https://sentry.io:1234/subpath/api/123/store/?sentry_key=abc&sentry_secret=xyz&sentry_version=7', + ); + expect(new API(dsnPublic).getStoreEndpoint()).toEqual('https://sentry.io:1234/subpath/api/123/store/'); + expect(new API(dsnWithSecret).getStoreEndpoint()).toEqual('https://sentry.io:1234/subpath/api/123/store/'); + }); + + test('getRequestHeaders', () => { + expect(new API(dsnPublic).getRequestHeaders('a', '1.0')).toMatchObject({ + 'Content-Type': 'application/json', + 'X-Sentry-Auth': expect.stringMatching( + /^Sentry sentry_version=\d, sentry_timestamp=\d+, sentry_client=a\/1\.0, sentry_key=abc$/, + ), + }); + expect(new API(dsnWithSecret).getRequestHeaders('a', '1.0')).toMatchObject({ + 'Content-Type': 'application/json', + 'X-Sentry-Auth': expect.stringMatching( + /^Sentry sentry_version=\d, sentry_timestamp=\d+, sentry_client=a\/1\.0, sentry_key=abc, sentry_secret=xyz$/, + ), + }); + }); + test('getReportDialogEndpoint', () => { + expect(new API(dsnPublic).getReportDialogEndpoint({})).toEqual( + 'https://sentry.io:1234/subpath/api/embed/error-page/', + ); + expect( + new API(dsnPublic).getReportDialogEndpoint({ + eventId: 'abc', + testy: '2', + }), + ).toEqual('https://sentry.io:1234/subpath/api/embed/error-page/?eventId=abc&testy=2'); + + expect( + new API(dsnPublic).getReportDialogEndpoint({ + eventId: 'abc', + user: { + email: 'email', + name: 'yo', + }, + }), + ).toEqual('https://sentry.io:1234/subpath/api/embed/error-page/?eventId=abc&name=yo&email=email'); + + expect( + new API(dsnPublic).getReportDialogEndpoint({ + eventId: 'abc', + user: false, + }), + ).toEqual('https://sentry.io:1234/subpath/api/embed/error-page/?eventId=abc'); + }); + test('getDSN', () => { + expect(new API(dsnPublic).getDSN()).toEqual(new DSN(dsnPublic)); + }); +}); diff --git a/packages/node/src/transports/base.ts b/packages/node/src/transports/base.ts index 332e82c43cd0..64e810eaea53 100644 --- a/packages/node/src/transports/base.ts +++ b/packages/node/src/transports/base.ts @@ -1,4 +1,4 @@ -import { DSN, SentryError } from '@sentry/core'; +import { API, SentryError } from '@sentry/core'; import { SentryEvent, SentryResponse, Status, Transport, TransportOptions } from '@sentry/types'; import { serialize } from '@sentry/utils/object'; import * as http from 'http'; @@ -15,46 +15,31 @@ export interface HTTPRequest { /** Base Transport class implementation */ export abstract class BaseTransport implements Transport { - /** DSN object */ - protected dsn: DSN; + /** API object */ + protected api: API; /** The Agent used for corresponding transport */ protected client: http.Agent | https.Agent | undefined; /** Create instance and set this.dsn */ public constructor(public options: TransportOptions) { - this.dsn = new DSN(options.dsn); - } - - /** Returns a Sentry auth header string */ - private getAuthHeader(): string { - const header = ['Sentry sentry_version=7']; - header.push(`sentry_timestamp=${new Date().getTime()}`); - - header.push(`sentry_client=${SDK_NAME}/${SDK_VERSION}`); - - header.push(`sentry_key=${this.dsn.user}`); - if (this.dsn.pass) { - header.push(`sentry_secret=${this.dsn.pass}`); - } - return header.join(', '); + this.api = new API(options.dsn); } /** Returns a build request option object used by request */ protected getRequestOptions(): http.RequestOptions { const headers = { - 'Content-Type': 'application/json', - 'X-Sentry-Auth': this.getAuthHeader(), + ...this.api.getRequestHeaders(SDK_NAME, SDK_VERSION), ...this.options.headers, }; return { agent: this.client, headers, - hostname: this.dsn.host, + hostname: this.api.getDSN().host, method: 'POST', - path: `${this.dsn.path ? `/${this.dsn.path}` : ''}/api/${this.dsn.projectId}/store/`, - port: this.dsn.port, + path: this.api.getStoreEndpointPath(), + port: this.api.getDSN().port, }; } From 658a39740bcc23f567100a06e791ac919c1c49ec Mon Sep 17 00:00:00 2001 From: HazA Date: Mon, 6 Aug 2018 10:05:16 +0200 Subject: [PATCH 2/4] ref: API cleanup --- packages/browser/src/backend.ts | 3 +- packages/browser/src/transports/base.ts | 2 +- packages/core/src/api.ts | 64 ++++++++++--------------- packages/core/test/lib/api.test.ts | 13 +---- 4 files changed, 30 insertions(+), 52 deletions(-) diff --git a/packages/browser/src/backend.ts b/packages/browser/src/backend.ts index 479be73a2bb5..59bf1d2a43eb 100644 --- a/packages/browser/src/backend.ts +++ b/packages/browser/src/backend.ts @@ -1,4 +1,4 @@ -import { Backend, DSN, Options, SentryError } from '@sentry/core'; +import { Backend, logger, Options, SentryError } from '@sentry/core'; import { SentryEvent, SentryResponse, Status } from '@sentry/types'; import { isDOMError, isDOMException, isError, isErrorEvent, isPlainObject } from '@sentry/utils/is'; import { supportsFetch } from '@sentry/utils/supports'; @@ -133,6 +133,7 @@ export class BrowserBackend implements Backend { */ public async sendEvent(event: SentryEvent): Promise { if (!this.options.dsn) { + logger.warn(`Event has been skipped because no DSN is configured.`); // We do nothing in case there is no DSN return { status: Status.Skipped }; } diff --git a/packages/browser/src/transports/base.ts b/packages/browser/src/transports/base.ts index fed01577eb74..ffc70d8ace8a 100644 --- a/packages/browser/src/transports/base.ts +++ b/packages/browser/src/transports/base.ts @@ -9,7 +9,7 @@ export abstract class BaseTransport implements Transport { public url: string; public constructor(public options: TransportOptions) { - this.url = new API(this.options.dsn).getStoreEndpoint(); + this.url = new API(this.options.dsn).getStoreEndpointWithUrlEncodedAuth(); } /** diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index af022548d2f0..868ed5764bf9 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -19,28 +19,19 @@ export class API { } /** Returns a string with auth headers in the url to the store endpoint. */ - public getStoreEndpoint(urlEncodedHeader: boolean = false): string { + public getStoreEndpoint(): string { + return `${this.getBaseUrl()}${this.getStoreEndpointPath()}`; + } + + public getStoreEndpointWithUrlEncodedAuth(): string { const dsn = this.dsnObject; const auth = { sentry_key: dsn.user, - sentry_secret: '', sentry_version: SENTRY_API_VERSION, }; - - if (dsn.pass) { - auth.sentry_secret = dsn.pass; - } else { - delete auth.sentry_secret; - } - - const endpoint = `${this.getBaseUrl()}${this.getStoreEndpointPath()}`; - // Auth is intentionally sent as part of query string (NOT as custom HTTP header) // to avoid preflight CORS requests - if (urlEncodedHeader) { - return `${endpoint}?${urlEncode(auth)}`; - } - return endpoint; + return `${this.getStoreEndpoint()}?${urlEncode(auth)}`; } /** Returns the base path of the url including the port. */ @@ -64,9 +55,6 @@ export class API { header.push(`sentry_timestamp=${new Date().getTime()}`); header.push(`sentry_client=${clientName}/${clientVersion}`); header.push(`sentry_key=${dsn.user}`); - if (dsn.pass) { - header.push(`sentry_secret=${dsn.pass}`); - } return { 'Content-Type': 'application/json', 'X-Sentry-Auth': header.join(', '), @@ -74,32 +62,32 @@ export class API { } /** Returns the url to the report dialog endpoint. */ - public getReportDialogEndpoint(dialogOptions: { [key: string]: any }): string { + public getReportDialogEndpoint(dialogOptions: { [key: string]: any } = {}): string { const dsn = this.dsnObject; const endpoint = `${this.getBaseUrl()}${dsn.path ? `/${dsn.path}` : ''}/api/embed/error-page/`; - if (dialogOptions) { - const encodedOptions = []; - for (const key in dialogOptions) { - if (key === 'user') { - const user = dialogOptions.user; - if (!user) { - continue; - } - if (user.name) { - encodedOptions.push(`name=${encodeURIComponent(user.name)}`); - } - if (user.email) { - encodedOptions.push(`email=${encodeURIComponent(user.email)}`); - } - } else { - encodedOptions.push(`${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key] as string)}`); + const encodedOptions = []; + for (const key in dialogOptions) { + if (key === 'user') { + const user = dialogOptions.user; + if (!user) { + continue; } + + if (user.name) { + encodedOptions.push(`name=${encodeURIComponent(user.name)}`); + } + if (user.email) { + encodedOptions.push(`email=${encodeURIComponent(user.email)}`); + } + } else { + encodedOptions.push(`${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key] as string)}`); } - if (encodedOptions.length) { - return `${endpoint}?${encodedOptions.join('&')}`; - } } + if (encodedOptions.length) { + return `${endpoint}?${encodedOptions.join('&')}`; + } + return endpoint; } } diff --git a/packages/core/test/lib/api.test.ts b/packages/core/test/lib/api.test.ts index 7f82bf9e59b3..081827e4c9c0 100644 --- a/packages/core/test/lib/api.test.ts +++ b/packages/core/test/lib/api.test.ts @@ -2,18 +2,13 @@ import { API } from '../../src/api'; import { DSN } from '../../src/dsn'; const dsnPublic = 'https://abc@sentry.io:1234/subpath/123'; -const dsnWithSecret = 'https://abc:xyz@sentry.io:1234/subpath/123'; describe('API', () => { test('getStoreEndpoint', () => { - expect(new API(dsnPublic).getStoreEndpoint(true)).toEqual( + expect(new API(dsnPublic).getStoreEndpointWithUrlEncodedAuth()).toEqual( 'https://sentry.io:1234/subpath/api/123/store/?sentry_key=abc&sentry_version=7', ); - expect(new API(dsnWithSecret).getStoreEndpoint(true)).toEqual( - 'https://sentry.io:1234/subpath/api/123/store/?sentry_key=abc&sentry_secret=xyz&sentry_version=7', - ); expect(new API(dsnPublic).getStoreEndpoint()).toEqual('https://sentry.io:1234/subpath/api/123/store/'); - expect(new API(dsnWithSecret).getStoreEndpoint()).toEqual('https://sentry.io:1234/subpath/api/123/store/'); }); test('getRequestHeaders', () => { @@ -23,12 +18,6 @@ describe('API', () => { /^Sentry sentry_version=\d, sentry_timestamp=\d+, sentry_client=a\/1\.0, sentry_key=abc$/, ), }); - expect(new API(dsnWithSecret).getRequestHeaders('a', '1.0')).toMatchObject({ - 'Content-Type': 'application/json', - 'X-Sentry-Auth': expect.stringMatching( - /^Sentry sentry_version=\d, sentry_timestamp=\d+, sentry_client=a\/1\.0, sentry_key=abc, sentry_secret=xyz$/, - ), - }); }); test('getReportDialogEndpoint', () => { expect(new API(dsnPublic).getReportDialogEndpoint({})).toEqual( From 0f642a0520d2f22a55b96babfdbd3933bbbb32a9 Mon Sep 17 00:00:00 2001 From: HazA Date: Mon, 6 Aug 2018 10:45:58 +0200 Subject: [PATCH 3/4] fix: Tests + Lint --- packages/browser/src/client.ts | 2 +- packages/core/src/api.ts | 22 +++++++++++++--------- packages/core/test/lib/api.test.ts | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 1ab4c4a4aa4c..060858fcb13e 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,4 +1,4 @@ -import { API, BaseClient, DSN, SentryError } from '@sentry/core'; +import { API, BaseClient, SentryError } from '@sentry/core'; import { DSNLike } from '@sentry/types'; import { getGlobalObject } from '@sentry/utils/misc'; import { BrowserBackend, BrowserOptions } from './backend'; diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index 868ed5764bf9..d095c83d9fc3 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -7,7 +7,7 @@ const SENTRY_API_VERSION = '7'; /** Helper class to provide urls to different Sentry endpoints. */ export class API { /** The internally used DSN object. */ - private dsnObject: DSN; + private readonly dsnObject: DSN; /** Create a new instance of API */ public constructor(public dsn: DSNLike) { this.dsnObject = new DSN(dsn); @@ -23,6 +23,7 @@ export class API { return `${this.getBaseUrl()}${this.getStoreEndpointPath()}`; } + /** Returns the store endpoint with auth added in url encoded. */ public getStoreEndpointWithUrlEncodedAuth(): string { const dsn = this.dsnObject; const auth = { @@ -62,23 +63,26 @@ export class API { } /** Returns the url to the report dialog endpoint. */ - public getReportDialogEndpoint(dialogOptions: { [key: string]: any } = {}): string { + public getReportDialogEndpoint( + dialogOptions: { + [key: string]: any; + user?: { name?: string; email?: string }; + } = {}, + ): string { const dsn = this.dsnObject; const endpoint = `${this.getBaseUrl()}${dsn.path ? `/${dsn.path}` : ''}/api/embed/error-page/`; const encodedOptions = []; for (const key in dialogOptions) { if (key === 'user') { - const user = dialogOptions.user; - if (!user) { + if (!dialogOptions.user) { continue; } - - if (user.name) { - encodedOptions.push(`name=${encodeURIComponent(user.name)}`); + if (dialogOptions.user.name) { + encodedOptions.push(`name=${encodeURIComponent(dialogOptions.user.name)}`); } - if (user.email) { - encodedOptions.push(`email=${encodeURIComponent(user.email)}`); + if (dialogOptions.user.email) { + encodedOptions.push(`email=${encodeURIComponent(dialogOptions.user.email)}`); } } else { encodedOptions.push(`${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key] as string)}`); diff --git a/packages/core/test/lib/api.test.ts b/packages/core/test/lib/api.test.ts index 081827e4c9c0..0266aad88dc8 100644 --- a/packages/core/test/lib/api.test.ts +++ b/packages/core/test/lib/api.test.ts @@ -43,7 +43,7 @@ describe('API', () => { expect( new API(dsnPublic).getReportDialogEndpoint({ eventId: 'abc', - user: false, + user: undefined, }), ).toEqual('https://sentry.io:1234/subpath/api/embed/error-page/?eventId=abc'); }); From f5a588002581b4efbe3098b17016995732ae6646 Mon Sep 17 00:00:00 2001 From: HazA Date: Mon, 6 Aug 2018 11:05:18 +0200 Subject: [PATCH 4/4] fix: Remove overwrite composeurl --- packages/browser/test/transports/base.test.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/browser/test/transports/base.test.ts b/packages/browser/test/transports/base.test.ts index 005617f55697..f990591700df 100644 --- a/packages/browser/test/transports/base.test.ts +++ b/packages/browser/test/transports/base.test.ts @@ -1,16 +1,9 @@ -import { DSNComponents } from '@sentry/types'; import { expect } from 'chai'; import { BaseTransport } from '../../src/transports/base'; const testDSN = 'https://123@sentry.io/42'; class SimpleTransport extends BaseTransport {} -// tslint:disable-next-line:max-classes-per-file -class ComplexTransport extends BaseTransport { - public composeUrl(dsn: DSNComponents): string { - return `https://${dsn.host}/${dsn.user}`; - } -} describe('BaseTransport', () => { it('doesnt provide send() implementation', async () => { @@ -23,13 +16,8 @@ describe('BaseTransport', () => { } }); - it('provides composeEndpointUrl() implementation', () => { + it('has correct endpoint url', () => { const transport = new SimpleTransport({ dsn: testDSN }); expect(transport.url).equal('https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7'); }); - - it('allows overriding composeEndpointUrl() implementation', () => { - const transport = new ComplexTransport({ dsn: testDSN }); - expect(transport.url).equal('https://sentry.io/123'); - }); });