@oneuptime/common 11.3.25 → 11.3.28

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 (397) hide show
  1. package/Models/DatabaseModels/AIConversation.ts +322 -0
  2. package/Models/DatabaseModels/AIConversationMessage.ts +495 -0
  3. package/Models/DatabaseModels/AIRun.ts +584 -0
  4. package/Models/DatabaseModels/AIRunEvent.ts +443 -0
  5. package/Models/DatabaseModels/IncidentEpisodeRoleMember.ts +1 -1
  6. package/Models/DatabaseModels/IncidentMember.ts +1 -1
  7. package/Models/DatabaseModels/IncomingCallPolicy.ts +13 -0
  8. package/Models/DatabaseModels/Index.ts +11 -0
  9. package/Models/DatabaseModels/LlmLog.ts +26 -0
  10. package/Models/DatabaseModels/LlmProvider.ts +2 -2
  11. package/Models/DatabaseModels/MigrationFailure.ts +170 -0
  12. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +51 -2
  13. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +44 -0
  14. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +6 -0
  15. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.ts +6 -0
  16. package/Models/DatabaseModels/OnCallDutyPolicyTimeLog.ts +1 -1
  17. package/Models/DatabaseModels/OnCallDutyPolicyUserOverride.ts +2 -2
  18. package/Server/API/AIChatAPI.ts +693 -0
  19. package/Server/API/AlertAPI.ts +1 -1
  20. package/Server/API/IncidentAPI.ts +2 -2
  21. package/Server/API/IncidentEpisodeAPI.ts +1 -1
  22. package/Server/API/LlmProviderAPI.ts +170 -0
  23. package/Server/API/MicrosoftTeamsAPI.ts +5 -0
  24. package/Server/API/ScheduledMaintenanceAPI.ts +1 -1
  25. package/Server/API/SlackAPI.ts +663 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.ts +245 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.ts +36 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.ts +46 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.ts +119 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.ts +25 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.ts +81 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.ts +25 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.ts +35 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
  35. package/Server/Infrastructure/PostgresDatabase.ts +11 -0
  36. package/Server/Services/AIConversationMessageService.ts +39 -0
  37. package/Server/Services/AIConversationService.ts +63 -0
  38. package/Server/Services/AIRunEventService.ts +39 -0
  39. package/Server/Services/AIRunService.ts +39 -0
  40. package/Server/Services/AIService.ts +127 -33
  41. package/Server/Services/AnalyticsDatabaseService.ts +10 -1
  42. package/Server/Services/DatabaseService.ts +29 -1
  43. package/Server/Services/IncidentService.ts +1 -1
  44. package/Server/Services/IncomingCallPolicyEscalationRuleService.ts +75 -2
  45. package/Server/Services/IncomingCallPolicyService.ts +42 -1
  46. package/Server/Services/Index.ts +2 -0
  47. package/Server/Services/LlmProviderService.ts +110 -0
  48. package/Server/Services/MigrationFailureService.ts +9 -0
  49. package/Server/Services/MonitorProbeService.ts +33 -7
  50. package/Server/Services/MonitorService.ts +19 -7
  51. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  52. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +185 -41
  53. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +23 -15
  54. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +39 -0
  55. package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +148 -0
  56. package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +11 -2
  57. package/Server/Services/OnCallDutyPolicyScheduleService.ts +502 -26
  58. package/Server/Services/OnCallDutyPolicyTimeLogService.ts +49 -7
  59. package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +218 -4
  60. package/Server/Services/ProjectCallSMSConfigService.ts +82 -1
  61. package/Server/Services/ProjectService.ts +24 -0
  62. package/Server/Services/TeamMemberService.ts +6 -0
  63. package/Server/Services/UserNotificationRuleService.ts +13 -42
  64. package/Server/Services/UserOnCallLogService.ts +90 -0
  65. package/Server/Services/UserOnCallLogTimelineService.ts +43 -1
  66. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +74 -9
  67. package/Server/Types/Database/QueryUtil.ts +57 -0
  68. package/Server/Utils/AI/AIChatPrivacyFilter.ts +28 -0
  69. package/Server/Utils/AI/AlertAIContextBuilder.ts +6 -1
  70. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +1066 -0
  71. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +239 -0
  72. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +51 -0
  73. package/Server/Utils/AI/IncidentAIContextBuilder.ts +18 -3
  74. package/Server/Utils/AI/IncidentEpisodeAIContextBuilder.ts +6 -1
  75. package/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.ts +12 -2
  76. package/Server/Utils/AI/Toolbox/AlertTools.ts +201 -0
  77. package/Server/Utils/AI/Toolbox/AlertWriteTools.ts +174 -0
  78. package/Server/Utils/AI/Toolbox/ContextTools.ts +189 -0
  79. package/Server/Utils/AI/Toolbox/ExceptionTools.ts +149 -0
  80. package/Server/Utils/AI/Toolbox/IncidentTools.ts +386 -0
  81. package/Server/Utils/AI/Toolbox/IncidentWriteTools.ts +350 -0
  82. package/Server/Utils/AI/Toolbox/Index.ts +231 -0
  83. package/Server/Utils/AI/Toolbox/LogTools.ts +339 -0
  84. package/Server/Utils/AI/Toolbox/MetricTools.ts +179 -0
  85. package/Server/Utils/AI/Toolbox/MonitorTools.ts +193 -0
  86. package/Server/Utils/AI/Toolbox/RecentChangesTools.ts +208 -0
  87. package/Server/Utils/AI/Toolbox/Serializer.ts +299 -0
  88. package/Server/Utils/AI/Toolbox/ToolTypes.ts +249 -0
  89. package/Server/Utils/AI/Toolbox/TraceTools.ts +402 -0
  90. package/Server/Utils/AI/Toolbox/WidgetBuilder.ts +189 -0
  91. package/Server/Utils/Database/MigrationFailureLog.ts +240 -0
  92. package/Server/Utils/IncomingCallPhoneNumber.ts +41 -0
  93. package/Server/Utils/LLM/LLMService.ts +605 -96
  94. package/Server/Utils/Telemetry/IoTSnapshotScan.ts +12 -9
  95. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +12 -9
  96. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +512 -19
  97. package/Server/Utils/Workspace/Slack/Slack.ts +80 -0
  98. package/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
  99. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +0 -8
  100. package/Tests/Server/Services/OnCallDutyPolicyScheduleLayerUserReorder.test.ts +233 -0
  101. package/Tests/Server/Services/OnCallDutyPolicyTimeLogServiceScoping.test.ts +242 -0
  102. package/Tests/Server/Services/OnCallDutyPolicyUserOverrideEdit.test.ts +198 -0
  103. package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +495 -0
  104. package/Tests/Server/Types/Database/QueryUtil.test.ts +74 -0
  105. package/Tests/Server/Utils/AI/AIChatModelACL.test.ts +42 -0
  106. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +89 -0
  107. package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +139 -0
  108. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +386 -0
  109. package/Tests/Server/Utils/AI/ToolArgsGetTimeRange.test.ts +62 -0
  110. package/Tests/Server/Utils/AI/ToolArgsScopeServiceIds.test.ts +79 -0
  111. package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +223 -0
  112. package/Tests/Types/Billing/SubscriptionStatus.test.ts +127 -0
  113. package/Tests/Types/DateExhaustiveTimezone.test.ts +700 -0
  114. package/Tests/Types/DateTimezoneWallClock.test.ts +99 -0
  115. package/Tests/Types/Events/Recurring.test.ts +22 -9
  116. package/Tests/Types/Metrics/RecordingRuleDefinition.test.ts +213 -0
  117. package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixes.test.ts +291 -0
  118. package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixesRound2.test.ts +398 -0
  119. package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeekendGap.test.ts +114 -0
  120. package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeeklyDayShift.test.ts +76 -0
  121. package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyMutation.test.ts +128 -0
  122. package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyProbe.test.ts +203 -0
  123. package/Tests/Types/OnCallDutyPolicy/LayerUtilDiffFuzz.test.ts +322 -0
  124. package/Tests/Types/OnCallDutyPolicy/LayerUtilEdgeCases.test.ts +280 -0
  125. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveCurrentUser.test.ts +863 -0
  126. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveDaily.test.ts +900 -0
  127. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveMultiLayer.test.ts +1104 -0
  128. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +957 -0
  129. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveWeekly.test.ts +1095 -0
  130. package/Tests/Types/OnCallDutyPolicy/LayerUtilInvariants.test.ts +232 -0
  131. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +227 -0
  132. package/Tests/Types/OnCallDutyPolicy/LayerUtilMonthYearAudit.test.ts +255 -0
  133. package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerAudit.test.ts +388 -0
  134. package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerEdge.test.ts +300 -0
  135. package/Tests/Types/OnCallDutyPolicy/LayerUtilOvernightDSTAudit.test.ts +158 -0
  136. package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapFix.test.ts +253 -0
  137. package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapRepro.test.ts +157 -0
  138. package/Tests/Types/OnCallDutyPolicy/LayerUtilRotationFixes.test.ts +414 -0
  139. package/Tests/Types/OnCallDutyPolicy/LayerUtilTimezone.test.ts +243 -0
  140. package/Tests/Types/OnCallDutyPolicy/LayerUtilWeekendGapRepro.test.ts +112 -0
  141. package/Tests/Types/OnCallDutyPolicy/OverrideLensAudit.test.ts +117 -0
  142. package/Tests/Types/OnCallDutyPolicy/RestrictionTimes.test.ts +8 -6
  143. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesDefaultDayAudit.test.ts +112 -0
  144. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesExhaustive.test.ts +821 -0
  145. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesMutationInvariant.test.ts +886 -0
  146. package/Tests/Types/OnCallDutyPolicy/UserOverrideUtil.test.ts +255 -0
  147. package/Tests/Types/OnCallDutyPolicy/UserOverrideUtilExhaustive.test.ts +1126 -0
  148. package/Types/AI/AIChatMessageRole.ts +6 -0
  149. package/Types/AI/AIChatMessageStatus.ts +32 -0
  150. package/Types/AI/AIChatPermissionMode.ts +69 -0
  151. package/Types/AI/AIChatTypes.ts +231 -0
  152. package/Types/AI/AIRunEventType.ts +17 -0
  153. package/Types/AI/AIRunStatus.ts +28 -0
  154. package/Types/AI/AIRunType.ts +6 -0
  155. package/Types/Date.ts +200 -15
  156. package/Types/LLM/LlmType.ts +6 -0
  157. package/Types/OnCallDutyPolicy/Layer.ts +790 -260
  158. package/Types/OnCallDutyPolicy/RestrictionTimes.ts +39 -13
  159. package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +21 -5
  160. package/UI/Components/Events/RecurringFieldElement.tsx +16 -0
  161. package/UI/Components/Header/HeaderIconDropdownButton.tsx +22 -12
  162. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +35 -3
  163. package/UI/Components/ModelFormModal/ModelFormModal.tsx +8 -0
  164. package/UI/Components/Page/Page.tsx +14 -0
  165. package/UI/Components/RadioButtons/BasicRadioButtons.tsx +1 -1
  166. package/UI/Utils/LlmTypeDropdownOptions.ts +40 -0
  167. package/UI/Utils/TestLLMProvider.ts +59 -0
  168. package/build/dist/Models/DatabaseModels/AIConversation.js +345 -0
  169. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -0
  170. package/build/dist/Models/DatabaseModels/AIConversationMessage.js +521 -0
  171. package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -0
  172. package/build/dist/Models/DatabaseModels/AIRun.js +619 -0
  173. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -0
  174. package/build/dist/Models/DatabaseModels/AIRunEvent.js +469 -0
  175. package/build/dist/Models/DatabaseModels/AIRunEvent.js.map +1 -0
  176. package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js +1 -1
  177. package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js.map +1 -1
  178. package/build/dist/Models/DatabaseModels/IncidentMember.js +1 -1
  179. package/build/dist/Models/DatabaseModels/IncidentMember.js.map +1 -1
  180. package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js +10 -0
  181. package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js.map +1 -1
  182. package/build/dist/Models/DatabaseModels/Index.js +10 -0
  183. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  184. package/build/dist/Models/DatabaseModels/LlmLog.js +28 -0
  185. package/build/dist/Models/DatabaseModels/LlmLog.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/LlmProvider.js +2 -2
  187. package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/MigrationFailure.js +196 -0
  189. package/build/dist/Models/DatabaseModels/MigrationFailure.js.map +1 -0
  190. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +52 -2
  191. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +45 -0
  193. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +6 -0
  195. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js +6 -0
  197. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +1 -1
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js +2 -2
  201. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js.map +1 -1
  202. package/build/dist/Server/API/AIChatAPI.js +498 -0
  203. package/build/dist/Server/API/AIChatAPI.js.map +1 -0
  204. package/build/dist/Server/API/AlertAPI.js +1 -1
  205. package/build/dist/Server/API/IncidentAPI.js +2 -2
  206. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  207. package/build/dist/Server/API/LlmProviderAPI.js +108 -1
  208. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  209. package/build/dist/Server/API/MicrosoftTeamsAPI.js +4 -0
  210. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  211. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  212. package/build/dist/Server/API/SlackAPI.js +442 -0
  213. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  214. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js +96 -0
  215. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js.map +1 -0
  216. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js +25 -0
  217. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js.map +1 -0
  218. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js +31 -0
  219. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js.map +1 -0
  220. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js +46 -0
  221. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js.map +1 -0
  222. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js +18 -0
  223. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js.map +1 -0
  224. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js +38 -0
  225. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js.map +1 -0
  226. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js +16 -0
  227. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js.map +1 -0
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js +18 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js.map +1 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  232. package/build/dist/Server/Infrastructure/PostgresDatabase.js +9 -0
  233. package/build/dist/Server/Infrastructure/PostgresDatabase.js.map +1 -1
  234. package/build/dist/Server/Services/AIConversationMessageService.js +34 -0
  235. package/build/dist/Server/Services/AIConversationMessageService.js.map +1 -0
  236. package/build/dist/Server/Services/AIConversationService.js +42 -0
  237. package/build/dist/Server/Services/AIConversationService.js.map +1 -0
  238. package/build/dist/Server/Services/AIRunEventService.js +34 -0
  239. package/build/dist/Server/Services/AIRunEventService.js.map +1 -0
  240. package/build/dist/Server/Services/AIRunService.js +34 -0
  241. package/build/dist/Server/Services/AIRunService.js.map +1 -0
  242. package/build/dist/Server/Services/AIService.js +84 -25
  243. package/build/dist/Server/Services/AIService.js.map +1 -1
  244. package/build/dist/Server/Services/AnalyticsDatabaseService.js +10 -1
  245. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  246. package/build/dist/Server/Services/DatabaseService.js +20 -1
  247. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  248. package/build/dist/Server/Services/IncidentService.js +1 -1
  249. package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js +55 -2
  250. package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js.map +1 -1
  251. package/build/dist/Server/Services/IncomingCallPolicyService.js +40 -0
  252. package/build/dist/Server/Services/IncomingCallPolicyService.js.map +1 -1
  253. package/build/dist/Server/Services/Index.js +2 -0
  254. package/build/dist/Server/Services/Index.js.map +1 -1
  255. package/build/dist/Server/Services/LlmProviderService.js +108 -0
  256. package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
  257. package/build/dist/Server/Services/MigrationFailureService.js +9 -0
  258. package/build/dist/Server/Services/MigrationFailureService.js.map +1 -0
  259. package/build/dist/Server/Services/MonitorProbeService.js +33 -7
  260. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  261. package/build/dist/Server/Services/MonitorService.js +12 -5
  262. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  263. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +81 -22
  264. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  265. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +134 -31
  266. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  267. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +21 -13
  268. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  269. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +36 -0
  270. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  271. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +121 -0
  272. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
  273. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +11 -2
  274. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
  275. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +409 -36
  276. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  277. package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js +52 -8
  278. package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js.map +1 -1
  279. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +170 -3
  280. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
  281. package/build/dist/Server/Services/ProjectCallSMSConfigService.js +72 -0
  282. package/build/dist/Server/Services/ProjectCallSMSConfigService.js.map +1 -1
  283. package/build/dist/Server/Services/ProjectService.js +25 -0
  284. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  285. package/build/dist/Server/Services/TeamMemberService.js +6 -0
  286. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  287. package/build/dist/Server/Services/UserNotificationRuleService.js +12 -30
  288. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  289. package/build/dist/Server/Services/UserOnCallLogService.js +75 -0
  290. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  291. package/build/dist/Server/Services/UserOnCallLogTimelineService.js +31 -1
  292. package/build/dist/Server/Services/UserOnCallLogTimelineService.js.map +1 -1
  293. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +50 -7
  294. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  295. package/build/dist/Server/Types/Database/QueryUtil.js +45 -0
  296. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  297. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +18 -0
  298. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -0
  299. package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js +6 -1
  300. package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js.map +1 -1
  301. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +756 -0
  302. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -0
  303. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +165 -0
  304. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -0
  305. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +44 -0
  306. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -0
  307. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js +18 -3
  308. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js.map +1 -1
  309. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js +6 -1
  310. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js.map +1 -1
  311. package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js +12 -2
  312. package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js.map +1 -1
  313. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +167 -0
  314. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -0
  315. package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js +136 -0
  316. package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js.map +1 -0
  317. package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js +141 -0
  318. package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js.map +1 -0
  319. package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js +117 -0
  320. package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js.map +1 -0
  321. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +318 -0
  322. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -0
  323. package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js +280 -0
  324. package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js.map +1 -0
  325. package/build/dist/Server/Utils/AI/Toolbox/Index.js +153 -0
  326. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -0
  327. package/build/dist/Server/Utils/AI/Toolbox/LogTools.js +246 -0
  328. package/build/dist/Server/Utils/AI/Toolbox/LogTools.js.map +1 -0
  329. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +120 -0
  330. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -0
  331. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +158 -0
  332. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -0
  333. package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js +165 -0
  334. package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js.map +1 -0
  335. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +228 -0
  336. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -0
  337. package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js +142 -0
  338. package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js.map +1 -0
  339. package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js +309 -0
  340. package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js.map +1 -0
  341. package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js +120 -0
  342. package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js.map +1 -0
  343. package/build/dist/Server/Utils/Database/MigrationFailureLog.js +174 -0
  344. package/build/dist/Server/Utils/Database/MigrationFailureLog.js.map +1 -0
  345. package/build/dist/Server/Utils/IncomingCallPhoneNumber.js +30 -0
  346. package/build/dist/Server/Utils/IncomingCallPhoneNumber.js.map +1 -0
  347. package/build/dist/Server/Utils/LLM/LLMService.js +452 -92
  348. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  349. package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js +11 -8
  350. package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js.map +1 -1
  351. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +11 -8
  352. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  353. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +378 -15
  354. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  355. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +59 -0
  356. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  357. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
  358. package/build/dist/Types/AI/AIChatMessageRole.js +7 -0
  359. package/build/dist/Types/AI/AIChatMessageRole.js.map +1 -0
  360. package/build/dist/Types/AI/AIChatMessageStatus.js +27 -0
  361. package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -0
  362. package/build/dist/Types/AI/AIChatPermissionMode.js +53 -0
  363. package/build/dist/Types/AI/AIChatPermissionMode.js.map +1 -0
  364. package/build/dist/Types/AI/AIChatTypes.js +74 -0
  365. package/build/dist/Types/AI/AIChatTypes.js.map +1 -0
  366. package/build/dist/Types/AI/AIRunEventType.js +18 -0
  367. package/build/dist/Types/AI/AIRunEventType.js.map +1 -0
  368. package/build/dist/Types/AI/AIRunStatus.js +26 -0
  369. package/build/dist/Types/AI/AIRunStatus.js.map +1 -0
  370. package/build/dist/Types/AI/AIRunType.js +7 -0
  371. package/build/dist/Types/AI/AIRunType.js.map +1 -0
  372. package/build/dist/Types/Date.js +153 -15
  373. package/build/dist/Types/Date.js.map +1 -1
  374. package/build/dist/Types/LLM/LlmType.js +6 -0
  375. package/build/dist/Types/LLM/LlmType.js.map +1 -1
  376. package/build/dist/Types/OnCallDutyPolicy/Layer.js +602 -171
  377. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  378. package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js +27 -13
  379. package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js.map +1 -1
  380. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +20 -5
  381. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
  382. package/build/dist/UI/Components/Events/RecurringFieldElement.js +13 -0
  383. package/build/dist/UI/Components/Events/RecurringFieldElement.js.map +1 -1
  384. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +10 -5
  385. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  386. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +18 -4
  387. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  388. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  389. package/build/dist/UI/Components/Page/Page.js +3 -1
  390. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  391. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +1 -1
  392. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
  393. package/build/dist/UI/Utils/LlmTypeDropdownOptions.js +38 -0
  394. package/build/dist/UI/Utils/LlmTypeDropdownOptions.js.map +1 -0
  395. package/build/dist/UI/Utils/TestLLMProvider.js +37 -0
  396. package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -0
  397. package/package.json +1 -1
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Tests for the timezone wall-clock helpers backing the on-call restriction
3
+ * time-picker fix (audit F1) and the summary formatting fix (audit F10):
4
+ * - getInstantFromLocalWallClockInTimezone
5
+ * - getLocalDateFromWallClockInTimezone (inverse)
6
+ * - getHourAndMinuteInTimezoneString
7
+ *
8
+ * Assertions are expressed in wall-clock via moment-timezone so they hold
9
+ * regardless of the process TZ the suite runs under.
10
+ */
11
+ import OneUptimeDate from "../../Types/Date";
12
+ import moment from "moment-timezone";
13
+
14
+ const NY: string = "America/New_York";
15
+ const KOLKATA: string = "Asia/Kolkata";
16
+
17
+ describe("OneUptimeDate timezone wall-clock helpers", () => {
18
+ describe("getInstantFromLocalWallClockInTimezone", () => {
19
+ it("plants the local wall-clock components as the same wall-clock in the target zone", () => {
20
+ // A local Date reading 09:00 on 2025-01-06 in the process zone.
21
+ const local: Date = new Date(2025, 0, 6, 9, 0, 0);
22
+ const instant: Date =
23
+ OneUptimeDate.getInstantFromLocalWallClockInTimezone(local, NY);
24
+ // The resulting instant must read 09:00 in New York.
25
+ expect(moment.tz(instant, NY).format("HH:mm")).toBe("09:00");
26
+ });
27
+
28
+ it("produces different absolute instants for the same wall-clock in different zones", () => {
29
+ const local: Date = new Date(2025, 5, 15, 9, 0, 0);
30
+ const inNY: Date = OneUptimeDate.getInstantFromLocalWallClockInTimezone(
31
+ local,
32
+ NY,
33
+ );
34
+ const inKolkata: Date =
35
+ OneUptimeDate.getInstantFromLocalWallClockInTimezone(local, KOLKATA);
36
+ // 09:00 NY and 09:00 Kolkata are different moments.
37
+ expect(inNY.getTime()).not.toBe(inKolkata.getTime());
38
+ expect(moment.tz(inNY, NY).format("HH:mm")).toBe("09:00");
39
+ expect(moment.tz(inKolkata, KOLKATA).format("HH:mm")).toBe("09:00");
40
+ });
41
+ });
42
+
43
+ describe("getLocalDateFromWallClockInTimezone", () => {
44
+ it("returns a local Date whose local wall-clock equals the target-zone wall-clock", () => {
45
+ // An instant that is 09:00 in New York.
46
+ const instant: Date = moment.tz("2025-01-06 09:00", NY).toDate();
47
+ const local: Date = OneUptimeDate.getLocalDateFromWallClockInTimezone(
48
+ instant,
49
+ NY,
50
+ );
51
+ // Its LOCAL hour/minute should read 09:00.
52
+ expect(local.getHours()).toBe(9);
53
+ expect(local.getMinutes()).toBe(0);
54
+ });
55
+ });
56
+
57
+ describe("round-trip", () => {
58
+ it("local -> tz-instant -> local preserves the wall-clock (incl. across a DST week)", () => {
59
+ for (const wall of [
60
+ new Date(2025, 0, 6, 9, 0, 0), // winter
61
+ new Date(2025, 6, 6, 14, 30, 0), // summer
62
+ new Date(2026, 2, 8, 1, 30, 0), // US spring-forward day
63
+ ]) {
64
+ const instant: Date =
65
+ OneUptimeDate.getInstantFromLocalWallClockInTimezone(wall, NY);
66
+ const back: Date = OneUptimeDate.getLocalDateFromWallClockInTimezone(
67
+ instant,
68
+ NY,
69
+ );
70
+ expect(back.getHours()).toBe(wall.getHours());
71
+ expect(back.getMinutes()).toBe(wall.getMinutes());
72
+ }
73
+ });
74
+ });
75
+
76
+ describe("getHourAndMinuteInTimezoneString", () => {
77
+ it("formats an instant in the given timezone (NY noon reads 09:00 in LA, differs from NY)", () => {
78
+ const nyNoon: Date = moment.tz("2025-01-06 12:00", NY).toDate();
79
+ const inNY: string = OneUptimeDate.getHourAndMinuteInTimezoneString(
80
+ nyNoon,
81
+ NY,
82
+ );
83
+ const inKolkata: string = OneUptimeDate.getHourAndMinuteInTimezoneString(
84
+ nyNoon,
85
+ KOLKATA,
86
+ );
87
+ expect(inNY).not.toBe(inKolkata);
88
+ // Kolkata is +5:30, so the minute component is :30.
89
+ expect(inKolkata).toContain("30");
90
+ });
91
+
92
+ it("falls back to local wall-clock when no timezone is given", () => {
93
+ const d: Date = new Date(2025, 0, 6, 8, 15, 0);
94
+ expect(OneUptimeDate.getHourAndMinuteInTimezoneString(d, undefined)).toBe(
95
+ OneUptimeDate.getLocalHourAndMinuteFromDate(d),
96
+ );
97
+ });
98
+ });
99
+ });
@@ -273,15 +273,28 @@ describe("Recurring", () => {
273
273
  });
274
274
 
275
275
  test("leaves a start date equal to now unchanged (zero intervals to add)", () => {
276
- const now: Date = OneUptimeDate.getCurrentDate();
277
-
278
- const next: Date = Recurring.getNextDate(
279
- now,
280
- makeRecurring(EventInterval.Day, 1),
281
- );
282
-
283
- // The <= branch is entered, diff is 0, so Math.ceil(0) === 0 intervals are added.
284
- expect(next.getTime()).toBe(now.getTime());
276
+ /*
277
+ * getNextDate() reads the clock internally via getCurrentDate(). Pin it so
278
+ * the start date and the internal "now" resolve to the exact same instant;
279
+ * otherwise a sub-millisecond gap between the two clock reads makes diff a
280
+ * tiny positive number and Math.ceil() rounds it up to a full extra day.
281
+ */
282
+ const now: Date = OneUptimeDate.fromString("2023-01-01T00:00:00.000Z");
283
+ const getCurrentDateSpy: jest.SpyInstance = jest
284
+ .spyOn(OneUptimeDate, "getCurrentDate")
285
+ .mockReturnValue(now);
286
+
287
+ try {
288
+ const next: Date = Recurring.getNextDate(
289
+ now,
290
+ makeRecurring(EventInterval.Day, 1),
291
+ );
292
+
293
+ // The <= branch is entered, diff is 0, so Math.ceil(0) === 0 intervals are added.
294
+ expect(next.getTime()).toBe(now.getTime());
295
+ } finally {
296
+ getCurrentDateSpy.mockRestore();
297
+ }
285
298
  });
286
299
 
287
300
  test("throws BadDataException for an unknown interval type when the start date is in the past", () => {
@@ -0,0 +1,213 @@
1
+ import AggregationType from "../../../Types/BaseDatabase/AggregationType";
2
+ import RecordingRuleDefinition, {
3
+ RecordingRuleDefinitionUtil,
4
+ RecordingRuleSource,
5
+ RECORDING_RULE_MAX_SOURCES,
6
+ RECORDING_RULE_MAX_EXPRESSION_LENGTH,
7
+ } from "../../../Types/Metrics/RecordingRuleDefinition";
8
+
9
+ describe("RecordingRuleDefinitionUtil", () => {
10
+ const makeSource: (
11
+ overrides?: Partial<RecordingRuleSource>,
12
+ ) => RecordingRuleSource = (
13
+ overrides: Partial<RecordingRuleSource> = {},
14
+ ): RecordingRuleSource => {
15
+ return {
16
+ alias: "A",
17
+ metricName: "http.requests",
18
+ aggregationType: AggregationType.Sum,
19
+ ...overrides,
20
+ };
21
+ };
22
+
23
+ describe("getAggregationOptions", () => {
24
+ test("returns a value/label option for each supported aggregation", () => {
25
+ const options: Array<{ value: AggregationType; label: string }> =
26
+ RecordingRuleDefinitionUtil.getAggregationOptions();
27
+
28
+ expect(options.length).toBeGreaterThan(0);
29
+ for (const option of options) {
30
+ expect(typeof option.label).toBe("string");
31
+ expect(option.label.length).toBeGreaterThan(0);
32
+ expect(Object.values(AggregationType)).toContain(option.value);
33
+ }
34
+ });
35
+ });
36
+
37
+ describe("getNextAlias", () => {
38
+ test("returns A when there are no sources", () => {
39
+ expect(RecordingRuleDefinitionUtil.getNextAlias(undefined)).toBe("A");
40
+ expect(RecordingRuleDefinitionUtil.getNextAlias([])).toBe("A");
41
+ });
42
+
43
+ test("returns the first unused letter", () => {
44
+ expect(
45
+ RecordingRuleDefinitionUtil.getNextAlias([makeSource({ alias: "A" })]),
46
+ ).toBe("B");
47
+ expect(
48
+ RecordingRuleDefinitionUtil.getNextAlias([
49
+ makeSource({ alias: "A" }),
50
+ makeSource({ alias: "B" }),
51
+ ]),
52
+ ).toBe("C");
53
+ });
54
+
55
+ test("skips gaps and returns the lowest free letter", () => {
56
+ expect(
57
+ RecordingRuleDefinitionUtil.getNextAlias([
58
+ makeSource({ alias: "A" }),
59
+ makeSource({ alias: "C" }),
60
+ ]),
61
+ ).toBe("B");
62
+ });
63
+ });
64
+
65
+ describe("getEmptyDefinition", () => {
66
+ test("returns a single source aliased A with expression A", () => {
67
+ const def: RecordingRuleDefinition =
68
+ RecordingRuleDefinitionUtil.getEmptyDefinition();
69
+
70
+ expect(def.sources.length).toBe(1);
71
+ expect(def.sources[0]!.alias).toBe("A");
72
+ expect(def.expression).toBe("A");
73
+ });
74
+
75
+ test("is itself a valid definition once a metric name is provided", () => {
76
+ const def: RecordingRuleDefinition =
77
+ RecordingRuleDefinitionUtil.getEmptyDefinition();
78
+ def.sources[0]!.metricName = "http.requests";
79
+
80
+ expect(RecordingRuleDefinitionUtil.getValidationError(def)).toBeNull();
81
+ });
82
+ });
83
+
84
+ describe("getValidationError", () => {
85
+ const validDefinition: () => RecordingRuleDefinition =
86
+ (): RecordingRuleDefinition => {
87
+ return {
88
+ sources: [makeSource({ alias: "A" }), makeSource({ alias: "B" })],
89
+ expression: "A / B * 100",
90
+ };
91
+ };
92
+
93
+ test("accepts a valid definition", () => {
94
+ expect(
95
+ RecordingRuleDefinitionUtil.getValidationError(validDefinition()),
96
+ ).toBeNull();
97
+ });
98
+
99
+ test("requires a definition", () => {
100
+ expect(RecordingRuleDefinitionUtil.getValidationError(undefined)).toBe(
101
+ "Definition is required.",
102
+ );
103
+ });
104
+
105
+ test("requires at least one source", () => {
106
+ expect(
107
+ RecordingRuleDefinitionUtil.getValidationError({
108
+ sources: [],
109
+ expression: "A",
110
+ }),
111
+ ).toBe("Add at least one source metric.");
112
+ });
113
+
114
+ test("rejects more than the maximum number of sources", () => {
115
+ const sources: Array<RecordingRuleSource> = Array.from(
116
+ { length: RECORDING_RULE_MAX_SOURCES + 1 },
117
+ (_unused: unknown, i: number) => {
118
+ return makeSource({ alias: "ABCDE"[i] as string });
119
+ },
120
+ );
121
+
122
+ expect(
123
+ RecordingRuleDefinitionUtil.getValidationError({
124
+ sources,
125
+ expression: "A",
126
+ }),
127
+ ).toContain(`at most ${RECORDING_RULE_MAX_SOURCES}`);
128
+ });
129
+
130
+ test("rejects an invalid alias", () => {
131
+ expect(
132
+ RecordingRuleDefinitionUtil.getValidationError({
133
+ sources: [makeSource({ alias: "a" })],
134
+ expression: "A",
135
+ }),
136
+ ).toContain("single uppercase letter");
137
+ });
138
+
139
+ test("rejects duplicate aliases", () => {
140
+ expect(
141
+ RecordingRuleDefinitionUtil.getValidationError({
142
+ sources: [makeSource({ alias: "A" }), makeSource({ alias: "A" })],
143
+ expression: "A",
144
+ }),
145
+ ).toContain("Duplicate alias");
146
+ });
147
+
148
+ test("requires a metric name", () => {
149
+ expect(
150
+ RecordingRuleDefinitionUtil.getValidationError({
151
+ sources: [makeSource({ alias: "A", metricName: " " })],
152
+ expression: "A",
153
+ }),
154
+ ).toContain("Metric name is required");
155
+ });
156
+
157
+ test("requires both a filter key and value, or neither", () => {
158
+ expect(
159
+ RecordingRuleDefinitionUtil.getValidationError({
160
+ sources: [makeSource({ alias: "A", filterAttributeKey: "env" })],
161
+ expression: "A",
162
+ }),
163
+ ).toContain("needs both a key and a value");
164
+ });
165
+
166
+ test("requires an expression", () => {
167
+ expect(
168
+ RecordingRuleDefinitionUtil.getValidationError({
169
+ sources: [makeSource({ alias: "A" })],
170
+ expression: " ",
171
+ }),
172
+ ).toBe("Expression is required.");
173
+ });
174
+
175
+ test("rejects an over-length expression", () => {
176
+ expect(
177
+ RecordingRuleDefinitionUtil.getValidationError({
178
+ sources: [makeSource({ alias: "A" })],
179
+ expression: "A".repeat(RECORDING_RULE_MAX_EXPRESSION_LENGTH + 1),
180
+ }),
181
+ ).toContain(
182
+ `${RECORDING_RULE_MAX_EXPRESSION_LENGTH} characters or fewer`,
183
+ );
184
+ });
185
+
186
+ test("rejects an expression referencing an undefined alias", () => {
187
+ expect(
188
+ RecordingRuleDefinitionUtil.getValidationError({
189
+ sources: [makeSource({ alias: "A" })],
190
+ expression: "A + B",
191
+ }),
192
+ ).toContain("references alias 'B'");
193
+ });
194
+
195
+ test("rejects an expression that references no alias", () => {
196
+ expect(
197
+ RecordingRuleDefinitionUtil.getValidationError({
198
+ sources: [makeSource({ alias: "A" })],
199
+ expression: "5 + 10",
200
+ }),
201
+ ).toContain("must reference at least one source alias");
202
+ });
203
+
204
+ test("rejects an expression with characters outside the DSL grammar", () => {
205
+ expect(
206
+ RecordingRuleDefinitionUtil.getValidationError({
207
+ sources: [makeSource({ alias: "A" }), makeSource({ alias: "B" })],
208
+ expression: "A & B",
209
+ }),
210
+ ).toContain("may only contain");
211
+ });
212
+ });
213
+ });
@@ -0,0 +1,291 @@
1
+ import LayerUtil, { LayerProps } from "../../../Types/OnCallDutyPolicy/Layer";
2
+ import CalendarEvent from "../../../Types/Calendar/CalendarEvent";
3
+ import RestrictionTimes, {
4
+ RestrictionType,
5
+ WeeklyResctriction,
6
+ } from "../../../Types/OnCallDutyPolicy/RestrictionTimes";
7
+ import Recurring from "../../../Types/Events/Recurring";
8
+ import OneUptimeDate from "../../../Types/Date";
9
+ import User from "../../../Models/DatabaseModels/User";
10
+ import EventInterval from "../../../Types/Events/EventInterval";
11
+ import DayOfWeek from "../../../Types/Day/DayOfWeek";
12
+
13
+ /*
14
+ * Regression tests for the on-call audit fixes:
15
+ * 1. Daily/weekly restriction dropped coverage for every day after the first
16
+ * in a multi-day rotation period (getEventsByDailyRestriction case 2 used
17
+ * to terminate instead of advancing).
18
+ * 2. Weekly wrap-around restriction emitted phantom all-day windows when the
19
+ * resolution window did not start on the ISO-week boundary.
20
+ * 3. Multi-layer merge silently deleted a lower-priority (fallback) layer's
21
+ * coverage after two adjacent higher-priority windows.
22
+ *
23
+ * These assert observable on-call coverage (who is on call at a given instant),
24
+ * which is what actually matters operationally.
25
+ */
26
+
27
+ function user(id: string): User {
28
+ return {
29
+ id: {
30
+ toString: () => {
31
+ return id;
32
+ },
33
+ } as any,
34
+ } as User;
35
+ }
36
+
37
+ function rotation(t: EventInterval, c: number): Recurring {
38
+ return Recurring.fromJSON({
39
+ _type: "Recurring",
40
+ value: {
41
+ intervalType: t,
42
+ intervalCount: { _type: "PositiveNumber", value: c },
43
+ },
44
+ } as any);
45
+ }
46
+
47
+ function noRestriction(): RestrictionTimes {
48
+ const r: RestrictionTimes = new RestrictionTimes();
49
+ r.restictionType = RestrictionType.None;
50
+ r.dayRestrictionTimes = null;
51
+ return r;
52
+ }
53
+
54
+ function dailyRestriction(
55
+ startHour: number,
56
+ endHour: number,
57
+ ): RestrictionTimes {
58
+ const r: RestrictionTimes = new RestrictionTimes();
59
+ r.restictionType = RestrictionType.Daily;
60
+ r.dayRestrictionTimes = {
61
+ startTime: OneUptimeDate.getDateWithCustomTime({
62
+ hours: startHour,
63
+ minutes: 0,
64
+ seconds: 0,
65
+ }),
66
+ endTime: OneUptimeDate.getDateWithCustomTime({
67
+ hours: endHour,
68
+ minutes: 0,
69
+ seconds: 0,
70
+ }),
71
+ };
72
+ return r;
73
+ }
74
+
75
+ // Which user (event title) covers instant t? (start <= t < end). null if none.
76
+ function coveringUser(events: Array<CalendarEvent>, t: Date): string | null {
77
+ for (const e of events) {
78
+ if (e.start.getTime() <= t.getTime() && e.end.getTime() > t.getTime()) {
79
+ return e.title;
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+
85
+ // Seconds in [rangeStart, rangeEnd] not covered by any event (tolerating 1s seams).
86
+ function uncoveredSeconds(
87
+ events: Array<CalendarEvent>,
88
+ rangeStart: Date,
89
+ rangeEnd: Date,
90
+ ): number {
91
+ const sorted: Array<CalendarEvent> = [...events].sort(
92
+ (a: CalendarEvent, b: CalendarEvent) => {
93
+ return a.start.getTime() - b.start.getTime();
94
+ },
95
+ );
96
+ let uncovered: number = 0;
97
+ let cursor: number = rangeStart.getTime();
98
+ for (const e of sorted) {
99
+ if (e.start.getTime() > cursor + 1000) {
100
+ uncovered += e.start.getTime() - cursor;
101
+ }
102
+ cursor = Math.max(cursor, e.end.getTime());
103
+ }
104
+ if (rangeEnd.getTime() > cursor + 1000) {
105
+ uncovered += rangeEnd.getTime() - cursor;
106
+ }
107
+ return Math.round(uncovered / 1000);
108
+ }
109
+
110
+ describe("LayerUtil audit fixes", () => {
111
+ describe("Fix 1: daily restriction keeps coverage every day of a multi-day rotation", () => {
112
+ test("weekly rotation + daily 09:00-17:00 + handoff at/after 09:00", () => {
113
+ const layerUtil: LayerUtil = new LayerUtil();
114
+ const layerStart: Date = new Date(2026, 0, 5, 0, 0, 0); // Mon Jan 5 2026
115
+ const events: Array<CalendarEvent> = layerUtil.getEvents({
116
+ users: [user("A"), user("B")],
117
+ startDateTimeOfLayer: layerStart,
118
+ restrictionTimes: dailyRestriction(9, 17),
119
+ handOffTime: new Date(2026, 0, 5, 10, 0, 0), // 10:00 (after restriction start)
120
+ rotation: rotation(EventInterval.Week, 1),
121
+ calendarStartDate: layerStart,
122
+ calendarEndDate: new Date(2026, 0, 19, 0, 0, 0),
123
+ });
124
+
125
+ /*
126
+ * Week 1 (B is on call after the 10:00 handoff on Jan 5): every day Jan 6-11
127
+ * must be covered by B during the 09-17 window (the bug left these empty).
128
+ */
129
+ for (let day: number = 6; day <= 11; day++) {
130
+ expect(coveringUser(events, new Date(2026, 0, day, 11, 0, 0))).toBe(
131
+ "B",
132
+ );
133
+ }
134
+ // Week 2 (A): every day Jan 13-18 covered by A during the window.
135
+ for (let day: number = 13; day <= 18; day++) {
136
+ expect(coveringUser(events, new Date(2026, 0, day, 11, 0, 0))).toBe(
137
+ "A",
138
+ );
139
+ }
140
+ // Outside the restriction window: nobody on call.
141
+ expect(coveringUser(events, new Date(2026, 0, 7, 3, 0, 0))).toBeNull();
142
+ expect(coveringUser(events, new Date(2026, 0, 7, 20, 0, 0))).toBeNull();
143
+ });
144
+
145
+ test("Day x2 rotation + daily 09:00-17:00 + noon handoff covers in-between days", () => {
146
+ const layerUtil: LayerUtil = new LayerUtil();
147
+ const layerStart: Date = new Date(2026, 0, 5, 0, 0, 0);
148
+ const events: Array<CalendarEvent> = layerUtil.getEvents({
149
+ users: [user("A"), user("B")],
150
+ startDateTimeOfLayer: layerStart,
151
+ restrictionTimes: dailyRestriction(9, 17),
152
+ handOffTime: new Date(2026, 0, 5, 12, 0, 0),
153
+ rotation: rotation(EventInterval.Day, 2),
154
+ calendarStartDate: layerStart,
155
+ calendarEndDate: new Date(2026, 0, 13, 0, 0, 0),
156
+ });
157
+ // Jan 6 is the "second day" of B's 2-day period and used to be uncovered.
158
+ expect(coveringUser(events, new Date(2026, 0, 6, 11, 0, 0))).toBe("B");
159
+ // Jan 8 is the second day of A's next 2-day period.
160
+ expect(coveringUser(events, new Date(2026, 0, 8, 11, 0, 0))).toBe("A");
161
+ });
162
+ });
163
+
164
+ describe("Fix 2: weekly wrap-around restriction has no phantom coverage", () => {
165
+ const weekend: WeeklyResctriction = {
166
+ startDay: DayOfWeek.Friday,
167
+ endDay: DayOfWeek.Monday,
168
+ startTime: new Date(2026, 0, 2, 20, 0, 0), // Fri 20:00
169
+ endTime: new Date(2026, 0, 5, 8, 0, 0), // Mon 08:00
170
+ };
171
+
172
+ function weekendRestriction(): RestrictionTimes {
173
+ const r: RestrictionTimes = new RestrictionTimes();
174
+ r.restictionType = RestrictionType.Weekly;
175
+ r.weeklyRestrictionTimes = [weekend];
176
+ return r;
177
+ }
178
+
179
+ test("resolved mid-week (Wed): excluded weekdays have NO coverage", () => {
180
+ const layerUtil: LayerUtil = new LayerUtil();
181
+ const events: Array<CalendarEvent> = layerUtil.getEvents({
182
+ users: [user("A")],
183
+ startDateTimeOfLayer: new Date(2026, 0, 1, 0, 0, 0),
184
+ restrictionTimes: weekendRestriction(),
185
+ handOffTime: new Date(2026, 0, 1, 0, 0, 0),
186
+ rotation: rotation(EventInterval.Week, 1),
187
+ calendarStartDate: new Date(2026, 0, 7, 0, 0, 0), // Wed Jan 7 (NOT week-aligned)
188
+ calendarEndDate: new Date(2026, 0, 13, 0, 0, 0),
189
+ });
190
+ // Weekday daytime is OUTSIDE the Fri20:00->Mon08:00 window: nobody on call.
191
+ expect(coveringUser(events, new Date(2026, 0, 7, 14, 30, 0))).toBeNull(); // Wed
192
+ expect(coveringUser(events, new Date(2026, 0, 8, 10, 0, 0))).toBeNull(); // Thu
193
+ expect(coveringUser(events, new Date(2026, 0, 9, 12, 0, 0))).toBeNull(); // Fri noon
194
+ // Inside the weekend window: A is on call.
195
+ expect(coveringUser(events, new Date(2026, 0, 9, 22, 0, 0))).toBe("A"); // Fri 22:00
196
+ expect(coveringUser(events, new Date(2026, 0, 10, 12, 0, 0))).toBe("A"); // Sat
197
+ expect(coveringUser(events, new Date(2026, 0, 11, 12, 0, 0))).toBe("A"); // Sun
198
+ expect(coveringUser(events, new Date(2026, 0, 12, 7, 0, 0))).toBe("A"); // Mon 07:00
199
+ expect(coveringUser(events, new Date(2026, 0, 12, 9, 0, 0))).toBeNull(); // Mon 09:00 (after handoff)
200
+ });
201
+
202
+ test("live 1-second window mid-week returns nobody during excluded hours", () => {
203
+ const layerUtil: LayerUtil = new LayerUtil();
204
+ const resolveAt: (now: Date) => string | null = (
205
+ now: Date,
206
+ ): string | null => {
207
+ const events: Array<CalendarEvent> = layerUtil.getMultiLayerEvents(
208
+ {
209
+ layers: [
210
+ {
211
+ users: [user("A")],
212
+ startDateTimeOfLayer: new Date(2026, 0, 1, 0, 0, 0),
213
+ restrictionTimes: weekendRestriction(),
214
+ handOffTime: new Date(2026, 0, 1, 0, 0, 0),
215
+ rotation: rotation(EventInterval.Week, 1),
216
+ },
217
+ ],
218
+ calendarStartDate: now,
219
+ calendarEndDate: OneUptimeDate.addRemoveSeconds(now, 1),
220
+ },
221
+ { getNumberOfEvents: 1 },
222
+ );
223
+ return events[0]?.title ?? null;
224
+ };
225
+ expect(resolveAt(new Date(2026, 0, 7, 14, 30, 0))).toBeNull(); // Wed excluded
226
+ expect(resolveAt(new Date(2026, 0, 8, 10, 0, 0))).toBeNull(); // Thu excluded
227
+ expect(resolveAt(new Date(2026, 0, 10, 22, 0, 0))).toBe("A"); // Sat included
228
+ });
229
+
230
+ test("resolved on the week boundary (Mon) still correct (no regression)", () => {
231
+ const layerUtil: LayerUtil = new LayerUtil();
232
+ const events: Array<CalendarEvent> = layerUtil.getEvents({
233
+ users: [user("A")],
234
+ startDateTimeOfLayer: new Date(2026, 0, 1, 0, 0, 0),
235
+ restrictionTimes: weekendRestriction(),
236
+ handOffTime: new Date(2026, 0, 1, 0, 0, 0),
237
+ rotation: rotation(EventInterval.Week, 1),
238
+ calendarStartDate: new Date(2026, 0, 5, 0, 0, 0), // Mon Jan 5
239
+ calendarEndDate: new Date(2026, 0, 13, 0, 0, 0),
240
+ });
241
+ expect(coveringUser(events, new Date(2026, 0, 5, 7, 0, 0))).toBe("A"); // Mon 07:00 (tail of prior weekend)
242
+ expect(coveringUser(events, new Date(2026, 0, 7, 12, 0, 0))).toBeNull(); // Wed excluded
243
+ expect(coveringUser(events, new Date(2026, 0, 10, 12, 0, 0))).toBe("A"); // Sat included
244
+ });
245
+ });
246
+
247
+ describe("Fix 3: multi-layer fallback coverage survives adjacent higher-priority windows", () => {
248
+ test("24/7 fallback under a primary that emits two back-to-back windows", () => {
249
+ const layerUtil: LayerUtil = new LayerUtil();
250
+ const calStart: Date = new Date(2026, 2, 2, 0, 0, 0);
251
+ const calEnd: Date = new Date(2026, 2, 4, 0, 0, 0);
252
+
253
+ /*
254
+ * Primary: A/B hourly rotation restricted to 10:00-12:00, handoff at 11:00
255
+ * -> emits two adjacent windows per day: A[10:00-11:00], B[11:00:01-12:00].
256
+ */
257
+ const primary: LayerProps = {
258
+ users: [user("A"), user("B")],
259
+ startDateTimeOfLayer: calStart,
260
+ restrictionTimes: dailyRestriction(10, 12),
261
+ handOffTime: new Date(2026, 2, 2, 11, 0, 0),
262
+ rotation: rotation(EventInterval.Hour, 1),
263
+ };
264
+ // Fallback: C on call 24/7.
265
+ const fallback: LayerProps = {
266
+ users: [user("C")],
267
+ startDateTimeOfLayer: calStart,
268
+ restrictionTimes: noRestriction(),
269
+ handOffTime: new Date(2026, 2, 2, 0, 0, 0),
270
+ rotation: rotation(EventInterval.Week, 1),
271
+ };
272
+
273
+ const events: Array<CalendarEvent> = layerUtil.getMultiLayerEvents({
274
+ layers: [primary, fallback],
275
+ calendarStartDate: calStart,
276
+ calendarEndDate: calEnd,
277
+ });
278
+
279
+ // No uncovered time anywhere in the window (fallback fills every gap).
280
+ expect(uncoveredSeconds(events, calStart, calEnd)).toBe(0);
281
+
282
+ // Primary owns 10:00-12:00; fallback C owns everything else.
283
+ expect(coveringUser(events, new Date(2026, 2, 2, 10, 30, 0))).toBe("A");
284
+ expect(coveringUser(events, new Date(2026, 2, 2, 11, 30, 0))).toBe("B");
285
+ expect(coveringUser(events, new Date(2026, 2, 2, 15, 0, 0))).toBe("C"); // afternoon
286
+ expect(coveringUser(events, new Date(2026, 2, 3, 3, 0, 0))).toBe("C"); // overnight
287
+ expect(coveringUser(events, new Date(2026, 2, 3, 8, 0, 0))).toBe("C"); // next morning
288
+ expect(coveringUser(events, new Date(2026, 2, 3, 10, 30, 0))).toBe("A"); // next day primary
289
+ });
290
+ });
291
+ });