@oneuptime/common 14.0.5 → 14.0.7
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/InventoryItem.ts +23 -12
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/AlertService.ts +32 -36
- package/Server/Services/IncidentAlertService.ts +609 -2
- package/Server/Services/IncidentService.ts +134 -44
- package/Server/Services/InventoryItemService.ts +38 -0
- package/Server/Services/IoTDeviceCredentialService.ts +13 -4
- package/Server/Services/MetricService.ts +1 -1
- package/Server/Services/ScheduledMaintenanceService.ts +50 -46
- package/Server/Services/UserNotificationRuleService.ts +55 -53
- package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
- package/Server/Types/Database/QueryHelper.ts +54 -1
- package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
- package/Server/Utils/AI/Toolbox/ResourceTools.ts +12 -1
- package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
- package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
- package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +4 -3
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +3 -1
- package/Server/Utils/Response.ts +31 -0
- package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
- package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
- package/Server/Utils/Topology/TopologyRequest.ts +463 -0
- package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
- package/Server/Utils/Topology/TopologySql.ts +1024 -0
- package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
- package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
- package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
- package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
- package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
- package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
- package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
- package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +292 -1
- package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
- package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
- package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
- package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
- package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
- package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
- package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
- package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
- package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
- package/Tests/Server/Services/DomainService.test.ts +525 -0
- package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
- package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
- package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
- package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
- package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
- package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
- package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
- package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
- package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
- package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
- package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
- package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
- package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
- package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
- package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +96 -0
- package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
- package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
- package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
- package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
- package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
- package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
- package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
- package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
- package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
- package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
- package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
- package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
- package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
- package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
- package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
- package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +8 -14
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +4 -3
- package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +27 -6
- package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
- package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
- package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
- package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
- package/Types/BaseDatabase/AggregationType.ts +7 -0
- package/Types/Dashboard/DashboardTemplates.ts +20 -7
- package/Types/Incident/IncidentAlertLink.ts +19 -0
- package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
- package/Types/Rum/WebVitals.ts +13 -0
- package/Types/Telemetry/InventoryLiveness.ts +26 -0
- package/Types/Topology/TopologyApi.ts +428 -0
- package/Types/Topology/TopologyTypeRules.ts +191 -0
- package/UI/Components/Input/Input.tsx +11 -0
- package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
- package/Utils/Incident/IncidentFromAlerts.ts +78 -0
- package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.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/AlertService.js +21 -33
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentAlertService.js +457 -3
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +96 -42
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +31 -0
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
- package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +37 -40
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +47 -1
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +12 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
- package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
- package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
- package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
- package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
- package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
- package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +30 -0
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
- package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
- package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
- package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
- package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
- package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
- package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
- package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +20 -7
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +12 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
- package/build/dist/Types/Topology/TopologyApi.js +55 -0
- package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
- package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
- package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
- package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterEach,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
jest,
|
|
7
|
+
test,
|
|
8
|
+
} from "@jest/globals";
|
|
9
|
+
import {
|
|
10
|
+
DECLARE_INCIDENT_FROM_ALERT_BUTTON_ID,
|
|
11
|
+
getDeclareIncidentFromAlertAction,
|
|
12
|
+
getDeclareIncidentFromAlertsGate,
|
|
13
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/Alert/DeclareIncidentFromAlert";
|
|
14
|
+
import { DECLARE_INCIDENT_ACTION_TITLE } from "../../../../App/FeatureSet/Dashboard/src/Components/Alert/BulkIncidentLinkActions";
|
|
15
|
+
import { EventPanelAction } from "../../../../App/FeatureSet/Dashboard/src/Components/EventView/EventStatusPanel";
|
|
16
|
+
import Incident from "../../../Models/DatabaseModels/Incident";
|
|
17
|
+
import IncidentAlert from "../../../Models/DatabaseModels/IncidentAlert";
|
|
18
|
+
import Route from "../../../Types/API/Route";
|
|
19
|
+
import IconProp from "../../../Types/Icon/IconProp";
|
|
20
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
21
|
+
import Permission from "../../../Types/Permission";
|
|
22
|
+
import Navigation from "../../../UI/Utils/Navigation";
|
|
23
|
+
import PermissionUtil from "../../../UI/Utils/Permission";
|
|
24
|
+
import PermissionGate, {
|
|
25
|
+
ModelAction,
|
|
26
|
+
PermissionGateResult,
|
|
27
|
+
} from "../../../UI/Utils/PermissionGate";
|
|
28
|
+
import ProjectUtil from "../../../UI/Utils/Project";
|
|
29
|
+
import UserUtil from "../../../UI/Utils/User";
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* "Declare Incident" in an alert's header. Declaring creates an incident and
|
|
33
|
+
* then links the alert to it, so the button needs Incident create AND
|
|
34
|
+
* IncidentAlert create, checked in that order (the same order as the bulk
|
|
35
|
+
* action and the Linked Incidents page), and the reason names the first one
|
|
36
|
+
* missing. The action follows PermissionGate's three outcomes:
|
|
37
|
+
*
|
|
38
|
+
* - allowed -> a working button that opens the create page;
|
|
39
|
+
* - a known reason -> a disabled button whose tooltip says which;
|
|
40
|
+
* - unknown answer -> no button at all (the snapshot has not loaded).
|
|
41
|
+
*
|
|
42
|
+
* The reason strings are pinned verbatim: they are what a locked-out user
|
|
43
|
+
* reads, and they list the model's real permission sets (Incident.ts and
|
|
44
|
+
* IncidentAlert.ts), so a change to either list shows up here.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
const PROJECT_ID: string = "8f2a1b3c-4d5e-4f60-9a7b-1c2d3e4f5a6b";
|
|
48
|
+
const ALERT_ID: string = "22222222-2222-4222-8222-000000000001";
|
|
49
|
+
const OTHER_ALERT_ID: string = "22222222-2222-4222-8222-000000000002";
|
|
50
|
+
|
|
51
|
+
const INCIDENT_CREATE_REASON: string =
|
|
52
|
+
"You do not have permission to create this Incident. You need one of these permissions: Project Owner, Project Admin, Project Member, Incident Admin, Incident Member, Create Incident.";
|
|
53
|
+
|
|
54
|
+
const INCIDENT_ALERT_CREATE_REASON: string =
|
|
55
|
+
"You do not have permission to create this Incident Alert. You need one of these permissions: Project Owner, Project Admin, Project Member, Incident Admin, Incident Member, Alert Admin, Alert Member, Create Incident Alert.";
|
|
56
|
+
|
|
57
|
+
let permissionsForTest: Array<Permission> = [];
|
|
58
|
+
let navigateSpy: ReturnType<typeof jest.spyOn>;
|
|
59
|
+
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
permissionsForTest = [Permission.ProjectMember];
|
|
62
|
+
PermissionGate.clearPermissionPropsCache();
|
|
63
|
+
|
|
64
|
+
jest.spyOn(PermissionUtil, "getAllPermissions").mockImplementation(() => {
|
|
65
|
+
return permissionsForTest;
|
|
66
|
+
});
|
|
67
|
+
jest.spyOn(UserUtil, "isMasterAdmin").mockReturnValue(false);
|
|
68
|
+
jest
|
|
69
|
+
.spyOn(ProjectUtil, "getCurrentProjectId")
|
|
70
|
+
.mockReturnValue(new ObjectID(PROJECT_ID));
|
|
71
|
+
navigateSpy = jest
|
|
72
|
+
.spyOn(Navigation, "navigate")
|
|
73
|
+
.mockImplementation((): void => {});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
jest.restoreAllMocks();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("the declare-incident reasons", () => {
|
|
81
|
+
/*
|
|
82
|
+
* Guards the pinned strings above against drifting away from what
|
|
83
|
+
* PermissionGate actually says for these two models.
|
|
84
|
+
*/
|
|
85
|
+
test("match PermissionGate's own message for each model", () => {
|
|
86
|
+
expect(
|
|
87
|
+
PermissionGate.getMissingPermissionMessage(
|
|
88
|
+
new Incident(),
|
|
89
|
+
ModelAction.Create,
|
|
90
|
+
),
|
|
91
|
+
).toBe(INCIDENT_CREATE_REASON);
|
|
92
|
+
expect(
|
|
93
|
+
PermissionGate.getMissingPermissionMessage(
|
|
94
|
+
new IncidentAlert(),
|
|
95
|
+
ModelAction.Create,
|
|
96
|
+
),
|
|
97
|
+
).toBe(INCIDENT_ALERT_CREATE_REASON);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("getDeclareIncidentFromAlertsGate", () => {
|
|
102
|
+
test.each([
|
|
103
|
+
["Project Owner", [Permission.ProjectOwner]],
|
|
104
|
+
["Project Admin", [Permission.ProjectAdmin]],
|
|
105
|
+
["Project Member", [Permission.ProjectMember]],
|
|
106
|
+
["Incident Admin", [Permission.IncidentAdmin]],
|
|
107
|
+
["Incident Member", [Permission.IncidentMember]],
|
|
108
|
+
[
|
|
109
|
+
"Alert Member + Create Incident",
|
|
110
|
+
[Permission.AlertMember, Permission.CreateProjectIncident],
|
|
111
|
+
],
|
|
112
|
+
[
|
|
113
|
+
"Alert Admin + Create Incident",
|
|
114
|
+
[Permission.AlertAdmin, Permission.CreateProjectIncident],
|
|
115
|
+
],
|
|
116
|
+
[
|
|
117
|
+
"Create Incident + Create Incident Alert",
|
|
118
|
+
[Permission.CreateProjectIncident, Permission.CreateIncidentAlert],
|
|
119
|
+
],
|
|
120
|
+
["Viewer + Project Member", [Permission.Viewer, Permission.ProjectMember]],
|
|
121
|
+
[
|
|
122
|
+
"Create All Operational Resources + Create Incident Alert",
|
|
123
|
+
[
|
|
124
|
+
Permission.CreateAllOperationalResources,
|
|
125
|
+
Permission.CreateIncidentAlert,
|
|
126
|
+
],
|
|
127
|
+
],
|
|
128
|
+
])("allows %s", (_label: string, permissions: Array<Permission>) => {
|
|
129
|
+
permissionsForTest = permissions;
|
|
130
|
+
|
|
131
|
+
expect(getDeclareIncidentFromAlertsGate()).toEqual({ isAllowed: true });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test.each([
|
|
135
|
+
["Viewer", [Permission.Viewer]],
|
|
136
|
+
["Alert Member", [Permission.AlertMember]],
|
|
137
|
+
["Alert Admin", [Permission.AlertAdmin]],
|
|
138
|
+
["Alert Viewer", [Permission.AlertViewer]],
|
|
139
|
+
["Incident Viewer", [Permission.IncidentViewer]],
|
|
140
|
+
["Create Incident Alert only", [Permission.CreateIncidentAlert]],
|
|
141
|
+
[
|
|
142
|
+
"Alert Member + Incident Viewer",
|
|
143
|
+
[Permission.AlertMember, Permission.IncidentViewer],
|
|
144
|
+
],
|
|
145
|
+
])(
|
|
146
|
+
"names the Incident permission for %s",
|
|
147
|
+
(_label: string, permissions: Array<Permission>) => {
|
|
148
|
+
permissionsForTest = permissions;
|
|
149
|
+
|
|
150
|
+
expect(getDeclareIncidentFromAlertsGate()).toEqual({
|
|
151
|
+
isAllowed: false,
|
|
152
|
+
disabledReason: INCIDENT_CREATE_REASON,
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
test.each([
|
|
158
|
+
["Create Incident only", [Permission.CreateProjectIncident]],
|
|
159
|
+
[
|
|
160
|
+
"Viewer + Create Incident",
|
|
161
|
+
[Permission.Viewer, Permission.CreateProjectIncident],
|
|
162
|
+
],
|
|
163
|
+
[
|
|
164
|
+
"Incident Viewer + Create Incident",
|
|
165
|
+
[Permission.IncidentViewer, Permission.CreateProjectIncident],
|
|
166
|
+
],
|
|
167
|
+
/*
|
|
168
|
+
* Incident is an operational resource, so the wildcard covers creating
|
|
169
|
+
* it. IncidentAlert is not, so the wildcard alone cannot link the alert.
|
|
170
|
+
*/
|
|
171
|
+
[
|
|
172
|
+
"Create All Operational Resources only",
|
|
173
|
+
[Permission.CreateAllOperationalResources],
|
|
174
|
+
],
|
|
175
|
+
])(
|
|
176
|
+
"names the Incident Alert permission for %s",
|
|
177
|
+
(_label: string, permissions: Array<Permission>) => {
|
|
178
|
+
permissionsForTest = permissions;
|
|
179
|
+
|
|
180
|
+
expect(getDeclareIncidentFromAlertsGate()).toEqual({
|
|
181
|
+
isAllowed: false,
|
|
182
|
+
disabledReason: INCIDENT_ALERT_CREATE_REASON,
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
test("checks Incident create first, then Incident Alert create", () => {
|
|
188
|
+
const checkSpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
189
|
+
PermissionGate,
|
|
190
|
+
"check",
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
getDeclareIncidentFromAlertsGate();
|
|
194
|
+
|
|
195
|
+
expect(checkSpy).toHaveBeenCalledTimes(2);
|
|
196
|
+
expect(checkSpy.mock.calls[0]![0]).toBeInstanceOf(Incident);
|
|
197
|
+
expect(checkSpy.mock.calls[0]![1]).toBe(ModelAction.Create);
|
|
198
|
+
expect(checkSpy.mock.calls[1]![0]).toBeInstanceOf(IncidentAlert);
|
|
199
|
+
expect(checkSpy.mock.calls[1]![1]).toBe(ModelAction.Create);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("stops at the first refusal and never asks about the link", () => {
|
|
203
|
+
permissionsForTest = [Permission.Viewer];
|
|
204
|
+
|
|
205
|
+
const checkSpy: ReturnType<typeof jest.spyOn> = jest.spyOn(
|
|
206
|
+
PermissionGate,
|
|
207
|
+
"check",
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const gate: PermissionGateResult = getDeclareIncidentFromAlertsGate();
|
|
211
|
+
|
|
212
|
+
expect(checkSpy).toHaveBeenCalledTimes(1);
|
|
213
|
+
expect(checkSpy.mock.calls[0]![0]).toBeInstanceOf(Incident);
|
|
214
|
+
// Both are missing for a viewer; the first missing one is the answer.
|
|
215
|
+
expect(gate.disabledReason).toBe(INCIDENT_CREATE_REASON);
|
|
216
|
+
expect(gate.disabledReason).not.toBe(INCIDENT_ALERT_CREATE_REASON);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("has no reason to give before the permission snapshot has loaded", () => {
|
|
220
|
+
permissionsForTest = [];
|
|
221
|
+
|
|
222
|
+
const gate: PermissionGateResult = getDeclareIncidentFromAlertsGate();
|
|
223
|
+
|
|
224
|
+
expect(gate.isAllowed).toBe(false);
|
|
225
|
+
expect(gate.disabledReason).toBeUndefined();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("allows a master admin even with an empty snapshot", () => {
|
|
229
|
+
permissionsForTest = [];
|
|
230
|
+
jest.spyOn(UserUtil, "isMasterAdmin").mockReturnValue(true);
|
|
231
|
+
|
|
232
|
+
expect(getDeclareIncidentFromAlertsGate()).toEqual({ isAllowed: true });
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("allows a master admin who holds only a viewer role", () => {
|
|
236
|
+
permissionsForTest = [Permission.Viewer];
|
|
237
|
+
jest.spyOn(UserUtil, "isMasterAdmin").mockReturnValue(true);
|
|
238
|
+
|
|
239
|
+
expect(getDeclareIncidentFromAlertsGate()).toEqual({ isAllowed: true });
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe("getDeclareIncidentFromAlertAction", () => {
|
|
244
|
+
test("uses the pinned button id", () => {
|
|
245
|
+
expect(DECLARE_INCIDENT_FROM_ALERT_BUTTON_ID).toBe(
|
|
246
|
+
"alert-declare-incident-btn",
|
|
247
|
+
);
|
|
248
|
+
expect(DECLARE_INCIDENT_ACTION_TITLE).toBe("Declare Incident");
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("is a working button for a project member", () => {
|
|
252
|
+
const action: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
253
|
+
new ObjectID(ALERT_ID),
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
expect(action).not.toBeNull();
|
|
257
|
+
expect(action).toEqual({
|
|
258
|
+
id: "alert-declare-incident-btn",
|
|
259
|
+
label: "Declare Incident",
|
|
260
|
+
icon: IconProp.Alert,
|
|
261
|
+
onClick: expect.any(Function),
|
|
262
|
+
});
|
|
263
|
+
expect(action).not.toHaveProperty("isDisabled");
|
|
264
|
+
expect(action).not.toHaveProperty("tooltip");
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("does not navigate until it is clicked", () => {
|
|
268
|
+
getDeclareIncidentFromAlertAction(new ObjectID(ALERT_ID));
|
|
269
|
+
|
|
270
|
+
expect(navigateSpy).not.toHaveBeenCalled();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("opens the create-incident page prefilled from this alert", () => {
|
|
274
|
+
const action: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
275
|
+
new ObjectID(ALERT_ID),
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
action!.onClick();
|
|
279
|
+
|
|
280
|
+
expect(navigateSpy).toHaveBeenCalledTimes(1);
|
|
281
|
+
|
|
282
|
+
const call: Array<unknown> = navigateSpy.mock.calls[0] as Array<unknown>;
|
|
283
|
+
|
|
284
|
+
expect(call[0]).toBeInstanceOf(Route);
|
|
285
|
+
expect((call[0] as Route).toString()).toBe(
|
|
286
|
+
`/dashboard/${PROJECT_ID}/incidents/create?alertIds=${ALERT_ID}`,
|
|
287
|
+
);
|
|
288
|
+
// Same tab, no forced reload: a plain in-app navigation.
|
|
289
|
+
expect(call[1]).toBeUndefined();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("navigates again on every click", () => {
|
|
293
|
+
const action: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
294
|
+
new ObjectID(ALERT_ID),
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
action!.onClick();
|
|
298
|
+
action!.onClick();
|
|
299
|
+
|
|
300
|
+
expect(navigateSpy).toHaveBeenCalledTimes(2);
|
|
301
|
+
expect((navigateSpy.mock.calls[1]![0] as Route).toString()).toBe(
|
|
302
|
+
`/dashboard/${PROJECT_ID}/incidents/create?alertIds=${ALERT_ID}`,
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("carries only the alert it was built for", () => {
|
|
307
|
+
const first: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
308
|
+
new ObjectID(ALERT_ID),
|
|
309
|
+
);
|
|
310
|
+
const second: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
311
|
+
new ObjectID(OTHER_ALERT_ID),
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
second!.onClick();
|
|
315
|
+
first!.onClick();
|
|
316
|
+
|
|
317
|
+
expect(
|
|
318
|
+
navigateSpy.mock.calls.map((call: Array<unknown>): string => {
|
|
319
|
+
return (call[0] as Route).toString();
|
|
320
|
+
}),
|
|
321
|
+
).toEqual([
|
|
322
|
+
`/dashboard/${PROJECT_ID}/incidents/create?alertIds=${OTHER_ALERT_ID}`,
|
|
323
|
+
`/dashboard/${PROJECT_ID}/incidents/create?alertIds=${ALERT_ID}`,
|
|
324
|
+
]);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("is hidden before the permission snapshot has loaded", () => {
|
|
328
|
+
permissionsForTest = [];
|
|
329
|
+
|
|
330
|
+
expect(
|
|
331
|
+
getDeclareIncidentFromAlertAction(new ObjectID(ALERT_ID)),
|
|
332
|
+
).toBeNull();
|
|
333
|
+
expect(navigateSpy).not.toHaveBeenCalled();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test("is locked for a viewer, naming the Incident permission, and goes nowhere", () => {
|
|
337
|
+
permissionsForTest = [Permission.Viewer];
|
|
338
|
+
|
|
339
|
+
const action: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
340
|
+
new ObjectID(ALERT_ID),
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
expect(action).toEqual({
|
|
344
|
+
id: "alert-declare-incident-btn",
|
|
345
|
+
label: "Declare Incident",
|
|
346
|
+
icon: IconProp.Alert,
|
|
347
|
+
isDisabled: true,
|
|
348
|
+
tooltip: INCIDENT_CREATE_REASON,
|
|
349
|
+
onClick: expect.any(Function),
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
expect(action!.onClick()).toBeUndefined();
|
|
353
|
+
expect(navigateSpy).not.toHaveBeenCalled();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("is locked for an alert member, naming the Incident permission", () => {
|
|
357
|
+
permissionsForTest = [Permission.AlertMember];
|
|
358
|
+
|
|
359
|
+
const action: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
360
|
+
new ObjectID(ALERT_ID),
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
expect(action!.isDisabled).toBe(true);
|
|
364
|
+
expect(action!.tooltip).toBe(INCIDENT_CREATE_REASON);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test("is locked for someone who may create incidents but not link them", () => {
|
|
368
|
+
permissionsForTest = [Permission.CreateProjectIncident];
|
|
369
|
+
|
|
370
|
+
const action: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
371
|
+
new ObjectID(ALERT_ID),
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
expect(action!.isDisabled).toBe(true);
|
|
375
|
+
expect(action!.tooltip).toBe(INCIDENT_ALERT_CREATE_REASON);
|
|
376
|
+
|
|
377
|
+
action!.onClick();
|
|
378
|
+
|
|
379
|
+
expect(navigateSpy).not.toHaveBeenCalled();
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test("works for an alert member who may also create incidents", () => {
|
|
383
|
+
permissionsForTest = [
|
|
384
|
+
Permission.AlertMember,
|
|
385
|
+
Permission.CreateProjectIncident,
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
const action: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
389
|
+
new ObjectID(ALERT_ID),
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
expect(action!.isDisabled).toBeUndefined();
|
|
393
|
+
expect(action!.tooltip).toBeUndefined();
|
|
394
|
+
|
|
395
|
+
action!.onClick();
|
|
396
|
+
|
|
397
|
+
expect(navigateSpy).toHaveBeenCalledTimes(1);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test("works for a master admin even before the snapshot has loaded", () => {
|
|
401
|
+
permissionsForTest = [];
|
|
402
|
+
jest.spyOn(UserUtil, "isMasterAdmin").mockReturnValue(true);
|
|
403
|
+
|
|
404
|
+
const action: EventPanelAction | null = getDeclareIncidentFromAlertAction(
|
|
405
|
+
new ObjectID(ALERT_ID),
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
expect(action).not.toBeNull();
|
|
409
|
+
expect(action!.isDisabled).toBeUndefined();
|
|
410
|
+
|
|
411
|
+
action!.onClick();
|
|
412
|
+
|
|
413
|
+
expect((navigateSpy.mock.calls[0]![0] as Route).toString()).toBe(
|
|
414
|
+
`/dashboard/${PROJECT_ID}/incidents/create?alertIds=${ALERT_ID}`,
|
|
415
|
+
);
|
|
416
|
+
});
|
|
417
|
+
});
|