Skip to content

Commit 01d32b1

Browse files
Update prod (#504)
* chore(event-email): event email notification now contains link to a particular repetition (#499) * Add repetitionId to event notification flow Introduces the repetitionId field to event notification data structures and templates, allowing emails and notifications to reference specific event repetitions. Updates TypeScript interfaces, worker logic, and email templates to support and display repetitionId where applicable. * fix grouper test * fix(sentry): replay skipping improved (#503) * fix sentry replay skipping * lint code * Update index.test.ts --------- Co-authored-by: Peter <[email protected]>
1 parent b53217a commit 01d32b1

File tree

10 files changed

+146
-21
lines changed

10 files changed

+146
-21
lines changed

lib/memoize/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function memoize(options: MemoizeOptions = {}): MethodDecorator {
4545
max = 50,
4646
ttl = 1000 * 60 * 30,
4747
strategy = 'concat',
48-
skipCache = []
48+
skipCache = [],
4949
} = options;
5050
/* eslint-enable */
5151

workers/archiver/src/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default class ArchiverWorker extends Worker {
8686

8787
const projects = await this.projectCollection.find({}).project({
8888
_id: 1,
89-
name: 1
89+
name: 1,
9090
});
9191
const projectsData: ReportDataByProject[] = [];
9292

@@ -155,11 +155,11 @@ export default class ArchiverWorker extends Worker {
155155
await this.projectCollection.updateOne({
156156
_id: project._id,
157157
},
158-
{
159-
$inc: {
160-
archivedEventsCount: deletedCount,
161-
},
162-
});
158+
{
159+
$inc: {
160+
archivedEventsCount: deletedCount,
161+
},
162+
});
163163
}
164164

165165
/**
@@ -351,7 +351,7 @@ export default class ArchiverWorker extends Worker {
351351
this.logger.info('Report notification response:', {
352352
status: response?.status,
353353
statusText: response?.statusText,
354-
data: response?.data
354+
data: response?.data,
355355
});
356356
}
357357

workers/email/scripts/emailOverview.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class EmailTestServer {
156156
tariffPlanId: '5f47f031ff71510040f433c1',
157157
password: '1as2eadd321a3cDf',
158158
plan: {
159-
name: 'Корпоративный'
159+
name: 'Корпоративный',
160160
},
161161
workspaceName: workspace.name,
162162
};

workers/javascript/tests/index.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,5 +442,4 @@ describe('JavaScript event worker', () => {
442442

443443
await worker.finish();
444444
});
445-
446445
});

workers/limiter/tests/dbHelper.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ describe('DbHelper', () => {
304304
/**
305305
* Act
306306
*/
307-
await dbHelper.updateWorkspacesEventsCountAndIsBlocked([updatedWorkspace]);
307+
await dbHelper.updateWorkspacesEventsCountAndIsBlocked([ updatedWorkspace ]);
308308

309309
/**
310310
* Assert

workers/paymaster/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,17 @@ export default class PaymasterWorker extends Worker {
160160

161161
/**
162162
* Finds plan by id from cached plans
163+
*
164+
* @param planId
163165
*/
164166
private findPlanById(planId: WorkspaceDBScheme['tariffPlanId']): PlanDBScheme | undefined {
165167
return this.plans.find((plan) => plan._id.toString() === planId.toString());
166168
}
167169

168170
/**
169171
* Returns workspace plan, refreshes cache when plan is missing
172+
*
173+
* @param workspace
170174
*/
171175
private async getWorkspacePlan(workspace: WorkspaceDBScheme): Promise<PlanDBScheme> {
172176
let currentPlan = this.findPlanById(workspace.tariffPlanId);
@@ -413,7 +417,6 @@ export default class PaymasterWorker extends Worker {
413417
});
414418
}
415419

416-
417420
/**
418421
* Sends reminder emails to blocked workspace admins
419422
*

workers/paymaster/tests/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ describe('PaymasterWorker', () => {
317317
}
318318

319319
MockDate.reset();
320+
320321
return addTaskSpy;
321322
};
322323

workers/release/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ export default class ReleaseWorker extends Worker {
281281
/**
282282
* Some bundlers could skip file in the source map content since it duplicates in map name
283283
* Like map name bundle.js.map is a source map for a bundle.js
284+
*
284285
* @see https://sourcemaps.info/spec.html - format
285286
*/
286287
originFileName: mapContent.file ?? file.name.replace(/\.map$/, ''),

workers/sentry/src/index.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ export default class SentryEventWorker extends Worker {
6767

6868
/**
6969
* Filter out binary items that crash parseEnvelope
70+
* Also filters out all Sentry Replay events (replay_event and replay_recording)
7071
*/
7172
private filterOutBinaryItems(rawEvent: string): string {
7273
const lines = rawEvent.split('\n');
7374
const filteredLines = [];
75+
let isInReplayBlock = false;
7476

7577
for (let i = 0; i < lines.length; i++) {
7678
const line = lines[i];
@@ -90,17 +92,42 @@ export default class SentryEventWorker extends Worker {
9092
// Try to parse as JSON to check if it's a header
9193
const parsed = JSON.parse(line);
9294

93-
// If it's a replay header, skip this line and the next one (payload)
95+
// Check if this is a replay event type
9496
if (parsed.type === 'replay_recording' || parsed.type === 'replay_event') {
95-
// Skip the next line too (which would be the payload)
96-
i++;
97+
// Mark that we're in a replay block and skip this line
98+
isInReplayBlock = true;
9799
continue;
98100
}
99101

100-
// Keep valid headers and other JSON data
101-
filteredLines.push(line);
102+
// If we're in a replay block, check if this is still part of it
103+
if (isInReplayBlock) {
104+
// Check if this line is part of replay data (segment_id, length, etc.)
105+
if ('segment_id' in parsed || ('length' in parsed && parsed.type !== 'event') || 'replay_id' in parsed) {
106+
// Still in replay block, skip this line
107+
continue;
108+
}
109+
110+
// If it's a new envelope item (like event), we've exited the replay block
111+
if (parsed.type === 'event' || parsed.type === 'transaction' || parsed.type === 'session') {
112+
isInReplayBlock = false;
113+
} else {
114+
// Unknown type, assume we're still in replay block
115+
continue;
116+
}
117+
}
118+
119+
// Keep valid headers and other JSON data (not in replay block)
120+
if (!isInReplayBlock) {
121+
filteredLines.push(line);
122+
}
102123
} catch {
103-
// If line doesn't parse as JSON, it might be binary data - skip it
124+
// If line doesn't parse as JSON, it might be binary data
125+
// If we're in a replay block, skip it (it's part of replay recording)
126+
if (isInReplayBlock) {
127+
continue;
128+
}
129+
130+
// If not in replay block and not JSON, it might be corrupted data - skip it
104131
continue;
105132
}
106133
}

workers/sentry/tests/index.test.ts

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -806,12 +806,18 @@ describe('SentryEventWorker', () => {
806806
event_id: '4c40fee730194a989439a86bf75634111',
807807
sent_at: '2025-08-29T10:59:29.952Z',
808808
/* eslint-enable @typescript-eslint/naming-convention */
809-
sdk: { name: 'sentry.javascript.react', version: '9.10.1' },
809+
sdk: {
810+
name: 'sentry.javascript.react',
811+
version: '9.10.1',
812+
},
810813
}),
811814
// Event item header
812815
JSON.stringify({ type: 'event' }),
813816
// Event item payload
814-
JSON.stringify({ message: 'Test event', level: 'error' }),
817+
JSON.stringify({
818+
message: 'Test event',
819+
level: 'error',
820+
}),
815821
// Replay event item header - should be filtered out
816822
JSON.stringify({ type: 'replay_event' }),
817823
// Replay event item payload - should be filtered out
@@ -822,7 +828,10 @@ describe('SentryEventWorker', () => {
822828
/* eslint-enable @typescript-eslint/naming-convention */
823829
}),
824830
// Replay recording item header - should be filtered out
825-
JSON.stringify({ type: 'replay_recording', length: 343 }),
831+
JSON.stringify({
832+
type: 'replay_recording',
833+
length: 343,
834+
}),
826835
// Replay recording binary payload - should be filtered out
827836
'binary-data-here-that-is-not-json',
828837
];
@@ -841,6 +850,7 @@ describe('SentryEventWorker', () => {
841850
expect(mockedAmqpChannel.sendToQueue).toHaveBeenCalledTimes(1);
842851

843852
const addedTaskPayload = getAddTaskPayloadFromLastCall();
853+
844854
expect(addedTaskPayload).toMatchObject({
845855
payload: expect.objectContaining({
846856
addons: {
@@ -852,6 +862,90 @@ describe('SentryEventWorker', () => {
852862
}),
853863
});
854864
});
865+
866+
it('should ignore envelope with only replay_event and replay_recording items', async () => {
867+
/**
868+
* Test case based on real-world scenario where envelope contains only replay data
869+
* This should not crash with "Unexpected end of JSON input" error
870+
*/
871+
const envelopeLines = [
872+
// Envelope header
873+
JSON.stringify({
874+
/* eslint-disable @typescript-eslint/naming-convention */
875+
event_id: '62680958b3ab4497886375e06533d86a',
876+
sent_at: '2025-12-24T13:16:34.580Z',
877+
/* eslint-enable @typescript-eslint/naming-convention */
878+
sdk: {
879+
name: 'sentry.javascript.react',
880+
version: '10.22.0',
881+
},
882+
}),
883+
// Replay event item header - should be filtered out
884+
JSON.stringify({ type: 'replay_event' }),
885+
// Replay event item payload (large JSON) - should be filtered out
886+
JSON.stringify({
887+
/* eslint-disable @typescript-eslint/naming-convention */
888+
type: 'replay_event',
889+
replay_start_timestamp: 1766582182.757,
890+
timestamp: 1766582194.579,
891+
error_ids: [],
892+
trace_ids: [],
893+
urls: ['https://my.huntio.ru/applicants', 'https://my.huntio.ru/applicants/1270067'],
894+
replay_id: '62680958b3ab4497886375e06533d86a',
895+
segment_id: 1,
896+
replay_type: 'session',
897+
request: {
898+
url: 'https://my.huntio.ru/applicants/1270067',
899+
headers: {
900+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
901+
},
902+
},
903+
event_id: '62680958b3ab4497886375e06533d86a',
904+
environment: 'production',
905+
release: '1.0.7',
906+
sdk: {
907+
integrations: ['InboundFilters', 'FunctionToString', 'BrowserApiErrors', 'Breadcrumbs'],
908+
name: 'sentry.javascript.react',
909+
version: '10.22.0',
910+
settings: { infer_ip: 'auto' },
911+
},
912+
user: {
913+
id: 487,
914+
915+
username: 'Прохорова Наталья',
916+
},
917+
contexts: { react: { version: '19.1.0' } },
918+
transaction: '/applicants/1270067',
919+
platform: 'javascript',
920+
/* eslint-enable @typescript-eslint/naming-convention */
921+
}),
922+
// Replay recording item header - should be filtered out
923+
JSON.stringify({
924+
type: 'replay_recording',
925+
length: 16385,
926+
}),
927+
/* eslint-disable @typescript-eslint/naming-convention */
928+
// Segment ID - should be filtered out
929+
JSON.stringify({ segment_id: 1 }),
930+
/* eslint-enable @typescript-eslint/naming-convention */
931+
// Binary data (simulated) - should be filtered out
932+
'xnFWy@v$xAlJ=&fS~¾˶IJ<Dڒ%8yX]]ˣ·9V|JGd!+%fF',
933+
];
934+
935+
const envelopeString = envelopeLines.join('\n');
936+
937+
// Should not throw "Unexpected end of JSON input" error
938+
await worker.handle({
939+
payload: {
940+
envelope: b64encode(envelopeString),
941+
},
942+
projectId: '123',
943+
catcherType: 'external/sentry',
944+
});
945+
946+
// Should not send any tasks since all items were replay-related and filtered out
947+
expect(mockedAmqpChannel.sendToQueue).not.toHaveBeenCalled();
948+
});
855949
});
856950

857951
describe('envelope parsing', () => {

0 commit comments

Comments
 (0)