Skip to content

Commit 3ac9bcb

Browse files
committed
feat(api): add assignee filter to project daily events search
1 parent c4d0111 commit 3ac9bcb

File tree

4 files changed

+117
-3
lines changed

4 files changed

+117
-3
lines changed

src/models/eventsFactory.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ class EventsFactory extends Factory {
216216
sort = 'BY_DATE',
217217
filters = {},
218218
search = '',
219-
release
219+
release,
220+
assignee
220221
) {
221222
if (typeof search !== 'string') {
222223
throw new Error('Search parameter must be a string');
@@ -334,10 +335,12 @@ class EventsFactory extends Factory {
334335
}
335336
: {};
336337

338+
const markFilters = ['resolved', 'starred', 'ignored'];
337339
const matchFilter = filters
338340
? Object.fromEntries(
339341
Object
340342
.entries(filters)
343+
.filter(([mark]) => markFilters.includes(mark))
341344
.map(([mark, exists]) => [`event.marks.${mark}`, { $exists: exists } ])
342345
)
343346
: {};
@@ -361,6 +364,10 @@ class EventsFactory extends Factory {
361364
}
362365
: {};
363366

367+
const assigneeFilter = assignee
368+
? { 'event.assignee': String(assignee) }
369+
: {};
370+
364371
pipeline.push(
365372
/**
366373
* Left outer join original event on groupHash field
@@ -398,6 +405,7 @@ class EventsFactory extends Factory {
398405
...matchFilter,
399406
...searchFilter,
400407
...releaseFilter,
408+
...assigneeFilter,
401409
},
402410
},
403411
{ $limit: limit + 1 },

src/resolvers/project.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ module.exports = {
583583
*
584584
* @return {Promise<RecentEventSchema[]>}
585585
*/
586-
async dailyEventsPortion(project, { limit, nextCursor, sort, filters, search, release }, context) {
586+
async dailyEventsPortion(project, { limit, nextCursor, sort, filters, search, release, assignee }, context) {
587587
if (search) {
588588
if (search.length > MAX_SEARCH_QUERY_LENGTH) {
589589
search = search.slice(0, MAX_SEARCH_QUERY_LENGTH);
@@ -592,7 +592,15 @@ module.exports = {
592592

593593
const factory = getEventsFactory(context, project._id);
594594

595-
const dailyEventsPortion = await factory.findDailyEventsPortion(limit, nextCursor, sort, filters, search, release);
595+
const dailyEventsPortion = await factory.findDailyEventsPortion(
596+
limit,
597+
nextCursor,
598+
sort,
599+
filters,
600+
search,
601+
release,
602+
assignee
603+
);
596604

597605
return dailyEventsPortion;
598606
},

src/typeDefs/project.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ input EventsFiltersInput {
123123
If True, includes events with ignored mark to the output
124124
"""
125125
ignored: Boolean
126+
"""
127+
Includes only events assigned to passed user id
128+
"""
129+
assignee: ID
126130
}
127131
128132
"""
@@ -347,6 +351,11 @@ type Project {
347351
Release label to filter events by payload.release
348352
"""
349353
release: String
354+
355+
"""
356+
User id to filter events by assignee
357+
"""
358+
assignee: ID
350359
): DailyEventsPortion
351360
352361
"""
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import '../../src/env-test';
2+
3+
jest.mock('../../src/integrations/github/service', () => require('../__mocks__/github-service'));
4+
5+
jest.mock('../../src/resolvers/helpers/eventsFactory', () => ({
6+
__esModule: true,
7+
default: jest.fn(),
8+
}));
9+
10+
// @ts-expect-error - CommonJS module, TypeScript can't infer types properly
11+
import projectResolverModule from '../../src/resolvers/project';
12+
import getEventsFactory from '../../src/resolvers/helpers/eventsFactory';
13+
14+
const projectResolver = projectResolverModule as {
15+
Project: {
16+
dailyEventsPortion: (...args: unknown[]) => Promise<unknown>;
17+
};
18+
};
19+
20+
describe('Project resolver dailyEventsPortion', () => {
21+
beforeEach(() => {
22+
jest.clearAllMocks();
23+
});
24+
25+
it('should pass assignee filter to events factory', async () => {
26+
const findDailyEventsPortion = jest.fn().mockResolvedValue({
27+
nextCursor: null,
28+
dailyEvents: [],
29+
});
30+
(getEventsFactory as unknown as jest.Mock).mockReturnValue({
31+
findDailyEventsPortion,
32+
});
33+
34+
const project = { _id: 'project-1' };
35+
const args = {
36+
limit: 50,
37+
nextCursor: null,
38+
sort: 'BY_DATE',
39+
filters: { ignored: true },
40+
search: 'TypeError',
41+
release: '1.0.0',
42+
assignee: 'user-123',
43+
};
44+
45+
await projectResolver.Project.dailyEventsPortion(project, args, {});
46+
47+
expect(findDailyEventsPortion).toHaveBeenCalledWith(
48+
50,
49+
null,
50+
'BY_DATE',
51+
{ ignored: true },
52+
'TypeError',
53+
'1.0.0',
54+
'user-123'
55+
);
56+
});
57+
58+
it('should keep old call shape when assignee is not provided', async () => {
59+
const findDailyEventsPortion = jest.fn().mockResolvedValue({
60+
nextCursor: null,
61+
dailyEvents: [],
62+
});
63+
(getEventsFactory as unknown as jest.Mock).mockReturnValue({
64+
findDailyEventsPortion,
65+
});
66+
67+
const project = { _id: 'project-1' };
68+
const args = {
69+
limit: 10,
70+
nextCursor: null,
71+
sort: 'BY_DATE',
72+
filters: {},
73+
search: '',
74+
release: undefined,
75+
};
76+
77+
await projectResolver.Project.dailyEventsPortion(project, args, {});
78+
79+
expect(findDailyEventsPortion).toHaveBeenCalledWith(
80+
10,
81+
null,
82+
'BY_DATE',
83+
{},
84+
'',
85+
undefined,
86+
undefined
87+
);
88+
});
89+
});

0 commit comments

Comments
 (0)