@flusys/nestjs-notification 7.1.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/adapters/index.d.ts +1 -1
  2. package/adapters/notification.adapter.d.ts +2 -2
  3. package/config/index.d.ts +3 -3
  4. package/controllers/index.d.ts +1 -1
  5. package/controllers/notification.controller.d.ts +4 -4
  6. package/docs/index.d.ts +1 -1
  7. package/docs/notification-swagger.config.d.ts +2 -2
  8. package/dtos/index.d.ts +1 -1
  9. package/entities/index.d.ts +5 -5
  10. package/entities/notification-with-company.entity.d.ts +1 -1
  11. package/fesm/244.js +816 -0
  12. package/fesm/446.js +939 -0
  13. package/fesm/adapters/index.js +79 -1
  14. package/fesm/config/index.js +84 -3
  15. package/fesm/controllers/index.js +13 -1
  16. package/fesm/docs/index.js +112 -1
  17. package/fesm/dtos/index.js +311 -1
  18. package/fesm/entities/index.js +211 -8
  19. package/fesm/gateways/index.js +286 -1
  20. package/fesm/index.js +467 -10
  21. package/fesm/interfaces/index.js +16 -2
  22. package/fesm/modules/index.js +269 -1
  23. package/fesm/runtime.js +85 -0
  24. package/fesm/services/index.js +357 -4
  25. package/gateways/index.d.ts +1 -1
  26. package/gateways/notification.gateway.d.ts +2 -2
  27. package/index.d.ts +10 -10
  28. package/interfaces/index.d.ts +2 -2
  29. package/interfaces/notification-module.interface.d.ts +3 -3
  30. package/interfaces/notification.interface.d.ts +1 -1
  31. package/modules/index.d.ts +1 -1
  32. package/modules/notification.module.d.ts +2 -2
  33. package/package.json +33 -44
  34. package/services/index.d.ts +4 -4
  35. package/services/notification-config.service.d.ts +3 -3
  36. package/services/notification-datasource.provider.d.ts +3 -3
  37. package/services/notification-helper.service.d.ts +5 -5
  38. package/services/notification.service.d.ts +8 -8
  39. package/cjs/adapters/index.js +0 -18
  40. package/cjs/adapters/notification.adapter.js +0 -62
  41. package/cjs/adapters/notification.adapter.spec.js +0 -73
  42. package/cjs/config/event-actions.js +0 -33
  43. package/cjs/config/index.js +0 -20
  44. package/cjs/config/message-keys.js +0 -19
  45. package/cjs/config/notification.constants.js +0 -11
  46. package/cjs/controllers/index.js +0 -18
  47. package/cjs/controllers/notification.controller.js +0 -299
  48. package/cjs/controllers/notification.controller.spec.js +0 -214
  49. package/cjs/docs/index.js +0 -18
  50. package/cjs/docs/notification-swagger.config.js +0 -97
  51. package/cjs/docs/notification-swagger.config.spec.js +0 -68
  52. package/cjs/dtos/index.js +0 -18
  53. package/cjs/dtos/notification.dto.js +0 -295
  54. package/cjs/entities/index.js +0 -48
  55. package/cjs/entities/index.spec.js +0 -13
  56. package/cjs/entities/notification-with-company.entity.js +0 -57
  57. package/cjs/entities/notification.entity.js +0 -112
  58. package/cjs/gateways/index.js +0 -18
  59. package/cjs/gateways/notification.gateway.js +0 -172
  60. package/cjs/gateways/notification.gateway.spec.js +0 -359
  61. package/cjs/index.js +0 -28
  62. package/cjs/interfaces/index.js +0 -19
  63. package/cjs/interfaces/notification-module.interface.js +0 -4
  64. package/cjs/interfaces/notification.interface.js +0 -4
  65. package/cjs/modules/index.js +0 -18
  66. package/cjs/modules/notification.module.js +0 -139
  67. package/cjs/modules/notification.module.spec.js +0 -152
  68. package/cjs/services/index.js +0 -21
  69. package/cjs/services/notification-config.service.js +0 -69
  70. package/cjs/services/notification-config.service.spec.js +0 -113
  71. package/cjs/services/notification-datasource.provider.js +0 -133
  72. package/cjs/services/notification-datasource.provider.spec.js +0 -136
  73. package/cjs/services/notification-helper.service.js +0 -197
  74. package/cjs/services/notification-helper.service.spec.js +0 -392
  75. package/cjs/services/notification.service.js +0 -281
  76. package/cjs/services/notification.service.spec.js +0 -560
  77. package/fesm/adapters/notification.adapter.js +0 -52
  78. package/fesm/adapters/notification.adapter.spec.js +0 -69
  79. package/fesm/config/event-actions.js +0 -12
  80. package/fesm/config/message-keys.js +0 -9
  81. package/fesm/config/notification.constants.js +0 -1
  82. package/fesm/controllers/notification.controller.js +0 -289
  83. package/fesm/controllers/notification.controller.spec.js +0 -210
  84. package/fesm/docs/notification-swagger.config.js +0 -87
  85. package/fesm/docs/notification-swagger.config.spec.js +0 -64
  86. package/fesm/dtos/notification.dto.js +0 -262
  87. package/fesm/entities/index.spec.js +0 -9
  88. package/fesm/entities/notification-with-company.entity.js +0 -47
  89. package/fesm/entities/notification.entity.js +0 -102
  90. package/fesm/gateways/notification.gateway.js +0 -162
  91. package/fesm/gateways/notification.gateway.spec.js +0 -355
  92. package/fesm/interfaces/notification-module.interface.js +0 -1
  93. package/fesm/interfaces/notification.interface.js +0 -1
  94. package/fesm/modules/notification.module.js +0 -129
  95. package/fesm/modules/notification.module.spec.js +0 -148
  96. package/fesm/services/notification-config.service.js +0 -59
  97. package/fesm/services/notification-config.service.spec.js +0 -109
  98. package/fesm/services/notification-datasource.provider.js +0 -82
  99. package/fesm/services/notification-datasource.provider.spec.js +0 -132
  100. package/fesm/services/notification-helper.service.js +0 -187
  101. package/fesm/services/notification-helper.service.spec.js +0 -388
  102. package/fesm/services/notification.service.js +0 -271
  103. package/fesm/services/notification.service.spec.js +0 -556
@@ -1,388 +0,0 @@
1
- import { Test } from '@nestjs/testing';
2
- import { EventBusRegistry } from '@flusys/nestjs-shared/classes';
3
- import { UtilsService } from '@flusys/nestjs-shared/modules';
4
- import { NotificationType } from '@flusys/nestjs-shared/enums';
5
- import { NotificationHelperService } from './notification-helper.service';
6
- import { NotificationConfigService } from './notification-config.service';
7
- import { NotificationDataSourceProvider } from './notification-datasource.provider';
8
- import { NotificationGateway } from '../gateways';
9
- import { Notification, NotificationWithCompany } from '../entities';
10
- function createMockRepo() {
11
- return {
12
- save: jest.fn()
13
- };
14
- }
15
- function createMockDataSource(repo) {
16
- return {
17
- getRepository: jest.fn().mockReturnValue(repo)
18
- };
19
- }
20
- describe('NotificationHelperService', ()=>{
21
- let mockCacheManager;
22
- let mockUtilsService;
23
- let mockConfigService;
24
- let mockDataSourceProvider;
25
- let mockGateway;
26
- let mockRepo;
27
- let mockDataSource;
28
- async function buildService(withGateway = true) {
29
- const providers = [
30
- NotificationHelperService,
31
- {
32
- provide: 'CACHE_INSTANCE',
33
- useValue: mockCacheManager
34
- },
35
- {
36
- provide: UtilsService,
37
- useValue: mockUtilsService
38
- },
39
- {
40
- provide: NotificationConfigService,
41
- useValue: mockConfigService
42
- },
43
- {
44
- provide: NotificationDataSourceProvider,
45
- useValue: mockDataSourceProvider
46
- }
47
- ];
48
- if (withGateway) {
49
- providers.push({
50
- provide: NotificationGateway,
51
- useValue: mockGateway
52
- });
53
- }
54
- const module = await Test.createTestingModule({
55
- providers
56
- }).compile();
57
- return module.get(NotificationHelperService);
58
- }
59
- beforeEach(()=>{
60
- mockRepo = createMockRepo();
61
- mockDataSource = createMockDataSource(mockRepo);
62
- mockCacheManager = {};
63
- mockUtilsService = {
64
- clearCache: jest.fn().mockResolvedValue(undefined)
65
- };
66
- mockConfigService = {
67
- isCompanyFeatureEnabled: jest.fn().mockReturnValue(false)
68
- };
69
- mockDataSourceProvider = {
70
- getDataSource: jest.fn().mockResolvedValue(mockDataSource)
71
- };
72
- mockGateway = {
73
- emitToUser: jest.fn(),
74
- emitToUserInCompany: jest.fn(),
75
- isUserOnline: jest.fn(),
76
- getOnlineUserCount: jest.fn()
77
- };
78
- });
79
- describe('send', ()=>{
80
- it('saves the notification, clears the cache, and emits to the user in real time', async ()=>{
81
- const service = await buildService();
82
- const options = {
83
- userId: 'user-1',
84
- title: 'Hi',
85
- message: 'body',
86
- type: NotificationType.SUCCESS,
87
- data: {
88
- a: 1
89
- }
90
- };
91
- const saved = {
92
- id: 'notif-1',
93
- ...options,
94
- isRead: false
95
- };
96
- mockRepo.save.mockResolvedValue(saved);
97
- const result = await service.send(options);
98
- expect(mockDataSource.getRepository).toHaveBeenCalledWith(Notification);
99
- expect(mockRepo.save).toHaveBeenCalledWith(expect.objectContaining({
100
- userId: 'user-1',
101
- title: 'Hi',
102
- isRead: false
103
- }));
104
- expect(mockUtilsService.clearCache).toHaveBeenCalledWith('notification', mockCacheManager);
105
- expect(mockGateway.emitToUser).toHaveBeenCalledWith('user-1', 'notification:new', expect.objectContaining({
106
- id: 'notif-1',
107
- title: 'Hi'
108
- }));
109
- expect(result).toBe(saved);
110
- });
111
- it('defaults type to INFO and message/data to null when omitted', async ()=>{
112
- const service = await buildService();
113
- mockRepo.save.mockImplementation(async (entity)=>({
114
- id: 'notif-2',
115
- ...entity
116
- }));
117
- await service.send({
118
- userId: 'user-1',
119
- title: 'Only title'
120
- });
121
- expect(mockRepo.save).toHaveBeenCalledWith(expect.objectContaining({
122
- type: NotificationType.INFO,
123
- message: null,
124
- data: null
125
- }));
126
- });
127
- it('does not emit in real time when realtime is explicitly false', async ()=>{
128
- const service = await buildService();
129
- mockRepo.save.mockResolvedValue({
130
- id: 'notif-3',
131
- userId: 'user-1',
132
- title: 'Silent'
133
- });
134
- await service.send({
135
- userId: 'user-1',
136
- title: 'Silent',
137
- realtime: false
138
- });
139
- expect(mockGateway.emitToUser).not.toHaveBeenCalled();
140
- expect(mockGateway.emitToUserInCompany).not.toHaveBeenCalled();
141
- });
142
- it('does not throw when no gateway is bound (gateway not registered)', async ()=>{
143
- const service = await buildService(false);
144
- mockRepo.save.mockResolvedValue({
145
- id: 'notif-4',
146
- userId: 'user-1',
147
- title: 'No gateway'
148
- });
149
- await expect(service.send({
150
- userId: 'user-1',
151
- title: 'No gateway'
152
- })).resolves.toBeDefined();
153
- });
154
- it('uses NotificationWithCompany and includes companyId when the company feature is enabled', async ()=>{
155
- mockConfigService.isCompanyFeatureEnabled.mockReturnValue(true);
156
- const service = await buildService();
157
- mockRepo.save.mockImplementation(async (entity)=>({
158
- id: 'notif-5',
159
- ...entity
160
- }));
161
- await service.send({
162
- userId: 'user-1',
163
- title: 'Scoped',
164
- companyId: 'company-1'
165
- });
166
- expect(mockDataSource.getRepository).toHaveBeenCalledWith(NotificationWithCompany);
167
- expect(mockRepo.save).toHaveBeenCalledWith(expect.objectContaining({
168
- companyId: 'company-1'
169
- }));
170
- expect(mockGateway.emitToUserInCompany).toHaveBeenCalledWith('user-1', 'company-1', 'notification:new', expect.any(Object));
171
- });
172
- it('omits companyId when the company feature is enabled but none was provided', async ()=>{
173
- mockConfigService.isCompanyFeatureEnabled.mockReturnValue(true);
174
- const service = await buildService();
175
- mockRepo.save.mockImplementation(async (entity)=>({
176
- id: 'notif-6',
177
- ...entity
178
- }));
179
- await service.send({
180
- userId: 'user-1',
181
- title: 'No company'
182
- });
183
- const savedArg = mockRepo.save.mock.calls[0][0];
184
- expect(savedArg.companyId).toBeUndefined();
185
- });
186
- it('reuses the same cached data source across multiple calls', async ()=>{
187
- const service = await buildService();
188
- mockRepo.save.mockResolvedValue({
189
- id: 'notif-7',
190
- userId: 'user-1',
191
- title: 'A'
192
- });
193
- await service.send({
194
- userId: 'user-1',
195
- title: 'A'
196
- });
197
- await service.send({
198
- userId: 'user-1',
199
- title: 'B'
200
- });
201
- expect(mockDataSourceProvider.getDataSource).toHaveBeenCalledTimes(1);
202
- });
203
- });
204
- describe('sendToMany', ()=>{
205
- it('saves a notification for every user id and clears the cache once', async ()=>{
206
- const service = await buildService();
207
- const options = {
208
- userIds: [
209
- 'user-1',
210
- 'user-2',
211
- 'user-3'
212
- ],
213
- title: 'Broadcast'
214
- };
215
- mockRepo.save.mockImplementation(async (entities)=>entities.map((e, i)=>({
216
- id: `notif-${i}`,
217
- ...e
218
- })));
219
- const result = await service.sendToMany(options);
220
- expect(mockRepo.save).toHaveBeenCalledWith([
221
- expect.objectContaining({
222
- userId: 'user-1'
223
- }),
224
- expect.objectContaining({
225
- userId: 'user-2'
226
- }),
227
- expect.objectContaining({
228
- userId: 'user-3'
229
- })
230
- ]);
231
- expect(mockUtilsService.clearCache).toHaveBeenCalledTimes(1);
232
- expect(result).toHaveLength(3);
233
- });
234
- it('handles an empty userIds array without saving or emitting', async ()=>{
235
- const service = await buildService();
236
- mockRepo.save.mockResolvedValue([]);
237
- const result = await service.sendToMany({
238
- userIds: [],
239
- title: 'Nobody'
240
- });
241
- expect(mockRepo.save).toHaveBeenCalledWith([]);
242
- expect(result).toEqual([]);
243
- });
244
- it('emits to each user asynchronously (after the microtask queue flushes) when realtime', async ()=>{
245
- const service = await buildService();
246
- mockRepo.save.mockImplementation(async (entities)=>entities.map((e, i)=>({
247
- id: `notif-${i}`,
248
- ...e
249
- })));
250
- await service.sendToMany({
251
- userIds: [
252
- 'user-1',
253
- 'user-2'
254
- ],
255
- title: 'Broadcast'
256
- });
257
- expect(mockGateway.emitToUser).not.toHaveBeenCalled();
258
- await new Promise((resolve)=>setImmediate(resolve));
259
- expect(mockGateway.emitToUser).toHaveBeenCalledTimes(2);
260
- expect(mockGateway.emitToUser).toHaveBeenCalledWith('user-1', 'notification:new', expect.any(Object));
261
- expect(mockGateway.emitToUser).toHaveBeenCalledWith('user-2', 'notification:new', expect.any(Object));
262
- });
263
- it('does not schedule emits when realtime is false', async ()=>{
264
- const service = await buildService();
265
- mockRepo.save.mockImplementation(async (entities)=>entities.map((e, i)=>({
266
- id: `notif-${i}`,
267
- ...e
268
- })));
269
- await service.sendToMany({
270
- userIds: [
271
- 'user-1'
272
- ],
273
- title: 'Silent',
274
- realtime: false
275
- });
276
- await new Promise((resolve)=>setImmediate(resolve));
277
- expect(mockGateway.emitToUser).not.toHaveBeenCalled();
278
- });
279
- });
280
- describe('isUserOnline', ()=>{
281
- it('delegates to the gateway when present', async ()=>{
282
- const service = await buildService();
283
- mockGateway.isUserOnline.mockReturnValue(true);
284
- expect(service.isUserOnline('user-1')).toBe(true);
285
- expect(mockGateway.isUserOnline).toHaveBeenCalledWith('user-1');
286
- });
287
- it('returns false when no gateway is bound', async ()=>{
288
- const service = await buildService(false);
289
- expect(service.isUserOnline('user-1')).toBe(false);
290
- });
291
- });
292
- describe('getOnlineUserCount', ()=>{
293
- it('delegates to the gateway when present', async ()=>{
294
- const service = await buildService();
295
- mockGateway.getOnlineUserCount.mockReturnValue(5);
296
- expect(service.getOnlineUserCount()).toBe(5);
297
- });
298
- it('returns 0 when no gateway is bound', async ()=>{
299
- const service = await buildService(false);
300
- expect(service.getOnlineUserCount()).toBe(0);
301
- });
302
- });
303
- describe('domain events', ()=>{
304
- let publish;
305
- beforeEach(()=>{
306
- publish = jest.fn().mockResolvedValue(undefined);
307
- EventBusRegistry.reset();
308
- EventBusRegistry.setBus({
309
- publish,
310
- subscribe: jest.fn(),
311
- close: jest.fn()
312
- });
313
- EventBusRegistry.configureModule('notification', {
314
- enabled: true
315
- });
316
- });
317
- afterEach(()=>{
318
- EventBusRegistry.reset();
319
- });
320
- it('publishes notification.notification.sent for a single send', async ()=>{
321
- const service = await buildService();
322
- mockRepo.save.mockResolvedValue({
323
- id: 'notif-1',
324
- userId: 'user-1'
325
- });
326
- await service.send({
327
- userId: 'user-1',
328
- title: 'Hi'
329
- });
330
- expect(publish).toHaveBeenCalledWith(expect.objectContaining({
331
- name: 'notification.notification.sent',
332
- payload: expect.objectContaining({
333
- ids: [
334
- 'notif-1'
335
- ],
336
- metadata: expect.objectContaining({
337
- recipientCount: 1
338
- })
339
- })
340
- }));
341
- });
342
- it('reports the recipient count for a bulk send', async ()=>{
343
- const service = await buildService();
344
- mockRepo.save.mockResolvedValue([
345
- {
346
- id: 'notif-1',
347
- userId: 'user-1'
348
- },
349
- {
350
- id: 'notif-2',
351
- userId: 'user-2'
352
- }
353
- ]);
354
- await service.sendToMany({
355
- userIds: [
356
- 'user-1',
357
- 'user-2'
358
- ],
359
- title: 'Hi',
360
- realtime: false
361
- });
362
- expect(publish.mock.calls[0][0].payload).toEqual(expect.objectContaining({
363
- ids: [
364
- 'notif-1',
365
- 'notif-2'
366
- ],
367
- metadata: expect.objectContaining({
368
- recipientCount: 2
369
- })
370
- }));
371
- });
372
- it('does not publish when the notification module has events disabled', async ()=>{
373
- EventBusRegistry.configureModule('notification', {
374
- enabled: false
375
- });
376
- const service = await buildService();
377
- mockRepo.save.mockResolvedValue({
378
- id: 'notif-1',
379
- userId: 'user-1'
380
- });
381
- await service.send({
382
- userId: 'user-1',
383
- title: 'Hi'
384
- });
385
- expect(publish).not.toHaveBeenCalled();
386
- });
387
- });
388
- });
@@ -1,271 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- function _ts_param(paramIndex, decorator) {
24
- return function(target, key) {
25
- decorator(target, key, paramIndex);
26
- };
27
- }
28
- import { LogAction } from '@flusys/nestjs-shared';
29
- import { ApiService, HybridCache } from '@flusys/nestjs-shared/classes';
30
- import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
31
- import { UtilsService } from '@flusys/nestjs-shared/modules';
32
- import { applyCompanyFilter } from '@flusys/nestjs-shared/utils';
33
- import { Inject, Injectable, NotFoundException, Optional, Scope } from '@nestjs/common';
34
- import { NOTIFICATION_EVENT_ACTIONS, NOTIFICATION_EVENT_ENTITIES, NOTIFICATION_EVENT_MODULE, NOTIFICATION_MESSAGES } from '../config';
35
- import { Notification, NotificationWithCompany } from '../entities';
36
- import { NotificationGateway } from '../gateways';
37
- import { NotificationConfigService } from './notification-config.service';
38
- import { NotificationDataSourceProvider } from './notification-datasource.provider';
39
- export class NotificationService extends ApiService {
40
- async convertSingleDtoToEntity(dto, user) {
41
- let entity = {};
42
- if ('id' in dto && dto.id) {
43
- const existing = await this.repository.findOne({
44
- where: {
45
- id: dto.id
46
- }
47
- });
48
- if (!existing) {
49
- throw new NotFoundException({
50
- message: 'Notification not found',
51
- messageKey: NOTIFICATION_MESSAGES.NOT_FOUND
52
- });
53
- }
54
- entity = existing;
55
- }
56
- const merged = {
57
- ...entity,
58
- userId: user?.id ?? entity.userId,
59
- type: dto.type ?? entity.type ?? 'info',
60
- title: dto.title ?? entity.title,
61
- message: dto.message ?? entity.message,
62
- data: dto.data ?? entity.data,
63
- isRead: entity.isRead ?? false,
64
- readAt: entity.readAt
65
- };
66
- if (this.notificationConfig.isCompanyFeatureEnabled()) {
67
- merged.companyId = dto.companyId ?? user?.companyId ?? entity.companyId ?? null;
68
- }
69
- return merged;
70
- }
71
- async getSelectQuery(query, _user, select) {
72
- const fields = select && select.length ? select : [];
73
- const always = [
74
- 'createdAt',
75
- 'updatedAt'
76
- ];
77
- const merged = [
78
- ...new Set([
79
- ...fields,
80
- ...always
81
- ])
82
- ];
83
- query.addSelect(merged.map((f)=>`${this.entityName}.${f}`));
84
- return {
85
- query,
86
- isRaw: false
87
- };
88
- }
89
- async getFilterQuery(query, filter, _user) {
90
- for (const [key, value] of Object.entries(filter)){
91
- if (key === 'isRead' && typeof value === 'boolean') {
92
- query.andWhere(`${this.entityName}.isRead = :isRead`, {
93
- isRead: value
94
- });
95
- } else if (key === 'type' && value) {
96
- query.andWhere(`${this.entityName}.type = :type`, {
97
- type: value
98
- });
99
- } else if (key === 'userId' && value) {
100
- query.andWhere(`${this.entityName}.userId = :userId`, {
101
- userId: value
102
- });
103
- }
104
- }
105
- return {
106
- query,
107
- isRaw: false
108
- };
109
- }
110
- async getExtraManipulateQuery(query, filterDto, user) {
111
- const result = await super.getExtraManipulateQuery(query, filterDto, user);
112
- if (user?.id) {
113
- query.andWhere(`${this.entityName}.userId = :currentUserId`, {
114
- currentUserId: user.id
115
- });
116
- }
117
- applyCompanyFilter(query, {
118
- isCompanyFeatureEnabled: this.notificationConfig.isCompanyFeatureEnabled(),
119
- entityAlias: this.entityName
120
- }, user);
121
- const filterSort = filterDto?.sort;
122
- if (!filterSort || Object.keys(filterSort).length === 0) {
123
- query.orderBy(`${this.entityName}.createdAt`, 'DESC');
124
- }
125
- return result;
126
- }
127
- async afterInsertOperation(entities, _dto, _user, _qr) {
128
- for (const entity of entities){
129
- const ext = entity;
130
- this.emitToUser(entity.userId, ext.companyId, 'notification:new', this.toPayload(entity));
131
- }
132
- }
133
- async markAsRead(id, user) {
134
- await this.ensureDataSourceRepository();
135
- const notification = await this.repository.findOne({
136
- where: {
137
- id,
138
- userId: user.id
139
- }
140
- });
141
- if (!notification) {
142
- throw new NotFoundException({
143
- message: 'Notification not found',
144
- messageKey: NOTIFICATION_MESSAGES.NOT_FOUND
145
- });
146
- }
147
- notification.isRead = true;
148
- notification.readAt = new Date();
149
- await this.repository.save(notification);
150
- await Promise.all([
151
- this.clearCacheForAll(),
152
- this.clearCacheForId([
153
- notification
154
- ])
155
- ]);
156
- // Emit to user's sockets in the same company context
157
- const ext = notification;
158
- this.emitToUser(user.id, ext.companyId ?? user.companyId, 'notification:read', {
159
- id
160
- });
161
- await this.publishDomainAction(NOTIFICATION_EVENT_ACTIONS.READ, {
162
- ids: [
163
- notification.id
164
- ],
165
- user
166
- });
167
- return notification;
168
- }
169
- async markAllAsRead(user) {
170
- await this.ensureDataSourceRepository();
171
- const where = {
172
- userId: user.id,
173
- isRead: false
174
- };
175
- if (this.notificationConfig.isCompanyFeatureEnabled() && user.companyId) {
176
- where.companyId = user.companyId;
177
- }
178
- const result = await this.repository.update(where, {
179
- isRead: true,
180
- readAt: new Date()
181
- });
182
- await this.clearCacheForAll();
183
- // Emit to user's sockets in the current company context
184
- this.emitToUser(user.id, user.companyId, 'notification:read-all', {});
185
- await this.publishDomainAction(NOTIFICATION_EVENT_ACTIONS.ALL_READ, {
186
- metadata: {
187
- count: result.affected ?? 0
188
- },
189
- user
190
- });
191
- return result.affected ?? 0;
192
- }
193
- async getUnreadCount(user) {
194
- await this.ensureDataSourceRepository();
195
- const where = {
196
- userId: user.id,
197
- isRead: false
198
- };
199
- if (this.notificationConfig.isCompanyFeatureEnabled() && user.companyId) {
200
- where.companyId = user.companyId;
201
- }
202
- return this.repository.count({
203
- where
204
- });
205
- }
206
- toPayload(entity) {
207
- return {
208
- id: entity.id,
209
- type: entity.type,
210
- title: entity.title,
211
- message: entity.message,
212
- data: entity.data,
213
- createdAt: entity.createdAt
214
- };
215
- }
216
- emitToUser(userId, companyId, event, payload) {
217
- if (!this.gateway) {
218
- return;
219
- }
220
- if (this.notificationConfig.isCompanyFeatureEnabled() && companyId) {
221
- this.gateway.emitToUserInCompany(userId, companyId, event, payload);
222
- } else {
223
- this.gateway.emitToUser(userId, event, payload);
224
- }
225
- }
226
- constructor(cacheManager, utilsService, notificationConfig, dataSourceProvider, gateway){
227
- super(NOTIFICATION_EVENT_ENTITIES.NOTIFICATION, cacheManager, utilsService, NotificationService.name, false, NOTIFICATION_EVENT_MODULE, dataSourceProvider, notificationConfig.isCompanyFeatureEnabled() ? NotificationWithCompany : Notification), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "notificationConfig", void 0), _define_property(this, "gateway", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.notificationConfig = notificationConfig, this.gateway = gateway;
228
- }
229
- }
230
- _ts_decorate([
231
- LogAction({
232
- action: 'notification.markAsRead',
233
- module: 'notification'
234
- }),
235
- _ts_metadata("design:type", Function),
236
- _ts_metadata("design:paramtypes", [
237
- String,
238
- typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
239
- ]),
240
- _ts_metadata("design:returntype", Promise)
241
- ], NotificationService.prototype, "markAsRead", null);
242
- _ts_decorate([
243
- LogAction({
244
- action: 'notification.markAllAsRead',
245
- module: 'notification'
246
- }),
247
- _ts_metadata("design:type", Function),
248
- _ts_metadata("design:paramtypes", [
249
- typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
250
- ]),
251
- _ts_metadata("design:returntype", Promise)
252
- ], NotificationService.prototype, "markAllAsRead", null);
253
- NotificationService = _ts_decorate([
254
- Injectable({
255
- scope: Scope.REQUEST
256
- }),
257
- _ts_param(0, Inject('CACHE_INSTANCE')),
258
- _ts_param(1, Inject(UtilsService)),
259
- _ts_param(2, Inject(NotificationConfigService)),
260
- _ts_param(3, Inject(NotificationDataSourceProvider)),
261
- _ts_param(4, Optional()),
262
- _ts_param(4, Inject(NotificationGateway)),
263
- _ts_metadata("design:type", Function),
264
- _ts_metadata("design:paramtypes", [
265
- typeof HybridCache === "undefined" ? Object : HybridCache,
266
- typeof UtilsService === "undefined" ? Object : UtilsService,
267
- typeof NotificationConfigService === "undefined" ? Object : NotificationConfigService,
268
- typeof NotificationDataSourceProvider === "undefined" ? Object : NotificationDataSourceProvider,
269
- typeof NotificationGateway === "undefined" ? Object : NotificationGateway
270
- ])
271
- ], NotificationService);