@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
@@ -5,22 +5,159 @@ import MetricType from "../../../Models/DatabaseModels/MetricType";
5
5
  import MetricTypeService from "../../Services/MetricTypeService";
6
6
  import Service from "../../../Models/DatabaseModels/Service";
7
7
  import Dictionary from "../../../Types/Dictionary";
8
+ import GlobalCache from "../../Infrastructure/GlobalCache";
9
+ import logger from "../Logger";
10
+ import crypto from "crypto";
8
11
 
9
12
  export type AttributeType = string | number | boolean | null;
10
13
 
14
+ /*
15
+ * Shares the namespace used by the OTLP maintenance fences so the metric
16
+ * catalog is throttled by the same machinery, with the same TTL, as every
17
+ * other per-batch reconcile on the ingest path.
18
+ */
19
+ const METRIC_TYPE_FENCE_NAMESPACE: string = "otel-maintenance-fence";
20
+ const METRIC_TYPE_FENCE_TTL_SECONDS: number = 5 * 60; // 5 minutes
21
+
11
22
  export default class TelemetryUtil {
23
+ /*
24
+ * Reconcile the metric catalog: one MetricType row per (project, metric
25
+ * name), carrying its description/unit/counter-semantics and the set of
26
+ * services that emit it.
27
+ *
28
+ * This runs on the ingest hot path, once per distinct metric name per batch,
29
+ * and it used to be the only per-batch Postgres writer with no fence, no
30
+ * cache and no dedup of any kind. It had two separate defects that together
31
+ * made it write on EVERY batch, forever:
32
+ *
33
+ * 1. Rows are created with `description || ""`, so the stored value is `""`
34
+ * whenever OTLP omits the field — but the INCOMING value is `undefined`
35
+ * for the same case (protobuf omits unset scalars). `"" !== undefined`
36
+ * is true, so the "has anything changed?" check answered yes on every
37
+ * batch for every metric without a description or unit. The counter
38
+ * semantics below already had the correct `!== undefined` guard;
39
+ * description and unit were simply left behind.
40
+ * 2. The write shipped the whole `services` array, which routes through
41
+ * `save()` — a multi-round-trip transaction holding the row's write lock
42
+ * — and deletes any association not present in the current batch.
43
+ *
44
+ * Both are fixed here. Absent now means "unknown", not "clear", and the two
45
+ * kinds of change are tracked separately so a scalar edit takes a single
46
+ * auto-committed UPDATE and an association takes an additive insert.
47
+ *
48
+ * A fence in front of the whole thing removes the SELECT as well as the
49
+ * write in the steady state. The fence key includes the batch's service set:
50
+ * `servicesInMap` holds only the services in THIS batch, so keying on the
51
+ * name alone would let a batch from service A satisfy the fence and suppress
52
+ * the association that a concurrent batch from service B still needs.
53
+ */
12
54
  @CaptureSpan()
13
55
  public static async indexMetricNameServiceNameMap(data: {
14
56
  projectId: ObjectID;
15
57
  metricNameServiceNameMap: Dictionary<MetricType>;
16
58
  }): Promise<void> {
17
59
  for (const metricName of Object.keys(data.metricNameServiceNameMap)) {
18
- // fetch metric
19
- const metricType: MetricType | null = await MetricTypeService.findOneBy({
20
- query: {
60
+ const metricTypeInMap: MetricType =
61
+ data.metricNameServiceNameMap[metricName]!;
62
+
63
+ // check if services are same as the ones in the map
64
+ const servicesInMap: Array<ObjectID> =
65
+ metricTypeInMap?.services?.map((service: Service) => {
66
+ return service.id!;
67
+ }) || [];
68
+
69
+ const fenceKey: string = TelemetryUtil.buildMetricTypeFenceKey({
70
+ projectId: data.projectId,
71
+ metricName: metricName,
72
+ serviceIds: servicesInMap,
73
+ });
74
+
75
+ if (
76
+ !(await TelemetryUtil.shouldIndexMetricType(
77
+ fenceKey,
78
+ TelemetryUtil.fingerprintMetricTypeScalars(metricTypeInMap),
79
+ ))
80
+ ) {
81
+ continue;
82
+ }
83
+
84
+ try {
85
+ await TelemetryUtil.reconcileMetricType({
21
86
  projectId: data.projectId,
22
- name: metricName,
23
- },
87
+ metricName: metricName,
88
+ metricTypeInMap: metricTypeInMap,
89
+ servicesInMap: servicesInMap,
90
+ });
91
+ } catch (err) {
92
+ /*
93
+ * Per-metric isolation. This loop used to let any failure escape and
94
+ * abort indexing for every REMAINING metric name in the batch — and
95
+ * the only caller just logs, so a single bad row silently stopped the
96
+ * whole catalog from converging.
97
+ */
98
+ await TelemetryUtil.clearMetricTypeFence(fenceKey);
99
+
100
+ logger.warn(
101
+ `TelemetryUtil.indexMetricNameServiceNameMap: failed to reconcile metric "${metricName}": ${
102
+ err instanceof Error ? err.message : String(err)
103
+ }`,
104
+ );
105
+ }
106
+ }
107
+ }
108
+
109
+ private static async reconcileMetricType(data: {
110
+ projectId: ObjectID;
111
+ metricName: string;
112
+ metricTypeInMap: MetricType;
113
+ servicesInMap: Array<ObjectID>;
114
+ }): Promise<void> {
115
+ const { projectId, metricName, metricTypeInMap, servicesInMap } = data;
116
+
117
+ const metricType: MetricType | null = await MetricTypeService.findOneBy({
118
+ query: {
119
+ projectId: projectId,
120
+ name: metricName,
121
+ },
122
+ select: {
123
+ services: true,
124
+ name: true,
125
+ description: true,
126
+ unit: true,
127
+ isMonotonic: true,
128
+ aggregationTemporality: true,
129
+ },
130
+ props: {
131
+ isRoot: true,
132
+ },
133
+ });
134
+
135
+ if (metricType) {
136
+ await TelemetryUtil.updateExistingMetricType({
137
+ metricType,
138
+ metricTypeInMap,
139
+ servicesInMap,
140
+ });
141
+ return;
142
+ }
143
+
144
+ try {
145
+ await TelemetryUtil.createMetricType({
146
+ projectId,
147
+ metricName,
148
+ metricTypeInMap,
149
+ servicesInMap,
150
+ });
151
+ } catch (err) {
152
+ /*
153
+ * Uniqueness on (projectId, name) is enforced in application code by a
154
+ * countBy, not by a database constraint, so two workers can both decide
155
+ * to create the same metric. Re-find to disambiguate: a row now means a
156
+ * concurrent worker won (harmless — reconcile against it); no row means
157
+ * the insert itself was invalid and must surface.
158
+ */
159
+ const winner: MetricType | null = await MetricTypeService.findOneBy({
160
+ query: { projectId: projectId, name: metricName },
24
161
  select: {
25
162
  services: true,
26
163
  name: true,
@@ -29,137 +166,229 @@ export default class TelemetryUtil {
29
166
  isMonotonic: true,
30
167
  aggregationTemporality: true,
31
168
  },
32
- props: {
33
- isRoot: true,
34
- },
169
+ props: { isRoot: true },
35
170
  });
36
171
 
37
- const metricTypeInMap: MetricType =
38
- data.metricNameServiceNameMap[metricName]!;
172
+ if (!winner) {
173
+ throw err;
174
+ }
39
175
 
40
- // check if services are same as the ones in the map
41
- const servicesInMap: Array<ObjectID> =
42
- metricTypeInMap?.services?.map((service: Service) => {
43
- return service.id!;
44
- }) || [];
176
+ logger.debug(
177
+ `TelemetryUtil: create raced for metric "${metricName}" (concurrent insert); reconciling against the winning row.`,
178
+ );
45
179
 
46
- if (metricType) {
47
- if (!metricType.services) {
48
- metricType.services = [];
49
- }
180
+ await TelemetryUtil.updateExistingMetricType({
181
+ metricType: winner,
182
+ metricTypeInMap,
183
+ servicesInMap,
184
+ });
185
+ }
186
+ }
50
187
 
51
- const serviceIds: Array<ObjectID> = metricType.services!.map(
52
- (service: Service) => {
53
- return service.id!;
54
- },
55
- );
188
+ private static async updateExistingMetricType(data: {
189
+ metricType: MetricType;
190
+ metricTypeInMap: MetricType;
191
+ servicesInMap: Array<ObjectID>;
192
+ }): Promise<void> {
193
+ const { metricType, metricTypeInMap, servicesInMap } = data;
56
194
 
57
- let isSame: boolean = true;
195
+ if (!metricType.services) {
196
+ metricType.services = [];
197
+ }
58
198
 
59
- // check if description is same
60
- if (metricType.description !== metricTypeInMap.description) {
61
- isSame = false;
62
- metricType.description = metricTypeInMap.description || "";
63
- }
199
+ const existingServiceIds: Set<string> = new Set<string>(
200
+ metricType.services.map((service: Service) => {
201
+ return service.id!.toString();
202
+ }),
203
+ );
204
+
205
+ /*
206
+ * Two flags, not one. A scalar edit and a new association need completely
207
+ * different statements, and conflating them is what forced every scalar
208
+ * edit through the relation-writing `save()` path.
209
+ */
210
+ let scalarsChanged: boolean = false;
211
+
212
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
213
+ const scalarUpdate: any = {};
214
+
215
+ /*
216
+ * An absent incoming value means "this producer didn't say", not "clear
217
+ * the stored value" — the same rule the counter-semantics block below has
218
+ * always used. Without the truthiness guard, a stored `""` compared
219
+ * against an incoming `undefined` reported a change on every single batch.
220
+ */
221
+ if (
222
+ metricTypeInMap.description &&
223
+ (metricType.description ?? "") !== metricTypeInMap.description
224
+ ) {
225
+ scalarsChanged = true;
226
+ scalarUpdate.description = metricTypeInMap.description;
227
+ }
64
228
 
65
- // check if unit is same
66
- if (metricType.unit !== metricTypeInMap.unit) {
67
- isSame = false;
68
- metricType.unit = metricTypeInMap.unit || "";
69
- }
229
+ if (
230
+ metricTypeInMap.unit &&
231
+ (metricType.unit ?? "") !== metricTypeInMap.unit
232
+ ) {
233
+ scalarsChanged = true;
234
+ scalarUpdate.unit = metricTypeInMap.unit;
235
+ }
70
236
 
71
- /*
72
- * Counter semantics (isMonotonic / aggregationTemporality) are only
73
- * known on OTel metric ingest paths — other callers (monitor
74
- * metrics, heartbeat metrics) never set them, so an undefined
75
- * incoming value means "unknown", not "clear the stored value".
76
- */
77
- if (
78
- metricTypeInMap.isMonotonic !== undefined &&
79
- metricType.isMonotonic !== metricTypeInMap.isMonotonic
80
- ) {
81
- isSame = false;
82
- metricType.isMonotonic = metricTypeInMap.isMonotonic;
83
- }
237
+ /*
238
+ * Counter semantics (isMonotonic / aggregationTemporality) are only
239
+ * known on OTel metric ingest paths — other callers (monitor
240
+ * metrics, heartbeat metrics) never set them, so an undefined
241
+ * incoming value means "unknown", not "clear the stored value".
242
+ */
243
+ if (
244
+ metricTypeInMap.isMonotonic !== undefined &&
245
+ metricType.isMonotonic !== metricTypeInMap.isMonotonic
246
+ ) {
247
+ scalarsChanged = true;
248
+ scalarUpdate.isMonotonic = metricTypeInMap.isMonotonic;
249
+ }
84
250
 
85
- if (
86
- metricTypeInMap.aggregationTemporality !== undefined &&
87
- metricType.aggregationTemporality !==
88
- metricTypeInMap.aggregationTemporality
89
- ) {
90
- isSame = false;
91
- metricType.aggregationTemporality =
92
- metricTypeInMap.aggregationTemporality;
93
- }
251
+ if (
252
+ metricTypeInMap.aggregationTemporality !== undefined &&
253
+ metricType.aggregationTemporality !==
254
+ metricTypeInMap.aggregationTemporality
255
+ ) {
256
+ scalarsChanged = true;
257
+ scalarUpdate.aggregationTemporality =
258
+ metricTypeInMap.aggregationTemporality;
259
+ }
94
260
 
95
- // check if services are same
96
-
97
- for (const serviceId of servicesInMap) {
98
- if (
99
- serviceIds.filter((existingServiceId: ObjectID) => {
100
- return existingServiceId.toString() === serviceId.toString();
101
- }).length === 0
102
- ) {
103
- isSame = false;
104
- // add the service id to the list
105
- const service: Service = new Service();
106
- service.id = serviceId;
107
- metricType.services!.push(service);
108
- }
109
- }
261
+ const servicesToAttach: Array<ObjectID> = servicesInMap.filter(
262
+ (serviceId: ObjectID) => {
263
+ return !existingServiceIds.has(serviceId.toString());
264
+ },
265
+ );
266
+
267
+ if (scalarsChanged) {
268
+ /*
269
+ * One auto-committed UPDATE. Deliberately NOT updateOneById: with
270
+ * `services` absent from the payload that path still opens a transaction
271
+ * and bumps `version`, and MetricType declares no update workflow, no
272
+ * realtime events and no audit log, so the full pipeline buys nothing
273
+ * here. Nothing in the codebase uses TypeORM optimistic locking, so the
274
+ * skipped version bump has no consumer.
275
+ */
276
+ await MetricTypeService.updateColumnsByIdWithoutHooks({
277
+ id: metricType.id!,
278
+ data: scalarUpdate,
279
+ });
280
+ }
110
281
 
111
- // if its not the same then update the metric type
112
-
113
- if (!isSame) {
114
- // update metric type
115
- await MetricTypeService.updateOneById({
116
- id: metricType.id!,
117
- data: {
118
- services: metricType.services || [],
119
- description: metricTypeInMap.description || "",
120
- unit: metricTypeInMap.unit || "",
121
- ...(metricType.isMonotonic !== undefined
122
- ? { isMonotonic: metricType.isMonotonic }
123
- : {}),
124
- ...(metricType.aggregationTemporality !== undefined
125
- ? { aggregationTemporality: metricType.aggregationTemporality }
126
- : {}),
127
- },
128
- props: {
129
- isRoot: true,
130
- },
131
- } as any);
132
- }
133
- } else {
134
- // create metric type
135
- const metricType: MetricType = new MetricType();
136
- metricType.name = metricName;
137
- metricType.description = metricTypeInMap.description || "";
138
- metricType.unit = metricTypeInMap.unit || "";
139
- if (metricTypeInMap.isMonotonic !== undefined) {
140
- metricType.isMonotonic = metricTypeInMap.isMonotonic;
141
- }
142
- if (metricTypeInMap.aggregationTemporality !== undefined) {
143
- metricType.aggregationTemporality =
144
- metricTypeInMap.aggregationTemporality;
145
- }
146
- metricType.projectId = data.projectId;
147
- metricType.services = [];
282
+ if (servicesToAttach.length > 0) {
283
+ await MetricTypeService.attachServices({
284
+ metricTypeId: metricType.id!,
285
+ serviceIds: servicesToAttach,
286
+ });
287
+ }
288
+ }
148
289
 
149
- for (const serviceId of servicesInMap) {
150
- const service: Service = new Service();
151
- service.id = serviceId;
152
- metricType.services!.push(service);
153
- }
290
+ private static async createMetricType(data: {
291
+ projectId: ObjectID;
292
+ metricName: string;
293
+ metricTypeInMap: MetricType;
294
+ servicesInMap: Array<ObjectID>;
295
+ }): Promise<void> {
296
+ const metricType: MetricType = new MetricType();
297
+ metricType.name = data.metricName;
298
+ metricType.description = data.metricTypeInMap.description || "";
299
+ metricType.unit = data.metricTypeInMap.unit || "";
300
+ if (data.metricTypeInMap.isMonotonic !== undefined) {
301
+ metricType.isMonotonic = data.metricTypeInMap.isMonotonic;
302
+ }
303
+ if (data.metricTypeInMap.aggregationTemporality !== undefined) {
304
+ metricType.aggregationTemporality =
305
+ data.metricTypeInMap.aggregationTemporality;
306
+ }
307
+ metricType.projectId = data.projectId;
308
+ metricType.services = [];
154
309
 
155
- // save metric type
156
- await MetricTypeService.create({
157
- data: metricType,
158
- props: {
159
- isRoot: true,
160
- },
161
- });
162
- }
310
+ for (const serviceId of data.servicesInMap) {
311
+ const service: Service = new Service();
312
+ service.id = serviceId;
313
+ metricType.services!.push(service);
314
+ }
315
+
316
+ /*
317
+ * Creates stay on the ORM path: a raw INSERT would bypass tenant stamping,
318
+ * slug generation and the uniqueness check. Creates are also rare — this
319
+ * runs once per metric name in a project's lifetime.
320
+ */
321
+ await MetricTypeService.create({
322
+ data: metricType,
323
+ props: {
324
+ isRoot: true,
325
+ },
326
+ });
327
+ }
328
+
329
+ private static buildMetricTypeFenceKey(data: {
330
+ projectId: ObjectID;
331
+ metricName: string;
332
+ serviceIds: Array<ObjectID>;
333
+ }): string {
334
+ const services: string = data.serviceIds
335
+ .map((serviceId: ObjectID) => {
336
+ return serviceId.toString();
337
+ })
338
+ .sort()
339
+ .join(",");
340
+
341
+ return `metric-type:${data.projectId.toString()}:${
342
+ data.metricName
343
+ }:${crypto.createHash("sha1").update(services).digest("hex")}`;
344
+ }
345
+
346
+ private static fingerprintMetricTypeScalars(metricType: MetricType): string {
347
+ return crypto
348
+ .createHash("sha1")
349
+ .update(
350
+ JSON.stringify({
351
+ description: metricType.description ?? null,
352
+ unit: metricType.unit ?? null,
353
+ isMonotonic: metricType.isMonotonic ?? null,
354
+ aggregationTemporality: metricType.aggregationTemporality ?? null,
355
+ }),
356
+ )
357
+ .digest("hex");
358
+ }
359
+
360
+ /*
361
+ * Compare-and-claim rather than plain set-if-absent: a metric whose
362
+ * description or unit genuinely changed must be written promptly, not wait
363
+ * out the window. Atomic, so concurrent workers cannot all pass the gate the
364
+ * way a read-then-write lets them.
365
+ */
366
+ private static async shouldIndexMetricType(
367
+ fenceKey: string,
368
+ fingerprint: string,
369
+ ): Promise<boolean> {
370
+ try {
371
+ return await GlobalCache.setStringIfChanged(
372
+ METRIC_TYPE_FENCE_NAMESPACE,
373
+ fenceKey,
374
+ fingerprint,
375
+ {
376
+ expiresInSeconds: GlobalCache.withJitter(
377
+ METRIC_TYPE_FENCE_TTL_SECONDS,
378
+ ),
379
+ },
380
+ );
381
+ } catch {
382
+ // If the cache is down, default to running the reconcile.
383
+ return true;
384
+ }
385
+ }
386
+
387
+ private static async clearMetricTypeFence(fenceKey: string): Promise<void> {
388
+ try {
389
+ await GlobalCache.deleteKey(METRIC_TYPE_FENCE_NAMESPACE, fenceKey);
390
+ } catch {
391
+ // Best effort — losing the release costs one window of staleness.
163
392
  }
164
393
  }
165
394