@oneuptime/common 12.0.4 → 12.0.6

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 (838) hide show
  1. package/Models/DatabaseModels/AIRun.ts +71 -1
  2. package/Models/DatabaseModels/AlertFeed.ts +30 -0
  3. package/Models/DatabaseModels/Dashboard.ts +28 -0
  4. package/Models/DatabaseModels/DataSource.ts +744 -0
  5. package/Models/DatabaseModels/Incident.ts +2 -0
  6. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  7. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  8. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  9. package/Models/DatabaseModels/IncidentFeed.ts +30 -0
  10. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  11. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  12. package/Models/DatabaseModels/Index.ts +3 -0
  13. package/Models/DatabaseModels/LogSavedView.ts +33 -0
  14. package/Models/DatabaseModels/Project.ts +231 -9
  15. package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
  16. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  17. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  18. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  19. package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +4 -4
  20. package/Models/DatabaseModels/StatusPage.ts +28 -0
  21. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  22. package/Models/DatabaseModels/User.ts +2 -0
  23. package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
  24. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
  25. package/Models/DatabaseModels/UserSession.ts +2 -0
  26. package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
  27. package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
  28. package/Server/API/AIAgentDataAPI.ts +44 -55
  29. package/Server/API/AIInsightAPI.ts +47 -4
  30. package/Server/API/AIInvestigationAPI.ts +200 -17
  31. package/Server/API/DashboardAPI.ts +186 -502
  32. package/Server/API/DataSourceAPI.ts +268 -0
  33. package/Server/API/StatusPageAPI.ts +49 -20
  34. package/Server/API/UserAPI.ts +119 -0
  35. package/Server/EnvironmentConfig.ts +16 -0
  36. package/Server/Infrastructure/ClickhouseConfig.ts +74 -1
  37. package/Server/Infrastructure/ClickhouseDatabase.ts +1 -1
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.ts +27 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.ts +89 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.ts +27 -0
  41. package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
  42. package/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.ts +40 -0
  43. package/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.ts +26 -0
  44. package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
  45. package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
  46. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
  47. package/Server/Infrastructure/Queue.ts +26 -0
  48. package/Server/Infrastructure/QueueWorker.ts +17 -0
  49. package/Server/Middleware/TelemetryIngest.ts +8 -3
  50. package/Server/Services/AIInsightService.ts +132 -12
  51. package/Server/Services/AIRunService.ts +112 -10
  52. package/Server/Services/AIService.ts +109 -22
  53. package/Server/Services/AlertFeedService.ts +5 -0
  54. package/Server/Services/AlertService.ts +38 -32
  55. package/Server/Services/AnalyticsDatabaseService.ts +37 -4
  56. package/Server/Services/DataSourceService.ts +200 -0
  57. package/Server/Services/DatabaseService.ts +43 -2
  58. package/Server/Services/HostService.ts +92 -0
  59. package/Server/Services/IncidentFeedService.ts +5 -0
  60. package/Server/Services/IncidentService.ts +45 -0
  61. package/Server/Services/Index.ts +2 -0
  62. package/Server/Services/LlmLogService.ts +46 -2
  63. package/Server/Services/MonitorService.ts +49 -0
  64. package/Server/Services/MonitorStatusService.ts +166 -0
  65. package/Server/Services/OpenTelemetryIngestService.ts +255 -184
  66. package/Server/Services/RumSessionPinService.ts +7 -21
  67. package/Server/Services/StatusPageSubscriberService.ts +13 -0
  68. package/Server/Services/UserService.ts +280 -9
  69. package/Server/Static/Vendor/README.md +62 -0
  70. package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
  71. package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
  72. package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
  73. package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
  74. package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
  75. package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
  76. package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
  77. package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
  78. package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
  79. package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
  80. package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
  81. package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
  82. package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
  83. package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
  84. package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
  85. package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
  86. package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
  87. package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
  88. package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
  89. package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
  90. package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
  91. package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
  92. package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
  93. package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
  94. package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
  95. package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
  96. package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
  97. package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
  98. package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
  99. package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
  100. package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
  101. package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
  102. package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
  103. package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
  104. package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
  105. package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
  106. package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
  107. package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
  108. package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
  109. package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
  110. package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
  111. package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
  112. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +6 -0
  113. package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +4 -0
  114. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +8 -5
  115. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +90 -16
  116. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +2 -0
  117. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +2 -0
  118. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +311 -31
  119. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +26 -24
  120. package/Server/Utils/AI/SRE/ConfidenceSignal.ts +182 -71
  121. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +252 -43
  122. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +26 -24
  123. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
  124. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
  125. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
  126. package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
  127. package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +47 -13
  128. package/Server/Utils/AI/SRE/InvestigationGrader.ts +9 -5
  129. package/Server/Utils/AI/SRE/InvestigationQueue.ts +141 -24
  130. package/Server/Utils/AI/SRE/InvestigationSubjectLock.ts +66 -0
  131. package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
  132. package/Server/Utils/AI/SRE/PostedRootCause.ts +94 -3
  133. package/Server/Utils/AI/SRE/README.md +1 -1
  134. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +132 -10
  135. package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +24 -0
  136. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
  137. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
  138. package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
  139. package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
  140. package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
  141. package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
  142. package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
  143. package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
  144. package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
  145. package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
  146. package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
  147. package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
  148. package/Server/Utils/DataSource/EgressGuard.ts +446 -0
  149. package/Server/Utils/DataSource/HttpFetch.ts +221 -0
  150. package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
  151. package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
  152. package/Server/Utils/DataSource/Types.ts +93 -0
  153. package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
  154. package/Server/Utils/InProcessMemo.ts +141 -0
  155. package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
  156. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +29 -0
  157. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +56 -0
  158. package/Server/Utils/Monitor/MonitorMetricUtil.ts +104 -0
  159. package/Server/Utils/Monitor/MonitorResource.ts +14 -0
  160. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
  161. package/Server/Utils/PasswordHash.ts +28 -4
  162. package/Server/Utils/Response.ts +12 -1
  163. package/Server/Utils/SSRFProtection.ts +237 -0
  164. package/Server/Utils/StartServer.ts +8 -0
  165. package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
  166. package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
  167. package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
  168. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
  169. package/Server/Utils/Telemetry/Telemetry.ts +112 -1
  170. package/Server/Utils/Telemetry.ts +104 -3
  171. package/Server/Utils/VendorAssets.ts +165 -0
  172. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
  173. package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
  174. package/Server/Views/Partials/Head.ejs +21 -7
  175. package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
  176. package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
  177. package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
  178. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +251 -0
  179. package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
  180. package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
  181. package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
  182. package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
  183. package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
  184. package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
  185. package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
  186. package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
  187. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +747 -0
  188. package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
  189. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +480 -0
  190. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1695 -0
  191. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +500 -0
  192. package/Tests/App/Dashboard/PortMonitorCriteriaFilter.test.ts +93 -0
  193. package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
  194. package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
  195. package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
  196. package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
  197. package/Tests/App/StatusPage/PublicStatusPageAPIErrorHandling.test.ts +117 -0
  198. package/Tests/App/StatusPage/StatusPageModelAPIPolymorphism.test.ts +223 -0
  199. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
  200. package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
  201. package/Tests/Models/DatabaseModels/PermissionCatalogueCoverage.test.ts +243 -0
  202. package/Tests/Server/API/AIAgentDataFixFromIncidentContext.test.ts +266 -0
  203. package/Tests/Server/API/AIInvestigationAPI.test.ts +781 -0
  204. package/Tests/Server/API/AIInvestigationCreateFixTask.test.ts +80 -0
  205. package/Tests/Server/API/DashboardMasterPasswordAPI.test.ts +172 -1
  206. package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
  207. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
  208. package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
  209. package/Tests/Server/API/StatusPageMasterPasswordAPI.test.ts +335 -0
  210. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
  211. package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
  212. package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
  213. package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
  214. package/Tests/Server/Services/AIInsightService.test.ts +266 -11
  215. package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
  216. package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +59 -5
  217. package/Tests/Server/Services/AIRunHumanVerdict.test.ts +127 -58
  218. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +400 -20
  219. package/Tests/Server/Services/AddMasterPasswordSaltMigration.test.ts +283 -0
  220. package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
  221. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +25 -1
  222. package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
  223. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +45 -35
  224. package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +54 -0
  225. package/Tests/Server/Services/FeedAIRunAssociation.test.ts +125 -0
  226. package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +418 -57
  227. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
  228. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
  229. package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
  230. package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +173 -14
  231. package/Tests/Server/Services/MasterPasswordScrypt.test.ts +567 -0
  232. package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
  233. package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
  234. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
  235. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
  236. package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
  237. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
  238. package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
  239. package/Tests/Server/Services/SeparateIncidentAlertAiSettingsMigration.test.ts +194 -0
  240. package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
  241. package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
  242. package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
  243. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +37 -4
  244. package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +330 -29
  245. package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +25 -0
  246. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +293 -5
  247. package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +59 -1
  248. package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +342 -19
  249. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +214 -30
  250. package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
  251. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
  252. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +2 -0
  253. package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
  254. package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +65 -17
  255. package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +3 -0
  256. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +605 -20
  257. package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
  258. package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
  259. package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
  260. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +4 -0
  261. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +2 -0
  262. package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +44 -0
  263. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +30 -0
  264. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
  265. package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
  266. package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
  267. package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
  268. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
  269. package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
  270. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
  271. package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
  272. package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
  273. package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
  274. package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
  275. package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
  276. package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
  277. package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
  278. package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
  279. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
  280. package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
  281. package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
  282. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
  283. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +212 -0
  284. package/Tests/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.test.ts +401 -0
  285. package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
  286. package/Tests/Server/Utils/Monitor/MonitorMetricUtilPortTimings.test.ts +243 -0
  287. package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
  288. package/Tests/Server/Utils/PasswordHash.test.ts +30 -0
  289. package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
  290. package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
  291. package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
  292. package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
  293. package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
  294. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
  295. package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
  296. package/Tests/Server/Utils/Telemetry.test.ts +87 -0
  297. package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
  298. package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
  299. package/Tests/Server/Views/OfflineRender.test.ts +297 -0
  300. package/Tests/Types/Dashboard/DashboardTemplates.test.ts +111 -0
  301. package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
  302. package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
  303. package/Tests/Types/Monitor/CephMetricCatalog.test.ts +104 -0
  304. package/Tests/Types/Monitor/CriteriaFilter.test.ts +4 -0
  305. package/Tests/Types/Monitor/DockerMetricCatalog.test.ts +109 -0
  306. package/Tests/Types/Monitor/DockerSwarmMetricCatalog.test.ts +118 -0
  307. package/Tests/Types/Monitor/HostMetricCatalog.test.ts +104 -0
  308. package/Tests/Types/Monitor/IotMetricCatalog.test.ts +104 -0
  309. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +118 -0
  310. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +42 -0
  311. package/Tests/Types/Monitor/MonitorStep.test.ts +38 -0
  312. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +75 -0
  313. package/Tests/Types/Monitor/PodmanMetricCatalog.test.ts +109 -0
  314. package/Tests/Types/Monitor/ProxmoxMetricCatalog.test.ts +107 -0
  315. package/Tests/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.test.ts +151 -0
  316. package/Tests/Types/Password.test.ts +363 -0
  317. package/Tests/Types/Permission.test.ts +137 -0
  318. package/Tests/Types/Rum/SessionReplayMaskingMode.test.ts +101 -0
  319. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +5 -5
  320. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +3 -5
  321. package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
  322. package/Tests/UI/Components/ErrorBoundary.test.tsx +211 -0
  323. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +74 -0
  324. package/Tests/UI/Components/Modal.test.tsx +281 -1
  325. package/Tests/UI/Components/MoreMenu.test.tsx +756 -0
  326. package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
  327. package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
  328. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +62 -0
  329. package/Tests/UI/Monitor/PortMonitorView.test.tsx +237 -0
  330. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +91 -0
  331. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
  332. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  333. package/Tests/UI/Utils/DownloadFile.test.ts +103 -0
  334. package/Tests/UI/Utils/ErrorSupportBundle.test.ts +582 -0
  335. package/Tests/UI/Utils/Project.test.ts +31 -0
  336. package/Tests/UI/Utils/StatusPageModelAPIInjection.test.ts +245 -0
  337. package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +820 -0
  338. package/Tests/Utils/Analytics.test.ts +23 -0
  339. package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
  340. package/Tests/Utils/Dashboard/DashboardViewConfig.test.ts +329 -0
  341. package/Tests/Utils/Dashboard/GridLayout.test.ts +951 -0
  342. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
  343. package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
  344. package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
  345. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +117 -0
  346. package/Tests/Utils/RecordingRuleExpression.test.ts +327 -0
  347. package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
  348. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +186 -0
  349. package/Tests/Utils/StatusPage/GroupTree.test.ts +456 -0
  350. package/Tests/Utils/StatusPage/OverviewGroupHierarchyVisibility.test.ts +588 -0
  351. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
  352. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
  353. package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
  354. package/Tests/Utils/Telemetry/SavedViewTimeRange.test.ts +244 -0
  355. package/Tests/Utils/Telemetry/TelemetryQueryTimeRange.test.ts +825 -0
  356. package/Types/AI/AIInsightEvidence.ts +6 -0
  357. package/Types/AI/AIRunCodeFixRecommendation.ts +17 -0
  358. package/Types/AI/CodeFixTaskContext.ts +63 -3
  359. package/Types/AI/CodeFixTaskType.ts +3 -3
  360. package/Types/Analytics/RevenueEvent.ts +31 -0
  361. package/Types/Dashboard/DashboardComponentType.ts +31 -0
  362. package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
  363. package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
  364. package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
  365. package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
  366. package/Types/DataSource/DataSourceLimits.ts +62 -0
  367. package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
  368. package/Types/DataSource/DataSourceTableResult.ts +38 -0
  369. package/Types/DataSource/DataSourceType.ts +213 -0
  370. package/Types/Date.ts +43 -3
  371. package/Types/HashedString.ts +4 -3
  372. package/Types/Monitor/CriteriaFilter.ts +4 -0
  373. package/Types/Monitor/DomainMonitor/DomainLookupMethod.ts +23 -0
  374. package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +7 -0
  375. package/Types/Monitor/MonitorCriteriaInstance.ts +24 -7
  376. package/Types/Monitor/MonitorMetricType.ts +8 -0
  377. package/Types/Monitor/MonitorStep.ts +10 -1
  378. package/Types/Monitor/MonitorStepDomainMonitor.ts +21 -0
  379. package/Types/Monitor/PortMonitor/PortMonitorTimings.ts +10 -0
  380. package/Types/Password.ts +64 -0
  381. package/Types/Permission.ts +69 -175
  382. package/Types/Probe/ProbeMonitorResponse.ts +6 -0
  383. package/Types/UserAuthenticationStatus.ts +34 -0
  384. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +2 -3
  385. package/UI/Components/Button/Button.tsx +1 -1
  386. package/UI/Components/ErrorBoundary.tsx +549 -71
  387. package/UI/Components/Feed/FeedItem.tsx +9 -2
  388. package/UI/Components/Icon/Icon.tsx +10 -9
  389. package/UI/Components/Modal/ConfirmModal.tsx +6 -1
  390. package/UI/Components/Modal/Modal.tsx +267 -35
  391. package/UI/Components/Modal/ModalFooter.tsx +6 -1
  392. package/UI/Components/ModelDetail/CardModelDetail.tsx +0 -12
  393. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  394. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +12 -2
  395. package/UI/Components/MoreMenu/MoreMenu.tsx +301 -98
  396. package/UI/Components/MoreMenu/MoreMenuItem.tsx +13 -5
  397. package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
  398. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
  399. package/UI/Components/StatusPage/ResourceList.tsx +570 -0
  400. package/UI/Styles/Theme.css +18 -0
  401. package/UI/Utils/API/RequestOptions.ts +7 -0
  402. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
  403. package/UI/Utils/DownloadFile.ts +16 -8
  404. package/UI/Utils/ErrorSupportBundle.ts +805 -0
  405. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -8
  406. package/UI/Utils/Project.ts +4 -0
  407. package/UI/Utils/StatusPage.ts +8 -2
  408. package/UI/Utils/TestDataSource.ts +61 -0
  409. package/UI/Utils/UseDashboardGridDnd.ts +917 -0
  410. package/UI/esbuild-config.js +45 -0
  411. package/Utils/Analytics.ts +15 -0
  412. package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
  413. package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
  414. package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
  415. package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
  416. package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
  417. package/Utils/Dashboard/Components/Index.ts +28 -0
  418. package/Utils/Dashboard/DashboardViewConfig.ts +80 -36
  419. package/Utils/Dashboard/GridLayout.ts +523 -0
  420. package/Utils/DataSource/DataSourceQueryText.ts +94 -0
  421. package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
  422. package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
  423. package/Utils/Metrics/RecordingRuleExpression.ts +19 -17
  424. package/Utils/Monitor/MonitorMetricType.ts +44 -2
  425. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
  426. package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
  427. package/Utils/StatusPage/GroupNestingLayout.ts +69 -0
  428. package/Utils/StatusPage/GroupTree.ts +157 -66
  429. package/Utils/StatusPage/ResourceExplorer.ts +853 -0
  430. package/Utils/StatusPage/ResourceUptime.ts +37 -2
  431. package/Utils/Telemetry/SavedViewTimeRange.ts +114 -0
  432. package/Utils/Telemetry/TelemetryQueryTimeRange.ts +334 -0
  433. package/build/dist/Models/DatabaseModels/AIRun.js +73 -1
  434. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  435. package/build/dist/Models/DatabaseModels/AlertFeed.js +32 -0
  436. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  437. package/build/dist/Models/DatabaseModels/Dashboard.js +29 -0
  438. package/build/dist/Models/DatabaseModels/Dashboard.js.map +1 -1
  439. package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
  440. package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
  441. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  442. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  443. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  444. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  445. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  446. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  447. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  448. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  449. package/build/dist/Models/DatabaseModels/IncidentFeed.js +32 -0
  450. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  451. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  452. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  453. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  454. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  455. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  456. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  457. package/build/dist/Models/DatabaseModels/LogSavedView.js +33 -0
  458. package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
  459. package/build/dist/Models/DatabaseModels/Project.js +241 -11
  460. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  461. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
  462. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
  463. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  464. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  465. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  466. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  467. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  468. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  469. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +4 -4
  470. package/build/dist/Models/DatabaseModels/StatusPage.js +29 -0
  471. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  472. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  473. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  474. package/build/dist/Models/DatabaseModels/User.js +2 -0
  475. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  476. package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
  477. package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
  478. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
  479. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  480. package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
  481. package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
  482. package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
  483. package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
  484. package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
  485. package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
  486. package/build/dist/Server/API/AIAgentDataAPI.js +28 -46
  487. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  488. package/build/dist/Server/API/AIInsightAPI.js +34 -4
  489. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  490. package/build/dist/Server/API/AIInvestigationAPI.js +134 -11
  491. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  492. package/build/dist/Server/API/DashboardAPI.js +127 -438
  493. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  494. package/build/dist/Server/API/DataSourceAPI.js +173 -0
  495. package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
  496. package/build/dist/Server/API/StatusPageAPI.js +40 -12
  497. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  498. package/build/dist/Server/API/UserAPI.js +94 -2
  499. package/build/dist/Server/API/UserAPI.js.map +1 -1
  500. package/build/dist/Server/EnvironmentConfig.js +14 -0
  501. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  502. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +61 -1
  503. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  504. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +1 -1
  505. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
  506. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js +22 -0
  507. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js.map +1 -0
  508. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js +36 -0
  509. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js.map +1 -0
  510. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js +18 -0
  511. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js.map +1 -0
  512. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
  513. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
  514. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js +19 -0
  515. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js.map +1 -0
  516. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js +19 -0
  517. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js.map +1 -0
  518. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
  519. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
  520. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
  521. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
  522. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
  523. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  524. package/build/dist/Server/Infrastructure/Queue.js +27 -0
  525. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  526. package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
  527. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  528. package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
  529. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  530. package/build/dist/Server/Services/AIInsightService.js +110 -10
  531. package/build/dist/Server/Services/AIInsightService.js.map +1 -1
  532. package/build/dist/Server/Services/AIRunService.js +92 -12
  533. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  534. package/build/dist/Server/Services/AIService.js +88 -21
  535. package/build/dist/Server/Services/AIService.js.map +1 -1
  536. package/build/dist/Server/Services/AlertFeedService.js +3 -0
  537. package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
  538. package/build/dist/Server/Services/AlertService.js +28 -35
  539. package/build/dist/Server/Services/AlertService.js.map +1 -1
  540. package/build/dist/Server/Services/AnalyticsDatabaseService.js +37 -5
  541. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  542. package/build/dist/Server/Services/DataSourceService.js +159 -0
  543. package/build/dist/Server/Services/DataSourceService.js.map +1 -0
  544. package/build/dist/Server/Services/DatabaseService.js +21 -2
  545. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  546. package/build/dist/Server/Services/HostService.js +74 -0
  547. package/build/dist/Server/Services/HostService.js.map +1 -1
  548. package/build/dist/Server/Services/IncidentFeedService.js +3 -0
  549. package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
  550. package/build/dist/Server/Services/IncidentService.js +35 -24
  551. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  552. package/build/dist/Server/Services/Index.js +2 -0
  553. package/build/dist/Server/Services/Index.js.map +1 -1
  554. package/build/dist/Server/Services/LlmLogService.js +22 -1
  555. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  556. package/build/dist/Server/Services/MonitorService.js +45 -0
  557. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  558. package/build/dist/Server/Services/MonitorStatusService.js +129 -0
  559. package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
  560. package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
  561. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  562. package/build/dist/Server/Services/RumSessionPinService.js +7 -15
  563. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  564. package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
  565. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  566. package/build/dist/Server/Services/UserService.js +241 -10
  567. package/build/dist/Server/Services/UserService.js.map +1 -1
  568. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
  569. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  570. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
  571. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
  572. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
  573. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  574. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
  575. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  576. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  577. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  578. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +4 -0
  579. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
  580. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +8 -5
  581. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  582. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +63 -24
  583. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  584. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +2 -0
  585. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  586. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +2 -0
  587. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  588. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +222 -34
  589. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  590. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +22 -19
  591. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  592. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +128 -49
  593. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
  594. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +170 -31
  595. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
  596. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +22 -19
  597. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  598. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
  599. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
  600. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
  601. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  602. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
  603. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  604. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
  605. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
  606. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +33 -9
  607. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -1
  608. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +9 -5
  609. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
  610. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +121 -41
  611. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  612. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js +46 -0
  613. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js.map +1 -0
  614. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
  615. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
  616. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +61 -14
  617. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -1
  618. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +79 -1
  619. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  620. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +21 -0
  621. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
  622. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
  623. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
  624. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
  625. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
  626. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
  627. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
  628. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
  629. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
  630. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
  631. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
  632. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
  633. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
  634. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
  635. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
  636. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
  637. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
  638. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
  639. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
  640. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
  641. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
  642. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
  643. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
  644. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
  645. package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
  646. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
  647. package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
  648. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
  649. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
  650. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
  651. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
  652. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
  653. package/build/dist/Server/Utils/DataSource/Types.js +2 -0
  654. package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
  655. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
  656. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
  657. package/build/dist/Server/Utils/InProcessMemo.js +113 -0
  658. package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
  659. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
  660. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
  661. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +20 -3
  662. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  663. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +43 -0
  664. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  665. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +67 -0
  666. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  667. package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
  668. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  669. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
  670. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  671. package/build/dist/Server/Utils/PasswordHash.js +25 -4
  672. package/build/dist/Server/Utils/PasswordHash.js.map +1 -1
  673. package/build/dist/Server/Utils/Response.js +9 -1
  674. package/build/dist/Server/Utils/Response.js.map +1 -1
  675. package/build/dist/Server/Utils/SSRFProtection.js +192 -0
  676. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
  677. package/build/dist/Server/Utils/StartServer.js +7 -0
  678. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  679. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
  680. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
  681. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
  682. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  683. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
  684. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
  685. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
  686. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
  687. package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
  688. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  689. package/build/dist/Server/Utils/Telemetry.js +93 -6
  690. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  691. package/build/dist/Server/Utils/VendorAssets.js +121 -0
  692. package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
  693. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
  694. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  695. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
  696. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  697. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js +18 -0
  698. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js.map +1 -0
  699. package/build/dist/Types/AI/CodeFixTaskContext.js +23 -3
  700. package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
  701. package/build/dist/Types/AI/CodeFixTaskType.js +3 -3
  702. package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
  703. package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
  704. package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
  705. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  706. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
  707. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
  708. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
  709. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
  710. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
  711. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
  712. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
  713. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
  714. package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
  715. package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
  716. package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
  717. package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
  718. package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
  719. package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
  720. package/build/dist/Types/DataSource/DataSourceType.js +183 -0
  721. package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
  722. package/build/dist/Types/Date.js +38 -3
  723. package/build/dist/Types/Date.js.map +1 -1
  724. package/build/dist/Types/HashedString.js +4 -3
  725. package/build/dist/Types/HashedString.js.map +1 -1
  726. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  727. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  728. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js +24 -0
  729. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js.map +1 -0
  730. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +24 -7
  731. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  732. package/build/dist/Types/Monitor/MonitorMetricType.js +7 -0
  733. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  734. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  735. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  736. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +15 -0
  737. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
  738. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js +2 -0
  739. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js.map +1 -0
  740. package/build/dist/Types/Password.js +56 -0
  741. package/build/dist/Types/Password.js.map +1 -0
  742. package/build/dist/Types/Permission.js +65 -155
  743. package/build/dist/Types/Permission.js.map +1 -1
  744. package/build/dist/Types/UserAuthenticationStatus.js +2 -0
  745. package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
  746. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +2 -5
  747. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  748. package/build/dist/UI/Components/ErrorBoundary.js +427 -52
  749. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  750. package/build/dist/UI/Components/Feed/FeedItem.js +2 -2
  751. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  752. package/build/dist/UI/Components/Icon/Icon.js +10 -4
  753. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  754. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  755. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  756. package/build/dist/UI/Components/Modal/Modal.js +178 -19
  757. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  758. package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
  759. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  760. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +0 -12
  761. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  762. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  763. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  764. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +12 -2
  765. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  766. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +197 -66
  767. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  768. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
  769. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  770. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
  771. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
  772. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
  773. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
  774. package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
  775. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
  776. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
  777. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  778. package/build/dist/UI/Utils/DownloadFile.js +15 -8
  779. package/build/dist/UI/Utils/DownloadFile.js.map +1 -1
  780. package/build/dist/UI/Utils/ErrorSupportBundle.js +476 -0
  781. package/build/dist/UI/Utils/ErrorSupportBundle.js.map +1 -0
  782. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +20 -15
  783. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  784. package/build/dist/UI/Utils/Project.js +3 -0
  785. package/build/dist/UI/Utils/Project.js.map +1 -1
  786. package/build/dist/UI/Utils/StatusPage.js +4 -4
  787. package/build/dist/UI/Utils/StatusPage.js.map +1 -1
  788. package/build/dist/UI/Utils/TestDataSource.js +39 -0
  789. package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
  790. package/build/dist/UI/Utils/UseDashboardGridDnd.js +551 -0
  791. package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -0
  792. package/build/dist/Utils/Analytics.js +4 -0
  793. package/build/dist/Utils/Analytics.js.map +1 -1
  794. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
  795. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
  796. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
  797. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
  798. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
  799. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
  800. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
  801. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
  802. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
  803. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
  804. package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
  805. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  806. package/build/dist/Utils/Dashboard/DashboardViewConfig.js +42 -26
  807. package/build/dist/Utils/Dashboard/DashboardViewConfig.js.map +1 -1
  808. package/build/dist/Utils/Dashboard/GridLayout.js +338 -0
  809. package/build/dist/Utils/Dashboard/GridLayout.js.map +1 -0
  810. package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
  811. package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
  812. package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
  813. package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
  814. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
  815. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
  816. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +19 -17
  817. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -1
  818. package/build/dist/Utils/Monitor/MonitorMetricType.js +38 -6
  819. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  820. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
  821. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  822. package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
  823. package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
  824. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +46 -0
  825. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -1
  826. package/build/dist/Utils/StatusPage/GroupTree.js +79 -34
  827. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -1
  828. package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
  829. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
  830. package/build/dist/Utils/StatusPage/ResourceUptime.js +22 -1
  831. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  832. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js +79 -0
  833. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js.map +1 -0
  834. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js +236 -0
  835. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js.map +1 -0
  836. package/jest.config.json +1 -0
  837. package/package.json +5 -1
  838. package/tsconfig.json +12 -10
@@ -0,0 +1,1452 @@
1
+ import StatusPageGroupHierarchyViewUtil, {
2
+ STATUS_PAGE_GROUP_PATH_SEPARATOR,
3
+ StatusPageGroupHierarchyRow,
4
+ StatusPageGroupHierarchySummary,
5
+ } from "../../../Utils/StatusPage/GroupHierarchyView";
6
+ import StatusPageGroupTreeUtil from "../../../Utils/StatusPage/GroupTree";
7
+ import ObjectID from "../../../Types/ObjectID";
8
+ import StatusPageGroup from "../../../Models/DatabaseModels/StatusPageGroup";
9
+ import StatusPageGroupViewMode from "../../../Types/StatusPage/StatusPageGroupViewMode";
10
+ import { describe, expect, test } from "@jest/globals";
11
+
12
+ /*
13
+ * Contract under test - the admin view of the status page group hierarchy.
14
+ *
15
+ * The page this backs replaced a flat table, so everything a table gave away
16
+ * for free has to be earned here instead: which rows are on screen once
17
+ * subtrees are collapsed, how deep each row sits, which guide rails are drawn
18
+ * beside it, which way it may be moved, and what a search leaves visible.
19
+ *
20
+ * Two things run through all of it. First, the same bad data the tree utils
21
+ * defend against - a group that points at itself, at a parent that was never
22
+ * fetched, or around a cycle - must not drop a row or hang a walk here either;
23
+ * a group missing from this page is a group an operator cannot fix. Second,
24
+ * anything that will be written to the API (a reorder, the parent picker) has
25
+ * to agree with what StatusPageGroupService will accept, because the only thing
26
+ * worse than not offering a choice is offering one that always fails.
27
+ */
28
+
29
+ const CORPORATE: ObjectID = new ObjectID(
30
+ "11111111-1111-4111-8111-111111111111",
31
+ );
32
+ const REGION_ONE: ObjectID = new ObjectID(
33
+ "22222222-2222-4222-8222-222222222222",
34
+ );
35
+ const REGION_TWO: ObjectID = new ObjectID(
36
+ "33333333-3333-4333-8333-333333333333",
37
+ );
38
+ const MARKET_ONE: ObjectID = new ObjectID(
39
+ "44444444-4444-4444-8444-444444444444",
40
+ );
41
+ const MARKET_TWO: ObjectID = new ObjectID(
42
+ "66666666-6666-4666-8666-666666666666",
43
+ );
44
+ const UNIT: ObjectID = new ObjectID("55555555-5555-4555-8555-555555555555");
45
+ const STANDALONE: ObjectID = new ObjectID(
46
+ "77777777-7777-4777-8777-777777777777",
47
+ );
48
+ const MISSING: ObjectID = new ObjectID("99999999-9999-4999-8999-999999999999");
49
+
50
+ function makeGroup(data: {
51
+ id: ObjectID;
52
+ name: string;
53
+ parentId?: ObjectID | undefined;
54
+ order?: number | undefined;
55
+ description?: string | undefined;
56
+ viewMode?: StatusPageGroupViewMode | undefined;
57
+ }): StatusPageGroup {
58
+ const group: StatusPageGroup = new StatusPageGroup();
59
+ group._id = data.id.toString();
60
+ group.name = data.name;
61
+
62
+ if (data.parentId) {
63
+ group.parentStatusPageGroupId = data.parentId;
64
+ }
65
+
66
+ if (data.order !== undefined) {
67
+ group.order = data.order;
68
+ }
69
+
70
+ if (data.description !== undefined) {
71
+ group.description = data.description;
72
+ }
73
+
74
+ if (data.viewMode !== undefined) {
75
+ group.viewMode = data.viewMode;
76
+ }
77
+
78
+ return group;
79
+ }
80
+
81
+ /*
82
+ * Corporate
83
+ * Region 1000
84
+ * Market 1001
85
+ * Unit 0152
86
+ * Market 1002
87
+ * Region 2000
88
+ * Standalone
89
+ *
90
+ * Chosen so every rail case appears at least once: a middle child with
91
+ * children, a last child, and a level that is three deep under a parent that
92
+ * still has siblings of its own.
93
+ */
94
+ function makeHierarchy(): Array<StatusPageGroup> {
95
+ return [
96
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
97
+ makeGroup({
98
+ id: REGION_ONE,
99
+ name: "Region 1000",
100
+ parentId: CORPORATE,
101
+ order: 2,
102
+ }),
103
+ makeGroup({
104
+ id: MARKET_ONE,
105
+ name: "Market 1001",
106
+ parentId: REGION_ONE,
107
+ order: 3,
108
+ }),
109
+ makeGroup({
110
+ id: UNIT,
111
+ name: "Unit 0152",
112
+ parentId: MARKET_ONE,
113
+ order: 4,
114
+ }),
115
+ makeGroup({
116
+ id: MARKET_TWO,
117
+ name: "Market 1002",
118
+ parentId: REGION_ONE,
119
+ order: 5,
120
+ }),
121
+ makeGroup({
122
+ id: REGION_TWO,
123
+ name: "Region 2000",
124
+ parentId: CORPORATE,
125
+ order: 6,
126
+ }),
127
+ makeGroup({ id: STANDALONE, name: "Standalone", order: 7 }),
128
+ ];
129
+ }
130
+
131
+ function allIds(statusPageGroups: Array<StatusPageGroup>): Set<string> {
132
+ return new Set<string>(
133
+ statusPageGroups.map((group: StatusPageGroup) => {
134
+ return group._id?.toString() || "";
135
+ }),
136
+ );
137
+ }
138
+
139
+ function rowNames(rows: Array<StatusPageGroupHierarchyRow>): Array<string> {
140
+ return rows.map((row: StatusPageGroupHierarchyRow) => {
141
+ return row.name;
142
+ });
143
+ }
144
+
145
+ function rowById(
146
+ rows: Array<StatusPageGroupHierarchyRow>,
147
+ id: ObjectID,
148
+ ): StatusPageGroupHierarchyRow {
149
+ const row: StatusPageGroupHierarchyRow | undefined = rows.find(
150
+ (candidate: StatusPageGroupHierarchyRow) => {
151
+ return candidate.id === id.toString();
152
+ },
153
+ );
154
+
155
+ if (!row) {
156
+ throw new Error(`No row for ${id.toString()}`);
157
+ }
158
+
159
+ return row;
160
+ }
161
+
162
+ describe("StatusPageGroupHierarchyViewUtil", () => {
163
+ describe("getRows - what is on screen", () => {
164
+ test("a status page with no groups produces no rows", () => {
165
+ expect(
166
+ StatusPageGroupHierarchyViewUtil.getRows({ statusPageGroups: [] }),
167
+ ).toEqual([]);
168
+ });
169
+
170
+ /*
171
+ * Collapsed has to mean absent: the component renders one flat list, so a
172
+ * subtree that is still in the array is a subtree that is still on screen.
173
+ */
174
+ test("nothing is expanded by default, so only the top level is drawn", () => {
175
+ const rows: Array<StatusPageGroupHierarchyRow> =
176
+ StatusPageGroupHierarchyViewUtil.getRows({
177
+ statusPageGroups: makeHierarchy(),
178
+ });
179
+
180
+ expect(rowNames(rows)).toEqual(["Corporate", "Standalone"]);
181
+ });
182
+
183
+ test("expanding a group reveals only its direct children", () => {
184
+ const rows: Array<StatusPageGroupHierarchyRow> =
185
+ StatusPageGroupHierarchyViewUtil.getRows({
186
+ statusPageGroups: makeHierarchy(),
187
+ expandedGroupIds: new Set<string>([CORPORATE.toString()]),
188
+ });
189
+
190
+ expect(rowNames(rows)).toEqual([
191
+ "Corporate",
192
+ "Region 1000",
193
+ "Region 2000",
194
+ "Standalone",
195
+ ]);
196
+ });
197
+
198
+ test("the whole hierarchy comes back in render order when everything is open", () => {
199
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
200
+
201
+ const rows: Array<StatusPageGroupHierarchyRow> =
202
+ StatusPageGroupHierarchyViewUtil.getRows({
203
+ statusPageGroups: statusPageGroups,
204
+ expandedGroupIds: allIds(statusPageGroups),
205
+ });
206
+
207
+ expect(rowNames(rows)).toEqual([
208
+ "Corporate",
209
+ "Region 1000",
210
+ "Market 1001",
211
+ "Unit 0152",
212
+ "Market 1002",
213
+ "Region 2000",
214
+ "Standalone",
215
+ ]);
216
+ });
217
+
218
+ test("siblings come back in order, not in the order they were fetched", () => {
219
+ const statusPageGroups: Array<StatusPageGroup> = [
220
+ makeGroup({ id: STANDALONE, name: "Standalone", order: 9 }),
221
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
222
+ makeGroup({ id: REGION_TWO, name: "Region 2000", order: 5 }),
223
+ ];
224
+
225
+ expect(
226
+ rowNames(
227
+ StatusPageGroupHierarchyViewUtil.getRows({
228
+ statusPageGroups: statusPageGroups,
229
+ }),
230
+ ),
231
+ ).toEqual(["Corporate", "Region 2000", "Standalone"]);
232
+ });
233
+
234
+ test("each row carries its depth and its ancestors", () => {
235
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
236
+
237
+ const rows: Array<StatusPageGroupHierarchyRow> =
238
+ StatusPageGroupHierarchyViewUtil.getRows({
239
+ statusPageGroups: statusPageGroups,
240
+ expandedGroupIds: allIds(statusPageGroups),
241
+ });
242
+
243
+ const unit: StatusPageGroupHierarchyRow = rowById(rows, UNIT);
244
+
245
+ expect(unit.depth).toBe(3);
246
+ expect(unit.ancestorNames).toEqual([
247
+ "Corporate",
248
+ "Region 1000",
249
+ "Market 1001",
250
+ ]);
251
+ expect(rowById(rows, CORPORATE).depth).toBe(0);
252
+ expect(rowById(rows, CORPORATE).ancestorNames).toEqual([]);
253
+ });
254
+
255
+ /*
256
+ * The badge counts the hierarchy, not the screen: an operator collapsing a
257
+ * branch has not changed how many groups are inside it.
258
+ */
259
+ test("sub group and descendant counts describe the real subtree", () => {
260
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
261
+
262
+ const rows: Array<StatusPageGroupHierarchyRow> =
263
+ StatusPageGroupHierarchyViewUtil.getRows({
264
+ statusPageGroups: statusPageGroups,
265
+ });
266
+
267
+ const corporate: StatusPageGroupHierarchyRow = rowById(rows, CORPORATE);
268
+
269
+ expect(corporate.hasSubGroups).toBe(true);
270
+ expect(corporate.subGroupCount).toBe(2);
271
+ expect(corporate.descendantCount).toBe(5);
272
+
273
+ const standalone: StatusPageGroupHierarchyRow = rowById(rows, STANDALONE);
274
+
275
+ expect(standalone.hasSubGroups).toBe(false);
276
+ expect(standalone.subGroupCount).toBe(0);
277
+ expect(standalone.descendantCount).toBe(0);
278
+ });
279
+
280
+ test("a group is only reported as expanded when it has children to show", () => {
281
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
282
+
283
+ const rows: Array<StatusPageGroupHierarchyRow> =
284
+ StatusPageGroupHierarchyViewUtil.getRows({
285
+ statusPageGroups: statusPageGroups,
286
+ expandedGroupIds: allIds(statusPageGroups),
287
+ });
288
+
289
+ expect(rowById(rows, CORPORATE).isExpanded).toBe(true);
290
+ expect(rowById(rows, STANDALONE).isExpanded).toBe(false);
291
+ expect(rowById(rows, STANDALONE).hasVisibleSubGroups).toBe(false);
292
+ });
293
+
294
+ test("a group with no name still produces a row", () => {
295
+ const nameless: StatusPageGroup = new StatusPageGroup();
296
+ nameless._id = CORPORATE.toString();
297
+
298
+ const rows: Array<StatusPageGroupHierarchyRow> =
299
+ StatusPageGroupHierarchyViewUtil.getRows({
300
+ statusPageGroups: [nameless],
301
+ });
302
+
303
+ expect(rows).toHaveLength(1);
304
+ expect(rows[0]!.name).toBe("");
305
+ expect(rows[0]!.id).toBe(CORPORATE.toString());
306
+ });
307
+ });
308
+
309
+ describe("getRows - the guide rails", () => {
310
+ /*
311
+ * The rails are the whole reason a tree reads as a tree. Each indent column
312
+ * left of a row's own connector belongs to one ancestor level, and carries a
313
+ * line only while that level still has a row below this one.
314
+ */
315
+ test("a top level row has no indent columns at all", () => {
316
+ const rows: Array<StatusPageGroupHierarchyRow> =
317
+ StatusPageGroupHierarchyViewUtil.getRows({
318
+ statusPageGroups: makeHierarchy(),
319
+ });
320
+
321
+ expect(rowById(rows, CORPORATE).ancestorRails).toEqual([]);
322
+ expect(rowById(rows, CORPORATE).depth).toBe(0);
323
+ });
324
+
325
+ test("a first level row has only its own connector", () => {
326
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
327
+
328
+ const rows: Array<StatusPageGroupHierarchyRow> =
329
+ StatusPageGroupHierarchyViewUtil.getRows({
330
+ statusPageGroups: statusPageGroups,
331
+ expandedGroupIds: allIds(statusPageGroups),
332
+ });
333
+
334
+ expect(rowById(rows, REGION_ONE).ancestorRails).toEqual([]);
335
+ });
336
+
337
+ test("a rail is drawn for every ancestor level that still has rows below", () => {
338
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
339
+
340
+ const rows: Array<StatusPageGroupHierarchyRow> =
341
+ StatusPageGroupHierarchyViewUtil.getRows({
342
+ statusPageGroups: statusPageGroups,
343
+ expandedGroupIds: allIds(statusPageGroups),
344
+ });
345
+
346
+ /*
347
+ * Unit 0152 sits under Market 1001 (which is followed by Market 1002)
348
+ * under Region 1000 (which is followed by Region 2000), so both of its
349
+ * ancestor columns carry a line.
350
+ */
351
+ expect(rowById(rows, UNIT).ancestorRails).toEqual([true, true]);
352
+ });
353
+
354
+ test("a rail stops once its level's last row has been drawn", () => {
355
+ const statusPageGroups: Array<StatusPageGroup> = [
356
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
357
+ makeGroup({
358
+ id: REGION_TWO,
359
+ name: "Region 2000",
360
+ parentId: CORPORATE,
361
+ order: 2,
362
+ }),
363
+ makeGroup({
364
+ id: MARKET_ONE,
365
+ name: "Market 1001",
366
+ parentId: REGION_TWO,
367
+ order: 3,
368
+ }),
369
+ ];
370
+
371
+ const rows: Array<StatusPageGroupHierarchyRow> =
372
+ StatusPageGroupHierarchyViewUtil.getRows({
373
+ statusPageGroups: statusPageGroups,
374
+ expandedGroupIds: allIds(statusPageGroups),
375
+ });
376
+
377
+ // Region 2000 is Corporate's only child, so its column is blank.
378
+ expect(rowById(rows, MARKET_ONE).ancestorRails).toEqual([false]);
379
+ });
380
+
381
+ test("the last child of a level ends its connector in an elbow", () => {
382
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
383
+
384
+ const rows: Array<StatusPageGroupHierarchyRow> =
385
+ StatusPageGroupHierarchyViewUtil.getRows({
386
+ statusPageGroups: statusPageGroups,
387
+ expandedGroupIds: allIds(statusPageGroups),
388
+ });
389
+
390
+ expect(rowById(rows, REGION_ONE).isLastVisibleSibling).toBe(false);
391
+ expect(rowById(rows, REGION_TWO).isLastVisibleSibling).toBe(true);
392
+ expect(rowById(rows, STANDALONE).isLastVisibleSibling).toBe(true);
393
+ expect(rowById(rows, CORPORATE).isLastVisibleSibling).toBe(false);
394
+ });
395
+
396
+ /*
397
+ * "Last" has to mean last among the rows that are drawn. During a search
398
+ * the row that used to be in the middle of a level can become the only one
399
+ * left in it, and an elbow that says otherwise draws a rail into thin air.
400
+ */
401
+ test("a filtered level ends its elbow on the last row that survives the filter", () => {
402
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
403
+
404
+ const rows: Array<StatusPageGroupHierarchyRow> =
405
+ StatusPageGroupHierarchyViewUtil.getRows({
406
+ statusPageGroups: statusPageGroups,
407
+ expandedGroupIds: allIds(statusPageGroups),
408
+ searchText: "Region 1000",
409
+ });
410
+
411
+ expect(rowById(rows, REGION_ONE).isLastVisibleSibling).toBe(true);
412
+ });
413
+
414
+ test("sibling positions describe the rows on screen", () => {
415
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
416
+
417
+ const rows: Array<StatusPageGroupHierarchyRow> =
418
+ StatusPageGroupHierarchyViewUtil.getRows({
419
+ statusPageGroups: statusPageGroups,
420
+ expandedGroupIds: allIds(statusPageGroups),
421
+ });
422
+
423
+ expect(rowById(rows, REGION_ONE).visibleSiblingIndex).toBe(0);
424
+ expect(rowById(rows, REGION_ONE).visibleSiblingCount).toBe(2);
425
+ expect(rowById(rows, REGION_TWO).visibleSiblingIndex).toBe(1);
426
+ expect(rowById(rows, STANDALONE).visibleSiblingIndex).toBe(1);
427
+ expect(rowById(rows, STANDALONE).visibleSiblingCount).toBe(2);
428
+ });
429
+
430
+ /*
431
+ * Nesting is capped on write at 10 levels, but rows written straight to the
432
+ * database are not, and a row that has indented itself off the right of the
433
+ * card tells the operator nothing at all.
434
+ */
435
+ test("indent columns stop growing past the cap, keeping the nearest rails", () => {
436
+ const statusPageGroups: Array<StatusPageGroup> = [];
437
+ const ids: Array<ObjectID> = [];
438
+
439
+ for (let level: number = 0; level < 16; level++) {
440
+ const id: ObjectID = new ObjectID(
441
+ `aaaaaaaa-0000-4000-8000-${level.toString().padStart(12, "0")}`,
442
+ );
443
+ ids.push(id);
444
+
445
+ statusPageGroups.push(
446
+ makeGroup({
447
+ id: id,
448
+ name: `Level ${level}`,
449
+ order: level + 1,
450
+ ...(level === 0 ? {} : { parentId: ids[level - 1]! }),
451
+ }),
452
+ );
453
+ }
454
+
455
+ const rows: Array<StatusPageGroupHierarchyRow> =
456
+ StatusPageGroupHierarchyViewUtil.getRows({
457
+ statusPageGroups: statusPageGroups,
458
+ expandedGroupIds: allIds(statusPageGroups),
459
+ });
460
+
461
+ const deepest: StatusPageGroupHierarchyRow = rows[rows.length - 1]!;
462
+
463
+ expect(deepest.depth).toBe(15);
464
+ expect(deepest.ancestorRails).toHaveLength(
465
+ StatusPageGroupHierarchyViewUtil.MaxIndentColumns - 1,
466
+ );
467
+
468
+ const atCap: StatusPageGroupHierarchyRow = rows[
469
+ StatusPageGroupHierarchyViewUtil.MaxIndentColumns
470
+ ] as StatusPageGroupHierarchyRow;
471
+
472
+ expect(atCap.ancestorRails).toHaveLength(
473
+ StatusPageGroupHierarchyViewUtil.MaxIndentColumns - 1,
474
+ );
475
+ });
476
+
477
+ test("getIndentColumnCount refuses to be broken by a nonsense depth", () => {
478
+ expect(
479
+ StatusPageGroupHierarchyViewUtil.getIndentColumnCount({ depth: 0 }),
480
+ ).toBe(0);
481
+ expect(
482
+ StatusPageGroupHierarchyViewUtil.getIndentColumnCount({ depth: -3 }),
483
+ ).toBe(0);
484
+ expect(
485
+ StatusPageGroupHierarchyViewUtil.getIndentColumnCount({ depth: NaN }),
486
+ ).toBe(0);
487
+ expect(
488
+ StatusPageGroupHierarchyViewUtil.getIndentColumnCount({
489
+ depth: Number.POSITIVE_INFINITY,
490
+ }),
491
+ ).toBe(0);
492
+ expect(
493
+ StatusPageGroupHierarchyViewUtil.getIndentColumnCount({ depth: 2.7 }),
494
+ ).toBe(2);
495
+ expect(
496
+ StatusPageGroupHierarchyViewUtil.getIndentColumnCount({ depth: 99 }),
497
+ ).toBe(StatusPageGroupHierarchyViewUtil.MaxIndentColumns);
498
+ });
499
+ });
500
+
501
+ describe("search", () => {
502
+ test("matches on the group name, ignoring case", () => {
503
+ const matched: Set<string> =
504
+ StatusPageGroupHierarchyViewUtil.getMatchingGroupIds({
505
+ statusPageGroups: makeHierarchy(),
506
+ searchText: "unit 0152",
507
+ });
508
+
509
+ expect(Array.from(matched)).toEqual([UNIT.toString()]);
510
+ });
511
+
512
+ test("matches on the description too", () => {
513
+ const statusPageGroups: Array<StatusPageGroup> = [
514
+ makeGroup({
515
+ id: CORPORATE,
516
+ name: "Corporate",
517
+ description: "Everything the payments team owns",
518
+ }),
519
+ makeGroup({ id: STANDALONE, name: "Standalone" }),
520
+ ];
521
+
522
+ expect(
523
+ Array.from(
524
+ StatusPageGroupHierarchyViewUtil.getMatchingGroupIds({
525
+ statusPageGroups: statusPageGroups,
526
+ searchText: "payments",
527
+ }),
528
+ ),
529
+ ).toEqual([CORPORATE.toString()]);
530
+ });
531
+
532
+ test("an empty or blank search matches nothing in particular", () => {
533
+ expect(
534
+ StatusPageGroupHierarchyViewUtil.getMatchingGroupIds({
535
+ statusPageGroups: makeHierarchy(),
536
+ searchText: " ",
537
+ }).size,
538
+ ).toBe(0);
539
+
540
+ expect(
541
+ StatusPageGroupHierarchyViewUtil.getMatchingGroupIds({
542
+ statusPageGroups: makeHierarchy(),
543
+ }).size,
544
+ ).toBe(0);
545
+ });
546
+
547
+ test("a blank search leaves the tree exactly as it was", () => {
548
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
549
+
550
+ expect(
551
+ rowNames(
552
+ StatusPageGroupHierarchyViewUtil.getRows({
553
+ statusPageGroups: statusPageGroups,
554
+ expandedGroupIds: allIds(statusPageGroups),
555
+ searchText: " ",
556
+ }),
557
+ ),
558
+ ).toEqual(
559
+ rowNames(
560
+ StatusPageGroupHierarchyViewUtil.getRows({
561
+ statusPageGroups: statusPageGroups,
562
+ expandedGroupIds: allIds(statusPageGroups),
563
+ }),
564
+ ),
565
+ );
566
+ });
567
+
568
+ /*
569
+ * A match behind a collapsed parent is a match the operator cannot see,
570
+ * which reads exactly like a search that found nothing.
571
+ */
572
+ test("a match is revealed even when its ancestors were collapsed", () => {
573
+ const rows: Array<StatusPageGroupHierarchyRow> =
574
+ StatusPageGroupHierarchyViewUtil.getRows({
575
+ statusPageGroups: makeHierarchy(),
576
+ expandedGroupIds: new Set<string>(),
577
+ searchText: "Unit 0152",
578
+ });
579
+
580
+ expect(rowNames(rows)).toEqual([
581
+ "Corporate",
582
+ "Region 1000",
583
+ "Market 1001",
584
+ "Unit 0152",
585
+ ]);
586
+ });
587
+
588
+ test("ancestors are context, and are marked as such", () => {
589
+ const rows: Array<StatusPageGroupHierarchyRow> =
590
+ StatusPageGroupHierarchyViewUtil.getRows({
591
+ statusPageGroups: makeHierarchy(),
592
+ searchText: "Unit 0152",
593
+ });
594
+
595
+ expect(rowById(rows, UNIT).isSearchMatch).toBe(true);
596
+ expect(rowById(rows, CORPORATE).isSearchMatch).toBe(false);
597
+ expect(rowById(rows, MARKET_ONE).isSearchMatch).toBe(false);
598
+ });
599
+
600
+ test("every row is a match when nothing is being searched for", () => {
601
+ const rows: Array<StatusPageGroupHierarchyRow> =
602
+ StatusPageGroupHierarchyViewUtil.getRows({
603
+ statusPageGroups: makeHierarchy(),
604
+ });
605
+
606
+ for (const row of rows) {
607
+ expect(row.isSearchMatch).toBe(true);
608
+ }
609
+ });
610
+
611
+ /*
612
+ * Searching for a branch and being shown an empty branch would be answering
613
+ * a different question from the one that was asked.
614
+ */
615
+ test("a matched group keeps its own subtree available", () => {
616
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
617
+
618
+ const rows: Array<StatusPageGroupHierarchyRow> =
619
+ StatusPageGroupHierarchyViewUtil.getRows({
620
+ statusPageGroups: statusPageGroups,
621
+ expandedGroupIds: allIds(statusPageGroups),
622
+ searchText: "Region 1000",
623
+ });
624
+
625
+ expect(rowNames(rows)).toEqual([
626
+ "Corporate",
627
+ "Region 1000",
628
+ "Market 1001",
629
+ "Unit 0152",
630
+ "Market 1002",
631
+ ]);
632
+ });
633
+
634
+ test("branches with nothing matching in them are gone entirely", () => {
635
+ const rows: Array<StatusPageGroupHierarchyRow> =
636
+ StatusPageGroupHierarchyViewUtil.getRows({
637
+ statusPageGroups: makeHierarchy(),
638
+ searchText: "Standalone",
639
+ });
640
+
641
+ expect(rowNames(rows)).toEqual(["Standalone"]);
642
+ });
643
+
644
+ test("a search that matches nothing produces no rows at all", () => {
645
+ expect(
646
+ StatusPageGroupHierarchyViewUtil.getRows({
647
+ statusPageGroups: makeHierarchy(),
648
+ searchText: "nothing here by that name",
649
+ }),
650
+ ).toEqual([]);
651
+ });
652
+
653
+ test("getSearchVisibleGroupIds covers the match, its ancestors and its subtree", () => {
654
+ const visible: Set<string> =
655
+ StatusPageGroupHierarchyViewUtil.getSearchVisibleGroupIds({
656
+ statusPageGroups: makeHierarchy(),
657
+ searchText: "Region 1000",
658
+ });
659
+
660
+ expect(visible.has(REGION_ONE.toString())).toBe(true);
661
+ expect(visible.has(CORPORATE.toString())).toBe(true);
662
+ expect(visible.has(MARKET_ONE.toString())).toBe(true);
663
+ expect(visible.has(UNIT.toString())).toBe(true);
664
+ expect(visible.has(REGION_TWO.toString())).toBe(false);
665
+ expect(visible.has(STANDALONE.toString())).toBe(false);
666
+ });
667
+
668
+ test("matchesSearch treats no search text as matching everything", () => {
669
+ expect(
670
+ StatusPageGroupHierarchyViewUtil.matchesSearch({
671
+ statusPageGroup: makeGroup({ id: CORPORATE, name: "Corporate" }),
672
+ }),
673
+ ).toBe(true);
674
+ });
675
+ });
676
+
677
+ describe("reordering", () => {
678
+ test("a row knows the siblings it can swap with", () => {
679
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
680
+
681
+ const rows: Array<StatusPageGroupHierarchyRow> =
682
+ StatusPageGroupHierarchyViewUtil.getRows({
683
+ statusPageGroups: statusPageGroups,
684
+ expandedGroupIds: allIds(statusPageGroups),
685
+ });
686
+
687
+ const marketOne: StatusPageGroupHierarchyRow = rowById(rows, MARKET_ONE);
688
+
689
+ expect(marketOne.canMoveUp).toBe(false);
690
+ expect(marketOne.canMoveDown).toBe(true);
691
+ expect(marketOne.nextSiblingId).toBe(MARKET_TWO.toString());
692
+
693
+ const marketTwo: StatusPageGroupHierarchyRow = rowById(rows, MARKET_TWO);
694
+
695
+ expect(marketTwo.canMoveUp).toBe(true);
696
+ expect(marketTwo.previousSiblingId).toBe(MARKET_ONE.toString());
697
+ expect(marketTwo.canMoveDown).toBe(false);
698
+ });
699
+
700
+ test("an only child can be moved neither way", () => {
701
+ const statusPageGroups: Array<StatusPageGroup> = [
702
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
703
+ makeGroup({
704
+ id: REGION_ONE,
705
+ name: "Region 1000",
706
+ parentId: CORPORATE,
707
+ order: 2,
708
+ }),
709
+ ];
710
+
711
+ const rows: Array<StatusPageGroupHierarchyRow> =
712
+ StatusPageGroupHierarchyViewUtil.getRows({
713
+ statusPageGroups: statusPageGroups,
714
+ expandedGroupIds: allIds(statusPageGroups),
715
+ });
716
+
717
+ expect(rowById(rows, REGION_ONE).canMoveUp).toBe(false);
718
+ expect(rowById(rows, REGION_ONE).canMoveDown).toBe(false);
719
+ });
720
+
721
+ /*
722
+ * A move is a change to the hierarchy, so it has to mean the same thing
723
+ * whatever the operator has typed into the search box. Reading neighbours
724
+ * off the filtered view would let "move up" swap two rows that are not
725
+ * actually adjacent.
726
+ */
727
+ test("neighbours are the real siblings, not the ones a search left visible", () => {
728
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
729
+
730
+ const rows: Array<StatusPageGroupHierarchyRow> =
731
+ StatusPageGroupHierarchyViewUtil.getRows({
732
+ statusPageGroups: statusPageGroups,
733
+ expandedGroupIds: allIds(statusPageGroups),
734
+ searchText: "Market 1002",
735
+ });
736
+
737
+ const marketTwo: StatusPageGroupHierarchyRow = rowById(rows, MARKET_TWO);
738
+
739
+ expect(marketTwo.previousSiblingId).toBe(MARKET_ONE.toString());
740
+ expect(marketTwo.canMoveUp).toBe(true);
741
+ });
742
+
743
+ test("moving up targets the order of the sibling above", () => {
744
+ expect(
745
+ StatusPageGroupHierarchyViewUtil.getReorderTargetOrder({
746
+ statusPageGroups: makeHierarchy(),
747
+ statusPageGroupId: MARKET_TWO.toString(),
748
+ direction: "up",
749
+ }),
750
+ ).toBe(3);
751
+ });
752
+
753
+ test("moving down targets the order of the sibling below", () => {
754
+ expect(
755
+ StatusPageGroupHierarchyViewUtil.getReorderTargetOrder({
756
+ statusPageGroups: makeHierarchy(),
757
+ statusPageGroupId: REGION_ONE.toString(),
758
+ direction: "down",
759
+ }),
760
+ ).toBe(6);
761
+ });
762
+
763
+ test("a group with no neighbour that way has nothing to write", () => {
764
+ expect(
765
+ StatusPageGroupHierarchyViewUtil.getReorderTargetOrder({
766
+ statusPageGroups: makeHierarchy(),
767
+ statusPageGroupId: MARKET_ONE.toString(),
768
+ direction: "up",
769
+ }),
770
+ ).toBeNull();
771
+
772
+ expect(
773
+ StatusPageGroupHierarchyViewUtil.getReorderTargetOrder({
774
+ statusPageGroups: makeHierarchy(),
775
+ statusPageGroupId: STANDALONE.toString(),
776
+ direction: "down",
777
+ }),
778
+ ).toBeNull();
779
+ });
780
+
781
+ test("a group that is not in the list at all has nothing to write", () => {
782
+ expect(
783
+ StatusPageGroupHierarchyViewUtil.getReorderTargetOrder({
784
+ statusPageGroups: makeHierarchy(),
785
+ statusPageGroupId: MISSING.toString(),
786
+ direction: "up",
787
+ }),
788
+ ).toBeNull();
789
+ });
790
+
791
+ /*
792
+ * `order` is nullable in the database. Writing `undefined` as a target order
793
+ * would blank the column rather than move the row.
794
+ */
795
+ test("a neighbour with no order of its own is not a move target", () => {
796
+ const statusPageGroups: Array<StatusPageGroup> = [
797
+ makeGroup({ id: CORPORATE, name: "Corporate" }),
798
+ makeGroup({ id: STANDALONE, name: "Standalone", order: 2 }),
799
+ ];
800
+
801
+ expect(
802
+ StatusPageGroupHierarchyViewUtil.getReorderTargetOrder({
803
+ statusPageGroups: statusPageGroups,
804
+ statusPageGroupId: STANDALONE.toString(),
805
+ direction: "up",
806
+ }),
807
+ ).toBeNull();
808
+ });
809
+
810
+ test("top level neighbours are read from the top level, not from a parent bucket", () => {
811
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
812
+
813
+ const siblings: Array<StatusPageGroup> =
814
+ StatusPageGroupHierarchyViewUtil.getSiblingGroups({
815
+ statusPageGroup: statusPageGroups[0]!,
816
+ statusPageGroups: statusPageGroups,
817
+ });
818
+
819
+ expect(
820
+ siblings.map((group: StatusPageGroup) => {
821
+ return group.name;
822
+ }),
823
+ ).toEqual(["Corporate", "Standalone"]);
824
+ });
825
+ });
826
+
827
+ describe("expansion helpers", () => {
828
+ test("every group with sub groups is expandable", () => {
829
+ const expandable: Set<string> =
830
+ StatusPageGroupHierarchyViewUtil.getExpandableGroupIds({
831
+ statusPageGroups: makeHierarchy(),
832
+ });
833
+
834
+ expect(expandable.has(CORPORATE.toString())).toBe(true);
835
+ expect(expandable.has(REGION_ONE.toString())).toBe(true);
836
+ expect(expandable.has(MARKET_ONE.toString())).toBe(true);
837
+ expect(expandable.has(MARKET_TWO.toString())).toBe(false);
838
+ expect(expandable.has(STANDALONE.toString())).toBe(false);
839
+ expect(expandable.size).toBe(3);
840
+ });
841
+
842
+ test("expanding everything really does draw every group", () => {
843
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
844
+
845
+ const rows: Array<StatusPageGroupHierarchyRow> =
846
+ StatusPageGroupHierarchyViewUtil.getRows({
847
+ statusPageGroups: statusPageGroups,
848
+ expandedGroupIds:
849
+ StatusPageGroupHierarchyViewUtil.getExpandableGroupIds({
850
+ statusPageGroups: statusPageGroups,
851
+ }),
852
+ });
853
+
854
+ expect(rows).toHaveLength(statusPageGroups.length);
855
+ });
856
+
857
+ /*
858
+ * A hierarchy that arrives fully expanded is unreadable at any real size,
859
+ * and one that arrives fully collapsed hides the fact that it nests at all -
860
+ * which is the single thing this page exists to show.
861
+ */
862
+ test("the default opens one level of children and no more", () => {
863
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
864
+
865
+ const expanded: Set<string> =
866
+ StatusPageGroupHierarchyViewUtil.getDefaultExpandedGroupIds({
867
+ statusPageGroups: statusPageGroups,
868
+ maxAutoExpandDepth: 1,
869
+ });
870
+
871
+ expect(expanded.has(CORPORATE.toString())).toBe(true);
872
+ expect(expanded.has(REGION_ONE.toString())).toBe(true);
873
+ expect(expanded.has(MARKET_ONE.toString())).toBe(false);
874
+
875
+ expect(
876
+ rowNames(
877
+ StatusPageGroupHierarchyViewUtil.getRows({
878
+ statusPageGroups: statusPageGroups,
879
+ expandedGroupIds: expanded,
880
+ }),
881
+ ),
882
+ ).toEqual([
883
+ "Corporate",
884
+ "Region 1000",
885
+ "Market 1001",
886
+ "Market 1002",
887
+ "Region 2000",
888
+ "Standalone",
889
+ ]);
890
+ });
891
+
892
+ test("a depth of zero opens only the top level", () => {
893
+ const expanded: Set<string> =
894
+ StatusPageGroupHierarchyViewUtil.getDefaultExpandedGroupIds({
895
+ statusPageGroups: makeHierarchy(),
896
+ maxAutoExpandDepth: 0,
897
+ });
898
+
899
+ expect(Array.from(expanded)).toEqual([CORPORATE.toString()]);
900
+ });
901
+
902
+ test("leaves are never in the expanded set - there is nothing to open", () => {
903
+ const expanded: Set<string> =
904
+ StatusPageGroupHierarchyViewUtil.getDefaultExpandedGroupIds({
905
+ statusPageGroups: makeHierarchy(),
906
+ maxAutoExpandDepth: 9,
907
+ });
908
+
909
+ expect(expanded.has(STANDALONE.toString())).toBe(false);
910
+ expect(expanded.has(UNIT.toString())).toBe(false);
911
+ });
912
+ });
913
+
914
+ describe("getSummary", () => {
915
+ test("counts the whole hierarchy and how deep it goes", () => {
916
+ const summary: StatusPageGroupHierarchySummary =
917
+ StatusPageGroupHierarchyViewUtil.getSummary({
918
+ statusPageGroups: makeHierarchy(),
919
+ });
920
+
921
+ expect(summary).toEqual({
922
+ totalCount: 7,
923
+ topLevelCount: 2,
924
+ nestedCount: 5,
925
+ levelCount: 4,
926
+ });
927
+ });
928
+
929
+ test("a flat list is one level deep, not zero", () => {
930
+ expect(
931
+ StatusPageGroupHierarchyViewUtil.getSummary({
932
+ statusPageGroups: [
933
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
934
+ makeGroup({ id: STANDALONE, name: "Standalone", order: 2 }),
935
+ ],
936
+ }),
937
+ ).toEqual({
938
+ totalCount: 2,
939
+ topLevelCount: 2,
940
+ nestedCount: 0,
941
+ levelCount: 1,
942
+ });
943
+ });
944
+
945
+ test("an empty status page has no levels", () => {
946
+ expect(
947
+ StatusPageGroupHierarchyViewUtil.getSummary({ statusPageGroups: [] }),
948
+ ).toEqual({
949
+ totalCount: 0,
950
+ topLevelCount: 0,
951
+ nestedCount: 0,
952
+ levelCount: 0,
953
+ });
954
+ });
955
+ });
956
+
957
+ describe("getGroupPathLabel", () => {
958
+ test("reads top down and ends on the group itself", () => {
959
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
960
+
961
+ expect(
962
+ StatusPageGroupHierarchyViewUtil.getGroupPathLabel({
963
+ statusPageGroup: statusPageGroups[3]!,
964
+ statusPageGroups: statusPageGroups,
965
+ }),
966
+ ).toBe(
967
+ ["Corporate", "Region 1000", "Market 1001", "Unit 0152"].join(
968
+ STATUS_PAGE_GROUP_PATH_SEPARATOR,
969
+ ),
970
+ );
971
+ });
972
+
973
+ test("a top level group is just its own name", () => {
974
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
975
+
976
+ expect(
977
+ StatusPageGroupHierarchyViewUtil.getGroupPathLabel({
978
+ statusPageGroup: statusPageGroups[0]!,
979
+ statusPageGroups: statusPageGroups,
980
+ }),
981
+ ).toBe("Corporate");
982
+ });
983
+
984
+ /*
985
+ * The rest of the admin - Resources, Monitor Rules, the CSV import - all
986
+ * spell a path the same way, and an operator reads them side by side.
987
+ */
988
+ test("uses the separator the rest of the admin uses", () => {
989
+ expect(STATUS_PAGE_GROUP_PATH_SEPARATOR).toBe(" › ");
990
+ });
991
+ });
992
+
993
+ describe("getSubtreeHeight", () => {
994
+ test("a leaf has no height", () => {
995
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
996
+
997
+ expect(
998
+ StatusPageGroupHierarchyViewUtil.getSubtreeHeight({
999
+ statusPageGroup: statusPageGroups[6]!,
1000
+ statusPageGroups: statusPageGroups,
1001
+ }),
1002
+ ).toBe(0);
1003
+ });
1004
+
1005
+ test("counts the levels below the group, not its own depth", () => {
1006
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
1007
+
1008
+ expect(
1009
+ StatusPageGroupHierarchyViewUtil.getSubtreeHeight({
1010
+ statusPageGroup: statusPageGroups[0]!,
1011
+ statusPageGroups: statusPageGroups,
1012
+ }),
1013
+ ).toBe(3);
1014
+
1015
+ expect(
1016
+ StatusPageGroupHierarchyViewUtil.getSubtreeHeight({
1017
+ statusPageGroup: statusPageGroups[1]!,
1018
+ statusPageGroups: statusPageGroups,
1019
+ }),
1020
+ ).toBe(2);
1021
+ });
1022
+ });
1023
+
1024
+ describe("getParentGroupCandidates", () => {
1025
+ test("creating a group may pick any group as its parent", () => {
1026
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
1027
+
1028
+ const candidates: Array<StatusPageGroup> =
1029
+ StatusPageGroupHierarchyViewUtil.getParentGroupCandidates({
1030
+ statusPageGroups: statusPageGroups,
1031
+ });
1032
+
1033
+ expect(
1034
+ candidates.map((group: StatusPageGroup) => {
1035
+ return group.name;
1036
+ }),
1037
+ ).toEqual([
1038
+ "Corporate",
1039
+ "Region 1000",
1040
+ "Market 1001",
1041
+ "Unit 0152",
1042
+ "Market 1002",
1043
+ "Region 2000",
1044
+ "Standalone",
1045
+ ]);
1046
+ });
1047
+
1048
+ /* "A group cannot be its own parent group." - StatusPageGroupService. */
1049
+ test("a group is never offered itself", () => {
1050
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
1051
+
1052
+ const candidateIds: Array<string | undefined> =
1053
+ StatusPageGroupHierarchyViewUtil.getParentGroupCandidates({
1054
+ statusPageGroups: statusPageGroups,
1055
+ statusPageGroupId: REGION_ONE.toString(),
1056
+ }).map((group: StatusPageGroup) => {
1057
+ return group._id?.toString();
1058
+ });
1059
+
1060
+ expect(candidateIds).not.toContain(REGION_ONE.toString());
1061
+ });
1062
+
1063
+ /*
1064
+ * "This group cannot be nested under one of its own sub groups." A cycle
1065
+ * would make every rolled up number on the status page meaningless, so the
1066
+ * picker does not offer the move in the first place.
1067
+ */
1068
+ test("a group is never offered one of its own sub groups", () => {
1069
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
1070
+
1071
+ const candidateIds: Array<string | undefined> =
1072
+ StatusPageGroupHierarchyViewUtil.getParentGroupCandidates({
1073
+ statusPageGroups: statusPageGroups,
1074
+ statusPageGroupId: REGION_ONE.toString(),
1075
+ }).map((group: StatusPageGroup) => {
1076
+ return group._id?.toString();
1077
+ });
1078
+
1079
+ expect(candidateIds).not.toContain(MARKET_ONE.toString());
1080
+ expect(candidateIds).not.toContain(MARKET_TWO.toString());
1081
+ expect(candidateIds).not.toContain(UNIT.toString());
1082
+ expect(candidateIds).toContain(CORPORATE.toString());
1083
+ expect(candidateIds).toContain(REGION_TWO.toString());
1084
+ expect(candidateIds).toContain(STANDALONE.toString());
1085
+ });
1086
+
1087
+ /*
1088
+ * The service refuses a move when the parent's depth plus the moved
1089
+ * subtree's own height reaches MaxNestingDepth. The picker applies exactly
1090
+ * the same arithmetic so it cannot offer a parent the API will reject.
1091
+ */
1092
+ test("a parent too deep to hold the group is not offered", () => {
1093
+ const statusPageGroups: Array<StatusPageGroup> = [];
1094
+ const ids: Array<ObjectID> = [];
1095
+
1096
+ for (
1097
+ let level: number = 0;
1098
+ level < StatusPageGroupTreeUtil.MaxNestingDepth;
1099
+ level++
1100
+ ) {
1101
+ const id: ObjectID = new ObjectID(
1102
+ `bbbbbbbb-0000-4000-8000-${level.toString().padStart(12, "0")}`,
1103
+ );
1104
+ ids.push(id);
1105
+
1106
+ statusPageGroups.push(
1107
+ makeGroup({
1108
+ id: id,
1109
+ name: `Level ${level}`,
1110
+ order: level + 1,
1111
+ ...(level === 0 ? {} : { parentId: ids[level - 1]! }),
1112
+ }),
1113
+ );
1114
+ }
1115
+
1116
+ const loose: ObjectID = new ObjectID(
1117
+ "cccccccc-0000-4000-8000-000000000000",
1118
+ );
1119
+ statusPageGroups.push(
1120
+ makeGroup({ id: loose, name: "Loose", order: 100 }),
1121
+ );
1122
+
1123
+ const candidateNames: Array<string | undefined> =
1124
+ StatusPageGroupHierarchyViewUtil.getParentGroupCandidates({
1125
+ statusPageGroups: statusPageGroups,
1126
+ statusPageGroupId: loose.toString(),
1127
+ }).map((group: StatusPageGroup) => {
1128
+ return group.name;
1129
+ });
1130
+
1131
+ /* The deepest legal parent leaves room for exactly one more level. */
1132
+ expect(candidateNames).toContain(
1133
+ `Level ${StatusPageGroupTreeUtil.MaxNestingDepth - 2}`,
1134
+ );
1135
+ expect(candidateNames).not.toContain(
1136
+ `Level ${StatusPageGroupTreeUtil.MaxNestingDepth - 1}`,
1137
+ );
1138
+ });
1139
+
1140
+ test("a group carrying a tall subtree loses parents a leaf could still use", () => {
1141
+ const statusPageGroups: Array<StatusPageGroup> = [];
1142
+ const ids: Array<ObjectID> = [];
1143
+
1144
+ for (let level: number = 0; level < 8; level++) {
1145
+ const id: ObjectID = new ObjectID(
1146
+ `dddddddd-0000-4000-8000-${level.toString().padStart(12, "0")}`,
1147
+ );
1148
+ ids.push(id);
1149
+
1150
+ statusPageGroups.push(
1151
+ makeGroup({
1152
+ id: id,
1153
+ name: `Level ${level}`,
1154
+ order: level + 1,
1155
+ ...(level === 0 ? {} : { parentId: ids[level - 1]! }),
1156
+ }),
1157
+ );
1158
+ }
1159
+
1160
+ const branchRoot: ObjectID = new ObjectID(
1161
+ "eeeeeeee-0000-4000-8000-000000000000",
1162
+ );
1163
+ const branchChild: ObjectID = new ObjectID(
1164
+ "eeeeeeee-0000-4000-8000-000000000001",
1165
+ );
1166
+ const branchGrandChild: ObjectID = new ObjectID(
1167
+ "eeeeeeee-0000-4000-8000-000000000002",
1168
+ );
1169
+
1170
+ statusPageGroups.push(
1171
+ makeGroup({ id: branchRoot, name: "Branch", order: 50 }),
1172
+ makeGroup({
1173
+ id: branchChild,
1174
+ name: "Branch Child",
1175
+ parentId: branchRoot,
1176
+ order: 51,
1177
+ }),
1178
+ makeGroup({
1179
+ id: branchGrandChild,
1180
+ name: "Branch Grandchild",
1181
+ parentId: branchChild,
1182
+ order: 52,
1183
+ }),
1184
+ );
1185
+
1186
+ const leafCandidates: Array<string | undefined> =
1187
+ StatusPageGroupHierarchyViewUtil.getParentGroupCandidates({
1188
+ statusPageGroups: statusPageGroups,
1189
+ statusPageGroupId: branchGrandChild.toString(),
1190
+ }).map((group: StatusPageGroup) => {
1191
+ return group.name;
1192
+ });
1193
+
1194
+ const branchCandidates: Array<string | undefined> =
1195
+ StatusPageGroupHierarchyViewUtil.getParentGroupCandidates({
1196
+ statusPageGroups: statusPageGroups,
1197
+ statusPageGroupId: branchRoot.toString(),
1198
+ }).map((group: StatusPageGroup) => {
1199
+ return group.name;
1200
+ });
1201
+
1202
+ expect(leafCandidates).toContain("Level 7");
1203
+ expect(branchCandidates).not.toContain("Level 7");
1204
+ expect(branchCandidates).toContain("Level 5");
1205
+ });
1206
+
1207
+ test("a group id that is not on this status page excludes nothing", () => {
1208
+ expect(
1209
+ StatusPageGroupHierarchyViewUtil.getParentGroupCandidates({
1210
+ statusPageGroups: makeHierarchy(),
1211
+ statusPageGroupId: MISSING.toString(),
1212
+ }),
1213
+ ).toHaveLength(7);
1214
+ });
1215
+ });
1216
+
1217
+ /*
1218
+ * The parent pointer is a plain nullable column. Rows written straight to the
1219
+ * database, or a group whose parent the page did not fetch, can point at a
1220
+ * missing parent, at themselves, or around a cycle - and a group that
1221
+ * disappears from this page is a group nobody can repair.
1222
+ */
1223
+ describe("data the database allows and the page has to survive", () => {
1224
+ test("a group that points at itself renders at the top level", () => {
1225
+ const selfParented: StatusPageGroup = makeGroup({
1226
+ id: CORPORATE,
1227
+ name: "Corporate",
1228
+ parentId: CORPORATE,
1229
+ order: 1,
1230
+ });
1231
+
1232
+ const rows: Array<StatusPageGroupHierarchyRow> =
1233
+ StatusPageGroupHierarchyViewUtil.getRows({
1234
+ statusPageGroups: [selfParented],
1235
+ });
1236
+
1237
+ expect(rows).toHaveLength(1);
1238
+ expect(rows[0]!.depth).toBe(0);
1239
+ });
1240
+
1241
+ test("a group whose parent was never fetched still renders", () => {
1242
+ const orphan: StatusPageGroup = makeGroup({
1243
+ id: REGION_ONE,
1244
+ name: "Region 1000",
1245
+ parentId: MISSING,
1246
+ order: 2,
1247
+ });
1248
+
1249
+ const rows: Array<StatusPageGroupHierarchyRow> =
1250
+ StatusPageGroupHierarchyViewUtil.getRows({
1251
+ statusPageGroups: [
1252
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
1253
+ orphan,
1254
+ ],
1255
+ });
1256
+
1257
+ expect(rowNames(rows)).toEqual(["Corporate", "Region 1000"]);
1258
+ expect(rowById(rows, REGION_ONE).depth).toBe(0);
1259
+ });
1260
+
1261
+ test("an orphan can still be reordered against the top level", () => {
1262
+ const statusPageGroups: Array<StatusPageGroup> = [
1263
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
1264
+ makeGroup({
1265
+ id: REGION_ONE,
1266
+ name: "Region 1000",
1267
+ parentId: MISSING,
1268
+ order: 2,
1269
+ }),
1270
+ ];
1271
+
1272
+ const rows: Array<StatusPageGroupHierarchyRow> =
1273
+ StatusPageGroupHierarchyViewUtil.getRows({
1274
+ statusPageGroups: statusPageGroups,
1275
+ });
1276
+
1277
+ expect(rowById(rows, REGION_ONE).canMoveUp).toBe(true);
1278
+ expect(
1279
+ StatusPageGroupHierarchyViewUtil.getReorderTargetOrder({
1280
+ statusPageGroups: statusPageGroups,
1281
+ statusPageGroupId: REGION_ONE.toString(),
1282
+ direction: "up",
1283
+ }),
1284
+ ).toBe(1);
1285
+ });
1286
+
1287
+ test("a cycle neither hangs the walk nor loses a group", () => {
1288
+ const statusPageGroups: Array<StatusPageGroup> = [
1289
+ makeGroup({
1290
+ id: CORPORATE,
1291
+ name: "Corporate",
1292
+ parentId: REGION_ONE,
1293
+ order: 1,
1294
+ }),
1295
+ makeGroup({
1296
+ id: REGION_ONE,
1297
+ name: "Region 1000",
1298
+ parentId: CORPORATE,
1299
+ order: 2,
1300
+ }),
1301
+ ];
1302
+
1303
+ const rows: Array<StatusPageGroupHierarchyRow> =
1304
+ StatusPageGroupHierarchyViewUtil.getRows({
1305
+ statusPageGroups: statusPageGroups,
1306
+ expandedGroupIds: allIds(statusPageGroups),
1307
+ });
1308
+
1309
+ expect(rows).toHaveLength(2);
1310
+ expect(rowNames(rows).sort()).toEqual(["Corporate", "Region 1000"]);
1311
+ });
1312
+
1313
+ test("a cycle does not hang the summary either", () => {
1314
+ const statusPageGroups: Array<StatusPageGroup> = [
1315
+ makeGroup({
1316
+ id: CORPORATE,
1317
+ name: "Corporate",
1318
+ parentId: REGION_ONE,
1319
+ order: 1,
1320
+ }),
1321
+ makeGroup({
1322
+ id: REGION_ONE,
1323
+ name: "Region 1000",
1324
+ parentId: CORPORATE,
1325
+ order: 2,
1326
+ }),
1327
+ ];
1328
+
1329
+ expect(
1330
+ StatusPageGroupHierarchyViewUtil.getSummary({
1331
+ statusPageGroups: statusPageGroups,
1332
+ }).totalCount,
1333
+ ).toBe(2);
1334
+ });
1335
+
1336
+ test("groups without an order keep the position they arrived in", () => {
1337
+ const statusPageGroups: Array<StatusPageGroup> = [
1338
+ makeGroup({ id: STANDALONE, name: "Standalone" }),
1339
+ makeGroup({ id: CORPORATE, name: "Corporate" }),
1340
+ ];
1341
+
1342
+ expect(
1343
+ rowNames(
1344
+ StatusPageGroupHierarchyViewUtil.getRows({
1345
+ statusPageGroups: statusPageGroups,
1346
+ }),
1347
+ ),
1348
+ ).toEqual(["Standalone", "Corporate"]);
1349
+ });
1350
+
1351
+ test("every group appears exactly once, whatever shape the data is in", () => {
1352
+ const statusPageGroups: Array<StatusPageGroup> = [
1353
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
1354
+ makeGroup({
1355
+ id: REGION_ONE,
1356
+ name: "Region 1000",
1357
+ parentId: CORPORATE,
1358
+ order: 2,
1359
+ }),
1360
+ makeGroup({
1361
+ id: REGION_TWO,
1362
+ name: "Region 2000",
1363
+ parentId: MISSING,
1364
+ order: 3,
1365
+ }),
1366
+ makeGroup({
1367
+ id: MARKET_ONE,
1368
+ name: "Market 1001",
1369
+ parentId: MARKET_TWO,
1370
+ order: 4,
1371
+ }),
1372
+ makeGroup({
1373
+ id: MARKET_TWO,
1374
+ name: "Market 1002",
1375
+ parentId: MARKET_ONE,
1376
+ order: 5,
1377
+ }),
1378
+ makeGroup({
1379
+ id: UNIT,
1380
+ name: "Unit 0152",
1381
+ parentId: UNIT,
1382
+ order: 6,
1383
+ }),
1384
+ ];
1385
+
1386
+ const rows: Array<StatusPageGroupHierarchyRow> =
1387
+ StatusPageGroupHierarchyViewUtil.getRows({
1388
+ statusPageGroups: statusPageGroups,
1389
+ expandedGroupIds: allIds(statusPageGroups),
1390
+ });
1391
+
1392
+ expect(rows).toHaveLength(statusPageGroups.length);
1393
+ expect(new Set<string>(rowNames(rows)).size).toBe(
1394
+ statusPageGroups.length,
1395
+ );
1396
+ });
1397
+ });
1398
+
1399
+ describe("what the row hands the renderer", () => {
1400
+ test("the group itself comes along, so the row can draw its settings", () => {
1401
+ const grid: StatusPageGroup = makeGroup({
1402
+ id: CORPORATE,
1403
+ name: "Corporate",
1404
+ order: 1,
1405
+ viewMode: StatusPageGroupViewMode.Grid,
1406
+ description: "The whole corporate unit",
1407
+ });
1408
+
1409
+ const rows: Array<StatusPageGroupHierarchyRow> =
1410
+ StatusPageGroupHierarchyViewUtil.getRows({
1411
+ statusPageGroups: [grid],
1412
+ });
1413
+
1414
+ expect(rows[0]!.statusPageGroup).toBe(grid);
1415
+ expect(rows[0]!.statusPageGroup.viewMode).toBe(
1416
+ StatusPageGroupViewMode.Grid,
1417
+ );
1418
+ expect(rows[0]!.statusPageGroup.description).toBe(
1419
+ "The whole corporate unit",
1420
+ );
1421
+ });
1422
+
1423
+ /*
1424
+ * Callers hold the expanded set in React state and hand the same Set in on
1425
+ * every render; a util that wrote into it would be mutating state.
1426
+ */
1427
+ test("the expanded set handed in is never written to", () => {
1428
+ const statusPageGroups: Array<StatusPageGroup> = makeHierarchy();
1429
+ const expanded: Set<string> = new Set<string>([CORPORATE.toString()]);
1430
+
1431
+ StatusPageGroupHierarchyViewUtil.getRows({
1432
+ statusPageGroups: statusPageGroups,
1433
+ expandedGroupIds: expanded,
1434
+ });
1435
+
1436
+ expect(Array.from(expanded)).toEqual([CORPORATE.toString()]);
1437
+ });
1438
+
1439
+ test("the group list handed in is never reordered in place", () => {
1440
+ const statusPageGroups: Array<StatusPageGroup> = [
1441
+ makeGroup({ id: STANDALONE, name: "Standalone", order: 9 }),
1442
+ makeGroup({ id: CORPORATE, name: "Corporate", order: 1 }),
1443
+ ];
1444
+
1445
+ StatusPageGroupHierarchyViewUtil.getRows({
1446
+ statusPageGroups: statusPageGroups,
1447
+ });
1448
+
1449
+ expect(statusPageGroups[0]!.name).toBe("Standalone");
1450
+ });
1451
+ });
1452
+ });