@oneuptime/common 14.0.2 → 14.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/AlertFeed.ts +2 -0
- package/Models/DatabaseModels/IncidentAlert.ts +540 -0
- package/Models/DatabaseModels/IncidentFeed.ts +2 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/Project.ts +60 -0
- package/Models/DatabaseModels/RumApplication.ts +52 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/IncidentAlertService.ts +1811 -0
- package/Server/Services/IncidentService.ts +210 -10
- package/Server/Services/IncidentStateTimelineService.ts +24 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
- package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
- package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
- package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
- package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
- package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
- package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
- package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
- package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
- package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
- package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +38 -18
- package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
- package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
- package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
- package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
- package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
- package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
- package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
- package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
- package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
- package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
- package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
- package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
- package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
- package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
- package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
- package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
- package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
- package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
- package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
- package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
- package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
- package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
- package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
- package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
- package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
- package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
- package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
- package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
- package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
- package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
- package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
- package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
- package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
- package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
- package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
- package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
- package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
- package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
- package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
- package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
- package/Tests/Types/Rum/WebVitals.test.ts +173 -0
- package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
- package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
- package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
- package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
- package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
- package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
- package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
- package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
- package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
- package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
- package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/UI/Utils/PermissionGate.test.ts +76 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
- package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
- package/Types/Incident/IncidentAlertLink.ts +38 -0
- package/Types/Permission.ts +48 -0
- package/Types/Rum/SessionReplay.ts +25 -6
- package/Types/Rum/WebVitals.ts +52 -0
- package/UI/Components/Detail/FieldLabel.tsx +8 -0
- package/UI/Components/InfoCard/InfoCard.tsx +47 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +95 -17
- package/UI/Components/ModelTable/Column.ts +5 -0
- package/UI/Components/Table/TableHeader.tsx +47 -2
- package/UI/Components/Table/Types/Column.ts +7 -0
- package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
- package/UI/Utils/PermissionGate.ts +11 -2
- package/Utils/Incident/IncidentFromAlerts.ts +516 -0
- package/Utils/Rum/SessionTraceState.ts +212 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
- package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.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/Project.js +62 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.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/IncidentAlertService.js +1370 -0
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +164 -12
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +21 -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/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +25 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
- package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +66 -14
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +18 -2
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
- package/build/dist/UI/Utils/PermissionGate.js +4 -2
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
- package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
- package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterEach,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
jest,
|
|
7
|
+
test,
|
|
8
|
+
} from "@jest/globals";
|
|
9
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* The helpers every alert-to-incident link entry point shares (the alerts
|
|
13
|
+
* table's bulk action and the two Linked pages): numbered option labels, the
|
|
14
|
+
* newest-first option lists, creating the link, recognising a duplicate, and
|
|
15
|
+
* locking a link button the user could not use.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const getListMock: MockFunction = getJestMockFunction();
|
|
19
|
+
const createMock: MockFunction = getJestMockFunction();
|
|
20
|
+
|
|
21
|
+
jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
|
|
22
|
+
return {
|
|
23
|
+
__esModule: true,
|
|
24
|
+
default: {
|
|
25
|
+
getList: (...args: Array<any>) => {
|
|
26
|
+
return getListMock(...args);
|
|
27
|
+
},
|
|
28
|
+
create: (...args: Array<any>) => {
|
|
29
|
+
return createMock(...args);
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
LINK_OPTIONS_LIMIT,
|
|
37
|
+
createIncidentAlertLink,
|
|
38
|
+
fetchAlertLinkOptions,
|
|
39
|
+
fetchIncidentLinkOptions,
|
|
40
|
+
getAlertOptionLabel,
|
|
41
|
+
getIncidentOptionLabel,
|
|
42
|
+
isAlreadyLinkedError,
|
|
43
|
+
lockUnlessAllowed,
|
|
44
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/IncidentAlert/IncidentAlertLink";
|
|
45
|
+
import Alert from "../../../Models/DatabaseModels/Alert";
|
|
46
|
+
import Incident from "../../../Models/DatabaseModels/Incident";
|
|
47
|
+
import IncidentAlert from "../../../Models/DatabaseModels/IncidentAlert";
|
|
48
|
+
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
|
|
49
|
+
import IconProp from "../../../Types/Icon/IconProp";
|
|
50
|
+
import { INCIDENT_ALERT_ALREADY_LINKED_MESSAGE } from "../../../Types/Incident/IncidentAlertLink";
|
|
51
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
52
|
+
import { ButtonStyleType } from "../../../UI/Components/Button/Button";
|
|
53
|
+
import { CardButtonSchema } from "../../../UI/Components/Card/Card";
|
|
54
|
+
import { DropdownOption } from "../../../UI/Components/Dropdown/Dropdown";
|
|
55
|
+
import ProjectUtil from "../../../UI/Utils/Project";
|
|
56
|
+
|
|
57
|
+
const PROJECT_ID: string = "8f2a1b3c-4d5e-4f60-9a7b-1c2d3e4f5a6b";
|
|
58
|
+
const INCIDENT_ID: string = "44444444-4444-4444-8444-444444444444";
|
|
59
|
+
const ALERT_ID: string = "22222222-2222-4222-8222-222222222222";
|
|
60
|
+
|
|
61
|
+
type ListResultFunction = (data: Array<unknown>) => {
|
|
62
|
+
data: Array<unknown>;
|
|
63
|
+
count: number;
|
|
64
|
+
skip: number;
|
|
65
|
+
limit: number;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const listResult: ListResultFunction = (data: Array<unknown>) => {
|
|
69
|
+
return { data: data, count: data.length, skip: 0, limit: data.length };
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
type MakeIncidentFunction = (data: {
|
|
73
|
+
id?: string;
|
|
74
|
+
title?: string;
|
|
75
|
+
incidentNumber?: number;
|
|
76
|
+
incidentNumberWithPrefix?: string;
|
|
77
|
+
}) => Incident;
|
|
78
|
+
|
|
79
|
+
const makeIncident: MakeIncidentFunction = (data: {
|
|
80
|
+
id?: string;
|
|
81
|
+
title?: string;
|
|
82
|
+
incidentNumber?: number;
|
|
83
|
+
incidentNumberWithPrefix?: string;
|
|
84
|
+
}): Incident => {
|
|
85
|
+
const incident: Incident = new Incident();
|
|
86
|
+
if (data.id !== undefined) {
|
|
87
|
+
incident._id = data.id;
|
|
88
|
+
}
|
|
89
|
+
if (data.title !== undefined) {
|
|
90
|
+
incident.title = data.title;
|
|
91
|
+
}
|
|
92
|
+
if (data.incidentNumber !== undefined) {
|
|
93
|
+
incident.incidentNumber = data.incidentNumber;
|
|
94
|
+
}
|
|
95
|
+
if (data.incidentNumberWithPrefix !== undefined) {
|
|
96
|
+
incident.incidentNumberWithPrefix = data.incidentNumberWithPrefix;
|
|
97
|
+
}
|
|
98
|
+
return incident;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
type MakeAlertFunction = (data: {
|
|
102
|
+
id?: string;
|
|
103
|
+
title?: string;
|
|
104
|
+
alertNumber?: number;
|
|
105
|
+
alertNumberWithPrefix?: string;
|
|
106
|
+
}) => Alert;
|
|
107
|
+
|
|
108
|
+
const makeAlert: MakeAlertFunction = (data: {
|
|
109
|
+
id?: string;
|
|
110
|
+
title?: string;
|
|
111
|
+
alertNumber?: number;
|
|
112
|
+
alertNumberWithPrefix?: string;
|
|
113
|
+
}): Alert => {
|
|
114
|
+
const alert: Alert = new Alert();
|
|
115
|
+
if (data.id !== undefined) {
|
|
116
|
+
alert._id = data.id;
|
|
117
|
+
}
|
|
118
|
+
if (data.title !== undefined) {
|
|
119
|
+
alert.title = data.title;
|
|
120
|
+
}
|
|
121
|
+
if (data.alertNumber !== undefined) {
|
|
122
|
+
alert.alertNumber = data.alertNumber;
|
|
123
|
+
}
|
|
124
|
+
if (data.alertNumberWithPrefix !== undefined) {
|
|
125
|
+
alert.alertNumberWithPrefix = data.alertNumberWithPrefix;
|
|
126
|
+
}
|
|
127
|
+
return alert;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
describe("the incident alert link helpers", () => {
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
getListMock.mockReset();
|
|
133
|
+
createMock.mockReset();
|
|
134
|
+
jest
|
|
135
|
+
.spyOn(ProjectUtil, "getCurrentProjectId")
|
|
136
|
+
.mockReturnValue(new ObjectID(PROJECT_ID));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
afterEach(() => {
|
|
140
|
+
jest.restoreAllMocks();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("option labels", () => {
|
|
144
|
+
test("label an incident with its number when it has one", () => {
|
|
145
|
+
expect(
|
|
146
|
+
getIncidentOptionLabel(
|
|
147
|
+
makeIncident({
|
|
148
|
+
title: "Down",
|
|
149
|
+
incidentNumber: 3,
|
|
150
|
+
incidentNumberWithPrefix: "INC-3",
|
|
151
|
+
}),
|
|
152
|
+
),
|
|
153
|
+
).toBe("INC-3: Down");
|
|
154
|
+
expect(
|
|
155
|
+
getIncidentOptionLabel(
|
|
156
|
+
makeIncident({ title: "Down", incidentNumber: 3 }),
|
|
157
|
+
),
|
|
158
|
+
).toBe("#3: Down");
|
|
159
|
+
expect(getIncidentOptionLabel(makeIncident({ title: "Down" }))).toBe(
|
|
160
|
+
"Down",
|
|
161
|
+
);
|
|
162
|
+
expect(getIncidentOptionLabel(makeIncident({ incidentNumber: 3 }))).toBe(
|
|
163
|
+
"#3",
|
|
164
|
+
);
|
|
165
|
+
expect(getIncidentOptionLabel(makeIncident({}))).toBe("");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("label an alert with its number when it has one", () => {
|
|
169
|
+
expect(
|
|
170
|
+
getAlertOptionLabel(
|
|
171
|
+
makeAlert({
|
|
172
|
+
title: "Checkout API is offline",
|
|
173
|
+
alertNumber: 63,
|
|
174
|
+
alertNumberWithPrefix: "ALT-63",
|
|
175
|
+
}),
|
|
176
|
+
),
|
|
177
|
+
).toBe("ALT-63: Checkout API is offline");
|
|
178
|
+
expect(
|
|
179
|
+
getAlertOptionLabel(
|
|
180
|
+
makeAlert({ title: "Checkout API is offline", alertNumber: 63 }),
|
|
181
|
+
),
|
|
182
|
+
).toBe("#63: Checkout API is offline");
|
|
183
|
+
expect(getAlertOptionLabel(makeAlert({ title: "Offline" }))).toBe(
|
|
184
|
+
"Offline",
|
|
185
|
+
);
|
|
186
|
+
expect(getAlertOptionLabel(makeAlert({ alertNumber: 63 }))).toBe("#63");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
/*
|
|
190
|
+
* The reason the labels exist: a monitor that goes offline three times
|
|
191
|
+
* raises three alerts with the same title.
|
|
192
|
+
*/
|
|
193
|
+
test("tell same-titled alerts apart", () => {
|
|
194
|
+
const labels: Array<string> = [41, 57, 63].map(
|
|
195
|
+
(alertNumber: number): string => {
|
|
196
|
+
return getAlertOptionLabel(
|
|
197
|
+
makeAlert({
|
|
198
|
+
title: "Checkout API is offline",
|
|
199
|
+
alertNumber: alertNumber,
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
expect(new Set(labels).size).toBe(3);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe("option lists", () => {
|
|
210
|
+
test("list recent incidents newest first, by number", async () => {
|
|
211
|
+
getListMock.mockResolvedValue(
|
|
212
|
+
listResult([
|
|
213
|
+
makeIncident({
|
|
214
|
+
id: "44444444-4444-4444-8444-000000000002",
|
|
215
|
+
title: "Database is down",
|
|
216
|
+
incidentNumber: 42,
|
|
217
|
+
incidentNumberWithPrefix: "INC-42",
|
|
218
|
+
}),
|
|
219
|
+
makeIncident({ title: "No id, so not selectable" }),
|
|
220
|
+
makeIncident({
|
|
221
|
+
id: "44444444-4444-4444-8444-000000000001",
|
|
222
|
+
title: "Checkout errors",
|
|
223
|
+
incidentNumber: 41,
|
|
224
|
+
}),
|
|
225
|
+
]),
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const options: Array<DropdownOption> = await fetchIncidentLinkOptions();
|
|
229
|
+
const request: any = getListMock.mock.calls[0]![0];
|
|
230
|
+
|
|
231
|
+
expect(request.modelType).toBe(Incident);
|
|
232
|
+
expect(request.query).toEqual({});
|
|
233
|
+
expect(request.limit).toBe(LINK_OPTIONS_LIMIT);
|
|
234
|
+
expect(request.skip).toBe(0);
|
|
235
|
+
expect(request.sort).toEqual({ createdAt: SortOrder.Descending });
|
|
236
|
+
expect(request.select).toEqual({
|
|
237
|
+
_id: true,
|
|
238
|
+
title: true,
|
|
239
|
+
incidentNumber: true,
|
|
240
|
+
incidentNumberWithPrefix: true,
|
|
241
|
+
});
|
|
242
|
+
expect(options).toEqual([
|
|
243
|
+
{
|
|
244
|
+
label: "INC-42: Database is down",
|
|
245
|
+
value: "44444444-4444-4444-8444-000000000002",
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
label: "#41: Checkout errors",
|
|
249
|
+
value: "44444444-4444-4444-8444-000000000001",
|
|
250
|
+
},
|
|
251
|
+
]);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("list recent alerts newest first, by number", async () => {
|
|
255
|
+
getListMock.mockResolvedValue(
|
|
256
|
+
listResult([
|
|
257
|
+
makeAlert({
|
|
258
|
+
id: "22222222-2222-4222-8222-000000000063",
|
|
259
|
+
title: "Checkout API is offline",
|
|
260
|
+
alertNumber: 63,
|
|
261
|
+
alertNumberWithPrefix: "ALT-63",
|
|
262
|
+
}),
|
|
263
|
+
makeAlert({
|
|
264
|
+
id: "22222222-2222-4222-8222-000000000057",
|
|
265
|
+
title: "Checkout API is offline",
|
|
266
|
+
alertNumber: 57,
|
|
267
|
+
}),
|
|
268
|
+
]),
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const options: Array<DropdownOption> = await fetchAlertLinkOptions();
|
|
272
|
+
const request: any = getListMock.mock.calls[0]![0];
|
|
273
|
+
|
|
274
|
+
expect(request.modelType).toBe(Alert);
|
|
275
|
+
expect(request.limit).toBe(LINK_OPTIONS_LIMIT);
|
|
276
|
+
expect(request.sort).toEqual({ createdAt: SortOrder.Descending });
|
|
277
|
+
expect(request.select).toEqual({
|
|
278
|
+
_id: true,
|
|
279
|
+
title: true,
|
|
280
|
+
alertNumber: true,
|
|
281
|
+
alertNumberWithPrefix: true,
|
|
282
|
+
});
|
|
283
|
+
expect(options).toEqual([
|
|
284
|
+
{
|
|
285
|
+
label: "ALT-63: Checkout API is offline",
|
|
286
|
+
value: "22222222-2222-4222-8222-000000000063",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
label: "#57: Checkout API is offline",
|
|
290
|
+
value: "22222222-2222-4222-8222-000000000057",
|
|
291
|
+
},
|
|
292
|
+
]);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("pass a failed read on to the caller", async () => {
|
|
296
|
+
getListMock.mockImplementation(async (): Promise<never> => {
|
|
297
|
+
throw new Error("Forbidden");
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
await expect(fetchAlertLinkOptions()).rejects.toThrow("Forbidden");
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe("creating a link", () => {
|
|
305
|
+
test("creates one IncidentAlert in the current project", async () => {
|
|
306
|
+
createMock.mockResolvedValue({});
|
|
307
|
+
|
|
308
|
+
await createIncidentAlertLink({
|
|
309
|
+
incidentId: new ObjectID(INCIDENT_ID),
|
|
310
|
+
alertId: new ObjectID(ALERT_ID),
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
expect(createMock).toHaveBeenCalledTimes(1);
|
|
314
|
+
|
|
315
|
+
const request: any = createMock.mock.calls[0]![0];
|
|
316
|
+
const link: IncidentAlert = request.model;
|
|
317
|
+
|
|
318
|
+
expect(request.modelType).toBe(IncidentAlert);
|
|
319
|
+
expect(link).toBeInstanceOf(IncidentAlert);
|
|
320
|
+
expect(link.incidentId?.toString()).toBe(INCIDENT_ID);
|
|
321
|
+
expect(link.alertId?.toString()).toBe(ALERT_ID);
|
|
322
|
+
expect(link.projectId?.toString()).toBe(PROJECT_ID);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("leaves the project to the server when none is selected", async () => {
|
|
326
|
+
jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(null);
|
|
327
|
+
createMock.mockResolvedValue({});
|
|
328
|
+
|
|
329
|
+
await createIncidentAlertLink({
|
|
330
|
+
incidentId: new ObjectID(INCIDENT_ID),
|
|
331
|
+
alertId: new ObjectID(ALERT_ID),
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
expect(
|
|
335
|
+
(createMock.mock.calls[0]![0] as any).model.projectId,
|
|
336
|
+
).toBeUndefined();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("passes a refusal on to the caller", async () => {
|
|
340
|
+
createMock.mockImplementation(async (): Promise<never> => {
|
|
341
|
+
throw new Error(INCIDENT_ALERT_ALREADY_LINKED_MESSAGE);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
await expect(
|
|
345
|
+
createIncidentAlertLink({
|
|
346
|
+
incidentId: new ObjectID(INCIDENT_ID),
|
|
347
|
+
alertId: new ObjectID(ALERT_ID),
|
|
348
|
+
}),
|
|
349
|
+
).rejects.toThrow(INCIDENT_ALERT_ALREADY_LINKED_MESSAGE);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe("recognising a duplicate link", () => {
|
|
354
|
+
test("matches the server's already-linked message", () => {
|
|
355
|
+
expect(isAlreadyLinkedError(INCIDENT_ALERT_ALREADY_LINKED_MESSAGE)).toBe(
|
|
356
|
+
true,
|
|
357
|
+
);
|
|
358
|
+
expect(
|
|
359
|
+
isAlreadyLinkedError(
|
|
360
|
+
` ${INCIDENT_ALERT_ALREADY_LINKED_MESSAGE.toUpperCase()} `,
|
|
361
|
+
),
|
|
362
|
+
).toBe(true);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// The model's own unique-together message is the same constant.
|
|
366
|
+
test("matches what the model says about a duplicate", () => {
|
|
367
|
+
const message: string = new IncidentAlert().getUniqueColumnsTogether()[0]!
|
|
368
|
+
.errorMessage;
|
|
369
|
+
|
|
370
|
+
expect(isAlreadyLinkedError(message)).toBe(true);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
/*
|
|
374
|
+
* Two requests that race past the model's check are caught by the unique
|
|
375
|
+
* index, whose translated message is worded differently.
|
|
376
|
+
*/
|
|
377
|
+
test("matches the unique index's own wording", () => {
|
|
378
|
+
expect(
|
|
379
|
+
isAlreadyLinkedError(
|
|
380
|
+
"A Incident Alert with the same Incident Id, Alert Id, Project Id already exists. Please use different values and try again.",
|
|
381
|
+
),
|
|
382
|
+
).toBe(true);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test("does not match other failures", () => {
|
|
386
|
+
expect(isAlreadyLinkedError("")).toBe(false);
|
|
387
|
+
expect(
|
|
388
|
+
isAlreadyLinkedError(
|
|
389
|
+
"The incident to link does not exist in this project, or you do not have access to it.",
|
|
390
|
+
),
|
|
391
|
+
).toBe(false);
|
|
392
|
+
expect(
|
|
393
|
+
isAlreadyLinkedError(
|
|
394
|
+
"A Monitor with this Name already exists. Please use a different value and try again.",
|
|
395
|
+
),
|
|
396
|
+
).toBe(false);
|
|
397
|
+
expect(isAlreadyLinkedError("Alert already linked")).toBe(false);
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
describe("locking a link button", () => {
|
|
402
|
+
const BUTTON: CardButtonSchema = {
|
|
403
|
+
title: "Link Alert",
|
|
404
|
+
buttonStyle: ButtonStyleType.NORMAL,
|
|
405
|
+
icon: IconProp.Link,
|
|
406
|
+
onClick: (): void => {},
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
test("leaves an allowed button alone", () => {
|
|
410
|
+
expect(lockUnlessAllowed(BUTTON, { isAllowed: true })).toBe(BUTTON);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("locks it, saying why, for a known missing permission", () => {
|
|
414
|
+
const onClick: MockFunction = getJestMockFunction();
|
|
415
|
+
const locked: CardButtonSchema | null = lockUnlessAllowed(
|
|
416
|
+
{ ...BUTTON, onClick: onClick as unknown as () => void },
|
|
417
|
+
{
|
|
418
|
+
isAllowed: false,
|
|
419
|
+
disabledReason: "You do not have permission to read this Alert.",
|
|
420
|
+
},
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
expect(locked?.disabled).toBe(true);
|
|
424
|
+
expect(locked?.tooltip).toBe(
|
|
425
|
+
"You do not have permission to read this Alert.",
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
locked!.onClick();
|
|
429
|
+
|
|
430
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test("leaves it alone when the answer is not known", () => {
|
|
434
|
+
expect(lockUnlessAllowed(BUTTON, { isAllowed: false })).toBe(BUTTON);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test("keeps the reason a locked button already has, and a hidden one hidden", () => {
|
|
438
|
+
const alreadyLocked: CardButtonSchema = {
|
|
439
|
+
...BUTTON,
|
|
440
|
+
disabled: true,
|
|
441
|
+
tooltip: "You do not have permission to create this Incident Alert.",
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
expect(
|
|
445
|
+
lockUnlessAllowed(alreadyLocked, {
|
|
446
|
+
isAllowed: false,
|
|
447
|
+
disabledReason: "You do not have permission to read this Alert.",
|
|
448
|
+
}),
|
|
449
|
+
).toBe(alreadyLocked);
|
|
450
|
+
expect(
|
|
451
|
+
lockUnlessAllowed(null, {
|
|
452
|
+
isAllowed: false,
|
|
453
|
+
disabledReason: "You do not have permission to read this Alert.",
|
|
454
|
+
}),
|
|
455
|
+
).toBeNull();
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
});
|