@oneuptime/common 13.0.3 → 13.0.5

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 (687) hide show
  1. package/Models/DatabaseModels/Index.ts +4 -0
  2. package/Models/DatabaseModels/RumApplication.ts +1 -1
  3. package/Models/DatabaseModels/SecurityEventConnection.ts +457 -0
  4. package/Models/DatabaseModels/SecurityEventConnectionRun.ts +263 -0
  5. package/Models/DatabaseModels/TelemetryIngestionKey.ts +2 -2
  6. package/Server/API/AIInvestigationAPI.ts +484 -24
  7. package/Server/API/GoogleSecOpsConnectionAPI.ts +249 -8
  8. package/Server/API/SecurityEventConnectionAPI.ts +250 -0
  9. package/Server/API/TelemetryAPI.ts +402 -265
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.ts +99 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  12. package/Server/Middleware/TelemetryIngest.ts +101 -6
  13. package/Server/Services/ExceptionAggregationService.ts +7 -16
  14. package/Server/Services/GoogleSecOpsConnectionService.ts +15 -0
  15. package/Server/Services/Index.ts +4 -0
  16. package/Server/Services/InventoryItemService.ts +28 -0
  17. package/Server/Services/LogAggregationService.ts +17 -23
  18. package/Server/Services/MetricAggregationService.ts +4 -14
  19. package/Server/Services/SecurityEventConnectionRunService.ts +10 -0
  20. package/Server/Services/SecurityEventConnectionService.ts +538 -0
  21. package/Server/Services/TraceAggregationService.ts +77 -28
  22. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  23. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +13 -1
  24. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +15 -1
  25. package/Server/Utils/AI/SRE/InvestigationEvidence.ts +658 -0
  26. package/Server/Utils/AI/SRE/InvestigationReferences.ts +324 -0
  27. package/Server/Utils/AI/Toolbox/Index.ts +38 -0
  28. package/Server/Utils/AI/Toolbox/TimelineTools.ts +9 -2
  29. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +80 -0
  30. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +5 -3
  31. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.ts +1108 -0
  32. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.ts +732 -0
  33. package/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.ts +495 -0
  34. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.ts +689 -0
  35. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.ts +767 -0
  36. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.ts +571 -0
  37. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.ts +827 -0
  38. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.ts +610 -0
  39. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.ts +549 -0
  40. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.ts +830 -0
  41. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.ts +684 -0
  42. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.ts +487 -0
  43. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.ts +778 -0
  44. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.ts +1002 -0
  45. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.ts +556 -0
  46. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.ts +275 -0
  47. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.ts +53 -0
  48. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.ts +709 -0
  49. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.ts +596 -0
  50. package/Server/Utils/SecurityEvent/Connectors/Types.ts +191 -0
  51. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +353 -65
  52. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.ts +777 -0
  53. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +747 -148
  54. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +48 -2
  55. package/Server/Utils/SecurityEvent/SecurityEventDedupe.ts +81 -0
  56. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +741 -40
  57. package/Server/Utils/StartServer.ts +10 -0
  58. package/Server/Utils/Telemetry/EntityRegistry.ts +8 -0
  59. package/Server/Utils/Telemetry/KubernetesResourceAttributes.ts +108 -0
  60. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +174 -93
  61. package/Server/Utils/Telemetry/ResourceFacetPlanner.ts +251 -0
  62. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +248 -413
  63. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +668 -0
  64. package/Server/Utils/Telemetry/TraceContextPropagation.ts +348 -0
  65. package/Server/Utils/Telemetry.ts +18 -0
  66. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +413 -0
  67. package/Tests/App/Dashboard/AIRootCauseFeedItem.test.tsx +285 -0
  68. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +242 -0
  69. package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +601 -15
  70. package/Tests/App/Dashboard/ChangeAlertStateAIHeader.test.tsx +835 -0
  71. package/Tests/App/Dashboard/CloudResourceTelemetryScope.test.ts +41 -0
  72. package/Tests/App/Dashboard/ConnectionTestModal.test.tsx +604 -0
  73. package/Tests/App/Dashboard/ConnectorTestReportView.test.tsx +738 -0
  74. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +153 -6
  75. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +165 -0
  76. package/Tests/App/Dashboard/EpisodeChangeState.test.tsx +1069 -0
  77. package/Tests/App/Dashboard/EpisodeMembersCard.test.tsx +772 -0
  78. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +2354 -0
  79. package/Tests/App/Dashboard/EventOverviewSkeleton.test.tsx +210 -0
  80. package/Tests/App/Dashboard/EventStatBar.test.tsx +392 -0
  81. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +247 -0
  82. package/Tests/App/Dashboard/ExceptionAIAssistance.test.tsx +606 -0
  83. package/Tests/App/Dashboard/ExceptionBreadcrumbTimeline.test.tsx +273 -0
  84. package/Tests/App/Dashboard/ExceptionDetailSideMenu.test.tsx +176 -0
  85. package/Tests/App/Dashboard/ExceptionOverviewCards.test.tsx +366 -0
  86. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +371 -0
  87. package/Tests/App/Dashboard/ExceptionStackFrameViewer.test.tsx +440 -0
  88. package/Tests/App/Dashboard/ExceptionSummary.test.tsx +381 -0
  89. package/Tests/App/Dashboard/ExceptionTriage.test.tsx +306 -0
  90. package/Tests/App/Dashboard/ExceptionsResourceFacets.test.tsx +604 -0
  91. package/Tests/App/Dashboard/ExceptionsViewerEntityChips.test.tsx +706 -0
  92. package/Tests/App/Dashboard/FeedOrdering.test.ts +32 -0
  93. package/Tests/App/Dashboard/GoogleSecOpsConnectionForm.test.tsx +838 -0
  94. package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +18 -0
  95. package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +297 -20
  96. package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +618 -13
  97. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +254 -207
  98. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +303 -135
  99. package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +1251 -0
  100. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +303 -0
  101. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +876 -8
  102. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +35 -0
  103. package/Tests/App/Dashboard/InvestigationReferenceLink.test.tsx +177 -0
  104. package/Tests/App/Dashboard/InvestigationReportView.test.tsx +1295 -0
  105. package/Tests/App/Dashboard/KubernetesImageReferenceView.test.tsx +114 -0
  106. package/Tests/App/Dashboard/KubernetesLogsTabChips.test.tsx +285 -0
  107. package/Tests/App/Dashboard/LogsEntityChipNames.test.tsx +316 -0
  108. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +361 -0
  109. package/Tests/App/Dashboard/ProfileTableEntityNames.test.tsx +432 -0
  110. package/Tests/App/Dashboard/ProfileViewEntityName.test.tsx +250 -0
  111. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +10 -0
  112. package/Tests/App/Dashboard/ResourceLogsTabsBareLayout.test.tsx +700 -0
  113. package/Tests/App/Dashboard/ResourcePageEntityNameProps.test.tsx +803 -0
  114. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +11 -1
  115. package/Tests/App/Dashboard/ScheduledMaintenanceChangeState.test.tsx +1026 -0
  116. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +436 -0
  117. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +1195 -0
  118. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +1022 -0
  119. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +905 -0
  120. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +247 -0
  121. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +375 -274
  122. package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +410 -0
  123. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +774 -0
  124. package/Tests/App/Dashboard/TopErrorsPanelResourceNames.test.tsx +316 -0
  125. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +30 -0
  126. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +88 -24
  127. package/Tests/App/Dashboard/TraceExplorer.test.tsx +1590 -0
  128. package/Tests/App/Dashboard/TraceWaterfall.test.tsx +488 -0
  129. package/Tests/App/Dashboard/TracesAnalyticsSeriesLabels.test.tsx +367 -0
  130. package/Tests/App/Dashboard/TracesEntityNames.test.tsx +585 -0
  131. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +613 -0
  132. package/Tests/App/Dashboard/UseServiceNames.test.tsx +359 -0
  133. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +7 -0
  134. package/Tests/Models/InventoryItemNaming.test.ts +132 -16
  135. package/Tests/Models/SecurityDomainAccessControl.test.ts +61 -17
  136. package/Tests/Server/API/AIInvestigationAPI.test.ts +611 -2
  137. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +1098 -0
  138. package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +409 -0
  139. package/Tests/Server/API/SecurityEventConnectionAPI.test.ts +856 -0
  140. package/Tests/Server/API/SessionReplayAPI.test.ts +528 -1
  141. package/Tests/Server/API/TelemetryFacetsAPI.test.ts +810 -0
  142. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +240 -0
  143. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +229 -0
  144. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +66 -0
  145. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
  146. package/Tests/Server/Services/SecurityEventConnectionService.test.ts +1233 -0
  147. package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +53 -0
  148. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +218 -0
  149. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +155 -1
  150. package/Tests/Server/Services/TraceAggregationExceptionScope.test.ts +142 -0
  151. package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +85 -24
  152. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +33 -0
  153. package/Tests/Server/Utils/AI/InvestigationEvidence.test.ts +1395 -0
  154. package/Tests/Server/Utils/AI/InvestigationReferences.test.ts +616 -0
  155. package/Tests/Server/Utils/AI/TimelineTools.test.ts +119 -0
  156. package/Tests/Server/Utils/AI/Toolbox/AIToolbox.test.ts +151 -0
  157. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +65 -0
  158. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorExceptionScope.test.ts +224 -0
  159. package/Tests/Server/Utils/LoggerCore.test.ts +597 -0
  160. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPerSeriesFanout.test.ts +55 -2
  161. package/Tests/Server/Utils/Monitor/MonitorTemplateUtil.test.ts +1109 -0
  162. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.test.ts +1240 -0
  163. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.test.ts +1853 -0
  164. package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.test.ts +1129 -0
  165. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.test.ts +1066 -0
  166. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.test.ts +1568 -0
  167. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.test.ts +930 -0
  168. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.test.ts +1409 -0
  169. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.test.ts +763 -0
  170. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.test.ts +1296 -0
  171. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.test.ts +1102 -0
  172. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.test.ts +1174 -0
  173. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.test.ts +1032 -0
  174. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.test.ts +1558 -0
  175. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.test.ts +3041 -0
  176. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.test.ts +1168 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.test.ts +906 -0
  178. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.test.ts +1068 -0
  179. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.test.ts +1004 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.test.ts +784 -0
  181. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +113 -1
  182. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +5 -1
  183. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +93 -13
  184. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +23 -1
  185. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +25 -3
  186. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +22 -8
  187. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +82 -16
  188. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSearchDetections.test.ts +730 -0
  189. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsThreePassPoller.test.ts +1577 -0
  190. package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +41 -0
  191. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1453 -18
  192. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +711 -0
  193. package/Tests/Server/Utils/Telemetry/KubernetesResourceAttributes.test.ts +162 -0
  194. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +460 -13
  195. package/Tests/Server/Utils/Telemetry/ResourceFacetPlanner.test.ts +678 -0
  196. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +868 -0
  197. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +525 -0
  198. package/Tests/Server/Utils/Telemetry/TraceContextPropagation.test.ts +420 -0
  199. package/Tests/Types/OnCallDutyPolicy/LayerUtilCorePaths.test.ts +1014 -0
  200. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +9 -6
  201. package/Tests/Types/Rules/RuleCriteriaFieldRegistry.test.ts +276 -0
  202. package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +73 -0
  203. package/Tests/Types/Rum/SessionReplayTransportContracts.test.ts +95 -0
  204. package/Tests/Types/SecurityEvent/Connectors/ConnectorDiagnostics.test.ts +82 -0
  205. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +314 -0
  206. package/Tests/Types/Telemetry/ExceptionSpanScope.test.ts +89 -0
  207. package/Tests/Types/Telemetry/LlmConventions.test.ts +495 -0
  208. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +96 -0
  209. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +304 -0
  210. package/Tests/UI/Components/ActiveFilterChipsLockedFilters.test.tsx +357 -0
  211. package/Tests/UI/Components/Card.test.tsx +312 -1
  212. package/Tests/UI/Components/CardModelDetailHeaderLayout.test.tsx +253 -0
  213. package/Tests/UI/Components/CardModelDetailRefresher.test.tsx +342 -0
  214. package/Tests/UI/Components/Checkbox.test.tsx +79 -0
  215. package/Tests/UI/Components/CopyTextButton.test.tsx +142 -0
  216. package/Tests/UI/Components/DetailCompactStyle.test.tsx +621 -0
  217. package/Tests/UI/Components/ErrorBoundary.test.tsx +60 -0
  218. package/Tests/UI/Components/FacetSections.test.tsx +581 -0
  219. package/Tests/UI/Components/FacetSidebarControls.test.tsx +812 -0
  220. package/Tests/UI/Components/FacetVisibility.test.ts +429 -0
  221. package/Tests/UI/Components/LockedFilterActions.test.tsx +396 -0
  222. package/Tests/UI/Components/LockedFilterChip.test.tsx +651 -0
  223. package/Tests/UI/Components/LogsEntityNames.test.ts +1769 -0
  224. package/Tests/UI/Components/LogsEntityNamesComponents.test.tsx +696 -0
  225. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +600 -0
  226. package/Tests/UI/Components/LogsViewerEntityNames.test.tsx +713 -0
  227. package/Tests/UI/Components/MarkdownInlineReferences.test.tsx +2079 -0
  228. package/Tests/UI/Components/MemberRoleAssignment.test.tsx +446 -0
  229. package/Tests/UI/Components/ModelDetailRefetch.test.tsx +397 -0
  230. package/Tests/UI/Components/ModelListRefetch.test.tsx +233 -0
  231. package/Tests/UI/Components/ModelPageModelChange.test.tsx +409 -0
  232. package/Tests/UI/Components/PaginationIntegration.test.tsx +25 -0
  233. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +172 -0
  234. package/Tests/UI/Components/SavedViewsSidebarIntegration.test.tsx +32 -14
  235. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +44 -1
  236. package/Tests/UI/Components/TelemetryActiveFilterChipsLockedFilters.test.tsx +304 -0
  237. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +718 -0
  238. package/Tests/UI/Monitor/EvaluationLogList.test.tsx +303 -0
  239. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +55 -1
  240. package/Tests/UI/Rum/FidelityNotices.test.ts +22 -0
  241. package/Tests/UI/Rum/RecordingHealthCard.test.tsx +120 -136
  242. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +988 -0
  243. package/Tests/UI/Rum/ReplayCard.test.tsx +32 -0
  244. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +709 -12
  245. package/Tests/UI/Rum/ReplayHeader.test.tsx +50 -1
  246. package/Tests/UI/Rum/ReplayPinControl.test.tsx +67 -10
  247. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +41 -12
  248. package/Tests/UI/Rum/ReplayStage.test.tsx +663 -1
  249. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +173 -0
  250. package/Tests/UI/Rum/ReplayUi.test.tsx +189 -2
  251. package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +91 -0
  252. package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +1 -1
  253. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +585 -0
  254. package/Tests/UI/Rum/SessionReplayTable.test.tsx +844 -51
  255. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +29 -1
  256. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +288 -0
  257. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +31 -1
  258. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +6 -0
  259. package/Tests/UI/Utils/Clipboard.test.ts +154 -0
  260. package/Tests/UI/Utils/Navigation.test.ts +24 -0
  261. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +2205 -0
  262. package/Tests/UI/Utils/Telemetry/UseTelemetryEntityNames.test.tsx +844 -0
  263. package/Tests/Utils/AI/InvestigationReport.test.ts +2430 -0
  264. package/Tests/Utils/APIOutgoingRequestTracer.test.ts +137 -0
  265. package/Tests/Utils/Rum/SessionReplayHealth.test.ts +884 -0
  266. package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +40 -0
  267. package/Tests/Utils/Rum/SessionReplayRecordingEnded.test.ts +619 -0
  268. package/Tests/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.test.ts +631 -0
  269. package/Tests/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.test.ts +479 -0
  270. package/Tests/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.test.ts +507 -0
  271. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.test.ts +686 -0
  272. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.test.ts +474 -0
  273. package/Tests/Utils/SecurityEvent/Connectors/OktaNormalizer.test.ts +736 -0
  274. package/Tests/Utils/SecurityEvent/Connectors/SplunkNormalizer.test.ts +407 -0
  275. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.test.ts +129 -0
  276. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +57 -1
  277. package/Tests/Utils/Telemetry/EntityRelationship.test.ts +36 -0
  278. package/Tests/Utils/Telemetry/LockedFilterSearch.test.ts +600 -0
  279. package/Tests/Utils/Telemetry/NetworkHost.test.ts +48 -0
  280. package/Tests/Utils/Telemetry/OriginAllowList.test.ts +154 -0
  281. package/Tests/Utils/Traces/CriticalPath.test.ts +25 -0
  282. package/Types/AI/AIChatTypes.ts +8 -0
  283. package/Types/AI/InvestigationEvidence.ts +96 -0
  284. package/Types/Monitor/MonitorEvaluationSummary.ts +5 -0
  285. package/Types/OnCallDutyPolicy/Layer.ts +12 -0
  286. package/Types/Permission.ts +2 -2
  287. package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
  288. package/Types/Rum/SessionReplay.ts +109 -0
  289. package/Types/Rum/SessionReplayApi.ts +31 -0
  290. package/Types/SecurityEvent/Connectors/ConnectorDiagnostics.ts +111 -0
  291. package/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.ts +87 -0
  292. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +492 -0
  293. package/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.ts +41 -0
  294. package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -1
  295. package/Types/SecurityEvent/GoogleSecOpsRegion.ts +34 -0
  296. package/Types/Telemetry/EntityType.ts +14 -0
  297. package/Types/Telemetry/ExceptionSpanScope.ts +83 -0
  298. package/Types/Telemetry/LockedFilterDetail.ts +46 -0
  299. package/Types/Telemetry/ResourceEntityFacet.ts +17 -10
  300. package/Types/Telemetry/ResourceFacetCatalog.ts +171 -0
  301. package/UI/Components/Card/Card.tsx +118 -83
  302. package/UI/Components/Checkbox/Checkbox.tsx +8 -5
  303. package/UI/Components/CopyTextButton/CopyTextButton.tsx +7 -1
  304. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +4 -1
  305. package/UI/Components/Detail/Detail.tsx +137 -22
  306. package/UI/Components/ErrorBoundary.tsx +38 -0
  307. package/UI/Components/LogsViewer/LogsEntityNames.ts +912 -0
  308. package/UI/Components/LogsViewer/LogsViewer.tsx +156 -77
  309. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +28 -11
  310. package/UI/Components/LogsViewer/components/FacetSection.tsx +66 -79
  311. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +17 -2
  312. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +45 -5
  313. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +100 -19
  314. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +156 -39
  315. package/UI/Components/LogsViewer/components/LogsTable.tsx +30 -8
  316. package/UI/Components/LogsViewer/types.ts +6 -0
  317. package/UI/Components/Markdown.tsx/InlineReferences.tsx +628 -0
  318. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +385 -312
  319. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +50 -22
  320. package/UI/Components/ModelDetail/CardModelDetail.tsx +15 -0
  321. package/UI/Components/ModelDetail/ModelDetail.tsx +64 -19
  322. package/UI/Components/ModelList/ModelList.tsx +33 -8
  323. package/UI/Components/Page/ModelPage.tsx +119 -54
  324. package/UI/Components/Table/Table.tsx +8 -0
  325. package/UI/Components/Table/TableRow.tsx +13 -5
  326. package/UI/Components/TelemetryViewer/FacetVisibility.ts +229 -0
  327. package/UI/Components/TelemetryViewer/ResourceFacetConfigs.ts +46 -0
  328. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +11 -0
  329. package/UI/Components/TelemetryViewer/components/FacetSearchInput.tsx +79 -0
  330. package/UI/Components/TelemetryViewer/components/FacetSectionHeader.tsx +69 -0
  331. package/UI/Components/TelemetryViewer/components/FacetShowMoreButton.tsx +36 -0
  332. package/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.tsx +88 -0
  333. package/UI/Components/TelemetryViewer/components/LockedFilterActions.tsx +205 -0
  334. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +496 -0
  335. package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +30 -10
  336. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +203 -10
  337. package/UI/Components/TelemetryViewer/components/TelemetryFacetSection.tsx +60 -73
  338. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +94 -7
  339. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +17 -2
  340. package/UI/Components/TelemetryViewer/types.ts +19 -0
  341. package/UI/Components/TelemetryViewer/useFacetSearchExemptions.ts +158 -0
  342. package/UI/Components/TelemetryViewer/useFacetSectionSearch.ts +102 -0
  343. package/UI/Utils/Clipboard.ts +68 -2
  344. package/UI/Utils/Navigation.ts +17 -2
  345. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +620 -0
  346. package/UI/Utils/Telemetry/UseTelemetryEntityNames.ts +151 -0
  347. package/Utils/AI/InvestigationReport.ts +1789 -0
  348. package/Utils/API.ts +90 -1
  349. package/Utils/Rum/SessionReplayHealth.ts +11 -0
  350. package/Utils/Rum/SessionReplayRecordingEnded.ts +134 -0
  351. package/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.ts +427 -0
  352. package/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.ts +197 -0
  353. package/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.ts +464 -0
  354. package/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.ts +567 -0
  355. package/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.ts +299 -0
  356. package/Utils/SecurityEvent/Connectors/OktaNormalizer.ts +467 -0
  357. package/Utils/SecurityEvent/Connectors/SplunkNormalizer.ts +439 -0
  358. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +124 -12
  359. package/Utils/Telemetry/CrossSignalScope.ts +22 -8
  360. package/Utils/Telemetry/EntityRelationship.ts +8 -0
  361. package/Utils/Telemetry/LockedFilterSearch.ts +412 -0
  362. package/Utils/Telemetry/NetworkHost.ts +43 -0
  363. package/Utils/Telemetry/OriginAllowList.ts +139 -4
  364. package/Utils/Traces/CriticalPath.ts +10 -4
  365. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  366. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  367. package/build/dist/Models/DatabaseModels/RumApplication.js +1 -1
  368. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  369. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js +490 -0
  370. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js.map +1 -0
  371. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js +287 -0
  372. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js.map +1 -0
  373. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +2 -2
  374. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
  375. package/build/dist/Server/API/AIInvestigationAPI.js +336 -27
  376. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  377. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +182 -7
  378. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -1
  379. package/build/dist/Server/API/SecurityEventConnectionAPI.js +171 -0
  380. package/build/dist/Server/API/SecurityEventConnectionAPI.js.map +1 -0
  381. package/build/dist/Server/API/TelemetryAPI.js +248 -170
  382. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  383. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js +40 -0
  384. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js.map +1 -0
  385. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  386. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  387. package/build/dist/Server/Middleware/TelemetryIngest.js +57 -4
  388. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  389. package/build/dist/Server/Services/ExceptionAggregationService.js +7 -16
  390. package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
  391. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +8 -0
  392. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
  393. package/build/dist/Server/Services/Index.js +4 -0
  394. package/build/dist/Server/Services/Index.js.map +1 -1
  395. package/build/dist/Server/Services/InventoryItemService.js +22 -0
  396. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  397. package/build/dist/Server/Services/LogAggregationService.js +8 -17
  398. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  399. package/build/dist/Server/Services/MetricAggregationService.js +4 -14
  400. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  401. package/build/dist/Server/Services/SecurityEventConnectionRunService.js +9 -0
  402. package/build/dist/Server/Services/SecurityEventConnectionRunService.js.map +1 -0
  403. package/build/dist/Server/Services/SecurityEventConnectionService.js +356 -0
  404. package/build/dist/Server/Services/SecurityEventConnectionService.js.map +1 -0
  405. package/build/dist/Server/Services/TraceAggregationService.js +51 -25
  406. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  407. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  408. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  409. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  410. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  411. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +14 -7
  412. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  413. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js +468 -0
  414. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js.map +1 -0
  415. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js +230 -0
  416. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js.map +1 -0
  417. package/build/dist/Server/Utils/AI/Toolbox/Index.js +32 -0
  418. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  419. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +6 -2
  420. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -1
  421. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +59 -0
  422. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  423. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +5 -3
  424. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  425. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js +752 -0
  426. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js.map +1 -0
  427. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js +493 -0
  428. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js.map +1 -0
  429. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js +352 -0
  430. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js.map +1 -0
  431. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js +405 -0
  432. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js.map +1 -0
  433. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js +466 -0
  434. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js.map +1 -0
  435. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js +344 -0
  436. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js.map +1 -0
  437. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js +511 -0
  438. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js.map +1 -0
  439. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js +406 -0
  440. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js.map +1 -0
  441. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js +336 -0
  442. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js.map +1 -0
  443. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js +474 -0
  444. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js.map +1 -0
  445. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js +417 -0
  446. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js.map +1 -0
  447. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js +314 -0
  448. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js.map +1 -0
  449. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js +494 -0
  450. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js.map +1 -0
  451. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js +719 -0
  452. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js.map +1 -0
  453. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js +404 -0
  454. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js.map +1 -0
  455. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js +209 -0
  456. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js.map +1 -0
  457. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js +40 -0
  458. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js.map +1 -0
  459. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js +494 -0
  460. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js.map +1 -0
  461. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js +379 -0
  462. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js.map +1 -0
  463. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js +27 -0
  464. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js.map +1 -0
  465. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +234 -61
  466. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  467. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js +537 -0
  468. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js.map +1 -0
  469. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +572 -115
  470. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  471. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +35 -1
  472. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -1
  473. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js +52 -0
  474. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js.map +1 -0
  475. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +521 -32
  476. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  477. package/build/dist/Server/Utils/StartServer.js +10 -0
  478. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  479. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +8 -0
  480. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  481. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js +80 -0
  482. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js.map +1 -0
  483. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +122 -67
  484. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  485. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js +146 -0
  486. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js.map +1 -0
  487. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +174 -248
  488. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  489. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +492 -0
  490. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -0
  491. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js +259 -0
  492. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js.map +1 -0
  493. package/build/dist/Server/Utils/Telemetry.js +11 -5
  494. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  495. package/build/dist/Types/AI/InvestigationEvidence.js +2 -0
  496. package/build/dist/Types/AI/InvestigationEvidence.js.map +1 -0
  497. package/build/dist/Types/OnCallDutyPolicy/Layer.js +11 -0
  498. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  499. package/build/dist/Types/Permission.js +2 -2
  500. package/build/dist/Types/Permission.js.map +1 -1
  501. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +9 -0
  502. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  503. package/build/dist/Types/Rum/SessionReplay.js +95 -0
  504. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  505. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  506. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js +18 -0
  507. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js.map +1 -0
  508. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js +2 -0
  509. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js.map +1 -0
  510. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +374 -0
  511. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -0
  512. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js +35 -0
  513. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js.map +1 -0
  514. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js +35 -0
  515. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js.map +1 -0
  516. package/build/dist/Types/Telemetry/EntityType.js +14 -0
  517. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  518. package/build/dist/Types/Telemetry/ExceptionSpanScope.js +59 -0
  519. package/build/dist/Types/Telemetry/ExceptionSpanScope.js.map +1 -0
  520. package/build/dist/Types/Telemetry/LockedFilterDetail.js +16 -0
  521. package/build/dist/Types/Telemetry/LockedFilterDetail.js.map +1 -0
  522. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +16 -10
  523. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -1
  524. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +119 -0
  525. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -0
  526. package/build/dist/UI/Components/Card/Card.js +34 -17
  527. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  528. package/build/dist/UI/Components/Checkbox/Checkbox.js +6 -4
  529. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  530. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  531. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  532. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +1 -1
  533. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  534. package/build/dist/UI/Components/Detail/Detail.js +63 -13
  535. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  536. package/build/dist/UI/Components/ErrorBoundary.js +34 -0
  537. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  538. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js +535 -0
  539. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js.map +1 -0
  540. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +117 -57
  541. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  542. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +5 -7
  543. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  544. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +31 -44
  545. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -1
  546. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +17 -3
  547. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  548. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -3
  549. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  550. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +64 -18
  551. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  552. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +100 -41
  553. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  554. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +13 -5
  555. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  556. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  557. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js +403 -0
  558. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js.map +1 -0
  559. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +197 -179
  560. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  561. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +30 -21
  562. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  563. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +12 -0
  564. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  565. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +46 -12
  566. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  567. package/build/dist/UI/Components/ModelList/ModelList.js +26 -7
  568. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  569. package/build/dist/UI/Components/Page/ModelPage.js +62 -34
  570. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  571. package/build/dist/UI/Components/Table/Table.js +3 -1
  572. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  573. package/build/dist/UI/Components/Table/TableRow.js +8 -1
  574. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  575. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +111 -0
  576. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -0
  577. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js +22 -0
  578. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js.map +1 -0
  579. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  580. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  581. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js +36 -0
  582. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js.map +1 -0
  583. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js +21 -0
  584. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js.map +1 -0
  585. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js +14 -0
  586. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js.map +1 -0
  587. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js +27 -0
  588. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js.map +1 -0
  589. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js +88 -0
  590. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js.map +1 -0
  591. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +228 -0
  592. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -0
  593. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +6 -6
  594. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -1
  595. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +111 -12
  596. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  597. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js +27 -40
  598. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js.map +1 -1
  599. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +70 -10
  600. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  601. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +17 -3
  602. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  603. package/build/dist/UI/Components/TelemetryViewer/types.js +0 -4
  604. package/build/dist/UI/Components/TelemetryViewer/types.js.map +1 -1
  605. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js +70 -0
  606. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js.map +1 -0
  607. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js +52 -0
  608. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js.map +1 -0
  609. package/build/dist/UI/Utils/Clipboard.js +59 -2
  610. package/build/dist/UI/Utils/Clipboard.js.map +1 -1
  611. package/build/dist/UI/Utils/Navigation.js +11 -2
  612. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  613. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +471 -0
  614. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -0
  615. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js +113 -0
  616. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js.map +1 -0
  617. package/build/dist/Utils/AI/InvestigationReport.js +1241 -0
  618. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -0
  619. package/build/dist/Utils/API.js +25 -0
  620. package/build/dist/Utils/API.js.map +1 -1
  621. package/build/dist/Utils/Rum/SessionReplayHealth.js +10 -0
  622. package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -1
  623. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js +54 -0
  624. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js.map +1 -0
  625. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js +311 -0
  626. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js.map +1 -0
  627. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js +150 -0
  628. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js.map +1 -0
  629. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js +328 -0
  630. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js.map +1 -0
  631. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js +405 -0
  632. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js.map +1 -0
  633. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js +212 -0
  634. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js.map +1 -0
  635. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js +351 -0
  636. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js.map +1 -0
  637. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js +348 -0
  638. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js.map +1 -0
  639. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +103 -10
  640. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  641. package/build/dist/Utils/Telemetry/CrossSignalScope.js +16 -5
  642. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  643. package/build/dist/Utils/Telemetry/EntityRelationship.js +7 -0
  644. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  645. package/build/dist/Utils/Telemetry/LockedFilterSearch.js +277 -0
  646. package/build/dist/Utils/Telemetry/LockedFilterSearch.js.map +1 -0
  647. package/build/dist/Utils/Telemetry/NetworkHost.js +39 -0
  648. package/build/dist/Utils/Telemetry/NetworkHost.js.map +1 -0
  649. package/build/dist/Utils/Telemetry/OriginAllowList.js +105 -4
  650. package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
  651. package/build/dist/Utils/Traces/CriticalPath.js +9 -4
  652. package/build/dist/Utils/Traces/CriticalPath.js.map +1 -1
  653. package/package.json +1 -1
  654. package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +0 -600
  655. package/UI/Components/GanttChart/Bar/BarLabel.tsx +0 -17
  656. package/UI/Components/GanttChart/Bar/Index.tsx +0 -149
  657. package/UI/Components/GanttChart/ChartContainer.tsx +0 -50
  658. package/UI/Components/GanttChart/Index.tsx +0 -90
  659. package/UI/Components/GanttChart/Row/Index.tsx +0 -22
  660. package/UI/Components/GanttChart/Row/Row.tsx +0 -231
  661. package/UI/Components/GanttChart/Row/RowLabel.tsx +0 -30
  662. package/UI/Components/GanttChart/Rows.tsx +0 -59
  663. package/UI/Components/GanttChart/Timeline/Index.tsx +0 -46
  664. package/UI/Components/GanttChart/Timeline/TimelineInterval.tsx +0 -33
  665. package/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.tsx +0 -43
  666. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js +0 -8
  667. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js.map +0 -1
  668. package/build/dist/UI/Components/GanttChart/Bar/Index.js +0 -77
  669. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +0 -1
  670. package/build/dist/UI/Components/GanttChart/ChartContainer.js +0 -33
  671. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +0 -1
  672. package/build/dist/UI/Components/GanttChart/Index.js +0 -39
  673. package/build/dist/UI/Components/GanttChart/Index.js.map +0 -1
  674. package/build/dist/UI/Components/GanttChart/Row/Index.js +0 -7
  675. package/build/dist/UI/Components/GanttChart/Row/Index.js.map +0 -1
  676. package/build/dist/UI/Components/GanttChart/Row/Row.js +0 -106
  677. package/build/dist/UI/Components/GanttChart/Row/Row.js.map +0 -1
  678. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +0 -10
  679. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +0 -1
  680. package/build/dist/UI/Components/GanttChart/Rows.js +0 -19
  681. package/build/dist/UI/Components/GanttChart/Rows.js.map +0 -1
  682. package/build/dist/UI/Components/GanttChart/Timeline/Index.js +0 -16
  683. package/build/dist/UI/Components/GanttChart/Timeline/Index.js.map +0 -1
  684. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js +0 -13
  685. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js.map +0 -1
  686. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js +0 -13
  687. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js.map +0 -1
@@ -0,0 +1,1195 @@
1
+ /**
2
+ * @timezone UTC
3
+ */
4
+ import {
5
+ afterEach,
6
+ beforeEach,
7
+ describe,
8
+ expect,
9
+ jest,
10
+ test,
11
+ } from "@jest/globals";
12
+ import "@testing-library/jest-dom";
13
+ import {
14
+ act,
15
+ cleanup,
16
+ fireEvent,
17
+ render,
18
+ RenderResult,
19
+ screen,
20
+ within,
21
+ } from "@testing-library/react";
22
+ import React from "react";
23
+ import getJestMockFunction, { MockFunction } from "../../MockType";
24
+
25
+ /*
26
+ * The scheduled maintenance overview page, rendered for real with its heavy
27
+ * children replaced by recorders. It covers what only the page decides: the
28
+ * first-load skeleton, that a refresh never unmounts the page, the error and
29
+ * not-found states, the header facts and stat bar, the feed refresh signal,
30
+ * the compact details card (including a resend failure that used to be
31
+ * swallowed) and the full set of affected resource relations.
32
+ */
33
+
34
+ const EVENT_ID: string = "66666666-6666-4666-8666-666666666666";
35
+ const OTHER_EVENT_ID: string = "77777777-7777-4777-8777-777777777777";
36
+
37
+ const getItemMock: MockFunction = getJestMockFunction();
38
+ const updateByIdMock: MockFunction = getJestMockFunction();
39
+ const changeStateRenderMock: MockFunction = getJestMockFunction();
40
+ const changeStateMountMock: MockFunction = getJestMockFunction();
41
+ const feedRenderMock: MockFunction = getJestMockFunction();
42
+ const cardModelDetailRenderMock: MockFunction = getJestMockFunction();
43
+ const customFieldsRenderMock: MockFunction = getJestMockFunction();
44
+
45
+ // Which item each stubbed CardModelDetail renders its field elements against.
46
+ const detailItemsByCardName: Record<string, unknown> = {};
47
+
48
+ jest.mock("react-i18next", () => {
49
+ return {
50
+ useTranslation: () => {
51
+ return {
52
+ t: (key: string, options?: { defaultValue?: string }): string => {
53
+ return options?.defaultValue ?? key;
54
+ },
55
+ };
56
+ },
57
+ };
58
+ });
59
+
60
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
61
+ return {
62
+ __esModule: true,
63
+ default: {
64
+ getItem: (...args: Array<any>) => {
65
+ return getItemMock(...args);
66
+ },
67
+ updateById: (...args: Array<any>) => {
68
+ return updateByIdMock(...args);
69
+ },
70
+ },
71
+ };
72
+ });
73
+
74
+ jest.mock(
75
+ "../../../../App/FeatureSet/Dashboard/src/Components/ScheduledMaintenance/ChangeState",
76
+ () => {
77
+ return {
78
+ __esModule: true,
79
+ default: (props: {
80
+ title?: string;
81
+ onActionComplete: () => void;
82
+ }): React.ReactElement => {
83
+ changeStateRenderMock(props);
84
+ React.useEffect(() => {
85
+ changeStateMountMock();
86
+ }, []);
87
+
88
+ return React.createElement(
89
+ "div",
90
+ { "data-testid": "change-state" },
91
+ React.createElement("h2", {}, props.title),
92
+ React.createElement(
93
+ "button",
94
+ { type: "button", onClick: props.onActionComplete },
95
+ "Complete action",
96
+ ),
97
+ );
98
+ },
99
+ };
100
+ },
101
+ );
102
+
103
+ jest.mock(
104
+ "../../../../App/FeatureSet/Dashboard/src/Components/ScheduledMaintenance/ScheduledMaintenanceFeed",
105
+ () => {
106
+ return {
107
+ __esModule: true,
108
+ default: (props: unknown): React.ReactElement => {
109
+ feedRenderMock(props);
110
+ return React.createElement("div", { "data-testid": "feed" });
111
+ },
112
+ };
113
+ },
114
+ );
115
+
116
+ jest.mock(
117
+ "../../../../App/FeatureSet/Dashboard/src/Components/Runbook/EntityRunbooks",
118
+ () => {
119
+ return {
120
+ __esModule: true,
121
+ default: (): React.ReactElement => {
122
+ return React.createElement("div", { "data-testid": "runbooks" });
123
+ },
124
+ };
125
+ },
126
+ );
127
+
128
+ jest.mock(
129
+ "../../../../App/FeatureSet/Dashboard/src/Components/CustomFields/OverviewCustomFields",
130
+ () => {
131
+ return {
132
+ __esModule: true,
133
+ default: (props: unknown): React.ReactElement => {
134
+ customFieldsRenderMock(props);
135
+ return React.createElement("div", { "data-testid": "custom-fields" });
136
+ },
137
+ };
138
+ },
139
+ );
140
+
141
+ jest.mock(
142
+ "../../../../App/FeatureSet/Dashboard/src/Components/AffectedResources/AffectedResourcesPicker",
143
+ () => {
144
+ return {
145
+ __esModule: true,
146
+ default: (): null => {
147
+ return null;
148
+ },
149
+ isAffectedResourcesPayload: (value: unknown): boolean => {
150
+ return Boolean(
151
+ value &&
152
+ (value as { __affectedResourcesPayload?: boolean })
153
+ .__affectedResourcesPayload,
154
+ );
155
+ },
156
+ };
157
+ },
158
+ );
159
+
160
+ jest.mock(
161
+ "../../../../App/FeatureSet/Dashboard/src/Components/AffectedResources/AffectedResourcesDisplay",
162
+ () => {
163
+ return {
164
+ __esModule: true,
165
+ default: (): React.ReactElement => {
166
+ return React.createElement("div", { "data-testid": "resources" });
167
+ },
168
+ };
169
+ },
170
+ );
171
+
172
+ jest.mock(
173
+ "../../../../App/FeatureSet/Dashboard/src/Components/StatusPage/StatusPagesElement",
174
+ () => {
175
+ return {
176
+ __esModule: true,
177
+ default: (): React.ReactElement => {
178
+ return React.createElement("div", { "data-testid": "status-pages" });
179
+ },
180
+ };
181
+ },
182
+ );
183
+
184
+ jest.mock(
185
+ "../../../../App/FeatureSet/Dashboard/src/Components/StatusPageSubscribers/SubscriberNotificationStatus",
186
+ () => {
187
+ return {
188
+ __esModule: true,
189
+ default: (props: {
190
+ onResendNotification: () => void;
191
+ }): React.ReactElement => {
192
+ return React.createElement(
193
+ "button",
194
+ { type: "button", onClick: props.onResendNotification },
195
+ "Resend notifications",
196
+ );
197
+ },
198
+ };
199
+ },
200
+ );
201
+
202
+ /*
203
+ * Renders each field's title, and its element when the test supplied an item
204
+ * for that card, so element-typed fields are exercised for real.
205
+ */
206
+ jest.mock("../../../UI/Components/ModelDetail/CardModelDetail", () => {
207
+ return {
208
+ __esModule: true,
209
+ default: (props: {
210
+ name: string;
211
+ modelDetailProps: {
212
+ fields: Array<{
213
+ title?: string;
214
+ getElement?: (item: unknown) => React.ReactElement;
215
+ }>;
216
+ };
217
+ }): React.ReactElement => {
218
+ cardModelDetailRenderMock(props);
219
+ const item: unknown = detailItemsByCardName[props.name];
220
+
221
+ return React.createElement(
222
+ "section",
223
+ { "data-testid": `card-${props.name}` },
224
+ props.modelDetailProps.fields.map(
225
+ (
226
+ field: {
227
+ title?: string;
228
+ getElement?: (item: unknown) => React.ReactElement;
229
+ },
230
+ index: number,
231
+ ): React.ReactElement => {
232
+ return React.createElement(
233
+ "div",
234
+ { key: index, "data-testid": "detail-field" },
235
+ React.createElement("span", {}, field.title),
236
+ item && field.getElement ? field.getElement(item) : null,
237
+ );
238
+ },
239
+ ),
240
+ );
241
+ },
242
+ };
243
+ });
244
+
245
+ import ScheduledMaintenanceView from "../../../../App/FeatureSet/Dashboard/src/Pages/ScheduledMaintenanceEvents/View/Index";
246
+ import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
247
+ import { EventStatusFact } from "../../../../App/FeatureSet/Dashboard/src/Components/EventView/EventStatusPanel";
248
+ import ScheduledMaintenance from "../../../Models/DatabaseModels/ScheduledMaintenance";
249
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
250
+ import User from "../../../Models/DatabaseModels/User";
251
+ import Route from "../../../Types/API/Route";
252
+ import OneUptimeDate from "../../../Types/Date";
253
+ import Email from "../../../Types/Email";
254
+ import EventInterval from "../../../Types/Events/EventInterval";
255
+ import Recurring from "../../../Types/Events/Recurring";
256
+ import IconProp from "../../../Types/Icon/IconProp";
257
+ import Name from "../../../Types/Name";
258
+ import ObjectID from "../../../Types/ObjectID";
259
+ import PositiveNumber from "../../../Types/PositiveNumber";
260
+ import StatusPageSubscriberNotificationStatus from "../../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
261
+ import { DetailStyle } from "../../../UI/Components/Detail/Detail";
262
+ import Navigation from "../../../UI/Utils/Navigation";
263
+
264
+ const MINUTE: number = 60 * 1000;
265
+ const HOUR: number = 60 * MINUTE;
266
+
267
+ interface Deferred<T> {
268
+ promise: Promise<T>;
269
+ resolve: (value: T) => void;
270
+ reject: (error: Error) => void;
271
+ }
272
+
273
+ function createDeferred<T>(): Deferred<T> {
274
+ let resolvePromise: ((value: T) => void) | undefined;
275
+ let rejectPromise: ((error: Error) => void) | undefined;
276
+ const promise: Promise<T> = new Promise<T>(
277
+ (resolve: (value: T) => void, reject: (error: Error) => void) => {
278
+ resolvePromise = resolve;
279
+ rejectPromise = reject;
280
+ },
281
+ );
282
+
283
+ return {
284
+ promise,
285
+ resolve: (value: T): void => {
286
+ resolvePromise!(value);
287
+ },
288
+ reject: (error: Error): void => {
289
+ rejectPromise!(error);
290
+ },
291
+ };
292
+ }
293
+
294
+ interface ChangeStateProps {
295
+ eventNumber?: string;
296
+ title?: string;
297
+ eventStartsAt?: Date;
298
+ eventEndsAt?: Date;
299
+ facts?: Array<EventStatusFact>;
300
+ }
301
+
302
+ interface FeedProps {
303
+ refreshToken?: number;
304
+ }
305
+
306
+ interface DetailField {
307
+ title?: string;
308
+ field: Record<string, unknown>;
309
+ getElement?: (item: ScheduledMaintenance) => React.ReactElement;
310
+ getCustomElement?: (
311
+ values: Record<string, unknown>,
312
+ elementProps: { onChange?: (value: unknown) => void },
313
+ ) => React.ReactElement;
314
+ onChange?: (
315
+ value: unknown,
316
+ currentValues: Record<string, unknown>,
317
+ setNewFormValues: (values: Record<string, unknown>) => void,
318
+ ) => void;
319
+ }
320
+
321
+ interface CardProps {
322
+ name: string;
323
+ refresher?: boolean;
324
+ isEditable?: boolean;
325
+ editButtonText?: string;
326
+ cardProps: {
327
+ title?: string;
328
+ description?: string;
329
+ headerLayout?: string;
330
+ };
331
+ formFields: Array<DetailField>;
332
+ modelDetailProps: {
333
+ style?: DetailStyle;
334
+ showDetailsInNumberOfColumns?: number;
335
+ onBeforeFetch?: unknown;
336
+ fields: Array<DetailField>;
337
+ };
338
+ }
339
+
340
+ const PAGE_PROPS: PageComponentProps = {
341
+ pageRoute: new Route("/scheduled-maintenance-events"),
342
+ currentProject: null,
343
+ hasPaymentMethod: false,
344
+ } as unknown as PageComponentProps;
345
+
346
+ let currentEventId: string = EVENT_ID;
347
+
348
+ function makeEvent(overrides?: {
349
+ title?: string;
350
+ statusPageNames?: Array<string>;
351
+ createdBy?: User | null;
352
+ }): ScheduledMaintenance {
353
+ const now: number = Date.now();
354
+ const event: ScheduledMaintenance = new ScheduledMaintenance();
355
+ event.title = overrides?.title || "Primary database failover drill";
356
+ event.scheduledMaintenanceNumber = 58;
357
+ event.scheduledMaintenanceNumberWithPrefix = "#58";
358
+ // Half a minute past the hour, so relative text is not on a boundary.
359
+ event.startsAt = new Date(now + 2 * HOUR + 30 * 1000);
360
+ event.endsAt = new Date(now + 4 * HOUR + 30 * 1000);
361
+ event.statusPages = (
362
+ overrides?.statusPageNames || ["Acme Internal Status", "Acme Public"]
363
+ ).map((name: string): StatusPage => {
364
+ const statusPage: StatusPage = new StatusPage();
365
+ statusPage.name = name;
366
+ return statusPage;
367
+ });
368
+
369
+ if (overrides?.createdBy !== null) {
370
+ const user: User = overrides?.createdBy || new User();
371
+
372
+ if (!overrides?.createdBy) {
373
+ user.name = new Name("Jane Doe");
374
+ }
375
+
376
+ event.createdByUser = user;
377
+ }
378
+
379
+ return event;
380
+ }
381
+
382
+ async function flush(): Promise<void> {
383
+ await act(async () => {
384
+ for (let i: number = 0; i < 20; i++) {
385
+ await Promise.resolve();
386
+ }
387
+ });
388
+ }
389
+
390
+ async function renderPage(): Promise<RenderResult> {
391
+ const result: RenderResult = render(
392
+ <ScheduledMaintenanceView {...PAGE_PROPS} />,
393
+ );
394
+ await flush();
395
+ return result;
396
+ }
397
+
398
+ function lastProps<T>(mock: MockFunction): T {
399
+ return mock.mock.calls[mock.mock.calls.length - 1]![0] as T;
400
+ }
401
+
402
+ function cardProps(name: string): CardProps {
403
+ const calls: Array<Array<CardProps>> = cardModelDetailRenderMock.mock
404
+ .calls as Array<Array<CardProps>>;
405
+
406
+ for (let i: number = calls.length - 1; i >= 0; i--) {
407
+ if (calls[i]![0]!.name === name) {
408
+ return calls[i]![0]!;
409
+ }
410
+ }
411
+
412
+ throw new Error(`No CardModelDetail named ${name} was rendered`);
413
+ }
414
+
415
+ describe("Scheduled maintenance overview page", () => {
416
+ beforeEach(() => {
417
+ currentEventId = EVENT_ID;
418
+ jest.spyOn(Navigation, "getLastParamAsObjectID").mockImplementation(() => {
419
+ return new ObjectID(currentEventId);
420
+ });
421
+ getItemMock.mockReset();
422
+ updateByIdMock.mockReset();
423
+ changeStateRenderMock.mockReset();
424
+ changeStateMountMock.mockReset();
425
+ feedRenderMock.mockReset();
426
+ cardModelDetailRenderMock.mockReset();
427
+ customFieldsRenderMock.mockReset();
428
+
429
+ for (const key of Object.keys(detailItemsByCardName)) {
430
+ delete detailItemsByCardName[key];
431
+ }
432
+ });
433
+
434
+ afterEach(() => {
435
+ cleanup();
436
+ jest.restoreAllMocks();
437
+ });
438
+
439
+ describe("loading", () => {
440
+ test("shows the overview skeleton until the event loads, then the page", async () => {
441
+ const item: Deferred<ScheduledMaintenance> =
442
+ createDeferred<ScheduledMaintenance>();
443
+ getItemMock.mockReturnValue(item.promise);
444
+
445
+ await renderPage();
446
+
447
+ const skeleton: HTMLElement = screen.getByRole("status");
448
+
449
+ expect(skeleton).toHaveTextContent("Loading scheduled maintenance event");
450
+ expect(screen.queryByTestId("change-state")).toBe(null);
451
+ expect(screen.queryByTestId("feed")).toBe(null);
452
+
453
+ item.resolve(makeEvent());
454
+ await flush();
455
+
456
+ expect(screen.queryByText("Loading scheduled maintenance event")).toBe(
457
+ null,
458
+ );
459
+ expect(screen.getByTestId("change-state")).toBeInTheDocument();
460
+ expect(screen.getByTestId("feed")).toBeInTheDocument();
461
+ expect(changeStateMountMock).toHaveBeenCalledTimes(1);
462
+ });
463
+
464
+ test("asks for the header facts in the same request as the dates", async () => {
465
+ getItemMock.mockResolvedValue(makeEvent() as never);
466
+
467
+ await renderPage();
468
+
469
+ expect(getItemMock).toHaveBeenCalledTimes(1);
470
+
471
+ const request: {
472
+ id: ObjectID;
473
+ modelType: unknown;
474
+ select: Record<string, unknown>;
475
+ } = getItemMock.mock.calls[0]![0];
476
+
477
+ expect(request.id.toString()).toBe(EVENT_ID);
478
+ expect(request.modelType).toBe(ScheduledMaintenance);
479
+ expect(request.select).toEqual({
480
+ startsAt: true,
481
+ endsAt: true,
482
+ title: true,
483
+ scheduledMaintenanceNumber: true,
484
+ scheduledMaintenanceNumberWithPrefix: true,
485
+ statusPages: { _id: true, name: true },
486
+ createdByUser: { name: true, email: true },
487
+ });
488
+ });
489
+
490
+ test("a failed first load shows the error with a working retry", async () => {
491
+ getItemMock.mockRejectedValueOnce(new Error("Server exploded") as never);
492
+
493
+ await renderPage();
494
+
495
+ expect(screen.getByText("Server exploded")).toBeInTheDocument();
496
+ expect(screen.queryByTestId("change-state")).toBe(null);
497
+
498
+ getItemMock.mockResolvedValueOnce(makeEvent() as never);
499
+ fireEvent.click(screen.getByTestId("refresh-button"));
500
+ await flush();
501
+
502
+ expect(screen.queryByText("Server exploded")).toBe(null);
503
+ expect(screen.getByTestId("change-state")).toBeInTheDocument();
504
+ expect(getItemMock).toHaveBeenCalledTimes(2);
505
+ });
506
+
507
+ test("says so when the event does not exist", async () => {
508
+ getItemMock.mockResolvedValue(null as never);
509
+
510
+ await renderPage();
511
+
512
+ expect(
513
+ screen.getByText(
514
+ "This scheduled maintenance event could not be found.",
515
+ ),
516
+ ).toBeInTheDocument();
517
+ expect(screen.queryByRole("status")).toBe(null);
518
+ });
519
+
520
+ test("ignores a slow response for the event the reader already left", async () => {
521
+ const first: Deferred<ScheduledMaintenance> =
522
+ createDeferred<ScheduledMaintenance>();
523
+ const second: Deferred<ScheduledMaintenance> =
524
+ createDeferred<ScheduledMaintenance>();
525
+ getItemMock
526
+ .mockReturnValueOnce(first.promise)
527
+ .mockReturnValueOnce(second.promise);
528
+
529
+ const view: RenderResult = await renderPage();
530
+
531
+ currentEventId = OTHER_EVENT_ID;
532
+ view.rerender(<ScheduledMaintenanceView {...PAGE_PROPS} />);
533
+ await flush();
534
+
535
+ expect(getItemMock).toHaveBeenCalledTimes(2);
536
+
537
+ second.resolve(makeEvent({ title: "The event now on screen" }));
538
+ await flush();
539
+ first.resolve(makeEvent({ title: "The event left behind" }));
540
+ await flush();
541
+
542
+ expect(
543
+ screen.getByRole("heading", { name: "The event now on screen" }),
544
+ ).toBeInTheDocument();
545
+ expect(screen.queryByText("The event left behind")).toBe(null);
546
+ });
547
+
548
+ test("switching to another event shows the skeleton again instead of the old event", async () => {
549
+ const second: Deferred<ScheduledMaintenance> =
550
+ createDeferred<ScheduledMaintenance>();
551
+ getItemMock
552
+ .mockResolvedValueOnce(makeEvent({ title: "First event" }) as never)
553
+ .mockReturnValueOnce(second.promise);
554
+
555
+ const view: RenderResult = await renderPage();
556
+
557
+ expect(
558
+ screen.getByRole("heading", { name: "First event" }),
559
+ ).toBeInTheDocument();
560
+
561
+ currentEventId = OTHER_EVENT_ID;
562
+ view.rerender(<ScheduledMaintenanceView {...PAGE_PROPS} />);
563
+ await flush();
564
+
565
+ expect(screen.queryByText("First event")).toBe(null);
566
+ expect(screen.getByRole("status")).toHaveTextContent(
567
+ "Loading scheduled maintenance event",
568
+ );
569
+
570
+ second.resolve(makeEvent({ title: "Second event" }));
571
+ await flush();
572
+
573
+ expect(
574
+ screen.getByRole("heading", { name: "Second event" }),
575
+ ).toBeInTheDocument();
576
+ });
577
+
578
+ /*
579
+ * The page stays mounted across events. A failed first load for the next
580
+ * event used to fall back to the item still in state, which belonged to
581
+ * the previous event: its title, number, dates and facts rendered under
582
+ * the new URL, with a header whose actions acted on the new id.
583
+ */
584
+ test("a failed first load of the next event shows the error, not the previous event", async () => {
585
+ getItemMock
586
+ .mockResolvedValueOnce(makeEvent({ title: "First event" }) as never)
587
+ .mockRejectedValueOnce(new Error("Server exploded") as never);
588
+
589
+ const view: RenderResult = await renderPage();
590
+
591
+ expect(
592
+ screen.getByRole("heading", { name: "First event" }),
593
+ ).toBeInTheDocument();
594
+ expect(changeStateMountMock).toHaveBeenCalledTimes(1);
595
+
596
+ const headerRendersBefore: number =
597
+ changeStateRenderMock.mock.calls.length;
598
+
599
+ currentEventId = OTHER_EVENT_ID;
600
+ view.rerender(<ScheduledMaintenanceView {...PAGE_PROPS} />);
601
+ await flush();
602
+
603
+ expect(getItemMock).toHaveBeenCalledTimes(2);
604
+ expect(screen.queryByText("First event")).toBe(null);
605
+ expect(screen.getByText("Server exploded")).toBeInTheDocument();
606
+ expect(screen.queryByTestId("change-state")).toBe(null);
607
+ expect(screen.queryByTestId("feed")).toBe(null);
608
+ expect(screen.queryByRole("group", { name: "Maintenance window" })).toBe(
609
+ null,
610
+ );
611
+ // The header never rendered again, not even once, for the new id.
612
+ expect(changeStateRenderMock.mock.calls.length).toBe(headerRendersBefore);
613
+
614
+ // The retry recovers the event the reader is actually on.
615
+ getItemMock.mockResolvedValueOnce(
616
+ makeEvent({ title: "Second event" }) as never,
617
+ );
618
+ fireEvent.click(screen.getByTestId("refresh-button"));
619
+ await flush();
620
+
621
+ expect(screen.queryByText("Server exploded")).toBe(null);
622
+ expect(
623
+ screen.getByRole("heading", { name: "Second event" }),
624
+ ).toBeInTheDocument();
625
+ expect(
626
+ (getItemMock.mock.calls[2]![0] as { id: ObjectID }).id.toString(),
627
+ ).toBe(OTHER_EVENT_ID);
628
+ });
629
+
630
+ test("a failed first load of the next event is not replaced by the previous event on the way back", async () => {
631
+ const backAgain: Deferred<ScheduledMaintenance> =
632
+ createDeferred<ScheduledMaintenance>();
633
+
634
+ getItemMock
635
+ .mockResolvedValueOnce(makeEvent({ title: "First event" }) as never)
636
+ .mockRejectedValueOnce(new Error("Server exploded") as never)
637
+ .mockReturnValueOnce(backAgain.promise);
638
+
639
+ const view: RenderResult = await renderPage();
640
+
641
+ currentEventId = OTHER_EVENT_ID;
642
+ view.rerender(<ScheduledMaintenanceView {...PAGE_PROPS} />);
643
+ await flush();
644
+
645
+ expect(screen.getByText("Server exploded")).toBeInTheDocument();
646
+
647
+ // Back to the first event: a fresh first load, not the error, not stale data.
648
+ currentEventId = EVENT_ID;
649
+ view.rerender(<ScheduledMaintenanceView {...PAGE_PROPS} />);
650
+ await flush();
651
+
652
+ expect(screen.queryByText("Server exploded")).toBe(null);
653
+ expect(screen.getByRole("status")).toHaveTextContent(
654
+ "Loading scheduled maintenance event",
655
+ );
656
+
657
+ backAgain.resolve(makeEvent({ title: "First event, reloaded" }));
658
+ await flush();
659
+
660
+ expect(
661
+ screen.getByRole("heading", { name: "First event, reloaded" }),
662
+ ).toBeInTheDocument();
663
+ });
664
+ });
665
+
666
+ describe("refreshing", () => {
667
+ test("a state change refreshes the event, details and feed without unmounting the page", async () => {
668
+ getItemMock.mockResolvedValueOnce(makeEvent() as never);
669
+
670
+ await renderPage();
671
+
672
+ expect(lastProps<FeedProps>(feedRenderMock).refreshToken).toBe(0);
673
+ const refresherBefore: boolean | undefined = cardProps(
674
+ "Scheduled Maintenance Details",
675
+ ).refresher;
676
+
677
+ const refetch: Deferred<ScheduledMaintenance> =
678
+ createDeferred<ScheduledMaintenance>();
679
+ getItemMock.mockReturnValueOnce(refetch.promise);
680
+
681
+ fireEvent.click(screen.getByRole("button", { name: "Complete action" }));
682
+ await flush();
683
+
684
+ // Still mounted while the refetch is in flight: no skeleton, no remount.
685
+ expect(screen.queryByRole("status")).toBe(null);
686
+ expect(screen.getByTestId("change-state")).toBeInTheDocument();
687
+ expect(getItemMock).toHaveBeenCalledTimes(2);
688
+ expect(lastProps<FeedProps>(feedRenderMock).refreshToken).toBe(1);
689
+ expect(cardProps("Scheduled Maintenance Details").refresher).toBe(
690
+ !refresherBefore,
691
+ );
692
+
693
+ refetch.resolve(makeEvent({ title: "Renamed drill" }));
694
+ await flush();
695
+
696
+ expect(
697
+ screen.getByRole("heading", { name: "Renamed drill" }),
698
+ ).toBeInTheDocument();
699
+ expect(changeStateMountMock).toHaveBeenCalledTimes(1);
700
+ });
701
+
702
+ test("a failed background refresh keeps the page on screen", async () => {
703
+ getItemMock
704
+ .mockResolvedValueOnce(makeEvent() as never)
705
+ .mockRejectedValueOnce(new Error("Temporary glitch") as never);
706
+
707
+ await renderPage();
708
+
709
+ fireEvent.click(screen.getByRole("button", { name: "Complete action" }));
710
+ await flush();
711
+
712
+ expect(screen.queryByText("Temporary glitch")).toBe(null);
713
+ expect(screen.getByTestId("change-state")).toBeInTheDocument();
714
+ expect(changeStateMountMock).toHaveBeenCalledTimes(1);
715
+ });
716
+
717
+ test("saving either card refreshes the feed", async () => {
718
+ getItemMock.mockResolvedValue(makeEvent() as never);
719
+
720
+ await renderPage();
721
+
722
+ await act(async () => {
723
+ (
724
+ cardProps("Scheduled Maintenance Details") as unknown as {
725
+ onSaveSuccess: () => void;
726
+ }
727
+ ).onSaveSuccess();
728
+ });
729
+ await flush();
730
+
731
+ expect(lastProps<FeedProps>(feedRenderMock).refreshToken).toBe(1);
732
+
733
+ await act(async () => {
734
+ (
735
+ cardProps("Affected Resources") as unknown as {
736
+ onSaveSuccess: () => void;
737
+ }
738
+ ).onSaveSuccess();
739
+ });
740
+ await flush();
741
+
742
+ expect(lastProps<FeedProps>(feedRenderMock).refreshToken).toBe(2);
743
+ });
744
+ });
745
+
746
+ describe("header", () => {
747
+ test("passes the number, title, window and facts to the header", async () => {
748
+ const event: ScheduledMaintenance = makeEvent();
749
+ getItemMock.mockResolvedValue(event as never);
750
+
751
+ await renderPage();
752
+
753
+ const props: ChangeStateProps = lastProps<ChangeStateProps>(
754
+ changeStateRenderMock,
755
+ );
756
+
757
+ expect(props.eventNumber).toBe("#58");
758
+ expect(props.title).toBe("Primary database failover drill");
759
+ expect(props.eventStartsAt).toBe(event.startsAt);
760
+ expect(props.eventEndsAt).toBe(event.endsAt);
761
+ expect(props.facts).toEqual([
762
+ {
763
+ label: "Status pages",
764
+ value: "Acme Internal Status, Acme Public",
765
+ icon: IconProp.Globe,
766
+ },
767
+ { label: "Created by", value: "Jane Doe", icon: IconProp.User },
768
+ ]);
769
+ });
770
+
771
+ const STATUS_PAGE_CASES: Array<[Array<string>, string]> = [
772
+ [[], "None"],
773
+ [["Only page"], "Only page"],
774
+ [["A", "B", "C"], "A, B +1 more"],
775
+ [["A", "B", "C", "D", "E"], "A, B +3 more"],
776
+ [[" ", "Named"], "Named"],
777
+ ];
778
+
779
+ test.each(STATUS_PAGE_CASES)(
780
+ "summarises status pages %j as %s",
781
+ async (names: Array<string>, text: string) => {
782
+ getItemMock.mockResolvedValue(
783
+ makeEvent({ statusPageNames: names }) as never,
784
+ );
785
+
786
+ await renderPage();
787
+
788
+ expect(
789
+ lastProps<ChangeStateProps>(changeStateRenderMock).facts?.[0]?.value,
790
+ ).toBe(text);
791
+ },
792
+ );
793
+
794
+ test("falls back to the creator's email, and leaves the fact empty without a creator", async () => {
795
+ const user: User = new User();
796
+ user.email = new Email("jane@example.com");
797
+ getItemMock
798
+ .mockResolvedValueOnce(makeEvent({ createdBy: user }) as never)
799
+ .mockResolvedValueOnce(makeEvent({ createdBy: null }) as never);
800
+
801
+ await renderPage();
802
+
803
+ expect(
804
+ lastProps<ChangeStateProps>(changeStateRenderMock).facts?.[1]?.value,
805
+ ).toBe("jane@example.com");
806
+
807
+ fireEvent.click(screen.getByRole("button", { name: "Complete action" }));
808
+ await flush();
809
+
810
+ expect(
811
+ lastProps<ChangeStateProps>(changeStateRenderMock).facts?.[1]?.value,
812
+ ).toBe("");
813
+ });
814
+ });
815
+
816
+ describe("stat bar", () => {
817
+ test("shows start, end and window length with relative times, and the timezone inside the bar", async () => {
818
+ const event: ScheduledMaintenance = makeEvent();
819
+ getItemMock.mockResolvedValue(event as never);
820
+
821
+ await renderPage();
822
+
823
+ const bar: HTMLElement = screen.getByRole("group", {
824
+ name: "Maintenance window",
825
+ });
826
+ const cells: Array<HTMLElement> = Array.from(
827
+ bar.children,
828
+ ) as Array<HTMLElement>;
829
+
830
+ expect(cells).toHaveLength(3);
831
+ expect(cells[0]).toHaveTextContent("Starts");
832
+ expect(cells[0]).toHaveTextContent(
833
+ OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
834
+ event.startsAt!,
835
+ ),
836
+ );
837
+ expect(cells[0]).toHaveTextContent("in 2 hours");
838
+ expect(cells[1]).toHaveTextContent("Ends");
839
+ expect(cells[1]).toHaveTextContent("in 4 hours");
840
+ expect(cells[2]).toHaveTextContent("Duration");
841
+ expect(cells[2]).toHaveTextContent("2 hours");
842
+ expect(cells[2]).not.toHaveTextContent("0 minutes");
843
+ expect(cells[2]).toHaveTextContent(
844
+ "Planned window · times in " + OneUptimeDate.getCurrentTimezoneString(),
845
+ );
846
+
847
+ // Segment cells: no card chrome of their own, values wrap.
848
+ for (const cell of cells) {
849
+ expect(cell).not.toHaveClass("rounded-xl");
850
+ expect(cell).toHaveClass("px-5", "py-4");
851
+ }
852
+
853
+ /*
854
+ * The timezone used to be a loose footnote floating under the bar. It
855
+ * is said once, inside the Duration cell, and nowhere else.
856
+ */
857
+ expect(screen.queryByText(/^Times in /)).toBeNull();
858
+ expect(screen.getAllByText(/times in /i)).toHaveLength(1);
859
+ expect(bar.nextElementSibling).toBeNull();
860
+ });
861
+
862
+ test("is left out when the event has no window", async () => {
863
+ const event: ScheduledMaintenance = makeEvent();
864
+ delete event.endsAt;
865
+ getItemMock.mockResolvedValue(event as never);
866
+
867
+ await renderPage();
868
+
869
+ expect(screen.queryByRole("group", { name: "Maintenance window" })).toBe(
870
+ null,
871
+ );
872
+ expect(screen.getByTestId("change-state")).toBeInTheDocument();
873
+ });
874
+ });
875
+
876
+ describe("details card", () => {
877
+ test("uses the compact style, one column, no timeline pre-fetch and the agreed field order", async () => {
878
+ getItemMock.mockResolvedValue(makeEvent() as never);
879
+
880
+ await renderPage();
881
+
882
+ const details: CardProps = cardProps("Scheduled Maintenance Details");
883
+
884
+ expect(details.modelDetailProps.style).toBe(DetailStyle.Compact);
885
+ expect(details.modelDetailProps.showDetailsInNumberOfColumns).toBe(1);
886
+ expect(details.modelDetailProps.onBeforeFetch).toBe(undefined);
887
+ expect(
888
+ details.modelDetailProps.fields.map(
889
+ (field: DetailField): string | undefined => {
890
+ return field.title;
891
+ },
892
+ ),
893
+ ).toEqual([
894
+ "Starts At",
895
+ "Ends At",
896
+ "Created At",
897
+ "Shown on Status Pages",
898
+ "Subscriber Reminders",
899
+ "Subscriber Notifications",
900
+ "Labels",
901
+ "Scheduled Maintenance Number",
902
+ "Scheduled Maintenance ID",
903
+ ]);
904
+ });
905
+
906
+ test("describes reminders in plain words", async () => {
907
+ const withoutReminders: ScheduledMaintenance = makeEvent();
908
+ detailItemsByCardName["Scheduled Maintenance Details"] = withoutReminders;
909
+ getItemMock.mockResolvedValue(makeEvent() as never);
910
+
911
+ const view: RenderResult = await renderPage();
912
+
913
+ const card: () => HTMLElement = (): HTMLElement => {
914
+ return screen.getByTestId("card-Scheduled Maintenance Details");
915
+ };
916
+
917
+ expect(
918
+ within(card()).getByText("No reminders configured"),
919
+ ).toBeInTheDocument();
920
+
921
+ const reminder: Recurring = new Recurring();
922
+ reminder.intervalType = EventInterval.Day;
923
+ reminder.intervalCount = new PositiveNumber(2);
924
+
925
+ const nextReminderAt: Date = new Date(Date.now() + HOUR);
926
+ const withReminders: ScheduledMaintenance = makeEvent();
927
+ withReminders.sendSubscriberNotificationsOnBeforeTheEvent = [reminder];
928
+ withReminders.nextSubscriberNotificationBeforeTheEventAt = nextReminderAt;
929
+ detailItemsByCardName["Scheduled Maintenance Details"] = withReminders;
930
+ view.rerender(<ScheduledMaintenanceView {...PAGE_PROPS} />);
931
+ await flush();
932
+
933
+ expect(card()).toHaveTextContent("2 Days before the event begins");
934
+ expect(card()).not.toHaveTextContent("is begins");
935
+ expect(card()).toHaveTextContent(
936
+ "Next reminder: " +
937
+ OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
938
+ nextReminderAt,
939
+ ),
940
+ );
941
+
942
+ const noUpcoming: ScheduledMaintenance = makeEvent();
943
+ noUpcoming.sendSubscriberNotificationsOnBeforeTheEvent = [reminder];
944
+ detailItemsByCardName["Scheduled Maintenance Details"] = noUpcoming;
945
+ view.rerender(<ScheduledMaintenanceView {...PAGE_PROPS} />);
946
+ await flush();
947
+
948
+ expect(
949
+ within(card()).getByText("No upcoming reminders"),
950
+ ).toBeInTheDocument();
951
+ });
952
+
953
+ test("shows a failed resend instead of swallowing it, and clears it on success", async () => {
954
+ detailItemsByCardName["Scheduled Maintenance Details"] = makeEvent();
955
+ getItemMock.mockResolvedValue(makeEvent() as never);
956
+ updateByIdMock.mockRejectedValueOnce(
957
+ new Error("Permission denied") as never,
958
+ );
959
+
960
+ await renderPage();
961
+
962
+ fireEvent.click(
963
+ screen.getByRole("button", { name: "Resend notifications" }),
964
+ );
965
+ await flush();
966
+
967
+ expect(screen.getByRole("alert")).toHaveTextContent(
968
+ "Could not resend notifications: Permission denied",
969
+ );
970
+ expect(getItemMock).toHaveBeenCalledTimes(1);
971
+
972
+ updateByIdMock.mockResolvedValueOnce({} as never);
973
+ fireEvent.click(
974
+ screen.getByRole("button", { name: "Resend notifications" }),
975
+ );
976
+ await flush();
977
+
978
+ expect(screen.queryByRole("alert")).toBe(null);
979
+ expect(updateByIdMock).toHaveBeenCalledTimes(2);
980
+
981
+ const update: {
982
+ id: ObjectID;
983
+ modelType: unknown;
984
+ data: Record<string, unknown>;
985
+ } = updateByIdMock.mock.calls[1]![0];
986
+
987
+ expect(update.id.toString()).toBe(EVENT_ID);
988
+ expect(update.modelType).toBe(ScheduledMaintenance);
989
+ expect(update.data).toEqual({
990
+ subscriberNotificationStatusOnEventScheduled:
991
+ StatusPageSubscriberNotificationStatus.Pending,
992
+ subscriberNotificationStatusMessage:
993
+ "Notification queued for resending",
994
+ });
995
+ // A successful resend refreshes the page to show the new status.
996
+ expect(getItemMock).toHaveBeenCalledTimes(2);
997
+ });
998
+
999
+ test("a failed resend belongs to its event and is not shown on the next one", async () => {
1000
+ detailItemsByCardName["Scheduled Maintenance Details"] = makeEvent();
1001
+ getItemMock.mockResolvedValue(makeEvent() as never);
1002
+ updateByIdMock.mockRejectedValueOnce(
1003
+ new Error("Permission denied") as never,
1004
+ );
1005
+
1006
+ const view: RenderResult = await renderPage();
1007
+
1008
+ fireEvent.click(
1009
+ screen.getByRole("button", { name: "Resend notifications" }),
1010
+ );
1011
+ await flush();
1012
+
1013
+ expect(screen.getByRole("alert")).toHaveTextContent(
1014
+ "Could not resend notifications: Permission denied",
1015
+ );
1016
+
1017
+ currentEventId = OTHER_EVENT_ID;
1018
+ view.rerender(<ScheduledMaintenanceView {...PAGE_PROPS} />);
1019
+ await flush();
1020
+
1021
+ expect(
1022
+ screen.getByRole("button", { name: "Resend notifications" }),
1023
+ ).toBeInTheDocument();
1024
+ expect(screen.queryByRole("alert")).toBe(null);
1025
+ });
1026
+ });
1027
+
1028
+ describe("right column", () => {
1029
+ /*
1030
+ * The column is about 300px wide. The old side-by-side headers put "Edit
1031
+ * Scheduled Maintenance Event" beside each title and squeezed the title
1032
+ * and description into a column one word wide; two resource tiles per
1033
+ * row clipped every name.
1034
+ */
1035
+ test("every card stacks its header and edit buttons just say Edit", async () => {
1036
+ getItemMock.mockResolvedValue(makeEvent() as never);
1037
+
1038
+ await renderPage();
1039
+
1040
+ const details: CardProps = cardProps("Scheduled Maintenance Details");
1041
+ const resources: CardProps = cardProps("Affected Resources");
1042
+
1043
+ expect(details.cardProps).toEqual({
1044
+ title: "Maintenance Details",
1045
+ description: "Key facts about this maintenance event.",
1046
+ headerLayout: "stacked",
1047
+ });
1048
+ expect(resources.cardProps).toEqual({
1049
+ title: "Affected Resources",
1050
+ description:
1051
+ "Monitors, services and infrastructure this maintenance affects.",
1052
+ headerLayout: "stacked",
1053
+ });
1054
+
1055
+ for (const card of [details, resources]) {
1056
+ expect(card.editButtonText).toBe("Edit");
1057
+ // The short label changes the words, not whether editing is offered.
1058
+ expect(card.isEditable).toBe(true);
1059
+ }
1060
+
1061
+ expect(
1062
+ lastProps<{ headerLayout?: string }>(customFieldsRenderMock)
1063
+ .headerLayout,
1064
+ ).toBe("stacked");
1065
+ });
1066
+
1067
+ test("affected resources render in a single column", async () => {
1068
+ getItemMock.mockResolvedValue(makeEvent() as never);
1069
+
1070
+ await renderPage();
1071
+
1072
+ const display: React.ReactElement = cardProps("Affected Resources")
1073
+ .modelDetailProps.fields[0]!.getElement!(new ScheduledMaintenance());
1074
+
1075
+ expect((display.props as { columns?: number }).columns).toBe(1);
1076
+ });
1077
+ });
1078
+
1079
+ describe("affected resources card", () => {
1080
+ const RELATIONS: Array<string> = [
1081
+ "monitors",
1082
+ "hosts",
1083
+ "kubernetesClusters",
1084
+ "dockerHosts",
1085
+ "podmanHosts",
1086
+ "proxmoxClusters",
1087
+ "vmwareVCenters",
1088
+ "cephClusters",
1089
+ "dockerSwarmClusters",
1090
+ "iotFleets",
1091
+ "networkSites",
1092
+ "services",
1093
+ ];
1094
+
1095
+ test("offers every resource type the model supports", async () => {
1096
+ getItemMock.mockResolvedValue(makeEvent() as never);
1097
+
1098
+ await renderPage();
1099
+
1100
+ const resources: CardProps = cardProps("Affected Resources");
1101
+ const picker: React.ReactElement = resources.formFields[0]!
1102
+ .getCustomElement!({}, {});
1103
+ const pickerProps: Record<string, unknown> = picker.props as Record<
1104
+ string,
1105
+ unknown
1106
+ >;
1107
+
1108
+ expect(pickerProps["resourceTypes"]).toEqual([
1109
+ "Monitor",
1110
+ "Host",
1111
+ "KubernetesCluster",
1112
+ "DockerHost",
1113
+ "PodmanHost",
1114
+ "ProxmoxCluster",
1115
+ "VMwareVCenter",
1116
+ "CephCluster",
1117
+ "DockerSwarmCluster",
1118
+ "IoTFleet",
1119
+ "NetworkSite",
1120
+ "Service",
1121
+ ]);
1122
+
1123
+ for (const relation of RELATIONS) {
1124
+ expect(pickerProps).toHaveProperty(relation);
1125
+ }
1126
+ });
1127
+
1128
+ test("writes every relation back from the picker's payload", async () => {
1129
+ getItemMock.mockResolvedValue(makeEvent() as never);
1130
+
1131
+ await renderPage();
1132
+
1133
+ const payload: Record<string, unknown> = {
1134
+ __affectedResourcesPayload: true,
1135
+ };
1136
+
1137
+ for (const relation of RELATIONS) {
1138
+ payload[relation] = [`${relation}-id`];
1139
+ }
1140
+
1141
+ const setNewFormValues: MockFunction = getJestMockFunction();
1142
+
1143
+ cardProps("Affected Resources").formFields[0]!.onChange!(
1144
+ payload,
1145
+ { title: "kept" },
1146
+ setNewFormValues,
1147
+ );
1148
+ await flush();
1149
+
1150
+ expect(setNewFormValues).toHaveBeenCalledTimes(1);
1151
+
1152
+ const written: Record<string, unknown> =
1153
+ setNewFormValues.mock.calls[0]![0];
1154
+
1155
+ expect(written["title"]).toBe("kept");
1156
+
1157
+ for (const relation of RELATIONS) {
1158
+ expect(written[relation]).toEqual([`${relation}-id`]);
1159
+ }
1160
+ });
1161
+
1162
+ test("registers, selects and displays every relation", async () => {
1163
+ getItemMock.mockResolvedValue(makeEvent() as never);
1164
+
1165
+ await renderPage();
1166
+
1167
+ const resources: CardProps = cardProps("Affected Resources");
1168
+ const registered: Array<string> = resources.formFields
1169
+ .slice(1)
1170
+ .map((field: DetailField): string => {
1171
+ return Object.keys(field.field)[0]!;
1172
+ });
1173
+
1174
+ expect(registered).toEqual(
1175
+ RELATIONS.filter((relation: string): boolean => {
1176
+ return relation !== "monitors";
1177
+ }),
1178
+ );
1179
+
1180
+ const displayField: DetailField = resources.modelDetailProps.fields[0]!;
1181
+
1182
+ expect(Object.keys(displayField.field)).toEqual(RELATIONS);
1183
+
1184
+ const display: React.ReactElement = displayField.getElement!(
1185
+ new ScheduledMaintenance(),
1186
+ );
1187
+
1188
+ for (const relation of RELATIONS) {
1189
+ expect((display.props as Record<string, unknown>)[relation]).toEqual(
1190
+ [],
1191
+ );
1192
+ }
1193
+ });
1194
+ });
1195
+ });