Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions workers/grouper/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Worker } from '../../../lib/worker';
import * as WorkerNames from '../../../lib/workerNames';
import * as pkg from '../package.json';
import type { GroupWorkerTask, RepetitionDelta } from '../types/group-worker-task';
import type { EventAddons, EventDataAccepted, GroupedEventDBScheme, BacktraceFrame, SourceCodeLine } from '@hawk.so/types';
import type { EventAddons, EventDataAccepted, GroupedEventDBScheme, BacktraceFrame, SourceCodeLine, ProjectEventGroupingPatternsDBScheme } from '@hawk.so/types';
import type { RepetitionDBScheme } from '../types/repetition';
import { DatabaseReadWriteError, DiffCalculationError, ValidationError } from '../../../lib/workerErrors';
import { decodeUnsafeFields, encodeUnsafeFields } from '../../../lib/utils/unsafeFields';
Expand Down Expand Up @@ -317,11 +317,11 @@ export default class GrouperWorker extends Worker {

if (matchingPattern !== null && matchingPattern !== undefined) {
try {
const originalEvent = await this.cache.get(`${projectId}:${matchingPattern}:originalEvent`, async () => {
const originalEvent = await this.cache.get(`${projectId}:${matchingPattern._id}:originalEvent`, async () => {
return await this.eventsDb.getConnection()
.collection(`events:${projectId}`)
.findOne(
{ 'payload.title': { $regex: matchingPattern } },
{ 'payload.title': { $regex: matchingPattern.pattern } },
{ sort: { _id: 1 } }
);
});
Expand All @@ -332,7 +332,7 @@ export default class GrouperWorker extends Worker {
return originalEvent;
}
} catch (e) {
this.logger.error(`Error while getting original event for pattern ${matchingPattern}`);
this.logger.error(`Error while getting original event for pattern ${matchingPattern}: ${e.message}`);
}
}
}
Expand All @@ -345,15 +345,18 @@ export default class GrouperWorker extends Worker {
*
* @param patterns - list of the patterns of the related project
* @param event - event which title would be cheched
* @returns {string | null} matched pattern or null if no match
* @returns {ProjectEventGroupingPatternsDBScheme | null} matched pattern object or null if no match
*/
private async findMatchingPattern(patterns: string[], event: EventDataAccepted<EventAddons>): Promise<string | null> {
private async findMatchingPattern(
patterns: ProjectEventGroupingPatternsDBScheme[],
event: EventDataAccepted<EventAddons>
): Promise<ProjectEventGroupingPatternsDBScheme | null> {
if (!patterns || patterns.length === 0) {
return null;
}

return patterns.filter(pattern => {
const patternRegExp = new RegExp(pattern);
const patternRegExp = new RegExp(pattern.pattern);

return event.title.match(patternRegExp);
}).pop() || null;
Expand All @@ -363,9 +366,9 @@ export default class GrouperWorker extends Worker {
* Method that gets event patterns for a project
*
* @param projectId - id of the project to find related event patterns
* @returns {string[]} EventPatterns object with projectId and list of patterns
* @returns {ProjectEventGroupingPatternsDBScheme[]} EventPatterns object with projectId and list of patterns
*/
private async getProjectPatterns(projectId: string): Promise<string[]> {
private async getProjectPatterns(projectId: string): Promise<ProjectEventGroupingPatternsDBScheme[]> {
return this.cache.get(`project:${projectId}:patterns`, async () => {
const project = await this.accountsDb.getConnection()
.collection('projects')
Expand Down
24 changes: 13 additions & 11 deletions workers/grouper/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
unreadCount: 0,
description: 'Test project for grouper worker tests',
eventGroupingPatterns: [ 'New error .*' ],
eventGroupingPatterns: [ { _id: mongodb.ObjectId(), pattern: 'New error .*' }],
};

/**
Expand Down Expand Up @@ -490,14 +490,16 @@
});

test('should group events with titles matching one pattern', async () => {
jest.spyOn(GrouperWorker.prototype as any, 'getProjectPatterns').mockResolvedValue([ 'New error .*' ]);
jest.spyOn(GrouperWorker.prototype as any, 'getProjectPatterns').mockResolvedValue([
{ _id: new mongodb.ObjectId, pattern: 'New error .*' }
]);
const findMatchingPatternSpy = jest.spyOn(GrouperWorker.prototype as any, 'findMatchingPattern');

Check warning on line 496 in workers/grouper/tests/index.test.ts

View workflow job for this annotation

GitHub Actions / ESlint

Unexpected any. Specify a different type

await worker.handle(generateTask({ title: 'New error 0000000000000000' }));
await worker.handle(generateTask({ title: 'New error 1111111111111111' }));
await worker.handle(generateTask({ title: 'New error 2222222222222222' }));

const originalEvent = await eventsCollection.findOne({});

Check warning on line 502 in workers/grouper/tests/index.test.ts

View workflow job for this annotation

GitHub Actions / ESlint

Unexpected any. Specify a different type

expect(findMatchingPatternSpy).toHaveBeenCalledTimes(3);
expect((await repetitionsCollection.find({
Expand All @@ -507,12 +509,12 @@

test('should handle multiple patterns and match the first one that applies', async () => {
jest.spyOn(GrouperWorker.prototype as any, 'getProjectPatterns').mockResolvedValue([
'Database error: .*',
'Network error: .*',
'New error: .*',
{ _id: mongodb.ObjectId(), pattern: 'Database error: .*' },
{ _id: mongodb.ObjectId(), pattern: 'Network error: .*' },
{ _id: mongodb.ObjectId(), pattern: 'New error: .*' },
]);

await worker.handle(generateTask({ title: 'Database error: connection failed' }));

Check warning on line 517 in workers/grouper/tests/index.test.ts

View workflow job for this annotation

GitHub Actions / ESlint

Unexpected any. Specify a different type
await worker.handle(generateTask({ title: 'Database error: timeout' }));
await worker.handle(generateTask({ title: 'Network error: timeout' }));

Expand All @@ -526,8 +528,8 @@

test('should handle complex regex patterns', async () => {
jest.spyOn(GrouperWorker.prototype as any, 'getProjectPatterns').mockResolvedValue([
'Error \\d{3}: [A-Za-z\\s]+ in file .*\\.js$',
'Warning \\d{3}: .*',
{ _id: mongodb.ObjectId(), pattern: 'Error \\d{3}: [A-Za-z\\s]+ in file .*\\.js$' },
{ _id: mongodb.ObjectId(), pattern: 'Warning \\d{3}: .*' },
]);

await worker.handle(generateTask({ title: 'Error 404: Not Found in file index.js' }));
Expand All @@ -540,12 +542,12 @@
expect(error404Events.length).toBe(1);
expect(warningEvents.length).toBe(1);
expect(await repetitionsCollection.find().count()).toBe(1);
});

Check warning on line 545 in workers/grouper/tests/index.test.ts

View workflow job for this annotation

GitHub Actions / ESlint

Unexpected any. Specify a different type

test('should maintain separate groups for different patterns', async () => {
jest.spyOn(GrouperWorker.prototype as any, 'getProjectPatterns').mockResolvedValue([
'TypeError: .*',
'ReferenceError: .*',
{ _id: mongodb.ObjectId(), pattern: 'TypeError: .*' },
{ _id: mongodb.ObjectId(), pattern: 'ReferenceError: .*' },
]);

await worker.handle(generateTask({ title: 'TypeError: null is not an object' }));
Expand All @@ -564,10 +566,10 @@
expect(typeErrors[0].groupHash).not.toBe(referenceErrors[0].groupHash);
});

test('should handle patterns with special regex characters', async () => {

Check warning on line 569 in workers/grouper/tests/index.test.ts

View workflow job for this annotation

GitHub Actions / ESlint

Unexpected any. Specify a different type
jest.spyOn(GrouperWorker.prototype as any, 'getProjectPatterns').mockResolvedValue([
'Error \\[\\d+\\]: .*',
'Warning \\(code=\\d+\\): .*',
{ _id: new mongodb.ObjectID(), pattern: 'Error \\[\\d+\\]: .*'} ,
{ _id: new mongodb.ObjectID(), pattern: 'Warning \\(code=\\d+\\): .*'} ,
]);

await worker.handle(generateTask({ title: 'Error [123]: Database connection failed' }));
Expand All @@ -592,3 +594,3 @@
await connection.close();
});
});
Loading