@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,420 @@
|
|
|
1
|
+
import DatabaseConfig from "../../../Server/DatabaseConfig";
|
|
2
|
+
import AlertFeedService from "../../../Server/Services/AlertFeedService";
|
|
3
|
+
import AlertService from "../../../Server/Services/AlertService";
|
|
4
|
+
import IncidentFeedService from "../../../Server/Services/IncidentFeedService";
|
|
5
|
+
import IncidentService from "../../../Server/Services/IncidentService";
|
|
6
|
+
import ScheduledMaintenanceFeedService from "../../../Server/Services/ScheduledMaintenanceFeedService";
|
|
7
|
+
import ScheduledMaintenanceService from "../../../Server/Services/ScheduledMaintenanceService";
|
|
8
|
+
import AlertWorkspaceMessages from "../../../Server/Utils/Workspace/WorkspaceMessages/Alert";
|
|
9
|
+
import IncidentWorkspaceMessages from "../../../Server/Utils/Workspace/WorkspaceMessages/Incident";
|
|
10
|
+
import ScheduledMaintenanceWorkspaceMessages from "../../../Server/Utils/Workspace/WorkspaceMessages/ScheduledMaintenance";
|
|
11
|
+
import Alert from "../../../Models/DatabaseModels/Alert";
|
|
12
|
+
import Incident from "../../../Models/DatabaseModels/Incident";
|
|
13
|
+
import ScheduledMaintenance from "../../../Models/DatabaseModels/ScheduledMaintenance";
|
|
14
|
+
import URL from "../../../Types/API/URL";
|
|
15
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
16
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
17
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* The "🌎 Resources Affected" section of an alert's, incident's or scheduled
|
|
21
|
+
* maintenance event's feed items named monitors (and SLOs) only, while the
|
|
22
|
+
* dashboard's Affected Resources cards beside it list hosts, clusters,
|
|
23
|
+
* container hosts, databases, services and so on too. An incident attached
|
|
24
|
+
* to a host and a Kubernetes cluster had no section at all. These pin the
|
|
25
|
+
* section to the card: every relation, in the card's order, held to the
|
|
26
|
+
* record's own project, with the monitor bullet unchanged.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const DASHBOARD: string = "https://oneuptime.example/dashboard";
|
|
30
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
31
|
+
"0193c0de-eeee-4aaa-8bbb-000000000001",
|
|
32
|
+
);
|
|
33
|
+
const OTHER_PROJECT_ID: ObjectID = new ObjectID(
|
|
34
|
+
"0193c0de-eeee-4aaa-8bbb-000000000009",
|
|
35
|
+
);
|
|
36
|
+
const RECORD_ID: ObjectID = new ObjectID(
|
|
37
|
+
"0193c0de-eeee-4aaa-8bbb-000000000002",
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const MONITOR_ID: string = "0193c0de-eeee-4aaa-8bbb-0000000000a1";
|
|
41
|
+
const HOST_ID: string = "0193c0de-eeee-4aaa-8bbb-0000000000b1";
|
|
42
|
+
const FOREIGN_HOST_ID: string = "0193c0de-eeee-4aaa-8bbb-0000000000b9";
|
|
43
|
+
const CLUSTER_ID: string = "0193c0de-eeee-4aaa-8bbb-0000000000d1";
|
|
44
|
+
const DATABASE_ID: string = "0193c0de-eeee-4aaa-8bbb-0000000000d2";
|
|
45
|
+
const SERVICE_ID: string = "0193c0de-eeee-4aaa-8bbb-0000000000e1";
|
|
46
|
+
const SLO_ID: string = "0193c0de-eeee-4aaa-8bbb-0000000000c1";
|
|
47
|
+
const SITE_ID: string = "0193c0de-eeee-4aaa-8bbb-0000000000f1";
|
|
48
|
+
|
|
49
|
+
const HEADER: string = "🌎 **Resources Affected**:\n";
|
|
50
|
+
|
|
51
|
+
function link(route: string, id: string): string {
|
|
52
|
+
return `${DASHBOARD}/${PROJECT_ID.toString()}/${route}/${id}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function related(
|
|
56
|
+
id: string,
|
|
57
|
+
name: string,
|
|
58
|
+
projectId: ObjectID = PROJECT_ID,
|
|
59
|
+
): JSONObject {
|
|
60
|
+
return { _id: id, name, projectId: projectId as unknown as JSONObject };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* Answers each per-relation read from `relations`, as the database would:
|
|
65
|
+
* one row for the record, carrying only the relation that was selected.
|
|
66
|
+
*/
|
|
67
|
+
function answerRelationReads(
|
|
68
|
+
service: { findAllBy: unknown },
|
|
69
|
+
relations: JSONObject,
|
|
70
|
+
): jest.SpyInstance {
|
|
71
|
+
return jest
|
|
72
|
+
.spyOn(service as typeof IncidentService, "findAllBy")
|
|
73
|
+
.mockImplementation((async (findAllBy: {
|
|
74
|
+
select: JSONObject;
|
|
75
|
+
}): Promise<Array<JSONObject>> => {
|
|
76
|
+
const row: JSONObject = {
|
|
77
|
+
_id: RECORD_ID.toString(),
|
|
78
|
+
projectId: PROJECT_ID as unknown as JSONObject,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
for (const column of Object.keys(findAllBy.select)) {
|
|
82
|
+
if (column !== "_id" && column !== "projectId") {
|
|
83
|
+
row[column] = relations[column] || null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return [row];
|
|
88
|
+
}) as never);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The bullets under the header, or null when there is no section.
|
|
92
|
+
function sectionLines(markdown: string, header: string): Array<string> | null {
|
|
93
|
+
const start: number = markdown.indexOf(header);
|
|
94
|
+
|
|
95
|
+
if (start === -1) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const lines: Array<string> = markdown
|
|
100
|
+
.slice(start + header.length)
|
|
101
|
+
.replace(/^\n+/, "")
|
|
102
|
+
.split("\n");
|
|
103
|
+
const end: number = lines.indexOf("");
|
|
104
|
+
|
|
105
|
+
return end === -1 ? lines : lines.slice(0, end);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function postedMarkdown(spy: jest.SpyInstance): string {
|
|
109
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
110
|
+
|
|
111
|
+
return (spy.mock.calls[0]![0] as { feedInfoInMarkdown: string })
|
|
112
|
+
.feedInfoInMarkdown;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
beforeEach(() => {
|
|
116
|
+
jest
|
|
117
|
+
.spyOn(DatabaseConfig, "getDashboardUrl")
|
|
118
|
+
.mockImplementation(async (): Promise<URL> => {
|
|
119
|
+
return URL.fromString(DASHBOARD);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
jest
|
|
123
|
+
.spyOn(IncidentWorkspaceMessages, "getIncidentCreateMessageBlocks")
|
|
124
|
+
.mockResolvedValue([] as never);
|
|
125
|
+
jest
|
|
126
|
+
.spyOn(AlertWorkspaceMessages, "getAlertCreateMessageBlocks")
|
|
127
|
+
.mockResolvedValue([] as never);
|
|
128
|
+
jest
|
|
129
|
+
.spyOn(
|
|
130
|
+
ScheduledMaintenanceWorkspaceMessages,
|
|
131
|
+
"getScheduledMaintenanceCreateMessageBlocks",
|
|
132
|
+
)
|
|
133
|
+
.mockResolvedValue([] as never);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
afterEach(() => {
|
|
137
|
+
jest.restoreAllMocks();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("incident created feed item", () => {
|
|
141
|
+
type CreateIncidentFeedAsyncFunction = (incident: Incident) => Promise<void>;
|
|
142
|
+
|
|
143
|
+
let feedItem: jest.SpyInstance;
|
|
144
|
+
|
|
145
|
+
beforeEach(() => {
|
|
146
|
+
feedItem = jest
|
|
147
|
+
.spyOn(IncidentFeedService, "createIncidentFeedItem")
|
|
148
|
+
.mockResolvedValue(undefined as never);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
async function createFeed(relations: JSONObject): Promise<string> {
|
|
152
|
+
answerRelationReads(IncidentService, relations);
|
|
153
|
+
|
|
154
|
+
const incident: Incident = new Incident();
|
|
155
|
+
incident._id = RECORD_ID.toString();
|
|
156
|
+
incident.projectId = PROJECT_ID;
|
|
157
|
+
incident.incidentNumber = 3;
|
|
158
|
+
incident.title = "Checkout is down";
|
|
159
|
+
|
|
160
|
+
await (
|
|
161
|
+
IncidentService as unknown as {
|
|
162
|
+
createIncidentFeedAsync: CreateIncidentFeedAsyncFunction;
|
|
163
|
+
}
|
|
164
|
+
).createIncidentFeedAsync(incident);
|
|
165
|
+
|
|
166
|
+
return postedMarkdown(feedItem);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
test("names every resource the Affected Resources card lists, in its order", async () => {
|
|
170
|
+
const markdown: string = await createFeed({
|
|
171
|
+
serviceLevelObjectives: [related(SLO_ID, "Checkout availability")],
|
|
172
|
+
services: [related(SERVICE_ID, "checkout")],
|
|
173
|
+
databaseServers: [related(DATABASE_ID, "orders")],
|
|
174
|
+
kubernetesClusters: [related(CLUSTER_ID, "prod")],
|
|
175
|
+
hosts: [related(HOST_ID, "web")],
|
|
176
|
+
monitors: [related(MONITOR_ID, "checkout-web")],
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(sectionLines(markdown, HEADER)).toEqual([
|
|
180
|
+
`- [checkout-web](${link("monitors", MONITOR_ID)})`,
|
|
181
|
+
`- [Host web](${link("host", HOST_ID)})`,
|
|
182
|
+
`- [Kubernetes Cluster prod](${link("kubernetes", CLUSTER_ID)})`,
|
|
183
|
+
`- [Database orders](${link("databases", DATABASE_ID)})`,
|
|
184
|
+
`- [Service checkout](${link("service", SERVICE_ID)})`,
|
|
185
|
+
`- [SLO Checkout availability](${link("slos", SLO_ID)})`,
|
|
186
|
+
]);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("an incident on a host alone - no monitors - still has a section", async () => {
|
|
190
|
+
const markdown: string = await createFeed({
|
|
191
|
+
hosts: [related(HOST_ID, "web")],
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(sectionLines(markdown, HEADER)).toEqual([
|
|
195
|
+
`- [Host web](${link("host", HOST_ID)})`,
|
|
196
|
+
]);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("another project's host, linked before the write guard, is not named", async () => {
|
|
200
|
+
const markdown: string = await createFeed({
|
|
201
|
+
hosts: [
|
|
202
|
+
related(
|
|
203
|
+
FOREIGN_HOST_ID,
|
|
204
|
+
"payments-db-of-another-project",
|
|
205
|
+
OTHER_PROJECT_ID,
|
|
206
|
+
),
|
|
207
|
+
related(HOST_ID, "web"),
|
|
208
|
+
],
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
expect(sectionLines(markdown, HEADER)).toEqual([
|
|
212
|
+
`- [Host web](${link("host", HOST_ID)})`,
|
|
213
|
+
]);
|
|
214
|
+
expect(markdown).not.toContain("payments-db-of-another-project");
|
|
215
|
+
expect(markdown).not.toContain(FOREIGN_HOST_ID);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("a host name an agent reported cannot inject markdown", async () => {
|
|
219
|
+
const markdown: string = await createFeed({
|
|
220
|
+
hosts: [
|
|
221
|
+
related(
|
|
222
|
+
HOST_ID,
|
|
223
|
+
"web](https://evil.example) ",
|
|
224
|
+
),
|
|
225
|
+
],
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
expect(markdown).not.toContain("](https://evil.example)");
|
|
229
|
+
expect(markdown).not.toContain(";
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("an incident linked to nothing has no section", async () => {
|
|
233
|
+
expect(sectionLines(await createFeed({}), HEADER)).toBeNull();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe("alert created feed item", () => {
|
|
238
|
+
type CreateAlertFeedAsyncFunction = (alertId: ObjectID) => Promise<void>;
|
|
239
|
+
|
|
240
|
+
let feedItem: jest.SpyInstance;
|
|
241
|
+
|
|
242
|
+
beforeEach(() => {
|
|
243
|
+
feedItem = jest
|
|
244
|
+
.spyOn(AlertFeedService, "createAlertFeedItem")
|
|
245
|
+
.mockResolvedValue(undefined as never);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
async function createFeed(relations: JSONObject): Promise<string> {
|
|
249
|
+
answerRelationReads(AlertService, relations);
|
|
250
|
+
|
|
251
|
+
const alert: Alert = new Alert();
|
|
252
|
+
alert._id = RECORD_ID.toString();
|
|
253
|
+
alert.projectId = PROJECT_ID;
|
|
254
|
+
alert.alertNumber = 4;
|
|
255
|
+
alert.title = "Checkout is slow";
|
|
256
|
+
|
|
257
|
+
jest.spyOn(AlertService, "findOneById").mockResolvedValue(alert as never);
|
|
258
|
+
|
|
259
|
+
await (
|
|
260
|
+
AlertService as unknown as {
|
|
261
|
+
createAlertFeedAsync: CreateAlertFeedAsyncFunction;
|
|
262
|
+
}
|
|
263
|
+
).createAlertFeedAsync(RECORD_ID);
|
|
264
|
+
|
|
265
|
+
return postedMarkdown(feedItem);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
test("names the monitor first, then everything else the card lists", async () => {
|
|
269
|
+
const markdown: string = await createFeed({
|
|
270
|
+
monitor: related(MONITOR_ID, "checkout-web"),
|
|
271
|
+
kubernetesClusters: [related(CLUSTER_ID, "prod")],
|
|
272
|
+
services: [related(SERVICE_ID, "checkout")],
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
expect(sectionLines(markdown, HEADER)).toEqual([
|
|
276
|
+
`- [checkout-web](${link("monitors", MONITOR_ID)})`,
|
|
277
|
+
`- [Kubernetes Cluster prod](${link("kubernetes", CLUSTER_ID)})`,
|
|
278
|
+
`- [Service checkout](${link("service", SERVICE_ID)})`,
|
|
279
|
+
]);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("an SLO burn-rate alert on a service names both", async () => {
|
|
283
|
+
const markdown: string = await createFeed({
|
|
284
|
+
monitor: null,
|
|
285
|
+
services: [related(SERVICE_ID, "checkout")],
|
|
286
|
+
serviceLevelObjectives: [related(SLO_ID, "Checkout availability")],
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
expect(sectionLines(markdown, HEADER)).toEqual([
|
|
290
|
+
`- [Service checkout](${link("service", SERVICE_ID)})`,
|
|
291
|
+
`- [SLO Checkout availability](${link("slos", SLO_ID)})`,
|
|
292
|
+
]);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test("another project's monitor is not named", async () => {
|
|
296
|
+
const markdown: string = await createFeed({
|
|
297
|
+
monitor: related(MONITOR_ID, "someone-elses-monitor", OTHER_PROJECT_ID),
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
expect(sectionLines(markdown, HEADER)).toBeNull();
|
|
301
|
+
expect(markdown).not.toContain("someone-elses-monitor");
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe("scheduled maintenance feed items", () => {
|
|
306
|
+
let feedItem: jest.SpyInstance;
|
|
307
|
+
|
|
308
|
+
beforeEach(() => {
|
|
309
|
+
feedItem = jest
|
|
310
|
+
.spyOn(
|
|
311
|
+
ScheduledMaintenanceFeedService,
|
|
312
|
+
"createScheduledMaintenanceFeedItem",
|
|
313
|
+
)
|
|
314
|
+
.mockResolvedValue(undefined as never);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("the created item names monitors and network sites", async () => {
|
|
318
|
+
type CreateFeedFunction = (
|
|
319
|
+
scheduledMaintenance: ScheduledMaintenance,
|
|
320
|
+
) => Promise<void>;
|
|
321
|
+
|
|
322
|
+
answerRelationReads(ScheduledMaintenanceService, {
|
|
323
|
+
networkSites: [related(SITE_ID, "London DC")],
|
|
324
|
+
monitors: [related(MONITOR_ID, "checkout-web")],
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const event: ScheduledMaintenance = new ScheduledMaintenance();
|
|
328
|
+
event._id = RECORD_ID.toString();
|
|
329
|
+
event.projectId = PROJECT_ID;
|
|
330
|
+
event.scheduledMaintenanceNumber = 5;
|
|
331
|
+
event.title = "Database upgrade";
|
|
332
|
+
|
|
333
|
+
await (
|
|
334
|
+
ScheduledMaintenanceService as unknown as {
|
|
335
|
+
createScheduledMaintenanceFeedAsync: CreateFeedFunction;
|
|
336
|
+
}
|
|
337
|
+
).createScheduledMaintenanceFeedAsync(event);
|
|
338
|
+
|
|
339
|
+
expect(sectionLines(postedMarkdown(feedItem), HEADER)).toEqual([
|
|
340
|
+
`- [checkout-web](${link("monitors", MONITOR_ID)})`,
|
|
341
|
+
`- [Network Site London DC](${link("network-sites/view", SITE_ID)})`,
|
|
342
|
+
]);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
describe("the updated item", () => {
|
|
346
|
+
type OnUpdateSuccessFunction = (
|
|
347
|
+
onUpdate: JSONObject,
|
|
348
|
+
updatedItemIds: Array<ObjectID>,
|
|
349
|
+
) => Promise<JSONObject>;
|
|
350
|
+
|
|
351
|
+
const UPDATED_HEADER: string = "**Resources Affected**:\n";
|
|
352
|
+
|
|
353
|
+
async function update(data: JSONObject): Promise<void> {
|
|
354
|
+
await (
|
|
355
|
+
ScheduledMaintenanceService as unknown as {
|
|
356
|
+
onUpdateSuccess: OnUpdateSuccessFunction;
|
|
357
|
+
}
|
|
358
|
+
).onUpdateSuccess(
|
|
359
|
+
{
|
|
360
|
+
updateBy: {
|
|
361
|
+
query: { _id: RECORD_ID.toString() },
|
|
362
|
+
data: data,
|
|
363
|
+
props: { tenantId: PROJECT_ID, userId: undefined },
|
|
364
|
+
},
|
|
365
|
+
carryForward: null,
|
|
366
|
+
},
|
|
367
|
+
[RECORD_ID],
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
test("editing the hosts alone lists what the event now affects", async () => {
|
|
372
|
+
const reads: jest.SpyInstance = answerRelationReads(
|
|
373
|
+
ScheduledMaintenanceService,
|
|
374
|
+
{
|
|
375
|
+
monitors: [related(MONITOR_ID, "checkout-web")],
|
|
376
|
+
hosts: [related(HOST_ID, "web")],
|
|
377
|
+
},
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
await update({ hosts: [{ _id: HOST_ID }] });
|
|
381
|
+
|
|
382
|
+
expect(sectionLines(postedMarkdown(feedItem), UPDATED_HEADER)).toEqual([
|
|
383
|
+
`- [checkout-web](${link("monitors", MONITOR_ID)})`,
|
|
384
|
+
`- [Host web](${link("host", HOST_ID)})`,
|
|
385
|
+
]);
|
|
386
|
+
|
|
387
|
+
// Read back as root, inside the tenant's project.
|
|
388
|
+
for (const call of reads.mock.calls) {
|
|
389
|
+
const args: { query: JSONObject; props: JSONObject } = call[0];
|
|
390
|
+
|
|
391
|
+
expect(args.query["projectId"]).toBe(PROJECT_ID);
|
|
392
|
+
expect(args.props).toEqual({ isRoot: true });
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("another project's monitor put in the payload is not named", async () => {
|
|
397
|
+
answerRelationReads(ScheduledMaintenanceService, {
|
|
398
|
+
monitors: [
|
|
399
|
+
related(MONITOR_ID, "someone-elses-monitor", OTHER_PROJECT_ID),
|
|
400
|
+
],
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
await update({ monitors: [{ _id: MONITOR_ID }] });
|
|
404
|
+
|
|
405
|
+
expect(feedItem).not.toHaveBeenCalled();
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test("clearing the lists, as before, writes no section and reads nothing", async () => {
|
|
409
|
+
const reads: jest.SpyInstance = answerRelationReads(
|
|
410
|
+
ScheduledMaintenanceService,
|
|
411
|
+
{},
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
await update({ monitors: [], hosts: [] });
|
|
415
|
+
|
|
416
|
+
expect(reads).not.toHaveBeenCalled();
|
|
417
|
+
expect(feedItem).not.toHaveBeenCalled();
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
});
|
|
@@ -203,6 +203,18 @@ describe("MetricService aggregate statement generation", () => {
|
|
|
203
203
|
expect(query).not.toContain("__attr_grp_0");
|
|
204
204
|
expect(query).toContain("GROUP BY time");
|
|
205
205
|
});
|
|
206
|
+
|
|
207
|
+
it("computes P75 - the Core Web Vitals level - over the histogram buckets", () => {
|
|
208
|
+
const query: string = getQuery(
|
|
209
|
+
buildAggregateBy({
|
|
210
|
+
aggregationType: AggregationType.P75,
|
|
211
|
+
groupByAttributeKeys: ["app.route"],
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
expect(query).toContain("quantileExactWeighted(0.75)");
|
|
216
|
+
expect(query).toContain("GROUP BY time, __attr_grp_0");
|
|
217
|
+
});
|
|
206
218
|
});
|
|
207
219
|
|
|
208
220
|
/*
|