Skip to content

Commit f8047f6

Browse files
authored
fix(browser): Ensure dedupe integration ignores non-errors (#7172)
1 parent f993317 commit f8047f6

File tree

4 files changed

+72
-2
lines changed

4 files changed

+72
-2
lines changed

packages/browser/src/integrations/dedupe.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export class Dedupe implements Integration {
2323
*/
2424
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
2525
const eventProcessor: EventProcessor = currentEvent => {
26+
// We want to ignore any non-error type events, e.g. transactions or replays
27+
// These should never be deduped, and also not be compared against as _previousEvent.
28+
if (currentEvent.type) {
29+
return currentEvent;
30+
}
31+
2632
const self = getCurrentHub().getIntegration(Dedupe);
2733
if (self) {
2834
// Juuust in case something goes wrong

packages/browser/test/integration/suites/api.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,26 @@ describe('API', function () {
112112

113113
// Same exceptions, different stacktrace (different line number), don't dedupe
114114
throwSameConsecutiveErrors('bar');
115+
116+
// Same exception, with transaction in between, dedupe
117+
throwError();
118+
Sentry.captureEvent({
119+
event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2',
120+
message: 'someMessage',
121+
transaction: 'wat',
122+
type: 'transaction',
123+
});
124+
throwError();
115125
}).then(function (summary) {
126+
// We have a length of one here since transactions don't go through beforeSend
127+
// and we add events to summary in beforeSend
128+
assert.equal(summary.events.length, 6);
116129
assert.match(summary.events[0].exception.values[0].value, /Exception no \d+/);
117130
assert.match(summary.events[1].exception.values[0].value, /Exception no \d+/);
118131
assert.equal(summary.events[2].exception.values[0].value, 'foo');
119132
assert.equal(summary.events[3].exception.values[0].value, 'bar');
120133
assert.equal(summary.events[4].exception.values[0].value, 'bar');
134+
assert.equal(summary.events[5].exception.values[0].value, 'foo');
121135
});
122136
});
123137

packages/integrations/src/dedupe.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export class Dedupe implements Integration {
2323
*/
2424
public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void {
2525
const eventProcessor: EventProcessor = currentEvent => {
26+
// We want to ignore any non-error type events, e.g. transactions or replays
27+
// These should never be deduped, and also not be compared against as _previousEvent.
28+
if (currentEvent.type) {
29+
return currentEvent;
30+
}
31+
2632
const self = getCurrentHub().getIntegration(Dedupe);
2733
if (self) {
2834
// Juuust in case something goes wrong

packages/integrations/test/dedupe.test.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { Event as SentryEvent, Exception, StackFrame, Stacktrace } from '@sentry/types';
1+
import type { Event as SentryEvent, EventProcessor, Exception, Hub, StackFrame, Stacktrace } from '@sentry/types';
22

3-
import { _shouldDropEvent } from '../src/dedupe';
3+
import { _shouldDropEvent, Dedupe } from '../src/dedupe';
44

55
type EventWithException = SentryEvent & {
66
exception: {
@@ -175,4 +175,48 @@ describe('Dedupe', () => {
175175
expect(_shouldDropEvent(eventB, eventC)).toBe(false);
176176
});
177177
});
178+
179+
describe('setupOnce', () => {
180+
let dedupeFunc: EventProcessor;
181+
182+
beforeEach(function () {
183+
const integration = new Dedupe();
184+
const addGlobalEventProcessor = (callback: EventProcessor) => {
185+
dedupeFunc = callback;
186+
};
187+
188+
const getCurrentHub = () => {
189+
return {
190+
getIntegration() {
191+
return integration;
192+
},
193+
} as unknown as Hub;
194+
};
195+
196+
integration.setupOnce(addGlobalEventProcessor, getCurrentHub);
197+
});
198+
199+
it('ignores consecutive errors', () => {
200+
expect(dedupeFunc(clone(exceptionEvent), {})).not.toBeNull();
201+
expect(dedupeFunc(clone(exceptionEvent), {})).toBeNull();
202+
expect(dedupeFunc(clone(exceptionEvent), {})).toBeNull();
203+
});
204+
205+
it('ignores transactions between errors', () => {
206+
expect(dedupeFunc(clone(exceptionEvent), {})).not.toBeNull();
207+
expect(
208+
dedupeFunc(
209+
{
210+
event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2',
211+
message: 'someMessage',
212+
transaction: 'wat',
213+
type: 'transaction',
214+
},
215+
{},
216+
),
217+
).not.toBeNull();
218+
expect(dedupeFunc(clone(exceptionEvent), {})).toBeNull();
219+
expect(dedupeFunc(clone(exceptionEvent), {})).toBeNull();
220+
});
221+
});
178222
});

0 commit comments

Comments
 (0)