From 855556fdf743796accbeb8261c04d583c86c2dac Mon Sep 17 00:00:00 2001 From: Konrad Dysput Date: Thu, 27 Jul 2023 14:14:25 +0200 Subject: [PATCH 1/3] Before send event support --- packages/sdk-core/src/BacktraceCoreClient.ts | 16 +++++- .../configuration/BacktraceConfiguration.ts | 8 +++ .../tests/client/attributesTests.spec.ts | 4 +- .../tests/client/clientCallbacksTests.spec.ts | 50 +++++++++++++++++++ .../sdk-core/tests/client/clientTests.spec.ts | 4 +- .../tests/mocks/BacktraceTestClient.ts | 17 +++++-- 6 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 packages/sdk-core/tests/client/clientCallbacksTests.spec.ts diff --git a/packages/sdk-core/src/BacktraceCoreClient.ts b/packages/sdk-core/src/BacktraceCoreClient.ts index b220a571..6e28694e 100644 --- a/packages/sdk-core/src/BacktraceCoreClient.ts +++ b/packages/sdk-core/src/BacktraceCoreClient.ts @@ -8,7 +8,7 @@ import { } from '.'; import { SdkOptions } from './builder/SdkOptions'; import { BacktraceConfiguration } from './model/configuration/BacktraceConfiguration'; -import { AttributeType } from './model/data/BacktraceData'; +import { AttributeType, BacktraceData } from './model/data/BacktraceData'; import { BacktraceReportSubmission } from './model/http/BacktraceReportSubmission'; import { BacktraceRequestHandler } from './model/http/BacktraceRequestHandler'; import { BacktraceReport } from './model/report/BacktraceReport'; @@ -154,9 +154,21 @@ export abstract class BacktraceCoreClient { skipFrames: this.skipFrameOnMessage(data), }); + const backtraceData = this.generateSubmissionData(report); + if (!backtraceData) { + return; + } + + await this._reportSubmission.send(backtraceData, this.generateSubmissionAttachments(report, reportAttachments)); + } + + private generateSubmissionData(report: BacktraceReport): BacktraceData | undefined { const { annotations, attributes } = this._attributeProvider.get(); const backtraceData = this._dataBuilder.build(report, attributes, annotations); - await this._reportSubmission.send(backtraceData, this.generateSubmissionAttachments(report, reportAttachments)); + if (!this.options.beforeSend) { + return backtraceData; + } + return this.options.beforeSend(backtraceData); } private generateSubmissionAttachments( diff --git a/packages/sdk-core/src/model/configuration/BacktraceConfiguration.ts b/packages/sdk-core/src/model/configuration/BacktraceConfiguration.ts index 3465b4cd..847d1a28 100644 --- a/packages/sdk-core/src/model/configuration/BacktraceConfiguration.ts +++ b/packages/sdk-core/src/model/configuration/BacktraceConfiguration.ts @@ -1,4 +1,5 @@ import { BacktraceAttachment } from '../attachment'; +import { BacktraceData } from '../data/BacktraceData'; import { BacktraceDatabaseConfiguration } from './BacktraceDatabaseConfiguration'; export interface BacktraceMetricsOptions { @@ -56,6 +57,13 @@ export interface BacktraceConfiguration { timeout?: number; ignoreSslCertificate?: boolean; + /** + * Triggers an event every time an exception in the managed environment occurs, which allows you to skip the report (by returning a null value) + * or to modify data that library collected before sending the report. You can use the BeforeSend event to extend attributes or JSON object + * data based on data the application has at the time of exception. + */ + beforeSend?: (data: BacktraceData) => BacktraceData | undefined; + /** * Limits the number of reports the client will send per minute. If set to '0', there is no limit. * If set to a value greater than '0' and the value is reached, the client will not send any reports until the next minute. diff --git a/packages/sdk-core/tests/client/attributesTests.spec.ts b/packages/sdk-core/tests/client/attributesTests.spec.ts index 5f177b9d..bcdd1812 100644 --- a/packages/sdk-core/tests/client/attributesTests.spec.ts +++ b/packages/sdk-core/tests/client/attributesTests.spec.ts @@ -47,7 +47,7 @@ describe('Attributes tests', () => { const scopedAttributeGetFunction = jest .fn() .mockReturnValue({ [providerAttributeKey]: providerAttributeValue }); - const fakeClient = BacktraceTestClient.buildFakeClient([ + const fakeClient = BacktraceTestClient.buildFakeClient({}, [ { type: 'scoped', get: scopedAttributeGetFunction, @@ -66,7 +66,7 @@ describe('Attributes tests', () => { const scopedAttributeGetFunction = jest .fn() .mockReturnValue({ [providerAttributeKey]: providerAttributeValue }); - const fakeClient = BacktraceTestClient.buildFakeClient([ + const fakeClient = BacktraceTestClient.buildFakeClient({}, [ { type: 'dynamic', get: scopedAttributeGetFunction, diff --git a/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts b/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts new file mode 100644 index 00000000..895317fa --- /dev/null +++ b/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts @@ -0,0 +1,50 @@ +import { BacktraceReportSubmissionResult } from '../../src'; +import { BacktraceData } from '../../src/model/data/BacktraceData'; +import { BacktraceTestClient } from '../mocks/BacktraceTestClient'; + +describe('Client callbacks tests', () => { + describe('Submission data modification tests', () => { + it('Should invoke the before send event', async () => { + let triggered = false; + const client = BacktraceTestClient.buildFakeClient({ + beforeSend: (data) => { + triggered = true; + return data; + }, + }); + + await client.send(new Error()); + expect(triggered).toBeTruthy(); + }); + + it('Should allow to modify the report attribute', async () => { + const attributeName = 'test'; + const expectedAttributeVaue = 'invoked'; + const client = BacktraceTestClient.buildFakeClient({ + beforeSend: (data) => { + data.attributes[attributeName] = expectedAttributeVaue; + return data; + }, + }); + jest.spyOn(client.requestHandler, 'postError').mockImplementationOnce((_: string, data: BacktraceData) => { + expect(data.attributes[attributeName]).toEqual(expectedAttributeVaue); + return Promise.resolve(BacktraceReportSubmissionResult.Ok({})); + }); + + client.addAttribute({ [attributeName]: 'not-invoked' }); + + await client.send(new Error()); + }); + + it('Should skip sending events if the user decide not to send it', async () => { + const client = BacktraceTestClient.buildFakeClient({ + beforeSend: () => { + return undefined; + }, + }); + + await client.send(new Error()); + expect(client.requestHandler.postError).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/sdk-core/tests/client/clientTests.spec.ts b/packages/sdk-core/tests/client/clientTests.spec.ts index e87b63b8..a2921d6d 100644 --- a/packages/sdk-core/tests/client/clientTests.spec.ts +++ b/packages/sdk-core/tests/client/clientTests.spec.ts @@ -43,7 +43,7 @@ describe('Client tests', () => { }, }; - const client = BacktraceTestClient.buildFakeClient([], [inMemoryAttachment]); + const client = BacktraceTestClient.buildFakeClient({}, [], [inMemoryAttachment]); expect(client.attachments).toBeDefined(); expect(client.attachments.length).toEqual(1); @@ -90,7 +90,7 @@ describe('Client tests', () => { return new Uint8Array(0); }, }; - const client = BacktraceTestClient.buildFakeClient([], [clientAttachment]); + const client = BacktraceTestClient.buildFakeClient({}, [], [clientAttachment]); await client.send(new Error(''), {}, [reportAttachment]); diff --git a/packages/sdk-core/tests/mocks/BacktraceTestClient.ts b/packages/sdk-core/tests/mocks/BacktraceTestClient.ts index 27c47875..da8b5749 100644 --- a/packages/sdk-core/tests/mocks/BacktraceTestClient.ts +++ b/packages/sdk-core/tests/mocks/BacktraceTestClient.ts @@ -1,6 +1,7 @@ import { BacktraceAttachment, BacktraceAttributeProvider, + BacktraceConfiguration, BacktraceCoreClient, BacktraceReportSubmissionResult, BacktraceRequestHandler, @@ -13,18 +14,22 @@ export const APPLICATION_VERSION = '5.4.3'; export class BacktraceTestClient extends BacktraceCoreClient { public readonly requestHandler: BacktraceRequestHandler; constructor( + options: Partial, handler: BacktraceRequestHandler, attributeProviders: BacktraceAttributeProvider[] = [], attachments: BacktraceAttachment[] = [], ) { super( { - url: TEST_SUBMISSION_URL, - token: TOKEN, - attachments, - metrics: { - enable: false, + ...{ + url: TEST_SUBMISSION_URL, + token: TOKEN, + attachments, + metrics: { + enable: false, + }, }, + ...(options ?? {}), }, { agent: 'test', @@ -39,6 +44,7 @@ export class BacktraceTestClient extends BacktraceCoreClient { } public static buildFakeClient( + options: Partial = {}, attributeProviders: BacktraceAttributeProvider[] = [], attachments: BacktraceAttachment[] = [], ) { @@ -52,6 +58,7 @@ export class BacktraceTestClient extends BacktraceCoreClient { }, }); return new BacktraceTestClient( + options, { post: jest.fn().mockResolvedValue(Promise.resolve(BacktraceReportSubmissionResult.Ok('Ok'))), postError: jest.fn().mockResolvedValue(Promise.resolve()), From d93d63f167908738dfdc6d6bdbfd2a15c29319d9 Mon Sep 17 00:00:00 2001 From: Konrad Dysput Date: Thu, 27 Jul 2023 14:28:47 +0200 Subject: [PATCH 2/3] Skip report callback --- packages/sdk-core/src/BacktraceCoreClient.ts | 3 ++ .../configuration/BacktraceConfiguration.ts | 7 +++++ .../tests/client/clientCallbacksTests.spec.ts | 31 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/packages/sdk-core/src/BacktraceCoreClient.ts b/packages/sdk-core/src/BacktraceCoreClient.ts index 6e28694e..d6ec9cca 100644 --- a/packages/sdk-core/src/BacktraceCoreClient.ts +++ b/packages/sdk-core/src/BacktraceCoreClient.ts @@ -153,6 +153,9 @@ export abstract class BacktraceCoreClient { : new BacktraceReport(data, reportAttributes, [], { skipFrames: this.skipFrameOnMessage(data), }); + if (this.options.skipReport && this.options.skipReport(report)) { + return; + } const backtraceData = this.generateSubmissionData(report); if (!backtraceData) { diff --git a/packages/sdk-core/src/model/configuration/BacktraceConfiguration.ts b/packages/sdk-core/src/model/configuration/BacktraceConfiguration.ts index 847d1a28..8dd31aca 100644 --- a/packages/sdk-core/src/model/configuration/BacktraceConfiguration.ts +++ b/packages/sdk-core/src/model/configuration/BacktraceConfiguration.ts @@ -1,5 +1,6 @@ import { BacktraceAttachment } from '../attachment'; import { BacktraceData } from '../data/BacktraceData'; +import { BacktraceReport } from '../report/BacktraceReport'; import { BacktraceDatabaseConfiguration } from './BacktraceDatabaseConfiguration'; export interface BacktraceMetricsOptions { @@ -64,6 +65,12 @@ export interface BacktraceConfiguration { */ beforeSend?: (data: BacktraceData) => BacktraceData | undefined; + /** + * If you want to ignore specific types of error reports, we recommend that you use the skipReport callback. + * By using it, based on the data generated in the report, you can decide to filter the report, or send it to + * Backtrace. + */ + skipReport?: (report: BacktraceReport) => boolean; /** * Limits the number of reports the client will send per minute. If set to '0', there is no limit. * If set to a value greater than '0' and the value is reached, the client will not send any reports until the next minute. diff --git a/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts b/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts index 895317fa..d0d2a5e6 100644 --- a/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts +++ b/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts @@ -47,4 +47,35 @@ describe('Client callbacks tests', () => { expect(client.requestHandler.postError).not.toHaveBeenCalled(); }); }); + + describe('Report filtering tests', () => { + it('Should send a report if the filter is not set', async () => { + const client = BacktraceTestClient.buildFakeClient(); + + await client.send(new Error()); + expect(client.requestHandler.postError).toHaveBeenCalled(); + }); + + it('Should send a report if the filter returns false ', async () => { + const client = BacktraceTestClient.buildFakeClient({ + skipReport: () => { + return false; + }, + }); + + await client.send(new Error()); + expect(client.requestHandler.postError).toHaveBeenCalled(); + }); + + it('Should send a report if the filter returns true ', async () => { + const client = BacktraceTestClient.buildFakeClient({ + skipReport: () => { + return true; + }, + }); + + await client.send(new Error()); + expect(client.requestHandler.postError).not.toHaveBeenCalled(); + }); + }); }); From d827ff028c58980f31a432a77a52b001612738f9 Mon Sep 17 00:00:00 2001 From: Konrad Dysput Date: Mon, 31 Jul 2023 13:49:28 +0200 Subject: [PATCH 3/3] Test name fix --- packages/sdk-core/tests/client/clientCallbacksTests.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts b/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts index d0d2a5e6..16853d70 100644 --- a/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts +++ b/packages/sdk-core/tests/client/clientCallbacksTests.spec.ts @@ -67,7 +67,7 @@ describe('Client callbacks tests', () => { expect(client.requestHandler.postError).toHaveBeenCalled(); }); - it('Should send a report if the filter returns true ', async () => { + it('Should not send a report if the filter returns true ', async () => { const client = BacktraceTestClient.buildFakeClient({ skipReport: () => { return true;