@oneuptime/common 14.0.6 → 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.
Files changed (127) hide show
  1. package/Models/DatabaseModels/InventoryItem.ts +23 -12
  2. package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
  3. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  4. package/Server/Services/AlertService.ts +32 -36
  5. package/Server/Services/IncidentAlertService.ts +609 -2
  6. package/Server/Services/IncidentService.ts +134 -44
  7. package/Server/Services/InventoryItemService.ts +38 -0
  8. package/Server/Services/IoTDeviceCredentialService.ts +13 -4
  9. package/Server/Services/ScheduledMaintenanceService.ts +50 -46
  10. package/Server/Services/UserNotificationRuleService.ts +55 -53
  11. package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
  12. package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
  13. package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
  14. package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
  15. package/Server/Utils/Response.ts +31 -0
  16. package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
  17. package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
  18. package/Server/Utils/Topology/TopologyRequest.ts +463 -0
  19. package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
  20. package/Server/Utils/Topology/TopologySql.ts +1024 -0
  21. package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
  22. package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
  23. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
  24. package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
  25. package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
  26. package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
  27. package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
  28. package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
  29. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
  30. package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
  31. package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
  32. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +292 -1
  33. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
  34. package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
  35. package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
  36. package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
  37. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
  38. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
  39. package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
  40. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
  41. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
  42. package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
  43. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
  44. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
  45. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
  46. package/Tests/Server/Services/DomainService.test.ts +525 -0
  47. package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
  48. package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
  49. package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
  50. package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
  51. package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
  52. package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
  53. package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
  54. package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
  55. package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
  56. package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
  57. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
  58. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
  59. package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
  60. package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
  61. package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
  62. package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
  63. package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
  64. package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
  65. package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
  66. package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
  67. package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
  68. package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
  69. package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
  70. package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
  71. package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
  72. package/Types/Incident/IncidentAlertLink.ts +19 -0
  73. package/Types/Telemetry/InventoryLiveness.ts +26 -0
  74. package/Types/Topology/TopologyApi.ts +428 -0
  75. package/Types/Topology/TopologyTypeRules.ts +191 -0
  76. package/Utils/Incident/IncidentFromAlerts.ts +78 -0
  77. package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
  78. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  79. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
  80. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js.map +1 -0
  81. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  82. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  83. package/build/dist/Server/Services/AlertService.js +21 -33
  84. package/build/dist/Server/Services/AlertService.js.map +1 -1
  85. package/build/dist/Server/Services/IncidentAlertService.js +457 -3
  86. package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
  87. package/build/dist/Server/Services/IncidentService.js +96 -42
  88. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  89. package/build/dist/Server/Services/InventoryItemService.js +31 -0
  90. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  91. package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
  92. package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
  93. package/build/dist/Server/Services/ScheduledMaintenanceService.js +37 -40
  94. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  95. package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
  96. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  97. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
  98. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
  99. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
  100. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
  101. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
  102. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
  103. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
  104. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
  105. package/build/dist/Server/Utils/Response.js +30 -0
  106. package/build/dist/Server/Utils/Response.js.map +1 -1
  107. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
  108. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
  109. package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
  110. package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
  111. package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
  112. package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
  113. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
  114. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
  115. package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
  116. package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
  117. package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
  118. package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
  119. package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
  120. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
  121. package/build/dist/Types/Topology/TopologyApi.js +55 -0
  122. package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
  123. package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
  124. package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
  125. package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
  126. package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
  127. package/package.json +1 -1
@@ -7,7 +7,14 @@ import {
7
7
  jest,
8
8
  test,
9
9
  } from "@jest/globals";
10
- import { cleanup, render, screen, waitFor } from "@testing-library/react";
10
+ import {
11
+ RenderResult,
12
+ act,
13
+ cleanup,
14
+ render,
15
+ screen,
16
+ waitFor,
17
+ } from "@testing-library/react";
11
18
  import * as React from "react";
12
19
  import getJestMockFunction, { MockFunction } from "../../MockType";
13
20
 
@@ -41,6 +48,25 @@ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
41
48
  },
42
49
  };
43
50
  });
51
+ /*
52
+ * The drawer resolves its links from the full row the entity endpoint
53
+ * returns; the fake answers with the node under test.
54
+ */
55
+ const fetchEntityDetailMock: MockFunction = getJestMockFunction();
56
+ jest.mock(
57
+ "../../../../App/FeatureSet/Dashboard/src/Components/Topology/EntityDetailApi",
58
+ () => {
59
+ return {
60
+ __esModule: true,
61
+ ...(jest.requireActual(
62
+ "../../../../App/FeatureSet/Dashboard/src/Components/Topology/EntityDetailApi",
63
+ ) as Record<string, unknown>),
64
+ fetchEntityDetail: (...args: Array<any>) => {
65
+ return fetchEntityDetailMock(...args);
66
+ },
67
+ };
68
+ },
69
+ );
44
70
  jest.mock("../../../UI/Utils/Translation", () => {
45
71
  return {
46
72
  __esModule: true,
@@ -77,8 +103,12 @@ import {
77
103
  resolveTypedRowLink,
78
104
  } from "../../../../App/FeatureSet/Dashboard/src/Components/Inventory/ResolveTypedRowLink";
79
105
  import EntityDetailPanel from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/EntityDetailPanel";
106
+ import {
107
+ EntityDetail,
108
+ EntityDetailData,
109
+ emptyConnectionSection,
110
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/Topology/EntityDetailApi";
80
111
  import InventoryItem from "../../../Models/DatabaseModels/InventoryItem";
81
- import InventoryItemRelationship from "../../../Models/DatabaseModels/InventoryItemRelationship";
82
112
  import StartsWith from "../../../Types/BaseDatabase/StartsWith";
83
113
  import { JSONObject } from "../../../Types/JSON";
84
114
  import ObjectID from "../../../Types/ObjectID";
@@ -1002,23 +1032,78 @@ describe("resolveDatabaseServerLink", () => {
1002
1032
  });
1003
1033
 
1004
1034
  describe("the Service Map detail drawer", () => {
1005
- function renderPanel(entity: InventoryItem): void {
1006
- render(
1035
+ const RANGE_START: Date = new Date("2026-09-26T10:00:00.000Z");
1036
+
1037
+ function detailOf(
1038
+ node: InventoryItem,
1039
+ id: string = "1a1a0000-0000-4000-8000-000000000001",
1040
+ ): EntityDetail {
1041
+ const detail: EntityDetail = {
1042
+ id: id,
1043
+ entityKey: node.entityKey!,
1044
+ entityType: node.entityType!,
1045
+ };
1046
+ if (node.displayName) {
1047
+ detail.displayName = node.displayName;
1048
+ }
1049
+ if (node.identifyingAttributes) {
1050
+ detail.identifyingAttributes = node.identifyingAttributes;
1051
+ }
1052
+ if (node.descriptiveAttributes) {
1053
+ detail.descriptiveAttributes = node.descriptiveAttributes;
1054
+ }
1055
+ return detail;
1056
+ }
1057
+
1058
+ function answerWith(node: InventoryItem, id?: string): EntityDetailData {
1059
+ return {
1060
+ rangeStart: RANGE_START,
1061
+ entity: detailOf(node, id),
1062
+ sections: {
1063
+ calls: emptyConnectionSection(),
1064
+ calledBy: emptyConnectionSection(),
1065
+ runsOn: emptyConnectionSection(),
1066
+ related: emptyConnectionSection(),
1067
+ },
1068
+ isScanLimited: false,
1069
+ };
1070
+ }
1071
+
1072
+ function endpointLookups(): Array<GetListArgs> {
1073
+ return calls().filter((args: GetListArgs): boolean => {
1074
+ return args.modelType.name === "DatabaseServerEndpoint";
1075
+ });
1076
+ }
1077
+
1078
+ function panelFor(node: InventoryItem, rangeStart: Date): React.ReactElement {
1079
+ /* A map hands over what it drew: key, type and name — no attributes. */
1080
+ return (
1007
1081
  <EntityDetailPanel
1008
- entity={entity}
1009
- relationships={[] as Array<InventoryItemRelationship>}
1010
- entityByKey={new Map<string, InventoryItem>([["db-node", entity]])}
1082
+ entity={{
1083
+ entityKey: node.entityKey!,
1084
+ entityType: node.entityType,
1085
+ displayName: node.displayName,
1086
+ }}
1087
+ rangeStart={rangeStart}
1011
1088
  metricsWindowSeconds={60}
1012
1089
  onClose={() => {
1013
1090
  return undefined;
1014
1091
  }}
1015
- onFocus={() => {
1016
- return undefined;
1017
- }}
1018
- />,
1092
+ />
1019
1093
  );
1020
1094
  }
1021
1095
 
1096
+ function renderPanel(node: InventoryItem): RenderResult {
1097
+ fetchEntityDetailMock.mockImplementation(async () => {
1098
+ return answerWith(node);
1099
+ });
1100
+ return render(panelFor(node, RANGE_START));
1101
+ }
1102
+
1103
+ beforeEach(() => {
1104
+ fetchEntityDetailMock.mockReset();
1105
+ });
1106
+
1022
1107
  test("offers 'Open database' for a database node a DatabaseServer owns", async () => {
1023
1108
  useTable([{ owner: DATABASE_ID, endpoint: "db.prod.example.com:5432" }]);
1024
1109
 
@@ -1074,12 +1159,113 @@ describe("the Service Map detail drawer", () => {
1074
1159
  entityType: EntityType.Container,
1075
1160
  } as InventoryItem);
1076
1161
 
1162
+ await screen.findByText("Inventory details");
1077
1163
  expect(screen.queryByText("Open database")).not.toBeInTheDocument();
1078
- const endpointLookups: Array<GetListArgs> = calls().filter(
1079
- (args: GetListArgs): boolean => {
1080
- return args.modelType.name === "DatabaseServerEndpoint";
1164
+ expect(endpointLookups()).toHaveLength(0);
1165
+ });
1166
+
1167
+ test("resolves from the full row: the preview alone names no endpoint", async () => {
1168
+ useTable([{ owner: DATABASE_ID, endpoint: "db.prod.example.com:5432" }]);
1169
+ const node: InventoryItem = databaseNode({
1170
+ "db.system.name": "postgresql",
1171
+ "server.address": "db.prod.example.com",
1172
+ });
1173
+ let answer: (value: EntityDetailData) => void = () => {
1174
+ return undefined;
1175
+ };
1176
+ fetchEntityDetailMock.mockImplementation(() => {
1177
+ return new Promise<EntityDetailData>(
1178
+ (resolve: (value: EntityDetailData) => void) => {
1179
+ answer = resolve;
1180
+ },
1181
+ );
1182
+ });
1183
+
1184
+ render(panelFor(node, RANGE_START));
1185
+ /* Only the preview so far: a database type, but no address to resolve. */
1186
+ expect(endpointLookups()).toHaveLength(0);
1187
+
1188
+ await act(async () => {
1189
+ answer(answerWith(node));
1190
+ });
1191
+
1192
+ expect(await screen.findByText("Open database")).toBeInTheDocument();
1193
+ expect(endpointLookups()[0]!.query["endpoint"]).toBe(
1194
+ "db.prod.example.com:5432",
1195
+ );
1196
+ });
1197
+
1198
+ test("looks the database up once per row, not once per range", async () => {
1199
+ useTable([{ owner: DATABASE_ID, endpoint: "db.prod.example.com:5432" }]);
1200
+ const node: InventoryItem = databaseNode({
1201
+ "db.system.name": "postgresql",
1202
+ "server.address": "db.prod.example.com",
1203
+ });
1204
+
1205
+ const view: RenderResult = renderPanel(node);
1206
+ await screen.findByText("Open database");
1207
+ const lookups: number = endpointLookups().length;
1208
+ expect(lookups).toBeGreaterThan(0);
1209
+
1210
+ view.rerender(panelFor(node, new Date("2026-09-26T11:00:00.000Z")));
1211
+ await waitFor(() => {
1212
+ expect(fetchEntityDetailMock).toHaveBeenCalledTimes(2);
1213
+ });
1214
+ expect(await screen.findByText("Open database")).toBeInTheDocument();
1215
+
1216
+ expect(endpointLookups()).toHaveLength(lookups);
1217
+ });
1218
+
1219
+ test("moving the open drawer to another database never shows the first one's link", async () => {
1220
+ useTable([{ owner: DATABASE_ID, endpoint: "db.prod.example.com:5432" }]);
1221
+ const orders: InventoryItem = databaseNode({
1222
+ "db.system.name": "postgresql",
1223
+ "server.address": "db.prod.example.com",
1224
+ });
1225
+ /* Another database on the map, which no DatabaseServer owns. */
1226
+ const reports: InventoryItem = {
1227
+ ...databaseNode({
1228
+ "db.system.name": "postgresql",
1229
+ "server.address": "reports.example.com",
1230
+ }),
1231
+ entityKey: "reports-node",
1232
+ displayName: "reports",
1233
+ } as unknown as InventoryItem;
1234
+ let answerReports: (value: EntityDetailData) => void = () => {
1235
+ return undefined;
1236
+ };
1237
+ fetchEntityDetailMock.mockImplementation(
1238
+ (target: { entityKey: string }) => {
1239
+ if (target.entityKey !== "reports-node") {
1240
+ return Promise.resolve(answerWith(orders));
1241
+ }
1242
+ return new Promise<EntityDetailData>(
1243
+ (resolve: (value: EntityDetailData) => void) => {
1244
+ answerReports = resolve;
1245
+ },
1246
+ );
1081
1247
  },
1082
1248
  );
1083
- expect(endpointLookups).toHaveLength(0);
1249
+
1250
+ const view: RenderResult = render(panelFor(orders, RANGE_START));
1251
+ expect(
1252
+ (await screen.findByText("Open database")).closest("a"),
1253
+ ).toHaveAttribute("href", databasePage(DATABASE_ID));
1254
+
1255
+ /* The same drawer, now for the other database: no remount. */
1256
+ view.rerender(panelFor(reports, RANGE_START));
1257
+ expect(screen.queryByText("Open database")).not.toBeInTheDocument();
1258
+
1259
+ const lookupsBefore: number = endpointLookups().length;
1260
+ await act(async () => {
1261
+ answerReports(
1262
+ answerWith(reports, "1a1a0000-0000-4000-8000-000000000002"),
1263
+ );
1264
+ });
1265
+ await waitFor(() => {
1266
+ expect(endpointLookups().length).toBeGreaterThan(lookupsBefore);
1267
+ });
1268
+ await screen.findByText("Inventory details");
1269
+ expect(screen.queryByText("Open database")).not.toBeInTheDocument();
1084
1270
  });
1085
1271
  });