@oneuptime/common 12.0.28 → 12.0.30

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 (367) hide show
  1. package/Models/DatabaseModels/DashboardDomain.ts +43 -0
  2. package/Models/DatabaseModels/Index.ts +11 -0
  3. package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
  4. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +43 -0
  5. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +662 -0
  6. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +584 -0
  7. package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
  8. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +546 -0
  9. package/Models/DatabaseModels/UserOnCallShiftReminder.ts +231 -0
  10. package/Models/DatabaseModels/UserOnCallShiftReminderLog.ts +331 -0
  11. package/Server/API/DashboardDomainAPI.ts +74 -0
  12. package/Server/API/MicrosoftTeamsAPI.ts +81 -180
  13. package/Server/API/OnCallCalendarAPI.ts +2194 -0
  14. package/Server/API/StatusPageDomainAPI.ts +74 -0
  15. package/Server/EnvironmentConfig.ts +138 -0
  16. package/Server/Infrastructure/OnCallCalendarFeedCache.ts +1050 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.ts +253 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  21. package/Server/Middleware/OnCallCalendarFeedRateLimit.ts +487 -0
  22. package/Server/Middleware/ProjectAuthorization.ts +6 -1
  23. package/Server/Services/DashboardDomainService.ts +131 -0
  24. package/Server/Services/Index.ts +12 -0
  25. package/Server/Services/LogPipelineProcessorService.ts +88 -0
  26. package/Server/Services/MonitorTemplateService.ts +70 -11
  27. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +32 -0
  28. package/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.ts +260 -0
  29. package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +70 -3
  30. package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +138 -3
  31. package/Server/Services/OnCallDutyPolicyScheduleService.ts +1465 -134
  32. package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +167 -31
  33. package/Server/Services/ProjectOnCallCalendarFeedService.ts +192 -0
  34. package/Server/Services/StatusPageDomainService.ts +163 -3
  35. package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
  36. package/Server/Services/TeamMemberService.ts +489 -1
  37. package/Server/Services/TeamService.ts +50 -0
  38. package/Server/Services/UserNotificationSettingService.ts +44 -1
  39. package/Server/Services/UserOnCallCalendarFeedService.ts +159 -0
  40. package/Server/Services/UserOnCallShiftReminderLogService.ts +151 -0
  41. package/Server/Services/UserOnCallShiftReminderService.ts +112 -0
  42. package/Server/Types/Domain.ts +17 -0
  43. package/Server/Utils/APIKey/AccessPermission.ts +24 -0
  44. package/Server/Utils/Greenlock/Greenlock.ts +156 -58
  45. package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
  46. package/Server/Utils/OnCall/CalendarFeedToken.ts +241 -0
  47. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1488 -0
  48. package/Server/Utils/OnCall/OnCallCalendarFeedUrls.ts +230 -0
  49. package/Server/Utils/OnCall/OnCallShiftChangeListeners.ts +227 -0
  50. package/Server/Utils/OnCall/OnCallShiftMaterializer.ts +1127 -0
  51. package/Server/Utils/OnCall/OnCallShiftReminderListener.ts +174 -0
  52. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +2584 -0
  53. package/Server/Utils/Response.ts +134 -0
  54. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
  55. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
  56. package/Server/Utils/StartServer.ts +10 -0
  57. package/Server/Utils/Telemetry/AppMetrics.ts +51 -0
  58. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
  59. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
  60. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
  61. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
  62. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
  63. package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
  64. package/Tests/App/Dashboard/OnCallCalendarFeedLinks.test.tsx +229 -0
  65. package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +489 -0
  66. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +149 -0
  67. package/Tests/App/Dashboard/OnCallCalendarFeedUtil.test.ts +855 -0
  68. package/Tests/App/Dashboard/OnCallPersonalCalendarFeed.test.tsx +1209 -0
  69. package/Tests/App/Dashboard/OnCallSharedCalendarFeedCard.test.tsx +1019 -0
  70. package/Tests/App/Dashboard/OnCallShiftRemindersAndUpcomingShifts.test.tsx +655 -0
  71. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +73 -0
  72. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
  73. package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
  74. package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
  75. package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
  76. package/Tests/Models/OnCallCalendarFeedModels.test.ts +642 -0
  77. package/Tests/Models/OnCallDutyPolicyScheduleCalendarFeed.test.ts +288 -0
  78. package/Tests/Models/OnCallDutyPolicyScheduleShiftConfigVersion.test.ts +88 -0
  79. package/Tests/Models/UserOnCallShiftReminderModels.test.ts +368 -0
  80. package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
  81. package/Tests/Server/API/Helpers.ts +8 -0
  82. package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
  83. package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
  84. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
  85. package/Tests/Server/API/OnCallCalendarAPI.test.ts +4461 -0
  86. package/Tests/Server/EnvironmentConfigOnCallCalendarFeed.test.ts +357 -0
  87. package/Tests/Server/Infrastructure/OnCallCalendarFeedCache.test.ts +1589 -0
  88. package/Tests/Server/Infrastructure/Postgres/AddOnCallCalendarFeedsMigration.test.ts +369 -0
  89. package/Tests/Server/Middleware/OnCallCalendarFeedRateLimit.test.ts +1365 -0
  90. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
  91. package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
  92. package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
  93. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +70 -0
  94. package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
  95. package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
  96. package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
  97. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
  98. package/Tests/Server/Services/OnCallCalendarFeedServices.test.ts +1103 -0
  99. package/Tests/Server/Services/OnCallDutyPolicyScheduleResolver.test.ts +1037 -0
  100. package/Tests/Server/Services/OnCallShiftConfigPropagation.test.ts +1270 -0
  101. package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
  102. package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
  103. package/Tests/Server/Services/TeamDeleteMemberCleanup.test.ts +156 -0
  104. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +755 -0
  105. package/Tests/Server/Services/UserNotificationSettingShiftReminderDefaults.test.ts +267 -0
  106. package/Tests/Server/Services/UserOnCallShiftReminderServices.test.ts +583 -0
  107. package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
  108. package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
  109. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +254 -0
  110. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
  111. package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
  112. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
  113. package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
  114. package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
  115. package/Tests/Server/Utils/OnCall/CalendarFeedToken.test.ts +467 -0
  116. package/Tests/Server/Utils/OnCall/OnCallCalendarFeedRenderer.test.ts +2142 -0
  117. package/Tests/Server/Utils/OnCall/OnCallCalendarFeedUrls.test.ts +278 -0
  118. package/Tests/Server/Utils/OnCall/OnCallResolverTestHarness.ts +440 -0
  119. package/Tests/Server/Utils/OnCall/OnCallShiftChangeListeners.test.ts +314 -0
  120. package/Tests/Server/Utils/OnCall/OnCallShiftMaterializer.test.ts +1502 -0
  121. package/Tests/Server/Utils/OnCall/OnCallShiftReminderListener.test.ts +491 -0
  122. package/Tests/Server/Utils/OnCall/OnCallShiftReminderRunner.test.ts +3088 -0
  123. package/Tests/Server/Utils/OnCall/OnCallShiftReminderTestHarness.ts +910 -0
  124. package/Tests/Server/Utils/ResponseSendCalendarResponse.test.ts +561 -0
  125. package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
  126. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
  127. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
  128. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
  129. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
  130. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
  131. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
  132. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
  133. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
  134. package/Tests/Server/Utils/StartServerEncryptionSecretWarning.test.ts +64 -0
  135. package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +29 -0
  136. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
  137. package/Tests/Types/API/HTTPResponse.test.ts +175 -0
  138. package/Tests/Types/API/Route.test.ts +36 -4
  139. package/Tests/Types/Calendar/ICalendar.test.ts +488 -0
  140. package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
  141. package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
  142. package/Tests/Types/DateLocalTimeString.test.ts +259 -0
  143. package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
  144. package/Tests/Types/NotificationSetting/NotificationSettingEventType.test.ts +53 -0
  145. package/Tests/Types/OnCallDutyPolicy/CalendarFeedTestFixtures.ts +310 -0
  146. package/Tests/Types/OnCallDutyPolicy/CalendarFeedWindow.test.ts +205 -0
  147. package/Tests/Types/OnCallDutyPolicy/LayerUtilLayerMetaAndIterationCap.test.ts +458 -0
  148. package/Tests/Types/OnCallDutyPolicy/MaterializedShift.test.ts +197 -0
  149. package/Tests/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.test.ts +1986 -0
  150. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtilGroupKey.test.ts +678 -0
  151. package/Tests/Types/OnCallDutyPolicy/ShiftSeamUtil.test.ts +417 -0
  152. package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
  153. package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
  154. package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
  155. package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
  156. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
  157. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
  158. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  159. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  160. package/Tests/UI/Utils/Navigation.test.ts +82 -0
  161. package/Tests/Utils/Array.test.ts +100 -0
  162. package/Tests/Utils/CertificateReissue.test.ts +225 -0
  163. package/Tests/Utils/Grok/Grok.test.ts +492 -0
  164. package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
  165. package/Types/API/Route.ts +20 -5
  166. package/Types/Calendar/ICalendar.ts +442 -0
  167. package/Types/Date.ts +143 -13
  168. package/Types/Email/EmailTemplateType.ts +2 -0
  169. package/Types/NotificationSetting/NotificationSettingEventType.ts +4 -0
  170. package/Types/OnCallDutyPolicy/CalendarFeedWindow.ts +140 -0
  171. package/Types/OnCallDutyPolicy/Layer.ts +190 -35
  172. package/Types/OnCallDutyPolicy/MaterializedShift.ts +274 -0
  173. package/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.ts +1415 -0
  174. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +159 -4
  175. package/Types/OnCallDutyPolicy/ShiftSeamUtil.ts +106 -0
  176. package/Types/Rum/SessionReplay.ts +62 -1
  177. package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
  178. package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
  179. package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
  180. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
  181. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
  182. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
  183. package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
  184. package/UI/Utils/Navigation.ts +42 -0
  185. package/Utils/Array.ts +37 -0
  186. package/Utils/CertificateReissue.ts +128 -0
  187. package/Utils/Grok/Grok.ts +461 -0
  188. package/Utils/Grok/GrokPatterns.ts +118 -0
  189. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
  190. package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
  191. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/Index.js +11 -0
  193. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
  195. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +44 -0
  197. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +699 -0
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -0
  200. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +620 -0
  201. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -0
  202. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
  203. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +585 -0
  205. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -0
  206. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js +247 -0
  207. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js.map +1 -0
  208. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js +355 -0
  209. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js.map +1 -0
  210. package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
  211. package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
  212. package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
  213. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  214. package/build/dist/Server/API/OnCallCalendarAPI.js +1371 -0
  215. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -0
  216. package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
  217. package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
  218. package/build/dist/Server/EnvironmentConfig.js +102 -0
  219. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  220. package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js +663 -0
  221. package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js.map +1 -0
  222. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
  223. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
  224. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
  225. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
  226. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js +103 -0
  227. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js.map +1 -0
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  230. package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js +342 -0
  231. package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js.map +1 -0
  232. package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
  233. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  234. package/build/dist/Server/Services/DashboardDomainService.js +104 -0
  235. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  236. package/build/dist/Server/Services/Index.js +12 -0
  237. package/build/dist/Server/Services/Index.js.map +1 -1
  238. package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
  239. package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
  240. package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
  241. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  242. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +20 -0
  243. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  244. package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js +219 -0
  245. package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js.map +1 -0
  246. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +47 -3
  247. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
  248. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +112 -3
  249. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
  250. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +955 -9
  251. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  252. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +93 -6
  253. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
  254. package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js +157 -0
  255. package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js.map +1 -0
  256. package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
  257. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  258. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
  259. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
  260. package/build/dist/Server/Services/TeamMemberService.js +396 -1
  261. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  262. package/build/dist/Server/Services/TeamService.js +43 -0
  263. package/build/dist/Server/Services/TeamService.js.map +1 -1
  264. package/build/dist/Server/Services/UserNotificationSettingService.js +26 -1
  265. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  266. package/build/dist/Server/Services/UserOnCallCalendarFeedService.js +140 -0
  267. package/build/dist/Server/Services/UserOnCallCalendarFeedService.js.map +1 -0
  268. package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js +116 -0
  269. package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js.map +1 -0
  270. package/build/dist/Server/Services/UserOnCallShiftReminderService.js +92 -0
  271. package/build/dist/Server/Services/UserOnCallShiftReminderService.js.map +1 -0
  272. package/build/dist/Server/Types/Domain.js +16 -0
  273. package/build/dist/Server/Types/Domain.js.map +1 -1
  274. package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
  275. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  276. package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
  277. package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
  278. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
  279. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
  280. package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js +161 -0
  281. package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js.map +1 -0
  282. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js +983 -0
  283. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js.map +1 -0
  284. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js +155 -0
  285. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js.map +1 -0
  286. package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js +145 -0
  287. package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js.map +1 -0
  288. package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js +752 -0
  289. package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js.map +1 -0
  290. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js +92 -0
  291. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js.map +1 -0
  292. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1786 -0
  293. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -0
  294. package/build/dist/Server/Utils/Response.js +97 -0
  295. package/build/dist/Server/Utils/Response.js.map +1 -1
  296. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
  297. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  298. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
  299. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  300. package/build/dist/Server/Utils/StartServer.js +9 -1
  301. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  302. package/build/dist/Server/Utils/Telemetry/AppMetrics.js +41 -0
  303. package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
  304. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
  305. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  306. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
  307. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  308. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
  309. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
  310. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
  311. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  312. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
  313. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  314. package/build/dist/Types/API/Route.js +16 -5
  315. package/build/dist/Types/API/Route.js.map +1 -1
  316. package/build/dist/Types/Calendar/ICalendar.js +290 -0
  317. package/build/dist/Types/Calendar/ICalendar.js.map +1 -0
  318. package/build/dist/Types/Date.js +115 -13
  319. package/build/dist/Types/Date.js.map +1 -1
  320. package/build/dist/Types/Email/EmailTemplateType.js +2 -0
  321. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  322. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
  323. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
  324. package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js +97 -0
  325. package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js.map +1 -0
  326. package/build/dist/Types/OnCallDutyPolicy/Layer.js +113 -19
  327. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  328. package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js +134 -0
  329. package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js.map +1 -0
  330. package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js +800 -0
  331. package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js.map +1 -0
  332. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +97 -3
  333. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  334. package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js +77 -0
  335. package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js.map +1 -0
  336. package/build/dist/Types/Rum/SessionReplay.js +38 -0
  337. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  338. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
  339. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
  340. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
  341. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  342. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
  343. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
  344. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
  345. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  346. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
  347. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  348. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
  349. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  350. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
  351. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
  352. package/build/dist/UI/Utils/Navigation.js +30 -0
  353. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  354. package/build/dist/Utils/Array.js +25 -0
  355. package/build/dist/Utils/Array.js.map +1 -1
  356. package/build/dist/Utils/CertificateReissue.js +97 -0
  357. package/build/dist/Utils/CertificateReissue.js.map +1 -0
  358. package/build/dist/Utils/Grok/Grok.js +324 -0
  359. package/build/dist/Utils/Grok/Grok.js.map +1 -0
  360. package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
  361. package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
  362. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
  363. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  364. package/package.json +1 -1
  365. package/UI/Utils/JsonWebToken.ts +0 -14
  366. package/build/dist/UI/Utils/JsonWebToken.js +0 -10
  367. package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
@@ -1,10 +1,98 @@
1
1
  import DatabaseService from "./DatabaseService";
2
2
  import Model from "../../Models/DatabaseModels/LogPipelineProcessor";
3
+ import CreateBy from "../Types/Database/CreateBy";
4
+ import UpdateBy from "../Types/Database/UpdateBy";
5
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
6
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
7
+ import { JSONObject } from "../../Types/JSON";
8
+ import { validateLogPipelineProcessor } from "../Utils/LogPipelineProcessorValidation";
9
+
10
+ /*
11
+ * `PartialEntity` values may be `() => string` raw SQL expressions rather
12
+ * than literals. One cannot be evaluated here, so an update that sets a
13
+ * validated field to an expression is skipped - validating a stringified
14
+ * function would be worse than not validating at all. Nothing in the
15
+ * product writes processor fields this way.
16
+ */
17
+ function isSqlExpressionValue(value: unknown): boolean {
18
+ return typeof value === "function";
19
+ }
3
20
 
4
21
  export class Service extends DatabaseService<Model> {
5
22
  public constructor() {
6
23
  super(Model);
7
24
  }
25
+
26
+ protected override async onBeforeCreate(
27
+ createBy: CreateBy<Model>,
28
+ ): Promise<OnCreate<Model>> {
29
+ validateLogPipelineProcessor({
30
+ processorType: createBy.data.processorType,
31
+ configuration: createBy.data.configuration as JSONObject | undefined,
32
+ });
33
+
34
+ return { createBy, carryForward: null };
35
+ }
36
+
37
+ protected override async onBeforeUpdate(
38
+ updateBy: UpdateBy<Model>,
39
+ ): Promise<OnUpdate<Model>> {
40
+ /*
41
+ * An edit can break a row without naming both fields: switching
42
+ * `processorType` to GrokParser while the stored configuration is
43
+ * still a severity-remapper blob, or replacing the pattern on a row
44
+ * that is already a GrokParser. So validate the MERGED row.
45
+ *
46
+ * Only pay for the SELECT when the update touches one of them.
47
+ * Processor edits are rare, human-driven config changes - this is
48
+ * not a hot path.
49
+ */
50
+ const incomingProcessorType: unknown = updateBy.data.processorType;
51
+ const incomingConfiguration: unknown = updateBy.data.configuration;
52
+
53
+ if (
54
+ incomingProcessorType === undefined &&
55
+ incomingConfiguration === undefined
56
+ ) {
57
+ return { updateBy, carryForward: null };
58
+ }
59
+
60
+ if (
61
+ isSqlExpressionValue(incomingProcessorType) ||
62
+ isSqlExpressionValue(incomingConfiguration)
63
+ ) {
64
+ return { updateBy, carryForward: null };
65
+ }
66
+
67
+ const existingRows: Array<Model> = await this.findBy({
68
+ query: updateBy.query,
69
+ skip: 0,
70
+ limit: LIMIT_MAX,
71
+ select: {
72
+ _id: true,
73
+ processorType: true,
74
+ configuration: true,
75
+ },
76
+ props: {
77
+ isRoot: true,
78
+ },
79
+ });
80
+
81
+ for (const existing of existingRows) {
82
+ validateLogPipelineProcessor({
83
+ processorType:
84
+ incomingProcessorType !== undefined
85
+ ? (incomingProcessorType as string | null)
86
+ : existing.processorType,
87
+ configuration:
88
+ incomingConfiguration !== undefined
89
+ ? (incomingConfiguration as JSONObject | string | null)
90
+ : (existing.configuration as JSONObject | undefined),
91
+ });
92
+ }
93
+
94
+ return { updateBy, carryForward: null };
95
+ }
8
96
  }
9
97
 
10
98
  export default new Service();
@@ -6,6 +6,7 @@ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
6
6
  import MonitorStepsProjectValidator from "../Utils/Monitor/MonitorStepsProjectValidator";
7
7
  import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
8
8
  import BadDataException from "../../Types/Exception/BadDataException";
9
+ import Includes from "../../Types/BaseDatabase/Includes";
9
10
  import LIMIT_MAX from "../../Types/Database/LimitMax";
10
11
  import { JSONObject } from "../../Types/JSON";
11
12
  import MonitorSteps from "../../Types/Monitor/MonitorSteps";
@@ -275,7 +276,7 @@ export class Service extends DatabaseService<Model> {
275
276
  monitorSteps: true,
276
277
  autoProvisionedNetworkDeviceId: true,
277
278
  },
278
- sort: { createdAt: SortOrder.Ascending },
279
+ sort: { createdAt: SortOrder.Ascending, _id: SortOrder.Ascending },
279
280
  limit: LIMIT_MAX,
280
281
  skip: skip,
281
282
  props: { isRoot: true },
@@ -321,16 +322,74 @@ export class Service extends DatabaseService<Model> {
321
322
  return { totalLinkedMonitors, syncedMonitors };
322
323
  }
323
324
 
324
- const syncedMonitors: number = await MonitorService.updateBy({
325
- query: {
326
- monitorTemplateId: template.id!,
327
- projectId: template.projectId,
328
- },
329
- data: updateData as any,
330
- limit: LIMIT_MAX,
331
- skip: 0,
332
- props: data.props,
333
- });
325
+ /*
326
+ * A single updateBy is capped at its `limit`, so a template with more
327
+ * linked monitors than LIMIT_MAX used to leave the remainder on the old
328
+ * config and still return success. Offset paging cannot fix that here:
329
+ * updateBy takes no sort, and rewriting a row moves its tuple, so a
330
+ * second page at a higher skip can step over rows the first page moved.
331
+ * Page the ids first instead, then update by id batch.
332
+ *
333
+ * `_id` rides along in the sort because skip/limit paging is only stable
334
+ * over a total order: a fleet provisioned by one auto-import run shares a
335
+ * createdAt, and without the tiebreaker a tie straddling a page boundary
336
+ * returns one row twice and another never — which here would inflate
337
+ * syncedMonitors by exactly the number of rows it dropped.
338
+ */
339
+ const linkedMonitorIds: Array<ObjectID> = [];
340
+
341
+ for (let skip: number = 0; ; skip += LIMIT_MAX) {
342
+ const monitors: Array<Monitor> = await MonitorService.findBy({
343
+ query: {
344
+ monitorTemplateId: template.id!,
345
+ projectId: template.projectId,
346
+ },
347
+ select: { _id: true },
348
+ sort: { createdAt: SortOrder.Ascending, _id: SortOrder.Ascending },
349
+ limit: LIMIT_MAX,
350
+ skip: skip,
351
+ props: { isRoot: true },
352
+ });
353
+
354
+ for (const monitor of monitors) {
355
+ linkedMonitorIds.push(monitor.id!);
356
+ }
357
+
358
+ if (monitors.length < LIMIT_MAX) {
359
+ break;
360
+ }
361
+ }
362
+
363
+ let syncedMonitors: number = 0;
364
+
365
+ for (
366
+ let batchStart: number = 0;
367
+ batchStart < linkedMonitorIds.length;
368
+ batchStart += LIMIT_MAX
369
+ ) {
370
+ const batch: Array<ObjectID> = linkedMonitorIds.slice(
371
+ batchStart,
372
+ batchStart + LIMIT_MAX,
373
+ );
374
+
375
+ /*
376
+ * Still scoped by template and project, so a stale id from the paging
377
+ * read above cannot widen the write beyond this template's fleet.
378
+ * props stays the caller's — updateBy narrows each batch to what they
379
+ * may actually update.
380
+ */
381
+ syncedMonitors += await MonitorService.updateBy({
382
+ query: {
383
+ _id: new Includes(batch),
384
+ monitorTemplateId: template.id!,
385
+ projectId: template.projectId,
386
+ },
387
+ data: updateData as any,
388
+ limit: LIMIT_MAX,
389
+ skip: 0,
390
+ props: data.props,
391
+ });
392
+ }
334
393
 
335
394
  return {
336
395
  totalLinkedMonitors,
@@ -25,12 +25,35 @@ import { WhatsAppMessagePayload } from "../../Types/WhatsApp/WhatsAppMessage";
25
25
  import OnCallDutyPolicyTimeLogService from "./OnCallDutyPolicyTimeLogService";
26
26
  import OneUptimeDate from "../../Types/Date";
27
27
  import logger, { LogAttributes } from "../Utils/Logger";
28
+ import { OnCallShiftChangeReason } from "../Utils/OnCall/OnCallShiftChangeListeners";
28
29
 
29
30
  export class Service extends DatabaseService<Model> {
30
31
  public constructor() {
31
32
  super(Model);
32
33
  }
33
34
 
35
+ /**
36
+ * Attaching to / detaching from a policy changes the schedule's policy
37
+ * context (which overrides apply, and which policies its calendar events
38
+ * list), so it is a configuration change for the calendar feeds and the
39
+ * reminders: bump shiftConfigVersion, purge the feed caches, notify the
40
+ * listeners. Runs after the roster refresh; never throws.
41
+ */
42
+ private async propagateAttachmentChange(
43
+ onCallDutyPolicyScheduleId: ObjectID | null | undefined,
44
+ projectId: ObjectID | null | undefined,
45
+ ): Promise<void> {
46
+ if (!onCallDutyPolicyScheduleId) {
47
+ return;
48
+ }
49
+
50
+ await OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
51
+ scheduleIds: [onCallDutyPolicyScheduleId],
52
+ projectId: projectId || null,
53
+ reason: OnCallShiftChangeReason.PolicyAttachmentChanged,
54
+ });
55
+ }
56
+
34
57
  /**
35
58
  * Re-resolve and persist the schedule's roster after it is attached to, or
36
59
  * detached from, an on-call policy.
@@ -123,6 +146,11 @@ export class Service extends DatabaseService<Model> {
123
146
  */
124
147
  await this.refreshScheduleRoster(createdModel.onCallDutyPolicyScheduleId);
125
148
 
149
+ await this.propagateAttachmentChange(
150
+ createdModel.onCallDutyPolicyScheduleId,
151
+ createdModel.projectId,
152
+ );
153
+
126
154
  // send notification to the new current user.
127
155
 
128
156
  const userOnSchedule: ObjectID | null =
@@ -387,6 +415,10 @@ export class Service extends DatabaseService<Model> {
387
415
  }
388
416
  refreshedScheduleIds.add(scheduleId);
389
417
  await this.refreshScheduleRoster(deletedItem.onCallDutyPolicyScheduleId);
418
+ await this.propagateAttachmentChange(
419
+ deletedItem.onCallDutyPolicyScheduleId,
420
+ deletedItem.projectId,
421
+ );
390
422
  }
391
423
 
392
424
  for (const deletedItem of deletedItems) {
@@ -0,0 +1,260 @@
1
+ import CreateBy from "../Types/Database/CreateBy";
2
+ import UpdateBy from "../Types/Database/UpdateBy";
3
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
4
+ import DatabaseService from "./DatabaseService";
5
+ import OnCallDutyPolicyScheduleService from "./OnCallDutyPolicyScheduleService";
6
+ import CalendarFeedToken, {
7
+ CalendarFeedRotation,
8
+ } from "../Utils/OnCall/CalendarFeedToken";
9
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
10
+ import Model from "../../Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed";
11
+ import OnCallDutyPolicySchedule from "../../Models/DatabaseModels/OnCallDutyPolicySchedule";
12
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
13
+ import BadDataException from "../../Types/Exception/BadDataException";
14
+ import NotFoundException from "../../Types/Exception/NotFoundException";
15
+ import CalendarFeedWindow from "../../Types/OnCallDutyPolicy/CalendarFeedWindow";
16
+ import ObjectID from "../../Types/ObjectID";
17
+ import PositiveNumber from "../../Types/PositiveNumber";
18
+
19
+ /*
20
+ * Bounds for the coverage-gap threshold: a gap shorter than a minute is a
21
+ * seam artefact, and a threshold longer than a week would never emit.
22
+ */
23
+ export const MIN_MINIMUM_GAP_MINUTES: number = 1;
24
+ export const MAX_MINIMUM_GAP_MINUTES: number = 7 * 24 * 60;
25
+
26
+ /*
27
+ * The shared, project-owned calendar feed of one on-call schedule.
28
+ *
29
+ * WHAT THE HOOKS GUARANTEE
30
+ *
31
+ * - The schedule exists in the caller's project. The foreign key alone
32
+ * would accept a schedule from another project (the FK does not know
33
+ * about tenants), and a feed that renders another project's roster to
34
+ * whoever holds this project's link is exactly the leak the tenant column
35
+ * exists to prevent.
36
+ * - One feed per schedule. The unique index enforces it at the database;
37
+ * the hook turns the constraint violation into a message a person can
38
+ * act on.
39
+ * - The token is minted HERE, on every create, whatever the request
40
+ * carried. Publishing a feed is a normal, permission-checked create
41
+ * (the schedule's Edit list), and the plaintext is then read back by the
42
+ * calendar API's `/current` route through the encrypted `token` column.
43
+ * - pastDays / futureDays are clamped and minimumGapMinutes is bounded on
44
+ * every write.
45
+ *
46
+ * rotateTokenById and rotateFeedsForMemberLeave are root writes for the
47
+ * calendar API and the team-member cleanup respectively; cache purging stays
48
+ * with the caller.
49
+ */
50
+ export class Service extends DatabaseService<Model> {
51
+ public constructor() {
52
+ super(Model);
53
+ }
54
+
55
+ @CaptureSpan()
56
+ protected override async onBeforeCreate(
57
+ createBy: CreateBy<Model>,
58
+ ): Promise<OnCreate<Model>> {
59
+ const projectId: ObjectID | undefined =
60
+ createBy.data.projectId || createBy.props.tenantId;
61
+
62
+ if (!projectId) {
63
+ throw new BadDataException("projectId is required");
64
+ }
65
+
66
+ const scheduleId: ObjectID | undefined =
67
+ createBy.data.onCallDutyPolicyScheduleId;
68
+
69
+ if (!scheduleId) {
70
+ throw new BadDataException("onCallDutyPolicyScheduleId is required");
71
+ }
72
+
73
+ createBy.data.projectId = projectId;
74
+
75
+ const schedule: OnCallDutyPolicySchedule | null =
76
+ await OnCallDutyPolicyScheduleService.findOneBy({
77
+ query: {
78
+ _id: scheduleId,
79
+ projectId: projectId,
80
+ },
81
+ select: {
82
+ _id: true,
83
+ },
84
+ props: {
85
+ isRoot: true,
86
+ ignoreHooks: true,
87
+ },
88
+ });
89
+
90
+ if (!schedule) {
91
+ throw new BadDataException("On-call schedule not found in this project.");
92
+ }
93
+
94
+ const existing: PositiveNumber = await this.countBy({
95
+ query: {
96
+ onCallDutyPolicyScheduleId: scheduleId,
97
+ },
98
+ props: {
99
+ isRoot: true,
100
+ },
101
+ });
102
+
103
+ if (existing.toNumber() > 0) {
104
+ throw new BadDataException(
105
+ "This schedule already has a shared calendar feed. Regenerate or edit the existing one instead.",
106
+ );
107
+ }
108
+
109
+ /*
110
+ * A request body never chooses the secret. Root callers (the calendar
111
+ * API) may pass a token they minted so they can answer with the URL in
112
+ * the same response; everyone else gets a fresh mint.
113
+ */
114
+ CalendarFeedToken.applyTokenColumnsOnCreate(createBy.data, {
115
+ trustSuppliedToken: createBy.props.isRoot === true,
116
+ });
117
+
118
+ createBy.data.pastDays = CalendarFeedWindow.clampPastDays(
119
+ createBy.data.pastDays,
120
+ );
121
+ createBy.data.futureDays = CalendarFeedWindow.clampFutureDays(
122
+ createBy.data.futureDays,
123
+ );
124
+
125
+ if (createBy.data.minimumGapMinutes !== undefined) {
126
+ createBy.data.minimumGapMinutes = Service.validateMinimumGapMinutes(
127
+ createBy.data.minimumGapMinutes,
128
+ );
129
+ }
130
+
131
+ return { createBy, carryForward: null };
132
+ }
133
+
134
+ @CaptureSpan()
135
+ protected override async onBeforeUpdate(
136
+ updateBy: UpdateBy<Model>,
137
+ ): Promise<OnUpdate<Model>> {
138
+ if (updateBy.data.pastDays !== undefined) {
139
+ updateBy.data.pastDays = CalendarFeedWindow.clampPastDays(
140
+ updateBy.data.pastDays,
141
+ );
142
+ }
143
+
144
+ if (updateBy.data.futureDays !== undefined) {
145
+ updateBy.data.futureDays = CalendarFeedWindow.clampFutureDays(
146
+ updateBy.data.futureDays,
147
+ );
148
+ }
149
+
150
+ if (updateBy.data.minimumGapMinutes !== undefined) {
151
+ updateBy.data.minimumGapMinutes = Service.validateMinimumGapMinutes(
152
+ updateBy.data.minimumGapMinutes,
153
+ );
154
+ }
155
+
156
+ return { updateBy, carryForward: null };
157
+ }
158
+
159
+ /*
160
+ * A whole number of minutes between MIN_MINIMUM_GAP_MINUTES and
161
+ * MAX_MINIMUM_GAP_MINUTES. Numeric strings are accepted because a root
162
+ * caller may hand over what a form posted; anything else is a 400.
163
+ */
164
+ public static validateMinimumGapMinutes(value: unknown): number {
165
+ const parsed: number =
166
+ typeof value === "string" && value.trim() !== ""
167
+ ? Number(value)
168
+ : (value as number);
169
+
170
+ if (
171
+ typeof parsed !== "number" ||
172
+ !Number.isInteger(parsed) ||
173
+ parsed < MIN_MINIMUM_GAP_MINUTES ||
174
+ parsed > MAX_MINIMUM_GAP_MINUTES
175
+ ) {
176
+ throw new BadDataException(
177
+ `Minimum gap must be a whole number of minutes between ${MIN_MINIMUM_GAP_MINUTES} and ${MAX_MINIMUM_GAP_MINUTES}.`,
178
+ );
179
+ }
180
+
181
+ return parsed;
182
+ }
183
+
184
+ /**
185
+ * Rotate the token of an existing feed: mint a new one, move the current
186
+ * hash into the 30-day grace slot, stamp rotatedAt. Returns the new
187
+ * plaintext token ONCE. Root write; the caller purges caches.
188
+ */
189
+ @CaptureSpan()
190
+ public async rotateTokenById(data: {
191
+ id: ObjectID;
192
+ }): Promise<CalendarFeedRotation> {
193
+ const existing: Model | null = await this.findOneBy({
194
+ query: { _id: data.id },
195
+ select: { _id: true, tokenHash: true },
196
+ props: { isRoot: true, ignoreHooks: true },
197
+ });
198
+
199
+ if (!existing) {
200
+ throw new NotFoundException("Calendar feed not found.");
201
+ }
202
+
203
+ const rotation: CalendarFeedRotation = CalendarFeedToken.buildRotation({
204
+ currentTokenHash: existing.tokenHash,
205
+ });
206
+
207
+ await this.updateOneById({
208
+ id: data.id,
209
+ data: CalendarFeedToken.toRotationUpdateData(rotation),
210
+ props: { isRoot: true },
211
+ });
212
+
213
+ return rotation;
214
+ }
215
+
216
+ /**
217
+ * Rotate every ENABLED feed in the project that opted into
218
+ * rotateWhenMemberLeaves. Called by the team-member cleanup when a user's
219
+ * last team membership in the project goes away. Returns the ids rotated
220
+ * so the caller can purge their caches. A disabled feed is left alone: it
221
+ * already serves an empty calendar, and rotating it would only reset the
222
+ * grace clock on a link nobody should be refreshing anyway.
223
+ */
224
+ @CaptureSpan()
225
+ public async rotateFeedsForMemberLeave(data: {
226
+ projectId: ObjectID;
227
+ }): Promise<Array<ObjectID>> {
228
+ const feeds: Array<Model> = await this.findBy({
229
+ query: {
230
+ projectId: data.projectId,
231
+ isEnabled: true,
232
+ rotateWhenMemberLeaves: true,
233
+ },
234
+ select: {
235
+ _id: true,
236
+ },
237
+ limit: LIMIT_MAX,
238
+ skip: 0,
239
+ props: {
240
+ isRoot: true,
241
+ ignoreHooks: true,
242
+ },
243
+ });
244
+
245
+ const rotated: Array<ObjectID> = [];
246
+
247
+ for (const feed of feeds) {
248
+ if (!feed.id) {
249
+ continue;
250
+ }
251
+
252
+ await this.rotateTokenById({ id: feed.id });
253
+ rotated.push(feed.id);
254
+ }
255
+
256
+ return rotated;
257
+ }
258
+ }
259
+
260
+ export default new Service();
@@ -17,12 +17,61 @@ import UpdateBy from "../Types/Database/UpdateBy";
17
17
  import Model from "../../Models/DatabaseModels/OnCallDutyPolicyScheduleLayer";
18
18
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
19
19
  import OnCallDutyPolicyScheduleService from "./OnCallDutyPolicyScheduleService";
20
+ import { OnCallShiftChangeReason } from "../Utils/OnCall/OnCallShiftChangeListeners";
21
+ import logger from "../Utils/Logger";
20
22
 
21
23
  export class Service extends DatabaseService<Model> {
22
24
  public constructor() {
23
25
  super(Model);
24
26
  }
25
27
 
28
+ /*
29
+ * A layer edit changes who is on call and when for the whole schedule:
30
+ * bump its shiftConfigVersion, drop the calendar-feed caches and let the
31
+ * shift-change listeners re-plan. Runs AFTER the roster refresh so a
32
+ * listener that re-materializes sees the new configuration. Best-effort by
33
+ * construction (propagateShiftConfigChange never throws).
34
+ */
35
+ private async propagateLayerChange(
36
+ scheduleId: ObjectID | null | undefined,
37
+ projectId: ObjectID | null | undefined,
38
+ ): Promise<void> {
39
+ if (!scheduleId) {
40
+ return;
41
+ }
42
+
43
+ await OnCallDutyPolicyScheduleService.propagateShiftConfigChange({
44
+ scheduleIds: [scheduleId],
45
+ projectId: projectId || null,
46
+ reason: OnCallShiftChangeReason.LayerChanged,
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Re-resolve and persist the schedule's roster after a layer change.
52
+ * Best-effort: the row is already committed when the success hooks run, so
53
+ * a throwing refresh (a concurrently-deleted schedule, a transient
54
+ * persistence or notification failure) must not abort the hook before
55
+ * propagateLayerChange bumps the shiftConfigVersion and purges the feed
56
+ * caches — otherwise the caches would keep serving the pre-edit roster for
57
+ * up to their TTL and the reminder change pass would be skipped. Mirrors
58
+ * OnCallDutyPolicyEscalationRuleScheduleService.refreshScheduleRoster.
59
+ */
60
+ private async refreshScheduleRosterBestEffort(
61
+ scheduleId: ObjectID,
62
+ ): Promise<void> {
63
+ try {
64
+ await OnCallDutyPolicyScheduleService.refreshCurrentUserIdAndHandoffTimeInSchedule(
65
+ scheduleId,
66
+ );
67
+ } catch (err) {
68
+ logger.error(
69
+ "Error refreshing the schedule roster after a layer change (best-effort).",
70
+ );
71
+ logger.error(err);
72
+ }
73
+ }
74
+
26
75
  /*
27
76
  * A rotation interval count of 0 / NaN / negative breaks the handoff math
28
77
  * (division by zero, Invalid Date), so reject it at the persistence boundary.
@@ -153,6 +202,7 @@ export class Service extends DatabaseService<Model> {
153
202
  id: createdItem.id!,
154
203
  select: {
155
204
  onCallDutyPolicyScheduleId: true,
205
+ projectId: true,
156
206
  },
157
207
  props: {
158
208
  isRoot: true,
@@ -163,8 +213,13 @@ export class Service extends DatabaseService<Model> {
163
213
  return createdItem;
164
214
  }
165
215
 
166
- await OnCallDutyPolicyScheduleService.refreshCurrentUserIdAndHandoffTimeInSchedule(
216
+ await this.refreshScheduleRosterBestEffort(
217
+ resource.onCallDutyPolicyScheduleId,
218
+ );
219
+
220
+ await this.propagateLayerChange(
167
221
  resource.onCallDutyPolicyScheduleId,
222
+ resource.projectId,
168
223
  );
169
224
 
170
225
  return createdItem;
@@ -192,6 +247,7 @@ export class Service extends DatabaseService<Model> {
192
247
  id: item,
193
248
  select: {
194
249
  onCallDutyPolicyScheduleId: true,
250
+ projectId: true,
195
251
  },
196
252
  props: {
197
253
  isRoot: true,
@@ -202,8 +258,13 @@ export class Service extends DatabaseService<Model> {
202
258
  continue;
203
259
  }
204
260
 
205
- await OnCallDutyPolicyScheduleService.refreshCurrentUserIdAndHandoffTimeInSchedule(
261
+ await this.refreshScheduleRosterBestEffort(
262
+ resource.onCallDutyPolicyScheduleId,
263
+ );
264
+
265
+ await this.propagateLayerChange(
206
266
  resource.onCallDutyPolicyScheduleId,
267
+ resource.projectId,
207
268
  );
208
269
  }
209
270
 
@@ -245,6 +306,7 @@ export class Service extends DatabaseService<Model> {
245
306
  select: {
246
307
  order: true,
247
308
  onCallDutyPolicyScheduleId: true,
309
+ projectId: true,
248
310
  },
249
311
  });
250
312
  }
@@ -271,8 +333,13 @@ export class Service extends DatabaseService<Model> {
271
333
  false,
272
334
  );
273
335
 
274
- await OnCallDutyPolicyScheduleService.refreshCurrentUserIdAndHandoffTimeInSchedule(
336
+ await this.refreshScheduleRosterBestEffort(
337
+ resource.onCallDutyPolicyScheduleId,
338
+ );
339
+
340
+ await this.propagateLayerChange(
275
341
  resource.onCallDutyPolicyScheduleId,
342
+ resource.projectId,
276
343
  );
277
344
  }
278
345
  }