@oneuptime/common 12.0.2 → 12.0.3
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/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +39 -0
- package/Server/EnvironmentConfig.ts +31 -1
- package/Server/Infrastructure/GlobalCache.ts +115 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/DatabaseService.ts +111 -0
- package/Server/Services/DockerHostService.ts +19 -79
- package/Server/Services/DockerSwarmClusterService.ts +27 -88
- package/Server/Services/HostService.ts +46 -99
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetService.ts +19 -79
- package/Server/Services/KubernetesClusterService.ts +17 -78
- package/Server/Services/LabelService.ts +57 -10
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +89 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/PodmanHostService.ts +19 -79
- package/Server/Services/ProxmoxClusterService.ts +23 -83
- package/Server/Services/RumApplicationService.ts +19 -80
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +24 -85
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Monitor/MonitorResource.ts +63 -36
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
- package/Server/Utils/SingleFlight.ts +83 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
- package/Server/Utils/Telemetry/Telemetry.ts +351 -122
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
- package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
- package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
- package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
- package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
- package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
- package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
- package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
- package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
- package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
- package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
- package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
- package/Tests/Types/Decimal.test.ts +37 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
- package/Types/Dashboard/DashboardTemplates.ts +397 -0
- package/Types/Database/BigIntColumnTransformer.ts +74 -0
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Decimal.ts +16 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +39 -0
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +27 -1
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +91 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -76
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -73
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +86 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -69
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -102
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -69
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +42 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MetricTypeService.js +74 -0
- package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +81 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -69
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -70
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +236 -61
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
- package/build/dist/Server/Utils/SingleFlight.js +76 -0
- package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
- package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
- package/build/dist/Types/Database/DatabaseProperty.js +25 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/NumericColumnValue.js +97 -0
- package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Port.js +15 -5
- package/build/dist/Types/Port.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,25 +1,40 @@
|
|
|
1
1
|
import MonitorStepsProjectValidator from "../../../../Server/Utils/Monitor/MonitorStepsProjectValidator";
|
|
2
2
|
import AlertSeverityService from "../../../../Server/Services/AlertSeverityService";
|
|
3
3
|
import IncidentSeverityService from "../../../../Server/Services/IncidentSeverityService";
|
|
4
|
+
import LabelService from "../../../../Server/Services/LabelService";
|
|
4
5
|
import MonitorStatusService from "../../../../Server/Services/MonitorStatusService";
|
|
6
|
+
import OnCallDutyPolicyService from "../../../../Server/Services/OnCallDutyPolicyService";
|
|
7
|
+
import ServiceService from "../../../../Server/Services/ServiceService";
|
|
5
8
|
import AlertSeverity from "../../../../Models/DatabaseModels/AlertSeverity";
|
|
6
9
|
import IncidentSeverity from "../../../../Models/DatabaseModels/IncidentSeverity";
|
|
10
|
+
import Label from "../../../../Models/DatabaseModels/Label";
|
|
7
11
|
import MonitorStatus from "../../../../Models/DatabaseModels/MonitorStatus";
|
|
12
|
+
import OnCallDutyPolicy from "../../../../Models/DatabaseModels/OnCallDutyPolicy";
|
|
8
13
|
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
14
|
+
import { JSONObject, ObjectType } from "../../../../Types/JSON";
|
|
9
15
|
import ObjectID from "../../../../Types/ObjectID";
|
|
10
|
-
import { JSONObject } from "../../../../Types/JSON";
|
|
11
16
|
import { afterEach, describe, expect, it, jest } from "@jest/globals";
|
|
12
17
|
|
|
13
18
|
/*
|
|
14
|
-
* Contract under test: monitorSteps embeds ids of
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* left the referenced project undeletable, so it must be rejected on write.
|
|
19
|
+
* Contract under test: monitorSteps embeds ids of other records — the default
|
|
20
|
+
* monitor status, the status each criteria switches to, the severities used to
|
|
21
|
+
* open incidents and alerts, and the on-call policies, labels, teams and users
|
|
22
|
+
* stamped on what those create. Two things can be wrong with one of them.
|
|
19
23
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
24
|
+
* An id belonging to a *different* project ties two projects together and
|
|
25
|
+
* leaves the referenced project undeletable. That is refused whether the field
|
|
26
|
+
* is used at run time or not.
|
|
27
|
+
*
|
|
28
|
+
* An id that exists NOWHERE is issue #3039: the monitor saved fine and then
|
|
29
|
+
* failed on every check, inside the probe worker, with
|
|
30
|
+
* insert or update on table "MonitorStatusTimeline" violates foreign key
|
|
31
|
+
* constraint
|
|
32
|
+
* and nothing surfaced to whoever supplied the id. That is refused for the ids
|
|
33
|
+
* the monitor actually reads — and only when the write is INTRODUCING them, so
|
|
34
|
+
* a monitor stored broken before this guard existed can still be saved.
|
|
35
|
+
*
|
|
36
|
+
* monitorSteps is also full of uuids that are NOT references (step ids,
|
|
37
|
+
* criteria ids, incident/alert template ids). Those must keep saving fine.
|
|
23
38
|
*/
|
|
24
39
|
|
|
25
40
|
const PROJECT_ID: ObjectID = new ObjectID(
|
|
@@ -31,14 +46,21 @@ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
|
|
|
31
46
|
|
|
32
47
|
const OWN_STATUS_ID: string = "59b5ab80-63f6-4ffd-b3df-31c6ad127695";
|
|
33
48
|
const FOREIGN_STATUS_ID: string = "cfc2f04f-79cb-4344-8c54-dafe5e3a290c";
|
|
34
|
-
const
|
|
35
|
-
|
|
49
|
+
const UNKNOWN_STATUS_ID: string = "b8a7d5d1-2f5a-45cf-8f2c-2f2b83d1d9aa";
|
|
50
|
+
const OWN_INCIDENT_SEVERITY_ID: string = "1de3b8f4-0f2d-4a6f-9a2e-7cf3a1b2c3d4";
|
|
51
|
+
const UNKNOWN_INCIDENT_SEVERITY_ID: string =
|
|
52
|
+
"2ef4c905-1a3e-4b70-8b3f-8d04b2c3d4e5";
|
|
53
|
+
const UNKNOWN_ALERT_SEVERITY_ID: string =
|
|
54
|
+
"3f05da16-2b4f-4c81-9c40-9e15c3d4e5f6";
|
|
55
|
+
const UNKNOWN_ON_CALL_POLICY_ID: string =
|
|
56
|
+
"4016eb27-3c50-4d92-8d51-af26d4e5f607";
|
|
57
|
+
const FOREIGN_LABEL_ID: string = "5127fc38-4d61-4ea3-9e62-b037e5f60718";
|
|
58
|
+
const UNKNOWN_TELEMETRY_SERVICE_ID: string =
|
|
59
|
+
"62380d49-5e72-4fb4-8f73-c148f6071829";
|
|
36
60
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
alertSeverities?: Array<AlertSeverity>;
|
|
41
|
-
};
|
|
61
|
+
const STEP_ID: string = "4b752ccd-92e8-4295-9d33-b670e1721fbb";
|
|
62
|
+
const CRITERIA_ID: string = "5c863dde-a3f9-43a6-8e44-c781f2832acc";
|
|
63
|
+
const INCIDENT_TEMPLATE_ID: string = "6d974eef-b40a-44b7-9f55-d892031943dd";
|
|
42
64
|
|
|
43
65
|
const monitorStatus: (id: string, projectId: ObjectID) => MonitorStatus = (
|
|
44
66
|
id: string,
|
|
@@ -51,17 +73,37 @@ const monitorStatus: (id: string, projectId: ObjectID) => MonitorStatus = (
|
|
|
51
73
|
return status;
|
|
52
74
|
};
|
|
53
75
|
|
|
54
|
-
const
|
|
76
|
+
const incidentSeverity: (
|
|
55
77
|
id: string,
|
|
56
78
|
projectId: ObjectID,
|
|
57
|
-
):
|
|
58
|
-
const severity:
|
|
79
|
+
) => IncidentSeverity = (id: string, projectId: ObjectID): IncidentSeverity => {
|
|
80
|
+
const severity: IncidentSeverity = new IncidentSeverity();
|
|
59
81
|
severity._id = id;
|
|
60
|
-
severity.name = "
|
|
82
|
+
severity.name = "Critical";
|
|
61
83
|
severity.projectId = projectId;
|
|
62
84
|
return severity;
|
|
63
85
|
};
|
|
64
86
|
|
|
87
|
+
const label: (id: string, projectId: ObjectID) => Label = (
|
|
88
|
+
id: string,
|
|
89
|
+
projectId: ObjectID,
|
|
90
|
+
): Label => {
|
|
91
|
+
const item: Label = new Label();
|
|
92
|
+
item._id = id;
|
|
93
|
+
item.name = "Production";
|
|
94
|
+
item.projectId = projectId;
|
|
95
|
+
return item;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
type FoundRecords = {
|
|
99
|
+
monitorStatuses?: Array<MonitorStatus>;
|
|
100
|
+
incidentSeverities?: Array<IncidentSeverity>;
|
|
101
|
+
alertSeverities?: Array<AlertSeverity>;
|
|
102
|
+
onCallDutyPolicies?: Array<OnCallDutyPolicy>;
|
|
103
|
+
labels?: Array<Label>;
|
|
104
|
+
telemetryServices?: Array<JSONObject>;
|
|
105
|
+
};
|
|
106
|
+
|
|
65
107
|
const stubLookups: (found: FoundRecords) => void = (
|
|
66
108
|
found: FoundRecords,
|
|
67
109
|
): void => {
|
|
@@ -74,18 +116,67 @@ const stubLookups: (found: FoundRecords) => void = (
|
|
|
74
116
|
jest
|
|
75
117
|
.spyOn(AlertSeverityService, "findBy")
|
|
76
118
|
.mockResolvedValue(found.alertSeverities || []);
|
|
119
|
+
jest
|
|
120
|
+
.spyOn(OnCallDutyPolicyService, "findBy")
|
|
121
|
+
.mockResolvedValue(found.onCallDutyPolicies || []);
|
|
122
|
+
jest.spyOn(LabelService, "findBy").mockResolvedValue(found.labels || []);
|
|
123
|
+
jest
|
|
124
|
+
.spyOn(ServiceService, "findBy")
|
|
125
|
+
.mockResolvedValue((found.telemetryServices || []) as never);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const objectIdJSON: (id: string) => JSONObject = (id: string): JSONObject => {
|
|
129
|
+
return { _type: ObjectType.ObjectID, value: id };
|
|
77
130
|
};
|
|
78
131
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
132
|
+
/*
|
|
133
|
+
* A monitorSteps document with one step and one criteria, shaped exactly the
|
|
134
|
+
* way MonitorSteps.toJSON writes it.
|
|
135
|
+
*/
|
|
136
|
+
const steps: (data: {
|
|
137
|
+
defaultMonitorStatusId?: string | undefined;
|
|
138
|
+
criteria?: JSONObject | undefined;
|
|
139
|
+
step?: JSONObject | undefined;
|
|
140
|
+
}) => JSONObject = (data: {
|
|
141
|
+
defaultMonitorStatusId?: string | undefined;
|
|
142
|
+
criteria?: JSONObject | undefined;
|
|
143
|
+
step?: JSONObject | undefined;
|
|
144
|
+
}): JSONObject => {
|
|
82
145
|
return {
|
|
83
|
-
_type:
|
|
146
|
+
_type: ObjectType.MonitorSteps,
|
|
84
147
|
value: {
|
|
85
|
-
defaultMonitorStatusId:
|
|
86
|
-
monitorStepsInstanceArray:
|
|
87
|
-
|
|
88
|
-
|
|
148
|
+
defaultMonitorStatusId: data.defaultMonitorStatusId,
|
|
149
|
+
monitorStepsInstanceArray: [
|
|
150
|
+
{
|
|
151
|
+
_type: ObjectType.MonitorStep,
|
|
152
|
+
value: {
|
|
153
|
+
id: STEP_ID,
|
|
154
|
+
...(data.step || {}),
|
|
155
|
+
monitorCriteria: {
|
|
156
|
+
_type: ObjectType.MonitorCriteria,
|
|
157
|
+
value: {
|
|
158
|
+
monitorCriteriaInstanceArray: data.criteria
|
|
159
|
+
? [
|
|
160
|
+
{
|
|
161
|
+
_type: ObjectType.MonitorCriteriaInstance,
|
|
162
|
+
value: {
|
|
163
|
+
id: CRITERIA_ID,
|
|
164
|
+
name: "Monitor is offline",
|
|
165
|
+
description: "",
|
|
166
|
+
filterCondition: "all",
|
|
167
|
+
filters: [],
|
|
168
|
+
incidents: [],
|
|
169
|
+
alerts: [],
|
|
170
|
+
...data.criteria,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
]
|
|
174
|
+
: [],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
],
|
|
89
180
|
},
|
|
90
181
|
};
|
|
91
182
|
};
|
|
@@ -95,87 +186,542 @@ describe("MonitorStepsProjectValidator", () => {
|
|
|
95
186
|
jest.restoreAllMocks();
|
|
96
187
|
});
|
|
97
188
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
189
|
+
describe("references that do not exist (issue #3039)", () => {
|
|
190
|
+
it("rejects a default monitor status that exists nowhere", () => {
|
|
191
|
+
stubLookups({});
|
|
192
|
+
|
|
193
|
+
return expect(
|
|
194
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
195
|
+
monitorSteps: steps({
|
|
196
|
+
defaultMonitorStatusId: UNKNOWN_STATUS_ID,
|
|
197
|
+
}),
|
|
198
|
+
projectId: PROJECT_ID,
|
|
199
|
+
}),
|
|
200
|
+
).rejects.toThrow(BadDataException);
|
|
101
201
|
});
|
|
102
202
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}),
|
|
108
|
-
).rejects.toThrow(BadDataException);
|
|
109
|
-
});
|
|
203
|
+
it("rejects the criteria status that produced the MonitorStatusTimeline foreign key error", () => {
|
|
204
|
+
stubLookups({
|
|
205
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
206
|
+
});
|
|
110
207
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
208
|
+
return expect(
|
|
209
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
210
|
+
monitorSteps: steps({
|
|
211
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
212
|
+
criteria: {
|
|
213
|
+
monitorStatusId: UNKNOWN_STATUS_ID,
|
|
214
|
+
changeMonitorStatus: true,
|
|
215
|
+
},
|
|
216
|
+
}),
|
|
217
|
+
projectId: PROJECT_ID,
|
|
218
|
+
}),
|
|
219
|
+
).rejects.toThrow(
|
|
220
|
+
`Monitor Status (criteria "Monitor is offline" monitor status) "${UNKNOWN_STATUS_ID}"`,
|
|
221
|
+
);
|
|
117
222
|
});
|
|
118
223
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
224
|
+
it("rejects an incident severity that exists nowhere", () => {
|
|
225
|
+
stubLookups({
|
|
226
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
return expect(
|
|
230
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
231
|
+
monitorSteps: steps({
|
|
232
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
233
|
+
criteria: {
|
|
234
|
+
createIncidents: true,
|
|
235
|
+
incidents: [
|
|
236
|
+
{
|
|
237
|
+
id: INCIDENT_TEMPLATE_ID,
|
|
238
|
+
title: "Down",
|
|
239
|
+
description: "",
|
|
240
|
+
incidentSeverityId: objectIdJSON(
|
|
241
|
+
UNKNOWN_INCIDENT_SEVERITY_ID,
|
|
242
|
+
),
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
}),
|
|
247
|
+
projectId: PROJECT_ID,
|
|
248
|
+
}),
|
|
249
|
+
).rejects.toThrow(
|
|
250
|
+
`Incident Severity (criteria "Monitor is offline" incident severity) "${UNKNOWN_INCIDENT_SEVERITY_ID}"`,
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("rejects an alert severity that exists nowhere", () => {
|
|
255
|
+
stubLookups({
|
|
256
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return expect(
|
|
260
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
261
|
+
monitorSteps: steps({
|
|
262
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
263
|
+
criteria: {
|
|
264
|
+
createAlerts: true,
|
|
265
|
+
alerts: [
|
|
266
|
+
{
|
|
267
|
+
id: INCIDENT_TEMPLATE_ID,
|
|
268
|
+
title: "Down",
|
|
269
|
+
description: "",
|
|
270
|
+
alertSeverityId: objectIdJSON(UNKNOWN_ALERT_SEVERITY_ID),
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
}),
|
|
275
|
+
projectId: PROJECT_ID,
|
|
276
|
+
}),
|
|
277
|
+
).rejects.toThrow(
|
|
278
|
+
`Alert Severity (criteria "Monitor is offline" alert severity) "${UNKNOWN_ALERT_SEVERITY_ID}"`,
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("rejects an on-call policy that exists nowhere", () => {
|
|
283
|
+
stubLookups({
|
|
284
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
285
|
+
incidentSeverities: [
|
|
286
|
+
incidentSeverity(OWN_INCIDENT_SEVERITY_ID, PROJECT_ID),
|
|
287
|
+
],
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return expect(
|
|
291
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
292
|
+
monitorSteps: steps({
|
|
293
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
294
|
+
criteria: {
|
|
295
|
+
createIncidents: true,
|
|
296
|
+
incidents: [
|
|
297
|
+
{
|
|
298
|
+
id: INCIDENT_TEMPLATE_ID,
|
|
299
|
+
title: "Down",
|
|
300
|
+
description: "",
|
|
301
|
+
incidentSeverityId: objectIdJSON(OWN_INCIDENT_SEVERITY_ID),
|
|
302
|
+
onCallPolicyIds: [objectIdJSON(UNKNOWN_ON_CALL_POLICY_ID)],
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
},
|
|
306
|
+
}),
|
|
307
|
+
projectId: PROJECT_ID,
|
|
308
|
+
}),
|
|
309
|
+
).rejects.toThrow(
|
|
310
|
+
`On-Call Duty Policy (criteria "Monitor is offline" incident on-call policy) "${UNKNOWN_ON_CALL_POLICY_ID}"`,
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("does not require an id the monitor never reads", () => {
|
|
315
|
+
/*
|
|
316
|
+
* A criteria keeps its monitorStatusId when `changeMonitorStatus` is off,
|
|
317
|
+
* and keeps whole incident templates when `createIncidents` is off, so
|
|
318
|
+
* toggling the switch back on restores what you had. None of that can
|
|
319
|
+
* produce the foreign key violation, and refusing a save over a field
|
|
320
|
+
* that does nothing would be a worse failure than the one being fixed.
|
|
321
|
+
*/
|
|
322
|
+
stubLookups({
|
|
323
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return expect(
|
|
327
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
328
|
+
monitorSteps: steps({
|
|
329
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
330
|
+
criteria: {
|
|
331
|
+
monitorStatusId: UNKNOWN_STATUS_ID,
|
|
332
|
+
changeMonitorStatus: false,
|
|
333
|
+
createIncidents: false,
|
|
334
|
+
incidents: [
|
|
335
|
+
{
|
|
336
|
+
id: INCIDENT_TEMPLATE_ID,
|
|
337
|
+
title: "Down",
|
|
338
|
+
description: "",
|
|
339
|
+
incidentSeverityId: objectIdJSON(
|
|
340
|
+
UNKNOWN_INCIDENT_SEVERITY_ID,
|
|
341
|
+
),
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
}),
|
|
346
|
+
projectId: PROJECT_ID,
|
|
347
|
+
}),
|
|
348
|
+
).resolves.toBeUndefined();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it("does not require a telemetry service the step is scoped to", () => {
|
|
352
|
+
/*
|
|
353
|
+
* A deleted telemetry service leaves the monitor querying nothing, not
|
|
354
|
+
* violating a foreign key, and deleting one is ordinary enough that
|
|
355
|
+
* requiring it would refuse every later edit of a monitor that once
|
|
356
|
+
* watched it.
|
|
357
|
+
*/
|
|
358
|
+
stubLookups({
|
|
359
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
return expect(
|
|
363
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
364
|
+
monitorSteps: steps({
|
|
365
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
366
|
+
step: {
|
|
367
|
+
metricMonitor: {
|
|
368
|
+
telemetryServiceIds: [
|
|
369
|
+
objectIdJSON(UNKNOWN_TELEMETRY_SERVICE_ID),
|
|
370
|
+
],
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
}),
|
|
374
|
+
projectId: PROJECT_ID,
|
|
375
|
+
}),
|
|
376
|
+
).resolves.toBeUndefined();
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("ignores uuids that are not references at all", () => {
|
|
380
|
+
/*
|
|
381
|
+
* Step ids, criteria ids and incident template ids are uuids too and
|
|
382
|
+
* match no record. The old regex-scraping guard could not tell them apart
|
|
383
|
+
* from a real reference, which is precisely why it could never check
|
|
384
|
+
* existence.
|
|
385
|
+
*/
|
|
386
|
+
stubLookups({
|
|
387
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
return expect(
|
|
391
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
392
|
+
monitorSteps: steps({
|
|
393
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
394
|
+
criteria: {
|
|
395
|
+
createIncidents: true,
|
|
396
|
+
incidents: [
|
|
397
|
+
{ id: INCIDENT_TEMPLATE_ID, title: "Down", description: "" },
|
|
398
|
+
],
|
|
399
|
+
},
|
|
400
|
+
}),
|
|
401
|
+
projectId: PROJECT_ID,
|
|
402
|
+
}),
|
|
403
|
+
).resolves.toBeUndefined();
|
|
404
|
+
});
|
|
130
405
|
});
|
|
131
406
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
407
|
+
describe("ids the record already holds", () => {
|
|
408
|
+
it("lets an update through when the dangling id was already stored", () => {
|
|
409
|
+
/*
|
|
410
|
+
* These ids have no foreign key behind them, so one really can end up
|
|
411
|
+
* dangling through no fault of the person now saving — deleting a monitor
|
|
412
|
+
* status does not rewrite the criteria that name it. The dashboard
|
|
413
|
+
* re-submits the whole blob on every save, so a strict check would refuse
|
|
414
|
+
* every edit of such a monitor, including renaming it.
|
|
415
|
+
*/
|
|
416
|
+
stubLookups({
|
|
417
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const stored: JSONObject = steps({
|
|
421
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
422
|
+
criteria: {
|
|
423
|
+
monitorStatusId: UNKNOWN_STATUS_ID,
|
|
424
|
+
changeMonitorStatus: true,
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
return expect(
|
|
429
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
430
|
+
monitorSteps: stored,
|
|
431
|
+
projectId: PROJECT_ID,
|
|
432
|
+
alreadyStoredMonitorSteps: stored,
|
|
433
|
+
}),
|
|
434
|
+
).resolves.toBeUndefined();
|
|
135
435
|
});
|
|
136
436
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
437
|
+
it("grandfathers regardless of the case the id is re-sent in", () => {
|
|
438
|
+
/*
|
|
439
|
+
* ObjectID keeps whatever case it was handed, so the same id can be
|
|
440
|
+
* stored as Postgres rendered it (lower) and re-sent by a caller in
|
|
441
|
+
* upper. Treating that as a new id would refuse the very save this
|
|
442
|
+
* exemption exists to allow.
|
|
443
|
+
*/
|
|
444
|
+
stubLookups({
|
|
445
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
return expect(
|
|
449
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
450
|
+
monitorSteps: steps({
|
|
451
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
452
|
+
criteria: {
|
|
453
|
+
monitorStatusId: UNKNOWN_STATUS_ID.toUpperCase(),
|
|
454
|
+
changeMonitorStatus: true,
|
|
455
|
+
},
|
|
456
|
+
}),
|
|
457
|
+
projectId: PROJECT_ID,
|
|
458
|
+
alreadyStoredMonitorSteps: steps({
|
|
459
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
460
|
+
criteria: {
|
|
461
|
+
monitorStatusId: UNKNOWN_STATUS_ID,
|
|
462
|
+
changeMonitorStatus: true,
|
|
463
|
+
},
|
|
464
|
+
}),
|
|
465
|
+
}),
|
|
466
|
+
).resolves.toBeUndefined();
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it("still refuses a dangling id the update is introducing", () => {
|
|
470
|
+
stubLookups({
|
|
471
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
return expect(
|
|
475
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
476
|
+
monitorSteps: steps({
|
|
477
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
478
|
+
criteria: {
|
|
479
|
+
monitorStatusId: UNKNOWN_STATUS_ID,
|
|
480
|
+
changeMonitorStatus: true,
|
|
481
|
+
},
|
|
482
|
+
}),
|
|
483
|
+
projectId: PROJECT_ID,
|
|
484
|
+
alreadyStoredMonitorSteps: steps({
|
|
485
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
486
|
+
criteria: {
|
|
487
|
+
monitorStatusId: OWN_STATUS_ID,
|
|
488
|
+
changeMonitorStatus: true,
|
|
489
|
+
},
|
|
490
|
+
}),
|
|
491
|
+
}),
|
|
492
|
+
).rejects.toThrow("do not exist");
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it("lets an update through when the cross-project id was already stored", () => {
|
|
496
|
+
/*
|
|
497
|
+
* The 1785240000000 repair migration deliberately left behind the
|
|
498
|
+
* cross-project ids it could not map to a local equivalent, and there is
|
|
499
|
+
* no migration for the labels / teams / on-call policies this guard now
|
|
500
|
+
* also covers. Refusing them here would leave those monitors permanently
|
|
501
|
+
* unsaveable — including a rename — with no way out.
|
|
502
|
+
*
|
|
503
|
+
* Letting one ride is safe now in a way it was not before: the writes
|
|
504
|
+
* that would turn it into a real foreign key are guarded at their own
|
|
505
|
+
* choke points, so it can no longer make the other project undeletable.
|
|
506
|
+
*/
|
|
507
|
+
stubLookups({
|
|
508
|
+
monitorStatuses: [
|
|
509
|
+
monitorStatus(OWN_STATUS_ID, PROJECT_ID),
|
|
510
|
+
monitorStatus(FOREIGN_STATUS_ID, OTHER_PROJECT_ID),
|
|
511
|
+
],
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const stored: JSONObject = steps({
|
|
515
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
516
|
+
criteria: {
|
|
517
|
+
monitorStatusId: FOREIGN_STATUS_ID,
|
|
518
|
+
changeMonitorStatus: true,
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
return expect(
|
|
523
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
524
|
+
monitorSteps: stored,
|
|
525
|
+
projectId: PROJECT_ID,
|
|
526
|
+
alreadyStoredMonitorSteps: stored,
|
|
527
|
+
}),
|
|
528
|
+
).resolves.toBeUndefined();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it("still refuses a cross-project id the update is introducing", () => {
|
|
532
|
+
stubLookups({
|
|
533
|
+
monitorStatuses: [
|
|
534
|
+
monitorStatus(OWN_STATUS_ID, PROJECT_ID),
|
|
535
|
+
monitorStatus(FOREIGN_STATUS_ID, OTHER_PROJECT_ID),
|
|
536
|
+
],
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
return expect(
|
|
540
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
541
|
+
monitorSteps: steps({
|
|
542
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
543
|
+
criteria: {
|
|
544
|
+
monitorStatusId: FOREIGN_STATUS_ID,
|
|
545
|
+
changeMonitorStatus: true,
|
|
546
|
+
},
|
|
547
|
+
}),
|
|
548
|
+
projectId: PROJECT_ID,
|
|
549
|
+
alreadyStoredMonitorSteps: steps({
|
|
550
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
551
|
+
}),
|
|
552
|
+
}),
|
|
553
|
+
).rejects.toThrow("belong to a different project");
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it("issues no query at all when the update introduces nothing new", () => {
|
|
557
|
+
/*
|
|
558
|
+
* Template sync pushes one blob onto every linked monitor. If each of
|
|
559
|
+
* them re-validated the ids it already had, one click would fan out into
|
|
560
|
+
* a lookup per model per monitor.
|
|
561
|
+
*/
|
|
562
|
+
stubLookups({
|
|
563
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
const stored: JSONObject = steps({
|
|
567
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
568
|
+
criteria: {
|
|
569
|
+
monitorStatusId: OWN_STATUS_ID,
|
|
570
|
+
changeMonitorStatus: true,
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
return MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
575
|
+
monitorSteps: stored,
|
|
140
576
|
projectId: PROJECT_ID,
|
|
141
|
-
|
|
142
|
-
|
|
577
|
+
alreadyStoredMonitorSteps: stored,
|
|
578
|
+
}).then(() => {
|
|
579
|
+
expect(MonitorStatusService.findBy).not.toHaveBeenCalled();
|
|
580
|
+
});
|
|
581
|
+
});
|
|
143
582
|
});
|
|
144
583
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
584
|
+
describe("references that belong to another project", () => {
|
|
585
|
+
it("rejects a monitor status from another project", () => {
|
|
586
|
+
stubLookups({
|
|
587
|
+
monitorStatuses: [monitorStatus(FOREIGN_STATUS_ID, OTHER_PROJECT_ID)],
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
return expect(
|
|
591
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
592
|
+
monitorSteps: steps({
|
|
593
|
+
defaultMonitorStatusId: FOREIGN_STATUS_ID,
|
|
594
|
+
}),
|
|
595
|
+
projectId: PROJECT_ID,
|
|
596
|
+
}),
|
|
597
|
+
).rejects.toThrow(
|
|
598
|
+
'Monitor Status (default monitor status) "Operational"',
|
|
599
|
+
);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it("rejects a label from another project even though labels are never required to exist", () => {
|
|
603
|
+
stubLookups({
|
|
604
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
605
|
+
incidentSeverities: [
|
|
606
|
+
incidentSeverity(OWN_INCIDENT_SEVERITY_ID, PROJECT_ID),
|
|
607
|
+
],
|
|
608
|
+
labels: [label(FOREIGN_LABEL_ID, OTHER_PROJECT_ID)],
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
return expect(
|
|
612
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
613
|
+
monitorSteps: steps({
|
|
614
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
615
|
+
criteria: {
|
|
616
|
+
createIncidents: false,
|
|
617
|
+
incidents: [
|
|
618
|
+
{
|
|
619
|
+
id: INCIDENT_TEMPLATE_ID,
|
|
620
|
+
title: "Down",
|
|
621
|
+
description: "",
|
|
622
|
+
labelIds: [objectIdJSON(FOREIGN_LABEL_ID)],
|
|
623
|
+
},
|
|
624
|
+
],
|
|
625
|
+
},
|
|
626
|
+
}),
|
|
627
|
+
projectId: PROJECT_ID,
|
|
628
|
+
}),
|
|
629
|
+
).rejects.toThrow("belong to a different project");
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it("names every offending record in the message", () => {
|
|
633
|
+
stubLookups({
|
|
634
|
+
monitorStatuses: [monitorStatus(FOREIGN_STATUS_ID, OTHER_PROJECT_ID)],
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
return expect(
|
|
638
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
639
|
+
monitorSteps: steps({
|
|
640
|
+
defaultMonitorStatusId: FOREIGN_STATUS_ID,
|
|
641
|
+
criteria: {
|
|
642
|
+
monitorStatusId: UNKNOWN_STATUS_ID,
|
|
643
|
+
changeMonitorStatus: true,
|
|
644
|
+
},
|
|
645
|
+
}),
|
|
646
|
+
projectId: PROJECT_ID,
|
|
647
|
+
}),
|
|
648
|
+
).rejects.toThrow(
|
|
649
|
+
/belong to a different project: Monitor Status \(default monitor status\) "Operational"[\s\S]*do not exist: Monitor Status/,
|
|
650
|
+
);
|
|
651
|
+
});
|
|
159
652
|
});
|
|
160
653
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
* while the jest.SpyInstance namespace resolves to @types/jest's.
|
|
167
|
-
*/
|
|
168
|
-
stubLookups({});
|
|
654
|
+
describe("when there is nothing to check", () => {
|
|
655
|
+
it("accepts records that belong to the monitor's own project", () => {
|
|
656
|
+
stubLookups({
|
|
657
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
658
|
+
});
|
|
169
659
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
660
|
+
return expect(
|
|
661
|
+
MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
662
|
+
monitorSteps: steps({
|
|
663
|
+
defaultMonitorStatusId: OWN_STATUS_ID,
|
|
664
|
+
criteria: {
|
|
665
|
+
monitorStatusId: OWN_STATUS_ID,
|
|
666
|
+
changeMonitorStatus: true,
|
|
667
|
+
},
|
|
668
|
+
}),
|
|
669
|
+
projectId: PROJECT_ID,
|
|
670
|
+
}),
|
|
671
|
+
).resolves.toBeUndefined();
|
|
173
672
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
673
|
+
|
|
674
|
+
it("does no lookups when there are no monitorSteps or no project", async () => {
|
|
675
|
+
/*
|
|
676
|
+
* stubLookups installs the spy, so the lookup is asserted on the service
|
|
677
|
+
* method itself. Annotating the spy handle is not an option here: this
|
|
678
|
+
* file takes jest from @jest/globals, so spyOn returns jest-mock's type
|
|
679
|
+
* while the jest.SpyInstance namespace resolves to @types/jest's.
|
|
680
|
+
*/
|
|
681
|
+
stubLookups({});
|
|
682
|
+
|
|
683
|
+
await MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
684
|
+
monitorSteps: undefined,
|
|
685
|
+
projectId: PROJECT_ID,
|
|
686
|
+
});
|
|
687
|
+
await MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
688
|
+
monitorSteps: steps({ defaultMonitorStatusId: OWN_STATUS_ID }),
|
|
689
|
+
projectId: undefined,
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
expect(MonitorStatusService.findBy).not.toHaveBeenCalled();
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it("does no lookups when monitorSteps holds no references at all", () => {
|
|
696
|
+
stubLookups({});
|
|
697
|
+
|
|
698
|
+
return MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
699
|
+
monitorSteps: steps({}),
|
|
700
|
+
projectId: PROJECT_ID,
|
|
701
|
+
}).then(() => {
|
|
702
|
+
expect(MonitorStatusService.findBy).not.toHaveBeenCalled();
|
|
703
|
+
});
|
|
177
704
|
});
|
|
178
705
|
|
|
179
|
-
|
|
706
|
+
it("only queries the models that are actually referenced", () => {
|
|
707
|
+
/*
|
|
708
|
+
* Every referenced model costs a round trip on the monitor write path, so
|
|
709
|
+
* a monitor that names no label must not pay for a Label lookup.
|
|
710
|
+
*/
|
|
711
|
+
stubLookups({
|
|
712
|
+
monitorStatuses: [monitorStatus(OWN_STATUS_ID, PROJECT_ID)],
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
return MonitorStepsProjectValidator.validateMonitorStepsBelongToProject({
|
|
716
|
+
monitorSteps: steps({ defaultMonitorStatusId: OWN_STATUS_ID }),
|
|
717
|
+
projectId: PROJECT_ID,
|
|
718
|
+
}).then(() => {
|
|
719
|
+
expect(MonitorStatusService.findBy).toHaveBeenCalledTimes(1);
|
|
720
|
+
expect(LabelService.findBy).not.toHaveBeenCalled();
|
|
721
|
+
expect(OnCallDutyPolicyService.findBy).not.toHaveBeenCalled();
|
|
722
|
+
expect(IncidentSeverityService.findBy).not.toHaveBeenCalled();
|
|
723
|
+
expect(AlertSeverityService.findBy).not.toHaveBeenCalled();
|
|
724
|
+
});
|
|
725
|
+
});
|
|
180
726
|
});
|
|
181
727
|
});
|