@oneuptime/common 14.0.0 → 14.0.1
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/Index.ts +4 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/DatabaseService.ts +10 -0
- package/Server/Services/Index.ts +4 -0
- package/Server/Services/MonitorService.ts +3 -2
- package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
- package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
- package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
- package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
- package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
- package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
- package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
- package/Server/Utils/Monitor/AffectedResourceList.ts +31 -73
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +181 -136
- package/Server/Utils/Monitor/RootCauseList.ts +139 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
- package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
- package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
- package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
- package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
- package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
- package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
- package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
- package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
- package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
- package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
- package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
- package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
- package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
- package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
- package/Tests/Server/Types/Markdown.test.ts +2 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
- package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +405 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
- package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
- package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
- package/Tests/Types/API/Route.test.ts +56 -0
- package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
- package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
- package/Tests/UI/Components/Icon.test.tsx +26 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
- package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
- package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
- package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
- package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
- package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
- package/Types/API/Route.ts +19 -2
- package/Types/API/URL.ts +26 -2
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
- package/Types/Permission.ts +90 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/UI/Components/Feed/FeedOptions.ts +427 -0
- package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
- package/UI/Components/Feed/useFeedItems.ts +28 -9
- package/UI/Components/Feed/useFeedOptions.ts +225 -0
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/Utils/LabelRuleImportExport.ts +27 -9
- package/Utils/Monitor/MetricValueFormatter.ts +2 -2
- package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
- package/Utils/Slo/SloDuration.ts +22 -6
- package/Utils/Slo/SloHealth.ts +8 -7
- package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
- package/Utils/Slo/SloOverviewText.ts +33 -0
- package/Utils/Slo/SloProjection.ts +4 -3
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.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/DatabaseService.js +7 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +4 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +3 -2
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +22 -51
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +139 -109
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
- package/build/dist/Types/API/Route.js +18 -2
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/API/URL.js +23 -2
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Permission.js +84 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
- package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
- package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
- package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
- package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +22 -6
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
- package/build/dist/Utils/Slo/SloDuration.js +13 -5
- package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
- package/build/dist/Utils/Slo/SloHealth.js +8 -7
- package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
- package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
- package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
- package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import Label from "../../Models/DatabaseModels/Label";
|
|
2
|
+
import ServiceLevelObjective from "../../Models/DatabaseModels/ServiceLevelObjective";
|
|
3
|
+
import { ServiceLevelObjectiveFeedEventType } from "../../Models/DatabaseModels/ServiceLevelObjectiveFeed";
|
|
4
|
+
import ServiceLevelObjectiveLabelRule from "../../Models/DatabaseModels/ServiceLevelObjectiveLabelRule";
|
|
5
|
+
import { Purple500 } from "../../Types/BrandColors";
|
|
6
|
+
import ObjectID from "../../Types/ObjectID";
|
|
7
|
+
import { escapeMarkdownInline } from "../../Utils/Markdown/MarkdownEscape";
|
|
8
|
+
import { MAX_RULES_EVALUATED_PER_PROJECT } from "../../Utils/Rules/RuleEngineLimits";
|
|
9
|
+
import { RuleCriteriaMatcher } from "../../Utils/Rules/RuleCriteriaMatcher";
|
|
10
|
+
import RulePatternMatchUtil from "../../Utils/Rules/RulePatternMatchUtil";
|
|
11
|
+
import Select from "../Types/Database/Select";
|
|
12
|
+
import logger, { LogAttributes } from "../Utils/Logger";
|
|
13
|
+
import logIfRuleReadWasTruncated from "../Utils/Rules/RuleEngineRuleRead";
|
|
14
|
+
import {
|
|
15
|
+
ApplyRulesToExistingResourceData,
|
|
16
|
+
RuleApplicationResult,
|
|
17
|
+
RuleApplicationResultUtil,
|
|
18
|
+
RuleRunEngine,
|
|
19
|
+
} from "../Utils/Rules/RuleRun/RuleApplication";
|
|
20
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
21
|
+
import ServiceLevelObjectiveFeedService from "./ServiceLevelObjectiveFeedService";
|
|
22
|
+
import ServiceLevelObjectiveLabelRuleService from "./ServiceLevelObjectiveLabelRuleService";
|
|
23
|
+
import ServiceLevelObjectiveService from "./ServiceLevelObjectiveService";
|
|
24
|
+
|
|
25
|
+
class ServiceLevelObjectiveLabelRuleEngineServiceClass
|
|
26
|
+
implements
|
|
27
|
+
RuleRunEngine<ServiceLevelObjective, ServiceLevelObjectiveLabelRule>
|
|
28
|
+
{
|
|
29
|
+
public readonly ruleSelect: Select<ServiceLevelObjectiveLabelRule> = {
|
|
30
|
+
_id: true,
|
|
31
|
+
name: true,
|
|
32
|
+
criteria: true,
|
|
33
|
+
serviceLevelObjectiveLabels: { _id: true },
|
|
34
|
+
serviceLevelObjectiveNamePattern: true,
|
|
35
|
+
serviceLevelObjectiveDescriptionPattern: true,
|
|
36
|
+
labelsToAdd: { _id: true },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Evaluation re-reads the SLO, so a run only has to name it.
|
|
40
|
+
public readonly resourceSelectForRuleRun: Select<ServiceLevelObjective> = {
|
|
41
|
+
_id: true,
|
|
42
|
+
projectId: true,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Evaluates ServiceLevelObjectiveLabelRule rows for the given SLO and
|
|
47
|
+
* attaches matched labels to it. The union is deduped against labels already
|
|
48
|
+
* on the SLO before insert to avoid PK conflicts on the join table.
|
|
49
|
+
*/
|
|
50
|
+
@CaptureSpan()
|
|
51
|
+
public async applyRulesToServiceLevelObjective(
|
|
52
|
+
serviceLevelObjective: ServiceLevelObjective,
|
|
53
|
+
): Promise<void> {
|
|
54
|
+
if (!serviceLevelObjective.id || !serviceLevelObjective.projectId) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const rules: Array<ServiceLevelObjectiveLabelRule> =
|
|
60
|
+
await ServiceLevelObjectiveLabelRuleService.findBy({
|
|
61
|
+
query: {
|
|
62
|
+
projectId: serviceLevelObjective.projectId,
|
|
63
|
+
isEnabled: true,
|
|
64
|
+
},
|
|
65
|
+
props: { isRoot: true },
|
|
66
|
+
select: this.ruleSelect,
|
|
67
|
+
limit: MAX_RULES_EVALUATED_PER_PROJECT,
|
|
68
|
+
skip: 0,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
logIfRuleReadWasTruncated({
|
|
72
|
+
ruleKind: "ServiceLevelObjectiveLabelRule",
|
|
73
|
+
projectId: serviceLevelObjective.projectId,
|
|
74
|
+
rulesRead: rules.length,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (rules.length === 0) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
await this.applyRules({
|
|
82
|
+
serviceLevelObjective: serviceLevelObjective,
|
|
83
|
+
rules: rules,
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
logger.error(`Error applying SLO label rules: ${error}`, {
|
|
87
|
+
projectId: serviceLevelObjective.projectId?.toString(),
|
|
88
|
+
serviceLevelObjectiveId: serviceLevelObjective.id?.toString(),
|
|
89
|
+
} as LogAttributes);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* "Run now": the same evaluation, for an SLO that already exists and only
|
|
95
|
+
* the rules being run.
|
|
96
|
+
*/
|
|
97
|
+
@CaptureSpan()
|
|
98
|
+
public async applyRulesToExistingResource(
|
|
99
|
+
data: ApplyRulesToExistingResourceData<
|
|
100
|
+
ServiceLevelObjective,
|
|
101
|
+
ServiceLevelObjectiveLabelRule
|
|
102
|
+
>,
|
|
103
|
+
): Promise<RuleApplicationResult> {
|
|
104
|
+
try {
|
|
105
|
+
return await this.applyRules({
|
|
106
|
+
serviceLevelObjective: data.resource,
|
|
107
|
+
rules: data.rules,
|
|
108
|
+
});
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger.error(`Error running SLO label rules: ${error}`, {
|
|
111
|
+
projectId: data.resource.projectId?.toString(),
|
|
112
|
+
serviceLevelObjectiveId: data.resource.id?.toString(),
|
|
113
|
+
} as LogAttributes);
|
|
114
|
+
|
|
115
|
+
return RuleApplicationResultUtil.failed();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private async applyRules(data: {
|
|
120
|
+
serviceLevelObjective: ServiceLevelObjective;
|
|
121
|
+
rules: Array<ServiceLevelObjectiveLabelRule>;
|
|
122
|
+
}): Promise<RuleApplicationResult> {
|
|
123
|
+
const { serviceLevelObjective, rules } = data;
|
|
124
|
+
|
|
125
|
+
if (
|
|
126
|
+
!serviceLevelObjective.id ||
|
|
127
|
+
!serviceLevelObjective.projectId ||
|
|
128
|
+
rules.length === 0
|
|
129
|
+
) {
|
|
130
|
+
return RuleApplicationResultUtil.noMatch();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const sloWithDetails: ServiceLevelObjective | null =
|
|
134
|
+
await ServiceLevelObjectiveService.findOneById({
|
|
135
|
+
id: serviceLevelObjective.id,
|
|
136
|
+
select: {
|
|
137
|
+
name: true,
|
|
138
|
+
description: true,
|
|
139
|
+
labels: { _id: true },
|
|
140
|
+
},
|
|
141
|
+
props: { isRoot: true },
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!sloWithDetails) {
|
|
145
|
+
return RuleApplicationResultUtil.noMatch();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const labelIdsToAdd: Set<string> = new Set();
|
|
149
|
+
const matchedRuleNames: Array<string> = [];
|
|
150
|
+
let matchedAnyRule: boolean = false;
|
|
151
|
+
|
|
152
|
+
for (const rule of rules) {
|
|
153
|
+
if (!this.doesServiceLevelObjectiveMatchRule(sloWithDetails, rule)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
matchedAnyRule = true;
|
|
157
|
+
if ((rule.labelsToAdd || []).length > 0) {
|
|
158
|
+
matchedRuleNames.push(
|
|
159
|
+
rule.name || rule.id?.toString() || "Unnamed rule",
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
for (const label of rule.labelsToAdd || []) {
|
|
163
|
+
if (label.id) {
|
|
164
|
+
labelIdsToAdd.add(label.id.toString());
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!matchedAnyRule) {
|
|
170
|
+
return RuleApplicationResultUtil.noMatch();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (labelIdsToAdd.size === 0) {
|
|
174
|
+
return RuleApplicationResultUtil.alreadyApplied();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const existingLabelIds: Set<string> = new Set(
|
|
178
|
+
(sloWithDetails.labels || [])
|
|
179
|
+
.map((l: Label) => {
|
|
180
|
+
return l.id?.toString() || "";
|
|
181
|
+
})
|
|
182
|
+
.filter((id: string) => {
|
|
183
|
+
return id !== "";
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const newLabelIds: Array<string> = Array.from(labelIdsToAdd).filter(
|
|
188
|
+
(id: string) => {
|
|
189
|
+
return !existingLabelIds.has(id);
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
if (newLabelIds.length === 0) {
|
|
193
|
+
return RuleApplicationResultUtil.alreadyApplied();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
await ServiceLevelObjectiveService.getRepository()
|
|
197
|
+
.createQueryBuilder()
|
|
198
|
+
.relation(ServiceLevelObjective, "labels")
|
|
199
|
+
.of(serviceLevelObjective.id.toString())
|
|
200
|
+
.add(newLabelIds);
|
|
201
|
+
|
|
202
|
+
/*
|
|
203
|
+
* Sync in-memory labels so the owner-rule engine that runs next in the
|
|
204
|
+
* same onCreateSuccess chain can match on rule-added labels.
|
|
205
|
+
*/
|
|
206
|
+
const mergedLabelIds: Set<string> = new Set([
|
|
207
|
+
...existingLabelIds,
|
|
208
|
+
...newLabelIds,
|
|
209
|
+
]);
|
|
210
|
+
serviceLevelObjective.labels = Array.from(mergedLabelIds).map(
|
|
211
|
+
(id: string) => {
|
|
212
|
+
const label: Label = new Label();
|
|
213
|
+
label.id = new ObjectID(id);
|
|
214
|
+
return label;
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
logger.debug(
|
|
219
|
+
`ServiceLevelObjectiveLabelRuleEngine attached ${newLabelIds.length} labels to SLO ${serviceLevelObjective.id}`,
|
|
220
|
+
{
|
|
221
|
+
projectId: serviceLevelObjective.projectId.toString(),
|
|
222
|
+
} as LogAttributes,
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
await this.postLabelRuleFeedItem({
|
|
226
|
+
serviceLevelObjectiveId: serviceLevelObjective.id,
|
|
227
|
+
projectId: serviceLevelObjective.projectId,
|
|
228
|
+
sloName: sloWithDetails.name,
|
|
229
|
+
labelsAdded: newLabelIds.length,
|
|
230
|
+
matchedRuleNames: matchedRuleNames,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return RuleApplicationResultUtil.updated(newLabelIds.length);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/*
|
|
237
|
+
* Labels arriving from a rule rather than from a person is exactly what the
|
|
238
|
+
* SLO's feed cannot otherwise explain, so it records which rules did it.
|
|
239
|
+
* The labels are already attached by now: a feed failure is logged, never
|
|
240
|
+
* thrown, or a run would report an SLO it changed as one it failed on.
|
|
241
|
+
*/
|
|
242
|
+
private async postLabelRuleFeedItem(data: {
|
|
243
|
+
serviceLevelObjectiveId: ObjectID;
|
|
244
|
+
projectId: ObjectID;
|
|
245
|
+
sloName: string | undefined;
|
|
246
|
+
labelsAdded: number;
|
|
247
|
+
matchedRuleNames: Array<string>;
|
|
248
|
+
}): Promise<void> {
|
|
249
|
+
try {
|
|
250
|
+
const sloLink: string =
|
|
251
|
+
await ServiceLevelObjectiveService.getSloMarkdownLink({
|
|
252
|
+
projectId: data.projectId,
|
|
253
|
+
sloId: data.serviceLevelObjectiveId,
|
|
254
|
+
sloName: data.sloName,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await ServiceLevelObjectiveFeedService.createServiceLevelObjectiveFeedItem(
|
|
258
|
+
{
|
|
259
|
+
serviceLevelObjectiveId: data.serviceLevelObjectiveId,
|
|
260
|
+
projectId: data.projectId,
|
|
261
|
+
serviceLevelObjectiveFeedEventType:
|
|
262
|
+
ServiceLevelObjectiveFeedEventType.LabelRuleExecuted,
|
|
263
|
+
displayColor: Purple500,
|
|
264
|
+
feedInfoInMarkdown: `🏷️ ${data.labelsAdded} label(s) were attached to ${sloLink} by label ${data.matchedRuleNames.length === 1 ? "rule" : "rules"}.`,
|
|
265
|
+
moreInformationInMarkdown: `**Label rules that matched**: ${data.matchedRuleNames
|
|
266
|
+
.map((name: string): string => {
|
|
267
|
+
// Rule names are user-controlled and the feed renders markdown.
|
|
268
|
+
return `**${escapeMarkdownInline(name)}**`;
|
|
269
|
+
})
|
|
270
|
+
.join(", ")}`,
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
logger.error(
|
|
275
|
+
`Error writing the label rule feed item for SLO ${data.serviceLevelObjectiveId.toString()}: ${error}`,
|
|
276
|
+
{ projectId: data.projectId.toString() } as LogAttributes,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private doesServiceLevelObjectiveMatchRule(
|
|
282
|
+
serviceLevelObjective: ServiceLevelObjective,
|
|
283
|
+
rule: ServiceLevelObjectiveLabelRule,
|
|
284
|
+
): boolean {
|
|
285
|
+
return RuleCriteriaMatcher.matchesWithLegacySync({
|
|
286
|
+
rule,
|
|
287
|
+
legacyFields: [
|
|
288
|
+
"serviceLevelObjectiveLabels",
|
|
289
|
+
"serviceLevelObjectiveNamePattern",
|
|
290
|
+
"serviceLevelObjectiveDescriptionPattern",
|
|
291
|
+
],
|
|
292
|
+
emptyResult: true,
|
|
293
|
+
matchesLegacyRule: (
|
|
294
|
+
legacyRule: ServiceLevelObjectiveLabelRule,
|
|
295
|
+
): boolean => {
|
|
296
|
+
return this.doesServiceLevelObjectiveMatchLegacyRule(
|
|
297
|
+
serviceLevelObjective,
|
|
298
|
+
legacyRule,
|
|
299
|
+
);
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private doesServiceLevelObjectiveMatchLegacyRule(
|
|
305
|
+
serviceLevelObjective: ServiceLevelObjective,
|
|
306
|
+
rule: ServiceLevelObjectiveLabelRule,
|
|
307
|
+
): boolean {
|
|
308
|
+
if (
|
|
309
|
+
rule.serviceLevelObjectiveLabels &&
|
|
310
|
+
rule.serviceLevelObjectiveLabels.length > 0
|
|
311
|
+
) {
|
|
312
|
+
if (
|
|
313
|
+
!serviceLevelObjective.labels ||
|
|
314
|
+
serviceLevelObjective.labels.length === 0
|
|
315
|
+
) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
const ruleLabelIds: Array<string> = rule.serviceLevelObjectiveLabels.map(
|
|
319
|
+
(l: Label) => {
|
|
320
|
+
return l.id?.toString() || "";
|
|
321
|
+
},
|
|
322
|
+
);
|
|
323
|
+
const labelIds: Array<string> = serviceLevelObjective.labels.map(
|
|
324
|
+
(l: Label) => {
|
|
325
|
+
return l.id?.toString() || "";
|
|
326
|
+
},
|
|
327
|
+
);
|
|
328
|
+
if (
|
|
329
|
+
!ruleLabelIds.some((id: string) => {
|
|
330
|
+
return labelIds.includes(id);
|
|
331
|
+
})
|
|
332
|
+
) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (
|
|
338
|
+
rule.serviceLevelObjectiveNamePattern &&
|
|
339
|
+
(!serviceLevelObjective.name ||
|
|
340
|
+
!this.testPattern(
|
|
341
|
+
rule.serviceLevelObjectiveNamePattern,
|
|
342
|
+
serviceLevelObjective.name,
|
|
343
|
+
rule,
|
|
344
|
+
))
|
|
345
|
+
) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (
|
|
350
|
+
rule.serviceLevelObjectiveDescriptionPattern &&
|
|
351
|
+
(!serviceLevelObjective.description ||
|
|
352
|
+
!this.testPattern(
|
|
353
|
+
rule.serviceLevelObjectiveDescriptionPattern,
|
|
354
|
+
serviceLevelObjective.description,
|
|
355
|
+
rule,
|
|
356
|
+
))
|
|
357
|
+
) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/*
|
|
365
|
+
* Patterns are regexes, with a '*' wildcard fallback - the same two syntaxes
|
|
366
|
+
* the SLO monitor rules take. SloRulePatternValidator rejects anything else
|
|
367
|
+
* on save, so this only warns about a rule written before that check.
|
|
368
|
+
*/
|
|
369
|
+
private testPattern(
|
|
370
|
+
pattern: string,
|
|
371
|
+
value: string,
|
|
372
|
+
rule: ServiceLevelObjectiveLabelRule,
|
|
373
|
+
): boolean {
|
|
374
|
+
if (!RulePatternMatchUtil.isSupportedPattern(pattern)) {
|
|
375
|
+
logger.warn(
|
|
376
|
+
`Invalid pattern in SLO label rule ${rule.id}: ${pattern}. It is neither a valid regular expression nor a wildcard pattern, so it will never match.`,
|
|
377
|
+
);
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return RulePatternMatchUtil.matches(value, pattern);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export default new ServiceLevelObjectiveLabelRuleEngineServiceClass();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import Model from "../../Models/DatabaseModels/ServiceLevelObjectiveLabelRule";
|
|
3
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
4
|
+
import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
|
|
5
|
+
import UpdateBy from "../Types/Database/UpdateBy";
|
|
6
|
+
import SloRulePatternValidator from "../Utils/Slo/SloRulePatternValidator";
|
|
7
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* No hard-delete retention: a rule is configuration, not an event log, so it
|
|
11
|
+
* stays until somebody deletes it.
|
|
12
|
+
*/
|
|
13
|
+
export class Service extends DatabaseService<Model> {
|
|
14
|
+
public constructor() {
|
|
15
|
+
super(Model);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* A pattern that is neither a valid regex nor a wildcard can only ever match
|
|
20
|
+
* nothing, and the rule engine has no way to say so - it just quietly stops
|
|
21
|
+
* labelling. Reject it while the user is still looking at the form.
|
|
22
|
+
*/
|
|
23
|
+
@CaptureSpan()
|
|
24
|
+
protected override async onBeforeCreate(
|
|
25
|
+
createBy: CreateBy<Model>,
|
|
26
|
+
): Promise<OnCreate<Model>> {
|
|
27
|
+
SloRulePatternValidator.validate({
|
|
28
|
+
namePattern: createBy.data.serviceLevelObjectiveNamePattern,
|
|
29
|
+
descriptionPattern: createBy.data.serviceLevelObjectiveDescriptionPattern,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return { createBy: createBy, carryForward: null };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@CaptureSpan()
|
|
36
|
+
protected override async onBeforeUpdate(
|
|
37
|
+
updateBy: UpdateBy<Model>,
|
|
38
|
+
): Promise<OnUpdate<Model>> {
|
|
39
|
+
SloRulePatternValidator.validate({
|
|
40
|
+
namePattern: updateBy.data.serviceLevelObjectiveNamePattern as
|
|
41
|
+
| string
|
|
42
|
+
| undefined,
|
|
43
|
+
descriptionPattern: updateBy.data
|
|
44
|
+
.serviceLevelObjectiveDescriptionPattern as string | undefined,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return { updateBy: updateBy, carryForward: null };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default new Service();
|
|
@@ -7,8 +7,19 @@ import { Gray500, Green500 } from "../../Types/BrandColors";
|
|
|
7
7
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
8
8
|
import PartialEntity from "../../Types/Database/PartialEntity";
|
|
9
9
|
import ObjectID from "../../Types/ObjectID";
|
|
10
|
+
import FilterCondition from "../../Types/Filter/FilterCondition";
|
|
11
|
+
import RuleCriteria, {
|
|
12
|
+
RuleCriteriaFilter,
|
|
13
|
+
RuleCriteriaOperator,
|
|
14
|
+
} from "../../Types/Rules/RuleCriteria";
|
|
15
|
+
import {
|
|
16
|
+
getMonitorTypeCriteriaValidationError,
|
|
17
|
+
isMonitorTypeCriteriaValue,
|
|
18
|
+
} from "../../Utils/Rules/MonitorTypeRuleCriteria";
|
|
10
19
|
import { escapeMarkdownInline } from "../../Utils/Markdown/MarkdownEscape";
|
|
11
|
-
import RuleCriteriaMatcher
|
|
20
|
+
import RuleCriteriaMatcher, {
|
|
21
|
+
isValidRuleCriteria,
|
|
22
|
+
} from "../../Utils/Rules/RuleCriteriaMatcher";
|
|
12
23
|
import { MAX_RULES_EVALUATED_PER_PROJECT } from "../../Utils/Rules/RuleEngineLimits";
|
|
13
24
|
import RulePatternMatchUtil from "../../Utils/Rules/RulePatternMatchUtil";
|
|
14
25
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
@@ -96,6 +107,7 @@ const RULE_SELECT: Select<ServiceLevelObjectiveMonitorRule> = {
|
|
|
96
107
|
serviceLevelObjectiveId: true,
|
|
97
108
|
isEnabled: true,
|
|
98
109
|
monitorLabels: { _id: true },
|
|
110
|
+
monitorType: true,
|
|
99
111
|
monitorNamePattern: true,
|
|
100
112
|
monitorDescriptionPattern: true,
|
|
101
113
|
criteria: true,
|
|
@@ -122,7 +134,7 @@ const MAX_MONITORS_LISTED_IN_FEED_DETAILS: number = 50;
|
|
|
122
134
|
*
|
|
123
135
|
* - the SLO side (one of its rules was created, edited, disabled or
|
|
124
136
|
* deleted) -> syncMonitorsForSlo
|
|
125
|
-
* - the monitor side (its labels, name or description changed, or it was
|
|
137
|
+
* - the monitor side (its labels, type, name or description changed, or it was
|
|
126
138
|
* just created) -> syncSlosForMonitor
|
|
127
139
|
*
|
|
128
140
|
* Both write through ServiceLevelObjective.autoAddedMonitors, which records
|
|
@@ -215,6 +227,7 @@ export class ServiceLevelObjectiveMonitorRuleEngineServiceClass {
|
|
|
215
227
|
projectId: true,
|
|
216
228
|
name: true,
|
|
217
229
|
description: true,
|
|
230
|
+
monitorType: true,
|
|
218
231
|
labels: {
|
|
219
232
|
_id: true,
|
|
220
233
|
},
|
|
@@ -479,20 +492,64 @@ export class ServiceLevelObjectiveMonitorRuleEngineServiceClass {
|
|
|
479
492
|
monitor: Monitor;
|
|
480
493
|
rule: ServiceLevelObjectiveMonitorRule;
|
|
481
494
|
}): boolean {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
495
|
+
const criteria: RuleCriteria | null | undefined = data.rule.criteria;
|
|
496
|
+
|
|
497
|
+
if (criteria === undefined || criteria === null) {
|
|
498
|
+
return this.doesMonitorMatchLegacyRule(data);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// A malformed type condition must fail the whole rule, even under Match any.
|
|
502
|
+
if (
|
|
503
|
+
!isValidRuleCriteria(criteria) ||
|
|
504
|
+
criteria.filters.some((filter: RuleCriteriaFilter): boolean => {
|
|
505
|
+
return (
|
|
506
|
+
filter.field === "monitorType" &&
|
|
507
|
+
getMonitorTypeCriteriaValidationError(filter) !== null
|
|
508
|
+
);
|
|
509
|
+
})
|
|
510
|
+
) {
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return RuleCriteriaMatcher.matchesSync({
|
|
515
|
+
criteria: criteria,
|
|
489
516
|
emptyResult: false,
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
517
|
+
matchesFilter: (filter: RuleCriteriaFilter): boolean => {
|
|
518
|
+
if (filter.field === "monitorType") {
|
|
519
|
+
if (!isMonitorTypeCriteriaValue(data.monitor.monitorType)) {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return filter.operator === RuleCriteriaOperator.Equals
|
|
524
|
+
? data.monitor.monitorType === filter.value
|
|
525
|
+
: data.monitor.monitorType !== filter.value;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Existing text and label conditions retain their legacy-compatible semantics.
|
|
529
|
+
return RuleCriteriaMatcher.matchesWithLegacySync({
|
|
530
|
+
rule: {
|
|
531
|
+
...data.rule,
|
|
532
|
+
criteria: {
|
|
533
|
+
...criteria,
|
|
534
|
+
filterCondition: FilterCondition.All,
|
|
535
|
+
filters: [filter],
|
|
536
|
+
},
|
|
537
|
+
} as ServiceLevelObjectiveMonitorRule,
|
|
538
|
+
legacyFields: [
|
|
539
|
+
"monitorLabels",
|
|
540
|
+
"monitorType",
|
|
541
|
+
"monitorNamePattern",
|
|
542
|
+
"monitorDescriptionPattern",
|
|
543
|
+
],
|
|
544
|
+
emptyResult: false,
|
|
545
|
+
matchesLegacyRule: (
|
|
546
|
+
legacyRule: ServiceLevelObjectiveMonitorRule,
|
|
547
|
+
): boolean => {
|
|
548
|
+
return this.doesMonitorMatchLegacyRule({
|
|
549
|
+
monitor: data.monitor,
|
|
550
|
+
rule: legacyRule,
|
|
551
|
+
});
|
|
552
|
+
},
|
|
496
553
|
});
|
|
497
554
|
},
|
|
498
555
|
});
|
|
@@ -512,7 +569,24 @@ export class ServiceLevelObjectiveMonitorRuleEngineServiceClass {
|
|
|
512
569
|
rule.monitorDescriptionPattern,
|
|
513
570
|
);
|
|
514
571
|
|
|
515
|
-
|
|
572
|
+
const hasTypeCriteria: boolean =
|
|
573
|
+
rule.monitorType !== undefined && rule.monitorType !== null;
|
|
574
|
+
|
|
575
|
+
if (
|
|
576
|
+
!hasLabelCriteria &&
|
|
577
|
+
!hasNameCriteria &&
|
|
578
|
+
!hasDescriptionCriteria &&
|
|
579
|
+
!hasTypeCriteria
|
|
580
|
+
) {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (
|
|
585
|
+
hasTypeCriteria &&
|
|
586
|
+
(!isMonitorTypeCriteriaValue(rule.monitorType) ||
|
|
587
|
+
!isMonitorTypeCriteriaValue(monitor.monitorType) ||
|
|
588
|
+
monitor.monitorType !== rule.monitorType)
|
|
589
|
+
) {
|
|
516
590
|
return false;
|
|
517
591
|
}
|
|
518
592
|
|
|
@@ -638,6 +712,7 @@ export class ServiceLevelObjectiveMonitorRuleEngineServiceClass {
|
|
|
638
712
|
_id: true,
|
|
639
713
|
name: true,
|
|
640
714
|
description: true,
|
|
715
|
+
monitorType: true,
|
|
641
716
|
labels: {
|
|
642
717
|
_id: true,
|
|
643
718
|
},
|
|
@@ -663,6 +738,7 @@ export class ServiceLevelObjectiveMonitorRuleEngineServiceClass {
|
|
|
663
738
|
|
|
664
739
|
return Boolean(
|
|
665
740
|
(rule.monitorLabels && rule.monitorLabels.length > 0) ||
|
|
741
|
+
rule.monitorType ||
|
|
666
742
|
rule.monitorNamePattern ||
|
|
667
743
|
rule.monitorDescriptionPattern,
|
|
668
744
|
);
|
|
@@ -11,6 +11,11 @@ import Color from "../../Types/Color";
|
|
|
11
11
|
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
12
12
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
13
13
|
import ObjectID from "../../Types/ObjectID";
|
|
14
|
+
import MonitorType from "../../Types/Monitor/MonitorType";
|
|
15
|
+
import {
|
|
16
|
+
getMonitorTypeCriteriaValidationError,
|
|
17
|
+
isMonitorTypeCriteriaValue,
|
|
18
|
+
} from "../../Utils/Rules/MonitorTypeRuleCriteria";
|
|
14
19
|
import { escapeMarkdownInline } from "../../Utils/Markdown/MarkdownEscape";
|
|
15
20
|
import {
|
|
16
21
|
getRuleCriteriaValidationError,
|
|
@@ -51,6 +56,7 @@ const RULE_SNAPSHOT_SELECT: Select<Model> = {
|
|
|
51
56
|
_id: true,
|
|
52
57
|
name: true,
|
|
53
58
|
},
|
|
59
|
+
monitorType: true,
|
|
54
60
|
monitorNamePattern: true,
|
|
55
61
|
monitorDescriptionPattern: true,
|
|
56
62
|
criteria: true,
|
|
@@ -92,6 +98,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
92
98
|
|
|
93
99
|
this.assertHasMatchCriteria({
|
|
94
100
|
monitorLabelCount: (createBy.data.monitorLabels || []).length,
|
|
101
|
+
monitorType: createBy.data.monitorType,
|
|
95
102
|
monitorNamePattern: createBy.data.monitorNamePattern,
|
|
96
103
|
monitorDescriptionPattern: createBy.data.monitorDescriptionPattern,
|
|
97
104
|
criteria: createBy.data.criteria,
|
|
@@ -188,6 +195,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
188
195
|
|
|
189
196
|
const touchesCriteria: boolean =
|
|
190
197
|
updateBy.data.monitorLabels !== undefined ||
|
|
198
|
+
updateBy.data.monitorType !== undefined ||
|
|
191
199
|
updateBy.data.monitorNamePattern !== undefined ||
|
|
192
200
|
updateBy.data.monitorDescriptionPattern !== undefined ||
|
|
193
201
|
updateBy.data.criteria !== undefined;
|
|
@@ -656,6 +664,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
656
664
|
*/
|
|
657
665
|
private assertHasMatchCriteria(data: {
|
|
658
666
|
monitorLabelCount: number;
|
|
667
|
+
monitorType?: MonitorType | undefined;
|
|
659
668
|
monitorNamePattern?: string | undefined;
|
|
660
669
|
monitorDescriptionPattern?: string | undefined;
|
|
661
670
|
criteria?: Model["criteria"];
|
|
@@ -673,6 +682,17 @@ export class Service extends DatabaseService<Model> {
|
|
|
673
682
|
isValidRuleCriteria(data.criteria) &&
|
|
674
683
|
data.criteria.filters.length > 0
|
|
675
684
|
) {
|
|
685
|
+
for (const filter of data.criteria.filters) {
|
|
686
|
+
if (filter.field === "monitorType") {
|
|
687
|
+
const typeError: string | null =
|
|
688
|
+
getMonitorTypeCriteriaValidationError(filter);
|
|
689
|
+
|
|
690
|
+
if (typeError) {
|
|
691
|
+
throw new BadDataException(typeError);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
676
696
|
return;
|
|
677
697
|
}
|
|
678
698
|
|
|
@@ -681,13 +701,24 @@ export class Service extends DatabaseService<Model> {
|
|
|
681
701
|
);
|
|
682
702
|
}
|
|
683
703
|
|
|
704
|
+
if (
|
|
705
|
+
data.monitorType !== undefined &&
|
|
706
|
+
data.monitorType !== null &&
|
|
707
|
+
!isMonitorTypeCriteriaValue(data.monitorType)
|
|
708
|
+
) {
|
|
709
|
+
throw new BadDataException(
|
|
710
|
+
"Monitor type criteria require a valid monitor type.",
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
684
714
|
if (
|
|
685
715
|
data.monitorLabelCount === 0 &&
|
|
716
|
+
!data.monitorType &&
|
|
686
717
|
!data.monitorNamePattern &&
|
|
687
718
|
!data.monitorDescriptionPattern
|
|
688
719
|
) {
|
|
689
720
|
throw new BadDataException(
|
|
690
|
-
"An SLO monitor rule needs at least one match criterion: monitor labels, a monitor name pattern, or a monitor description pattern. Use .* as the name pattern to match every monitor in the project.",
|
|
721
|
+
"An SLO monitor rule needs at least one match criterion: monitor labels, a monitor type, a monitor name pattern, or a monitor description pattern. Use .* as the name pattern to match every monitor in the project.",
|
|
691
722
|
);
|
|
692
723
|
}
|
|
693
724
|
}
|
|
@@ -744,6 +775,10 @@ export class Service extends DatabaseService<Model> {
|
|
|
744
775
|
? rule.monitorLabels || []
|
|
745
776
|
: nextLabels || []
|
|
746
777
|
).length,
|
|
778
|
+
monitorType:
|
|
779
|
+
updateBy.data.monitorType === undefined
|
|
780
|
+
? rule.monitorType
|
|
781
|
+
: (updateBy.data.monitorType as MonitorType | undefined),
|
|
747
782
|
monitorNamePattern:
|
|
748
783
|
updateBy.data.monitorNamePattern === undefined
|
|
749
784
|
? rule.monitorNamePattern
|