@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
@@ -24,6 +24,10 @@ import Model from "../../Models/DatabaseModels/ScheduledMaintenance";
24
24
  import ScheduledMaintenanceOwnerTeam from "../../Models/DatabaseModels/ScheduledMaintenanceOwnerTeam";
25
25
  import ScheduledMaintenanceOwnerUser from "../../Models/DatabaseModels/ScheduledMaintenanceOwnerUser";
26
26
  import ScheduledMaintenanceState from "../../Models/DatabaseModels/ScheduledMaintenanceState";
27
+ import MonitorStatusService from "./MonitorStatusService";
28
+ import ProjectScopedReferenceValidator, {
29
+ resolveReferenceId,
30
+ } from "../Utils/Database/ProjectScopedReferenceValidator";
27
31
  import ScheduledMaintenanceStateTimeline from "../../Models/DatabaseModels/ScheduledMaintenanceStateTimeline";
28
32
  import User from "../../Models/DatabaseModels/User";
29
33
  import Recurring from "../../Types/Events/Recurring";
@@ -599,12 +603,91 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
599
603
  }
600
604
  }
601
605
 
606
+ await this.validateProjectScopedReferences(updateBy);
607
+
602
608
  return {
603
609
  updateBy,
604
610
  carryForward: null,
605
611
  };
606
612
  }
607
613
 
614
+ /*
615
+ * An update can repoint a scheduled maintenance event at another project's
616
+ * state or monitor status just as easily as a create can, and the result is
617
+ * the same: the referenced project can no longer be deleted. Only the
618
+ * columns actually being written are checked, so ordinary updates cost no
619
+ * extra queries.
620
+ */
621
+ private async validateProjectScopedReferences(
622
+ updateBy: UpdateBy<Model>,
623
+ ): Promise<void> {
624
+ const stateId: ObjectID | string | undefined =
625
+ resolveReferenceId(updateBy.data.currentScheduledMaintenanceStateId) ||
626
+ resolveReferenceId(updateBy.data.currentScheduledMaintenanceState);
627
+
628
+ const monitorStatusId: ObjectID | string | undefined =
629
+ resolveReferenceId(updateBy.data.changeMonitorStatusToId) ||
630
+ resolveReferenceId(updateBy.data.changeMonitorStatusTo);
631
+
632
+ if (!stateId && !monitorStatusId) {
633
+ return;
634
+ }
635
+
636
+ /*
637
+ * Root/API updates do not always carry a tenantId, so fall back to the
638
+ * project of each event the query actually matches.
639
+ */
640
+ const projectIds: Array<ObjectID> = updateBy.props.tenantId
641
+ ? [updateBy.props.tenantId]
642
+ : await this.getProjectIdsForUpdateQuery(updateBy);
643
+
644
+ for (const projectId of projectIds) {
645
+ await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
646
+ projectId: projectId,
647
+ subject: "scheduled maintenance event",
648
+ references: [
649
+ {
650
+ modelName: "Scheduled Maintenance State",
651
+ id: stateId,
652
+ service: ScheduledMaintenanceStateService,
653
+ },
654
+ {
655
+ modelName: "Monitor Status",
656
+ id: monitorStatusId,
657
+ service: MonitorStatusService,
658
+ },
659
+ ],
660
+ });
661
+ }
662
+ }
663
+
664
+ private async getProjectIdsForUpdateQuery(
665
+ updateBy: UpdateBy<Model>,
666
+ ): Promise<Array<ObjectID>> {
667
+ const scheduledMaintenanceEvents: Array<Model> = await this.findBy({
668
+ query: updateBy.query,
669
+ select: {
670
+ projectId: true,
671
+ },
672
+ limit: LIMIT_MAX,
673
+ skip: 0,
674
+ props: {
675
+ isRoot: true,
676
+ },
677
+ });
678
+
679
+ const projectIds: Dictionary<ObjectID> = {};
680
+
681
+ for (const scheduledMaintenance of scheduledMaintenanceEvents) {
682
+ if (scheduledMaintenance.projectId) {
683
+ projectIds[scheduledMaintenance.projectId.toString()] =
684
+ scheduledMaintenance.projectId;
685
+ }
686
+ }
687
+
688
+ return Object.values(projectIds);
689
+ }
690
+
608
691
  @CaptureSpan()
609
692
  protected override async onBeforeDelete(
610
693
  deleteBy: DeleteBy<Model>,
@@ -758,6 +841,26 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
758
841
  createBy.data.currentScheduledMaintenanceStateId =
759
842
  scheduledMaintenanceState.id;
760
843
 
844
+ /*
845
+ * changeMonitorStatusToId comes straight from the API caller or a
846
+ * template, and nothing checked it belongs to this project. Persisting
847
+ * another project's id leaves that project undeletable. Runs before the
848
+ * counter increment so a rejected create does not burn an event number.
849
+ */
850
+ await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
851
+ projectId: projectId,
852
+ subject: "scheduled maintenance event",
853
+ references: [
854
+ {
855
+ modelName: "Monitor Status",
856
+ id:
857
+ resolveReferenceId(createBy.data.changeMonitorStatusToId) ||
858
+ resolveReferenceId(createBy.data.changeMonitorStatusTo),
859
+ service: MonitorStatusService,
860
+ },
861
+ ],
862
+ });
863
+
761
864
  const scheduledMaintenanceCounterResult: {
762
865
  counter: number;
763
866
  prefix: string | undefined;
@@ -6,6 +6,12 @@ import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCom
6
6
  import ObjectID from "../../Types/ObjectID";
7
7
  import Typeof from "../../Types/Typeof";
8
8
  import Model from "../../Models/DatabaseModels/ScheduledMaintenanceTemplate";
9
+ import MonitorStatusService from "./MonitorStatusService";
10
+ import Dictionary from "../../Types/Dictionary";
11
+ import ProjectScopedReferenceValidator, {
12
+ ProjectScopedReference,
13
+ resolveReferenceId,
14
+ } from "../Utils/Database/ProjectScopedReferenceValidator";
9
15
  import ScheduledMaintenanceTemplateOwnerTeam from "../../Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerTeam";
10
16
  import ScheduledMaintenanceTemplateOwnerUser from "../../Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser";
11
17
  import CreateBy from "../Types/Database/CreateBy";
@@ -113,6 +119,12 @@ export class Service extends DatabaseService<Model> {
113
119
  ): Promise<OnCreate<Model>> {
114
120
  this.validateEventTemplate(createBy.data);
115
121
 
122
+ await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
123
+ projectId: createBy.props.tenantId || createBy.data.projectId,
124
+ subject: "scheduled maintenance template",
125
+ references: this.getProjectScopedReferences(createBy.data),
126
+ });
127
+
116
128
  if (createBy.data.isRecurringEvent) {
117
129
  // if all is good then the next scheduled at time should be set.
118
130
  createBy.data.scheduleNextEventAt = this.getNextEventTime({
@@ -253,12 +265,90 @@ export class Service extends DatabaseService<Model> {
253
265
 
254
266
  updateBy.data = newTemplate;
255
267
 
268
+ await this.validateProjectScopedReferences(updateBy);
269
+
256
270
  return {
257
271
  updateBy: updateBy,
258
272
  carryForward: false,
259
273
  };
260
274
  }
261
275
 
276
+ /*
277
+ * A template is copied onto every event created from it, so an id belonging
278
+ * to another project here becomes a cross-project reference on each of those
279
+ * events — and the referenced project can then no longer be deleted.
280
+ */
281
+ private async validateProjectScopedReferences(
282
+ updateBy: UpdateBy<Model>,
283
+ ): Promise<void> {
284
+ const references: Array<ProjectScopedReference> =
285
+ this.getProjectScopedReferences(updateBy.data);
286
+
287
+ if (
288
+ references.every((reference: ProjectScopedReference) => {
289
+ return !reference.id;
290
+ })
291
+ ) {
292
+ return;
293
+ }
294
+
295
+ /*
296
+ * Root/API updates do not always carry a tenantId, so fall back to the
297
+ * project of each template the query actually matches.
298
+ */
299
+ const projectIds: Array<ObjectID> = updateBy.props.tenantId
300
+ ? [updateBy.props.tenantId]
301
+ : await this.getProjectIdsForUpdateQuery(updateBy);
302
+
303
+ for (const projectId of projectIds) {
304
+ await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
305
+ projectId: projectId,
306
+ subject: "scheduled maintenance template",
307
+ references: references,
308
+ });
309
+ }
310
+ }
311
+
312
+ private getProjectScopedReferences(
313
+ data: Model | QueryDeepPartialEntity<Model>,
314
+ ): Array<ProjectScopedReference> {
315
+ return [
316
+ {
317
+ modelName: "Monitor Status",
318
+ id:
319
+ resolveReferenceId(data.changeMonitorStatusToId) ||
320
+ resolveReferenceId(data.changeMonitorStatusTo),
321
+ service: MonitorStatusService,
322
+ },
323
+ ];
324
+ }
325
+
326
+ private async getProjectIdsForUpdateQuery(
327
+ updateBy: UpdateBy<Model>,
328
+ ): Promise<Array<ObjectID>> {
329
+ const templates: Array<Model> = await this.findBy({
330
+ query: updateBy.query,
331
+ select: {
332
+ projectId: true,
333
+ },
334
+ limit: LIMIT_MAX,
335
+ skip: 0,
336
+ props: {
337
+ isRoot: true,
338
+ },
339
+ });
340
+
341
+ const projectIds: Dictionary<ObjectID> = {};
342
+
343
+ for (const template of templates) {
344
+ if (template.projectId) {
345
+ projectIds[template.projectId.toString()] = template.projectId;
346
+ }
347
+ }
348
+
349
+ return Object.values(projectIds);
350
+ }
351
+
262
352
  @CaptureSpan()
263
353
  protected override async onCreateSuccess(
264
354
  onCreate: OnCreate<Model>,
@@ -1,6 +1,8 @@
1
1
  import CreateBy from "../Types/Database/CreateBy";
2
+ import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
2
3
  import DeleteBy from "../Types/Database/DeleteBy";
3
4
  import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
5
+ import Query from "../Types/Database/Query";
4
6
  import QueryHelper from "../Types/Database/QueryHelper";
5
7
  import UpdateBy from "../Types/Database/UpdateBy";
6
8
  import DatabaseService from "./DatabaseService";
@@ -10,6 +12,7 @@ import LIMIT_MAX from "../../Types/Database/LimitMax";
10
12
  import BadDataException from "../../Types/Exception/BadDataException";
11
13
  import ObjectID from "../../Types/ObjectID";
12
14
  import PositiveNumber from "../../Types/PositiveNumber";
15
+ import StatusPageGroupTreeUtil from "../../Utils/StatusPage/GroupTree";
13
16
  import Model from "../../Models/DatabaseModels/StatusPageGroup";
14
17
 
15
18
  export class Service extends DatabaseService<Model> {
@@ -25,6 +28,14 @@ export class Service extends DatabaseService<Model> {
25
28
  throw new BadDataException("Status Page Group statusPageId is required");
26
29
  }
27
30
 
31
+ if (createBy.data.parentStatusPageGroupId) {
32
+ await this.assertParentIsValid({
33
+ statusPageGroupId: null,
34
+ parentStatusPageGroupId: createBy.data.parentStatusPageGroupId,
35
+ statusPageId: createBy.data.statusPageId,
36
+ });
37
+ }
38
+
28
39
  if (!createBy.data.order) {
29
40
  const count: PositiveNumber = await this.countBy({
30
41
  query: {
@@ -101,10 +112,185 @@ export class Service extends DatabaseService<Model> {
101
112
  };
102
113
  }
103
114
 
115
+ /*
116
+ * The hierarchy hooks run BEFORE DatabaseService applies tenant scoping to
117
+ * the caller's query, so reading the raw client query with props.isRoot
118
+ * would hand the hook rows from other projects. Re-apply the caller's tenant
119
+ * so validation can never look at a row outside the caller's project.
120
+ */
121
+ private scopeQueryToCallerTenant(
122
+ query: Query<Model>,
123
+ props: DatabaseCommonInteractionProps,
124
+ ): Query<Model> {
125
+ if (props.isRoot || !props.tenantId) {
126
+ return query;
127
+ }
128
+
129
+ return {
130
+ ...query,
131
+ projectId: props.tenantId,
132
+ };
133
+ }
134
+
135
+ /*
136
+ * A parent has to be a real group on the same status page, and the move must
137
+ * not build a loop (A under B under A) or nest deeper than the tree utils
138
+ * are willing to walk. A loop would make every rolled up number on the page
139
+ * meaningless and the group itself unreachable in the rendered tree.
140
+ */
141
+ private async assertParentIsValid(data: {
142
+ statusPageGroupId: ObjectID | null;
143
+ parentStatusPageGroupId: ObjectID;
144
+ statusPageId: ObjectID;
145
+ }): Promise<void> {
146
+ if (
147
+ data.statusPageGroupId &&
148
+ data.statusPageGroupId.toString() ===
149
+ data.parentStatusPageGroupId.toString()
150
+ ) {
151
+ throw new BadDataException("A group cannot be its own parent group.");
152
+ }
153
+
154
+ const parent: Model | null = await this.findOneById({
155
+ id: data.parentStatusPageGroupId,
156
+ select: {
157
+ _id: true,
158
+ statusPageId: true,
159
+ },
160
+ props: {
161
+ isRoot: true,
162
+ },
163
+ });
164
+
165
+ if (!parent) {
166
+ throw new BadDataException("Parent group not found.");
167
+ }
168
+
169
+ if (parent.statusPageId?.toString() !== data.statusPageId.toString()) {
170
+ throw new BadDataException(
171
+ "Parent group must belong to the same status page.",
172
+ );
173
+ }
174
+
175
+ const groupsOnStatusPage: Array<Model> = await this.findBy({
176
+ query: {
177
+ statusPageId: data.statusPageId,
178
+ },
179
+ select: {
180
+ _id: true,
181
+ parentStatusPageGroupId: true,
182
+ },
183
+ limit: LIMIT_MAX,
184
+ skip: 0,
185
+ props: {
186
+ isRoot: true,
187
+ },
188
+ });
189
+
190
+ const ancestorsOfParent: Array<Model> =
191
+ StatusPageGroupTreeUtil.getAncestorGroups({
192
+ statusPageGroup: parent,
193
+ statusPageGroups: groupsOnStatusPage,
194
+ });
195
+
196
+ if (
197
+ data.statusPageGroupId &&
198
+ ancestorsOfParent.some((ancestor: Model) => {
199
+ return ancestor._id?.toString() === data.statusPageGroupId!.toString();
200
+ })
201
+ ) {
202
+ throw new BadDataException(
203
+ "This group cannot be nested under one of its own sub groups.",
204
+ );
205
+ }
206
+
207
+ /*
208
+ * Depth of the parent chain, plus the group being placed. A group that is
209
+ * moved carries its own subtree along, so the deepest descendant has to
210
+ * fit under the limit too.
211
+ */
212
+ const depthOfNewParent: number = ancestorsOfParent.length + 1;
213
+
214
+ let deepestDescendantOffset: number = 0;
215
+
216
+ if (data.statusPageGroupId) {
217
+ const group: Model | undefined = groupsOnStatusPage.find(
218
+ (item: Model) => {
219
+ return item._id?.toString() === data.statusPageGroupId!.toString();
220
+ },
221
+ );
222
+
223
+ if (group) {
224
+ for (const descendant of StatusPageGroupTreeUtil.getDescendantGroups({
225
+ statusPageGroup: group,
226
+ statusPageGroups: groupsOnStatusPage,
227
+ })) {
228
+ const relativeDepth: number =
229
+ StatusPageGroupTreeUtil.getDepth({
230
+ statusPageGroup: descendant,
231
+ statusPageGroups: groupsOnStatusPage,
232
+ }) -
233
+ StatusPageGroupTreeUtil.getDepth({
234
+ statusPageGroup: group,
235
+ statusPageGroups: groupsOnStatusPage,
236
+ });
237
+
238
+ deepestDescendantOffset = Math.max(
239
+ deepestDescendantOffset,
240
+ relativeDepth,
241
+ );
242
+ }
243
+ }
244
+ }
245
+
246
+ if (
247
+ depthOfNewParent + deepestDescendantOffset >=
248
+ StatusPageGroupTreeUtil.MaxNestingDepth
249
+ ) {
250
+ throw new BadDataException(
251
+ `Status page groups can only be nested ${StatusPageGroupTreeUtil.MaxNestingDepth} levels deep.`,
252
+ );
253
+ }
254
+ }
255
+
104
256
  @CaptureSpan()
105
257
  protected override async onBeforeUpdate(
106
258
  updateBy: UpdateBy<Model>,
107
259
  ): Promise<OnUpdate<Model>> {
260
+ const newParentIdValue: unknown = (updateBy.data as any)[
261
+ "parentStatusPageGroupId"
262
+ ];
263
+
264
+ // detaching a group (parent set to null) has no parent to validate.
265
+ if (newParentIdValue) {
266
+ const newParentId: ObjectID = new ObjectID(newParentIdValue.toString());
267
+
268
+ const groupsBeingUpdated: Array<Model> = await this.findBy({
269
+ query: this.scopeQueryToCallerTenant(updateBy.query, updateBy.props),
270
+ select: {
271
+ _id: true,
272
+ statusPageId: true,
273
+ },
274
+ limit: LIMIT_MAX,
275
+ skip: 0,
276
+ props: {
277
+ isRoot: true,
278
+ },
279
+ });
280
+
281
+ for (const group of groupsBeingUpdated) {
282
+ if (!group.id || !group.statusPageId) {
283
+ continue;
284
+ }
285
+
286
+ await this.assertParentIsValid({
287
+ statusPageGroupId: group.id,
288
+ parentStatusPageGroupId: newParentId,
289
+ statusPageId: group.statusPageId,
290
+ });
291
+ }
292
+ }
293
+
108
294
  if (updateBy.data.order && !updateBy.props.isRoot && updateBy.query._id) {
109
295
  const group: Model | null = await this.findOneBy({
110
296
  query: {
@@ -1,10 +1,143 @@
1
1
  import DatabaseService from "./DatabaseService";
2
2
  import Model from "../../Models/DatabaseModels/TraceDropFilter";
3
+ import CreateBy from "../Types/Database/CreateBy";
4
+ import UpdateBy from "../Types/Database/UpdateBy";
5
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
6
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
7
+ import ObjectID from "../../Types/ObjectID";
8
+ import OneUptimeDate from "../../Types/Date";
9
+ import PartialEntity from "../../Types/Database/PartialEntity";
10
+ import {
11
+ DropFilterCandidate,
12
+ DropFilterValidationOptions,
13
+ isSqlExpressionValue,
14
+ validateDropFilter,
15
+ } from "../Utils/DropFilterValidation";
16
+
17
+ const VALIDATION_OPTIONS: DropFilterValidationOptions = {
18
+ recordNoun: "spans",
19
+ };
3
20
 
4
21
  export class Service extends DatabaseService<Model> {
5
22
  public constructor() {
6
23
  super(Model);
7
24
  }
25
+
26
+ protected override async onBeforeCreate(
27
+ createBy: CreateBy<Model>,
28
+ ): Promise<OnCreate<Model>> {
29
+ validateDropFilter(
30
+ {
31
+ action: createBy.data.action,
32
+ samplePercentage: createBy.data.samplePercentage,
33
+ filterQuery: createBy.data.filterQuery as string | undefined,
34
+ },
35
+ VALIDATION_OPTIONS,
36
+ );
37
+
38
+ return { createBy, carryForward: null };
39
+ }
40
+
41
+ protected override async onBeforeUpdate(
42
+ updateBy: UpdateBy<Model>,
43
+ ): Promise<OnUpdate<Model>> {
44
+ /*
45
+ * An update can make a row invalid without naming every field: flipping
46
+ * `action` to "sample" while the stored `samplePercentage` is null, or
47
+ * blanking `filterQuery` on a row whose action is already "drop". So
48
+ * validate the MERGED row, not the incoming partial.
49
+ *
50
+ * Only pay for the SELECT when the update actually touches one of the
51
+ * three fields that can break the row. Drop-filter edits are rare,
52
+ * human-driven config changes — this is not a hot path.
53
+ */
54
+ const incomingAction: unknown = updateBy.data.action;
55
+ const incomingSamplePercentage: unknown = updateBy.data.samplePercentage;
56
+ const incomingFilterQuery: unknown = updateBy.data.filterQuery;
57
+
58
+ const touchesValidatedFields: boolean =
59
+ incomingAction !== undefined ||
60
+ incomingSamplePercentage !== undefined ||
61
+ incomingFilterQuery !== undefined;
62
+
63
+ if (!touchesValidatedFields) {
64
+ return { updateBy, carryForward: null };
65
+ }
66
+
67
+ if (
68
+ isSqlExpressionValue(incomingAction) ||
69
+ isSqlExpressionValue(incomingSamplePercentage) ||
70
+ isSqlExpressionValue(incomingFilterQuery)
71
+ ) {
72
+ // Cannot evaluate a raw SQL expression here. See isSqlExpressionValue.
73
+ return { updateBy, carryForward: null };
74
+ }
75
+
76
+ const existingRows: Array<Model> = await this.findBy({
77
+ query: updateBy.query,
78
+ skip: 0,
79
+ limit: LIMIT_MAX,
80
+ select: {
81
+ _id: true,
82
+ action: true,
83
+ samplePercentage: true,
84
+ filterQuery: true,
85
+ },
86
+ props: {
87
+ isRoot: true,
88
+ },
89
+ });
90
+
91
+ for (const existing of existingRows) {
92
+ const merged: DropFilterCandidate = {
93
+ action:
94
+ incomingAction !== undefined
95
+ ? (incomingAction as string | null)
96
+ : existing.action,
97
+ samplePercentage:
98
+ incomingSamplePercentage !== undefined
99
+ ? (incomingSamplePercentage as number | null)
100
+ : existing.samplePercentage,
101
+ filterQuery:
102
+ incomingFilterQuery !== undefined
103
+ ? (incomingFilterQuery as string | null)
104
+ : (existing.filterQuery as string | null),
105
+ };
106
+
107
+ validateDropFilter(merged, VALIDATION_OPTIONS);
108
+ }
109
+
110
+ return { updateBy, carryForward: null };
111
+ }
112
+
113
+ /*
114
+ * Ingest-path counter flush: add `count` drops to this filter's running
115
+ * total and stamp `lastDroppedAt`, in one atomic statement.
116
+ *
117
+ * Called from the telemetry ingest workers on a throttle (many dropped
118
+ * spans batched into one UPDATE), never once per record, and never on the
119
+ * request path. Runs no hooks and does not bump `version`, so it cannot
120
+ * conflict with a human editing the same filter in the dashboard.
121
+ *
122
+ * See App/FeatureSet/Telemetry/Utils/DropFilterDropRecorder.ts.
123
+ */
124
+ public async addToDroppedCount(data: {
125
+ id: ObjectID;
126
+ count: number;
127
+ lastDroppedAt?: Date | undefined;
128
+ }): Promise<void> {
129
+ if (!data.count || data.count <= 0) {
130
+ return;
131
+ }
132
+
133
+ await this.atomicAddToColumnsByIdWithoutHooks({
134
+ id: data.id,
135
+ add: { droppedCount: data.count },
136
+ set: {
137
+ lastDroppedAt: data.lastDroppedAt || OneUptimeDate.getCurrentDate(),
138
+ } as PartialEntity<Model>,
139
+ });
140
+ }
8
141
  }
9
142
 
10
143
  export default new Service();
@@ -48,6 +48,18 @@ export default class ApiDelete extends ComponentCode {
48
48
  headers: args["request-headers"] as Dictionary<string>,
49
49
  });
50
50
 
51
+ /*
52
+ * On the server API resolves with an HTTPErrorResponse instead of
53
+ * throwing, so the catch below never sees an HTTP error - a 4xx/5xx used
54
+ * to take the success port.
55
+ */
56
+ if (apiResult instanceof HTTPErrorResponse) {
57
+ return Promise.resolve({
58
+ returnValues: ApiComponentUtils.getReturnValues(apiResult),
59
+ executePort: result.errorPort,
60
+ });
61
+ }
62
+
51
63
  return Promise.resolve({
52
64
  returnValues: ApiComponentUtils.getReturnValues(apiResult),
53
65
  executePort: result.successPort,
@@ -48,6 +48,18 @@ export default class ApiGet extends ComponentCode {
48
48
  headers: args["request-headers"] as Dictionary<string>,
49
49
  });
50
50
 
51
+ /*
52
+ * On the server API resolves with an HTTPErrorResponse instead of
53
+ * throwing, so the catch below never sees an HTTP error - a 4xx/5xx used
54
+ * to take the success port.
55
+ */
56
+ if (apiResult instanceof HTTPErrorResponse) {
57
+ return Promise.resolve({
58
+ returnValues: ApiComponentUtils.getReturnValues(apiResult),
59
+ executePort: result.errorPort,
60
+ });
61
+ }
62
+
51
63
  return Promise.resolve({
52
64
  returnValues: ApiComponentUtils.getReturnValues(apiResult),
53
65
  executePort: result.successPort,
@@ -48,6 +48,18 @@ export default class ApiPut extends ComponentCode {
48
48
  headers: args["request-headers"] as Dictionary<string>,
49
49
  });
50
50
 
51
+ /*
52
+ * On the server API resolves with an HTTPErrorResponse instead of
53
+ * throwing, so the catch below never sees an HTTP error - a 4xx/5xx used
54
+ * to take the success port.
55
+ */
56
+ if (apiResult instanceof HTTPErrorResponse) {
57
+ return Promise.resolve({
58
+ returnValues: ApiComponentUtils.getReturnValues(apiResult),
59
+ executePort: result.errorPort,
60
+ });
61
+ }
62
+
51
63
  return Promise.resolve({
52
64
  returnValues: ApiComponentUtils.getReturnValues(apiResult),
53
65
  executePort: result.successPort,
@@ -81,6 +81,18 @@ export default class ApiPost extends ComponentCode {
81
81
 
82
82
  logger.debug("API Post Component is done.", workflowLogAttributes);
83
83
 
84
+ /*
85
+ * On the server API resolves with an HTTPErrorResponse instead of
86
+ * throwing, so the catch below never sees an HTTP error - a 4xx/5xx used
87
+ * to take the success port.
88
+ */
89
+ if (apiResult instanceof HTTPErrorResponse) {
90
+ return Promise.resolve({
91
+ returnValues: ApiComponentUtils.getReturnValues(apiResult),
92
+ executePort: result.errorPort,
93
+ });
94
+ }
95
+
84
96
  return Promise.resolve({
85
97
  returnValues: ApiComponentUtils.getReturnValues(apiResult),
86
98
  executePort: result.successPort,