@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,810 @@
1
+ import AlertEpisodeService from "../../../Server/Services/AlertEpisodeService";
2
+ import AlertService from "../../../Server/Services/AlertService";
3
+ import AlertSeverityService from "../../../Server/Services/AlertSeverityService";
4
+ import AlertStateService from "../../../Server/Services/AlertStateService";
5
+ import IncidentEpisodeService from "../../../Server/Services/IncidentEpisodeService";
6
+ import IncidentService from "../../../Server/Services/IncidentService";
7
+ import IncidentSeverityService from "../../../Server/Services/IncidentSeverityService";
8
+ import IncidentStateService from "../../../Server/Services/IncidentStateService";
9
+ import IncidentTemplateService from "../../../Server/Services/IncidentTemplateService";
10
+ import MonitorService from "../../../Server/Services/MonitorService";
11
+ import MonitorStatusService from "../../../Server/Services/MonitorStatusService";
12
+ import ProjectService from "../../../Server/Services/ProjectService";
13
+ import ScheduledMaintenanceService from "../../../Server/Services/ScheduledMaintenanceService";
14
+ import ScheduledMaintenanceStateService from "../../../Server/Services/ScheduledMaintenanceStateService";
15
+ import ScheduledMaintenanceTemplateService from "../../../Server/Services/ScheduledMaintenanceTemplateService";
16
+ import DatabaseService from "../../../Server/Services/DatabaseService";
17
+ import ProjectScopedReferenceValidator from "../../../Server/Utils/Database/ProjectScopedReferenceValidator";
18
+ import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
19
+ import Alert from "../../../Models/DatabaseModels/Alert";
20
+ import AlertEpisode from "../../../Models/DatabaseModels/AlertEpisode";
21
+ import AlertState from "../../../Models/DatabaseModels/AlertState";
22
+ import Incident from "../../../Models/DatabaseModels/Incident";
23
+ import IncidentEpisode from "../../../Models/DatabaseModels/IncidentEpisode";
24
+ import IncidentState from "../../../Models/DatabaseModels/IncidentState";
25
+ import IncidentTemplate from "../../../Models/DatabaseModels/IncidentTemplate";
26
+ import ScheduledMaintenance from "../../../Models/DatabaseModels/ScheduledMaintenance";
27
+ import ScheduledMaintenanceState from "../../../Models/DatabaseModels/ScheduledMaintenanceState";
28
+ import ScheduledMaintenanceTemplate from "../../../Models/DatabaseModels/ScheduledMaintenanceTemplate";
29
+ import BadDataException from "../../../Types/Exception/BadDataException";
30
+ import ObjectID from "../../../Types/ObjectID";
31
+ import {
32
+ afterEach,
33
+ beforeEach,
34
+ describe,
35
+ expect,
36
+ jest,
37
+ test,
38
+ } from "@jest/globals";
39
+
40
+ /*
41
+ * The undeletable project came from writes, not from deletes: a record was
42
+ * saved holding another project's state / severity / monitor status id, and
43
+ * the ON DELETE NO ACTION foreign key then refused to let that other project
44
+ * go. The repair migration cleans up the rows that already exist; these tests
45
+ * cover the other half — the write paths that produced them must now refuse.
46
+ *
47
+ * Each captured reference is asserted as the full triple (model name, id,
48
+ * lookup service). The service is the part that picks the table the id is
49
+ * checked against, so a test that ignores it passes even when a column is
50
+ * validated against the wrong model — which is exactly what a mutation run
51
+ * showed the first version of this file doing.
52
+ */
53
+
54
+ const PROJECT_ID: ObjectID = new ObjectID(
55
+ "0b5fbbd0-8d1c-4a72-9a2b-4b8bd1cba0e9",
56
+ );
57
+ const STATE_ID: ObjectID = new ObjectID("2b0a94a4-2f8c-49f0-8a2e-0f1ff5df41c9");
58
+ const SEVERITY_ID: ObjectID = new ObjectID(
59
+ "6a56b0f9-6c8f-4f76-9b53-0a1a5b0ec1a2",
60
+ );
61
+ const MONITOR_STATUS_ID: ObjectID = new ObjectID(
62
+ "8ff02a3f-3f18-4c1d-9db1-6cf27bb2a4a1",
63
+ );
64
+
65
+ // Identity -> readable name, so an assertion can name the service it expects.
66
+ const SERVICE_NAMES: Map<unknown, string> = new Map<unknown, string>([
67
+ [IncidentStateService, "IncidentStateService"],
68
+ [IncidentSeverityService, "IncidentSeverityService"],
69
+ [AlertStateService, "AlertStateService"],
70
+ [AlertSeverityService, "AlertSeverityService"],
71
+ [ScheduledMaintenanceStateService, "ScheduledMaintenanceStateService"],
72
+ [MonitorStatusService, "MonitorStatusService"],
73
+ ]);
74
+
75
+ type CapturedReference = {
76
+ subject: string;
77
+ modelName: string;
78
+ id: string;
79
+ service: string;
80
+ };
81
+
82
+ type ValidatorCall = {
83
+ projectId: ObjectID | undefined;
84
+ subject?: string | undefined;
85
+ references: Array<{
86
+ modelName: string;
87
+ id: ObjectID | string | undefined | null;
88
+ service: DatabaseService<DatabaseBaseModel>;
89
+ }>;
90
+ };
91
+
92
+ let captured: Array<CapturedReference> = [];
93
+ let validatorCalls: Array<ValidatorCall> = [];
94
+
95
+ /*
96
+ * Records every (subject, model, id, service) the production code asks about,
97
+ * and optionally rejects. `rejects` throws only when the call carries at least
98
+ * one id, so a guard that has been emptied out cannot masquerade as a working
99
+ * one by throwing anyway.
100
+ */
101
+ function spyOnValidator(rejects?: boolean): void {
102
+ jest
103
+ .spyOn(ProjectScopedReferenceValidator, "validateReferencesBelongToProject")
104
+ .mockImplementation((async (data: ValidatorCall): Promise<void> => {
105
+ validatorCalls.push(data);
106
+
107
+ let hasId: boolean = false;
108
+
109
+ for (const reference of data.references) {
110
+ if (!reference.id) {
111
+ continue;
112
+ }
113
+
114
+ hasId = true;
115
+
116
+ captured.push({
117
+ subject: data.subject || "",
118
+ modelName: reference.modelName,
119
+ id: reference.id.toString(),
120
+ service: SERVICE_NAMES.get(reference.service) || "UNKNOWN_SERVICE",
121
+ });
122
+ }
123
+
124
+ if (rejects && hasId) {
125
+ throw new BadDataException(
126
+ "This record references records that belong to a different project.",
127
+ );
128
+ }
129
+ }) as never);
130
+ }
131
+
132
+ function callHook(
133
+ service: unknown,
134
+ hook: "onBeforeCreate" | "onBeforeUpdate",
135
+ payload: unknown,
136
+ ): Promise<unknown> {
137
+ return (service as Record<string, (input: unknown) => Promise<unknown>>)[
138
+ hook
139
+ ]!(payload);
140
+ }
141
+
142
+ describe("cross-project reference guard on write", () => {
143
+ beforeEach(() => {
144
+ captured = [];
145
+ validatorCalls = [];
146
+ });
147
+
148
+ afterEach(() => {
149
+ jest.restoreAllMocks();
150
+ });
151
+
152
+ describe("IncidentService", () => {
153
+ function mockCreatedState(): void {
154
+ const createdState: IncidentState = new IncidentState();
155
+ createdState._id = STATE_ID.toString();
156
+ jest
157
+ .spyOn(IncidentStateService, "findOneBy")
158
+ .mockResolvedValue(createdState as never);
159
+ }
160
+
161
+ test("create checks state, severity and monitor status against the right models", async () => {
162
+ mockCreatedState();
163
+ spyOnValidator();
164
+ jest
165
+ .spyOn(ProjectService, "incrementAndGetIncidentCounter")
166
+ .mockResolvedValue({ counter: 1, prefix: undefined } as never);
167
+
168
+ await callHook(IncidentService, "onBeforeCreate", {
169
+ data: {
170
+ title: "test",
171
+ incidentSeverityId: SEVERITY_ID,
172
+ changeMonitorStatusToId: MONITOR_STATUS_ID,
173
+ } as Incident,
174
+ props: { tenantId: PROJECT_ID },
175
+ });
176
+
177
+ expect(captured).toEqual([
178
+ {
179
+ subject: "incident",
180
+ modelName: "Incident State",
181
+ id: STATE_ID.toString(),
182
+ service: "IncidentStateService",
183
+ },
184
+ {
185
+ subject: "incident",
186
+ modelName: "Incident Severity",
187
+ id: SEVERITY_ID.toString(),
188
+ service: "IncidentSeverityService",
189
+ },
190
+ {
191
+ subject: "incident",
192
+ modelName: "Monitor Status",
193
+ id: MONITOR_STATUS_ID.toString(),
194
+ service: "MonitorStatusService",
195
+ },
196
+ ]);
197
+ });
198
+
199
+ test("create is rejected, and burns no incident number, when a reference is foreign", async () => {
200
+ mockCreatedState();
201
+ spyOnValidator(true);
202
+ const counter: jest.Mock = jest.fn() as unknown as jest.Mock;
203
+ jest
204
+ .spyOn(ProjectService, "incrementAndGetIncidentCounter")
205
+ .mockImplementation(counter as never);
206
+
207
+ await expect(
208
+ callHook(IncidentService, "onBeforeCreate", {
209
+ data: { title: "test", incidentSeverityId: SEVERITY_ID } as Incident,
210
+ props: { tenantId: PROJECT_ID },
211
+ }),
212
+ ).rejects.toThrow("belong to a different project");
213
+
214
+ expect(counter).not.toHaveBeenCalled();
215
+ });
216
+
217
+ test("update checks every reference column it writes", async () => {
218
+ spyOnValidator();
219
+
220
+ /*
221
+ * Writing changeMonitorStatusToId also sends onBeforeUpdate into its
222
+ * monitor-status-propagation branch, which reads the matched incidents
223
+ * with the caller's own props. Unrelated to the guard — stub it out.
224
+ */
225
+ jest.spyOn(IncidentService, "findBy").mockResolvedValue([] as never);
226
+
227
+ await callHook(IncidentService, "onBeforeUpdate", {
228
+ data: {
229
+ currentIncidentStateId: STATE_ID,
230
+ incidentSeverityId: SEVERITY_ID,
231
+ changeMonitorStatusToId: MONITOR_STATUS_ID,
232
+ },
233
+ query: {},
234
+ props: { tenantId: PROJECT_ID },
235
+ });
236
+
237
+ expect(captured).toEqual([
238
+ {
239
+ subject: "incident",
240
+ modelName: "Incident State",
241
+ id: STATE_ID.toString(),
242
+ service: "IncidentStateService",
243
+ },
244
+ {
245
+ subject: "incident",
246
+ modelName: "Incident Severity",
247
+ id: SEVERITY_ID.toString(),
248
+ service: "IncidentSeverityService",
249
+ },
250
+ {
251
+ subject: "incident",
252
+ modelName: "Monitor Status",
253
+ id: MONITOR_STATUS_ID.toString(),
254
+ service: "MonitorStatusService",
255
+ },
256
+ ]);
257
+ });
258
+
259
+ test("update catches the bare-string relation shape the API accepts", async () => {
260
+ /*
261
+ * PUT /api/incident with {"incidentSeverity": "<uuid>"} leaves a plain
262
+ * string in the relation slot until sanitizeCreateOrUpdate turns it into
263
+ * an entity — which happens after this hook. Reading ?._id alone missed
264
+ * it and the guard silently passed.
265
+ */
266
+ spyOnValidator();
267
+
268
+ await callHook(IncidentService, "onBeforeUpdate", {
269
+ data: { incidentSeverity: SEVERITY_ID.toString() },
270
+ query: {},
271
+ props: { tenantId: PROJECT_ID },
272
+ });
273
+
274
+ expect(captured).toEqual([
275
+ {
276
+ subject: "incident",
277
+ modelName: "Incident Severity",
278
+ id: SEVERITY_ID.toString(),
279
+ service: "IncidentSeverityService",
280
+ },
281
+ ]);
282
+ });
283
+
284
+ test("update catches the relation-object shape", async () => {
285
+ spyOnValidator();
286
+
287
+ await callHook(IncidentService, "onBeforeUpdate", {
288
+ data: { incidentSeverity: { _id: SEVERITY_ID.toString() } },
289
+ query: {},
290
+ props: { tenantId: PROJECT_ID },
291
+ });
292
+
293
+ expect(captured).toHaveLength(1);
294
+ expect(captured[0]?.id).toBe(SEVERITY_ID.toString());
295
+ });
296
+
297
+ test("update looks nothing up when no reference column is written", async () => {
298
+ spyOnValidator();
299
+
300
+ await callHook(IncidentService, "onBeforeUpdate", {
301
+ data: { title: "renamed" },
302
+ query: {},
303
+ props: { tenantId: PROJECT_ID },
304
+ });
305
+
306
+ expect(validatorCalls).toHaveLength(0);
307
+ });
308
+
309
+ test("update falls back to the projects of the matched rows when there is no tenant", async () => {
310
+ /*
311
+ * Root and API updates do not always carry a tenantId. Skipping the
312
+ * check there would leave the exact hole this guard exists to close.
313
+ */
314
+ const incident: Incident = new Incident();
315
+ incident.projectId = PROJECT_ID;
316
+
317
+ jest
318
+ .spyOn(IncidentService, "findBy")
319
+ .mockResolvedValue([incident] as never);
320
+
321
+ spyOnValidator();
322
+
323
+ await callHook(IncidentService, "onBeforeUpdate", {
324
+ data: { incidentSeverityId: SEVERITY_ID },
325
+ query: {},
326
+ props: { isRoot: true },
327
+ });
328
+
329
+ expect(validatorCalls).toHaveLength(1);
330
+ expect(validatorCalls[0]?.projectId).toBe(PROJECT_ID);
331
+ });
332
+
333
+ test("update is rejected when a reference is foreign", async () => {
334
+ spyOnValidator(true);
335
+
336
+ await expect(
337
+ callHook(IncidentService, "onBeforeUpdate", {
338
+ data: { incidentSeverityId: SEVERITY_ID },
339
+ query: {},
340
+ props: { tenantId: PROJECT_ID },
341
+ }),
342
+ ).rejects.toThrow("belong to a different project");
343
+ });
344
+ });
345
+
346
+ describe("AlertService", () => {
347
+ function mockCreatedState(): void {
348
+ const createdState: AlertState = new AlertState();
349
+ createdState._id = STATE_ID.toString();
350
+ jest
351
+ .spyOn(AlertStateService, "findOneBy")
352
+ .mockResolvedValue(createdState as never);
353
+ }
354
+
355
+ test("create checks severity and monitor status against the right models", async () => {
356
+ mockCreatedState();
357
+ spyOnValidator();
358
+ jest
359
+ .spyOn(ProjectService, "incrementAndGetAlertCounter")
360
+ .mockResolvedValue({ counter: 1, prefix: undefined } as never);
361
+
362
+ await callHook(AlertService, "onBeforeCreate", {
363
+ data: {
364
+ title: "test",
365
+ alertSeverityId: SEVERITY_ID,
366
+ monitorStatusWhenThisAlertWasCreatedId: MONITOR_STATUS_ID,
367
+ } as Alert,
368
+ props: { tenantId: PROJECT_ID },
369
+ });
370
+
371
+ expect(captured).toEqual([
372
+ {
373
+ subject: "alert",
374
+ modelName: "Alert Severity",
375
+ id: SEVERITY_ID.toString(),
376
+ service: "AlertSeverityService",
377
+ },
378
+ {
379
+ subject: "alert",
380
+ modelName: "Monitor Status",
381
+ id: MONITOR_STATUS_ID.toString(),
382
+ service: "MonitorStatusService",
383
+ },
384
+ ]);
385
+ });
386
+
387
+ test("create is rejected, and burns no alert number, when a reference is foreign", async () => {
388
+ mockCreatedState();
389
+ spyOnValidator(true);
390
+ const counter: jest.Mock = jest.fn() as unknown as jest.Mock;
391
+ jest
392
+ .spyOn(ProjectService, "incrementAndGetAlertCounter")
393
+ .mockImplementation(counter as never);
394
+
395
+ await expect(
396
+ callHook(AlertService, "onBeforeCreate", {
397
+ data: { title: "test", alertSeverityId: SEVERITY_ID } as Alert,
398
+ props: { tenantId: PROJECT_ID },
399
+ }),
400
+ ).rejects.toThrow("belong to a different project");
401
+
402
+ expect(counter).not.toHaveBeenCalled();
403
+ });
404
+
405
+ test("update checks state, severity and monitor status", async () => {
406
+ spyOnValidator();
407
+
408
+ await callHook(AlertService, "onBeforeUpdate", {
409
+ data: {
410
+ currentAlertStateId: STATE_ID,
411
+ alertSeverityId: SEVERITY_ID,
412
+ monitorStatusWhenThisAlertWasCreatedId: MONITOR_STATUS_ID,
413
+ },
414
+ query: {},
415
+ props: { tenantId: PROJECT_ID },
416
+ });
417
+
418
+ expect(captured).toEqual([
419
+ {
420
+ subject: "alert",
421
+ modelName: "Alert State",
422
+ id: STATE_ID.toString(),
423
+ service: "AlertStateService",
424
+ },
425
+ {
426
+ subject: "alert",
427
+ modelName: "Alert Severity",
428
+ id: SEVERITY_ID.toString(),
429
+ service: "AlertSeverityService",
430
+ },
431
+ {
432
+ subject: "alert",
433
+ modelName: "Monitor Status",
434
+ id: MONITOR_STATUS_ID.toString(),
435
+ service: "MonitorStatusService",
436
+ },
437
+ ]);
438
+ });
439
+ });
440
+
441
+ describe("ScheduledMaintenanceService", () => {
442
+ function mockScheduledState(): void {
443
+ const scheduledState: ScheduledMaintenanceState =
444
+ new ScheduledMaintenanceState();
445
+ scheduledState._id = STATE_ID.toString();
446
+ jest
447
+ .spyOn(ScheduledMaintenanceStateService, "findOneBy")
448
+ .mockResolvedValue(scheduledState as never);
449
+ }
450
+
451
+ test("create checks the monitor status it switches monitors to", async () => {
452
+ mockScheduledState();
453
+ spyOnValidator();
454
+ jest
455
+ .spyOn(ProjectService, "incrementAndGetScheduledMaintenanceCounter")
456
+ .mockResolvedValue({ counter: 1, prefix: undefined } as never);
457
+
458
+ await callHook(ScheduledMaintenanceService, "onBeforeCreate", {
459
+ data: {
460
+ title: "test",
461
+ changeMonitorStatusToId: MONITOR_STATUS_ID,
462
+ } as ScheduledMaintenance,
463
+ props: { tenantId: PROJECT_ID },
464
+ });
465
+
466
+ expect(captured).toEqual([
467
+ {
468
+ subject: "scheduled maintenance event",
469
+ modelName: "Monitor Status",
470
+ id: MONITOR_STATUS_ID.toString(),
471
+ service: "MonitorStatusService",
472
+ },
473
+ ]);
474
+ });
475
+
476
+ test("create is rejected, and burns no event number, when the status is foreign", async () => {
477
+ mockScheduledState();
478
+ spyOnValidator(true);
479
+ const counter: jest.Mock = jest.fn() as unknown as jest.Mock;
480
+ jest
481
+ .spyOn(ProjectService, "incrementAndGetScheduledMaintenanceCounter")
482
+ .mockImplementation(counter as never);
483
+
484
+ await expect(
485
+ callHook(ScheduledMaintenanceService, "onBeforeCreate", {
486
+ data: {
487
+ title: "test",
488
+ changeMonitorStatusToId: MONITOR_STATUS_ID,
489
+ } as ScheduledMaintenance,
490
+ props: { tenantId: PROJECT_ID },
491
+ }),
492
+ ).rejects.toThrow("belong to a different project");
493
+
494
+ expect(counter).not.toHaveBeenCalled();
495
+ });
496
+
497
+ test("update checks the state and the monitor status", async () => {
498
+ spyOnValidator();
499
+
500
+ await callHook(ScheduledMaintenanceService, "onBeforeUpdate", {
501
+ data: {
502
+ currentScheduledMaintenanceStateId: STATE_ID,
503
+ changeMonitorStatusToId: MONITOR_STATUS_ID,
504
+ },
505
+ query: {},
506
+ props: { tenantId: PROJECT_ID },
507
+ });
508
+
509
+ expect(captured).toEqual([
510
+ {
511
+ subject: "scheduled maintenance event",
512
+ modelName: "Scheduled Maintenance State",
513
+ id: STATE_ID.toString(),
514
+ service: "ScheduledMaintenanceStateService",
515
+ },
516
+ {
517
+ subject: "scheduled maintenance event",
518
+ modelName: "Monitor Status",
519
+ id: MONITOR_STATUS_ID.toString(),
520
+ service: "MonitorStatusService",
521
+ },
522
+ ]);
523
+ });
524
+ });
525
+
526
+ describe("IncidentTemplateService", () => {
527
+ test("create checks every reference the template copies onto its incidents", async () => {
528
+ spyOnValidator();
529
+
530
+ await callHook(IncidentTemplateService, "onBeforeCreate", {
531
+ data: {
532
+ templateName: "test",
533
+ initialIncidentStateId: STATE_ID,
534
+ incidentSeverityId: SEVERITY_ID,
535
+ changeMonitorStatusToId: MONITOR_STATUS_ID,
536
+ } as IncidentTemplate,
537
+ props: { tenantId: PROJECT_ID },
538
+ });
539
+
540
+ expect(captured).toEqual([
541
+ {
542
+ subject: "incident template",
543
+ modelName: "Incident State",
544
+ id: STATE_ID.toString(),
545
+ service: "IncidentStateService",
546
+ },
547
+ {
548
+ subject: "incident template",
549
+ modelName: "Incident Severity",
550
+ id: SEVERITY_ID.toString(),
551
+ service: "IncidentSeverityService",
552
+ },
553
+ {
554
+ subject: "incident template",
555
+ modelName: "Monitor Status",
556
+ id: MONITOR_STATUS_ID.toString(),
557
+ service: "MonitorStatusService",
558
+ },
559
+ ]);
560
+ });
561
+
562
+ test("update checks the same references", async () => {
563
+ spyOnValidator();
564
+
565
+ await callHook(IncidentTemplateService, "onBeforeUpdate", {
566
+ data: { incidentSeverityId: SEVERITY_ID },
567
+ query: {},
568
+ props: { tenantId: PROJECT_ID },
569
+ });
570
+
571
+ expect(captured).toEqual([
572
+ {
573
+ subject: "incident template",
574
+ modelName: "Incident Severity",
575
+ id: SEVERITY_ID.toString(),
576
+ service: "IncidentSeverityService",
577
+ },
578
+ ]);
579
+ });
580
+
581
+ test("create is rejected when a reference is foreign", async () => {
582
+ spyOnValidator(true);
583
+
584
+ await expect(
585
+ callHook(IncidentTemplateService, "onBeforeCreate", {
586
+ data: {
587
+ templateName: "test",
588
+ incidentSeverityId: SEVERITY_ID,
589
+ } as IncidentTemplate,
590
+ props: { tenantId: PROJECT_ID },
591
+ }),
592
+ ).rejects.toThrow("belong to a different project");
593
+ });
594
+ });
595
+
596
+ describe("ScheduledMaintenanceTemplateService", () => {
597
+ test("create checks the monitor status the template carries", async () => {
598
+ spyOnValidator();
599
+
600
+ await callHook(ScheduledMaintenanceTemplateService, "onBeforeCreate", {
601
+ data: {
602
+ templateName: "test",
603
+ title: "test",
604
+ description: "test",
605
+ changeMonitorStatusToId: MONITOR_STATUS_ID,
606
+ } as ScheduledMaintenanceTemplate,
607
+ props: { tenantId: PROJECT_ID },
608
+ });
609
+
610
+ expect(captured).toEqual([
611
+ {
612
+ subject: "scheduled maintenance template",
613
+ modelName: "Monitor Status",
614
+ id: MONITOR_STATUS_ID.toString(),
615
+ service: "MonitorStatusService",
616
+ },
617
+ ]);
618
+ });
619
+
620
+ test("create is rejected when the status is foreign", async () => {
621
+ spyOnValidator(true);
622
+
623
+ await expect(
624
+ callHook(ScheduledMaintenanceTemplateService, "onBeforeCreate", {
625
+ data: {
626
+ templateName: "test",
627
+ title: "test",
628
+ description: "test",
629
+ changeMonitorStatusToId: MONITOR_STATUS_ID,
630
+ } as ScheduledMaintenanceTemplate,
631
+ props: { tenantId: PROJECT_ID },
632
+ }),
633
+ ).rejects.toThrow("belong to a different project");
634
+ });
635
+ });
636
+
637
+ describe("episodes", () => {
638
+ /*
639
+ * IncidentEpisode and AlertEpisode hold the same state and severity
640
+ * columns on equally NO ACTION foreign keys. Leaving them unguarded means
641
+ * a single API call re-creates the undeletable project the migration just
642
+ * repaired, and there is no second repair scheduled.
643
+ */
644
+ test("incident episode create checks the severity", async () => {
645
+ const createdState: IncidentState = new IncidentState();
646
+ createdState._id = STATE_ID.toString();
647
+ jest
648
+ .spyOn(IncidentStateService, "findOneBy")
649
+ .mockResolvedValue(createdState as never);
650
+ jest
651
+ .spyOn(ProjectService, "incrementAndGetIncidentEpisodeCounter")
652
+ .mockResolvedValue({ counter: 1, prefix: undefined } as never);
653
+ spyOnValidator();
654
+
655
+ await callHook(IncidentEpisodeService, "onBeforeCreate", {
656
+ data: {
657
+ title: "test",
658
+ incidentSeverityId: SEVERITY_ID,
659
+ } as IncidentEpisode,
660
+ props: { tenantId: PROJECT_ID },
661
+ });
662
+
663
+ expect(captured).toEqual([
664
+ {
665
+ subject: "incident episode",
666
+ modelName: "Incident Severity",
667
+ id: SEVERITY_ID.toString(),
668
+ service: "IncidentSeverityService",
669
+ },
670
+ ]);
671
+ });
672
+
673
+ test("incident episode update checks state and severity", async () => {
674
+ spyOnValidator();
675
+
676
+ await callHook(IncidentEpisodeService, "onBeforeUpdate", {
677
+ data: {
678
+ currentIncidentStateId: STATE_ID,
679
+ incidentSeverityId: SEVERITY_ID,
680
+ },
681
+ query: {},
682
+ props: { tenantId: PROJECT_ID },
683
+ });
684
+
685
+ expect(captured).toEqual([
686
+ {
687
+ subject: "incident episode",
688
+ modelName: "Incident State",
689
+ id: STATE_ID.toString(),
690
+ service: "IncidentStateService",
691
+ },
692
+ {
693
+ subject: "incident episode",
694
+ modelName: "Incident Severity",
695
+ id: SEVERITY_ID.toString(),
696
+ service: "IncidentSeverityService",
697
+ },
698
+ ]);
699
+ });
700
+
701
+ test("alert episode update checks state and severity", async () => {
702
+ spyOnValidator();
703
+
704
+ await callHook(AlertEpisodeService, "onBeforeUpdate", {
705
+ data: {
706
+ currentAlertStateId: STATE_ID,
707
+ alertSeverityId: SEVERITY_ID,
708
+ },
709
+ query: {},
710
+ props: { tenantId: PROJECT_ID },
711
+ });
712
+
713
+ expect(captured).toEqual([
714
+ {
715
+ subject: "alert episode",
716
+ modelName: "Alert State",
717
+ id: STATE_ID.toString(),
718
+ service: "AlertStateService",
719
+ },
720
+ {
721
+ subject: "alert episode",
722
+ modelName: "Alert Severity",
723
+ id: SEVERITY_ID.toString(),
724
+ service: "AlertSeverityService",
725
+ },
726
+ ]);
727
+ });
728
+
729
+ test("alert episode create checks the severity", async () => {
730
+ const createdState: AlertState = new AlertState();
731
+ createdState._id = STATE_ID.toString();
732
+ jest
733
+ .spyOn(AlertStateService, "findOneBy")
734
+ .mockResolvedValue(createdState as never);
735
+ jest
736
+ .spyOn(ProjectService, "incrementAndGetAlertEpisodeCounter")
737
+ .mockResolvedValue({ counter: 1, prefix: undefined } as never);
738
+ spyOnValidator();
739
+
740
+ await callHook(AlertEpisodeService, "onBeforeCreate", {
741
+ data: {
742
+ title: "test",
743
+ alertSeverityId: SEVERITY_ID,
744
+ } as AlertEpisode,
745
+ props: { tenantId: PROJECT_ID },
746
+ });
747
+
748
+ expect(captured).toEqual([
749
+ {
750
+ subject: "alert episode",
751
+ modelName: "Alert Severity",
752
+ id: SEVERITY_ID.toString(),
753
+ service: "AlertSeverityService",
754
+ },
755
+ ]);
756
+ });
757
+ });
758
+
759
+ describe("MonitorService", () => {
760
+ /*
761
+ * Monitor.currentMonitorStatusId is the one project-scoped reference that
762
+ * intentionally keeps ON DELETE NO ACTION, and it is writable by any
763
+ * project member — so it can block a project delete on its own. The
764
+ * 1785240000000 migration repaired the rows that existed then; this is
765
+ * what stops new ones.
766
+ */
767
+ test("update checks currentMonitorStatusId", async () => {
768
+ spyOnValidator();
769
+
770
+ await callHook(MonitorService, "onBeforeUpdate", {
771
+ data: { currentMonitorStatusId: MONITOR_STATUS_ID },
772
+ query: {},
773
+ props: { tenantId: PROJECT_ID },
774
+ });
775
+
776
+ expect(captured).toEqual([
777
+ {
778
+ subject: "monitor",
779
+ modelName: "Monitor Status",
780
+ id: MONITOR_STATUS_ID.toString(),
781
+ service: "MonitorStatusService",
782
+ },
783
+ ]);
784
+ });
785
+
786
+ test("update is rejected when the status belongs to another project", async () => {
787
+ spyOnValidator(true);
788
+
789
+ await expect(
790
+ callHook(MonitorService, "onBeforeUpdate", {
791
+ data: { currentMonitorStatusId: MONITOR_STATUS_ID },
792
+ query: {},
793
+ props: { tenantId: PROJECT_ID },
794
+ }),
795
+ ).rejects.toThrow("belong to a different project");
796
+ });
797
+
798
+ test("update looks nothing up when neither monitorSteps nor the status is written", async () => {
799
+ spyOnValidator();
800
+
801
+ await callHook(MonitorService, "onBeforeUpdate", {
802
+ data: { description: "renamed" },
803
+ query: {},
804
+ props: { tenantId: PROJECT_ID },
805
+ });
806
+
807
+ expect(validatorCalls).toHaveLength(0);
808
+ });
809
+ });
810
+ });