@oneuptime/common 14.0.1 → 14.0.2

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 (1090) hide show
  1. package/Models/DatabaseModels/AlertEpisodeMember.ts +8 -1
  2. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +63 -0
  3. package/Models/DatabaseModels/IncidentEpisodeMember.ts +8 -1
  4. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +9 -2
  5. package/Models/DatabaseModels/IncidentPublicNote.ts +9 -2
  6. package/Models/DatabaseModels/KubernetesCluster.ts +429 -0
  7. package/Models/DatabaseModels/Monitor.ts +48 -0
  8. package/Models/DatabaseModels/Runner.ts +5 -4
  9. package/Models/DatabaseModels/RunnerJob.ts +66 -0
  10. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
  11. package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
  12. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
  13. package/Server/API/AIInvestigationAPI.ts +341 -0
  14. package/Server/API/AutoRemediationAPI.ts +184 -2
  15. package/Server/API/CommonAPI.ts +70 -0
  16. package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
  17. package/Server/API/MicrosoftTeamsAPI.ts +770 -315
  18. package/Server/API/MonitorAPI.ts +140 -0
  19. package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
  20. package/Server/API/SlackAPI.ts +447 -99
  21. package/Server/API/StatusPageAPI.ts +146 -26
  22. package/Server/API/TelemetryAPI.ts +216 -5
  23. package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
  24. package/Server/API/WorkflowVariableAPI.ts +156 -0
  25. package/Server/Infrastructure/GlobalCache.ts +35 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  32. package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
  33. package/Server/Middleware/TelemetryIngest.ts +47 -2
  34. package/Server/Middleware/UserAuthorization.ts +56 -2
  35. package/Server/Services/AIAgentService.ts +1 -0
  36. package/Server/Services/AccessTokenService.ts +121 -48
  37. package/Server/Services/AlertEpisodeMemberService.ts +9 -0
  38. package/Server/Services/AlertEpisodeService.ts +14 -1
  39. package/Server/Services/AlertService.ts +134 -22
  40. package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
  41. package/Server/Services/AutoRemediationRuleService.ts +118 -0
  42. package/Server/Services/BillingService.ts +1 -0
  43. package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
  44. package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
  45. package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
  46. package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
  47. package/Server/Services/IncidentEpisodeService.ts +14 -1
  48. package/Server/Services/IncidentGroupingEngineService.ts +11 -0
  49. package/Server/Services/IncidentMemberService.ts +21 -0
  50. package/Server/Services/IncidentPublicNoteService.ts +63 -0
  51. package/Server/Services/IncidentService.ts +120 -20
  52. package/Server/Services/IncidentStateTimelineService.ts +15 -0
  53. package/Server/Services/IncidentTemplateService.ts +83 -3
  54. package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
  55. package/Server/Services/KubernetesClusterService.ts +1570 -1
  56. package/Server/Services/LogAggregationService.ts +14 -6
  57. package/Server/Services/MonitorService.ts +651 -52
  58. package/Server/Services/MonitorStatusTimelineService.ts +639 -2
  59. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
  60. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
  61. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
  62. package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
  63. package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
  64. package/Server/Services/OnCallSetupReminderService.ts +1 -0
  65. package/Server/Services/ProbeService.ts +7 -1
  66. package/Server/Services/ProjectService.ts +11 -1
  67. package/Server/Services/RunbookCredentialService.ts +26 -1
  68. package/Server/Services/RunbookSecretService.ts +36 -0
  69. package/Server/Services/RunnerJobService.ts +941 -23
  70. package/Server/Services/RunnerService.ts +439 -6
  71. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
  72. package/Server/Services/ScheduledMaintenanceService.ts +124 -19
  73. package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
  74. package/Server/Services/ServiceLevelObjectiveService.ts +14 -1
  75. package/Server/Services/StatusPagePrivateUserService.ts +1 -0
  76. package/Server/Services/StatusPageService.ts +301 -74
  77. package/Server/Services/StatusPageSubscriberService.ts +4 -0
  78. package/Server/Services/TeamMemberService.ts +309 -7
  79. package/Server/Services/TraceAggregationService.ts +10 -6
  80. package/Server/Services/UserEmailService.ts +1 -0
  81. package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
  82. package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
  83. package/Server/Services/UserNotificationRuleService.ts +4 -0
  84. package/Server/Services/UserService.ts +122 -1
  85. package/Server/Services/WorkflowVariableService.ts +593 -8
  86. package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
  87. package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
  88. package/Server/Types/Database/QueryHelper.ts +22 -0
  89. package/Server/Types/Markdown.ts +502 -27
  90. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
  91. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
  92. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
  93. package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
  94. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
  95. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
  96. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
  97. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
  98. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
  99. package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
  100. package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
  101. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
  102. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
  103. package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
  104. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
  105. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
  106. package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
  107. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
  108. package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
  109. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
  110. package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
  111. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
  112. package/Server/Utils/PushNotificationUtil.ts +45 -0
  113. package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
  114. package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
  115. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
  116. package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
  117. package/Server/Utils/StartServer.ts +4 -3
  118. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
  119. package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
  120. package/Server/Utils/UserPermission/UserPermission.ts +16 -0
  121. package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
  122. package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
  123. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
  124. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
  125. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
  126. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
  127. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
  128. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
  129. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
  130. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
  131. package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
  132. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
  133. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
  134. package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
  135. package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
  136. package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
  137. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
  138. package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
  139. package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
  140. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
  141. package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
  142. package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  143. package/Server/Utils/Workspace/Workspace.ts +171 -54
  144. package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
  145. package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
  146. package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
  147. package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
  148. package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
  149. package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
  150. package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
  151. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
  152. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
  153. package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
  154. package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
  155. package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
  156. package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
  157. package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
  158. package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
  159. package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
  160. package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
  161. package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +218 -0
  162. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
  163. package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
  164. package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
  165. package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
  166. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
  167. package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
  168. package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
  169. package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
  170. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +12 -0
  171. package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
  172. package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
  173. package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
  174. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
  175. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
  176. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
  177. package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
  178. package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
  179. package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
  180. package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
  181. package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
  182. package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
  183. package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
  184. package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
  185. package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
  186. package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
  187. package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
  188. package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
  189. package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
  190. package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
  191. package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
  192. package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
  193. package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
  194. package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
  195. package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
  196. package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
  197. package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
  198. package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
  199. package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
  200. package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
  201. package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
  202. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
  203. package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
  204. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
  205. package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
  206. package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
  207. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
  208. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
  209. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
  210. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
  211. package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
  212. package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
  213. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
  214. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
  215. package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
  216. package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
  217. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
  218. package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
  219. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
  220. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
  221. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
  222. package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
  223. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
  224. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
  225. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
  226. package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
  227. package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
  228. package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
  229. package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
  230. package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
  231. package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
  232. package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
  233. package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
  234. package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
  235. package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
  236. package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
  237. package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
  238. package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
  239. package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
  240. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
  241. package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
  242. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
  243. package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
  244. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
  245. package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
  246. package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
  247. package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
  248. package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
  249. package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
  250. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
  251. package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
  252. package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
  253. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
  254. package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
  255. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
  256. package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
  257. package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
  258. package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
  259. package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
  260. package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
  261. package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
  262. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
  263. package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
  264. package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
  265. package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
  266. package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
  267. package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
  268. package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
  269. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
  270. package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
  271. package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
  272. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
  273. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
  274. package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
  275. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
  276. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
  277. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
  278. package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
  279. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
  280. package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
  281. package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
  282. package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
  283. package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
  284. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
  285. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
  286. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
  287. package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
  288. package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
  289. package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
  290. package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
  291. package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
  292. package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
  293. package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
  294. package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
  295. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
  296. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
  297. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
  298. package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
  299. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
  300. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
  301. package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
  302. package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
  303. package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
  304. package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
  305. package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
  306. package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
  307. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
  308. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
  309. package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
  310. package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
  311. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
  312. package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
  313. package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
  314. package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
  315. package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
  316. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
  317. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +9 -1
  318. package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
  319. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
  320. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
  321. package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
  322. package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
  323. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
  324. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
  325. package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
  326. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
  327. package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +5 -0
  328. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +12 -0
  329. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
  330. package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
  331. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
  332. package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
  333. package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
  334. package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
  335. package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
  336. package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
  337. package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
  338. package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
  339. package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
  340. package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
  341. package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
  342. package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
  343. package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
  344. package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
  345. package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
  346. package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
  347. package/Tests/Server/Types/Markdown.test.ts +1033 -0
  348. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
  349. package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
  350. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
  351. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
  352. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
  353. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
  354. package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
  355. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
  356. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
  357. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
  358. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
  359. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
  360. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
  361. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
  362. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
  363. package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
  364. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
  365. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
  366. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
  367. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
  368. package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
  369. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
  370. package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
  371. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
  372. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +55 -0
  373. package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
  374. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
  375. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
  376. package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
  377. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
  378. package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
  379. package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
  380. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
  381. package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
  382. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
  383. package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
  384. package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
  385. package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
  386. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
  387. package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
  388. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
  389. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
  390. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
  391. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
  392. package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
  393. package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
  394. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
  395. package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
  396. package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
  397. package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
  398. package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
  399. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
  400. package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
  401. package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
  402. package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
  403. package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
  404. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
  405. package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
  406. package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
  407. package/Tests/Types/DateUserTimezone.test.ts +177 -4
  408. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
  409. package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
  410. package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
  411. package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
  412. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
  413. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
  414. package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
  415. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
  416. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
  417. package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
  418. package/Tests/Types/Timezone.test.ts +5 -0
  419. package/Tests/Types/TimezoneAlias.test.ts +1034 -0
  420. package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
  421. package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
  422. package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
  423. package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
  424. package/Tests/Types/Workflow/Templates.test.ts +79 -4
  425. package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
  426. package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
  427. package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
  428. package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
  429. package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
  430. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
  431. package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
  432. package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
  433. package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
  434. package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
  435. package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
  436. package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
  437. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
  438. package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
  439. package/Tests/UI/Components/Dropdown.test.tsx +999 -2
  440. package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
  441. package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
  442. package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
  443. package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
  444. package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
  445. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
  446. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
  447. package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
  448. package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
  449. package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
  450. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
  451. package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
  452. package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
  453. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
  454. package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
  455. package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
  456. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
  457. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
  458. package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
  459. package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
  460. package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
  461. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
  462. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -0
  463. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
  464. package/Tests/UI/Utils/Timezone.test.ts +747 -33
  465. package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
  466. package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
  467. package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
  468. package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
  469. package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
  470. package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
  471. package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
  472. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
  473. package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
  474. package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
  475. package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
  476. package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
  477. package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
  478. package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
  479. package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
  480. package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
  481. package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
  482. package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
  483. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
  484. package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
  485. package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
  486. package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
  487. package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
  488. package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
  489. package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
  490. package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
  491. package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
  492. package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
  493. package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
  494. package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
  495. package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
  496. package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
  497. package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
  498. package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
  499. package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
  500. package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
  501. package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
  502. package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
  503. package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
  504. package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
  505. package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
  506. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
  507. package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
  508. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
  509. package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
  510. package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
  511. package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
  512. package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
  513. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
  514. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
  515. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
  516. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
  517. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
  518. package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
  519. package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
  520. package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
  521. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
  522. package/Types/Date.ts +121 -7
  523. package/Types/Email/EmailMessage.ts +8 -0
  524. package/Types/Email/EmailTemplateType.ts +1 -0
  525. package/Types/Exception/ExceptionMessages.ts +1 -0
  526. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
  527. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
  528. package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
  529. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
  530. package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
  531. package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
  532. package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
  533. package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
  534. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
  535. package/Types/Rum/SessionReplay.ts +55 -2
  536. package/Types/Rum/SessionReplayApi.ts +44 -1
  537. package/Types/Rum/UserFlow.ts +187 -0
  538. package/Types/Runbook/RunbookStepType.ts +10 -0
  539. package/Types/Runbook/RunnerJobOrigin.ts +15 -0
  540. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
  541. package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
  542. package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
  543. package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
  544. package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
  545. package/Types/Timezone.ts +5 -0
  546. package/Types/TimezoneAlias.ts +385 -0
  547. package/Types/Workflow/Templates.ts +3041 -2
  548. package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
  549. package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
  550. package/Typings/Index.d.ts +24 -0
  551. package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
  552. package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
  553. package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
  554. package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
  555. package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
  556. package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
  557. package/UI/Components/Calendar/Calendar.css +83 -0
  558. package/UI/Components/Calendar/Calendar.tsx +136 -7
  559. package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
  560. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
  561. package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
  562. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
  563. package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
  564. package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
  565. package/UI/Components/Dropdown/Dropdown.tsx +58 -9
  566. package/UI/Components/Forms/Fields/FormField.tsx +49 -14
  567. package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
  568. package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
  569. package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
  570. package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
  571. package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
  572. package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
  573. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
  574. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
  575. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
  576. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
  577. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
  578. package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
  579. package/UI/Components/ModelTable/BaseModelTable.tsx +66 -14
  580. package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
  581. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
  582. package/UI/Components/Page/ModelPage.tsx +59 -3
  583. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
  584. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
  585. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
  586. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
  587. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
  588. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
  589. package/UI/Utils/Timezone.ts +69 -28
  590. package/Utils/AI/InvestigationReport.ts +195 -25
  591. package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
  592. package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
  593. package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
  594. package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
  595. package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
  596. package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
  597. package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
  598. package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
  599. package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
  600. package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
  601. package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
  602. package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
  603. package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
  604. package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
  605. package/Utils/Rum/SessionIdentity.ts +44 -0
  606. package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
  607. package/Utils/Rum/UserFlow.ts +1527 -0
  608. package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
  609. package/Utils/StatusPage/ResourceUptime.ts +99 -0
  610. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
  611. package/Utils/Telemetry/AttributesJSON.ts +367 -0
  612. package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
  613. package/Utils/Uptime/UptimeUtil.ts +122 -38
  614. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
  615. package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
  616. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  617. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  618. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
  619. package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
  620. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
  621. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  622. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
  623. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  624. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
  625. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  626. package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
  627. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  628. package/build/dist/Models/DatabaseModels/Runner.js +4 -4
  629. package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
  630. package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
  631. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  632. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
  633. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  634. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
  635. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  636. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
  637. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  638. package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
  639. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  640. package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
  641. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  642. package/build/dist/Server/API/CommonAPI.js +49 -0
  643. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  644. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
  645. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
  646. package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
  647. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  648. package/build/dist/Server/API/MonitorAPI.js +107 -1
  649. package/build/dist/Server/API/MonitorAPI.js.map +1 -1
  650. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
  651. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
  652. package/build/dist/Server/API/SlackAPI.js +322 -78
  653. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  654. package/build/dist/Server/API/StatusPageAPI.js +123 -17
  655. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  656. package/build/dist/Server/API/TelemetryAPI.js +144 -5
  657. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  658. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
  659. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
  660. package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
  661. package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
  662. package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
  663. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  664. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
  665. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
  666. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
  667. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
  668. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
  669. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
  670. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
  671. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
  672. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
  673. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
  674. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  675. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  676. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
  677. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  678. package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
  679. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  680. package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
  681. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  682. package/build/dist/Server/Services/AIAgentService.js +1 -0
  683. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  684. package/build/dist/Server/Services/AccessTokenService.js +87 -42
  685. package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
  686. package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
  687. package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
  688. package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
  689. package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
  690. package/build/dist/Server/Services/AlertService.js +110 -23
  691. package/build/dist/Server/Services/AlertService.js.map +1 -1
  692. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
  693. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  694. package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
  695. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  696. package/build/dist/Server/Services/BillingService.js +1 -0
  697. package/build/dist/Server/Services/BillingService.js.map +1 -1
  698. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
  699. package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
  700. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
  701. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  702. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
  703. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  704. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
  705. package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
  706. package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
  707. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  708. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
  709. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  710. package/build/dist/Server/Services/IncidentMemberService.js +14 -0
  711. package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
  712. package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
  713. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  714. package/build/dist/Server/Services/IncidentService.js +99 -21
  715. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  716. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  717. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  718. package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
  719. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  720. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
  721. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
  722. package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
  723. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  724. package/build/dist/Server/Services/LogAggregationService.js +14 -6
  725. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  726. package/build/dist/Server/Services/MonitorService.js +487 -48
  727. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  728. package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
  729. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  730. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
  731. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  732. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
  733. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  734. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
  735. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  736. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
  737. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  738. package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
  739. package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
  740. package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
  741. package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
  742. package/build/dist/Server/Services/ProbeService.js +7 -1
  743. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  744. package/build/dist/Server/Services/ProjectService.js +11 -1
  745. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  746. package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
  747. package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
  748. package/build/dist/Server/Services/RunbookSecretService.js +44 -0
  749. package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
  750. package/build/dist/Server/Services/RunnerJobService.js +641 -21
  751. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  752. package/build/dist/Server/Services/RunnerService.js +344 -5
  753. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  754. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
  755. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  756. package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
  757. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  758. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
  759. package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
  760. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +12 -1
  761. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  762. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
  763. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  764. package/build/dist/Server/Services/StatusPageService.js +230 -32
  765. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  766. package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
  767. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  768. package/build/dist/Server/Services/TeamMemberService.js +270 -11
  769. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  770. package/build/dist/Server/Services/TraceAggregationService.js +10 -6
  771. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  772. package/build/dist/Server/Services/UserEmailService.js +1 -0
  773. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  774. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
  775. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  776. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
  777. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  778. package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
  779. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  780. package/build/dist/Server/Services/UserService.js +107 -1
  781. package/build/dist/Server/Services/UserService.js.map +1 -1
  782. package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
  783. package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
  784. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
  785. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  786. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
  787. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  788. package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
  789. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  790. package/build/dist/Server/Types/Markdown.js +394 -27
  791. package/build/dist/Server/Types/Markdown.js.map +1 -1
  792. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
  793. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
  794. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
  795. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
  796. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
  797. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
  798. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
  799. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
  800. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
  801. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  802. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
  803. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  804. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
  805. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  806. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
  807. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  808. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
  809. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  810. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
  811. package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
  812. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
  813. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  814. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
  815. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  816. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
  817. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  818. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
  819. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
  820. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
  821. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  822. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
  823. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  824. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
  825. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  826. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
  827. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  828. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
  829. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  830. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
  831. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
  832. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
  833. package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
  834. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
  835. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
  836. package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
  837. package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
  838. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
  839. package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
  840. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
  841. package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
  842. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
  843. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  844. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
  845. package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
  846. package/build/dist/Server/Utils/StartServer.js +4 -3
  847. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  848. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
  849. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
  850. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
  851. package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
  852. package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
  853. package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
  854. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
  855. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
  856. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
  857. package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
  858. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
  859. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  860. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
  861. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  862. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
  863. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  864. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
  865. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  866. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
  867. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
  868. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
  869. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  870. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
  871. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  872. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
  873. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
  874. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
  875. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
  876. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
  877. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  878. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
  879. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  880. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
  881. package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
  882. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
  883. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
  884. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
  885. package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
  886. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
  887. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  888. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
  889. package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
  890. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
  891. package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
  892. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
  893. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  894. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
  895. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  896. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
  897. package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
  898. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  899. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
  900. package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
  901. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
  902. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  903. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
  904. package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
  905. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
  906. package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
  907. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
  908. package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
  909. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
  910. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  911. package/build/dist/Types/Date.js +97 -7
  912. package/build/dist/Types/Date.js.map +1 -1
  913. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  914. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  915. package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
  916. package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
  917. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
  918. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
  919. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
  920. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
  921. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
  922. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
  923. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
  924. package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
  925. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
  926. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  927. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
  928. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
  929. package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
  930. package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
  931. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
  932. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  933. package/build/dist/Types/Rum/SessionReplay.js +52 -2
  934. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  935. package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
  936. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  937. package/build/dist/Types/Rum/UserFlow.js +115 -0
  938. package/build/dist/Types/Rum/UserFlow.js.map +1 -0
  939. package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
  940. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  941. package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
  942. package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
  943. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
  944. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
  945. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
  946. package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
  947. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
  948. package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
  949. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
  950. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  951. package/build/dist/Types/Timezone.js +5 -0
  952. package/build/dist/Types/Timezone.js.map +1 -1
  953. package/build/dist/Types/TimezoneAlias.js +340 -0
  954. package/build/dist/Types/TimezoneAlias.js.map +1 -0
  955. package/build/dist/Types/Workflow/Templates.js +2694 -2
  956. package/build/dist/Types/Workflow/Templates.js.map +1 -1
  957. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
  958. package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
  959. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
  960. package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
  961. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
  962. package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
  963. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
  964. package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
  965. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
  966. package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
  967. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
  968. package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
  969. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
  970. package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
  971. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
  972. package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
  973. package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
  974. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  975. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
  976. package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
  977. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
  978. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  979. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
  980. package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
  981. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
  982. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
  983. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
  984. package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
  985. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
  986. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
  987. package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
  988. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  989. package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
  990. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  991. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
  992. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  993. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
  994. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  995. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
  996. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
  997. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  998. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
  999. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
  1000. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
  1001. package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
  1002. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
  1003. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  1004. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  1005. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1006. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
  1007. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  1008. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
  1009. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  1010. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
  1011. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
  1012. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
  1013. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
  1014. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +23 -8
  1015. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1016. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
  1017. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  1018. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
  1019. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  1020. package/build/dist/UI/Components/Page/ModelPage.js +37 -3
  1021. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  1022. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
  1023. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1024. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
  1025. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  1026. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  1027. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1028. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
  1029. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  1030. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
  1031. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  1032. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
  1033. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
  1034. package/build/dist/UI/Utils/Timezone.js +52 -17
  1035. package/build/dist/UI/Utils/Timezone.js.map +1 -1
  1036. package/build/dist/Utils/AI/InvestigationReport.js +116 -21
  1037. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1038. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
  1039. package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
  1040. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
  1041. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
  1042. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
  1043. package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
  1044. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
  1045. package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
  1046. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
  1047. package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
  1048. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
  1049. package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
  1050. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
  1051. package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
  1052. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
  1053. package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
  1054. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
  1055. package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
  1056. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
  1057. package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
  1058. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
  1059. package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
  1060. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
  1061. package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
  1062. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
  1063. package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
  1064. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
  1065. package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
  1066. package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
  1067. package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
  1068. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
  1069. package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
  1070. package/build/dist/Utils/Rum/UserFlow.js +941 -0
  1071. package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
  1072. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
  1073. package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
  1074. package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
  1075. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  1076. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  1077. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
  1078. package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
  1079. package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
  1080. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
  1081. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
  1082. package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
  1083. package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
  1084. package/jest.config.json +2 -1
  1085. package/package.json +2 -1
  1086. package/tsconfig.tests.json +36 -0
  1087. package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
  1088. package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
  1089. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
  1090. package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
@@ -0,0 +1,2064 @@
1
+ import {
2
+ afterEach,
3
+ beforeAll,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ test,
8
+ } from "@jest/globals";
9
+ import express from "express";
10
+ import http from "http";
11
+ import { AddressInfo } from "net";
12
+ import CommonAPI from "../../../Server/API/CommonAPI";
13
+ import MicrosoftTeamsAPI from "../../../Server/API/MicrosoftTeamsAPI";
14
+ import SlackAPI from "../../../Server/API/SlackAPI";
15
+ import UserMiddleware from "../../../Server/Middleware/UserAuthorization";
16
+ import ProjectService from "../../../Server/Services/ProjectService";
17
+ import WorkspaceNotificationRuleService from "../../../Server/Services/WorkspaceNotificationRuleService";
18
+ import {
19
+ ExpressRequest,
20
+ ExpressResponse,
21
+ ExpressRouter,
22
+ OneUptimeRequest,
23
+ } from "../../../Server/Utils/Express";
24
+ import logger from "../../../Server/Utils/Logger";
25
+ import { WorkspaceThread } from "../../../Server/Utils/Workspace/WorkspaceBase";
26
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
27
+ import Dictionary from "../../../Types/Dictionary";
28
+ import BadDataException from "../../../Types/Exception/BadDataException";
29
+ import Exception from "../../../Types/Exception/Exception";
30
+ import ExceptionCode from "../../../Types/Exception/ExceptionCode";
31
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
32
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
33
+ import { JSONObject } from "../../../Types/JSON";
34
+ import ObjectID from "../../../Types/ObjectID";
35
+ import Permission, {
36
+ UserPermission,
37
+ UserTenantAccessPermission,
38
+ } from "../../../Types/Permission";
39
+ import UserType from "../../../Types/UserType";
40
+ import WorkspaceType from "../../../Types/Workspace/WorkspaceType";
41
+ import WorkspaceNotificationRule from "../../../Models/DatabaseModels/WorkspaceNotificationRule";
42
+
43
+ /*
44
+ * The three "Send Test" routes behind the button on every row of the Slack
45
+ * Channels, Microsoft Teams Channels and Microsoft Teams Chats cards in
46
+ * Project Settings:
47
+ *
48
+ * POST /slack/channels/test { channelId }
49
+ * POST /microsoft-teams/channels/test { teamId, channelId }
50
+ * POST /microsoft-teams/chats/test { chatId }
51
+ *
52
+ * The service method they call (sendTestNotificationToDestination) validates
53
+ * the ids and posts through the same bot delivery path real notifications use,
54
+ * so it is covered by its own suite. What this file protects is the layer
55
+ * above it, which is where the mistakes that matter live:
56
+ *
57
+ * - The routes are mounted behind getUserMiddleware, which admits a request
58
+ * with no credentials as "public" and takes the project from a
59
+ * caller-supplied `tenantid` header. Posting into a channel is a side
60
+ * effect, so each route must itself prove the caller is an authenticated
61
+ * member of that project AND could already make OneUptime post there,
62
+ * i.e. could create a workspace notification rule through its CRUD
63
+ * endpoint. That gate is CommonAPI.assertCanCreateTable, which applies
64
+ * both halves of the CRUD create's table check: an Allow grant from the
65
+ * model's create list, and no unlabelled team BLOCK row on it. A Viewer
66
+ * must not be able to spam a customer's channels, and neither may a
67
+ * member whose team is blocked from creating rules - the CRUD create
68
+ * refuses that member, so the test route must too.
69
+ * - A refused caller must never reach the service - nothing may be sent.
70
+ * - The project, the workspace and the "tested by" user must come from the
71
+ * authenticated request, never from the body, and each route may only
72
+ * forward the destination fields that belong to it: the channel route must
73
+ * not smuggle a chat id through, the chat route must not smuggle a
74
+ * channel, and Slack has neither chats nor teams.
75
+ * - Body fields are untrusted JSON: anything that is not a string reaches
76
+ * the service as "" so the service's own validation produces the
77
+ * user-facing error, instead of a TypeError on `.trim()`.
78
+ * - Service failures surface with the status of the exception thrown and
79
+ * its exact message, because the dashboard shows that message verbatim in
80
+ * the "Test Notification Failed" modal.
81
+ *
82
+ * Handlers are pulled out of the routers the API classes really build (so the
83
+ * path, method and middleware wiring are the real ones) and run against the
84
+ * real Response utility. A second group then drives each route over real HTTP
85
+ * through express and the real getUserMiddleware.
86
+ */
87
+
88
+ type ExpressRouteHandler = (
89
+ req: ExpressRequest,
90
+ res: ExpressResponse,
91
+ next?: (err?: unknown) => void,
92
+ ) => Promise<void> | void;
93
+
94
+ type ExpressRouteLayer = {
95
+ route?: {
96
+ path: string;
97
+ methods: Record<string, boolean>;
98
+ stack: Array<{ handle: ExpressRouteHandler; method?: string }>;
99
+ };
100
+ };
101
+
102
+ type DestinationField = "teamId" | "channelId" | "chatId";
103
+
104
+ type ApiName = "MicrosoftTeams" | "Slack";
105
+
106
+ interface RouteSpec {
107
+ path: string;
108
+ api: ApiName;
109
+ workspaceType: WorkspaceType;
110
+ // The only destination fields this route may forward to the service.
111
+ forwardedFields: Array<DestinationField>;
112
+ // A realistic body for this route.
113
+ validBody: JSONObject;
114
+ }
115
+
116
+ type SendTestArgs = Parameters<
117
+ typeof WorkspaceNotificationRuleService.sendTestNotificationToDestination
118
+ >[0];
119
+
120
+ const TEAMS_CHANNEL_ROUTE: RouteSpec = {
121
+ path: "/microsoft-teams/channels/test",
122
+ api: "MicrosoftTeams",
123
+ workspaceType: WorkspaceType.MicrosoftTeams,
124
+ forwardedFields: ["teamId", "channelId"],
125
+ validBody: {
126
+ teamId: "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
127
+ channelId: "19:0123456789abcdef0123456789abcdef@thread.tacv2",
128
+ },
129
+ };
130
+
131
+ const TEAMS_CHAT_ROUTE: RouteSpec = {
132
+ path: "/microsoft-teams/chats/test",
133
+ api: "MicrosoftTeams",
134
+ workspaceType: WorkspaceType.MicrosoftTeams,
135
+ forwardedFields: ["chatId"],
136
+ validBody: {
137
+ chatId: "19:3a9c1f0e5b7d4e2f8a6b0c1d2e3f4a5b@thread.v2",
138
+ },
139
+ };
140
+
141
+ const SLACK_CHANNEL_ROUTE: RouteSpec = {
142
+ path: "/slack/channels/test",
143
+ api: "Slack",
144
+ workspaceType: WorkspaceType.Slack,
145
+ forwardedFields: ["channelId"],
146
+ validBody: {
147
+ channelId: "C0123456789",
148
+ },
149
+ };
150
+
151
+ const ALL_ROUTES: Array<[string, RouteSpec]> = [
152
+ [`POST ${TEAMS_CHANNEL_ROUTE.path}`, TEAMS_CHANNEL_ROUTE],
153
+ [`POST ${TEAMS_CHAT_ROUTE.path}`, TEAMS_CHAT_ROUTE],
154
+ [`POST ${SLACK_CHANNEL_ROUTE.path}`, SLACK_CHANNEL_ROUTE],
155
+ ];
156
+
157
+ const ALL_DESTINATION_FIELDS: Array<DestinationField> = [
158
+ "teamId",
159
+ "channelId",
160
+ "chatId",
161
+ ];
162
+
163
+ const PERMISSION_DENIED_MESSAGE: string =
164
+ "You do not have permission to send test notifications in this project.";
165
+
166
+ const NOT_A_MEMBER_MESSAGE: string =
167
+ "You are not authorized to access this project's data.";
168
+
169
+ /*
170
+ * What a team BLOCK row answers with. It is the block check's own wording
171
+ * (the same one POST /workspace-notification-rule gives), not the send-test
172
+ * message: the caller does hold an Allow grant, and this names the block
173
+ * that overrides it.
174
+ */
175
+ type BlockedMessageFunction = (permission: Permission) => string;
176
+
177
+ const blockedMessage: BlockedMessageFunction = (
178
+ permission: Permission,
179
+ ): string => {
180
+ return `You are not authorized to create Workspace Notification Rule because ${permission} is in your team's permission block list.`;
181
+ };
182
+
183
+ const LABEL_ID: ObjectID = new ObjectID("99999999-9999-4999-8999-999999999999");
184
+
185
+ /*
186
+ * Routers are built once from the real API classes. Building them is cheap;
187
+ * importing the API modules is what pulls in the big module graph, and that
188
+ * happens at file load, outside every test's timeout.
189
+ */
190
+ const routers: Record<ApiName, ExpressRouter | null> = {
191
+ MicrosoftTeams: null,
192
+ Slack: null,
193
+ };
194
+
195
+ type GetRouterFunction = (api: ApiName) => ExpressRouter;
196
+
197
+ const getRouter: GetRouterFunction = (api: ApiName): ExpressRouter => {
198
+ const router: ExpressRouter | null = routers[api];
199
+
200
+ if (!router) {
201
+ throw new Error(`The ${api} router has not been built yet.`);
202
+ }
203
+
204
+ return router;
205
+ };
206
+
207
+ type GetLayersFunction = (api: ApiName) => Array<ExpressRouteLayer>;
208
+
209
+ const getLayers: GetLayersFunction = (
210
+ api: ApiName,
211
+ ): Array<ExpressRouteLayer> => {
212
+ return (getRouter(api) as unknown as { stack: Array<ExpressRouteLayer> })
213
+ .stack;
214
+ };
215
+
216
+ type FindRouteLayersFunction = (data: {
217
+ api: ApiName;
218
+ path: string;
219
+ method: string;
220
+ }) => Array<ExpressRouteLayer>;
221
+
222
+ const findRouteLayers: FindRouteLayersFunction = (data: {
223
+ api: ApiName;
224
+ path: string;
225
+ method: string;
226
+ }): Array<ExpressRouteLayer> => {
227
+ return getLayers(data.api).filter((layer: ExpressRouteLayer) => {
228
+ return (
229
+ layer.route?.path === data.path &&
230
+ layer.route?.methods[data.method] === true
231
+ );
232
+ });
233
+ };
234
+
235
+ type GetRouteLayerFunction = (data: {
236
+ api: ApiName;
237
+ path: string;
238
+ method: string;
239
+ }) => ExpressRouteLayer;
240
+
241
+ const getRouteLayer: GetRouteLayerFunction = (data: {
242
+ api: ApiName;
243
+ path: string;
244
+ method: string;
245
+ }): ExpressRouteLayer => {
246
+ const layers: Array<ExpressRouteLayer> = findRouteLayers(data);
247
+
248
+ if (layers.length === 0 || !layers[0]!.route) {
249
+ throw new Error(
250
+ `${data.method.toUpperCase()} ${data.path} is not registered on the ${data.api} router`,
251
+ );
252
+ }
253
+
254
+ return layers[0]!;
255
+ };
256
+
257
+ type GetHandlerFunction = (route: RouteSpec) => ExpressRouteHandler;
258
+
259
+ // The route's own handler: the last function in its stack.
260
+ const getHandler: GetHandlerFunction = (
261
+ route: RouteSpec,
262
+ ): ExpressRouteHandler => {
263
+ const layer: ExpressRouteLayer = getRouteLayer({
264
+ api: route.api,
265
+ path: route.path,
266
+ method: "post",
267
+ });
268
+
269
+ const stack: Array<{ handle: ExpressRouteHandler }> = layer.route!.stack;
270
+
271
+ return stack[stack.length - 1]!.handle;
272
+ };
273
+
274
+ /*
275
+ * Props builders. These mirror what getUserMiddleware + getDatabaseCommon-
276
+ * InteractionProps produce for real requests: a tenant id taken from the
277
+ * `tenantid` header, the user id from the access token, and a per-project
278
+ * dictionary of the permissions the user's teams grant in that project.
279
+ */
280
+ type BuildPermissionFunction = (
281
+ permission: Permission,
282
+ isBlockPermission?: boolean,
283
+ labelIds?: Array<ObjectID>,
284
+ ) => UserPermission;
285
+
286
+ const buildPermission: BuildPermissionFunction = (
287
+ permission: Permission,
288
+ isBlockPermission?: boolean,
289
+ labelIds?: Array<ObjectID>,
290
+ ): UserPermission => {
291
+ return {
292
+ _type: "UserPermission",
293
+ permission: permission,
294
+ labelIds: labelIds || [],
295
+ isBlockPermission: Boolean(isBlockPermission),
296
+ };
297
+ };
298
+
299
+ type BuildTenantPermissionsFunction = (
300
+ grants: Array<{ projectId: ObjectID; permissions: Array<UserPermission> }>,
301
+ ) => Dictionary<UserTenantAccessPermission>;
302
+
303
+ const buildTenantPermissions: BuildTenantPermissionsFunction = (
304
+ grants: Array<{ projectId: ObjectID; permissions: Array<UserPermission> }>,
305
+ ): Dictionary<UserTenantAccessPermission> => {
306
+ const map: Dictionary<UserTenantAccessPermission> = {};
307
+
308
+ grants.forEach(
309
+ (grant: { projectId: ObjectID; permissions: Array<UserPermission> }) => {
310
+ map[grant.projectId.toString()] = {
311
+ _type: "UserTenantAccessPermission",
312
+ projectId: grant.projectId,
313
+ permissions: grant.permissions,
314
+ };
315
+ },
316
+ );
317
+
318
+ return map;
319
+ };
320
+
321
+ type BuildMemberPropsFunction = (data: {
322
+ projectId: ObjectID;
323
+ userId: ObjectID;
324
+ permissions: Array<Permission>;
325
+ blockedPermissions?: Array<Permission> | undefined;
326
+ // Labels every block row is scoped to; none means a table-wide block.
327
+ blockLabelIds?: Array<ObjectID> | undefined;
328
+ userType?: UserType | undefined;
329
+ isMasterAdmin?: boolean | undefined;
330
+ }) => DatabaseCommonInteractionProps;
331
+
332
+ const buildMemberProps: BuildMemberPropsFunction = (data: {
333
+ projectId: ObjectID;
334
+ userId: ObjectID;
335
+ permissions: Array<Permission>;
336
+ blockedPermissions?: Array<Permission> | undefined;
337
+ blockLabelIds?: Array<ObjectID> | undefined;
338
+ userType?: UserType | undefined;
339
+ isMasterAdmin?: boolean | undefined;
340
+ }): DatabaseCommonInteractionProps => {
341
+ const userPermissions: Array<UserPermission> = [
342
+ ...data.permissions.map((permission: Permission) => {
343
+ return buildPermission(permission);
344
+ }),
345
+ ...(data.blockedPermissions || []).map((permission: Permission) => {
346
+ return buildPermission(permission, true, data.blockLabelIds);
347
+ }),
348
+ ];
349
+
350
+ const props: DatabaseCommonInteractionProps = {
351
+ tenantId: data.projectId,
352
+ userId: data.userId,
353
+ userType: data.userType || UserType.User,
354
+ userTenantAccessPermission: buildTenantPermissions([
355
+ { projectId: data.projectId, permissions: userPermissions },
356
+ ]),
357
+ };
358
+
359
+ if (data.isMasterAdmin) {
360
+ props.isMasterAdmin = true;
361
+ }
362
+
363
+ return props;
364
+ };
365
+
366
+ type InvokedResponse = {
367
+ statusCode: number | null;
368
+ body: JSONObject | null;
369
+ statusCalls: Array<number>;
370
+ sendCalls: Array<unknown>;
371
+ };
372
+
373
+ type InvokeRouteFunction = (data: {
374
+ route: RouteSpec;
375
+ body?: unknown;
376
+ omitBody?: boolean | undefined;
377
+ }) => Promise<{ response: InvokedResponse; req: ExpressRequest }>;
378
+
379
+ /*
380
+ * Calls the real handler with a minimal request and a response that records
381
+ * every status()/send() the real Response utility makes. A handler that
382
+ * forgets to answer, or answers twice, shows up in statusCalls/sendCalls.
383
+ */
384
+ const invokeRoute: InvokeRouteFunction = async (data: {
385
+ route: RouteSpec;
386
+ body?: unknown;
387
+ omitBody?: boolean | undefined;
388
+ }): Promise<{ response: InvokedResponse; req: ExpressRequest }> => {
389
+ const handler: ExpressRouteHandler = getHandler(data.route);
390
+
391
+ const response: InvokedResponse = {
392
+ statusCode: null,
393
+ body: null,
394
+ statusCalls: [],
395
+ sendCalls: [],
396
+ };
397
+
398
+ const res: ExpressResponse = {
399
+ status(statusCode: number): ExpressResponse {
400
+ response.statusCode = statusCode;
401
+ response.statusCalls.push(statusCode);
402
+ return res;
403
+ },
404
+ send(body: JSONObject): ExpressResponse {
405
+ response.body = body;
406
+ response.sendCalls.push(body);
407
+ return res;
408
+ },
409
+ } as unknown as ExpressResponse;
410
+
411
+ const reqObject: Record<string, unknown> = {
412
+ headers: {},
413
+ query: {},
414
+ params: {},
415
+ };
416
+
417
+ if (!data.omitBody) {
418
+ reqObject["body"] =
419
+ data.body === undefined ? data.route.validBody : data.body;
420
+ }
421
+
422
+ const req: ExpressRequest = reqObject as unknown as ExpressRequest;
423
+
424
+ const next: (err?: unknown) => void = (err?: unknown): void => {
425
+ throw new Error(
426
+ `The route handler called next() instead of answering: ${String(err)}`,
427
+ );
428
+ };
429
+
430
+ await handler(req, res, next);
431
+
432
+ return { response, req };
433
+ };
434
+
435
+ type ExpectedArgsFunction = (data: {
436
+ route: RouteSpec;
437
+ props: DatabaseCommonInteractionProps;
438
+ body: unknown;
439
+ }) => SendTestArgs;
440
+
441
+ /*
442
+ * What the spec says the route forwards: the project and user from the
443
+ * authenticated props, the route's own workspace type, and - for each of
444
+ * the route's own destination fields only - the body value when it is a
445
+ * string, "" otherwise.
446
+ */
447
+ const expectedArgs: ExpectedArgsFunction = (data: {
448
+ route: RouteSpec;
449
+ props: DatabaseCommonInteractionProps;
450
+ body: unknown;
451
+ }): SendTestArgs => {
452
+ const args: SendTestArgs = {
453
+ projectId: data.props.tenantId!,
454
+ workspaceType: data.route.workspaceType,
455
+ testByUserId: data.props.userId!,
456
+ };
457
+
458
+ const body: Record<string, unknown> | null =
459
+ data.body && typeof data.body === "object"
460
+ ? (data.body as Record<string, unknown>)
461
+ : null;
462
+
463
+ data.route.forwardedFields.forEach((field: DestinationField) => {
464
+ const value: unknown = body ? body[field] : undefined;
465
+ args[field] = typeof value === "string" ? value : "";
466
+ });
467
+
468
+ return args;
469
+ };
470
+
471
+ const SAMPLE_THREAD: WorkspaceThread = {
472
+ channel: {
473
+ id: "C0123456789",
474
+ name: "alerts",
475
+ workspaceType: WorkspaceType.Slack,
476
+ },
477
+ threadId: "1700000000.000100",
478
+ };
479
+
480
+ describe("Send Test notification routes (Slack channels, Microsoft Teams channels and chats)", () => {
481
+ let sendTestSpy: jest.SpyInstance;
482
+ let getPropsSpy: jest.SpyInstance;
483
+ let projectId: ObjectID;
484
+ let otherProjectId: ObjectID;
485
+ let userId: ObjectID;
486
+
487
+ type MockPropsFunction = (props: DatabaseCommonInteractionProps) => void;
488
+
489
+ const mockProps: MockPropsFunction = (
490
+ props: DatabaseCommonInteractionProps,
491
+ ): void => {
492
+ getPropsSpy.mockResolvedValue(props);
493
+ };
494
+
495
+ type OwnerPropsFunction = () => DatabaseCommonInteractionProps;
496
+
497
+ const ownerProps: OwnerPropsFunction = (): DatabaseCommonInteractionProps => {
498
+ return buildMemberProps({
499
+ projectId: projectId,
500
+ userId: userId,
501
+ permissions: [Permission.ProjectOwner],
502
+ });
503
+ };
504
+
505
+ beforeAll(() => {
506
+ routers.MicrosoftTeams = new MicrosoftTeamsAPI().getRouter();
507
+ routers.Slack = new SlackAPI().getRouter();
508
+ });
509
+
510
+ beforeEach(() => {
511
+ projectId = ObjectID.generate();
512
+ otherProjectId = ObjectID.generate();
513
+ userId = ObjectID.generate();
514
+
515
+ sendTestSpy = jest
516
+ .spyOn(
517
+ WorkspaceNotificationRuleService,
518
+ "sendTestNotificationToDestination",
519
+ )
520
+ .mockResolvedValue(SAMPLE_THREAD);
521
+
522
+ getPropsSpy = jest
523
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
524
+ .mockResolvedValue(ownerProps());
525
+
526
+ // sendErrorResponse logs every refusal; keep the test output readable.
527
+ jest.spyOn(logger, "error").mockImplementation(() => {
528
+ return undefined;
529
+ });
530
+ });
531
+
532
+ afterEach(() => {
533
+ jest.restoreAllMocks();
534
+ });
535
+
536
+ describe("wiring", () => {
537
+ test.each(ALL_ROUTES)(
538
+ "%s is registered exactly once, for POST only",
539
+ (_label: string, route: RouteSpec) => {
540
+ const postLayers: Array<ExpressRouteLayer> = findRouteLayers({
541
+ api: route.api,
542
+ path: route.path,
543
+ method: "post",
544
+ });
545
+
546
+ expect(postLayers).toHaveLength(1);
547
+
548
+ const methods: Record<string, boolean> = postLayers[0]!.route!.methods;
549
+ expect(methods["post"]).toBe(true);
550
+ expect(methods["get"]).toBeFalsy();
551
+ expect(methods["put"]).toBeFalsy();
552
+ expect(methods["delete"]).toBeFalsy();
553
+
554
+ /*
555
+ * No other layer (any method) may claim the same path, otherwise a
556
+ * GET or PUT to it would reach a different handler.
557
+ */
558
+ const allLayersForPath: Array<ExpressRouteLayer> = getLayers(
559
+ route.api,
560
+ ).filter((layer: ExpressRouteLayer) => {
561
+ return layer.route?.path === route.path;
562
+ });
563
+ expect(allLayersForPath).toHaveLength(1);
564
+ },
565
+ );
566
+
567
+ test.each(ALL_ROUTES)(
568
+ "%s runs getUserMiddleware and then exactly one handler",
569
+ (_label: string, route: RouteSpec) => {
570
+ const layer: ExpressRouteLayer = getRouteLayer({
571
+ api: route.api,
572
+ path: route.path,
573
+ method: "post",
574
+ });
575
+
576
+ const handles: Array<ExpressRouteHandler> = layer.route!.stack.map(
577
+ (entry: { handle: ExpressRouteHandler }) => {
578
+ return entry.handle;
579
+ },
580
+ );
581
+
582
+ /*
583
+ * Without getUserMiddleware the request would never carry a user or a
584
+ * tenant, so every caller would look anonymous - and a route that
585
+ * forgot to check would have no middleware to blame.
586
+ */
587
+ expect(handles).toHaveLength(2);
588
+ expect(handles[0]).toBe(UserMiddleware.getUserMiddleware);
589
+ expect(typeof handles[1]).toBe("function");
590
+ expect(handles[1]).not.toBe(UserMiddleware.getUserMiddleware);
591
+ },
592
+ );
593
+
594
+ test.each([
595
+ ["MicrosoftTeams" as ApiName, "/microsoft-teams/channels"],
596
+ ["MicrosoftTeams" as ApiName, "/microsoft-teams/chats"],
597
+ ["Slack" as ApiName, "/slack/channels"],
598
+ ])(
599
+ "the existing GET list route on the %s router, %s, is still registered behind getUserMiddleware",
600
+ (api: ApiName, path: string) => {
601
+ const layers: Array<ExpressRouteLayer> = findRouteLayers({
602
+ api: api,
603
+ path: path,
604
+ method: "get",
605
+ });
606
+
607
+ expect(layers).toHaveLength(1);
608
+ expect(layers[0]!.route!.stack[0]!.handle).toBe(
609
+ UserMiddleware.getUserMiddleware,
610
+ );
611
+ // Adding the POST beside it must not have turned the list route into a POST.
612
+ expect(layers[0]!.route!.methods["post"]).toBeFalsy();
613
+ },
614
+ );
615
+
616
+ test("each route has its own handler (they are not one shared function)", () => {
617
+ const handlers: Array<ExpressRouteHandler> = [
618
+ getHandler(TEAMS_CHANNEL_ROUTE),
619
+ getHandler(TEAMS_CHAT_ROUTE),
620
+ getHandler(SLACK_CHANNEL_ROUTE),
621
+ ];
622
+
623
+ expect(new Set(handlers).size).toBe(3);
624
+ });
625
+
626
+ test("the Slack router does not serve the Microsoft Teams test routes, and vice versa", () => {
627
+ expect(
628
+ findRouteLayers({
629
+ api: "Slack",
630
+ path: TEAMS_CHANNEL_ROUTE.path,
631
+ method: "post",
632
+ }),
633
+ ).toHaveLength(0);
634
+ expect(
635
+ findRouteLayers({
636
+ api: "Slack",
637
+ path: TEAMS_CHAT_ROUTE.path,
638
+ method: "post",
639
+ }),
640
+ ).toHaveLength(0);
641
+ expect(
642
+ findRouteLayers({
643
+ api: "MicrosoftTeams",
644
+ path: SLACK_CHANNEL_ROUTE.path,
645
+ method: "post",
646
+ }),
647
+ ).toHaveLength(0);
648
+ });
649
+ });
650
+
651
+ describe.each(ALL_ROUTES)("%s", (_label: string, route: RouteSpec) => {
652
+ type ExpectRefusedFunction = (
653
+ response: InvokedResponse,
654
+ status: number,
655
+ message: string,
656
+ ) => void;
657
+
658
+ const expectRefused: ExpectRefusedFunction = (
659
+ response: InvokedResponse,
660
+ status: number,
661
+ message: string,
662
+ ): void => {
663
+ expect(response.statusCalls).toEqual([status]);
664
+ expect(response.sendCalls).toHaveLength(1);
665
+ expect(response.body).toEqual({ message: message });
666
+ expect(sendTestSpy).not.toHaveBeenCalled();
667
+ };
668
+
669
+ describe("authentication", () => {
670
+ test("reads the caller's props from the request it was given", async () => {
671
+ const { req } = await invokeRoute({ route });
672
+
673
+ expect(getPropsSpy).toHaveBeenCalledTimes(1);
674
+ expect(getPropsSpy.mock.calls[0]![0]).toBe(req);
675
+ });
676
+
677
+ test("an unauthenticated (public) caller with a tenant header gets 401 and nothing is sent", async () => {
678
+ mockProps({
679
+ tenantId: projectId,
680
+ userType: UserType.Public,
681
+ });
682
+
683
+ const { response } = await invokeRoute({ route });
684
+
685
+ expectRefused(response, 401, CommonAPI.AUTHENTICATION_REQUIRED_MESSAGE);
686
+ });
687
+
688
+ test("a caller with no credentials and no tenant gets 401 (not 400) and nothing is sent", async () => {
689
+ mockProps({});
690
+
691
+ const { response } = await invokeRoute({ route });
692
+
693
+ expectRefused(response, 401, CommonAPI.AUTHENTICATION_REQUIRED_MESSAGE);
694
+ });
695
+
696
+ test("an anonymous caller carrying a forged ProjectOwner grant is still 401", async () => {
697
+ /*
698
+ * No user id means no identity, whatever permission map rides along.
699
+ * The route must not decide on permissions before identity.
700
+ */
701
+ mockProps({
702
+ tenantId: projectId,
703
+ userType: UserType.Public,
704
+ userTenantAccessPermission: buildTenantPermissions([
705
+ {
706
+ projectId: projectId,
707
+ permissions: [buildPermission(Permission.ProjectOwner)],
708
+ },
709
+ ]),
710
+ });
711
+
712
+ const { response } = await invokeRoute({ route });
713
+
714
+ expectRefused(response, 401, CommonAPI.AUTHENTICATION_REQUIRED_MESSAGE);
715
+ });
716
+
717
+ test("an authenticated user with no tenant is refused with 400 and nothing is sent", async () => {
718
+ mockProps({
719
+ userId: userId,
720
+ userType: UserType.User,
721
+ });
722
+
723
+ const { response } = await invokeRoute({ route });
724
+
725
+ expectRefused(response, 400, "Project ID is required");
726
+ });
727
+
728
+ test("an authenticated user who is not a member of the tenant is refused and nothing is sent", async () => {
729
+ mockProps({
730
+ tenantId: projectId,
731
+ userId: userId,
732
+ userType: UserType.User,
733
+ });
734
+
735
+ const { response } = await invokeRoute({ route });
736
+
737
+ expectRefused(response, 422, NOT_A_MEMBER_MESSAGE);
738
+ });
739
+
740
+ test("an owner of ANOTHER project who names this project in the tenant header is refused and nothing is sent", async () => {
741
+ mockProps({
742
+ tenantId: projectId,
743
+ userId: userId,
744
+ userType: UserType.User,
745
+ userTenantAccessPermission: buildTenantPermissions([
746
+ {
747
+ projectId: otherProjectId,
748
+ permissions: [buildPermission(Permission.ProjectOwner)],
749
+ },
750
+ ]),
751
+ });
752
+
753
+ const { response } = await invokeRoute({ route });
754
+
755
+ expectRefused(response, 422, NOT_A_MEMBER_MESSAGE);
756
+ });
757
+
758
+ test("a project API key is refused: the test is sent as a person, so it needs a user", async () => {
759
+ mockProps({
760
+ tenantId: projectId,
761
+ userType: UserType.API,
762
+ userTenantAccessPermission: buildTenantPermissions([
763
+ {
764
+ projectId: projectId,
765
+ permissions: [buildPermission(Permission.ProjectOwner)],
766
+ },
767
+ ]),
768
+ });
769
+
770
+ const { response } = await invokeRoute({ route });
771
+
772
+ expectRefused(response, 422, NOT_A_MEMBER_MESSAGE);
773
+ });
774
+
775
+ test("a failure while reading the caller's props is answered as an error and nothing is sent", async () => {
776
+ getPropsSpy.mockRejectedValue(
777
+ new BadDataException("Could not read the current plan."),
778
+ );
779
+
780
+ const { response } = await invokeRoute({ route });
781
+
782
+ expectRefused(response, 400, "Could not read the current plan.");
783
+ });
784
+ });
785
+
786
+ describe("authorization", () => {
787
+ test.each([
788
+ ["Viewer", [Permission.Viewer]],
789
+ [
790
+ "ReadWorkspaceNotificationRule",
791
+ [Permission.ReadWorkspaceNotificationRule],
792
+ ],
793
+ ["SettingsViewer", [Permission.SettingsViewer]],
794
+ [
795
+ "EditWorkspaceNotificationRule",
796
+ [Permission.EditWorkspaceNotificationRule],
797
+ ],
798
+ [
799
+ "DeleteWorkspaceNotificationRule",
800
+ [Permission.DeleteWorkspaceNotificationRule],
801
+ ],
802
+ [
803
+ "every read-only grant at once",
804
+ [
805
+ Permission.Viewer,
806
+ Permission.SettingsViewer,
807
+ Permission.ReadWorkspaceNotificationRule,
808
+ ],
809
+ ],
810
+ ["no grants at all", []],
811
+ ])(
812
+ "a member whose only grant is %s is refused with the send-test permission message and nothing is sent",
813
+ async (_permissionLabel: string, permissions: Array<Permission>) => {
814
+ mockProps(
815
+ buildMemberProps({
816
+ projectId: projectId,
817
+ userId: userId,
818
+ permissions: permissions,
819
+ }),
820
+ );
821
+
822
+ const { response } = await invokeRoute({ route });
823
+
824
+ expectRefused(response, 422, PERMISSION_DENIED_MESSAGE);
825
+ },
826
+ );
827
+
828
+ test("a team BLOCK on CreateWorkspaceNotificationRule is not counted as a grant", async () => {
829
+ mockProps(
830
+ buildMemberProps({
831
+ projectId: projectId,
832
+ userId: userId,
833
+ permissions: [Permission.Viewer],
834
+ blockedPermissions: [Permission.CreateWorkspaceNotificationRule],
835
+ }),
836
+ );
837
+
838
+ const { response } = await invokeRoute({ route });
839
+
840
+ /*
841
+ * With no Allow grant at all, the Allow half refuses first, so the
842
+ * caller sees the send-test message rather than the block wording.
843
+ */
844
+ expectRefused(response, 422, PERMISSION_DENIED_MESSAGE);
845
+ });
846
+
847
+ test("an owner grant in another project does not leak into this one", async () => {
848
+ const props: DatabaseCommonInteractionProps = {
849
+ tenantId: projectId,
850
+ userId: userId,
851
+ userType: UserType.User,
852
+ userTenantAccessPermission: buildTenantPermissions([
853
+ {
854
+ projectId: projectId,
855
+ permissions: [buildPermission(Permission.Viewer)],
856
+ },
857
+ {
858
+ projectId: otherProjectId,
859
+ permissions: [
860
+ buildPermission(Permission.ProjectOwner),
861
+ buildPermission(Permission.CreateWorkspaceNotificationRule),
862
+ ],
863
+ },
864
+ ]),
865
+ };
866
+
867
+ mockProps(props);
868
+
869
+ const { response } = await invokeRoute({ route });
870
+
871
+ expectRefused(response, 422, PERMISSION_DENIED_MESSAGE);
872
+ });
873
+
874
+ test.each([
875
+ ["ProjectOwner", [Permission.ProjectOwner]],
876
+ ["ProjectAdmin", [Permission.ProjectAdmin]],
877
+ ["ProjectMember", [Permission.ProjectMember]],
878
+ ["SettingsAdmin", [Permission.SettingsAdmin]],
879
+ ["SettingsMember", [Permission.SettingsMember]],
880
+ [
881
+ "CreateWorkspaceNotificationRule",
882
+ [Permission.CreateWorkspaceNotificationRule],
883
+ ],
884
+ [
885
+ "Viewer plus CreateWorkspaceNotificationRule",
886
+ [Permission.Viewer, Permission.CreateWorkspaceNotificationRule],
887
+ ],
888
+ ])(
889
+ "a member with %s sends the test once with the exact arguments and gets 200 {}",
890
+ async (_permissionLabel: string, permissions: Array<Permission>) => {
891
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
892
+ projectId: projectId,
893
+ userId: userId,
894
+ permissions: permissions,
895
+ });
896
+
897
+ mockProps(props);
898
+
899
+ const { response } = await invokeRoute({ route });
900
+
901
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
902
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual(
903
+ expectedArgs({ route, props, body: route.validBody }),
904
+ );
905
+
906
+ expect(response.statusCalls).toEqual([200]);
907
+ expect(response.sendCalls).toHaveLength(1);
908
+ expect(response.body).toEqual({});
909
+ },
910
+ );
911
+
912
+ test("a master admin who is a member with only Viewer bypasses the permission check", async () => {
913
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
914
+ projectId: projectId,
915
+ userId: userId,
916
+ permissions: [Permission.Viewer],
917
+ userType: UserType.MasterAdmin,
918
+ isMasterAdmin: true,
919
+ });
920
+
921
+ mockProps(props);
922
+
923
+ const { response } = await invokeRoute({ route });
924
+
925
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
926
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual(
927
+ expectedArgs({ route, props, body: route.validBody }),
928
+ );
929
+ expect(response.statusCalls).toEqual([200]);
930
+ expect(response.body).toEqual({});
931
+ });
932
+
933
+ test("a master admin who is a member with no grants at all bypasses the permission check", async () => {
934
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
935
+ projectId: projectId,
936
+ userId: userId,
937
+ permissions: [],
938
+ userType: UserType.MasterAdmin,
939
+ isMasterAdmin: true,
940
+ });
941
+
942
+ mockProps(props);
943
+
944
+ const { response } = await invokeRoute({ route });
945
+
946
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
947
+ expect(response.statusCalls).toEqual([200]);
948
+ });
949
+
950
+ test("the master admin bypass covers permissions only: a master admin who is not a member of the tenant is still refused", async () => {
951
+ mockProps({
952
+ tenantId: projectId,
953
+ userId: userId,
954
+ userType: UserType.MasterAdmin,
955
+ isMasterAdmin: true,
956
+ });
957
+
958
+ const { response } = await invokeRoute({ route });
959
+
960
+ expectRefused(response, 422, NOT_A_MEMBER_MESSAGE);
961
+ });
962
+ });
963
+
964
+ describe("the permission gate", () => {
965
+ test("is CommonAPI.assertCanCreateTable for WorkspaceNotificationRule, with the caller's props and the send-test message, before anything is sent", async () => {
966
+ const gateSpy: jest.SpyInstance = jest.spyOn(
967
+ CommonAPI,
968
+ "assertCanCreateTable",
969
+ );
970
+
971
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
972
+ projectId: projectId,
973
+ userId: userId,
974
+ permissions: [Permission.ProjectMember],
975
+ });
976
+
977
+ mockProps(props);
978
+
979
+ const { response } = await invokeRoute({ route });
980
+
981
+ expect(gateSpy).toHaveBeenCalledTimes(1);
982
+
983
+ const gateArgs: {
984
+ modelType: unknown;
985
+ props: DatabaseCommonInteractionProps;
986
+ errorMessage?: string | undefined;
987
+ } = gateSpy.mock.calls[0]![0] as {
988
+ modelType: unknown;
989
+ props: DatabaseCommonInteractionProps;
990
+ errorMessage?: string | undefined;
991
+ };
992
+
993
+ expect(gateArgs.modelType).toBe(WorkspaceNotificationRule);
994
+ expect(gateArgs.props).toBe(props);
995
+ expect(gateArgs.errorMessage).toBe(PERMISSION_DENIED_MESSAGE);
996
+
997
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
998
+ expect(gateSpy.mock.invocationCallOrder[0]!).toBeLessThan(
999
+ sendTestSpy.mock.invocationCallOrder[0]!,
1000
+ );
1001
+ expect(response.statusCalls).toEqual([200]);
1002
+ });
1003
+
1004
+ test("whatever the gate refuses is answered with its exception, and nothing is sent", async () => {
1005
+ // An owner would pass; only the stubbed gate stands in the way.
1006
+ jest.spyOn(CommonAPI, "assertCanCreateTable").mockImplementation(() => {
1007
+ throw new NotAuthorizedException("Refused by the create gate.");
1008
+ });
1009
+
1010
+ const { response } = await invokeRoute({ route });
1011
+
1012
+ expectRefused(response, 422, "Refused by the create gate.");
1013
+ });
1014
+
1015
+ test("identity is settled before the gate runs: an anonymous caller never reaches it", async () => {
1016
+ const gateSpy: jest.SpyInstance = jest.spyOn(
1017
+ CommonAPI,
1018
+ "assertCanCreateTable",
1019
+ );
1020
+
1021
+ mockProps({
1022
+ tenantId: projectId,
1023
+ userType: UserType.Public,
1024
+ });
1025
+
1026
+ const { response } = await invokeRoute({ route });
1027
+
1028
+ expectRefused(response, 401, CommonAPI.AUTHENTICATION_REQUIRED_MESSAGE);
1029
+ expect(gateSpy).not.toHaveBeenCalled();
1030
+ });
1031
+
1032
+ test("membership is settled before the gate runs: a non-member never reaches it", async () => {
1033
+ const gateSpy: jest.SpyInstance = jest.spyOn(
1034
+ CommonAPI,
1035
+ "assertCanCreateTable",
1036
+ );
1037
+
1038
+ mockProps({
1039
+ tenantId: projectId,
1040
+ userId: userId,
1041
+ userType: UserType.User,
1042
+ userTenantAccessPermission: buildTenantPermissions([
1043
+ {
1044
+ projectId: otherProjectId,
1045
+ permissions: [buildPermission(Permission.ProjectOwner)],
1046
+ },
1047
+ ]),
1048
+ });
1049
+
1050
+ const { response } = await invokeRoute({ route });
1051
+
1052
+ expectRefused(response, 422, NOT_A_MEMBER_MESSAGE);
1053
+ expect(gateSpy).not.toHaveBeenCalled();
1054
+ });
1055
+ });
1056
+
1057
+ describe("team block list", () => {
1058
+ test.each([
1059
+ ["ProjectOwner", Permission.ProjectOwner],
1060
+ ["ProjectAdmin", Permission.ProjectAdmin],
1061
+ ["ProjectMember", Permission.ProjectMember],
1062
+ ["SettingsAdmin", Permission.SettingsAdmin],
1063
+ ["SettingsMember", Permission.SettingsMember],
1064
+ [
1065
+ "CreateWorkspaceNotificationRule",
1066
+ Permission.CreateWorkspaceNotificationRule,
1067
+ ],
1068
+ ])(
1069
+ "a member with %s whose team has an unlabelled BLOCK on CreateWorkspaceNotificationRule is refused with the block message and nothing is sent",
1070
+ async (_permissionLabel: string, permission: Permission) => {
1071
+ mockProps(
1072
+ buildMemberProps({
1073
+ projectId: projectId,
1074
+ userId: userId,
1075
+ permissions: [permission],
1076
+ blockedPermissions: [Permission.CreateWorkspaceNotificationRule],
1077
+ }),
1078
+ );
1079
+
1080
+ const { response } = await invokeRoute({ route });
1081
+
1082
+ expectRefused(
1083
+ response,
1084
+ 422,
1085
+ blockedMessage(Permission.CreateWorkspaceNotificationRule),
1086
+ );
1087
+ },
1088
+ );
1089
+
1090
+ test("a BLOCK on any other permission in the create list refuses too, as the CRUD create does", async () => {
1091
+ mockProps(
1092
+ buildMemberProps({
1093
+ projectId: projectId,
1094
+ userId: userId,
1095
+ permissions: [Permission.ProjectAdmin],
1096
+ blockedPermissions: [Permission.ProjectMember],
1097
+ }),
1098
+ );
1099
+
1100
+ const { response } = await invokeRoute({ route });
1101
+
1102
+ expectRefused(response, 422, blockedMessage(Permission.ProjectMember));
1103
+ });
1104
+
1105
+ test("a BLOCK on reading, editing or deleting rules does not stop a ProjectMember from sending a test", async () => {
1106
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
1107
+ projectId: projectId,
1108
+ userId: userId,
1109
+ permissions: [Permission.ProjectMember],
1110
+ blockedPermissions: [
1111
+ Permission.ReadWorkspaceNotificationRule,
1112
+ Permission.EditWorkspaceNotificationRule,
1113
+ Permission.DeleteWorkspaceNotificationRule,
1114
+ ],
1115
+ });
1116
+
1117
+ mockProps(props);
1118
+
1119
+ const { response } = await invokeRoute({ route });
1120
+
1121
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1122
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual(
1123
+ expectedArgs({ route, props, body: route.validBody }),
1124
+ );
1125
+ expect(response.statusCalls).toEqual([200]);
1126
+ });
1127
+
1128
+ test("a BLOCK scoped to labels is left to the row-level rules, as the CRUD create's table check leaves it, so the test is sent", async () => {
1129
+ /*
1130
+ * checkTableLevelBlockPermissions only refuses a block row with no
1131
+ * labels; a labelled row covers the labelled records alone. A test
1132
+ * notification creates no record, so there is nothing for it to
1133
+ * cover here.
1134
+ */
1135
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
1136
+ projectId: projectId,
1137
+ userId: userId,
1138
+ permissions: [Permission.ProjectMember],
1139
+ blockedPermissions: [Permission.CreateWorkspaceNotificationRule],
1140
+ blockLabelIds: [LABEL_ID],
1141
+ });
1142
+
1143
+ mockProps(props);
1144
+
1145
+ const { response } = await invokeRoute({ route });
1146
+
1147
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1148
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual(
1149
+ expectedArgs({ route, props, body: route.validBody }),
1150
+ );
1151
+ expect(response.statusCalls).toEqual([200]);
1152
+ });
1153
+
1154
+ test("an unlabelled BLOCK still wins when a labelled BLOCK row for the same permission comes first", async () => {
1155
+ mockProps({
1156
+ tenantId: projectId,
1157
+ userId: userId,
1158
+ userType: UserType.User,
1159
+ userTenantAccessPermission: buildTenantPermissions([
1160
+ {
1161
+ projectId: projectId,
1162
+ permissions: [
1163
+ buildPermission(Permission.ProjectMember),
1164
+ buildPermission(
1165
+ Permission.CreateWorkspaceNotificationRule,
1166
+ true,
1167
+ [LABEL_ID],
1168
+ ),
1169
+ buildPermission(
1170
+ Permission.CreateWorkspaceNotificationRule,
1171
+ true,
1172
+ ),
1173
+ ],
1174
+ },
1175
+ ]),
1176
+ });
1177
+
1178
+ const { response } = await invokeRoute({ route });
1179
+
1180
+ expectRefused(
1181
+ response,
1182
+ 422,
1183
+ blockedMessage(Permission.CreateWorkspaceNotificationRule),
1184
+ );
1185
+ });
1186
+
1187
+ test("a BLOCK in another project does not refuse the test in this one", async () => {
1188
+ const props: DatabaseCommonInteractionProps = {
1189
+ tenantId: projectId,
1190
+ userId: userId,
1191
+ userType: UserType.User,
1192
+ userTenantAccessPermission: buildTenantPermissions([
1193
+ {
1194
+ projectId: projectId,
1195
+ permissions: [buildPermission(Permission.ProjectMember)],
1196
+ },
1197
+ {
1198
+ projectId: otherProjectId,
1199
+ permissions: [
1200
+ buildPermission(Permission.ProjectMember),
1201
+ buildPermission(
1202
+ Permission.CreateWorkspaceNotificationRule,
1203
+ true,
1204
+ ),
1205
+ ],
1206
+ },
1207
+ ]),
1208
+ };
1209
+
1210
+ mockProps(props);
1211
+
1212
+ const { response } = await invokeRoute({ route });
1213
+
1214
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1215
+ expect(response.statusCalls).toEqual([200]);
1216
+ });
1217
+
1218
+ test.each([
1219
+ ["ProjectMember", [Permission.ProjectMember]],
1220
+ ["Viewer", [Permission.Viewer]],
1221
+ ["no grants at all", []],
1222
+ ])(
1223
+ "a master admin who is a member with %s and the same unlabelled BLOCK still sends the test",
1224
+ async (_permissionLabel: string, permissions: Array<Permission>) => {
1225
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
1226
+ projectId: projectId,
1227
+ userId: userId,
1228
+ permissions: permissions,
1229
+ blockedPermissions: [Permission.CreateWorkspaceNotificationRule],
1230
+ userType: UserType.MasterAdmin,
1231
+ isMasterAdmin: true,
1232
+ });
1233
+
1234
+ mockProps(props);
1235
+
1236
+ const { response } = await invokeRoute({ route });
1237
+
1238
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1239
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual(
1240
+ expectedArgs({ route, props, body: route.validBody }),
1241
+ );
1242
+ expect(response.statusCalls).toEqual([200]);
1243
+ expect(response.body).toEqual({});
1244
+ },
1245
+ );
1246
+
1247
+ test("an anonymous caller carrying a forged grant and a BLOCK is still 401, not the block message", async () => {
1248
+ mockProps({
1249
+ tenantId: projectId,
1250
+ userType: UserType.Public,
1251
+ userTenantAccessPermission: buildTenantPermissions([
1252
+ {
1253
+ projectId: projectId,
1254
+ permissions: [
1255
+ buildPermission(Permission.ProjectMember),
1256
+ buildPermission(
1257
+ Permission.CreateWorkspaceNotificationRule,
1258
+ true,
1259
+ ),
1260
+ ],
1261
+ },
1262
+ ]),
1263
+ });
1264
+
1265
+ const { response } = await invokeRoute({ route });
1266
+
1267
+ expectRefused(response, 401, CommonAPI.AUTHENTICATION_REQUIRED_MESSAGE);
1268
+ });
1269
+ });
1270
+
1271
+ describe("what is forwarded to the service", () => {
1272
+ test("the project and the tested-by user are the very objects from the authenticated props", async () => {
1273
+ const props: DatabaseCommonInteractionProps = ownerProps();
1274
+ mockProps(props);
1275
+
1276
+ await invokeRoute({ route });
1277
+
1278
+ const args: SendTestArgs = sendTestSpy.mock
1279
+ .calls[0]![0] as SendTestArgs;
1280
+ expect(args.projectId).toBe(props.tenantId);
1281
+ expect(args.testByUserId).toBe(props.userId);
1282
+ expect(args.workspaceType).toBe(route.workspaceType);
1283
+ });
1284
+
1285
+ test("projectId, workspaceType, testByUserId and userId in the body are ignored", async () => {
1286
+ const props: DatabaseCommonInteractionProps = ownerProps();
1287
+ mockProps(props);
1288
+
1289
+ const body: JSONObject = {
1290
+ ...route.validBody,
1291
+ projectId: otherProjectId.toString(),
1292
+ tenantId: otherProjectId.toString(),
1293
+ workspaceType:
1294
+ route.workspaceType === WorkspaceType.Slack
1295
+ ? WorkspaceType.MicrosoftTeams
1296
+ : WorkspaceType.Slack,
1297
+ testByUserId: ObjectID.generate().toString(),
1298
+ userId: ObjectID.generate().toString(),
1299
+ };
1300
+
1301
+ await invokeRoute({ route, body });
1302
+
1303
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1304
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual(
1305
+ expectedArgs({ route, props, body }),
1306
+ );
1307
+
1308
+ const args: SendTestArgs = sendTestSpy.mock
1309
+ .calls[0]![0] as SendTestArgs;
1310
+ expect(args.projectId.toString()).toBe(projectId.toString());
1311
+ expect(args.workspaceType).toBe(route.workspaceType);
1312
+ expect(args.testByUserId.toString()).toBe(userId.toString());
1313
+ });
1314
+
1315
+ test("forwards only this route's own destination fields, even when the body carries all of them", async () => {
1316
+ const props: DatabaseCommonInteractionProps = ownerProps();
1317
+ mockProps(props);
1318
+
1319
+ const body: JSONObject = {
1320
+ teamId: "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
1321
+ channelId: "C0123456789",
1322
+ chatId: "19:3a9c1f0e5b7d4e2f8a6b0c1d2e3f4a5b@thread.v2",
1323
+ };
1324
+
1325
+ await invokeRoute({ route, body });
1326
+
1327
+ const args: SendTestArgs = sendTestSpy.mock
1328
+ .calls[0]![0] as SendTestArgs;
1329
+
1330
+ expect(Object.keys(args).sort()).toEqual(
1331
+ [
1332
+ "projectId",
1333
+ "testByUserId",
1334
+ "workspaceType",
1335
+ ...route.forwardedFields,
1336
+ ].sort(),
1337
+ );
1338
+
1339
+ ALL_DESTINATION_FIELDS.forEach((field: DestinationField) => {
1340
+ if (route.forwardedFields.includes(field)) {
1341
+ expect(args[field]).toBe(body[field]);
1342
+ } else {
1343
+ expect(args[field]).toBeUndefined();
1344
+ }
1345
+ });
1346
+ });
1347
+
1348
+ test("string values are forwarded verbatim; trimming and validation belong to the service", async () => {
1349
+ const props: DatabaseCommonInteractionProps = ownerProps();
1350
+ mockProps(props);
1351
+
1352
+ const body: JSONObject = {};
1353
+ route.forwardedFields.forEach((field: DestinationField) => {
1354
+ body[field] = ` ${field}-value with spaces/and#symbols `;
1355
+ });
1356
+
1357
+ await invokeRoute({ route, body });
1358
+
1359
+ const args: SendTestArgs = sendTestSpy.mock
1360
+ .calls[0]![0] as SendTestArgs;
1361
+ route.forwardedFields.forEach((field: DestinationField) => {
1362
+ expect(args[field]).toBe(
1363
+ ` ${field}-value with spaces/and#symbols `,
1364
+ );
1365
+ });
1366
+ });
1367
+
1368
+ test("empty strings are forwarded as empty strings", async () => {
1369
+ const props: DatabaseCommonInteractionProps = ownerProps();
1370
+ mockProps(props);
1371
+
1372
+ const body: JSONObject = {};
1373
+ route.forwardedFields.forEach((field: DestinationField) => {
1374
+ body[field] = "";
1375
+ });
1376
+
1377
+ await invokeRoute({ route, body });
1378
+
1379
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual(
1380
+ expectedArgs({ route, props, body }),
1381
+ );
1382
+ });
1383
+
1384
+ test('a request with no body at all forwards "" for every destination field', async () => {
1385
+ const props: DatabaseCommonInteractionProps = ownerProps();
1386
+ mockProps(props);
1387
+
1388
+ const { response } = await invokeRoute({ route, omitBody: true });
1389
+
1390
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1391
+
1392
+ const args: SendTestArgs = sendTestSpy.mock
1393
+ .calls[0]![0] as SendTestArgs;
1394
+ route.forwardedFields.forEach((field: DestinationField) => {
1395
+ expect(args[field]).toBe("");
1396
+ });
1397
+ expect(args).toStrictEqual(expectedArgs({ route, props, body: null }));
1398
+ expect(response.statusCalls).toEqual([200]);
1399
+ });
1400
+
1401
+ test.each([
1402
+ ["null", null],
1403
+ ["an empty object", {}],
1404
+ ["an array", ["C0123456789"]],
1405
+ ["a string", "channelId=C0123456789"],
1406
+ ["a number", 42],
1407
+ ])(
1408
+ 'a body that is %s forwards "" for every destination field',
1409
+ async (_bodyLabel: string, body: unknown) => {
1410
+ const props: DatabaseCommonInteractionProps = ownerProps();
1411
+ mockProps(props);
1412
+
1413
+ await invokeRoute({ route, body });
1414
+
1415
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1416
+
1417
+ const args: SendTestArgs = sendTestSpy.mock
1418
+ .calls[0]![0] as SendTestArgs;
1419
+ route.forwardedFields.forEach((field: DestinationField) => {
1420
+ expect(args[field]).toBe("");
1421
+ });
1422
+ },
1423
+ );
1424
+
1425
+ test.each([
1426
+ ["a number", 12345],
1427
+ ["zero", 0],
1428
+ ["true", true],
1429
+ ["false", false],
1430
+ ["null", null],
1431
+ ["an object", { id: "C0123456789" }],
1432
+ ["an object with a toString", { toString: "C0123456789" }],
1433
+ ["an array of strings", ["C0123456789"]],
1434
+ ["an empty array", []],
1435
+ ["a nested array", [["19:chat@thread.v2"]]],
1436
+ ])(
1437
+ 'a destination field that is %s reaches the service as ""',
1438
+ async (_valueLabel: string, value: unknown) => {
1439
+ const props: DatabaseCommonInteractionProps = ownerProps();
1440
+ mockProps(props);
1441
+
1442
+ const body: Record<string, unknown> = {};
1443
+ route.forwardedFields.forEach((field: DestinationField) => {
1444
+ body[field] = value;
1445
+ });
1446
+
1447
+ await invokeRoute({ route, body });
1448
+
1449
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1450
+
1451
+ const args: SendTestArgs = sendTestSpy.mock
1452
+ .calls[0]![0] as SendTestArgs;
1453
+ route.forwardedFields.forEach((field: DestinationField) => {
1454
+ expect(args[field]).toBe("");
1455
+ });
1456
+ expect(args).toStrictEqual(expectedArgs({ route, props, body }));
1457
+ },
1458
+ );
1459
+
1460
+ test("each destination field is sanitised on its own: a good field survives beside a bad one", async () => {
1461
+ const props: DatabaseCommonInteractionProps = ownerProps();
1462
+ mockProps(props);
1463
+
1464
+ const body: Record<string, unknown> = {};
1465
+ route.forwardedFields.forEach(
1466
+ (field: DestinationField, index: number) => {
1467
+ body[field] = index === 0 ? 987 : `good-${field}`;
1468
+ },
1469
+ );
1470
+
1471
+ await invokeRoute({ route, body });
1472
+
1473
+ const args: SendTestArgs = sendTestSpy.mock
1474
+ .calls[0]![0] as SendTestArgs;
1475
+ route.forwardedFields.forEach(
1476
+ (field: DestinationField, index: number) => {
1477
+ expect(args[field]).toBe(index === 0 ? "" : `good-${field}`);
1478
+ },
1479
+ );
1480
+ });
1481
+ });
1482
+
1483
+ describe("responses", () => {
1484
+ test("success answers 200 with an empty object and does not echo the thread", async () => {
1485
+ const { response } = await invokeRoute({ route });
1486
+
1487
+ expect(response.statusCalls).toEqual([200]);
1488
+ expect(response.sendCalls).toEqual([{}]);
1489
+
1490
+ const serialized: string = JSON.stringify(response.sendCalls);
1491
+ expect(serialized).not.toContain(SAMPLE_THREAD.threadId);
1492
+ expect(serialized).not.toContain(SAMPLE_THREAD.channel.id);
1493
+ });
1494
+
1495
+ test("the service is awaited: a slow send still answers only once it resolves", async () => {
1496
+ let resolveSend: (thread: WorkspaceThread) => void = (): void => {
1497
+ return undefined;
1498
+ };
1499
+
1500
+ sendTestSpy.mockImplementation(() => {
1501
+ return new Promise<WorkspaceThread>(
1502
+ (resolve: (thread: WorkspaceThread) => void) => {
1503
+ resolveSend = resolve;
1504
+ },
1505
+ );
1506
+ });
1507
+
1508
+ let settled: boolean = false;
1509
+
1510
+ const pending: Promise<{
1511
+ response: InvokedResponse;
1512
+ req: ExpressRequest;
1513
+ }> = invokeRoute({ route }).then(
1514
+ (result: { response: InvokedResponse; req: ExpressRequest }) => {
1515
+ settled = true;
1516
+ return result;
1517
+ },
1518
+ );
1519
+
1520
+ // Let the handler run up to the service call.
1521
+ await new Promise<void>((resolve: () => void) => {
1522
+ setTimeout(resolve, 20);
1523
+ });
1524
+
1525
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1526
+ expect(settled).toBe(false);
1527
+
1528
+ resolveSend(SAMPLE_THREAD);
1529
+
1530
+ const { response } = await pending;
1531
+
1532
+ expect(response.statusCalls).toEqual([200]);
1533
+ });
1534
+
1535
+ test.each([
1536
+ ["Please choose a channel or a chat to send the test notification to."],
1537
+ ["The Slack channel id is not valid."],
1538
+ ["The Microsoft Teams channel id is not valid."],
1539
+ ["The Microsoft Teams chat id is not valid."],
1540
+ ["Please select the team this channel belongs to."],
1541
+ [
1542
+ "This chat is no longer connected to OneUptime. Add the OneUptime app to the chat in Microsoft Teams, click Refresh Chats, and try again.",
1543
+ ],
1544
+ [
1545
+ "Could not send the test notification to #alerts. channel_not_found; not_in_channel",
1546
+ ],
1547
+ ])(
1548
+ "a BadDataException from the service answers 400 with exactly its message: %s",
1549
+ async (message: string) => {
1550
+ sendTestSpy.mockRejectedValue(new BadDataException(message));
1551
+
1552
+ const { response } = await invokeRoute({ route });
1553
+
1554
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1555
+ expect(response.statusCalls).toEqual([400]);
1556
+ expect(response.sendCalls).toHaveLength(1);
1557
+ expect(response.body).toEqual({ message: message });
1558
+ },
1559
+ );
1560
+
1561
+ test("a NotAuthorizedException from the service answers with its own status (422) and message", async () => {
1562
+ sendTestSpy.mockRejectedValue(
1563
+ new NotAuthorizedException("This chat belongs to another project."),
1564
+ );
1565
+
1566
+ const { response } = await invokeRoute({ route });
1567
+
1568
+ expect(response.statusCalls).toEqual([422]);
1569
+ expect(response.body).toEqual({
1570
+ message: "This chat belongs to another project.",
1571
+ });
1572
+ });
1573
+
1574
+ test("a NotAuthenticatedException from the service answers 401", async () => {
1575
+ sendTestSpy.mockRejectedValue(
1576
+ new NotAuthenticatedException("Session expired."),
1577
+ );
1578
+
1579
+ const { response } = await invokeRoute({ route });
1580
+
1581
+ expect(response.statusCalls).toEqual([401]);
1582
+ expect(response.body).toEqual({ message: "Session expired." });
1583
+ });
1584
+
1585
+ test("an unexpected Error from the service answers 500 with its message instead of crashing the request", async () => {
1586
+ sendTestSpy.mockRejectedValue(new Error("boom"));
1587
+
1588
+ const { response } = await invokeRoute({ route });
1589
+
1590
+ expect(response.statusCalls).toEqual([500]);
1591
+ expect(response.sendCalls).toHaveLength(1);
1592
+ expect(response.body).toEqual({ message: "boom" });
1593
+ });
1594
+
1595
+ test("a failed send is never reported as success", async () => {
1596
+ sendTestSpy.mockRejectedValue(
1597
+ new BadDataException(
1598
+ "OneUptime could not send the test notification. Please reconnect from Project Settings and try again.",
1599
+ ),
1600
+ );
1601
+
1602
+ const { response } = await invokeRoute({ route });
1603
+
1604
+ expect(response.statusCalls).not.toContain(200);
1605
+ expect(response.body).not.toEqual({});
1606
+ });
1607
+
1608
+ test("an exception whose code is not an HTTP status is answered as 500", async () => {
1609
+ // APIException is 2: a code, but not an HTTP status.
1610
+ const odd: Exception = new Exception(ExceptionCode.APIException, "odd");
1611
+
1612
+ sendTestSpy.mockRejectedValue(odd);
1613
+
1614
+ const { response } = await invokeRoute({ route });
1615
+
1616
+ expect(response.statusCalls).toEqual([500]);
1617
+ expect(response.body).toEqual({ message: "odd" });
1618
+ });
1619
+ });
1620
+ });
1621
+
1622
+ describe("route isolation, spelled out per route", () => {
1623
+ const EVERYTHING: JSONObject = {
1624
+ teamId: "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
1625
+ channelId: "19:0123456789abcdef0123456789abcdef@thread.tacv2",
1626
+ chatId: "19:3a9c1f0e5b7d4e2f8a6b0c1d2e3f4a5b@thread.v2",
1627
+ };
1628
+
1629
+ test("the Microsoft Teams channel route never forwards chatId", async () => {
1630
+ const props: DatabaseCommonInteractionProps = ownerProps();
1631
+ mockProps(props);
1632
+
1633
+ await invokeRoute({ route: TEAMS_CHANNEL_ROUTE, body: EVERYTHING });
1634
+
1635
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1636
+
1637
+ const args: SendTestArgs = sendTestSpy.mock.calls[0]![0] as SendTestArgs;
1638
+ expect(args).toStrictEqual({
1639
+ projectId: props.tenantId,
1640
+ workspaceType: WorkspaceType.MicrosoftTeams,
1641
+ testByUserId: props.userId,
1642
+ teamId: EVERYTHING["teamId"],
1643
+ channelId: EVERYTHING["channelId"],
1644
+ });
1645
+ expect(Object.prototype.hasOwnProperty.call(args, "chatId")).toBe(false);
1646
+ });
1647
+
1648
+ test("the Microsoft Teams channel route sends a body holding only a chatId as a channel test with no destination", async () => {
1649
+ const props: DatabaseCommonInteractionProps = ownerProps();
1650
+ mockProps(props);
1651
+
1652
+ await invokeRoute({
1653
+ route: TEAMS_CHANNEL_ROUTE,
1654
+ body: { chatId: EVERYTHING["chatId"] },
1655
+ });
1656
+
1657
+ /*
1658
+ * The chat must not be reachable through the channel route: the service
1659
+ * sees an empty channel and refuses, rather than posting to the chat.
1660
+ */
1661
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual({
1662
+ projectId: props.tenantId,
1663
+ workspaceType: WorkspaceType.MicrosoftTeams,
1664
+ testByUserId: props.userId,
1665
+ teamId: "",
1666
+ channelId: "",
1667
+ });
1668
+ });
1669
+
1670
+ test("the Microsoft Teams chat route never forwards channelId or teamId", async () => {
1671
+ const props: DatabaseCommonInteractionProps = ownerProps();
1672
+ mockProps(props);
1673
+
1674
+ await invokeRoute({ route: TEAMS_CHAT_ROUTE, body: EVERYTHING });
1675
+
1676
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1677
+
1678
+ const args: SendTestArgs = sendTestSpy.mock.calls[0]![0] as SendTestArgs;
1679
+ expect(args).toStrictEqual({
1680
+ projectId: props.tenantId,
1681
+ workspaceType: WorkspaceType.MicrosoftTeams,
1682
+ testByUserId: props.userId,
1683
+ chatId: EVERYTHING["chatId"],
1684
+ });
1685
+ expect(Object.prototype.hasOwnProperty.call(args, "channelId")).toBe(
1686
+ false,
1687
+ );
1688
+ expect(Object.prototype.hasOwnProperty.call(args, "teamId")).toBe(false);
1689
+ });
1690
+
1691
+ test("the Microsoft Teams chat route sends a body holding only a channel as a chat test with no destination", async () => {
1692
+ const props: DatabaseCommonInteractionProps = ownerProps();
1693
+ mockProps(props);
1694
+
1695
+ await invokeRoute({
1696
+ route: TEAMS_CHAT_ROUTE,
1697
+ body: {
1698
+ teamId: EVERYTHING["teamId"],
1699
+ channelId: EVERYTHING["channelId"],
1700
+ },
1701
+ });
1702
+
1703
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual({
1704
+ projectId: props.tenantId,
1705
+ workspaceType: WorkspaceType.MicrosoftTeams,
1706
+ testByUserId: props.userId,
1707
+ chatId: "",
1708
+ });
1709
+ });
1710
+
1711
+ test("the Slack channel route never forwards chatId or teamId, and always says Slack", async () => {
1712
+ const props: DatabaseCommonInteractionProps = ownerProps();
1713
+ mockProps(props);
1714
+
1715
+ await invokeRoute({
1716
+ route: SLACK_CHANNEL_ROUTE,
1717
+ body: { ...EVERYTHING, channelId: "C0123456789" },
1718
+ });
1719
+
1720
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
1721
+
1722
+ const args: SendTestArgs = sendTestSpy.mock.calls[0]![0] as SendTestArgs;
1723
+ expect(args).toStrictEqual({
1724
+ projectId: props.tenantId,
1725
+ workspaceType: WorkspaceType.Slack,
1726
+ testByUserId: props.userId,
1727
+ channelId: "C0123456789",
1728
+ });
1729
+ expect(Object.prototype.hasOwnProperty.call(args, "chatId")).toBe(false);
1730
+ expect(Object.prototype.hasOwnProperty.call(args, "teamId")).toBe(false);
1731
+ });
1732
+
1733
+ test("the two Microsoft Teams routes always say MicrosoftTeams and the Slack route always says Slack", async () => {
1734
+ mockProps(ownerProps());
1735
+
1736
+ await invokeRoute({ route: TEAMS_CHANNEL_ROUTE });
1737
+ await invokeRoute({ route: TEAMS_CHAT_ROUTE });
1738
+ await invokeRoute({ route: SLACK_CHANNEL_ROUTE });
1739
+
1740
+ const workspaceTypes: Array<WorkspaceType> = sendTestSpy.mock.calls.map(
1741
+ (call: Array<unknown>) => {
1742
+ return (call[0] as SendTestArgs).workspaceType;
1743
+ },
1744
+ );
1745
+
1746
+ expect(workspaceTypes).toEqual([
1747
+ WorkspaceType.MicrosoftTeams,
1748
+ WorkspaceType.MicrosoftTeams,
1749
+ WorkspaceType.Slack,
1750
+ ]);
1751
+ });
1752
+ });
1753
+ });
1754
+
1755
+ interface HttpProbeResult {
1756
+ status: number;
1757
+ body: unknown;
1758
+ }
1759
+
1760
+ type HttpRequestJsonFunction = (data: {
1761
+ port: number;
1762
+ method: string;
1763
+ path: string;
1764
+ headers: http.OutgoingHttpHeaders;
1765
+ body?: JSONObject | undefined;
1766
+ }) => Promise<HttpProbeResult>;
1767
+
1768
+ /*
1769
+ * The jest environment here is jsdom, which has no global fetch, so the probe
1770
+ * talks to the server with Node's http client.
1771
+ */
1772
+ const httpRequestJson: HttpRequestJsonFunction = (data: {
1773
+ port: number;
1774
+ method: string;
1775
+ path: string;
1776
+ headers: http.OutgoingHttpHeaders;
1777
+ body?: JSONObject | undefined;
1778
+ }): Promise<HttpProbeResult> => {
1779
+ return new Promise<HttpProbeResult>(
1780
+ (
1781
+ resolve: (result: HttpProbeResult) => void,
1782
+ reject: (error: Error) => void,
1783
+ ) => {
1784
+ const payload: string | undefined =
1785
+ data.body === undefined ? undefined : JSON.stringify(data.body);
1786
+
1787
+ const headers: http.OutgoingHttpHeaders = { ...data.headers };
1788
+
1789
+ if (payload !== undefined) {
1790
+ headers["content-type"] = "application/json";
1791
+ headers["content-length"] = Buffer.byteLength(payload).toString();
1792
+ }
1793
+
1794
+ const request: http.ClientRequest = http.request(
1795
+ {
1796
+ host: "127.0.0.1",
1797
+ port: data.port,
1798
+ path: data.path,
1799
+ method: data.method,
1800
+ headers: headers,
1801
+ },
1802
+ (response: http.IncomingMessage) => {
1803
+ const chunks: Array<Buffer> = [];
1804
+
1805
+ response.on("data", (chunk: Buffer) => {
1806
+ chunks.push(chunk);
1807
+ });
1808
+
1809
+ response.on("end", () => {
1810
+ const raw: string = Buffer.concat(chunks).toString("utf8");
1811
+
1812
+ let parsed: unknown = null;
1813
+ try {
1814
+ parsed = raw ? JSON.parse(raw) : null;
1815
+ } catch {
1816
+ parsed = raw;
1817
+ }
1818
+
1819
+ resolve({
1820
+ status: response.statusCode || 0,
1821
+ body: parsed,
1822
+ });
1823
+ });
1824
+ },
1825
+ );
1826
+
1827
+ request.on("error", (error: Error) => {
1828
+ return reject(error);
1829
+ });
1830
+
1831
+ if (payload !== undefined) {
1832
+ request.write(payload);
1833
+ }
1834
+
1835
+ request.end();
1836
+ },
1837
+ );
1838
+ };
1839
+
1840
+ type WithServerFunction = (
1841
+ router: ExpressRouter,
1842
+ run: (port: number) => Promise<void>,
1843
+ ) => Promise<void>;
1844
+
1845
+ /*
1846
+ * Mounts the router the way the app does (under /api, JSON bodies parsed) and
1847
+ * always closes the server, even when an assertion fails - a leaked listener
1848
+ * keeps the event loop alive and hangs the whole jest run at exit.
1849
+ */
1850
+ const withServer: WithServerFunction = async (
1851
+ router: ExpressRouter,
1852
+ run: (port: number) => Promise<void>,
1853
+ ): Promise<void> => {
1854
+ const app: express.Express = express();
1855
+ app.use(express.json());
1856
+ app.use("/api", router);
1857
+
1858
+ const server: http.Server = http.createServer(app);
1859
+
1860
+ await new Promise<void>((resolve: () => void) => {
1861
+ server.listen(0, "127.0.0.1", resolve);
1862
+ });
1863
+
1864
+ try {
1865
+ await run((server.address() as AddressInfo).port);
1866
+ } finally {
1867
+ await new Promise<void>((resolve: () => void) => {
1868
+ server.close(() => {
1869
+ return resolve();
1870
+ });
1871
+ });
1872
+ }
1873
+ };
1874
+
1875
+ describe("Send Test routes over real HTTP", () => {
1876
+ let sendTestSpy: jest.SpyInstance;
1877
+ let teamsRouterForHttp: ExpressRouter;
1878
+ let slackRouterForHttp: ExpressRouter;
1879
+
1880
+ const TENANT_ID: string = "6f4e4b2c-1111-4c2a-9e3d-abcdefabcdef";
1881
+
1882
+ beforeAll(() => {
1883
+ teamsRouterForHttp = new MicrosoftTeamsAPI().getRouter();
1884
+ slackRouterForHttp = new SlackAPI().getRouter();
1885
+ }, 600000);
1886
+
1887
+ beforeEach(() => {
1888
+ sendTestSpy = jest
1889
+ .spyOn(
1890
+ WorkspaceNotificationRuleService,
1891
+ "sendTestNotificationToDestination",
1892
+ )
1893
+ .mockResolvedValue(SAMPLE_THREAD);
1894
+
1895
+ /*
1896
+ * getUserMiddleware fires ProjectService.updateLastActive for any request
1897
+ * with a tenant header; keep it off the database.
1898
+ */
1899
+ jest.spyOn(ProjectService, "updateLastActive").mockResolvedValue(undefined);
1900
+ jest.spyOn(ProjectService, "getCurrentPlan").mockResolvedValue({
1901
+ plan: null,
1902
+ isSubscriptionUnpaid: false,
1903
+ });
1904
+
1905
+ jest.spyOn(logger, "error").mockImplementation(() => {
1906
+ return undefined;
1907
+ });
1908
+ });
1909
+
1910
+ afterEach(() => {
1911
+ jest.restoreAllMocks();
1912
+ });
1913
+
1914
+ type GetHttpRouterFunction = (route: RouteSpec) => ExpressRouter;
1915
+
1916
+ const getHttpRouter: GetHttpRouterFunction = (
1917
+ route: RouteSpec,
1918
+ ): ExpressRouter => {
1919
+ return route.api === "Slack" ? slackRouterForHttp : teamsRouterForHttp;
1920
+ };
1921
+
1922
+ test.each(ALL_ROUTES)(
1923
+ "%s with only a tenantid header (no credentials) answers 401 and sends nothing",
1924
+ async (_label: string, route: RouteSpec) => {
1925
+ // Calls through: the real props are built from what the middleware set.
1926
+ const propsSpy: jest.SpyInstance = jest.spyOn(
1927
+ CommonAPI,
1928
+ "getDatabaseCommonInteractionProps",
1929
+ );
1930
+
1931
+ await withServer(getHttpRouter(route), async (port: number) => {
1932
+ const result: HttpProbeResult = await httpRequestJson({
1933
+ port: port,
1934
+ method: "POST",
1935
+ path: `/api${route.path}`,
1936
+ headers: { tenantid: TENANT_ID },
1937
+ body: route.validBody,
1938
+ });
1939
+
1940
+ /*
1941
+ * Pinned to exactly 401: a request with only a tenantid header is what
1942
+ * a signed-in user's browser sends once the access-token cookie has
1943
+ * expired, and the dashboard refreshes the session and resends on a
1944
+ * 401 and on nothing else.
1945
+ */
1946
+ expect(result.status).toBe(401);
1947
+ expect((result.body as { message?: string }).message).toBe(
1948
+ CommonAPI.AUTHENTICATION_REQUIRED_MESSAGE,
1949
+ );
1950
+ });
1951
+
1952
+ // The request reached this route's handler, and it stopped there.
1953
+ expect(propsSpy).toHaveBeenCalledTimes(1);
1954
+ expect(sendTestSpy).not.toHaveBeenCalled();
1955
+ },
1956
+ 60000,
1957
+ );
1958
+
1959
+ test.each(ALL_ROUTES)(
1960
+ "%s with no headers at all answers 401 and sends nothing",
1961
+ async (_label: string, route: RouteSpec) => {
1962
+ await withServer(getHttpRouter(route), async (port: number) => {
1963
+ const result: HttpProbeResult = await httpRequestJson({
1964
+ port: port,
1965
+ method: "POST",
1966
+ path: `/api${route.path}`,
1967
+ headers: {},
1968
+ body: route.validBody,
1969
+ });
1970
+
1971
+ expect(result.status).toBe(401);
1972
+ });
1973
+
1974
+ expect(sendTestSpy).not.toHaveBeenCalled();
1975
+ },
1976
+ 60000,
1977
+ );
1978
+
1979
+ test.each(ALL_ROUTES)(
1980
+ "%s is dispatched to its own handler through the real middleware, and forwards the JSON body",
1981
+ async (_label: string, route: RouteSpec) => {
1982
+ const projectId: ObjectID = new ObjectID(TENANT_ID);
1983
+ const userId: ObjectID = ObjectID.generate();
1984
+
1985
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
1986
+ projectId: projectId,
1987
+ userId: userId,
1988
+ permissions: [Permission.ProjectMember],
1989
+ });
1990
+
1991
+ /*
1992
+ * Identity is stubbed at the props layer (minting a real access token
1993
+ * needs the signing secret); routing, the real getUserMiddleware, body
1994
+ * parsing and the handler are all real.
1995
+ */
1996
+ const propsSpy: jest.SpyInstance = jest
1997
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
1998
+ .mockResolvedValue(props);
1999
+
2000
+ await withServer(getHttpRouter(route), async (port: number) => {
2001
+ const result: HttpProbeResult = await httpRequestJson({
2002
+ port: port,
2003
+ method: "POST",
2004
+ path: `/api${route.path}`,
2005
+ headers: { tenantid: TENANT_ID },
2006
+ body: {
2007
+ ...route.validBody,
2008
+ projectId: ObjectID.generate().toString(),
2009
+ },
2010
+ });
2011
+
2012
+ expect(result.status).toBe(200);
2013
+ expect(result.body).toEqual({});
2014
+ });
2015
+
2016
+ // getUserMiddleware ran first: it put the header's tenant on the request.
2017
+ expect(propsSpy).toHaveBeenCalledTimes(1);
2018
+ const reachedReq: OneUptimeRequest = propsSpy.mock
2019
+ .calls[0]![0] as OneUptimeRequest;
2020
+ expect(reachedReq.tenantId?.toString()).toBe(TENANT_ID);
2021
+ expect(reachedReq.userType).toBe(UserType.Public);
2022
+
2023
+ expect(sendTestSpy).toHaveBeenCalledTimes(1);
2024
+ expect(sendTestSpy.mock.calls[0]![0]).toStrictEqual(
2025
+ expectedArgs({ route, props, body: route.validBody }),
2026
+ );
2027
+ },
2028
+ 60000,
2029
+ );
2030
+
2031
+ test.each(ALL_ROUTES)(
2032
+ "%s refuses a ProjectMember whose team blocks CreateWorkspaceNotificationRule with 422 and sends nothing",
2033
+ async (_label: string, route: RouteSpec) => {
2034
+ const props: DatabaseCommonInteractionProps = buildMemberProps({
2035
+ projectId: new ObjectID(TENANT_ID),
2036
+ userId: ObjectID.generate(),
2037
+ permissions: [Permission.ProjectMember],
2038
+ blockedPermissions: [Permission.CreateWorkspaceNotificationRule],
2039
+ });
2040
+
2041
+ jest
2042
+ .spyOn(CommonAPI, "getDatabaseCommonInteractionProps")
2043
+ .mockResolvedValue(props);
2044
+
2045
+ await withServer(getHttpRouter(route), async (port: number) => {
2046
+ const result: HttpProbeResult = await httpRequestJson({
2047
+ port: port,
2048
+ method: "POST",
2049
+ path: `/api${route.path}`,
2050
+ headers: { tenantid: TENANT_ID },
2051
+ body: route.validBody,
2052
+ });
2053
+
2054
+ expect(result.status).toBe(422);
2055
+ expect(result.body).toEqual({
2056
+ message: blockedMessage(Permission.CreateWorkspaceNotificationRule),
2057
+ });
2058
+ });
2059
+
2060
+ expect(sendTestSpy).not.toHaveBeenCalled();
2061
+ },
2062
+ 60000,
2063
+ );
2064
+ });