@oneuptime/common 7.0.3822 → 7.0.3827

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.
@@ -26,7 +26,7 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
26
26
  public constructor() {
27
27
  super(IncidentStateTimeline);
28
28
  if (IsBillingEnabled) {
29
- this.hardDeleteItemsOlderThanInDays("createdAt", 120);
29
+ this.hardDeleteItemsOlderThanInDays("startsAt", 120);
30
30
  }
31
31
  }
32
32
 
@@ -91,21 +91,58 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
91
91
  }
92
92
  }
93
93
 
94
- const lastIncidentStateTimeline: IncidentStateTimeline | null =
95
- await this.findOneBy({
96
- query: {
97
- incidentId: createBy.data.incidentId,
98
- },
99
- sort: {
100
- createdAt: SortOrder.Descending,
101
- },
102
- props: {
103
- isRoot: true,
104
- },
105
- select: {
106
- _id: true,
107
- },
108
- });
94
+ const stateBeforeThis: IncidentStateTimeline | null = await this.findOneBy({
95
+ query: {
96
+ incidentId: createBy.data.incidentId,
97
+ startsAt: QueryHelper.lessThanEqualTo(createBy.data.startsAt),
98
+ },
99
+ sort: {
100
+ startsAt: SortOrder.Descending,
101
+ },
102
+ props: {
103
+ isRoot: true,
104
+ },
105
+ select: {
106
+ incidentStateId: true,
107
+ startsAt: true,
108
+ endsAt: true,
109
+ },
110
+ });
111
+
112
+ logger.debug("State Before this");
113
+ logger.debug(stateBeforeThis);
114
+
115
+ // If this is the first state, then do not notify the owner.
116
+ if (!stateBeforeThis) {
117
+ // since this is the first status, do not notify the owner.
118
+ createBy.data.isOwnerNotified = true;
119
+ }
120
+
121
+ const stateAfterThis: IncidentStateTimeline | null = await this.findOneBy({
122
+ query: {
123
+ incidentId: createBy.data.incidentId,
124
+ startsAt: QueryHelper.greaterThan(createBy.data.startsAt),
125
+ },
126
+ sort: {
127
+ startsAt: SortOrder.Ascending,
128
+ },
129
+ props: {
130
+ isRoot: true,
131
+ },
132
+ select: {
133
+ incidentStateId: true,
134
+ startsAt: true,
135
+ endsAt: true,
136
+ },
137
+ });
138
+
139
+ // compute ends at. It's the start of the next status.
140
+ if (stateAfterThis && stateAfterThis.startsAt) {
141
+ createBy.data.endsAt = stateAfterThis.startsAt;
142
+ }
143
+
144
+ logger.debug("State After this");
145
+ logger.debug(stateAfterThis);
109
146
 
110
147
  const publicNote: string | undefined = (
111
148
  createBy.miscDataProps as JSONObject | undefined
@@ -121,7 +158,8 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
121
158
  return {
122
159
  createBy,
123
160
  carryForward: {
124
- lastIncidentStateTimelineId: lastIncidentStateTimeline?.id || null,
161
+ statusTimelineBeforeThisStatus: stateBeforeThis || null,
162
+ statusTimelineAfterThisStatus: stateAfterThis || null,
125
163
  publicNote: publicNote,
126
164
  },
127
165
  };
@@ -138,33 +176,72 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
138
176
  if (!createdItem.incidentStateId) {
139
177
  throw new BadDataException("incidentStateId is null");
140
178
  }
141
-
142
179
  // update the last status as ended.
143
180
 
144
- if (onCreate.carryForward.lastIncidentStateTimelineId) {
181
+ logger.debug("Status Timeline Before this");
182
+ logger.debug(onCreate.carryForward.statusTimelineBeforeThisStatus);
183
+
184
+ logger.debug("Status Timeline After this");
185
+ logger.debug(onCreate.carryForward.statusTimelineAfterThisStatus);
186
+
187
+ logger.debug("Created Item");
188
+ logger.debug(createdItem);
189
+
190
+ // now there are three cases.
191
+ // 1. This is the first status OR there's no status after this.
192
+ if (!onCreate.carryForward.statusTimelineBeforeThisStatus) {
193
+ // This is the first status, no need to update previous status.
194
+ logger.debug("This is the first status.");
195
+ } else if (!onCreate.carryForward.statusTimelineAfterThisStatus) {
196
+ // 2. This is the last status.
197
+ // Update the previous status to end at the start of this status.
145
198
  await this.updateOneById({
146
- id: onCreate.carryForward.lastIncidentStateTimelineId!,
199
+ id: onCreate.carryForward.statusTimelineBeforeThisStatus.id!,
147
200
  data: {
148
- endsAt: createdItem.createdAt || OneUptimeDate.getCurrentDate(),
201
+ endsAt: createdItem.startsAt!,
202
+ },
203
+ props: {
204
+ isRoot: true,
205
+ },
206
+ });
207
+ logger.debug("This is the last status.");
208
+ } else {
209
+ // 3. This is in the middle.
210
+ // Update the previous status to end at the start of this status.
211
+ await this.updateOneById({
212
+ id: onCreate.carryForward.statusTimelineBeforeThisStatus.id!,
213
+ data: {
214
+ endsAt: createdItem.startsAt!,
149
215
  },
150
216
  props: {
151
217
  isRoot: true,
152
218
  },
153
219
  });
154
- }
155
220
 
156
- await IncidentService.updateOneBy({
157
- query: {
158
- _id: createdItem.incidentId?.toString(),
159
- },
160
- data: {
161
- currentIncidentStateId: createdItem.incidentStateId,
162
- },
163
- props: onCreate.createBy.props,
164
- });
221
+ // Update the next status to start at the end of this status.
222
+ await this.updateOneById({
223
+ id: onCreate.carryForward.statusTimelineAfterThisStatus.id!,
224
+ data: {
225
+ startsAt: createdItem.endsAt!,
226
+ },
227
+ props: {
228
+ isRoot: true,
229
+ },
230
+ });
231
+ logger.debug("This status is in the middle.");
232
+ }
165
233
 
166
- // TODO: DELETE THIS WHEN WORKFLOW IS IMPLEMENMTED.
167
- // check if this is resolved state, and if it is then resolve all the monitors.
234
+ if (!createdItem.endsAt) {
235
+ await IncidentService.updateOneBy({
236
+ query: {
237
+ _id: createdItem.incidentId?.toString(),
238
+ },
239
+ data: {
240
+ currentIncidentStateId: createdItem.incidentStateId,
241
+ },
242
+ props: onCreate.createBy.props,
243
+ });
244
+ }
168
245
 
169
246
  const incidentState: IncidentState | null =
170
247
  await IncidentStateService.findOneBy({
@@ -291,6 +368,7 @@ ${createdItem.rootCause}`,
291
368
  select: {
292
369
  incidentId: true,
293
370
  startsAt: true,
371
+ endsAt: true,
294
372
  },
295
373
  props: {
296
374
  isRoot: true,
@@ -310,80 +388,104 @@ ${createdItem.rootCause}`,
310
388
  },
311
389
  });
312
390
 
391
+ if (!incidentStateTimelineToBeDeleted) {
392
+ throw new BadDataException("Incident state timeline not found.");
393
+ }
394
+
313
395
  if (incidentStateTimeline.isOne()) {
314
396
  throw new BadDataException(
315
397
  "Cannot delete the only state timeline. Incident should have at least one state in its timeline.",
316
398
  );
317
399
  }
318
400
 
319
- if (incidentStateTimelineToBeDeleted?.startsAt) {
320
- const beforeState: IncidentStateTimeline | null =
321
- await this.findOneBy({
322
- query: {
323
- incidentId: incidentId,
324
- startsAt: QueryHelper.lessThan(
325
- incidentStateTimelineToBeDeleted?.startsAt,
326
- ),
327
- },
328
- sort: {
329
- createdAt: SortOrder.Descending,
330
- },
331
- props: {
332
- isRoot: true,
333
- },
334
- select: {
335
- _id: true,
336
- startsAt: true,
337
- },
338
- });
339
-
340
- if (beforeState) {
341
- const afterState: IncidentStateTimeline | null =
342
- await this.findOneBy({
343
- query: {
344
- incidentId: incidentId,
345
- startsAt: QueryHelper.greaterThan(
346
- incidentStateTimelineToBeDeleted?.startsAt,
347
- ),
348
- },
349
- sort: {
350
- createdAt: SortOrder.Ascending,
351
- },
352
- props: {
353
- isRoot: true,
354
- },
355
- select: {
356
- _id: true,
357
- startsAt: true,
358
- },
359
- });
360
-
361
- if (!afterState) {
362
- // if there's nothing after then end date of before state is null.
363
-
364
- await this.updateOneById({
365
- id: beforeState.id!,
366
- data: {
367
- endsAt: null as any,
368
- },
369
- props: {
370
- isRoot: true,
371
- },
372
- });
373
- } else {
374
- // if there's something after then end date of before state is start date of after state.
375
-
376
- await this.updateOneById({
377
- id: beforeState.id!,
378
- data: {
379
- endsAt: afterState.startsAt!,
380
- },
381
- props: {
382
- isRoot: true,
383
- },
384
- });
385
- }
386
- }
401
+ // There are three cases.
402
+ // 1. This is the first state.
403
+ // 2. This is the last state.
404
+ // 3. This is in the middle.
405
+
406
+ const stateBeforeThis: IncidentStateTimeline | null =
407
+ await this.findOneBy({
408
+ query: {
409
+ _id: QueryHelper.notEquals(deleteBy.query._id as string),
410
+ incidentId: incidentId,
411
+ startsAt: QueryHelper.lessThanEqualTo(
412
+ incidentStateTimelineToBeDeleted.startsAt!,
413
+ ),
414
+ },
415
+ sort: {
416
+ startsAt: SortOrder.Descending,
417
+ },
418
+ props: {
419
+ isRoot: true,
420
+ },
421
+ select: {
422
+ incidentStateId: true,
423
+ startsAt: true,
424
+ endsAt: true,
425
+ },
426
+ });
427
+
428
+ const stateAfterThis: IncidentStateTimeline | null =
429
+ await this.findOneBy({
430
+ query: {
431
+ incidentId: incidentId,
432
+ startsAt: QueryHelper.greaterThan(
433
+ incidentStateTimelineToBeDeleted.startsAt!,
434
+ ),
435
+ },
436
+ sort: {
437
+ startsAt: SortOrder.Ascending,
438
+ },
439
+ props: {
440
+ isRoot: true,
441
+ },
442
+ select: {
443
+ incidentStateId: true,
444
+ startsAt: true,
445
+ endsAt: true,
446
+ },
447
+ });
448
+
449
+ if (!stateBeforeThis) {
450
+ // This is the first state, no need to update previous state.
451
+ logger.debug("This is the first state.");
452
+ } else if (!stateAfterThis) {
453
+ // This is the last state.
454
+ // Update the previous state to end at the end of this state.
455
+ await this.updateOneById({
456
+ id: stateBeforeThis.id!,
457
+ data: {
458
+ endsAt: incidentStateTimelineToBeDeleted.endsAt!,
459
+ },
460
+ props: {
461
+ isRoot: true,
462
+ },
463
+ });
464
+ logger.debug("This is the last state.");
465
+ } else {
466
+ // This state is in the middle.
467
+ // Update the previous state to end at the start of the next state.
468
+ await this.updateOneById({
469
+ id: stateBeforeThis.id!,
470
+ data: {
471
+ endsAt: stateAfterThis.startsAt!,
472
+ },
473
+ props: {
474
+ isRoot: true,
475
+ },
476
+ });
477
+
478
+ // Update the next state to start at the start of this state.
479
+ await this.updateOneById({
480
+ id: stateAfterThis.id!,
481
+ data: {
482
+ startsAt: incidentStateTimelineToBeDeleted.startsAt!,
483
+ },
484
+ props: {
485
+ isRoot: true,
486
+ },
487
+ });
488
+ logger.debug("This state is in the middle.");
387
489
  }
388
490
  }
389
491
 
@@ -401,14 +503,14 @@ ${createdItem.rootCause}`,
401
503
  // this is incidentId.
402
504
  const incidentId: ObjectID = onDelete.carryForward as ObjectID;
403
505
 
404
- // get last status of this monitor.
506
+ // get last status of this incident.
405
507
  const incidentStateTimeline: IncidentStateTimeline | null =
406
508
  await this.findOneBy({
407
509
  query: {
408
510
  incidentId: incidentId,
409
511
  },
410
512
  sort: {
411
- createdAt: SortOrder.Descending,
513
+ startsAt: SortOrder.Descending,
412
514
  },
413
515
  props: {
414
516
  isRoot: true,