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.
- package/TROUBLESHOOTING.md +184 -0
- package/dist/config/config.d.ts +9 -2
- package/dist/config/config.js +97 -14
- package/dist/constants/Event.d.ts +75 -0
- package/dist/constants/Event.js +78 -0
- package/dist/db/db.d.ts +1 -0
- package/dist/db/db.js +18 -2
- package/dist/device/local/entities/AlertBuilder.d.ts +87 -0
- package/dist/device/local/entities/AlertBuilder.example.d.ts +11 -0
- package/dist/device/local/entities/AlertBuilder.example.js +117 -0
- package/dist/device/local/entities/AlertBuilder.js +179 -0
- package/dist/device/local/entities/IssueBuilder.d.ts +109 -0
- package/dist/device/local/entities/IssueBuilder.example.d.ts +16 -0
- package/dist/device/local/entities/IssueBuilder.example.js +196 -0
- package/dist/device/local/entities/IssueBuilder.js +237 -0
- package/dist/device/local/entities/index.d.ts +2 -0
- package/dist/device/local/entities/index.js +7 -0
- package/dist/device/local/interfaces/IDevice.d.ts +10 -9
- package/dist/device/local/interfaces/IDevice.js +7 -0
- package/dist/device/local/models/Alert.model.d.ts +28 -0
- package/dist/device/local/models/Alert.model.js +222 -0
- package/dist/device/local/models/Issue.model.d.ts +28 -0
- package/dist/device/local/models/Issue.model.js +260 -0
- package/dist/device/local/repository/Alert.repository.d.ts +106 -0
- package/dist/device/local/repository/Alert.repository.js +374 -0
- package/dist/device/local/repository/Device.repository.d.ts +10 -2
- package/dist/device/local/repository/Device.repository.js +153 -30
- package/dist/device/local/repository/Hub.repository.d.ts +1 -1
- package/dist/device/local/repository/Hub.repository.js +60 -18
- package/dist/device/local/repository/Issue.repository.d.ts +113 -0
- package/dist/device/local/repository/Issue.repository.js +401 -0
- package/dist/device/local/repository/Schedule.repository.d.ts +1 -1
- package/dist/device/local/repository/Schedule.repository.js +14 -18
- package/dist/device/local/services/Alert.service.d.ts +135 -5
- package/dist/device/local/services/Alert.service.js +471 -7
- package/dist/device/local/services/AlertService.example.d.ts +55 -0
- package/dist/device/local/services/AlertService.example.js +148 -0
- package/dist/device/local/services/Device.service.d.ts +8 -5
- package/dist/device/local/services/Device.service.js +58 -40
- package/dist/device/local/services/Issue.service.d.ts +168 -0
- package/dist/device/local/services/Issue.service.js +642 -0
- package/dist/device/local/services/IssueService.example.d.ts +68 -0
- package/dist/device/local/services/IssueService.example.js +177 -0
- package/dist/device/local/services/index.d.ts +7 -5
- package/dist/device/local/services/index.js +21 -11
- package/dist/events/BaseEventHandler.d.ts +43 -0
- package/dist/events/BaseEventHandler.js +111 -0
- package/dist/events/BaseEventTransformer.d.ts +26 -0
- package/dist/events/BaseEventTransformer.js +72 -0
- package/dist/events/DeviceEventHandler.d.ts +15 -0
- package/dist/events/DeviceEventHandler.js +152 -0
- package/dist/events/DeviceEventTransformerFactory.d.ts +27 -0
- package/dist/events/DeviceEventTransformerFactory.js +116 -0
- package/dist/events/EventHandler.d.ts +11 -0
- package/dist/events/EventHandler.js +106 -0
- package/dist/events/EventHandlerOrchestrator.d.ts +35 -0
- package/dist/events/EventHandlerOrchestrator.js +141 -0
- package/dist/events/EventProcessingService.d.ts +43 -0
- package/dist/events/EventProcessingService.js +243 -0
- package/dist/events/InternalEventSubscription.d.ts +44 -0
- package/dist/events/InternalEventSubscription.js +152 -0
- package/dist/events/index.d.ts +9 -0
- package/dist/events/index.js +21 -0
- package/dist/events/interfaces/DeviceEvent.d.ts +48 -0
- package/dist/events/interfaces/DeviceEvent.js +2 -0
- package/dist/events/interfaces/IEventHandler.d.ts +23 -0
- package/dist/events/interfaces/IEventHandler.js +2 -0
- package/dist/events/interfaces/IEventTransformer.d.ts +7 -0
- package/dist/events/interfaces/IEventTransformer.js +2 -0
- package/dist/events/interfaces/IInternalEvent.d.ts +42 -0
- package/dist/events/interfaces/IInternalEvent.js +2 -0
- package/dist/events/interfaces/index.d.ts +4 -0
- package/dist/events/interfaces/index.js +20 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +9 -2
- package/dist/types/alert.types.d.ts +57 -0
- package/dist/types/alert.types.js +22 -0
- package/dist/types/config.types.d.ts +15 -4
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/issue.types.d.ts +90 -0
- package/dist/types/issue.types.js +40 -0
- package/dist/utils/http-utils.d.ts +13 -0
- package/dist/utils/http-utils.js +117 -0
- package/package.json +2 -1
- package/src/config/config.ts +117 -14
- package/src/{device/local/events/Events.ts → constants/Event.ts} +34 -13
- package/src/db/db.ts +14 -5
- package/src/device/local/entities/AlertBuilder.example.ts +126 -0
- package/src/device/local/entities/AlertBuilder.ts +202 -0
- package/src/device/local/entities/IssueBuilder.example.ts +210 -0
- package/src/device/local/entities/IssueBuilder.ts +263 -0
- package/src/device/local/entities/README.md +173 -0
- package/src/device/local/entities/index.ts +2 -0
- package/src/device/local/interfaces/IDevice.ts +11 -9
- package/src/device/local/models/Alert.model.md +319 -0
- package/src/device/local/models/Alert.model.ts +283 -0
- package/src/device/local/models/Issue.model.md +386 -0
- package/src/device/local/models/Issue.model.ts +350 -0
- package/src/device/local/models/README.md +312 -0
- package/src/device/local/repository/Alert.repository.ts +465 -0
- package/src/device/local/repository/Device.repository.ts +241 -32
- package/src/device/local/repository/Hub.repository.ts +74 -18
- package/src/device/local/repository/Issue.repository.ts +517 -0
- package/src/device/local/repository/Schedule.repository.ts +28 -22
- package/src/device/local/services/Alert.service.ts +617 -5
- package/src/device/local/services/AlertService.example.ts +229 -0
- package/src/device/local/services/Device.service.ts +70 -50
- package/src/device/local/services/Issue.service.ts +872 -0
- package/src/device/local/services/IssueService.example.ts +307 -0
- package/src/device/local/services/index.ts +7 -5
- package/src/events/BaseEventHandler.ts +145 -0
- package/src/events/BaseEventTransformer.ts +97 -0
- package/src/events/DeviceEventHandler.ts +211 -0
- package/src/events/DeviceEventTransformerFactory.ts +77 -0
- package/src/{device/local/events → events}/EventHandler.ts +19 -15
- package/src/events/EventHandlerOrchestrator.ts +119 -0
- package/src/events/EventProcessingService.ts +248 -0
- package/src/events/InternalEventSubscription.ts +219 -0
- package/src/events/index.ts +9 -0
- package/src/events/interfaces/DeviceEvent.ts +56 -0
- package/src/events/interfaces/IEventHandler.ts +28 -0
- package/src/events/interfaces/IEventTransformer.ts +8 -0
- package/src/events/interfaces/IInternalEvent.ts +47 -0
- package/src/events/interfaces/index.ts +4 -0
- package/src/index.ts +9 -2
- package/src/types/alert.types.ts +64 -0
- package/src/types/config.types.ts +17 -4
- package/src/types/index.ts +2 -0
- package/src/types/issue.types.ts +98 -0
- package/src/utils/http-utils.ts +143 -0
- package/src/device/local/events/index.ts +0 -2
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import mongoose, { Schema, Model } from "mongoose";
|
|
2
|
+
import {
|
|
3
|
+
IssuesCategory,
|
|
4
|
+
EntityType,
|
|
5
|
+
IssueStatus,
|
|
6
|
+
IssuePriority,
|
|
7
|
+
IssueComment,
|
|
8
|
+
IssueDocument as IIssueDocument,
|
|
9
|
+
CreateIssueData,
|
|
10
|
+
UpdateIssueData,
|
|
11
|
+
AddCommentData,
|
|
12
|
+
} from "../../../types/issue.types";
|
|
13
|
+
|
|
14
|
+
// Comment sub-schema
|
|
15
|
+
const CommentSchema = new Schema<IssueComment>(
|
|
16
|
+
{
|
|
17
|
+
id: { type: String, required: true },
|
|
18
|
+
userId: { type: String, required: true },
|
|
19
|
+
content: { type: String, required: true },
|
|
20
|
+
createdAt: { type: Date, default: Date.now },
|
|
21
|
+
updatedAt: { type: Date },
|
|
22
|
+
},
|
|
23
|
+
{ _id: false }
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Interface for instance methods
|
|
27
|
+
interface IIssueMethods {
|
|
28
|
+
addComment(commentData: AddCommentData): void;
|
|
29
|
+
updateComment(commentId: string, content: string, userId: string): boolean;
|
|
30
|
+
removeComment(commentId: string): boolean;
|
|
31
|
+
resolve(resolvedBy: string): void;
|
|
32
|
+
reopen(updatedBy: string): void;
|
|
33
|
+
assign(userId: string, assignedBy: string): void;
|
|
34
|
+
unassign(unassignedBy: string): void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Interface for static methods
|
|
38
|
+
interface IIssueModel extends Model<IIssueDocument, {}, IIssueMethods> {
|
|
39
|
+
findByProperty(
|
|
40
|
+
propertyId: string,
|
|
41
|
+
includeDeleted?: boolean
|
|
42
|
+
): Promise<IIssueDocument[]>;
|
|
43
|
+
findByAssignee(
|
|
44
|
+
assignedTo: string,
|
|
45
|
+
includeDeleted?: boolean
|
|
46
|
+
): Promise<IIssueDocument[]>;
|
|
47
|
+
findByEntity(
|
|
48
|
+
entityId: string,
|
|
49
|
+
entityType: EntityType,
|
|
50
|
+
includeDeleted?: boolean
|
|
51
|
+
): Promise<IIssueDocument[]>;
|
|
52
|
+
findByStatus(
|
|
53
|
+
status: IssueStatus,
|
|
54
|
+
includeDeleted?: boolean
|
|
55
|
+
): Promise<IIssueDocument[]>;
|
|
56
|
+
findByPriority(
|
|
57
|
+
priority: IssuePriority,
|
|
58
|
+
includeDeleted?: boolean
|
|
59
|
+
): Promise<IIssueDocument[]>;
|
|
60
|
+
findOverdue(includeDeleted?: boolean): Promise<IIssueDocument[]>;
|
|
61
|
+
findUpcoming(
|
|
62
|
+
days?: number,
|
|
63
|
+
includeDeleted?: boolean
|
|
64
|
+
): Promise<IIssueDocument[]>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Main Issue schema
|
|
68
|
+
const IssueSchema = new Schema<IIssueDocument, IIssueModel, IIssueMethods>(
|
|
69
|
+
{
|
|
70
|
+
category: {
|
|
71
|
+
type: String,
|
|
72
|
+
enum: Object.values(IssuesCategory),
|
|
73
|
+
required: true,
|
|
74
|
+
},
|
|
75
|
+
propertyId: {
|
|
76
|
+
type: String,
|
|
77
|
+
required: true,
|
|
78
|
+
index: true,
|
|
79
|
+
},
|
|
80
|
+
title: {
|
|
81
|
+
type: String,
|
|
82
|
+
required: true,
|
|
83
|
+
trim: true,
|
|
84
|
+
},
|
|
85
|
+
description: {
|
|
86
|
+
type: String,
|
|
87
|
+
required: true,
|
|
88
|
+
trim: true,
|
|
89
|
+
},
|
|
90
|
+
entityId: {
|
|
91
|
+
type: String,
|
|
92
|
+
index: true,
|
|
93
|
+
},
|
|
94
|
+
entityType: {
|
|
95
|
+
type: String,
|
|
96
|
+
enum: Object.values(EntityType),
|
|
97
|
+
required: true,
|
|
98
|
+
index: true,
|
|
99
|
+
},
|
|
100
|
+
status: {
|
|
101
|
+
type: String,
|
|
102
|
+
enum: Object.values(IssueStatus),
|
|
103
|
+
default: IssueStatus.PENDING,
|
|
104
|
+
index: true,
|
|
105
|
+
},
|
|
106
|
+
priority: {
|
|
107
|
+
type: String,
|
|
108
|
+
enum: Object.values(IssuePriority),
|
|
109
|
+
default: IssuePriority.MEDIUM,
|
|
110
|
+
},
|
|
111
|
+
assignedTo: {
|
|
112
|
+
type: String,
|
|
113
|
+
index: true,
|
|
114
|
+
},
|
|
115
|
+
createdBy: {
|
|
116
|
+
type: String,
|
|
117
|
+
required: true,
|
|
118
|
+
},
|
|
119
|
+
updatedBy: {
|
|
120
|
+
type: String,
|
|
121
|
+
},
|
|
122
|
+
isDeleted: {
|
|
123
|
+
type: Boolean,
|
|
124
|
+
default: false,
|
|
125
|
+
},
|
|
126
|
+
createdAt: {
|
|
127
|
+
type: Date,
|
|
128
|
+
default: Date.now,
|
|
129
|
+
},
|
|
130
|
+
updatedAt: {
|
|
131
|
+
type: Date,
|
|
132
|
+
default: Date.now,
|
|
133
|
+
},
|
|
134
|
+
resolvedAt: {
|
|
135
|
+
type: Date,
|
|
136
|
+
},
|
|
137
|
+
dueDate: {
|
|
138
|
+
type: Date,
|
|
139
|
+
},
|
|
140
|
+
comments: {
|
|
141
|
+
type: [CommentSchema],
|
|
142
|
+
default: [],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
timestamps: true,
|
|
147
|
+
collection: "dt_issues",
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
IssueSchema.index({ propertyId: 1, status: 1 });
|
|
152
|
+
IssueSchema.index({ assignedTo: 1, status: 1 });
|
|
153
|
+
IssueSchema.index({ entityId: 1, entityType: 1 });
|
|
154
|
+
|
|
155
|
+
// Pre-save middleware to update the updatedAt field
|
|
156
|
+
IssueSchema.pre("save", function (next) {
|
|
157
|
+
this.updatedAt = new Date();
|
|
158
|
+
next();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Pre-update middleware to update the updatedAt field
|
|
162
|
+
IssueSchema.pre(
|
|
163
|
+
["updateOne", "findOneAndUpdate", "updateMany"],
|
|
164
|
+
function (next) {
|
|
165
|
+
this.set({ updatedAt: new Date() });
|
|
166
|
+
next();
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Instance methods
|
|
171
|
+
IssueSchema.methods.addComment = function (commentData: AddCommentData): void {
|
|
172
|
+
const comment: IssueComment = {
|
|
173
|
+
id: new mongoose.Types.ObjectId().toString(),
|
|
174
|
+
userId: commentData.userId,
|
|
175
|
+
content: commentData.content,
|
|
176
|
+
createdAt: new Date(),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (!this.comments) {
|
|
180
|
+
this.comments = [];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
this.comments.push(comment);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
IssueSchema.methods.updateComment = function (
|
|
187
|
+
commentId: string,
|
|
188
|
+
content: string,
|
|
189
|
+
userId: string
|
|
190
|
+
): boolean {
|
|
191
|
+
if (!this.comments) return false;
|
|
192
|
+
|
|
193
|
+
const comment = this.comments.find((c: IssueComment) => c.id === commentId);
|
|
194
|
+
if (comment) {
|
|
195
|
+
comment.content = content;
|
|
196
|
+
comment.updatedAt = new Date();
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
IssueSchema.methods.removeComment = function (commentId: string): boolean {
|
|
203
|
+
if (!this.comments) return false;
|
|
204
|
+
|
|
205
|
+
const initialLength = this.comments.length;
|
|
206
|
+
this.comments = this.comments.filter((c: IssueComment) => c.id !== commentId);
|
|
207
|
+
return this.comments.length < initialLength;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
IssueSchema.methods.resolve = function (resolvedBy: string): void {
|
|
211
|
+
this.status = IssueStatus.RESOLVED;
|
|
212
|
+
this.resolvedAt = new Date();
|
|
213
|
+
this.updatedBy = resolvedBy;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
IssueSchema.methods.reopen = function (updatedBy: string): void {
|
|
217
|
+
this.status = IssueStatus.PENDING;
|
|
218
|
+
this.resolvedAt = undefined;
|
|
219
|
+
this.updatedBy = updatedBy;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
IssueSchema.methods.assign = function (
|
|
223
|
+
userId: string,
|
|
224
|
+
assignedBy: string
|
|
225
|
+
): void {
|
|
226
|
+
this.assignedTo = userId;
|
|
227
|
+
this.updatedBy = assignedBy;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
IssueSchema.methods.unassign = function (unassignedBy: string): void {
|
|
231
|
+
this.assignedTo = undefined;
|
|
232
|
+
this.updatedBy = unassignedBy;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Static methods
|
|
236
|
+
IssueSchema.statics.findByProperty = function (
|
|
237
|
+
propertyId: string,
|
|
238
|
+
includeDeleted = false
|
|
239
|
+
) {
|
|
240
|
+
const query: any = { propertyId };
|
|
241
|
+
if (!includeDeleted) {
|
|
242
|
+
query.isDeleted = false;
|
|
243
|
+
}
|
|
244
|
+
return this.find(query).sort({ createdAt: -1 });
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
IssueSchema.statics.findByAssignee = function (
|
|
248
|
+
assignedTo: string,
|
|
249
|
+
includeDeleted = false
|
|
250
|
+
) {
|
|
251
|
+
const query: any = { assignedTo };
|
|
252
|
+
if (!includeDeleted) {
|
|
253
|
+
query.isDeleted = false;
|
|
254
|
+
}
|
|
255
|
+
return this.find(query).sort({ priority: -1, createdAt: -1 });
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
IssueSchema.statics.findByEntity = function (
|
|
259
|
+
entityId: string,
|
|
260
|
+
entityType: EntityType,
|
|
261
|
+
includeDeleted = false
|
|
262
|
+
) {
|
|
263
|
+
const query: any = { entityId, entityType };
|
|
264
|
+
if (!includeDeleted) {
|
|
265
|
+
query.isDeleted = false;
|
|
266
|
+
}
|
|
267
|
+
return this.find(query).sort({ createdAt: -1 });
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
IssueSchema.statics.findByStatus = function (
|
|
271
|
+
status: IssueStatus,
|
|
272
|
+
includeDeleted = false
|
|
273
|
+
) {
|
|
274
|
+
const query: any = { status };
|
|
275
|
+
if (!includeDeleted) {
|
|
276
|
+
query.isDeleted = false;
|
|
277
|
+
}
|
|
278
|
+
return this.find(query).sort({ priority: -1, createdAt: -1 });
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
IssueSchema.statics.findByPriority = function (
|
|
282
|
+
priority: IssuePriority,
|
|
283
|
+
includeDeleted = false
|
|
284
|
+
) {
|
|
285
|
+
const query: any = { priority };
|
|
286
|
+
if (!includeDeleted) {
|
|
287
|
+
query.isDeleted = false;
|
|
288
|
+
}
|
|
289
|
+
return this.find(query).sort({ createdAt: -1 });
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
IssueSchema.statics.findOverdue = function (includeDeleted = false) {
|
|
293
|
+
const query: any = {
|
|
294
|
+
dueDate: { $lt: new Date() },
|
|
295
|
+
status: {
|
|
296
|
+
$nin: [IssueStatus.RESOLVED, IssueStatus.CLOSED, IssueStatus.CANCELLED],
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
if (!includeDeleted) {
|
|
300
|
+
query.isDeleted = false;
|
|
301
|
+
}
|
|
302
|
+
return this.find(query).sort({ dueDate: 1 });
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
IssueSchema.statics.findUpcoming = function (
|
|
306
|
+
days: number = 7,
|
|
307
|
+
includeDeleted = false
|
|
308
|
+
) {
|
|
309
|
+
const futureDate = new Date();
|
|
310
|
+
futureDate.setDate(futureDate.getDate() + days);
|
|
311
|
+
|
|
312
|
+
const query: any = {
|
|
313
|
+
dueDate: { $gte: new Date(), $lte: futureDate },
|
|
314
|
+
status: {
|
|
315
|
+
$nin: [IssueStatus.RESOLVED, IssueStatus.CLOSED, IssueStatus.CANCELLED],
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
if (!includeDeleted) {
|
|
319
|
+
query.isDeleted = false;
|
|
320
|
+
}
|
|
321
|
+
return this.find(query).sort({ dueDate: 1 });
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// Virtual for soft delete
|
|
325
|
+
IssueSchema.virtual("isActive").get(function () {
|
|
326
|
+
return !this.isDeleted;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Ensure virtuals are serialized
|
|
330
|
+
IssueSchema.set("toJSON", { virtuals: true });
|
|
331
|
+
IssueSchema.set("toObject", { virtuals: true });
|
|
332
|
+
|
|
333
|
+
// Create and export the model
|
|
334
|
+
export const IssueModel = mongoose.model<IIssueDocument, IIssueModel>(
|
|
335
|
+
"Issue",
|
|
336
|
+
IssueSchema
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
// Export the schema for potential reuse
|
|
340
|
+
export { IssueSchema };
|
|
341
|
+
|
|
342
|
+
// Export types for external use
|
|
343
|
+
export type {
|
|
344
|
+
IIssueDocument,
|
|
345
|
+
CreateIssueData,
|
|
346
|
+
UpdateIssueData,
|
|
347
|
+
AddCommentData,
|
|
348
|
+
IIssueMethods,
|
|
349
|
+
IIssueModel,
|
|
350
|
+
};
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Local Device Models
|
|
2
|
+
|
|
3
|
+
This directory contains Mongoose models for local device management, implementing Prisma schema compatibility.
|
|
4
|
+
|
|
5
|
+
## Models
|
|
6
|
+
|
|
7
|
+
### Alert Model (`Alert.model.ts`)
|
|
8
|
+
|
|
9
|
+
A comprehensive alert management system with snooze functionality, severity levels, and read status tracking.
|
|
10
|
+
|
|
11
|
+
**📖 [Detailed Documentation](./Alert.model.md)**
|
|
12
|
+
|
|
13
|
+
#### Key Features
|
|
14
|
+
|
|
15
|
+
- **Severity Management**: INFO, LOW, MEDIUM, HIGH, CRITICAL levels
|
|
16
|
+
- **Snooze Functionality**: Temporarily suppress alerts until specified date
|
|
17
|
+
- **Read Status**: Track read/unread status with automatic logging
|
|
18
|
+
- **Category System**: READINESS, OPERATIONS, SECURITY, ENERGY, OTHER
|
|
19
|
+
- **Entity References**: Link alerts to devices, hubs, and other entities
|
|
20
|
+
- **Statistics**: Comprehensive alert statistics and monitoring
|
|
21
|
+
|
|
22
|
+
#### Quick Example
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { AlertService } from "../services/Alert.service";
|
|
26
|
+
import {
|
|
27
|
+
AlertCategory,
|
|
28
|
+
AlertSeverity,
|
|
29
|
+
EntityType,
|
|
30
|
+
} from "../../../types/alert.types";
|
|
31
|
+
|
|
32
|
+
const alertService = new AlertService();
|
|
33
|
+
|
|
34
|
+
// Create a critical alert
|
|
35
|
+
const alert = await alertService.createAlert({
|
|
36
|
+
category: AlertCategory.READINESS,
|
|
37
|
+
propertyId: "property-123",
|
|
38
|
+
title: "Device Offline",
|
|
39
|
+
description: "Device has been offline for more than 5 minutes",
|
|
40
|
+
entityId: "device-456",
|
|
41
|
+
entityType: EntityType.DEVICE,
|
|
42
|
+
severity: AlertSeverity.HIGH,
|
|
43
|
+
createdBy: "user-789",
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Snooze for 24 hours
|
|
47
|
+
await alertService.snoozeAlert(
|
|
48
|
+
alert._id,
|
|
49
|
+
new Date(Date.now() + 24 * 60 * 60 * 1000),
|
|
50
|
+
"user-789"
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Issue Model (`Issue.model.ts`)
|
|
55
|
+
|
|
56
|
+
A comprehensive issue tracking system with assignment, comments, and resolution workflow.
|
|
57
|
+
|
|
58
|
+
**📖 [Detailed Documentation](./Issue.model.md)**
|
|
59
|
+
|
|
60
|
+
#### Key Features
|
|
61
|
+
|
|
62
|
+
- **Status Workflow**: OPEN → IN_PROGRESS → RESOLVED → CLOSED
|
|
63
|
+
- **Assignment System**: Assign issues to users with validation
|
|
64
|
+
- **Comment System**: Add, update, and remove comments on issues
|
|
65
|
+
- **Priority Management**: LOW, MEDIUM, HIGH, CRITICAL with overdue calculation
|
|
66
|
+
- **Due Date Tracking**: Set and monitor due dates with automatic overdue detection
|
|
67
|
+
- **Search & Statistics**: Full-text search and comprehensive reporting
|
|
68
|
+
|
|
69
|
+
#### Quick Example
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { IssueService } from "../services/Issue.service";
|
|
73
|
+
import {
|
|
74
|
+
IssueCategory,
|
|
75
|
+
IssuePriority,
|
|
76
|
+
EntityType,
|
|
77
|
+
} from "../../../types/issue.types";
|
|
78
|
+
|
|
79
|
+
const issueService = new IssueService();
|
|
80
|
+
|
|
81
|
+
// Create an issue
|
|
82
|
+
const issue = await issueService.createIssue({
|
|
83
|
+
category: IssueCategory.READINESS,
|
|
84
|
+
propertyId: "property-123",
|
|
85
|
+
title: "Device Connection Issue",
|
|
86
|
+
description: "Device is not responding to commands",
|
|
87
|
+
entityId: "device-456",
|
|
88
|
+
entityType: EntityType.DEVICE,
|
|
89
|
+
priority: IssuePriority.HIGH,
|
|
90
|
+
createdBy: "user-789",
|
|
91
|
+
dueDate: new Date("2024-01-15"),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Assign and start progress
|
|
95
|
+
await issueService.assignIssue(issue._id, "tech-user-123", "user-789");
|
|
96
|
+
await issueService.updateIssue(issue._id, {
|
|
97
|
+
status: IssueStatus.IN_PROGRESS,
|
|
98
|
+
updatedBy: "tech-user-123",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Add comment
|
|
102
|
+
await issueService.addComment(issue._id, {
|
|
103
|
+
userId: "tech-user-123",
|
|
104
|
+
content: "Investigating the connection issue",
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Architecture
|
|
109
|
+
|
|
110
|
+
### Clean Architecture Pattern
|
|
111
|
+
|
|
112
|
+
Both models follow a clean architecture pattern with clear separation of concerns:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
Service Layer (Business Logic)
|
|
116
|
+
↓
|
|
117
|
+
Repository Layer (Data Access)
|
|
118
|
+
↓
|
|
119
|
+
Model Layer (Data Structure)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Service Layer
|
|
123
|
+
|
|
124
|
+
- **Business Logic**: Validation, business rules, workflow management
|
|
125
|
+
- **Error Handling**: Comprehensive error messages and validation
|
|
126
|
+
- **Monitoring**: Built-in logging for important events
|
|
127
|
+
- **Repository Usage**: All data access through repository layer
|
|
128
|
+
|
|
129
|
+
#### Repository Layer
|
|
130
|
+
|
|
131
|
+
- **Data Access**: CRUD operations with error handling
|
|
132
|
+
- **Query Optimization**: Efficient filtering and sorting
|
|
133
|
+
- **Bulk Operations**: Support for bulk updates and deletes
|
|
134
|
+
- **Statistics**: Aggregation and reporting functions
|
|
135
|
+
|
|
136
|
+
#### Model Layer
|
|
137
|
+
|
|
138
|
+
- **Schema Definition**: Mongoose schemas with validation
|
|
139
|
+
- **Instance Methods**: Common operations on individual documents
|
|
140
|
+
- **Static Methods**: Complex queries and filtering
|
|
141
|
+
- **Virtual Fields**: Computed properties
|
|
142
|
+
- **Middleware**: Pre/post save hooks for automation
|
|
143
|
+
|
|
144
|
+
## Common Patterns
|
|
145
|
+
|
|
146
|
+
### Business Rules Implementation
|
|
147
|
+
|
|
148
|
+
Both models implement business rules in the service layer:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Alert Service - Severity Assignment
|
|
152
|
+
private determineDefaultSeverity(category: AlertCategory): AlertSeverity {
|
|
153
|
+
const categorySeverities: Record<AlertCategory, AlertSeverity> = {
|
|
154
|
+
[AlertCategory.READINESS]: AlertSeverity.HIGH,
|
|
155
|
+
[AlertCategory.SECURITY]: AlertSeverity.CRITICAL,
|
|
156
|
+
// ... more mappings
|
|
157
|
+
};
|
|
158
|
+
return categorySeverities[category] || AlertSeverity.MEDIUM;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Issue Service - Status Transitions
|
|
162
|
+
private validateStatusTransition(currentStatus: IssueStatus, newStatus: IssueStatus): void {
|
|
163
|
+
const validTransitions = {
|
|
164
|
+
[IssueStatus.OPEN]: [IssueStatus.IN_PROGRESS],
|
|
165
|
+
[IssueStatus.IN_PROGRESS]: [IssueStatus.RESOLVED],
|
|
166
|
+
[IssueStatus.RESOLVED]: [IssueStatus.CLOSED, IssueStatus.OPEN],
|
|
167
|
+
// ... more transitions
|
|
168
|
+
};
|
|
169
|
+
// Validation logic
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Error Handling
|
|
174
|
+
|
|
175
|
+
Consistent error handling across all layers:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// Repository Layer
|
|
179
|
+
try {
|
|
180
|
+
return await AlertModel.findById(id);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Failed to find alert by ID: ${
|
|
184
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
185
|
+
}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Service Layer
|
|
190
|
+
if (!id) {
|
|
191
|
+
throw new Error("Alert ID is required");
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Monitoring and Logging
|
|
196
|
+
|
|
197
|
+
Built-in monitoring for important events:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// Alert monitoring
|
|
201
|
+
if (stats.bySeverity[AlertSeverity.CRITICAL] > 0) {
|
|
202
|
+
console.error(
|
|
203
|
+
`Alert: ${
|
|
204
|
+
stats.bySeverity[AlertSeverity.CRITICAL]
|
|
205
|
+
} critical alerts require immediate attention`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Issue monitoring
|
|
210
|
+
if (stats.overdue > 0) {
|
|
211
|
+
console.warn(`Alert: ${stats.overdue} overdue issues require attention`);
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Best Practices
|
|
216
|
+
|
|
217
|
+
### 1. **Use Service Layer for Business Logic**
|
|
218
|
+
|
|
219
|
+
Always use the service layer for operations that involve business rules, validation, or workflow management.
|
|
220
|
+
|
|
221
|
+
### 2. **Use Repository Layer for Data Access**
|
|
222
|
+
|
|
223
|
+
Use the repository layer for direct data access operations, bulk operations, and complex queries.
|
|
224
|
+
|
|
225
|
+
### 3. **Validate Input Data**
|
|
226
|
+
|
|
227
|
+
Always validate input data before processing to ensure data integrity.
|
|
228
|
+
|
|
229
|
+
### 4. **Handle Errors Gracefully**
|
|
230
|
+
|
|
231
|
+
Use try-catch blocks and provide meaningful error messages for better debugging.
|
|
232
|
+
|
|
233
|
+
### 5. **Use Soft Deletion**
|
|
234
|
+
|
|
235
|
+
Always use soft deletion unless explicitly required to permanently remove data.
|
|
236
|
+
|
|
237
|
+
### 6. **Monitor Important Events**
|
|
238
|
+
|
|
239
|
+
Implement logging for critical events like overdue issues, critical alerts, and status changes.
|
|
240
|
+
|
|
241
|
+
### 7. **Use TypeScript Interfaces**
|
|
242
|
+
|
|
243
|
+
Leverage TypeScript interfaces for type safety and better development experience.
|
|
244
|
+
|
|
245
|
+
### 8. **Follow Naming Conventions**
|
|
246
|
+
|
|
247
|
+
Use consistent naming conventions across all layers for better maintainability.
|
|
248
|
+
|
|
249
|
+
### 9. **Document Business Rules**
|
|
250
|
+
|
|
251
|
+
Clearly document business rules and validation logic for future reference.
|
|
252
|
+
|
|
253
|
+
### 10. **Test Business Logic**
|
|
254
|
+
|
|
255
|
+
Write tests for business logic to ensure rules are correctly implemented.
|
|
256
|
+
|
|
257
|
+
## Migration from Prisma
|
|
258
|
+
|
|
259
|
+
Both models maintain full compatibility with their original Prisma schemas:
|
|
260
|
+
|
|
261
|
+
- ✅ All fields and types preserved
|
|
262
|
+
- ✅ Indexes match Prisma schema
|
|
263
|
+
- ✅ Relationships maintained through foreign key references
|
|
264
|
+
- ✅ Collection names match Prisma (`dt_alerts`, `dt_issues`)
|
|
265
|
+
|
|
266
|
+
Additional features beyond Prisma:
|
|
267
|
+
|
|
268
|
+
- ✅ Instance methods for common operations
|
|
269
|
+
- ✅ Static methods for complex queries
|
|
270
|
+
- ✅ Virtual fields for computed properties
|
|
271
|
+
- ✅ Comprehensive business logic validation
|
|
272
|
+
- ✅ Built-in monitoring and logging
|
|
273
|
+
- ✅ Comment management system (Issues)
|
|
274
|
+
- ✅ Snooze functionality (Alerts)
|
|
275
|
+
- ✅ Overdue calculation logic (Issues)
|
|
276
|
+
|
|
277
|
+
## Quick Start
|
|
278
|
+
|
|
279
|
+
1. **Import Services**:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { AlertService, IssueService } from "../services";
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
2. **Create Instances**:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
const alertService = new AlertService();
|
|
289
|
+
const issueService = new IssueService();
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
3. **Use Business Logic**:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// Create alert with automatic severity assignment
|
|
296
|
+
const alert = await alertService.createAlert(alertData);
|
|
297
|
+
|
|
298
|
+
// Create issue with workflow validation
|
|
299
|
+
const issue = await issueService.createIssue(issueData);
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
4. **Monitor and Report**:
|
|
303
|
+
```typescript
|
|
304
|
+
// Get statistics
|
|
305
|
+
const alertStats = await alertService.getAlertStatistics();
|
|
306
|
+
const issueStats = await issueService.getIssueStatistics();
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
For detailed usage examples and advanced features, refer to the individual model documentation:
|
|
310
|
+
|
|
311
|
+
- [Alert Model Documentation](./Alert.model.md)
|
|
312
|
+
- [Issue Model Documentation](./Issue.model.md)
|