@oneuptime/common 11.7.3 → 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 (365) hide show
  1. package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
  2. package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
  3. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  4. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  5. package/Models/DatabaseModels/Index.ts +5 -0
  6. package/Models/DatabaseModels/LogDropFilter.ts +89 -0
  7. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +13 -3
  8. package/Models/DatabaseModels/RecommendationDismissal.ts +463 -0
  9. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  10. package/Models/DatabaseModels/StatusPageGroup.ts +95 -0
  11. package/Models/DatabaseModels/TraceDropFilter.ts +89 -0
  12. package/Models/DatabaseModels/WorkflowVariable.ts +5 -1
  13. package/Server/API/AlertAPI.ts +32 -17
  14. package/Server/API/BaseAPI.ts +31 -2
  15. package/Server/API/BaseAnalyticsAPI.ts +36 -2
  16. package/Server/API/CommonAPI.ts +57 -0
  17. package/Server/API/IncidentAPI.ts +60 -33
  18. package/Server/API/IncidentEpisodeAPI.ts +31 -16
  19. package/Server/API/MonitorAPI.ts +30 -0
  20. package/Server/API/MonitorTemplateAPI.ts +8 -0
  21. package/Server/API/ProbeAPI.ts +196 -15
  22. package/Server/API/ProjectAPI.ts +79 -1
  23. package/Server/API/ScheduledMaintenanceAPI.ts +33 -18
  24. package/Server/API/StatusPageAPI.ts +74 -37
  25. package/Server/API/WorkspaceNotificationRuleAPI.ts +34 -4
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.ts +527 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.ts +31 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.ts +55 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.ts +53 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  31. package/Server/Services/AlertEpisodeService.ts +47 -0
  32. package/Server/Services/AlertService.ts +125 -0
  33. package/Server/Services/BillingService.ts +98 -0
  34. package/Server/Services/DatabaseService.ts +184 -20
  35. package/Server/Services/IncidentEpisodeService.ts +47 -0
  36. package/Server/Services/IncidentService.ts +123 -0
  37. package/Server/Services/IncidentTemplateService.ts +123 -1
  38. package/Server/Services/Index.ts +2 -0
  39. package/Server/Services/LogDropFilterService.ts +133 -0
  40. package/Server/Services/MonitorProbeService.ts +94 -4
  41. package/Server/Services/MonitorService.ts +48 -43
  42. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +70 -0
  43. package/Server/Services/ProbeService.ts +66 -0
  44. package/Server/Services/ProjectService.ts +131 -7
  45. package/Server/Services/RecommendationDismissalService.ts +52 -0
  46. package/Server/Services/RunbookAgentJobService.ts +68 -7
  47. package/Server/Services/ScheduledMaintenanceService.ts +103 -0
  48. package/Server/Services/ScheduledMaintenanceTemplateService.ts +90 -0
  49. package/Server/Services/StatusPageGroupService.ts +186 -0
  50. package/Server/Services/TraceDropFilterService.ts +133 -0
  51. package/Server/Types/Workflow/Components/API/Delete.ts +12 -0
  52. package/Server/Types/Workflow/Components/API/Get.ts +12 -0
  53. package/Server/Types/Workflow/Components/API/Patch.ts +12 -0
  54. package/Server/Types/Workflow/Components/API/Post.ts +12 -0
  55. package/Server/Types/Workflow/Components/API/Put.ts +13 -0
  56. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +212 -0
  57. package/Server/Utils/DropFilterValidation.ts +127 -0
  58. package/Server/Utils/Errors.ts +5 -0
  59. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  60. package/Server/Utils/Monitor/MonitorIncident.ts +31 -2
  61. package/Server/Utils/Telemetry/AppMetrics.ts +31 -0
  62. package/Tests/Models/DatabaseModels/MonitorProbeColumnAccessControl.test.ts +196 -0
  63. package/Tests/Models/RecommendationDismissalModel.test.ts +269 -0
  64. package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +320 -0
  65. package/Tests/Server/API/BaseAnalyticsAPIUpdatePayloadValidation.test.ts +293 -0
  66. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +164 -0
  67. package/Tests/Server/API/ProbeAPI.test.ts +422 -53
  68. package/Tests/Server/API/ProjectAPI.test.ts +180 -0
  69. package/Tests/Server/API/ProjectAPIExtendTrialBillingDisabled.test.ts +138 -0
  70. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +458 -0
  71. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +359 -0
  72. package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +427 -0
  73. package/Tests/Server/Services/BillingServiceExtendTrial.test.ts +439 -0
  74. package/Tests/Server/Services/CrossProjectReferenceWriteGuard.test.ts +810 -0
  75. package/Tests/Server/Services/DatabaseServiceAtomicAddToColumns.test.ts +269 -0
  76. package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +277 -0
  77. package/Tests/Server/Services/DatabaseServiceSortColumnSelect.test.ts +201 -0
  78. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +81 -23
  79. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +398 -0
  80. package/Tests/Server/Services/MonitorProbeDeleteHooks.test.ts +280 -0
  81. package/Tests/Server/Services/MonitorProbeServiceProbeTenancy.test.ts +304 -0
  82. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +262 -0
  83. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +614 -0
  84. package/Tests/Server/Services/RecommendationDismissalService.test.ts +306 -0
  85. package/Tests/Server/Services/RepairCrossProjectIncidentReferencesMigration.test.ts +528 -0
  86. package/Tests/Server/Services/StatusPageGroupNesting.test.ts +395 -0
  87. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +26 -21
  88. package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +227 -0
  89. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +285 -0
  90. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +463 -0
  91. package/Tests/Server/Utils/DropFilterValidation.test.ts +269 -0
  92. package/Tests/Server/Utils/Telemetry/SpanUtil.test.ts +94 -0
  93. package/Tests/Types/DateUserTimezone.test.ts +52 -0
  94. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +177 -9
  95. package/Tests/Types/Monitor/MonitorStepDnsMonitor.test.ts +105 -0
  96. package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +131 -0
  97. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +60 -0
  98. package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +82 -0
  99. package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +268 -0
  100. package/Tests/Types/Monitor/MonitorStepMetricMonitor.test.ts +386 -0
  101. package/Tests/Types/Monitor/MonitorStepProfileMonitor.test.ts +185 -0
  102. package/Tests/Types/Monitor/MonitorStepSnmpMonitor.test.ts +156 -0
  103. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +371 -0
  104. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +901 -0
  105. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +607 -0
  106. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +754 -0
  107. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +960 -0
  108. package/Tests/Types/RollingTime/RollingTimeUtil.test.ts +186 -0
  109. package/Tests/Types/Runbook/RunbookStepTimeout.test.ts +410 -0
  110. package/Tests/Types/Telemetry/DropFilterSampling.test.ts +241 -0
  111. package/Tests/Types/Workspace/NotificationRuleConditionUtil.test.ts +353 -0
  112. package/Tests/Types/Workspace/NotificationRuleUtil.test.ts +566 -0
  113. package/Tests/UI/Components/CodeEditor.test.tsx +174 -0
  114. package/Tests/UI/Components/ErrorBoundary.test.tsx +555 -24
  115. package/Tests/UI/Components/InfoCard.test.tsx +358 -0
  116. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +138 -0
  117. package/Tests/UI/Components/Modal.test.tsx +43 -5
  118. package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +397 -0
  119. package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +427 -0
  120. package/Tests/UI/Components/Runbook/StepTimeoutInput.test.tsx +386 -0
  121. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +671 -0
  122. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +404 -0
  123. package/Tests/UI/Utils/DropdownAlignment.test.ts +306 -0
  124. package/Tests/UI/Utils/TableFilterUrlState.test.ts +292 -0
  125. package/Tests/Utils/Monitor/MonitorSummaryProbeUtil.test.ts +263 -0
  126. package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +583 -0
  127. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +1072 -0
  128. package/Tests/Utils/StatusPage/GroupTree.test.ts +583 -0
  129. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +411 -0
  130. package/Types/Date.ts +15 -6
  131. package/Types/Monitor/KubernetesAlertTemplates.ts +205 -0
  132. package/Types/Monitor/MonitorStep.ts +10 -1
  133. package/Types/Monitor/MonitorStepMetricMonitor.ts +46 -1
  134. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +413 -0
  135. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +168 -0
  136. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +175 -0
  137. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +743 -0
  138. package/Types/Permission.ts +45 -0
  139. package/Types/Recommendation/RecommendationType.ts +20 -0
  140. package/Types/Runbook/RunbookExecutionDeadline.ts +129 -0
  141. package/Types/Runbook/RunbookStepTimeout.ts +202 -0
  142. package/Types/Telemetry/DropFilterSampling.ts +92 -0
  143. package/UI/Components/AI/GenerateFromAIModal.tsx +0 -2
  144. package/UI/Components/Accordion/Accordion.tsx +10 -2
  145. package/UI/Components/BulkUpdate/BulkLabelActions.tsx +0 -1
  146. package/UI/Components/CodeEditor/CodeEditor.tsx +34 -20
  147. package/UI/Components/Date/RangeStartAndEndDateView.tsx +0 -1
  148. package/UI/Components/EditionLabel/EditionLabel.tsx +1 -27
  149. package/UI/Components/Error/PageError.tsx +0 -4
  150. package/UI/Components/ErrorBoundary.tsx +252 -15
  151. package/UI/Components/InfoCard/InfoCard.tsx +58 -1
  152. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +34 -1
  153. package/UI/Components/Modal/Modal.tsx +16 -60
  154. package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
  155. package/UI/Components/ModelTable/BaseModelTable.tsx +116 -67
  156. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +150 -47
  157. package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
  158. package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -0
  159. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +34 -1
  160. package/UI/Utils/DropdownAlignment.ts +162 -0
  161. package/UI/Utils/TableFilterUrlState.ts +59 -20
  162. package/Utils/API.ts +106 -2
  163. package/Utils/Monitor/MonitorSummaryProbeUtil.ts +130 -0
  164. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +434 -0
  165. package/Utils/StatusPage/GroupNestingLayout.ts +343 -0
  166. package/Utils/StatusPage/GroupTree.ts +307 -0
  167. package/Utils/StatusPage/ResourceUptime.ts +90 -17
  168. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
  169. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
  170. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
  171. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
  172. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  173. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  174. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  175. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  176. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  177. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  178. package/build/dist/Models/DatabaseModels/LogDropFilter.js +91 -0
  179. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  180. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +14 -4
  181. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  182. package/build/dist/Models/DatabaseModels/RecommendationDismissal.js +484 -0
  183. package/build/dist/Models/DatabaseModels/RecommendationDismissal.js.map +1 -0
  184. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  185. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/StatusPageGroup.js +94 -0
  187. package/build/dist/Models/DatabaseModels/StatusPageGroup.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +91 -0
  189. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +5 -1
  191. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  192. package/build/dist/Server/API/AlertAPI.js +22 -13
  193. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  194. package/build/dist/Server/API/BaseAPI.js +22 -1
  195. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  196. package/build/dist/Server/API/BaseAnalyticsAPI.js +24 -1
  197. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  198. package/build/dist/Server/API/CommonAPI.js +42 -0
  199. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  200. package/build/dist/Server/API/IncidentAPI.js +43 -26
  201. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  202. package/build/dist/Server/API/IncidentEpisodeAPI.js +21 -12
  203. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  204. package/build/dist/Server/API/MonitorAPI.js +23 -0
  205. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  206. package/build/dist/Server/API/MonitorTemplateAPI.js +4 -0
  207. package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -1
  208. package/build/dist/Server/API/ProbeAPI.js +131 -15
  209. package/build/dist/Server/API/ProbeAPI.js.map +1 -1
  210. package/build/dist/Server/API/ProjectAPI.js +62 -3
  211. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  212. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +23 -14
  213. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  214. package/build/dist/Server/API/StatusPageAPI.js +61 -33
  215. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  216. package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js +26 -2
  217. package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js.map +1 -1
  218. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js +397 -0
  219. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js.map +1 -0
  220. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js +16 -0
  221. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js.map +1 -0
  222. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js +30 -0
  223. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js.map +1 -0
  224. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js +34 -0
  225. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js.map +1 -0
  226. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  227. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  228. package/build/dist/Server/Services/AlertEpisodeService.js +39 -0
  229. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  230. package/build/dist/Server/Services/AlertService.js +95 -0
  231. package/build/dist/Server/Services/AlertService.js.map +1 -1
  232. package/build/dist/Server/Services/BillingService.js +71 -0
  233. package/build/dist/Server/Services/BillingService.js.map +1 -1
  234. package/build/dist/Server/Services/DatabaseService.js +140 -17
  235. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  236. package/build/dist/Server/Services/IncidentEpisodeService.js +39 -0
  237. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  238. package/build/dist/Server/Services/IncidentService.js +100 -0
  239. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  240. package/build/dist/Server/Services/IncidentTemplateService.js +97 -0
  241. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  242. package/build/dist/Server/Services/Index.js +2 -0
  243. package/build/dist/Server/Services/Index.js.map +1 -1
  244. package/build/dist/Server/Services/LogDropFilterService.js +93 -0
  245. package/build/dist/Server/Services/LogDropFilterService.js.map +1 -1
  246. package/build/dist/Server/Services/MonitorProbeService.js +73 -5
  247. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  248. package/build/dist/Server/Services/MonitorService.js +34 -39
  249. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  250. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +71 -0
  251. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  252. package/build/dist/Server/Services/ProbeService.js +64 -0
  253. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  254. package/build/dist/Server/Services/ProjectService.js +103 -7
  255. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  256. package/build/dist/Server/Services/RecommendationDismissalService.js +50 -0
  257. package/build/dist/Server/Services/RecommendationDismissalService.js.map +1 -0
  258. package/build/dist/Server/Services/RunbookAgentJobService.js +63 -5
  259. package/build/dist/Server/Services/RunbookAgentJobService.js.map +1 -1
  260. package/build/dist/Server/Services/ScheduledMaintenanceService.js +83 -0
  261. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  262. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +65 -0
  263. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  264. package/build/dist/Server/Services/StatusPageGroupService.js +136 -0
  265. package/build/dist/Server/Services/StatusPageGroupService.js.map +1 -1
  266. package/build/dist/Server/Services/TraceDropFilterService.js +93 -0
  267. package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -1
  268. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +11 -0
  269. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  270. package/build/dist/Server/Types/Workflow/Components/API/Get.js +11 -0
  271. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  272. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +11 -0
  273. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  274. package/build/dist/Server/Types/Workflow/Components/API/Post.js +11 -0
  275. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  276. package/build/dist/Server/Types/Workflow/Components/API/Put.js +12 -0
  277. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  278. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +132 -0
  279. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -0
  280. package/build/dist/Server/Utils/DropFilterValidation.js +80 -0
  281. package/build/dist/Server/Utils/DropFilterValidation.js.map +1 -0
  282. package/build/dist/Server/Utils/Errors.js +3 -0
  283. package/build/dist/Server/Utils/Errors.js.map +1 -1
  284. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +30 -12
  285. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  286. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +38 -18
  287. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  288. package/build/dist/Server/Utils/Telemetry/AppMetrics.js +26 -0
  289. package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
  290. package/build/dist/Types/Date.js +14 -4
  291. package/build/dist/Types/Date.js.map +1 -1
  292. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +192 -0
  293. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  294. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  295. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  296. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +32 -1
  297. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
  298. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +294 -0
  299. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -0
  300. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +110 -0
  301. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -0
  302. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +58 -0
  303. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -0
  304. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +451 -0
  305. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -0
  306. package/build/dist/Types/Permission.js +40 -0
  307. package/build/dist/Types/Permission.js.map +1 -1
  308. package/build/dist/Types/Recommendation/RecommendationType.js +21 -0
  309. package/build/dist/Types/Recommendation/RecommendationType.js.map +1 -0
  310. package/build/dist/Types/Runbook/RunbookExecutionDeadline.js +93 -0
  311. package/build/dist/Types/Runbook/RunbookExecutionDeadline.js.map +1 -0
  312. package/build/dist/Types/Runbook/RunbookStepTimeout.js +142 -0
  313. package/build/dist/Types/Runbook/RunbookStepTimeout.js.map +1 -0
  314. package/build/dist/Types/Telemetry/DropFilterSampling.js +75 -0
  315. package/build/dist/Types/Telemetry/DropFilterSampling.js.map +1 -0
  316. package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -2
  317. package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
  318. package/build/dist/UI/Components/Accordion/Accordion.js +9 -2
  319. package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
  320. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +1 -1
  321. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
  322. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +27 -14
  323. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  324. package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js +1 -1
  325. package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js.map +1 -1
  326. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +2 -20
  327. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  328. package/build/dist/UI/Components/Error/PageError.js +1 -3
  329. package/build/dist/UI/Components/Error/PageError.js.map +1 -1
  330. package/build/dist/UI/Components/ErrorBoundary.js +145 -11
  331. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  332. package/build/dist/UI/Components/InfoCard/InfoCard.js +28 -1
  333. package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
  334. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +13 -2
  335. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  336. package/build/dist/UI/Components/Modal/Modal.js +4 -32
  337. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  338. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  339. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +69 -33
  340. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  341. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +73 -32
  342. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
  343. package/build/dist/UI/Components/Runbook/StepTimeoutInput.js +101 -0
  344. package/build/dist/UI/Components/Runbook/StepTimeoutInput.js.map +1 -0
  345. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +73 -0
  346. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
  347. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +13 -2
  348. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  349. package/build/dist/UI/Utils/DropdownAlignment.js +98 -0
  350. package/build/dist/UI/Utils/DropdownAlignment.js.map +1 -0
  351. package/build/dist/UI/Utils/TableFilterUrlState.js +48 -15
  352. package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
  353. package/build/dist/Utils/API.js +39 -2
  354. package/build/dist/Utils/API.js.map +1 -1
  355. package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js +97 -0
  356. package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js.map +1 -0
  357. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +330 -0
  358. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -0
  359. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +280 -0
  360. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -0
  361. package/build/dist/Utils/StatusPage/GroupTree.js +214 -0
  362. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -0
  363. package/build/dist/Utils/StatusPage/ResourceUptime.js +60 -16
  364. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  365. package/package.json +1 -1
@@ -0,0 +1,754 @@
1
+ import MonitorRecommendationSeverityMapper, {
2
+ MonitorRecommendationSeverityOption,
3
+ } from "../../../../Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper";
4
+ import MonitorRecommendationCatalog from "../../../../Types/Monitor/Recommendation/MonitorRecommendationCatalog";
5
+ import MonitorRecommendationUtil from "../../../../Types/Monitor/Recommendation/MonitorRecommendationUtil";
6
+ import {
7
+ MonitorRecommendation,
8
+ MonitorRecommendationArgs,
9
+ MonitorRecommendationSeverity,
10
+ MonitorRecommendationSeverityMap,
11
+ } from "../../../../Types/Monitor/Recommendation/MonitorRecommendationTypes";
12
+ import MonitorStep from "../../../../Types/Monitor/MonitorStep";
13
+ import MonitorCriteriaInstance from "../../../../Types/Monitor/MonitorCriteriaInstance";
14
+ import { CriteriaIncident } from "../../../../Types/Monitor/CriteriaIncident";
15
+ import { CriteriaAlert } from "../../../../Types/Monitor/CriteriaAlert";
16
+ import ObjectID from "../../../../Types/ObjectID";
17
+
18
+ /*
19
+ * This mapper is the only thing standing between "the card said Warning" and
20
+ * "the incident it opened said Critical".
21
+ *
22
+ * Before it existed, `MonitorRecommendationArgs` carried a single
23
+ * `defaultIncidentSeverityId` that the page filled with the project's first
24
+ * severity, so every template — Critical and Warning alike — opened the same
25
+ * severity of incident. That failure is invisible in every obvious place: the
26
+ * page renders correctly, the monitor is created successfully, the criteria
27
+ * validate, and the mis-signal only shows up weeks later when someone is paged
28
+ * at 3am for a replica-count drift. Nothing but these tests notices.
29
+ *
30
+ * Two properties are worth naming up front because they are easy to "clean up"
31
+ * into bugs:
32
+ *
33
+ * 1. Rank comes from `order`, never from array position and never from the
34
+ * severity NAME. Severities are user-renameable and user-reorderable
35
+ * ("Sev1", "P0", localized names), so name matching works for the default
36
+ * seed and for no other project on the planet.
37
+ *
38
+ * 2. A row with no `order` is UNRANKED, not rank zero. Coercing it to zero
39
+ * (which `a.order || MAX` quietly does, and which is the natural way to
40
+ * write this) promotes a legacy row to most-severe and maps every
41
+ * Critical recommendation onto it.
42
+ */
43
+
44
+ /*
45
+ * The real default-project seed, from ProjectService.addDefaultIncidentSeverity
46
+ * and addDefaultAlertSeverity. Written out rather than imported because the
47
+ * mapper deliberately takes a structural slice — the point of these tests is
48
+ * that the mapper produces the right answer for the shape the overwhelming
49
+ * majority of real projects actually have.
50
+ */
51
+ const CRITICAL_INCIDENT_ID: ObjectID = ObjectID.generate();
52
+ const MAJOR_INCIDENT_ID: ObjectID = ObjectID.generate();
53
+ const MINOR_INCIDENT_ID: ObjectID = ObjectID.generate();
54
+
55
+ const HIGH_ALERT_ID: ObjectID = ObjectID.generate();
56
+ const LOW_ALERT_ID: ObjectID = ObjectID.generate();
57
+
58
+ const DEFAULT_INCIDENT_SEVERITIES: Array<MonitorRecommendationSeverityOption> =
59
+ [
60
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
61
+ { id: MAJOR_INCIDENT_ID, name: "Major Incident", order: 2 },
62
+ { id: MINOR_INCIDENT_ID, name: "Minor Incident", order: 3 },
63
+ ];
64
+
65
+ const DEFAULT_ALERT_SEVERITIES: Array<MonitorRecommendationSeverityOption> = [
66
+ { id: HIGH_ALERT_ID, name: "High", order: 1 },
67
+ { id: LOW_ALERT_ID, name: "Low", order: 2 },
68
+ ];
69
+
70
+ function getNames(
71
+ options: Array<MonitorRecommendationSeverityOption>,
72
+ ): Array<string> {
73
+ return options.map((option: MonitorRecommendationSeverityOption) => {
74
+ return option.name;
75
+ });
76
+ }
77
+
78
+ function idString(id: ObjectID | undefined): string | undefined {
79
+ return id ? id.toString() : undefined;
80
+ }
81
+
82
+ describe("MonitorRecommendationSeverityMapper", () => {
83
+ describe("rankSeverities", () => {
84
+ it("sorts by order ascending regardless of array position", () => {
85
+ const ranked: Array<MonitorRecommendationSeverityOption> =
86
+ MonitorRecommendationSeverityMapper.rankSeverities([
87
+ { id: MINOR_INCIDENT_ID, name: "Minor Incident", order: 3 },
88
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
89
+ { id: MAJOR_INCIDENT_ID, name: "Major Incident", order: 2 },
90
+ ]);
91
+
92
+ expect(getNames(ranked)).toEqual([
93
+ "Critical Incident",
94
+ "Major Incident",
95
+ "Minor Incident",
96
+ ]);
97
+ });
98
+
99
+ it("pushes rows with no order to the very end", () => {
100
+ /*
101
+ * The unranked row is placed FIRST in the input on purpose: an
102
+ * implementation that treats a missing order as 0 — or that simply
103
+ * leaves unsortable rows where they were — leaves it at index 0, where
104
+ * `getDefaultSeverityMapping` reads it as the project's most severe.
105
+ */
106
+ const ranked: Array<MonitorRecommendationSeverityOption> =
107
+ MonitorRecommendationSeverityMapper.rankSeverities([
108
+ { id: ObjectID.generate(), name: "Legacy Unranked" },
109
+ { id: MAJOR_INCIDENT_ID, name: "Major Incident", order: 2 },
110
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
111
+ ]);
112
+
113
+ expect(getNames(ranked)).toEqual([
114
+ "Critical Incident",
115
+ "Major Incident",
116
+ "Legacy Unranked",
117
+ ]);
118
+ });
119
+
120
+ it("treats an explicitly undefined order the same as an absent one", () => {
121
+ // `{ order: undefined }` and `{}` are different objects to `in`/hasOwnProperty.
122
+ const ranked: Array<MonitorRecommendationSeverityOption> =
123
+ MonitorRecommendationSeverityMapper.rankSeverities([
124
+ {
125
+ id: ObjectID.generate(),
126
+ name: "Legacy Unranked",
127
+ order: undefined,
128
+ },
129
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
130
+ ]);
131
+
132
+ expect(getNames(ranked)).toEqual([
133
+ "Critical Incident",
134
+ "Legacy Unranked",
135
+ ]);
136
+ });
137
+
138
+ it("keeps order 0 as the most severe rather than treating it as unset", () => {
139
+ /*
140
+ * Zero is a legitimate order and the exact value a truthiness check
141
+ * (`a.order || MAX_SAFE_INTEGER`) mangles: it would demote the project's
142
+ * most severe row to last and hand Critical to whatever came next.
143
+ */
144
+ const zeroOrderId: ObjectID = ObjectID.generate();
145
+
146
+ const ranked: Array<MonitorRecommendationSeverityOption> =
147
+ MonitorRecommendationSeverityMapper.rankSeverities([
148
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
149
+ { id: zeroOrderId, name: "Sev0", order: 0 },
150
+ ]);
151
+
152
+ expect(getNames(ranked)).toEqual(["Sev0", "Critical Incident"]);
153
+ });
154
+
155
+ it("does not mutate the array it was given", () => {
156
+ /*
157
+ * Callers pass the API's own severity list straight in and keep using it
158
+ * to render the dropdown. Sorting it in place would silently reorder the
159
+ * form's options as a side effect of computing the mapping.
160
+ */
161
+ const input: Array<MonitorRecommendationSeverityOption> = [
162
+ { id: MINOR_INCIDENT_ID, name: "Minor Incident", order: 3 },
163
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
164
+ ];
165
+
166
+ const ranked: Array<MonitorRecommendationSeverityOption> =
167
+ MonitorRecommendationSeverityMapper.rankSeverities(input);
168
+
169
+ expect(getNames(input)).toEqual(["Minor Incident", "Critical Incident"]);
170
+ expect(ranked).not.toBe(input);
171
+ });
172
+
173
+ it("keeps the input order for severities that share an order", () => {
174
+ /*
175
+ * The product permits two severities with the same order. An unstable
176
+ * tiebreak would mean the mapping shown in the create form and the
177
+ * mapping written onto the monitor could disagree between two calls with
178
+ * identical input.
179
+ */
180
+ const firstTiedId: ObjectID = ObjectID.generate();
181
+ const secondTiedId: ObjectID = ObjectID.generate();
182
+
183
+ const input: Array<MonitorRecommendationSeverityOption> = [
184
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
185
+ { id: firstTiedId, name: "Tied A", order: 2 },
186
+ { id: secondTiedId, name: "Tied B", order: 2 },
187
+ ];
188
+
189
+ expect(
190
+ getNames(MonitorRecommendationSeverityMapper.rankSeverities(input)),
191
+ ).toEqual(["Critical Incident", "Tied A", "Tied B"]);
192
+
193
+ expect(
194
+ getNames(MonitorRecommendationSeverityMapper.rankSeverities(input)),
195
+ ).toEqual(["Critical Incident", "Tied A", "Tied B"]);
196
+ });
197
+
198
+ it("keeps the input order among several unranked rows", () => {
199
+ const input: Array<MonitorRecommendationSeverityOption> = [
200
+ { id: ObjectID.generate(), name: "Unranked A" },
201
+ { id: ObjectID.generate(), name: "Unranked B" },
202
+ ];
203
+
204
+ expect(
205
+ getNames(MonitorRecommendationSeverityMapper.rankSeverities(input)),
206
+ ).toEqual(["Unranked A", "Unranked B"]);
207
+ });
208
+
209
+ it("returns an empty array for empty input", () => {
210
+ expect(MonitorRecommendationSeverityMapper.rankSeverities([])).toEqual(
211
+ [],
212
+ );
213
+ });
214
+ });
215
+
216
+ describe("getDefaultSeverityMapping", () => {
217
+ it("maps Critical to Critical Incident and Warning to Major Incident on the default seed", () => {
218
+ const map: MonitorRecommendationSeverityMap =
219
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping(
220
+ DEFAULT_INCIDENT_SEVERITIES,
221
+ );
222
+
223
+ expect(idString(map.Critical)).toBe(CRITICAL_INCIDENT_ID.toString());
224
+ expect(idString(map.Warning)).toBe(MAJOR_INCIDENT_ID.toString());
225
+ });
226
+
227
+ it("maps Critical to High and Warning to Low on the default alert seed", () => {
228
+ const map: MonitorRecommendationSeverityMap =
229
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping(
230
+ DEFAULT_ALERT_SEVERITIES,
231
+ );
232
+
233
+ expect(idString(map.Critical)).toBe(HIGH_ALERT_ID.toString());
234
+ expect(idString(map.Warning)).toBe(LOW_ALERT_ID.toString());
235
+ });
236
+
237
+ it("never maps Critical and Warning to the same severity when the project has more than one", () => {
238
+ /*
239
+ * The single assertion that the whole feature reduces to. If these two
240
+ * ever collapse back to one id, every recommendation pages identically
241
+ * again and the Critical/Warning badge on each card describes nothing.
242
+ */
243
+ const map: MonitorRecommendationSeverityMap =
244
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping(
245
+ DEFAULT_INCIDENT_SEVERITIES,
246
+ );
247
+
248
+ expect(idString(map.Critical)).not.toBe(idString(map.Warning));
249
+ });
250
+
251
+ it("maps by order, not by position in the array it was handed", () => {
252
+ /*
253
+ * The API sorts severities today, so an implementation that just took
254
+ * `options[0]` and `options[1]` would pass every other test here and
255
+ * break the day the API's sort changes or a caller passes an unsorted
256
+ * list from a cache.
257
+ */
258
+ const map: MonitorRecommendationSeverityMap =
259
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping([
260
+ { id: MINOR_INCIDENT_ID, name: "Minor Incident", order: 3 },
261
+ { id: MAJOR_INCIDENT_ID, name: "Major Incident", order: 2 },
262
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
263
+ ]);
264
+
265
+ expect(idString(map.Critical)).toBe(CRITICAL_INCIDENT_ID.toString());
266
+ expect(idString(map.Warning)).toBe(MAJOR_INCIDENT_ID.toString());
267
+ });
268
+
269
+ it("does not hand Critical to an unranked legacy severity", () => {
270
+ /*
271
+ * The end-to-end consequence of the rankSeverities ordering rule: a
272
+ * project carrying one severity row with no order (created before the
273
+ * field existed, or through an import) must not have every Critical
274
+ * recommendation silently routed to it.
275
+ */
276
+ const unrankedId: ObjectID = ObjectID.generate();
277
+
278
+ const map: MonitorRecommendationSeverityMap =
279
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping([
280
+ { id: unrankedId, name: "Imported Severity" },
281
+ ...DEFAULT_INCIDENT_SEVERITIES,
282
+ ]);
283
+
284
+ expect(idString(map.Critical)).toBe(CRITICAL_INCIDENT_ID.toString());
285
+ expect(idString(map.Warning)).toBe(MAJOR_INCIDENT_ID.toString());
286
+ expect(idString(map.Critical)).not.toBe(unrankedId.toString());
287
+ expect(idString(map.Warning)).not.toBe(unrankedId.toString());
288
+ });
289
+
290
+ it("maps both Critical and Warning to the only severity a one-severity project has", () => {
291
+ /*
292
+ * A project with one severity cannot express the distinction, and
293
+ * leaving Warning unmapped would drop those monitors back onto the
294
+ * template's own `defaultIncidentSeverityId` — the pre-fix behaviour
295
+ * this class exists to replace. Mapping both is the deliberate choice.
296
+ */
297
+ const onlyId: ObjectID = ObjectID.generate();
298
+
299
+ const map: MonitorRecommendationSeverityMap =
300
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping([
301
+ { id: onlyId, name: "Sev1", order: 1 },
302
+ ]);
303
+
304
+ expect(idString(map.Critical)).toBe(onlyId.toString());
305
+ expect(idString(map.Warning)).toBe(onlyId.toString());
306
+ });
307
+
308
+ it("returns an empty map when the project has no severities at all", () => {
309
+ /*
310
+ * Empty rather than a map onto undefined ids: `resolveSeverityId` returns
311
+ * undefined for a missing entry, and the util reads that as "leave the
312
+ * template's severity alone". A map containing `Critical: undefined`
313
+ * would travel the same path, but an empty object says so unambiguously
314
+ * and keeps `describeMapping` from rendering half a row.
315
+ */
316
+ expect(
317
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping([]),
318
+ ).toEqual({});
319
+ });
320
+
321
+ it("gives Warning the SECOND severity, not the least severe one", () => {
322
+ /*
323
+ * Pinned because it is a judgement call that the default three-severity
324
+ * seed hides completely: there, second and last-but-one are the same row.
325
+ * On a five-severity project the two answers diverge, and the intended
326
+ * one is second — a recommendation the catalog calls Warning ("Disk Will
327
+ * Fill In 24h", "Deployment Replica Mismatch") is a real production
328
+ * problem, not the bottom of the scale.
329
+ *
330
+ * If someone deliberately changes this to "least severe", this test is
331
+ * where that decision gets recorded.
332
+ */
333
+ const sev1: ObjectID = ObjectID.generate();
334
+ const sev2: ObjectID = ObjectID.generate();
335
+ const sev5: ObjectID = ObjectID.generate();
336
+
337
+ const map: MonitorRecommendationSeverityMap =
338
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping([
339
+ { id: sev1, name: "Sev1", order: 1 },
340
+ { id: sev2, name: "Sev2", order: 2 },
341
+ { id: ObjectID.generate(), name: "Sev3", order: 3 },
342
+ { id: ObjectID.generate(), name: "Sev4", order: 4 },
343
+ { id: sev5, name: "Sev5", order: 5 },
344
+ ]);
345
+
346
+ expect(idString(map.Critical)).toBe(sev1.toString());
347
+ expect(idString(map.Warning)).toBe(sev2.toString());
348
+ expect(idString(map.Warning)).not.toBe(sev5.toString());
349
+ });
350
+
351
+ it("does not mutate the options array it was given", () => {
352
+ const input: Array<MonitorRecommendationSeverityOption> = [
353
+ { id: MINOR_INCIDENT_ID, name: "Minor Incident", order: 3 },
354
+ { id: CRITICAL_INCIDENT_ID, name: "Critical Incident", order: 1 },
355
+ ];
356
+
357
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping(input);
358
+
359
+ expect(getNames(input)).toEqual(["Minor Incident", "Critical Incident"]);
360
+ });
361
+ });
362
+
363
+ describe("resolveSeverityId", () => {
364
+ it("returns the id the map holds for that severity", () => {
365
+ const map: MonitorRecommendationSeverityMap = {
366
+ Critical: CRITICAL_INCIDENT_ID,
367
+ Warning: MAJOR_INCIDENT_ID,
368
+ };
369
+
370
+ expect(
371
+ idString(
372
+ MonitorRecommendationSeverityMapper.resolveSeverityId({
373
+ severity: "Critical",
374
+ severityMap: map,
375
+ }),
376
+ ),
377
+ ).toBe(CRITICAL_INCIDENT_ID.toString());
378
+
379
+ expect(
380
+ idString(
381
+ MonitorRecommendationSeverityMapper.resolveSeverityId({
382
+ severity: "Warning",
383
+ severityMap: map,
384
+ }),
385
+ ),
386
+ ).toBe(MAJOR_INCIDENT_ID.toString());
387
+ });
388
+
389
+ it("returns undefined when there is no map", () => {
390
+ /*
391
+ * "No map" is the state of every caller that predates this feature, and
392
+ * it must mean "leave the template's severity alone" rather than throw or
393
+ * return a blank id — a criteria instance with a populated incident and
394
+ * no severity fails MonitorCriteriaInstance.getValidationError, so the
395
+ * whole create would fail at submit with a message about a field the user
396
+ * never saw.
397
+ */
398
+ expect(
399
+ MonitorRecommendationSeverityMapper.resolveSeverityId({
400
+ severity: "Critical",
401
+ severityMap: undefined,
402
+ }),
403
+ ).toBeUndefined();
404
+
405
+ expect(
406
+ MonitorRecommendationSeverityMapper.resolveSeverityId({
407
+ severity: "Warning",
408
+ }),
409
+ ).toBeUndefined();
410
+ });
411
+
412
+ it("returns undefined for a severity the map has no entry for", () => {
413
+ // Same "leave it alone" contract, reached through a partial map.
414
+ expect(
415
+ MonitorRecommendationSeverityMapper.resolveSeverityId({
416
+ severity: "Warning",
417
+ severityMap: { Critical: CRITICAL_INCIDENT_ID },
418
+ }),
419
+ ).toBeUndefined();
420
+
421
+ expect(
422
+ MonitorRecommendationSeverityMapper.resolveSeverityId({
423
+ severity: "Critical",
424
+ severityMap: {},
425
+ }),
426
+ ).toBeUndefined();
427
+ });
428
+ });
429
+
430
+ describe("describeMapping", () => {
431
+ it("describes both severities by their project-facing names, Critical first", () => {
432
+ /*
433
+ * This string is the only thing that makes an automatic severity choice
434
+ * reviewable instead of surprising: it is what the create form renders so
435
+ * the user can see "Critical -> Critical Incident" before submitting.
436
+ */
437
+ expect(
438
+ MonitorRecommendationSeverityMapper.describeMapping({
439
+ options: DEFAULT_INCIDENT_SEVERITIES,
440
+ severityMap:
441
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping(
442
+ DEFAULT_INCIDENT_SEVERITIES,
443
+ ),
444
+ }),
445
+ ).toEqual([
446
+ { severity: "Critical", name: "Critical Incident" },
447
+ { severity: "Warning", name: "Major Incident" },
448
+ ]);
449
+ });
450
+
451
+ it("skips a severity the map does not cover", () => {
452
+ expect(
453
+ MonitorRecommendationSeverityMapper.describeMapping({
454
+ options: DEFAULT_INCIDENT_SEVERITIES,
455
+ severityMap: { Critical: CRITICAL_INCIDENT_ID },
456
+ }),
457
+ ).toEqual([{ severity: "Critical", name: "Critical Incident" }]);
458
+ });
459
+
460
+ it("skips an id that matches none of the options", () => {
461
+ /*
462
+ * A stale id is reachable in normal use: the form holds a mapping while
463
+ * the user deletes that severity in another tab, or the map is restored
464
+ * from a previous session. Rendering "Critical -> undefined" (or throwing
465
+ * on the missing option) is worse than saying nothing about Critical.
466
+ */
467
+ expect(
468
+ MonitorRecommendationSeverityMapper.describeMapping({
469
+ options: DEFAULT_INCIDENT_SEVERITIES,
470
+ severityMap: {
471
+ Critical: ObjectID.generate(),
472
+ Warning: MAJOR_INCIDENT_ID,
473
+ },
474
+ }),
475
+ ).toEqual([{ severity: "Warning", name: "Major Incident" }]);
476
+ });
477
+
478
+ it("matches options by id value rather than by object identity", () => {
479
+ /*
480
+ * The map's ObjectIDs and the options' ObjectIDs come from different
481
+ * fetches in the real page, so they are never the same instance even when
482
+ * they hold the same uuid. A `===` comparison would describe nothing.
483
+ */
484
+ expect(
485
+ MonitorRecommendationSeverityMapper.describeMapping({
486
+ options: DEFAULT_INCIDENT_SEVERITIES,
487
+ severityMap: {
488
+ Critical: new ObjectID(CRITICAL_INCIDENT_ID.toString()),
489
+ },
490
+ }),
491
+ ).toEqual([{ severity: "Critical", name: "Critical Incident" }]);
492
+ });
493
+
494
+ it("returns an empty list when there is no map", () => {
495
+ expect(
496
+ MonitorRecommendationSeverityMapper.describeMapping({
497
+ options: DEFAULT_INCIDENT_SEVERITIES,
498
+ severityMap: undefined,
499
+ }),
500
+ ).toEqual([]);
501
+
502
+ expect(
503
+ MonitorRecommendationSeverityMapper.describeMapping({
504
+ options: DEFAULT_INCIDENT_SEVERITIES,
505
+ }),
506
+ ).toEqual([]);
507
+ });
508
+
509
+ it("returns an empty list when the project has no severities to name", () => {
510
+ expect(
511
+ MonitorRecommendationSeverityMapper.describeMapping({
512
+ options: [],
513
+ severityMap: { Critical: CRITICAL_INCIDENT_ID },
514
+ }),
515
+ ).toEqual([]);
516
+ });
517
+ });
518
+
519
+ describe("end to end with real catalog recommendations", () => {
520
+ /*
521
+ * The mapper is only useful if its output survives the trip into
522
+ * `applyNotificationSettingsToMonitorStep` and lands on the criteria
523
+ * incidents the monitor evaluator actually reads. The unit tests above pin
524
+ * the mapping; this pins that nothing between here and the created monitor
525
+ * flattens it back to one severity — which is exactly what the code did
526
+ * before this feature, because every template took the single
527
+ * `args.defaultIncidentSeverityId`.
528
+ */
529
+
530
+ const ALL_RECOMMENDATIONS: Array<MonitorRecommendation> =
531
+ MonitorRecommendationCatalog.getAllRecommendations();
532
+
533
+ const CRITICAL_RECOMMENDATION: MonitorRecommendation | undefined =
534
+ ALL_RECOMMENDATIONS.find((recommendation: MonitorRecommendation) => {
535
+ return recommendation.severity === "Critical";
536
+ });
537
+
538
+ const WARNING_RECOMMENDATION: MonitorRecommendation | undefined =
539
+ ALL_RECOMMENDATIONS.find((recommendation: MonitorRecommendation) => {
540
+ return recommendation.severity === "Warning";
541
+ });
542
+
543
+ const TEMPLATE_INCIDENT_SEVERITY_ID: ObjectID = ObjectID.generate();
544
+ const TEMPLATE_ALERT_SEVERITY_ID: ObjectID = ObjectID.generate();
545
+
546
+ function buildArgs(): MonitorRecommendationArgs {
547
+ return {
548
+ resourceIdentifier: "prod-cluster-01",
549
+ onlineMonitorStatusId: ObjectID.generate(),
550
+ offlineMonitorStatusId: ObjectID.generate(),
551
+ defaultIncidentSeverityId: TEMPLATE_INCIDENT_SEVERITY_ID,
552
+ defaultAlertSeverityId: TEMPLATE_ALERT_SEVERITY_ID,
553
+ monitorName: "Test Monitor",
554
+ };
555
+ }
556
+
557
+ function buildStepWithDefaultMapping(
558
+ recommendation: MonitorRecommendation,
559
+ ): MonitorStep {
560
+ return MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
561
+ monitorStep: recommendation.getMonitorStep(buildArgs()),
562
+ notificationSettings: {
563
+ incidentSeverityIdBySeverity:
564
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping(
565
+ DEFAULT_INCIDENT_SEVERITIES,
566
+ ),
567
+ alertSeverityIdBySeverity:
568
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping(
569
+ DEFAULT_ALERT_SEVERITIES,
570
+ ),
571
+ },
572
+ severity: recommendation.severity,
573
+ });
574
+ }
575
+
576
+ function getIncidents(monitorStep: MonitorStep): Array<CriteriaIncident> {
577
+ return (
578
+ monitorStep.data?.monitorCriteria?.data?.monitorCriteriaInstanceArray ||
579
+ []
580
+ ).flatMap((instance: MonitorCriteriaInstance) => {
581
+ return instance.data?.incidents || [];
582
+ });
583
+ }
584
+
585
+ function getAlerts(monitorStep: MonitorStep): Array<CriteriaAlert> {
586
+ return (
587
+ monitorStep.data?.monitorCriteria?.data?.monitorCriteriaInstanceArray ||
588
+ []
589
+ ).flatMap((instance: MonitorCriteriaInstance) => {
590
+ return instance.data?.alerts || [];
591
+ });
592
+ }
593
+
594
+ it("ships both a Critical and a Warning recommendation to compare", () => {
595
+ /*
596
+ * Guards the two tests below against passing vacuously if the catalog
597
+ * ever stops shipping one of the two severities.
598
+ */
599
+ expect(CRITICAL_RECOMMENDATION).toBeDefined();
600
+ expect(WARNING_RECOMMENDATION).toBeDefined();
601
+ });
602
+
603
+ it("gives a Critical and a Warning recommendation DIFFERENT incident severities", () => {
604
+ const criticalIncidents: Array<CriteriaIncident> = getIncidents(
605
+ buildStepWithDefaultMapping(CRITICAL_RECOMMENDATION!),
606
+ );
607
+ const warningIncidents: Array<CriteriaIncident> = getIncidents(
608
+ buildStepWithDefaultMapping(WARNING_RECOMMENDATION!),
609
+ );
610
+
611
+ expect(criticalIncidents.length).toBeGreaterThan(0);
612
+ expect(warningIncidents.length).toBeGreaterThan(0);
613
+
614
+ for (const incident of criticalIncidents) {
615
+ expect(idString(incident.incidentSeverityId)).toBe(
616
+ CRITICAL_INCIDENT_ID.toString(),
617
+ );
618
+ }
619
+
620
+ for (const incident of warningIncidents) {
621
+ expect(idString(incident.incidentSeverityId)).toBe(
622
+ MAJOR_INCIDENT_ID.toString(),
623
+ );
624
+ }
625
+
626
+ /*
627
+ * The regression this whole feature fixes, stated directly: both used to
628
+ * come out as `args.defaultIncidentSeverityId`, so the two arrays were
629
+ * identical and neither told the truth about the card's badge.
630
+ */
631
+ expect(idString(criticalIncidents[0]!.incidentSeverityId)).not.toBe(
632
+ idString(warningIncidents[0]!.incidentSeverityId),
633
+ );
634
+ expect(idString(warningIncidents[0]!.incidentSeverityId)).not.toBe(
635
+ TEMPLATE_INCIDENT_SEVERITY_ID.toString(),
636
+ );
637
+ });
638
+
639
+ it("gives a Critical and a Warning recommendation DIFFERENT alert severities", () => {
640
+ const criticalAlerts: Array<CriteriaAlert> = getAlerts(
641
+ buildStepWithDefaultMapping(CRITICAL_RECOMMENDATION!),
642
+ );
643
+ const warningAlerts: Array<CriteriaAlert> = getAlerts(
644
+ buildStepWithDefaultMapping(WARNING_RECOMMENDATION!),
645
+ );
646
+
647
+ expect(criticalAlerts.length).toBeGreaterThan(0);
648
+ expect(warningAlerts.length).toBeGreaterThan(0);
649
+
650
+ for (const alert of criticalAlerts) {
651
+ expect(idString(alert.alertSeverityId)).toBe(HIGH_ALERT_ID.toString());
652
+ }
653
+
654
+ for (const alert of warningAlerts) {
655
+ expect(idString(alert.alertSeverityId)).toBe(LOW_ALERT_ID.toString());
656
+ }
657
+ });
658
+
659
+ it("routes every catalog recommendation to the severity its own badge claims", () => {
660
+ /*
661
+ * The per-recommendation version of the above, across all eight resource
662
+ * types. A single template whose declared severity disagrees with what it
663
+ * writes onto its criteria would be invisible on the page and would only
664
+ * surface as a mis-severity incident in production.
665
+ */
666
+ expect(ALL_RECOMMENDATIONS.length).toBeGreaterThan(0);
667
+
668
+ for (const recommendation of ALL_RECOMMENDATIONS) {
669
+ const expectedIncidentSeverityId: ObjectID =
670
+ recommendation.severity === "Critical"
671
+ ? CRITICAL_INCIDENT_ID
672
+ : MAJOR_INCIDENT_ID;
673
+
674
+ for (const incident of getIncidents(
675
+ buildStepWithDefaultMapping(recommendation),
676
+ )) {
677
+ expect(idString(incident.incidentSeverityId)).toBe(
678
+ expectedIncidentSeverityId.toString(),
679
+ );
680
+ }
681
+ }
682
+ });
683
+
684
+ it("falls back to the template's own severity when the project has none", () => {
685
+ /*
686
+ * A project with no severities produces an empty map, and an empty map
687
+ * must leave the templates untouched rather than blank the severity out —
688
+ * a criteria incident with no severity id fails validation and the create
689
+ * fails at submit.
690
+ */
691
+ const severities: Array<MonitorRecommendationSeverity> = [
692
+ "Critical",
693
+ "Warning",
694
+ ];
695
+
696
+ for (const severity of severities) {
697
+ const monitorStep: MonitorStep =
698
+ MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
699
+ monitorStep: CRITICAL_RECOMMENDATION!.getMonitorStep(buildArgs()),
700
+ notificationSettings: {
701
+ incidentSeverityIdBySeverity:
702
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping(
703
+ [],
704
+ ),
705
+ },
706
+ severity: severity,
707
+ });
708
+
709
+ for (const incident of getIncidents(monitorStep)) {
710
+ expect(idString(incident.incidentSeverityId)).toBe(
711
+ TEMPLATE_INCIDENT_SEVERITY_ID.toString(),
712
+ );
713
+ }
714
+ }
715
+ });
716
+
717
+ it("maps Critical and Warning to the same severity on a one-severity project", () => {
718
+ /*
719
+ * The documented single-severity fallback, checked where it actually
720
+ * matters: the monitor still gets a valid severity (so the create
721
+ * succeeds), it just cannot express the distinction.
722
+ */
723
+ const onlyId: ObjectID = ObjectID.generate();
724
+ const oneSeverityMap: MonitorRecommendationSeverityMap =
725
+ MonitorRecommendationSeverityMapper.getDefaultSeverityMapping([
726
+ { id: onlyId, name: "Sev1", order: 1 },
727
+ ]);
728
+
729
+ const recommendations: Array<MonitorRecommendation> = [
730
+ CRITICAL_RECOMMENDATION!,
731
+ WARNING_RECOMMENDATION!,
732
+ ];
733
+
734
+ for (const recommendation of recommendations) {
735
+ const monitorStep: MonitorStep =
736
+ MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
737
+ monitorStep: recommendation.getMonitorStep(buildArgs()),
738
+ notificationSettings: {
739
+ incidentSeverityIdBySeverity: oneSeverityMap,
740
+ },
741
+ severity: recommendation.severity,
742
+ });
743
+
744
+ const incidents: Array<CriteriaIncident> = getIncidents(monitorStep);
745
+
746
+ expect(incidents.length).toBeGreaterThan(0);
747
+
748
+ for (const incident of incidents) {
749
+ expect(idString(incident.incidentSeverityId)).toBe(onlyId.toString());
750
+ }
751
+ }
752
+ });
753
+ });
754
+ });