@oneuptime/common 11.7.3 → 11.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
  2. package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
  3. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  4. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  5. package/Models/DatabaseModels/Index.ts +5 -0
  6. package/Models/DatabaseModels/LogDropFilter.ts +89 -0
  7. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +13 -3
  8. package/Models/DatabaseModels/RecommendationDismissal.ts +463 -0
  9. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  10. package/Models/DatabaseModels/StatusPageGroup.ts +95 -0
  11. package/Models/DatabaseModels/TraceDropFilter.ts +89 -0
  12. package/Models/DatabaseModels/WorkflowVariable.ts +5 -1
  13. package/Server/API/AlertAPI.ts +32 -17
  14. package/Server/API/BaseAPI.ts +31 -2
  15. package/Server/API/BaseAnalyticsAPI.ts +36 -2
  16. package/Server/API/CommonAPI.ts +57 -0
  17. package/Server/API/IncidentAPI.ts +60 -33
  18. package/Server/API/IncidentEpisodeAPI.ts +31 -16
  19. package/Server/API/MonitorAPI.ts +30 -0
  20. package/Server/API/MonitorTemplateAPI.ts +8 -0
  21. package/Server/API/ProbeAPI.ts +196 -15
  22. package/Server/API/ProjectAPI.ts +79 -1
  23. package/Server/API/ScheduledMaintenanceAPI.ts +33 -18
  24. package/Server/API/StatusPageAPI.ts +74 -37
  25. package/Server/API/WorkspaceNotificationRuleAPI.ts +34 -4
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.ts +527 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.ts +31 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.ts +55 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.ts +53 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  31. package/Server/Services/AlertEpisodeService.ts +47 -0
  32. package/Server/Services/AlertService.ts +125 -0
  33. package/Server/Services/BillingService.ts +98 -0
  34. package/Server/Services/DatabaseService.ts +184 -20
  35. package/Server/Services/IncidentEpisodeService.ts +47 -0
  36. package/Server/Services/IncidentService.ts +123 -0
  37. package/Server/Services/IncidentTemplateService.ts +123 -1
  38. package/Server/Services/Index.ts +2 -0
  39. package/Server/Services/LogDropFilterService.ts +133 -0
  40. package/Server/Services/MonitorProbeService.ts +94 -4
  41. package/Server/Services/MonitorService.ts +48 -43
  42. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +70 -0
  43. package/Server/Services/ProbeService.ts +66 -0
  44. package/Server/Services/ProjectService.ts +131 -7
  45. package/Server/Services/RecommendationDismissalService.ts +52 -0
  46. package/Server/Services/RunbookAgentJobService.ts +68 -7
  47. package/Server/Services/ScheduledMaintenanceService.ts +103 -0
  48. package/Server/Services/ScheduledMaintenanceTemplateService.ts +90 -0
  49. package/Server/Services/StatusPageGroupService.ts +186 -0
  50. package/Server/Services/TraceDropFilterService.ts +133 -0
  51. package/Server/Types/Workflow/Components/API/Delete.ts +12 -0
  52. package/Server/Types/Workflow/Components/API/Get.ts +12 -0
  53. package/Server/Types/Workflow/Components/API/Patch.ts +12 -0
  54. package/Server/Types/Workflow/Components/API/Post.ts +12 -0
  55. package/Server/Types/Workflow/Components/API/Put.ts +13 -0
  56. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +212 -0
  57. package/Server/Utils/DropFilterValidation.ts +127 -0
  58. package/Server/Utils/Errors.ts +5 -0
  59. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  60. package/Server/Utils/Monitor/MonitorIncident.ts +31 -2
  61. package/Server/Utils/Telemetry/AppMetrics.ts +31 -0
  62. package/Tests/Models/DatabaseModels/MonitorProbeColumnAccessControl.test.ts +196 -0
  63. package/Tests/Models/RecommendationDismissalModel.test.ts +269 -0
  64. package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +320 -0
  65. package/Tests/Server/API/BaseAnalyticsAPIUpdatePayloadValidation.test.ts +293 -0
  66. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +164 -0
  67. package/Tests/Server/API/ProbeAPI.test.ts +422 -53
  68. package/Tests/Server/API/ProjectAPI.test.ts +180 -0
  69. package/Tests/Server/API/ProjectAPIExtendTrialBillingDisabled.test.ts +138 -0
  70. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +458 -0
  71. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +359 -0
  72. package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +427 -0
  73. package/Tests/Server/Services/BillingServiceExtendTrial.test.ts +439 -0
  74. package/Tests/Server/Services/CrossProjectReferenceWriteGuard.test.ts +810 -0
  75. package/Tests/Server/Services/DatabaseServiceAtomicAddToColumns.test.ts +269 -0
  76. package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +277 -0
  77. package/Tests/Server/Services/DatabaseServiceSortColumnSelect.test.ts +201 -0
  78. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +81 -23
  79. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +398 -0
  80. package/Tests/Server/Services/MonitorProbeDeleteHooks.test.ts +280 -0
  81. package/Tests/Server/Services/MonitorProbeServiceProbeTenancy.test.ts +304 -0
  82. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +262 -0
  83. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +614 -0
  84. package/Tests/Server/Services/RecommendationDismissalService.test.ts +306 -0
  85. package/Tests/Server/Services/RepairCrossProjectIncidentReferencesMigration.test.ts +528 -0
  86. package/Tests/Server/Services/StatusPageGroupNesting.test.ts +395 -0
  87. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +26 -21
  88. package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +227 -0
  89. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +285 -0
  90. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +463 -0
  91. package/Tests/Server/Utils/DropFilterValidation.test.ts +269 -0
  92. package/Tests/Server/Utils/Telemetry/SpanUtil.test.ts +94 -0
  93. package/Tests/Types/DateUserTimezone.test.ts +52 -0
  94. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +177 -9
  95. package/Tests/Types/Monitor/MonitorStepDnsMonitor.test.ts +105 -0
  96. package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +131 -0
  97. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +60 -0
  98. package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +82 -0
  99. package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +268 -0
  100. package/Tests/Types/Monitor/MonitorStepMetricMonitor.test.ts +386 -0
  101. package/Tests/Types/Monitor/MonitorStepProfileMonitor.test.ts +185 -0
  102. package/Tests/Types/Monitor/MonitorStepSnmpMonitor.test.ts +156 -0
  103. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +371 -0
  104. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +901 -0
  105. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +607 -0
  106. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +754 -0
  107. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +960 -0
  108. package/Tests/Types/RollingTime/RollingTimeUtil.test.ts +186 -0
  109. package/Tests/Types/Runbook/RunbookStepTimeout.test.ts +410 -0
  110. package/Tests/Types/Telemetry/DropFilterSampling.test.ts +241 -0
  111. package/Tests/Types/Workspace/NotificationRuleConditionUtil.test.ts +353 -0
  112. package/Tests/Types/Workspace/NotificationRuleUtil.test.ts +566 -0
  113. package/Tests/UI/Components/CodeEditor.test.tsx +174 -0
  114. package/Tests/UI/Components/ErrorBoundary.test.tsx +555 -24
  115. package/Tests/UI/Components/InfoCard.test.tsx +358 -0
  116. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +138 -0
  117. package/Tests/UI/Components/Modal.test.tsx +43 -5
  118. package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +397 -0
  119. package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +427 -0
  120. package/Tests/UI/Components/Runbook/StepTimeoutInput.test.tsx +386 -0
  121. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +671 -0
  122. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +404 -0
  123. package/Tests/UI/Utils/DropdownAlignment.test.ts +306 -0
  124. package/Tests/UI/Utils/TableFilterUrlState.test.ts +292 -0
  125. package/Tests/Utils/Monitor/MonitorSummaryProbeUtil.test.ts +263 -0
  126. package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +583 -0
  127. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +1072 -0
  128. package/Tests/Utils/StatusPage/GroupTree.test.ts +583 -0
  129. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +411 -0
  130. package/Types/Date.ts +15 -6
  131. package/Types/Monitor/KubernetesAlertTemplates.ts +205 -0
  132. package/Types/Monitor/MonitorStep.ts +10 -1
  133. package/Types/Monitor/MonitorStepMetricMonitor.ts +46 -1
  134. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +413 -0
  135. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +168 -0
  136. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +175 -0
  137. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +743 -0
  138. package/Types/Permission.ts +45 -0
  139. package/Types/Recommendation/RecommendationType.ts +20 -0
  140. package/Types/Runbook/RunbookExecutionDeadline.ts +129 -0
  141. package/Types/Runbook/RunbookStepTimeout.ts +202 -0
  142. package/Types/Telemetry/DropFilterSampling.ts +92 -0
  143. package/UI/Components/AI/GenerateFromAIModal.tsx +0 -2
  144. package/UI/Components/Accordion/Accordion.tsx +10 -2
  145. package/UI/Components/BulkUpdate/BulkLabelActions.tsx +0 -1
  146. package/UI/Components/CodeEditor/CodeEditor.tsx +34 -20
  147. package/UI/Components/Date/RangeStartAndEndDateView.tsx +0 -1
  148. package/UI/Components/EditionLabel/EditionLabel.tsx +1 -27
  149. package/UI/Components/Error/PageError.tsx +0 -4
  150. package/UI/Components/ErrorBoundary.tsx +252 -15
  151. package/UI/Components/InfoCard/InfoCard.tsx +58 -1
  152. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +34 -1
  153. package/UI/Components/Modal/Modal.tsx +16 -60
  154. package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
  155. package/UI/Components/ModelTable/BaseModelTable.tsx +116 -67
  156. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +150 -47
  157. package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
  158. package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -0
  159. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +34 -1
  160. package/UI/Utils/DropdownAlignment.ts +162 -0
  161. package/UI/Utils/TableFilterUrlState.ts +59 -20
  162. package/Utils/API.ts +106 -2
  163. package/Utils/Monitor/MonitorSummaryProbeUtil.ts +130 -0
  164. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +434 -0
  165. package/Utils/StatusPage/GroupNestingLayout.ts +343 -0
  166. package/Utils/StatusPage/GroupTree.ts +307 -0
  167. package/Utils/StatusPage/ResourceUptime.ts +90 -17
  168. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
  169. package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
  170. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
  171. package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
  172. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  173. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  174. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  175. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  176. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  177. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  178. package/build/dist/Models/DatabaseModels/LogDropFilter.js +91 -0
  179. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  180. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +14 -4
  181. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  182. package/build/dist/Models/DatabaseModels/RecommendationDismissal.js +484 -0
  183. package/build/dist/Models/DatabaseModels/RecommendationDismissal.js.map +1 -0
  184. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  185. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/StatusPageGroup.js +94 -0
  187. package/build/dist/Models/DatabaseModels/StatusPageGroup.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +91 -0
  189. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +5 -1
  191. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  192. package/build/dist/Server/API/AlertAPI.js +22 -13
  193. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  194. package/build/dist/Server/API/BaseAPI.js +22 -1
  195. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  196. package/build/dist/Server/API/BaseAnalyticsAPI.js +24 -1
  197. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  198. package/build/dist/Server/API/CommonAPI.js +42 -0
  199. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  200. package/build/dist/Server/API/IncidentAPI.js +43 -26
  201. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  202. package/build/dist/Server/API/IncidentEpisodeAPI.js +21 -12
  203. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  204. package/build/dist/Server/API/MonitorAPI.js +23 -0
  205. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  206. package/build/dist/Server/API/MonitorTemplateAPI.js +4 -0
  207. package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -1
  208. package/build/dist/Server/API/ProbeAPI.js +131 -15
  209. package/build/dist/Server/API/ProbeAPI.js.map +1 -1
  210. package/build/dist/Server/API/ProjectAPI.js +62 -3
  211. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  212. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +23 -14
  213. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  214. package/build/dist/Server/API/StatusPageAPI.js +61 -33
  215. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  216. package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js +26 -2
  217. package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js.map +1 -1
  218. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js +397 -0
  219. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js.map +1 -0
  220. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js +16 -0
  221. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js.map +1 -0
  222. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js +30 -0
  223. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js.map +1 -0
  224. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js +34 -0
  225. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js.map +1 -0
  226. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  227. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  228. package/build/dist/Server/Services/AlertEpisodeService.js +39 -0
  229. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  230. package/build/dist/Server/Services/AlertService.js +95 -0
  231. package/build/dist/Server/Services/AlertService.js.map +1 -1
  232. package/build/dist/Server/Services/BillingService.js +71 -0
  233. package/build/dist/Server/Services/BillingService.js.map +1 -1
  234. package/build/dist/Server/Services/DatabaseService.js +140 -17
  235. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  236. package/build/dist/Server/Services/IncidentEpisodeService.js +39 -0
  237. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  238. package/build/dist/Server/Services/IncidentService.js +100 -0
  239. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  240. package/build/dist/Server/Services/IncidentTemplateService.js +97 -0
  241. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  242. package/build/dist/Server/Services/Index.js +2 -0
  243. package/build/dist/Server/Services/Index.js.map +1 -1
  244. package/build/dist/Server/Services/LogDropFilterService.js +93 -0
  245. package/build/dist/Server/Services/LogDropFilterService.js.map +1 -1
  246. package/build/dist/Server/Services/MonitorProbeService.js +73 -5
  247. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  248. package/build/dist/Server/Services/MonitorService.js +34 -39
  249. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  250. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +71 -0
  251. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  252. package/build/dist/Server/Services/ProbeService.js +64 -0
  253. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  254. package/build/dist/Server/Services/ProjectService.js +103 -7
  255. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  256. package/build/dist/Server/Services/RecommendationDismissalService.js +50 -0
  257. package/build/dist/Server/Services/RecommendationDismissalService.js.map +1 -0
  258. package/build/dist/Server/Services/RunbookAgentJobService.js +63 -5
  259. package/build/dist/Server/Services/RunbookAgentJobService.js.map +1 -1
  260. package/build/dist/Server/Services/ScheduledMaintenanceService.js +83 -0
  261. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  262. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +65 -0
  263. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  264. package/build/dist/Server/Services/StatusPageGroupService.js +136 -0
  265. package/build/dist/Server/Services/StatusPageGroupService.js.map +1 -1
  266. package/build/dist/Server/Services/TraceDropFilterService.js +93 -0
  267. package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -1
  268. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +11 -0
  269. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  270. package/build/dist/Server/Types/Workflow/Components/API/Get.js +11 -0
  271. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  272. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +11 -0
  273. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  274. package/build/dist/Server/Types/Workflow/Components/API/Post.js +11 -0
  275. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  276. package/build/dist/Server/Types/Workflow/Components/API/Put.js +12 -0
  277. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  278. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +132 -0
  279. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -0
  280. package/build/dist/Server/Utils/DropFilterValidation.js +80 -0
  281. package/build/dist/Server/Utils/DropFilterValidation.js.map +1 -0
  282. package/build/dist/Server/Utils/Errors.js +3 -0
  283. package/build/dist/Server/Utils/Errors.js.map +1 -1
  284. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +30 -12
  285. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  286. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +38 -18
  287. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  288. package/build/dist/Server/Utils/Telemetry/AppMetrics.js +26 -0
  289. package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
  290. package/build/dist/Types/Date.js +14 -4
  291. package/build/dist/Types/Date.js.map +1 -1
  292. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +192 -0
  293. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  294. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  295. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  296. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +32 -1
  297. package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
  298. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +294 -0
  299. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -0
  300. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +110 -0
  301. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -0
  302. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +58 -0
  303. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -0
  304. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +451 -0
  305. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -0
  306. package/build/dist/Types/Permission.js +40 -0
  307. package/build/dist/Types/Permission.js.map +1 -1
  308. package/build/dist/Types/Recommendation/RecommendationType.js +21 -0
  309. package/build/dist/Types/Recommendation/RecommendationType.js.map +1 -0
  310. package/build/dist/Types/Runbook/RunbookExecutionDeadline.js +93 -0
  311. package/build/dist/Types/Runbook/RunbookExecutionDeadline.js.map +1 -0
  312. package/build/dist/Types/Runbook/RunbookStepTimeout.js +142 -0
  313. package/build/dist/Types/Runbook/RunbookStepTimeout.js.map +1 -0
  314. package/build/dist/Types/Telemetry/DropFilterSampling.js +75 -0
  315. package/build/dist/Types/Telemetry/DropFilterSampling.js.map +1 -0
  316. package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -2
  317. package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
  318. package/build/dist/UI/Components/Accordion/Accordion.js +9 -2
  319. package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
  320. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +1 -1
  321. package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
  322. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +27 -14
  323. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  324. package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js +1 -1
  325. package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js.map +1 -1
  326. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +2 -20
  327. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  328. package/build/dist/UI/Components/Error/PageError.js +1 -3
  329. package/build/dist/UI/Components/Error/PageError.js.map +1 -1
  330. package/build/dist/UI/Components/ErrorBoundary.js +145 -11
  331. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  332. package/build/dist/UI/Components/InfoCard/InfoCard.js +28 -1
  333. package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
  334. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +13 -2
  335. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  336. package/build/dist/UI/Components/Modal/Modal.js +4 -32
  337. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  338. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  339. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +69 -33
  340. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  341. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +73 -32
  342. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
  343. package/build/dist/UI/Components/Runbook/StepTimeoutInput.js +101 -0
  344. package/build/dist/UI/Components/Runbook/StepTimeoutInput.js.map +1 -0
  345. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +73 -0
  346. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
  347. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +13 -2
  348. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  349. package/build/dist/UI/Utils/DropdownAlignment.js +98 -0
  350. package/build/dist/UI/Utils/DropdownAlignment.js.map +1 -0
  351. package/build/dist/UI/Utils/TableFilterUrlState.js +48 -15
  352. package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
  353. package/build/dist/Utils/API.js +39 -2
  354. package/build/dist/Utils/API.js.map +1 -1
  355. package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js +97 -0
  356. package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js.map +1 -0
  357. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +330 -0
  358. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -0
  359. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +280 -0
  360. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -0
  361. package/build/dist/Utils/StatusPage/GroupTree.js +214 -0
  362. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -0
  363. package/build/dist/Utils/StatusPage/ResourceUptime.js +60 -16
  364. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  365. package/package.json +1 -1
@@ -0,0 +1,269 @@
1
+ import DatabaseService from "../../../Server/Services/DatabaseService";
2
+ import LogDropFilter from "../../../Models/DatabaseModels/LogDropFilter";
3
+ import BadDataException from "../../../Types/Exception/BadDataException";
4
+ import ObjectID from "../../../Types/ObjectID";
5
+ import { getJestSpyOn } from "../../Spy";
6
+ import { beforeEach, describe, expect, it, jest } from "@jest/globals";
7
+
8
+ /*
9
+ * Contract under test — `atomicAddToColumnsByIdWithoutHooks`, the primitive
10
+ * the drop-filter counter flush is built on.
11
+ *
12
+ * Neither existing helper could express this write:
13
+ *
14
+ * - `updateColumnsByIdWithoutHooks` sets literals, so a counter bump would
15
+ * have to be a read-modify-write, which loses concurrent writers'
16
+ * increments (several ingest replicas flush the same filter row).
17
+ * - `getRepository().increment()` goes through TypeORM's
18
+ * UpdateQueryBuilder, which always appends `version = version + 1`. An
19
+ * ingest-driven bump would then fight the optimistic lock on a row a
20
+ * human may be editing in the dashboard — and it cannot set a second
21
+ * column (`lastDroppedAt`) in the same statement anyway.
22
+ *
23
+ * So this issues one raw parameterized UPDATE. The tests below pin the three
24
+ * properties that make it safe to run from the ingest path: it adds rather
25
+ * than assigns, it does not touch `version`, and identifiers come from entity
26
+ * metadata rather than from the caller.
27
+ */
28
+
29
+ const FILTER_ID: ObjectID = new ObjectID(
30
+ "11111111-1111-4111-8111-111111111111",
31
+ );
32
+
33
+ interface CapturedQuery {
34
+ sql: string;
35
+ params: Array<unknown>;
36
+ }
37
+
38
+ describe("DatabaseService.atomicAddToColumnsByIdWithoutHooks", () => {
39
+ let service: DatabaseService<LogDropFilter>;
40
+ let captured: Array<CapturedQuery>;
41
+
42
+ beforeEach(() => {
43
+ jest.restoreAllMocks();
44
+ captured = [];
45
+
46
+ service = new DatabaseService<LogDropFilter>(LogDropFilter);
47
+
48
+ /*
49
+ * `getRepository()` needs a live Postgres connection, so the whole
50
+ * repository is stood in for — the same approach the other
51
+ * DatabaseService unit tests take.
52
+ *
53
+ * The stand-in models the two things this method actually reads from
54
+ * TypeORM: metadata (to resolve property names to real column names, and
55
+ * to find the primary key and updatedAt columns) and the driver's
56
+ * persist-value hook. Column names are deliberately spelled out here
57
+ * rather than derived, so if a model rename ever silently changed the
58
+ * generated SQL, these assertions would still be checking the names the
59
+ * migration actually created.
60
+ */
61
+ const columns: Record<string, string> = {
62
+ droppedCount: "droppedCount",
63
+ lastDroppedAt: "lastDroppedAt",
64
+ sortOrder: "sortOrder",
65
+ _id: "_id",
66
+ updatedAt: "updatedAt",
67
+ };
68
+
69
+ getJestSpyOn(service, "getRepository").mockReturnValue({
70
+ metadata: {
71
+ tableName: "LogDropFilter",
72
+ findColumnWithPropertyName: (
73
+ propertyName: string,
74
+ ): { databaseName: string } | undefined => {
75
+ return columns[propertyName]
76
+ ? { databaseName: columns[propertyName]! }
77
+ : undefined;
78
+ },
79
+ updateDateColumn: { databaseName: "updatedAt" },
80
+ primaryColumns: [{ databaseName: "_id" }],
81
+ },
82
+ manager: {
83
+ connection: {
84
+ driver: {
85
+ // Identity persist: these tests are about the SQL, not coercion.
86
+ preparePersistentValue: (value: unknown): unknown => {
87
+ return value;
88
+ },
89
+ },
90
+ },
91
+ query: async (
92
+ sql: string,
93
+ params: Array<unknown>,
94
+ ): Promise<Array<unknown>> => {
95
+ captured.push({ sql, params });
96
+ return [];
97
+ },
98
+ },
99
+ } as any);
100
+ });
101
+
102
+ it("adds to the column instead of assigning it", async () => {
103
+ await service.atomicAddToColumnsByIdWithoutHooks({
104
+ id: FILTER_ID,
105
+ add: { droppedCount: 42 },
106
+ });
107
+
108
+ expect(captured).toHaveLength(1);
109
+ expect(captured[0]!.sql).toContain(
110
+ `"droppedCount" = COALESCE("droppedCount", 0) +`,
111
+ );
112
+ });
113
+
114
+ /*
115
+ * COALESCE matters for rows that predate the column, or any future nullable
116
+ * counter: `NULL + 1` is NULL, so without it a counter could stay null
117
+ * forever no matter how many drops happened.
118
+ */
119
+ it("treats a NULL counter as zero rather than propagating NULL", async () => {
120
+ await service.atomicAddToColumnsByIdWithoutHooks({
121
+ id: FILTER_ID,
122
+ add: { droppedCount: 1 },
123
+ });
124
+
125
+ expect(captured[0]!.sql).toContain("COALESCE");
126
+ });
127
+
128
+ it("binds the delta as a parameter, not as inline SQL", async () => {
129
+ await service.atomicAddToColumnsByIdWithoutHooks({
130
+ id: FILTER_ID,
131
+ add: { droppedCount: 7 },
132
+ });
133
+
134
+ expect(captured[0]!.params).toContain(7);
135
+ expect(captured[0]!.sql).not.toContain("7");
136
+ });
137
+
138
+ it("sets literal columns in the same statement as the add", async () => {
139
+ const when: Date = new Date("2026-07-29T05:14:03.000Z");
140
+
141
+ await service.atomicAddToColumnsByIdWithoutHooks({
142
+ id: FILTER_ID,
143
+ add: { droppedCount: 3 },
144
+ set: { lastDroppedAt: when } as any,
145
+ });
146
+
147
+ expect(captured).toHaveLength(1);
148
+ expect(captured[0]!.sql).toContain(`"droppedCount" = COALESCE(`);
149
+ expect(captured[0]!.sql).toContain(`"lastDroppedAt" =`);
150
+ expect(captured[0]!.params).toContain(when);
151
+ });
152
+
153
+ /*
154
+ * The reason this helper exists rather than using increment(). A bumped
155
+ * `version` on an ingest write would make the next dashboard save fail its
156
+ * optimistic-lock check for no reason the user could understand.
157
+ */
158
+ it("never bumps the optimistic-lock version column", async () => {
159
+ await service.atomicAddToColumnsByIdWithoutHooks({
160
+ id: FILTER_ID,
161
+ add: { droppedCount: 1 },
162
+ set: { lastDroppedAt: new Date() } as any,
163
+ });
164
+
165
+ expect(captured[0]!.sql).not.toContain("version");
166
+ });
167
+
168
+ it("refreshes updatedAt so the row does not look stale", async () => {
169
+ await service.atomicAddToColumnsByIdWithoutHooks({
170
+ id: FILTER_ID,
171
+ add: { droppedCount: 1 },
172
+ });
173
+
174
+ expect(captured[0]!.sql).toContain(`"updatedAt" = CURRENT_TIMESTAMP`);
175
+ });
176
+
177
+ it("scopes the update to the one row by primary key", async () => {
178
+ await service.atomicAddToColumnsByIdWithoutHooks({
179
+ id: FILTER_ID,
180
+ add: { droppedCount: 1 },
181
+ });
182
+
183
+ expect(captured[0]!.sql).toMatch(/WHERE "_id" = \$\d+$/);
184
+ expect(captured[0]!.params[captured[0]!.params.length - 1]).toBe(
185
+ FILTER_ID.toString(),
186
+ );
187
+ });
188
+
189
+ it("targets the model's real table", async () => {
190
+ await service.atomicAddToColumnsByIdWithoutHooks({
191
+ id: FILTER_ID,
192
+ add: { droppedCount: 1 },
193
+ });
194
+
195
+ expect(captured[0]!.sql).toContain(`UPDATE "LogDropFilter"`);
196
+ });
197
+
198
+ it("adds to several columns in one statement", async () => {
199
+ await service.atomicAddToColumnsByIdWithoutHooks({
200
+ id: FILTER_ID,
201
+ add: { droppedCount: 2, sortOrder: 1 } as any,
202
+ });
203
+
204
+ expect(captured).toHaveLength(1);
205
+ expect(captured[0]!.sql).toContain(`"droppedCount" = COALESCE(`);
206
+ expect(captured[0]!.sql).toContain(`"sortOrder" = COALESCE(`);
207
+ });
208
+
209
+ describe("rejects inputs it cannot execute safely", () => {
210
+ it("requires an id", async () => {
211
+ await expect(
212
+ service.atomicAddToColumnsByIdWithoutHooks({
213
+ id: undefined as unknown as ObjectID,
214
+ add: { droppedCount: 1 },
215
+ }),
216
+ ).rejects.toThrow(BadDataException);
217
+ });
218
+
219
+ /*
220
+ * Identifiers come from entity metadata, never from the caller, so an
221
+ * unknown property is a programming error rather than an injection
222
+ * vector — but it must fail loudly instead of silently writing nothing.
223
+ */
224
+ it("rejects a column that does not exist on the model", async () => {
225
+ await expect(
226
+ service.atomicAddToColumnsByIdWithoutHooks({
227
+ id: FILTER_ID,
228
+ add: { notAColumn: 1 } as any,
229
+ }),
230
+ ).rejects.toThrow(/unknown column/);
231
+ expect(captured).toHaveLength(0);
232
+ });
233
+
234
+ it("rejects a non-numeric delta", async () => {
235
+ for (const bad of ["5", null, undefined, {}, NaN, Infinity]) {
236
+ await expect(
237
+ service.atomicAddToColumnsByIdWithoutHooks({
238
+ id: FILTER_ID,
239
+ add: { droppedCount: bad } as any,
240
+ }),
241
+ ).rejects.toThrow(/finite number/);
242
+ }
243
+ expect(captured).toHaveLength(0);
244
+ });
245
+
246
+ it("rejects a SQL-expression value in the literal set", async () => {
247
+ await expect(
248
+ service.atomicAddToColumnsByIdWithoutHooks({
249
+ id: FILTER_ID,
250
+ add: { droppedCount: 1 },
251
+ set: {
252
+ lastDroppedAt: () => {
253
+ return "NOW()";
254
+ },
255
+ } as any,
256
+ }),
257
+ ).rejects.toThrow(/SQL-expression values are not supported/);
258
+ });
259
+
260
+ it("issues no statement at all when there is nothing to write", async () => {
261
+ await service.atomicAddToColumnsByIdWithoutHooks({
262
+ id: FILTER_ID,
263
+ add: {},
264
+ });
265
+
266
+ expect(captured).toHaveLength(0);
267
+ });
268
+ });
269
+ });
@@ -0,0 +1,277 @@
1
+ import DatabaseService from "../../../Server/Services/DatabaseService";
2
+ import ModelPermission from "../../../Server/Types/Database/Permissions/Index";
3
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
4
+ import Probe from "../../../Models/DatabaseModels/Probe";
5
+ import ObjectID from "../../../Types/ObjectID";
6
+ import getJestMockFunction, { MockFunction } from "../../MockType";
7
+ import { getJestSpyOn } from "../../Spy";
8
+ import {
9
+ afterEach,
10
+ beforeEach,
11
+ describe,
12
+ expect,
13
+ it,
14
+ jest,
15
+ } from "@jest/globals";
16
+
17
+ /*
18
+ * A customer's workflow PUT a flat body to /api/workflow-variable/<id>, so
19
+ * BaseAPI read `body["data"]` as undefined and handed DatabaseService an empty
20
+ * update payload. _updateBy still ran its internal find, matched the row, wrote
21
+ * no columns at all, and returned the MATCHED count (1). Every caller - the API
22
+ * included - reads that number as "rows written", so the request answered 200
23
+ * while the database was untouched, and the guard that exists precisely for
24
+ * this ("numberOfDocsAffected === 0") could never fire.
25
+ *
26
+ * The fix: when the sanitized update data carries no columns, _updateBy skips
27
+ * the find entirely and reports 0. These tests pin that down, and pin down that
28
+ * it did not change anything about updates that DO carry columns.
29
+ */
30
+
31
+ type ProbeUpdateData = UpdateBy<Probe>["data"];
32
+
33
+ const EMPTY_DATA: ProbeUpdateData = {} as ProbeUpdateData;
34
+
35
+ const TEST_ID: string = "550e8400-e29b-41d4-a716-446655440021";
36
+ const OTHER_ID: string = "550e8400-e29b-41d4-a716-446655440022";
37
+
38
+ const matchedRow: (id: string) => Probe = (id: string): Probe => {
39
+ const probe: Probe = new Probe();
40
+ probe._id = id;
41
+ return probe;
42
+ };
43
+
44
+ describe("DatabaseService._updateBy when the update carries no columns", () => {
45
+ let service: DatabaseService<Probe>;
46
+ let findBySpy: jest.SpyInstance;
47
+ let saveMock: MockFunction;
48
+
49
+ beforeEach(() => {
50
+ jest.restoreAllMocks();
51
+ jest.clearAllMocks();
52
+
53
+ service = new DatabaseService<Probe>(Probe);
54
+
55
+ /*
56
+ * Only the two surfaces that need a live Postgres are stubbed - the
57
+ * internal find (`_findBy`) and the repository behind `save()`. Mocking
58
+ * the TypeORM repository itself is impractical here because `_findBy`
59
+ * builds real query metadata against a DataSource that does not exist in
60
+ * a unit test; stubbing `_findBy` keeps the mock at the exact boundary the
61
+ * fix is about (was the find run at all?) while leaving every line of
62
+ * _updateBy under test - sanitizing, the hooks and the return arithmetic.
63
+ *
64
+ * The stub deliberately returns a MATCHING row, because that is what a
65
+ * real database does here: the row exists and the query finds it, there is
66
+ * simply nothing to write to it. That is the whole trap - so if _updateBy
67
+ * ever runs this find again for an empty payload, every expectation below
68
+ * reports the old, wrong answer (1 row "changed") instead of 0.
69
+ */
70
+ findBySpy = getJestSpyOn(service as any, "_findBy").mockResolvedValue([
71
+ matchedRow(TEST_ID),
72
+ ]);
73
+
74
+ saveMock = getJestMockFunction();
75
+ saveMock.mockImplementation((item: unknown) => {
76
+ return Promise.resolve(item);
77
+ });
78
+ getJestSpyOn(service, "getRepository").mockReturnValue({
79
+ save: saveMock,
80
+ });
81
+
82
+ // The permission layer is not what these tests are about, and needs a DB.
83
+ getJestSpyOn(
84
+ ModelPermission,
85
+ "checkUpdateQueryPermissions",
86
+ ).mockImplementation((_modelType: unknown, query: unknown) => {
87
+ return Promise.resolve(query);
88
+ });
89
+ getJestSpyOn(
90
+ ModelPermission,
91
+ "checkUpdatePermissionByModel",
92
+ ).mockResolvedValue(undefined);
93
+ });
94
+
95
+ afterEach(() => {
96
+ jest.restoreAllMocks();
97
+ });
98
+
99
+ it("reports zero rows changed when updateOneBy is handed an empty payload", async () => {
100
+ const numberOfDocsAffected: number = await service.updateOneBy({
101
+ query: { _id: TEST_ID } as any,
102
+ data: EMPTY_DATA,
103
+ props: { isRoot: true },
104
+ });
105
+
106
+ expect(numberOfDocsAffected).toBe(0);
107
+ });
108
+
109
+ it("reports zero rows changed when updateBy is handed an empty payload", async () => {
110
+ const numberOfDocsAffected: number = await service.updateBy({
111
+ query: {} as any,
112
+ data: EMPTY_DATA,
113
+ props: { isRoot: true },
114
+ limit: 10,
115
+ skip: 0,
116
+ });
117
+
118
+ expect(numberOfDocsAffected).toBe(0);
119
+ });
120
+
121
+ it("reports zero rows changed when the empty payload arrives through updateOneById, which is the path the API takes", async () => {
122
+ const numberOfDocsAffected: number = await service.updateOneById({
123
+ id: new ObjectID(TEST_ID),
124
+ data: EMPTY_DATA,
125
+ props: { isRoot: true },
126
+ });
127
+
128
+ /*
129
+ * This is the exact number BaseAPI.updateItem checks before answering
130
+ * 200, so a no-column update can no longer be reported as a save.
131
+ */
132
+ expect(numberOfDocsAffected).toBe(0);
133
+ });
134
+
135
+ it("never runs the find, because a payload with no columns cannot change any row", async () => {
136
+ await service.updateOneBy({
137
+ query: { _id: TEST_ID } as any,
138
+ data: EMPTY_DATA,
139
+ props: { isRoot: true },
140
+ });
141
+
142
+ expect(findBySpy).not.toHaveBeenCalled();
143
+ });
144
+
145
+ it("never writes anything, so the count it reports and the rows it touched agree", async () => {
146
+ /*
147
+ * The semantic in prose: an update that writes no columns must not be
148
+ * reported as a successful write. Nothing is saved AND the caller is told
149
+ * zero - previously the second half of that sentence was a lie, because
150
+ * `items.length` counted rows the query MATCHED, not rows it changed, and
151
+ * the row matched here (see the `_findBy` stub above).
152
+ */
153
+ const numberOfDocsAffected: number = await service.updateOneBy({
154
+ query: { _id: TEST_ID } as any,
155
+ data: EMPTY_DATA,
156
+ props: { isRoot: true },
157
+ });
158
+
159
+ expect(saveMock).not.toHaveBeenCalled();
160
+ expect(numberOfDocsAffected).toBe(0);
161
+ });
162
+
163
+ it("still calls onUpdateSuccess, with an empty list of ids, so hooks behave exactly as they do when the query matches nothing", async () => {
164
+ const onUpdateSuccessSpy: jest.SpyInstance = getJestSpyOn(
165
+ service as any,
166
+ "onUpdateSuccess",
167
+ );
168
+
169
+ await service.updateOneBy({
170
+ query: { _id: TEST_ID } as any,
171
+ data: EMPTY_DATA,
172
+ props: { isRoot: true, ignoreHooks: false },
173
+ });
174
+
175
+ /*
176
+ * Skipping the find must not quietly skip the hooks too: subclasses
177
+ * override onUpdateSuccess and some of them rely on being told about
178
+ * every update attempt. The id list is empty because nothing was written -
179
+ * before the fix this hook was handed the id of the row that merely
180
+ * matched, telling every subclass a write had happened.
181
+ */
182
+ expect(onUpdateSuccessSpy).toHaveBeenCalledTimes(1);
183
+ expect(onUpdateSuccessSpy.mock.calls[0]![1]).toEqual([]);
184
+ });
185
+ });
186
+
187
+ describe("DatabaseService._updateBy when the update does carry columns", () => {
188
+ let service: DatabaseService<Probe>;
189
+ let findBySpy: jest.SpyInstance;
190
+ let saveMock: MockFunction;
191
+
192
+ beforeEach(() => {
193
+ jest.restoreAllMocks();
194
+ jest.clearAllMocks();
195
+
196
+ service = new DatabaseService<Probe>(Probe);
197
+
198
+ findBySpy = getJestSpyOn(service as any, "_findBy").mockResolvedValue([
199
+ matchedRow(TEST_ID),
200
+ matchedRow(OTHER_ID),
201
+ ]);
202
+
203
+ saveMock = getJestMockFunction();
204
+ saveMock.mockImplementation((item: unknown) => {
205
+ return Promise.resolve(item);
206
+ });
207
+ getJestSpyOn(service, "getRepository").mockReturnValue({
208
+ save: saveMock,
209
+ });
210
+
211
+ getJestSpyOn(
212
+ ModelPermission,
213
+ "checkUpdateQueryPermissions",
214
+ ).mockImplementation((_modelType: unknown, query: unknown) => {
215
+ return Promise.resolve(query);
216
+ });
217
+ getJestSpyOn(
218
+ ModelPermission,
219
+ "checkUpdatePermissionByModel",
220
+ ).mockResolvedValue(undefined);
221
+ });
222
+
223
+ afterEach(() => {
224
+ jest.restoreAllMocks();
225
+ });
226
+
227
+ it("still runs the find and still reports the number of rows it matched", async () => {
228
+ const numberOfDocsAffected: number = await service.updateBy({
229
+ query: {} as any,
230
+ data: { name: "renamed-probe" } as ProbeUpdateData,
231
+ props: { isRoot: true },
232
+ limit: 10,
233
+ skip: 0,
234
+ });
235
+
236
+ /*
237
+ * This assertion is also what proves the `_findBy` spy in the sibling
238
+ * describe is wired to the method _updateBy really calls: the same spy,
239
+ * on the same private method, IS exercised as soon as the payload has a
240
+ * column. "not.toHaveBeenCalled()" over there is therefore meaningful.
241
+ */
242
+ expect(findBySpy).toHaveBeenCalledTimes(1);
243
+ expect(numberOfDocsAffected).toBe(2);
244
+ });
245
+
246
+ it("still writes every row the find returned", async () => {
247
+ await service.updateOneBy({
248
+ query: { _id: TEST_ID } as any,
249
+ data: { name: "renamed-probe" } as ProbeUpdateData,
250
+ props: { isRoot: true },
251
+ });
252
+
253
+ expect(saveMock).toHaveBeenCalledTimes(2);
254
+ });
255
+
256
+ it("still calls onUpdateSuccess with the ids of the rows it updated", async () => {
257
+ const onUpdateSuccessSpy: jest.SpyInstance = getJestSpyOn(
258
+ service as any,
259
+ "onUpdateSuccess",
260
+ );
261
+
262
+ await service.updateOneBy({
263
+ query: { _id: TEST_ID } as any,
264
+ data: { name: "renamed-probe" } as ProbeUpdateData,
265
+ props: { isRoot: true, ignoreHooks: false },
266
+ });
267
+
268
+ const updatedIds: Array<ObjectID> = onUpdateSuccessSpy.mock
269
+ .calls[0]![1] as Array<ObjectID>;
270
+
271
+ expect(
272
+ updatedIds.map((id: ObjectID) => {
273
+ return id.toString();
274
+ }),
275
+ ).toEqual([TEST_ID, OTHER_ID]);
276
+ });
277
+ });