@oneuptime/common 12.0.1 → 12.0.3

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 (385) hide show
  1. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
  2. package/Models/DatabaseModels/Host.ts +12 -4
  3. package/Models/DatabaseModels/Index.ts +2 -0
  4. package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
  5. package/Models/DatabaseModels/KubernetesResource.ts +8 -7
  6. package/Models/DatabaseModels/LogDropFilter.ts +9 -20
  7. package/Models/DatabaseModels/PodmanResource.ts +6 -6
  8. package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
  9. package/Models/DatabaseModels/StatusPage.ts +95 -0
  10. package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
  11. package/Models/DatabaseModels/StatusPageResource.ts +73 -0
  12. package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
  13. package/Server/API/BaseAPI.ts +13 -2
  14. package/Server/API/DashboardAPI.ts +39 -0
  15. package/Server/API/TeamMemberAPI.ts +85 -0
  16. package/Server/EnvironmentConfig.ts +31 -1
  17. package/Server/Infrastructure/GlobalCache.ts +115 -0
  18. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  25. package/Server/Middleware/ProjectAuthorization.ts +66 -4
  26. package/Server/Services/AlertStateTimelineService.ts +20 -0
  27. package/Server/Services/BillingService.ts +600 -51
  28. package/Server/Services/CephClusterService.ts +27 -45
  29. package/Server/Services/CephResourceService.ts +43 -4
  30. package/Server/Services/CloudResourceService.ts +24 -42
  31. package/Server/Services/DatabaseService.ts +198 -6
  32. package/Server/Services/DockerHostService.ts +20 -38
  33. package/Server/Services/DockerResourceService.ts +62 -8
  34. package/Server/Services/DockerSwarmClusterService.ts +28 -46
  35. package/Server/Services/DockerSwarmResourceService.ts +53 -4
  36. package/Server/Services/HostService.ts +47 -57
  37. package/Server/Services/IncidentStateTimelineService.ts +20 -0
  38. package/Server/Services/Index.ts +2 -0
  39. package/Server/Services/IoTDeviceService.ts +1 -17
  40. package/Server/Services/IoTFleetService.ts +20 -38
  41. package/Server/Services/KubernetesClusterService.ts +18 -36
  42. package/Server/Services/KubernetesContainerService.ts +50 -9
  43. package/Server/Services/KubernetesResourceService.ts +53 -7
  44. package/Server/Services/LabelService.ts +153 -1
  45. package/Server/Services/MetricTypeService.ts +84 -0
  46. package/Server/Services/MonitorService.ts +146 -14
  47. package/Server/Services/MonitorStatusTimelineService.ts +29 -0
  48. package/Server/Services/MonitorTemplateService.ts +65 -0
  49. package/Server/Services/PodmanHostService.ts +20 -38
  50. package/Server/Services/PodmanResourceService.ts +62 -8
  51. package/Server/Services/ProjectService.ts +165 -35
  52. package/Server/Services/ProxmoxClusterService.ts +24 -42
  53. package/Server/Services/ProxmoxResourceService.ts +43 -4
  54. package/Server/Services/RumApplicationService.ts +20 -38
  55. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
  56. package/Server/Services/ServerlessFunctionService.ts +25 -43
  57. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
  58. package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
  59. package/Server/Services/ServiceService.ts +299 -73
  60. package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
  61. package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
  62. package/Server/Services/StatusPageService.ts +95 -36
  63. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
  64. package/Server/Services/StatusPageSubscriberService.ts +3 -0
  65. package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
  66. package/Server/Services/TelemetryEntityService.ts +1 -0
  67. package/Server/Types/Database/JSONColumnQuery.ts +621 -0
  68. package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
  69. package/Server/Types/Database/QueryHelper.ts +17 -32
  70. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
  71. package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
  72. package/Server/Utils/Monitor/MonitorResource.ts +63 -36
  73. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
  74. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
  75. package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
  76. package/Server/Utils/SingleFlight.ts +83 -0
  77. package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
  78. package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
  79. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
  80. package/Server/Utils/Telemetry/Telemetry.ts +351 -122
  81. package/Tests/App/Dashboard/UsersTableGroupsByPerson.test.tsx +448 -0
  82. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  83. package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
  84. package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
  85. package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
  86. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +704 -0
  87. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
  88. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
  89. package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
  90. package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
  91. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
  92. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
  93. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
  94. package/Tests/Server/Services/BillingService.test.ts +78 -3
  95. package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
  96. package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
  97. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
  98. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
  99. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
  100. package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
  101. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +658 -0
  102. package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
  103. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
  104. package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
  105. package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
  106. package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
  107. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
  108. package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +650 -0
  109. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
  110. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +636 -0
  111. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
  112. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
  113. package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
  114. package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -0
  115. package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
  116. package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
  117. package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
  118. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +29 -13
  119. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
  120. package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
  121. package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
  122. package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
  123. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
  124. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
  125. package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
  126. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
  127. package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
  128. package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
  129. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
  130. package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
  131. package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
  132. package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
  133. package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
  134. package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
  135. package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
  136. package/Tests/Types/Decimal.test.ts +37 -0
  137. package/Tests/Types/Events/Recurring.test.ts +157 -0
  138. package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
  139. package/Tests/Types/Port.test.ts +38 -0
  140. package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
  141. package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
  142. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
  143. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
  144. package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
  145. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
  146. package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
  147. package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
  148. package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
  149. package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
  150. package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
  151. package/Types/CustomField/CustomFieldDefinition.ts +24 -0
  152. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  153. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  154. package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
  155. package/Types/Dashboard/DashboardTemplates.ts +397 -0
  156. package/Types/Database/BigIntColumnTransformer.ts +74 -0
  157. package/Types/Database/DatabaseProperty.ts +26 -0
  158. package/Types/Database/NumericColumnValue.ts +128 -0
  159. package/Types/Date.ts +112 -0
  160. package/Types/Decimal.ts +16 -4
  161. package/Types/Events/Recurring.ts +99 -1
  162. package/Types/Permission.ts +48 -0
  163. package/Types/Port.ts +16 -4
  164. package/Types/StatusPage/StatusPageReport.ts +11 -0
  165. package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
  166. package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
  167. package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
  168. package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
  169. package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
  170. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
  171. package/UI/Utils/TeamMembersByUser.ts +302 -0
  172. package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
  173. package/Utils/Dashboard/Components/Index.ts +7 -0
  174. package/Utils/StatusPage/ReportPeriod.ts +236 -0
  175. package/Utils/Telemetry/HostIpAddresses.ts +98 -0
  176. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
  177. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  178. package/build/dist/Models/DatabaseModels/Host.js +12 -4
  179. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  180. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  181. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  182. package/build/dist/Models/DatabaseModels/KubernetesContainer.js +4 -4
  183. package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
  184. package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
  185. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
  187. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/PodmanResource.js +6 -6
  189. package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
  191. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
  193. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
  195. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
  196. package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
  197. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
  199. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  200. package/build/dist/Server/API/BaseAPI.js +11 -1
  201. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  202. package/build/dist/Server/API/DashboardAPI.js +39 -0
  203. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  204. package/build/dist/Server/API/TeamMemberAPI.js +57 -2
  205. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  206. package/build/dist/Server/EnvironmentConfig.js +27 -1
  207. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  208. package/build/dist/Server/Infrastructure/GlobalCache.js +91 -0
  209. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  210. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
  211. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  212. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js +45 -0
  213. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
  214. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
  215. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
  216. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
  217. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
  218. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
  219. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
  220. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
  221. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
  222. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  223. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  224. package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
  225. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  226. package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
  227. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  228. package/build/dist/Server/Services/BillingService.js +466 -36
  229. package/build/dist/Server/Services/BillingService.js.map +1 -1
  230. package/build/dist/Server/Services/CephClusterService.js +26 -39
  231. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  232. package/build/dist/Server/Services/CephResourceService.js +28 -3
  233. package/build/dist/Server/Services/CephResourceService.js.map +1 -1
  234. package/build/dist/Server/Services/CloudResourceService.js +23 -36
  235. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  236. package/build/dist/Server/Services/DatabaseService.js +155 -2
  237. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  238. package/build/dist/Server/Services/DockerHostService.js +19 -32
  239. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  240. package/build/dist/Server/Services/DockerResourceService.js +34 -4
  241. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  242. package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -40
  243. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  244. package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
  245. package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
  246. package/build/dist/Server/Services/HostService.js +58 -64
  247. package/build/dist/Server/Services/HostService.js.map +1 -1
  248. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  249. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  250. package/build/dist/Server/Services/Index.js +2 -0
  251. package/build/dist/Server/Services/Index.js.map +1 -1
  252. package/build/dist/Server/Services/IoTDeviceService.js +1 -8
  253. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  254. package/build/dist/Server/Services/IoTFleetService.js +19 -32
  255. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  256. package/build/dist/Server/Services/KubernetesClusterService.js +17 -30
  257. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  258. package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
  259. package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
  260. package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
  261. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  262. package/build/dist/Server/Services/LabelService.js +127 -0
  263. package/build/dist/Server/Services/LabelService.js.map +1 -1
  264. package/build/dist/Server/Services/MetricTypeService.js +74 -0
  265. package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
  266. package/build/dist/Server/Services/MonitorService.js +128 -9
  267. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  268. package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
  269. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  270. package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
  271. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  272. package/build/dist/Server/Services/PodmanHostService.js +19 -32
  273. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  274. package/build/dist/Server/Services/PodmanResourceService.js +34 -4
  275. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  276. package/build/dist/Server/Services/ProjectService.js +135 -21
  277. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  278. package/build/dist/Server/Services/ProxmoxClusterService.js +23 -36
  279. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  280. package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
  281. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  282. package/build/dist/Server/Services/RumApplicationService.js +19 -32
  283. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  284. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
  285. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  286. package/build/dist/Server/Services/ServerlessFunctionService.js +24 -37
  287. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  288. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
  289. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
  290. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
  291. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  292. package/build/dist/Server/Services/ServiceService.js +236 -61
  293. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  294. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
  295. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
  296. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
  297. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
  298. package/build/dist/Server/Services/StatusPageService.js +69 -46
  299. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  300. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
  301. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  302. package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
  303. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  304. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
  305. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  306. package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
  307. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  308. package/build/dist/Server/Types/Database/JSONColumnQuery.js +402 -0
  309. package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
  310. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
  311. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
  312. package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
  313. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  314. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
  315. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  316. package/build/dist/Server/Utils/Database/TruncateColumnValue.js +32 -0
  317. package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
  318. package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
  319. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  320. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
  321. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  322. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
  323. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
  324. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
  325. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
  326. package/build/dist/Server/Utils/SingleFlight.js +76 -0
  327. package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
  328. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
  329. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
  330. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
  331. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  332. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
  333. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
  334. package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
  335. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  336. package/build/dist/Types/CustomField/CustomFieldDefinition.js +2 -0
  337. package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
  338. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  339. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  340. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  341. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  342. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
  343. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
  344. package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
  345. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  346. package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
  347. package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
  348. package/build/dist/Types/Database/DatabaseProperty.js +25 -0
  349. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  350. package/build/dist/Types/Database/NumericColumnValue.js +97 -0
  351. package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
  352. package/build/dist/Types/Date.js +68 -0
  353. package/build/dist/Types/Date.js.map +1 -1
  354. package/build/dist/Types/Decimal.js +14 -3
  355. package/build/dist/Types/Decimal.js.map +1 -1
  356. package/build/dist/Types/Events/Recurring.js +63 -0
  357. package/build/dist/Types/Events/Recurring.js.map +1 -1
  358. package/build/dist/Types/Permission.js +42 -0
  359. package/build/dist/Types/Permission.js.map +1 -1
  360. package/build/dist/Types/Port.js +15 -5
  361. package/build/dist/Types/Port.js.map +1 -1
  362. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
  363. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
  364. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
  365. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
  366. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
  367. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
  368. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  369. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
  370. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  371. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
  372. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
  373. package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
  374. package/build/dist/UI/Utils/TeamMembersByUser.js.map +1 -0
  375. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
  376. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
  377. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  378. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  379. package/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
  380. package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
  381. package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
  382. package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
  383. package/jest.config.json +2 -0
  384. package/package.json +1 -1
  385. package/tsconfig.json +12 -1
@@ -5,6 +5,7 @@ import Model from "../../Models/DatabaseModels/KubernetesCluster";
5
5
  import Label from "../../Models/DatabaseModels/Label";
6
6
  import { OnCreate } from "../Types/Database/Hooks";
7
7
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
8
+ import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
8
9
  import ObjectID from "../../Types/ObjectID";
9
10
  import QueryHelper from "../Types/Database/QueryHelper";
10
11
  import OneUptimeDate from "../../Types/Date";
@@ -145,7 +146,6 @@ export class Service extends DatabaseService<Model> {
145
146
  agentVersion?: string | undefined;
146
147
  },
147
148
  ): Promise<void> {
148
- const cacheKey: string = clusterId.toString();
149
149
  const extrasFingerprint: string = crypto
150
150
  .createHash("sha1")
151
151
  .update(
@@ -155,52 +155,34 @@ export class Service extends DatabaseService<Model> {
155
155
  )
156
156
  .digest("hex");
157
157
 
158
- let cached: string | null = null;
159
- try {
160
- cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
161
- } catch {
162
- /*
163
- * Cache unavailable — fail open and refresh lastSeenAt anyway. A
164
- * cache error must never skip the DB write below, otherwise the
165
- * resource is wrongly marked "disconnected" while telemetry is
166
- * still flowing. Mirrors shouldRunMaintenance's fail-open stance.
167
- */
168
- cached = null;
169
- }
170
-
171
- if (cached === extrasFingerprint) {
172
- return; // same data was written recently
173
- }
174
-
175
- try {
176
- await GlobalCache.setString(
177
- LAST_SEEN_CACHE_NAMESPACE,
178
- cacheKey,
179
- extrasFingerprint,
180
- { expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
181
- );
182
- } catch {
183
- // Best-effort throttle write; proceed with the DB update regardless.
184
- }
185
-
186
158
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
- const data: any = {
159
+ const liveness: any = {
188
160
  lastSeenAt: OneUptimeDate.getCurrentDate(),
189
161
  otelCollectorStatus: "connected",
190
162
  };
191
163
 
164
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
165
+ const metadata: any = {};
166
+
192
167
  if (extra?.agentVersion) {
193
- data.agentVersion = extra.agentVersion;
168
+ metadata.agentVersion = extra.agentVersion;
194
169
  }
195
170
 
196
171
  /*
197
- * Heartbeat write: a single-statement UPDATE with no hooks and no
198
- * `version` bump, avoiding the hot-row Postgres lock convoy that the
199
- * full updateOneById pipeline causes. See ServiceService.updateLastSeen.
172
+ * One gated, non-blocking heartbeat write. The gates, the fail-open /
173
+ * fail-closed split and the liveness-only fallback all live in
174
+ * ResourceHeartbeat see there for why this row's throttle used to
175
+ * provide no throttling at all.
200
176
  */
201
- await this.updateColumnsByIdWithoutHooks({
177
+ await ResourceHeartbeat.write({
178
+ service: this,
202
179
  id: clusterId,
203
- data: data,
180
+ cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
181
+ throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
182
+ liveness: liveness,
183
+ metadata: metadata,
184
+ fingerprint: extrasFingerprint,
185
+ describe: `kubernetes cluster ${clusterId.toString()}`,
204
186
  });
205
187
  }
206
188
 
@@ -1,7 +1,12 @@
1
1
  import DatabaseService from "./DatabaseService";
2
2
  import Model from "../../Models/DatabaseModels/KubernetesContainer";
3
3
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
4
+ import ColumnLength from "../../Types/Database/ColumnLength";
4
5
  import ObjectID from "../../Types/ObjectID";
6
+ import {
7
+ truncateLongText,
8
+ truncateShortText,
9
+ } from "../Utils/Database/TruncateColumnValue";
5
10
  import logger from "../Utils/Logger";
6
11
 
7
12
  /*
@@ -61,6 +66,29 @@ const UPSERT_COLUMNS: Array<string> = [
61
66
  "version",
62
67
  ];
63
68
 
69
+ /*
70
+ * KubernetesContainer's text columns are bounded varchars — podName and
71
+ * image at 500 chars, podNamespaceKey / name / state / reason at 100.
72
+ * The bulk paths below go through manager.query(), which skips the
73
+ * length validation DatabaseService applies to ordinary writes, so a
74
+ * single oversized value aborts the whole 500-row INSERT chunk it rides
75
+ * in. Clamp per value instead, so one pathological pod can never drop
76
+ * the rest of the snapshot.
77
+ */
78
+ function sanitizeContainer(
79
+ c: ParsedKubernetesContainer,
80
+ ): ParsedKubernetesContainer {
81
+ return {
82
+ ...c,
83
+ podNamespaceKey: truncateShortText(c.podNamespaceKey),
84
+ podName: truncateLongText(c.podName),
85
+ name: truncateShortText(c.name),
86
+ image: truncateLongText(c.image),
87
+ state: truncateShortText(c.state),
88
+ reason: truncateShortText(c.reason),
89
+ };
90
+ }
91
+
64
92
  export class Service extends DatabaseService<Model> {
65
93
  public constructor() {
66
94
  super(Model);
@@ -86,12 +114,20 @@ export class Service extends DatabaseService<Model> {
86
114
  return;
87
115
  }
88
116
 
89
- for (
90
- let i: number = 0;
91
- i < data.containers.length;
92
- i += UPSERT_BATCH_SIZE
93
- ) {
94
- const chunk: Array<ParsedKubernetesContainer> = data.containers.slice(
117
+ const containers: Array<ParsedKubernetesContainer> = data.containers.map(
118
+ (c: ParsedKubernetesContainer) => {
119
+ const sanitized: ParsedKubernetesContainer = sanitizeContainer(c);
120
+ if (sanitized.podName !== c.podName) {
121
+ logger.warn(
122
+ `KubernetesContainer podName exceeds ${ColumnLength.LongText} chars; truncated to "${sanitized.podName}" (cluster ${data.kubernetesClusterId.toString()}).`,
123
+ );
124
+ }
125
+ return sanitized;
126
+ },
127
+ );
128
+
129
+ for (let i: number = 0; i < containers.length; i += UPSERT_BATCH_SIZE) {
130
+ const chunk: Array<ParsedKubernetesContainer> = containers.slice(
95
131
  i,
96
132
  i + UPSERT_BATCH_SIZE,
97
133
  );
@@ -224,10 +260,15 @@ export class Service extends DatabaseService<Model> {
224
260
  valueFragments.push(
225
261
  `($${paramIndex++}, $${paramIndex++}, $${paramIndex++}, $${paramIndex++}::numeric, $${paramIndex++}::bigint, $${paramIndex++}::timestamptz)`,
226
262
  );
263
+ /*
264
+ * The identity clamps MUST match bulkUpsert's, or this mirror
265
+ * UPDATE would silently miss the row the upsert just wrote
266
+ * under the truncated podName.
267
+ */
227
268
  params.push(
228
- m.podNamespaceKey,
229
- m.podName,
230
- m.name,
269
+ truncateShortText(m.podNamespaceKey),
270
+ truncateLongText(m.podName),
271
+ truncateShortText(m.name),
231
272
  m.cpuPercent !== null && m.cpuPercent !== undefined
232
273
  ? m.cpuPercent
233
274
  : null,
@@ -1,9 +1,14 @@
1
1
  import DatabaseService from "./DatabaseService";
2
2
  import Model from "../../Models/DatabaseModels/KubernetesResource";
3
3
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
4
+ import ColumnLength from "../../Types/Database/ColumnLength";
4
5
  import ObjectID from "../../Types/ObjectID";
5
6
  import OneUptimeDate from "../../Types/Date";
6
7
  import { ParsedKubernetesResource } from "../../Types/Kubernetes/KubernetesInventoryExtractor";
8
+ import {
9
+ truncateLongText,
10
+ truncateShortText,
11
+ } from "../Utils/Database/TruncateColumnValue";
7
12
  import logger from "../Utils/Logger";
8
13
 
9
14
  /*
@@ -317,6 +322,28 @@ const UPSERT_COLUMNS: Array<keyof ParsedKubernetesResource | string> = [
317
322
  "version",
318
323
  ];
319
324
 
325
+ /*
326
+ * KubernetesResource's text columns are bounded varchars — name and
327
+ * controllerDeploymentName at 500 chars, kind / namespaceKey / uid /
328
+ * phase at 100. The bulk paths below go through manager.query(), which
329
+ * skips the length validation DatabaseService applies to ordinary
330
+ * writes, so a single oversized value aborts the whole 500-row INSERT
331
+ * chunk it rides in. Clamp per value instead, so one pathological
332
+ * object can never drop the rest of the snapshot.
333
+ */
334
+ function sanitizeResource(
335
+ r: ParsedKubernetesResource,
336
+ ): ParsedKubernetesResource {
337
+ return {
338
+ ...r,
339
+ kind: truncateShortText(r.kind),
340
+ namespaceKey: truncateShortText(r.namespaceKey),
341
+ name: truncateLongText(r.name),
342
+ uid: truncateShortText(r.uid),
343
+ phase: truncateShortText(r.phase),
344
+ };
345
+ }
346
+
320
347
  export class Service extends DatabaseService<Model> {
321
348
  public constructor() {
322
349
  super(Model);
@@ -338,9 +365,21 @@ export class Service extends DatabaseService<Model> {
338
365
  return;
339
366
  }
340
367
 
368
+ const resources: Array<ParsedKubernetesResource> = data.resources.map(
369
+ (r: ParsedKubernetesResource) => {
370
+ const sanitized: ParsedKubernetesResource = sanitizeResource(r);
371
+ if (sanitized.name !== r.name) {
372
+ logger.warn(
373
+ `KubernetesResource name exceeds ${ColumnLength.LongText} chars; truncated to "${sanitized.name}" (cluster ${data.kubernetesClusterId.toString()}).`,
374
+ );
375
+ }
376
+ return sanitized;
377
+ },
378
+ );
379
+
341
380
  // Chunk to keep individual statement parameter counts reasonable.
342
- for (let i: number = 0; i < data.resources.length; i += UPSERT_BATCH_SIZE) {
343
- const chunk: Array<ParsedKubernetesResource> = data.resources.slice(
381
+ for (let i: number = 0; i < resources.length; i += UPSERT_BATCH_SIZE) {
382
+ const chunk: Array<ParsedKubernetesResource> = resources.slice(
344
383
  i,
345
384
  i + UPSERT_BATCH_SIZE,
346
385
  );
@@ -450,10 +489,17 @@ export class Service extends DatabaseService<Model> {
450
489
  valueFragments.push(
451
490
  `($${paramIndex++}, $${paramIndex++}, $${paramIndex++}, $${paramIndex++}::numeric, $${paramIndex++}::bigint, $${paramIndex++}::numeric, $${paramIndex++}::timestamptz, $${paramIndex++}, $${paramIndex++})`,
452
491
  );
492
+ /*
493
+ * The identity clamps MUST match bulkUpsert's, or this mirror
494
+ * UPDATE would silently miss the row the upsert just wrote
495
+ * under the truncated name. controllerDeploymentName /
496
+ * controllerCronJobName are assigned columns, so an oversized
497
+ * value there aborts the chunk exactly like the INSERT path.
498
+ */
453
499
  params.push(
454
- m.kind,
455
- m.namespaceKey,
456
- m.name,
500
+ truncateShortText(m.kind),
501
+ truncateShortText(m.namespaceKey),
502
+ truncateLongText(m.name),
457
503
  m.cpuPercent !== null && m.cpuPercent !== undefined
458
504
  ? m.cpuPercent
459
505
  : null,
@@ -464,8 +510,8 @@ export class Service extends DatabaseService<Model> {
464
510
  ? m.memoryPercent
465
511
  : null,
466
512
  m.observedAt,
467
- m.controllerDeploymentName ?? null,
468
- m.controllerCronJobName ?? null,
513
+ truncateLongText(m.controllerDeploymentName ?? null),
514
+ truncateShortText(m.controllerCronJobName ?? null),
469
515
  );
470
516
  }
471
517
 
@@ -1,14 +1,23 @@
1
1
  import CreateBy from "../Types/Database/CreateBy";
2
- import { OnCreate } from "../Types/Database/Hooks";
2
+ import DeleteBy from "../Types/Database/DeleteBy";
3
+ import { OnCreate, OnDelete } from "../Types/Database/Hooks";
3
4
  import QueryHelper from "../Types/Database/QueryHelper";
4
5
  import DatabaseService from "./DatabaseService";
6
+ import ServiceLevelObjectiveMonitorRuleEngineService from "./ServiceLevelObjectiveMonitorRuleEngineService";
7
+ import ServiceLevelObjectiveService from "./ServiceLevelObjectiveService";
8
+ import StatusPageMonitorRuleEngineService from "./StatusPageMonitorRuleEngineService";
9
+ import StatusPageMonitorRuleService from "./StatusPageMonitorRuleService";
10
+ import ServiceLevelObjective from "../../Models/DatabaseModels/ServiceLevelObjective";
11
+ import StatusPageMonitorRule from "../../Models/DatabaseModels/StatusPageMonitorRule";
5
12
  import BadDataException from "../../Types/Exception/BadDataException";
13
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
6
14
  import ObjectID from "../../Types/ObjectID";
7
15
  import Color from "../../Types/Color";
8
16
  import { BrightColors } from "../../Types/BrandColors";
9
17
  import GlobalCache from "../Infrastructure/GlobalCache";
10
18
  import Model from "../../Models/DatabaseModels/Label";
11
19
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
20
+ import logger from "../Utils/Logger";
12
21
 
13
22
  const AUTO_LABEL_ID_CACHE_NAMESPACE: string = "auto-label-id";
14
23
  const AUTO_LABEL_ID_CACHE_TTL_SECONDS: number = 24 * 60 * 60;
@@ -57,6 +66,149 @@ export class Service extends DatabaseService<Model> {
57
66
  return Promise.resolve({ createBy, carryForward: null });
58
67
  }
59
68
 
69
+ /*
70
+ * Deleting a label cascades its join rows away at the database level, so no
71
+ * service hook ever fires for the SLO label rules or status page monitor
72
+ * rules that referenced it. Those would keep the monitors the rule attached,
73
+ * with nothing left to explain why. Note the ids down while the label still
74
+ * exists, and re-run the (now smaller) rules once it is gone.
75
+ */
76
+ @CaptureSpan()
77
+ protected override async onBeforeDelete(
78
+ deleteBy: DeleteBy<Model>,
79
+ ): Promise<OnDelete<Model>> {
80
+ let serviceLevelObjectiveIds: Array<ObjectID> = [];
81
+ let statusPageMonitorRuleIds: Array<ObjectID> = [];
82
+
83
+ try {
84
+ const labels: Array<Model> = await this.findBy({
85
+ query: deleteBy.query,
86
+ select: {
87
+ _id: true,
88
+ },
89
+ limit: LIMIT_MAX,
90
+ skip: 0,
91
+ props: {
92
+ isRoot: true,
93
+ },
94
+ });
95
+
96
+ const labelIds: Array<ObjectID> = labels
97
+ .map((label: Model) => {
98
+ return label.id;
99
+ })
100
+ .filter((id: ObjectID | null): id is ObjectID => {
101
+ return Boolean(id);
102
+ });
103
+
104
+ if (labelIds.length > 0) {
105
+ const slos: Array<ServiceLevelObjective> =
106
+ await ServiceLevelObjectiveService.findBy({
107
+ query: {
108
+ monitorLabels: labelIds,
109
+ },
110
+ select: {
111
+ _id: true,
112
+ },
113
+ limit: LIMIT_MAX,
114
+ skip: 0,
115
+ props: {
116
+ isRoot: true,
117
+ },
118
+ });
119
+
120
+ serviceLevelObjectiveIds = slos
121
+ .map((slo: ServiceLevelObjective) => {
122
+ return slo.id;
123
+ })
124
+ .filter((id: ObjectID | null): id is ObjectID => {
125
+ return Boolean(id);
126
+ });
127
+
128
+ const statusPageMonitorRules: Array<StatusPageMonitorRule> =
129
+ await StatusPageMonitorRuleService.findBy({
130
+ query: {
131
+ monitorLabels: labelIds,
132
+ },
133
+ select: {
134
+ _id: true,
135
+ },
136
+ limit: LIMIT_MAX,
137
+ skip: 0,
138
+ props: {
139
+ isRoot: true,
140
+ },
141
+ });
142
+
143
+ statusPageMonitorRuleIds = statusPageMonitorRules
144
+ .map((rule: StatusPageMonitorRule) => {
145
+ return rule.id;
146
+ })
147
+ .filter((id: ObjectID | null): id is ObjectID => {
148
+ return Boolean(id);
149
+ });
150
+ }
151
+ } catch (err) {
152
+ logger.error(
153
+ `Error collecting label rules affected by a label delete: ${err}`,
154
+ );
155
+ }
156
+
157
+ return {
158
+ deleteBy,
159
+ carryForward: {
160
+ serviceLevelObjectiveIds: serviceLevelObjectiveIds,
161
+ statusPageMonitorRuleIds: statusPageMonitorRuleIds,
162
+ },
163
+ };
164
+ }
165
+
166
+ @CaptureSpan()
167
+ protected override async onDeleteSuccess(
168
+ onDelete: OnDelete<Model>,
169
+ _itemIdsBeforeDelete: Array<ObjectID>,
170
+ ): Promise<OnDelete<Model>> {
171
+ const carryForward: {
172
+ serviceLevelObjectiveIds?: Array<ObjectID>;
173
+ statusPageMonitorRuleIds?: Array<ObjectID>;
174
+ } | null = onDelete.carryForward as {
175
+ serviceLevelObjectiveIds?: Array<ObjectID>;
176
+ statusPageMonitorRuleIds?: Array<ObjectID>;
177
+ } | null;
178
+
179
+ const serviceLevelObjectiveIds: Array<ObjectID> =
180
+ carryForward?.serviceLevelObjectiveIds || [];
181
+
182
+ for (const serviceLevelObjectiveId of serviceLevelObjectiveIds) {
183
+ try {
184
+ await ServiceLevelObjectiveMonitorRuleEngineService.syncMonitorsForSlo({
185
+ serviceLevelObjectiveId: serviceLevelObjectiveId,
186
+ });
187
+ } catch (err) {
188
+ logger.error(
189
+ `Error re-applying the monitor label rule for SLO ${serviceLevelObjectiveId.toString()} after a label delete: ${err}`,
190
+ );
191
+ }
192
+ }
193
+
194
+ const statusPageMonitorRuleIds: Array<ObjectID> =
195
+ carryForward?.statusPageMonitorRuleIds || [];
196
+
197
+ for (const statusPageMonitorRuleId of statusPageMonitorRuleIds) {
198
+ try {
199
+ await StatusPageMonitorRuleEngineService.syncResourcesForRule({
200
+ statusPageMonitorRuleId: statusPageMonitorRuleId,
201
+ });
202
+ } catch (err) {
203
+ logger.error(
204
+ `Error re-applying status page monitor rule ${statusPageMonitorRuleId.toString()} after a label delete: ${err}`,
205
+ );
206
+ }
207
+ }
208
+
209
+ return onDelete;
210
+ }
211
+
60
212
  /**
61
213
  * Find existing labels by name (case-insensitive) or auto-create
62
214
  * them, returning the ObjectIDs in the same order as the input.
@@ -1,10 +1,94 @@
1
1
  import DatabaseService from "./DatabaseService";
2
2
  import Model from "../../Models/DatabaseModels/MetricType";
3
+ import ObjectID from "../../Types/ObjectID";
4
+ import BadDataException from "../../Types/Exception/BadDataException";
5
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
6
+ import { RelationMetadata } from "typeorm/metadata/RelationMetadata";
3
7
 
4
8
  export class Service extends DatabaseService<Model> {
5
9
  public constructor() {
6
10
  super(Model);
7
11
  }
12
+
13
+ /*
14
+ * Additively associate services with a metric type, in ONE statement.
15
+ *
16
+ * This replaces routing the association through `updateOneById` with the
17
+ * whole `services` array in the payload. That looked innocuous and was not:
18
+ * `services` is a `TableColumnType.EntityArray`, and its mere PRESENCE as a
19
+ * key flips `hasRelationUpdates` in DatabaseService, which routes the write
20
+ * to `getRepository().save()` — a real BEGIN/COMMIT that reloads the entity,
21
+ * loads the relation ids, bumps `version`, and DELETEs then re-INSERTs
22
+ * junction rows, holding the MetricType row's write lock across every one of
23
+ * those round trips. On the ingest path, where this runs per metric name per
24
+ * batch with no backpressure, it was the only multi-round-trip lock hold in
25
+ * the pipeline.
26
+ *
27
+ * The whole-array write was also silently LOSING data. `save()` diffs the
28
+ * array it is given against what is currently in the database and deletes
29
+ * anything missing — but each ingest worker only knows the services in ITS
30
+ * batch. Two workers with different batches therefore deleted each other's
31
+ * associations and re-inserted them on the next batch: permanent junction
32
+ * churn, and real service-to-metric links disappearing from the UI in
33
+ * between. Additive insert makes that impossible to express.
34
+ *
35
+ * ON CONFLICT DO NOTHING is well-defined here: the junction's primary key is
36
+ * exactly (metricTypeId, serviceId), so a concurrent writer adding the same
37
+ * association is a no-op rather than a unique violation.
38
+ */
39
+ @CaptureSpan()
40
+ public async attachServices(data: {
41
+ metricTypeId: ObjectID;
42
+ serviceIds: Array<ObjectID>;
43
+ }): Promise<void> {
44
+ if (!data.metricTypeId) {
45
+ throw new BadDataException("metricTypeId is required");
46
+ }
47
+
48
+ if (!data.serviceIds || data.serviceIds.length === 0) {
49
+ return;
50
+ }
51
+
52
+ /*
53
+ * Identifiers come from entity metadata, never from the caller, and every
54
+ * value is bound as a parameter — the same rule the other raw-SQL paths in
55
+ * DatabaseService follow.
56
+ */
57
+ const relation: RelationMetadata | undefined =
58
+ this.getRepository().metadata.findRelationWithPropertyPath("services");
59
+
60
+ const junction: string | undefined =
61
+ relation?.junctionEntityMetadata?.tableName;
62
+ const metricTypeColumn: string | undefined =
63
+ relation?.junctionEntityMetadata?.columns[0]?.databaseName;
64
+ const serviceColumn: string | undefined =
65
+ relation?.junctionEntityMetadata?.columns[1]?.databaseName;
66
+
67
+ if (!junction || !metricTypeColumn || !serviceColumn) {
68
+ throw new BadDataException(
69
+ "MetricTypeService.attachServices: the services relation has no junction metadata",
70
+ );
71
+ }
72
+
73
+ /*
74
+ * Deduplicate and SORT. Concurrent statements that insert overlapping sets
75
+ * acquire their row locks in the same order this way, which is what keeps
76
+ * two workers associating the same services from deadlocking each other.
77
+ */
78
+ const serviceIds: Array<string> = Array.from(
79
+ new Set(
80
+ data.serviceIds.map((id: ObjectID) => {
81
+ return id.toString();
82
+ }),
83
+ ),
84
+ ).sort();
85
+
86
+ await this.getRepository().manager.query(
87
+ `INSERT INTO "${junction}" ("${metricTypeColumn}", "${serviceColumn}") ` +
88
+ `SELECT $1, unnest($2::uuid[]) ON CONFLICT DO NOTHING`,
89
+ [data.metricTypeId.toString(), serviceIds],
90
+ );
91
+ }
8
92
  }
9
93
 
10
94
  export default new Service();