dt-common-device 3.0.10 → 3.0.11

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