Skip to content

Commit 6535527

Browse files
committed
fix: notif
1 parent ccd4a7e commit 6535527

4 files changed

Lines changed: 127 additions & 16 deletions

File tree

src/resolvers/event.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ module.exports = {
304304
async bulkUpdateAssignee(_obj, { input }, { factories, user, ...context }) {
305305
const { projectId, eventIds, assignee } = input;
306306
const { validEventIds, invalidEventIds } = parseBulkEventIds(eventIds);
307+
let assigneeData = null;
307308

308309
if (validEventIds.length === 0) {
309310
return {
@@ -322,6 +323,8 @@ module.exports = {
322323
throw new UserInputError('assignee not found');
323324
}
324325

326+
assigneeData = userExists;
327+
325328
const project = await factories.projectsFactory.findById(projectId);
326329
const workspace = await factories.workspacesFactory.findById(project.workspaceId);
327330
const assigneeExistsInWorkspace = await workspace.getMemberInfo(assignee);
@@ -338,19 +341,13 @@ module.exports = {
338341
};
339342

340343
if (assignee && resultWithInvalid.updatedEventIds.length > 0) {
341-
factories.usersFactory.dataLoaders.userById.load(assignee)
342-
.then((assigneeData) => {
343-
fireAndForgetAssigneeNotifications({
344-
assigneeData,
345-
eventIds: resultWithInvalid.updatedEventIds,
346-
projectId,
347-
assigneeId: assignee,
348-
whoAssignedId: user.id,
349-
});
350-
})
351-
.catch((error) => {
352-
console.error('Failed to load assignee data for bulk notifications', error);
353-
});
344+
fireAndForgetAssigneeNotifications({
345+
assigneeData,
346+
eventIds: resultWithInvalid.updatedEventIds,
347+
projectId,
348+
assigneeId: assignee,
349+
whoAssignedId: user.id,
350+
});
354351
}
355352

356353
return resultWithInvalid;

src/resolvers/helpers/bulkEvents.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ function fireAndForgetAssigneeNotifications({
2020
assigneeId,
2121
whoAssignedId,
2222
}) {
23+
if (!assigneeData) {
24+
console.error('Failed to enqueue assignee notifications: assignee data is empty');
25+
26+
return;
27+
}
28+
2329
Promise.allSettled(eventIds.map(eventId => sendPersonalNotification(assigneeData, {
2430
type: 'assignee',
2531
payload: {
@@ -28,9 +34,17 @@ function fireAndForgetAssigneeNotifications({
2834
whoAssignedId,
2935
eventId,
3036
},
31-
}))).catch((error) => {
32-
console.error('Failed to enqueue assignee notifications', error);
33-
});
37+
})))
38+
.then((results) => {
39+
const failedResults = results.filter(result => result.status === 'rejected');
40+
41+
if (failedResults.length > 0) {
42+
console.error('Failed to enqueue assignee notifications', failedResults);
43+
}
44+
})
45+
.catch((error) => {
46+
console.error('Failed to enqueue assignee notifications', error);
47+
});
3448
}
3549

3650
/**
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import '../../src/env-test';
2+
3+
jest.mock('../../src/utils/personalNotifications', () => ({
4+
__esModule: true,
5+
default: jest.fn().mockResolvedValue(undefined),
6+
}));
7+
8+
import sendPersonalNotification from '../../src/utils/personalNotifications';
9+
// eslint-disable-next-line @typescript-eslint/no-var-requires
10+
const { fireAndForgetAssigneeNotifications } = require('../../src/resolvers/helpers/bulkEvents') as {
11+
fireAndForgetAssigneeNotifications: (args: {
12+
assigneeData: Record<string, unknown> | null;
13+
eventIds: string[];
14+
projectId: string;
15+
assigneeId: string;
16+
whoAssignedId: string;
17+
}) => void;
18+
};
19+
20+
describe('fireAndForgetAssigneeNotifications', () => {
21+
let consoleErrorSpy: jest.SpyInstance;
22+
23+
beforeEach(() => {
24+
jest.clearAllMocks();
25+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
26+
});
27+
28+
afterEach(() => {
29+
consoleErrorSpy.mockRestore();
30+
});
31+
32+
it('should enqueue personal notification for each event id', async () => {
33+
fireAndForgetAssigneeNotifications({
34+
assigneeData: { id: 'assignee-1', email: 'assignee@hawk.so' },
35+
eventIds: [ 'e-1', 'e-2' ],
36+
projectId: 'p-1',
37+
assigneeId: 'assignee-1',
38+
whoAssignedId: 'u-1',
39+
});
40+
41+
await Promise.resolve();
42+
43+
expect(sendPersonalNotification).toHaveBeenCalledTimes(2);
44+
expect(sendPersonalNotification).toHaveBeenNthCalledWith(
45+
1,
46+
expect.objectContaining({ id: 'assignee-1' }),
47+
{
48+
type: 'assignee',
49+
payload: {
50+
assigneeId: 'assignee-1',
51+
projectId: 'p-1',
52+
whoAssignedId: 'u-1',
53+
eventId: 'e-1',
54+
},
55+
}
56+
);
57+
expect(sendPersonalNotification).toHaveBeenNthCalledWith(
58+
2,
59+
expect.objectContaining({ id: 'assignee-1' }),
60+
{
61+
type: 'assignee',
62+
payload: {
63+
assigneeId: 'assignee-1',
64+
projectId: 'p-1',
65+
whoAssignedId: 'u-1',
66+
eventId: 'e-2',
67+
},
68+
}
69+
);
70+
});
71+
72+
it('should not call personal notifications when assignee data is empty', () => {
73+
fireAndForgetAssigneeNotifications({
74+
assigneeData: null,
75+
eventIds: [ 'e-1' ],
76+
projectId: 'p-1',
77+
assigneeId: 'assignee-1',
78+
whoAssignedId: 'u-1',
79+
});
80+
81+
expect(sendPersonalNotification).not.toHaveBeenCalled();
82+
expect(consoleErrorSpy).toHaveBeenCalledWith(
83+
'Failed to enqueue assignee notifications: assignee data is empty'
84+
);
85+
});
86+
});

test/resolvers/event-bulk-update-assignee.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ jest.mock('../../src/resolvers/helpers/eventsFactory', () => ({
1313
}));
1414

1515
import getEventsFactory from '../../src/resolvers/helpers/eventsFactory';
16+
import sendPersonalNotification from '../../src/utils/personalNotifications';
1617
// eslint-disable-next-line @typescript-eslint/no-var-requires
1718
const eventResolvers = require('../../src/resolvers/event') as {
1819
EventsMutations: {
@@ -90,6 +91,19 @@ describe('EventsMutations.bulkUpdateAssignee', () => {
9091
[ '507f1f77bcf86cd799439011' ],
9192
'assignee-1'
9293
);
94+
expect(sendPersonalNotification).toHaveBeenCalledTimes(1);
95+
expect(sendPersonalNotification).toHaveBeenCalledWith(
96+
expect.objectContaining({ id: 'assignee-1' }),
97+
expect.objectContaining({
98+
type: 'assignee',
99+
payload: expect.objectContaining({
100+
assigneeId: 'assignee-1',
101+
projectId: 'p1',
102+
whoAssignedId: 'u1',
103+
eventId: '507f1f77bcf86cd799439011',
104+
}),
105+
})
106+
);
93107
});
94108

95109
it('should validate ids on resolver level and merge invalid ids into failedEventIds', async () => {

0 commit comments

Comments
 (0)