@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,1103 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { afterEach, describe, expect, jest, test } from "@jest/globals";
4
+
5
+ /*
6
+ * PasswordHash carries a pre-existing TS5.9 diagnostic that fails any suite
7
+ * whose runtime require graph reaches it, and DatabaseService - the base class
8
+ * of every service below - imports it.
9
+ */
10
+ jest.mock("../../../Server/Utils/PasswordHash", () => {
11
+ return {
12
+ __esModule: true,
13
+ default: {
14
+ hash: jest.fn(),
15
+ verify: jest.fn(),
16
+ generateSalt: jest.fn(),
17
+ needsUpgrade: jest.fn(),
18
+ applyPepper: jest.fn(),
19
+ },
20
+ };
21
+ });
22
+
23
+ import UserOnCallCalendarFeedService from "../../../Server/Services/UserOnCallCalendarFeedService";
24
+ import OnCallDutyPolicyScheduleCalendarFeedService, {
25
+ MAX_MINIMUM_GAP_MINUTES,
26
+ MIN_MINIMUM_GAP_MINUTES,
27
+ } from "../../../Server/Services/OnCallDutyPolicyScheduleCalendarFeedService";
28
+ import ProjectOnCallCalendarFeedService from "../../../Server/Services/ProjectOnCallCalendarFeedService";
29
+ import OnCallDutyPolicyScheduleService from "../../../Server/Services/OnCallDutyPolicyScheduleService";
30
+ import UserOnCallCalendarFeed from "../../../Models/DatabaseModels/UserOnCallCalendarFeed";
31
+ import OnCallDutyPolicyScheduleCalendarFeed from "../../../Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed";
32
+ import ProjectOnCallCalendarFeed from "../../../Models/DatabaseModels/ProjectOnCallCalendarFeed";
33
+ import OnCallDutyPolicySchedule from "../../../Models/DatabaseModels/OnCallDutyPolicySchedule";
34
+ import CalendarFeedToken, {
35
+ CalendarFeedRotation,
36
+ } from "../../../Server/Utils/OnCall/CalendarFeedToken";
37
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
38
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
39
+ import { OnCreate, OnUpdate } from "../../../Server/Types/Database/Hooks";
40
+ import BadDataException from "../../../Types/Exception/BadDataException";
41
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
42
+ import NotFoundException from "../../../Types/Exception/NotFoundException";
43
+ import {
44
+ DEFAULT_FUTURE_DAYS,
45
+ DEFAULT_PAST_DAYS,
46
+ MAX_FUTURE_DAYS,
47
+ MAX_PAST_DAYS,
48
+ MIN_FUTURE_DAYS,
49
+ PREVIOUS_TOKEN_GRACE_DAYS,
50
+ } from "../../../Types/OnCallDutyPolicy/CalendarFeedWindow";
51
+ import ObjectID from "../../../Types/ObjectID";
52
+ import PositiveNumber from "../../../Types/PositiveNumber";
53
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
54
+ import { getJestSpyOn } from "../../Spy";
55
+
56
+ /*
57
+ * Fixture overrides may set a column to undefined on purpose (a missing
58
+ * field is the case under test), which Partial<Model> forbids under
59
+ * exactOptionalPropertyTypes.
60
+ */
61
+ type Loose<TModel> = { [K in keyof TModel]?: TModel[K] | undefined };
62
+
63
+ /*
64
+ * The three calendar-feed services share one contract: the token is minted
65
+ * server-side and never chosen by a request, the window is clamped on every
66
+ * write, uniqueness violations become messages, and rotation keeps the old
67
+ * hash in a 30-day grace slot. The hooks are protected, so they are invoked
68
+ * through a narrowed view of the singleton (the ThreatIntelFeedService test
69
+ * discipline); every database call underneath is spied.
70
+ */
71
+
72
+ const SERVICES_DIRECTORY: string = path.join(
73
+ __dirname,
74
+ "..",
75
+ "..",
76
+ "..",
77
+ "Server",
78
+ "Services",
79
+ );
80
+
81
+ const SERVICES_INDEX: string = fs.readFileSync(
82
+ path.join(SERVICES_DIRECTORY, "Index.ts"),
83
+ "utf8",
84
+ );
85
+
86
+ const NEW_SERVICE_NAMES: Array<string> = [
87
+ "UserOnCallCalendarFeedService",
88
+ "OnCallDutyPolicyScheduleCalendarFeedService",
89
+ "ProjectOnCallCalendarFeedService",
90
+ "UserOnCallShiftReminderService",
91
+ "UserOnCallShiftReminderLogService",
92
+ ];
93
+
94
+ const PROJECT_ID: ObjectID = new ObjectID(
95
+ "11111111-1111-4111-8111-111111111111",
96
+ );
97
+ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
98
+ "22222222-2222-4222-8222-222222222222",
99
+ );
100
+ const USER_ID: ObjectID = new ObjectID("33333333-3333-4333-8333-333333333333");
101
+ const SCHEDULE_ID: ObjectID = new ObjectID(
102
+ "44444444-4444-4444-8444-444444444444",
103
+ );
104
+ const FEED_ID: ObjectID = new ObjectID("55555555-5555-4555-8555-555555555555");
105
+ const FEED_ID_2: ObjectID = new ObjectID(
106
+ "66666666-6666-4666-8666-666666666666",
107
+ );
108
+
109
+ const DAY_MS: number = 24 * 60 * 60 * 1000;
110
+
111
+ type HookCaller<TModel extends BaseModel> = {
112
+ onBeforeCreate: (createBy: CreateBy<TModel>) => Promise<OnCreate<TModel>>;
113
+ onBeforeUpdate: (updateBy: UpdateBy<TModel>) => Promise<OnUpdate<TModel>>;
114
+ };
115
+
116
+ const userFeedHooks: HookCaller<UserOnCallCalendarFeed> =
117
+ UserOnCallCalendarFeedService as unknown as HookCaller<UserOnCallCalendarFeed>;
118
+
119
+ const scheduleFeedHooks: HookCaller<OnCallDutyPolicyScheduleCalendarFeed> =
120
+ OnCallDutyPolicyScheduleCalendarFeedService as unknown as HookCaller<OnCallDutyPolicyScheduleCalendarFeed>;
121
+
122
+ const projectFeedHooks: HookCaller<ProjectOnCallCalendarFeed> =
123
+ ProjectOnCallCalendarFeedService as unknown as HookCaller<ProjectOnCallCalendarFeed>;
124
+
125
+ function rootCreate<TModel extends BaseModel>(
126
+ data: TModel,
127
+ props: Record<string, unknown> = {},
128
+ ): CreateBy<TModel> {
129
+ return {
130
+ data,
131
+ props: { isRoot: true, ...props },
132
+ } as unknown as CreateBy<TModel>;
133
+ }
134
+
135
+ function userCreate<TModel extends BaseModel>(
136
+ data: TModel,
137
+ props: Record<string, unknown> = {},
138
+ ): CreateBy<TModel> {
139
+ return {
140
+ data,
141
+ props: {
142
+ isRoot: false,
143
+ userId: USER_ID,
144
+ tenantId: PROJECT_ID,
145
+ ...props,
146
+ },
147
+ } as unknown as CreateBy<TModel>;
148
+ }
149
+
150
+ function update<TModel extends BaseModel>(
151
+ data: Loose<TModel>,
152
+ isRoot: boolean = false,
153
+ ): UpdateBy<TModel> {
154
+ return {
155
+ query: { _id: FEED_ID },
156
+ data,
157
+ props: { isRoot },
158
+ limit: 1,
159
+ skip: 0,
160
+ } as unknown as UpdateBy<TModel>;
161
+ }
162
+
163
+ function expectConsistentTokenColumns(data: {
164
+ token?: string | undefined;
165
+ tokenHash?: string | undefined;
166
+ tokenHint?: string | undefined;
167
+ rotatedAt?: Date | undefined;
168
+ previousTokenHash?: string | undefined;
169
+ previousTokenExpiresAt?: Date | undefined;
170
+ }): void {
171
+ expect(CalendarFeedToken.isValidShape(data.token)).toBe(true);
172
+ expect(data.tokenHash).toBe(CalendarFeedToken.hash(data.token as string));
173
+ expect(data.tokenHint).toBe(CalendarFeedToken.hint(data.token as string));
174
+ expect(data.rotatedAt).toBeInstanceOf(Date);
175
+ expect(data.previousTokenHash).toBeUndefined();
176
+ expect(data.previousTokenExpiresAt).toBeUndefined();
177
+ }
178
+
179
+ afterEach(() => {
180
+ jest.restoreAllMocks();
181
+ });
182
+
183
+ describe("Service wiring", () => {
184
+ test.each(NEW_SERVICE_NAMES)("%s exists on disk", (serviceName: string) => {
185
+ expect(
186
+ fs.existsSync(path.join(SERVICES_DIRECTORY, `${serviceName}.ts`)),
187
+ ).toBe(true);
188
+ });
189
+
190
+ test.each(NEW_SERVICE_NAMES)(
191
+ "%s is registered in Server/Services/Index.ts",
192
+ (serviceName: string) => {
193
+ /*
194
+ * That array is what boot-time createTables() iterates. An unregistered
195
+ * service has no table, however correct the model is.
196
+ */
197
+ expect(SERVICES_INDEX).toContain(
198
+ `import ${serviceName} from "./${serviceName}";`,
199
+ );
200
+ expect(SERVICES_INDEX).toMatch(
201
+ new RegExp(`^\\s{2}${serviceName},$`, "m"),
202
+ );
203
+ },
204
+ );
205
+
206
+ test("registers each new service exactly once", () => {
207
+ for (const serviceName of NEW_SERVICE_NAMES) {
208
+ const occurrences: number = (
209
+ SERVICES_INDEX.match(new RegExp(`^\\s{2}${serviceName},$`, "gm")) || []
210
+ ).length;
211
+
212
+ expect({ serviceName, occurrences }).toEqual({
213
+ serviceName,
214
+ occurrences: 1,
215
+ });
216
+ }
217
+ });
218
+ });
219
+
220
+ describe("UserOnCallCalendarFeedService.onBeforeCreate", () => {
221
+ function feed(
222
+ overrides: Loose<UserOnCallCalendarFeed> = {},
223
+ ): UserOnCallCalendarFeed {
224
+ const model: UserOnCallCalendarFeed = new UserOnCallCalendarFeed();
225
+ model.projectId = PROJECT_ID;
226
+ model.userId = USER_ID;
227
+ Object.assign(model, overrides);
228
+ return model;
229
+ }
230
+
231
+ test("refuses a non-root create even with a valid session", async () => {
232
+ await expect(
233
+ userFeedHooks.onBeforeCreate(userCreate(feed())),
234
+ ).rejects.toThrow(NotAuthorizedException);
235
+ });
236
+
237
+ test("requires a project and a user", async () => {
238
+ await expect(
239
+ userFeedHooks.onBeforeCreate(rootCreate(feed({ projectId: undefined }))),
240
+ ).rejects.toThrow(BadDataException);
241
+
242
+ await expect(
243
+ userFeedHooks.onBeforeCreate(rootCreate(feed({ userId: undefined }))),
244
+ ).rejects.toThrow(BadDataException);
245
+ });
246
+
247
+ test("takes the project from the tenant when the row does not carry one", async () => {
248
+ const onCreate: OnCreate<UserOnCallCalendarFeed> =
249
+ await userFeedHooks.onBeforeCreate(
250
+ rootCreate(feed({ projectId: undefined }), {
251
+ tenantId: OTHER_PROJECT_ID,
252
+ }),
253
+ );
254
+
255
+ expect(onCreate.createBy.data.projectId).toBe(OTHER_PROJECT_ID);
256
+ });
257
+
258
+ test("mints a consistent token set when none is supplied", async () => {
259
+ const onCreate: OnCreate<UserOnCallCalendarFeed> =
260
+ await userFeedHooks.onBeforeCreate(rootCreate(feed()));
261
+
262
+ expectConsistentTokenColumns(onCreate.createBy.data);
263
+ });
264
+
265
+ test("derives hash and hint from a token the caller minted", async () => {
266
+ const token: string = CalendarFeedToken.mint();
267
+
268
+ const onCreate: OnCreate<UserOnCallCalendarFeed> =
269
+ await userFeedHooks.onBeforeCreate(
270
+ rootCreate(feed({ token, tokenHash: "not-the-hash", tokenHint: "zz" })),
271
+ );
272
+
273
+ expect(onCreate.createBy.data.token).toBe(token);
274
+ expect(onCreate.createBy.data.tokenHash).toBe(
275
+ CalendarFeedToken.hash(token),
276
+ );
277
+ expect(onCreate.createBy.data.tokenHint).toBe(
278
+ CalendarFeedToken.hint(token),
279
+ );
280
+ });
281
+
282
+ test("replaces a malformed caller token and never trusts a bare hash", async () => {
283
+ const onCreate: OnCreate<UserOnCallCalendarFeed> =
284
+ await userFeedHooks.onBeforeCreate(
285
+ rootCreate(feed({ token: "too-short", tokenHash: "chosen-hash" })),
286
+ );
287
+
288
+ expect(onCreate.createBy.data.token).not.toBe("too-short");
289
+ expect(onCreate.createBy.data.tokenHash).not.toBe("chosen-hash");
290
+ expectConsistentTokenColumns(onCreate.createBy.data);
291
+ });
292
+
293
+ test("never carries a grace-period hash into a brand-new row", async () => {
294
+ const onCreate: OnCreate<UserOnCallCalendarFeed> =
295
+ await userFeedHooks.onBeforeCreate(
296
+ rootCreate(
297
+ feed({
298
+ previousTokenHash: "stale",
299
+ previousTokenExpiresAt: new Date(),
300
+ }),
301
+ ),
302
+ );
303
+
304
+ expect(onCreate.createBy.data.previousTokenHash).toBeUndefined();
305
+ expect(onCreate.createBy.data.previousTokenExpiresAt).toBeUndefined();
306
+ });
307
+
308
+ test("applies the window defaults when nothing is supplied", async () => {
309
+ const onCreate: OnCreate<UserOnCallCalendarFeed> =
310
+ await userFeedHooks.onBeforeCreate(rootCreate(feed()));
311
+
312
+ expect(onCreate.createBy.data.pastDays).toBe(DEFAULT_PAST_DAYS);
313
+ expect(onCreate.createBy.data.futureDays).toBe(DEFAULT_FUTURE_DAYS);
314
+ });
315
+
316
+ test("clamps the window into the CalendarFeedWindow bounds", async () => {
317
+ const tooBig: OnCreate<UserOnCallCalendarFeed> =
318
+ await userFeedHooks.onBeforeCreate(
319
+ rootCreate(feed({ pastDays: 999, futureDays: 9999 })),
320
+ );
321
+
322
+ expect(tooBig.createBy.data.pastDays).toBe(MAX_PAST_DAYS);
323
+ expect(tooBig.createBy.data.futureDays).toBe(MAX_FUTURE_DAYS);
324
+
325
+ const tooSmall: OnCreate<UserOnCallCalendarFeed> =
326
+ await userFeedHooks.onBeforeCreate(
327
+ rootCreate(feed({ pastDays: -5, futureDays: 1 })),
328
+ );
329
+
330
+ expect(tooSmall.createBy.data.pastDays).toBe(0);
331
+ expect(tooSmall.createBy.data.futureDays).toBe(MIN_FUTURE_DAYS);
332
+
333
+ const numericStrings: OnCreate<UserOnCallCalendarFeed> =
334
+ await userFeedHooks.onBeforeCreate(
335
+ rootCreate(
336
+ feed({
337
+ pastDays: "10" as unknown as number,
338
+ futureDays: "45" as unknown as number,
339
+ }),
340
+ ),
341
+ );
342
+
343
+ expect(numericStrings.createBy.data.pastDays).toBe(10);
344
+ expect(numericStrings.createBy.data.futureDays).toBe(45);
345
+ });
346
+ });
347
+
348
+ describe("UserOnCallCalendarFeedService.onBeforeUpdate", () => {
349
+ test("clamps pastDays and futureDays when present", async () => {
350
+ const onUpdate: OnUpdate<UserOnCallCalendarFeed> =
351
+ await userFeedHooks.onBeforeUpdate(
352
+ update<UserOnCallCalendarFeed>({ pastDays: 500, futureDays: 3 }),
353
+ );
354
+
355
+ expect(onUpdate.updateBy.data.pastDays).toBe(MAX_PAST_DAYS);
356
+ expect(onUpdate.updateBy.data.futureDays).toBe(MIN_FUTURE_DAYS);
357
+ });
358
+
359
+ test("leaves an update that does not touch the window alone", async () => {
360
+ const onUpdate: OnUpdate<UserOnCallCalendarFeed> =
361
+ await userFeedHooks.onBeforeUpdate(
362
+ update<UserOnCallCalendarFeed>({ isEnabled: false }),
363
+ );
364
+
365
+ expect(onUpdate.updateBy.data).toEqual({ isEnabled: false });
366
+ expect(onUpdate.updateBy.data.pastDays).toBeUndefined();
367
+ expect(onUpdate.updateBy.data.futureDays).toBeUndefined();
368
+ });
369
+
370
+ test("accepts in-range values unchanged", async () => {
371
+ const onUpdate: OnUpdate<UserOnCallCalendarFeed> =
372
+ await userFeedHooks.onBeforeUpdate(
373
+ update<UserOnCallCalendarFeed>({ pastDays: 7, futureDays: 120 }),
374
+ );
375
+
376
+ expect(onUpdate.updateBy.data.pastDays).toBe(7);
377
+ expect(onUpdate.updateBy.data.futureDays).toBe(120);
378
+ });
379
+ });
380
+
381
+ describe("UserOnCallCalendarFeedService.createForUser", () => {
382
+ test("creates as root with a freshly minted token and returns the plaintext once", async () => {
383
+ const create: jest.SpyInstance = getJestSpyOn(
384
+ UserOnCallCalendarFeedService,
385
+ "create",
386
+ ).mockImplementation(async (createBy: CreateBy<UserOnCallCalendarFeed>) => {
387
+ const created: UserOnCallCalendarFeed = createBy.data;
388
+ created.id = FEED_ID;
389
+ return created;
390
+ });
391
+
392
+ const result: {
393
+ feed: UserOnCallCalendarFeed;
394
+ minted: { token: string; tokenHash: string; tokenHint: string };
395
+ } = await UserOnCallCalendarFeedService.createForUser({
396
+ projectId: PROJECT_ID,
397
+ userId: USER_ID,
398
+ });
399
+
400
+ expect(create).toHaveBeenCalledTimes(1);
401
+
402
+ const createBy: CreateBy<UserOnCallCalendarFeed> = create.mock
403
+ .calls[0]![0] as CreateBy<UserOnCallCalendarFeed>;
404
+
405
+ expect(createBy.props.isRoot).toBe(true);
406
+ expect(createBy.data.projectId).toBe(PROJECT_ID);
407
+ expect(createBy.data.userId).toBe(USER_ID);
408
+ expect(createBy.data.token).toBe(result.minted.token);
409
+ expect(CalendarFeedToken.isValidShape(result.minted.token)).toBe(true);
410
+ expect(result.minted.tokenHash).toBe(
411
+ CalendarFeedToken.hash(result.minted.token),
412
+ );
413
+ expect(result.minted.tokenHint).toBe(
414
+ CalendarFeedToken.hint(result.minted.token),
415
+ );
416
+ expect(result.feed.id?.toString()).toBe(FEED_ID.toString());
417
+ });
418
+ });
419
+
420
+ /*
421
+ * rotateTokenById has the same body in all three services; the table below
422
+ * drives one set of assertions over each so a divergence shows up by name.
423
+ */
424
+ type RotatableService = {
425
+ findOneBy: (...args: Array<unknown>) => Promise<unknown>;
426
+ updateOneById: (...args: Array<unknown>) => Promise<number>;
427
+ rotateTokenById: (data: { id: ObjectID }) => Promise<CalendarFeedRotation>;
428
+ };
429
+
430
+ const ROTATABLE: Array<[string, RotatableService]> = [
431
+ [
432
+ "UserOnCallCalendarFeedService",
433
+ UserOnCallCalendarFeedService as unknown as RotatableService,
434
+ ],
435
+ [
436
+ "OnCallDutyPolicyScheduleCalendarFeedService",
437
+ OnCallDutyPolicyScheduleCalendarFeedService as unknown as RotatableService,
438
+ ],
439
+ [
440
+ "ProjectOnCallCalendarFeedService",
441
+ ProjectOnCallCalendarFeedService as unknown as RotatableService,
442
+ ],
443
+ ];
444
+
445
+ describe.each(ROTATABLE)(
446
+ "%s.rotateTokenById",
447
+ (_name: string, service: RotatableService) => {
448
+ test("mints a new token, parks the old hash for the grace period and stamps rotatedAt", async () => {
449
+ const before: number = Date.now();
450
+
451
+ getJestSpyOn(service, "findOneBy").mockResolvedValue({
452
+ id: FEED_ID,
453
+ _id: FEED_ID.toString(),
454
+ tokenHash: "old-hash",
455
+ });
456
+ const updateOneById: jest.SpyInstance = getJestSpyOn(
457
+ service,
458
+ "updateOneById",
459
+ ).mockResolvedValue(1);
460
+
461
+ const rotation: CalendarFeedRotation = await service.rotateTokenById({
462
+ id: FEED_ID,
463
+ });
464
+
465
+ expect(updateOneById).toHaveBeenCalledTimes(1);
466
+
467
+ const call: {
468
+ id: ObjectID;
469
+ data: Record<string, unknown>;
470
+ props: { isRoot?: boolean };
471
+ } = updateOneById.mock.calls[0]![0] as {
472
+ id: ObjectID;
473
+ data: Record<string, unknown>;
474
+ props: { isRoot?: boolean };
475
+ };
476
+
477
+ expect(call.id).toBe(FEED_ID);
478
+ expect(call.props.isRoot).toBe(true);
479
+
480
+ expect(CalendarFeedToken.isValidShape(rotation.token)).toBe(true);
481
+ expect(rotation.tokenHash).toBe(CalendarFeedToken.hash(rotation.token));
482
+ expect(rotation.tokenHint).toBe(CalendarFeedToken.hint(rotation.token));
483
+ expect(rotation.tokenHash).not.toBe("old-hash");
484
+ expect(rotation.previousTokenHash).toBe("old-hash");
485
+
486
+ expect(call.data["token"]).toBe(rotation.token);
487
+ expect(call.data["tokenHash"]).toBe(rotation.tokenHash);
488
+ expect(call.data["tokenHint"]).toBe(rotation.tokenHint);
489
+ expect(call.data["previousTokenHash"]).toBe("old-hash");
490
+ expect(call.data["rotatedAt"]).toBe(rotation.rotatedAt);
491
+
492
+ const rotatedAt: number = (call.data["rotatedAt"] as Date).getTime();
493
+ expect(rotatedAt).toBeGreaterThanOrEqual(before);
494
+ expect(rotatedAt).toBeLessThanOrEqual(Date.now());
495
+
496
+ const expiresAt: number = (
497
+ call.data["previousTokenExpiresAt"] as Date
498
+ ).getTime();
499
+ const expectedExpiry: number =
500
+ rotatedAt + PREVIOUS_TOKEN_GRACE_DAYS * DAY_MS;
501
+ // moment.add(days) is DST-aware; allow an hour either side.
502
+ expect(Math.abs(expiresAt - expectedExpiry)).toBeLessThanOrEqual(
503
+ 60 * 60 * 1000,
504
+ );
505
+ });
506
+
507
+ test("reads only the hash (never the encrypted token) and as root without hooks", async () => {
508
+ const findOneBy: jest.SpyInstance = getJestSpyOn(
509
+ service,
510
+ "findOneBy",
511
+ ).mockResolvedValue({ id: FEED_ID, tokenHash: "old-hash" });
512
+ getJestSpyOn(service, "updateOneById").mockResolvedValue(1);
513
+
514
+ await service.rotateTokenById({ id: FEED_ID });
515
+
516
+ const call: {
517
+ query: Record<string, unknown>;
518
+ select: Record<string, unknown>;
519
+ props: { isRoot?: boolean; ignoreHooks?: boolean };
520
+ } = findOneBy.mock.calls[0]![0] as {
521
+ query: Record<string, unknown>;
522
+ select: Record<string, unknown>;
523
+ props: { isRoot?: boolean; ignoreHooks?: boolean };
524
+ };
525
+
526
+ expect(call.query).toEqual({ _id: FEED_ID });
527
+ expect(call.select).toEqual({ _id: true, tokenHash: true });
528
+ expect(call.select["token"]).toBeUndefined();
529
+ expect(call.props.isRoot).toBe(true);
530
+ expect(call.props.ignoreHooks).toBe(true);
531
+ });
532
+
533
+ test("a first mint on a row without a hash parks nothing", async () => {
534
+ getJestSpyOn(service, "findOneBy").mockResolvedValue({
535
+ id: FEED_ID,
536
+ tokenHash: undefined,
537
+ });
538
+ const updateOneById: jest.SpyInstance = getJestSpyOn(
539
+ service,
540
+ "updateOneById",
541
+ ).mockResolvedValue(1);
542
+
543
+ const rotation: CalendarFeedRotation = await service.rotateTokenById({
544
+ id: FEED_ID,
545
+ });
546
+
547
+ expect(rotation.previousTokenHash).toBeNull();
548
+ expect(rotation.previousTokenExpiresAt).toBeNull();
549
+
550
+ const data: Record<string, unknown> = (
551
+ updateOneById.mock.calls[0]![0] as { data: Record<string, unknown> }
552
+ ).data;
553
+
554
+ expect(data["previousTokenHash"]).toBeNull();
555
+ expect(data["previousTokenExpiresAt"]).toBeNull();
556
+ });
557
+
558
+ test("throws NotFound for a feed that does not exist and writes nothing", async () => {
559
+ getJestSpyOn(service, "findOneBy").mockResolvedValue(null);
560
+ const updateOneById: jest.SpyInstance = getJestSpyOn(
561
+ service,
562
+ "updateOneById",
563
+ ).mockResolvedValue(1);
564
+
565
+ await expect(service.rotateTokenById({ id: FEED_ID })).rejects.toThrow(
566
+ NotFoundException,
567
+ );
568
+ expect(updateOneById).not.toHaveBeenCalled();
569
+ });
570
+
571
+ test("two rotations never mint the same token", async () => {
572
+ getJestSpyOn(service, "findOneBy").mockResolvedValue({
573
+ id: FEED_ID,
574
+ tokenHash: "old-hash",
575
+ });
576
+ getJestSpyOn(service, "updateOneById").mockResolvedValue(1);
577
+
578
+ const first: CalendarFeedRotation = await service.rotateTokenById({
579
+ id: FEED_ID,
580
+ });
581
+ const second: CalendarFeedRotation = await service.rotateTokenById({
582
+ id: FEED_ID,
583
+ });
584
+
585
+ expect(first.token).not.toBe(second.token);
586
+ expect(first.tokenHash).not.toBe(second.tokenHash);
587
+ });
588
+ },
589
+ );
590
+
591
+ describe("OnCallDutyPolicyScheduleCalendarFeedService.onBeforeCreate", () => {
592
+ function feed(
593
+ overrides: Loose<OnCallDutyPolicyScheduleCalendarFeed> = {},
594
+ ): OnCallDutyPolicyScheduleCalendarFeed {
595
+ const model: OnCallDutyPolicyScheduleCalendarFeed =
596
+ new OnCallDutyPolicyScheduleCalendarFeed();
597
+ model.projectId = PROJECT_ID;
598
+ model.onCallDutyPolicyScheduleId = SCHEDULE_ID;
599
+ Object.assign(model, overrides);
600
+ return model;
601
+ }
602
+
603
+ function scheduleExists(exists: boolean): jest.SpyInstance {
604
+ return getJestSpyOn(
605
+ OnCallDutyPolicyScheduleService,
606
+ "findOneBy",
607
+ ).mockResolvedValue(
608
+ exists ? ({ id: SCHEDULE_ID } as OnCallDutyPolicySchedule) : null,
609
+ );
610
+ }
611
+
612
+ function existingFeeds(count: number): jest.SpyInstance {
613
+ return getJestSpyOn(
614
+ OnCallDutyPolicyScheduleCalendarFeedService,
615
+ "countBy",
616
+ ).mockResolvedValue(new PositiveNumber(count));
617
+ }
618
+
619
+ test("requires the schedule id", async () => {
620
+ scheduleExists(true);
621
+ existingFeeds(0);
622
+
623
+ await expect(
624
+ scheduleFeedHooks.onBeforeCreate(
625
+ userCreate(feed({ onCallDutyPolicyScheduleId: undefined })),
626
+ ),
627
+ ).rejects.toThrow(BadDataException);
628
+ });
629
+
630
+ test("requires a project (row or tenant)", async () => {
631
+ scheduleExists(true);
632
+ existingFeeds(0);
633
+
634
+ await expect(
635
+ scheduleFeedHooks.onBeforeCreate(
636
+ userCreate(feed({ projectId: undefined }), { tenantId: undefined }),
637
+ ),
638
+ ).rejects.toThrow(BadDataException);
639
+ });
640
+
641
+ test("refuses a schedule that is not in the caller's project", async () => {
642
+ const findOneBy: jest.SpyInstance = scheduleExists(false);
643
+ existingFeeds(0);
644
+
645
+ await expect(
646
+ scheduleFeedHooks.onBeforeCreate(userCreate(feed())),
647
+ ).rejects.toThrow(BadDataException);
648
+
649
+ // The lookup is by (schedule, project) as root: the FK alone cannot tell.
650
+ const call: {
651
+ query: Record<string, unknown>;
652
+ props: { isRoot?: boolean };
653
+ } = findOneBy.mock.calls[0]![0] as {
654
+ query: Record<string, unknown>;
655
+ props: { isRoot?: boolean };
656
+ };
657
+
658
+ expect(call.query).toEqual({ _id: SCHEDULE_ID, projectId: PROJECT_ID });
659
+ expect(call.props.isRoot).toBe(true);
660
+ });
661
+
662
+ test("scopes the schedule lookup to the tenant when the row has no projectId", async () => {
663
+ const findOneBy: jest.SpyInstance = scheduleExists(true);
664
+ existingFeeds(0);
665
+
666
+ const onCreate: OnCreate<OnCallDutyPolicyScheduleCalendarFeed> =
667
+ await scheduleFeedHooks.onBeforeCreate(
668
+ userCreate(feed({ projectId: undefined }), {
669
+ tenantId: OTHER_PROJECT_ID,
670
+ }),
671
+ );
672
+
673
+ expect(onCreate.createBy.data.projectId).toBe(OTHER_PROJECT_ID);
674
+ expect(
675
+ (findOneBy.mock.calls[0]![0] as { query: Record<string, unknown> }).query,
676
+ ).toEqual({ _id: SCHEDULE_ID, projectId: OTHER_PROJECT_ID });
677
+ });
678
+
679
+ test("refuses a second feed for the same schedule with a readable message", async () => {
680
+ scheduleExists(true);
681
+ const countBy: jest.SpyInstance = existingFeeds(1);
682
+
683
+ await expect(
684
+ scheduleFeedHooks.onBeforeCreate(userCreate(feed())),
685
+ ).rejects.toThrow(/already has a shared calendar feed/);
686
+
687
+ expect(
688
+ (countBy.mock.calls[0]![0] as { query: Record<string, unknown> }).query,
689
+ ).toEqual({ onCallDutyPolicyScheduleId: SCHEDULE_ID });
690
+ });
691
+
692
+ test("mints the token for a non-root (permission-checked) publish", async () => {
693
+ scheduleExists(true);
694
+ existingFeeds(0);
695
+
696
+ const onCreate: OnCreate<OnCallDutyPolicyScheduleCalendarFeed> =
697
+ await scheduleFeedHooks.onBeforeCreate(userCreate(feed()));
698
+
699
+ expectConsistentTokenColumns(onCreate.createBy.data);
700
+ });
701
+
702
+ test("ignores a token a non-root request tried to choose", async () => {
703
+ scheduleExists(true);
704
+ existingFeeds(0);
705
+
706
+ const chosen: string = CalendarFeedToken.mint();
707
+
708
+ const onCreate: OnCreate<OnCallDutyPolicyScheduleCalendarFeed> =
709
+ await scheduleFeedHooks.onBeforeCreate(
710
+ userCreate(
711
+ feed({
712
+ token: chosen,
713
+ tokenHash: CalendarFeedToken.hash(chosen),
714
+ tokenHint: CalendarFeedToken.hint(chosen),
715
+ }),
716
+ ),
717
+ );
718
+
719
+ expect(onCreate.createBy.data.token).not.toBe(chosen);
720
+ expect(onCreate.createBy.data.tokenHash).not.toBe(
721
+ CalendarFeedToken.hash(chosen),
722
+ );
723
+ expectConsistentTokenColumns(onCreate.createBy.data);
724
+ });
725
+
726
+ test("honours a token a ROOT caller minted (so the API can answer with the URL)", async () => {
727
+ scheduleExists(true);
728
+ existingFeeds(0);
729
+
730
+ const minted: string = CalendarFeedToken.mint();
731
+
732
+ const onCreate: OnCreate<OnCallDutyPolicyScheduleCalendarFeed> =
733
+ await scheduleFeedHooks.onBeforeCreate(
734
+ rootCreate(feed({ token: minted })),
735
+ );
736
+
737
+ expect(onCreate.createBy.data.token).toBe(minted);
738
+ expect(onCreate.createBy.data.tokenHash).toBe(
739
+ CalendarFeedToken.hash(minted),
740
+ );
741
+ });
742
+
743
+ test("applies window defaults and clamps, and bounds the gap threshold", async () => {
744
+ scheduleExists(true);
745
+ existingFeeds(0);
746
+
747
+ const defaults: OnCreate<OnCallDutyPolicyScheduleCalendarFeed> =
748
+ await scheduleFeedHooks.onBeforeCreate(userCreate(feed()));
749
+
750
+ expect(defaults.createBy.data.pastDays).toBe(DEFAULT_PAST_DAYS);
751
+ expect(defaults.createBy.data.futureDays).toBe(DEFAULT_FUTURE_DAYS);
752
+ expect(defaults.createBy.data.minimumGapMinutes).toBeUndefined();
753
+
754
+ const clamped: OnCreate<OnCallDutyPolicyScheduleCalendarFeed> =
755
+ await scheduleFeedHooks.onBeforeCreate(
756
+ userCreate(
757
+ feed({
758
+ pastDays: 400,
759
+ futureDays: 2,
760
+ minimumGapMinutes: "30" as unknown as number,
761
+ }),
762
+ ),
763
+ );
764
+
765
+ expect(clamped.createBy.data.pastDays).toBe(MAX_PAST_DAYS);
766
+ expect(clamped.createBy.data.futureDays).toBe(MIN_FUTURE_DAYS);
767
+ expect(clamped.createBy.data.minimumGapMinutes).toBe(30);
768
+
769
+ await expect(
770
+ scheduleFeedHooks.onBeforeCreate(
771
+ userCreate(feed({ minimumGapMinutes: 0 })),
772
+ ),
773
+ ).rejects.toThrow(BadDataException);
774
+ });
775
+ });
776
+
777
+ describe("OnCallDutyPolicyScheduleCalendarFeedService.onBeforeUpdate", () => {
778
+ test("clamps the window and validates the gap threshold when present", async () => {
779
+ const onUpdate: OnUpdate<OnCallDutyPolicyScheduleCalendarFeed> =
780
+ await scheduleFeedHooks.onBeforeUpdate(
781
+ update<OnCallDutyPolicyScheduleCalendarFeed>({
782
+ pastDays: -1,
783
+ futureDays: 1000,
784
+ minimumGapMinutes: 15,
785
+ }),
786
+ );
787
+
788
+ expect(onUpdate.updateBy.data.pastDays).toBe(0);
789
+ expect(onUpdate.updateBy.data.futureDays).toBe(MAX_FUTURE_DAYS);
790
+ expect(onUpdate.updateBy.data.minimumGapMinutes).toBe(15);
791
+ });
792
+
793
+ test("rejects an out-of-range gap threshold", async () => {
794
+ await expect(
795
+ scheduleFeedHooks.onBeforeUpdate(
796
+ update<OnCallDutyPolicyScheduleCalendarFeed>({
797
+ minimumGapMinutes: MAX_MINIMUM_GAP_MINUTES + 1,
798
+ }),
799
+ ),
800
+ ).rejects.toThrow(BadDataException);
801
+
802
+ await expect(
803
+ scheduleFeedHooks.onBeforeUpdate(
804
+ update<OnCallDutyPolicyScheduleCalendarFeed>({
805
+ minimumGapMinutes: 2.5,
806
+ }),
807
+ ),
808
+ ).rejects.toThrow(BadDataException);
809
+ });
810
+
811
+ test("leaves a settings-only update alone", async () => {
812
+ const onUpdate: OnUpdate<OnCallDutyPolicyScheduleCalendarFeed> =
813
+ await scheduleFeedHooks.onBeforeUpdate(
814
+ update<OnCallDutyPolicyScheduleCalendarFeed>({
815
+ includeCoverageGaps: true,
816
+ rotateWhenMemberLeaves: true,
817
+ }),
818
+ );
819
+
820
+ expect(onUpdate.updateBy.data).toEqual({
821
+ includeCoverageGaps: true,
822
+ rotateWhenMemberLeaves: true,
823
+ });
824
+ });
825
+ });
826
+
827
+ describe("validateMinimumGapMinutes", () => {
828
+ const validators: Array<[string, (value: unknown) => number]> = [
829
+ [
830
+ "OnCallDutyPolicyScheduleCalendarFeedService",
831
+ (value: unknown): number => {
832
+ return (
833
+ OnCallDutyPolicyScheduleCalendarFeedService.constructor as unknown as {
834
+ validateMinimumGapMinutes: (value: unknown) => number;
835
+ }
836
+ ).validateMinimumGapMinutes(value);
837
+ },
838
+ ],
839
+ [
840
+ "ProjectOnCallCalendarFeedService",
841
+ (value: unknown): number => {
842
+ return (
843
+ ProjectOnCallCalendarFeedService.constructor as unknown as {
844
+ validateMinimumGapMinutes: (value: unknown) => number;
845
+ }
846
+ ).validateMinimumGapMinutes(value);
847
+ },
848
+ ],
849
+ ];
850
+
851
+ test("publishes sensible bounds", () => {
852
+ expect(MIN_MINIMUM_GAP_MINUTES).toBe(1);
853
+ expect(MAX_MINIMUM_GAP_MINUTES).toBe(7 * 24 * 60);
854
+ });
855
+
856
+ test.each(validators)(
857
+ "%s accepts whole minutes inside the bounds, incl. numeric strings",
858
+ (_name: string, validate: (value: unknown) => number) => {
859
+ expect(validate(MIN_MINIMUM_GAP_MINUTES)).toBe(MIN_MINIMUM_GAP_MINUTES);
860
+ expect(validate(MAX_MINIMUM_GAP_MINUTES)).toBe(MAX_MINIMUM_GAP_MINUTES);
861
+ expect(validate(60)).toBe(60);
862
+ expect(validate("90")).toBe(90);
863
+ },
864
+ );
865
+
866
+ test.each(validators)(
867
+ "%s rejects everything else",
868
+ (_name: string, validate: (value: unknown) => number) => {
869
+ for (const bad of [
870
+ 0,
871
+ -1,
872
+ MAX_MINIMUM_GAP_MINUTES + 1,
873
+ 1.5,
874
+ Number.NaN,
875
+ Number.POSITIVE_INFINITY,
876
+ "",
877
+ "abc",
878
+ null,
879
+ undefined,
880
+ true,
881
+ {},
882
+ ]) {
883
+ expect(() => {
884
+ return validate(bad);
885
+ }).toThrow(BadDataException);
886
+ }
887
+ },
888
+ );
889
+ });
890
+
891
+ describe("OnCallDutyPolicyScheduleCalendarFeedService.rotateFeedsForMemberLeave", () => {
892
+ test("rotates every enabled, opted-in feed of the project and returns their ids", async () => {
893
+ const findBy: jest.SpyInstance = getJestSpyOn(
894
+ OnCallDutyPolicyScheduleCalendarFeedService,
895
+ "findBy",
896
+ ).mockResolvedValue([{ id: FEED_ID }, { id: FEED_ID_2 }]);
897
+ const rotate: jest.SpyInstance = getJestSpyOn(
898
+ OnCallDutyPolicyScheduleCalendarFeedService,
899
+ "rotateTokenById",
900
+ ).mockResolvedValue({} as CalendarFeedRotation);
901
+
902
+ const rotated: Array<ObjectID> =
903
+ await OnCallDutyPolicyScheduleCalendarFeedService.rotateFeedsForMemberLeave(
904
+ { projectId: PROJECT_ID },
905
+ );
906
+
907
+ expect(rotated).toEqual([FEED_ID, FEED_ID_2]);
908
+ expect(rotate).toHaveBeenCalledTimes(2);
909
+ expect(rotate).toHaveBeenNthCalledWith(1, { id: FEED_ID });
910
+ expect(rotate).toHaveBeenNthCalledWith(2, { id: FEED_ID_2 });
911
+
912
+ const call: {
913
+ query: Record<string, unknown>;
914
+ props: { isRoot?: boolean };
915
+ } = findBy.mock.calls[0]![0] as {
916
+ query: Record<string, unknown>;
917
+ props: { isRoot?: boolean };
918
+ };
919
+
920
+ // Disabled or un-flagged feeds are filtered at the query, not in memory.
921
+ expect(call.query).toEqual({
922
+ projectId: PROJECT_ID,
923
+ isEnabled: true,
924
+ rotateWhenMemberLeaves: true,
925
+ });
926
+ expect(call.props.isRoot).toBe(true);
927
+ });
928
+
929
+ test("is a no-op when nothing opted in", async () => {
930
+ getJestSpyOn(
931
+ OnCallDutyPolicyScheduleCalendarFeedService,
932
+ "findBy",
933
+ ).mockResolvedValue([]);
934
+ const rotate: jest.SpyInstance = getJestSpyOn(
935
+ OnCallDutyPolicyScheduleCalendarFeedService,
936
+ "rotateTokenById",
937
+ ).mockResolvedValue({} as CalendarFeedRotation);
938
+
939
+ await expect(
940
+ OnCallDutyPolicyScheduleCalendarFeedService.rotateFeedsForMemberLeave({
941
+ projectId: PROJECT_ID,
942
+ }),
943
+ ).resolves.toEqual([]);
944
+ expect(rotate).not.toHaveBeenCalled();
945
+ });
946
+ });
947
+
948
+ describe("ProjectOnCallCalendarFeedService.onBeforeCreate", () => {
949
+ function feed(
950
+ overrides: Loose<ProjectOnCallCalendarFeed> = {},
951
+ ): ProjectOnCallCalendarFeed {
952
+ const model: ProjectOnCallCalendarFeed = new ProjectOnCallCalendarFeed();
953
+ model.projectId = PROJECT_ID;
954
+ Object.assign(model, overrides);
955
+ return model;
956
+ }
957
+
958
+ function existingFeeds(count: number): jest.SpyInstance {
959
+ return getJestSpyOn(
960
+ ProjectOnCallCalendarFeedService,
961
+ "countBy",
962
+ ).mockResolvedValue(new PositiveNumber(count));
963
+ }
964
+
965
+ test("requires a project (row or tenant)", async () => {
966
+ existingFeeds(0);
967
+
968
+ await expect(
969
+ projectFeedHooks.onBeforeCreate(
970
+ userCreate(feed({ projectId: undefined }), { tenantId: undefined }),
971
+ ),
972
+ ).rejects.toThrow(BadDataException);
973
+
974
+ const onCreate: OnCreate<ProjectOnCallCalendarFeed> =
975
+ await projectFeedHooks.onBeforeCreate(
976
+ userCreate(feed({ projectId: undefined }), {
977
+ tenantId: OTHER_PROJECT_ID,
978
+ }),
979
+ );
980
+
981
+ expect(onCreate.createBy.data.projectId).toBe(OTHER_PROJECT_ID);
982
+ });
983
+
984
+ test("refuses a second feed for the project", async () => {
985
+ const countBy: jest.SpyInstance = existingFeeds(1);
986
+
987
+ await expect(
988
+ projectFeedHooks.onBeforeCreate(userCreate(feed())),
989
+ ).rejects.toThrow(/already has a shared calendar feed/);
990
+
991
+ expect(
992
+ (countBy.mock.calls[0]![0] as { query: Record<string, unknown> }).query,
993
+ ).toEqual({ projectId: PROJECT_ID });
994
+ });
995
+
996
+ test("mints the token, ignoring one a non-root request chose", async () => {
997
+ existingFeeds(0);
998
+
999
+ const chosen: string = CalendarFeedToken.mint();
1000
+
1001
+ const onCreate: OnCreate<ProjectOnCallCalendarFeed> =
1002
+ await projectFeedHooks.onBeforeCreate(
1003
+ userCreate(feed({ token: chosen })),
1004
+ );
1005
+
1006
+ expect(onCreate.createBy.data.token).not.toBe(chosen);
1007
+ expectConsistentTokenColumns(onCreate.createBy.data);
1008
+ });
1009
+
1010
+ test("applies defaults, clamps the window and bounds the gap threshold", async () => {
1011
+ existingFeeds(0);
1012
+
1013
+ const onCreate: OnCreate<ProjectOnCallCalendarFeed> =
1014
+ await projectFeedHooks.onBeforeCreate(
1015
+ userCreate(
1016
+ feed({ pastDays: 61, futureDays: 181, minimumGapMinutes: 120 }),
1017
+ ),
1018
+ );
1019
+
1020
+ expect(onCreate.createBy.data.pastDays).toBe(MAX_PAST_DAYS);
1021
+ expect(onCreate.createBy.data.futureDays).toBe(MAX_FUTURE_DAYS);
1022
+ expect(onCreate.createBy.data.minimumGapMinutes).toBe(120);
1023
+
1024
+ await expect(
1025
+ projectFeedHooks.onBeforeCreate(
1026
+ userCreate(feed({ minimumGapMinutes: -1 })),
1027
+ ),
1028
+ ).rejects.toThrow(BadDataException);
1029
+ });
1030
+ });
1031
+
1032
+ describe("ProjectOnCallCalendarFeedService.onBeforeUpdate", () => {
1033
+ test("clamps and validates when present, leaves the rest alone", async () => {
1034
+ const onUpdate: OnUpdate<ProjectOnCallCalendarFeed> =
1035
+ await projectFeedHooks.onBeforeUpdate(
1036
+ update<ProjectOnCallCalendarFeed>({
1037
+ futureDays: 5,
1038
+ minimumGapMinutes: "45" as unknown as number,
1039
+ isEnabled: false,
1040
+ }),
1041
+ );
1042
+
1043
+ expect(onUpdate.updateBy.data).toEqual({
1044
+ futureDays: MIN_FUTURE_DAYS,
1045
+ minimumGapMinutes: 45,
1046
+ isEnabled: false,
1047
+ });
1048
+
1049
+ await expect(
1050
+ projectFeedHooks.onBeforeUpdate(
1051
+ update<ProjectOnCallCalendarFeed>({
1052
+ minimumGapMinutes: "x" as unknown as number,
1053
+ }),
1054
+ ),
1055
+ ).rejects.toThrow(BadDataException);
1056
+ });
1057
+ });
1058
+
1059
+ describe("ProjectOnCallCalendarFeedService.rotateFeedsForMemberLeave", () => {
1060
+ test("rotates the project's feed when it is enabled and opted in", async () => {
1061
+ const findOneBy: jest.SpyInstance = getJestSpyOn(
1062
+ ProjectOnCallCalendarFeedService,
1063
+ "findOneBy",
1064
+ ).mockResolvedValue({ id: FEED_ID });
1065
+ const rotate: jest.SpyInstance = getJestSpyOn(
1066
+ ProjectOnCallCalendarFeedService,
1067
+ "rotateTokenById",
1068
+ ).mockResolvedValue({} as CalendarFeedRotation);
1069
+
1070
+ await expect(
1071
+ ProjectOnCallCalendarFeedService.rotateFeedsForMemberLeave({
1072
+ projectId: PROJECT_ID,
1073
+ }),
1074
+ ).resolves.toEqual([FEED_ID]);
1075
+
1076
+ expect(rotate).toHaveBeenCalledWith({ id: FEED_ID });
1077
+ expect(
1078
+ (findOneBy.mock.calls[0]![0] as { query: Record<string, unknown> }).query,
1079
+ ).toEqual({
1080
+ projectId: PROJECT_ID,
1081
+ isEnabled: true,
1082
+ rotateWhenMemberLeaves: true,
1083
+ });
1084
+ });
1085
+
1086
+ test("is a no-op without an opted-in feed", async () => {
1087
+ getJestSpyOn(
1088
+ ProjectOnCallCalendarFeedService,
1089
+ "findOneBy",
1090
+ ).mockResolvedValue(null);
1091
+ const rotate: jest.SpyInstance = getJestSpyOn(
1092
+ ProjectOnCallCalendarFeedService,
1093
+ "rotateTokenById",
1094
+ ).mockResolvedValue({} as CalendarFeedRotation);
1095
+
1096
+ await expect(
1097
+ ProjectOnCallCalendarFeedService.rotateFeedsForMemberLeave({
1098
+ projectId: PROJECT_ID,
1099
+ }),
1100
+ ).resolves.toEqual([]);
1101
+ expect(rotate).not.toHaveBeenCalled();
1102
+ });
1103
+ });