@oneuptime/common 12.0.2 → 12.0.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 (384) hide show
  1. package/Models/DatabaseModels/CephCluster.ts +14 -0
  2. package/Models/DatabaseModels/CodeRepository.ts +14 -6
  3. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
  4. package/Models/DatabaseModels/DockerHost.ts +14 -0
  5. package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
  6. package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
  7. package/Models/DatabaseModels/Index.ts +2 -0
  8. package/Models/DatabaseModels/IoTFleet.ts +14 -0
  9. package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
  10. package/Models/DatabaseModels/LogDropFilter.ts +9 -20
  11. package/Models/DatabaseModels/NetworkDevice.ts +14 -0
  12. package/Models/DatabaseModels/NetworkInterface.ts +18 -0
  13. package/Models/DatabaseModels/Project.ts +16 -2
  14. package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
  15. package/Models/DatabaseModels/Service.ts +30 -0
  16. package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
  17. package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
  18. package/Models/DatabaseModels/StatusPageResource.ts +73 -0
  19. package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
  20. package/Models/DatabaseModels/User.ts +30 -0
  21. package/Server/API/AIAgentDataAPI.ts +31 -0
  22. package/Server/API/BaseAPI.ts +13 -2
  23. package/Server/API/DashboardAPI.ts +458 -12
  24. package/Server/API/GitHubAPI.ts +119 -283
  25. package/Server/API/UserAPI.ts +263 -1
  26. package/Server/EnvironmentConfig.ts +38 -4
  27. package/Server/Infrastructure/GlobalCache.ts +152 -0
  28. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  36. package/Server/Services/AlertStateTimelineService.ts +20 -0
  37. package/Server/Services/AnalyticsDatabaseService.ts +72 -1
  38. package/Server/Services/CephClusterService.ts +26 -86
  39. package/Server/Services/CloudResourceService.ts +23 -83
  40. package/Server/Services/CodeRepositoryService.ts +105 -2
  41. package/Server/Services/DatabaseService.ts +296 -2
  42. package/Server/Services/DockerHostService.ts +19 -79
  43. package/Server/Services/DockerSwarmClusterService.ts +27 -88
  44. package/Server/Services/HostService.ts +46 -99
  45. package/Server/Services/IncidentStateTimelineService.ts +20 -0
  46. package/Server/Services/Index.ts +2 -0
  47. package/Server/Services/IoTFleetService.ts +19 -79
  48. package/Server/Services/KubernetesClusterService.ts +17 -78
  49. package/Server/Services/LabelService.ts +57 -10
  50. package/Server/Services/MetricTypeService.ts +84 -0
  51. package/Server/Services/MonitorService.ts +89 -14
  52. package/Server/Services/MonitorStatusTimelineService.ts +29 -0
  53. package/Server/Services/MonitorTemplateService.ts +65 -0
  54. package/Server/Services/OpenTelemetryIngestService.ts +230 -26
  55. package/Server/Services/PodmanHostService.ts +19 -79
  56. package/Server/Services/ProxmoxClusterService.ts +23 -83
  57. package/Server/Services/RumApplicationService.ts +19 -80
  58. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
  59. package/Server/Services/ServerlessFunctionService.ts +24 -85
  60. package/Server/Services/ServiceService.ts +299 -73
  61. package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
  62. package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
  63. package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
  64. package/Server/Services/TelemetryEntityService.ts +1 -0
  65. package/Server/Services/UserService.ts +184 -0
  66. package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
  67. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
  68. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
  69. package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
  70. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
  71. package/Server/Utils/Monitor/MonitorResource.ts +63 -36
  72. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
  73. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
  74. package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
  75. package/Server/Utils/PasswordHash.ts +306 -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/OverviewCustomFields.test.tsx +333 -0
  82. package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
  83. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  84. package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
  85. package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
  86. package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
  87. package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
  88. package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
  89. package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
  90. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
  91. package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
  92. package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
  93. package/Tests/Server/API/Helpers.ts +6 -1
  94. package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
  95. package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
  96. package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
  97. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
  98. package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
  99. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
  100. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
  101. package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
  102. package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
  103. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
  104. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
  105. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
  106. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
  107. package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
  108. package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
  109. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
  110. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
  111. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
  112. package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
  113. package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
  114. package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -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/UserServiceFirstMasterAdminElection.test.ts +885 -0
  119. package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
  120. package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
  121. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
  122. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
  123. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
  124. package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
  125. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
  126. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
  127. package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
  128. package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
  129. package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
  130. package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
  131. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
  132. package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
  133. package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
  134. package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
  135. package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
  136. package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
  137. package/Tests/Types/Database/ColumnLength.test.ts +7 -1
  138. package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
  139. package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
  140. package/Tests/Types/Decimal.test.ts +37 -0
  141. package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
  142. package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
  143. package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
  144. package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
  145. package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
  146. package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
  147. package/Tests/Types/Port.test.ts +38 -0
  148. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
  149. package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
  150. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
  151. package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
  152. package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
  153. package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
  154. package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
  155. package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
  156. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  157. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  158. package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
  159. package/Types/Dashboard/DashboardTemplates.ts +397 -0
  160. package/Types/Database/BigIntColumnTransformer.ts +74 -0
  161. package/Types/Database/ColumnLength.ts +9 -1
  162. package/Types/Database/DatabaseProperty.ts +26 -0
  163. package/Types/Database/NumericColumnValue.ts +128 -0
  164. package/Types/Database/TableColumn.ts +11 -0
  165. package/Types/Database/UnsynchronizedIndex.ts +48 -0
  166. package/Types/Decimal.ts +16 -4
  167. package/Types/HashedString.ts +139 -4
  168. package/Types/Permission.ts +48 -0
  169. package/Types/Port.ts +16 -4
  170. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
  171. package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
  172. package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
  173. package/UI/Components/Navbar/NavBar.tsx +12 -2
  174. package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
  175. package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
  176. package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
  177. package/Utils/Dashboard/Components/Index.ts +7 -0
  178. package/Utils/TeamMembersByProject.ts +237 -0
  179. package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
  180. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  181. package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
  182. package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
  184. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
  186. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  187. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
  188. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
  189. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
  190. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
  191. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  192. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  193. package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
  194. package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
  195. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
  196. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  197. package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
  198. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  199. package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
  200. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  201. package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
  202. package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
  203. package/build/dist/Models/DatabaseModels/Project.js +16 -2
  204. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  205. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
  206. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  207. package/build/dist/Models/DatabaseModels/Service.js +28 -1
  208. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  209. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
  210. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
  211. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
  212. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
  213. package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
  214. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  215. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
  216. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/User.js +32 -0
  218. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/BaseAPI.js +11 -1
  222. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +343 -10
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/GitHubAPI.js +94 -167
  226. package/build/dist/Server/API/GitHubAPI.js.map +1 -1
  227. package/build/dist/Server/API/UserAPI.js +187 -2
  228. package/build/dist/Server/API/UserAPI.js.map +1 -1
  229. package/build/dist/Server/EnvironmentConfig.js +34 -4
  230. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
  232. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
  234. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
  245. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
  246. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
  247. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  248. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  249. package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
  250. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/CephClusterService.js +26 -76
  254. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  255. package/build/dist/Server/Services/CloudResourceService.js +23 -73
  256. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  257. package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
  258. package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
  259. package/build/dist/Server/Services/DatabaseService.js +246 -2
  260. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  261. package/build/dist/Server/Services/DockerHostService.js +19 -69
  262. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  263. package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
  264. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  265. package/build/dist/Server/Services/HostService.js +58 -102
  266. package/build/dist/Server/Services/HostService.js.map +1 -1
  267. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  268. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  269. package/build/dist/Server/Services/Index.js +2 -0
  270. package/build/dist/Server/Services/Index.js.map +1 -1
  271. package/build/dist/Server/Services/IoTFleetService.js +19 -69
  272. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  273. package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
  274. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  275. package/build/dist/Server/Services/LabelService.js +42 -7
  276. package/build/dist/Server/Services/LabelService.js.map +1 -1
  277. package/build/dist/Server/Services/MetricTypeService.js +74 -0
  278. package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
  279. package/build/dist/Server/Services/MonitorService.js +81 -9
  280. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  281. package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
  282. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  283. package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
  284. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  285. package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
  286. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  287. package/build/dist/Server/Services/PodmanHostService.js +19 -69
  288. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  289. package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
  290. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  291. package/build/dist/Server/Services/RumApplicationService.js +19 -70
  292. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  293. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
  294. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  295. package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
  296. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  297. package/build/dist/Server/Services/ServiceService.js +236 -61
  298. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  299. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
  300. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
  301. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
  302. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
  303. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
  304. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  305. package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
  306. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  307. package/build/dist/Server/Services/UserService.js +165 -0
  308. package/build/dist/Server/Services/UserService.js.map +1 -1
  309. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
  310. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
  311. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
  312. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  313. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
  314. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  315. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
  316. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
  317. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
  318. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  319. package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
  320. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  321. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
  322. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  323. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
  324. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
  325. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
  326. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
  327. package/build/dist/Server/Utils/PasswordHash.js +237 -0
  328. package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
  329. package/build/dist/Server/Utils/SingleFlight.js +76 -0
  330. package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
  331. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
  332. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
  333. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
  334. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  335. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
  336. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
  337. package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
  338. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  339. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  340. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  341. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  342. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  343. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
  344. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
  345. package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
  346. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  347. package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
  348. package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
  349. package/build/dist/Types/Database/ColumnLength.js +9 -1
  350. package/build/dist/Types/Database/ColumnLength.js.map +1 -1
  351. package/build/dist/Types/Database/DatabaseProperty.js +25 -0
  352. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  353. package/build/dist/Types/Database/NumericColumnValue.js +97 -0
  354. package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
  355. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  356. package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
  357. package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
  358. package/build/dist/Types/Decimal.js +14 -3
  359. package/build/dist/Types/Decimal.js.map +1 -1
  360. package/build/dist/Types/HashedString.js +95 -5
  361. package/build/dist/Types/HashedString.js.map +1 -1
  362. package/build/dist/Types/Permission.js +42 -0
  363. package/build/dist/Types/Permission.js.map +1 -1
  364. package/build/dist/Types/Port.js +15 -5
  365. package/build/dist/Types/Port.js.map +1 -1
  366. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
  367. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  368. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
  369. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
  370. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
  371. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
  372. package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
  373. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  374. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
  375. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  376. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
  377. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
  378. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
  379. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
  380. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  381. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  382. package/build/dist/Utils/TeamMembersByProject.js +145 -0
  383. package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
  384. package/package.json +1 -1
@@ -0,0 +1,320 @@
1
+ import {
2
+ IoTAlertTemplate,
3
+ IoTAlertTemplateArgs,
4
+ IoTAlertTemplateCategory,
5
+ getAllIoTAlertTemplates,
6
+ getIoTAlertTemplateById,
7
+ getIoTAlertTemplatesByCategory,
8
+ } from "../../../Types/Monitor/IotAlertTemplates";
9
+ import MonitorStep from "../../../Types/Monitor/MonitorStep";
10
+ import MonitorStepIoTMonitor from "../../../Types/Monitor/MonitorStepIoTMonitor";
11
+ import MetricsAggregationType from "../../../Types/Metrics/MetricsAggregationType";
12
+ import {
13
+ FilterType,
14
+ NoDataPolicy,
15
+ } from "../../../Types/Monitor/CriteriaFilter";
16
+ import ObjectID from "../../../Types/ObjectID";
17
+
18
+ /*
19
+ * The IoT templates carry two decisions the container templates don't, and
20
+ * both are the point of this suite:
21
+ *
22
+ * 1. INVERTED thresholds. "Bad" for a device is a value that is too LOW —
23
+ * no heartbeat (iot_device_up < 1), a draining battery (< 20%), a fading
24
+ * radio (< -100 dBm). So the unhealthy criterion is `<` and the healthy
25
+ * one is `>=`, the opposite direction from a CPU/memory ceiling. A
26
+ * copy-paste of a `>`/`<=` pair from the ceiling templates would make the
27
+ * monitor alert exactly when the device is fine. Temperature and CPU are
28
+ * genuine ceilings and keep the `>`/`<=` direction — the mix is what makes
29
+ * pinning each direction per template worthwhile.
30
+ *
31
+ * 2. Per-device grouping. Every template groups by the `device.id` datapoint
32
+ * label (NOT `resource.`-prefixed — it is a datapoint attribute, not a
33
+ * resource attribute) so one incident fires per device rather than one
34
+ * collapsed series for the whole fleet.
35
+ *
36
+ * 3. The Device Offline template — and ONLY it — opts into TreatAsZero so a
37
+ * registered device that goes completely silent (an empty series) folds
38
+ * to 0 and trips `Min(iot_device_up) < 1`. Enabling that on a battery or
39
+ * temperature threshold would false-alarm for any silent device, so the
40
+ * other templates must NOT carry it.
41
+ */
42
+
43
+ interface IoTTemplateCase {
44
+ id: string;
45
+ category: IoTAlertTemplateCategory;
46
+ severity: "Critical" | "Warning";
47
+ metricName: string;
48
+ metricAlias: string;
49
+ aggregation: MetricsAggregationType;
50
+ offlineFilterType: FilterType;
51
+ onlineFilterType: FilterType;
52
+ threshold: number;
53
+ treatNoDataAsZero: boolean;
54
+ }
55
+
56
+ const IOT_TEMPLATES: Array<IoTTemplateCase> = [
57
+ {
58
+ id: "iot-device-offline",
59
+ category: "Availability",
60
+ severity: "Critical",
61
+ metricName: "iot_device_up",
62
+ metricAlias: "device_up",
63
+ // Min: a single down push in the window must win over healthy pushes.
64
+ aggregation: MetricsAggregationType.Min,
65
+ offlineFilterType: FilterType.LessThan,
66
+ onlineFilterType: FilterType.GreaterThanOrEqualTo,
67
+ threshold: 1,
68
+ treatNoDataAsZero: true,
69
+ },
70
+ {
71
+ id: "iot-low-battery",
72
+ category: "Power",
73
+ severity: "Warning",
74
+ metricName: "iot_battery_percent",
75
+ metricAlias: "battery_percent",
76
+ aggregation: MetricsAggregationType.Avg,
77
+ offlineFilterType: FilterType.LessThan,
78
+ onlineFilterType: FilterType.GreaterThanOrEqualTo,
79
+ threshold: 20,
80
+ treatNoDataAsZero: false,
81
+ },
82
+ {
83
+ id: "iot-weak-signal",
84
+ category: "Connectivity",
85
+ severity: "Warning",
86
+ metricName: "iot_signal_strength_dbm",
87
+ metricAlias: "signal_strength",
88
+ aggregation: MetricsAggregationType.Avg,
89
+ offlineFilterType: FilterType.LessThan,
90
+ onlineFilterType: FilterType.GreaterThanOrEqualTo,
91
+ // dBm is negative; -100 is a real threshold, not a placeholder.
92
+ threshold: -100,
93
+ treatNoDataAsZero: false,
94
+ },
95
+ {
96
+ id: "iot-high-temperature",
97
+ category: "Environment",
98
+ severity: "Critical",
99
+ metricName: "iot_temperature_celsius",
100
+ metricAlias: "temperature_celsius",
101
+ // Max: a single hot reading must win over cooler ones in the minute.
102
+ aggregation: MetricsAggregationType.Max,
103
+ offlineFilterType: FilterType.GreaterThan,
104
+ onlineFilterType: FilterType.LessThanOrEqualTo,
105
+ threshold: 70,
106
+ treatNoDataAsZero: false,
107
+ },
108
+ {
109
+ id: "iot-high-cpu",
110
+ category: "System",
111
+ severity: "Warning",
112
+ metricName: "iot_cpu_usage_ratio",
113
+ metricAlias: "cpu_usage",
114
+ aggregation: MetricsAggregationType.Avg,
115
+ offlineFilterType: FilterType.GreaterThan,
116
+ onlineFilterType: FilterType.LessThanOrEqualTo,
117
+ // iot_cpu_usage_ratio is a 0-1 ratio, so 0.9 == 90%.
118
+ threshold: 0.9,
119
+ treatNoDataAsZero: false,
120
+ },
121
+ ];
122
+
123
+ function buildArgs(): IoTAlertTemplateArgs {
124
+ return {
125
+ fleetIdentifier: "field-fleet-a",
126
+ onlineMonitorStatusId: ObjectID.generate(),
127
+ offlineMonitorStatusId: ObjectID.generate(),
128
+ defaultIncidentSeverityId: ObjectID.generate(),
129
+ defaultAlertSeverityId: ObjectID.generate(),
130
+ monitorName: "Test IoT Monitor",
131
+ };
132
+ }
133
+
134
+ function getIoTMonitor(step: MonitorStep): MonitorStepIoTMonitor {
135
+ const iotMonitor: MonitorStepIoTMonitor | undefined = step.data?.iotMonitor;
136
+ if (!iotMonitor) {
137
+ throw new Error("iotMonitor missing from monitor step");
138
+ }
139
+ return iotMonitor;
140
+ }
141
+
142
+ describe("IotAlertTemplates", () => {
143
+ test("every documented template id is registered and the suite is exhaustive", () => {
144
+ const ids: Array<string> = getAllIoTAlertTemplates().map(
145
+ (t: IoTAlertTemplate) => {
146
+ return t.id;
147
+ },
148
+ );
149
+ for (const tc of IOT_TEMPLATES) {
150
+ expect(ids).toContain(tc.id);
151
+ }
152
+ expect(ids.sort()).toEqual(
153
+ IOT_TEMPLATES.map((t: IoTTemplateCase) => {
154
+ return t.id;
155
+ }).sort(),
156
+ );
157
+ });
158
+
159
+ test("every template id is unique", () => {
160
+ const ids: Array<string> = getAllIoTAlertTemplates().map(
161
+ (t: IoTAlertTemplate) => {
162
+ return t.id;
163
+ },
164
+ );
165
+ expect(new Set(ids).size).toBe(ids.length);
166
+ });
167
+
168
+ test("getIoTAlertTemplateById returns undefined for an unknown id", () => {
169
+ expect(getIoTAlertTemplateById("nope")).toBeUndefined();
170
+ });
171
+
172
+ test("getIoTAlertTemplatesByCategory returns only that category and covers the catalog", () => {
173
+ const all: Array<IoTAlertTemplate> = getAllIoTAlertTemplates();
174
+ const categories: Array<IoTAlertTemplateCategory> = [
175
+ "Availability",
176
+ "Power",
177
+ "Connectivity",
178
+ "Environment",
179
+ "System",
180
+ ];
181
+
182
+ let total: number = 0;
183
+ for (const category of categories) {
184
+ const inCategory: Array<IoTAlertTemplate> =
185
+ getIoTAlertTemplatesByCategory(category);
186
+ for (const template of inCategory) {
187
+ expect(template.category).toBe(category);
188
+ }
189
+ total += inCategory.length;
190
+ }
191
+ expect(total).toBe(all.length);
192
+ });
193
+
194
+ test.each(IOT_TEMPLATES)(
195
+ "$id is a $severity $category template with populated copy",
196
+ (tc: IoTTemplateCase) => {
197
+ const template: IoTAlertTemplate | undefined = getIoTAlertTemplateById(
198
+ tc.id,
199
+ );
200
+ expect(template).toBeDefined();
201
+ expect(template!.category).toBe(tc.category);
202
+ expect(template!.severity).toBe(tc.severity);
203
+ expect(template!.name.length).toBeGreaterThan(0);
204
+ expect(template!.description.length).toBeGreaterThan(0);
205
+ },
206
+ );
207
+
208
+ test.each(IOT_TEMPLATES)(
209
+ "$id queries $metricName grouped per device with the intended aggregation",
210
+ (tc: IoTTemplateCase) => {
211
+ const template: IoTAlertTemplate = getIoTAlertTemplateById(tc.id)!;
212
+ const step: MonitorStep = template.getMonitorStep(buildArgs());
213
+ const monitor: MonitorStepIoTMonitor = getIoTMonitor(step);
214
+
215
+ expect(monitor.fleetIdentifier).toBe("field-fleet-a");
216
+
217
+ const queryConfigs: Array<any> = monitor.metricViewConfig
218
+ .queryConfigs as Array<any>;
219
+ expect(queryConfigs).toHaveLength(1);
220
+ expect(monitor.metricViewConfig.formulaConfigs).toHaveLength(0);
221
+
222
+ const queryData: any = queryConfigs[0].metricQueryData;
223
+ expect(queryData.filterData.metricName).toBe(tc.metricName);
224
+ expect(queryData.filterData.aggegationType).toBe(tc.aggregation);
225
+
226
+ /*
227
+ * Decision (2): one incident per device — group by the raw datapoint
228
+ * label, which is NOT resource-prefixed.
229
+ */
230
+ expect(queryData.groupByAttributeKeys).toEqual(["device.id"]);
231
+ expect(queryData.groupByAttributeKeys[0].startsWith("resource.")).toBe(
232
+ false,
233
+ );
234
+ },
235
+ );
236
+
237
+ test.each(IOT_TEMPLATES)(
238
+ "$id unhealthy/healthy criteria partition the range at $threshold",
239
+ (tc: IoTTemplateCase) => {
240
+ const template: IoTAlertTemplate = getIoTAlertTemplateById(tc.id)!;
241
+ const step: MonitorStep = template.getMonitorStep(buildArgs());
242
+
243
+ const instances: Array<any> = step.data?.monitorCriteria.data
244
+ ?.monitorCriteriaInstanceArray as Array<any>;
245
+ expect(instances).toHaveLength(2);
246
+ const [offline, online] = instances;
247
+
248
+ const offlineFilter: any = offline.data.filters[0];
249
+ const onlineFilter: any = online.data.filters[0];
250
+
251
+ expect(offlineFilter.metricMonitorOptions.metricAlias).toBe(
252
+ tc.metricAlias,
253
+ );
254
+ expect(onlineFilter.metricMonitorOptions.metricAlias).toBe(
255
+ tc.metricAlias,
256
+ );
257
+ expect(offlineFilter.value).toBe(tc.threshold);
258
+ expect(onlineFilter.value).toBe(tc.threshold);
259
+
260
+ /*
261
+ * Decision (1): pin the exact comparison direction per template — the
262
+ * low-value alerts invert relative to the ceiling alerts.
263
+ */
264
+ expect(offlineFilter.filterType).toBe(tc.offlineFilterType);
265
+ expect(onlineFilter.filterType).toBe(tc.onlineFilterType);
266
+
267
+ expect(offline.data.createIncidents).toBe(true);
268
+ expect(offline.data.createAlerts).toBe(true);
269
+ expect(online.data.createIncidents).toBe(false);
270
+ expect(online.data.createAlerts).toBe(false);
271
+ },
272
+ );
273
+
274
+ test.each(IOT_TEMPLATES)(
275
+ "$id applies TreatAsZero only when it is the Device Offline template",
276
+ (tc: IoTTemplateCase) => {
277
+ const template: IoTAlertTemplate = getIoTAlertTemplateById(tc.id)!;
278
+ const step: MonitorStep = template.getMonitorStep(buildArgs());
279
+
280
+ const offline: any = (
281
+ step.data?.monitorCriteria.data
282
+ ?.monitorCriteriaInstanceArray as Array<any>
283
+ )[0];
284
+ const online: any = (
285
+ step.data?.monitorCriteria.data
286
+ ?.monitorCriteriaInstanceArray as Array<any>
287
+ )[1];
288
+
289
+ const offlineNoDataPolicy: NoDataPolicy | undefined =
290
+ offline.data.filters[0].metricMonitorOptions.onNoDataPolicy;
291
+
292
+ if (tc.treatNoDataAsZero) {
293
+ // Decision (3): a silent registered device must fold to 0 and alert.
294
+ expect(offlineNoDataPolicy).toBe(NoDataPolicy.TreatAsZero);
295
+ } else {
296
+ // Everything else leaves no-data untouched to avoid false alarms.
297
+ expect(offlineNoDataPolicy).toBeUndefined();
298
+ }
299
+
300
+ // The healthy criterion never overrides the no-data policy.
301
+ expect(
302
+ online.data.filters[0].metricMonitorOptions.onNoDataPolicy,
303
+ ).toBeUndefined();
304
+ },
305
+ );
306
+
307
+ test("exactly one template opts into TreatAsZero", () => {
308
+ /*
309
+ * Guards decision (3) at the catalog level: if a second template ever
310
+ * enables TreatAsZero, this fails and forces a deliberate review.
311
+ */
312
+ const withTreatAsZero: Array<IoTTemplateCase> = IOT_TEMPLATES.filter(
313
+ (tc: IoTTemplateCase) => {
314
+ return tc.treatNoDataAsZero;
315
+ },
316
+ );
317
+ expect(withTreatAsZero).toHaveLength(1);
318
+ expect(withTreatAsZero[0]!.id).toBe("iot-device-offline");
319
+ });
320
+ });
@@ -0,0 +1,271 @@
1
+ import {
2
+ PodmanAlertTemplate,
3
+ PodmanAlertTemplateArgs,
4
+ PodmanAlertTemplateCategory,
5
+ getAllPodmanAlertTemplates,
6
+ getPodmanAlertTemplateById,
7
+ getPodmanAlertTemplatesByCategory,
8
+ } from "../../../Types/Monitor/PodmanAlertTemplates";
9
+ import MonitorStep from "../../../Types/Monitor/MonitorStep";
10
+ import MonitorStepPodmanMonitor from "../../../Types/Monitor/MonitorStepPodmanMonitor";
11
+ import MetricsAggregationType from "../../../Types/Metrics/MetricsAggregationType";
12
+ import RollingTime from "../../../Types/RollingTime/RollingTime";
13
+ import { FilterType } from "../../../Types/Monitor/CriteriaFilter";
14
+ import ObjectID from "../../../Types/ObjectID";
15
+
16
+ /*
17
+ * Podman containers emit the same OTel container.* metrics as Docker, so the
18
+ * Podman templates mirror the Docker set. These tests lock in the same three
19
+ * regression-prone decisions (metric name, per-minute aggregation, and the
20
+ * unhealthy/healthy range partition) independently, so a divergence between
21
+ * the two catalogs — a renamed metric or a flipped comparison copied from one
22
+ * into the other — is caught here rather than in production.
23
+ */
24
+
25
+ interface PodmanTemplateCase {
26
+ id: string;
27
+ category: PodmanAlertTemplateCategory;
28
+ severity: "Critical" | "Warning";
29
+ metricName: string;
30
+ metricAlias: string;
31
+ aggregation: MetricsAggregationType;
32
+ rollingTime: RollingTime;
33
+ offlineFilterType: FilterType;
34
+ onlineFilterType: FilterType;
35
+ threshold: number;
36
+ }
37
+
38
+ const PODMAN_TEMPLATES: Array<PodmanTemplateCase> = [
39
+ {
40
+ id: "podman-high-cpu",
41
+ category: "Resource",
42
+ severity: "Warning",
43
+ metricName: "container.cpu.utilization",
44
+ metricAlias: "container_cpu",
45
+ aggregation: MetricsAggregationType.Max,
46
+ rollingTime: RollingTime.Past5Minutes,
47
+ offlineFilterType: FilterType.GreaterThan,
48
+ onlineFilterType: FilterType.LessThanOrEqualTo,
49
+ threshold: 80,
50
+ },
51
+ {
52
+ id: "podman-high-memory",
53
+ category: "Resource",
54
+ severity: "Warning",
55
+ metricName: "container.memory.percent",
56
+ metricAlias: "container_memory",
57
+ aggregation: MetricsAggregationType.Max,
58
+ rollingTime: RollingTime.Past5Minutes,
59
+ offlineFilterType: FilterType.GreaterThan,
60
+ onlineFilterType: FilterType.LessThanOrEqualTo,
61
+ threshold: 85,
62
+ },
63
+ {
64
+ id: "podman-restart-loop",
65
+ category: "Container",
66
+ severity: "Critical",
67
+ metricName: "container.restarts",
68
+ metricAlias: "container_restarts",
69
+ aggregation: MetricsAggregationType.Max,
70
+ rollingTime: RollingTime.Past5Minutes,
71
+ offlineFilterType: FilterType.GreaterThan,
72
+ onlineFilterType: FilterType.LessThanOrEqualTo,
73
+ threshold: 5,
74
+ },
75
+ {
76
+ id: "podman-cpu-throttling",
77
+ category: "Resource",
78
+ severity: "Warning",
79
+ metricName: "container.cpu.throttling_data.throttled_time",
80
+ metricAlias: "cpu_throttled",
81
+ aggregation: MetricsAggregationType.Max,
82
+ rollingTime: RollingTime.Past5Minutes,
83
+ // throttled_time is non-negative: (> 0) unhealthy, (= 0) healthy partitions it.
84
+ offlineFilterType: FilterType.GreaterThan,
85
+ onlineFilterType: FilterType.EqualTo,
86
+ threshold: 0,
87
+ },
88
+ {
89
+ id: "podman-high-pids",
90
+ category: "Container",
91
+ severity: "Warning",
92
+ metricName: "container.pids.count",
93
+ metricAlias: "pids_count",
94
+ aggregation: MetricsAggregationType.Max,
95
+ rollingTime: RollingTime.Past5Minutes,
96
+ offlineFilterType: FilterType.GreaterThan,
97
+ onlineFilterType: FilterType.LessThanOrEqualTo,
98
+ threshold: 500,
99
+ },
100
+ {
101
+ id: "podman-container-down",
102
+ category: "Container",
103
+ severity: "Critical",
104
+ metricName: "container.uptime",
105
+ metricAlias: "container_uptime",
106
+ aggregation: MetricsAggregationType.Min,
107
+ rollingTime: RollingTime.Past1Minute,
108
+ // uptime is non-negative: (= 0) unhealthy, (> 0) healthy partitions it.
109
+ offlineFilterType: FilterType.EqualTo,
110
+ onlineFilterType: FilterType.GreaterThan,
111
+ threshold: 0,
112
+ },
113
+ ];
114
+
115
+ function buildArgs(): PodmanAlertTemplateArgs {
116
+ return {
117
+ hostIdentifier: "podman-host-01",
118
+ onlineMonitorStatusId: ObjectID.generate(),
119
+ offlineMonitorStatusId: ObjectID.generate(),
120
+ defaultIncidentSeverityId: ObjectID.generate(),
121
+ defaultAlertSeverityId: ObjectID.generate(),
122
+ monitorName: "Test Podman Monitor",
123
+ };
124
+ }
125
+
126
+ function getPodmanMonitor(step: MonitorStep): MonitorStepPodmanMonitor {
127
+ const podmanMonitor: MonitorStepPodmanMonitor | undefined =
128
+ step.data?.podmanMonitor;
129
+ if (!podmanMonitor) {
130
+ throw new Error("podmanMonitor missing from monitor step");
131
+ }
132
+ return podmanMonitor;
133
+ }
134
+
135
+ describe("PodmanAlertTemplates", () => {
136
+ test("every documented template id is registered and the suite is exhaustive", () => {
137
+ const ids: Array<string> = getAllPodmanAlertTemplates().map(
138
+ (t: PodmanAlertTemplate) => {
139
+ return t.id;
140
+ },
141
+ );
142
+ for (const tc of PODMAN_TEMPLATES) {
143
+ expect(ids).toContain(tc.id);
144
+ }
145
+ expect(ids.sort()).toEqual(
146
+ PODMAN_TEMPLATES.map((t: PodmanTemplateCase) => {
147
+ return t.id;
148
+ }).sort(),
149
+ );
150
+ });
151
+
152
+ test("every template id is unique", () => {
153
+ const ids: Array<string> = getAllPodmanAlertTemplates().map(
154
+ (t: PodmanAlertTemplate) => {
155
+ return t.id;
156
+ },
157
+ );
158
+ expect(new Set(ids).size).toBe(ids.length);
159
+ });
160
+
161
+ test("getPodmanAlertTemplateById returns undefined for an unknown id", () => {
162
+ expect(getPodmanAlertTemplateById("does-not-exist")).toBeUndefined();
163
+ });
164
+
165
+ test("getPodmanAlertTemplatesByCategory partitions the catalog by category", () => {
166
+ const all: Array<PodmanAlertTemplate> = getAllPodmanAlertTemplates();
167
+ const categories: Array<PodmanAlertTemplateCategory> = [
168
+ "Container",
169
+ "Resource",
170
+ "Host",
171
+ ];
172
+
173
+ let total: number = 0;
174
+ for (const category of categories) {
175
+ const inCategory: Array<PodmanAlertTemplate> =
176
+ getPodmanAlertTemplatesByCategory(category);
177
+ for (const template of inCategory) {
178
+ expect(template.category).toBe(category);
179
+ }
180
+ total += inCategory.length;
181
+ }
182
+ expect(total).toBe(all.length);
183
+ });
184
+
185
+ test.each(PODMAN_TEMPLATES)(
186
+ "$id is a $severity $category template with populated copy",
187
+ (tc: PodmanTemplateCase) => {
188
+ const template: PodmanAlertTemplate | undefined =
189
+ getPodmanAlertTemplateById(tc.id);
190
+ expect(template).toBeDefined();
191
+ expect(template!.category).toBe(tc.category);
192
+ expect(template!.severity).toBe(tc.severity);
193
+ expect(template!.name.length).toBeGreaterThan(0);
194
+ expect(template!.description.length).toBeGreaterThan(0);
195
+ },
196
+ );
197
+
198
+ test.each(PODMAN_TEMPLATES)(
199
+ "$id queries $metricName with the intended aggregation and window",
200
+ (tc: PodmanTemplateCase) => {
201
+ const template: PodmanAlertTemplate = getPodmanAlertTemplateById(tc.id)!;
202
+ const step: MonitorStep = template.getMonitorStep(buildArgs());
203
+ const monitor: MonitorStepPodmanMonitor = getPodmanMonitor(step);
204
+
205
+ expect(monitor.hostIdentifier).toBe("podman-host-01");
206
+
207
+ const queryConfigs: Array<any> = monitor.metricViewConfig
208
+ .queryConfigs as Array<any>;
209
+ expect(queryConfigs).toHaveLength(1);
210
+ expect(monitor.metricViewConfig.formulaConfigs).toHaveLength(0);
211
+
212
+ const filterData: any = queryConfigs[0].metricQueryData.filterData;
213
+ expect(filterData.metricName).toBe(tc.metricName);
214
+ expect(filterData.aggegationType).toBe(tc.aggregation);
215
+ expect(monitor.rollingTime).toBe(tc.rollingTime);
216
+ },
217
+ );
218
+
219
+ test.each(PODMAN_TEMPLATES)(
220
+ "$id unhealthy/healthy criteria partition the range at $threshold",
221
+ (tc: PodmanTemplateCase) => {
222
+ const template: PodmanAlertTemplate = getPodmanAlertTemplateById(tc.id)!;
223
+ const step: MonitorStep = template.getMonitorStep(buildArgs());
224
+
225
+ const instances: Array<any> = step.data?.monitorCriteria.data
226
+ ?.monitorCriteriaInstanceArray as Array<any>;
227
+ expect(instances).toHaveLength(2);
228
+ const [offline, online] = instances;
229
+
230
+ const offlineFilter: any = offline.data.filters[0];
231
+ const onlineFilter: any = online.data.filters[0];
232
+
233
+ expect(offlineFilter.metricMonitorOptions.metricAlias).toBe(
234
+ tc.metricAlias,
235
+ );
236
+ expect(onlineFilter.metricMonitorOptions.metricAlias).toBe(
237
+ tc.metricAlias,
238
+ );
239
+ expect(offlineFilter.value).toBe(tc.threshold);
240
+ expect(onlineFilter.value).toBe(tc.threshold);
241
+
242
+ expect(offlineFilter.filterType).toBe(tc.offlineFilterType);
243
+ expect(onlineFilter.filterType).toBe(tc.onlineFilterType);
244
+
245
+ expect(offline.data.createIncidents).toBe(true);
246
+ expect(offline.data.createAlerts).toBe(true);
247
+ expect(offline.data.incidents).toHaveLength(1);
248
+ expect(offline.data.alerts).toHaveLength(1);
249
+ expect(offline.data.monitorStatusId).toBeDefined();
250
+
251
+ expect(online.data.createIncidents).toBe(false);
252
+ expect(online.data.createAlerts).toBe(false);
253
+ expect(online.data.incidents).toHaveLength(0);
254
+ expect(online.data.alerts).toHaveLength(0);
255
+ },
256
+ );
257
+
258
+ test("incidents auto-resolve so a recovered container clears itself", () => {
259
+ for (const tc of PODMAN_TEMPLATES) {
260
+ const step: MonitorStep = getPodmanAlertTemplateById(
261
+ tc.id,
262
+ )!.getMonitorStep(buildArgs());
263
+ const offline: any = (
264
+ step.data?.monitorCriteria.data
265
+ ?.monitorCriteriaInstanceArray as Array<any>
266
+ )[0];
267
+ expect(offline.data.incidents[0].autoResolveIncident).toBe(true);
268
+ expect(offline.data.alerts[0].autoResolveAlert).toBe(true);
269
+ }
270
+ });
271
+ });