@oneuptime/common 12.0.34 → 13.0.0

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 (259) hide show
  1. package/Models/DatabaseModels/GlobalConfig.ts +12 -12
  2. package/Models/DatabaseModels/InventoryItem.ts +44 -1
  3. package/Models/DatabaseModels/NetworkDevice.ts +126 -0
  4. package/Server/EnvironmentConfig.ts +47 -16
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  7. package/Server/Infrastructure/Redis.ts +8 -8
  8. package/Server/Infrastructure/Status.ts +10 -4
  9. package/Server/Services/DatabaseService.ts +65 -13
  10. package/Server/Services/InventoryItemService.ts +31 -1
  11. package/Server/Services/MonitorTemplateService.ts +140 -19
  12. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +349 -41
  13. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
  14. package/Server/Services/NetworkDeviceService.ts +69 -0
  15. package/Server/Services/UserNotificationRuleService.ts +76 -25
  16. package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
  17. package/Server/Types/Database/QueryUtil.ts +56 -0
  18. package/Server/Types/Markdown.ts +93 -1
  19. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
  20. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +10 -1
  21. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
  22. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +82 -35
  23. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
  24. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +202 -37
  25. package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
  26. package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
  27. package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
  28. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
  29. package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
  30. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
  31. package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
  32. package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
  33. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
  34. package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
  35. package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
  36. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
  37. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
  38. package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
  39. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
  40. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
  41. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
  42. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
  43. package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
  44. package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
  45. package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
  46. package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
  47. package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
  48. package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
  49. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
  50. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
  51. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
  52. package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
  53. package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
  54. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
  55. package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
  56. package/Tests/Models/InventoryItemStatus.test.ts +147 -0
  57. package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
  58. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -0
  59. package/Tests/Server/EnvironmentConfigValkey.test.ts +227 -0
  60. package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
  61. package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +62 -23
  62. package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
  63. package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
  64. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
  65. package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
  66. package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
  67. package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
  68. package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
  69. package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
  70. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1133 -81
  71. package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
  72. package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
  73. package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
  74. package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
  75. package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
  76. package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
  77. package/Tests/Server/Types/Markdown.test.ts +194 -1
  78. package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
  79. package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -0
  80. package/Tests/Server/Utils/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
  81. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
  82. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1 -1
  83. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
  84. package/Tests/Server/Utils/EventLoop.test.ts +359 -0
  85. package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
  86. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
  87. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +13 -7
  88. package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
  89. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
  90. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
  91. package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
  92. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +224 -10
  93. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
  94. package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
  95. package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
  96. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
  97. package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
  98. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
  99. package/Tests/Server/Utils/Telemetry/ContextSpanProcessor.test.ts +281 -0
  100. package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
  101. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
  102. package/Tests/Types/Database/IncludesAnyOfGroups.test.ts +106 -0
  103. package/Tests/Types/JSONFunctions.test.ts +40 -0
  104. package/Tests/Types/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
  105. package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
  106. package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
  107. package/Tests/Types/Telemetry/InventoryLiveness.test.ts +99 -0
  108. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +160 -0
  109. package/Tests/UI/ReactRouterSingletonBuild.test.ts +133 -0
  110. package/Tests/UI/Rum/ReplayHeader.test.tsx +10 -2
  111. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +514 -0
  112. package/Tests/UI/Rum/ReplayTimeFormat.test.ts +94 -0
  113. package/Tests/UI/Rum/ReplayUi.test.tsx +559 -0
  114. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  115. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
  116. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
  117. package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
  118. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
  119. package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
  120. package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
  121. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
  122. package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
  123. package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
  124. package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
  125. package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
  126. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  127. package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
  128. package/Types/Dashboard/DashboardVariable.ts +8 -0
  129. package/Types/JSON.ts +4 -0
  130. package/Types/JSONFunctions.ts +16 -0
  131. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
  132. package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
  133. package/Types/SerializableObjectDictionary.ts +4 -0
  134. package/Types/Telemetry/InventoryLiveness.ts +56 -0
  135. package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
  136. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +46 -21
  137. package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -0
  138. package/UI/esbuild-config.js +6 -0
  139. package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
  140. package/Utils/Dashboard/LabelVariable.ts +152 -0
  141. package/Utils/Dashboard/VariableUrlState.ts +27 -5
  142. package/Utils/MetricUnitUtil.ts +34 -0
  143. package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
  144. package/Utils/Monitor/MetricValueFormatter.ts +342 -0
  145. package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
  146. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
  147. package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
  148. package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
  149. package/build/dist/Models/DatabaseModels/GlobalConfig.js +12 -12
  150. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  151. package/build/dist/Models/DatabaseModels/InventoryItem.js +34 -1
  152. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  153. package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
  154. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  155. package/build/dist/Server/EnvironmentConfig.js +44 -12
  156. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  157. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
  158. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js.map +1 -0
  159. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  160. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  161. package/build/dist/Server/Infrastructure/Redis.js +8 -8
  162. package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
  163. package/build/dist/Server/Infrastructure/Status.js +10 -4
  164. package/build/dist/Server/Infrastructure/Status.js.map +1 -1
  165. package/build/dist/Server/Services/DatabaseService.js +58 -13
  166. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  167. package/build/dist/Server/Services/InventoryItemService.js +28 -1
  168. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  169. package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
  170. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  171. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +270 -37
  172. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  173. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
  174. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  175. package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
  176. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  177. package/build/dist/Server/Services/UserNotificationRuleService.js +60 -25
  178. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  179. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +29 -4
  180. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  181. package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
  182. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  183. package/build/dist/Server/Types/Markdown.js +75 -0
  184. package/build/dist/Server/Types/Markdown.js.map +1 -1
  185. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
  186. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  187. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +10 -1
  188. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  189. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
  190. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
  191. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +58 -35
  192. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  193. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +29 -5
  194. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  195. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +166 -35
  196. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  197. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
  198. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
  199. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
  200. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
  201. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
  202. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
  203. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
  204. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  205. package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
  206. package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
  207. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
  208. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  209. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +9 -1
  210. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  211. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +70 -13
  212. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  213. package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js +54 -0
  214. package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
  215. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  216. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  217. package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
  218. package/build/dist/Types/Dashboard/DashboardVariable.js.map +1 -1
  219. package/build/dist/Types/JSON.js +1 -0
  220. package/build/dist/Types/JSON.js.map +1 -1
  221. package/build/dist/Types/JSONFunctions.js +13 -0
  222. package/build/dist/Types/JSONFunctions.js.map +1 -1
  223. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
  224. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
  225. package/build/dist/Types/SerializableObjectDictionary.js +4 -0
  226. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  227. package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
  228. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -0
  229. package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
  230. package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js.map +1 -1
  231. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +9 -2
  232. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  233. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +137 -0
  234. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
  235. package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
  236. package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
  237. package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
  238. package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
  239. package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
  240. package/build/dist/Utils/Dashboard/VariableUrlState.js.map +1 -1
  241. package/build/dist/Utils/MetricUnitUtil.js +26 -0
  242. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  243. package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
  244. package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
  245. package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
  246. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
  247. package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
  248. package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
  249. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
  250. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  251. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
  252. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  253. package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
  254. package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
  255. package/jest.config.json +2 -0
  256. package/package.json +1 -1
  257. package/test-setup.sh +9 -9
  258. package/tsconfig.json +51 -11
  259. package/Tests/App/Dashboard/NotificationEmailPreferences.test.tsx +0 -399
@@ -0,0 +1,99 @@
1
+ import EntitySource from "../../../Types/Telemetry/EntitySource";
2
+ import {
3
+ InventoryLiveness,
4
+ INVENTORY_LIVE_WINDOW_MINUTES,
5
+ INVENTORY_STALE_AFTER_MINUTES,
6
+ getInventoryLivenessState,
7
+ } from "../../../Types/Telemetry/InventoryLiveness";
8
+
9
+ const NOW: Date = new Date("2026-09-07T12:00:00.000Z");
10
+
11
+ describe("inventory heartbeat classification", () => {
12
+ test.each([
13
+ [-60_000, InventoryLiveness.Live],
14
+ [0, InventoryLiveness.Live],
15
+ [30 * 60_000, InventoryLiveness.Live],
16
+ [31 * 60_000 - 1, InventoryLiveness.Live],
17
+ [31 * 60_000, InventoryLiveness.Recent],
18
+ [31 * 60_000 + 1, InventoryLiveness.Recent],
19
+ [1440 * 60_000, InventoryLiveness.Recent],
20
+ [1441 * 60_000 - 1, InventoryLiveness.Recent],
21
+ [1441 * 60_000, InventoryLiveness.Stale],
22
+ [1441 * 60_000 + 1, InventoryLiveness.Stale],
23
+ [365 * 24 * 60 * 60_000, InventoryLiveness.Stale],
24
+ ])(
25
+ "an age of %s ms has status %s",
26
+ (age: number, status: InventoryLiveness) => {
27
+ expect(
28
+ getInventoryLivenessState({
29
+ source: EntitySource.Discovered,
30
+ lastSeenAt: new Date(NOW.getTime() - age),
31
+ now: NOW,
32
+ }),
33
+ ).toBe(status);
34
+ },
35
+ );
36
+
37
+ test.each([undefined, null, "not-a-date", new Date(NaN)])(
38
+ "an absent or invalid discovered heartbeat is never seen (%s)",
39
+ (lastSeenAt: Date | string | undefined | null) => {
40
+ expect(
41
+ getInventoryLivenessState({
42
+ source: EntitySource.Discovered,
43
+ lastSeenAt,
44
+ now: NOW,
45
+ }),
46
+ ).toBe(InventoryLiveness.Never);
47
+ },
48
+ );
49
+
50
+ test.each([
51
+ EntitySource.Inventory,
52
+ EntitySource.Manual,
53
+ "unknown",
54
+ undefined,
55
+ ])("%s is never aged as telemetry", (source: string | undefined) => {
56
+ for (const lastSeenAt of [
57
+ undefined,
58
+ null,
59
+ "not-a-date",
60
+ NOW,
61
+ new Date("2000-01-01T00:00:00.000Z"),
62
+ ]) {
63
+ expect(getInventoryLivenessState({ source, lastSeenAt, now: NOW })).toBe(
64
+ InventoryLiveness.NotTracked,
65
+ );
66
+ }
67
+ });
68
+
69
+ test("serialized dates and Date objects classify identically", () => {
70
+ for (const minutes of [0, 30, 31, 1440, 1441]) {
71
+ const lastSeenAt: Date = new Date(NOW.getTime() - minutes * 60_000);
72
+ expect(
73
+ getInventoryLivenessState({
74
+ source: EntitySource.Discovered,
75
+ lastSeenAt: lastSeenAt.toISOString(),
76
+ now: NOW,
77
+ }),
78
+ ).toBe(
79
+ getInventoryLivenessState({
80
+ source: EntitySource.Discovered,
81
+ lastSeenAt,
82
+ now: NOW,
83
+ }),
84
+ );
85
+ }
86
+ });
87
+
88
+ test("thresholds retain the established inventory badge vocabulary", () => {
89
+ expect(INVENTORY_LIVE_WINDOW_MINUTES).toBe(30);
90
+ expect(INVENTORY_STALE_AFTER_MINUTES).toBe(1440);
91
+ expect(Object.values(InventoryLiveness)).toEqual([
92
+ "live",
93
+ "recent",
94
+ "stale",
95
+ "never",
96
+ "not-tracked",
97
+ ]);
98
+ });
99
+ });
@@ -78,7 +78,9 @@ import CustomFieldsDetail from "../../../../UI/Components/CustomFields/CustomFie
78
78
  import BaseModel from "../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
79
79
  import Incident from "../../../../Models/DatabaseModels/Incident";
80
80
  import IncidentCustomField from "../../../../Models/DatabaseModels/IncidentCustomField";
81
+ import { CardButtonSchema } from "../../../../UI/Components/Card/Card";
81
82
  import CustomFieldType from "../../../../Types/CustomField/CustomFieldType";
83
+ import IconProp from "../../../../Types/Icon/IconProp";
82
84
  import { JSONObject } from "../../../../Types/JSON";
83
85
  import ObjectID from "../../../../Types/ObjectID";
84
86
 
@@ -153,6 +155,8 @@ const resolveListWith: ResolveListFunction = (data: Array<BaseModel>): void => {
153
155
  interface RenderOptions {
154
156
  hideIfEmpty?: boolean | undefined;
155
157
  isEditable?: boolean | undefined;
158
+ additionalButtons?: Array<CardButtonSchema> | undefined;
159
+ onValuesLoaded?: ((customFields: JSONObject) => void) | undefined;
156
160
  }
157
161
 
158
162
  type RenderCardFunction = (options?: RenderOptions) => void;
@@ -169,6 +173,8 @@ const renderCard: RenderCardFunction = (options?: RenderOptions): void => {
169
173
  modelId={INCIDENT_ID}
170
174
  hideIfEmpty={options?.hideIfEmpty}
171
175
  isEditable={options?.isEditable}
176
+ additionalButtons={options?.additionalButtons}
177
+ onValuesLoaded={options?.onValuesLoaded}
172
178
  />,
173
179
  );
174
180
  };
@@ -717,3 +723,157 @@ describe("CustomFieldsDetail - a rejected save", () => {
717
723
  expect(container).toBeEmptyDOMElement();
718
724
  });
719
725
  });
726
+
727
+ /*
728
+ * Both props exist for the Monitor Template's Custom Field Defaults card
729
+ * (issue #3548), which needs a second button — "Sync Custom Fields to Linked
730
+ * Monitors" — and needs to word and enable that button from the values this
731
+ * card has actually loaded.
732
+ */
733
+ describe("CustomFieldsDetail - additionalButtons", () => {
734
+ const buildSyncButton: (onClick: () => void) => CardButtonSchema = (
735
+ onClick: () => void,
736
+ ): CardButtonSchema => {
737
+ return {
738
+ title: "Sync Custom Fields",
739
+ icon: IconProp.Refresh,
740
+ onClick: onClick,
741
+ };
742
+ };
743
+
744
+ test("renders beside the edit button and calls back when clicked", async () => {
745
+ const onSyncClick: MockFunction = getJestMockFunction();
746
+ resolveListWith(buildSchema([{ name: "Vendor" }]));
747
+ getItemMock.mockResolvedValue(buildIncident({ Vendor: "Cisco" }) as never);
748
+
749
+ renderCard({
750
+ additionalButtons: [
751
+ buildSyncButton(() => {
752
+ onSyncClick();
753
+ }),
754
+ ],
755
+ });
756
+
757
+ expect(await screen.findByText("Edit Fields")).toBeInTheDocument();
758
+ const syncButton: HTMLElement = screen.getByText("Sync Custom Fields");
759
+
760
+ fireEvent.click(syncButton);
761
+
762
+ expect(onSyncClick).toHaveBeenCalledTimes(1);
763
+ });
764
+
765
+ /*
766
+ * A disabled extra button stays on screen: its tooltip is where the reason
767
+ * lives ("Set at least one default above before syncing"), and a button that
768
+ * vanishes takes the explanation with it.
769
+ */
770
+ test("honours disabled without removing the button", async () => {
771
+ const onSyncClick: MockFunction = getJestMockFunction();
772
+ resolveListWith(buildSchema([{ name: "Vendor" }]));
773
+ getItemMock.mockResolvedValue(buildIncident(undefined) as never);
774
+
775
+ renderCard({
776
+ additionalButtons: [
777
+ {
778
+ ...buildSyncButton(() => {
779
+ onSyncClick();
780
+ }),
781
+ disabled: true,
782
+ },
783
+ ],
784
+ });
785
+
786
+ fireEvent.click(await screen.findByText("Sync Custom Fields"));
787
+
788
+ expect(onSyncClick).not.toHaveBeenCalled();
789
+ });
790
+
791
+ /*
792
+ * The gating on "Edit Fields" is about whether there is anything to edit
793
+ * here. Whether a push to OTHER records is worth offering is the caller's
794
+ * question, and it still has an answer on a record with no values — that is
795
+ * exactly when the caller wants to say why the push is unavailable.
796
+ */
797
+ test("renders even when there is no edit button to sit beside", async () => {
798
+ resolveListWith([]);
799
+ getItemMock.mockResolvedValue(buildIncident(undefined) as never);
800
+
801
+ renderCard({
802
+ additionalButtons: [
803
+ buildSyncButton(() => {
804
+ // Nothing: this test is about the button existing.
805
+ }),
806
+ ],
807
+ });
808
+
809
+ expect(await screen.findByText(NO_FIELDS_MESSAGE)).toBeInTheDocument();
810
+ expect(screen.queryByText("Edit Fields")).not.toBeInTheDocument();
811
+ expect(screen.getByText("Sync Custom Fields")).toBeInTheDocument();
812
+ });
813
+ });
814
+
815
+ describe("CustomFieldsDetail - onValuesLoaded", () => {
816
+ test("reports the record's stored bag once the load settles", async () => {
817
+ const onValuesLoaded: MockFunction = getJestMockFunction();
818
+ resolveListWith(buildSchema([{ name: "Vendor" }]));
819
+ getItemMock.mockResolvedValue(buildIncident({ Vendor: "Cisco" }) as never);
820
+
821
+ renderCard({
822
+ onValuesLoaded: (customFields: JSONObject): void => {
823
+ onValuesLoaded(customFields);
824
+ },
825
+ });
826
+
827
+ await waitFor(() => {
828
+ expect(onValuesLoaded).toHaveBeenCalledWith({ Vendor: "Cisco" });
829
+ });
830
+ });
831
+
832
+ // A record with nothing stored is a value, not a missing callback.
833
+ test("reports an empty bag for a record with no custom fields", async () => {
834
+ const onValuesLoaded: MockFunction = getJestMockFunction();
835
+ resolveListWith(buildSchema([{ name: "Vendor" }]));
836
+ getItemMock.mockResolvedValue(buildIncident(undefined) as never);
837
+
838
+ renderCard({
839
+ onValuesLoaded: (customFields: JSONObject): void => {
840
+ onValuesLoaded(customFields);
841
+ },
842
+ });
843
+
844
+ await waitFor(() => {
845
+ expect(onValuesLoaded).toHaveBeenCalledWith({});
846
+ });
847
+ });
848
+
849
+ /*
850
+ * The reason this is a callback rather than something the caller fetches
851
+ * itself: an edit made in this card has to reach the caller, or its extra
852
+ * button goes on explaining a state that is two saves out of date.
853
+ */
854
+ test("reports again with the new values after a save", async () => {
855
+ const onValuesLoaded: MockFunction = getJestMockFunction();
856
+ resolveListWith(buildSchema([{ name: "Vendor" }]));
857
+ getItemMock.mockResolvedValue(buildIncident(undefined) as never);
858
+ updateByIdMock.mockResolvedValue(undefined as never);
859
+
860
+ renderCard({
861
+ onValuesLoaded: (customFields: JSONObject): void => {
862
+ onValuesLoaded(customFields);
863
+ },
864
+ });
865
+
866
+ await waitFor(() => {
867
+ expect(onValuesLoaded).toHaveBeenCalledWith({});
868
+ });
869
+
870
+ getItemMock.mockResolvedValue(buildIncident({ Vendor: "Cisco" }) as never);
871
+
872
+ fireEvent.click(await screen.findByText("Edit Fields"));
873
+ fireEvent.click(await screen.findByText("Save"));
874
+
875
+ await waitFor(() => {
876
+ expect(onValuesLoaded).toHaveBeenCalledWith({ Vendor: "Cisco" });
877
+ });
878
+ });
879
+ });
@@ -0,0 +1,133 @@
1
+ import { afterAll, describe, expect, test } from "@jest/globals";
2
+ import childProcess from "child_process";
3
+ import fs from "fs";
4
+ import os from "os";
5
+ import path from "path";
6
+
7
+ const REPOSITORY_ROOT: string = path.resolve(__dirname, "..", "..", "..");
8
+ const COMMON_MODULES: string = path.join(
9
+ REPOSITORY_ROOT,
10
+ "Common",
11
+ "node_modules",
12
+ );
13
+ const ESBUILD_CONFIG: string = path.join(
14
+ REPOSITORY_ROOT,
15
+ "Common",
16
+ "UI",
17
+ "esbuild-config.js",
18
+ );
19
+ const APP_LINK: string = path.join(
20
+ REPOSITORY_ROOT,
21
+ "App/FeatureSet/Dashboard/src/Components/AppLink/AppLink.tsx",
22
+ );
23
+ const temporaryRoots: Array<string> = [];
24
+
25
+ interface RenderResult {
26
+ html: string;
27
+ error: string;
28
+ }
29
+
30
+ /*
31
+ * PublicDashboard mounts its own router but reuses Dashboard widgets and their
32
+ * AppLink. Separate package installations otherwise create two router contexts.
33
+ * A mocked AppLink or Jest's react-router-dom module mapper hides that failure,
34
+ * so bundle the actual link source against two physical router installations.
35
+ */
36
+ function renderSharedLink(nodeEnv: string): RenderResult {
37
+ const root: string = fs.mkdtempSync(
38
+ path.join(os.tmpdir(), "oneuptime-router-"),
39
+ );
40
+ temporaryRoots.push(root);
41
+ fs.symlinkSync(COMMON_MODULES, path.join(root, "node_modules"), "dir");
42
+
43
+ for (const packageName of ["public", "shared"]) {
44
+ const moduleDirectory: string = path.join(
45
+ root,
46
+ packageName,
47
+ "node_modules",
48
+ );
49
+ fs.mkdirSync(moduleDirectory, { recursive: true });
50
+ for (const dependency of ["react-router", "react-router-dom"]) {
51
+ fs.cpSync(
52
+ path.join(COMMON_MODULES, dependency),
53
+ path.join(moduleDirectory, dependency),
54
+ { recursive: true, dereference: true },
55
+ );
56
+ }
57
+ }
58
+
59
+ fs.copyFileSync(APP_LINK, path.join(root, "shared", "AppLink.tsx"));
60
+ const entry: string = path.join(root, "public", "Entry.tsx");
61
+ fs.writeFileSync(
62
+ entry,
63
+ `
64
+ import React from "react";
65
+ import { MemoryRouter } from "react-router-dom";
66
+ import { renderToStaticMarkup } from "react-dom/server";
67
+ import AppLink from "../shared/AppLink";
68
+
69
+ globalThis.routerFixtureMarkup = renderToStaticMarkup(
70
+ <MemoryRouter initialEntries={["/public-dashboard"]}>
71
+ <AppLink to={{ toString: () => "/dashboard/monitors/example" }}>
72
+ Unit 0660 monitor
73
+ </AppLink>
74
+ </MemoryRouter>,
75
+ );
76
+ `,
77
+ );
78
+
79
+ const script: string = `
80
+ const path = require("path");
81
+ const configModule = require(${JSON.stringify(ESBUILD_CONFIG)});
82
+ const esbuild = require(require.resolve("esbuild", {
83
+ paths: [${JSON.stringify(COMMON_MODULES)}],
84
+ }));
85
+ const config = configModule.createConfig({
86
+ serviceName: "PublicDashboard",
87
+ publicPath: "/public-dashboard/dist/",
88
+ entryPoint: ${JSON.stringify(entry)},
89
+ });
90
+ config.write = false;
91
+ config.splitting = false;
92
+ config.format = "cjs";
93
+ config.sourcemap = false;
94
+ config.logLevel = "silent";
95
+
96
+ esbuild.build(config).then((result) => {
97
+ const output = result.outputFiles[0].text;
98
+ new Function("require", "module", "exports", output)(require, { exports: {} }, {});
99
+ console.log(JSON.stringify({ html: globalThis.routerFixtureMarkup, error: "" }));
100
+ }).catch((error) => {
101
+ console.log(JSON.stringify({ html: "", error: String(error) }));
102
+ });
103
+ `;
104
+
105
+ return JSON.parse(
106
+ childProcess.execFileSync(process.execPath, ["-e", script], {
107
+ cwd: REPOSITORY_ROOT,
108
+ env: { ...process.env, NODE_ENV: nodeEnv },
109
+ encoding: "utf8",
110
+ timeout: 120000,
111
+ maxBuffer: 1024 * 1024,
112
+ }),
113
+ ) as RenderResult;
114
+ }
115
+
116
+ afterAll(() => {
117
+ for (const root of temporaryRoots) {
118
+ fs.rmSync(root, { recursive: true, force: true });
119
+ }
120
+ });
121
+
122
+ describe("shared public dashboard router context", () => {
123
+ test.each(["production", "development"])(
124
+ "renders the real shared AppLink inside the public router in %s",
125
+ (nodeEnv: string) => {
126
+ const result: RenderResult = renderSharedLink(nodeEnv);
127
+ expect(result.error).toBe("");
128
+ expect(result.html).toContain('href="/dashboard/monitors/example"');
129
+ expect(result.html).toContain("Unit 0660 monitor");
130
+ },
131
+ 180000,
132
+ );
133
+ });
@@ -139,6 +139,14 @@ describe("ReplayHeader", () => {
139
139
  });
140
140
  });
141
141
 
142
+ /*
143
+ * The clock is its own right-hand block since the header redesign: the
144
+ * wall-clock time leads on its own line, with the start date and the
145
+ * "playhead / length" pair under it. It used to be one inline run
146
+ * where the offset pair needed parentheses to stay legible; the pair
147
+ * is asserted without them now, and the wall clock is asserted from
148
+ * its own element either way.
149
+ */
142
150
  describe("clock", () => {
143
151
  it("shows the playhead as wall-clock time next to the offset and the length", () => {
144
152
  render(<ReplayHeader {...makeProps()} />);
@@ -148,7 +156,7 @@ describe("ReplayHeader", () => {
148
156
  "10:13:22",
149
157
  );
150
158
  expect(screen.getByTestId("replay-header-clock")).toHaveTextContent(
151
- "(0:41 / 4:12)",
159
+ "0:41 / 4:12",
152
160
  );
153
161
  });
154
162
 
@@ -162,7 +170,7 @@ describe("ReplayHeader", () => {
162
170
  /Invalid/,
163
171
  );
164
172
  expect(screen.getByTestId("replay-header-clock")).toHaveTextContent(
165
- "(0:41 / 4:12)",
173
+ "0:41 / 4:12",
166
174
  );
167
175
  });
168
176