@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,320 @@
1
+ import BaseAPI from "../../../Server/API/BaseAPI";
2
+ import DatabaseService from "../../../Server/Services/DatabaseService";
3
+ import {
4
+ ExpressResponse,
5
+ OneUptimeRequest,
6
+ } from "../../../Server/Utils/Express";
7
+ import Response from "../../../Server/Utils/Response";
8
+ import { mockRouter } from "./Helpers";
9
+ import { beforeEach, describe, expect, it, jest } from "@jest/globals";
10
+ import BadDataException from "../../../Types/Exception/BadDataException";
11
+ import { JSONObject } from "../../../Types/JSON";
12
+ import Probe from "../../../Models/DatabaseModels/Probe";
13
+ import { getJestSpyOn } from "../../Spy";
14
+
15
+ jest.mock("../../../Server/Utils/Express", () => {
16
+ return {
17
+ getRouter: () => {
18
+ return mockRouter;
19
+ },
20
+ };
21
+ });
22
+
23
+ jest.mock("../../../Server/Utils/Response", () => {
24
+ return {
25
+ sendEmptySuccessResponse: jest.fn(),
26
+ sendEntityResponse: jest.fn(),
27
+ sendEntityArrayResponse: jest.fn(),
28
+ sendJsonObjectResponse: jest.fn(),
29
+ };
30
+ });
31
+
32
+ /*
33
+ * PUT /<model>/:id takes its payload wrapped as { "data": { ...columns... } }.
34
+ *
35
+ * A caller that sent the columns flat instead - no "data" key - got HTTP 200
36
+ * and an empty body, and nothing was written. body["data"] was undefined,
37
+ * deserializing that handed back {}, and an update carrying no columns still
38
+ * matched the row, so the "matched nothing" guard never fired. The fields were
39
+ * dropped on the floor and the caller was told the write succeeded.
40
+ *
41
+ * A customer hit this rotating an API token from a Workflow: every run
42
+ * reported success and every run read back the previous token.
43
+ */
44
+
45
+ const TEST_ID: string = "550e8400-e29b-41d4-a716-446655440009";
46
+
47
+ describe("BaseAPI.updateItem payload validation", () => {
48
+ let service: DatabaseService<Probe>;
49
+ let api: BaseAPI<Probe, DatabaseService<Probe>>;
50
+ let response: ExpressResponse;
51
+
52
+ type RequestWithBodyFunction = (body: JSONObject) => OneUptimeRequest;
53
+
54
+ const requestWithBody: RequestWithBodyFunction = (
55
+ body: JSONObject,
56
+ ): OneUptimeRequest => {
57
+ return {
58
+ params: { id: TEST_ID },
59
+ body: body,
60
+ headers: {},
61
+ } as unknown as OneUptimeRequest;
62
+ };
63
+
64
+ beforeEach(() => {
65
+ jest.restoreAllMocks();
66
+ jest.clearAllMocks();
67
+
68
+ service = new DatabaseService<Probe>(Probe);
69
+ api = new BaseAPI<Probe, DatabaseService<Probe>>(Probe, service);
70
+
71
+ response = {
72
+ status: jest.fn().mockReturnThis(),
73
+ json: jest.fn().mockReturnThis(),
74
+ send: jest.fn().mockReturnThis(),
75
+ } as unknown as ExpressResponse;
76
+
77
+ getJestSpyOn(service, "updateOneById").mockResolvedValue(1);
78
+ });
79
+
80
+ describe("when the payload is not wrapped in a data object", () => {
81
+ /*
82
+ * This is the customer's exact request body, minus the token value. It is
83
+ * the case the whole fix exists for.
84
+ */
85
+ it("refuses a flat body that puts the columns at the top level", async () => {
86
+ const request: OneUptimeRequest = requestWithBody({
87
+ content: "new-token-value",
88
+ name: "Airflow-Token",
89
+ description: "Token to connect to Airflow.",
90
+ });
91
+
92
+ await expect(api.updateItem(request, response)).rejects.toThrow(
93
+ BadDataException,
94
+ );
95
+
96
+ expect(service.updateOneById).not.toHaveBeenCalled();
97
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
98
+ });
99
+
100
+ it("names the shape it wants so the caller can fix the request", async () => {
101
+ const request: OneUptimeRequest = requestWithBody({
102
+ content: "new-token-value",
103
+ });
104
+
105
+ await expect(api.updateItem(request, response)).rejects.toThrow(/data/);
106
+ });
107
+
108
+ it("refuses an empty body", async () => {
109
+ await expect(
110
+ api.updateItem(requestWithBody({}), response),
111
+ ).rejects.toThrow(BadDataException);
112
+
113
+ expect(service.updateOneById).not.toHaveBeenCalled();
114
+ });
115
+
116
+ it("refuses a null data payload", async () => {
117
+ await expect(
118
+ api.updateItem(requestWithBody({ data: null }), response),
119
+ ).rejects.toThrow(BadDataException);
120
+
121
+ expect(service.updateOneById).not.toHaveBeenCalled();
122
+ });
123
+
124
+ /*
125
+ * A string used to survive deserialization as its own index map -
126
+ * "abc" became { "0": "a", "1": "b", "2": "c" } - and went to the service
127
+ * as three nonsense columns.
128
+ */
129
+ it("refuses a string data payload rather than indexing it into columns", async () => {
130
+ await expect(
131
+ api.updateItem(
132
+ requestWithBody({ data: "abc" } as JSONObject),
133
+ response,
134
+ ),
135
+ ).rejects.toThrow(BadDataException);
136
+
137
+ expect(service.updateOneById).not.toHaveBeenCalled();
138
+ });
139
+
140
+ it("refuses a numeric data payload", async () => {
141
+ await expect(
142
+ api.updateItem(requestWithBody({ data: 42 } as JSONObject), response),
143
+ ).rejects.toThrow(BadDataException);
144
+ });
145
+
146
+ it("refuses a boolean data payload", async () => {
147
+ await expect(
148
+ api.updateItem(requestWithBody({ data: true } as JSONObject), response),
149
+ ).rejects.toThrow(BadDataException);
150
+ });
151
+
152
+ it("refuses an empty array data payload", async () => {
153
+ await expect(
154
+ api.updateItem(requestWithBody({ data: [] } as JSONObject), response),
155
+ ).rejects.toThrow(BadDataException);
156
+
157
+ expect(service.updateOneById).not.toHaveBeenCalled();
158
+ });
159
+
160
+ it("refuses an array of objects, which updates one row not many", async () => {
161
+ await expect(
162
+ api.updateItem(
163
+ requestWithBody({ data: [{ name: "renamed-probe" }] } as JSONObject),
164
+ response,
165
+ ),
166
+ ).rejects.toThrow(BadDataException);
167
+
168
+ expect(service.updateOneById).not.toHaveBeenCalled();
169
+ });
170
+ });
171
+
172
+ describe("when the payload carries no fields to write", () => {
173
+ it("refuses an empty data object", async () => {
174
+ await expect(
175
+ api.updateItem(requestWithBody({ data: {} }), response),
176
+ ).rejects.toThrow(BadDataException);
177
+
178
+ expect(service.updateOneById).not.toHaveBeenCalled();
179
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
180
+ });
181
+
182
+ it("says there is nothing to update", async () => {
183
+ await expect(
184
+ api.updateItem(requestWithBody({ data: {} }), response),
185
+ ).rejects.toThrow(/No fields to update/i);
186
+ });
187
+
188
+ /*
189
+ * _id, createdAt and updatedAt are stripped before the write, so a payload
190
+ * of nothing but those leaves zero columns. The emptiness check has to run
191
+ * after the strip to catch it.
192
+ */
193
+ it("refuses a payload of only the fields it strips", async () => {
194
+ const request: OneUptimeRequest = requestWithBody({
195
+ data: {
196
+ _id: TEST_ID,
197
+ createdAt: "2026-07-28T20:58:55.000Z",
198
+ updatedAt: "2026-07-28T20:58:55.000Z",
199
+ },
200
+ });
201
+
202
+ await expect(api.updateItem(request, response)).rejects.toThrow(
203
+ /No fields to update/i,
204
+ );
205
+
206
+ expect(service.updateOneById).not.toHaveBeenCalled();
207
+ });
208
+ });
209
+
210
+ describe("when the payload is well formed", () => {
211
+ it("writes the update and reports success", async () => {
212
+ const request: OneUptimeRequest = requestWithBody({
213
+ data: { name: "renamed-probe" },
214
+ });
215
+
216
+ await api.updateItem(request, response);
217
+
218
+ expect(service.updateOneById).toHaveBeenCalledTimes(1);
219
+ expect(Response.sendEmptySuccessResponse).toHaveBeenCalledWith(
220
+ request,
221
+ response,
222
+ );
223
+ });
224
+
225
+ it("passes the payload through to the service untouched", async () => {
226
+ await api.updateItem(
227
+ requestWithBody({ data: { name: "renamed-probe" } }),
228
+ response,
229
+ );
230
+
231
+ expect(service.updateOneById).toHaveBeenCalledWith(
232
+ expect.objectContaining({
233
+ data: { name: "renamed-probe" },
234
+ }),
235
+ );
236
+ });
237
+
238
+ /*
239
+ * The strip has to survive the new guard: a real client payload echoes
240
+ * _id/createdAt/updatedAt back and only the remaining columns may be
241
+ * written.
242
+ */
243
+ it("still strips _id, createdAt and updatedAt from a payload that also has real fields", async () => {
244
+ await api.updateItem(
245
+ requestWithBody({
246
+ data: {
247
+ _id: TEST_ID,
248
+ createdAt: "2026-07-28T20:58:55.000Z",
249
+ updatedAt: "2026-07-28T20:58:55.000Z",
250
+ name: "renamed-probe",
251
+ },
252
+ }),
253
+ response,
254
+ );
255
+
256
+ expect(service.updateOneById).toHaveBeenCalledWith(
257
+ expect.objectContaining({
258
+ data: { name: "renamed-probe" },
259
+ }),
260
+ );
261
+ });
262
+ });
263
+
264
+ /*
265
+ * PUT /:id, POST /:id/update-item and GET /:id/update-item all land on this
266
+ * one handler, so none of them can be left answering 200 to a payload that
267
+ * writes nothing. GET carries no body at all.
268
+ */
269
+ describe("across every route that reaches updateItem", () => {
270
+ it("refuses the flat body on the POST update-item route", async () => {
271
+ const request: OneUptimeRequest = requestWithBody({
272
+ content: "new-token-value",
273
+ name: "Airflow-Token",
274
+ });
275
+
276
+ await expect(api.updateItem(request, response)).rejects.toThrow(
277
+ BadDataException,
278
+ );
279
+ });
280
+
281
+ it("refuses the bodyless GET update-item route instead of quietly writing nothing", async () => {
282
+ await expect(
283
+ api.updateItem(requestWithBody({}), response),
284
+ ).rejects.toThrow(BadDataException);
285
+
286
+ expect(service.updateOneById).not.toHaveBeenCalled();
287
+ });
288
+
289
+ it("registers all three update routes", () => {
290
+ const crudPath: string = new Probe().getCrudApiPath()!.toString();
291
+
292
+ expect(mockRouter.match("PUT", `${crudPath}/:id`)).toBeTruthy();
293
+ expect(
294
+ mockRouter.match("POST", `${crudPath}/:id/update-item`),
295
+ ).toBeTruthy();
296
+ expect(
297
+ mockRouter.match("GET", `${crudPath}/:id/update-item`),
298
+ ).toBeTruthy();
299
+ });
300
+ });
301
+
302
+ /*
303
+ * The original symptom, stated as an assertion: the customer saw
304
+ * "response-status":200,"response-body":{} on every run while the variable
305
+ * never changed.
306
+ */
307
+ it("never reports success for a request that would write nothing", async () => {
308
+ const request: OneUptimeRequest = requestWithBody({
309
+ content: "new-token-value",
310
+ name: "Airflow-Token",
311
+ description: "Token to connect to Airflow.",
312
+ });
313
+
314
+ await expect(api.updateItem(request, response)).rejects.toThrow(
315
+ BadDataException,
316
+ );
317
+
318
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
319
+ });
320
+ });
@@ -0,0 +1,293 @@
1
+ import "../TestingUtils/Init";
2
+ import BaseAnalyticsAPI from "../../../Server/API/BaseAnalyticsAPI";
3
+ import AnalyticsDatabaseService from "../../../Server/Services/AnalyticsDatabaseService";
4
+ import MonitorLog from "../../../Models/AnalyticsModels/MonitorLog";
5
+ import {
6
+ ExpressResponse,
7
+ OneUptimeRequest,
8
+ } from "../../../Server/Utils/Express";
9
+ import Response from "../../../Server/Utils/Response";
10
+ import { mockRouter } from "./Helpers";
11
+ import { getJestSpyOn } from "../../Spy";
12
+ import BadDataException from "../../../Types/Exception/BadDataException";
13
+ import { JSONObject } from "../../../Types/JSON";
14
+ import { beforeEach, describe, expect, it, jest } from "@jest/globals";
15
+
16
+ jest.mock("../../../Server/Utils/Express", () => {
17
+ return {
18
+ getRouter: () => {
19
+ return mockRouter;
20
+ },
21
+ };
22
+ });
23
+
24
+ jest.mock("../../../Server/Utils/Response", () => {
25
+ return {
26
+ sendEmptySuccessResponse: jest.fn(),
27
+ sendEntityResponse: jest.fn(),
28
+ sendEntityArrayResponse: jest.fn(),
29
+ sendJsonObjectResponse: jest.fn(),
30
+ };
31
+ });
32
+
33
+ /*
34
+ * PUT /<analytics-model>/:id answered 200 with an empty body while writing
35
+ * nothing at all.
36
+ *
37
+ * The wire contract is a WRAPPED payload - { "data": { ...columns... } } - but
38
+ * nothing enforced it. A client that sent the columns flat (the shape a
39
+ * hand-written Workflow naturally produces) had body["data"] === undefined,
40
+ * which deserialized into an empty model, which matched the row and updated
41
+ * zero columns. The request looked successful and the edit silently vanished.
42
+ *
43
+ * These tests pin the guards in BaseAnalyticsAPI.updateItem: the payload must
44
+ * be a wrapped plain object, and it must still carry at least one updatable
45
+ * field once _id / createdAt / updatedAt are stripped.
46
+ *
47
+ * Note the emptiness check has to count fields on the RAW body, not on the
48
+ * hydrated model: an analytics model instance always carries its own internal
49
+ * properties (data, _tableColumns), so Object.keys() on the instance is never
50
+ * empty and could never detect a no-op update.
51
+ */
52
+
53
+ const TEST_ID: string = "550e8400-e29b-41d4-a716-446655440011";
54
+
55
+ describe("BaseAnalyticsAPI.updateItem payload validation", () => {
56
+ let service: AnalyticsDatabaseService<MonitorLog>;
57
+ let api: BaseAnalyticsAPI<MonitorLog, AnalyticsDatabaseService<MonitorLog>>;
58
+ let response: ExpressResponse;
59
+
60
+ type MakeRequestFunction = (body: unknown) => OneUptimeRequest;
61
+
62
+ const makeRequest: MakeRequestFunction = (
63
+ body: unknown,
64
+ ): OneUptimeRequest => {
65
+ return {
66
+ params: { id: TEST_ID },
67
+ body: body as JSONObject,
68
+ headers: {},
69
+ } as unknown as OneUptimeRequest;
70
+ };
71
+
72
+ beforeEach(() => {
73
+ jest.restoreAllMocks();
74
+ jest.clearAllMocks();
75
+
76
+ service = new AnalyticsDatabaseService<MonitorLog>({
77
+ modelType: MonitorLog,
78
+ });
79
+
80
+ api = new BaseAnalyticsAPI<
81
+ MonitorLog,
82
+ AnalyticsDatabaseService<MonitorLog>
83
+ >(MonitorLog, service);
84
+
85
+ /*
86
+ * Stubbed so a payload that WRONGLY gets past the guards still fails the
87
+ * assertion rather than trying to reach ClickHouse.
88
+ */
89
+ getJestSpyOn(service, "updateBy").mockResolvedValue(undefined);
90
+
91
+ response = {
92
+ status: jest.fn().mockReturnThis(),
93
+ json: jest.fn().mockReturnThis(),
94
+ send: jest.fn().mockReturnThis(),
95
+ } as unknown as ExpressResponse;
96
+ });
97
+
98
+ it("rejects a flat body that forgot to wrap its columns in data", async () => {
99
+ const request: OneUptimeRequest = makeRequest({
100
+ logBody: { message: "flat-payload" },
101
+ monitorId: TEST_ID,
102
+ });
103
+
104
+ await expect(api.updateItem(request, response)).rejects.toThrow(
105
+ BadDataException,
106
+ );
107
+
108
+ expect(service.updateBy).not.toHaveBeenCalled();
109
+ });
110
+
111
+ it("tells the caller the payload has to look like { data: { ... } }", async () => {
112
+ const request: OneUptimeRequest = makeRequest({
113
+ logBody: { message: "flat-payload" },
114
+ });
115
+
116
+ await expect(api.updateItem(request, response)).rejects.toThrow(/"data"/);
117
+ });
118
+
119
+ it("rejects a completely empty body", async () => {
120
+ const request: OneUptimeRequest = makeRequest({});
121
+
122
+ await expect(api.updateItem(request, response)).rejects.toThrow(
123
+ BadDataException,
124
+ );
125
+
126
+ expect(service.updateBy).not.toHaveBeenCalled();
127
+ });
128
+
129
+ it("rejects data: {} and says there are no fields to update", async () => {
130
+ const request: OneUptimeRequest = makeRequest({ data: {} });
131
+
132
+ await expect(api.updateItem(request, response)).rejects.toThrow(
133
+ BadDataException,
134
+ );
135
+ await expect(api.updateItem(request, response)).rejects.toThrow(
136
+ /no fields to update/i,
137
+ );
138
+
139
+ expect(service.updateBy).not.toHaveBeenCalled();
140
+ });
141
+
142
+ it("rejects data: null", async () => {
143
+ const request: OneUptimeRequest = makeRequest({ data: null });
144
+
145
+ await expect(api.updateItem(request, response)).rejects.toThrow(
146
+ BadDataException,
147
+ );
148
+
149
+ expect(service.updateBy).not.toHaveBeenCalled();
150
+ });
151
+
152
+ /*
153
+ * A string is iterable and `for (const key in "abc")` yields "0", "1", "2",
154
+ * so a lenient implementation could quietly treat it as an object with
155
+ * numeric keys. Assert both that it is refused AND that nothing about it
156
+ * looked like a field set worth forwarding.
157
+ */
158
+ it("rejects a string data payload instead of treating it as an object", async () => {
159
+ const request: OneUptimeRequest = makeRequest({ data: "some string" });
160
+
161
+ await expect(api.updateItem(request, response)).rejects.toThrow(
162
+ BadDataException,
163
+ );
164
+
165
+ expect(service.updateBy).not.toHaveBeenCalled();
166
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
167
+ });
168
+
169
+ it("rejects an empty array data payload", async () => {
170
+ const request: OneUptimeRequest = makeRequest({ data: [] });
171
+
172
+ await expect(api.updateItem(request, response)).rejects.toThrow(
173
+ BadDataException,
174
+ );
175
+
176
+ expect(service.updateBy).not.toHaveBeenCalled();
177
+ });
178
+
179
+ it("rejects an array of objects data payload", async () => {
180
+ const request: OneUptimeRequest = makeRequest({
181
+ data: [{ logBody: { message: "one" } }, { logBody: { message: "two" } }],
182
+ });
183
+
184
+ await expect(api.updateItem(request, response)).rejects.toThrow(
185
+ BadDataException,
186
+ );
187
+
188
+ expect(service.updateBy).not.toHaveBeenCalled();
189
+ });
190
+
191
+ it("rejects a numeric data payload", async () => {
192
+ const request: OneUptimeRequest = makeRequest({ data: 42 });
193
+
194
+ await expect(api.updateItem(request, response)).rejects.toThrow(
195
+ BadDataException,
196
+ );
197
+
198
+ expect(service.updateBy).not.toHaveBeenCalled();
199
+ });
200
+
201
+ it("rejects a boolean data payload", async () => {
202
+ const request: OneUptimeRequest = makeRequest({ data: true });
203
+
204
+ await expect(api.updateItem(request, response)).rejects.toThrow(
205
+ BadDataException,
206
+ );
207
+
208
+ expect(service.updateBy).not.toHaveBeenCalled();
209
+ });
210
+
211
+ /*
212
+ * _id / createdAt / updatedAt are stripped before the update is issued, so a
213
+ * payload made only of those would write nothing. This proves the emptiness
214
+ * check runs on the STRIPPED field set, not on the raw key count.
215
+ */
216
+ it("rejects a payload of only _id, createdAt and updatedAt because nothing updatable survives stripping", async () => {
217
+ const request: OneUptimeRequest = makeRequest({
218
+ data: {
219
+ _id: TEST_ID,
220
+ createdAt: "2026-07-29T00:00:00.000Z",
221
+ updatedAt: "2026-07-29T00:00:00.000Z",
222
+ },
223
+ });
224
+
225
+ await expect(api.updateItem(request, response)).rejects.toThrow(
226
+ BadDataException,
227
+ );
228
+ await expect(api.updateItem(request, response)).rejects.toThrow(
229
+ /no fields to update/i,
230
+ );
231
+
232
+ expect(service.updateBy).not.toHaveBeenCalled();
233
+ });
234
+
235
+ /*
236
+ * No-regression guard: the guards above must not have made the happy path
237
+ * stricter. A correctly wrapped payload with one real column of the model
238
+ * still reaches the service and still reports success.
239
+ */
240
+ it("still updates and reports success for a correctly wrapped payload", async () => {
241
+ const request: OneUptimeRequest = makeRequest({
242
+ data: { logBody: { message: "wrapped-payload" } },
243
+ });
244
+
245
+ await api.updateItem(request, response);
246
+
247
+ expect(service.updateBy).toHaveBeenCalledTimes(1);
248
+ expect(Response.sendEmptySuccessResponse).toHaveBeenCalledWith(
249
+ request,
250
+ response,
251
+ );
252
+ });
253
+
254
+ it("keeps the _id in the update query and out of the written columns", async () => {
255
+ const request: OneUptimeRequest = makeRequest({
256
+ data: { _id: TEST_ID, logBody: { message: "wrapped-payload" } },
257
+ });
258
+
259
+ await api.updateItem(request, response);
260
+
261
+ expect(service.updateBy).toHaveBeenCalledTimes(1);
262
+
263
+ const updateBy: { query: JSONObject; data: MonitorLog } = (
264
+ service.updateBy as unknown as jest.Mock
265
+ ).mock.calls[0]![0] as { query: JSONObject; data: MonitorLog };
266
+
267
+ expect(updateBy.query["_id"]).toBe(TEST_ID);
268
+ expect(updateBy.data.getColumnValue("logBody")).toEqual({
269
+ message: "wrapped-payload",
270
+ });
271
+ });
272
+
273
+ /*
274
+ * Named for the original symptom: the customer's Workflow PUT a flat body,
275
+ * got HTTP 200 with an empty object back, and nothing was written. The 200
276
+ * came from sendEmptySuccessResponse, so that is the exact call that must
277
+ * never happen for an unwrapped payload.
278
+ */
279
+ it("never reports an empty success for the flat body that silently wrote nothing", async () => {
280
+ const request: OneUptimeRequest = makeRequest({
281
+ content: "<token>",
282
+ name: "Airflow-Token",
283
+ description: "token used by the airflow workflow",
284
+ });
285
+
286
+ await expect(api.updateItem(request, response)).rejects.toThrow(
287
+ BadDataException,
288
+ );
289
+
290
+ expect(Response.sendEmptySuccessResponse).not.toHaveBeenCalled();
291
+ expect(service.updateBy).not.toHaveBeenCalled();
292
+ });
293
+ });