Skip to content

Commit 31dea62

Browse files
authored
Merge pull request #635 from codex-team/feat/events-assignee-filter
Feat/events assignee filter
2 parents 3d9de27 + e74a018 commit 31dea62

8 files changed

Lines changed: 127 additions & 5 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hawk.api",
3-
"version": "1.4.7",
3+
"version": "1.4.10",
44
"main": "index.ts",
55
"license": "BUSL-1.1",
66
"scripts": {

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/billingNew.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export default {
8686
currency: string;
8787
checksum: string;
8888
nextPaymentDate: Date;
89+
cloudPaymentsPublicId: string;
8990
}> {
9091
const { workspaceId, tariffPlanId, shouldSaveCard } = input;
9192

@@ -178,6 +179,7 @@ debug: ${Boolean(workspace.isDebug)}`
178179
currency: 'RUB',
179180
checksum,
180181
nextPaymentDate,
182+
cloudPaymentsPublicId: process.env.CLOUDPAYMENTS_PUBLIC_ID || '',
181183
};
182184
},
183185
},

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/resolvers/workspace.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,8 @@ module.exports = {
436436

437437
const defaultPlan = await factories.plansFactory.getDefaultPlan();
438438

439-
if (workspaceModel.tariffPlanId === defaultPlan.id) {
439+
// Prevent re-applying the free plan if workspace is already on it.
440+
if (workspaceModel.tariffPlanId.toString() === defaultPlan._id.toString()) {
440441
throw new UserInputError('You already use default plan');
441442
}
442443

src/typeDefs/billing.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,11 @@ type ComposePaymentResponse {
270270
Next payment date (recurrent start)
271271
"""
272272
nextPaymentDate: DateTime!
273+
274+
"""
275+
CloudPayments public id (merchant identifier for payment widget)
276+
"""
277+
cloudPaymentsPublicId: String!
273278
}
274279
275280

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)