@oneuptime/common 11.7.2 → 11.7.4

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 (414) hide show
  1. package/Models/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
  2. package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
  3. package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
  4. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  5. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  6. package/Models/DatabaseModels/Index.ts +5 -0
  7. package/Models/DatabaseModels/LogDropFilter.ts +89 -0
  8. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +13 -3
  9. package/Models/DatabaseModels/RecommendationDismissal.ts +463 -0
  10. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  11. package/Models/DatabaseModels/StatusPageGroup.ts +95 -0
  12. package/Models/DatabaseModels/TableView.ts +40 -0
  13. package/Models/DatabaseModels/TraceDropFilter.ts +89 -0
  14. package/Models/DatabaseModels/WorkflowVariable.ts +5 -1
  15. package/Server/API/AlertAPI.ts +32 -17
  16. package/Server/API/BaseAPI.ts +31 -2
  17. package/Server/API/BaseAnalyticsAPI.ts +36 -2
  18. package/Server/API/CommonAPI.ts +57 -0
  19. package/Server/API/IncidentAPI.ts +60 -33
  20. package/Server/API/IncidentEpisodeAPI.ts +31 -16
  21. package/Server/API/MonitorAPI.ts +30 -0
  22. package/Server/API/MonitorTemplateAPI.ts +8 -0
  23. package/Server/API/ProbeAPI.ts +196 -15
  24. package/Server/API/ProjectAPI.ts +79 -1
  25. package/Server/API/ScheduledMaintenanceAPI.ts +33 -18
  26. package/Server/API/StatusPageAPI.ts +74 -37
  27. package/Server/API/WorkspaceNotificationRuleAPI.ts +34 -4
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.ts +527 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.ts +31 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.ts +55 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.ts +53 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  34. package/Server/Services/AlertEpisodeService.ts +47 -0
  35. package/Server/Services/AlertService.ts +125 -0
  36. package/Server/Services/BillingService.ts +98 -0
  37. package/Server/Services/DatabaseService.ts +184 -20
  38. package/Server/Services/IncidentEpisodeService.ts +47 -0
  39. package/Server/Services/IncidentService.ts +123 -0
  40. package/Server/Services/IncidentTemplateService.ts +123 -1
  41. package/Server/Services/Index.ts +2 -0
  42. package/Server/Services/LogDropFilterService.ts +133 -0
  43. package/Server/Services/MonitorProbeService.ts +94 -4
  44. package/Server/Services/MonitorService.ts +48 -43
  45. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +70 -0
  46. package/Server/Services/ProbeService.ts +66 -0
  47. package/Server/Services/ProjectService.ts +131 -7
  48. package/Server/Services/RecommendationDismissalService.ts +52 -0
  49. package/Server/Services/RunbookAgentJobService.ts +68 -7
  50. package/Server/Services/ScheduledMaintenanceService.ts +103 -0
  51. package/Server/Services/ScheduledMaintenanceTemplateService.ts +90 -0
  52. package/Server/Services/StatusPageGroupService.ts +186 -0
  53. package/Server/Services/TraceDropFilterService.ts +133 -0
  54. package/Server/Types/Workflow/Components/API/Delete.ts +12 -0
  55. package/Server/Types/Workflow/Components/API/Get.ts +12 -0
  56. package/Server/Types/Workflow/Components/API/Patch.ts +12 -0
  57. package/Server/Types/Workflow/Components/API/Post.ts +12 -0
  58. package/Server/Types/Workflow/Components/API/Put.ts +13 -0
  59. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +212 -0
  60. package/Server/Utils/DropFilterValidation.ts +127 -0
  61. package/Server/Utils/Errors.ts +5 -0
  62. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  63. package/Server/Utils/Monitor/MonitorIncident.ts +31 -2
  64. package/Server/Utils/Telemetry/AppMetrics.ts +31 -0
  65. package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
  66. package/Tests/Models/DatabaseModels/MonitorProbeColumnAccessControl.test.ts +196 -0
  67. package/Tests/Models/RecommendationDismissalModel.test.ts +269 -0
  68. package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +320 -0
  69. package/Tests/Server/API/BaseAnalyticsAPIUpdatePayloadValidation.test.ts +293 -0
  70. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +164 -0
  71. package/Tests/Server/API/ProbeAPI.test.ts +422 -53
  72. package/Tests/Server/API/ProjectAPI.test.ts +180 -0
  73. package/Tests/Server/API/ProjectAPIExtendTrialBillingDisabled.test.ts +138 -0
  74. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +458 -0
  75. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +359 -0
  76. package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +427 -0
  77. package/Tests/Server/Services/BillingServiceExtendTrial.test.ts +439 -0
  78. package/Tests/Server/Services/CrossProjectReferenceWriteGuard.test.ts +810 -0
  79. package/Tests/Server/Services/DatabaseServiceAtomicAddToColumns.test.ts +269 -0
  80. package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +277 -0
  81. package/Tests/Server/Services/DatabaseServiceSortColumnSelect.test.ts +201 -0
  82. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +81 -23
  83. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +398 -0
  84. package/Tests/Server/Services/MonitorProbeDeleteHooks.test.ts +280 -0
  85. package/Tests/Server/Services/MonitorProbeServiceProbeTenancy.test.ts +304 -0
  86. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +262 -0
  87. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +614 -0
  88. package/Tests/Server/Services/RecommendationDismissalService.test.ts +306 -0
  89. package/Tests/Server/Services/RepairCrossProjectIncidentReferencesMigration.test.ts +528 -0
  90. package/Tests/Server/Services/StatusPageGroupNesting.test.ts +395 -0
  91. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +26 -21
  92. package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +227 -0
  93. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +285 -0
  94. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +463 -0
  95. package/Tests/Server/Utils/DropFilterValidation.test.ts +269 -0
  96. package/Tests/Server/Utils/Telemetry/SpanUtil.test.ts +94 -0
  97. package/Tests/Types/DateUserTimezone.test.ts +52 -0
  98. package/Tests/Types/JSONFunctions.test.ts +103 -1
  99. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +177 -9
  100. package/Tests/Types/Monitor/MonitorStepDnsMonitor.test.ts +105 -0
  101. package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +131 -0
  102. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +60 -0
  103. package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +82 -0
  104. package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +268 -0
  105. package/Tests/Types/Monitor/MonitorStepMetricMonitor.test.ts +386 -0
  106. package/Tests/Types/Monitor/MonitorStepProfileMonitor.test.ts +185 -0
  107. package/Tests/Types/Monitor/MonitorStepSnmpMonitor.test.ts +156 -0
  108. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +371 -0
  109. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +901 -0
  110. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +607 -0
  111. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +754 -0
  112. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +960 -0
  113. package/Tests/Types/RollingTime/RollingTimeUtil.test.ts +186 -0
  114. package/Tests/Types/Runbook/RunbookStepTimeout.test.ts +410 -0
  115. package/Tests/Types/Telemetry/DropFilterSampling.test.ts +241 -0
  116. package/Tests/Types/Workspace/NotificationRuleConditionUtil.test.ts +353 -0
  117. package/Tests/Types/Workspace/NotificationRuleUtil.test.ts +566 -0
  118. package/Tests/UI/Components/CodeEditor.test.tsx +174 -0
  119. package/Tests/UI/Components/ErrorBoundary.test.tsx +555 -24
  120. package/Tests/UI/Components/InfoCard.test.tsx +358 -0
  121. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +138 -0
  122. package/Tests/UI/Components/Modal.test.tsx +43 -5
  123. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
  124. package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +397 -0
  125. package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +999 -0
  126. package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
  127. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
  128. package/Tests/UI/Components/Runbook/StepTimeoutInput.test.tsx +386 -0
  129. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +671 -0
  130. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +404 -0
  131. package/Tests/UI/Utils/DropdownAlignment.test.ts +306 -0
  132. package/Tests/UI/Utils/TableFilterUrlState.test.ts +292 -0
  133. package/Tests/Utils/Monitor/MonitorSummaryProbeUtil.test.ts +263 -0
  134. package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +583 -0
  135. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +1072 -0
  136. package/Tests/Utils/StatusPage/GroupTree.test.ts +583 -0
  137. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +411 -0
  138. package/Tests/Utils/UserPreferences.test.ts +563 -0
  139. package/Types/Date.ts +15 -6
  140. package/Types/JSONFunctions.ts +7 -2
  141. package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
  142. package/Types/Monitor/KubernetesAlertTemplates.ts +205 -0
  143. package/Types/Monitor/MonitorStep.ts +10 -1
  144. package/Types/Monitor/MonitorStepMetricMonitor.ts +46 -1
  145. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +413 -0
  146. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +168 -0
  147. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +175 -0
  148. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +743 -0
  149. package/Types/Permission.ts +45 -0
  150. package/Types/Recommendation/RecommendationType.ts +20 -0
  151. package/Types/Runbook/RunbookExecutionDeadline.ts +129 -0
  152. package/Types/Runbook/RunbookStepTimeout.ts +202 -0
  153. package/Types/Telemetry/DropFilterSampling.ts +92 -0
  154. package/UI/Components/AI/GenerateFromAIModal.tsx +0 -2
  155. package/UI/Components/Accordion/Accordion.tsx +10 -2
  156. package/UI/Components/BulkUpdate/BulkLabelActions.tsx +0 -1
  157. package/UI/Components/CodeEditor/CodeEditor.tsx +34 -20
  158. package/UI/Components/Date/RangeStartAndEndDateView.tsx +0 -1
  159. package/UI/Components/EditionLabel/EditionLabel.tsx +1 -27
  160. package/UI/Components/Error/PageError.tsx +0 -4
  161. package/UI/Components/ErrorBoundary.tsx +252 -15
  162. package/UI/Components/InfoCard/InfoCard.tsx +58 -1
  163. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +34 -1
  164. package/UI/Components/MasterPage/MasterPage.tsx +65 -0
  165. package/UI/Components/Modal/Modal.tsx +16 -60
  166. package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
  167. package/UI/Components/ModelTable/BaseModelTable.tsx +503 -71
  168. package/UI/Components/ModelTable/Column.ts +17 -0
  169. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +523 -0
  170. package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
  171. package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
  172. package/UI/Components/ModelTable/TableView.tsx +24 -2
  173. package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
  174. package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
  175. package/UI/Components/SideMenu/SideMenu.tsx +24 -4
  176. package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -0
  177. package/UI/Components/Table/Table.tsx +14 -1
  178. package/UI/Components/Table/TableRow.tsx +180 -175
  179. package/UI/Components/Table/Types/Column.ts +2 -0
  180. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +34 -1
  181. package/UI/Utils/DropdownAlignment.ts +162 -0
  182. package/UI/Utils/TableFilterUrlState.ts +59 -20
  183. package/Utils/API.ts +106 -2
  184. package/Utils/Monitor/MonitorSummaryProbeUtil.ts +130 -0
  185. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +434 -0
  186. package/Utils/StatusPage/GroupNestingLayout.ts +343 -0
  187. package/Utils/StatusPage/GroupTree.ts +307 -0
  188. package/Utils/StatusPage/ResourceUptime.ts +90 -17
  189. package/Utils/UserPreferences.ts +53 -0
  190. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
  191. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
  193. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
  195. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  197. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  199. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  201. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/LogDropFilter.js +91 -0
  203. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +14 -4
  205. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/RecommendationDismissal.js +484 -0
  207. package/build/dist/Models/DatabaseModels/RecommendationDismissal.js.map +1 -0
  208. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  209. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/StatusPageGroup.js +94 -0
  211. package/build/dist/Models/DatabaseModels/StatusPageGroup.js.map +1 -1
  212. package/build/dist/Models/DatabaseModels/TableView.js +40 -0
  213. package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
  214. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +91 -0
  215. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  216. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +5 -1
  217. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  218. package/build/dist/Server/API/AlertAPI.js +22 -13
  219. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  220. package/build/dist/Server/API/BaseAPI.js +22 -1
  221. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  222. package/build/dist/Server/API/BaseAnalyticsAPI.js +24 -1
  223. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  224. package/build/dist/Server/API/CommonAPI.js +42 -0
  225. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  226. package/build/dist/Server/API/IncidentAPI.js +43 -26
  227. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  228. package/build/dist/Server/API/IncidentEpisodeAPI.js +21 -12
  229. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  230. package/build/dist/Server/API/MonitorAPI.js +23 -0
  231. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  232. package/build/dist/Server/API/MonitorTemplateAPI.js +4 -0
  233. package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -1
  234. package/build/dist/Server/API/ProbeAPI.js +131 -15
  235. package/build/dist/Server/API/ProbeAPI.js.map +1 -1
  236. package/build/dist/Server/API/ProjectAPI.js +62 -3
  237. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  238. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +23 -14
  239. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  240. package/build/dist/Server/API/StatusPageAPI.js +61 -33
  241. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  242. package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js +26 -2
  243. package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js.map +1 -1
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
  245. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
  246. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js +397 -0
  247. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js.map +1 -0
  248. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js +16 -0
  249. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js.map +1 -0
  250. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js +30 -0
  251. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js.map +1 -0
  252. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js +34 -0
  253. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js.map +1 -0
  254. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  255. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  256. package/build/dist/Server/Services/AlertEpisodeService.js +39 -0
  257. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  258. package/build/dist/Server/Services/AlertService.js +95 -0
  259. package/build/dist/Server/Services/AlertService.js.map +1 -1
  260. package/build/dist/Server/Services/BillingService.js +71 -0
  261. package/build/dist/Server/Services/BillingService.js.map +1 -1
  262. package/build/dist/Server/Services/DatabaseService.js +140 -17
  263. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  264. package/build/dist/Server/Services/IncidentEpisodeService.js +39 -0
  265. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  266. package/build/dist/Server/Services/IncidentService.js +100 -0
  267. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  268. package/build/dist/Server/Services/IncidentTemplateService.js +97 -0
  269. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  270. package/build/dist/Server/Services/Index.js +2 -0
  271. package/build/dist/Server/Services/Index.js.map +1 -1
  272. package/build/dist/Server/Services/LogDropFilterService.js +93 -0
  273. package/build/dist/Server/Services/LogDropFilterService.js.map +1 -1
  274. package/build/dist/Server/Services/MonitorProbeService.js +73 -5
  275. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  276. package/build/dist/Server/Services/MonitorService.js +34 -39
  277. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  278. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +71 -0
  279. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  280. package/build/dist/Server/Services/ProbeService.js +64 -0
  281. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  282. package/build/dist/Server/Services/ProjectService.js +103 -7
  283. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  284. package/build/dist/Server/Services/RecommendationDismissalService.js +50 -0
  285. package/build/dist/Server/Services/RecommendationDismissalService.js.map +1 -0
  286. package/build/dist/Server/Services/RunbookAgentJobService.js +63 -5
  287. package/build/dist/Server/Services/RunbookAgentJobService.js.map +1 -1
  288. package/build/dist/Server/Services/ScheduledMaintenanceService.js +83 -0
  289. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  290. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +65 -0
  291. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  292. package/build/dist/Server/Services/StatusPageGroupService.js +136 -0
  293. package/build/dist/Server/Services/StatusPageGroupService.js.map +1 -1
  294. package/build/dist/Server/Services/TraceDropFilterService.js +93 -0
  295. package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -1
  296. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +11 -0
  297. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  298. package/build/dist/Server/Types/Workflow/Components/API/Get.js +11 -0
  299. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  300. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +11 -0
  301. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  302. package/build/dist/Server/Types/Workflow/Components/API/Post.js +11 -0
  303. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  304. package/build/dist/Server/Types/Workflow/Components/API/Put.js +12 -0
  305. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  306. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +132 -0
  307. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -0
  308. package/build/dist/Server/Utils/DropFilterValidation.js +80 -0
  309. package/build/dist/Server/Utils/DropFilterValidation.js.map +1 -0
  310. package/build/dist/Server/Utils/Errors.js +3 -0
  311. package/build/dist/Server/Utils/Errors.js.map +1 -1
  312. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +30 -12
  313. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  314. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +38 -18
  315. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  316. package/build/dist/Server/Utils/Telemetry/AppMetrics.js +26 -0
  317. package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
  318. package/build/dist/Types/Date.js +14 -4
  319. package/build/dist/Types/Date.js.map +1 -1
  320. package/build/dist/Types/JSONFunctions.js +8 -3
  321. package/build/dist/Types/JSONFunctions.js.map +1 -1
  322. package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
  323. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +192 -0
  324. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  325. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  326. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  327. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +32 -1
  328. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
  329. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +294 -0
  330. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -0
  331. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +110 -0
  332. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -0
  333. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +58 -0
  334. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -0
  335. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +451 -0
  336. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -0
  337. package/build/dist/Types/Permission.js +40 -0
  338. package/build/dist/Types/Permission.js.map +1 -1
  339. package/build/dist/Types/Recommendation/RecommendationType.js +21 -0
  340. package/build/dist/Types/Recommendation/RecommendationType.js.map +1 -0
  341. package/build/dist/Types/Runbook/RunbookExecutionDeadline.js +93 -0
  342. package/build/dist/Types/Runbook/RunbookExecutionDeadline.js.map +1 -0
  343. package/build/dist/Types/Runbook/RunbookStepTimeout.js +142 -0
  344. package/build/dist/Types/Runbook/RunbookStepTimeout.js.map +1 -0
  345. package/build/dist/Types/Telemetry/DropFilterSampling.js +75 -0
  346. package/build/dist/Types/Telemetry/DropFilterSampling.js.map +1 -0
  347. package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -2
  348. package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
  349. package/build/dist/UI/Components/Accordion/Accordion.js +9 -2
  350. package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
  351. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +1 -1
  352. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
  353. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +27 -14
  354. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  355. package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js +1 -1
  356. package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js.map +1 -1
  357. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +2 -20
  358. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  359. package/build/dist/UI/Components/Error/PageError.js +1 -3
  360. package/build/dist/UI/Components/Error/PageError.js.map +1 -1
  361. package/build/dist/UI/Components/ErrorBoundary.js +145 -11
  362. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  363. package/build/dist/UI/Components/InfoCard/InfoCard.js +28 -1
  364. package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
  365. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +13 -2
  366. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  367. package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
  368. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  369. package/build/dist/UI/Components/Modal/Modal.js +4 -32
  370. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  371. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  372. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +308 -40
  373. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  374. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +230 -0
  375. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
  376. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
  377. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
  378. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
  379. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
  380. package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
  381. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  382. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
  383. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
  384. package/build/dist/UI/Components/Runbook/StepTimeoutInput.js +101 -0
  385. package/build/dist/UI/Components/Runbook/StepTimeoutInput.js.map +1 -0
  386. package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
  387. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  388. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +73 -0
  389. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
  390. package/build/dist/UI/Components/Table/Table.js +15 -2
  391. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  392. package/build/dist/UI/Components/Table/TableRow.js +11 -6
  393. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  394. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +13 -2
  395. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  396. package/build/dist/UI/Utils/DropdownAlignment.js +98 -0
  397. package/build/dist/UI/Utils/DropdownAlignment.js.map +1 -0
  398. package/build/dist/UI/Utils/TableFilterUrlState.js +48 -15
  399. package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
  400. package/build/dist/Utils/API.js +39 -2
  401. package/build/dist/Utils/API.js.map +1 -1
  402. package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js +97 -0
  403. package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js.map +1 -0
  404. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +330 -0
  405. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -0
  406. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +280 -0
  407. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -0
  408. package/build/dist/Utils/StatusPage/GroupTree.js +214 -0
  409. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -0
  410. package/build/dist/Utils/StatusPage/ResourceUptime.js +60 -16
  411. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  412. package/build/dist/Utils/UserPreferences.js +33 -0
  413. package/build/dist/Utils/UserPreferences.js.map +1 -1
  414. package/package.json +1 -1
@@ -0,0 +1,343 @@
1
+ import StatusPageGroup from "../../Models/DatabaseModels/StatusPageGroup";
2
+ import StatusPageResource from "../../Models/DatabaseModels/StatusPageResource";
3
+ import StatusPageGroupViewMode from "../../Types/StatusPage/StatusPageGroupViewMode";
4
+
5
+ /*
6
+ * Status page groups nest (Corporate Unit -> Region -> Market -> Site) and the
7
+ * overview page draws that nesting.
8
+ *
9
+ * Drawing each level as a card inside a card inside a card is what it used to
10
+ * do, and it does not survive contact with a real hierarchy: four borders end
11
+ * up wrapped around one uptime bar, every level eats horizontal room the bars
12
+ * need, and a leaf resource ends up rendered larger than the group that
13
+ * contains it. Below the top level a group is therefore drawn as a row hanging
14
+ * off a tree rail - one hairline down the left, one indent step, no extra card.
15
+ *
16
+ * The numbers live here rather than inline in the page because they are the
17
+ * part that has to hold at depth 1 and still hold at depth 12, and that is
18
+ * worth pinning down in tests.
19
+ */
20
+
21
+ /* What a group header shows on its right hand side, if anything. */
22
+ export enum StatusPageGroupRollupKind {
23
+ UptimePercent = "UptimePercent",
24
+ CurrentStatus = "CurrentStatus",
25
+ None = "None",
26
+ }
27
+
28
+ export default class StatusPageGroupNestingLayoutUtil {
29
+ /*
30
+ * Beyond this depth the indent step shrinks instead of growing. Nesting is
31
+ * capped on write at StatusPageGroupTreeUtil.MaxNestingDepth, but rows
32
+ * written straight to the database are not, so the layout is not allowed to
33
+ * assume any particular ceiling.
34
+ */
35
+ public static readonly MaxIndentedDepth: number = 4;
36
+
37
+ /*
38
+ * A depth that is not a whole number >= 0 is treated as the top level. The
39
+ * page derives depth from a recursive render, but nothing stops a caller (or
40
+ * a future refactor) from handing over -1 or NaN, and a broken indent is not
41
+ * worth a broken page.
42
+ */
43
+ public static getVisualDepth(data: { depth: number }): number {
44
+ if (!Number.isFinite(data.depth)) {
45
+ return 0;
46
+ }
47
+
48
+ const depth: number = Math.floor(data.depth);
49
+
50
+ if (depth <= 0) {
51
+ return 0;
52
+ }
53
+
54
+ return Math.min(depth, this.MaxIndentedDepth);
55
+ }
56
+
57
+ public static isRootLevel(data: { depth: number }): boolean {
58
+ return this.getVisualDepth({ depth: data.depth }) === 0;
59
+ }
60
+
61
+ /*
62
+ * Nesting is expressed with the rail, so the title only ever steps down once
63
+ * - and never below the size a resource name renders at. That inversion, a
64
+ * text-sm group heading over a text-lg resource, is what made the old nesting
65
+ * read upside down.
66
+ */
67
+ /*
68
+ * Truncates at every width, including on a phone. Wrapping was tried and is
69
+ * worse: the title is the only shrinkable item in the header row, so next to a
70
+ * sub group badge and a rollup it collapses to a ~30px column and breaks a
71
+ * name one character per line. An ellipsis with the full name in `title` is the
72
+ * lesser evil.
73
+ */
74
+ public static getTitleClassName(data: { depth: number }): string {
75
+ const base: string = "min-w-0 truncate font-semibold text-gray-900";
76
+
77
+ if (this.isRootLevel(data)) {
78
+ return `${base} text-base sm:text-lg tracking-tight`;
79
+ }
80
+
81
+ return `${base} text-base tracking-tight`;
82
+ }
83
+
84
+ /*
85
+ * A resource name, as rendered by MonitorOverview. It lives here so the floor
86
+ * a group title has to clear is the same string the resource actually renders
87
+ * with, rather than a number written down next to it.
88
+ */
89
+ public static getResourceTitleClassName(): string {
90
+ return "text-base font-medium";
91
+ }
92
+
93
+ /*
94
+ * The chevron box and the gap after it are what set where a title starts, and
95
+ * a sub group's contents and a group description are both aligned to that. All
96
+ * three are derived from these two, so moving the chevron moves them with it.
97
+ */
98
+ public static getChevronBoxClassName(): string {
99
+ return "flex-shrink-0 flex items-center justify-center w-6 h-6 rounded-md transition-colors";
100
+ }
101
+
102
+ public static getHeaderRowClassName(): string {
103
+ return "flex min-w-0 flex-1 items-center gap-2.5";
104
+ }
105
+
106
+ /*
107
+ * A nested group is not a card. It is a block in the parent's body, so all it
108
+ * gets is the vertical rhythm that separates it from its siblings.
109
+ */
110
+ public static getContainerClassName(data: { depth: number }): string {
111
+ if (this.isRootLevel(data)) {
112
+ return "bg-white rounded-xl shadow px-4 py-3 sm:px-6 sm:py-4";
113
+ }
114
+
115
+ return "";
116
+ }
117
+
118
+ /*
119
+ * Resources that belong to no group at all get their own card at the top of
120
+ * the page. It is the same surface as a top level group, minus the header.
121
+ */
122
+ public static getUngroupedResourcesCardClassName(): string {
123
+ return "bg-white rounded-xl shadow px-4 py-4 sm:px-6 sm:py-5";
124
+ }
125
+
126
+ /* Vertical rhythm between the resources of one group. */
127
+ public static getResourceListClassName(): string {
128
+ return "space-y-4 sm:space-y-5";
129
+ }
130
+
131
+ /*
132
+ * Quieter than the per-resource labels it replaces. It is a scale, not a
133
+ * reading, and it is what made a nested block look shouty when it appeared
134
+ * three times in one screen.
135
+ */
136
+ public static getTimeAxisClassName(): string {
137
+ return "flex justify-between text-[11px] text-gray-400";
138
+ }
139
+
140
+ /*
141
+ * The rolled up reading on the right of a group header. Sized to match the
142
+ * per-resource readings below it: a rollup and a resource reading are the same
143
+ * kind of number, and the status dot is what tells them apart.
144
+ */
145
+ public static getRollupClassName(): string {
146
+ return "flex-shrink-0 text-sm sm:text-base font-medium";
147
+ }
148
+
149
+ public static getHeaderClassName(data: { depth: number }): string {
150
+ /*
151
+ * The whole row is the disclosure control, so it carries the hover state.
152
+ * Negative horizontal margin with matching padding lets that hover reach the
153
+ * edge of the content column while the row's contents stay aligned with the
154
+ * resources below it.
155
+ *
156
+ * The width has to pay for both margins explicitly. A button shrink wraps
157
+ * even as a flex container, so it needs a width; but `w-full` with both
158
+ * negative margins over-constrains the box, and the browser resolves that by
159
+ * dropping margin-right (CSS 2.1 10.3.3) - which leaves every rollup 16px
160
+ * short of the card edge the resource readings below it sit on. 100% + 1rem
161
+ * satisfies the equation exactly, so nothing is dropped.
162
+ */
163
+ const base: string =
164
+ "flex w-[calc(100%+1rem)] items-center justify-between gap-3 text-left rounded-lg -mx-2 px-2 transition-colors hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-300";
165
+
166
+ return this.isRootLevel(data) ? `${base} py-2` : `${base} py-1.5`;
167
+ }
168
+
169
+ /*
170
+ * The rail and the indent that carry one nesting step.
171
+ *
172
+ * `ml` moves the rail under its parent's chevron and `pl` pushes the contents
173
+ * clear of it, so the two together land a sub group's contents at the parent's
174
+ * title. Past MaxIndentedDepth the step tightens rather than stops: the rails
175
+ * have to stay distinguishable, but a deep tree still has to leave room for
176
+ * the uptime bars on a phone.
177
+ */
178
+ private static getIndentClassName(data: { depth: number }): string {
179
+ const visualDepth: number = this.getVisualDepth(data);
180
+
181
+ if (visualDepth === 0) {
182
+ return "";
183
+ }
184
+
185
+ if (visualDepth < this.MaxIndentedDepth) {
186
+ return "ml-2.5 sm:ml-3 pl-4 sm:pl-5";
187
+ }
188
+
189
+ return "ml-1.5 pl-3";
190
+ }
191
+
192
+ /*
193
+ * Everything a group contains - its own resources and then its sub groups -
194
+ * lives in one body so a single rail covers all of it.
195
+ */
196
+ public static getBodyClassName(data: { depth: number }): string {
197
+ if (this.isRootLevel(data)) {
198
+ // The card's own padding is the boundary at the top level; no rail.
199
+ return "mt-3 space-y-4";
200
+ }
201
+
202
+ return `mt-2.5 space-y-3.5 border-l border-gray-200 ${this.getIndentClassName(
203
+ data,
204
+ )}`;
205
+ }
206
+
207
+ public static showRail(data: { depth: number }): boolean {
208
+ return !this.isRootLevel(data);
209
+ }
210
+
211
+ public static getSubGroupListClassName(data: { depth: number }): string {
212
+ return this.isRootLevel(data) ? "space-y-3" : "space-y-2.5";
213
+ }
214
+
215
+ /*
216
+ * A group with resources of its own *and* sub groups needs a line between the
217
+ * two, otherwise its last resource reads as belonging to the first sub group.
218
+ */
219
+ public static shouldShowSubGroupDivider(data: {
220
+ hasOwnResources: boolean;
221
+ subGroupCount: number;
222
+ }): boolean {
223
+ return data.hasOwnResources && data.subGroupCount > 0;
224
+ }
225
+
226
+ public static getSubGroupDividerClassName(): string {
227
+ return "border-t border-gray-100 pt-3.5";
228
+ }
229
+
230
+ /*
231
+ * The group description hangs under the title, indented past the chevron so
232
+ * it lines up with the name rather than with the disclosure control. It sits
233
+ * outside the header button on purpose: a description is markdown and may
234
+ * contain links, which cannot live inside a button.
235
+ *
236
+ * The indent is 34px: the 24px chevron box (w-6, see getChevronBoxClassName)
237
+ * plus the 10px gap after it (gap-2.5, see getHeaderRowClassName).
238
+ *
239
+ * Alignment only. Type and vertical rhythm are MarkdownViewer's - it sets
240
+ * them on the paragraph it renders, which beats anything inherited from here,
241
+ * so declaring a size or a colour on this wrapper would just be a comment that
242
+ * looks like code.
243
+ */
244
+ public static getDescriptionClassName(): string {
245
+ return "pl-[34px] pr-2";
246
+ }
247
+
248
+ public static getSubGroupCountBadgeClassName(): string {
249
+ return "flex-shrink-0 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[11px] font-medium text-gray-500 tabular-nums";
250
+ }
251
+
252
+ /* The status dot that marks a reading as a rollup rather than a resource's own. */
253
+ public static getRollupDotClassName(): string {
254
+ return "h-1.5 w-1.5 flex-shrink-0 rounded-full";
255
+ }
256
+
257
+ public static getRollupLabelClassName(): string {
258
+ return "font-semibold tabular-nums";
259
+ }
260
+
261
+ /*
262
+ * A group that only exists to hold sub groups has no resources of its own,
263
+ * and telling the visitor it is empty would be wrong - the sub groups below
264
+ * it are its content. A group with neither still renders its (empty) resource
265
+ * list, because that is where the "no resources" message belongs.
266
+ */
267
+ public static shouldRenderOwnResources(data: {
268
+ ownResourceCount: number;
269
+ subGroupCount: number;
270
+ }): boolean {
271
+ return data.ownResourceCount > 0 || data.subGroupCount === 0;
272
+ }
273
+
274
+ /*
275
+ * Whether a group draws the time axis under its own resource list.
276
+ *
277
+ * Once per resource list rather than once per resource: every bar in one list
278
+ * is drawn over the same window and sits in the same column, so one axis
279
+ * describes all of them. It deliberately does not cover sub groups - their
280
+ * bars are indented past this one, and they may be collapsed, so an axis up
281
+ * here would be labelling a column that is not there.
282
+ *
283
+ * A group in grid view draws a matrix of cells instead of bars, so its
284
+ * resources are not something a time axis describes.
285
+ */
286
+ public static shouldRenderTimeAxis(data: {
287
+ statusPageGroup: StatusPageGroup | null;
288
+ statusPageResources: Array<StatusPageResource>;
289
+ }): boolean {
290
+ if (data.statusPageGroup?.viewMode === StatusPageGroupViewMode.Grid) {
291
+ return false;
292
+ }
293
+
294
+ const groupId: string | null =
295
+ data.statusPageGroup?._id?.toString() || null;
296
+
297
+ return (
298
+ data.statusPageResources.find((resource: StatusPageResource) => {
299
+ if (!resource.showStatusHistoryChart) {
300
+ return false;
301
+ }
302
+
303
+ /*
304
+ * Skipped for the same reason the page skips them - a resource that
305
+ * resolves to neither a monitor nor a monitor group draws nothing.
306
+ */
307
+ if (!resource.monitor && !resource.monitorGroupId) {
308
+ return false;
309
+ }
310
+
311
+ const resourceGroupId: string | null =
312
+ resource.statusPageGroupId?.toString() || null;
313
+
314
+ return resourceGroupId === groupId;
315
+ }) !== undefined
316
+ );
317
+ }
318
+
319
+ /*
320
+ * Which rolled up reading a group header shows.
321
+ *
322
+ * A group that is currently down shows its status rather than an uptime
323
+ * percent - "99.9% uptime" next to a red group is not the thing a visitor came
324
+ * to read - and a group whose uptime cannot be worked out (no resources under
325
+ * it at all) shows nothing rather than a zero.
326
+ */
327
+ public static getRollupKind(data: {
328
+ showUptimePercent: boolean;
329
+ showCurrentStatus: boolean;
330
+ isCurrentlyDown: boolean;
331
+ uptimePercent: number | null;
332
+ }): StatusPageGroupRollupKind {
333
+ if (data.showUptimePercent && !data.isCurrentlyDown) {
334
+ return data.uptimePercent === null
335
+ ? StatusPageGroupRollupKind.None
336
+ : StatusPageGroupRollupKind.UptimePercent;
337
+ }
338
+
339
+ return data.showCurrentStatus
340
+ ? StatusPageGroupRollupKind.CurrentStatus
341
+ : StatusPageGroupRollupKind.None;
342
+ }
343
+ }
@@ -0,0 +1,307 @@
1
+ import StatusPageGroup from "../../Models/DatabaseModels/StatusPageGroup";
2
+
3
+ export interface StatusPageGroupTreeNode {
4
+ group: StatusPageGroup;
5
+ depth: number;
6
+ children: Array<StatusPageGroupTreeNode>;
7
+ }
8
+
9
+ /*
10
+ * Status page groups form a tree: a group may be nested under another group
11
+ * (Corporate Unit -> Region -> Market -> Site), and each level rolls up the
12
+ * status and uptime of everything below it.
13
+ *
14
+ * Everything here is defensive about the shape of the data it is handed. The
15
+ * parent pointer lives in a plain nullable column, so rows written directly to
16
+ * the database (or a group whose parent the caller did not fetch) can point at
17
+ * a missing parent, at themselves, or around a cycle. None of those may make a
18
+ * group disappear from the status page or hang the render, so every walk is
19
+ * bounded by a visited set and every group ends up in the tree exactly once.
20
+ */
21
+ export default class StatusPageGroupTreeUtil {
22
+ /*
23
+ * How deep a group may be nested. Enforced on write (see
24
+ * StatusPageGroupService) - reads never drop levels beyond it, they only
25
+ * refuse to loop.
26
+ */
27
+ public static readonly MaxNestingDepth: number = 10;
28
+
29
+ public static getParentId(statusPageGroup: StatusPageGroup): string | null {
30
+ const parentId: string | null =
31
+ statusPageGroup.parentStatusPageGroupId?.toString() || null;
32
+
33
+ if (!parentId) {
34
+ return null;
35
+ }
36
+
37
+ // a group that points at itself is a root, not an infinite loop.
38
+ if (parentId === statusPageGroup._id?.toString()) {
39
+ return null;
40
+ }
41
+
42
+ return parentId;
43
+ }
44
+
45
+ public static getChildGroups(data: {
46
+ statusPageGroupId: string | null;
47
+ statusPageGroups: Array<StatusPageGroup>;
48
+ }): Array<StatusPageGroup> {
49
+ const children: Array<StatusPageGroup> = data.statusPageGroups.filter(
50
+ (group: StatusPageGroup) => {
51
+ return this.getParentId(group) === data.statusPageGroupId;
52
+ },
53
+ );
54
+
55
+ return this.sortByOrder(children);
56
+ }
57
+
58
+ /*
59
+ * Top level groups: no parent, or a parent that is not part of the supplied
60
+ * list (an orphan still has to render somewhere).
61
+ */
62
+ public static getRootGroups(data: {
63
+ statusPageGroups: Array<StatusPageGroup>;
64
+ }): Array<StatusPageGroup> {
65
+ const idsInList: Set<string> = this.getIdSet(data.statusPageGroups);
66
+
67
+ const roots: Array<StatusPageGroup> = data.statusPageGroups.filter(
68
+ (group: StatusPageGroup) => {
69
+ const parentId: string | null = this.getParentId(group);
70
+ return !parentId || !idsInList.has(parentId);
71
+ },
72
+ );
73
+
74
+ return this.sortByOrder(roots);
75
+ }
76
+
77
+ /*
78
+ * The full tree, in render order. Every supplied group appears exactly once:
79
+ * orphans are promoted to roots, and groups that are only reachable through a
80
+ * cycle are promoted too (rather than being silently dropped).
81
+ */
82
+ public static buildTree(data: {
83
+ statusPageGroups: Array<StatusPageGroup>;
84
+ }): Array<StatusPageGroupTreeNode> {
85
+ const visited: Set<string> = new Set<string>();
86
+
87
+ const buildNode: (
88
+ group: StatusPageGroup,
89
+ depth: number,
90
+ ) => StatusPageGroupTreeNode = (
91
+ group: StatusPageGroup,
92
+ depth: number,
93
+ ): StatusPageGroupTreeNode => {
94
+ const groupId: string = group._id?.toString() || "";
95
+ visited.add(groupId);
96
+
97
+ const children: Array<StatusPageGroupTreeNode> = this.getChildGroups({
98
+ statusPageGroupId: groupId,
99
+ statusPageGroups: data.statusPageGroups,
100
+ })
101
+ .filter((child: StatusPageGroup) => {
102
+ return !visited.has(child._id?.toString() || "");
103
+ })
104
+ .map((child: StatusPageGroup) => {
105
+ return buildNode(child, depth + 1);
106
+ });
107
+
108
+ return {
109
+ group: group,
110
+ depth: depth,
111
+ children: children,
112
+ };
113
+ };
114
+
115
+ const tree: Array<StatusPageGroupTreeNode> = this.getRootGroups({
116
+ statusPageGroups: data.statusPageGroups,
117
+ }).map((root: StatusPageGroup) => {
118
+ return buildNode(root, 0);
119
+ });
120
+
121
+ /*
122
+ * Anything left unvisited is inside a cycle that no root points into. Pull
123
+ * each one up to the top level so the page still shows it.
124
+ */
125
+ for (const group of this.sortByOrder(data.statusPageGroups)) {
126
+ if (!visited.has(group._id?.toString() || "")) {
127
+ tree.push(buildNode(group, 0));
128
+ }
129
+ }
130
+
131
+ return tree;
132
+ }
133
+
134
+ /*
135
+ * Every group below this one, at any depth. Excludes the group itself.
136
+ */
137
+ public static getDescendantGroups(data: {
138
+ statusPageGroup: StatusPageGroup;
139
+ statusPageGroups: Array<StatusPageGroup>;
140
+ }): Array<StatusPageGroup> {
141
+ const descendants: Array<StatusPageGroup> = [];
142
+ const visited: Set<string> = new Set<string>([
143
+ data.statusPageGroup._id?.toString() || "",
144
+ ]);
145
+
146
+ let frontier: Array<StatusPageGroup> = this.getChildGroups({
147
+ statusPageGroupId: data.statusPageGroup._id?.toString() || "",
148
+ statusPageGroups: data.statusPageGroups,
149
+ });
150
+
151
+ while (frontier.length > 0) {
152
+ const nextFrontier: Array<StatusPageGroup> = [];
153
+
154
+ for (const group of frontier) {
155
+ const groupId: string = group._id?.toString() || "";
156
+
157
+ if (visited.has(groupId)) {
158
+ continue;
159
+ }
160
+
161
+ visited.add(groupId);
162
+ descendants.push(group);
163
+
164
+ nextFrontier.push(
165
+ ...this.getChildGroups({
166
+ statusPageGroupId: groupId,
167
+ statusPageGroups: data.statusPageGroups,
168
+ }),
169
+ );
170
+ }
171
+
172
+ frontier = nextFrontier;
173
+ }
174
+
175
+ return descendants;
176
+ }
177
+
178
+ /*
179
+ * The group itself followed by all of its descendants - the set of groups a
180
+ * rolled up number has to cover.
181
+ */
182
+ public static getGroupAndDescendants(data: {
183
+ statusPageGroup: StatusPageGroup;
184
+ statusPageGroups: Array<StatusPageGroup>;
185
+ }): Array<StatusPageGroup> {
186
+ return [
187
+ data.statusPageGroup,
188
+ ...this.getDescendantGroups({
189
+ statusPageGroup: data.statusPageGroup,
190
+ statusPageGroups: data.statusPageGroups,
191
+ }),
192
+ ];
193
+ }
194
+
195
+ /*
196
+ * Ancestors, closest parent first. Stops at a cycle instead of looping.
197
+ */
198
+ public static getAncestorGroups(data: {
199
+ statusPageGroup: StatusPageGroup;
200
+ statusPageGroups: Array<StatusPageGroup>;
201
+ }): Array<StatusPageGroup> {
202
+ const byId: Map<string, StatusPageGroup> = this.getGroupsById(
203
+ data.statusPageGroups,
204
+ );
205
+
206
+ const ancestors: Array<StatusPageGroup> = [];
207
+ const visited: Set<string> = new Set<string>([
208
+ data.statusPageGroup._id?.toString() || "",
209
+ ]);
210
+
211
+ let parentId: string | null = this.getParentId(data.statusPageGroup);
212
+
213
+ while (parentId && !visited.has(parentId)) {
214
+ const parent: StatusPageGroup | undefined = byId.get(parentId);
215
+
216
+ if (!parent) {
217
+ break;
218
+ }
219
+
220
+ ancestors.push(parent);
221
+ visited.add(parentId);
222
+ parentId = this.getParentId(parent);
223
+ }
224
+
225
+ return ancestors;
226
+ }
227
+
228
+ /*
229
+ * 0 for a top level group, 1 for its children, and so on.
230
+ */
231
+ public static getDepth(data: {
232
+ statusPageGroup: StatusPageGroup;
233
+ statusPageGroups: Array<StatusPageGroup>;
234
+ }): number {
235
+ return this.getAncestorGroups({
236
+ statusPageGroup: data.statusPageGroup,
237
+ statusPageGroups: data.statusPageGroups,
238
+ }).length;
239
+ }
240
+
241
+ public static isDescendantOf(data: {
242
+ statusPageGroup: StatusPageGroup;
243
+ possibleAncestorId: string;
244
+ statusPageGroups: Array<StatusPageGroup>;
245
+ }): boolean {
246
+ return this.getAncestorGroups({
247
+ statusPageGroup: data.statusPageGroup,
248
+ statusPageGroups: data.statusPageGroups,
249
+ }).some((ancestor: StatusPageGroup) => {
250
+ return ancestor._id?.toString() === data.possibleAncestorId;
251
+ });
252
+ }
253
+
254
+ private static getGroupsById(
255
+ statusPageGroups: Array<StatusPageGroup>,
256
+ ): Map<string, StatusPageGroup> {
257
+ const byId: Map<string, StatusPageGroup> = new Map<
258
+ string,
259
+ StatusPageGroup
260
+ >();
261
+
262
+ for (const group of statusPageGroups) {
263
+ const groupId: string | undefined = group._id?.toString();
264
+
265
+ if (groupId) {
266
+ byId.set(groupId, group);
267
+ }
268
+ }
269
+
270
+ return byId;
271
+ }
272
+
273
+ private static getIdSet(
274
+ statusPageGroups: Array<StatusPageGroup>,
275
+ ): Set<string> {
276
+ const ids: Set<string> = new Set<string>();
277
+
278
+ for (const group of statusPageGroups) {
279
+ const groupId: string | undefined = group._id?.toString();
280
+
281
+ if (groupId) {
282
+ ids.add(groupId);
283
+ }
284
+ }
285
+
286
+ return ids;
287
+ }
288
+
289
+ /*
290
+ * Siblings render in `order`. Groups without an order keep their incoming
291
+ * position, after the ordered ones.
292
+ */
293
+ private static sortByOrder(
294
+ statusPageGroups: Array<StatusPageGroup>,
295
+ ): Array<StatusPageGroup> {
296
+ return [...statusPageGroups].sort(
297
+ (a: StatusPageGroup, b: StatusPageGroup) => {
298
+ const orderA: number =
299
+ typeof a.order === "number" ? a.order : Number.MAX_SAFE_INTEGER;
300
+ const orderB: number =
301
+ typeof b.order === "number" ? b.order : Number.MAX_SAFE_INTEGER;
302
+
303
+ return orderA - orderB;
304
+ },
305
+ );
306
+ }
307
+ }