@oneuptime/common 10.0.73 → 10.0.78
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/Server/Services/IncidentService.ts +48 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +0 -78
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +24 -0
- package/Server/Utils/Monitor/MonitorIncident.ts +0 -78
- package/Types/Dashboard/DashboardLanguage.ts +30 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +14 -0
- package/Types/Monitor/MonitorEvaluationSummary.ts +2 -0
- package/UI/Components/Icon/Icon.tsx +8 -0
- package/build/dist/Server/Services/IncidentService.js +44 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +0 -67
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +26 -6
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +0 -67
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardLanguage.js +22 -0
- package/build/dist/Types/Dashboard/DashboardLanguage.js.map +1 -0
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +10 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +3 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/package.json +1 -1
|
@@ -1932,6 +1932,41 @@ ${incidentSeverity.name}
|
|
|
1932
1932
|
return incidentCount.toNumber() > 0;
|
|
1933
1933
|
}
|
|
1934
1934
|
|
|
1935
|
+
@CaptureSpan()
|
|
1936
|
+
public async doesMonitorHaveActiveIncidents(
|
|
1937
|
+
monitorId: ObjectID,
|
|
1938
|
+
projectId: ObjectID,
|
|
1939
|
+
): Promise<boolean> {
|
|
1940
|
+
const resolvedState: IncidentState | null =
|
|
1941
|
+
await IncidentStateService.findOneBy({
|
|
1942
|
+
query: {
|
|
1943
|
+
projectId: projectId,
|
|
1944
|
+
isResolvedState: true,
|
|
1945
|
+
},
|
|
1946
|
+
props: {
|
|
1947
|
+
isRoot: true,
|
|
1948
|
+
},
|
|
1949
|
+
select: {
|
|
1950
|
+
_id: true,
|
|
1951
|
+
order: true,
|
|
1952
|
+
},
|
|
1953
|
+
});
|
|
1954
|
+
|
|
1955
|
+
const incidentCount: PositiveNumber = await this.countBy({
|
|
1956
|
+
query: {
|
|
1957
|
+
monitors: QueryHelper.inRelationArray([monitorId]),
|
|
1958
|
+
currentIncidentState: {
|
|
1959
|
+
order: QueryHelper.lessThan(resolvedState?.order as number),
|
|
1960
|
+
},
|
|
1961
|
+
},
|
|
1962
|
+
props: {
|
|
1963
|
+
isRoot: true,
|
|
1964
|
+
},
|
|
1965
|
+
});
|
|
1966
|
+
|
|
1967
|
+
return incidentCount.toNumber() > 0;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1935
1970
|
@CaptureSpan()
|
|
1936
1971
|
public async markMonitorsActiveForMonitoring(
|
|
1937
1972
|
projectId: ObjectID,
|
|
@@ -1980,6 +2015,19 @@ ${incidentSeverity.name}
|
|
|
1980
2015
|
},
|
|
1981
2016
|
});
|
|
1982
2017
|
|
|
2018
|
+
/*
|
|
2019
|
+
* Don't flip the monitor to operational while other incidents
|
|
2020
|
+
* are still open on it — e.g. a metric monitor with group-by
|
|
2021
|
+
* may have one incident per series, and resolving one series
|
|
2022
|
+
* shouldn't claim the whole monitor is healthy.
|
|
2023
|
+
*/
|
|
2024
|
+
const hasOtherActiveIncidents: boolean =
|
|
2025
|
+
await this.doesMonitorHaveActiveIncidents(monitor.id!, projectId!);
|
|
2026
|
+
|
|
2027
|
+
if (hasOtherActiveIncidents) {
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
1983
2031
|
const latestState: MonitorStatusTimeline | null =
|
|
1984
2032
|
await MonitorStatusTimelineService.findOneBy({
|
|
1985
2033
|
query: {
|
|
@@ -16,9 +16,7 @@ import { TelemetryQuery } from "../../../Types/Telemetry/TelemetryQuery";
|
|
|
16
16
|
import { DisableAutomaticAlertCreation } from "../../EnvironmentConfig";
|
|
17
17
|
import AlertService from "../../Services/AlertService";
|
|
18
18
|
import AlertSeverityService from "../../Services/AlertSeverityService";
|
|
19
|
-
import AlertStateService from "../../Services/AlertStateService";
|
|
20
19
|
import AlertStateTimelineService from "../../Services/AlertStateTimelineService";
|
|
21
|
-
import AlertState from "../../../Models/DatabaseModels/AlertState";
|
|
22
20
|
import logger, { LogAttributes } from "../Logger";
|
|
23
21
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
24
22
|
import DataToProcess from "./DataToProcess";
|
|
@@ -372,82 +370,6 @@ export default class MonitorAlert {
|
|
|
372
370
|
input.openAlert.projectId!,
|
|
373
371
|
);
|
|
374
372
|
|
|
375
|
-
/*
|
|
376
|
-
* Skip the Resolved insert if the alert's timeline is already at or past
|
|
377
|
-
* the Resolved state in the project's workflow order. Two cases:
|
|
378
|
-
* 1. Latest timeline state is Resolved but Alert.currentAlertStateId is
|
|
379
|
-
* stuck on an earlier state (partial-failure from a prior resolve).
|
|
380
|
-
* Re-inserting Resolved would throw "Alert state cannot be same as
|
|
381
|
-
* previous state" from AlertStateTimelineService.onBeforeCreate.
|
|
382
|
-
* 2. The project defines a custom state after Resolved (e.g. Closed) and
|
|
383
|
-
* the alert has moved into it. Inserting Resolved would throw
|
|
384
|
-
* "cannot transition to Resolved from Closed because Resolved is
|
|
385
|
-
* before Closed in the order of alert states."
|
|
386
|
-
* Either failure bubbles up through ingest workers and causes monitors to
|
|
387
|
-
* flap. Reconcile Alert.currentAlertStateId if out of sync with the
|
|
388
|
-
* timeline, then return.
|
|
389
|
-
*/
|
|
390
|
-
const [resolvedState, latestTimeline]: [
|
|
391
|
-
AlertState | null,
|
|
392
|
-
AlertStateTimeline | null,
|
|
393
|
-
] = await Promise.all([
|
|
394
|
-
AlertStateService.findOneBy({
|
|
395
|
-
query: {
|
|
396
|
-
_id: resolvedStateId.toString(),
|
|
397
|
-
},
|
|
398
|
-
select: {
|
|
399
|
-
order: true,
|
|
400
|
-
},
|
|
401
|
-
props: {
|
|
402
|
-
isRoot: true,
|
|
403
|
-
},
|
|
404
|
-
}),
|
|
405
|
-
AlertStateTimelineService.findOneBy({
|
|
406
|
-
query: {
|
|
407
|
-
alertId: input.openAlert.id!,
|
|
408
|
-
},
|
|
409
|
-
sort: {
|
|
410
|
-
startsAt: SortOrder.Descending,
|
|
411
|
-
},
|
|
412
|
-
select: {
|
|
413
|
-
alertStateId: true,
|
|
414
|
-
alertState: {
|
|
415
|
-
order: true,
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
props: {
|
|
419
|
-
isRoot: true,
|
|
420
|
-
},
|
|
421
|
-
}),
|
|
422
|
-
]);
|
|
423
|
-
|
|
424
|
-
const latestOrder: number | undefined | null =
|
|
425
|
-
latestTimeline?.alertState?.order;
|
|
426
|
-
const resolvedOrder: number | undefined | null = resolvedState?.order;
|
|
427
|
-
|
|
428
|
-
if (
|
|
429
|
-
latestTimeline?.alertStateId &&
|
|
430
|
-
typeof latestOrder === "number" &&
|
|
431
|
-
typeof resolvedOrder === "number" &&
|
|
432
|
-
latestOrder >= resolvedOrder
|
|
433
|
-
) {
|
|
434
|
-
if (
|
|
435
|
-
input.openAlert.currentAlertStateId?.toString() !==
|
|
436
|
-
latestTimeline.alertStateId.toString()
|
|
437
|
-
) {
|
|
438
|
-
await AlertService.updateOneById({
|
|
439
|
-
id: input.openAlert.id!,
|
|
440
|
-
data: {
|
|
441
|
-
currentAlertStateId: latestTimeline.alertStateId,
|
|
442
|
-
},
|
|
443
|
-
props: {
|
|
444
|
-
isRoot: true,
|
|
445
|
-
},
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
373
|
const alertStateTimeline: AlertStateTimeline = new AlertStateTimeline();
|
|
452
374
|
alertStateTimeline.alertId = input.openAlert.id!;
|
|
453
375
|
alertStateTimeline.alertStateId = resolvedStateId;
|
|
@@ -78,6 +78,30 @@ export default class MonitorCriteriaEvaluator {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
for (const criteriaInstance of criteria.data.monitorCriteriaInstanceArray) {
|
|
81
|
+
/*
|
|
82
|
+
* Record disabled criteria in the summary (so the user can see why
|
|
83
|
+
* nothing happened) but skip evaluation, status changes, incidents,
|
|
84
|
+
* and alerts.
|
|
85
|
+
*/
|
|
86
|
+
if (criteriaInstance.data?.isEnabled === false) {
|
|
87
|
+
const skipReason: string =
|
|
88
|
+
"This criteria is disabled and was not evaluated.";
|
|
89
|
+
const skippedCriteriaResult: MonitorEvaluationCriteriaResult = {
|
|
90
|
+
criteriaId: criteriaInstance.data?.id,
|
|
91
|
+
criteriaName: criteriaInstance.data?.name,
|
|
92
|
+
filterCondition:
|
|
93
|
+
criteriaInstance.data?.filterCondition || FilterCondition.All,
|
|
94
|
+
met: false,
|
|
95
|
+
message: skipReason,
|
|
96
|
+
filters: [],
|
|
97
|
+
skipped: true,
|
|
98
|
+
skipReason: skipReason,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
input.evaluationSummary.criteriaResults.push(skippedCriteriaResult);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
81
105
|
const criteriaResult: MonitorEvaluationCriteriaResult = {
|
|
82
106
|
criteriaId: criteriaInstance.data?.id,
|
|
83
107
|
criteriaName: criteriaInstance.data?.name,
|
|
@@ -17,10 +17,8 @@ import { TelemetryQuery } from "../../../Types/Telemetry/TelemetryQuery";
|
|
|
17
17
|
import { DisableAutomaticIncidentCreation } from "../../EnvironmentConfig";
|
|
18
18
|
import IncidentService from "../../Services/IncidentService";
|
|
19
19
|
import IncidentSeverityService from "../../Services/IncidentSeverityService";
|
|
20
|
-
import IncidentStateService from "../../Services/IncidentStateService";
|
|
21
20
|
import IncidentStateTimelineService from "../../Services/IncidentStateTimelineService";
|
|
22
21
|
import IncidentMemberService from "../../Services/IncidentMemberService";
|
|
23
|
-
import IncidentState from "../../../Models/DatabaseModels/IncidentState";
|
|
24
22
|
import logger, { LogAttributes } from "../Logger";
|
|
25
23
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
26
24
|
import DataToProcess from "./DataToProcess";
|
|
@@ -468,82 +466,6 @@ export default class MonitorIncident {
|
|
|
468
466
|
input.openIncident.projectId!,
|
|
469
467
|
);
|
|
470
468
|
|
|
471
|
-
/*
|
|
472
|
-
* Skip the Resolved insert if the incident's timeline is already at or
|
|
473
|
-
* past the Resolved state in the project's workflow order. Two cases:
|
|
474
|
-
* 1. Latest timeline state is Resolved but Incident.currentIncidentStateId
|
|
475
|
-
* is stuck on an earlier state (partial-failure from a prior resolve).
|
|
476
|
-
* Re-inserting Resolved would throw "state cannot be same as previous"
|
|
477
|
-
* from IncidentStateTimelineService.onBeforeCreate.
|
|
478
|
-
* 2. The project defines a custom state after Resolved (e.g. Closed) and
|
|
479
|
-
* the incident has moved into it. Inserting Resolved would throw
|
|
480
|
-
* "cannot transition to Resolved from Closed because Resolved is
|
|
481
|
-
* before Closed in the order of incident states."
|
|
482
|
-
* Either failure bubbles up through ingest workers and causes monitors to
|
|
483
|
-
* flap. Reconcile Incident.currentIncidentStateId if out of sync with the
|
|
484
|
-
* timeline, then return.
|
|
485
|
-
*/
|
|
486
|
-
const [resolvedState, latestTimeline]: [
|
|
487
|
-
IncidentState | null,
|
|
488
|
-
IncidentStateTimeline | null,
|
|
489
|
-
] = await Promise.all([
|
|
490
|
-
IncidentStateService.findOneBy({
|
|
491
|
-
query: {
|
|
492
|
-
_id: resolvedStateId.toString(),
|
|
493
|
-
},
|
|
494
|
-
select: {
|
|
495
|
-
order: true,
|
|
496
|
-
},
|
|
497
|
-
props: {
|
|
498
|
-
isRoot: true,
|
|
499
|
-
},
|
|
500
|
-
}),
|
|
501
|
-
IncidentStateTimelineService.findOneBy({
|
|
502
|
-
query: {
|
|
503
|
-
incidentId: input.openIncident.id!,
|
|
504
|
-
},
|
|
505
|
-
sort: {
|
|
506
|
-
startsAt: SortOrder.Descending,
|
|
507
|
-
},
|
|
508
|
-
select: {
|
|
509
|
-
incidentStateId: true,
|
|
510
|
-
incidentState: {
|
|
511
|
-
order: true,
|
|
512
|
-
},
|
|
513
|
-
},
|
|
514
|
-
props: {
|
|
515
|
-
isRoot: true,
|
|
516
|
-
},
|
|
517
|
-
}),
|
|
518
|
-
]);
|
|
519
|
-
|
|
520
|
-
const latestOrder: number | undefined | null =
|
|
521
|
-
latestTimeline?.incidentState?.order;
|
|
522
|
-
const resolvedOrder: number | undefined | null = resolvedState?.order;
|
|
523
|
-
|
|
524
|
-
if (
|
|
525
|
-
latestTimeline?.incidentStateId &&
|
|
526
|
-
typeof latestOrder === "number" &&
|
|
527
|
-
typeof resolvedOrder === "number" &&
|
|
528
|
-
latestOrder >= resolvedOrder
|
|
529
|
-
) {
|
|
530
|
-
if (
|
|
531
|
-
input.openIncident.currentIncidentStateId?.toString() !==
|
|
532
|
-
latestTimeline.incidentStateId.toString()
|
|
533
|
-
) {
|
|
534
|
-
await IncidentService.updateOneById({
|
|
535
|
-
id: input.openIncident.id!,
|
|
536
|
-
data: {
|
|
537
|
-
currentIncidentStateId: latestTimeline.incidentStateId,
|
|
538
|
-
},
|
|
539
|
-
props: {
|
|
540
|
-
isRoot: true,
|
|
541
|
-
},
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
469
|
const incidentStateTimeline: IncidentStateTimeline =
|
|
548
470
|
new IncidentStateTimeline();
|
|
549
471
|
incidentStateTimeline.incidentId = input.openIncident.id!;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface DashboardLanguage {
|
|
2
|
+
code: string;
|
|
3
|
+
nativeName: string;
|
|
4
|
+
englishName: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_DASHBOARD_LANGUAGE: string = "en";
|
|
8
|
+
|
|
9
|
+
export const SUPPORTED_DASHBOARD_LANGUAGES: Array<DashboardLanguage> = [
|
|
10
|
+
{ code: "en", nativeName: "English", englishName: "English" },
|
|
11
|
+
{ code: "de", nativeName: "Deutsch", englishName: "German" },
|
|
12
|
+
{ code: "fr", nativeName: "Français", englishName: "French" },
|
|
13
|
+
{ code: "es", nativeName: "Español", englishName: "Spanish" },
|
|
14
|
+
{ code: "it", nativeName: "Italiano", englishName: "Italian" },
|
|
15
|
+
{ code: "pt", nativeName: "Português", englishName: "Portuguese" },
|
|
16
|
+
{ code: "nl", nativeName: "Nederlands", englishName: "Dutch" },
|
|
17
|
+
{ code: "da", nativeName: "Dansk", englishName: "Danish" },
|
|
18
|
+
{ code: "no", nativeName: "Norsk", englishName: "Norwegian" },
|
|
19
|
+
{ code: "sv", nativeName: "Svenska", englishName: "Swedish" },
|
|
20
|
+
{ code: "ru", nativeName: "Русский", englishName: "Russian" },
|
|
21
|
+
{ code: "ja", nativeName: "日本語", englishName: "Japanese" },
|
|
22
|
+
{ code: "ko", nativeName: "한국어", englishName: "Korean" },
|
|
23
|
+
{ code: "zh", nativeName: "中文", englishName: "Chinese" },
|
|
24
|
+
{ code: "hi", nativeName: "हिन्दी", englishName: "Hindi" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export const SUPPORTED_DASHBOARD_LANGUAGE_CODES: Array<string> =
|
|
28
|
+
SUPPORTED_DASHBOARD_LANGUAGES.map((language: DashboardLanguage) => {
|
|
29
|
+
return language.code;
|
|
30
|
+
});
|
package/Types/Icon/IconProp.ts
CHANGED
|
@@ -30,6 +30,7 @@ export interface MonitorCriteriaInstanceType {
|
|
|
30
30
|
changeMonitorStatus?: boolean | undefined;
|
|
31
31
|
createIncidents?: boolean | undefined;
|
|
32
32
|
createAlerts?: boolean | undefined;
|
|
33
|
+
isEnabled?: boolean | undefined;
|
|
33
34
|
id: string;
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -52,6 +53,7 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
|
|
|
52
53
|
createIncidents: false,
|
|
53
54
|
createAlerts: false,
|
|
54
55
|
changeMonitorStatus: false,
|
|
56
|
+
isEnabled: true,
|
|
55
57
|
incidents: [],
|
|
56
58
|
alerts: [],
|
|
57
59
|
name: "",
|
|
@@ -1375,6 +1377,14 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
|
|
|
1375
1377
|
return this;
|
|
1376
1378
|
}
|
|
1377
1379
|
|
|
1380
|
+
public setIsEnabled(isEnabled: boolean | undefined): MonitorCriteriaInstance {
|
|
1381
|
+
if (this.data) {
|
|
1382
|
+
this.data.isEnabled = isEnabled;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
return this;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1378
1388
|
public override toJSON(): JSONObject {
|
|
1379
1389
|
if (!this.data) {
|
|
1380
1390
|
return MonitorCriteriaInstance.getNewMonitorCriteriaInstanceAsJSON();
|
|
@@ -1392,6 +1402,7 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
|
|
|
1392
1402
|
createAlerts: this.data.createAlerts,
|
|
1393
1403
|
changeMonitorStatus: this.data.changeMonitorStatus,
|
|
1394
1404
|
createIncidents: this.data.createIncidents,
|
|
1405
|
+
isEnabled: this.data.isEnabled,
|
|
1395
1406
|
name: this.data.name,
|
|
1396
1407
|
description: this.data.description,
|
|
1397
1408
|
} as any,
|
|
@@ -1499,6 +1510,8 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
|
|
|
1499
1510
|
changeMonitorStatus: (json["changeMonitorStatus"] as boolean) || false,
|
|
1500
1511
|
createIncidents: (json["createIncidents"] as boolean) || false,
|
|
1501
1512
|
createAlerts: (json["createAlerts"] as boolean) || false,
|
|
1513
|
+
isEnabled:
|
|
1514
|
+
json["isEnabled"] === undefined ? true : (json["isEnabled"] as boolean),
|
|
1502
1515
|
filters: filters as any,
|
|
1503
1516
|
incidents: incidents as any,
|
|
1504
1517
|
alerts: alerts as any,
|
|
@@ -1524,6 +1537,7 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
|
|
|
1524
1537
|
changeMonitorStatus: Zod.boolean().optional(),
|
|
1525
1538
|
createIncidents: Zod.boolean().optional(),
|
|
1526
1539
|
createAlerts: Zod.boolean().optional(),
|
|
1540
|
+
isEnabled: Zod.boolean().optional(),
|
|
1527
1541
|
}).openapi({
|
|
1528
1542
|
type: "object",
|
|
1529
1543
|
example: {
|
|
@@ -28,6 +28,8 @@ export interface MonitorEvaluationCriteriaResult {
|
|
|
28
28
|
met: boolean;
|
|
29
29
|
message: string;
|
|
30
30
|
filters: Array<MonitorEvaluationFilterResult>;
|
|
31
|
+
skipped?: boolean | undefined;
|
|
32
|
+
skipReason?: string | undefined;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export interface MonitorEvaluationEvent {
|
|
@@ -1243,6 +1243,14 @@ const Icon: FunctionComponent<ComponentProps> = ({
|
|
|
1243
1243
|
d="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z"
|
|
1244
1244
|
/>,
|
|
1245
1245
|
);
|
|
1246
|
+
} else if (icon === IconProp.GripVertical) {
|
|
1247
|
+
return getSvgWrapper(
|
|
1248
|
+
<path
|
|
1249
|
+
fill="currentColor"
|
|
1250
|
+
stroke="none"
|
|
1251
|
+
d="M9 5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM9 12a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM9 19a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM18 5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM18 12a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM18 19a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z"
|
|
1252
|
+
/>,
|
|
1253
|
+
);
|
|
1246
1254
|
} else if (icon === IconProp.Graph) {
|
|
1247
1255
|
return getSvgWrapper(
|
|
1248
1256
|
<path
|
|
@@ -1427,6 +1427,33 @@ ${incidentSeverity.name}
|
|
|
1427
1427
|
});
|
|
1428
1428
|
return incidentCount.toNumber() > 0;
|
|
1429
1429
|
}
|
|
1430
|
+
async doesMonitorHaveActiveIncidents(monitorId, projectId) {
|
|
1431
|
+
const resolvedState = await IncidentStateService.findOneBy({
|
|
1432
|
+
query: {
|
|
1433
|
+
projectId: projectId,
|
|
1434
|
+
isResolvedState: true,
|
|
1435
|
+
},
|
|
1436
|
+
props: {
|
|
1437
|
+
isRoot: true,
|
|
1438
|
+
},
|
|
1439
|
+
select: {
|
|
1440
|
+
_id: true,
|
|
1441
|
+
order: true,
|
|
1442
|
+
},
|
|
1443
|
+
});
|
|
1444
|
+
const incidentCount = await this.countBy({
|
|
1445
|
+
query: {
|
|
1446
|
+
monitors: QueryHelper.inRelationArray([monitorId]),
|
|
1447
|
+
currentIncidentState: {
|
|
1448
|
+
order: QueryHelper.lessThan(resolvedState === null || resolvedState === void 0 ? void 0 : resolvedState.order),
|
|
1449
|
+
},
|
|
1450
|
+
},
|
|
1451
|
+
props: {
|
|
1452
|
+
isRoot: true,
|
|
1453
|
+
},
|
|
1454
|
+
});
|
|
1455
|
+
return incidentCount.toNumber() > 0;
|
|
1456
|
+
}
|
|
1430
1457
|
async markMonitorsActiveForMonitoring(projectId, monitors, startsAt) {
|
|
1431
1458
|
// resolve all the monitors.
|
|
1432
1459
|
var _a;
|
|
@@ -1460,6 +1487,16 @@ ${incidentSeverity.name}
|
|
|
1460
1487
|
isRoot: true,
|
|
1461
1488
|
},
|
|
1462
1489
|
});
|
|
1490
|
+
/*
|
|
1491
|
+
* Don't flip the monitor to operational while other incidents
|
|
1492
|
+
* are still open on it — e.g. a metric monitor with group-by
|
|
1493
|
+
* may have one incident per series, and resolving one series
|
|
1494
|
+
* shouldn't claim the whole monitor is healthy.
|
|
1495
|
+
*/
|
|
1496
|
+
const hasOtherActiveIncidents = await this.doesMonitorHaveActiveIncidents(monitor.id, projectId);
|
|
1497
|
+
if (hasOtherActiveIncidents) {
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1463
1500
|
const latestState = await MonitorStatusTimelineService.findOneBy({
|
|
1464
1501
|
query: {
|
|
1465
1502
|
monitorId: monitor.id,
|
|
@@ -2214,6 +2251,13 @@ __decorate([
|
|
|
2214
2251
|
ObjectID]),
|
|
2215
2252
|
__metadata("design:returntype", Promise)
|
|
2216
2253
|
], Service.prototype, "doesMonitorHasMoreActiveManualIncidents", null);
|
|
2254
|
+
__decorate([
|
|
2255
|
+
CaptureSpan(),
|
|
2256
|
+
__metadata("design:type", Function),
|
|
2257
|
+
__metadata("design:paramtypes", [ObjectID,
|
|
2258
|
+
ObjectID]),
|
|
2259
|
+
__metadata("design:returntype", Promise)
|
|
2260
|
+
], Service.prototype, "doesMonitorHaveActiveIncidents", null);
|
|
2217
2261
|
__decorate([
|
|
2218
2262
|
CaptureSpan(),
|
|
2219
2263
|
__metadata("design:type", Function),
|