From 7c331a3924deabe4a618f2ede2a59b19ac801f82 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Mon, 22 Apr 2024 15:40:03 +0200 Subject: [PATCH 1/5] feat(core): Add `dropEvent` options to `moduleMetadataIntegration` --- packages/core/src/integrations/metadata.ts | 43 ++++++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/core/src/integrations/metadata.ts b/packages/core/src/integrations/metadata.ts index 917d8a12beb8..6ffb7163280d 100644 --- a/packages/core/src/integrations/metadata.ts +++ b/packages/core/src/integrations/metadata.ts @@ -1,14 +1,36 @@ -import type { EventItem, IntegrationFn } from '@sentry/types'; +import type { EventItem, Exception, IntegrationFn } from '@sentry/types'; import { forEachEnvelopeItem } from '@sentry/utils'; import { defineIntegration } from '../integration'; import { addMetadataToStackFrames, stripMetadataFromStackFrames } from '../metadata'; -const INTEGRATION_NAME = 'ModuleMetadata'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type ModuleMetadata = any; -const _moduleMetadataIntegration = (() => { +function getAllModuleMetadata(exceptions: Exception[]): ModuleMetadata[] { + return exceptions.reduce( + (acc, exception) => { + if (exception.stacktrace && exception.stacktrace.frames) { + acc.push(...exception.stacktrace.frames.map(frame => frame.module_metadata)); + } + return acc; + }, + [] as ModuleMetadata[], + ); +} + +interface Options { + dropEvent?: { + /** + * Drop event if no stack frames have matching metadata + */ + ifNoStackFrameMetadataMatch?: (metadata: ModuleMetadata) => boolean; + }; +} + +const _moduleMetadataIntegration = ((options: Options = {}) => { return { - name: INTEGRATION_NAME, + name: 'ModuleMetadata', setup(client) { // We need to strip metadata from stack frames before sending them to Sentry since these are client side only. client.on('beforeEnvelope', envelope => { @@ -28,6 +50,19 @@ const _moduleMetadataIntegration = (() => { processEvent(event, _hint, client) { const stackParser = client.getOptions().stackParser; addMetadataToStackFrames(stackParser, event); + + if ( + event.exception && + event.exception.values && + options.dropEvent && + options.dropEvent.ifNoStackFrameMetadataMatch + ) { + const metadata = getAllModuleMetadata(event.exception.values); + if (!metadata.some(options.dropEvent.ifNoStackFrameMetadataMatch)) { + return null; + } + } + return event; }, }; From 2790aac2ad721608a4c37b1187c61d433ed46b02 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Mon, 22 Apr 2024 15:54:22 +0200 Subject: [PATCH 2/5] rename --- packages/core/src/integrations/metadata.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/integrations/metadata.ts b/packages/core/src/integrations/metadata.ts index 6ffb7163280d..299fc1a8f01c 100644 --- a/packages/core/src/integrations/metadata.ts +++ b/packages/core/src/integrations/metadata.ts @@ -24,7 +24,7 @@ interface Options { /** * Drop event if no stack frames have matching metadata */ - ifNoStackFrameMetadataMatch?: (metadata: ModuleMetadata) => boolean; + ifNoStackFrameMetadataMatches?: (metadata: ModuleMetadata) => boolean; }; } @@ -55,10 +55,10 @@ const _moduleMetadataIntegration = ((options: Options = {}) => { event.exception && event.exception.values && options.dropEvent && - options.dropEvent.ifNoStackFrameMetadataMatch + options.dropEvent.ifNoStackFrameMetadataMatches ) { const metadata = getAllModuleMetadata(event.exception.values); - if (!metadata.some(options.dropEvent.ifNoStackFrameMetadataMatch)) { + if (!metadata.some(options.dropEvent.ifNoStackFrameMetadataMatches)) { return null; } } From b1bc1bee9e70fc165220ef7e16d8fdbc0cd8a3b1 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Mon, 22 Apr 2024 17:11:27 +0200 Subject: [PATCH 3/5] Add tests --- .../test/lib/integrations/metadata.test.ts | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/packages/core/test/lib/integrations/metadata.test.ts b/packages/core/test/lib/integrations/metadata.test.ts index 7f53ce090275..b9da0ced3551 100644 --- a/packages/core/test/lib/integrations/metadata.test.ts +++ b/packages/core/test/lib/integrations/metadata.test.ts @@ -63,4 +63,67 @@ describe('ModuleMetadata integration', () => { captureException(new Error('Some error')); }); + + test('Drops event if no stack frames have matching metadata', done => { + expect.assertions(0); + + const options = getDefaultTestClientOptions({ + dsn: 'https://username@domain/123', + enableSend: true, + stackParser, + integrations: [ + moduleMetadataIntegration({ dropEvent: { ifNoStackFrameMetadataMatches: m => m?.team === 'backend' } }), + ], + transport: () => + createTransport({ recordDroppedEvent: () => undefined }, async req => { + expect(req.body).toBeUndefined(); + done(); + return {}; + }), + }); + + const client = new TestClient(options); + setCurrentClient(client); + client.init(); + + captureException(new Error('Some error')); + + setTimeout(() => { + done(); + }, 2000); + }); + + test('Sends event if stack frames have matching metadata', done => { + expect.assertions(1); + + let callbackCalled = false; + + const options = getDefaultTestClientOptions({ + dsn: 'https://username@domain/123', + enableSend: true, + stackParser, + integrations: [ + moduleMetadataIntegration({ + dropEvent: { + ifNoStackFrameMetadataMatches: m => { + callbackCalled = true; + return m?.team === 'frontend'; + }, + }, + }), + ], + transport: () => + createTransport({ recordDroppedEvent: () => undefined }, async _ => { + expect(callbackCalled).toBe(true); + done(); + return {}; + }), + }); + + const client = new TestClient(options); + setCurrentClient(client); + client.init(); + + captureException(new Error('Some error')); + }); }); From 1fcea587def0aed6ae1e0b4a5c112d789360bcbb Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 30 Apr 2024 14:03:10 +0100 Subject: [PATCH 4/5] Ensure metadata is never undefined --- packages/core/src/integrations/metadata.ts | 2 +- packages/core/test/lib/integrations/metadata.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/src/integrations/metadata.ts b/packages/core/src/integrations/metadata.ts index 299fc1a8f01c..7c19183744bf 100644 --- a/packages/core/src/integrations/metadata.ts +++ b/packages/core/src/integrations/metadata.ts @@ -11,7 +11,7 @@ function getAllModuleMetadata(exceptions: Exception[]): ModuleMetadata[] { return exceptions.reduce( (acc, exception) => { if (exception.stacktrace && exception.stacktrace.frames) { - acc.push(...exception.stacktrace.frames.map(frame => frame.module_metadata)); + acc.push(...exception.stacktrace.frames.map(frame => frame.module_metadata).filter(metadata => !!metadata)); } return acc; }, diff --git a/packages/core/test/lib/integrations/metadata.test.ts b/packages/core/test/lib/integrations/metadata.test.ts index b9da0ced3551..c0ae653011d5 100644 --- a/packages/core/test/lib/integrations/metadata.test.ts +++ b/packages/core/test/lib/integrations/metadata.test.ts @@ -72,7 +72,7 @@ describe('ModuleMetadata integration', () => { enableSend: true, stackParser, integrations: [ - moduleMetadataIntegration({ dropEvent: { ifNoStackFrameMetadataMatches: m => m?.team === 'backend' } }), + moduleMetadataIntegration({ dropEvent: { ifNoStackFrameMetadataMatches: m => m.team === 'backend' } }), ], transport: () => createTransport({ recordDroppedEvent: () => undefined }, async req => { @@ -107,7 +107,7 @@ describe('ModuleMetadata integration', () => { dropEvent: { ifNoStackFrameMetadataMatches: m => { callbackCalled = true; - return m?.team === 'frontend'; + return m.team === 'frontend'; }, }, }), From f83289acf603f4d1c3245990e919da906b25c753 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 30 Apr 2024 14:11:33 +0100 Subject: [PATCH 5/5] Add undefined to callback types --- packages/core/src/integrations/metadata.ts | 4 ++-- packages/core/test/lib/integrations/metadata.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/integrations/metadata.ts b/packages/core/src/integrations/metadata.ts index 7c19183744bf..28207ff188c3 100644 --- a/packages/core/src/integrations/metadata.ts +++ b/packages/core/src/integrations/metadata.ts @@ -11,7 +11,7 @@ function getAllModuleMetadata(exceptions: Exception[]): ModuleMetadata[] { return exceptions.reduce( (acc, exception) => { if (exception.stacktrace && exception.stacktrace.frames) { - acc.push(...exception.stacktrace.frames.map(frame => frame.module_metadata).filter(metadata => !!metadata)); + acc.push(...exception.stacktrace.frames.map(frame => frame.module_metadata)); } return acc; }, @@ -24,7 +24,7 @@ interface Options { /** * Drop event if no stack frames have matching metadata */ - ifNoStackFrameMetadataMatches?: (metadata: ModuleMetadata) => boolean; + ifNoStackFrameMetadataMatches?: (metadata: ModuleMetadata | undefined) => boolean; }; } diff --git a/packages/core/test/lib/integrations/metadata.test.ts b/packages/core/test/lib/integrations/metadata.test.ts index c0ae653011d5..b9da0ced3551 100644 --- a/packages/core/test/lib/integrations/metadata.test.ts +++ b/packages/core/test/lib/integrations/metadata.test.ts @@ -72,7 +72,7 @@ describe('ModuleMetadata integration', () => { enableSend: true, stackParser, integrations: [ - moduleMetadataIntegration({ dropEvent: { ifNoStackFrameMetadataMatches: m => m.team === 'backend' } }), + moduleMetadataIntegration({ dropEvent: { ifNoStackFrameMetadataMatches: m => m?.team === 'backend' } }), ], transport: () => createTransport({ recordDroppedEvent: () => undefined }, async req => { @@ -107,7 +107,7 @@ describe('ModuleMetadata integration', () => { dropEvent: { ifNoStackFrameMetadataMatches: m => { callbackCalled = true; - return m.team === 'frontend'; + return m?.team === 'frontend'; }, }, }),