@oneuptime/common 13.0.7 → 13.0.8

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 (463) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  2. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  3. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  4. package/Server/API/AIBillingAPI.ts +1 -0
  5. package/Server/API/AIInsightAPI.ts +1 -4
  6. package/Server/API/AIInvestigationAPI.ts +1 -4
  7. package/Server/API/AIReadinessAPI.ts +4 -5
  8. package/Server/API/AlertAPI.ts +1 -0
  9. package/Server/API/AutoRemediationAPI.ts +1 -4
  10. package/Server/API/BaseAPI.ts +13 -0
  11. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  12. package/Server/API/BillingAPI.ts +2 -0
  13. package/Server/API/BillingInvoiceAPI.ts +1 -0
  14. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  15. package/Server/API/CodeFixRunAPI.ts +1 -3
  16. package/Server/API/CommonAPI.ts +68 -4
  17. package/Server/API/DashboardAPI.ts +12 -12
  18. package/Server/API/GlobalConfigAPI.ts +17 -0
  19. package/Server/API/IncidentAPI.ts +2 -0
  20. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  21. package/Server/API/NotificationAPI.ts +1 -0
  22. package/Server/API/OnCallCalendarAPI.ts +4 -5
  23. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  24. package/Server/API/OnCallReadinessAPI.ts +4 -1
  25. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  26. package/Server/API/ProjectAPI.ts +1 -0
  27. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  28. package/Server/API/StatusPageAPI.ts +161 -32
  29. package/Server/API/TeamMemberAPI.ts +3 -0
  30. package/Server/API/UserCallAPI.ts +2 -0
  31. package/Server/API/UserEmailAPI.ts +2 -0
  32. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  33. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  34. package/Server/API/UserSlackAPI.ts +1 -0
  35. package/Server/API/UserSmsAPI.ts +2 -0
  36. package/Server/API/UserTotpAuthAPI.ts +1 -0
  37. package/Server/API/UserWebAuthnAPI.ts +2 -0
  38. package/Server/API/UserWebhookAPI.ts +1 -0
  39. package/Server/API/UserWhatsAppAPI.ts +2 -0
  40. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  41. package/Server/Middleware/UserAuthorization.ts +79 -5
  42. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  43. package/Server/Services/DatabaseService.ts +245 -0
  44. package/Server/Services/DetectionRuleService.ts +5 -4
  45. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  46. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  47. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  48. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  49. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  50. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  51. package/Server/Services/ProjectService.ts +18 -0
  52. package/Server/Services/RumSessionPinService.ts +274 -22
  53. package/Server/Services/TeamPermissionService.ts +120 -26
  54. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  55. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  56. package/Server/Types/Database/Permissions/Index.ts +75 -11
  57. package/Server/Types/Database/Permissions/TablePermission.ts +24 -28
  58. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  59. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  60. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  61. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  62. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  63. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  64. package/Server/Utils/Realtime.ts +163 -45
  65. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  66. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  67. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  68. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  69. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  70. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  71. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  72. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  73. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  74. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  75. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  76. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  77. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  78. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  79. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  80. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  81. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  82. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  83. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  84. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  85. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  86. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  87. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  88. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  89. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +95 -68
  90. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  91. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +28 -3
  92. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +761 -0
  93. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  94. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  95. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  96. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  97. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +321 -0
  98. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  99. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  100. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  101. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  102. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  103. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  104. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
  105. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  106. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
  107. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  108. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  109. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  110. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  111. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  112. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  113. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  114. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  115. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  116. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  117. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  118. package/Tests/Server/API/GlobalConfigLicense.test.ts +140 -0
  119. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  120. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  121. package/Tests/Server/API/OnCallReadinessAPI.test.ts +175 -17
  122. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  123. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  124. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  125. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  126. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  127. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  128. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
  129. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  130. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  131. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  132. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  133. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  134. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  135. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  136. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  137. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  138. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  139. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  140. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  141. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  142. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  143. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  144. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  145. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  146. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  147. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  148. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  149. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  150. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  151. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  152. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  153. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  154. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  155. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  156. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  157. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  158. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  159. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  160. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  161. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  162. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  163. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  164. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  165. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  166. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  167. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  168. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  169. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  170. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  171. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  172. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  173. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  174. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  175. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  176. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  178. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  179. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  181. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  182. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  183. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  184. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  185. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  186. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  187. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  188. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  189. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  190. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  191. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  192. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  193. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  194. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  195. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  196. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  197. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  198. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  199. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  200. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  201. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +89 -1
  202. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  203. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  204. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  205. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  206. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  207. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  208. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  209. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  210. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  211. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  212. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  213. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  214. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  215. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  216. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  217. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  218. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  219. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  220. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  221. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  222. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  223. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  224. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  225. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  226. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  227. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  228. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  229. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  230. package/Types/Realtime/EventName.ts +11 -0
  231. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  232. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  233. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  234. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  235. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  236. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  237. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  238. package/UI/Components/EditionLabel/EditionLabel.tsx +12 -0
  239. package/UI/Components/EventItem/EventItem.tsx +12 -1
  240. package/UI/Components/Forms/BasicForm.tsx +151 -126
  241. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  242. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  243. package/UI/Components/Forms/FormSummary.tsx +7 -0
  244. package/UI/Components/Forms/Types/Field.ts +10 -0
  245. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  246. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  247. package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
  248. package/UI/Components/ModelTable/TableView.tsx +1 -1
  249. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  250. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  251. package/UI/Utils/API/API.ts +173 -12
  252. package/UI/Utils/API/RequestOptions.ts +11 -0
  253. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  254. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  255. package/UI/Utils/Navigation.ts +9 -0
  256. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  257. package/UI/Utils/Realtime.ts +176 -30
  258. package/Utils/API.ts +14 -5
  259. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  260. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  261. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  262. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  263. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  264. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  265. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  266. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  267. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  268. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  269. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  270. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  271. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  272. package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
  273. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  274. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  275. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  276. package/build/dist/Server/API/AlertAPI.js +1 -1
  277. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  278. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  279. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  280. package/build/dist/Server/API/BaseAPI.js +12 -0
  281. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  282. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  283. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  284. package/build/dist/Server/API/BillingAPI.js +2 -2
  285. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  286. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  287. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  288. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  289. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  290. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  291. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  292. package/build/dist/Server/API/CommonAPI.js +50 -5
  293. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  294. package/build/dist/Server/API/DashboardAPI.js +12 -12
  295. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  296. package/build/dist/Server/API/GlobalConfigAPI.js +12 -0
  297. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  298. package/build/dist/Server/API/IncidentAPI.js +2 -2
  299. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  300. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  301. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  302. package/build/dist/Server/API/NotificationAPI.js +1 -1
  303. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  304. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  305. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  306. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  307. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  308. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  309. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  310. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  311. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  312. package/build/dist/Server/API/ProjectAPI.js +1 -1
  313. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  314. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  315. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  316. package/build/dist/Server/API/StatusPageAPI.js +125 -33
  317. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  318. package/build/dist/Server/API/TeamMemberAPI.js +3 -3
  319. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  320. package/build/dist/Server/API/UserCallAPI.js +2 -2
  321. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  322. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  323. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  324. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  325. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  326. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  327. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  328. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  329. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  330. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  331. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  332. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  333. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  334. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  335. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  336. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  337. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  338. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  339. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  340. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  341. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  342. package/build/dist/Server/Middleware/UserAuthorization.js +64 -4
  343. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  344. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  345. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  346. package/build/dist/Server/Services/DatabaseService.js +171 -0
  347. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  348. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  349. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  350. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  351. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  352. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  353. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  354. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  355. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  356. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  357. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  358. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  359. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  360. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  361. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  362. package/build/dist/Server/Services/ProjectService.js +17 -0
  363. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  364. package/build/dist/Server/Services/RumSessionPinService.js +180 -16
  365. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  366. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  367. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  368. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  369. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  370. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  371. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  372. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  373. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  374. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +14 -13
  375. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  376. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  377. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  378. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  379. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  380. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  381. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  382. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  383. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  384. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  385. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  386. package/build/dist/Server/Utils/Realtime.js +126 -37
  387. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  388. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  389. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  390. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  391. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  392. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  393. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  394. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  395. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  396. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  397. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  398. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  399. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  400. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  401. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  402. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  403. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  404. package/build/dist/Types/Realtime/EventName.js +10 -0
  405. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  406. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  407. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  408. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  409. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  410. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  411. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  412. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  413. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  414. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  415. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  416. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  417. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  418. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  419. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  420. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +11 -0
  421. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  422. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  423. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  424. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  425. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  426. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  427. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  428. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  429. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  430. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  431. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  432. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  433. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  434. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  435. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  436. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
  437. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  438. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  439. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  440. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  441. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  442. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  443. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  444. package/build/dist/UI/Utils/API/API.js +143 -19
  445. package/build/dist/UI/Utils/API/API.js.map +1 -1
  446. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  447. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  448. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  449. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  450. package/build/dist/UI/Utils/Navigation.js +8 -0
  451. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  452. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  453. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  454. package/build/dist/UI/Utils/Realtime.js +120 -15
  455. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  456. package/build/dist/Utils/API.js +13 -5
  457. package/build/dist/Utils/API.js.map +1 -1
  458. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  459. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  460. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  461. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  462. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  463. package/package.json +1 -1
@@ -1,5 +1,12 @@
1
1
  import "@testing-library/jest-dom";
2
- import { afterEach, describe, expect, jest, test } from "@jest/globals";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
3
10
  import {
4
11
  cleanup,
5
12
  fireEvent,
@@ -93,6 +100,25 @@ jest.mock("react-i18next", () => {
93
100
  };
94
101
  });
95
102
 
103
+ /*
104
+ * axios is a pass-through spy: every call still goes to the real axios, so
105
+ * nothing else in this file behaves differently. Only the lapsed-session
106
+ * regression below scripts it, to drive the REAL browser client (not the mock
107
+ * above) through a 401, a refresh and a replay.
108
+ */
109
+ jest.mock("axios", () => {
110
+ const actualAxios: (...args: Array<unknown>) => unknown = jest.requireActual(
111
+ "axios",
112
+ ) as (...args: Array<unknown>) => unknown;
113
+
114
+ return Object.assign(
115
+ jest.fn((...args: Array<unknown>): unknown => {
116
+ return actualAxios(...args);
117
+ }),
118
+ actualAxios,
119
+ );
120
+ });
121
+
96
122
  import EscalationRules from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/EscalationRule/EscalationRules";
97
123
  import {
98
124
  ResponderGroupRef,
@@ -125,9 +151,24 @@ import {
125
151
  parseReadinessSummary,
126
152
  } from "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/Readiness/ReadinessTypes";
127
153
  import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
154
+ import HTTPMethod from "../../../Types/API/HTTPMethod";
128
155
  import HTTPResponse from "../../../Types/API/HTTPResponse";
156
+ import Route from "../../../Types/API/Route";
157
+ import URL from "../../../Types/API/URL";
129
158
  import { JSONObject } from "../../../Types/JSON";
130
159
  import ObjectID from "../../../Types/ObjectID";
160
+ import { APP_API_URL, IDENTITY_URL } from "../../../UI/Config";
161
+ import type RealBaseAPI from "../../../UI/Utils/API/API";
162
+ import Navigation from "../../../UI/Utils/Navigation";
163
+ import User from "../../../UI/Utils/User";
164
+ import FakeAxiosServer, {
165
+ FakeTransport,
166
+ SentRequest,
167
+ } from "../../UI/Utils/API/FakeAxiosServer";
168
+ import axios from "axios";
169
+ import fs from "fs";
170
+ import type { SpyInstance } from "jest-mock";
171
+ import path from "path";
131
172
 
132
173
  const USER_ALEX: string = "aaaaaaaa-1111-4111-8111-111111111111";
133
174
  const USER_SAM: string = "bbbbbbbb-2222-4222-8222-222222222222";
@@ -1624,6 +1665,240 @@ describe("Sending the reminder", () => {
1624
1665
  });
1625
1666
  });
1626
1667
 
1668
+ /*
1669
+ * ---------------------------------------------------------------------------
1670
+ * THE REMINDER AFTER THE SESSION LAPSED
1671
+ * ---------------------------------------------------------------------------
1672
+ *
1673
+ * The customer's report, reproduced. The Dashboard was left open for more than
1674
+ * fifteen minutes, so the browser had already dropped the access-token cookie
1675
+ * (its maxAge is the JWT's lifetime) when "Send setup reminder" was pressed.
1676
+ * The request reached the server with no credentials, and the reminder came
1677
+ * back "You are not authorized to access this project's data." - a 422, which
1678
+ * the browser client has no reason to treat as a lapsed session, so it never
1679
+ * refreshed and the reminder was reported as failed.
1680
+ *
1681
+ * The server now answers a request with no credentials 401, and the client
1682
+ * refreshes on a 401 and replays. These cases run requestSetupReminders
1683
+ * through the REAL browser client rather than the API mock the rest of this
1684
+ * file uses: the mock's post is pointed at the actual BaseAPI.post, and axios
1685
+ * underneath it is scripted like a server.
1686
+ */
1687
+ describe("Sending the reminder after the session lapsed", () => {
1688
+ const SEND_URL: string = URL.fromString(APP_API_URL.toString())
1689
+ .addRoute("/on-call-readiness/send-setup-reminder")
1690
+ .toString();
1691
+
1692
+ const REFRESH_URL: string = URL.fromString(IDENTITY_URL.toString())
1693
+ .addRoute("/refresh-token")
1694
+ .toString();
1695
+
1696
+ const NO_CREDENTIALS_BODY: JSONObject = {
1697
+ message: "Authentication required. Please log in to access this resource.",
1698
+ };
1699
+
1700
+ const realBaseAPI: typeof RealBaseAPI = (
1701
+ jest.requireActual("../../../UI/Utils/API/API") as {
1702
+ default: typeof RealBaseAPI;
1703
+ }
1704
+ ).default;
1705
+
1706
+ const mockedAxios: FakeTransport = axios as unknown as FakeTransport;
1707
+
1708
+ let server: FakeAxiosServer;
1709
+ let logoutSpy: SpyInstance<typeof User.logout>;
1710
+ let navigateSpy: SpyInstance<typeof Navigation.navigate>;
1711
+ let currentRouteSpy: SpyInstance<typeof Navigation.getCurrentRoute>;
1712
+
1713
+ beforeEach(() => {
1714
+ server = new FakeAxiosServer(mockedAxios);
1715
+
1716
+ getCommonHeadersMock.mockReturnValue({ tenantid: PROJECT_ID.toString() });
1717
+
1718
+ postMock.mockImplementation((...args: Array<any>): Promise<unknown> => {
1719
+ return realBaseAPI.post(args[0]);
1720
+ });
1721
+
1722
+ logoutSpy = jest.spyOn(User, "logout").mockImplementation((): void => {});
1723
+
1724
+ navigateSpy = jest
1725
+ .spyOn(Navigation, "navigate")
1726
+ .mockImplementation((): void => {});
1727
+
1728
+ currentRouteSpy = jest
1729
+ .spyOn(Navigation, "getCurrentRoute")
1730
+ .mockReturnValue(
1731
+ new Route(`/dashboard/${PROJECT_ID.toString()}/on-call-duty`),
1732
+ );
1733
+ });
1734
+
1735
+ afterEach(() => {
1736
+ logoutSpy.mockRestore();
1737
+ navigateSpy.mockRestore();
1738
+ currentRouteSpy.mockRestore();
1739
+
1740
+ // Back to the pass-through the rest of this file runs on.
1741
+ const actualAxios: (...args: Array<unknown>) => unknown =
1742
+ jest.requireActual("axios") as (...args: Array<unknown>) => unknown;
1743
+
1744
+ mockedAxios.mockImplementation(((...args: Array<unknown>): unknown => {
1745
+ return actualAxios(...args);
1746
+ }) as never);
1747
+ });
1748
+
1749
+ type SentFunction = () => Array<string>;
1750
+
1751
+ const sentOnTheWire: SentFunction = (): Array<string> => {
1752
+ return server.sent.map((request: SentRequest): string => {
1753
+ return `${request.method.toUpperCase()} ${request.url}`;
1754
+ });
1755
+ };
1756
+
1757
+ test("a reminder refused 401 is refreshed, replayed, and reported as SENT", async () => {
1758
+ server
1759
+ .on(HTTPMethod.POST, SEND_URL, [
1760
+ { status: 401, data: NO_CREDENTIALS_BODY },
1761
+ {
1762
+ status: 200,
1763
+ data: {
1764
+ sentCount: 1,
1765
+ results: [
1766
+ { userId: USER_ALEX, outcome: "Sent", message: "Emailed." },
1767
+ ],
1768
+ },
1769
+ },
1770
+ ])
1771
+ .on(HTTPMethod.POST, REFRESH_URL, [{ status: 200 }]);
1772
+
1773
+ const statuses: SetupReminderStatuses = await requestSetupReminders([
1774
+ USER_ALEX,
1775
+ ]);
1776
+
1777
+ // Not the "failed" / "not authorized" state from the customer's screenshot.
1778
+ expect(statuses).toEqual({
1779
+ [USER_ALEX]: { state: "sent", message: "Emailed." },
1780
+ });
1781
+
1782
+ expect(sentOnTheWire()).toEqual([
1783
+ `POST ${SEND_URL}`,
1784
+ `POST ${REFRESH_URL}`,
1785
+ `POST ${SEND_URL}`,
1786
+ ]);
1787
+
1788
+ // The replay is the same reminder, for the same project, sent once more.
1789
+ const replay: SentRequest = server.sent[2]!;
1790
+
1791
+ expect(replay.data).toEqual({ userIds: [USER_ALEX] });
1792
+ expect(replay.data).toEqual(server.sent[0]!.data);
1793
+ expect(replay.headers["tenantid"]).toBe(PROJECT_ID.toString());
1794
+
1795
+ // The lapse is invisible: nobody is logged out, nothing navigates.
1796
+ expect(logoutSpy).not.toHaveBeenCalled();
1797
+ expect(navigateSpy).not.toHaveBeenCalled();
1798
+ });
1799
+
1800
+ test("a batch refused 401 is replayed as one batch, and each person is reported", async () => {
1801
+ server
1802
+ .on(HTTPMethod.POST, SEND_URL, [
1803
+ { status: 401, data: NO_CREDENTIALS_BODY },
1804
+ {
1805
+ status: 200,
1806
+ data: {
1807
+ sentCount: 1,
1808
+ results: [
1809
+ { userId: USER_ALEX, outcome: "Sent", message: "Emailed." },
1810
+ {
1811
+ userId: USER_SAM,
1812
+ outcome: "SkippedThrottled",
1813
+ message: "Already reminded in the last 24 hours.",
1814
+ },
1815
+ ],
1816
+ },
1817
+ },
1818
+ ])
1819
+ .on(HTTPMethod.POST, REFRESH_URL, [{ status: 200 }]);
1820
+
1821
+ const statuses: SetupReminderStatuses = await requestSetupReminders([
1822
+ USER_ALEX,
1823
+ USER_SAM,
1824
+ ]);
1825
+
1826
+ expect(statuses[USER_ALEX]).toEqual({ state: "sent", message: "Emailed." });
1827
+ expect(statuses[USER_SAM]).toEqual({
1828
+ state: "skipped",
1829
+ message: "Already reminded in the last 24 hours.",
1830
+ });
1831
+ expect(server.requestsTo(HTTPMethod.POST, REFRESH_URL)).toHaveLength(1);
1832
+ expect(server.requestsTo(HTTPMethod.POST, SEND_URL)).toHaveLength(2);
1833
+ });
1834
+
1835
+ /*
1836
+ * The symptom itself, kept as a contrast: had the server kept answering 422
1837
+ * for a request with no credentials, the client could not have told it from
1838
+ * a real permission refusal, and correctly would not have refreshed. The
1839
+ * reminder stays failed. This is why the fix is a 401 on the server and not
1840
+ * a wider net on the client.
1841
+ */
1842
+ test("the old 422 answer is terminal: no refresh, no replay, reported as failed", async () => {
1843
+ server.on(HTTPMethod.POST, SEND_URL, [
1844
+ {
1845
+ status: 422,
1846
+ data: {
1847
+ message: "You are not authorized to access this project's data.",
1848
+ },
1849
+ },
1850
+ ]);
1851
+
1852
+ const statuses: SetupReminderStatuses = await requestSetupReminders([
1853
+ USER_ALEX,
1854
+ ]);
1855
+
1856
+ expect(statuses[USER_ALEX]!.state).toBe("failed");
1857
+ expect(sentOnTheWire()).toEqual([`POST ${SEND_URL}`]);
1858
+ expect(logoutSpy).not.toHaveBeenCalled();
1859
+ });
1860
+
1861
+ test("a session that cannot be refreshed reports the reminder as not sent and goes to login", async () => {
1862
+ server
1863
+ .on(HTTPMethod.POST, SEND_URL, [
1864
+ { status: 401, data: NO_CREDENTIALS_BODY },
1865
+ ])
1866
+ .on(HTTPMethod.POST, REFRESH_URL, [{ status: 401 }]);
1867
+
1868
+ const statuses: SetupReminderStatuses = await requestSetupReminders([
1869
+ USER_ALEX,
1870
+ ]);
1871
+
1872
+ // A 401 is refused before any mail is sent, so "not sent" is certain.
1873
+ expect(statuses[USER_ALEX]!.state).toBe("failed");
1874
+ // Never replayed: the reminder was not sent twice behind the reader's back.
1875
+ expect(server.requestsTo(HTTPMethod.POST, SEND_URL)).toHaveLength(1);
1876
+ expect(logoutSpy).toHaveBeenCalled();
1877
+ expect(navigateSpy).toHaveBeenCalledWith(new Route("/accounts/login"), {
1878
+ forceNavigate: true,
1879
+ });
1880
+ });
1881
+
1882
+ /*
1883
+ * The mock at the top of this file stands in for the refresh-aware client,
1884
+ * so every other test here would pass just the same if the reminder were
1885
+ * sent through the bare Common/Utils/API - which has no refresh at all. Pin
1886
+ * the import itself.
1887
+ */
1888
+ test("the reminder is sent through the refresh-aware client", () => {
1889
+ const source: string = fs.readFileSync(
1890
+ path.join(
1891
+ __dirname,
1892
+ "../../../../App/FeatureSet/Dashboard/src/Components/OnCallPolicy/EscalationRule/EscalationRuleReadiness.tsx",
1893
+ ),
1894
+ "utf8",
1895
+ );
1896
+
1897
+ expect(source).toMatch(/import API from "Common\/UI\/Utils\/API\/API";/);
1898
+ expect(source).not.toMatch(/from "Common\/Utils\/API"/);
1899
+ });
1900
+ });
1901
+
1627
1902
  /*
1628
1903
  * ---------------------------------------------------------------------------
1629
1904
  * EXPANDING TEAMS AND SCHEDULES
@@ -115,11 +115,19 @@ describe("On-Call Duty side menu - Calendar Feeds item", () => {
115
115
  return link.title;
116
116
  });
117
117
 
118
+ /*
119
+ * The two views of the schedules come first - the list and the timeline
120
+ * of all of them - and the feeds that export them follow directly.
121
+ */
118
122
  expect(titles).toContain("On-Call Schedules");
123
+ expect(titles).toContain("Schedule Timeline");
119
124
  expect(titles).toContain("Calendar Feeds");
120
- expect(titles.indexOf("Calendar Feeds")).toBe(
125
+ expect(titles.indexOf("Schedule Timeline")).toBe(
121
126
  titles.indexOf("On-Call Schedules") + 1,
122
127
  );
128
+ expect(titles.indexOf("Calendar Feeds")).toBe(
129
+ titles.indexOf("Schedule Timeline") + 1,
130
+ );
123
131
 
124
132
  const calendarFeeds: MenuLink | undefined = links.find(
125
133
  (link: MenuLink): boolean => {
@@ -311,38 +311,46 @@ describe("Pay as you go creation notices", () => {
311
311
  });
312
312
 
313
313
  describe("creating a telemetry ingestion key off the Free plan", () => {
314
- it("asks for nothing extra on a paid plan", async () => {
315
- getJestSpyOn(ProjectUtil, "getCurrentPlan").mockReturnValue(
316
- PlanType.Growth,
317
- );
318
-
319
- renderIngestionKeyForm();
320
-
321
- await userEvent.type(
322
- await screen.findByTestId(
323
- "ingestion-key-name",
324
- {},
325
- { timeout: WAIT_TIMEOUT },
326
- ),
327
- "Production",
328
- );
314
+ it.each([PlanType.Growth, PlanType.Scale, PlanType.Enterprise])(
315
+ "creates a key on the %s plan without checking for a payment method",
316
+ async (plan: PlanType) => {
317
+ getJestSpyOn(ProjectUtil, "getCurrentPlan").mockReturnValue(plan);
318
+ jest
319
+ .spyOn(BaseAPI, "get")
320
+ .mockResolvedValue({ data: { isAllowed: false } } as any);
321
+
322
+ renderIngestionKeyForm();
323
+
324
+ await userEvent.type(
325
+ await screen.findByTestId(
326
+ "ingestion-key-name",
327
+ {},
328
+ { timeout: WAIT_TIMEOUT },
329
+ ),
330
+ "Production",
331
+ );
329
332
 
330
- expect(
331
- screen.queryByTestId("telemetry-pay-as-you-go-consent"),
332
- ).not.toBeInTheDocument();
333
- expect(
334
- screen.queryByRole("region", { name: "Telemetry pricing" }),
335
- ).not.toBeInTheDocument();
333
+ expect(
334
+ screen.queryByTestId("telemetry-pay-as-you-go-consent"),
335
+ ).not.toBeInTheDocument();
336
+ expect(
337
+ screen.queryByRole("region", { name: "Telemetry pricing" }),
338
+ ).not.toBeInTheDocument();
339
+ expect(
340
+ screen.queryByText(/Add a payment method/),
341
+ ).not.toBeInTheDocument();
336
342
 
337
- await userEvent.click(screen.getByText("Create Ingestion Key"));
343
+ await userEvent.click(screen.getByText("Create Ingestion Key"));
338
344
 
339
- await waitFor(
340
- () => {
341
- expect(createOrUpdateMock).toHaveBeenCalled();
342
- },
343
- { timeout: WAIT_TIMEOUT },
344
- );
345
- });
345
+ await waitFor(
346
+ () => {
347
+ expect(createOrUpdateMock).toHaveBeenCalledTimes(1);
348
+ },
349
+ { timeout: WAIT_TIMEOUT },
350
+ );
351
+ expect(BaseAPI.get).not.toHaveBeenCalled();
352
+ },
353
+ );
346
354
 
347
355
  it("asks for nothing extra on a self-hosted install", async () => {
348
356
  config.billingEnabled = false;
@@ -411,21 +419,29 @@ describe("Pay as you go creation notices", () => {
411
419
  });
412
420
 
413
421
  describe("creating a monitor off the Free plan", () => {
414
- it("advances without a warning or acknowledgement on a paid plan", async () => {
415
- getJestSpyOn(ProjectUtil, "getCurrentPlan").mockReturnValue(
416
- PlanType.Growth,
417
- );
418
- renderMonitorPage(MonitorType.Website);
422
+ it.each([PlanType.Growth, PlanType.Scale, PlanType.Enterprise])(
423
+ "advances on the %s plan without a payment method or billing warning",
424
+ async (plan: PlanType) => {
425
+ getJestSpyOn(ProjectUtil, "getCurrentPlan").mockReturnValue(plan);
426
+ jest
427
+ .spyOn(BaseAPI, "get")
428
+ .mockResolvedValue({ data: { isAllowed: false } } as any);
429
+ renderMonitorPage(MonitorType.Website);
419
430
 
420
- await advanceMonitorInfo();
431
+ await advanceMonitorInfo();
421
432
 
422
- expect(
423
- screen.queryByTestId("monitor-pay-as-you-go-card"),
424
- ).not.toBeInTheDocument();
425
- expect(
426
- screen.queryByTestId("monitor-pay-as-you-go-consent"),
427
- ).not.toBeInTheDocument();
428
- });
433
+ expect(
434
+ screen.queryByTestId("monitor-pay-as-you-go-card"),
435
+ ).not.toBeInTheDocument();
436
+ expect(
437
+ screen.queryByTestId("monitor-pay-as-you-go-consent"),
438
+ ).not.toBeInTheDocument();
439
+ expect(
440
+ screen.queryByText(/Add a payment method/),
441
+ ).not.toBeInTheDocument();
442
+ expect(BaseAPI.get).not.toHaveBeenCalled();
443
+ },
444
+ );
429
445
 
430
446
  it("advances without a warning or acknowledgement on a self-hosted install", async () => {
431
447
  config.billingEnabled = false;
@@ -759,18 +759,55 @@ describe("Pay as you go notices", () => {
759
759
  expect(container).toBeEmptyDOMElement();
760
760
  });
761
761
 
762
- it("renders nothing off the Free plan", () => {
763
- setPlan(PlanType.Growth);
762
+ it("keeps a Free plan batch locked when no payment method is available", async () => {
763
+ const onChange: MockFunction = getJestMockFunction();
764
+ jest
765
+ .spyOn(BaseAPI, "get")
766
+ .mockResolvedValue({ data: { isAllowed: false } } as any);
764
767
 
765
- const { container }: { container: HTMLElement } = render(
768
+ render(
766
769
  <MonitorBatchPayAsYouGoConsent
767
770
  monitorTypes={[MonitorType.Kubernetes]}
768
771
  value={false}
769
- onChange={() => {}}
772
+ onChange={onChange as (value: boolean) => void}
770
773
  />,
771
774
  );
772
775
 
773
- expect(container).toBeEmptyDOMElement();
776
+ expect(
777
+ await screen.findByText(/Add a payment method before using/),
778
+ ).toBeInTheDocument();
779
+ const consent: HTMLElement = screen.getByTestId(
780
+ "monitor-batch-pay-as-you-go-consent",
781
+ );
782
+ expect(consent).toBeDisabled();
783
+ await userEvent.click(consent);
784
+ expect(onChange).not.toHaveBeenCalled();
785
+ expect(BaseAPI.get).toHaveBeenCalledTimes(1);
774
786
  });
787
+
788
+ it.each([PlanType.Growth, PlanType.Scale, PlanType.Enterprise])(
789
+ "does not check or require a payment method for a %s plan batch",
790
+ (plan: PlanType) => {
791
+ setPlan(plan);
792
+ jest
793
+ .spyOn(BaseAPI, "get")
794
+ .mockResolvedValue({ data: { isAllowed: false } } as any);
795
+
796
+ const { container }: { container: HTMLElement } = render(
797
+ <MonitorBatchPayAsYouGoConsent
798
+ monitorTypes={[
799
+ MonitorType.Kubernetes,
800
+ MonitorType.Website,
801
+ MonitorType.Manual,
802
+ ]}
803
+ value={false}
804
+ onChange={() => {}}
805
+ />,
806
+ );
807
+
808
+ expect(container).toBeEmptyDOMElement();
809
+ expect(BaseAPI.get).not.toHaveBeenCalled();
810
+ },
811
+ );
775
812
  });
776
813
  });
@@ -92,8 +92,9 @@ interface ResourceAuditMenuCase {
92
92
  name: string;
93
93
  menu: () => ReactElement;
94
94
  auditPage: PageMap;
95
+ auditSection: string;
95
96
  sections: Array<string>;
96
- advancedLinks: Array<ExpectedLink>;
97
+ auditSectionLinks: Array<ExpectedLink>;
97
98
  }
98
99
 
99
100
  const RESOURCE_CASES: Array<ResourceAuditMenuCase> = [
@@ -103,6 +104,7 @@ const RESOURCE_CASES: Array<ResourceAuditMenuCase> = [
103
104
  return <IncidentSideMenu modelId={MODEL_ID} />;
104
105
  },
105
106
  auditPage: PageMap.INCIDENT_VIEW_AUDIT_LOGS,
107
+ auditSection: "Advanced",
106
108
  sections: [
107
109
  "Overview",
108
110
  "Investigation",
@@ -111,7 +113,7 @@ const RESOURCE_CASES: Array<ResourceAuditMenuCase> = [
111
113
  "Notes",
112
114
  "Advanced",
113
115
  ],
114
- advancedLinks: [
116
+ auditSectionLinks: [
115
117
  {
116
118
  title: "Custom Fields",
117
119
  page: PageMap.INCIDENT_VIEW_CUSTOM_FIELDS,
@@ -129,8 +131,9 @@ const RESOURCE_CASES: Array<ResourceAuditMenuCase> = [
129
131
  );
130
132
  },
131
133
  auditPage: PageMap.MONITOR_VIEW_AUDIT_LOGS,
134
+ auditSection: "Advanced",
132
135
  sections: ["Overview", "Activity", "Configuration", "Advanced"],
133
- advancedLinks: [
136
+ auditSectionLinks: [
134
137
  { title: "Owners", page: PageMap.MONITOR_VIEW_OWNERS },
135
138
  { title: "Custom Fields", page: PageMap.MONITOR_VIEW_CUSTOM_FIELDS },
136
139
  { title: "Settings", page: PageMap.MONITOR_VIEW_SETTINGS },
@@ -144,8 +147,9 @@ const RESOURCE_CASES: Array<ResourceAuditMenuCase> = [
144
147
  return <SloSideMenu modelId={MODEL_ID} />;
145
148
  },
146
149
  auditPage: PageMap.SLO_VIEW_AUDIT_LOGS,
147
- sections: ["SLO", "Advanced"],
148
- advancedLinks: [
150
+ auditSection: "Management",
151
+ sections: ["Overview", "Activity", "Configuration", "Management"],
152
+ auditSectionLinks: [
149
153
  { title: "Owners", page: PageMap.SLO_VIEW_OWNERS },
150
154
  { title: "Settings", page: PageMap.SLO_VIEW_SETTINGS },
151
155
  { title: "Audit Logs", page: PageMap.SLO_VIEW_AUDIT_LOGS },
@@ -158,6 +162,7 @@ const RESOURCE_CASES: Array<ResourceAuditMenuCase> = [
158
162
  return <StatusPageSideMenu modelId={MODEL_ID} />;
159
163
  },
160
164
  auditPage: PageMap.STATUS_PAGE_VIEW_AUDIT_LOGS,
165
+ auditSection: "Advanced",
161
166
  sections: [
162
167
  "Basic",
163
168
  "Resources",
@@ -168,7 +173,7 @@ const RESOURCE_CASES: Array<ResourceAuditMenuCase> = [
168
173
  "AI",
169
174
  "Advanced",
170
175
  ],
171
- advancedLinks: [
176
+ auditSectionLinks: [
172
177
  { title: "Embedded Status", page: PageMap.STATUS_PAGE_VIEW_EMBEDDED },
173
178
  { title: "Reports", page: PageMap.STATUS_PAGE_VIEW_REPORTS },
174
179
  {
@@ -228,11 +233,11 @@ describe.each(RESOURCE_CASES)(
228
233
  expect(sectionTitlesInOrder()).not.toContain("Settings");
229
234
  });
230
235
 
231
- test("keeps every Advanced destination in the expected order", async () => {
236
+ test("keeps every destination in the audit section in the expected order", async () => {
232
237
  await renderAuditPage(resource);
233
238
 
234
- expect(linksIn("Advanced")).toEqual(
235
- resource.advancedLinks.map((link: ExpectedLink): MenuLink => {
239
+ expect(linksIn(resource.auditSection)).toEqual(
240
+ resource.auditSectionLinks.map((link: ExpectedLink): MenuLink => {
236
241
  return {
237
242
  title: link.title,
238
243
  href: resourceRoute(link.page),
@@ -259,25 +264,29 @@ describe.each(RESOURCE_CASES)(
259
264
  });
260
265
  });
261
266
 
262
- test("marks Audit Logs active and expands Advanced", async () => {
267
+ test("marks Audit Logs active and expands its section", async () => {
263
268
  const auditPath: string = await renderAuditPage(resource);
264
269
  const auditLogAnchor: HTMLAnchorElement | undefined = Array.from(
265
- sectionRoot("Advanced").querySelectorAll<HTMLAnchorElement>("a"),
270
+ sectionRoot(resource.auditSection).querySelectorAll<HTMLAnchorElement>(
271
+ "a",
272
+ ),
266
273
  ).find((anchor: HTMLAnchorElement): boolean => {
267
274
  return anchor.getAttribute("href") === auditPath;
268
275
  });
269
276
 
270
277
  expect(auditLogAnchor).toBeDefined();
271
278
  expect(auditLogAnchor).toHaveClass("bg-indigo-50", "text-indigo-700");
272
- expect(isExpanded("Advanced")).toBe(true);
279
+ expect(isExpanded(resource.auditSection)).toBe(true);
273
280
  });
274
281
 
275
- test("names Audit Logs as an Advanced page in the mobile summary", async () => {
282
+ test("names the Audit Logs section in the mobile summary", async () => {
276
283
  setViewportWidth(MOBILE_WIDTH);
277
284
 
278
285
  await renderAuditPage(resource);
279
286
 
280
- expect(mobileSummaryText()).toContain("Advanced / Audit Logs");
287
+ expect(mobileSummaryText()).toContain(
288
+ `${resource.auditSection} / Audit Logs`,
289
+ );
281
290
  });
282
291
 
283
292
  test("retains a visible label and icon for every destination", async () => {
@@ -296,20 +305,18 @@ describe.each(RESOURCE_CASES)(
296
305
  );
297
306
 
298
307
  /*
299
- * The SLO section follows what an SLO is made of: the monitors it measures
300
- * and the rules that attach them, its burn-rate rules and metrics, what it
301
- * raised, then its feed. Charts is deliberately absent - its history lives in
302
- * Metrics, and its route only survives for bookmarks.
308
+ * SLO performance and activity precede configuration. Charts remains absent:
309
+ * its history lives in Metrics, and its route only survives for bookmarks.
303
310
  */
304
311
  const SLO_SECTION_LINKS: Array<ExpectedLink> = [
305
312
  { title: "Overview", page: PageMap.SLO_VIEW },
306
- { title: "Monitors", page: PageMap.SLO_VIEW_MONITORS },
307
- { title: "Monitor Rules", page: PageMap.SLO_VIEW_MONITOR_RULES },
308
- { title: "Burn Rate Rules", page: PageMap.SLO_VIEW_BURN_RATE_RULES },
309
313
  { title: "Metrics", page: PageMap.SLO_VIEW_METRICS },
310
- { title: "Alerts", page: PageMap.SLO_VIEW_ALERTS },
311
314
  { title: "Incidents", page: PageMap.SLO_VIEW_INCIDENTS },
315
+ { title: "Alerts", page: PageMap.SLO_VIEW_ALERTS },
312
316
  { title: "Feed", page: PageMap.SLO_VIEW_FEED },
317
+ { title: "Monitors", page: PageMap.SLO_VIEW_MONITORS },
318
+ { title: "Monitor Rules", page: PageMap.SLO_VIEW_MONITOR_RULES },
319
+ { title: "Burn Rate Rules", page: PageMap.SLO_VIEW_BURN_RATE_RULES },
313
320
  ];
314
321
 
315
322
  describe("SLO resource menu", () => {
@@ -324,7 +331,11 @@ describe("SLO resource menu", () => {
324
331
 
325
332
  await renderAuditPage(slo!);
326
333
 
327
- expect(linksIn("SLO")).toEqual(
334
+ expect([
335
+ ...linksIn("Overview"),
336
+ ...linksIn("Activity"),
337
+ ...linksIn("Configuration"),
338
+ ]).toEqual(
328
339
  SLO_SECTION_LINKS.map((link: ExpectedLink): MenuLink => {
329
340
  return {
330
341
  title: link.title,