@oneuptime/common 8.0.5492 → 8.0.5496
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/Models/DatabaseModels/Incident.ts +33 -0
- package/Models/DatabaseModels/IncidentFeed.ts +1 -0
- package/Models/DatabaseModels/IncidentPostmortemTemplate.ts +353 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1761834523183-MigrationName.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/IncidentPostmortemTemplateService.ts +10 -0
- package/Server/Services/IncidentService.ts +92 -39
- package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
- package/UI/Components/LogsViewer/components/LiveLogsToggle.tsx +46 -0
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +4 -0
- package/UI/Components/LogsViewer/types.ts +5 -0
- package/build/dist/Models/DatabaseModels/Incident.js +35 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPostmortemTemplate.js +373 -0
- package/build/dist/Models/DatabaseModels/IncidentPostmortemTemplate.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1761834523183-MigrationName.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1761834523183-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/IncidentPostmortemTemplateService.js +9 -0
- package/build/dist/Server/Services/IncidentPostmortemTemplateService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +54 -35
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -5
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js +22 -0
- package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +4 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/types.js +2 -0
- package/build/dist/UI/Components/LogsViewer/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -926,6 +926,39 @@ export default class Incident extends BaseModel {
|
|
|
926
926
|
})
|
|
927
927
|
public rootCause?: string = undefined;
|
|
928
928
|
|
|
929
|
+
@ColumnAccessControl({
|
|
930
|
+
create: [
|
|
931
|
+
Permission.ProjectOwner,
|
|
932
|
+
Permission.ProjectAdmin,
|
|
933
|
+
Permission.ProjectMember,
|
|
934
|
+
Permission.CreateProjectIncident,
|
|
935
|
+
],
|
|
936
|
+
read: [
|
|
937
|
+
Permission.ProjectOwner,
|
|
938
|
+
Permission.ProjectAdmin,
|
|
939
|
+
Permission.ProjectMember,
|
|
940
|
+
Permission.ReadProjectIncident,
|
|
941
|
+
],
|
|
942
|
+
update: [
|
|
943
|
+
Permission.ProjectOwner,
|
|
944
|
+
Permission.ProjectAdmin,
|
|
945
|
+
Permission.ProjectMember,
|
|
946
|
+
Permission.EditProjectIncident,
|
|
947
|
+
],
|
|
948
|
+
})
|
|
949
|
+
@TableColumn({
|
|
950
|
+
type: TableColumnType.Markdown,
|
|
951
|
+
required: false,
|
|
952
|
+
isDefaultValueColumn: false,
|
|
953
|
+
title: "Postmortem Note",
|
|
954
|
+
description: "Document the postmortem summary for this incident.",
|
|
955
|
+
})
|
|
956
|
+
@Column({
|
|
957
|
+
type: ColumnType.Markdown,
|
|
958
|
+
nullable: true,
|
|
959
|
+
})
|
|
960
|
+
public postmortemNote?: string = undefined;
|
|
961
|
+
|
|
929
962
|
@ColumnAccessControl({
|
|
930
963
|
create: [],
|
|
931
964
|
read: [
|
|
@@ -33,6 +33,7 @@ export enum IncidentFeedEventType {
|
|
|
33
33
|
IncidentUpdated = "IncidentUpdated",
|
|
34
34
|
RootCause = "RootCause",
|
|
35
35
|
RemediationNotes = "RemediationNotes",
|
|
36
|
+
PostmortemNote = "PostmortemNote",
|
|
36
37
|
OwnerUserRemoved = "OwnerUserRemoved",
|
|
37
38
|
OwnerTeamRemoved = "OwnerTeamRemoved",
|
|
38
39
|
OnCallPolicy = "OnCallPolicy",
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import Project from "./Project";
|
|
2
|
+
import User from "./User";
|
|
3
|
+
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
|
4
|
+
import Route from "../../Types/API/Route";
|
|
5
|
+
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
|
6
|
+
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
|
7
|
+
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
8
|
+
import ColumnType from "../../Types/Database/ColumnType";
|
|
9
|
+
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
10
|
+
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
11
|
+
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
12
|
+
import TableColumn from "../../Types/Database/TableColumn";
|
|
13
|
+
import TableColumnType from "../../Types/Database/TableColumnType";
|
|
14
|
+
import TableMetadata from "../../Types/Database/TableMetadata";
|
|
15
|
+
import TenantColumn from "../../Types/Database/TenantColumn";
|
|
16
|
+
import IconProp from "../../Types/Icon/IconProp";
|
|
17
|
+
import ObjectID from "../../Types/ObjectID";
|
|
18
|
+
import Permission from "../../Types/Permission";
|
|
19
|
+
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
20
|
+
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
|
|
21
|
+
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
|
|
22
|
+
|
|
23
|
+
@TableBillingAccessControl({
|
|
24
|
+
create: PlanType.Growth,
|
|
25
|
+
read: PlanType.Growth,
|
|
26
|
+
update: PlanType.Growth,
|
|
27
|
+
delete: PlanType.Growth,
|
|
28
|
+
})
|
|
29
|
+
@EnableDocumentation()
|
|
30
|
+
@TenantColumn("projectId")
|
|
31
|
+
@TableAccessControl({
|
|
32
|
+
create: [
|
|
33
|
+
Permission.ProjectOwner,
|
|
34
|
+
Permission.ProjectAdmin,
|
|
35
|
+
Permission.ProjectMember,
|
|
36
|
+
Permission.CreateIncidentNoteTemplate,
|
|
37
|
+
],
|
|
38
|
+
read: [
|
|
39
|
+
Permission.ProjectOwner,
|
|
40
|
+
Permission.ProjectAdmin,
|
|
41
|
+
Permission.ProjectMember,
|
|
42
|
+
Permission.ReadIncidentNoteTemplate,
|
|
43
|
+
],
|
|
44
|
+
delete: [
|
|
45
|
+
Permission.ProjectOwner,
|
|
46
|
+
Permission.ProjectAdmin,
|
|
47
|
+
Permission.ProjectMember,
|
|
48
|
+
Permission.DeleteIncidentNoteTemplate,
|
|
49
|
+
],
|
|
50
|
+
update: [
|
|
51
|
+
Permission.ProjectOwner,
|
|
52
|
+
Permission.ProjectAdmin,
|
|
53
|
+
Permission.ProjectMember,
|
|
54
|
+
Permission.EditIncidentNoteTemplate,
|
|
55
|
+
],
|
|
56
|
+
})
|
|
57
|
+
@CrudApiEndpoint(new Route("/incident-postmortem-template"))
|
|
58
|
+
@Entity({
|
|
59
|
+
name: "IncidentPostmortemTemplate",
|
|
60
|
+
})
|
|
61
|
+
@EnableWorkflow({
|
|
62
|
+
create: true,
|
|
63
|
+
delete: true,
|
|
64
|
+
update: true,
|
|
65
|
+
read: true,
|
|
66
|
+
})
|
|
67
|
+
@TableMetadata({
|
|
68
|
+
tableName: "IncidentPostmortemTemplate",
|
|
69
|
+
singularName: "Incident Postmortem Template",
|
|
70
|
+
pluralName: "Incident Postmortem Templates",
|
|
71
|
+
icon: IconProp.Book,
|
|
72
|
+
tableDescription: "Manage postmortem templates for your incidents",
|
|
73
|
+
})
|
|
74
|
+
export default class IncidentPostmortemTemplate extends BaseModel {
|
|
75
|
+
@ColumnAccessControl({
|
|
76
|
+
create: [
|
|
77
|
+
Permission.ProjectOwner,
|
|
78
|
+
Permission.ProjectAdmin,
|
|
79
|
+
Permission.ProjectMember,
|
|
80
|
+
Permission.CreateIncidentNoteTemplate,
|
|
81
|
+
],
|
|
82
|
+
read: [
|
|
83
|
+
Permission.ProjectOwner,
|
|
84
|
+
Permission.ProjectAdmin,
|
|
85
|
+
Permission.ProjectMember,
|
|
86
|
+
Permission.ReadIncidentNoteTemplate,
|
|
87
|
+
],
|
|
88
|
+
update: [],
|
|
89
|
+
})
|
|
90
|
+
@TableColumn({
|
|
91
|
+
manyToOneRelationColumn: "projectId",
|
|
92
|
+
type: TableColumnType.Entity,
|
|
93
|
+
modelType: Project,
|
|
94
|
+
title: "Project",
|
|
95
|
+
description: "Relation to Project Resource in which this object belongs",
|
|
96
|
+
})
|
|
97
|
+
@ManyToOne(
|
|
98
|
+
() => {
|
|
99
|
+
return Project;
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
eager: false,
|
|
103
|
+
nullable: true,
|
|
104
|
+
onDelete: "CASCADE",
|
|
105
|
+
orphanedRowAction: "nullify",
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
@JoinColumn({ name: "projectId" })
|
|
109
|
+
public project?: Project = undefined;
|
|
110
|
+
|
|
111
|
+
@ColumnAccessControl({
|
|
112
|
+
create: [
|
|
113
|
+
Permission.ProjectOwner,
|
|
114
|
+
Permission.ProjectAdmin,
|
|
115
|
+
Permission.ProjectMember,
|
|
116
|
+
Permission.CreateIncidentNoteTemplate,
|
|
117
|
+
],
|
|
118
|
+
read: [
|
|
119
|
+
Permission.ProjectOwner,
|
|
120
|
+
Permission.ProjectAdmin,
|
|
121
|
+
Permission.ProjectMember,
|
|
122
|
+
Permission.ReadIncidentNoteTemplate,
|
|
123
|
+
],
|
|
124
|
+
update: [],
|
|
125
|
+
})
|
|
126
|
+
@Index()
|
|
127
|
+
@TableColumn({
|
|
128
|
+
type: TableColumnType.ObjectID,
|
|
129
|
+
required: true,
|
|
130
|
+
canReadOnRelationQuery: true,
|
|
131
|
+
title: "Project ID",
|
|
132
|
+
description: "ID of your OneUptime Project in which this object belongs",
|
|
133
|
+
})
|
|
134
|
+
@Column({
|
|
135
|
+
type: ColumnType.ObjectID,
|
|
136
|
+
nullable: false,
|
|
137
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
138
|
+
})
|
|
139
|
+
public projectId?: ObjectID = undefined;
|
|
140
|
+
|
|
141
|
+
@ColumnAccessControl({
|
|
142
|
+
create: [
|
|
143
|
+
Permission.ProjectOwner,
|
|
144
|
+
Permission.ProjectAdmin,
|
|
145
|
+
Permission.ProjectMember,
|
|
146
|
+
Permission.CreateIncidentNoteTemplate,
|
|
147
|
+
],
|
|
148
|
+
read: [
|
|
149
|
+
Permission.ProjectOwner,
|
|
150
|
+
Permission.ProjectAdmin,
|
|
151
|
+
Permission.ProjectMember,
|
|
152
|
+
Permission.ReadIncidentNoteTemplate,
|
|
153
|
+
],
|
|
154
|
+
update: [
|
|
155
|
+
Permission.ProjectOwner,
|
|
156
|
+
Permission.ProjectAdmin,
|
|
157
|
+
Permission.ProjectMember,
|
|
158
|
+
Permission.EditIncidentNoteTemplate,
|
|
159
|
+
],
|
|
160
|
+
})
|
|
161
|
+
@Index()
|
|
162
|
+
@TableColumn({
|
|
163
|
+
type: TableColumnType.Markdown,
|
|
164
|
+
title: "Postmortem Note",
|
|
165
|
+
description:
|
|
166
|
+
"Markdown template used when documenting an incident postmortem.",
|
|
167
|
+
})
|
|
168
|
+
@Column({
|
|
169
|
+
type: ColumnType.Markdown,
|
|
170
|
+
nullable: false,
|
|
171
|
+
unique: false,
|
|
172
|
+
})
|
|
173
|
+
public postmortemNote?: string = undefined;
|
|
174
|
+
|
|
175
|
+
@ColumnAccessControl({
|
|
176
|
+
create: [
|
|
177
|
+
Permission.ProjectOwner,
|
|
178
|
+
Permission.ProjectAdmin,
|
|
179
|
+
Permission.ProjectMember,
|
|
180
|
+
Permission.CreateIncidentNoteTemplate,
|
|
181
|
+
],
|
|
182
|
+
read: [
|
|
183
|
+
Permission.ProjectOwner,
|
|
184
|
+
Permission.ProjectAdmin,
|
|
185
|
+
Permission.ProjectMember,
|
|
186
|
+
Permission.ReadIncidentNoteTemplate,
|
|
187
|
+
],
|
|
188
|
+
update: [
|
|
189
|
+
Permission.ProjectOwner,
|
|
190
|
+
Permission.ProjectAdmin,
|
|
191
|
+
Permission.ProjectMember,
|
|
192
|
+
Permission.EditIncidentNoteTemplate,
|
|
193
|
+
],
|
|
194
|
+
})
|
|
195
|
+
@TableColumn({
|
|
196
|
+
required: true,
|
|
197
|
+
type: TableColumnType.ShortText,
|
|
198
|
+
canReadOnRelationQuery: true,
|
|
199
|
+
title: "Name",
|
|
200
|
+
description: "Name of the Postmortem Template",
|
|
201
|
+
})
|
|
202
|
+
@Column({
|
|
203
|
+
nullable: false,
|
|
204
|
+
type: ColumnType.ShortText,
|
|
205
|
+
length: ColumnLength.ShortText,
|
|
206
|
+
})
|
|
207
|
+
public templateName?: string = undefined;
|
|
208
|
+
|
|
209
|
+
@ColumnAccessControl({
|
|
210
|
+
create: [
|
|
211
|
+
Permission.ProjectOwner,
|
|
212
|
+
Permission.ProjectAdmin,
|
|
213
|
+
Permission.ProjectMember,
|
|
214
|
+
Permission.CreateIncidentNoteTemplate,
|
|
215
|
+
],
|
|
216
|
+
read: [
|
|
217
|
+
Permission.ProjectOwner,
|
|
218
|
+
Permission.ProjectAdmin,
|
|
219
|
+
Permission.ProjectMember,
|
|
220
|
+
Permission.ReadIncidentNoteTemplate,
|
|
221
|
+
],
|
|
222
|
+
update: [
|
|
223
|
+
Permission.ProjectOwner,
|
|
224
|
+
Permission.ProjectAdmin,
|
|
225
|
+
Permission.ProjectMember,
|
|
226
|
+
Permission.EditIncidentNoteTemplate,
|
|
227
|
+
],
|
|
228
|
+
})
|
|
229
|
+
@TableColumn({
|
|
230
|
+
required: true,
|
|
231
|
+
type: TableColumnType.LongText,
|
|
232
|
+
canReadOnRelationQuery: true,
|
|
233
|
+
title: "Template Description",
|
|
234
|
+
description: "Description of the Postmortem Template",
|
|
235
|
+
})
|
|
236
|
+
@Column({
|
|
237
|
+
nullable: false,
|
|
238
|
+
type: ColumnType.LongText,
|
|
239
|
+
length: ColumnLength.LongText,
|
|
240
|
+
})
|
|
241
|
+
public templateDescription?: string = undefined;
|
|
242
|
+
|
|
243
|
+
@ColumnAccessControl({
|
|
244
|
+
create: [
|
|
245
|
+
Permission.ProjectOwner,
|
|
246
|
+
Permission.ProjectAdmin,
|
|
247
|
+
Permission.ProjectMember,
|
|
248
|
+
Permission.CreateIncidentNoteTemplate,
|
|
249
|
+
],
|
|
250
|
+
read: [
|
|
251
|
+
Permission.ProjectOwner,
|
|
252
|
+
Permission.ProjectAdmin,
|
|
253
|
+
Permission.ProjectMember,
|
|
254
|
+
Permission.ReadIncidentNoteTemplate,
|
|
255
|
+
],
|
|
256
|
+
update: [],
|
|
257
|
+
})
|
|
258
|
+
@TableColumn({
|
|
259
|
+
manyToOneRelationColumn: "createdByUserId",
|
|
260
|
+
type: TableColumnType.Entity,
|
|
261
|
+
modelType: User,
|
|
262
|
+
title: "Created by User",
|
|
263
|
+
description:
|
|
264
|
+
"Relation to User who created this object (if this object was created by a User)",
|
|
265
|
+
})
|
|
266
|
+
@ManyToOne(
|
|
267
|
+
() => {
|
|
268
|
+
return User;
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
eager: false,
|
|
272
|
+
nullable: true,
|
|
273
|
+
onDelete: "SET NULL",
|
|
274
|
+
orphanedRowAction: "nullify",
|
|
275
|
+
},
|
|
276
|
+
)
|
|
277
|
+
@JoinColumn({ name: "createdByUserId" })
|
|
278
|
+
public createdByUser?: User = undefined;
|
|
279
|
+
|
|
280
|
+
@ColumnAccessControl({
|
|
281
|
+
create: [
|
|
282
|
+
Permission.ProjectOwner,
|
|
283
|
+
Permission.ProjectAdmin,
|
|
284
|
+
Permission.ProjectMember,
|
|
285
|
+
Permission.CreateIncidentNoteTemplate,
|
|
286
|
+
],
|
|
287
|
+
read: [
|
|
288
|
+
Permission.ProjectOwner,
|
|
289
|
+
Permission.ProjectAdmin,
|
|
290
|
+
Permission.ProjectMember,
|
|
291
|
+
Permission.ReadIncidentNoteTemplate,
|
|
292
|
+
],
|
|
293
|
+
update: [],
|
|
294
|
+
})
|
|
295
|
+
@TableColumn({
|
|
296
|
+
type: TableColumnType.ObjectID,
|
|
297
|
+
title: "Created by User ID",
|
|
298
|
+
description:
|
|
299
|
+
"User ID who created this object (if this object was created by a User)",
|
|
300
|
+
})
|
|
301
|
+
@Column({
|
|
302
|
+
type: ColumnType.ObjectID,
|
|
303
|
+
nullable: true,
|
|
304
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
305
|
+
})
|
|
306
|
+
public createdByUserId?: ObjectID = undefined;
|
|
307
|
+
|
|
308
|
+
@ColumnAccessControl({
|
|
309
|
+
create: [],
|
|
310
|
+
read: [],
|
|
311
|
+
update: [],
|
|
312
|
+
})
|
|
313
|
+
@TableColumn({
|
|
314
|
+
manyToOneRelationColumn: "deletedByUserId",
|
|
315
|
+
type: TableColumnType.Entity,
|
|
316
|
+
title: "Deleted by User",
|
|
317
|
+
modelType: User,
|
|
318
|
+
description:
|
|
319
|
+
"Relation to User who deleted this object (if this object was deleted by a User)",
|
|
320
|
+
})
|
|
321
|
+
@ManyToOne(
|
|
322
|
+
() => {
|
|
323
|
+
return User;
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
cascade: false,
|
|
327
|
+
eager: false,
|
|
328
|
+
nullable: true,
|
|
329
|
+
onDelete: "SET NULL",
|
|
330
|
+
orphanedRowAction: "nullify",
|
|
331
|
+
},
|
|
332
|
+
)
|
|
333
|
+
@JoinColumn({ name: "deletedByUserId" })
|
|
334
|
+
public deletedByUser?: User = undefined;
|
|
335
|
+
|
|
336
|
+
@ColumnAccessControl({
|
|
337
|
+
create: [],
|
|
338
|
+
read: [],
|
|
339
|
+
update: [],
|
|
340
|
+
})
|
|
341
|
+
@TableColumn({
|
|
342
|
+
type: TableColumnType.ObjectID,
|
|
343
|
+
title: "Deleted by User ID",
|
|
344
|
+
description:
|
|
345
|
+
"User ID who deleted this object (if this object was deleted by a User)",
|
|
346
|
+
})
|
|
347
|
+
@Column({
|
|
348
|
+
type: ColumnType.ObjectID,
|
|
349
|
+
nullable: true,
|
|
350
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
351
|
+
})
|
|
352
|
+
public deletedByUserId?: ObjectID = undefined;
|
|
353
|
+
}
|
|
@@ -24,6 +24,7 @@ import IncidentFeed from "./IncidentFeed";
|
|
|
24
24
|
import IncidentCustomField from "./IncidentCustomField";
|
|
25
25
|
import IncidentInternalNote from "./IncidentInternalNote";
|
|
26
26
|
import IncidentNoteTemplate from "./IncidentNoteTemplate";
|
|
27
|
+
import IncidentPostmortemTemplate from "./IncidentPostmortemTemplate";
|
|
27
28
|
import IncidentOwnerTeam from "./IncidentOwnerTeam";
|
|
28
29
|
import IncidentOwnerUser from "./IncidentOwnerUser";
|
|
29
30
|
import IncidentPublicNote from "./IncidentPublicNote";
|
|
@@ -235,6 +236,7 @@ const AllModelTypes: Array<{
|
|
|
235
236
|
IncidentOwnerUser,
|
|
236
237
|
IncidentSeverity,
|
|
237
238
|
IncidentNoteTemplate,
|
|
239
|
+
IncidentPostmortemTemplate,
|
|
238
240
|
|
|
239
241
|
AlertState,
|
|
240
242
|
Alert,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1761834523183 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1761834523183";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`CREATE TABLE "IncidentPostmortemTemplate" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "postmortemNote" text NOT NULL, "templateName" character varying(100) NOT NULL, "templateDescription" character varying(500) NOT NULL, "createdByUserId" uuid, "deletedByUserId" uuid, CONSTRAINT "PK_76a09ebf10e7874f0c8ee1f0120" PRIMARY KEY ("_id"))`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`CREATE INDEX "IDX_2a18729813c7c666cc37683c4e" ON "IncidentPostmortemTemplate" ("projectId") `,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`CREATE INDEX "IDX_c791fe4d7179b57064ace561c3" ON "IncidentPostmortemTemplate" ("postmortemNote") `,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(`ALTER TABLE "Incident" ADD "postmortemNote" text`);
|
|
17
|
+
await queryRunner.query(
|
|
18
|
+
`ALTER TABLE "IncidentPostmortemTemplate" ADD CONSTRAINT "FK_2a18729813c7c666cc37683c4ea" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
19
|
+
);
|
|
20
|
+
await queryRunner.query(
|
|
21
|
+
`ALTER TABLE "IncidentPostmortemTemplate" ADD CONSTRAINT "FK_961ac93c4d7ea881170692333d0" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
|
22
|
+
);
|
|
23
|
+
await queryRunner.query(
|
|
24
|
+
`ALTER TABLE "IncidentPostmortemTemplate" ADD CONSTRAINT "FK_2e886387888f1311f361d569b8e" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
29
|
+
await queryRunner.query(
|
|
30
|
+
`ALTER TABLE "IncidentPostmortemTemplate" DROP CONSTRAINT "FK_2e886387888f1311f361d569b8e"`,
|
|
31
|
+
);
|
|
32
|
+
await queryRunner.query(
|
|
33
|
+
`ALTER TABLE "IncidentPostmortemTemplate" DROP CONSTRAINT "FK_961ac93c4d7ea881170692333d0"`,
|
|
34
|
+
);
|
|
35
|
+
await queryRunner.query(
|
|
36
|
+
`ALTER TABLE "IncidentPostmortemTemplate" DROP CONSTRAINT "FK_2a18729813c7c666cc37683c4ea"`,
|
|
37
|
+
);
|
|
38
|
+
await queryRunner.query(
|
|
39
|
+
`ALTER TABLE "Incident" DROP COLUMN "postmortemNote"`,
|
|
40
|
+
);
|
|
41
|
+
await queryRunner.query(
|
|
42
|
+
`DROP INDEX "public"."IDX_c791fe4d7179b57064ace561c3"`,
|
|
43
|
+
);
|
|
44
|
+
await queryRunner.query(
|
|
45
|
+
`DROP INDEX "public"."IDX_2a18729813c7c666cc37683c4e"`,
|
|
46
|
+
);
|
|
47
|
+
await queryRunner.query(`DROP TABLE "IncidentPostmortemTemplate"`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -178,6 +178,7 @@ import { MigrationName1759943124812 } from "./1759943124812-MigrationName";
|
|
|
178
178
|
import { MigrationName1760345757975 } from "./1760345757975-MigrationName";
|
|
179
179
|
import { MigrationName1760357680881 } from "./1760357680881-MigrationName";
|
|
180
180
|
import { MigrationName1761232578396 } from "./1761232578396-MigrationName";
|
|
181
|
+
import { MigrationName1761834523183 } from "./1761834523183-MigrationName";
|
|
181
182
|
|
|
182
183
|
export default [
|
|
183
184
|
InitialMigration,
|
|
@@ -360,4 +361,5 @@ export default [
|
|
|
360
361
|
MigrationName1760345757975,
|
|
361
362
|
MigrationName1760357680881,
|
|
362
363
|
MigrationName1761232578396,
|
|
364
|
+
MigrationName1761834523183,
|
|
363
365
|
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import Model from "../../Models/DatabaseModels/IncidentPostmortemTemplate";
|
|
3
|
+
|
|
4
|
+
export class Service extends DatabaseService<Model> {
|
|
5
|
+
public constructor() {
|
|
6
|
+
super(Model);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default new Service();
|
|
@@ -49,7 +49,7 @@ import Semaphore, {
|
|
|
49
49
|
} from "../../Server/Infrastructure/Semaphore";
|
|
50
50
|
import IncidentFeedService from "./IncidentFeedService";
|
|
51
51
|
import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
|
|
52
|
-
import { Gray500, Red500 } from "../../Types/BrandColors";
|
|
52
|
+
import { Blue500, Gray500, Red500 } from "../../Types/BrandColors";
|
|
53
53
|
import Label from "../../Models/DatabaseModels/Label";
|
|
54
54
|
import LabelService from "./LabelService";
|
|
55
55
|
import IncidentSeverity from "../../Models/DatabaseModels/IncidentSeverity";
|
|
@@ -74,6 +74,17 @@ type UpdateCarryForward = Dictionary<{
|
|
|
74
74
|
newMonitorChangeStatusIdTo: ObjectID | undefined;
|
|
75
75
|
}>;
|
|
76
76
|
|
|
77
|
+
type IncidentUpdatePayload = {
|
|
78
|
+
postmortemNote?: string | null;
|
|
79
|
+
title?: string | null;
|
|
80
|
+
rootCause?: string | null;
|
|
81
|
+
description?: string | null;
|
|
82
|
+
remediationNotes?: string | null;
|
|
83
|
+
labels?: unknown;
|
|
84
|
+
incidentSeverity?: unknown;
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
};
|
|
87
|
+
|
|
77
88
|
export class Service extends DatabaseService<Model> {
|
|
78
89
|
public constructor() {
|
|
79
90
|
super(Model);
|
|
@@ -1297,59 +1308,103 @@ ${incident.remediationNotes || "No remediation notes provided."}
|
|
|
1297
1308
|
|
|
1298
1309
|
const projectId: ObjectID = incident!.projectId!;
|
|
1299
1310
|
const incidentNumber: number = incident!.incidentNumber!;
|
|
1311
|
+
const incidentLabel: string = `Incident ${incidentNumber}`;
|
|
1312
|
+
const incidentLink: URL = await this.getIncidentLinkInDashboard(
|
|
1313
|
+
projectId,
|
|
1314
|
+
incidentId,
|
|
1315
|
+
);
|
|
1300
1316
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1317
|
+
const updatedIncidentData: IncidentUpdatePayload = (onUpdate.updateBy
|
|
1318
|
+
.data ?? {}) as IncidentUpdatePayload;
|
|
1303
1319
|
|
|
1304
1320
|
const createdByUserId: ObjectID | undefined | null =
|
|
1305
1321
|
onUpdate.updateBy.props.userId;
|
|
1306
1322
|
|
|
1307
|
-
if (
|
|
1308
|
-
|
|
1323
|
+
if (
|
|
1324
|
+
Object.prototype.hasOwnProperty.call(
|
|
1325
|
+
updatedIncidentData,
|
|
1326
|
+
"postmortemNote",
|
|
1327
|
+
)
|
|
1328
|
+
) {
|
|
1329
|
+
const noteValue: string =
|
|
1330
|
+
(updatedIncidentData.postmortemNote as string) || "";
|
|
1331
|
+
const hasNoteContent: boolean = noteValue.trim().length > 0;
|
|
1309
1332
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
`;
|
|
1313
|
-
|
|
1333
|
+
const postmortemFeedMarkdown: string = hasNoteContent
|
|
1334
|
+
? `**📘 Postmortem Note updated for [${incidentLabel}](${incidentLink.toString()})**\n\n${noteValue}`
|
|
1335
|
+
: `**📘 Postmortem Note cleared for [${incidentLabel}](${incidentLink.toString()})**\n\n_No postmortem note provided._`;
|
|
1336
|
+
|
|
1337
|
+
await IncidentFeedService.createIncidentFeedItem({
|
|
1338
|
+
incidentId,
|
|
1339
|
+
projectId,
|
|
1340
|
+
incidentFeedEventType: IncidentFeedEventType.PostmortemNote,
|
|
1341
|
+
displayColor: Blue500,
|
|
1342
|
+
feedInfoInMarkdown: postmortemFeedMarkdown,
|
|
1343
|
+
userId: createdByUserId || undefined,
|
|
1344
|
+
workspaceNotification: {
|
|
1345
|
+
sendWorkspaceNotification: true,
|
|
1346
|
+
},
|
|
1347
|
+
});
|
|
1314
1348
|
}
|
|
1315
1349
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
// add incident feed.
|
|
1350
|
+
let shouldAddIncidentFeed: boolean = false;
|
|
1351
|
+
let feedInfoInMarkdown: string = `**[${incidentLabel}](${incidentLink.toString()}) was updated.**`;
|
|
1319
1352
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1353
|
+
if (
|
|
1354
|
+
Object.prototype.hasOwnProperty.call(updatedIncidentData, "title")
|
|
1355
|
+
) {
|
|
1356
|
+
const title: string =
|
|
1357
|
+
(updatedIncidentData.title as string) || "No title provided.";
|
|
1358
|
+
feedInfoInMarkdown += `\n\n**Title**: \n${title}\n`;
|
|
1359
|
+
shouldAddIncidentFeed = true;
|
|
1325
1360
|
}
|
|
1326
1361
|
|
|
1327
|
-
if (
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1362
|
+
if (
|
|
1363
|
+
Object.prototype.hasOwnProperty.call(updatedIncidentData, "rootCause")
|
|
1364
|
+
) {
|
|
1365
|
+
const rootCause: string =
|
|
1366
|
+
(updatedIncidentData.rootCause as string) || "";
|
|
1367
|
+
const rootCauseText: string = rootCause.trim().length
|
|
1368
|
+
? rootCause
|
|
1369
|
+
: "Root cause removed.";
|
|
1370
|
+
feedInfoInMarkdown += `\n\n**📄 Root Cause**: \n${rootCauseText}\n`;
|
|
1333
1371
|
shouldAddIncidentFeed = true;
|
|
1334
1372
|
}
|
|
1335
1373
|
|
|
1336
|
-
if (
|
|
1337
|
-
|
|
1374
|
+
if (
|
|
1375
|
+
Object.prototype.hasOwnProperty.call(
|
|
1376
|
+
updatedIncidentData,
|
|
1377
|
+
"description",
|
|
1378
|
+
)
|
|
1379
|
+
) {
|
|
1380
|
+
const description: string =
|
|
1381
|
+
(updatedIncidentData.description as string) ||
|
|
1382
|
+
"No description provided.";
|
|
1383
|
+
feedInfoInMarkdown += `\n\n**Incident Description**: \n${description}\n`;
|
|
1384
|
+
shouldAddIncidentFeed = true;
|
|
1385
|
+
}
|
|
1338
1386
|
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1387
|
+
if (
|
|
1388
|
+
Object.prototype.hasOwnProperty.call(
|
|
1389
|
+
updatedIncidentData,
|
|
1390
|
+
"remediationNotes",
|
|
1391
|
+
)
|
|
1392
|
+
) {
|
|
1393
|
+
const remediationNotes: string =
|
|
1394
|
+
(updatedIncidentData.remediationNotes as string) || "";
|
|
1395
|
+
const remediationText: string = remediationNotes.trim().length
|
|
1396
|
+
? remediationNotes
|
|
1397
|
+
: "Remediation notes removed.";
|
|
1398
|
+
feedInfoInMarkdown += `\n\n**🎯 Remediation Notes**: \n${remediationText}\n`;
|
|
1342
1399
|
shouldAddIncidentFeed = true;
|
|
1343
1400
|
}
|
|
1344
1401
|
|
|
1345
1402
|
if (
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
Array.isArray(
|
|
1403
|
+
updatedIncidentData.labels &&
|
|
1404
|
+
(updatedIncidentData.labels as Array<Label>).length > 0 &&
|
|
1405
|
+
Array.isArray(updatedIncidentData.labels)
|
|
1349
1406
|
) {
|
|
1350
|
-
const labelIds: Array<ObjectID> = (
|
|
1351
|
-
onUpdate.updateBy.data.labels as any
|
|
1352
|
-
)
|
|
1407
|
+
const labelIds: Array<ObjectID> = (updatedIncidentData.labels as any)
|
|
1353
1408
|
.map((label: Label) => {
|
|
1354
1409
|
if (label._id) {
|
|
1355
1410
|
return new ObjectID(label._id?.toString());
|
|
@@ -1390,16 +1445,14 @@ ${labels
|
|
|
1390
1445
|
}
|
|
1391
1446
|
|
|
1392
1447
|
if (
|
|
1393
|
-
|
|
1394
|
-
(
|
|
1448
|
+
updatedIncidentData.incidentSeverity &&
|
|
1449
|
+
(updatedIncidentData.incidentSeverity as any)._id
|
|
1395
1450
|
) {
|
|
1396
1451
|
const incidentSeverity: IncidentSeverity | null =
|
|
1397
1452
|
await IncidentSeverityService.findOneBy({
|
|
1398
1453
|
query: {
|
|
1399
1454
|
_id: new ObjectID(
|
|
1400
|
-
(
|
|
1401
|
-
onUpdate.updateBy.data.incidentSeverity as any
|
|
1402
|
-
)?._id.toString(),
|
|
1455
|
+
(updatedIncidentData.incidentSeverity as any)?._id.toString(),
|
|
1403
1456
|
),
|
|
1404
1457
|
},
|
|
1405
1458
|
select: {
|