8000 fix: Browser SDK · DanielGibbsNZ/sentry-javascript@68ff201 · GitHub
[go: up one dir, main page]

Skip to content

Commit 68ff201

Browse files
committed
fix: Browser SDK
1 parent afcdb4c commit 68ff201

Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { SentryEvent, SentryEventHint, Severity, Transport } from '@sentry/types
33
import { SentryError } from '@sentry/utils/error';
44
import { isDOMError, isDOMException, isError, isErrorEvent, isPlainObject } from '@sentry/utils/is';
55
import { supportsBeacon, supportsFetch } from '@sentry/utils/supports';
6+
import { SyncPromise } from '@sentry/utils/syncpromise';
67
import { addExceptionTypeValue, eventFromPlainObject, eventFromStacktrace, prepareFramesForEvent } from './parsers';
78
import { computeStackTrace } from './tracekit';
89
import { BeaconTransport, FetchTransport, XHRTransport } from './transports';
@@ -70,48 +71,60 @@ export class BrowserBackend extends BaseBackend<BrowserOptions> {
7071
/**
7172
* @inheritDoc
7273
*/
73-
public eventFromException(exception: any, hint?: SentryEventHint): SentryEvent {
74-
let event;
75-
76-
if (isErrorEvent(exception as ErrorEvent) && (exception as ErrorEvent).error) {
77-
// If it is an ErrorEvent with `error` property, extract it to get actual Error
78-
const ex = exception as ErrorEvent;
79-
exception = ex.error; // tslint:disable-line:no-parameter-reassignment
80-
event = eventFromStacktrace(computeStackTrace(exception as Error));
81-
} else if (isDOMError(exception as DOMError) || isDOMException(exception as DOMException)) {
82-
// If it is a DOMError or DOMException (which are legacy APIs, but still supported in some browsers)
83-
// then we just extract the name and message, as they don't provide anything else
84-
// https://developer.mozilla.org/en-US/docs/Web/API/DOMError
85-
// https://developer.mozilla.org/en-US/docs/Web/API/DOMException
86-
const ex = exception as DOMException;
87-
const name = ex.name || (isDOMError(ex) ? 'DOMError' : 'DOMException');
88-
const message = ex.message ? `${name}: ${ex.message}` : name;
89-
90-
event = this.eventFromMessage(message, undefined, hint);
91-
addExceptionTypeValue(event, message);
92-
} else if (isError(exception as Error)) {
93-
// we have a real Error object, do nothing
94-
event = eventFromStacktrace(computeStackTrace(exception as Error));
95-
} else if (isPlainObject(exception as {}) && hint && hint.syntheticException) {
96-
// If it is plain Object, serialize it manually and extract options
97-
// This will allow us to group events based on top-level keys
98-
// which is much better than creating new group when any key/value change
99-
const ex = exception as {};
100-
event = eventFromPlainObject(ex, hint.syntheticException);
101-
addExceptionTypeValue(event, 'Custom Object');
102-
} else {
103-
// If none of previous checks were valid, then it means that
104-
// it's not a DOMError/DOMException
105-
// it's not a plain Object
106-
// it's not a valid ErrorEvent (one with an error property)
107-
// it's not an Error
108-
// So bail out and capture it as a simple message:
109-
const ex = exception as string;
110-
event = this.eventFromMessage(ex, undefined, hint);
111-
addExceptionTypeValue(event, `${ex}`);
112-
}
74+
public eventFromException(exception: any, hint?: SentryEventHint): SyncPromise<SentryEvent> {
75+
return new SyncPromise<SentryEvent>(resolve => {
76+
let event: SentryEvent;
77+
78+
if (isErrorEvent(exception as ErrorEvent) && (exception as ErrorEvent).error) {
79+
// If it is an ErrorEvent with `error` property, extract it to get actual Error
80+
const ex = exception as ErrorEvent;
81+
exception = ex.error; // tslint:disable-line:no-parameter-reassignment
82+
event = eventFromStacktrace(computeStackTrace(exception as Error));
83+
resolve(this.buildEvent(event));
84+
} else if (isDOMError(exception as DOMError) || isDOMException(exception as DOMException)) {
85+
// If it is a DOMError or DOMException (which are legacy APIs, but still supported in some browsers)
86+
// then we just extract the name and message, as they don't provide anything else
87+
// https://developer.mozilla.org/en-US/docs/Web/API/DOMError
88+
// https://developer.mozilla.org/en-US/docs/Web/API/DOMException
89+
const ex = exception as DOMException;
90+
const name = ex.name || (isDOMError(ex) ? 'DOMError' : 'DOMException');
91+
const message = ex.message ? `${name}: ${ex.message}` : name;
92+
93+
this.eventFromMessage(message, undefined, hint).then(messageEvent => {
94+
addExceptionTypeValue(messageEvent, message);
95+
resolve(this.buildEvent(messageEvent));
96+
});
97+
} else if (isError(exception as Error)) {
98+
// we have a real Error object, do nothing
99+
event = eventFromStacktrace(computeStackTrace(exception as Error));
100+
resolve(this.buildEvent(event));
101+
} else if (isPlainObject(exception as {}) && hint && hint.syntheticException) {
102+
// If it is plain Object, serialize it manually and extract options
103+
// This will allow us to group events based on top-level keys
104+
// which is much better than creating new group when any key/value change
105+
const ex = exception as {};
106+
event = eventFromPlainObject(ex, hint.syntheticException);
107+
addExceptionTypeValue(event, 'Custom Object');
108+
resolve(this.buildEvent(event));
109+
} else {
110+
// If none of previous checks were valid, then it means that
111+
// it's not a DOMError/DOMException
112+
// it's not a plain Object
113+
// it's not a valid ErrorEvent (one with an error property)
114+
// it's not an Error
115+
// So bail out and capture it as a simple message:
116+
const ex = exception as string;
117+
this.eventFromMessage(ex, undefined, hint).then(messageEvent => {
118+
addExceptionTypeValue(messageEvent, `${ex}`);
119+
resolve(this.buildEvent(messageEvent));
120+
});
121+
}
122+
});
123+
}
113124

114-
event = {
125+
/** JSDOC */
126+
private buildEvent(event: SentryEvent, hint?: SentryEventHint): SentryEvent {
127+
return {
115128
...event,
116129
event_id: hint && hint.event_id,
117130
exception: {
@@ -122,14 +135,16 @@ export class BrowserBackend extends BaseBackend<BrowserOptions> {
122135
},
123136
},
124137
};
125-
126-
return event;
127138
}
128139

129140
/**
130141
* @inheritDoc
131142
*/
132-
public eventFromMessage(message: string, level: Severity = Severity.Info, hint?: SentryEventHint): SentryEvent {
143+
public eventFromMessage(
144+
message: string,
145+
level: Severity = Severity.Info,
146+
hint?: SentryEventHint,
147+
): SyncPromise<SentryEvent> {
133148
const event: SentryEvent = {
134149
event_id: hint && hint.event_id,
135150
level,
@@ -144,6 +159,6 @@ export class BrowserBackend extends BaseBackend<BrowserOptions> {
144159
};
145160
}
146161

147-
return event;
162+
return SyncPromise.resolve(event);
148163
}
149164
}
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { API, BaseClient, Scope } from '@sentry/core';
22
import { DsnLike, SentryEvent, SentryEventHint } from '@sentry/types';
33
import { SentryError } from '@sentry/utils/error';
44
import { getGlobalObject } from '@sentry/utils/misc';
5+
import { SyncPromise } from '@sentry/utils/syncpromise';
56
import { BrowserBackend, BrowserOptions } from './backend';
67
import { SDK_NAME, SDK_VERSION } from './version';
78

@@ -49,7 +50,7 @@ export class BrowserClient extends BaseClient<BrowserBackend, BrowserOptions> {
4950
/**
5051
* @inheritDoc
5152
*/
52-
protected prepareEvent(event: SentryEvent, scope?: Scope, hint?: SentryEventHint): SentryEvent | null {
53+
protected prepareEvent(event: SentryEvent, scope?: Scope, hint?: SentryEventHint): SyncPromise<SentryEvent | null> {
5354
event.platform = event.platform || 'javascript';
5455
event.sdk = {
5556
...event.sdk,
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export {
2727
Scope,
2828
} from '@sentry/core';
2929

30-
export { BrowserBackend, BrowserOptions } from './backend';
30+
export { BrowserOptions } from './backend';
3131
export { BrowserClient, ReportDialogOptions } from './client';
3232
export { defaultIntegrations, forceLoad, init, lastEventId, onLoad, showReportDialog } from './sdk';
3333
export { SDK_NAME, SDK_VERSION } from './version';
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { expect } from 'chai';
2-
import { Status } from '../src';
32
import { BrowserBackend } from '../src/backend';
4-
import { SimpleTransport } from './mocks/simpletransport';
53

64
const dsn = 'https://123@sentry.io/42';
75
const testEvent = {
@@ -25,11 +23,5 @@ describe('BrowserBackend', () => {
2523
expect(e.message).equal('Cannot sendEvent without a valid Dsn');
2624
}
2725
});
28-
29-
it('should call sendEvent() on provided transport', async () => {
30-
backend = new BrowserBackend({ dsn, transport: SimpleTransport });
31-
const status = await backend.sendEvent(testEvent);
32-
expect(status.status).equal(Status.Success);
33-
});
3426
});
3527
});
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {
1111
import { isPrimitive } from '@sentry/utils/is';
1212
import { logger } from '@sentry/utils/logger';
1313
import { consoleSandbox, uuid4 } from '@sentry/utils/misc';
14-
import { SyncPromise } from '@sentry/utils/syncpromise';
1514
import { truncate } from '@sentry/utils/string';
15+
import { SyncPromise } from '@sentry/utils/syncpromise';
1616
import { BackendClass } from './basebackend';
1717
import { Dsn } from './dsn';
1818
import { IntegrationIndex, setupIntegrations } from './integration';
@@ -263,7 +263,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
263263
return scope.applyToEvent(prepared, hint, Math.min(maxBreadcrumbs, MAX_BREADCRUMBS));
264264
}
265265

266-
return SyncPromise.resolve(prepared);
266+
return SyncPromise.resolve(prepared as SentryEvent | null);
267267
}
268268

269269
/**
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { SentryEvent, SentryException, StackFrame } from '@sentry/types';
22
import { basename, dirname } from '@sentry/utils/path';
3-
import { SyncPromise } from '@sentry/utils/syncpromise';
43
import { snipLine } from '@sentry/utils/string';
4+
import { SyncPromise } from '@sentry/utils/syncpromise';
55
import { readFile } from 'fs';
66
import { LRUMap } from 'lru_map';
77
import * as stacktrace from 'stack-trace';
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,18 @@ export class SyncPromise<T> {
5757

5858
/** JSDoc */
5959
private readonly setResult = (value: T | any, state: States) => {
60-
const set = () => {
61-
if (this.state !== States.PENDING) {
62-
return null;
63-
}
64-
65-
if (isThenable(value)) {
66-
return (value as Thenable<T>).then(this.resolve, this.reject);
67-
}
60+
if (this.state !== States.PENDING) {
61+
return null;
62+
}
6863

69-
this.value = value;
70-
this.state = state;
64+
if (isThenable(value)) {
65+
return (value as Thenable<T>).then(this.resolve, this.reject);
66+
}
7167

72-
return this.executeHandlers();
73-
};
68+
this.value = value;
69+
this.state = state;
7470

75-
set();
71+
return this.executeHandlers();
7672
};
7773

7874
/** JSDoc */
@@ -96,8 +92,7 @@ export class SyncPromise<T> {
9692

9793
/** JSDoc */
9894
private readonly attachHandler = (handler: Handler<T, any>) => {
99-
this.handlers = [...this.handlers, handler];
100-
95+
this.handlers = this.handlers.concat(handler);
10196
this.executeHandlers();
10297
};
10398

Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ describe('SyncPromise', () => {
99
});
1010
});
1111

12+
test('simple static', () => {
13+
expect.assertions(1);
14+
15+
const p = SyncPromise.resolve(10);
16+
return p.then(val => {
17+
expect(val).toBe(10);
18+
});
19+
});
20+
1221
test('using new Promise internally', () => {
1322
expect.assertions(2);
1423