Skip to content

Commit c5ee428

Browse files
authored
sdk-core: use core client as event emitter, send reports from database via client (#290)
* sdk-core: use core client as event emitter * sdk-core: fix unit tests * sdk-core: emit events directly from database * sdk-core: extract BacktraceDatabaseEvents to separate file --------- Co-authored-by: Sebastian Alex <[email protected]>
1 parent 415c37c commit c5ee428

11 files changed

+68
-40
lines changed

packages/sdk-core/src/BacktraceCoreClient.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CoreClientSetup } from './builder/CoreClientSetup.js';
22
import { Events } from './common/Events.js';
3-
import { ReportEvents } from './events/ReportEvents.js';
3+
import { ClientEvents } from './events/ClientEvents.js';
44
import {
55
BacktraceAttachment,
66
BacktraceAttributeProvider,
@@ -34,7 +34,9 @@ import { MetricsBuilder } from './modules/metrics/MetricsBuilder.js';
3434
import { SingleSessionProvider } from './modules/metrics/SingleSessionProvider.js';
3535
import { RateLimitWatcher } from './modules/rateLimiter/RateLimitWatcher.js';
3636

37-
export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = BacktraceConfiguration> {
37+
export abstract class BacktraceCoreClient<
38+
O extends BacktraceConfiguration = BacktraceConfiguration,
39+
> extends Events<ClientEvents> {
3840
/**
3941
* Backtrace client instance
4042
*/
@@ -112,7 +114,7 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
112114
}
113115

114116
protected readonly options: O;
115-
protected readonly reportEvents: Events<ReportEvents>;
117+
116118
protected readonly attributeManager: AttributeManager;
117119
protected readonly attachmentManager: AttachmentManager;
118120
protected readonly fileSystem?: FileSystem;
@@ -128,7 +130,7 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
128130
private _enabled = false;
129131

130132
protected constructor(setup: CoreClientSetup<O>) {
131-
this.reportEvents = new Events();
133+
super();
132134

133135
this.options = setup.options;
134136
this.fileSystem = setup.fileSystem;
@@ -316,7 +318,7 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
316318
skipFrames: this.skipFrameOnMessage(data),
317319
});
318320

319-
this.reportEvents.emit('before-skip', report);
321+
this.emit('before-skip', report);
320322

321323
if (this.options.skipReport && this.options.skipReport(report)) {
322324
return Promise.resolve(BacktraceReportSubmissionResult.ReportSkipped());
@@ -329,12 +331,12 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
329331

330332
const submissionAttachments = this.generateSubmissionAttachments(report, reportAttachments);
331333

332-
this.reportEvents.emit('before-send', report, backtraceData, submissionAttachments);
334+
this.emit('before-send', report, backtraceData, submissionAttachments);
333335

334336
return this._reportSubmission
335337
.send(backtraceData, submissionAttachments, abortSignal)
336338
.then((submissionResult) => {
337-
this.reportEvents.emit('after-send', report, backtraceData, submissionAttachments, submissionResult);
339+
this.emit('after-send', report, backtraceData, submissionAttachments, submissionResult);
338340
return submissionResult;
339341
});
340342
}
@@ -403,7 +405,6 @@ export abstract class BacktraceCoreClient<O extends BacktraceConfiguration = Bac
403405
return {
404406
client: this,
405407
options: this.options,
406-
reportEvents: this.reportEvents,
407408
attributeManager: this.attributeManager,
408409
attachmentManager: this.attachmentManager,
409410
reportSubmission: this._reportSubmission,

packages/sdk-core/src/common/Events.ts

+12-14
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
interface EventCallback {
3-
callback: (...args: any[]) => unknown;
2+
interface EventCallback<A extends any[] = any[]> {
3+
callback: (...args: A) => unknown;
44
once?: boolean;
55
}
66

7-
export class Events<
8-
E extends Record<string | number | symbol, (...args: any[]) => unknown> = Record<
9-
string | number | symbol,
10-
(...args: any[]) => unknown
11-
>,
12-
> {
7+
/* eslint-disable @typescript-eslint/no-explicit-any */
8+
export type EventMap = Record<string, any[]>;
9+
10+
export class Events<E extends EventMap = EventMap> {
1311
private readonly _callbacks: Partial<Record<keyof E, EventCallback[]>> = {};
1412

15-
public on<N extends keyof E>(event: N, callback: E[N]): this {
13+
public on<N extends keyof E>(event: N, callback: (...args: E[N]) => unknown): this {
1614
this.addCallback(event, { callback });
1715
return this;
1816
}
1917

20-
public once<N extends keyof E>(event: N, callback: E[N]): this {
18+
public once<N extends keyof E>(event: N, callback: (...args: E[N]) => unknown): this {
2119
this.addCallback(event, { callback, once: true });
2220
return this;
2321
}
2422

25-
public off<N extends keyof E>(event: N, callback: E[N]): this {
23+
public off<N extends keyof E>(event: N, callback: (...args: E[N]) => unknown): this {
2624
this.removeCallback(event, callback);
2725
return this;
2826
}
2927

30-
public emit<N extends keyof E>(event: N, ...args: Parameters<E[N]>): boolean {
28+
public emit<N extends keyof E>(event: N, ...args: E[N]): boolean {
3129
const callbacks = this._callbacks[event];
3230
if (!callbacks || !callbacks.length) {
3331
return false;
@@ -48,7 +46,7 @@ export class Events<
4846
return true;
4947
}
5048

51-
private addCallback(event: keyof E, callback: EventCallback) {
49+
private addCallback<A extends unknown[]>(event: keyof E, callback: EventCallback<A>) {
5250
const list = this._callbacks[event];
5351
if (list) {
5452
list.push(callback);
@@ -57,7 +55,7 @@ export class Events<
5755
}
5856
}
5957

60-
private removeCallback(event: keyof E, callback: EventCallback['callback']) {
58+
private removeCallback<A extends unknown[]>(event: keyof E, callback: EventCallback<A>['callback']) {
6159
const list = this._callbacks[event];
6260
if (!list) {
6361
return;
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BacktraceAttachment } from '../model/attachment/index.js';
22

33
export type AttachmentEvents = {
4-
'scoped-attachments-updated'(attachments: BacktraceAttachment[]): void;
4+
'scoped-attachments-updated': [attachments: BacktraceAttachment[]];
55
};
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ReportData } from '../model/report/ReportData.js';
22

33
export type AttributeEvents = {
4-
'scoped-attributes-updated'(attributes: ReportData): void;
4+
'scoped-attributes-updated': [attributes: ReportData];
55
};

packages/sdk-core/src/events/ReportEvents.ts packages/sdk-core/src/events/ClientEvents.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { BacktraceData } from '../model/data/index.js';
33
import { BacktraceReportSubmissionResult, BacktraceSubmissionResponse } from '../model/http/index.js';
44
import { BacktraceReport } from '../model/report/BacktraceReport.js';
55

6-
export type ReportEvents = {
7-
'before-skip'(report: BacktraceReport): void;
8-
'before-send'(report: BacktraceReport, data: BacktraceData, attachments: BacktraceAttachment[]): void;
9-
'after-send'(
6+
export type ClientEvents = {
7+
'before-skip': [report: BacktraceReport];
8+
'before-send': [report: BacktraceReport, data: BacktraceData, attachments: BacktraceAttachment[]];
9+
'after-send': [
1010
report: BacktraceReport,
1111
data: BacktraceData,
1212
attachments: BacktraceAttachment[],
1313
result: BacktraceReportSubmissionResult<BacktraceSubmissionResponse>,
14-
): void;
14+
];
1515
};

packages/sdk-core/src/modules/BacktraceModule.ts

-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { Events } from '../common/Events.js';
2-
import { ReportEvents } from '../events/ReportEvents.js';
31
import {
42
BacktraceConfiguration,
53
BacktraceCoreClient,
@@ -17,7 +15,6 @@ export interface BacktraceModuleBindData {
1715
readonly options: BacktraceConfiguration;
1816
readonly attributeManager: AttributeManager;
1917
readonly attachmentManager: AttachmentManager;
20-
readonly reportEvents: Events<ReportEvents>;
2118
readonly reportSubmission: BacktraceReportSubmission;
2219
readonly requestHandler: BacktraceRequestHandler;
2320
readonly database?: BacktraceDatabase;

packages/sdk-core/src/modules/breadcrumbs/BreadcrumbsManager.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export class BreadcrumbsManager implements BacktraceBreadcrumbs, BacktraceModule
9595
}
9696
}
9797

98-
public bind({ client, reportEvents, attachmentManager }: BacktraceModuleBindData): void {
98+
public bind({ client, attachmentManager }: BacktraceModuleBindData): void {
9999
if (this._storage.getAttachmentProviders) {
100100
attachmentManager.addProviders(...this._storage.getAttachmentProviders());
101101
} else {
@@ -106,7 +106,7 @@ export class BreadcrumbsManager implements BacktraceBreadcrumbs, BacktraceModule
106106
[BREADCRUMB_ATTRIBUTE_NAME]: this._storage.lastBreadcrumbId,
107107
}));
108108

109-
reportEvents.on('before-skip', (report) => this.logReport(report));
109+
client.on('before-skip', (report) => this.logReport(report));
110110
}
111111

112112
public initialize() {

packages/sdk-core/src/modules/database/BacktraceDatabase.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { anySignal, createAbortController } from '../../common/AbortController.js';
2+
import { Events } from '../../common/Events.js';
23
import { IdGenerator } from '../../common/IdGenerator.js';
34
import { unrefInterval } from '../../common/intervalHelper.js';
45
import { TimeHelper } from '../../common/TimeHelper.js';
@@ -9,6 +10,7 @@ import { BacktraceReportSubmission } from '../../model/http/BacktraceReportSubmi
910
import { BacktraceModule, BacktraceModuleBindData } from '../BacktraceModule.js';
1011
import { SessionFiles } from '../storage/index.js';
1112
import { BacktraceDatabaseContext } from './BacktraceDatabaseContext.js';
13+
import { BacktraceDatabaseEvents } from './BacktraceDatabaseEvents.js';
1214
import { BacktraceDatabaseStorageProvider } from './BacktraceDatabaseStorageProvider.js';
1315
import {
1416
AttachmentBacktraceDatabaseRecord,
@@ -17,7 +19,7 @@ import {
1719
ReportBacktraceDatabaseRecord,
1820
} from './model/BacktraceDatabaseRecord.js';
1921

20-
export class BacktraceDatabase implements BacktraceModule {
22+
export class BacktraceDatabase extends Events<BacktraceDatabaseEvents> implements BacktraceModule {
2123
/**
2224
* Determines if the database is enabled.
2325
*/
@@ -46,6 +48,8 @@ export class BacktraceDatabase implements BacktraceModule {
4648
private readonly _requestHandler: BacktraceReportSubmission,
4749
private readonly _sessionFiles?: SessionFiles,
4850
) {
51+
super();
52+
4953
this._databaseRecordContext = new BacktraceDatabaseContext(this._options?.maximumRetries);
5054
this._recordLimits = {
5155
report: this._options?.maximumNumberOfRecords ?? 8,
@@ -83,7 +87,7 @@ export class BacktraceDatabase implements BacktraceModule {
8387
return true;
8488
}
8589

86-
public bind({ reportEvents }: BacktraceModuleBindData): void {
90+
public bind({ client }: BacktraceModuleBindData): void {
8791
if (this._enabled) {
8892
return;
8993
}
@@ -92,7 +96,7 @@ export class BacktraceDatabase implements BacktraceModule {
9296
return;
9397
}
9498

95-
reportEvents.on('before-send', (_, data, attachments) => {
99+
client.on('before-send', (_, data, attachments) => {
96100
const record = this.add(data, attachments);
97101

98102
if (!record || record.locked) {
@@ -102,7 +106,7 @@ export class BacktraceDatabase implements BacktraceModule {
102106
record.locked = true;
103107
});
104108

105-
reportEvents.on('after-send', (_, data, __, submissionResult) => {
109+
client.on('after-send', (_, data, __, submissionResult) => {
106110
const record = this._databaseRecordContext.find(
107111
(record) => record.type === 'report' && record.data.uuid === data.uuid,
108112
);
@@ -152,6 +156,8 @@ export class BacktraceDatabase implements BacktraceModule {
152156
this._databaseRecordContext.add(record);
153157
this.lockSessionWithRecord(record);
154158

159+
this.emit('added', record);
160+
155161
return record;
156162
}
157163

@@ -190,6 +196,8 @@ export class BacktraceDatabase implements BacktraceModule {
190196
this._databaseRecordContext.add(record);
191197
this.lockSessionWithRecord(record);
192198

199+
this.emit('added', record);
200+
193201
return record;
194202
}
195203

@@ -239,6 +247,8 @@ export class BacktraceDatabase implements BacktraceModule {
239247
this._databaseRecordContext.remove(record);
240248
this._storageProvider.delete(record);
241249
this._sessionFiles?.unlockPreviousSessions(record.id);
250+
251+
this.emit('removed', record);
242252
}
243253
}
244254

@@ -288,11 +298,15 @@ export class BacktraceDatabase implements BacktraceModule {
288298
try {
289299
record.locked = true;
290300

301+
this.emit('before-send', record);
302+
291303
const result =
292304
record.type === 'report'
293305
? await this._requestHandler.send(record.data, record.attachments, signal)
294306
: await this._requestHandler.sendAttachment(record.rxid, record.attachment, signal);
295307

308+
this.emit('after-send', record, result);
309+
296310
if (
297311
result.status === 'Ok' ||
298312
result.status === 'Unsupported' ||
@@ -357,10 +371,15 @@ export class BacktraceDatabase implements BacktraceModule {
357371

358372
for (const record of recordsToAdd) {
359373
this.lockSessionWithRecord(record);
374+
this.emit('added', record);
360375
}
361376
}
362377

363378
private async setupDatabaseAutoSend() {
379+
if (!this._enabled) {
380+
return;
381+
}
382+
364383
if (this._options?.autoSend === false) {
365384
return;
366385
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { BacktraceReportSubmissionResult } from '../../model/data/BacktraceSubmissionResult.js';
2+
import { BacktraceSubmissionResponse } from '../../model/http/index.js';
3+
import { BacktraceDatabaseRecord } from './model/BacktraceDatabaseRecord.js';
4+
5+
export type BacktraceDatabaseEvents = {
6+
added: [record: BacktraceDatabaseRecord];
7+
removed: [record: BacktraceDatabaseRecord];
8+
'before-send': [record: BacktraceDatabaseRecord];
9+
'after-send': [
10+
record: BacktraceDatabaseRecord,
11+
result: BacktraceReportSubmissionResult<BacktraceSubmissionResponse>,
12+
];
13+
};

packages/sdk-core/src/modules/storage/SessionFiles.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface FileSession {
1111
}
1212

1313
type SessionEvents = {
14-
unlocked(): void;
14+
unlocked: [];
1515
};
1616

1717
const SESSION_MARKER_PREFIX = 'bt-session';

packages/sdk-core/tests/mocks/testHttpClient.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ import { BacktraceReportSubmissionResult, BacktraceRequestHandler } from '../../
22

33
export const testHttpClient: BacktraceRequestHandler = {
44
post: jest.fn().mockResolvedValue(Promise.resolve(BacktraceReportSubmissionResult.Ok('Ok'))),
5-
postError: jest.fn().mockResolvedValue(Promise.resolve()),
5+
postError: jest.fn().mockResolvedValue(Promise.resolve(BacktraceReportSubmissionResult.Ok('Ok'))),
66
};

0 commit comments

Comments
 (0)