@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
@@ -0,0 +1,477 @@
1
+ import StatusPageDomainService from "../../../Server/Services/StatusPageDomainService";
2
+ import DashboardDomainService from "../../../Server/Services/DashboardDomainService";
3
+ import CertificateReissueUtil from "../../../Utils/CertificateReissue";
4
+ import OneUptimeDate from "../../../Types/Date";
5
+ import BadDataException from "../../../Types/Exception/BadDataException";
6
+ import TooManyRequestsException from "../../../Types/Exception/TooManyRequestsException";
7
+ import ObjectID from "../../../Types/ObjectID";
8
+ import { FindOperator } from "typeorm";
9
+ import { afterEach, describe, expect, jest, test } from "@jest/globals";
10
+
11
+ /*
12
+ * reissueCert - the service behind the dashboard's "Reissue SSL" button, on
13
+ * both custom-domain surfaces.
14
+ *
15
+ * Every certificate OneUptime orders is ordered against a single Let's
16
+ * Encrypt account shared by the whole installation, and the automated renewal
17
+ * cron spends from the same allowance. A customer-pressable button on top of
18
+ * that is only safe if three things hold, and each of them is a way the
19
+ * feature can quietly stop protecting anything:
20
+ *
21
+ * 1. The cooldown is claimed BEFORE the order is attempted, and is not
22
+ * rolled back when the order fails. A failed order still costs a
23
+ * validation attempt at the CA, and a failing domain is exactly the one
24
+ * somebody presses again.
25
+ * 2. The cooldown is claimed by the WRITE, not by a read above it - two
26
+ * clicks that arrive together both see a row that is not cooling down,
27
+ * so only a conditional update can pick a winner.
28
+ * 3. A domain with nothing to reissue never reaches the CA at all.
29
+ *
30
+ * No database and no ACME client: findOneBy, updateOneBy and orderCert are
31
+ * all spied on, so what is asserted is the ORDER and the CONDITIONS of those
32
+ * calls, which is precisely where the protection lives.
33
+ */
34
+
35
+ type DomainRow = {
36
+ _id: string;
37
+ id: ObjectID;
38
+ fullDomain: string;
39
+ isCnameVerified: boolean;
40
+ isSslOrdered: boolean;
41
+ isCustomCertificate: boolean;
42
+ certificateReissueRequestedAt?: Date | undefined;
43
+ };
44
+
45
+ const DOMAIN_ID: ObjectID = ObjectID.generate();
46
+
47
+ type MakeDomainFunction = (overrides?: Partial<DomainRow>) => DomainRow;
48
+
49
+ const makeDomain: MakeDomainFunction = (
50
+ overrides: Partial<DomainRow> = {},
51
+ ): DomainRow => {
52
+ return {
53
+ _id: DOMAIN_ID.toString(),
54
+ id: DOMAIN_ID,
55
+ fullDomain: "status.example.com",
56
+ isCnameVerified: true,
57
+ isSslOrdered: true,
58
+ isCustomCertificate: false,
59
+ certificateReissueRequestedAt: undefined,
60
+ ...overrides,
61
+ };
62
+ };
63
+
64
+ type ReissueService = {
65
+ reissueCert: (id: ObjectID) => Promise<void>;
66
+ };
67
+
68
+ type HarnessCalls = {
69
+ // Every updateOneBy the service made, in order.
70
+ updates: Array<{
71
+ query: Record<string, unknown>;
72
+ data: Record<string, unknown>;
73
+ }>;
74
+ // Domains handed to orderCert, in order.
75
+ ordered: Array<string>;
76
+ };
77
+
78
+ type SetUpHarnessFunction = (data: {
79
+ service: unknown;
80
+ domain: DomainRow | null;
81
+ // Rows the conditional claim reports as written. 1 = claim won, 0 = lost.
82
+ claimedRowCount?: number;
83
+ orderCertThrows?: Error;
84
+ }) => HarnessCalls;
85
+
86
+ const setUpHarness: SetUpHarnessFunction = (data: {
87
+ service: unknown;
88
+ domain: DomainRow | null;
89
+ claimedRowCount?: number;
90
+ orderCertThrows?: Error;
91
+ }): HarnessCalls => {
92
+ const calls: HarnessCalls = { updates: [], ordered: [] };
93
+
94
+ jest
95
+ .spyOn(data.service as never, "findOneBy")
96
+ .mockResolvedValue(data.domain as never);
97
+
98
+ jest
99
+ .spyOn(data.service as never, "updateOneBy")
100
+ .mockImplementation((async (update: {
101
+ query: Record<string, unknown>;
102
+ data: Record<string, unknown>;
103
+ }): Promise<number> => {
104
+ calls.updates.push({ query: update.query, data: update.data });
105
+ return data.claimedRowCount === undefined ? 1 : data.claimedRowCount;
106
+ }) as never);
107
+
108
+ jest
109
+ .spyOn(data.service as never, "orderCert")
110
+ .mockImplementation((async (domain: {
111
+ fullDomain?: string;
112
+ }): Promise<void> => {
113
+ calls.ordered.push(domain.fullDomain as string);
114
+
115
+ if (data.orderCertThrows) {
116
+ throw data.orderCertThrows;
117
+ }
118
+ }) as never);
119
+
120
+ return calls;
121
+ };
122
+
123
+ const services: Array<[string, unknown]> = [
124
+ ["StatusPageDomainService", StatusPageDomainService],
125
+ ["DashboardDomainService", DashboardDomainService],
126
+ ];
127
+
128
+ describe.each(services)("%s.reissueCert", (_name: string, service: unknown) => {
129
+ afterEach(() => {
130
+ jest.restoreAllMocks();
131
+ });
132
+
133
+ test("orders a fresh certificate for an eligible domain", async () => {
134
+ const calls: HarnessCalls = setUpHarness({
135
+ service,
136
+ domain: makeDomain(),
137
+ });
138
+
139
+ await (service as ReissueService).reissueCert(DOMAIN_ID);
140
+
141
+ expect(calls.ordered).toEqual(["status.example.com"]);
142
+ });
143
+
144
+ /*
145
+ * The whole point of a reissue: it must reach the CA even though the
146
+ * domain already has a working certificate. order-ssl refuses in exactly
147
+ * this state ("SSL is already provisioned"), which is why this button had
148
+ * to exist at all.
149
+ */
150
+ test("reissues a domain that already has a provisioned certificate", async () => {
151
+ const calls: HarnessCalls = setUpHarness({
152
+ service,
153
+ domain: makeDomain({ isSslOrdered: true }),
154
+ });
155
+
156
+ await (service as ReissueService).reissueCert(DOMAIN_ID);
157
+
158
+ expect(calls.ordered).toHaveLength(1);
159
+ });
160
+
161
+ describe("the cooldown", () => {
162
+ test("stamps the request before ordering anything", async () => {
163
+ const order: Array<string> = [];
164
+
165
+ jest
166
+ .spyOn(service as never, "findOneBy")
167
+ .mockResolvedValue(makeDomain() as never);
168
+
169
+ jest
170
+ .spyOn(service as never, "updateOneBy")
171
+ .mockImplementation((async (): Promise<number> => {
172
+ order.push("stamp");
173
+ return 1;
174
+ }) as never);
175
+
176
+ jest
177
+ .spyOn(service as never, "orderCert")
178
+ .mockImplementation((async (): Promise<void> => {
179
+ order.push("order");
180
+ }) as never);
181
+
182
+ await (service as ReissueService).reissueCert(DOMAIN_ID);
183
+
184
+ expect(order).toEqual(["stamp", "order"]);
185
+ });
186
+
187
+ /*
188
+ * The regression that would make the throttle useless. A domain whose
189
+ * CNAME is half broken fails validation on every attempt, and Let's
190
+ * Encrypt allows only five failed validations per hostname per hour.
191
+ * If a failed order released the cooldown, that budget - which the
192
+ * renewal cron shares - could be spent in seconds.
193
+ */
194
+ test("keeps the stamp when the order fails", async () => {
195
+ const calls: HarnessCalls = setUpHarness({
196
+ service,
197
+ domain: makeDomain(),
198
+ orderCertThrows: new Error("CA refused the order"),
199
+ });
200
+
201
+ await expect(
202
+ (service as ReissueService).reissueCert(DOMAIN_ID),
203
+ ).rejects.toThrow("CA refused the order");
204
+
205
+ expect(calls.updates).toHaveLength(1);
206
+ expect(
207
+ calls.updates[0]!.data["certificateReissueRequestedAt"],
208
+ ).toBeInstanceOf(Date);
209
+ });
210
+
211
+ test("claims the row with the cooldown in the query, not with a read", async () => {
212
+ /*
213
+ * Two clicks that arrive together both read a row that is not cooling
214
+ * down. Only a write whose WHERE clause carries the cooldown can pick
215
+ * a winner - which is why the condition has to be in the query.
216
+ */
217
+ const calls: HarnessCalls = setUpHarness({
218
+ service,
219
+ domain: makeDomain(),
220
+ });
221
+
222
+ await (service as ReissueService).reissueCert(DOMAIN_ID);
223
+
224
+ expect(calls.updates).toHaveLength(1);
225
+ expect(calls.updates[0]!.query["_id"]).toBe(DOMAIN_ID.toString());
226
+ expect(
227
+ calls.updates[0]!.query["certificateReissueRequestedAt"],
228
+ ).toBeDefined();
229
+ });
230
+
231
+ /*
232
+ * The condition is rendered to SQL rather than compared by shape,
233
+ * because the two mistakes worth catching here both produce a
234
+ * FindOperator that looks identical from the outside.
235
+ */
236
+ test("a never-reissued domain is eligible, so the first press works", async () => {
237
+ const calls: HarnessCalls = setUpHarness({
238
+ service,
239
+ domain: makeDomain(),
240
+ });
241
+
242
+ await (service as ReissueService).reissueCert(DOMAIN_ID);
243
+
244
+ const claim: FindOperator<Date> = calls.updates[0]!.query[
245
+ "certificateReissueRequestedAt"
246
+ ] as FindOperator<Date>;
247
+
248
+ /*
249
+ * The OR IS NULL half. Without it every domain that has never been
250
+ * reissued - which is every domain that exists the day this ships -
251
+ * fails the claim and the button refuses on its very first press.
252
+ */
253
+ const sql: string = claim.getSql!("col");
254
+
255
+ expect(sql).toContain("IS NULL");
256
+ expect(sql).toContain("<=");
257
+ });
258
+
259
+ test("the claim compares against exactly the cutoff the dashboard renders", async () => {
260
+ const calls: HarnessCalls = setUpHarness({
261
+ service,
262
+ domain: makeDomain(),
263
+ });
264
+
265
+ const before: Date = OneUptimeDate.getCurrentDate();
266
+ await (service as ReissueService).reissueCert(DOMAIN_ID);
267
+ const after: Date = OneUptimeDate.getCurrentDate();
268
+
269
+ const claim: FindOperator<Date> = calls.updates[0]!.query[
270
+ "certificateReissueRequestedAt"
271
+ ] as FindOperator<Date>;
272
+
273
+ const boundValues: Array<unknown> = Object.values(
274
+ claim.objectLiteralParameters as Record<string, unknown>,
275
+ );
276
+
277
+ expect(boundValues).toHaveLength(1);
278
+
279
+ const boundCutoff: Date = OneUptimeDate.fromString(
280
+ boundValues[0] as Date,
281
+ );
282
+
283
+ /*
284
+ * "now" moves between the assertion and the call, so the bound cutoff
285
+ * is pinned to the window the call could have observed rather than to
286
+ * a single instant. Anything other than one cooldown behind now -
287
+ * a cooldown ahead, or no offset at all - falls outside it.
288
+ */
289
+ expect(boundCutoff.getTime()).toBeGreaterThanOrEqual(
290
+ CertificateReissueUtil.getCooldownCutoff(before).getTime(),
291
+ );
292
+ expect(boundCutoff.getTime()).toBeLessThanOrEqual(
293
+ CertificateReissueUtil.getCooldownCutoff(after).getTime(),
294
+ );
295
+ });
296
+
297
+ test("refuses with a 429 when the claim finds the row still cooling down", async () => {
298
+ const calls: HarnessCalls = setUpHarness({
299
+ service,
300
+ domain: makeDomain({
301
+ certificateReissueRequestedAt: OneUptimeDate.addRemoveHours(
302
+ OneUptimeDate.getCurrentDate(),
303
+ -1,
304
+ ),
305
+ }),
306
+ claimedRowCount: 0,
307
+ });
308
+
309
+ await expect(
310
+ (service as ReissueService).reissueCert(DOMAIN_ID),
311
+ ).rejects.toThrow(TooManyRequestsException);
312
+
313
+ expect(calls.ordered).toEqual([]);
314
+ });
315
+
316
+ test("the refusal tells the customer how long is left", async () => {
317
+ setUpHarness({
318
+ service,
319
+ domain: makeDomain({
320
+ certificateReissueRequestedAt: OneUptimeDate.addRemoveHours(
321
+ OneUptimeDate.getCurrentDate(),
322
+ -1,
323
+ ),
324
+ }),
325
+ claimedRowCount: 0,
326
+ });
327
+
328
+ await expect(
329
+ (service as ReissueService).reissueCert(DOMAIN_ID),
330
+ ).rejects.toThrow(/try again in/i);
331
+ });
332
+
333
+ /*
334
+ * Losing the claim race against a request that landed a millisecond
335
+ * earlier looks identical to a cooldown at the database, but the row
336
+ * this request read has no stamp on it yet. It must still refuse rather
337
+ * than fall through and order.
338
+ */
339
+ test("refuses when the claim is lost and there is no stamp to explain it", async () => {
340
+ const calls: HarnessCalls = setUpHarness({
341
+ service,
342
+ domain: makeDomain({ certificateReissueRequestedAt: undefined }),
343
+ claimedRowCount: 0,
344
+ });
345
+
346
+ await expect(
347
+ (service as ReissueService).reissueCert(DOMAIN_ID),
348
+ ).rejects.toThrow(BadDataException);
349
+
350
+ expect(calls.ordered).toEqual([]);
351
+ });
352
+ });
353
+
354
+ describe("domains with nothing to reissue never reach the CA", () => {
355
+ test("a domain that does not exist", async () => {
356
+ const calls: HarnessCalls = setUpHarness({ service, domain: null });
357
+
358
+ await expect(
359
+ (service as ReissueService).reissueCert(DOMAIN_ID),
360
+ ).rejects.toThrow(BadDataException);
361
+
362
+ expect(calls.ordered).toEqual([]);
363
+ expect(calls.updates).toEqual([]);
364
+ });
365
+
366
+ test("a domain using a customer-uploaded certificate", async () => {
367
+ const calls: HarnessCalls = setUpHarness({
368
+ service,
369
+ domain: makeDomain({ isCustomCertificate: true }),
370
+ });
371
+
372
+ await expect(
373
+ (service as ReissueService).reissueCert(DOMAIN_ID),
374
+ ).rejects.toThrow(BadDataException);
375
+
376
+ expect(calls.ordered).toEqual([]);
377
+ expect(calls.updates).toEqual([]);
378
+ });
379
+
380
+ test("a domain whose CNAME is not verified", async () => {
381
+ const calls: HarnessCalls = setUpHarness({
382
+ service,
383
+ domain: makeDomain({ isCnameVerified: false }),
384
+ });
385
+
386
+ await expect(
387
+ (service as ReissueService).reissueCert(DOMAIN_ID),
388
+ ).rejects.toThrow(/CNAME is not verified/i);
389
+
390
+ expect(calls.ordered).toEqual([]);
391
+ expect(calls.updates).toEqual([]);
392
+ });
393
+
394
+ test("a domain that never ordered a certificate in the first place", async () => {
395
+ const calls: HarnessCalls = setUpHarness({
396
+ service,
397
+ domain: makeDomain({ isSslOrdered: false }),
398
+ });
399
+
400
+ await expect(
401
+ (service as ReissueService).reissueCert(DOMAIN_ID),
402
+ ).rejects.toThrow(/order one first/i);
403
+
404
+ expect(calls.ordered).toEqual([]);
405
+ expect(calls.updates).toEqual([]);
406
+ });
407
+
408
+ test("a domain with no full domain resolved", async () => {
409
+ const calls: HarnessCalls = setUpHarness({
410
+ service,
411
+ domain: makeDomain({ fullDomain: "" }),
412
+ });
413
+
414
+ await expect(
415
+ (service as ReissueService).reissueCert(DOMAIN_ID),
416
+ ).rejects.toThrow(BadDataException);
417
+
418
+ expect(calls.ordered).toEqual([]);
419
+ expect(calls.updates).toEqual([]);
420
+ });
421
+
422
+ /*
423
+ * An ineligible domain must be refused without a stamp: refusing it AND
424
+ * burning its cooldown would leave a customer who fixed their CNAME
425
+ * waiting a day for a button that never cost the CA anything.
426
+ */
427
+ test("an ineligible domain does not burn its cooldown", async () => {
428
+ const calls: HarnessCalls = setUpHarness({
429
+ service,
430
+ domain: makeDomain({ isCnameVerified: false }),
431
+ });
432
+
433
+ await expect(
434
+ (service as ReissueService).reissueCert(DOMAIN_ID),
435
+ ).rejects.toThrow();
436
+
437
+ expect(calls.updates).toEqual([]);
438
+ });
439
+ });
440
+
441
+ describe("what the service reads", () => {
442
+ test("selects every field the eligibility checks depend on", async () => {
443
+ const selects: Array<Record<string, unknown>> = [];
444
+
445
+ jest
446
+ .spyOn(service as never, "findOneBy")
447
+ .mockImplementation((async (find: {
448
+ select: Record<string, unknown>;
449
+ }): Promise<DomainRow> => {
450
+ selects.push(find.select);
451
+ return makeDomain();
452
+ }) as never);
453
+
454
+ jest.spyOn(service as never, "updateOneBy").mockResolvedValue(1 as never);
455
+ jest
456
+ .spyOn(service as never, "orderCert")
457
+ .mockResolvedValue(undefined as never);
458
+
459
+ await (service as ReissueService).reissueCert(DOMAIN_ID);
460
+
461
+ /*
462
+ * A field that is not selected reads back as undefined, which is
463
+ * falsy - so a forgotten select here would silently turn a guard into
464
+ * a refusal for everybody, or (for isCustomCertificate) into a hole.
465
+ */
466
+ for (const field of [
467
+ "fullDomain",
468
+ "isCnameVerified",
469
+ "isSslOrdered",
470
+ "isCustomCertificate",
471
+ "certificateReissueRequestedAt",
472
+ ]) {
473
+ expect(selects[0]![field]).toBe(true);
474
+ }
475
+ });
476
+ });
477
+ });
@@ -54,10 +54,46 @@ const THREAT_INTEL_FEED_MODEL_SOURCE: string = fs.readFileSync(
54
54
  const RETENTION_PATTERN: RegExp =
55
55
  /hardDeleteItemsOlderThanInDays\(\s*"(\w+)"\s*,\s*([^)]+?)\s*\)/;
56
56
 
57
+ /*
58
+ * Alphabetical, because it is compared with a sorted directory listing.
59
+ *
60
+ * The three on-call CALENDAR feeds are configuration in the same sense as the
61
+ * threat-intel feed: each row is a long-lived capability token (the calendar
62
+ * subscription URL) plus its settings and fetch bookkeeping. Age-expiring one
63
+ * would silently break every calendar app subscribed to it after three years.
64
+ * Their semantics are pinned in "calendar feeds own a subscription token"
65
+ * below.
66
+ */
57
67
  const CONFIGURATION_FEED_SERVICE_NAMES: ReadonlyArray<string> = [
68
+ "OnCallDutyPolicyScheduleCalendarFeedService",
69
+ "ProjectOnCallCalendarFeedService",
58
70
  "ThreatIntelFeedService",
71
+ "UserOnCallCalendarFeedService",
72
+ ];
73
+
74
+ const CALENDAR_FEED_MODEL_NAMES: ReadonlyArray<string> = [
75
+ "OnCallDutyPolicyScheduleCalendarFeed",
76
+ "ProjectOnCallCalendarFeed",
77
+ "UserOnCallCalendarFeed",
59
78
  ];
60
79
 
80
+ const readModelSource: (modelName: string) => string = (
81
+ modelName: string,
82
+ ): string => {
83
+ return fs.readFileSync(
84
+ path.join(
85
+ __dirname,
86
+ "..",
87
+ "..",
88
+ "..",
89
+ "Models",
90
+ "DatabaseModels",
91
+ `${modelName}.ts`,
92
+ ),
93
+ "utf8",
94
+ );
95
+ };
96
+
61
97
  interface FeedRetention {
62
98
  serviceName: string;
63
99
  columnName: string;
@@ -173,6 +209,40 @@ describe("Feed service retention windows", () => {
173
209
  }
174
210
  });
175
211
 
212
+ test.each(CALENDAR_FEED_MODEL_NAMES)(
213
+ "%s owns a subscription token, not an activity timeline",
214
+ (modelName: string) => {
215
+ /*
216
+ * What makes a calendar feed configuration rather than history: a
217
+ * capability token (hashed for lookup, encrypted for re-display), an
218
+ * enable switch, a rotation stamp and fetch bookkeeping. Deleting the
219
+ * row for age would revoke a link that calendar apps poll forever.
220
+ */
221
+ const source: string = readModelSource(modelName);
222
+
223
+ expect(source).toMatch(/@CrudApiEndpoint\(new Route\("\/[a-z-]+"\)\)/);
224
+ expect(source).not.toContain("@EnableDocumentation");
225
+
226
+ for (const fieldName of [
227
+ "tokenHash",
228
+ "token",
229
+ "previousTokenHash",
230
+ "previousTokenExpiresAt",
231
+ "tokenHint",
232
+ "isEnabled",
233
+ "rotatedAt",
234
+ "lastFetchedAt",
235
+ "fetchCount",
236
+ ]) {
237
+ expect(source).toMatch(new RegExp(`public ${fieldName}\\?`));
238
+ }
239
+
240
+ // An activity feed would carry these; a token row never does.
241
+ expect(source).not.toMatch(/public feedInfoInMarkdown\?/);
242
+ expect(source).not.toMatch(/public postedAt\?/);
243
+ },
244
+ );
245
+
176
246
  test.each(CONFIGURATION_FEED_SERVICE_NAMES)(
177
247
  "%s is not age-expired while it remains an active configuration",
178
248
  (serviceName: string) => {