@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,1415 @@
1
+ import moment from "moment-timezone";
2
+ import User from "../../Models/DatabaseModels/User";
3
+ import ICalendar, {
4
+ ICalendarCalendar,
5
+ ICalendarDocument,
6
+ ICalendarEvent,
7
+ ICalendarEventStatus,
8
+ ICalendarTransparency,
9
+ } from "../Calendar/ICalendar";
10
+ import OneUptimeDate from "../Date";
11
+ import ObjectID from "../ObjectID";
12
+ import Timezone from "../Timezone";
13
+ import CalendarFeedWindow, {
14
+ MAX_EVENTS,
15
+ MAX_GAP_EVENTS,
16
+ } from "./CalendarFeedWindow";
17
+ import LayerUtil, { LayerEventsResult, LayerProps } from "./Layer";
18
+ import MaterializedShiftUtil, {
19
+ MaterializedShift,
20
+ MaterializedShiftPolicy,
21
+ } from "./MaterializedShift";
22
+ import ScheduleShiftUtil, {
23
+ CoverageGap,
24
+ OnCallShift,
25
+ } from "./ScheduleShiftUtil";
26
+ import ShiftSeamUtil, { TimeSegment } from "./ShiftSeamUtil";
27
+
28
+ /*
29
+ * Pure mapper from materialized on-call shifts to an iCalendar document.
30
+ *
31
+ * Everything here is deterministic on its inputs (no clock reads, no I/O), so
32
+ * the same shifts always serialize to the same bytes — the property the body
33
+ * cache and ETag depend on. The server-side renderer decides WHAT to feed in
34
+ * (which shifts, which window, which timezone) and this module decides how
35
+ * that looks in a calendar.
36
+ *
37
+ * Rules implemented (see the on-call calendar feeds design):
38
+ * - UID is (schedule, seam-normalised start), never the user, so an override
39
+ * swap updates the event in place; policy variants add the policy id, gaps
40
+ * have their own namespace.
41
+ * - DTSTAMP/LAST-MODIFIED are the schedule inputs' last-modified instant and
42
+ * SEQUENCE is the schedule's shiftConfigVersion, so an unchanged schedule
43
+ * renders byte-identically.
44
+ * - Every DTSTART/DTEND is UTC; the DESCRIPTION carries the schedule-zone,
45
+ * UTC and viewer-zone wall clock.
46
+ * - TRANSP:TRANSPARENT, STATUS:CONFIRMED, CATEGORIES:On-Call; no CLASS.
47
+ */
48
+
49
+ export enum OnCallCalendarFeedKind {
50
+ Personal = "personal",
51
+ Schedule = "schedule",
52
+ Project = "project",
53
+ }
54
+
55
+ export interface CalendarNameOptions {
56
+ kind: OnCallCalendarFeedKind;
57
+ scheduleName?: string | undefined;
58
+ projectName?: string | undefined;
59
+ // Personal feed with a ?schedule= filter.
60
+ filterScheduleName?: string | undefined;
61
+ }
62
+
63
+ export interface CalendarName {
64
+ // Full name (NAME).
65
+ name: string;
66
+ // What clients display (X-WR-CALNAME); truncated for the schedule feed.
67
+ displayName: string;
68
+ }
69
+
70
+ export interface CalendarHeaderOptions extends CalendarNameOptions {
71
+ // X-WR-TIMEZONE; invalid or missing falls back to UTC.
72
+ timezone?: string | undefined;
73
+ lastModifiedAt?: Date | undefined;
74
+ // Extra sentences appended to X-WR-CALDESC (truncation, empty reason, ...).
75
+ notes?: Array<string> | undefined;
76
+ }
77
+
78
+ /*
79
+ * The policy-variant shifts of a feed, bucketed by schedule id and sorted
80
+ * inside each bucket. See buildVariantIndex.
81
+ */
82
+ export type PolicyVariantIndex = Map<string, Array<MaterializedShift>>;
83
+
84
+ export interface ShiftEventContext {
85
+ kind: OnCallCalendarFeedKind;
86
+ // Dashboard base URL including the /dashboard segment, no trailing slash needed.
87
+ dashboardUrl: string;
88
+ // The subscriber's own zone (personal feed); used for the "your zone" line.
89
+ viewerTimezone?: string | undefined;
90
+ /*
91
+ * Pre-built policy-variant index for the mirror lines (see
92
+ * buildVariantIndex). `null` means "built, and the feed has no variants";
93
+ * `undefined` means "not built" and falls back to scanning whatever shift
94
+ * list the caller passes to buildDescription / shiftToEvent.
95
+ */
96
+ variantIndex?: PolicyVariantIndex | null | undefined;
97
+ }
98
+
99
+ export interface CoverageEnvelopeInput {
100
+ layers: Array<LayerProps>;
101
+ windowStart: Date;
102
+ windowEnd: Date;
103
+ maxSimulationIterations?: number | undefined;
104
+ }
105
+
106
+ export interface CoverageEnvelopeResult {
107
+ // Union of every layer's restriction windows over the feed window, merged.
108
+ segments: Array<TimeSegment>;
109
+ truncated: boolean;
110
+ }
111
+
112
+ export interface CoverageGapEventsInput {
113
+ scheduleId: string;
114
+ scheduleName: string;
115
+ projectId: string;
116
+ // Resolved shifts of this schedule (any object with start/end).
117
+ shifts: Array<TimeSegment>;
118
+ feedStart: Date;
119
+ feedEnd: Date;
120
+ // Where a layer INTENDED coverage (see computeCoverageEnvelope).
121
+ envelope: Array<TimeSegment>;
122
+ // A hole shorter than this (after envelope clipping) is not reported.
123
+ minimumGapSeconds?: number | undefined;
124
+ lastModifiedAt: Date;
125
+ shiftConfigVersion: number;
126
+ dashboardUrl: string;
127
+ maxGapEvents?: number | undefined;
128
+ }
129
+
130
+ export interface CoverageGapEventsResult {
131
+ events: Array<ICalendarEvent>;
132
+ // The gaps behind the events, same order.
133
+ gaps: Array<TimeSegment>;
134
+ // True when more gaps existed than maxGapEvents allowed.
135
+ truncated: boolean;
136
+ }
137
+
138
+ export interface WindowShrinkInput {
139
+ shifts: Array<MaterializedShift>;
140
+ feedStart: Date;
141
+ feedEnd: Date;
142
+ maxEvents?: number | undefined;
143
+ }
144
+
145
+ export interface WindowShrinkResult {
146
+ // Shifts overlapping [feedStart, feedEnd), sorted, at most maxEvents.
147
+ shifts: Array<MaterializedShift>;
148
+ // The (possibly earlier) exclusive end actually used.
149
+ feedEnd: Date;
150
+ truncated: boolean;
151
+ // Whole days cut off the end of the window.
152
+ daysDropped: number;
153
+ }
154
+
155
+ export interface FeedRenderInput extends ShiftEventContext {
156
+ shifts: Array<MaterializedShift>;
157
+ /*
158
+ * Every shift of the candidate schedules, BEFORE any per-user filtering.
159
+ * Only the shifts in `shifts` become VEVENTs; these are the extra context
160
+ * an event's DESCRIPTION may need to explain itself. The personal feed
161
+ * keeps only the subscriber's own shifts, so without this a policy-scoped
162
+ * override that hands one of the schedule's policies to somebody else would
163
+ * be invisible on the rostered user's own event — it would claim to page
164
+ * them through a policy that pages the substitute. Defaults to `shifts`.
165
+ */
166
+ contextShifts?: Array<MaterializedShift> | undefined;
167
+ scheduleName?: string | undefined;
168
+ projectName?: string | undefined;
169
+ filterScheduleName?: string | undefined;
170
+ // X-WR-TIMEZONE; defaults to viewerTimezone, then UTC.
171
+ calendarTimezone?: string | undefined;
172
+ notes?: Array<string> | undefined;
173
+ gapEvents?: Array<ICalendarEvent> | undefined;
174
+ // Overrides the max of the shifts' lastModifiedAt.
175
+ lastModifiedAt?: Date | undefined;
176
+ }
177
+
178
+ export interface FeedRenderResult {
179
+ body: string;
180
+ eventCount: number;
181
+ lastModifiedAt: Date | null;
182
+ }
183
+
184
+ export interface EmptyFeedInput extends CalendarNameOptions {
185
+ // Why the calendar is empty; goes into X-WR-CALDESC so a subscriber can tell.
186
+ reason: string;
187
+ timezone?: string | undefined;
188
+ }
189
+
190
+ const MILLISECONDS_PER_DAY: number = 24 * 60 * 60 * 1000;
191
+
192
+ // Engine artefact tolerance when merging envelope segments (1 s seams).
193
+ const ENVELOPE_MERGE_TOLERANCE_MILLISECONDS: number = 1000;
194
+
195
+ /*
196
+ * ScheduleShiftUtil.getCoverageGaps ignores holes of five seconds or less
197
+ * (its contiguity tolerance for the engine's one-second seams). The trailing
198
+ * hole this file adds itself is held to the same threshold.
199
+ */
200
+ const TRAILING_GAP_TOLERANCE_MILLISECONDS: number = 5 * 1000;
201
+
202
+ const ENVELOPE_USER_ID: string = "00000000-0000-4000-8000-000000000000";
203
+
204
+ const EMAIL_LIKE_REGEX: RegExp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
205
+
206
+ export default class OnCallCalendarFeedUtil {
207
+ public static readonly PRODUCT_ID: string =
208
+ "-//OneUptime//On-Call Calendar Feed//EN";
209
+
210
+ public static readonly REFRESH_INTERVAL: string = "PT1H";
211
+
212
+ public static readonly UID_DOMAIN: string = "oneuptime";
213
+
214
+ public static readonly CATEGORY: string = "On-Call";
215
+
216
+ public static readonly PERSONAL_CALENDAR_NAME: string = "OneUptime On-Call";
217
+
218
+ // Confluence Team Calendars rejects longer calendar names.
219
+ public static readonly MAX_SCHEDULE_CALENDAR_NAME_LENGTH: number = 28;
220
+
221
+ public static readonly FALLBACK_USER_NAME: string = "Unnamed user";
222
+
223
+ public static readonly REFRESH_CAVEAT: string =
224
+ "Calendar apps refresh subscribed feeds on their own schedule (Google Calendar every 12-24 h, Outlook on the web about every 3 h, Apple Calendar per its fetch setting), so recent changes can lag.";
225
+
226
+ public static readonly REFRESH_LINE: string =
227
+ "Changes appear after your calendar app next refreshes (Google Calendar: up to 24 h).";
228
+
229
+ public static readonly PAST_SHIFT_LINE: string =
230
+ "Past shifts reflect the current rotation, not who was actually paged — see the On-Call Time Log report.";
231
+
232
+ public static readonly NO_POLICY_LINE: string =
233
+ "Not attached to any escalation policy, so it will not page anyone.";
234
+
235
+ public static readonly LEGACY_TIMEZONE_NOTE: string =
236
+ "schedule has no timezone set (expanded in the server's zone, as paging does)";
237
+
238
+ /*
239
+ * ---------------------------------------------------------------------
240
+ * Identity and links
241
+ * ---------------------------------------------------------------------
242
+ */
243
+
244
+ // oncall-<scheduleId>-<startEpochSeconds>@oneuptime (+ -<policyId> for variants).
245
+ public static getShiftUid(shift: MaterializedShift): string {
246
+ const base: string = `oncall-${shift.scheduleId}-${OnCallCalendarFeedUtil.epochSeconds(shift.start)}`;
247
+ const suffix: string = shift.policyVariantOf
248
+ ? `-${shift.policyVariantOf.policyId}`
249
+ : "";
250
+ return `${base}${suffix}@${OnCallCalendarFeedUtil.UID_DOMAIN}`;
251
+ }
252
+
253
+ public static getGapUid(scheduleId: string, start: Date): string {
254
+ return `oncall-gap-${scheduleId}-${OnCallCalendarFeedUtil.epochSeconds(start)}@${OnCallCalendarFeedUtil.UID_DOMAIN}`;
255
+ }
256
+
257
+ public static getScheduleUrl(
258
+ dashboardUrl: string,
259
+ projectId: string,
260
+ scheduleId: string,
261
+ ): string {
262
+ return `${OnCallCalendarFeedUtil.trimTrailingSlash(dashboardUrl)}/${projectId}/on-call-duty/schedules/${scheduleId}`;
263
+ }
264
+
265
+ public static getUserOverridesUrl(
266
+ dashboardUrl: string,
267
+ projectId: string,
268
+ ): string {
269
+ return `${OnCallCalendarFeedUtil.trimTrailingSlash(dashboardUrl)}/${projectId}/on-call-duty/user-overrides`;
270
+ }
271
+
272
+ public static getTimeLogUrl(dashboardUrl: string, projectId: string): string {
273
+ return `${OnCallCalendarFeedUtil.trimTrailingSlash(dashboardUrl)}/${projectId}/on-call-duty/user-time-logs`;
274
+ }
275
+
276
+ /*
277
+ * ---------------------------------------------------------------------
278
+ * Calendar header
279
+ * ---------------------------------------------------------------------
280
+ */
281
+
282
+ public static buildCalendarName(options: CalendarNameOptions): CalendarName {
283
+ const personal: string = OnCallCalendarFeedUtil.PERSONAL_CALENDAR_NAME;
284
+
285
+ if (options.kind === OnCallCalendarFeedKind.Schedule) {
286
+ const name: string =
287
+ OnCallCalendarFeedUtil.cleanName(options.scheduleName) ||
288
+ "On-Call Schedule";
289
+ return {
290
+ name,
291
+ displayName: OnCallCalendarFeedUtil.truncateName(
292
+ name,
293
+ OnCallCalendarFeedUtil.MAX_SCHEDULE_CALENDAR_NAME_LENGTH,
294
+ ),
295
+ };
296
+ }
297
+
298
+ if (options.kind === OnCallCalendarFeedKind.Project) {
299
+ const project: string =
300
+ OnCallCalendarFeedUtil.cleanName(options.projectName) || "Project";
301
+ const name: string = `${personal} · ${project}`;
302
+ return { name, displayName: name };
303
+ }
304
+
305
+ const filter: string = OnCallCalendarFeedUtil.cleanName(
306
+ options.filterScheduleName,
307
+ );
308
+ const name: string = filter ? `${personal} · ${filter}` : personal;
309
+ return { name, displayName: name };
310
+ }
311
+
312
+ public static buildCalendarDescription(
313
+ options: CalendarNameOptions & { notes?: Array<string> | undefined },
314
+ ): string {
315
+ let intro: string;
316
+
317
+ if (options.kind === OnCallCalendarFeedKind.Schedule) {
318
+ intro = `Everyone's on-call shifts on ${
319
+ OnCallCalendarFeedUtil.cleanName(options.scheduleName) ||
320
+ "this schedule"
321
+ } from OneUptime.`;
322
+ } else if (options.kind === OnCallCalendarFeedKind.Project) {
323
+ intro = `Everyone's on-call shifts across ${
324
+ OnCallCalendarFeedUtil.cleanName(options.projectName) || "this project"
325
+ } from OneUptime.`;
326
+ } else {
327
+ const filter: string = OnCallCalendarFeedUtil.cleanName(
328
+ options.filterScheduleName,
329
+ );
330
+ intro = filter
331
+ ? `Your on-call shifts on ${filter} from OneUptime.`
332
+ : "Your on-call shifts from OneUptime.";
333
+ }
334
+
335
+ const notes: Array<string> = (options.notes ?? [])
336
+ .map((note: string) => {
337
+ return note.trim();
338
+ })
339
+ .filter((note: string) => {
340
+ return note !== "";
341
+ });
342
+
343
+ return [intro, OnCallCalendarFeedUtil.REFRESH_CAVEAT, ...notes].join(" ");
344
+ }
345
+
346
+ public static buildCalendarHeader(
347
+ options: CalendarHeaderOptions,
348
+ ): ICalendarCalendar {
349
+ const names: CalendarName =
350
+ OnCallCalendarFeedUtil.buildCalendarName(options);
351
+
352
+ const header: ICalendarCalendar = {
353
+ productId: OnCallCalendarFeedUtil.PRODUCT_ID,
354
+ name: names.name,
355
+ displayName: names.displayName,
356
+ description: OnCallCalendarFeedUtil.buildCalendarDescription(options),
357
+ timezone: OnCallCalendarFeedUtil.isValidTimezone(options.timezone)
358
+ ? options.timezone
359
+ : Timezone.UTC,
360
+ refreshInterval: OnCallCalendarFeedUtil.REFRESH_INTERVAL,
361
+ };
362
+
363
+ if (options.lastModifiedAt !== undefined) {
364
+ header.lastModified = options.lastModifiedAt;
365
+ }
366
+
367
+ return header;
368
+ }
369
+
370
+ /*
371
+ * ---------------------------------------------------------------------
372
+ * Shift -> VEVENT
373
+ * ---------------------------------------------------------------------
374
+ */
375
+
376
+ /*
377
+ * How a person is named in this feed. The personal feed shows whatever the
378
+ * materializer resolved (an email fallback is fine — it is the subscriber's
379
+ * own data); the shared schedule/project feeds never show an email.
380
+ */
381
+ public static getDisplayName(
382
+ name: string | null | undefined,
383
+ kind: OnCallCalendarFeedKind,
384
+ ): string {
385
+ const trimmed: string = (name ?? "").trim();
386
+
387
+ if (trimmed === "") {
388
+ return OnCallCalendarFeedUtil.FALLBACK_USER_NAME;
389
+ }
390
+
391
+ if (
392
+ kind !== OnCallCalendarFeedKind.Personal &&
393
+ EMAIL_LIKE_REGEX.test(trimmed)
394
+ ) {
395
+ return OnCallCalendarFeedUtil.FALLBACK_USER_NAME;
396
+ }
397
+
398
+ return trimmed;
399
+ }
400
+
401
+ public static buildSummary(
402
+ shift: MaterializedShift,
403
+ kind: OnCallCalendarFeedKind,
404
+ ): string {
405
+ const scheduleName: string =
406
+ OnCallCalendarFeedUtil.cleanName(shift.scheduleName) || "Schedule";
407
+
408
+ let summary: string = `On-call · ${scheduleName}`;
409
+
410
+ const distinctPolicies: Array<MaterializedShiftPolicy> =
411
+ OnCallCalendarFeedUtil.getDistinctPolicies(shift.policies);
412
+
413
+ if (shift.policyVariantOf) {
414
+ summary += ` · ${shift.policyVariantOf.policyName}`;
415
+ } else if (
416
+ kind === OnCallCalendarFeedKind.Personal &&
417
+ distinctPolicies.length === 1
418
+ ) {
419
+ summary += ` · ${distinctPolicies[0]!.policyName}`;
420
+ }
421
+
422
+ if (shift.override) {
423
+ summary += ` (covering for ${OnCallCalendarFeedUtil.getDisplayName(
424
+ shift.override.originalUserName,
425
+ kind,
426
+ )})`;
427
+ }
428
+
429
+ if (kind === OnCallCalendarFeedKind.Personal) {
430
+ return summary;
431
+ }
432
+
433
+ return `${OnCallCalendarFeedUtil.getDisplayName(shift.userName, kind)} · ${summary}`;
434
+ }
435
+
436
+ public static buildDescription(
437
+ shift: MaterializedShift,
438
+ context: ShiftEventContext,
439
+ allShifts?: Array<MaterializedShift> | undefined,
440
+ ): string {
441
+ const kind: OnCallCalendarFeedKind = context.kind;
442
+ const lines: Array<string> = [];
443
+
444
+ const userName: string = OnCallCalendarFeedUtil.getDisplayName(
445
+ shift.userName,
446
+ kind,
447
+ );
448
+
449
+ const originalUserName: string | null = shift.override
450
+ ? OnCallCalendarFeedUtil.getDisplayName(
451
+ shift.override.originalUserName,
452
+ kind,
453
+ )
454
+ : null;
455
+
456
+ lines.push(
457
+ `Who: ${userName}${originalUserName ? `, covering for ${originalUserName}` : ""}`,
458
+ );
459
+
460
+ const scheduleZone: string = OnCallCalendarFeedUtil.getScheduleZone(shift);
461
+ const isLegacyZone: boolean = !OnCallCalendarFeedUtil.isValidTimezone(
462
+ shift.scheduleTimezone,
463
+ );
464
+
465
+ lines.push(
466
+ `Schedule: ${OnCallCalendarFeedUtil.cleanName(shift.scheduleName) || "Schedule"} (${
467
+ isLegacyZone
468
+ ? OnCallCalendarFeedUtil.LEGACY_TIMEZONE_NOTE
469
+ : scheduleZone
470
+ })`,
471
+ );
472
+
473
+ if (shift.layerName) {
474
+ lines.push(`Layer: ${shift.layerName}`);
475
+ }
476
+
477
+ lines.push(
478
+ ...OnCallCalendarFeedUtil.buildShiftTimeLines(
479
+ shift,
480
+ scheduleZone,
481
+ isLegacyZone,
482
+ context.viewerTimezone,
483
+ ),
484
+ );
485
+
486
+ const distinctPolicies: Array<MaterializedShiftPolicy> =
487
+ OnCallCalendarFeedUtil.getDistinctPolicies(shift.policies);
488
+
489
+ // A variant pages through ONE policy, not the schedule's whole attachment set.
490
+ const pagingPolicies: Array<MaterializedShiftPolicy> =
491
+ OnCallCalendarFeedUtil.getPagingPolicies(shift);
492
+
493
+ if (pagingPolicies.length === 0) {
494
+ lines.push(OnCallCalendarFeedUtil.NO_POLICY_LINE);
495
+ } else {
496
+ const label: string =
497
+ kind === OnCallCalendarFeedKind.Personal
498
+ ? "Pages you via"
499
+ : "Pages via";
500
+ lines.push(
501
+ `${label}: ${OnCallCalendarFeedUtil.describePolicies(pagingPolicies)}`,
502
+ );
503
+ }
504
+
505
+ if (shift.override) {
506
+ const scope: string = shift.override.onCallDutyPolicyId
507
+ ? `scoped to ${OnCallCalendarFeedUtil.findPolicyName(
508
+ distinctPolicies,
509
+ shift.override.onCallDutyPolicyId,
510
+ )}`
511
+ : "global override";
512
+
513
+ lines.push(
514
+ `Override: ${originalUserName} → ${userName} from ${OnCallCalendarFeedUtil.formatInZone(
515
+ shift.override.overrideStartsAt,
516
+ scheduleZone,
517
+ )} to ${OnCallCalendarFeedUtil.formatInZone(
518
+ shift.override.overrideEndsAt,
519
+ scheduleZone,
520
+ )} (${scope})`,
521
+ );
522
+ }
523
+
524
+ if (shift.policyVariantOf) {
525
+ const insteadOf: string = originalUserName ?? "the rostered user";
526
+ lines.push(
527
+ kind === OnCallCalendarFeedKind.Personal
528
+ ? `For ${shift.policyVariantOf.policyName} you are paged instead of ${insteadOf} because of a policy-specific override.`
529
+ : `For ${shift.policyVariantOf.policyName}, ${userName} is paged instead of ${insteadOf} because of a policy-specific override.`,
530
+ );
531
+ } else {
532
+ /*
533
+ * A pre-built index (every render builds one) beats re-scanning the
534
+ * whole shift array for every shift, which is quadratic at feed sizes
535
+ * near MAX_EVENTS.
536
+ */
537
+ const variantIndex: PolicyVariantIndex | null =
538
+ context.variantIndex !== undefined
539
+ ? context.variantIndex
540
+ : allShifts
541
+ ? OnCallCalendarFeedUtil.buildVariantIndex(allShifts)
542
+ : null;
543
+
544
+ if (variantIndex) {
545
+ lines.push(
546
+ ...OnCallCalendarFeedUtil.buildVariantMirrorLines(
547
+ shift,
548
+ variantIndex,
549
+ kind,
550
+ scheduleZone,
551
+ ),
552
+ );
553
+ }
554
+ }
555
+
556
+ if (shift.isPast) {
557
+ lines.push(
558
+ `${OnCallCalendarFeedUtil.PAST_SHIFT_LINE} ${OnCallCalendarFeedUtil.getTimeLogUrl(
559
+ context.dashboardUrl,
560
+ shift.projectId,
561
+ )}`,
562
+ );
563
+ }
564
+
565
+ lines.push(
566
+ `Need cover? ${OnCallCalendarFeedUtil.getUserOverridesUrl(
567
+ context.dashboardUrl,
568
+ shift.projectId,
569
+ )}`,
570
+ );
571
+
572
+ lines.push(OnCallCalendarFeedUtil.REFRESH_LINE);
573
+
574
+ return lines.join("\n");
575
+ }
576
+
577
+ public static shiftToEvent(
578
+ shift: MaterializedShift,
579
+ context: ShiftEventContext,
580
+ allShifts?: Array<MaterializedShift> | undefined,
581
+ ): ICalendarEvent {
582
+ return {
583
+ uid: OnCallCalendarFeedUtil.getShiftUid(shift),
584
+ dtStamp: shift.lastModifiedAt,
585
+ lastModified: shift.lastModifiedAt,
586
+ sequence: OnCallCalendarFeedUtil.toSequence(shift.shiftConfigVersion),
587
+ start: shift.start,
588
+ end: shift.end,
589
+ summary: OnCallCalendarFeedUtil.buildSummary(shift, context.kind),
590
+ description: OnCallCalendarFeedUtil.buildDescription(
591
+ shift,
592
+ context,
593
+ allShifts,
594
+ ),
595
+ url: OnCallCalendarFeedUtil.getScheduleUrl(
596
+ context.dashboardUrl,
597
+ shift.projectId,
598
+ shift.scheduleId,
599
+ ),
600
+ status: ICalendarEventStatus.Confirmed,
601
+ transparency: ICalendarTransparency.Transparent,
602
+ categories: [OnCallCalendarFeedUtil.CATEGORY],
603
+ };
604
+ }
605
+
606
+ /*
607
+ * One VEVENT per shift, in deterministic (start, schedule, key) order.
608
+ *
609
+ * `contextShifts` are shifts that do NOT become events but may explain the
610
+ * ones that do (the personal feed's pre-filter list); it defaults to the
611
+ * rendered shifts.
612
+ */
613
+ public static shiftsToEvents(
614
+ shifts: Array<MaterializedShift>,
615
+ context: ShiftEventContext,
616
+ contextShifts?: Array<MaterializedShift> | undefined,
617
+ ): Array<ICalendarEvent> {
618
+ const sorted: Array<MaterializedShift> =
619
+ MaterializedShiftUtil.sortByStart(shifts);
620
+
621
+ const eventContext: ShiftEventContext = {
622
+ ...context,
623
+ variantIndex: OnCallCalendarFeedUtil.buildVariantIndex(
624
+ contextShifts ?? sorted,
625
+ ),
626
+ };
627
+
628
+ return sorted.map((shift: MaterializedShift) => {
629
+ return OnCallCalendarFeedUtil.shiftToEvent(shift, eventContext);
630
+ });
631
+ }
632
+
633
+ /*
634
+ * ---------------------------------------------------------------------
635
+ * Window
636
+ * ---------------------------------------------------------------------
637
+ */
638
+
639
+ // Shifts that overlap [feedStart, feedEnd), unclipped, sorted.
640
+ public static filterShiftsToWindow(
641
+ shifts: Array<MaterializedShift>,
642
+ feedStart: Date,
643
+ feedEnd: Date,
644
+ ): Array<MaterializedShift> {
645
+ return MaterializedShiftUtil.sortByStart(shifts).filter(
646
+ (shift: MaterializedShift) => {
647
+ return (
648
+ shift.start.getTime() < feedEnd.getTime() &&
649
+ shift.end.getTime() > feedStart.getTime()
650
+ );
651
+ },
652
+ );
653
+ }
654
+
655
+ /*
656
+ * Keep the feed under maxEvents by cutting whole UTC days off the END of
657
+ * the window (the nearest future matters most). When even the first day
658
+ * holds more than maxEvents shifts, the cut lands on the first excluded
659
+ * shift's start instead.
660
+ */
661
+ public static shrinkWindowToFit(
662
+ input: WindowShrinkInput,
663
+ ): WindowShrinkResult {
664
+ const maxEvents: number =
665
+ input.maxEvents !== undefined && input.maxEvents > 0
666
+ ? Math.floor(input.maxEvents)
667
+ : MAX_EVENTS;
668
+
669
+ const inWindow: Array<MaterializedShift> =
670
+ OnCallCalendarFeedUtil.filterShiftsToWindow(
671
+ input.shifts,
672
+ input.feedStart,
673
+ input.feedEnd,
674
+ );
675
+
676
+ if (inWindow.length <= maxEvents) {
677
+ return {
678
+ shifts: inWindow,
679
+ feedEnd: input.feedEnd,
680
+ truncated: false,
681
+ daysDropped: 0,
682
+ };
683
+ }
684
+
685
+ const firstExcluded: MaterializedShift = inWindow[maxEvents]!;
686
+
687
+ let newFeedEnd: Date = new Date(
688
+ CalendarFeedWindow.startOfUtcDay(firstExcluded.start),
689
+ );
690
+
691
+ if (newFeedEnd.getTime() <= input.feedStart.getTime()) {
692
+ newFeedEnd = new Date(firstExcluded.start.getTime());
693
+ }
694
+
695
+ let kept: Array<MaterializedShift> = inWindow.filter(
696
+ (shift: MaterializedShift) => {
697
+ return shift.start.getTime() < newFeedEnd.getTime();
698
+ },
699
+ );
700
+
701
+ /*
702
+ * Shifts that started before the window (in progress at feedStart) all
703
+ * share "start < newFeedEnd"; if there are more than maxEvents of those
704
+ * no cut point exists and a hard slice is the only option left.
705
+ */
706
+ if (kept.length > maxEvents) {
707
+ kept = kept.slice(0, maxEvents);
708
+ }
709
+
710
+ const daysDropped: number = Math.max(
711
+ 0,
712
+ Math.ceil(
713
+ (input.feedEnd.getTime() - newFeedEnd.getTime()) / MILLISECONDS_PER_DAY,
714
+ ),
715
+ );
716
+
717
+ return { shifts: kept, feedEnd: newFeedEnd, truncated: true, daysDropped };
718
+ }
719
+
720
+ /*
721
+ * ---------------------------------------------------------------------
722
+ * Coverage gaps
723
+ * ---------------------------------------------------------------------
724
+ */
725
+
726
+ /*
727
+ * Where the schedule's layers INTEND to cover, regardless of who (or
728
+ * whether anyone) is assigned and of when the layer starts: each layer is
729
+ * expanded with one synthetic user from the window start, so the result is
730
+ * exactly its restriction windows — the whole window for an unrestricted
731
+ * layer, Mon-Fri 09:00-17:00 for a business-hours layer. A coverage hole
732
+ * is only worth an event when it falls inside this envelope.
733
+ */
734
+ public static computeCoverageEnvelope(
735
+ input: CoverageEnvelopeInput,
736
+ ): CoverageEnvelopeResult {
737
+ const layerUtil: LayerUtil = new LayerUtil();
738
+ const segments: Array<TimeSegment> = [];
739
+ let truncated: boolean = false;
740
+
741
+ const syntheticUser: User = new User();
742
+ syntheticUser.id = new ObjectID(ENVELOPE_USER_ID);
743
+
744
+ for (const layer of input.layers) {
745
+ const result: LayerEventsResult = layerUtil.getEventsWithMeta(
746
+ {
747
+ users: [syntheticUser],
748
+ startDateTimeOfLayer: input.windowStart,
749
+ restrictionTimes: layer.restrictionTimes,
750
+ handOffTime: layer.handOffTime,
751
+ rotation: layer.rotation,
752
+ timezone: layer.timezone,
753
+ calendarStartDate: input.windowStart,
754
+ calendarEndDate: input.windowEnd,
755
+ },
756
+ input.maxSimulationIterations !== undefined
757
+ ? { maxSimulationIterations: input.maxSimulationIterations }
758
+ : undefined,
759
+ );
760
+
761
+ truncated = truncated || result.truncated;
762
+
763
+ for (const event of result.events) {
764
+ segments.push({ start: event.start, end: event.end });
765
+ }
766
+ }
767
+
768
+ /*
769
+ * The engine's 1-second seams (09:00:01 starts) would otherwise leak into
770
+ * gap events; normalise them exactly as the shifts themselves are.
771
+ */
772
+ return {
773
+ segments: OnCallCalendarFeedUtil.mergeSegments(
774
+ ShiftSeamUtil.normalizeSeams(segments),
775
+ ENVELOPE_MERGE_TOLERANCE_MILLISECONDS,
776
+ ),
777
+ truncated,
778
+ };
779
+ }
780
+
781
+ /*
782
+ * "No coverage" events: every hole before, between and after the shifts
783
+ * inside [feedStart, feedEnd) that intersects the envelope, clipped to the
784
+ * envelope, merged, at least minimumGapSeconds long, oldest first, capped at
785
+ * maxGapEvents. Off-hours of a business-hours schedule are outside the
786
+ * envelope and are therefore never emitted.
787
+ */
788
+ public static buildCoverageGapEvents(
789
+ input: CoverageGapEventsInput,
790
+ ): CoverageGapEventsResult {
791
+ const maxGapEvents: number =
792
+ input.maxGapEvents !== undefined && input.maxGapEvents >= 0
793
+ ? Math.floor(input.maxGapEvents)
794
+ : MAX_GAP_EVENTS;
795
+
796
+ const minimumGapMilliseconds: number =
797
+ Math.max(0, input.minimumGapSeconds ?? 0) * 1000;
798
+
799
+ const shifts: Array<OnCallShift> = [...input.shifts]
800
+ .sort((a: TimeSegment, b: TimeSegment) => {
801
+ return a.start.getTime() - b.start.getTime();
802
+ })
803
+ .map((segment: TimeSegment) => {
804
+ return {
805
+ userId: "",
806
+ start: segment.start,
807
+ end: segment.end,
808
+ coverageSeconds: OneUptimeDate.getDifferenceInSeconds(
809
+ segment.end,
810
+ segment.start,
811
+ ),
812
+ };
813
+ });
814
+
815
+ const rawGaps: Array<CoverageGap> = ScheduleShiftUtil.getCoverageGaps(
816
+ shifts,
817
+ input.feedStart,
818
+ input.feedEnd,
819
+ );
820
+
821
+ /*
822
+ * getCoverageGaps (shared with the dashboard's coverage view) treats the
823
+ * window end as an arbitrary cut and never reports the stretch after the
824
+ * last shift. For a feed the window end IS the horizon the envelope was
825
+ * computed for, so a rotation that stops mid-window (every user removed
826
+ * after a date, a layer whose users run out) must show its tail as "No
827
+ * coverage" too. The envelope intersection below still clips it to the
828
+ * hours a layer intended to cover, so a business-hours schedule whose last
829
+ * shift ends on Friday still emits nothing for the weekend.
830
+ */
831
+ if (shifts.length > 0) {
832
+ const coveredUntil: number = shifts.reduce(
833
+ (max: number, shift: OnCallShift): number => {
834
+ return Math.max(max, shift.end.getTime());
835
+ },
836
+ shifts[0]!.end.getTime(),
837
+ );
838
+
839
+ if (
840
+ input.feedEnd.getTime() - coveredUntil >
841
+ TRAILING_GAP_TOLERANCE_MILLISECONDS
842
+ ) {
843
+ rawGaps.push({ start: new Date(coveredUntil), end: input.feedEnd });
844
+ }
845
+ }
846
+
847
+ const envelope: Array<TimeSegment> = OnCallCalendarFeedUtil.mergeSegments(
848
+ input.envelope
849
+ .map((segment: TimeSegment) => {
850
+ return {
851
+ start: new Date(
852
+ Math.max(segment.start.getTime(), input.feedStart.getTime()),
853
+ ),
854
+ end: new Date(
855
+ Math.min(segment.end.getTime(), input.feedEnd.getTime()),
856
+ ),
857
+ };
858
+ })
859
+ .filter((segment: TimeSegment) => {
860
+ return segment.end.getTime() > segment.start.getTime();
861
+ }),
862
+ ENVELOPE_MERGE_TOLERANCE_MILLISECONDS,
863
+ );
864
+
865
+ const pieces: Array<TimeSegment> = [];
866
+
867
+ for (const gap of rawGaps) {
868
+ for (const intended of envelope) {
869
+ const start: number = Math.max(
870
+ gap.start.getTime(),
871
+ intended.start.getTime(),
872
+ );
873
+ const end: number = Math.min(gap.end.getTime(), intended.end.getTime());
874
+
875
+ if (end > start) {
876
+ pieces.push({ start: new Date(start), end: new Date(end) });
877
+ }
878
+ }
879
+ }
880
+
881
+ const gaps: Array<TimeSegment> = OnCallCalendarFeedUtil.mergeSegments(
882
+ pieces,
883
+ 0,
884
+ ).filter((gap: TimeSegment) => {
885
+ return gap.end.getTime() - gap.start.getTime() >= minimumGapMilliseconds;
886
+ });
887
+
888
+ const truncated: boolean = gaps.length > maxGapEvents;
889
+ const kept: Array<TimeSegment> = gaps.slice(0, maxGapEvents);
890
+
891
+ return {
892
+ events: kept.map((gap: TimeSegment) => {
893
+ return OnCallCalendarFeedUtil.gapToEvent(gap, input);
894
+ }),
895
+ gaps: kept,
896
+ truncated,
897
+ };
898
+ }
899
+
900
+ public static gapToEvent(
901
+ gap: TimeSegment,
902
+ input: {
903
+ scheduleId: string;
904
+ scheduleName: string;
905
+ projectId: string;
906
+ lastModifiedAt: Date;
907
+ shiftConfigVersion: number;
908
+ dashboardUrl: string;
909
+ },
910
+ ): ICalendarEvent {
911
+ const scheduleName: string =
912
+ OnCallCalendarFeedUtil.cleanName(input.scheduleName) || "Schedule";
913
+
914
+ const url: string = OnCallCalendarFeedUtil.getScheduleUrl(
915
+ input.dashboardUrl,
916
+ input.projectId,
917
+ input.scheduleId,
918
+ );
919
+
920
+ return {
921
+ uid: OnCallCalendarFeedUtil.getGapUid(input.scheduleId, gap.start),
922
+ dtStamp: input.lastModifiedAt,
923
+ lastModified: input.lastModifiedAt,
924
+ sequence: OnCallCalendarFeedUtil.toSequence(input.shiftConfigVersion),
925
+ start: gap.start,
926
+ end: gap.end,
927
+ summary: `No coverage · ${scheduleName}`,
928
+ description: [
929
+ `Nobody is on call for ${scheduleName} during this time, although a layer is meant to cover it.`,
930
+ `Fix the rotation: ${url}`,
931
+ OnCallCalendarFeedUtil.REFRESH_LINE,
932
+ ].join("\n"),
933
+ url,
934
+ status: ICalendarEventStatus.Confirmed,
935
+ transparency: ICalendarTransparency.Transparent,
936
+ categories: [OnCallCalendarFeedUtil.CATEGORY],
937
+ };
938
+ }
939
+
940
+ /*
941
+ * ---------------------------------------------------------------------
942
+ * Whole documents
943
+ * ---------------------------------------------------------------------
944
+ */
945
+
946
+ public static buildDocument(input: FeedRenderInput): ICalendarDocument {
947
+ const context: ShiftEventContext = {
948
+ kind: input.kind,
949
+ dashboardUrl: input.dashboardUrl,
950
+ viewerTimezone: input.viewerTimezone,
951
+ };
952
+
953
+ const events: Array<ICalendarEvent> = [
954
+ ...OnCallCalendarFeedUtil.shiftsToEvents(
955
+ input.shifts,
956
+ context,
957
+ input.contextShifts,
958
+ ),
959
+ ...(input.gapEvents ?? []),
960
+ ].sort((a: ICalendarEvent, b: ICalendarEvent) => {
961
+ const byStart: number = a.start.getTime() - b.start.getTime();
962
+ if (byStart !== 0) {
963
+ return byStart;
964
+ }
965
+ if (a.uid === b.uid) {
966
+ return 0;
967
+ }
968
+ return a.uid < b.uid ? -1 : 1;
969
+ });
970
+
971
+ const lastModifiedAt: Date | null =
972
+ input.lastModifiedAt ??
973
+ OnCallCalendarFeedUtil.getLatestModification(
974
+ input.shifts,
975
+ input.gapEvents,
976
+ );
977
+
978
+ const calendar: ICalendarCalendar =
979
+ OnCallCalendarFeedUtil.buildCalendarHeader({
980
+ kind: input.kind,
981
+ scheduleName: input.scheduleName,
982
+ projectName: input.projectName,
983
+ filterScheduleName: input.filterScheduleName,
984
+ timezone: input.calendarTimezone ?? input.viewerTimezone,
985
+ lastModifiedAt: lastModifiedAt ?? undefined,
986
+ notes: input.notes,
987
+ });
988
+
989
+ return { calendar, events };
990
+ }
991
+
992
+ public static render(input: FeedRenderInput): FeedRenderResult {
993
+ const document: ICalendarDocument =
994
+ OnCallCalendarFeedUtil.buildDocument(input);
995
+
996
+ return {
997
+ body: ICalendar.serialize(document),
998
+ eventCount: document.events.length,
999
+ lastModifiedAt: document.calendar.lastModified ?? null,
1000
+ };
1001
+ }
1002
+
1003
+ /*
1004
+ * The empty VCALENDAR every "200 but nothing to show" case serves (feed
1005
+ * disabled, rotated token inside its grace, project below plan, no eligible
1006
+ * schedule). X-WR-CALDESC carries the reason so a subscriber who looks can
1007
+ * tell why their calendar went blank.
1008
+ */
1009
+ public static renderEmpty(input: EmptyFeedInput): string {
1010
+ const calendar: ICalendarCalendar =
1011
+ OnCallCalendarFeedUtil.buildCalendarHeader({
1012
+ kind: input.kind,
1013
+ scheduleName: input.scheduleName,
1014
+ projectName: input.projectName,
1015
+ filterScheduleName: input.filterScheduleName,
1016
+ timezone: input.timezone,
1017
+ notes: [input.reason],
1018
+ });
1019
+
1020
+ return ICalendar.serialize({ calendar, events: [] });
1021
+ }
1022
+
1023
+ /*
1024
+ * ---------------------------------------------------------------------
1025
+ * Helpers
1026
+ * ---------------------------------------------------------------------
1027
+ */
1028
+
1029
+ public static isValidTimezone(timezone: string | null | undefined): boolean {
1030
+ return (
1031
+ typeof timezone === "string" &&
1032
+ timezone.trim() !== "" &&
1033
+ moment.tz.zone(timezone) !== null
1034
+ );
1035
+ }
1036
+
1037
+ // The zone a shift's wall clock is rendered in; legacy schedules use UTC.
1038
+ public static getScheduleZone(shift: MaterializedShift): string {
1039
+ return OnCallCalendarFeedUtil.isValidTimezone(shift.scheduleTimezone)
1040
+ ? shift.scheduleTimezone!
1041
+ : Timezone.UTC;
1042
+ }
1043
+
1044
+ public static truncateName(name: string, maxLength: number): string {
1045
+ const characters: Array<string> = Array.from(name);
1046
+
1047
+ if (characters.length <= maxLength) {
1048
+ return name;
1049
+ }
1050
+
1051
+ return `${characters
1052
+ .slice(0, Math.max(0, maxLength - 1))
1053
+ .join("")
1054
+ .trimEnd()}…`;
1055
+ }
1056
+
1057
+ // Sort by start and merge segments that overlap or sit within `toleranceMs`.
1058
+ public static mergeSegments(
1059
+ segments: Array<TimeSegment>,
1060
+ toleranceMilliseconds: number,
1061
+ ): Array<TimeSegment> {
1062
+ const sorted: Array<TimeSegment> = segments
1063
+ .filter((segment: TimeSegment) => {
1064
+ return segment.end.getTime() > segment.start.getTime();
1065
+ })
1066
+ .map((segment: TimeSegment) => {
1067
+ return {
1068
+ start: new Date(segment.start.getTime()),
1069
+ end: new Date(segment.end.getTime()),
1070
+ };
1071
+ })
1072
+ .sort((a: TimeSegment, b: TimeSegment) => {
1073
+ return a.start.getTime() - b.start.getTime();
1074
+ });
1075
+
1076
+ const merged: Array<TimeSegment> = [];
1077
+
1078
+ for (const segment of sorted) {
1079
+ const last: TimeSegment | undefined = merged[merged.length - 1];
1080
+
1081
+ if (
1082
+ last &&
1083
+ segment.start.getTime() <= last.end.getTime() + toleranceMilliseconds
1084
+ ) {
1085
+ if (segment.end.getTime() > last.end.getTime()) {
1086
+ last.end = segment.end;
1087
+ }
1088
+ continue;
1089
+ }
1090
+
1091
+ merged.push(segment);
1092
+ }
1093
+
1094
+ return merged;
1095
+ }
1096
+
1097
+ public static getDistinctPolicies(
1098
+ policies: Array<MaterializedShiftPolicy>,
1099
+ ): Array<MaterializedShiftPolicy> {
1100
+ const seen: Set<string> = new Set<string>();
1101
+ const distinct: Array<MaterializedShiftPolicy> = [];
1102
+
1103
+ for (const policy of policies) {
1104
+ if (seen.has(policy.policyId)) {
1105
+ continue;
1106
+ }
1107
+ seen.add(policy.policyId);
1108
+ distinct.push(policy);
1109
+ }
1110
+
1111
+ return distinct.sort(
1112
+ (a: MaterializedShiftPolicy, b: MaterializedShiftPolicy) => {
1113
+ return OnCallCalendarFeedUtil.compareStrings(
1114
+ a.policyName,
1115
+ b.policyName,
1116
+ );
1117
+ },
1118
+ );
1119
+ }
1120
+
1121
+ // "Payments › Primary (step 1); Billing › Backup (step 2)"
1122
+ public static describePolicies(
1123
+ policies: Array<MaterializedShiftPolicy>,
1124
+ ): string {
1125
+ return [...policies]
1126
+ .sort((a: MaterializedShiftPolicy, b: MaterializedShiftPolicy) => {
1127
+ return (
1128
+ OnCallCalendarFeedUtil.compareStrings(a.policyName, b.policyName) ||
1129
+ a.ruleOrder - b.ruleOrder ||
1130
+ OnCallCalendarFeedUtil.compareStrings(a.ruleName, b.ruleName)
1131
+ );
1132
+ })
1133
+ .map((policy: MaterializedShiftPolicy) => {
1134
+ return `${policy.policyName} › ${policy.ruleName} (step ${policy.ruleOrder})`;
1135
+ })
1136
+ .join("; ");
1137
+ }
1138
+
1139
+ private static buildShiftTimeLines(
1140
+ shift: MaterializedShift,
1141
+ scheduleZone: string,
1142
+ isLegacyZone: boolean,
1143
+ viewerTimezone: string | undefined,
1144
+ ): Array<string> {
1145
+ const zones: Array<string> = [scheduleZone];
1146
+
1147
+ if (scheduleZone !== Timezone.UTC) {
1148
+ zones.push(Timezone.UTC);
1149
+ }
1150
+
1151
+ if (
1152
+ OnCallCalendarFeedUtil.isValidTimezone(viewerTimezone) &&
1153
+ !zones.includes(viewerTimezone!)
1154
+ ) {
1155
+ zones.push(viewerTimezone!);
1156
+ }
1157
+
1158
+ const starts: Array<string> = OnCallCalendarFeedUtil.formatInZones(
1159
+ shift.start,
1160
+ zones,
1161
+ );
1162
+ const ends: Array<string> = OnCallCalendarFeedUtil.formatInZones(
1163
+ shift.end,
1164
+ zones,
1165
+ );
1166
+
1167
+ const lines: Array<string> = [];
1168
+
1169
+ for (let i: number = 0; i < zones.length; i++) {
1170
+ const zone: string = zones[i]!;
1171
+ const range: string = `${starts[i]} → ${ends[i]}`;
1172
+
1173
+ if (i === 0) {
1174
+ lines.push(
1175
+ `Shift: ${range} (${
1176
+ isLegacyZone
1177
+ ? `UTC — ${OnCallCalendarFeedUtil.LEGACY_TIMEZONE_NOTE}`
1178
+ : `${zone} — schedule zone`
1179
+ })`,
1180
+ );
1181
+ continue;
1182
+ }
1183
+
1184
+ const label: string =
1185
+ zone === Timezone.UTC && zone !== viewerTimezone
1186
+ ? "UTC"
1187
+ : `${zone} (your zone)`;
1188
+
1189
+ lines.push(`Shift in ${label}: ${range}`);
1190
+ }
1191
+
1192
+ return lines;
1193
+ }
1194
+
1195
+ /*
1196
+ * Mirror lines on a base shift for every policy variant that takes part of
1197
+ * it over.
1198
+ *
1199
+ * Matching is (same schedule) + (time overlap) + (different person). The
1200
+ * variant's own `policyVariantOf.globalUserId` is deliberately NOT required
1201
+ * to equal this shift's user: the materializer records only the FIRST
1202
+ * overlapping base user, so a variant spanning a handover would otherwise
1203
+ * inform the first person and leave the second — whose shift the variant
1204
+ * also takes over — reading "Pages you via <policy>" for a window in which
1205
+ * somebody else is paged for it.
1206
+ *
1207
+ * The quoted window is clipped to the overlap so the sentence names exactly
1208
+ * the part of THIS shift that is taken over.
1209
+ */
1210
+ private static buildVariantMirrorLines(
1211
+ shift: MaterializedShift,
1212
+ variantIndex: PolicyVariantIndex,
1213
+ kind: OnCallCalendarFeedKind,
1214
+ scheduleZone: string,
1215
+ ): Array<string> {
1216
+ const candidates: Array<MaterializedShift> | undefined = variantIndex.get(
1217
+ shift.scheduleId,
1218
+ );
1219
+
1220
+ if (!candidates || candidates.length === 0) {
1221
+ return [];
1222
+ }
1223
+
1224
+ const lines: Array<string> = [];
1225
+
1226
+ for (const other of candidates) {
1227
+ if (!other.policyVariantOf || other.userId === shift.userId) {
1228
+ continue;
1229
+ }
1230
+
1231
+ const overlapStart: number = Math.max(
1232
+ other.start.getTime(),
1233
+ shift.start.getTime(),
1234
+ );
1235
+ const overlapEnd: number = Math.min(
1236
+ other.end.getTime(),
1237
+ shift.end.getTime(),
1238
+ );
1239
+
1240
+ if (overlapStart >= overlapEnd) {
1241
+ continue;
1242
+ }
1243
+
1244
+ lines.push(
1245
+ `For ${other.policyVariantOf.policyName}, ${OnCallCalendarFeedUtil.getDisplayName(
1246
+ other.userName,
1247
+ kind,
1248
+ )} is paged instead from ${OnCallCalendarFeedUtil.formatInZone(
1249
+ new Date(overlapStart),
1250
+ scheduleZone,
1251
+ )} to ${OnCallCalendarFeedUtil.formatInZone(
1252
+ new Date(overlapEnd),
1253
+ scheduleZone,
1254
+ )}.`,
1255
+ );
1256
+ }
1257
+
1258
+ return lines;
1259
+ }
1260
+
1261
+ /*
1262
+ * Bucket the policy-variant shifts of a feed by schedule id, sorted inside
1263
+ * each bucket so the DESCRIPTION lines are deterministic (the body cache and
1264
+ * the ETag depend on it). Returns null when there is not a single variant —
1265
+ * the overwhelmingly common case — so the mirror-line pass is skipped
1266
+ * outright instead of scanning every shift for every shift.
1267
+ */
1268
+ public static buildVariantIndex(
1269
+ shifts: Array<MaterializedShift>,
1270
+ ): PolicyVariantIndex | null {
1271
+ let index: PolicyVariantIndex | null = null;
1272
+
1273
+ for (const shift of shifts) {
1274
+ if (!shift.policyVariantOf) {
1275
+ continue;
1276
+ }
1277
+
1278
+ if (!index) {
1279
+ index = new Map<string, Array<MaterializedShift>>();
1280
+ }
1281
+
1282
+ const bucket: Array<MaterializedShift> | undefined = index.get(
1283
+ shift.scheduleId,
1284
+ );
1285
+
1286
+ if (bucket) {
1287
+ bucket.push(shift);
1288
+ } else {
1289
+ index.set(shift.scheduleId, [shift]);
1290
+ }
1291
+ }
1292
+
1293
+ if (!index) {
1294
+ return null;
1295
+ }
1296
+
1297
+ for (const bucket of index.values()) {
1298
+ bucket.sort((a: MaterializedShift, b: MaterializedShift) => {
1299
+ return (
1300
+ a.start.getTime() - b.start.getTime() ||
1301
+ a.end.getTime() - b.end.getTime() ||
1302
+ OnCallCalendarFeedUtil.compareStrings(
1303
+ a.policyVariantOf!.policyId,
1304
+ b.policyVariantOf!.policyId,
1305
+ ) ||
1306
+ OnCallCalendarFeedUtil.compareStrings(a.userId, b.userId)
1307
+ );
1308
+ });
1309
+ }
1310
+
1311
+ return index;
1312
+ }
1313
+
1314
+ /*
1315
+ * The policies this shift actually pages through. A policy-variant shift
1316
+ * exists for exactly one policy — the schedule's other policies still page
1317
+ * the rostered user — but the materializer stamps every shift of a schedule
1318
+ * with its full attachment set, so listing them all on a variant would
1319
+ * contradict the sentence directly below it. Falls back to the full list if
1320
+ * the variant's policy is somehow not among them, so a shift never claims to
1321
+ * page nobody.
1322
+ */
1323
+ public static getPagingPolicies(
1324
+ shift: MaterializedShift,
1325
+ ): Array<MaterializedShiftPolicy> {
1326
+ if (!shift.policyVariantOf) {
1327
+ return shift.policies;
1328
+ }
1329
+
1330
+ const variantPolicyId: string = shift.policyVariantOf.policyId;
1331
+
1332
+ const scoped: Array<MaterializedShiftPolicy> = shift.policies.filter(
1333
+ (policy: MaterializedShiftPolicy) => {
1334
+ return policy.policyId === variantPolicyId;
1335
+ },
1336
+ );
1337
+
1338
+ return scoped.length > 0 ? scoped : shift.policies;
1339
+ }
1340
+
1341
+ private static formatInZones(
1342
+ date: Date,
1343
+ zones: Array<string>,
1344
+ ): Array<string> {
1345
+ return OneUptimeDate.getDateAsFormattedStringInMultipleTimezones({
1346
+ date,
1347
+ timezones: zones as Array<Timezone>,
1348
+ use12HourFormat: false,
1349
+ }).split("\n");
1350
+ }
1351
+
1352
+ // Same formatter as the shift lines, so every date in a DESCRIPTION matches.
1353
+ private static formatInZone(date: Date, zone: string): string {
1354
+ return OnCallCalendarFeedUtil.formatInZones(date, [zone])[0] ?? "";
1355
+ }
1356
+
1357
+ private static findPolicyName(
1358
+ policies: Array<MaterializedShiftPolicy>,
1359
+ policyId: string,
1360
+ ): string {
1361
+ const match: MaterializedShiftPolicy | undefined = policies.find(
1362
+ (policy: MaterializedShiftPolicy) => {
1363
+ return policy.policyId === policyId;
1364
+ },
1365
+ );
1366
+
1367
+ return match ? match.policyName : "an escalation policy";
1368
+ }
1369
+
1370
+ private static getLatestModification(
1371
+ shifts: Array<MaterializedShift>,
1372
+ gapEvents: Array<ICalendarEvent> | undefined,
1373
+ ): Date | null {
1374
+ let latest: number | null = null;
1375
+
1376
+ for (const shift of shifts) {
1377
+ const time: number = shift.lastModifiedAt.getTime();
1378
+ if (latest === null || time > latest) {
1379
+ latest = time;
1380
+ }
1381
+ }
1382
+
1383
+ for (const event of gapEvents ?? []) {
1384
+ const time: number = event.dtStamp.getTime();
1385
+ if (latest === null || time > latest) {
1386
+ latest = time;
1387
+ }
1388
+ }
1389
+
1390
+ return latest === null ? null : new Date(latest);
1391
+ }
1392
+
1393
+ private static toSequence(version: number): number {
1394
+ return Number.isFinite(version) ? Math.max(0, Math.floor(version)) : 0;
1395
+ }
1396
+
1397
+ private static epochSeconds(date: Date): number {
1398
+ return Math.floor(date.getTime() / 1000);
1399
+ }
1400
+
1401
+ private static cleanName(name: string | null | undefined): string {
1402
+ return (name ?? "").replace(/\s+/g, " ").trim();
1403
+ }
1404
+
1405
+ private static trimTrailingSlash(url: string): string {
1406
+ return url.replace(/\/+$/, "");
1407
+ }
1408
+
1409
+ private static compareStrings(a: string, b: string): number {
1410
+ if (a === b) {
1411
+ return 0;
1412
+ }
1413
+ return a < b ? -1 : 1;
1414
+ }
1415
+ }