dt-common-device 1.3.0 → 2.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 (132) hide show
  1. package/TROUBLESHOOTING.md +184 -0
  2. package/dist/config/config.d.ts +9 -2
  3. package/dist/config/config.js +97 -14
  4. package/dist/constants/Event.d.ts +75 -0
  5. package/dist/constants/Event.js +78 -0
  6. package/dist/db/db.d.ts +1 -0
  7. package/dist/db/db.js +18 -2
  8. package/dist/device/local/entities/AlertBuilder.d.ts +87 -0
  9. package/dist/device/local/entities/AlertBuilder.example.d.ts +11 -0
  10. package/dist/device/local/entities/AlertBuilder.example.js +117 -0
  11. package/dist/device/local/entities/AlertBuilder.js +179 -0
  12. package/dist/device/local/entities/IssueBuilder.d.ts +109 -0
  13. package/dist/device/local/entities/IssueBuilder.example.d.ts +16 -0
  14. package/dist/device/local/entities/IssueBuilder.example.js +196 -0
  15. package/dist/device/local/entities/IssueBuilder.js +237 -0
  16. package/dist/device/local/entities/index.d.ts +2 -0
  17. package/dist/device/local/entities/index.js +7 -0
  18. package/dist/device/local/interfaces/IDevice.d.ts +10 -9
  19. package/dist/device/local/interfaces/IDevice.js +7 -0
  20. package/dist/device/local/models/Alert.model.d.ts +28 -0
  21. package/dist/device/local/models/Alert.model.js +222 -0
  22. package/dist/device/local/models/Issue.model.d.ts +28 -0
  23. package/dist/device/local/models/Issue.model.js +260 -0
  24. package/dist/device/local/repository/Alert.repository.d.ts +106 -0
  25. package/dist/device/local/repository/Alert.repository.js +374 -0
  26. package/dist/device/local/repository/Device.repository.d.ts +10 -2
  27. package/dist/device/local/repository/Device.repository.js +153 -30
  28. package/dist/device/local/repository/Hub.repository.d.ts +1 -1
  29. package/dist/device/local/repository/Hub.repository.js +60 -18
  30. package/dist/device/local/repository/Issue.repository.d.ts +113 -0
  31. package/dist/device/local/repository/Issue.repository.js +401 -0
  32. package/dist/device/local/repository/Schedule.repository.d.ts +1 -1
  33. package/dist/device/local/repository/Schedule.repository.js +14 -18
  34. package/dist/device/local/services/Alert.service.d.ts +135 -5
  35. package/dist/device/local/services/Alert.service.js +471 -7
  36. package/dist/device/local/services/AlertService.example.d.ts +55 -0
  37. package/dist/device/local/services/AlertService.example.js +148 -0
  38. package/dist/device/local/services/Device.service.d.ts +8 -5
  39. package/dist/device/local/services/Device.service.js +58 -40
  40. package/dist/device/local/services/Issue.service.d.ts +168 -0
  41. package/dist/device/local/services/Issue.service.js +642 -0
  42. package/dist/device/local/services/IssueService.example.d.ts +68 -0
  43. package/dist/device/local/services/IssueService.example.js +177 -0
  44. package/dist/device/local/services/index.d.ts +7 -5
  45. package/dist/device/local/services/index.js +21 -11
  46. package/dist/events/BaseEventHandler.d.ts +43 -0
  47. package/dist/events/BaseEventHandler.js +111 -0
  48. package/dist/events/BaseEventTransformer.d.ts +26 -0
  49. package/dist/events/BaseEventTransformer.js +72 -0
  50. package/dist/events/DeviceEventHandler.d.ts +15 -0
  51. package/dist/events/DeviceEventHandler.js +152 -0
  52. package/dist/events/DeviceEventTransformerFactory.d.ts +27 -0
  53. package/dist/events/DeviceEventTransformerFactory.js +116 -0
  54. package/dist/events/EventHandler.d.ts +11 -0
  55. package/dist/events/EventHandler.js +106 -0
  56. package/dist/events/EventHandlerOrchestrator.d.ts +35 -0
  57. package/dist/events/EventHandlerOrchestrator.js +141 -0
  58. package/dist/events/EventProcessingService.d.ts +43 -0
  59. package/dist/events/EventProcessingService.js +243 -0
  60. package/dist/events/InternalEventSubscription.d.ts +44 -0
  61. package/dist/events/InternalEventSubscription.js +152 -0
  62. package/dist/events/index.d.ts +9 -0
  63. package/dist/events/index.js +21 -0
  64. package/dist/events/interfaces/DeviceEvent.d.ts +48 -0
  65. package/dist/events/interfaces/DeviceEvent.js +2 -0
  66. package/dist/events/interfaces/IEventHandler.d.ts +23 -0
  67. package/dist/events/interfaces/IEventHandler.js +2 -0
  68. package/dist/events/interfaces/IEventTransformer.d.ts +7 -0
  69. package/dist/events/interfaces/IEventTransformer.js +2 -0
  70. package/dist/events/interfaces/IInternalEvent.d.ts +42 -0
  71. package/dist/events/interfaces/IInternalEvent.js +2 -0
  72. package/dist/events/interfaces/index.d.ts +4 -0
  73. package/dist/events/interfaces/index.js +20 -0
  74. package/dist/index.d.ts +6 -2
  75. package/dist/index.js +9 -2
  76. package/dist/types/alert.types.d.ts +57 -0
  77. package/dist/types/alert.types.js +22 -0
  78. package/dist/types/config.types.d.ts +15 -4
  79. package/dist/types/index.d.ts +2 -0
  80. package/dist/types/index.js +2 -0
  81. package/dist/types/issue.types.d.ts +90 -0
  82. package/dist/types/issue.types.js +40 -0
  83. package/dist/utils/http-utils.d.ts +13 -0
  84. package/dist/utils/http-utils.js +117 -0
  85. package/package.json +2 -1
  86. package/src/config/config.ts +117 -14
  87. package/src/{device/local/events/Events.ts → constants/Event.ts} +34 -13
  88. package/src/db/db.ts +14 -5
  89. package/src/device/local/entities/AlertBuilder.example.ts +126 -0
  90. package/src/device/local/entities/AlertBuilder.ts +202 -0
  91. package/src/device/local/entities/IssueBuilder.example.ts +210 -0
  92. package/src/device/local/entities/IssueBuilder.ts +263 -0
  93. package/src/device/local/entities/README.md +173 -0
  94. package/src/device/local/entities/index.ts +2 -0
  95. package/src/device/local/interfaces/IDevice.ts +11 -9
  96. package/src/device/local/models/Alert.model.md +319 -0
  97. package/src/device/local/models/Alert.model.ts +283 -0
  98. package/src/device/local/models/Issue.model.md +386 -0
  99. package/src/device/local/models/Issue.model.ts +350 -0
  100. package/src/device/local/models/README.md +312 -0
  101. package/src/device/local/repository/Alert.repository.ts +465 -0
  102. package/src/device/local/repository/Device.repository.ts +241 -32
  103. package/src/device/local/repository/Hub.repository.ts +74 -18
  104. package/src/device/local/repository/Issue.repository.ts +517 -0
  105. package/src/device/local/repository/Schedule.repository.ts +28 -22
  106. package/src/device/local/services/Alert.service.ts +617 -5
  107. package/src/device/local/services/AlertService.example.ts +229 -0
  108. package/src/device/local/services/Device.service.ts +70 -50
  109. package/src/device/local/services/Issue.service.ts +872 -0
  110. package/src/device/local/services/IssueService.example.ts +307 -0
  111. package/src/device/local/services/index.ts +7 -5
  112. package/src/events/BaseEventHandler.ts +145 -0
  113. package/src/events/BaseEventTransformer.ts +97 -0
  114. package/src/events/DeviceEventHandler.ts +211 -0
  115. package/src/events/DeviceEventTransformerFactory.ts +77 -0
  116. package/src/{device/local/events → events}/EventHandler.ts +19 -15
  117. package/src/events/EventHandlerOrchestrator.ts +119 -0
  118. package/src/events/EventProcessingService.ts +248 -0
  119. package/src/events/InternalEventSubscription.ts +219 -0
  120. package/src/events/index.ts +9 -0
  121. package/src/events/interfaces/DeviceEvent.ts +56 -0
  122. package/src/events/interfaces/IEventHandler.ts +28 -0
  123. package/src/events/interfaces/IEventTransformer.ts +8 -0
  124. package/src/events/interfaces/IInternalEvent.ts +47 -0
  125. package/src/events/interfaces/index.ts +4 -0
  126. package/src/index.ts +9 -2
  127. package/src/types/alert.types.ts +64 -0
  128. package/src/types/config.types.ts +17 -4
  129. package/src/types/index.ts +2 -0
  130. package/src/types/issue.types.ts +98 -0
  131. package/src/utils/http-utils.ts +143 -0
  132. package/src/device/local/events/index.ts +0 -2
@@ -1,8 +1,620 @@
1
+ import { Service } from "typedi";
2
+ import { AlertRepository } from "../repository/Alert.repository";
3
+ import { AlertModel, IAlertDocument } from "../models/Alert.model";
4
+ import {
5
+ CreateAlertData,
6
+ UpdateAlertData,
7
+ AlertCategory,
8
+ AlertSeverity,
9
+ EntityType,
10
+ } from "../../../types/alert.types";
11
+ import { AlertBuilder } from "../entities/AlertBuilder";
12
+
13
+ @Service()
1
14
  export class AlertService {
2
- constructor() {}
15
+ constructor(private readonly alertRepository: AlertRepository) {}
16
+
17
+ /**
18
+ * Create a readiness alert using AlertBuilder
19
+ */
20
+ async raiseReadinessAlert(
21
+ propertyId: string,
22
+ title: string,
23
+ description: string,
24
+ entityId?: string,
25
+ entityType?: EntityType,
26
+ createdBy?: string
27
+ ): Promise<IAlertDocument> {
28
+ const alertBuilder = AlertBuilder.createReadinessAlert()
29
+ .setPropertyId(propertyId)
30
+ .setTitle(title)
31
+ .setDescription(description);
32
+
33
+ if (entityId) alertBuilder.setEntityId(entityId);
34
+ if (entityType) alertBuilder.setEntityType(entityType);
35
+ if (createdBy) alertBuilder.setCreatedBy(createdBy);
36
+
37
+ return await this.createAlert(alertBuilder);
38
+ }
39
+
40
+ /**
41
+ * Create an operations alert using AlertBuilder
42
+ */
43
+ async raiseOperationsAlert(
44
+ propertyId: string,
45
+ title: string,
46
+ description: string,
47
+ entityId?: string,
48
+ entityType?: EntityType,
49
+ createdBy?: string
50
+ ): Promise<IAlertDocument> {
51
+ const alertBuilder = AlertBuilder.createOperationsAlert()
52
+ .setPropertyId(propertyId)
53
+ .setTitle(title)
54
+ .setDescription(description);
55
+
56
+ if (entityId) alertBuilder.setEntityId(entityId);
57
+ if (entityType) alertBuilder.setEntityType(entityType);
58
+ if (createdBy) alertBuilder.setCreatedBy(createdBy);
59
+
60
+ return await this.createAlert(alertBuilder);
61
+ }
62
+
63
+ /**
64
+ * Create a security alert using AlertBuilder
65
+ */
66
+ async raiseSecurityAlert(
67
+ propertyId: string,
68
+ title: string,
69
+ description: string,
70
+ entityId?: string,
71
+ entityType?: EntityType,
72
+ createdBy?: string
73
+ ): Promise<IAlertDocument> {
74
+ const alertBuilder = AlertBuilder.createSecurityAlert()
75
+ .setPropertyId(propertyId)
76
+ .setTitle(title)
77
+ .setDescription(description);
78
+
79
+ if (entityId) alertBuilder.setEntityId(entityId);
80
+ if (entityType) alertBuilder.setEntityType(entityType);
81
+ if (createdBy) alertBuilder.setCreatedBy(createdBy);
82
+
83
+ return await this.createAlert(alertBuilder);
84
+ }
85
+
86
+ /**
87
+ * Create an energy alert using AlertBuilder
88
+ */
89
+ async raiseEnergyAlert(
90
+ propertyId: string,
91
+ title: string,
92
+ description: string,
93
+ entityId?: string,
94
+ entityType?: EntityType,
95
+ createdBy?: string
96
+ ): Promise<IAlertDocument> {
97
+ const alertBuilder = AlertBuilder.createEnergyAlert()
98
+ .setPropertyId(propertyId)
99
+ .setTitle(title)
100
+ .setDescription(description);
101
+
102
+ if (entityId) alertBuilder.setEntityId(entityId);
103
+ if (entityType) alertBuilder.setEntityType(entityType);
104
+ if (createdBy) alertBuilder.setCreatedBy(createdBy);
105
+
106
+ return await this.createAlert(alertBuilder);
107
+ }
108
+
109
+ /**
110
+ * Create a device-specific alert using AlertBuilder
111
+ */
112
+ async raiseDeviceAlert(
113
+ deviceId: string,
114
+ propertyId: string,
115
+ title: string,
116
+ description: string,
117
+ category?: AlertCategory,
118
+ severity?: AlertSeverity,
119
+ createdBy?: string
120
+ ): Promise<IAlertDocument> {
121
+ const alertBuilder = AlertBuilder.createDeviceAlert(deviceId, propertyId)
122
+ .setTitle(title)
123
+ .setDescription(description);
124
+
125
+ if (category) alertBuilder.setCategory(category);
126
+ if (severity) alertBuilder.setSeverity(severity);
127
+ if (createdBy) alertBuilder.setCreatedBy(createdBy);
128
+
129
+ return await this.createAlert(alertBuilder);
130
+ }
131
+
132
+ /**
133
+ * Create a hub-specific alert using AlertBuilder
134
+ */
135
+ async raiseHubAlert(
136
+ hubId: string,
137
+ propertyId: string,
138
+ title: string,
139
+ description: string,
140
+ category?: AlertCategory,
141
+ severity?: AlertSeverity,
142
+ createdBy?: string
143
+ ): Promise<IAlertDocument> {
144
+ const alertBuilder = AlertBuilder.createHubAlert(hubId, propertyId)
145
+ .setTitle(title)
146
+ .setDescription(description);
147
+
148
+ if (category) alertBuilder.setCategory(category);
149
+ if (severity) alertBuilder.setSeverity(severity);
150
+ if (createdBy) alertBuilder.setCreatedBy(createdBy);
151
+
152
+ return await this.createAlert(alertBuilder);
153
+ }
154
+
155
+ /**
156
+ * Create a new alert with business logic validation
157
+ * Accepts either a CreateAlertData object or an AlertBuilder instance
158
+ */
159
+ async createAlert(alertData: CreateAlertData | AlertBuilder): Promise<IAlertDocument> {
160
+ let processedAlertData: CreateAlertData;
161
+
162
+ // Handle AlertBuilder instance
163
+ if (alertData instanceof AlertBuilder) {
164
+ processedAlertData = alertData.build();
165
+ } else {
166
+ processedAlertData = alertData;
167
+ }
168
+
169
+ // Business logic: Validate alert data
170
+ this.validateAlertData(processedAlertData);
171
+
172
+ // Business logic: Set default severity if not provided
173
+ if (!processedAlertData.severity) {
174
+ processedAlertData.severity = this.determineDefaultSeverity(processedAlertData.category);
175
+ }
176
+
177
+ // Business logic: Validate snooze date is in the future
178
+ if (processedAlertData.snoozeUntil && processedAlertData.snoozeUntil <= new Date()) {
179
+ throw new Error("Snooze date must be in the future");
180
+ }
181
+
182
+ return await this.alertRepository.create(processedAlertData);
183
+ }
184
+
185
+ /**
186
+ * Get alert by ID with business logic
187
+ */
188
+ async getAlertById(
189
+ id: string,
190
+ includeDeleted = false
191
+ ): Promise<IAlertDocument | null> {
192
+ if (!id) {
193
+ throw new Error("Alert ID is required");
194
+ }
195
+
196
+ const alert = await this.alertRepository.findById(id, includeDeleted);
197
+
198
+ // Business logic: Check if alert is snoozed and expired
199
+ if (alert?.snoozeUntil && alert.snoozeUntil <= new Date()) {
200
+ console.warn(`Alert ${id} snooze has expired`);
201
+ }
202
+
203
+ return alert;
204
+ }
205
+
206
+ /**
207
+ * Get all alerts with business logic filtering
208
+ */
209
+ async getAlerts(
210
+ filters: {
211
+ propertyId?: string;
212
+ category?: AlertCategory;
213
+ severity?: AlertSeverity;
214
+ entityType?: EntityType;
215
+ entityId?: string;
216
+ isActive?: boolean;
217
+ isRead?: boolean;
218
+ includeDeleted?: boolean;
219
+ limit?: number;
220
+ skip?: number;
221
+ } = {}
222
+ ): Promise<IAlertDocument[]> {
223
+ // Business logic: Validate filters
224
+ this.validateFilters(filters);
225
+
226
+ // Business logic: Apply business rules to filters
227
+ const enhancedFilters = this.applyBusinessRules(filters);
228
+
229
+ return await this.alertRepository.findAll(enhancedFilters);
230
+ }
231
+
232
+ /**
233
+ * Update an alert with business logic validation
234
+ */
235
+ async updateAlert(
236
+ id: string,
237
+ updateData: UpdateAlertData
238
+ ): Promise<IAlertDocument | null> {
239
+ if (!id) {
240
+ throw new Error("Alert ID is required");
241
+ }
242
+
243
+ // Business logic: Validate update data
244
+ this.validateUpdateData(updateData);
245
+
246
+ // Business logic: Check if alert exists and is not deleted
247
+ const existingAlert = await this.alertRepository.findById(id);
248
+ if (!existingAlert) {
249
+ throw new Error("Alert not found");
250
+ }
251
+
252
+ // Business logic: Handle snooze validation
253
+ if (updateData.snoozeUntil) {
254
+ this.validateSnoozeDate(updateData.snoozeUntil);
255
+ }
256
+
257
+ return await this.alertRepository.update(id, updateData);
258
+ }
259
+
260
+ /**
261
+ * Soft delete an alert with business logic
262
+ */
263
+ async deleteAlert(id: string, deletedBy: string): Promise<boolean> {
264
+ if (!id || !deletedBy) {
265
+ throw new Error("Alert ID and deleted by user are required");
266
+ }
267
+
268
+ // Business logic: Check if alert can be deleted
269
+ const alert = await this.alertRepository.findById(id);
270
+ if (!alert) {
271
+ throw new Error("Alert not found");
272
+ }
273
+
274
+ // Business logic: Prevent deletion of critical alerts (optional rule)
275
+ if (alert.severity === AlertSeverity.CRITICAL) {
276
+ throw new Error("Cannot delete critical alerts");
277
+ }
278
+
279
+ return await this.alertRepository.softDelete(id, deletedBy);
280
+ }
281
+
282
+ /**
283
+ * Mark alert as read with business logic
284
+ */
285
+ async markAsRead(
286
+ id: string,
287
+ updatedBy: string
288
+ ): Promise<IAlertDocument | null> {
289
+ if (!id || !updatedBy) {
290
+ throw new Error("Alert ID and updated by user are required");
291
+ }
292
+
293
+ const alertModel = await AlertModel.findById(id);
294
+ if (!alertModel) return null;
295
+
296
+ alertModel.markAsRead(updatedBy);
297
+ return await alertModel.save();
298
+ }
299
+
300
+ /**
301
+ * Mark alert as unread with business logic
302
+ */
303
+ async markAsUnread(
304
+ id: string,
305
+ updatedBy: string
306
+ ): Promise<IAlertDocument | null> {
307
+ if (!id || !updatedBy) {
308
+ throw new Error("Alert ID and updated by user are required");
309
+ }
310
+
311
+ const alertModel = await AlertModel.findById(id);
312
+ if (!alertModel) return null;
313
+
314
+ alertModel.markAsUnread(updatedBy);
315
+ return await alertModel.save();
316
+ }
317
+
318
+ /**
319
+ * Activate an alert with business logic
320
+ */
321
+ async activateAlert(
322
+ id: string,
323
+ updatedBy: string
324
+ ): Promise<IAlertDocument | null> {
325
+ if (!id || !updatedBy) {
326
+ throw new Error("Alert ID and updated by user are required");
327
+ }
328
+
329
+ const alertModel = await AlertModel.findById(id);
330
+ if (!alertModel) return null;
331
+
332
+ alertModel.activate(updatedBy);
333
+ return await alertModel.save();
334
+ }
335
+
336
+ /**
337
+ * Deactivate an alert with business logic
338
+ */
339
+ async deactivateAlert(
340
+ id: string,
341
+ updatedBy: string
342
+ ): Promise<IAlertDocument | null> {
343
+ if (!id || !updatedBy) {
344
+ throw new Error("Alert ID and updated by user are required");
345
+ }
346
+
347
+ const alertModel = await AlertModel.findById(id);
348
+ if (!alertModel) return null;
349
+
350
+ alertModel.deactivate(updatedBy);
351
+ return await alertModel.save();
352
+ }
353
+
354
+ /**
355
+ * Snooze an alert with business logic
356
+ */
357
+ async snoozeAlert(
358
+ id: string,
359
+ until: Date,
360
+ updatedBy: string
361
+ ): Promise<IAlertDocument | null> {
362
+ if (!id || !until || !updatedBy) {
363
+ throw new Error(
364
+ "Alert ID, snooze date, and updated by user are required"
365
+ );
366
+ }
367
+
368
+ // Business logic: Validate snooze date
369
+ this.validateSnoozeDate(until);
370
+
371
+ const alertModel = await AlertModel.findById(id);
372
+ if (!alertModel) return null;
373
+
374
+ alertModel.snooze(until, updatedBy);
375
+ return await alertModel.save();
376
+ }
377
+
378
+ /**
379
+ * Unsnooze an alert with business logic
380
+ */
381
+ async unsnoozeAlert(
382
+ id: string,
383
+ updatedBy: string
384
+ ): Promise<IAlertDocument | null> {
385
+ if (!id || !updatedBy) {
386
+ throw new Error("Alert ID and updated by user are required");
387
+ }
388
+
389
+ const alertModel = await AlertModel.findById(id);
390
+ if (!alertModel) return null;
391
+
392
+ alertModel.unsnooze(updatedBy);
393
+ return await alertModel.save();
394
+ }
395
+
396
+ /**
397
+ * Get alerts by property with business logic
398
+ */
399
+ async getAlertsByProperty(
400
+ propertyId: string,
401
+ includeDeleted = false
402
+ ): Promise<IAlertDocument[]> {
403
+ if (!propertyId) {
404
+ throw new Error("Property ID is required");
405
+ }
406
+
407
+ return await this.alertRepository.findByProperty(
408
+ propertyId,
409
+ includeDeleted
410
+ );
411
+ }
412
+
413
+ /**
414
+ * Get alerts by entity with business logic
415
+ */
416
+ async getAlertsByEntity(
417
+ entityId: string,
418
+ entityType: EntityType,
419
+ includeDeleted = false
420
+ ): Promise<IAlertDocument[]> {
421
+ if (!entityId || !entityType) {
422
+ throw new Error("Entity ID and entity type are required");
423
+ }
424
+
425
+ return await this.alertRepository.findByEntity(
426
+ entityId,
427
+ entityType,
428
+ includeDeleted
429
+ );
430
+ }
431
+
432
+ /**
433
+ * Get alerts by category with business logic
434
+ */
435
+ async getAlertsByCategory(
436
+ category: AlertCategory,
437
+ includeDeleted = false
438
+ ): Promise<IAlertDocument[]> {
439
+ if (!category) {
440
+ throw new Error("Alert category is required");
441
+ }
442
+
443
+ return await this.alertRepository.findByCategory(category, includeDeleted);
444
+ }
445
+
446
+ /**
447
+ * Get alerts by severity with business logic
448
+ */
449
+ async getAlertsBySeverity(
450
+ severity: AlertSeverity,
451
+ includeDeleted = false
452
+ ): Promise<IAlertDocument[]> {
453
+ if (!severity) {
454
+ throw new Error("Alert severity is required");
455
+ }
456
+
457
+ return await this.alertRepository.findBySeverity(severity, includeDeleted);
458
+ }
459
+
460
+ /**
461
+ * Get active alerts with business logic
462
+ */
463
+ async getActiveAlerts(includeDeleted = false): Promise<IAlertDocument[]> {
464
+ const activeAlerts = await this.alertRepository.findActive(includeDeleted);
465
+
466
+ // Business logic: Log active alerts for monitoring
467
+ if (activeAlerts.length > 0) {
468
+ console.log(`Found ${activeAlerts.length} active alerts`);
469
+ }
470
+
471
+ return activeAlerts;
472
+ }
473
+
474
+ /**
475
+ * Get unread alerts with business logic
476
+ */
477
+ async getUnreadAlerts(includeDeleted = false): Promise<IAlertDocument[]> {
478
+ const unreadAlerts = await this.alertRepository.findUnread(includeDeleted);
479
+
480
+ // Business logic: Log unread alerts for monitoring
481
+ if (unreadAlerts.length > 0) {
482
+ console.warn(`Found ${unreadAlerts.length} unread alerts`);
483
+ }
484
+
485
+ return unreadAlerts;
486
+ }
487
+
488
+ /**
489
+ * Get snoozed alerts with business logic
490
+ */
491
+ async getSnoozedAlerts(includeDeleted = false): Promise<IAlertDocument[]> {
492
+ return await this.alertRepository.findSnoozed(includeDeleted);
493
+ }
494
+
495
+ /**
496
+ * Get expired snooze alerts with business logic
497
+ */
498
+ async getExpiredSnoozeAlerts(
499
+ includeDeleted = false
500
+ ): Promise<IAlertDocument[]> {
501
+ const expiredAlerts = await this.alertRepository.findExpiredSnooze(
502
+ includeDeleted
503
+ );
504
+
505
+ // Business logic: Log expired snooze alerts
506
+ if (expiredAlerts.length > 0) {
507
+ console.warn(`Found ${expiredAlerts.length} alerts with expired snooze`);
508
+ }
509
+
510
+ return expiredAlerts;
511
+ }
512
+
513
+ /**
514
+ * Get alert statistics with business logic
515
+ */
516
+ async getAlertStatistics(propertyId?: string): Promise<{
517
+ total: number;
518
+ active: number;
519
+ unread: number;
520
+ snoozed: number;
521
+ bySeverity: Record<AlertSeverity, number>;
522
+ byCategory: Record<AlertCategory, number>;
523
+ }> {
524
+ const stats = await this.alertRepository.getStatistics(propertyId);
525
+
526
+ // Business logic: Add alerts for critical metrics
527
+ if (stats.unread > 0) {
528
+ console.warn(`Alert: ${stats.unread} unread alerts require attention`);
529
+ }
530
+
531
+ if (stats.bySeverity[AlertSeverity.CRITICAL] > 0) {
532
+ console.error(
533
+ `Alert: ${
534
+ stats.bySeverity[AlertSeverity.CRITICAL]
535
+ } critical alerts require immediate attention`
536
+ );
537
+ }
538
+
539
+ if (stats.bySeverity[AlertSeverity.HIGH] > 0) {
540
+ console.warn(
541
+ `Alert: ${
542
+ stats.bySeverity[AlertSeverity.HIGH]
543
+ } high severity alerts require attention`
544
+ );
545
+ }
546
+
547
+ return stats;
548
+ }
549
+
550
+ // Private business logic methods
551
+
552
+ private validateAlertData(data: CreateAlertData): void {
553
+ if (!data.title || data.title.trim().length < 3) {
554
+ throw new Error("Alert title must be at least 3 characters long");
555
+ }
556
+
557
+ if (!data.description || data.description.trim().length < 5) {
558
+ throw new Error("Alert description must be at least 5 characters long");
559
+ }
560
+
561
+ if (!data.propertyId) {
562
+ throw new Error("Property ID is required");
563
+ }
564
+
565
+ if (!data.entityType) {
566
+ throw new Error("Entity type is required");
567
+ }
568
+ }
569
+
570
+ private validateFilters(filters: any): void {
571
+ if (filters.limit && (filters.limit < 1 || filters.limit > 100)) {
572
+ throw new Error("Limit must be between 1 and 100");
573
+ }
574
+
575
+ if (filters.skip && filters.skip < 0) {
576
+ throw new Error("Skip must be non-negative");
577
+ }
578
+ }
579
+
580
+ private validateUpdateData(data: UpdateAlertData): void {
581
+ if (data.title && data.title.trim().length < 3) {
582
+ throw new Error("Alert title must be at least 3 characters long");
583
+ }
584
+
585
+ if (data.description && data.description.trim().length < 5) {
586
+ throw new Error("Alert description must be at least 5 characters long");
587
+ }
588
+ }
589
+
590
+ private validateSnoozeDate(snoozeUntil: Date): void {
591
+ if (snoozeUntil <= new Date()) {
592
+ throw new Error("Snooze date must be in the future");
593
+ }
594
+ }
595
+
596
+ private determineDefaultSeverity(category: AlertCategory): AlertSeverity {
597
+ // Business logic: Determine default severity based on category
598
+ const categorySeverities: Record<AlertCategory, AlertSeverity> = {
599
+ [AlertCategory.READINESS]: AlertSeverity.MEDIUM,
600
+ [AlertCategory.OPERATIONS]: AlertSeverity.HIGH,
601
+ [AlertCategory.SECURITY]: AlertSeverity.CRITICAL,
602
+ [AlertCategory.ENERGY]: AlertSeverity.LOW,
603
+ [AlertCategory.OTHER]: AlertSeverity.MEDIUM,
604
+ };
605
+
606
+ return categorySeverities[category] || AlertSeverity.MEDIUM;
607
+ }
608
+
609
+ private applyBusinessRules(filters: any): any {
610
+ // Business logic: Apply additional filters based on business rules
611
+ const enhancedFilters = { ...filters };
612
+
613
+ // Example: Always exclude deleted alerts unless explicitly requested
614
+ if (!enhancedFilters.includeDeleted) {
615
+ enhancedFilters.includeDeleted = false;
616
+ }
3
617
 
4
- raiseReadinessAlert() {}
5
- raiseOperationsAlert() {}
6
- raiseSecurityAlert() {}
7
- raiseEnergyAlert() {}
618
+ return enhancedFilters;
619
+ }
8
620
  }