@oneuptime/common 12.0.29 → 12.0.31

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 (664) hide show
  1. package/Models/AnalyticsModels/SecurityEvent.ts +48 -16
  2. package/Models/AnalyticsModels/ThreatIntelIndicator.ts +10 -5
  3. package/Models/DatabaseModels/AlertSeverity.ts +44 -0
  4. package/Models/DatabaseModels/DetectionRule.ts +16 -5
  5. package/Models/DatabaseModels/GoogleSecOpsConnection.ts +14 -5
  6. package/Models/DatabaseModels/IncidentSeverity.ts +44 -0
  7. package/Models/DatabaseModels/Index.ts +13 -0
  8. package/Models/DatabaseModels/NetworkDevice.ts +113 -3
  9. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +88 -0
  10. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +22 -0
  11. package/Models/DatabaseModels/NetworkDeviceOidTemplate.ts +499 -0
  12. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +43 -0
  13. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +662 -0
  14. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +584 -0
  15. package/Models/DatabaseModels/ThreatIntelFeed.ts +16 -5
  16. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +546 -0
  17. package/Models/DatabaseModels/UserOnCallShiftReminder.ts +231 -0
  18. package/Models/DatabaseModels/UserOnCallShiftReminderLog.ts +331 -0
  19. package/Server/API/GlobalConfigAPI.ts +362 -183
  20. package/Server/API/OnCallCalendarAPI.ts +2194 -0
  21. package/Server/API/TelemetryAPI.ts +42 -4
  22. package/Server/EnvironmentConfig.ts +250 -0
  23. package/Server/Infrastructure/OnCallCalendarFeedCache.ts +1050 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.ts +253 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.ts +59 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.ts +25 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  28. package/Server/Middleware/MultipartFormData.ts +118 -32
  29. package/Server/Middleware/OnCallCalendarFeedRateLimit.ts +487 -0
  30. package/Server/Services/AlertEpisodeLabelRuleEngineService.ts +9 -1
  31. package/Server/Services/AlertEpisodeOnCallRuleEngineService.ts +9 -1
  32. package/Server/Services/AlertEpisodeOwnerRuleEngineService.ts +9 -1
  33. package/Server/Services/AlertEpisodePrivacyRuleEngineService.ts +9 -1
  34. package/Server/Services/AlertGroupingEngineService.ts +9 -1
  35. package/Server/Services/AlertLabelRuleEngineService.ts +9 -1
  36. package/Server/Services/AlertOnCallRuleEngineService.ts +9 -1
  37. package/Server/Services/AlertOwnerRuleEngineService.ts +9 -1
  38. package/Server/Services/AlertPrivacyRuleEngineService.ts +9 -1
  39. package/Server/Services/AlertReminderRuleService.ts +9 -1
  40. package/Server/Services/AutoRemediationRuleEngineService.ts +9 -1
  41. package/Server/Services/CephClusterLabelRuleEngineService.ts +9 -1
  42. package/Server/Services/CephClusterOwnerRuleEngineService.ts +9 -1
  43. package/Server/Services/CloudResourceLabelRuleEngineService.ts +9 -1
  44. package/Server/Services/CloudResourceOwnerRuleEngineService.ts +9 -1
  45. package/Server/Services/DashboardLabelRuleEngineService.ts +9 -1
  46. package/Server/Services/DashboardOwnerRuleEngineService.ts +9 -1
  47. package/Server/Services/DockerHostLabelRuleEngineService.ts +9 -1
  48. package/Server/Services/DockerHostOwnerRuleEngineService.ts +9 -1
  49. package/Server/Services/DockerSwarmClusterLabelRuleEngineService.ts +9 -1
  50. package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +9 -1
  51. package/Server/Services/HostLabelRuleEngineService.ts +9 -1
  52. package/Server/Services/HostOwnerRuleEngineService.ts +9 -1
  53. package/Server/Services/IncidentEpisodeLabelRuleEngineService.ts +9 -1
  54. package/Server/Services/IncidentEpisodeOnCallRuleEngineService.ts +9 -1
  55. package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +9 -1
  56. package/Server/Services/IncidentEpisodePrivacyRuleEngineService.ts +9 -1
  57. package/Server/Services/IncidentGroupingEngineService.ts +9 -1
  58. package/Server/Services/IncidentLabelRuleEngineService.ts +9 -1
  59. package/Server/Services/IncidentOnCallRuleEngineService.ts +9 -1
  60. package/Server/Services/IncidentOwnerRuleEngineService.ts +9 -1
  61. package/Server/Services/IncidentPrivacyRuleEngineService.ts +9 -1
  62. package/Server/Services/IncidentReminderRuleService.ts +9 -1
  63. package/Server/Services/IncidentSlaRuleService.ts +9 -1
  64. package/Server/Services/IncomingCallPolicyLabelRuleEngineService.ts +9 -1
  65. package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +9 -1
  66. package/Server/Services/Index.ts +14 -0
  67. package/Server/Services/IoTFleetLabelRuleEngineService.ts +9 -1
  68. package/Server/Services/IoTFleetOwnerRuleEngineService.ts +9 -1
  69. package/Server/Services/KubernetesClusterLabelRuleEngineService.ts +9 -1
  70. package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +9 -1
  71. package/Server/Services/KubernetesResourceService.ts +5 -1
  72. package/Server/Services/MonitorLabelRuleEngineService.ts +9 -1
  73. package/Server/Services/MonitorOwnerRuleEngineService.ts +9 -1
  74. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +35 -0
  75. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +96 -0
  76. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +9 -1
  77. package/Server/Services/NetworkDeviceOidTemplateService.ts +201 -0
  78. package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +9 -1
  79. package/Server/Services/NetworkDeviceService.ts +209 -1
  80. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +32 -0
  81. package/Server/Services/OnCallDutyPolicyLabelRuleEngineService.ts +9 -1
  82. package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +9 -1
  83. package/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.ts +260 -0
  84. package/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.ts +9 -1
  85. package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +70 -3
  86. package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +138 -3
  87. package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +9 -1
  88. package/Server/Services/OnCallDutyPolicyScheduleService.ts +1465 -134
  89. package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +167 -31
  90. package/Server/Services/PodmanHostLabelRuleEngineService.ts +9 -1
  91. package/Server/Services/PodmanHostOwnerRuleEngineService.ts +9 -1
  92. package/Server/Services/ProjectOnCallCalendarFeedService.ts +192 -0
  93. package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +9 -1
  94. package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +9 -1
  95. package/Server/Services/RumApplicationLabelRuleEngineService.ts +9 -1
  96. package/Server/Services/RumApplicationOwnerRuleEngineService.ts +9 -1
  97. package/Server/Services/RunbookLabelRuleEngineService.ts +9 -1
  98. package/Server/Services/RunbookOwnerRuleEngineService.ts +9 -1
  99. package/Server/Services/RunbookRuleEngineService.ts +9 -1
  100. package/Server/Services/ScheduledMaintenanceLabelRuleEngineService.ts +9 -1
  101. package/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.ts +9 -1
  102. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +9 -1
  103. package/Server/Services/ServerlessFunctionLabelRuleEngineService.ts +9 -1
  104. package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +9 -1
  105. package/Server/Services/ServiceLabelRuleEngineService.ts +9 -1
  106. package/Server/Services/ServiceOwnerRuleEngineService.ts +9 -1
  107. package/Server/Services/StatusPageLabelRuleEngineService.ts +9 -1
  108. package/Server/Services/StatusPageMonitorRuleEngineService.ts +7 -0
  109. package/Server/Services/StatusPageOwnerRuleEngineService.ts +9 -1
  110. package/Server/Services/TeamMemberService.ts +489 -1
  111. package/Server/Services/TeamService.ts +50 -0
  112. package/Server/Services/TelemetrySourceMapService.ts +266 -23
  113. package/Server/Services/UserNotificationSettingService.ts +44 -1
  114. package/Server/Services/UserOnCallCalendarFeedService.ts +159 -0
  115. package/Server/Services/UserOnCallShiftReminderLogService.ts +151 -0
  116. package/Server/Services/UserOnCallShiftReminderService.ts +112 -0
  117. package/Server/Services/UserService.ts +27 -0
  118. package/Server/Services/WorkflowLabelRuleEngineService.ts +9 -1
  119. package/Server/Services/WorkflowOwnerRuleEngineService.ts +9 -1
  120. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +186 -0
  121. package/Server/Utils/Monitor/CapturedMetricAttributeUtil.ts +245 -0
  122. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +21 -2
  123. package/Server/Utils/Monitor/MonitorMetricUtil.ts +55 -21
  124. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +32 -0
  125. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +15 -2
  126. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +18 -1
  127. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +10 -0
  128. package/Server/Utils/Monitor/SeriesResourceLabels.ts +34 -0
  129. package/Server/Utils/OnCall/CalendarFeedToken.ts +241 -0
  130. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1488 -0
  131. package/Server/Utils/OnCall/OnCallCalendarFeedUrls.ts +230 -0
  132. package/Server/Utils/OnCall/OnCallShiftChangeListeners.ts +227 -0
  133. package/Server/Utils/OnCall/OnCallShiftMaterializer.ts +1127 -0
  134. package/Server/Utils/OnCall/OnCallShiftReminderListener.ts +174 -0
  135. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +2584 -0
  136. package/Server/Utils/Response.ts +134 -0
  137. package/Server/Utils/Rules/RuleEngineRuleRead.ts +48 -0
  138. package/Server/Utils/StartServer.ts +10 -0
  139. package/Server/Utils/Telemetry/AppMetrics.ts +51 -0
  140. package/Server/Utils/Telemetry/ExceptionSanitizer.ts +14 -1
  141. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +14 -2
  142. package/Server/Utils/Telemetry/SourceMapResolver.ts +16 -5
  143. package/Server/Utils/Telemetry/StackTraceParser.ts +399 -0
  144. package/Tests/App/Dashboard/EventOverlayHook.test.tsx +125 -0
  145. package/Tests/App/Dashboard/HeaderImageAssets.test.tsx +93 -0
  146. package/Tests/App/Dashboard/OnCallCalendarFeedLinks.test.tsx +229 -0
  147. package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +489 -0
  148. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +149 -0
  149. package/Tests/App/Dashboard/OnCallCalendarFeedUtil.test.ts +855 -0
  150. package/Tests/App/Dashboard/OnCallPersonalCalendarFeed.test.tsx +1209 -0
  151. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3 -3
  152. package/Tests/App/Dashboard/OnCallSharedCalendarFeedCard.test.tsx +1019 -0
  153. package/Tests/App/Dashboard/OnCallShiftRemindersAndUpcomingShifts.test.tsx +655 -0
  154. package/Tests/App/Dashboard/UserElementEmail.test.tsx +12 -4
  155. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +1 -1
  156. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +73 -0
  157. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +14 -0
  158. package/Tests/Models/DatabaseModels/ModelRegistryInvariants.test.ts +469 -0
  159. package/Tests/Models/OnCallCalendarFeedModels.test.ts +642 -0
  160. package/Tests/Models/OnCallDutyPolicyScheduleCalendarFeed.test.ts +288 -0
  161. package/Tests/Models/OnCallDutyPolicyScheduleShiftConfigVersion.test.ts +88 -0
  162. package/Tests/Models/SecurityDomainAccessControl.test.ts +429 -0
  163. package/Tests/Models/UserOnCallShiftReminderModels.test.ts +368 -0
  164. package/Tests/Server/API/GlobalConfigLicense.test.ts +657 -0
  165. package/Tests/Server/API/Helpers.ts +8 -0
  166. package/Tests/Server/API/OnCallCalendarAPI.test.ts +4461 -0
  167. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +84 -6
  168. package/Tests/Server/API/TelemetryResolveStackTraceAPI.test.ts +805 -0
  169. package/Tests/Server/EnvironmentConfigOnCallCalendarFeed.test.ts +357 -0
  170. package/Tests/Server/Infrastructure/OnCallCalendarFeedCache.test.ts +1589 -0
  171. package/Tests/Server/Infrastructure/Postgres/AddOnCallCalendarFeedsMigration.test.ts +394 -0
  172. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +1144 -0
  173. package/Tests/Server/Middleware/MultipartFormData.test.ts +618 -3
  174. package/Tests/Server/Middleware/OnCallCalendarFeedRateLimit.test.ts +1365 -0
  175. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +70 -0
  176. package/Tests/Server/Services/MonitorLabelRuleEngineScale.test.ts +520 -0
  177. package/Tests/Server/Services/NetworkDeviceOidTemplateService.test.ts +332 -0
  178. package/Tests/Server/Services/NetworkDeviceOidTemplateWriteGuards.test.ts +409 -0
  179. package/Tests/Server/Services/OnCallCalendarFeedServices.test.ts +1103 -0
  180. package/Tests/Server/Services/OnCallDutyPolicyScheduleResolver.test.ts +1037 -0
  181. package/Tests/Server/Services/OnCallShiftConfigPropagation.test.ts +1270 -0
  182. package/Tests/Server/Services/RuleEngineRuleFetchLimit.test.ts +239 -0
  183. package/Tests/Server/Services/TeamDeleteMemberCleanup.test.ts +156 -0
  184. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +755 -0
  185. package/Tests/Server/Services/TelemetrySourceMapService.test.ts +1210 -2
  186. package/Tests/Server/Services/UserNotificationSettingShiftReminderDefaults.test.ts +267 -0
  187. package/Tests/Server/Services/UserOnCallShiftReminderServices.test.ts +583 -0
  188. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +182 -0
  189. package/Tests/Server/Types/AnalyticsDatabase/SecurityEventModelPermission.test.ts +255 -0
  190. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +254 -0
  191. package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +212 -0
  192. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +413 -0
  193. package/Tests/Server/Utils/CodeRepository.test.ts +689 -0
  194. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +495 -0
  195. package/Tests/Server/Utils/ExceptionSanitizer.test.ts +77 -0
  196. package/Tests/Server/Utils/Monitor/CapturedMetricAttributeUtil.test.ts +284 -0
  197. package/Tests/Server/Utils/Monitor/MonitorCustomMetricAttributes.test.ts +503 -0
  198. package/Tests/Server/Utils/Monitor/MonitorTemplatePrototypePollution.test.ts +106 -0
  199. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkLogWritePath.test.ts +33 -4
  200. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +14 -4
  201. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +44 -0
  202. package/Tests/Server/Utils/OnCall/CalendarFeedToken.test.ts +467 -0
  203. package/Tests/Server/Utils/OnCall/OnCallCalendarFeedRenderer.test.ts +2142 -0
  204. package/Tests/Server/Utils/OnCall/OnCallCalendarFeedUrls.test.ts +278 -0
  205. package/Tests/Server/Utils/OnCall/OnCallResolverTestHarness.ts +440 -0
  206. package/Tests/Server/Utils/OnCall/OnCallShiftChangeListeners.test.ts +314 -0
  207. package/Tests/Server/Utils/OnCall/OnCallShiftMaterializer.test.ts +1502 -0
  208. package/Tests/Server/Utils/OnCall/OnCallShiftReminderListener.test.ts +491 -0
  209. package/Tests/Server/Utils/OnCall/OnCallShiftReminderRunner.test.ts +3088 -0
  210. package/Tests/Server/Utils/OnCall/OnCallShiftReminderTestHarness.ts +910 -0
  211. package/Tests/Server/Utils/OpenAPISpec.test.ts +536 -0
  212. package/Tests/Server/Utils/ResponseSendCalendarResponse.test.ts +561 -0
  213. package/Tests/Server/Utils/Rules/RuleEngineRuleRead.test.ts +115 -0
  214. package/Tests/Server/Utils/StackTraceParser.test.ts +610 -0
  215. package/Tests/Server/Utils/StartServerEncryptionSecretWarning.test.ts +64 -0
  216. package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +29 -0
  217. package/Tests/Server/Utils/Telemetry/LogExceptionExtractor.test.ts +0 -0
  218. package/Tests/Server/Utils/Telemetry/SourceMapLimits.test.ts +656 -0
  219. package/Tests/Server/Utils/Telemetry/SourceMapResolver.test.ts +100 -0
  220. package/Tests/Types/Calendar/ICalendar.test.ts +488 -0
  221. package/Tests/Types/Monitor/SnmpMonitor/SnmpOidListUtil.test.ts +454 -0
  222. package/Tests/Types/NotificationSetting/NotificationSettingEventType.test.ts +53 -0
  223. package/Tests/Types/OnCallDutyPolicy/CalendarFeedTestFixtures.ts +310 -0
  224. package/Tests/Types/OnCallDutyPolicy/CalendarFeedWindow.test.ts +205 -0
  225. package/Tests/Types/OnCallDutyPolicy/LayerUtilLayerMetaAndIterationCap.test.ts +458 -0
  226. package/Tests/Types/OnCallDutyPolicy/MaterializedShift.test.ts +197 -0
  227. package/Tests/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.test.ts +1986 -0
  228. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtilGroupKey.test.ts +678 -0
  229. package/Tests/Types/OnCallDutyPolicy/ShiftSeamUtil.test.ts +417 -0
  230. package/Tests/Types/SecurityRolePermissions.test.ts +193 -0
  231. package/Tests/UI/Components/Charts/AnnotationLayout.test.ts +773 -0
  232. package/Tests/UI/Components/Charts/ChartAnnotationRail.test.tsx +789 -0
  233. package/Tests/UI/Components/Charts/ChartBucketIdentity.test.ts +254 -0
  234. package/Tests/UI/Components/Charts/TimeAnnotation.test.ts +471 -0
  235. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +405 -0
  236. package/Tests/UI/Components/ErrorMessageRefreshControl.test.tsx +83 -0
  237. package/Tests/UI/Components/MarkdownConverters.test.ts +686 -0
  238. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteAndEmptyStates.test.tsx +341 -0
  239. package/Tests/UI/Components/OrderedStatesList.test.tsx +5 -5
  240. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +1 -1
  241. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +195 -0
  242. package/Tests/UI/Components/XAxis.test.ts +270 -5
  243. package/Tests/UI/Components/XAxisDaylightSaving.test.ts +113 -0
  244. package/Tests/UI/Styles/ChartFocusRing.test.ts +153 -0
  245. package/Tests/UI/Styles/ChartFocusRingCoverage.test.tsx +377 -0
  246. package/Tests/UI/Styles/ThemeStylesheet.ts +162 -0
  247. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  248. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  249. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +507 -0
  250. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +951 -1
  251. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +238 -0
  252. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +1056 -0
  253. package/Tests/Utils/NetworkDiscovery/ReverseDnsNameUtil.test.ts +882 -0
  254. package/Types/Calendar/ICalendar.ts +442 -0
  255. package/Types/Date.ts +39 -0
  256. package/Types/Email/EmailTemplateType.ts +2 -0
  257. package/Types/Monitor/SnmpMonitor/SnmpOidListUtil.ts +333 -0
  258. package/Types/NotificationSetting/NotificationSettingEventType.ts +4 -0
  259. package/Types/OnCallDutyPolicy/CalendarFeedWindow.ts +140 -0
  260. package/Types/OnCallDutyPolicy/Layer.ts +190 -35
  261. package/Types/OnCallDutyPolicy/MaterializedShift.ts +274 -0
  262. package/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.ts +1415 -0
  263. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +159 -4
  264. package/Types/OnCallDutyPolicy/ShiftSeamUtil.ts +106 -0
  265. package/Types/Permission.ts +96 -0
  266. package/Types/Rum/SessionReplay.ts +21 -0
  267. package/Types/Telemetry/SourceMap.ts +8 -0
  268. package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.tsx +176 -0
  269. package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.ts +745 -0
  270. package/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.tsx +576 -0
  271. package/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.tsx +204 -0
  272. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +491 -544
  273. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +324 -372
  274. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +487 -540
  275. package/UI/Components/Charts/ChartLibrary/Types/FormattedReferenceRegion.ts +7 -3
  276. package/UI/Components/Charts/ChartLibrary/Types/FormattedTimeReferenceLine.ts +14 -3
  277. package/UI/Components/Charts/Types/ChartEventKind.ts +13 -0
  278. package/UI/Components/Charts/Types/ReferenceRegionProps.ts +6 -0
  279. package/UI/Components/Charts/Types/TimeReferenceLineProps.ts +20 -3
  280. package/UI/Components/Charts/Utils/TimeAnnotation.ts +69 -35
  281. package/UI/Components/Charts/Utils/XAxis.ts +125 -18
  282. package/UI/Components/EditionLabel/EditionLabel.tsx +280 -26
  283. package/UI/Components/ErrorMessage/ErrorMessage.tsx +21 -10
  284. package/UI/Components/Feed/FeedItem.tsx +4 -5
  285. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -2
  286. package/UI/Components/Header/UserProfile/UserProfile.tsx +1 -2
  287. package/UI/Components/Image/Image.tsx +20 -3
  288. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  289. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +1 -2
  290. package/UI/Components/ModelTable/BaseModelTable.tsx +150 -24
  291. package/UI/Components/Table/TableHeader.tsx +64 -48
  292. package/UI/Components/Table/TableRow.tsx +13 -3
  293. package/UI/Styles/Theme.css +38 -0
  294. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +271 -0
  295. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +6 -0
  296. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +141 -10
  297. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +55 -3
  298. package/Utils/NetworkDiscovery/ReverseDnsNameUtil.ts +151 -0
  299. package/Utils/Rules/RuleEngineLimits.ts +32 -0
  300. package/build/dist/Models/AnalyticsModels/SecurityEvent.js +48 -16
  301. package/build/dist/Models/AnalyticsModels/SecurityEvent.js.map +1 -1
  302. package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js +10 -5
  303. package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js.map +1 -1
  304. package/build/dist/Models/DatabaseModels/AlertSeverity.js +46 -1
  305. package/build/dist/Models/DatabaseModels/AlertSeverity.js.map +1 -1
  306. package/build/dist/Models/DatabaseModels/DetectionRule.js +16 -5
  307. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  308. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +14 -5
  309. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +1 -1
  310. package/build/dist/Models/DatabaseModels/IncidentSeverity.js +46 -1
  311. package/build/dist/Models/DatabaseModels/IncidentSeverity.js.map +1 -1
  312. package/build/dist/Models/DatabaseModels/Index.js +13 -0
  313. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  314. package/build/dist/Models/DatabaseModels/NetworkDevice.js +115 -3
  315. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  316. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +88 -0
  317. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
  318. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  319. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js +519 -0
  320. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js.map +1 -0
  321. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +44 -0
  322. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  323. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +699 -0
  324. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -0
  325. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +620 -0
  326. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -0
  327. package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js +16 -5
  328. package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js.map +1 -1
  329. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +585 -0
  330. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -0
  331. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js +247 -0
  332. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js.map +1 -0
  333. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js +355 -0
  334. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js.map +1 -0
  335. package/build/dist/Server/API/GlobalConfigAPI.js +293 -176
  336. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  337. package/build/dist/Server/API/OnCallCalendarAPI.js +1371 -0
  338. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -0
  339. package/build/dist/Server/API/TelemetryAPI.js +31 -3
  340. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  341. package/build/dist/Server/EnvironmentConfig.js +185 -0
  342. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  343. package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js +663 -0
  344. package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js.map +1 -0
  345. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js +103 -0
  346. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js.map +1 -0
  347. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js +26 -0
  348. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js.map +1 -0
  349. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js +14 -0
  350. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js.map +1 -0
  351. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  352. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  353. package/build/dist/Server/Middleware/MultipartFormData.js +93 -28
  354. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  355. package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js +342 -0
  356. package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js.map +1 -0
  357. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js +8 -1
  358. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js.map +1 -1
  359. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js +8 -1
  360. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js.map +1 -1
  361. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js +8 -1
  362. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js.map +1 -1
  363. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js +8 -1
  364. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js.map +1 -1
  365. package/build/dist/Server/Services/AlertGroupingEngineService.js +8 -1
  366. package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
  367. package/build/dist/Server/Services/AlertLabelRuleEngineService.js +8 -1
  368. package/build/dist/Server/Services/AlertLabelRuleEngineService.js.map +1 -1
  369. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js +8 -1
  370. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js.map +1 -1
  371. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js +8 -1
  372. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js.map +1 -1
  373. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js +8 -1
  374. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js.map +1 -1
  375. package/build/dist/Server/Services/AlertReminderRuleService.js +8 -1
  376. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  377. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +8 -1
  378. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  379. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +8 -1
  380. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -1
  381. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +8 -1
  382. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
  383. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js +8 -1
  384. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js.map +1 -1
  385. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +8 -1
  386. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
  387. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js +8 -1
  388. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js.map +1 -1
  389. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +8 -1
  390. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
  391. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js +8 -1
  392. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js.map +1 -1
  393. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +8 -1
  394. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
  395. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js +8 -1
  396. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js.map +1 -1
  397. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +8 -1
  398. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
  399. package/build/dist/Server/Services/HostLabelRuleEngineService.js +8 -1
  400. package/build/dist/Server/Services/HostLabelRuleEngineService.js.map +1 -1
  401. package/build/dist/Server/Services/HostOwnerRuleEngineService.js +8 -1
  402. package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
  403. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js +8 -1
  404. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js.map +1 -1
  405. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js +8 -1
  406. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js.map +1 -1
  407. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +8 -1
  408. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
  409. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js +8 -1
  410. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js.map +1 -1
  411. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -1
  412. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  413. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js +8 -1
  414. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js.map +1 -1
  415. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js +8 -1
  416. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js.map +1 -1
  417. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js +8 -1
  418. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js.map +1 -1
  419. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js +8 -1
  420. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js.map +1 -1
  421. package/build/dist/Server/Services/IncidentReminderRuleService.js +8 -1
  422. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  423. package/build/dist/Server/Services/IncidentSlaRuleService.js +8 -1
  424. package/build/dist/Server/Services/IncidentSlaRuleService.js.map +1 -1
  425. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js +8 -1
  426. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js.map +1 -1
  427. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +8 -1
  428. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
  429. package/build/dist/Server/Services/Index.js +14 -0
  430. package/build/dist/Server/Services/Index.js.map +1 -1
  431. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js +8 -1
  432. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js.map +1 -1
  433. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +8 -1
  434. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
  435. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js +8 -1
  436. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js.map +1 -1
  437. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +8 -1
  438. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
  439. package/build/dist/Server/Services/KubernetesResourceService.js +5 -1
  440. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  441. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js +8 -1
  442. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js.map +1 -1
  443. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js +8 -1
  444. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js.map +1 -1
  445. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +30 -21
  446. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  447. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +64 -0
  448. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  449. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +8 -1
  450. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
  451. package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js +180 -0
  452. package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js.map +1 -0
  453. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +8 -1
  454. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
  455. package/build/dist/Server/Services/NetworkDeviceService.js +154 -1
  456. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  457. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +20 -0
  458. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  459. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js +8 -1
  460. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js.map +1 -1
  461. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +8 -1
  462. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
  463. package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js +219 -0
  464. package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js.map +1 -0
  465. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js +8 -1
  466. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js.map +1 -1
  467. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +47 -3
  468. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
  469. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +112 -3
  470. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
  471. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +8 -1
  472. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
  473. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +955 -9
  474. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  475. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +93 -6
  476. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
  477. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js +8 -1
  478. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js.map +1 -1
  479. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +8 -1
  480. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
  481. package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js +157 -0
  482. package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js.map +1 -0
  483. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +8 -1
  484. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -1
  485. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +8 -1
  486. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
  487. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js +8 -1
  488. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js.map +1 -1
  489. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +8 -1
  490. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
  491. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js +8 -1
  492. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js.map +1 -1
  493. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +8 -1
  494. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
  495. package/build/dist/Server/Services/RunbookRuleEngineService.js +8 -1
  496. package/build/dist/Server/Services/RunbookRuleEngineService.js.map +1 -1
  497. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js +8 -1
  498. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js.map +1 -1
  499. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js +8 -1
  500. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js.map +1 -1
  501. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +8 -1
  502. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  503. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js +8 -1
  504. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js.map +1 -1
  505. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +8 -1
  506. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
  507. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js +8 -1
  508. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js.map +1 -1
  509. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +8 -1
  510. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
  511. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js +8 -1
  512. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js.map +1 -1
  513. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +6 -0
  514. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -1
  515. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js +8 -1
  516. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js.map +1 -1
  517. package/build/dist/Server/Services/TeamMemberService.js +396 -1
  518. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  519. package/build/dist/Server/Services/TeamService.js +43 -0
  520. package/build/dist/Server/Services/TeamService.js.map +1 -1
  521. package/build/dist/Server/Services/TelemetrySourceMapService.js +213 -19
  522. package/build/dist/Server/Services/TelemetrySourceMapService.js.map +1 -1
  523. package/build/dist/Server/Services/UserNotificationSettingService.js +26 -1
  524. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  525. package/build/dist/Server/Services/UserOnCallCalendarFeedService.js +140 -0
  526. package/build/dist/Server/Services/UserOnCallCalendarFeedService.js.map +1 -0
  527. package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js +116 -0
  528. package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js.map +1 -0
  529. package/build/dist/Server/Services/UserOnCallShiftReminderService.js +92 -0
  530. package/build/dist/Server/Services/UserOnCallShiftReminderService.js.map +1 -0
  531. package/build/dist/Server/Services/UserService.js +25 -0
  532. package/build/dist/Server/Services/UserService.js.map +1 -1
  533. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js +8 -1
  534. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js.map +1 -1
  535. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +8 -1
  536. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
  537. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +145 -0
  538. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +1 -0
  539. package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js +201 -0
  540. package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js.map +1 -0
  541. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +19 -2
  542. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  543. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +42 -23
  544. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  545. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +29 -0
  546. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  547. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +11 -2
  548. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
  549. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +16 -1
  550. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
  551. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +10 -0
  552. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  553. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +33 -0
  554. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  555. package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js +161 -0
  556. package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js.map +1 -0
  557. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js +983 -0
  558. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js.map +1 -0
  559. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js +155 -0
  560. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js.map +1 -0
  561. package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js +145 -0
  562. package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js.map +1 -0
  563. package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js +752 -0
  564. package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js.map +1 -0
  565. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js +92 -0
  566. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js.map +1 -0
  567. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1786 -0
  568. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -0
  569. package/build/dist/Server/Utils/Response.js +97 -0
  570. package/build/dist/Server/Utils/Response.js.map +1 -1
  571. package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js +37 -0
  572. package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js.map +1 -0
  573. package/build/dist/Server/Utils/StartServer.js +9 -1
  574. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  575. package/build/dist/Server/Utils/Telemetry/AppMetrics.js +41 -0
  576. package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
  577. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +12 -1
  578. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -1
  579. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +14 -2
  580. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  581. package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js +15 -5
  582. package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js.map +1 -1
  583. package/build/dist/Server/Utils/Telemetry/StackTraceParser.js +307 -0
  584. package/build/dist/Server/Utils/Telemetry/StackTraceParser.js.map +1 -1
  585. package/build/dist/Types/Calendar/ICalendar.js +290 -0
  586. package/build/dist/Types/Calendar/ICalendar.js.map +1 -0
  587. package/build/dist/Types/Date.js +27 -0
  588. package/build/dist/Types/Date.js.map +1 -1
  589. package/build/dist/Types/Email/EmailTemplateType.js +2 -0
  590. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  591. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js +253 -0
  592. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js.map +1 -0
  593. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
  594. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
  595. package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js +97 -0
  596. package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js.map +1 -0
  597. package/build/dist/Types/OnCallDutyPolicy/Layer.js +113 -19
  598. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  599. package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js +134 -0
  600. package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js.map +1 -0
  601. package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js +800 -0
  602. package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js.map +1 -0
  603. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +97 -3
  604. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  605. package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js +77 -0
  606. package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js.map +1 -0
  607. package/build/dist/Types/Permission.js +86 -0
  608. package/build/dist/Types/Permission.js.map +1 -1
  609. package/build/dist/Types/Rum/SessionReplay.js +19 -0
  610. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  611. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js +83 -0
  612. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js.map +1 -0
  613. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js +531 -0
  614. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js.map +1 -0
  615. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js +264 -0
  616. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js.map +1 -0
  617. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js +122 -0
  618. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js.map +1 -0
  619. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +213 -233
  620. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  621. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +139 -152
  622. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  623. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +212 -232
  624. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  625. package/build/dist/UI/Components/Charts/Types/ChartEventKind.js +14 -0
  626. package/build/dist/UI/Components/Charts/Types/ChartEventKind.js.map +1 -0
  627. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js +57 -28
  628. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js.map +1 -1
  629. package/build/dist/UI/Components/Charts/Utils/XAxis.js +103 -17
  630. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  631. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +198 -23
  632. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  633. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +15 -5
  634. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
  635. package/build/dist/UI/Components/Feed/FeedItem.js +4 -5
  636. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  637. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -2
  638. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  639. package/build/dist/UI/Components/Header/UserProfile/UserProfile.js.map +1 -1
  640. package/build/dist/UI/Components/Image/Image.js +6 -1
  641. package/build/dist/UI/Components/Image/Image.js.map +1 -1
  642. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  643. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  644. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +1 -2
  645. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  646. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +104 -23
  647. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  648. package/build/dist/UI/Components/Table/TableHeader.js +20 -22
  649. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  650. package/build/dist/UI/Components/Table/TableRow.js +13 -4
  651. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  652. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +153 -0
  653. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -0
  654. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  655. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +127 -7
  656. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
  657. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +50 -3
  658. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  659. package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js +132 -0
  660. package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js.map +1 -0
  661. package/build/dist/Utils/Rules/RuleEngineLimits.js +31 -0
  662. package/build/dist/Utils/Rules/RuleEngineLimits.js.map +1 -0
  663. package/jest.config.json +1 -0
  664. package/package.json +2 -2
@@ -0,0 +1,1270 @@
1
+ import OnCallDutyPolicyScheduleService from "../../../Server/Services/OnCallDutyPolicyScheduleService";
2
+ import OnCallDutyPolicyScheduleLayerService from "../../../Server/Services/OnCallDutyPolicyScheduleLayerService";
3
+ import OnCallDutyPolicyScheduleLayerUserService from "../../../Server/Services/OnCallDutyPolicyScheduleLayerUserService";
4
+ import OnCallDutyPolicyUserOverrideService from "../../../Server/Services/OnCallDutyPolicyUserOverrideService";
5
+ import OnCallDutyPolicyEscalationRuleScheduleService from "../../../Server/Services/OnCallDutyPolicyEscalationRuleScheduleService";
6
+ import OnCallDutyPolicyFeedService from "../../../Server/Services/OnCallDutyPolicyFeedService";
7
+ import OnCallDutyPolicyTimeLogService from "../../../Server/Services/OnCallDutyPolicyTimeLogService";
8
+ import OnCallCalendarFeedCache from "../../../Server/Infrastructure/OnCallCalendarFeedCache";
9
+ import Semaphore from "../../../Server/Infrastructure/Semaphore";
10
+ import OnCallShiftChangeListeners, {
11
+ OnCallShiftChangeEvent,
12
+ OnCallShiftChangeReason,
13
+ } from "../../../Server/Utils/OnCall/OnCallShiftChangeListeners";
14
+ import logger from "../../../Server/Utils/Logger";
15
+ import ObjectID from "../../../Types/ObjectID";
16
+ import {
17
+ afterEach,
18
+ beforeEach,
19
+ describe,
20
+ expect,
21
+ jest,
22
+ test,
23
+ } from "@jest/globals";
24
+
25
+ /*
26
+ * Change propagation for the on-call calendar feeds:
27
+ *
28
+ * - bumpShiftConfigVersion: one atomic increment per distinct schedule,
29
+ * best-effort;
30
+ * - propagateShiftConfigChange: bump + cache purges + listener event;
31
+ * - every configuration hook (layer, layer user, override, policy
32
+ * attachment, schedule rename/timezone, schedule delete) calls it AFTER
33
+ * the roster refresh, with the right schedules, users and reason;
34
+ * - the roster refresh itself NEVER bumps the version.
35
+ *
36
+ * The hooks are protected, so they are invoked through `as any` — the same
37
+ * discipline as OnCallScheduleAttachRosterRefresh.test.ts.
38
+ */
39
+
40
+ const PROJECT_ID: ObjectID = new ObjectID("project-1");
41
+ const SCHEDULE_1: ObjectID = new ObjectID("schedule-1");
42
+ const SCHEDULE_2: ObjectID = new ObjectID("schedule-2");
43
+ const LAYER_1: ObjectID = new ObjectID("layer-1");
44
+ const USER_A: ObjectID = new ObjectID("user-a");
45
+ const USER_B: ObjectID = new ObjectID("user-b");
46
+ const USER_C: ObjectID = new ObjectID("user-c");
47
+ const USER_D: ObjectID = new ObjectID("user-d");
48
+ const ROW_ID: ObjectID = new ObjectID("row-1");
49
+ const POLICY_ID: ObjectID = new ObjectID("policy-1");
50
+ const RULE_ID: ObjectID = new ObjectID("rule-1");
51
+
52
+ const ROSTER_RESULT: {
53
+ currentUserId: null;
54
+ handOffTimeAt: null;
55
+ nextUserId: null;
56
+ nextHandOffTimeAt: null;
57
+ rosterStartAt: null;
58
+ nextRosterStartAt: null;
59
+ } = {
60
+ currentUserId: null,
61
+ handOffTimeAt: null,
62
+ nextUserId: null,
63
+ nextHandOffTimeAt: null,
64
+ rosterStartAt: null,
65
+ nextRosterStartAt: null,
66
+ };
67
+
68
+ function ids(list: Array<ObjectID>): Array<string> {
69
+ return list.map((id: ObjectID) => {
70
+ return id.toString();
71
+ });
72
+ }
73
+
74
+ function silenceLogger(): void {
75
+ jest.spyOn(logger, "error").mockImplementation((): void => {
76
+ return undefined;
77
+ });
78
+ jest.spyOn(logger, "debug").mockImplementation((): void => {
79
+ return undefined;
80
+ });
81
+ }
82
+
83
+ function spyRosterRefresh(): any {
84
+ return jest
85
+ .spyOn(
86
+ OnCallDutyPolicyScheduleService,
87
+ "refreshCurrentUserIdAndHandoffTimeInSchedule",
88
+ )
89
+ .mockResolvedValue(ROSTER_RESULT);
90
+ }
91
+
92
+ function spyPropagate(): any {
93
+ return jest
94
+ .spyOn(OnCallDutyPolicyScheduleService, "propagateShiftConfigChange")
95
+ .mockResolvedValue(undefined);
96
+ }
97
+
98
+ function lastCallArg(spy: any): any {
99
+ return spy.mock.calls[spy.mock.calls.length - 1]![0];
100
+ }
101
+
102
+ function expectCalledAfter(later: any, earlier: any): void {
103
+ const earlierOrder: number = earlier.mock.invocationCallOrder[0]!;
104
+ const laterOrder: number = later.mock.invocationCallOrder[0]!;
105
+ expect(laterOrder).toBeGreaterThan(earlierOrder);
106
+ }
107
+
108
+ describe("bumpShiftConfigVersion", () => {
109
+ beforeEach(silenceLogger);
110
+ afterEach(() => {
111
+ jest.restoreAllMocks();
112
+ });
113
+
114
+ test("adds one to shiftConfigVersion of each DISTINCT schedule with a single atomic statement", async () => {
115
+ const atomic: any = jest
116
+ .spyOn(
117
+ OnCallDutyPolicyScheduleService,
118
+ "atomicAddToColumnsByIdWithoutHooks",
119
+ )
120
+ .mockResolvedValue(undefined);
121
+
122
+ await OnCallDutyPolicyScheduleService.bumpShiftConfigVersion([
123
+ SCHEDULE_1,
124
+ SCHEDULE_2,
125
+ new ObjectID("schedule-1"),
126
+ ]);
127
+
128
+ expect(atomic).toHaveBeenCalledTimes(2);
129
+ expect(atomic.mock.calls[0]![0]).toEqual({
130
+ id: SCHEDULE_1,
131
+ add: { shiftConfigVersion: 1 },
132
+ });
133
+ expect(atomic.mock.calls[1]![0].id.toString()).toBe("schedule-2");
134
+ });
135
+
136
+ test("is best-effort: one failing schedule is logged and the rest are still bumped", async () => {
137
+ const atomic: any = jest
138
+ .spyOn(
139
+ OnCallDutyPolicyScheduleService,
140
+ "atomicAddToColumnsByIdWithoutHooks",
141
+ )
142
+ .mockImplementation((input: any): Promise<void> => {
143
+ if (input.id.toString() === "schedule-1") {
144
+ return Promise.reject(new Error("db down"));
145
+ }
146
+ return Promise.resolve();
147
+ });
148
+
149
+ await expect(
150
+ OnCallDutyPolicyScheduleService.bumpShiftConfigVersion([
151
+ SCHEDULE_1,
152
+ SCHEDULE_2,
153
+ ]),
154
+ ).resolves.toBeUndefined();
155
+
156
+ expect(atomic).toHaveBeenCalledTimes(2);
157
+ expect(logger.error).toHaveBeenCalled();
158
+ });
159
+
160
+ test("does nothing for an empty list", async () => {
161
+ const atomic: any = jest
162
+ .spyOn(
163
+ OnCallDutyPolicyScheduleService,
164
+ "atomicAddToColumnsByIdWithoutHooks",
165
+ )
166
+ .mockResolvedValue(undefined);
167
+
168
+ await OnCallDutyPolicyScheduleService.bumpShiftConfigVersion([]);
169
+ expect(atomic).not.toHaveBeenCalled();
170
+ });
171
+ });
172
+
173
+ describe("propagateShiftConfigChange", () => {
174
+ let bump: any;
175
+ let purgeSchedule: any;
176
+ let purgeUser: any;
177
+ let notify: any;
178
+
179
+ beforeEach(() => {
180
+ silenceLogger();
181
+ bump = jest
182
+ .spyOn(OnCallDutyPolicyScheduleService, "bumpShiftConfigVersion")
183
+ .mockResolvedValue(undefined);
184
+ purgeSchedule = jest
185
+ .spyOn(OnCallCalendarFeedCache, "purgeForSchedule")
186
+ .mockResolvedValue(undefined);
187
+ purgeUser = jest
188
+ .spyOn(OnCallCalendarFeedCache, "purgeForUser")
189
+ .mockResolvedValue(undefined);
190
+ notify = jest
191
+ .spyOn(OnCallShiftChangeListeners, "notify")
192
+ .mockResolvedValue(undefined);
193
+ });
194
+
195
+ afterEach(() => {
196
+ jest.restoreAllMocks();
197
+ });
198
+
199
+ test("bumps, purges the schedule caches and the named users' caches, and notifies with schedule members added", async () => {
200
+ jest
201
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
202
+ .mockResolvedValue([
203
+ { userId: USER_B },
204
+ { userId: USER_C },
205
+ { userId: USER_A },
206
+ ] as never);
207
+
208
+ await OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
209
+ scheduleIds: [SCHEDULE_1, SCHEDULE_1, SCHEDULE_2],
210
+ projectId: PROJECT_ID,
211
+ userIds: [USER_A],
212
+ reason: OnCallShiftChangeReason.LayerUserChanged,
213
+ });
214
+
215
+ expect(ids(bump.mock.calls[0]![0])).toEqual(["schedule-1", "schedule-2"]);
216
+
217
+ expect(
218
+ purgeSchedule.mock.calls.map((c: Array<unknown>) => {
219
+ return c[0];
220
+ }),
221
+ ).toEqual(["schedule-1", "schedule-2"]);
222
+ expect(purgeUser).toHaveBeenCalledTimes(1);
223
+ expect(purgeUser).toHaveBeenCalledWith("project-1", "user-a");
224
+
225
+ expect(notify).toHaveBeenCalledTimes(1);
226
+ const event: OnCallShiftChangeEvent = lastCallArg(notify);
227
+ expect(event.projectId).toBe(PROJECT_ID);
228
+ expect(ids(event.scheduleIds)).toEqual(["schedule-1", "schedule-2"]);
229
+ // Explicit users first, then the members, deduped.
230
+ expect(ids(event.userIds)).toEqual(["user-a", "user-b", "user-c"]);
231
+ expect(event.reason).toBe(OnCallShiftChangeReason.LayerUserChanged);
232
+ expect(event.occurredAt).toBeInstanceOf(Date);
233
+ });
234
+
235
+ test("skipVersionBump skips the bump but still purges and notifies", async () => {
236
+ jest
237
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
238
+ .mockResolvedValue([] as never);
239
+
240
+ await OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
241
+ scheduleIds: [SCHEDULE_1],
242
+ projectId: PROJECT_ID,
243
+ userIds: [USER_A],
244
+ reason: OnCallShiftChangeReason.ScheduleDeleted,
245
+ skipVersionBump: true,
246
+ });
247
+
248
+ expect(bump).not.toHaveBeenCalled();
249
+ expect(purgeSchedule).toHaveBeenCalledWith("schedule-1");
250
+ expect(notify).toHaveBeenCalledTimes(1);
251
+ expect(lastCallArg(notify).reason).toBe(
252
+ OnCallShiftChangeReason.ScheduleDeleted,
253
+ );
254
+ });
255
+
256
+ test("with no schedules: no bump, no schedule purge, but user purge and a notify for the users", async () => {
257
+ const findBy: any = jest.spyOn(
258
+ OnCallDutyPolicyScheduleLayerUserService,
259
+ "findBy",
260
+ );
261
+
262
+ await OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
263
+ scheduleIds: [],
264
+ projectId: PROJECT_ID,
265
+ userIds: [USER_A, USER_B],
266
+ reason: OnCallShiftChangeReason.OverrideChanged,
267
+ });
268
+
269
+ expect(bump).not.toHaveBeenCalled();
270
+ expect(purgeSchedule).not.toHaveBeenCalled();
271
+ expect(findBy).not.toHaveBeenCalled();
272
+ expect(purgeUser).toHaveBeenCalledTimes(2);
273
+ expect(ids(lastCallArg(notify).userIds)).toEqual(["user-a", "user-b"]);
274
+ expect(lastCallArg(notify).scheduleIds).toEqual([]);
275
+ });
276
+
277
+ test("without a project id the user purge is skipped and the event carries null", async () => {
278
+ jest
279
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
280
+ .mockResolvedValue([] as never);
281
+
282
+ await OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
283
+ scheduleIds: [SCHEDULE_1],
284
+ userIds: [USER_A],
285
+ reason: OnCallShiftChangeReason.LayerChanged,
286
+ });
287
+
288
+ expect(purgeUser).not.toHaveBeenCalled();
289
+ expect(lastCallArg(notify).projectId).toBeNull();
290
+ expect(ids(lastCallArg(notify).userIds)).toEqual(["user-a"]);
291
+ });
292
+
293
+ test("a failing member lookup still notifies with the explicit users", async () => {
294
+ jest
295
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
296
+ .mockRejectedValue(new Error("db down"));
297
+
298
+ await OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
299
+ scheduleIds: [SCHEDULE_1],
300
+ projectId: PROJECT_ID,
301
+ userIds: [USER_A],
302
+ reason: OnCallShiftChangeReason.LayerChanged,
303
+ });
304
+
305
+ expect(notify).toHaveBeenCalledTimes(1);
306
+ expect(ids(lastCallArg(notify).userIds)).toEqual(["user-a"]);
307
+ expect(logger.error).toHaveBeenCalled();
308
+ });
309
+
310
+ test("failing purges never throw and do not stop the notification", async () => {
311
+ purgeSchedule.mockRejectedValue(new Error("redis down"));
312
+ purgeUser.mockRejectedValue(new Error("redis down"));
313
+ jest
314
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
315
+ .mockResolvedValue([] as never);
316
+
317
+ await expect(
318
+ OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
319
+ scheduleIds: [SCHEDULE_1],
320
+ projectId: PROJECT_ID,
321
+ userIds: [USER_A],
322
+ reason: OnCallShiftChangeReason.LayerChanged,
323
+ }),
324
+ ).resolves.toBeUndefined();
325
+
326
+ expect(notify).toHaveBeenCalledTimes(1);
327
+ });
328
+
329
+ test("a throwing bump never propagates", async () => {
330
+ bump.mockRejectedValue(new Error("unexpected"));
331
+ jest
332
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
333
+ .mockResolvedValue([] as never);
334
+
335
+ await expect(
336
+ OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
337
+ scheduleIds: [SCHEDULE_1],
338
+ projectId: PROJECT_ID,
339
+ reason: OnCallShiftChangeReason.LayerChanged,
340
+ }),
341
+ ).resolves.toBeUndefined();
342
+ });
343
+
344
+ test("delivers the event to a REAL registered listener without awaiting it", async () => {
345
+ notify.mockRestore();
346
+ OnCallShiftChangeListeners.clear();
347
+
348
+ let resolveListener: () => void = (): void => {
349
+ return undefined;
350
+ };
351
+ const received: Array<OnCallShiftChangeEvent> = [];
352
+
353
+ OnCallShiftChangeListeners.register(
354
+ (event: OnCallShiftChangeEvent): Promise<void> => {
355
+ received.push(event);
356
+ return new Promise<void>((resolve: () => void) => {
357
+ resolveListener = resolve;
358
+ });
359
+ },
360
+ "slow-listener",
361
+ );
362
+
363
+ jest
364
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
365
+ .mockResolvedValue([] as never);
366
+
367
+ // Resolves although the listener has not: delivery is in the background.
368
+ await OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
369
+ scheduleIds: [SCHEDULE_1],
370
+ projectId: PROJECT_ID,
371
+ reason: OnCallShiftChangeReason.LayerChanged,
372
+ });
373
+
374
+ expect(received).toHaveLength(1);
375
+ expect(ids(received[0]!.scheduleIds)).toEqual(["schedule-1"]);
376
+
377
+ resolveListener();
378
+ OnCallShiftChangeListeners.clear();
379
+ });
380
+ });
381
+
382
+ describe("getScheduleIdsForUsersInProject", () => {
383
+ beforeEach(silenceLogger);
384
+ afterEach(() => {
385
+ jest.restoreAllMocks();
386
+ });
387
+
388
+ test("returns the distinct schedules of the users' layer-user rows", async () => {
389
+ const findBy: any = jest
390
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
391
+ .mockResolvedValue([
392
+ { onCallDutyPolicyScheduleId: SCHEDULE_1 },
393
+ { onCallDutyPolicyScheduleId: SCHEDULE_2 },
394
+ { onCallDutyPolicyScheduleId: new ObjectID("schedule-1") },
395
+ {},
396
+ ] as never);
397
+
398
+ const result: Array<ObjectID> =
399
+ await OnCallDutyPolicyScheduleService.getScheduleIdsForUsersInProject({
400
+ projectId: PROJECT_ID,
401
+ userIds: [USER_A, USER_B, USER_A],
402
+ });
403
+
404
+ expect(ids(result)).toEqual(["schedule-1", "schedule-2"]);
405
+ expect(findBy).toHaveBeenCalledTimes(1);
406
+ const query: any = findBy.mock.calls[0]![0].query;
407
+ expect(query.projectId).toBe(PROJECT_ID);
408
+ expect(query.userId).toBeDefined();
409
+ expect(findBy.mock.calls[0]![0].props.isRoot).toBe(true);
410
+ });
411
+
412
+ test("does not query for an empty user list", async () => {
413
+ const findBy: any = jest.spyOn(
414
+ OnCallDutyPolicyScheduleLayerUserService,
415
+ "findBy",
416
+ );
417
+
418
+ const result: Array<ObjectID> =
419
+ await OnCallDutyPolicyScheduleService.getScheduleIdsForUsersInProject({
420
+ projectId: PROJECT_ID,
421
+ userIds: [],
422
+ });
423
+
424
+ expect(result).toEqual([]);
425
+ expect(findBy).not.toHaveBeenCalled();
426
+ });
427
+ });
428
+
429
+ describe("the roster refresh never bumps shiftConfigVersion", () => {
430
+ beforeEach(silenceLogger);
431
+ afterEach(() => {
432
+ jest.restoreAllMocks();
433
+ });
434
+
435
+ test("refreshCurrentUserIdAndHandoffTimeInSchedule persists the roster without touching the version or the listeners", async () => {
436
+ jest.spyOn(Semaphore, "lock").mockRejectedValue(new Error("no redis"));
437
+ jest.spyOn(Semaphore, "release").mockResolvedValue(undefined as never);
438
+
439
+ jest
440
+ .spyOn(OnCallDutyPolicyScheduleService, "findOneById")
441
+ .mockResolvedValue({
442
+ currentUserIdOnRoster: USER_A,
443
+ rosterHandoffAt: null,
444
+ nextUserIdOnRoster: null,
445
+ rosterNextHandoffAt: null,
446
+ rosterStartAt: null,
447
+ rosterNextStartAt: null,
448
+ } as never);
449
+
450
+ jest
451
+ .spyOn(OnCallDutyPolicyEscalationRuleScheduleService, "findBy")
452
+ .mockResolvedValue([] as never);
453
+
454
+ jest
455
+ .spyOn(
456
+ OnCallDutyPolicyScheduleService,
457
+ "getCurrrentUserIdAndHandoffTimeInSchedule",
458
+ )
459
+ .mockResolvedValue({
460
+ currentUserId: USER_B,
461
+ handOffTimeAt: new Date("2026-09-01T09:00:00Z"),
462
+ nextUserId: USER_A,
463
+ nextHandOffTimeAt: new Date("2026-09-02T09:00:00Z"),
464
+ rosterStartAt: new Date("2026-08-31T09:00:00Z"),
465
+ nextRosterStartAt: new Date("2026-09-01T09:00:00Z"),
466
+ });
467
+
468
+ const updateOneById: any = jest
469
+ .spyOn(OnCallDutyPolicyScheduleService, "updateOneById")
470
+ .mockResolvedValue(undefined as never);
471
+
472
+ const bump: any = jest
473
+ .spyOn(OnCallDutyPolicyScheduleService, "bumpShiftConfigVersion")
474
+ .mockResolvedValue(undefined);
475
+ const atomic: any = jest
476
+ .spyOn(
477
+ OnCallDutyPolicyScheduleService,
478
+ "atomicAddToColumnsByIdWithoutHooks",
479
+ )
480
+ .mockResolvedValue(undefined);
481
+ const propagate: any = spyPropagate();
482
+ const notify: any = jest
483
+ .spyOn(OnCallShiftChangeListeners, "notify")
484
+ .mockResolvedValue(undefined);
485
+
486
+ await OnCallDutyPolicyScheduleService.refreshCurrentUserIdAndHandoffTimeInSchedule(
487
+ SCHEDULE_1,
488
+ );
489
+
490
+ // The roster was persisted through the hook-free path...
491
+ expect(updateOneById).toHaveBeenCalledTimes(1);
492
+ expect(updateOneById.mock.calls[0]![0].props).toEqual({
493
+ isRoot: true,
494
+ ignoreHooks: true,
495
+ });
496
+ expect(
497
+ updateOneById.mock.calls[0]![0].data.currentUserIdOnRoster.toString(),
498
+ ).toBe("user-b");
499
+
500
+ // ...and nothing that a calendar client would see as an edit happened.
501
+ expect(bump).not.toHaveBeenCalled();
502
+ expect(atomic).not.toHaveBeenCalled();
503
+ expect(propagate).not.toHaveBeenCalled();
504
+ expect(notify).not.toHaveBeenCalled();
505
+ });
506
+
507
+ test("onUpdateSuccess ignores roster-column writes and only reacts to name / timezone", async () => {
508
+ const propagate: any = spyPropagate();
509
+ const findBy: any = jest
510
+ .spyOn(OnCallDutyPolicyScheduleService, "findBy")
511
+ .mockResolvedValue([{ id: SCHEDULE_1, projectId: PROJECT_ID }] as never);
512
+
513
+ await (OnCallDutyPolicyScheduleService as any).onUpdateSuccess(
514
+ {
515
+ updateBy: {
516
+ data: { currentUserIdOnRoster: USER_A, rosterHandoffAt: new Date() },
517
+ query: { _id: SCHEDULE_1 },
518
+ props: { isRoot: true },
519
+ },
520
+ carryForward: null,
521
+ },
522
+ [SCHEDULE_1],
523
+ );
524
+
525
+ expect(findBy).not.toHaveBeenCalled();
526
+ expect(propagate).not.toHaveBeenCalled();
527
+ });
528
+ });
529
+
530
+ describe("hook wiring: OnCallDutyPolicyScheduleService (rename / timezone / delete)", () => {
531
+ beforeEach(silenceLogger);
532
+ afterEach(() => {
533
+ jest.restoreAllMocks();
534
+ });
535
+
536
+ test("a rename bumps every updated schedule, grouped by project", async () => {
537
+ const propagate: any = spyPropagate();
538
+ jest.spyOn(OnCallDutyPolicyScheduleService, "findBy").mockResolvedValue([
539
+ { id: SCHEDULE_1, projectId: PROJECT_ID },
540
+ { id: SCHEDULE_2, projectId: new ObjectID("project-2") },
541
+ ] as never);
542
+
543
+ const onUpdate: any = {
544
+ updateBy: {
545
+ data: { name: "Renamed" },
546
+ query: { _id: SCHEDULE_1 },
547
+ props: {},
548
+ },
549
+ carryForward: null,
550
+ };
551
+
552
+ const returned: any = await (
553
+ OnCallDutyPolicyScheduleService as any
554
+ ).onUpdateSuccess(onUpdate, [SCHEDULE_1, SCHEDULE_2]);
555
+
556
+ expect(returned).toBe(onUpdate);
557
+ expect(propagate).toHaveBeenCalledTimes(2);
558
+
559
+ const byProject: Record<string, Array<string>> = {};
560
+ for (const call of propagate.mock.calls) {
561
+ const arg: any = call[0];
562
+ byProject[arg.projectId.toString()] = ids(arg.scheduleIds);
563
+ expect(arg.reason).toBe(OnCallShiftChangeReason.ScheduleChanged);
564
+ }
565
+ expect(byProject).toEqual({
566
+ "project-1": ["schedule-1"],
567
+ "project-2": ["schedule-2"],
568
+ });
569
+ });
570
+
571
+ test("a timezone change bumps as well", async () => {
572
+ const propagate: any = spyPropagate();
573
+ jest
574
+ .spyOn(OnCallDutyPolicyScheduleService, "findBy")
575
+ .mockResolvedValue([{ id: SCHEDULE_1, projectId: PROJECT_ID }] as never);
576
+
577
+ await (OnCallDutyPolicyScheduleService as any).onUpdateSuccess(
578
+ {
579
+ updateBy: {
580
+ data: { timezone: "Europe/Stockholm" },
581
+ query: {},
582
+ props: {},
583
+ },
584
+ carryForward: null,
585
+ },
586
+ [SCHEDULE_1],
587
+ );
588
+
589
+ expect(propagate).toHaveBeenCalledTimes(1);
590
+ });
591
+
592
+ test("a failing lookup in onUpdateSuccess is logged, not thrown", async () => {
593
+ jest
594
+ .spyOn(OnCallDutyPolicyScheduleService, "findBy")
595
+ .mockRejectedValue(new Error("db down"));
596
+
597
+ await expect(
598
+ (OnCallDutyPolicyScheduleService as any).onUpdateSuccess(
599
+ {
600
+ updateBy: { data: { name: "x" }, query: {}, props: {} },
601
+ carryForward: null,
602
+ },
603
+ [SCHEDULE_1],
604
+ ),
605
+ ).resolves.toBeDefined();
606
+ expect(logger.error).toHaveBeenCalled();
607
+ });
608
+
609
+ test("onBeforeDelete captures each schedule's members; onDeleteSuccess purges without a version bump", async () => {
610
+ jest.spyOn(OnCallDutyPolicyScheduleService, "findBy").mockResolvedValue([
611
+ { id: SCHEDULE_1, _id: SCHEDULE_1, projectId: PROJECT_ID },
612
+ { id: SCHEDULE_2, _id: SCHEDULE_2, projectId: PROJECT_ID },
613
+ ] as never);
614
+ jest
615
+ .spyOn(OnCallDutyPolicyTimeLogService, "endTimeForSchedule")
616
+ .mockResolvedValue(undefined as never);
617
+ jest
618
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
619
+ .mockResolvedValue([
620
+ { userId: USER_A, onCallDutyPolicyScheduleId: SCHEDULE_1 },
621
+ { userId: USER_B, onCallDutyPolicyScheduleId: SCHEDULE_1 },
622
+ { userId: USER_A, onCallDutyPolicyScheduleId: SCHEDULE_1 },
623
+ { userId: USER_C, onCallDutyPolicyScheduleId: SCHEDULE_2 },
624
+ ] as never);
625
+
626
+ const onDelete: any = await (
627
+ OnCallDutyPolicyScheduleService as any
628
+ ).onBeforeDelete({
629
+ query: { projectId: PROJECT_ID },
630
+ props: { isRoot: true },
631
+ });
632
+
633
+ const deleted: Array<any> = onDelete.carryForward.deletedSchedules;
634
+ expect(deleted).toHaveLength(2);
635
+ expect(deleted[0].scheduleId.toString()).toBe("schedule-1");
636
+ expect(ids(deleted[0].userIds)).toEqual(["user-a", "user-b"]);
637
+ expect(deleted[1].scheduleId.toString()).toBe("schedule-2");
638
+ expect(ids(deleted[1].userIds)).toEqual(["user-c"]);
639
+
640
+ const propagate: any = spyPropagate();
641
+
642
+ await (OnCallDutyPolicyScheduleService as any).onDeleteSuccess(onDelete, [
643
+ SCHEDULE_1,
644
+ SCHEDULE_2,
645
+ ]);
646
+
647
+ expect(propagate).toHaveBeenCalledTimes(2);
648
+ for (const call of propagate.mock.calls) {
649
+ const arg: any = call[0];
650
+ expect(arg.skipVersionBump).toBe(true);
651
+ expect(arg.reason).toBe(OnCallShiftChangeReason.ScheduleDeleted);
652
+ expect(arg.projectId).toBe(PROJECT_ID);
653
+ }
654
+ expect(ids(propagate.mock.calls[0]![0].userIds)).toEqual([
655
+ "user-a",
656
+ "user-b",
657
+ ]);
658
+ });
659
+
660
+ test("onBeforeDelete stays best-effort when the member lookup fails", async () => {
661
+ jest
662
+ .spyOn(OnCallDutyPolicyScheduleService, "findBy")
663
+ .mockResolvedValue([
664
+ { id: SCHEDULE_1, _id: SCHEDULE_1, projectId: PROJECT_ID },
665
+ ] as never);
666
+ jest
667
+ .spyOn(OnCallDutyPolicyTimeLogService, "endTimeForSchedule")
668
+ .mockResolvedValue(undefined as never);
669
+ jest
670
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
671
+ .mockRejectedValue(new Error("db down"));
672
+
673
+ const onDelete: any = await (
674
+ OnCallDutyPolicyScheduleService as any
675
+ ).onBeforeDelete({ query: { _id: SCHEDULE_1 }, props: { isRoot: true } });
676
+
677
+ expect(onDelete.carryForward.deletedSchedules).toEqual([]);
678
+ expect(onDelete.deleteBy.query._id).toBe(SCHEDULE_1);
679
+ });
680
+ });
681
+
682
+ describe("hook wiring: OnCallDutyPolicyScheduleLayerService", () => {
683
+ beforeEach(silenceLogger);
684
+ afterEach(() => {
685
+ jest.restoreAllMocks();
686
+ });
687
+
688
+ test("onCreateSuccess: roster refresh, then propagate LayerChanged", async () => {
689
+ jest
690
+ .spyOn(OnCallDutyPolicyScheduleLayerService, "findOneById")
691
+ .mockResolvedValue({
692
+ onCallDutyPolicyScheduleId: SCHEDULE_1,
693
+ projectId: PROJECT_ID,
694
+ } as never);
695
+ const refresh: any = spyRosterRefresh();
696
+ const propagate: any = spyPropagate();
697
+
698
+ await (OnCallDutyPolicyScheduleLayerService as any).onCreateSuccess(
699
+ {},
700
+ { id: LAYER_1 },
701
+ );
702
+
703
+ expect(refresh).toHaveBeenCalledWith(SCHEDULE_1);
704
+ expect(propagate).toHaveBeenCalledTimes(1);
705
+ expect(lastCallArg(propagate)).toEqual({
706
+ scheduleIds: [SCHEDULE_1],
707
+ projectId: PROJECT_ID,
708
+ reason: OnCallShiftChangeReason.LayerChanged,
709
+ });
710
+ expectCalledAfter(propagate, refresh);
711
+ });
712
+
713
+ test("onCreateSuccess: nothing happens when the row has no schedule", async () => {
714
+ jest
715
+ .spyOn(OnCallDutyPolicyScheduleLayerService, "findOneById")
716
+ .mockResolvedValue({} as never);
717
+ const refresh: any = spyRosterRefresh();
718
+ const propagate: any = spyPropagate();
719
+
720
+ await (OnCallDutyPolicyScheduleLayerService as any).onCreateSuccess(
721
+ {},
722
+ { id: LAYER_1 },
723
+ );
724
+
725
+ expect(refresh).not.toHaveBeenCalled();
726
+ expect(propagate).not.toHaveBeenCalled();
727
+ });
728
+
729
+ test("onUpdateSuccess propagates once per updated layer", async () => {
730
+ jest
731
+ .spyOn(OnCallDutyPolicyScheduleLayerService, "findOneById")
732
+ .mockImplementation((args: any): Promise<any> => {
733
+ return Promise.resolve({
734
+ onCallDutyPolicyScheduleId:
735
+ args.id.toString() === "layer-1" ? SCHEDULE_1 : SCHEDULE_2,
736
+ projectId: PROJECT_ID,
737
+ });
738
+ });
739
+ const refresh: any = spyRosterRefresh();
740
+ const propagate: any = spyPropagate();
741
+
742
+ await (OnCallDutyPolicyScheduleLayerService as any).onUpdateSuccess(
743
+ { updateBy: { data: {}, query: {}, props: {} }, carryForward: null },
744
+ [LAYER_1, new ObjectID("layer-2")],
745
+ );
746
+
747
+ expect(refresh).toHaveBeenCalledTimes(2);
748
+ expect(propagate).toHaveBeenCalledTimes(2);
749
+ expect(
750
+ propagate.mock.calls.map((c: Array<any>) => {
751
+ return ids(c[0].scheduleIds)[0];
752
+ }),
753
+ ).toEqual(["schedule-1", "schedule-2"]);
754
+ });
755
+
756
+ test("onBeforeDelete captures projectId; onDeleteSuccess re-sequences, refreshes, then propagates", async () => {
757
+ jest
758
+ .spyOn(OnCallDutyPolicyScheduleLayerService, "findOneBy")
759
+ .mockResolvedValue({
760
+ order: 2,
761
+ onCallDutyPolicyScheduleId: SCHEDULE_1,
762
+ projectId: PROJECT_ID,
763
+ } as never);
764
+ jest
765
+ .spyOn(OnCallDutyPolicyScheduleLayerService, "findBy")
766
+ .mockResolvedValue([] as never);
767
+
768
+ const deleteBy: any = { query: { _id: LAYER_1 }, props: {} };
769
+ const onDelete: any = await (
770
+ OnCallDutyPolicyScheduleLayerService as any
771
+ ).onBeforeDelete(deleteBy);
772
+
773
+ expect(onDelete.carryForward.projectId).toBe(PROJECT_ID);
774
+
775
+ const refresh: any = spyRosterRefresh();
776
+ const propagate: any = spyPropagate();
777
+
778
+ await (OnCallDutyPolicyScheduleLayerService as any).onDeleteSuccess(
779
+ onDelete,
780
+ [LAYER_1],
781
+ );
782
+
783
+ expect(refresh).toHaveBeenCalledWith(SCHEDULE_1);
784
+ expect(lastCallArg(propagate)).toEqual({
785
+ scheduleIds: [SCHEDULE_1],
786
+ projectId: PROJECT_ID,
787
+ reason: OnCallShiftChangeReason.LayerChanged,
788
+ });
789
+ expectCalledAfter(propagate, refresh);
790
+ });
791
+
792
+ test("a root delete (no carried resource) neither refreshes nor propagates — unchanged behaviour", async () => {
793
+ const refresh: any = spyRosterRefresh();
794
+ const propagate: any = spyPropagate();
795
+
796
+ await (OnCallDutyPolicyScheduleLayerService as any).onDeleteSuccess(
797
+ { deleteBy: { query: {}, props: { isRoot: true } }, carryForward: null },
798
+ [LAYER_1],
799
+ );
800
+
801
+ expect(refresh).not.toHaveBeenCalled();
802
+ expect(propagate).not.toHaveBeenCalled();
803
+ });
804
+
805
+ /*
806
+ * Same contract as the layer-user hooks: the layer row is committed before
807
+ * the success hook runs, so a throwing roster refresh must not swallow the
808
+ * version bump and the cache purge.
809
+ */
810
+ test("a throwing roster refresh still propagates the layer change", async () => {
811
+ jest
812
+ .spyOn(OnCallDutyPolicyScheduleLayerService, "findOneById")
813
+ .mockResolvedValue({
814
+ onCallDutyPolicyScheduleId: SCHEDULE_1,
815
+ projectId: PROJECT_ID,
816
+ } as never);
817
+ const refresh: any = spyRosterRefresh();
818
+ refresh.mockRejectedValue(new Error("Schedule not found"));
819
+ const propagate: any = spyPropagate();
820
+
821
+ await (OnCallDutyPolicyScheduleLayerService as any).onCreateSuccess(
822
+ {},
823
+ { id: LAYER_1 },
824
+ );
825
+
826
+ expect(refresh).toHaveBeenCalledWith(SCHEDULE_1);
827
+ expect(lastCallArg(propagate)).toEqual({
828
+ scheduleIds: [SCHEDULE_1],
829
+ projectId: PROJECT_ID,
830
+ reason: OnCallShiftChangeReason.LayerChanged,
831
+ });
832
+ expect(logger.error).toHaveBeenCalled();
833
+ });
834
+ });
835
+
836
+ describe("hook wiring: OnCallDutyPolicyScheduleLayerUserService", () => {
837
+ beforeEach(silenceLogger);
838
+ afterEach(() => {
839
+ jest.restoreAllMocks();
840
+ });
841
+
842
+ test("onCreateSuccess names the added user, after the roster refresh", async () => {
843
+ jest
844
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findOneById")
845
+ .mockResolvedValue({
846
+ onCallDutyPolicyScheduleId: SCHEDULE_1,
847
+ projectId: PROJECT_ID,
848
+ userId: USER_A,
849
+ } as never);
850
+ const refresh: any = spyRosterRefresh();
851
+ const propagate: any = spyPropagate();
852
+
853
+ await (OnCallDutyPolicyScheduleLayerUserService as any).onCreateSuccess(
854
+ {},
855
+ { id: ROW_ID },
856
+ );
857
+
858
+ expect(refresh).toHaveBeenCalledWith(SCHEDULE_1);
859
+ expect(lastCallArg(propagate)).toEqual({
860
+ scheduleIds: [SCHEDULE_1],
861
+ projectId: PROJECT_ID,
862
+ userIds: [USER_A],
863
+ reason: OnCallShiftChangeReason.LayerUserChanged,
864
+ });
865
+ expectCalledAfter(propagate, refresh);
866
+ });
867
+
868
+ test("onUpdateSuccess (re-order) propagates per row", async () => {
869
+ jest
870
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findOneById")
871
+ .mockResolvedValue({
872
+ onCallDutyPolicyScheduleId: SCHEDULE_1,
873
+ projectId: PROJECT_ID,
874
+ userId: USER_B,
875
+ } as never);
876
+ spyRosterRefresh();
877
+ const propagate: any = spyPropagate();
878
+
879
+ await (OnCallDutyPolicyScheduleLayerUserService as any).onUpdateSuccess(
880
+ { updateBy: { data: { order: 1 }, query: {}, props: {} } },
881
+ [ROW_ID],
882
+ );
883
+
884
+ expect(propagate).toHaveBeenCalledTimes(1);
885
+ expect(ids(lastCallArg(propagate).userIds)).toEqual(["user-b"]);
886
+ });
887
+
888
+ test("onBeforeDelete captures user + project; onDeleteSuccess propagates with the removed user", async () => {
889
+ jest
890
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findOneBy")
891
+ .mockResolvedValue({
892
+ order: 1,
893
+ onCallDutyPolicyScheduleLayerId: LAYER_1,
894
+ onCallDutyPolicyScheduleId: SCHEDULE_1,
895
+ projectId: PROJECT_ID,
896
+ userId: USER_C,
897
+ } as never);
898
+ jest
899
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
900
+ .mockResolvedValue([] as never);
901
+
902
+ const onDelete: any = await (
903
+ OnCallDutyPolicyScheduleLayerUserService as any
904
+ ).onBeforeDelete({ query: { _id: ROW_ID }, props: {} });
905
+
906
+ expect(onDelete.carryForward.userId).toBe(USER_C);
907
+ expect(onDelete.carryForward.projectId).toBe(PROJECT_ID);
908
+
909
+ const refresh: any = spyRosterRefresh();
910
+ const propagate: any = spyPropagate();
911
+
912
+ await (OnCallDutyPolicyScheduleLayerUserService as any).onDeleteSuccess(
913
+ onDelete,
914
+ [ROW_ID],
915
+ );
916
+
917
+ expect(refresh).toHaveBeenCalledWith(SCHEDULE_1);
918
+ expect(lastCallArg(propagate)).toEqual({
919
+ scheduleIds: [SCHEDULE_1],
920
+ projectId: PROJECT_ID,
921
+ userIds: [USER_C],
922
+ reason: OnCallShiftChangeReason.LayerUserChanged,
923
+ });
924
+ expectCalledAfter(propagate, refresh);
925
+ });
926
+
927
+ test("resequenceOrderInLayer renumbers 1..n and leaves already-correct rows alone", async () => {
928
+ jest
929
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
930
+ .mockResolvedValue([
931
+ { _id: new ObjectID("r1"), order: 1 },
932
+ { _id: new ObjectID("r2"), order: 3 },
933
+ { _id: new ObjectID("r3"), order: 7 },
934
+ ] as never);
935
+ const update: any = jest
936
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "updateOneBy")
937
+ .mockResolvedValue(undefined as never);
938
+
939
+ await OnCallDutyPolicyScheduleLayerUserService.resequenceOrderInLayer(
940
+ LAYER_1,
941
+ );
942
+
943
+ expect(update).toHaveBeenCalledTimes(2);
944
+ expect(update.mock.calls[0]![0].query._id.toString()).toBe("r2");
945
+ expect(update.mock.calls[0]![0].data).toEqual({ order: 2 });
946
+ expect(update.mock.calls[1]![0].query._id.toString()).toBe("r3");
947
+ expect(update.mock.calls[1]![0].data).toEqual({ order: 3 });
948
+ /*
949
+ * ignoreHooks: renumbering is bookkeeping, not a roster change. With the
950
+ * hooks on, every renumbered row would run a full onUpdateSuccess
951
+ * (semaphore-locked roster refresh + version bump + cache purge +
952
+ * listener pass) — N-1 redundant refreshes per layer — and a throw from
953
+ * one of them would abort the rest of the renumbering. The caller
954
+ * refreshes and propagates once per schedule itself.
955
+ */
956
+ expect(update.mock.calls[0]![0].props).toEqual({
957
+ isRoot: true,
958
+ ignoreHooks: true,
959
+ });
960
+ expect(update.mock.calls[1]![0].props).toEqual({
961
+ isRoot: true,
962
+ ignoreHooks: true,
963
+ });
964
+ });
965
+
966
+ /*
967
+ * The row is already committed when the success hooks run, so a throwing
968
+ * roster refresh must not stop the version bump and the cache purge:
969
+ * otherwise the feed caches keep serving the pre-edit roster for up to
970
+ * their TTL (an hour for schedule segments) and the reminder change pass
971
+ * for that edit never runs.
972
+ */
973
+ test("a throwing roster refresh still propagates: onCreateSuccess", async () => {
974
+ jest
975
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findOneById")
976
+ .mockResolvedValue({
977
+ onCallDutyPolicyScheduleId: SCHEDULE_1,
978
+ projectId: PROJECT_ID,
979
+ userId: USER_A,
980
+ } as never);
981
+ const refresh: any = spyRosterRefresh();
982
+ refresh.mockRejectedValue(new Error("Schedule not found"));
983
+ const propagate: any = spyPropagate();
984
+
985
+ await expect(
986
+ (OnCallDutyPolicyScheduleLayerUserService as any).onCreateSuccess(
987
+ {},
988
+ { id: ROW_ID },
989
+ ),
990
+ ).resolves.toBeDefined();
991
+
992
+ expect(refresh).toHaveBeenCalledWith(SCHEDULE_1);
993
+ expect(lastCallArg(propagate)).toEqual({
994
+ scheduleIds: [SCHEDULE_1],
995
+ projectId: PROJECT_ID,
996
+ userIds: [USER_A],
997
+ reason: OnCallShiftChangeReason.LayerUserChanged,
998
+ });
999
+ expect(logger.error).toHaveBeenCalled();
1000
+ });
1001
+
1002
+ test("a throwing roster refresh still propagates: onDeleteSuccess", async () => {
1003
+ jest
1004
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "findBy")
1005
+ .mockResolvedValue([] as never);
1006
+ jest
1007
+ .spyOn(OnCallDutyPolicyScheduleLayerUserService, "updateOneBy")
1008
+ .mockResolvedValue(undefined as never);
1009
+ const refresh: any = spyRosterRefresh();
1010
+ refresh.mockRejectedValue(new Error("redis down"));
1011
+ const propagate: any = spyPropagate();
1012
+
1013
+ await (OnCallDutyPolicyScheduleLayerUserService as any).onDeleteSuccess(
1014
+ {
1015
+ deleteBy: { query: { _id: ROW_ID }, props: {} },
1016
+ carryForward: {
1017
+ order: 1,
1018
+ onCallDutyPolicyScheduleLayerId: LAYER_1,
1019
+ onCallDutyPolicyScheduleId: SCHEDULE_1,
1020
+ projectId: PROJECT_ID,
1021
+ userId: USER_A,
1022
+ },
1023
+ },
1024
+ [ROW_ID],
1025
+ );
1026
+
1027
+ expect(lastCallArg(propagate)).toEqual({
1028
+ scheduleIds: [SCHEDULE_1],
1029
+ projectId: PROJECT_ID,
1030
+ userIds: [USER_A],
1031
+ reason: OnCallShiftChangeReason.LayerUserChanged,
1032
+ });
1033
+ });
1034
+ });
1035
+
1036
+ describe("hook wiring: OnCallDutyPolicyUserOverrideService", () => {
1037
+ beforeEach(silenceLogger);
1038
+ afterEach(() => {
1039
+ jest.restoreAllMocks();
1040
+ });
1041
+
1042
+ function stubScheduleLookup(scheduleIds: Array<ObjectID>): any {
1043
+ return jest
1044
+ .spyOn(OnCallDutyPolicyScheduleService, "getScheduleIdsForUsersInProject")
1045
+ .mockResolvedValue(scheduleIds);
1046
+ }
1047
+
1048
+ test("onCreateSuccess: schedules of the overridden user are bumped; both users are named", async () => {
1049
+ const refreshRosters: any = jest
1050
+ .spyOn(OnCallDutyPolicyScheduleService, "refreshRostersForUserInProject")
1051
+ .mockResolvedValue(undefined);
1052
+ const lookup: any = stubScheduleLookup([SCHEDULE_1, SCHEDULE_2]);
1053
+ const propagate: any = spyPropagate();
1054
+
1055
+ await (OnCallDutyPolicyUserOverrideService as any).onCreateSuccess(
1056
+ {},
1057
+ {
1058
+ projectId: PROJECT_ID,
1059
+ overrideUserId: USER_A,
1060
+ routeAlertsToUserId: USER_C,
1061
+ // no onCallDutyPolicyId: global override, no policy feed item
1062
+ },
1063
+ );
1064
+
1065
+ expect(refreshRosters).toHaveBeenCalledTimes(1);
1066
+ expect(lookup).toHaveBeenCalledTimes(1);
1067
+ expect(ids(lookup.mock.calls[0]![0].userIds)).toEqual(["user-a"]);
1068
+
1069
+ expect(propagate).toHaveBeenCalledTimes(1);
1070
+ const arg: any = lastCallArg(propagate);
1071
+ expect(ids(arg.scheduleIds)).toEqual(["schedule-1", "schedule-2"]);
1072
+ expect(arg.projectId).toBe(PROJECT_ID);
1073
+ expect(ids(arg.userIds)).toEqual(["user-a", "user-c"]);
1074
+ expect(arg.reason).toBe(OnCallShiftChangeReason.OverrideChanged);
1075
+ expectCalledAfter(propagate, refreshRosters);
1076
+ });
1077
+
1078
+ test("onBeforeUpdate captures the substitute as well", async () => {
1079
+ jest
1080
+ .spyOn(OnCallDutyPolicyUserOverrideService, "findBy")
1081
+ .mockResolvedValue([
1082
+ {
1083
+ projectId: PROJECT_ID,
1084
+ overrideUserId: USER_A,
1085
+ routeAlertsToUserId: USER_C,
1086
+ },
1087
+ ] as never);
1088
+
1089
+ const onUpdate: any = await (
1090
+ OnCallDutyPolicyUserOverrideService as any
1091
+ ).onBeforeUpdate({ query: { _id: ROW_ID }, data: {}, props: {} });
1092
+
1093
+ expect(onUpdate.carryForward).toHaveLength(1);
1094
+ expect(onUpdate.carryForward[0].overrideUserId).toBe(USER_A);
1095
+ expect(onUpdate.carryForward[0].routeAlertsToUserId).toBe(USER_C);
1096
+ });
1097
+
1098
+ test("onUpdateSuccess: OLD and NEW overridden users' schedules, all four users named, one propagate per project", async () => {
1099
+ jest
1100
+ .spyOn(OnCallDutyPolicyScheduleService, "refreshRostersForUserInProject")
1101
+ .mockResolvedValue(undefined);
1102
+ jest
1103
+ .spyOn(OnCallDutyPolicyUserOverrideService, "findOneById")
1104
+ .mockResolvedValue({
1105
+ projectId: PROJECT_ID,
1106
+ overrideUserId: USER_B,
1107
+ routeAlertsToUserId: USER_D,
1108
+ } as never);
1109
+ const lookup: any = stubScheduleLookup([SCHEDULE_1]);
1110
+ const propagate: any = spyPropagate();
1111
+
1112
+ await (OnCallDutyPolicyUserOverrideService as any).onUpdateSuccess(
1113
+ {
1114
+ updateBy: { query: { _id: ROW_ID }, data: {}, props: {} },
1115
+ carryForward: [
1116
+ {
1117
+ projectId: PROJECT_ID,
1118
+ overrideUserId: USER_A,
1119
+ routeAlertsToUserId: USER_C,
1120
+ },
1121
+ ],
1122
+ },
1123
+ [ROW_ID],
1124
+ );
1125
+
1126
+ expect(lookup).toHaveBeenCalledTimes(1);
1127
+ expect(ids(lookup.mock.calls[0]![0].userIds)).toEqual(["user-a", "user-b"]);
1128
+
1129
+ expect(propagate).toHaveBeenCalledTimes(1);
1130
+ const arg: any = lastCallArg(propagate);
1131
+ expect(ids(arg.scheduleIds)).toEqual(["schedule-1"]);
1132
+ expect(ids(arg.userIds).sort()).toEqual([
1133
+ "user-a",
1134
+ "user-b",
1135
+ "user-c",
1136
+ "user-d",
1137
+ ]);
1138
+ });
1139
+
1140
+ test("onDeleteSuccess propagates for the deleted overrides", async () => {
1141
+ jest
1142
+ .spyOn(OnCallDutyPolicyScheduleService, "refreshRostersForUserInProject")
1143
+ .mockResolvedValue(undefined);
1144
+ stubScheduleLookup([SCHEDULE_2]);
1145
+ const propagate: any = spyPropagate();
1146
+
1147
+ await (OnCallDutyPolicyUserOverrideService as any).onDeleteSuccess(
1148
+ {
1149
+ deleteBy: { query: {}, props: {} },
1150
+ carryForward: [
1151
+ {
1152
+ projectId: PROJECT_ID,
1153
+ overrideUserId: USER_A,
1154
+ routeAlertsToUserId: USER_C,
1155
+ },
1156
+ ],
1157
+ },
1158
+ [ROW_ID],
1159
+ );
1160
+
1161
+ expect(propagate).toHaveBeenCalledTimes(1);
1162
+ expect(ids(lastCallArg(propagate).scheduleIds)).toEqual(["schedule-2"]);
1163
+ expect(lastCallArg(propagate).reason).toBe(
1164
+ OnCallShiftChangeReason.OverrideChanged,
1165
+ );
1166
+ });
1167
+
1168
+ test("a failing schedule lookup is logged and never thrown", async () => {
1169
+ jest
1170
+ .spyOn(OnCallDutyPolicyScheduleService, "refreshRostersForUserInProject")
1171
+ .mockResolvedValue(undefined);
1172
+ jest
1173
+ .spyOn(OnCallDutyPolicyScheduleService, "getScheduleIdsForUsersInProject")
1174
+ .mockRejectedValue(new Error("db down"));
1175
+ const propagate: any = spyPropagate();
1176
+
1177
+ await expect(
1178
+ (OnCallDutyPolicyUserOverrideService as any).onDeleteSuccess(
1179
+ {
1180
+ deleteBy: { query: {}, props: {} },
1181
+ carryForward: [{ projectId: PROJECT_ID, overrideUserId: USER_A }],
1182
+ },
1183
+ [ROW_ID],
1184
+ ),
1185
+ ).resolves.toBeDefined();
1186
+
1187
+ expect(propagate).not.toHaveBeenCalled();
1188
+ expect(logger.error).toHaveBeenCalled();
1189
+ });
1190
+ });
1191
+
1192
+ describe("hook wiring: OnCallDutyPolicyEscalationRuleScheduleService", () => {
1193
+ beforeEach(() => {
1194
+ silenceLogger();
1195
+ jest
1196
+ .spyOn(OnCallDutyPolicyScheduleService, "getCurrentUserIdInSchedule")
1197
+ .mockResolvedValue(null);
1198
+ jest
1199
+ .spyOn(OnCallDutyPolicyFeedService, "createOnCallDutyPolicyFeedItem")
1200
+ .mockResolvedValue(undefined as never);
1201
+ jest
1202
+ .spyOn(OnCallDutyPolicyTimeLogService, "startTimeLogForUser")
1203
+ .mockResolvedValue(undefined as never);
1204
+ jest
1205
+ .spyOn(OnCallDutyPolicyTimeLogService, "endTimeLogForUser")
1206
+ .mockResolvedValue(undefined as never);
1207
+ });
1208
+
1209
+ afterEach(() => {
1210
+ jest.restoreAllMocks();
1211
+ });
1212
+
1213
+ function linkRow(scheduleId: ObjectID): any {
1214
+ return {
1215
+ id: ROW_ID,
1216
+ projectId: PROJECT_ID,
1217
+ onCallDutyPolicyScheduleId: scheduleId,
1218
+ onCallDutyPolicySchedule: { name: "Primary schedule" },
1219
+ onCallDutyPolicyEscalationRule: { name: "Rule 1", id: RULE_ID, order: 1 },
1220
+ onCallDutyPolicy: { name: "Payments", id: POLICY_ID },
1221
+ createdByUserId: null,
1222
+ };
1223
+ }
1224
+
1225
+ test("attaching propagates PolicyAttachmentChanged after the roster refresh", async () => {
1226
+ jest
1227
+ .spyOn(OnCallDutyPolicyEscalationRuleScheduleService, "findOneById")
1228
+ .mockResolvedValue(linkRow(SCHEDULE_1));
1229
+ const refresh: any = spyRosterRefresh();
1230
+ const propagate: any = spyPropagate();
1231
+
1232
+ await (
1233
+ OnCallDutyPolicyEscalationRuleScheduleService as any
1234
+ ).onCreateSuccess({}, { id: ROW_ID });
1235
+
1236
+ expect(lastCallArg(propagate)).toEqual({
1237
+ scheduleIds: [SCHEDULE_1],
1238
+ projectId: PROJECT_ID,
1239
+ reason: OnCallShiftChangeReason.PolicyAttachmentChanged,
1240
+ });
1241
+ expectCalledAfter(propagate, refresh);
1242
+ });
1243
+
1244
+ test("detaching propagates once per distinct schedule", async () => {
1245
+ spyRosterRefresh();
1246
+ const propagate: any = spyPropagate();
1247
+
1248
+ await (
1249
+ OnCallDutyPolicyEscalationRuleScheduleService as any
1250
+ ).onDeleteSuccess(
1251
+ {
1252
+ carryForward: {
1253
+ deletedItems: [
1254
+ linkRow(SCHEDULE_1),
1255
+ linkRow(SCHEDULE_1),
1256
+ linkRow(SCHEDULE_2),
1257
+ ],
1258
+ },
1259
+ },
1260
+ [ROW_ID],
1261
+ );
1262
+
1263
+ expect(propagate).toHaveBeenCalledTimes(2);
1264
+ expect(
1265
+ propagate.mock.calls.map((c: Array<any>) => {
1266
+ return ids(c[0].scheduleIds)[0];
1267
+ }),
1268
+ ).toEqual(["schedule-1", "schedule-2"]);
1269
+ });
1270
+ });