@oneuptime/common 10.0.70 → 10.0.72
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/Alert.ts +55 -0
- package/Models/DatabaseModels/Incident.ts +55 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +6 -4
- package/Models/DatabaseModels/Project.ts +5 -5
- package/Models/DatabaseModels/StatusPage.ts +80 -0
- package/Server/API/StatusPageAPI.ts +4 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.ts +6 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/AIBillingService.ts +2 -2
- package/Server/Services/AnalyticsDatabaseService.ts +17 -7
- package/Server/Services/BillingService.ts +116 -48
- package/Server/Services/NotificationService.ts +2 -2
- package/Server/Types/Database/QueryUtil.ts +13 -7
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +175 -29
- package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +71 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +170 -7
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +171 -2
- package/Server/Utils/Monitor/MonitorIncident.ts +212 -8
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +423 -1
- package/Server/Utils/Monitor/MonitorResource.ts +2 -0
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +99 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +268 -0
- package/Types/BaseDatabase/IncludesNone.ts +1 -4
- package/Types/Email.ts +50 -0
- package/Types/Infrastructure/BasicMetrics.ts +75 -0
- package/Types/Metrics/MetricQueryData.ts +11 -0
- package/Types/Monitor/CriteriaFilter.ts +10 -0
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +11 -0
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +10 -0
- package/Types/Monitor/MetricMonitor/MetricSeriesResult.ts +20 -0
- package/Types/Monitor/MonitorMetricType.ts +34 -0
- package/Types/Monitor/ServerMonitor/ServerMonitorResponse.ts +8 -0
- package/Types/Probe/ProbeApiIngestResponse.ts +25 -0
- package/Types/StatusPage/StatusPageLanguage.ts +29 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +17 -12
- package/UI/Components/Charts/Bar/BarChart.tsx +16 -11
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -0
- package/UI/Components/Charts/Line/LineChart.tsx +16 -11
- package/UI/Components/Filters/DateFilter.tsx +16 -8
- package/UI/Components/Filters/EntityFilter.tsx +33 -18
- package/UI/Components/Filters/FilterViewer.tsx +7 -5
- package/UI/Components/Filters/FiltersForm.tsx +27 -5
- package/UI/Components/Filters/NumberFilter.tsx +3 -2
- package/UI/Components/Filters/TextFilter.tsx +5 -4
- package/UI/Components/ModelTable/BaseModelTable.tsx +5 -3
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +453 -0
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +229 -0
- package/Utils/Metrics/MetricSeriesFingerprint.ts +97 -0
- package/Utils/Monitor/MonitorMetricType.ts +309 -19
- package/build/dist/Models/DatabaseModels/Alert.js +57 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +57 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +6 -4
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +5 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +82 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +4 -0
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js +4 -2
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AIBillingService.js +2 -2
- package/build/dist/Server/Services/AIBillingService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +14 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +99 -39
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/NotificationService.js +2 -2
- package/build/dist/Server/Services/NotificationService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +13 -7
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +132 -30
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +58 -7
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +134 -12
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +112 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +159 -15
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +373 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +2 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +65 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +199 -0
- package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -1
- package/build/dist/Types/BaseDatabase/IncludesNone.js.map +1 -1
- package/build/dist/Types/Email.js +42 -0
- package/build/dist/Types/Email.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +10 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/MetricMonitor/MetricSeriesResult.js +2 -0
- package/build/dist/Types/Monitor/MetricMonitor/MetricSeriesResult.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorMetricType.js +28 -0
- package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Types/StatusPage/StatusPageLanguage.js +21 -0
- package/build/dist/Types/StatusPage/StatusPageLanguage.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +13 -12
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +12 -11
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +11 -3
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +12 -11
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Filters/DateFilter.js +1 -4
- package/build/dist/UI/Components/Filters/DateFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/EntityFilter.js +21 -14
- package/build/dist/UI/Components/Filters/EntityFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/FilterViewer.js +1 -2
- package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js +7 -3
- package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
- package/build/dist/UI/Components/Filters/NumberFilter.js +0 -1
- package/build/dist/UI/Components/Filters/NumberFilter.js.map +1 -1
- package/build/dist/UI/Components/Filters/TextFilter.js +5 -4
- package/build/dist/UI/Components/Filters/TextFilter.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +5 -3
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +383 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +109 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricSeriesFingerprint.js +81 -0
- package/build/dist/Utils/Metrics/MetricSeriesFingerprint.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js +287 -19
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/package.json +1 -1
|
@@ -17,8 +17,10 @@ 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";
|
|
20
21
|
import IncidentStateTimelineService from "../../Services/IncidentStateTimelineService";
|
|
21
22
|
import IncidentMemberService from "../../Services/IncidentMemberService";
|
|
23
|
+
import IncidentState from "../../../Models/DatabaseModels/IncidentState";
|
|
22
24
|
import logger, { LogAttributes } from "../Logger";
|
|
23
25
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
24
26
|
import DataToProcess from "./DataToProcess";
|
|
@@ -27,6 +29,7 @@ import { JSONObject } from "../../../Types/JSON";
|
|
|
27
29
|
import OneUptimeDate from "../../../Types/Date";
|
|
28
30
|
import MonitorEvaluationSummary from "../../../Types/Monitor/MonitorEvaluationSummary";
|
|
29
31
|
import { IncidentMemberRoleAssignment } from "../../../Types/Monitor/CriteriaIncident";
|
|
32
|
+
import { PerSeriesCriteriaMatch } from "../../../Types/Probe/ProbeApiIngestResponse";
|
|
30
33
|
|
|
31
34
|
export default class MonitorIncident {
|
|
32
35
|
@CaptureSpan()
|
|
@@ -39,6 +42,15 @@ export default class MonitorIncident {
|
|
|
39
42
|
criteriaInstance: MonitorCriteriaInstance | null;
|
|
40
43
|
dataToProcess: DataToProcess;
|
|
41
44
|
evaluationSummary?: MonitorEvaluationSummary | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* When set, the fingerprint set of series still breaching on this
|
|
47
|
+
* tick. Any open per-series incident whose fingerprint is NOT in
|
|
48
|
+
* this set is auto-resolved — that's how a series returning to
|
|
49
|
+
* normal closes its incident independently of other series on the
|
|
50
|
+
* same monitor. Undefined means "legacy mode" and per-series
|
|
51
|
+
* incidents are treated like any other for dedupe/resolve.
|
|
52
|
+
*/
|
|
53
|
+
breachingSeriesFingerprints?: Set<string> | undefined;
|
|
42
54
|
}): Promise<Array<Incident>> {
|
|
43
55
|
// check active incidents and if there are open incidents, do not cretae anothr incident.
|
|
44
56
|
const openIncidents: Array<Incident> = await IncidentService.findBy({
|
|
@@ -52,11 +64,15 @@ export default class MonitorIncident {
|
|
|
52
64
|
limit: LIMIT_PER_PROJECT,
|
|
53
65
|
select: {
|
|
54
66
|
_id: true,
|
|
67
|
+
title: true,
|
|
55
68
|
createdCriteriaId: true,
|
|
56
69
|
createdIncidentTemplateId: true,
|
|
57
70
|
projectId: true,
|
|
58
71
|
incidentNumber: true,
|
|
59
72
|
incidentNumberWithPrefix: true,
|
|
73
|
+
currentIncidentStateId: true,
|
|
74
|
+
seriesFingerprint: true,
|
|
75
|
+
seriesLabels: true,
|
|
60
76
|
},
|
|
61
77
|
props: {
|
|
62
78
|
isRoot: true,
|
|
@@ -71,6 +87,7 @@ export default class MonitorIncident {
|
|
|
71
87
|
autoResolveCriteriaInstanceIdIncidentIdsDictionary:
|
|
72
88
|
input.autoResolveCriteriaInstanceIdIncidentIdsDictionary,
|
|
73
89
|
criteriaInstance: input.criteriaInstance,
|
|
90
|
+
breachingSeriesFingerprints: input.breachingSeriesFingerprints,
|
|
74
91
|
});
|
|
75
92
|
|
|
76
93
|
if (shouldClose) {
|
|
@@ -112,6 +129,13 @@ export default class MonitorIncident {
|
|
|
112
129
|
props: {
|
|
113
130
|
telemetryQuery?: TelemetryQuery | undefined;
|
|
114
131
|
};
|
|
132
|
+
/**
|
|
133
|
+
* When set, create one incident per series instead of one per
|
|
134
|
+
* monitor. Each entry gets its own rootCause, seriesFingerprint,
|
|
135
|
+
* and seriesLabels so the incident title + description can
|
|
136
|
+
* reference `{{host.name}}` etc. via the template engine.
|
|
137
|
+
*/
|
|
138
|
+
matchesPerSeries?: Array<PerSeriesCriteriaMatch> | undefined;
|
|
115
139
|
}): Promise<void> {
|
|
116
140
|
const incidentLogAttributes: LogAttributes = {
|
|
117
141
|
projectId: input.monitor.projectId?.toString(),
|
|
@@ -122,6 +146,21 @@ export default class MonitorIncident {
|
|
|
122
146
|
`${input.monitor.id?.toString()} - Check open incidents.`,
|
|
123
147
|
incidentLogAttributes,
|
|
124
148
|
);
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* Per-series mode: close any open incident for a series that's no
|
|
152
|
+
* longer breaching *before* we look at the remaining open set, so
|
|
153
|
+
* dedupe decisions below match the post-resolve state.
|
|
154
|
+
*/
|
|
155
|
+
const breachingSeriesFingerprints: Set<string> | undefined =
|
|
156
|
+
input.matchesPerSeries
|
|
157
|
+
? new Set<string>(
|
|
158
|
+
input.matchesPerSeries.map((m: PerSeriesCriteriaMatch) => {
|
|
159
|
+
return m.fingerprint;
|
|
160
|
+
}),
|
|
161
|
+
)
|
|
162
|
+
: undefined;
|
|
163
|
+
|
|
125
164
|
// check active incidents and if there are open incidents, do not cretae anothr incident.
|
|
126
165
|
const openIncidents: Array<Incident> =
|
|
127
166
|
await this.checkOpenIncidentsAndCloseIfResolved({
|
|
@@ -132,14 +171,29 @@ export default class MonitorIncident {
|
|
|
132
171
|
criteriaInstance: input.criteriaInstance,
|
|
133
172
|
dataToProcess: input.dataToProcess,
|
|
134
173
|
evaluationSummary: input.evaluationSummary,
|
|
174
|
+
breachingSeriesFingerprints,
|
|
135
175
|
});
|
|
136
176
|
|
|
137
|
-
if (input.criteriaInstance.data?.createIncidents) {
|
|
138
|
-
|
|
177
|
+
if (!input.criteriaInstance.data?.createIncidents) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
139
180
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
181
|
+
/*
|
|
182
|
+
* Series-less path: one incident per criteriaIncident template as
|
|
183
|
+
* before. Series-aware path: one incident per (series × template).
|
|
184
|
+
*/
|
|
185
|
+
const seriesToProcess: Array<PerSeriesCriteriaMatch | undefined> =
|
|
186
|
+
input.matchesPerSeries && input.matchesPerSeries.length > 0
|
|
187
|
+
? input.matchesPerSeries
|
|
188
|
+
: [undefined];
|
|
189
|
+
|
|
190
|
+
for (const criteriaIncident of input.criteriaInstance.data?.incidents ||
|
|
191
|
+
[]) {
|
|
192
|
+
for (const seriesMatch of seriesToProcess) {
|
|
193
|
+
const seriesFingerprint: string | undefined = seriesMatch?.fingerprint;
|
|
194
|
+
const seriesLabels: JSONObject | undefined = seriesMatch?.labels;
|
|
195
|
+
const seriesRootCause: string =
|
|
196
|
+
seriesMatch?.rootCause || input.rootCause;
|
|
143
197
|
|
|
144
198
|
const alreadyOpenIncident: Incident | undefined = openIncidents.find(
|
|
145
199
|
(incident: Incident) => {
|
|
@@ -147,7 +201,8 @@ export default class MonitorIncident {
|
|
|
147
201
|
incident.createdCriteriaId ===
|
|
148
202
|
input.criteriaInstance.data?.id.toString() &&
|
|
149
203
|
incident.createdIncidentTemplateId ===
|
|
150
|
-
criteriaIncident.id.toString()
|
|
204
|
+
criteriaIncident.id.toString() &&
|
|
205
|
+
(incident.seriesFingerprint || undefined) === seriesFingerprint
|
|
151
206
|
);
|
|
152
207
|
},
|
|
153
208
|
);
|
|
@@ -165,9 +220,20 @@ export default class MonitorIncident {
|
|
|
165
220
|
);
|
|
166
221
|
|
|
167
222
|
if (hasAlreadyOpenIncident) {
|
|
223
|
+
/*
|
|
224
|
+
* Use the open incident's already-rendered title when
|
|
225
|
+
* available — the template (`criteriaIncident.title`) still
|
|
226
|
+
* contains unresolved `{{…}}` placeholders because it's the
|
|
227
|
+
* criterion's template string, not the instance's rendered
|
|
228
|
+
* output. Falling back to the template only when the open
|
|
229
|
+
* incident somehow has no title.
|
|
230
|
+
*/
|
|
231
|
+
const renderedTitle: string =
|
|
232
|
+
alreadyOpenIncident?.title || criteriaIncident.title;
|
|
233
|
+
|
|
168
234
|
input.evaluationSummary?.events.push({
|
|
169
235
|
type: "incident-skipped",
|
|
170
|
-
title: `Incident already active: ${
|
|
236
|
+
title: `Incident already active: ${renderedTitle}`,
|
|
171
237
|
message:
|
|
172
238
|
"Skipped creating a new incident because an active incident exists for this criteria.",
|
|
173
239
|
relatedCriteriaId: input.criteriaInstance.data?.id,
|
|
@@ -190,6 +256,8 @@ export default class MonitorIncident {
|
|
|
190
256
|
MonitorTemplateUtil.buildTemplateStorageMap({
|
|
191
257
|
monitorType: input.monitor.monitorType!,
|
|
192
258
|
dataToProcess: input.dataToProcess,
|
|
259
|
+
monitor: input.monitor,
|
|
260
|
+
seriesLabels,
|
|
193
261
|
});
|
|
194
262
|
|
|
195
263
|
incident.title = MonitorTemplateUtil.processTemplateString({
|
|
@@ -233,7 +301,7 @@ export default class MonitorIncident {
|
|
|
233
301
|
|
|
234
302
|
incident.monitors = [input.monitor];
|
|
235
303
|
incident.projectId = input.monitor.projectId!;
|
|
236
|
-
incident.rootCause =
|
|
304
|
+
incident.rootCause = seriesRootCause;
|
|
237
305
|
incident.createdStateLog = JSON.parse(
|
|
238
306
|
JSON.stringify(input.dataToProcess, null, 2),
|
|
239
307
|
);
|
|
@@ -242,6 +310,13 @@ export default class MonitorIncident {
|
|
|
242
310
|
|
|
243
311
|
incident.createdIncidentTemplateId = criteriaIncident.id.toString();
|
|
244
312
|
|
|
313
|
+
if (seriesFingerprint) {
|
|
314
|
+
incident.seriesFingerprint = seriesFingerprint;
|
|
315
|
+
}
|
|
316
|
+
if (seriesLabels && Object.keys(seriesLabels).length > 0) {
|
|
317
|
+
incident.seriesLabels = seriesLabels;
|
|
318
|
+
}
|
|
319
|
+
|
|
245
320
|
incident.onCallDutyPolicies =
|
|
246
321
|
criteriaIncident.onCallPolicyIds?.map((id: ObjectID) => {
|
|
247
322
|
const onCallPolicy: OnCallDutyPolicy = new OnCallDutyPolicy();
|
|
@@ -393,6 +468,82 @@ export default class MonitorIncident {
|
|
|
393
468
|
input.openIncident.projectId!,
|
|
394
469
|
);
|
|
395
470
|
|
|
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
|
+
|
|
396
547
|
const incidentStateTimeline: IncidentStateTimeline =
|
|
397
548
|
new IncidentStateTimeline();
|
|
398
549
|
incidentStateTimeline.incidentId = input.openIncident.id!;
|
|
@@ -425,7 +576,60 @@ export default class MonitorIncident {
|
|
|
425
576
|
Array<string>
|
|
426
577
|
>;
|
|
427
578
|
criteriaInstance: MonitorCriteriaInstance | null; // null if no criteia met.
|
|
579
|
+
breachingSeriesFingerprints?: Set<string> | undefined;
|
|
428
580
|
}): boolean {
|
|
581
|
+
const openSeriesFingerprint: string | undefined =
|
|
582
|
+
input.openIncident.seriesFingerprint || undefined;
|
|
583
|
+
|
|
584
|
+
/*
|
|
585
|
+
* Per-series auto-resolve: when the monitor emits a breaching-
|
|
586
|
+
* series set and this open incident has a fingerprint, resolve
|
|
587
|
+
* whenever the fingerprint is no longer in the set — regardless
|
|
588
|
+
* of whether some *other* series is still breaching the same
|
|
589
|
+
* criteria. This is the whole point of per-host incidents.
|
|
590
|
+
*/
|
|
591
|
+
if (
|
|
592
|
+
input.breachingSeriesFingerprints !== undefined &&
|
|
593
|
+
openSeriesFingerprint
|
|
594
|
+
) {
|
|
595
|
+
const stillBreaching: boolean = input.breachingSeriesFingerprints.has(
|
|
596
|
+
openSeriesFingerprint,
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
if (stillBreaching) {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/*
|
|
604
|
+
* Series no longer breaching. Only auto-close if the criteria
|
|
605
|
+
* was configured to auto-resolve in the first place; otherwise
|
|
606
|
+
* stay open so a human can acknowledge.
|
|
607
|
+
*/
|
|
608
|
+
if (!input.openIncident.createdCriteriaId?.toString()) {
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (!input.openIncident.createdIncidentTemplateId?.toString()) {
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const autoResolveTemplates: Array<string> | undefined =
|
|
617
|
+
input.autoResolveCriteriaInstanceIdIncidentIdsDictionary[
|
|
618
|
+
input.openIncident.createdCriteriaId.toString()
|
|
619
|
+
];
|
|
620
|
+
|
|
621
|
+
if (
|
|
622
|
+
autoResolveTemplates &&
|
|
623
|
+
autoResolveTemplates.includes(
|
|
624
|
+
input.openIncident.createdIncidentTemplateId.toString(),
|
|
625
|
+
)
|
|
626
|
+
) {
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
|
|
429
633
|
if (
|
|
430
634
|
input.openIncident.createdCriteriaId?.toString() ===
|
|
431
635
|
input.criteriaInstance?.data?.id.toString()
|