@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.
- package/adapters/index.d.ts +1 -1
- package/adapters/notification.adapter.d.ts +2 -2
- package/config/index.d.ts +3 -3
- package/controllers/index.d.ts +1 -1
- package/controllers/notification.controller.d.ts +4 -4
- package/docs/index.d.ts +1 -1
- package/docs/notification-swagger.config.d.ts +2 -2
- package/dtos/index.d.ts +1 -1
- package/entities/index.d.ts +5 -5
- package/entities/notification-with-company.entity.d.ts +1 -1
- package/fesm/244.js +816 -0
- package/fesm/446.js +939 -0
- package/fesm/adapters/index.js +79 -1
- package/fesm/config/index.js +84 -3
- package/fesm/controllers/index.js +13 -1
- package/fesm/docs/index.js +112 -1
- package/fesm/dtos/index.js +311 -1
- package/fesm/entities/index.js +211 -8
- package/fesm/gateways/index.js +286 -1
- package/fesm/index.js +467 -10
- package/fesm/interfaces/index.js +16 -2
- package/fesm/modules/index.js +269 -1
- package/fesm/runtime.js +85 -0
- package/fesm/services/index.js +357 -4
- package/gateways/index.d.ts +1 -1
- package/gateways/notification.gateway.d.ts +2 -2
- package/index.d.ts +10 -10
- package/interfaces/index.d.ts +2 -2
- package/interfaces/notification-module.interface.d.ts +3 -3
- package/interfaces/notification.interface.d.ts +1 -1
- package/modules/index.d.ts +1 -1
- package/modules/notification.module.d.ts +2 -2
- package/package.json +33 -44
- package/services/index.d.ts +4 -4
- package/services/notification-config.service.d.ts +3 -3
- package/services/notification-datasource.provider.d.ts +3 -3
- package/services/notification-helper.service.d.ts +5 -5
- package/services/notification.service.d.ts +8 -8
- package/cjs/adapters/index.js +0 -18
- package/cjs/adapters/notification.adapter.js +0 -62
- package/cjs/adapters/notification.adapter.spec.js +0 -73
- package/cjs/config/event-actions.js +0 -33
- package/cjs/config/index.js +0 -20
- package/cjs/config/message-keys.js +0 -19
- package/cjs/config/notification.constants.js +0 -11
- package/cjs/controllers/index.js +0 -18
- package/cjs/controllers/notification.controller.js +0 -299
- package/cjs/controllers/notification.controller.spec.js +0 -214
- package/cjs/docs/index.js +0 -18
- package/cjs/docs/notification-swagger.config.js +0 -97
- package/cjs/docs/notification-swagger.config.spec.js +0 -68
- package/cjs/dtos/index.js +0 -18
- package/cjs/dtos/notification.dto.js +0 -295
- package/cjs/entities/index.js +0 -48
- package/cjs/entities/index.spec.js +0 -13
- package/cjs/entities/notification-with-company.entity.js +0 -57
- package/cjs/entities/notification.entity.js +0 -112
- package/cjs/gateways/index.js +0 -18
- package/cjs/gateways/notification.gateway.js +0 -172
- package/cjs/gateways/notification.gateway.spec.js +0 -359
- package/cjs/index.js +0 -28
- package/cjs/interfaces/index.js +0 -19
- package/cjs/interfaces/notification-module.interface.js +0 -4
- package/cjs/interfaces/notification.interface.js +0 -4
- package/cjs/modules/index.js +0 -18
- package/cjs/modules/notification.module.js +0 -139
- package/cjs/modules/notification.module.spec.js +0 -152
- package/cjs/services/index.js +0 -21
- package/cjs/services/notification-config.service.js +0 -69
- package/cjs/services/notification-config.service.spec.js +0 -113
- package/cjs/services/notification-datasource.provider.js +0 -133
- package/cjs/services/notification-datasource.provider.spec.js +0 -136
- package/cjs/services/notification-helper.service.js +0 -197
- package/cjs/services/notification-helper.service.spec.js +0 -392
- package/cjs/services/notification.service.js +0 -281
- package/cjs/services/notification.service.spec.js +0 -560
- package/fesm/adapters/notification.adapter.js +0 -52
- package/fesm/adapters/notification.adapter.spec.js +0 -69
- package/fesm/config/event-actions.js +0 -12
- package/fesm/config/message-keys.js +0 -9
- package/fesm/config/notification.constants.js +0 -1
- package/fesm/controllers/notification.controller.js +0 -289
- package/fesm/controllers/notification.controller.spec.js +0 -210
- package/fesm/docs/notification-swagger.config.js +0 -87
- package/fesm/docs/notification-swagger.config.spec.js +0 -64
- package/fesm/dtos/notification.dto.js +0 -262
- package/fesm/entities/index.spec.js +0 -9
- package/fesm/entities/notification-with-company.entity.js +0 -47
- package/fesm/entities/notification.entity.js +0 -102
- package/fesm/gateways/notification.gateway.js +0 -162
- package/fesm/gateways/notification.gateway.spec.js +0 -355
- package/fesm/interfaces/notification-module.interface.js +0 -1
- package/fesm/interfaces/notification.interface.js +0 -1
- package/fesm/modules/notification.module.js +0 -129
- package/fesm/modules/notification.module.spec.js +0 -148
- package/fesm/services/notification-config.service.js +0 -59
- package/fesm/services/notification-config.service.spec.js +0 -109
- package/fesm/services/notification-datasource.provider.js +0 -82
- package/fesm/services/notification-datasource.provider.spec.js +0 -132
- package/fesm/services/notification-helper.service.js +0 -187
- package/fesm/services/notification-helper.service.spec.js +0 -388
- package/fesm/services/notification.service.js +0 -271
- package/fesm/services/notification.service.spec.js +0 -556
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
// ==================== NOTIFICATION MODULE MESSAGE KEYS ====================
|
|
2
|
-
export const NOTIFICATION_MESSAGES = {
|
|
3
|
-
NOT_FOUND: 'notification.not.found',
|
|
4
|
-
MARK_READ_SUCCESS: 'notification.mark.read.success',
|
|
5
|
-
MARK_ALL_READ_SUCCESS: 'notification.mark.all.read.success',
|
|
6
|
-
UNREAD_COUNT_SUCCESS: 'notification.unread.count.success',
|
|
7
|
-
SEND_SUCCESS: 'notification.send.success',
|
|
8
|
-
BROADCAST_SUCCESS: 'notification.broadcast.success'
|
|
9
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const NOTIFICATION_MODULE_OPTIONS = 'NOTIFICATION_MODULE_OPTIONS';
|
|
@@ -1,289 +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 { createApiController, NOTIFICATION_PERMISSIONS } from '@flusys/nestjs-shared/classes';
|
|
29
|
-
import { CurrentUser, RequirePermission } from '@flusys/nestjs-shared/decorators';
|
|
30
|
-
import { BulkResponseDto, MessageResponseDto, SingleResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
31
|
-
import { JwtAuthGuard, PermissionGuard } from '@flusys/nestjs-shared/guards';
|
|
32
|
-
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
33
|
-
import { Body, Controller, HttpCode, HttpStatus, Inject, Post, UseGuards } from '@nestjs/common';
|
|
34
|
-
import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|
35
|
-
import { plainToInstance } from 'class-transformer';
|
|
36
|
-
import { NOTIFICATION_MESSAGES } from '../config';
|
|
37
|
-
import { CreateNotificationDto, MarkReadDto, NotificationResponseDto, SendBulkNotificationDto, SendNotificationDto, UpdateNotificationDto } from '../dtos';
|
|
38
|
-
import { NotificationHelperService } from '../services/notification-helper.service';
|
|
39
|
-
import { NotificationService } from '../services/notification.service';
|
|
40
|
-
export class NotificationController extends createApiController(CreateNotificationDto, UpdateNotificationDto, NotificationResponseDto, {
|
|
41
|
-
entityName: 'notification',
|
|
42
|
-
security: {
|
|
43
|
-
insert: {
|
|
44
|
-
level: 'permission',
|
|
45
|
-
permissions: [
|
|
46
|
-
NOTIFICATION_PERMISSIONS.CREATE
|
|
47
|
-
]
|
|
48
|
-
},
|
|
49
|
-
insertMany: {
|
|
50
|
-
level: 'permission',
|
|
51
|
-
permissions: [
|
|
52
|
-
NOTIFICATION_PERMISSIONS.CREATE
|
|
53
|
-
]
|
|
54
|
-
},
|
|
55
|
-
getById: {
|
|
56
|
-
level: 'permission',
|
|
57
|
-
permissions: [
|
|
58
|
-
NOTIFICATION_PERMISSIONS.READ
|
|
59
|
-
]
|
|
60
|
-
},
|
|
61
|
-
getAll: {
|
|
62
|
-
level: 'jwt'
|
|
63
|
-
},
|
|
64
|
-
update: {
|
|
65
|
-
level: 'permission',
|
|
66
|
-
permissions: [
|
|
67
|
-
NOTIFICATION_PERMISSIONS.UPDATE
|
|
68
|
-
]
|
|
69
|
-
},
|
|
70
|
-
updateMany: {
|
|
71
|
-
level: 'permission',
|
|
72
|
-
permissions: [
|
|
73
|
-
NOTIFICATION_PERMISSIONS.UPDATE
|
|
74
|
-
]
|
|
75
|
-
},
|
|
76
|
-
delete: {
|
|
77
|
-
level: 'permission',
|
|
78
|
-
permissions: [
|
|
79
|
-
NOTIFICATION_PERMISSIONS.DELETE
|
|
80
|
-
]
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}) {
|
|
84
|
-
async markAsRead(dto, user) {
|
|
85
|
-
const notification = await this.service.markAsRead(dto.id, user);
|
|
86
|
-
const plainOptions = {
|
|
87
|
-
excludeExtraneousValues: true
|
|
88
|
-
};
|
|
89
|
-
return {
|
|
90
|
-
success: true,
|
|
91
|
-
message: 'Notification marked as read',
|
|
92
|
-
messageKey: NOTIFICATION_MESSAGES.MARK_READ_SUCCESS,
|
|
93
|
-
data: plainToInstance(NotificationResponseDto, notification, plainOptions)
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
async markAllAsRead(user) {
|
|
97
|
-
const count = await this.service.markAllAsRead(user);
|
|
98
|
-
return {
|
|
99
|
-
success: true,
|
|
100
|
-
message: `${count} notifications marked as read`,
|
|
101
|
-
messageKey: NOTIFICATION_MESSAGES.MARK_ALL_READ_SUCCESS,
|
|
102
|
-
messageVariables: {
|
|
103
|
-
count
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
async getUnreadCount(user) {
|
|
108
|
-
const count = await this.service.getUnreadCount(user);
|
|
109
|
-
return {
|
|
110
|
-
success: true,
|
|
111
|
-
message: 'Unread count retrieved',
|
|
112
|
-
messageKey: NOTIFICATION_MESSAGES.UNREAD_COUNT_SUCCESS,
|
|
113
|
-
data: {
|
|
114
|
-
count
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
async sendNotification(dto) {
|
|
119
|
-
const notification = await this.notificationHelper.send({
|
|
120
|
-
userId: dto.userId,
|
|
121
|
-
title: dto.title,
|
|
122
|
-
message: dto.message,
|
|
123
|
-
type: dto.type,
|
|
124
|
-
data: dto.data,
|
|
125
|
-
companyId: dto.companyId
|
|
126
|
-
});
|
|
127
|
-
const plainOptions = {
|
|
128
|
-
excludeExtraneousValues: true
|
|
129
|
-
};
|
|
130
|
-
return {
|
|
131
|
-
success: true,
|
|
132
|
-
message: 'Notification sent successfully',
|
|
133
|
-
messageKey: NOTIFICATION_MESSAGES.SEND_SUCCESS,
|
|
134
|
-
data: plainToInstance(NotificationResponseDto, notification, plainOptions)
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
async sendBulkNotification(dto) {
|
|
138
|
-
const notifications = await this.notificationHelper.sendToMany({
|
|
139
|
-
userIds: dto.userIds,
|
|
140
|
-
title: dto.title,
|
|
141
|
-
message: dto.message,
|
|
142
|
-
type: dto.type,
|
|
143
|
-
data: dto.data,
|
|
144
|
-
companyId: dto.companyId
|
|
145
|
-
});
|
|
146
|
-
const plainOptions = {
|
|
147
|
-
excludeExtraneousValues: true
|
|
148
|
-
};
|
|
149
|
-
return {
|
|
150
|
-
success: true,
|
|
151
|
-
message: `${notifications.length} notifications sent successfully`,
|
|
152
|
-
messageKey: NOTIFICATION_MESSAGES.BROADCAST_SUCCESS,
|
|
153
|
-
messageVariables: {
|
|
154
|
-
count: notifications.length
|
|
155
|
-
},
|
|
156
|
-
data: notifications.map((n)=>plainToInstance(NotificationResponseDto, n, plainOptions)),
|
|
157
|
-
meta: {
|
|
158
|
-
count: notifications.length,
|
|
159
|
-
total: dto.userIds.length
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
constructor(service, notificationHelper){
|
|
164
|
-
super(service), _define_property(this, "service", void 0), _define_property(this, "notificationHelper", void 0), this.service = service, this.notificationHelper = notificationHelper;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
_ts_decorate([
|
|
168
|
-
Post('mark-read'),
|
|
169
|
-
HttpCode(HttpStatus.OK),
|
|
170
|
-
UseGuards(JwtAuthGuard),
|
|
171
|
-
ApiBearerAuth(),
|
|
172
|
-
ApiOperation({
|
|
173
|
-
summary: 'Mark a notification as read'
|
|
174
|
-
}),
|
|
175
|
-
ApiBody({
|
|
176
|
-
type: MarkReadDto
|
|
177
|
-
}),
|
|
178
|
-
ApiResponse({
|
|
179
|
-
status: 200,
|
|
180
|
-
description: 'Notification marked as read',
|
|
181
|
-
type: SingleResponseDto
|
|
182
|
-
}),
|
|
183
|
-
_ts_param(0, Body()),
|
|
184
|
-
_ts_param(1, CurrentUser()),
|
|
185
|
-
_ts_metadata("design:type", Function),
|
|
186
|
-
_ts_metadata("design:paramtypes", [
|
|
187
|
-
typeof MarkReadDto === "undefined" ? Object : MarkReadDto,
|
|
188
|
-
typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
|
|
189
|
-
]),
|
|
190
|
-
_ts_metadata("design:returntype", Promise)
|
|
191
|
-
], NotificationController.prototype, "markAsRead", null);
|
|
192
|
-
_ts_decorate([
|
|
193
|
-
Post('mark-all-read'),
|
|
194
|
-
HttpCode(HttpStatus.OK),
|
|
195
|
-
UseGuards(JwtAuthGuard),
|
|
196
|
-
ApiBearerAuth(),
|
|
197
|
-
ApiOperation({
|
|
198
|
-
summary: 'Mark all notifications as read'
|
|
199
|
-
}),
|
|
200
|
-
ApiResponse({
|
|
201
|
-
status: 200,
|
|
202
|
-
description: 'All notifications marked as read',
|
|
203
|
-
type: MessageResponseDto
|
|
204
|
-
}),
|
|
205
|
-
_ts_param(0, CurrentUser()),
|
|
206
|
-
_ts_metadata("design:type", Function),
|
|
207
|
-
_ts_metadata("design:paramtypes", [
|
|
208
|
-
typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
|
|
209
|
-
]),
|
|
210
|
-
_ts_metadata("design:returntype", Promise)
|
|
211
|
-
], NotificationController.prototype, "markAllAsRead", null);
|
|
212
|
-
_ts_decorate([
|
|
213
|
-
Post('unread-count'),
|
|
214
|
-
HttpCode(HttpStatus.OK),
|
|
215
|
-
UseGuards(JwtAuthGuard),
|
|
216
|
-
ApiBearerAuth(),
|
|
217
|
-
ApiOperation({
|
|
218
|
-
summary: 'Get unread notification count'
|
|
219
|
-
}),
|
|
220
|
-
ApiResponse({
|
|
221
|
-
status: 200,
|
|
222
|
-
description: 'Unread count retrieved',
|
|
223
|
-
type: SingleResponseDto
|
|
224
|
-
}),
|
|
225
|
-
_ts_param(0, CurrentUser()),
|
|
226
|
-
_ts_metadata("design:type", Function),
|
|
227
|
-
_ts_metadata("design:paramtypes", [
|
|
228
|
-
typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
|
|
229
|
-
]),
|
|
230
|
-
_ts_metadata("design:returntype", Promise)
|
|
231
|
-
], NotificationController.prototype, "getUnreadCount", null);
|
|
232
|
-
_ts_decorate([
|
|
233
|
-
Post('send'),
|
|
234
|
-
HttpCode(HttpStatus.OK),
|
|
235
|
-
UseGuards(JwtAuthGuard, PermissionGuard),
|
|
236
|
-
RequirePermission(NOTIFICATION_PERMISSIONS.CREATE),
|
|
237
|
-
ApiOperation({
|
|
238
|
-
summary: 'Send a notification to a user'
|
|
239
|
-
}),
|
|
240
|
-
ApiBody({
|
|
241
|
-
type: SendNotificationDto
|
|
242
|
-
}),
|
|
243
|
-
ApiResponse({
|
|
244
|
-
status: 200,
|
|
245
|
-
description: 'Notification sent successfully',
|
|
246
|
-
type: SingleResponseDto
|
|
247
|
-
}),
|
|
248
|
-
_ts_param(0, Body()),
|
|
249
|
-
_ts_metadata("design:type", Function),
|
|
250
|
-
_ts_metadata("design:paramtypes", [
|
|
251
|
-
typeof SendNotificationDto === "undefined" ? Object : SendNotificationDto
|
|
252
|
-
]),
|
|
253
|
-
_ts_metadata("design:returntype", Promise)
|
|
254
|
-
], NotificationController.prototype, "sendNotification", null);
|
|
255
|
-
_ts_decorate([
|
|
256
|
-
Post('send-bulk'),
|
|
257
|
-
HttpCode(HttpStatus.OK),
|
|
258
|
-
UseGuards(JwtAuthGuard, PermissionGuard),
|
|
259
|
-
RequirePermission(NOTIFICATION_PERMISSIONS.CREATE),
|
|
260
|
-
ApiOperation({
|
|
261
|
-
summary: 'Send a notification to multiple users'
|
|
262
|
-
}),
|
|
263
|
-
ApiBody({
|
|
264
|
-
type: SendBulkNotificationDto
|
|
265
|
-
}),
|
|
266
|
-
ApiResponse({
|
|
267
|
-
status: 200,
|
|
268
|
-
description: 'Notifications sent successfully',
|
|
269
|
-
type: BulkResponseDto
|
|
270
|
-
}),
|
|
271
|
-
_ts_param(0, Body()),
|
|
272
|
-
_ts_metadata("design:type", Function),
|
|
273
|
-
_ts_metadata("design:paramtypes", [
|
|
274
|
-
typeof SendBulkNotificationDto === "undefined" ? Object : SendBulkNotificationDto
|
|
275
|
-
]),
|
|
276
|
-
_ts_metadata("design:returntype", Promise)
|
|
277
|
-
], NotificationController.prototype, "sendBulkNotification", null);
|
|
278
|
-
NotificationController = _ts_decorate([
|
|
279
|
-
ApiTags('Notifications'),
|
|
280
|
-
ApiBearerAuth(),
|
|
281
|
-
Controller('notification'),
|
|
282
|
-
_ts_param(0, Inject(NotificationService)),
|
|
283
|
-
_ts_param(1, Inject(NotificationHelperService)),
|
|
284
|
-
_ts_metadata("design:type", Function),
|
|
285
|
-
_ts_metadata("design:paramtypes", [
|
|
286
|
-
typeof NotificationService === "undefined" ? Object : NotificationService,
|
|
287
|
-
typeof NotificationHelperService === "undefined" ? Object : NotificationHelperService
|
|
288
|
-
])
|
|
289
|
-
], NotificationController);
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { NotificationType } from '@flusys/nestjs-shared/enums';
|
|
2
|
-
import { buildMockUser } from '@test-utils/mocks/logged-user.mock';
|
|
3
|
-
import { NotificationController } from './notification.controller';
|
|
4
|
-
import { NOTIFICATION_MESSAGES } from '../config';
|
|
5
|
-
function buildNotification(overrides = {}) {
|
|
6
|
-
return {
|
|
7
|
-
id: 'notif-1',
|
|
8
|
-
userId: 'user-uuid-1',
|
|
9
|
-
type: NotificationType.INFO,
|
|
10
|
-
title: 'Title',
|
|
11
|
-
message: 'Message',
|
|
12
|
-
data: null,
|
|
13
|
-
isRead: false,
|
|
14
|
-
readAt: null,
|
|
15
|
-
createdAt: new Date(),
|
|
16
|
-
updatedAt: new Date(),
|
|
17
|
-
...overrides
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
describe('NotificationController', ()=>{
|
|
21
|
-
let controller;
|
|
22
|
-
let mockService;
|
|
23
|
-
let mockHelper;
|
|
24
|
-
beforeEach(()=>{
|
|
25
|
-
mockService = {
|
|
26
|
-
markAsRead: jest.fn(),
|
|
27
|
-
markAllAsRead: jest.fn(),
|
|
28
|
-
getUnreadCount: jest.fn(),
|
|
29
|
-
insert: jest.fn(),
|
|
30
|
-
insertMany: jest.fn(),
|
|
31
|
-
findById: jest.fn(),
|
|
32
|
-
findByIds: jest.fn(),
|
|
33
|
-
update: jest.fn(),
|
|
34
|
-
updateMany: jest.fn(),
|
|
35
|
-
bulkUpsert: jest.fn(),
|
|
36
|
-
getAll: jest.fn(),
|
|
37
|
-
delete: jest.fn()
|
|
38
|
-
};
|
|
39
|
-
mockHelper = {
|
|
40
|
-
send: jest.fn(),
|
|
41
|
-
sendToMany: jest.fn(),
|
|
42
|
-
isUserOnline: jest.fn(),
|
|
43
|
-
getOnlineUserCount: jest.fn()
|
|
44
|
-
};
|
|
45
|
-
controller = new NotificationController(mockService, mockHelper);
|
|
46
|
-
});
|
|
47
|
-
describe('markAsRead', ()=>{
|
|
48
|
-
it('marks the notification as read and returns the mapped response', async ()=>{
|
|
49
|
-
const user = buildMockUser();
|
|
50
|
-
const notification = buildNotification({
|
|
51
|
-
isRead: true
|
|
52
|
-
});
|
|
53
|
-
mockService.markAsRead.mockResolvedValue(notification);
|
|
54
|
-
const result = await controller.markAsRead({
|
|
55
|
-
id: 'notif-1'
|
|
56
|
-
}, user);
|
|
57
|
-
expect(mockService.markAsRead).toHaveBeenCalledWith('notif-1', user);
|
|
58
|
-
expect(result.success).toBe(true);
|
|
59
|
-
expect(result.messageKey).toBe(NOTIFICATION_MESSAGES.MARK_READ_SUCCESS);
|
|
60
|
-
expect(result.data?.id).toBe('notif-1');
|
|
61
|
-
expect(result.data?.isRead).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
it('propagates a not-found error from the service', async ()=>{
|
|
64
|
-
mockService.markAsRead.mockRejectedValue(new Error(NOTIFICATION_MESSAGES.NOT_FOUND));
|
|
65
|
-
await expect(controller.markAsRead({
|
|
66
|
-
id: 'missing'
|
|
67
|
-
}, buildMockUser())).rejects.toThrow();
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
describe('markAllAsRead', ()=>{
|
|
71
|
-
it('returns the count of notifications marked as read', async ()=>{
|
|
72
|
-
mockService.markAllAsRead.mockResolvedValue(3);
|
|
73
|
-
const result = await controller.markAllAsRead(buildMockUser());
|
|
74
|
-
expect(result.success).toBe(true);
|
|
75
|
-
expect(result.messageKey).toBe(NOTIFICATION_MESSAGES.MARK_ALL_READ_SUCCESS);
|
|
76
|
-
expect(result.messageVariables).toEqual({
|
|
77
|
-
count: 3
|
|
78
|
-
});
|
|
79
|
-
expect(result.message).toBe('3 notifications marked as read');
|
|
80
|
-
});
|
|
81
|
-
it('handles a user with zero unread notifications gracefully', async ()=>{
|
|
82
|
-
mockService.markAllAsRead.mockResolvedValue(0);
|
|
83
|
-
const result = await controller.markAllAsRead(buildMockUser());
|
|
84
|
-
expect(result.messageVariables).toEqual({
|
|
85
|
-
count: 0
|
|
86
|
-
});
|
|
87
|
-
expect(result.message).toBe('0 notifications marked as read');
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
describe('getUnreadCount', ()=>{
|
|
91
|
-
it('returns the unread count wrapped in a single response', async ()=>{
|
|
92
|
-
mockService.getUnreadCount.mockResolvedValue(5);
|
|
93
|
-
const result = await controller.getUnreadCount(buildMockUser());
|
|
94
|
-
expect(result.success).toBe(true);
|
|
95
|
-
expect(result.messageKey).toBe(NOTIFICATION_MESSAGES.UNREAD_COUNT_SUCCESS);
|
|
96
|
-
expect(result.data).toEqual({
|
|
97
|
-
count: 5
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
it('returns zero for a user with no unread notifications', async ()=>{
|
|
101
|
-
mockService.getUnreadCount.mockResolvedValue(0);
|
|
102
|
-
const result = await controller.getUnreadCount(buildMockUser());
|
|
103
|
-
expect(result.data).toEqual({
|
|
104
|
-
count: 0
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
describe('sendNotification', ()=>{
|
|
109
|
-
it('sends a notification via the helper service and returns the mapped response', async ()=>{
|
|
110
|
-
const notification = buildNotification({
|
|
111
|
-
id: 'notif-2'
|
|
112
|
-
});
|
|
113
|
-
mockHelper.send.mockResolvedValue(notification);
|
|
114
|
-
const result = await controller.sendNotification({
|
|
115
|
-
userId: 'user-uuid-1',
|
|
116
|
-
title: 'Hello',
|
|
117
|
-
message: 'World'
|
|
118
|
-
});
|
|
119
|
-
expect(mockHelper.send).toHaveBeenCalledWith({
|
|
120
|
-
userId: 'user-uuid-1',
|
|
121
|
-
title: 'Hello',
|
|
122
|
-
message: 'World',
|
|
123
|
-
type: undefined,
|
|
124
|
-
data: undefined,
|
|
125
|
-
companyId: undefined
|
|
126
|
-
});
|
|
127
|
-
expect(result.success).toBe(true);
|
|
128
|
-
expect(result.messageKey).toBe(NOTIFICATION_MESSAGES.SEND_SUCCESS);
|
|
129
|
-
expect(result.data?.id).toBe('notif-2');
|
|
130
|
-
});
|
|
131
|
-
it('propagates errors from the helper service', async ()=>{
|
|
132
|
-
mockHelper.send.mockRejectedValue(new Error('db down'));
|
|
133
|
-
await expect(controller.sendNotification({
|
|
134
|
-
userId: 'user-1',
|
|
135
|
-
title: 'X'
|
|
136
|
-
})).rejects.toThrow('db down');
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
describe('sendBulkNotification', ()=>{
|
|
140
|
-
it('sends notifications to multiple users and returns bulk meta', async ()=>{
|
|
141
|
-
const notifications = [
|
|
142
|
-
buildNotification({
|
|
143
|
-
id: 'n1',
|
|
144
|
-
userId: 'user-1'
|
|
145
|
-
}),
|
|
146
|
-
buildNotification({
|
|
147
|
-
id: 'n2',
|
|
148
|
-
userId: 'user-2'
|
|
149
|
-
})
|
|
150
|
-
];
|
|
151
|
-
mockHelper.sendToMany.mockResolvedValue(notifications);
|
|
152
|
-
const result = await controller.sendBulkNotification({
|
|
153
|
-
userIds: [
|
|
154
|
-
'user-1',
|
|
155
|
-
'user-2'
|
|
156
|
-
],
|
|
157
|
-
title: 'Broadcast'
|
|
158
|
-
});
|
|
159
|
-
expect(mockHelper.sendToMany).toHaveBeenCalledWith({
|
|
160
|
-
userIds: [
|
|
161
|
-
'user-1',
|
|
162
|
-
'user-2'
|
|
163
|
-
],
|
|
164
|
-
title: 'Broadcast',
|
|
165
|
-
message: undefined,
|
|
166
|
-
type: undefined,
|
|
167
|
-
data: undefined,
|
|
168
|
-
companyId: undefined
|
|
169
|
-
});
|
|
170
|
-
expect(result.success).toBe(true);
|
|
171
|
-
expect(result.messageKey).toBe(NOTIFICATION_MESSAGES.BROADCAST_SUCCESS);
|
|
172
|
-
expect(result.data).toHaveLength(2);
|
|
173
|
-
expect(result.meta).toEqual({
|
|
174
|
-
count: 2,
|
|
175
|
-
total: 2
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
it('reports a mismatch between requested and sent counts in the meta', async ()=>{
|
|
179
|
-
mockHelper.sendToMany.mockResolvedValue([
|
|
180
|
-
buildNotification({
|
|
181
|
-
id: 'n1'
|
|
182
|
-
})
|
|
183
|
-
]);
|
|
184
|
-
const result = await controller.sendBulkNotification({
|
|
185
|
-
userIds: [
|
|
186
|
-
'user-1',
|
|
187
|
-
'user-2',
|
|
188
|
-
'user-3'
|
|
189
|
-
],
|
|
190
|
-
title: 'Partial broadcast'
|
|
191
|
-
});
|
|
192
|
-
expect(result.meta).toEqual({
|
|
193
|
-
count: 1,
|
|
194
|
-
total: 3
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
it('handles an empty userIds bulk send without throwing', async ()=>{
|
|
198
|
-
mockHelper.sendToMany.mockResolvedValue([]);
|
|
199
|
-
const result = await controller.sendBulkNotification({
|
|
200
|
-
userIds: [],
|
|
201
|
-
title: 'Nobody'
|
|
202
|
-
});
|
|
203
|
-
expect(result.data).toEqual([]);
|
|
204
|
-
expect(result.meta).toEqual({
|
|
205
|
-
count: 0,
|
|
206
|
-
total: 0
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/** Schema properties to exclude when company feature is disabled */ const COMPANY_SCHEMA_EXCLUSIONS = [
|
|
2
|
-
{
|
|
3
|
-
schemaName: 'NotificationResponseDto',
|
|
4
|
-
properties: [
|
|
5
|
-
'companyId'
|
|
6
|
-
]
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
schemaName: 'CreateNotificationDto',
|
|
10
|
-
properties: [
|
|
11
|
-
'companyId'
|
|
12
|
-
]
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
schemaName: 'SendNotificationDto',
|
|
16
|
-
properties: [
|
|
17
|
-
'companyId'
|
|
18
|
-
]
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
schemaName: 'SendBulkNotificationDto',
|
|
22
|
-
properties: [
|
|
23
|
-
'companyId'
|
|
24
|
-
]
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
schemaName: 'UpdateNotificationDto',
|
|
28
|
-
properties: [
|
|
29
|
-
'companyId'
|
|
30
|
-
]
|
|
31
|
-
}
|
|
32
|
-
];
|
|
33
|
-
export function notificationSwaggerConfig(bootstrapConfig) {
|
|
34
|
-
const enableCompanyFeature = bootstrapConfig?.enableCompanyFeature ?? true;
|
|
35
|
-
const isMultiTenant = bootstrapConfig?.databaseMode === 'multi-tenant';
|
|
36
|
-
const multiTenantNote = isMultiTenant ? `\n> **Multi-Tenant Mode**: Include \`x-tenant-id\` header to target a specific tenant database.\n` : '';
|
|
37
|
-
return {
|
|
38
|
-
title: 'Notification API',
|
|
39
|
-
description: `
|
|
40
|
-
## Notification API
|
|
41
|
-
${multiTenantNote}
|
|
42
|
-
Real-time notification system with WebSocket support.
|
|
43
|
-
|
|
44
|
-
### Features
|
|
45
|
-
- Push notifications to users
|
|
46
|
-
- Mark notifications as read
|
|
47
|
-
- Unread count tracking
|
|
48
|
-
- Real-time delivery via WebSocket
|
|
49
|
-
- Bulk notification sending
|
|
50
|
-
|
|
51
|
-
### Endpoints
|
|
52
|
-
|
|
53
|
-
#### User Notifications
|
|
54
|
-
- \`POST /notification/get-all\` - Get user's notifications (paginated, sorted by newest)
|
|
55
|
-
- \`POST /notification/get/:id\` - Get single notification
|
|
56
|
-
- \`POST /notification/mark-read\` - Mark notification as read
|
|
57
|
-
- \`POST /notification/mark-all-read\` - Mark all notifications as read
|
|
58
|
-
- \`POST /notification/unread-count\` - Get unread notification count
|
|
59
|
-
|
|
60
|
-
#### Admin/System
|
|
61
|
-
- \`POST /notification/send\` - Send notification to a user
|
|
62
|
-
- \`POST /notification/send-bulk\` - Send notification to multiple users
|
|
63
|
-
|
|
64
|
-
### WebSocket Events
|
|
65
|
-
|
|
66
|
-
Connect to WebSocket with JWT authentication:
|
|
67
|
-
\`\`\`
|
|
68
|
-
ws://localhost:2002/notification?token=<jwt>
|
|
69
|
-
\`\`\`
|
|
70
|
-
|
|
71
|
-
**Events:**
|
|
72
|
-
- \`notification:new\` - New notification received
|
|
73
|
-
- \`notification:read\` - Notification marked as read
|
|
74
|
-
- \`notification:read-all\` - All notifications marked as read
|
|
75
|
-
|
|
76
|
-
### Notification Types
|
|
77
|
-
- \`info\` - General information
|
|
78
|
-
- \`success\` - Success message
|
|
79
|
-
- \`warning\` - Warning message
|
|
80
|
-
- \`error\` - Error message
|
|
81
|
-
`,
|
|
82
|
-
version: '1.0',
|
|
83
|
-
path: 'api/docs/notification',
|
|
84
|
-
bearerAuth: true,
|
|
85
|
-
excludeSchemaProperties: enableCompanyFeature ? undefined : COMPANY_SCHEMA_EXCLUSIONS
|
|
86
|
-
};
|
|
87
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { notificationSwaggerConfig } from './notification-swagger.config';
|
|
2
|
-
describe('notificationSwaggerConfig', ()=>{
|
|
3
|
-
it('should default to company feature enabled and single-database mode when no config is given', ()=>{
|
|
4
|
-
const config = notificationSwaggerConfig();
|
|
5
|
-
expect(config.title).toBe('Notification API');
|
|
6
|
-
expect(config.path).toBe('api/docs/notification');
|
|
7
|
-
expect(config.bearerAuth).toBe(true);
|
|
8
|
-
expect(config.excludeSchemaProperties).toBeUndefined();
|
|
9
|
-
expect(config.description).not.toContain('Multi-Tenant Mode');
|
|
10
|
-
});
|
|
11
|
-
it('should exclude companyId schema properties across all notification DTOs when the company feature is disabled', ()=>{
|
|
12
|
-
const config = notificationSwaggerConfig({
|
|
13
|
-
enableCompanyFeature: false
|
|
14
|
-
});
|
|
15
|
-
expect(config.excludeSchemaProperties).toEqual([
|
|
16
|
-
{
|
|
17
|
-
schemaName: 'NotificationResponseDto',
|
|
18
|
-
properties: [
|
|
19
|
-
'companyId'
|
|
20
|
-
]
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
schemaName: 'CreateNotificationDto',
|
|
24
|
-
properties: [
|
|
25
|
-
'companyId'
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
schemaName: 'SendNotificationDto',
|
|
30
|
-
properties: [
|
|
31
|
-
'companyId'
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
schemaName: 'SendBulkNotificationDto',
|
|
36
|
-
properties: [
|
|
37
|
-
'companyId'
|
|
38
|
-
]
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
schemaName: 'UpdateNotificationDto',
|
|
42
|
-
properties: [
|
|
43
|
-
'companyId'
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
]);
|
|
47
|
-
});
|
|
48
|
-
it('should not exclude any schema properties when the company feature is enabled', ()=>{
|
|
49
|
-
const config = notificationSwaggerConfig({
|
|
50
|
-
enableCompanyFeature: true
|
|
51
|
-
});
|
|
52
|
-
expect(config.excludeSchemaProperties).toBeUndefined();
|
|
53
|
-
});
|
|
54
|
-
it('should mention multi-tenant mode in the description only for multi-tenant database mode', ()=>{
|
|
55
|
-
const multiTenant = notificationSwaggerConfig({
|
|
56
|
-
databaseMode: 'multi-tenant'
|
|
57
|
-
});
|
|
58
|
-
const single = notificationSwaggerConfig({
|
|
59
|
-
databaseMode: 'single'
|
|
60
|
-
});
|
|
61
|
-
expect(multiTenant.description).toContain('Multi-Tenant Mode');
|
|
62
|
-
expect(single.description).not.toContain('Multi-Tenant Mode');
|
|
63
|
-
});
|
|
64
|
-
});
|