@oneuptime/common 11.7.1 → 11.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/Models/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
  2. package/Models/DatabaseModels/DashboardDomain.ts +1 -0
  3. package/Models/DatabaseModels/Incident.ts +2 -0
  4. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  5. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  6. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  7. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  8. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  9. package/Models/DatabaseModels/Monitor.ts +1 -0
  10. package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
  11. package/Models/DatabaseModels/NetworkSite.ts +1 -0
  12. package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +1 -0
  13. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -0
  14. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +2 -0
  15. package/Models/DatabaseModels/Probe.ts +13 -13
  16. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  17. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  18. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  19. package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
  20. package/Models/DatabaseModels/StatusPage.ts +1 -0
  21. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  22. package/Models/DatabaseModels/StatusPageDomain.ts +1 -0
  23. package/Models/DatabaseModels/TableView.ts +40 -0
  24. package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +1 -0
  25. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +11 -0
  26. package/Server/API/BaseAPI.ts +17 -1
  27. package/Server/API/CommonAPI.ts +32 -0
  28. package/Server/API/MicrosoftTeamsAPI.ts +99 -2
  29. package/Server/API/ProbeAPI.ts +2 -0
  30. package/Server/API/SlackAPI.ts +62 -0
  31. package/Server/API/StatusPageAPI.ts +157 -192
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.ts +89 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.ts +61 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.ts +280 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  37. package/Server/Services/DatabaseService.ts +11 -3
  38. package/Server/Services/MonitorGroupService.ts +145 -0
  39. package/Server/Services/MonitorService.ts +231 -30
  40. package/Server/Services/ProbeService.ts +319 -16
  41. package/Server/Services/StatusPageService.ts +72 -42
  42. package/Server/Services/WorkspaceNotificationRuleService.ts +232 -2
  43. package/Server/Utils/Database/PostgresErrorTranslator.ts +131 -0
  44. package/Server/Utils/Monitor/MonitorResource.ts +88 -35
  45. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +21 -1
  46. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +158 -0
  47. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +706 -6
  48. package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
  49. package/Tests/Models/DatabaseModels/ProbeColumnAccessControl.test.ts +103 -0
  50. package/Tests/Server/API/BaseAPIUpdateNoOp.test.ts +135 -0
  51. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +317 -0
  52. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +228 -0
  53. package/Tests/Server/API/ProbeAPI.test.ts +6 -0
  54. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +167 -0
  55. package/Tests/Server/Services/AddHotQueryIndexesMigration.test.ts +255 -0
  56. package/Tests/Server/Services/AddHotQueryIndexesSecondPassMigration.test.ts +258 -0
  57. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +1 -1
  58. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +121 -0
  59. package/Tests/Server/Services/HeartbeatWriteFastPathSafety.test.ts +200 -0
  60. package/Tests/Server/Services/HookFreeWriteSafetySecondPass.test.ts +181 -0
  61. package/Tests/Server/Services/MonitorGroupServiceBatchedStatuses.test.ts +513 -0
  62. package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +492 -0
  63. package/Tests/Server/Services/MonitorServiceProbeSelection.test.ts +401 -0
  64. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +453 -0
  65. package/Tests/Server/Services/ProbeAuthKeyCacheAndHeartbeat.test.ts +679 -0
  66. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +3 -3
  67. package/Tests/Server/Services/StatusPageDomainResolutionCache.test.ts +245 -0
  68. package/Tests/Server/Services/StatusPageOverviewTimelineWindow.test.ts +283 -0
  69. package/Tests/Server/Services/StatusPageServiceMcp.test.ts +17 -1
  70. package/Tests/Server/Services/StatusPageServiceMonitorGroupBatching.test.ts +331 -0
  71. package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +12 -12
  72. package/Tests/Server/Services/WorkspaceNotificationRuleChats.test.ts +1390 -0
  73. package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +4 -4
  74. package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +2 -2
  75. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +1 -3
  76. package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +1 -3
  77. package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +1 -1
  78. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +5 -5
  79. package/Tests/Server/Utils/Alert/AlertPrivacyFilter.test.ts +187 -0
  80. package/Tests/Server/Utils/AlertEpisode/AlertEpisodePrivacyFilter.test.ts +170 -0
  81. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +125 -0
  82. package/Tests/Server/Utils/Incident/IncidentPrivacyFilter.test.ts +264 -0
  83. package/Tests/Server/Utils/IncidentEpisode/IncidentEpisodePrivacyFilter.test.ts +175 -0
  84. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +181 -0
  85. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +1 -1
  86. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +2 -2
  87. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +341 -0
  88. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +2175 -0
  89. package/Tests/Server/Utils/Workspace/SlackChannelsList.test.ts +585 -0
  90. package/Tests/Types/Billing/MeteredPlan.test.ts +46 -0
  91. package/Tests/Types/JSONFunctions.test.ts +103 -1
  92. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +219 -1
  93. package/Tests/UI/Components/BulkUpdateForm.test.tsx +174 -0
  94. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +315 -0
  95. package/Tests/UI/Components/ModelDetailSelect.test.tsx +229 -0
  96. package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1258 -0
  97. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
  98. package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +572 -0
  99. package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
  100. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
  101. package/Tests/UI/Components/ModelTableExportFromColumns.test.ts +153 -0
  102. package/Tests/UI/Components/MonacoLoader.test.ts +67 -0
  103. package/Tests/UI/Components/MonacoRuntime.test.ts +121 -0
  104. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +212 -3
  105. package/Tests/UI/Utils/Project.test.ts +809 -0
  106. package/Tests/UI/Utils/TableColumnsToCsv.test.ts +360 -0
  107. package/Tests/Utils/Alerts/AlertMetricType.test.ts +143 -0
  108. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +219 -0
  109. package/Tests/Utils/Incident/IncidentMetricType.test.ts +174 -0
  110. package/Tests/Utils/Monitor/MonitorProbeSelectionUtil.test.ts +145 -0
  111. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +263 -0
  112. package/Tests/Utils/UserPreferences.test.ts +563 -0
  113. package/Types/JSONFunctions.ts +7 -2
  114. package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
  115. package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +4 -0
  116. package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +25 -10
  117. package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
  118. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +63 -8
  119. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  120. package/UI/Components/CodeEditor/MonacoLoader.ts +36 -0
  121. package/UI/Components/Forms/ModelForm.tsx +41 -18
  122. package/UI/Components/MasterPage/MasterPage.tsx +65 -0
  123. package/UI/Components/ModelDetail/CardModelDetail.tsx +23 -1
  124. package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
  125. package/UI/Components/ModelTable/BaseModelTable.tsx +687 -34
  126. package/UI/Components/ModelTable/Column.ts +29 -0
  127. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +420 -0
  128. package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
  129. package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
  130. package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
  131. package/UI/Components/ModelTable/TableView.tsx +24 -2
  132. package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
  133. package/UI/Components/Navbar/NavBar.tsx +66 -1
  134. package/UI/Components/SideMenu/SideMenu.tsx +24 -4
  135. package/UI/Components/Table/Table.tsx +66 -14
  136. package/UI/Components/Table/TableRow.tsx +180 -175
  137. package/UI/Components/Table/Types/Column.ts +20 -0
  138. package/UI/Utils/Project.ts +214 -18
  139. package/UI/Utils/TableColumnsToCsv.ts +86 -6
  140. package/UI/esbuild-config.js +34 -0
  141. package/Utils/Monitor/MonitorProbeSelectionUtil.ts +72 -0
  142. package/Utils/UserPreferences.ts +53 -0
  143. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
  144. package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
  145. package/build/dist/Models/DatabaseModels/DashboardDomain.js +1 -0
  146. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  147. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  148. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  149. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  150. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  151. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  152. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  153. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  154. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  155. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  156. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  157. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  158. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  159. package/build/dist/Models/DatabaseModels/Monitor.js +1 -0
  160. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  161. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
  162. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  163. package/build/dist/Models/DatabaseModels/NetworkSite.js +1 -0
  164. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
  165. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +1 -0
  166. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -1
  167. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -0
  168. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  169. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +2 -0
  170. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  171. package/build/dist/Models/DatabaseModels/Probe.js +13 -13
  172. package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
  173. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  174. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  175. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  176. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  177. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  178. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  179. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
  180. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  181. package/build/dist/Models/DatabaseModels/StatusPage.js +1 -0
  182. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  184. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +1 -0
  186. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  187. package/build/dist/Models/DatabaseModels/TableView.js +40 -0
  188. package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
  189. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +1 -0
  190. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -1
  191. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  192. package/build/dist/Server/API/BaseAPI.js +12 -1
  193. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  194. package/build/dist/Server/API/CommonAPI.js +21 -0
  195. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  196. package/build/dist/Server/API/MicrosoftTeamsAPI.js +72 -2
  197. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  198. package/build/dist/Server/API/ProbeAPI.js +2 -0
  199. package/build/dist/Server/API/ProbeAPI.js.map +1 -1
  200. package/build/dist/Server/API/SlackAPI.js +44 -0
  201. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  202. package/build/dist/Server/API/StatusPageAPI.js +98 -152
  203. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  204. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js +36 -0
  205. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js.map +1 -0
  206. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js +26 -0
  207. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js.map +1 -0
  208. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js +254 -0
  209. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js.map +1 -0
  210. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
  211. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
  212. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  213. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  214. package/build/dist/Server/Services/DatabaseService.js +10 -2
  215. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  216. package/build/dist/Server/Services/MonitorGroupService.js +118 -0
  217. package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
  218. package/build/dist/Server/Services/MonitorService.js +198 -24
  219. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  220. package/build/dist/Server/Services/ProbeService.js +238 -9
  221. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  222. package/build/dist/Server/Services/StatusPageService.js +46 -39
  223. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  224. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +178 -3
  225. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  226. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +80 -0
  227. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -0
  228. package/build/dist/Server/Utils/Monitor/MonitorResource.js +67 -29
  229. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  230. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +31 -14
  231. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  232. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +117 -0
  233. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -0
  234. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +549 -7
  235. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  236. package/build/dist/Types/JSONFunctions.js +8 -3
  237. package/build/dist/Types/JSONFunctions.js.map +1 -1
  238. package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
  239. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +17 -7
  240. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
  241. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +37 -11
  242. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  243. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +2 -0
  244. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  245. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +34 -0
  246. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +1 -0
  247. package/build/dist/UI/Components/Forms/ModelForm.js +37 -17
  248. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  249. package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
  250. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  251. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +22 -1
  252. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  253. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +31 -1
  254. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  255. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +472 -27
  256. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  257. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +189 -0
  258. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
  259. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
  260. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
  261. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
  262. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
  263. package/build/dist/UI/Components/ModelTable/ExportFromColumns.js +30 -0
  264. package/build/dist/UI/Components/ModelTable/ExportFromColumns.js.map +1 -0
  265. package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
  266. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  267. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
  268. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
  269. package/build/dist/UI/Components/Navbar/NavBar.js +39 -1
  270. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  271. package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
  272. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  273. package/build/dist/UI/Components/Table/Table.js +46 -13
  274. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  275. package/build/dist/UI/Components/Table/TableRow.js +11 -6
  276. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  277. package/build/dist/UI/Utils/Project.js +161 -15
  278. package/build/dist/UI/Utils/Project.js.map +1 -1
  279. package/build/dist/UI/Utils/TableColumnsToCsv.js +67 -6
  280. package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -1
  281. package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
  282. package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
  283. package/build/dist/Utils/UserPreferences.js +33 -0
  284. package/build/dist/Utils/UserPreferences.js.map +1 -1
  285. package/package.json +4 -3
@@ -31,6 +31,8 @@ import WorkspaceMessagePayload, {
31
31
  WorkspacePayloadMarkdown,
32
32
  } from "../../Types/Workspace/WorkspaceMessagePayload";
33
33
  import WorkspaceProjectAuthToken, {
34
+ MicrosoftTeamsChat,
35
+ MicrosoftTeamsMiscData,
34
36
  MiscData,
35
37
  SlackMiscData,
36
38
  } from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
@@ -445,6 +447,97 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
445
447
  }
446
448
  }
447
449
  }
450
+
451
+ // Post test message to Microsoft Teams chats.
452
+ if (
453
+ rule.workspaceType === WorkspaceType.MicrosoftTeams &&
454
+ notificationRule.shouldPostToExistingChat
455
+ ) {
456
+ const chatIds: Array<string> =
457
+ this.getExistingChatIdsFromNotificationRules({
458
+ notificationRules: [notificationRule],
459
+ });
460
+
461
+ if (chatIds.length === 0) {
462
+ throw new BadDataException(
463
+ "No Microsoft Teams chats are selected in this rule. Please edit the rule and select at least one chat.",
464
+ );
465
+ }
466
+
467
+ const connectedChats: Record<string, MicrosoftTeamsChat> =
468
+ await this.getConnectedMicrosoftTeamsChats({
469
+ projectId: data.projectId,
470
+ });
471
+
472
+ for (const chatId of chatIds) {
473
+ if (!connectedChats[chatId]) {
474
+ throw new BadDataException(
475
+ `The selected chat (id: ${chatId}) is no longer connected to OneUptime. Please add the OneUptime app to the chat in Microsoft Teams and update this rule.`,
476
+ );
477
+ }
478
+ }
479
+
480
+ try {
481
+ const responses: Array<WorkspaceSendMessageResponse> =
482
+ await WorkspaceUtil.postMessageToAllWorkspaceChannelsAsBot({
483
+ projectId: data.projectId,
484
+ messagePayloadsByWorkspace: messageBlocksByWorkspaceTypes.map(
485
+ (messageBlocksByWorkspaceType: MessageBlocksByWorkspaceType) => {
486
+ const payload: WorkspaceMessagePayload = {
487
+ _type: "WorkspaceMessagePayload",
488
+ workspaceType: messageBlocksByWorkspaceType.workspaceType,
489
+ messageBlocks: messageBlocksByWorkspaceType.messageBlocks,
490
+ channelNames: [],
491
+ channelIds: [],
492
+ chatIds: chatIds,
493
+ };
494
+
495
+ return payload;
496
+ },
497
+ ),
498
+ });
499
+
500
+ for (const res of responses) {
501
+ // Check for errors in the response
502
+ if (res.errors && res.errors.length > 0) {
503
+ const errorMessages: Array<string> = res.errors.map(
504
+ (error: { channel: WorkspaceChannel; error: string }) => {
505
+ return `Chat ${error.channel.name}: ${error.error}`;
506
+ },
507
+ );
508
+ throw new BadDataException(
509
+ `Failed to send test message to some chats: ${errorMessages.join(
510
+ "; ",
511
+ )}`,
512
+ );
513
+ }
514
+
515
+ for (const thread of res.threads) {
516
+ const log: WorkspaceNotificationLog =
517
+ new WorkspaceNotificationLog();
518
+ log.projectId = data.projectId;
519
+ log.workspaceType = res.workspaceType;
520
+ log.channelId = thread.channel.id;
521
+ log.channelName = thread.channel.name;
522
+ log.threadId = thread.threadId;
523
+ log.message = `This is a test message for rule **${rule.name?.trim()}**`;
524
+ log.status = WorkspaceNotificationStatus.Success;
525
+ log.statusMessage = "Test message posted to workspace chat";
526
+ log.userId = data.testByUserId;
527
+ log.actionType = WorkspaceNotificationActionType.SendMessage;
528
+
529
+ await WorkspaceNotificationLogService.create({
530
+ data: log,
531
+ props: { isRoot: true },
532
+ });
533
+ }
534
+ }
535
+ } catch (err) {
536
+ throw new BadDataException(
537
+ "Cannot post message to chat. " + (err as Error)?.message,
538
+ );
539
+ }
540
+ }
448
541
  }
449
542
 
450
543
  @CaptureSpan()
@@ -614,6 +707,29 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
614
707
  };
615
708
  workspaceNotificationPaylaods.push(workspaceMessagePayload);
616
709
  }
710
+
711
+ // Microsoft Teams chats (group / personal chats the OneUptime app was added to).
712
+ const existingChatIds: Array<string> =
713
+ await this.getExistingChatIdsBasedOnEventType({
714
+ projectId: data.projectId,
715
+ notificationRuleEventType: this.getNotificationRuleEventType(
716
+ data.notificationFor,
717
+ ),
718
+ workspaceType: messageBlocksByWorkspaceType.workspaceType,
719
+ notificationFor: data.notificationFor,
720
+ });
721
+
722
+ for (const chatId of existingChatIds) {
723
+ const workspaceMessagePayload: WorkspaceMessagePayload = {
724
+ _type: "WorkspaceMessagePayload",
725
+ workspaceType: messageBlocksByWorkspaceType.workspaceType,
726
+ messageBlocks: messageBlocksByWorkspaceType.messageBlocks,
727
+ channelNames: [],
728
+ channelIds: [],
729
+ chatIds: [chatId],
730
+ };
731
+ workspaceNotificationPaylaods.push(workspaceMessagePayload);
732
+ }
617
733
  }
618
734
 
619
735
  const responses: Array<WorkspaceSendMessageResponse> =
@@ -655,8 +771,8 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
655
771
  const log: WorkspaceNotificationLog = new WorkspaceNotificationLog();
656
772
  log.projectId = data.projectId;
657
773
  log.workspaceType = res.workspaceType;
658
- log.channelId = thread.channel.id;
659
- log.channelName = thread.channel.name;
774
+ log.channelId = (thread.channel.id || "").substring(0, 100);
775
+ log.channelName = (thread.channel.name || "").substring(0, 100);
660
776
  log.threadId = thread.threadId;
661
777
  log.message = messageSummary;
662
778
  log.status = WorkspaceNotificationStatus.Success;
@@ -683,6 +799,45 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
683
799
  props: { isRoot: true },
684
800
  });
685
801
  }
802
+
803
+ /*
804
+ * Also log per-destination failures — otherwise a chat or channel
805
+ * send that errors (e.g. the OneUptime app was removed from a chat a
806
+ * rule still references) is invisible in the notification logs.
807
+ */
808
+ for (const sendError of res.errors || []) {
809
+ const log: WorkspaceNotificationLog = new WorkspaceNotificationLog();
810
+ log.projectId = data.projectId;
811
+ log.workspaceType = res.workspaceType;
812
+ log.channelId = (sendError.channel.id || "").substring(0, 100);
813
+ log.channelName = (sendError.channel.name || "").substring(0, 100);
814
+ log.message = messageSummary;
815
+ log.status = WorkspaceNotificationStatus.Error;
816
+ log.actionType = WorkspaceNotificationActionType.SendMessage;
817
+ log.statusMessage = (sendError.error || "Failed to send message")
818
+ .toString()
819
+ .substring(0, 500);
820
+
821
+ if (data.notificationFor.incidentId) {
822
+ log.incidentId = data.notificationFor.incidentId;
823
+ }
824
+ if (data.notificationFor.alertId) {
825
+ log.alertId = data.notificationFor.alertId;
826
+ }
827
+ if (data.notificationFor.scheduledMaintenanceId) {
828
+ log.scheduledMaintenanceId =
829
+ data.notificationFor.scheduledMaintenanceId;
830
+ }
831
+
832
+ if (data.workspaceNotification.notifyUserId) {
833
+ log.userId = data.workspaceNotification.notifyUserId;
834
+ }
835
+
836
+ await WorkspaceNotificationLogService.create({
837
+ data: log,
838
+ props: { isRoot: true },
839
+ });
840
+ }
686
841
  }
687
842
  }
688
843
 
@@ -1951,6 +2106,81 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
1951
2106
  return channels;
1952
2107
  }
1953
2108
 
2109
+ /*
2110
+ * Collects the Microsoft Teams chat ids (group / personal chats) that
2111
+ * matching rules want to post to. Chats are Teams-only destinations.
2112
+ */
2113
+ public getExistingChatIdsFromNotificationRules(data: {
2114
+ notificationRules: Array<BaseNotificationRule>;
2115
+ }): Array<string> {
2116
+ const chatIds: Array<string> = [];
2117
+
2118
+ for (const notificationRule of data.notificationRules) {
2119
+ if (!notificationRule.shouldPostToExistingChat) {
2120
+ continue;
2121
+ }
2122
+
2123
+ for (const chatId of notificationRule.existingChatIds || []) {
2124
+ if (!chatId) {
2125
+ continue;
2126
+ }
2127
+
2128
+ if (!chatIds.includes(chatId)) {
2129
+ chatIds.push(chatId);
2130
+ }
2131
+ }
2132
+ }
2133
+
2134
+ return chatIds;
2135
+ }
2136
+
2137
+ @CaptureSpan()
2138
+ public async getExistingChatIdsBasedOnEventType(data: {
2139
+ projectId: ObjectID;
2140
+ workspaceType: WorkspaceType;
2141
+ notificationRuleEventType: NotificationRuleEventType;
2142
+ notificationFor: NotificationFor;
2143
+ }): Promise<Array<string>> {
2144
+ if (data.workspaceType !== WorkspaceType.MicrosoftTeams) {
2145
+ return []; // chats are only supported for Microsoft Teams.
2146
+ }
2147
+
2148
+ const notificationRules: Array<WorkspaceNotificationRule> =
2149
+ await this.getMatchingNotificationRules({
2150
+ projectId: data.projectId,
2151
+ workspaceType: data.workspaceType,
2152
+ notificationRuleEventType: data.notificationRuleEventType,
2153
+ notificationFor: data.notificationFor,
2154
+ });
2155
+
2156
+ return this.getExistingChatIdsFromNotificationRules({
2157
+ notificationRules: notificationRules.map(
2158
+ (rule: WorkspaceNotificationRule) => {
2159
+ return rule.notificationRule as BaseNotificationRule;
2160
+ },
2161
+ ),
2162
+ });
2163
+ }
2164
+
2165
+ @CaptureSpan()
2166
+ public async getConnectedMicrosoftTeamsChats(data: {
2167
+ projectId: ObjectID;
2168
+ }): Promise<Record<string, MicrosoftTeamsChat>> {
2169
+ const projectAuth: WorkspaceProjectAuthToken | null =
2170
+ await WorkspaceProjectAuthTokenService.getProjectAuth({
2171
+ projectId: data.projectId,
2172
+ workspaceType: WorkspaceType.MicrosoftTeams,
2173
+ });
2174
+
2175
+ if (!projectAuth || !projectAuth.miscData) {
2176
+ return {};
2177
+ }
2178
+
2179
+ return (
2180
+ (projectAuth.miscData as MicrosoftTeamsMiscData).availableChats || {}
2181
+ );
2182
+ }
2183
+
1954
2184
  public getnotificationChannelssFromNotificationRules(data: {
1955
2185
  notificationEventType: NotificationRuleEventType;
1956
2186
  notificationRules: Array<WorkspaceNotificationRule>;
@@ -0,0 +1,131 @@
1
+ import BadDataException from "../../../Types/Exception/BadDataException";
2
+ import Exception from "../../../Types/Exception/Exception";
3
+
4
+ /*
5
+ * Postgres error codes we can turn into something the caller can act on.
6
+ * https://www.postgresql.org/docs/current/errcodes-appendix.html
7
+ */
8
+ const FOREIGN_KEY_VIOLATION: string = "23503";
9
+
10
+ /*
11
+ * TypeORM reports driver failures as QueryFailedError, which does not extend
12
+ * Exception. Those fall all the way through to the generic handler in
13
+ * StartServer and reach the client as a bare 500 `{"error": "Server Error"}`
14
+ * with nothing to act on — the user just sees "Server Error" in the UI.
15
+ *
16
+ * Translate the failures a user can actually do something about into a
17
+ * BadDataException so the API answers with a 400 and a readable message.
18
+ * Anything we do not recognise is returned untouched, so behaviour for the
19
+ * rest of the error surface is unchanged.
20
+ */
21
+
22
+ interface PostgresDriverError {
23
+ code?: string | undefined;
24
+ table?: string | undefined;
25
+ detail?: string | undefined;
26
+ }
27
+
28
+ export default class PostgresErrorTranslator {
29
+ public static translate(error: unknown): unknown {
30
+ const driverError: PostgresDriverError | null =
31
+ this.getPostgresDriverError(error);
32
+
33
+ if (!driverError || driverError.code !== FOREIGN_KEY_VIOLATION) {
34
+ return error;
35
+ }
36
+
37
+ const detail: string = driverError.detail || "";
38
+
39
+ /*
40
+ * DELETE blocked by a child row:
41
+ * Key (_id)=(...) is still referenced from table "MonitorStatusTimeline".
42
+ */
43
+ if (detail.includes("is still referenced from table")) {
44
+ const referencingTable: string =
45
+ this.getTableNameFromDetail(detail) || driverError.table || "";
46
+
47
+ return new BadDataException(
48
+ referencingTable
49
+ ? `This item cannot be deleted because ${this.humanizeTableName(
50
+ referencingTable,
51
+ )} records still reference it. Please delete those records first, and then try again.`
52
+ : "This item cannot be deleted because other records still reference it. Please delete those records first, and then try again.",
53
+ );
54
+ }
55
+
56
+ /*
57
+ * INSERT/UPDATE pointing at a row that does not exist:
58
+ * Key (monitorStatusId)=(...) is not present in table "MonitorStatus".
59
+ */
60
+ if (detail.includes("is not present in table")) {
61
+ const referencedTable: string = this.getTableNameFromDetail(detail) || "";
62
+
63
+ return new BadDataException(
64
+ referencedTable
65
+ ? `This request references ${this.humanizeTableName(
66
+ referencedTable,
67
+ )} that does not exist. Please check the request and try again.`
68
+ : "This request references an item that does not exist. Please check the request and try again.",
69
+ );
70
+ }
71
+
72
+ return error;
73
+ }
74
+
75
+ /*
76
+ * `throw`-friendly wrapper. Kept synchronous so callers can keep using
77
+ * `throw translator(...)` on the synchronous throw path.
78
+ */
79
+ public static translateException(error: Exception): Exception {
80
+ return this.translate(error) as Exception;
81
+ }
82
+
83
+ private static getPostgresDriverError(
84
+ error: unknown,
85
+ ): PostgresDriverError | null {
86
+ if (!error || typeof error !== "object") {
87
+ return null;
88
+ }
89
+
90
+ /*
91
+ * QueryFailedError hoists the pg fields onto itself and also keeps the
92
+ * original under `driverError`. Prefer whichever one carries the code.
93
+ */
94
+ const candidate: PostgresDriverError = error as PostgresDriverError;
95
+
96
+ if (typeof candidate.code === "string") {
97
+ return candidate;
98
+ }
99
+
100
+ const driverError: unknown = (error as { driverError?: unknown })
101
+ .driverError;
102
+
103
+ if (
104
+ driverError &&
105
+ typeof driverError === "object" &&
106
+ typeof (driverError as PostgresDriverError).code === "string"
107
+ ) {
108
+ return driverError as PostgresDriverError;
109
+ }
110
+
111
+ return null;
112
+ }
113
+
114
+ private static getTableNameFromDetail(detail: string): string | null {
115
+ const match: RegExpMatchArray | null = detail.match(/table "([^"]+)"/);
116
+
117
+ return match && match[1] ? match[1] : null;
118
+ }
119
+
120
+ /*
121
+ * "MonitorStatusTimeline" -> "Monitor Status Timeline". Table names are the
122
+ * PascalCase model names, so this reads well enough to put in front of a
123
+ * user without maintaining a lookup table of display names.
124
+ */
125
+ private static humanizeTableName(tableName: string): string {
126
+ return tableName
127
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
128
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
129
+ .trim();
130
+ }
131
+ }
@@ -27,7 +27,9 @@ import ObjectID from "../../../Types/ObjectID";
27
27
  import ProbeApiIngestResponse from "../../../Types/Probe/ProbeApiIngestResponse";
28
28
  import ProbeMonitorResponse from "../../../Types/Probe/ProbeMonitorResponse";
29
29
  import Monitor from "../../../Models/DatabaseModels/Monitor";
30
- import MonitorProbe from "../../../Models/DatabaseModels/MonitorProbe";
30
+ import MonitorProbe, {
31
+ MonitorStepProbeResponse,
32
+ } from "../../../Models/DatabaseModels/MonitorProbe";
31
33
  import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
32
34
  import MonitorStatusTimeline from "../../../Models/DatabaseModels/MonitorStatusTimeline";
33
35
  import OneUptimeDate from "../../../Types/Date";
@@ -241,6 +243,16 @@ export default class MonitorResourceUtil {
241
243
  let probeName: string | undefined = undefined;
242
244
  const monitorName: string | undefined = monitor.name || undefined;
243
245
 
246
+ /*
247
+ * All MonitorProbe rows for this monitor, fetched once per result and
248
+ * reused by checkProbeAgreement below. The rows carry the heavy
249
+ * lastMonitoringLog jsonb, so re-fetching them per result was the
250
+ * second-largest read on the hottest Postgres path. Safe to reuse: the
251
+ * per-monitor semaphore above serializes every writer of these rows
252
+ * for the duration of this function.
253
+ */
254
+ let monitorProbesForMonitor: Array<MonitorProbe> | null = null;
255
+
244
256
  /*
245
257
  * SNMP trap responses are event-driven, not check results. They are
246
258
  * evaluated ONLY against trap criteria; they must not overwrite the
@@ -266,21 +278,39 @@ export default class MonitorResourceUtil {
266
278
  ) {
267
279
  dataToProcess = dataToProcess as ProbeMonitorResponse;
268
280
  if ((dataToProcess as ProbeMonitorResponse).probeId) {
269
- const monitorProbe: MonitorProbe | null =
270
- await MonitorProbeService.findOneBy({
271
- query: {
272
- monitorId: monitor.id!,
273
- probeId: (dataToProcess as ProbeMonitorResponse).probeId!,
274
- },
275
- select: {
276
- lastMonitoringLog: true,
277
- probe: {
278
- name: true,
279
- },
280
- },
281
- props: {
282
- isRoot: true,
281
+ /*
282
+ * One query for every probe assigned to this monitor (instead of
283
+ * one for the current probe here plus another for all of them in
284
+ * checkProbeAgreement). The select is the union both consumers
285
+ * need.
286
+ */
287
+ monitorProbesForMonitor = await MonitorProbeService.findBy({
288
+ query: {
289
+ monitorId: monitor.id!,
290
+ },
291
+ select: {
292
+ _id: true,
293
+ probeId: true,
294
+ isEnabled: true,
295
+ lastMonitoringLog: true,
296
+ probe: {
297
+ name: true,
298
+ connectionStatus: true,
283
299
  },
300
+ },
301
+ limit: LIMIT_PER_PROJECT,
302
+ skip: 0,
303
+ props: {
304
+ isRoot: true,
305
+ },
306
+ });
307
+
308
+ const monitorProbe: MonitorProbe | undefined =
309
+ monitorProbesForMonitor.find((mp: MonitorProbe) => {
310
+ return (
311
+ mp.probeId?.toString() ===
312
+ (dataToProcess as ProbeMonitorResponse).probeId!.toString()
313
+ );
284
314
  });
285
315
 
286
316
  if (!monitorProbe) {
@@ -297,26 +327,39 @@ export default class MonitorResourceUtil {
297
327
  */
298
328
 
299
329
  if (!isSnmpTrapEvent) {
300
- await MonitorProbeService.updateOneBy({
301
- query: {
302
- monitorId: monitor.id!,
303
- probeId: (dataToProcess as ProbeMonitorResponse).probeId!,
330
+ /*
331
+ * Runs once per probe check result — the hottest recurring write
332
+ * in the product. The full updateOneBy pipeline would re-SELECT
333
+ * this row (including the large lastMonitoringLog jsonb we just
334
+ * fetched above) and then save() it (another SELECT + UPDATE in a
335
+ * transaction). MonitorProbe has no workflow/audit/realtime
336
+ * decorators, so those hooks were inert anyway — a single
337
+ * UPDATE by the id we already hold is equivalent and 3x cheaper.
338
+ * See the Monitor heartbeat writes below for the same pattern.
339
+ */
340
+ const updatedLastMonitoringLog: MonitorStepProbeResponse = {
341
+ ...(monitorProbe.lastMonitoringLog || {}),
342
+ [(
343
+ dataToProcess as ProbeMonitorResponse
344
+ ).monitorStepId.toString()]: {
345
+ ...JSON.parse(JSON.stringify(dataToProcess)),
346
+ monitoredAt: OneUptimeDate.getCurrentDate(),
304
347
  },
348
+ };
349
+
350
+ await MonitorProbeService.updateColumnsByIdWithoutHooks({
351
+ id: monitorProbe.id!,
305
352
  data: {
306
- lastMonitoringLog: {
307
- ...(monitorProbe.lastMonitoringLog || {}),
308
- [(
309
- dataToProcess as ProbeMonitorResponse
310
- ).monitorStepId.toString()]: {
311
- ...JSON.parse(JSON.stringify(dataToProcess)),
312
- monitoredAt: OneUptimeDate.getCurrentDate(),
313
- },
314
- } as any,
315
- },
316
- props: {
317
- isRoot: true,
353
+ lastMonitoringLog: updatedLastMonitoringLog as any,
318
354
  },
319
355
  });
356
+
357
+ /*
358
+ * Mirror the write onto the in-memory row so checkProbeAgreement
359
+ * (which reuses monitorProbesForMonitor instead of re-querying)
360
+ * sees exactly what a fresh read would return.
361
+ */
362
+ monitorProbe.lastMonitoringLog = updatedLastMonitoringLog;
320
363
  }
321
364
  }
322
365
  }
@@ -639,6 +682,7 @@ export default class MonitorResourceUtil {
639
682
  monitorStep: monitorStep,
640
683
  currentCriteriaMetId: response.criteriaMetId || null,
641
684
  currentRootCause: response.rootCause || null,
685
+ monitorProbes: monitorProbesForMonitor || undefined,
642
686
  });
643
687
 
644
688
  // Add probe agreement event to evaluation summary
@@ -1084,6 +1128,15 @@ export default class MonitorResourceUtil {
1084
1128
  monitorStep: MonitorStep;
1085
1129
  currentCriteriaMetId: string | null;
1086
1130
  currentRootCause: string | null;
1131
+ /*
1132
+ * Pre-fetched MonitorProbe rows for this monitor (probeId, isEnabled,
1133
+ * lastMonitoringLog, probe.name/connectionStatus). The probe-result hot
1134
+ * path passes these to avoid re-reading every row's lastMonitoringLog
1135
+ * jsonb per result; safe because the caller holds the per-monitor lock,
1136
+ * so the rows cannot change underneath us. When absent, fall back to
1137
+ * querying.
1138
+ */
1139
+ monitorProbes?: Array<MonitorProbe> | undefined;
1087
1140
  }): Promise<ProbeAgreementResult> {
1088
1141
  const { monitor, monitorStep, currentCriteriaMetId, currentRootCause } =
1089
1142
  input;
@@ -1092,8 +1145,9 @@ export default class MonitorResourceUtil {
1092
1145
  * If minimumProbeAgreement is not set, all probes must agree
1093
1146
  * Get all MonitorProbes for this monitor with their probe connection status
1094
1147
  */
1095
- const monitorProbes: Array<MonitorProbe> = await MonitorProbeService.findBy(
1096
- {
1148
+ const monitorProbes: Array<MonitorProbe> =
1149
+ input.monitorProbes ||
1150
+ (await MonitorProbeService.findBy({
1097
1151
  query: {
1098
1152
  monitorId: monitor.id!,
1099
1153
  },
@@ -1111,8 +1165,7 @@ export default class MonitorResourceUtil {
1111
1165
  props: {
1112
1166
  isRoot: true,
1113
1167
  },
1114
- },
1115
- );
1168
+ }));
1116
1169
 
1117
1170
  // Filter to only active probes (enabled AND connected)
1118
1171
  const activeProbes: Array<MonitorProbe> = monitorProbes.filter(
@@ -27,6 +27,26 @@ export default class MonitorStatusTimelineUtil {
27
27
  }): Promise<MonitorStatusTimeline | null> {
28
28
  // criteria filters are met, now process the actions.
29
29
 
30
+ /*
31
+ * Steady-state fast-path: the criteria's target status is already the
32
+ * monitor's current status (the standard "online criteria matched on a
33
+ * healthy check" case, i.e. almost every probe result). The sorted
34
+ * timeline SELECT below could only confirm what currentMonitorStatusId
35
+ * already tells us — Monitor.currentMonitorStatusId is kept in lockstep
36
+ * with the latest timeline row by MonitorStatusTimelineService — and the
37
+ * function would return null via the same-as-last-status check anyway.
38
+ * Skip the query entirely. MonitorStatusTimelineService.onBeforeCreate
39
+ * still dedupes as the concurrency backstop.
40
+ */
41
+ if (
42
+ input.criteriaInstance.data?.changeMonitorStatus &&
43
+ input.criteriaInstance.data?.monitorStatusId &&
44
+ input.criteriaInstance.data.monitorStatusId.toString() ===
45
+ input.monitor.currentMonitorStatusId?.toString()
46
+ ) {
47
+ return null;
48
+ }
49
+
30
50
  const lastMonitorStatusTimeline: MonitorStatusTimeline | null =
31
51
  await MonitorStatusTimelineService.findOneBy({
32
52
  query: {
@@ -56,7 +76,7 @@ export default class MonitorStatusTimelineUtil {
56
76
  input.criteriaInstance.data?.changeMonitorStatus &&
57
77
  input.criteriaInstance.data?.monitorStatusId &&
58
78
  input.criteriaInstance.data?.monitorStatusId.toString() !==
59
- lastMonitorStatusTimeline?.id?.toString()
79
+ lastMonitorStatusTimeline?.monitorStatusId?.toString()
60
80
  ) {
61
81
  // if monitor status is changed, then create a new status timeline.
62
82
  shouldUpdateStatus = true;