Skip to content

Commit 7cb255d

Browse files
committed
feat: Transports implementation
1 parent d3ec755 commit 7cb255d

File tree

15 files changed

+330
-17
lines changed

15 files changed

+330
-17
lines changed

packages/browser/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"@sentry/core": "0.5.4",
1919
"@sentry/hub": "0.5.4",
2020
"@sentry/minimal": "0.5.4",
21-
"@sentry/types": "0.5.4"
21+
"@sentry/types": "0.5.4",
22+
"@sentry/utils": "0.5.4"
2223
},
2324
"devDependencies": {
2425
"chai": "^4.1.2",

packages/browser/src/backend.ts

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { Backend, Options, SentryError } from '@sentry/core';
1+
import { Backend, DSN, Options, SentryError } from '@sentry/core';
22
import { addBreadcrumb, captureEvent } from '@sentry/minimal';
33
import { SentryEvent } from '@sentry/types';
4-
import { Raven, SendMethod } from './raven';
5-
6-
/** Original raven send function. */
7-
const sendRavenEvent = Raven._sendProcessedPayload.bind(Raven) as SendMethod;
4+
import { supportsFetch, urlEncode } from '@sentry/utils';
5+
import { Raven } from './raven';
6+
import { FetchTransport, XHRTransport } from './transports';
87

98
/**
109
* Configuration options for the Sentry Browser SDK.
@@ -117,12 +116,56 @@ export class BrowserBackend implements Backend {
117116
* @inheritDoc
118117
*/
119118
public async sendEvent(event: SentryEvent): Promise<number> {
120-
return new Promise<number>(resolve => {
121-
sendRavenEvent(event, error => {
122-
// TODO: Check the response status code
123-
resolve(error ? 500 : 200);
124-
});
125-
});
119+
let dsn;
120+
121+
if (!this.options.dsn) {
122+
throw new SentryError('Cannot sendEvent without a valid DSN');
123+
} else {
124+
dsn = new DSN(this.options.dsn);
125+
}
126+
127+
const auth = {
128+
sentry_client: `raven-js/${Raven.VERSION}`,
129+
sentry_key: dsn.user,
130+
sentry_secret: '',
131+
sentry_version: '7',
132+
};
133+
134+
if (dsn.pass) {
135+
auth.sentry_secret = dsn.pass;
136+
} else {
137+
delete auth.sentry_secret;
138+
}
139+
140+
const lastSlash = dsn.path.lastIndexOf('/');
141+
const path = dsn.path.substr(1, lastSlash);
142+
143+
const _globalProject = dsn.path.substr(lastSlash + 1);
144+
let globalServer = `//${dsn.host}${dsn.port ? `:${dsn.port}` : ''}`;
145+
146+
if (dsn.protocol) {
147+
globalServer = `${dsn.protocol}':'${globalServer}`;
148+
}
149+
150+
const _globalEndpoint = `${globalServer}/${path}api/${_globalProject}/store/`;
151+
152+
// Auth is intentionally sent as part of query string (NOT as custom HTTP header)
153+
// to avoid preflight CORS requests
154+
const url = `${_globalEndpoint}?${urlEncode(auth)}`;
155+
156+
const transport = this.options.transport
157+
? this.options.transport
158+
: supportsFetch()
159+
? new FetchTransport({ url })
160+
: new XHRTransport({ url });
161+
162+
// tslint:disable-next-line
163+
debugger;
164+
165+
return transport
166+
.send(event)
167+
.then((response: Response | XMLHttpRequest) => response.status)
168+
.catch((error: Response | XMLHttpRequest) => error.status);
126169
}
127170

128171
/**
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { SentryEvent, Transport } from '@sentry/types';
2+
import { serialize, supportsReferrerPolicy } from '@sentry/utils';
3+
4+
/** `fetch` based transport */
5+
export class FetchTransport implements Transport {
6+
public constructor(public config: { url: string }) {}
7+
8+
/**
9+
* @inheritDoc
10+
*/
11+
public async send(event: SentryEvent): Promise<Response> {
12+
const defaultOptions: RequestInit = {
13+
body: serialize(event),
14+
keepalive: true,
15+
method: 'POST',
16+
// Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
17+
// https://caniuse.com/#feat=referrer-policy
18+
// It doesn't. And it throw exception instead of ignoring this parameter...
19+
// REF: https://github.com/getsentry/raven-js/issues/1233
20+
referrerPolicy: (supportsReferrerPolicy()
21+
? 'origin'
22+
: '') as ReferrerPolicy,
23+
};
24+
25+
// TODO: Safe _window access
26+
return window.fetch(this.config.url, defaultOptions);
27+
}
28+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { FetchTransport } from './fetch';
2+
export { XHRTransport } from './xhr';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { SentryEvent, Transport } from '@sentry/types';
2+
import { serialize } from '@sentry/utils';
3+
4+
/** `XHR` based transport */
5+
export class XHRTransport implements Transport {
6+
public constructor(public config: { url: string }) {}
7+
8+
/**
9+
* @inheritDoc
10+
*/
11+
public async send(event: SentryEvent): Promise<XMLHttpRequest> {
12+
return new Promise<XMLHttpRequest>((resolve, reject) => {
13+
const request = new XMLHttpRequest();
14+
15+
request.onreadystatechange = () => {
16+
if (request.readyState !== 4) {
17+
return;
18+
}
19+
20+
if (request.status === 200) {
21+
resolve(request);
22+
}
23+
24+
reject(request);
25+
};
26+
27+
request.open('POST', this.config.url);
28+
request.send(serialize(event));
29+
});
30+
}
31+
}

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"dependencies": {
1818
"@sentry/hub": "0.5.4",
1919
"@sentry/minimal": "0.5.4",
20-
"@sentry/types": "0.5.4"
20+
"@sentry/types": "0.5.4",
21+
"@sentry/utils": "0.5.4"
2122
},
2223
"devDependencies": {
2324
"jest": "^22.4.3",

packages/core/src/base.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Scope } from '@sentry/hub';
22
import { Breadcrumb, SdkInfo, SentryEvent } from '@sentry/types';
3+
import { truncate, uuid4 } from '@sentry/utils';
34
import { DSN } from './dsn';
45
import { Backend, Client, Options } from './interfaces';
56
import { SendStatus } from './status';
@@ -16,6 +17,11 @@ const DEFAULT_BREADCRUMBS = 30;
1617
*/
1718
const MAX_BREADCRUMBS = 100;
1819

20+
/**
21+
* By default, truncates URL values to 250 chars
22+
*/
23+
const MAX_URL_LENGTH = 250;
24+
1925
/** A class object that can instanciate Backend objects. */
2026
export interface BackendClass<B extends Backend, O extends Options> {
2127
new (options: O): B;
@@ -240,6 +246,23 @@ export abstract class BaseClient<B extends Backend, O extends Options>
240246
scope.applyToEvent(prepared, Math.min(maxBreadcrumbs, MAX_BREADCRUMBS));
241247
}
242248

249+
if (prepared.message) {
250+
prepared.message = truncate(prepared.message, MAX_URL_LENGTH);
251+
}
252+
if (prepared.exception) {
253+
const exception = prepared.exception.values[0];
254+
if (exception.value) {
255+
exception.value = truncate(exception.value, MAX_URL_LENGTH);
256+
}
257+
}
258+
259+
const request = prepared.request;
260+
if (request && request.url) {
261+
request.url = truncate(request.url, MAX_URL_LENGTH);
262+
}
263+
264+
prepared.event_id = uuid4();
265+
243266
return prepared;
244267
}
245268

@@ -275,6 +298,10 @@ export abstract class BaseClient<B extends Backend, O extends Options>
275298
return SendStatus.Skipped;
276299
}
277300

301+
// TODO: Add breadcrumb with our own event?
302+
// Or should it be handled by the xhr/fetch integration itself?
303+
// Or maybe some other integration that'd use `afterSend`?
304+
278305
const finalEvent = beforeSend ? beforeSend(prepared) : prepared;
279306
const code = await send(finalEvent);
280307
const status = SendStatus.fromHttpCode(code);
@@ -284,6 +311,8 @@ export abstract class BaseClient<B extends Backend, O extends Options>
284311
// implementors to override this method and handle it themselves.
285312
}
286313

314+
// TODO: Handle duplicates and backoffs
315+
287316
if (afterSend) {
288317
afterSend(finalEvent, status);
289318
}

packages/core/src/interfaces.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Scope } from '@sentry/hub';
2-
import { Breadcrumb, Integration, SentryEvent } from '@sentry/types';
2+
import { Breadcrumb, Integration, SentryEvent, Transport } from '@sentry/types';
33
import { DSN } from './dsn';
44
import { SendStatus } from './status';
55

@@ -39,6 +39,11 @@ export interface Options {
3939
| Integration[]
4040
| ((integrations: Integration[]) => Integration[]);
4141

42+
/**
43+
* Transport object that should be used to send events to Sentry
44+
*/
45+
transport?: Transport;
46+
4247
/**
4348
* The release identifier used when uploading respective source maps. Specify
4449
* this value to allow Sentry to resolve the correct source maps when

packages/types/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,11 @@ export interface Integration {
127127
handler?: any;
128128
install(): void;
129129
}
130+
131+
/** TODO */
132+
export interface Transport {
133+
config: {
134+
url: string;
135+
};
136+
send(event: SentryEvent): Promise<Response | XMLHttpRequest>;
137+
}

packages/utils/src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
export { forget, filterAsync } from './async';
22
export { mkdirp, mkdirpSync } from './fs';
3-
export { clone, deserialize, fill, serialize } from './object';
3+
export { clone, deserialize, fill, serialize, urlEncode } from './object';
44
export { Store } from './store';
55
export { isError, isErrorEvent, isDOMError, isDOMException } from './is';
6+
export { truncate, uuid4 } from './string';
67
export {
7-
supportsErrorEvent,
88
supportsDOMError,
99
supportsDOMException,
10+
supportsErrorEvent,
11+
supportsFetch,
12+
supportsReferrerPolicy,
1013
} from './supports';

0 commit comments

Comments
 (0)